dobo 2.0.0 → 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 (139) 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 +38 -19
  6. package/docs/Dobo.html +26 -0
  7. package/docs/data/search.json +1 -0
  8. package/docs/fonts/Inconsolata-Regular.ttf +0 -0
  9. package/docs/fonts/OpenSans-Regular.ttf +0 -0
  10. package/docs/fonts/WorkSans-Bold.ttf +0 -0
  11. package/docs/global.html +7 -0
  12. package/docs/index.html +3 -0
  13. package/docs/index.js.html +578 -0
  14. package/docs/lib_collect-connections.js.html +39 -0
  15. package/docs/lib_collect-drivers.js.html +52 -0
  16. package/docs/lib_collect-features.js.html +36 -0
  17. package/docs/lib_collect-schemas.js.html +94 -0
  18. package/docs/lib_index.js.html +6 -0
  19. package/docs/method_model_create.js.html +35 -0
  20. package/docs/method_model_drop.js.html +34 -0
  21. package/docs/method_model_exists.js.html +40 -0
  22. package/docs/method_record_count.js.html +69 -0
  23. package/docs/method_record_create.js.html +114 -0
  24. package/docs/method_record_find-all.js.html +44 -0
  25. package/docs/method_record_find-one.js.html +73 -0
  26. package/docs/method_record_find.js.html +118 -0
  27. package/docs/method_record_get.js.html +92 -0
  28. package/docs/method_record_remove.js.html +75 -0
  29. package/docs/method_record_update.js.html +107 -0
  30. package/docs/method_record_upsert.js.html +54 -0
  31. package/docs/method_sanitize_body.js.html +88 -0
  32. package/docs/method_sanitize_date.js.html +30 -0
  33. package/docs/method_sanitize_id.js.html +20 -0
  34. package/docs/method_validate.js.html +249 -0
  35. package/docs/module-Lib.html +3 -0
  36. package/docs/scripts/core.js +726 -0
  37. package/docs/scripts/core.min.js +23 -0
  38. package/docs/scripts/resize.js +90 -0
  39. package/docs/scripts/search.js +265 -0
  40. package/docs/scripts/search.min.js +6 -0
  41. package/docs/scripts/third-party/Apache-License-2.0.txt +202 -0
  42. package/docs/scripts/third-party/fuse.js +9 -0
  43. package/docs/scripts/third-party/hljs-line-num-original.js +369 -0
  44. package/docs/scripts/third-party/hljs-line-num.js +1 -0
  45. package/docs/scripts/third-party/hljs-original.js +5171 -0
  46. package/docs/scripts/third-party/hljs.js +1 -0
  47. package/docs/scripts/third-party/popper.js +5 -0
  48. package/docs/scripts/third-party/tippy.js +1 -0
  49. package/docs/scripts/third-party/tocbot.js +672 -0
  50. package/docs/scripts/third-party/tocbot.min.js +1 -0
  51. package/docs/static/bitcoin.jpeg +0 -0
  52. package/docs/static/home.md +25 -0
  53. package/docs/static/logo-ecosystem.png +0 -0
  54. package/docs/static/logo.png +0 -0
  55. package/docs/styles/clean-jsdoc-theme-base.css +1159 -0
  56. package/docs/styles/clean-jsdoc-theme-dark.css +412 -0
  57. package/docs/styles/clean-jsdoc-theme-light.css +482 -0
  58. package/docs/styles/clean-jsdoc-theme-scrollbar.css +30 -0
  59. package/docs/styles/clean-jsdoc-theme-without-scrollbar.min.css +1 -0
  60. package/docs/styles/clean-jsdoc-theme.min.css +1 -0
  61. package/extend/bajo/intl/en-US.json +3 -2
  62. package/extend/bajo/intl/id.json +3 -2
  63. package/extend/bajoCli/applet/connection.js +5 -5
  64. package/extend/bajoCli/applet/lib/post-process.js +22 -16
  65. package/extend/bajoCli/applet/model-clear.js +4 -4
  66. package/extend/bajoCli/applet/model-rebuild.js +13 -13
  67. package/extend/bajoCli/applet/record-create.js +10 -8
  68. package/extend/bajoCli/applet/record-find.js +6 -5
  69. package/extend/bajoCli/applet/record-get.js +5 -5
  70. package/extend/bajoCli/applet/record-remove.js +5 -5
  71. package/extend/bajoCli/applet/record-update.js +9 -9
  72. package/extend/bajoCli/applet/schema.js +4 -4
  73. package/extend/bajoCli/applet/stat-count.js +4 -4
  74. package/extend/dobo/feature/created-at.js +1 -1
  75. package/extend/dobo/feature/removed-at.js +3 -3
  76. package/extend/dobo/feature/updated-at.js +2 -2
  77. package/extend/waibuMpa/route/attachment/@model/@id/@field/@file.js +3 -3
  78. package/index.js +230 -72
  79. package/lib/add-fixtures.js +5 -5
  80. package/lib/build-bulk-action.js +2 -2
  81. package/lib/check-unique.js +2 -2
  82. package/lib/collect-connections.js +14 -3
  83. package/lib/collect-drivers.js +14 -6
  84. package/lib/{collect-feature.js → collect-features.js} +12 -4
  85. package/lib/collect-schemas.js +17 -9
  86. package/lib/exec-feature-hook.js +1 -1
  87. package/lib/exec-validation.js +5 -5
  88. package/lib/generic-prop-sanitizer.js +6 -5
  89. package/lib/handle-attachment-upload.js +2 -2
  90. package/lib/index.js +3 -0
  91. package/lib/mem-db/conn-sanitizer.js +1 -1
  92. package/lib/mem-db/instantiate.js +4 -4
  93. package/lib/mem-db/method/record/find.js +1 -1
  94. package/lib/mem-db/method/record/get.js +1 -1
  95. package/lib/mem-db/method/record/remove.js +1 -1
  96. package/lib/mem-db/method/record/update.js +1 -1
  97. package/lib/mem-db/start.js +1 -1
  98. package/lib/merge-attachment-info.js +2 -2
  99. package/lib/multi-rel-rows.js +2 -2
  100. package/lib/resolve-method.js +3 -3
  101. package/lib/sanitize-schema.js +8 -7
  102. package/lib/single-rel-rows.js +2 -2
  103. package/method/attachment/copy-uploaded.js +2 -2
  104. package/method/attachment/create.js +3 -3
  105. package/method/attachment/find.js +2 -2
  106. package/method/attachment/get-path.js +3 -3
  107. package/method/attachment/get.js +1 -1
  108. package/method/attachment/pre-check.js +1 -1
  109. package/method/attachment/remove.js +1 -1
  110. package/method/bulk/create.js +6 -6
  111. package/method/model/clear.js +5 -5
  112. package/method/model/create.js +18 -5
  113. package/method/model/drop.js +17 -5
  114. package/method/model/exists.js +18 -5
  115. package/method/record/clear.js +5 -5
  116. package/method/record/count.js +27 -5
  117. package/method/record/create.js +46 -6
  118. package/method/record/find-all.js +16 -0
  119. package/method/record/find-one.js +20 -6
  120. package/method/record/find.js +69 -6
  121. package/method/record/get.js +48 -6
  122. package/method/record/remove.js +36 -5
  123. package/method/record/update.js +47 -6
  124. package/method/record/upsert.js +18 -2
  125. package/method/sanitize/body.js +18 -3
  126. package/method/sanitize/date.js +20 -7
  127. package/method/sanitize/id.js +10 -0
  128. package/method/stat/aggregate.js +4 -4
  129. package/method/stat/histogram.js +4 -4
  130. package/method/validate.js +96 -7
  131. package/package.json +41 -36
  132. package/wiki/APPLETS.md +57 -0
  133. package/wiki/CONFIG.md +25 -0
  134. package/wiki/CONTRIBUTING.md +5 -0
  135. package/wiki/DEV-GUIDE.md +1 -0
  136. package/wiki/ECOSYSTEM.md +20 -0
  137. package/wiki/GETTING-STARTED.md +166 -0
  138. package/wiki/USER-GUIDE.md +1 -0
  139. /package/{docs/query-language.md → wiki/QUERY-LANGUAGE.md} +0 -0
@@ -1,10 +1,10 @@
1
1
  async function execValidation ({ name, body, options, partial }) {
2
2
  const { runHook } = this.app.bajo
3
- const { keys, camelCase } = this.lib._
3
+ const { keys, camelCase } = this.app.lib._
4
4
  const { noHook } = options
5
5
  if (!noHook) {
6
- await runHook(`${this.name}:beforeRecordValidation`, name, body, options)
7
- await runHook(`${this.name}.${camelCase(name)}:beforeRecordValidation`, body, options)
6
+ await runHook(`${this.ns}:beforeRecordValidation`, name, body, options)
7
+ await runHook(`${this.ns}.${camelCase(name)}:beforeRecordValidation`, body, options)
8
8
  }
9
9
  const { validation = {} } = options
10
10
  if (partial) {
@@ -12,8 +12,8 @@ async function execValidation ({ name, body, options, partial }) {
12
12
  }
13
13
  body = await this.validate(body, name, validation)
14
14
  if (!noHook) {
15
- await runHook(`${this.name}:afterRecordValidation`, name, body, options)
16
- await runHook(`${this.name}.${camelCase(name)}:afterRecordValidation`, body, options)
15
+ await runHook(`${this.ns}:afterRecordValidation`, name, body, options)
16
+ await runHook(`${this.ns}.${camelCase(name)}:afterRecordValidation`, body, options)
17
17
  }
18
18
  return body
19
19
  }
@@ -2,8 +2,9 @@ const indexTypes = ['default', 'unique', 'primary', 'fulltext']
2
2
 
3
3
  async function genericPropSanitizer ({ prop, schema, driver }) {
4
4
  const { join } = this.app.bajo
5
- const { has, get, each } = this.lib._
6
- const def = this.propType[prop.type]
5
+ const { has, get, each } = this.app.lib._
6
+ const { propType } = this.app.pluginClass.dobo
7
+ const def = propType[prop.type]
7
8
  // detect from drivers
8
9
  if (prop.type === 'string') {
9
10
  def.minLength = prop.minLength ?? 0
@@ -13,14 +14,14 @@ async function genericPropSanitizer ({ prop, schema, driver }) {
13
14
  if (def.minLength > 0) prop.required = true
14
15
  }
15
16
  if (prop.autoInc && !['smallint', 'integer'].includes(prop.type)) delete prop.autoInc
16
- each(['minLength', 'maxLength', 'kind'], p => {
17
+ each(['minLength', 'maxLength', 'textType'], p => {
17
18
  if (!has(def, p)) {
18
19
  delete prop[p]
19
20
  return undefined
20
21
  }
21
22
  prop[p] = get(prop, p, get(this.config, `default.property.${prop.type}.${p}`, def[p]))
22
- if (def.choices && !def.choices.includes(prop[p])) {
23
- this.fatal('unsupportedAllowedChoices%s%s%s%s%s', p, prop[p], prop.name, schema.name, join(def.choices))
23
+ if (def.values && !def.values.includes(prop[p])) {
24
+ this.fatal('unsupportedAllowedChoices%s%s%s%s%s', p, prop[p], prop.name, schema.name, join(def.values))
24
25
  }
25
26
  })
26
27
  if (prop.index && !indexTypes.includes(prop.index.type)) {
@@ -1,12 +1,12 @@
1
1
  async function handleAttachmentUpload ({ action, name, id, options = {} } = {}) {
2
2
  const { getPluginDataDir } = this.app.bajo
3
- const { fs } = this.lib
3
+ const { fs } = this.app.lib
4
4
  const { req, mimeType, stats, setFile, setField } = options
5
5
 
6
6
  name = this.attachmentPreCheck(name)
7
7
  if (!name) return
8
8
  if (action === 'remove') {
9
- const dir = `${getPluginDataDir(this.name)}/attachment/${name}/${id}`
9
+ const dir = `${getPluginDataDir(this.ns)}/attachment/${name}/${id}`
10
10
  await fs.remove(dir)
11
11
  return
12
12
  }
package/lib/index.js ADDED
@@ -0,0 +1,3 @@
1
+ /**
2
+ * @module Lib
3
+ */
@@ -1,5 +1,5 @@
1
1
  async function connSanitizer (connection) {
2
- const { pick } = this.lib._
2
+ const { pick } = this.app.lib._
3
3
  const result = pick(connection, ['type', 'name', 'driver'])
4
4
  result.memory = true
5
5
  return result
@@ -2,15 +2,15 @@ let saving = false
2
2
 
3
3
  async function instantiate ({ connection, schemas, noRebuild }) {
4
4
  const { getPluginDataDir } = this.app.bajo
5
- const { fs } = this.lib
6
- const { pick } = this.lib._
5
+ const { fs } = this.app.lib
6
+ const { pick } = this.app.lib._
7
7
  this.memDb = this.memDb ?? {}
8
8
  this.memDb.storage = this.memDb.storage ?? {}
9
9
  this.memDb.instances = this.memDb.instances ?? []
10
10
  const instance = pick(connection, ['name', 'type'])
11
11
  this.memDb.instances.push(instance)
12
12
  // if (noRebuild) return
13
- const pdir = `${getPluginDataDir(this.name)}/memDb/data` // persistence dir
13
+ const pdir = `${getPluginDataDir(this.ns)}/memDb/data` // persistence dir
14
14
  fs.ensureDirSync(pdir)
15
15
  const persistence = []
16
16
  for (const schema of schemas) {
@@ -35,7 +35,7 @@ async function instantiate ({ connection, schemas, noRebuild }) {
35
35
  fs.writeFileSync(`${pdir}/${item}.json`, JSON.stringify(data), 'utf8')
36
36
  }
37
37
  saving = false
38
- }, this.config.memDb.persistence.syncPeriod * 1000)
38
+ }, this.config.memDb.persistence.syncPeriodDur)
39
39
  }
40
40
 
41
41
  export default instantiate
@@ -2,7 +2,7 @@ import { Query } from 'mingo'
2
2
 
3
3
  async function find ({ schema, filter = {}, options = {} }) {
4
4
  const { prepPagination } = this.app.dobo
5
- const { omit } = this.lib._
5
+ const { omit } = this.app.lib._
6
6
  const { limit, skip, sort, page } = await prepPagination(filter, schema)
7
7
  const criteria = filter.query ?? {}
8
8
  const q = new Query(criteria, { idKey: 'id' })
@@ -1,6 +1,6 @@
1
1
  async function get ({ schema, id, options = {} }) {
2
2
  const { thrownNotFound = true } = options
3
- const { find } = this.lib._
3
+ const { find } = this.app.lib._
4
4
  const result = find(this.memDb.storage[schema.name], { id })
5
5
  if (!result && thrownNotFound) throw this.error('recordNotFound%s%s', id, schema.name, { statusCode: 404 })
6
6
  return { data: result }
@@ -2,7 +2,7 @@ import getRecord from './get.js'
2
2
 
3
3
  async function remove ({ schema, id, options = {} }) {
4
4
  const { noResult } = options
5
- const { findIndex, pullAt } = this.lib._
5
+ const { findIndex, pullAt } = this.app.lib._
6
6
  const rec = noResult ? undefined : await getRecord.call(this, { schema, id })
7
7
  const idx = findIndex(this.memDb.storage[schema.name], { id })
8
8
  pullAt(this.memDb.storage[schema.name], [idx])
@@ -2,7 +2,7 @@ import getRecord from './get.js'
2
2
 
3
3
  async function update ({ schema, id, body, options }) {
4
4
  const { noResult } = options
5
- const { findIndex, merge } = this.lib._
5
+ const { findIndex, merge } = this.app.lib._
6
6
  const old = noResult ? undefined : await getRecord.call(this, { schema, id })
7
7
  const idx = findIndex(this.memDb.storage[schema.name], { id })
8
8
  const current = this.memDb.storage[schema.name][idx]
@@ -1,7 +1,7 @@
1
1
  import addFixtures from '../add-fixtures.js'
2
2
 
3
3
  async function start () {
4
- const { filter, map } = this.lib._
4
+ const { filter, map } = this.app.lib._
5
5
 
6
6
  const conns = filter(this.connections, { type: 'dobo:memory' })
7
7
  const schemas = filter(this.schemas, s => {
@@ -1,7 +1,7 @@
1
1
  async function mergeAttachmentInfo (rec, source, { mimeType, stats, fullPath }) {
2
2
  const { importPkg } = this.app.bajo
3
- const { fs } = this.lib
4
- const { pick } = this.lib._
3
+ const { fs } = this.app.lib
4
+ const { pick } = this.app.lib._
5
5
  if (!this.app.waibu) return
6
6
  const mime = await importPkg('waibu:mime')
7
7
 
@@ -1,6 +1,6 @@
1
1
  async function multiRelRows ({ schema, records, options = {} }) {
2
- const { isSet } = this.lib.aneka
3
- const { uniq, find, map } = this.lib._
2
+ const { isSet } = this.app.lib.aneka
3
+ const { uniq, find, map } = this.app.lib._
4
4
  const props = schema.properties.filter(p => isSet(p.rel) && !(options.hidden ?? []).includes(p.name))
5
5
  // const props = schema.properties.filter(p => isSet(p.rel))
6
6
  options.rels = options.rels ?? []
@@ -1,13 +1,13 @@
1
1
  async function resolveMethod (name, method, options = {}) {
2
2
  const { importModule } = this.app.bajo
3
- const { fs } = this.lib
4
- const { camelCase } = this.lib._
3
+ const { fs } = this.app.lib
4
+ const { camelCase } = this.app.lib._
5
5
  const { schema, driver, connection } = this.getInfo(name)
6
6
  const [group, action] = method.split('-')
7
7
  if (!options.force && (schema.disabled ?? []).includes(action)) throw this.error('methodIsDisabled%s%s', camelCase(method), name)
8
8
  let file
9
9
  if (connection.name === 'memory') file = `${this.app[driver.ns].dir.pkg}/lib/mem-db/method/${group}/${action}.js`
10
- else file = `${this.app[driver.ns].dir.pkg}/extend/${this.name}/method/${group}/${action}.js`
10
+ else file = `${this.app[driver.ns].dir.pkg}/extend/${this.ns}/method/${group}/${action}.js`
11
11
  if (!fs.existsSync(file)) throw this.error('methodUnsupported%s%s', camelCase(method), name)
12
12
  const handler = await importModule(file)
13
13
  return { handler, schema, driver, connection }
@@ -1,12 +1,12 @@
1
1
  import genericPropSanitizer from './generic-prop-sanitizer.js'
2
2
 
3
3
  async function sanitizeFeature (item) {
4
- const { get, isPlainObject, mergeWith, isArray } = this.lib._
4
+ const { get, isPlainObject, mergeWith, isArray } = this.app.lib._
5
5
  for (const f of item.feature) {
6
6
  const feature = get(this.feature, f.name) // source from collectFeature
7
7
  if (!feature) this.fatal('unknownFeature%s%s', f.name, item.name)
8
8
  let [ns, path] = f.name.split('.')
9
- if (!path) ns = this.name
9
+ if (!path) ns = this.ns
10
10
  const input = await feature.call(this.app[ns], f)
11
11
  let props = input.properties
12
12
  if (isPlainObject(props)) props = [props]
@@ -37,10 +37,11 @@ async function sanitizeFullText (item) {
37
37
  }
38
38
 
39
39
  async function sanitizeSchema (items) {
40
- const { defaultsDeep } = this.lib.aneka
40
+ const { defaultsDeep } = this.app.lib.aneka
41
41
  const { freeze, fatal, importModule, join, breakNsPath, runHook } = this.app.bajo
42
- const { isEmpty, orderBy, map, keys, findIndex, find, each, isString, get, isPlainObject, camelCase, uniq, filter } = this.lib._
43
- const propTypes = keys(this.propType)
42
+ const { isEmpty, orderBy, map, keys, findIndex, find, each, isString, get, isPlainObject, camelCase, uniq, filter } = this.app.lib._
43
+ const { propType } = this.app.pluginClass.dobo
44
+ const propTypes = keys(propType)
44
45
  const schemas = []
45
46
  this.log.debug('loadingDbSchemas')
46
47
  for (const k in items) {
@@ -57,7 +58,7 @@ async function sanitizeSchema (items) {
57
58
  if (isEmpty(type)) type = conn.type
58
59
  const driver = find(this.drivers, { type, ns, driver: conn.driver })
59
60
  if (driver.lowerCaseModel) item.name = item.name.toLowerCase()
60
- let file = `${ns}:/extend/${this.name}/lib/${type}/prop-sanitizer.js`
61
+ let file = `${ns}:/extend/${this.ns}/lib/${type}/prop-sanitizer.js`
61
62
  let propSanitizer = await importModule(file)
62
63
  if (!propSanitizer) propSanitizer = genericPropSanitizer
63
64
  for (const idx in item.properties) {
@@ -128,7 +129,7 @@ async function sanitizeSchema (items) {
128
129
  if (!all.includes(p.name)) all.push(p.name)
129
130
  else fatal.call(this, 'Field \'%s@%s\' should be used only once', p.name, item.name)
130
131
  })
131
- file = `${ns}:/extend/${this.name}/lib/${type}/schema-sanitizer.js`
132
+ file = `${ns}:/extend/${this.ns}/lib/${type}/schema-sanitizer.js`
132
133
  const schemaSanitizer = await importModule(file)
133
134
  if (schemaSanitizer) await schemaSanitizer.call(this, { schema: item, connection: conn, driver })
134
135
  schemas.push(item)
@@ -1,6 +1,6 @@
1
1
  async function singleRelRows ({ schema, record, options = {} }) {
2
- const { isSet } = this.lib.aneka
3
- const { find } = this.lib._
2
+ const { isSet } = this.app.lib.aneka
3
+ const { find } = this.app.lib._
4
4
  const props = schema.properties.filter(p => isSet(p.rel) && !(options.hidden ?? []).includes(p.name))
5
5
  const rels = {}
6
6
  options.rels = options.rels ?? []
@@ -1,12 +1,12 @@
1
1
  import path from 'path'
2
2
 
3
3
  async function copyUploaded (name, id, options = {}) {
4
- const { fs } = this.lib
4
+ const { fs } = this.app.lib
5
5
  const { req, setField, setFile, mimeType, stats, silent = true } = options
6
6
  name = this.attachmentPreCheck(name)
7
7
  if (!name) {
8
8
  if (silent) return
9
- throw this.error('isMissing%s', this.print.write('field.name'))
9
+ throw this.error('isMissing%s', this.t('field.name'))
10
10
  }
11
11
  if (!this.app.waibu) {
12
12
  if (silent) return
@@ -1,13 +1,13 @@
1
1
  import mergeAttachmentInfo from '../../lib/merge-attachment-info.js'
2
2
 
3
3
  async function create (name, id, options = {}) {
4
- const { fs } = this.lib
5
- const { isEmpty } = this.lib._
4
+ const { fs } = this.app.lib
5
+ const { isEmpty } = this.app.lib._
6
6
  name = this.attachmentPreCheck(name)
7
7
  if (!name) return
8
8
  const { source, field = 'file', file } = options
9
9
  if (isEmpty(file)) return
10
- if (!source) throw this.error('isMissing%s', this.print.write('field.source'))
10
+ if (!source) throw this.error('isMissing%s', this.t('field.source'))
11
11
  const baseDir = await this.attachmentGetPath(name, id, field, file, { dirOnly: true })
12
12
  const { fullPath, stats, mimeType, req } = options
13
13
 
@@ -1,11 +1,11 @@
1
1
  import mergeAttachmentInfo from '../../lib/merge-attachment-info.js'
2
2
 
3
3
  async function find (name, id, options = {}) {
4
- const { fastGlob, fs } = this.lib
4
+ const { fastGlob, fs } = this.app.lib
5
5
  const { getPluginDataDir } = this.app.bajo
6
6
  name = this.attachmentPreCheck(name)
7
7
  if (!name) return
8
- const dir = `${getPluginDataDir(this.name)}/attachment/${name}/${id}`
8
+ const dir = `${getPluginDataDir(this.ns)}/attachment/${name}/${id}`
9
9
  if (!fs.existsSync(dir)) return []
10
10
  const files = await fastGlob(`${dir}/**/*`)
11
11
  const { fullPath, stats, mimeType } = options
@@ -1,8 +1,8 @@
1
1
  async function getPath (name, id, field, file, options = {}) {
2
2
  const { getPluginDataDir } = this.app.bajo
3
- const { pascalCase } = this.lib.aneka
4
- const { fs } = this.lib
5
- const dir = `${getPluginDataDir(this.name)}/attachment/${pascalCase(name)}/${id}`
3
+ const { pascalCase } = this.app.lib.aneka
4
+ const { fs } = this.app.lib
5
+ const dir = `${getPluginDataDir(this.ns)}/attachment/${pascalCase(name)}/${id}`
6
6
  if (options.dirOnly) return dir
7
7
  const path = field ? `${dir}/${field}/${file}` : `${dir}/${file}`
8
8
  if (!fs.existsSync(path)) throw this.error('notFound')
@@ -1,7 +1,7 @@
1
1
  async function get (name, id, field, file, options = {}) {
2
2
  name = this.attachmentPreCheck(name)
3
3
  if (!name) return
4
- const { find } = this.lib._
4
+ const { find } = this.app.lib._
5
5
  const all = await this.attachmentFind(name, id, options)
6
6
  if (field === 'null') field = null
7
7
  const data = find(all, { field, file })
@@ -1,5 +1,5 @@
1
1
  function preCheck (name) {
2
- const { pascalCase } = this.lib.aneka
2
+ const { pascalCase } = this.app.lib.aneka
3
3
  name = pascalCase(name)
4
4
  const schema = this.getSchema(name)
5
5
  if (!schema.attachment) return false
@@ -1,5 +1,5 @@
1
1
  async function remove (name, id, field, file, options = {}) {
2
- const { fs } = this.lib
2
+ const { fs } = this.app.lib
3
3
  name = this.attachmentPreCheck(name)
4
4
  if (!name) return
5
5
  const path = await this.attachmentGetPath(name, id, field, file)
@@ -3,10 +3,10 @@ import execValidation from '../../lib/exec-validation.js'
3
3
  import execFeatureHook from '../../lib/exec-feature-hook.js'
4
4
 
5
5
  async function create (name, inputs, options) {
6
- const { isSet } = this.lib.aneka
6
+ const { isSet } = this.app.lib.aneka
7
7
  const { generateId, runHook } = this.app.bajo
8
8
  const { clearModel } = this.cache ?? {}
9
- const { find } = this.lib._
9
+ const { find } = this.app.lib._
10
10
  options.dataOnly = options.dataOnly ?? true
11
11
  options.truncateString = options.truncateString ?? true
12
12
  const { noHook, noValidation } = options
@@ -20,8 +20,8 @@ async function create (name, inputs, options) {
20
20
  if (!noValidation) b = await execValidation.call(this, { noHook, name, b, options })
21
21
  }
22
22
  if (!noHook) {
23
- await runHook(`${this.name}:beforeBulkCreate`, name, bodies, options)
24
- await runHook(`${this.name}.${name}:beforeBulkCreate`, bodies, options)
23
+ await runHook(`${this.ns}:beforeBulkCreate`, name, bodies, options)
24
+ await runHook(`${this.ns}.${name}:beforeBulkCreate`, bodies, options)
25
25
  }
26
26
  for (const idx in bodies) {
27
27
  await execFeatureHook.call(this, 'beforeCreate', { schema, body: bodies[idx] })
@@ -37,8 +37,8 @@ async function create (name, inputs, options) {
37
37
  await execFeatureHook.call(this, 'afterCreate', { schema, body: bodies[idx] })
38
38
  }
39
39
  if (!noHook) {
40
- await runHook(`${this.name}.${name}:afterBulkCreate`, bodies, options)
41
- await runHook(`${this.name}:afterBulkCreate`, name, bodies, options)
40
+ await runHook(`${this.ns}.${name}:afterBulkCreate`, bodies, options)
41
+ await runHook(`${this.ns}:afterBulkCreate`, name, bodies, options)
42
42
  }
43
43
  if (clearModel) await clearModel({ model: name })
44
44
  }
@@ -2,19 +2,19 @@ import resolveMethod from '../../lib/resolve-method.js'
2
2
 
3
3
  async function clear (name, options = {}) {
4
4
  const { runHook } = this.app.bajo
5
- const { camelCase } = this.lib._
5
+ const { camelCase } = this.app.lib._
6
6
 
7
7
  await this.modelExists(name, true)
8
8
  const { noHook } = options
9
9
  const { handler, schema, driver } = await resolveMethod.call(this, name, 'model-clear', options)
10
10
  if (!noHook) {
11
- await runHook(`${this.name}:beforeModelClear`, schema, options)
12
- await runHook(`${this.name}.${camelCase(name)}:beforeModelClear`, options)
11
+ await runHook(`${this.ns}:beforeModelClear`, schema, options)
12
+ await runHook(`${this.ns}.${camelCase(name)}:beforeModelClear`, options)
13
13
  }
14
14
  const resp = await handler.call(this.app[driver.ns], { schema, options })
15
15
  if (!noHook) {
16
- await runHook(`${this.name}.${camelCase(name)}:afterModelClear`, options, resp)
17
- await runHook(`${this.name}:afterModelClear`, schema, options, resp)
16
+ await runHook(`${this.ns}.${camelCase(name)}:afterModelClear`, options, resp)
17
+ await runHook(`${this.ns}:afterModelClear`, schema, options, resp)
18
18
  }
19
19
  return resp
20
20
  }
@@ -1,18 +1,31 @@
1
1
  import resolveMethod from '../../lib/resolve-method.js'
2
2
 
3
+ /**
4
+ * Create a new model:
5
+ * - read corresponding schema
6
+ * - attempt to create table/database/collection accordingly
7
+ *
8
+ * @method
9
+ * @memberof Dobo
10
+ * @async
11
+ * @instance
12
+ * @name modelCreate
13
+ * @param {string} name - Model's name
14
+ * @param {Object} [options={}] - Options object
15
+ */
3
16
  async function create (name, options = {}) {
4
17
  const { runHook } = this.app.bajo
5
- const { camelCase } = this.lib._
18
+ const { camelCase } = this.app.lib._
6
19
 
7
20
  const { handler, schema, driver } = await resolveMethod.call(this, name, 'model-create', options)
8
21
  if (!options.noHook) {
9
- await runHook(`${this.name}:beforeModelCreate`, schema, options)
10
- await runHook(`${this.name}.${camelCase(name)}:beforeModelCreate`, options)
22
+ await runHook(`${this.ns}:beforeModelCreate`, schema, options)
23
+ await runHook(`${this.ns}.${camelCase(name)}:beforeModelCreate`, options)
11
24
  }
12
25
  await handler.call(this.app[driver.ns], { schema, options })
13
26
  if (!options.noHook) {
14
- await runHook(`${this.name}.${camelCase(name)}:afterModelCreate`, options)
15
- await runHook(`${this.name}:afterModelCreate`, schema, options)
27
+ await runHook(`${this.ns}.${camelCase(name)}:afterModelCreate`, options)
28
+ await runHook(`${this.ns}:afterModelCreate`, schema, options)
16
29
  }
17
30
  }
18
31
 
@@ -1,18 +1,30 @@
1
1
  import resolveMethod from '../../lib/resolve-method.js'
2
2
 
3
+ /**
4
+ * Drop database model
5
+ *
6
+ * @method
7
+ * @memberof Dobo
8
+ * @async
9
+ * @instance
10
+ * @name modelDrop
11
+ * @param {string} name - Model's name
12
+ * @param {Object} [options={}] - Options object
13
+ */
14
+
3
15
  async function drop (name, options = {}) {
4
16
  const { runHook } = this.app.bajo
5
- const { camelCase } = this.lib._
17
+ const { camelCase } = this.app.lib._
6
18
  const { handler, schema, driver } = await resolveMethod.call(this, name, 'model-drop', options)
7
19
 
8
20
  if (!options.noHook) {
9
- await runHook(`${this.name}:beforeModelDrop`, schema, options)
10
- await runHook(`${this.name}.${camelCase(name)}:beforeModelDrop`, options)
21
+ await runHook(`${this.ns}:beforeModelDrop`, schema, options)
22
+ await runHook(`${this.ns}.${camelCase(name)}:beforeModelDrop`, options)
11
23
  }
12
24
  await handler.call(this.app[driver.ns], { schema, options })
13
25
  if (!options.noHook) {
14
- await runHook(`${this.name}.${camelCase(name)}:afterModelDrop`, options)
15
- await runHook(`${this.name}:afterModelDrop`, schema, options)
26
+ await runHook(`${this.ns}.${camelCase(name)}:afterModelDrop`, options)
27
+ await runHook(`${this.ns}:afterModelDrop`, schema, options)
16
28
  }
17
29
  }
18
30
 
@@ -2,19 +2,32 @@ import resolveMethod from '../../lib/resolve-method.js'
2
2
 
3
3
  const cache = {}
4
4
 
5
+ /**
6
+ * Check if model exists already
7
+ *
8
+ * @method
9
+ * @memberof Dobo
10
+ * @async
11
+ * @instance
12
+ * @name modelExists
13
+ * @param {string} name - Model's name
14
+ * @param {boolean} [thrown=false] - If ```true``` throw error if not exists instead of just silent
15
+ * @param {Object} [options={}] - Options object
16
+ * @returns {boolean}
17
+ */
5
18
  async function exists (name, thrown, options = {}) {
6
19
  if (cache[name]) return cache[name]
7
20
  const { runHook } = this.app.bajo
8
- const { camelCase } = this.lib._
21
+ const { camelCase } = this.app.lib._
9
22
  const { handler, schema, driver } = await resolveMethod.call(this, name, 'model-exists', options)
10
23
  if (!options.noHook) {
11
- await runHook(`${this.name}:beforeModelExists`, schema, options)
12
- await runHook(`${this.name}.${camelCase(name)}:beforeModelExists`, options)
24
+ await runHook(`${this.ns}:beforeModelExists`, schema, options)
25
+ await runHook(`${this.ns}.${camelCase(name)}:beforeModelExists`, options)
13
26
  }
14
27
  const exist = await handler.call(this.app[driver.ns], { schema, options })
15
28
  if (!options.noHook) {
16
- await runHook(`${this.name}.${camelCase(name)}:afterModelExists`, exist, options)
17
- await runHook(`${this.name}:afterModelExists`, schema, exist, options)
29
+ await runHook(`${this.ns}.${camelCase(name)}:afterModelExists`, exist, options)
30
+ await runHook(`${this.ns}:afterModelExists`, schema, exist, options)
18
31
  }
19
32
  if (!exist && thrown) throw this.error('modelNotExists%s', name)
20
33
  cache[name] = exist
@@ -3,20 +3,20 @@ import resolveMethod from '../../lib/resolve-method.js'
3
3
  async function clear (name, opts = {}) {
4
4
  const { runHook } = this.app.bajo
5
5
  await this.modelExists(name, true)
6
- const { cloneDeep, camelCase, omit } = this.lib._
6
+ const { cloneDeep, camelCase, omit } = this.app.lib._
7
7
  const options = cloneDeep(omit(opts, ['req', 'reply']))
8
8
  options.req = opts.req
9
9
  options.reply = opts.reply
10
10
  const { noHook } = options
11
11
  const { handler, schema, driver } = await resolveMethod.call(this, name, 'record-clear', options)
12
12
  if (!noHook) {
13
- await runHook(`${this.name}:beforeRecordClear`, name, options)
14
- await runHook(`${this.name}.${camelCase(name)}:beforeRecordClear`, options)
13
+ await runHook(`${this.ns}:beforeRecordClear`, name, options)
14
+ await runHook(`${this.ns}.${camelCase(name)}:beforeRecordClear`, options)
15
15
  }
16
16
  const resp = await handler.call(this.app[driver.ns], { schema, options })
17
17
  if (!noHook) {
18
- await runHook(`${this.name}.${camelCase(name)}:afterRecordClear`, options, resp)
19
- await runHook(`${this.name}:afterRecordClear`, name, options, resp)
18
+ await runHook(`${this.ns}.${camelCase(name)}:afterRecordClear`, options, resp)
19
+ await runHook(`${this.ns}:afterRecordClear`, name, options, resp)
20
20
  }
21
21
  return resp
22
22
  }
@@ -1,10 +1,32 @@
1
1
  import resolveMethod from '../../lib/resolve-method.js'
2
2
  import execFeatureHook from '../../lib/exec-feature-hook.js'
3
3
 
4
+ /**
5
+ * @typedef {Object} TRecordCountOptions
6
+ * @see Dobo#recordCount
7
+ * @property {boolean} [dataOnly=true] - If ```true``` (default) returns array of records. Otherwise {@link TFindRecordResult}
8
+ * @property {boolean} [noCache=true] - If ```true``` (default), result set won't be cached. This will overwrite model's ```cacheable``` property. Only applicable if {@link https://github.com/ardhi/bajo-cache|bajo-cache} is loaded
9
+ * @property {boolean} [noHook=false] - If ```true```, no model's hook will be executed
10
+ * @property {boolean} [noFeatureHook=false] - If ```true```, no model's feature hook will be executed
11
+ */
12
+
13
+ /**
14
+ * Return the number of records found by given filter
15
+ *
16
+ * @method
17
+ * @memberof Dobo
18
+ * @async
19
+ * @instance
20
+ * @name recordCount
21
+ * @param {string} name - Model's name
22
+ * @param {TRecordFilter} [filter={}] - Filter object
23
+ * @param {TRecordCountOptions} [options={}]
24
+ * @returns {(TRecordCountResult|number)} Return ```number``` of records if ```options.dataOnly``` is set. {@link TRecordCountResult} otherwise
25
+ */
4
26
  async function count (name, filter = {}, opts = {}) {
5
27
  const { runHook } = this.app.bajo
6
28
  const { get, set } = this.cache ?? {}
7
- const { cloneDeep, camelCase, omit } = this.lib._
29
+ const { cloneDeep, camelCase, omit } = this.app.lib._
8
30
  delete opts.record
9
31
  const options = cloneDeep(omit(opts, ['req', 'reply']))
10
32
  options.req = opts.req
@@ -19,8 +41,8 @@ async function count (name, filter = {}, opts = {}) {
19
41
  if (options.queryHandler) filter.query = await options.queryHandler.call(opts.req ? this.app[opts.req.ns] : this, filter.query, opts.req)
20
42
  filter.match = this.buildMatch({ input: filter.match, schema, options }) ?? {}
21
43
  if (!noHook) {
22
- await runHook(`${this.name}:beforeRecordCount`, name, filter, options)
23
- await runHook(`${this.name}.${camelCase(name)}:beforeRecordCount`, filter, options)
44
+ await runHook(`${this.ns}:beforeRecordCount`, name, filter, options)
45
+ await runHook(`${this.ns}.${camelCase(name)}:beforeRecordCount`, filter, options)
24
46
  }
25
47
  if (!noFeatureHook) await execFeatureHook.call(this, 'beforeCount', { schema, filter, options })
26
48
  if (get && !noCache && !options.record) {
@@ -33,8 +55,8 @@ async function count (name, filter = {}, opts = {}) {
33
55
  const record = options.record ?? (await handler.call(this.app[driver.ns], { schema, filter, options }))
34
56
  delete options.record
35
57
  if (!noHook) {
36
- await runHook(`${this.name}.${camelCase(name)}:afterRecordCount`, filter, options, record)
37
- await runHook(`${this.name}:afterRecordCount`, name, filter, options, record)
58
+ await runHook(`${this.ns}.${camelCase(name)}:afterRecordCount`, filter, options, record)
59
+ await runHook(`${this.ns}:afterRecordCount`, name, filter, options, record)
38
60
  }
39
61
  if (set && !noCache) await set({ model: name, filter, options, record })
40
62
  if (!noFeatureHook) await execFeatureHook.call(this, 'afterCount', { schema, filter, options, record })