@ditojs/server 0.272.0 → 0.275.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/lib/app/Application.js +48 -8
  2. package/lib/app/SessionStore.js +3 -1
  3. package/lib/app/Validator.js +13 -1
  4. package/lib/app/index.js +7 -1
  5. package/lib/cli/console.js +14 -4
  6. package/lib/cli/db/createMigration.js +16 -4
  7. package/lib/cli/db/index.js +7 -1
  8. package/lib/cli/db/migrate.js +3 -3
  9. package/lib/cli/db/reset.js +3 -3
  10. package/lib/cli/db/rollback.js +3 -3
  11. package/lib/cli/db/seed.js +14 -6
  12. package/lib/cli/db/unlock.js +3 -3
  13. package/lib/cli/index.js +9 -5
  14. package/lib/controllers/AdminController.js +12 -2
  15. package/lib/controllers/CollectionController.js +15 -1
  16. package/lib/controllers/Controller.js +28 -4
  17. package/lib/controllers/ControllerAction.js +23 -14
  18. package/lib/controllers/RelationController.js +9 -3
  19. package/lib/controllers/UserController.js +15 -1
  20. package/lib/controllers/index.js +7 -1
  21. package/lib/decorators/index.js +7 -1
  22. package/lib/decorators/parameters.js +3 -1
  23. package/lib/decorators/returns.js +3 -1
  24. package/lib/errors/DatabaseError.js +5 -19
  25. package/lib/errors/ResponseError.js +4 -16
  26. package/lib/errors/index.js +7 -1
  27. package/lib/graph/DitoGraphProcessor.js +5 -1
  28. package/lib/graph/expression.js +5 -1
  29. package/lib/graph/graph.js +18 -2
  30. package/lib/graph/index.js +7 -1
  31. package/lib/index.js +7 -1
  32. package/lib/lib/index.js +7 -1
  33. package/lib/middleware/findRoute.js +7 -1
  34. package/lib/middleware/index.js +7 -1
  35. package/lib/middleware/logRequests.js +4 -4
  36. package/lib/mixins/AssetMixin.js +4 -4
  37. package/lib/mixins/SessionMixin.js +4 -4
  38. package/lib/mixins/TimeStampedMixin.js +4 -4
  39. package/lib/mixins/UserMixin.js +10 -4
  40. package/lib/mixins/index.js +7 -1
  41. package/lib/models/Model.js +21 -4
  42. package/lib/models/definitions/filters.js +9 -1
  43. package/lib/models/definitions/properties.js +7 -1
  44. package/lib/models/definitions/scopes.js +9 -1
  45. package/lib/models/index.js +7 -1
  46. package/lib/query/QueryBuilder.js +11 -1
  47. package/lib/query/QueryFilters.js +11 -1
  48. package/lib/query/index.js +7 -1
  49. package/lib/schema/formats/index.js +7 -1
  50. package/lib/schema/index.js +10 -2
  51. package/lib/schema/keywords/index.js +7 -1
  52. package/lib/schema/properties.js +11 -1
  53. package/lib/schema/relations.js +18 -4
  54. package/lib/services/index.js +7 -1
  55. package/lib/storage/DiskStorage.js +7 -1
  56. package/lib/storage/Storage.js +5 -1
  57. package/lib/storage/index.js +7 -1
  58. package/lib/utils/emitter.js +5 -1
  59. package/lib/utils/index.js +7 -1
  60. package/lib/utils/object.js +9 -3
  61. package/package.json +31 -31
  62. package/src/app/Application.js +39 -9
  63. package/src/cli/console.js +3 -3
  64. package/src/cli/db/createMigration.js +3 -3
  65. package/src/cli/db/migrate.js +4 -4
  66. package/src/cli/db/reset.js +4 -4
  67. package/src/cli/db/rollback.js +4 -4
  68. package/src/cli/db/seed.js +6 -6
  69. package/src/cli/db/unlock.js +2 -2
  70. package/src/cli/index.js +3 -3
  71. package/src/controllers/AdminController.js +1 -1
  72. package/src/controllers/Controller.js +8 -8
  73. package/src/controllers/ControllerAction.js +7 -13
  74. package/src/controllers/RelationController.js +2 -2
  75. package/src/errors/DatabaseError.js +2 -23
  76. package/src/errors/ResponseError.js +1 -9
  77. package/src/middleware/logRequests.js +9 -9
  78. package/src/models/Model.js +10 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ditojs/server",
3
- "version": "0.272.0",
3
+ "version": "0.275.0",
4
4
  "description": "Dito.js Server – Dito.js is a declarative and modern web framework, based on Objection.js, Koa.js and Vue.js",
5
5
  "main": "lib/index.js",
6
6
  "repository": "https://github.com/ditojs/dito/tree/master/packages/server",
@@ -25,34 +25,33 @@
25
25
  "yarn": ">= 1.0.0"
26
26
  },
27
27
  "browserslist": [
28
- "node 14"
28
+ "node >= 14"
29
29
  ],
30
30
  "dependencies": {
31
- "@babel/core": "^7.15.5",
32
- "@babel/runtime": "^7.15.4",
33
- "@ditojs/admin": "^0.272.0",
34
- "@ditojs/router": "^0.272.0",
35
- "@ditojs/utils": "^0.272.0",
31
+ "@babel/core": "^7.17.5",
32
+ "@babel/runtime": "^7.17.2",
33
+ "@ditojs/admin": "^0.275.0",
34
+ "@ditojs/router": "^0.275.0",
35
+ "@ditojs/utils": "^0.275.0",
36
36
  "@koa/cors": "^3.1.0",
37
37
  "@koa/multer": "^3.0.0",
38
- "@vue/cli-service": "^4.5.13",
38
+ "@vue/cli-service": "^4.5.15",
39
39
  "ajv": "^7.2.4",
40
40
  "ajv-formats": "^1.6.1",
41
- "aws-sdk": "^2.999.0",
42
- "axios": "^0.22.0",
43
- "babel-loader": "^8.2.2",
41
+ "aws-sdk": "^2.1087.0",
42
+ "axios": "^0.26.0",
43
+ "babel-loader": "^8.2.3",
44
44
  "bcryptjs": "^2.4.3",
45
- "bytes": "^3.1.0",
46
- "chalk": "^4.1.2",
47
- "core-js": "^3.18.1",
45
+ "bytes": "^3.1.2",
46
+ "core-js": "^3.21.1",
48
47
  "data-uri-to-buffer": "^3.0.1",
49
- "eventemitter2": "^6.4.4",
48
+ "eventemitter2": "^6.4.5",
50
49
  "file-type": "^16.5.3",
51
- "fs-extra": "^10.0.0",
50
+ "fs-extra": "^10.0.1",
52
51
  "html-webpack-tags-plugin": "^2.0.17",
53
- "image-size": "^1.0.0",
54
- "is-svg": "^4.3.1",
55
- "koa": "^2.13.3",
52
+ "image-size": "^1.0.1",
53
+ "is-svg": "^4.3.2",
54
+ "koa": "^2.13.4",
56
55
  "koa-bodyparser": "^4.3.0",
57
56
  "koa-compose": "^4.1.0",
58
57
  "koa-compress": "^5.1.0",
@@ -66,14 +65,15 @@
66
65
  "koa-session": "^6.2.0",
67
66
  "koa-static": "^5.0.0",
68
67
  "koa-webpack": "^6.0.0",
69
- "mime-types": "^2.1.33",
70
- "multer": "^1.4.3",
71
- "multer-s3": "^2.9.0",
72
- "nanoid": "^3.1.28",
73
- "parse-duration": "^1.0.0",
68
+ "mime-types": "^2.1.34",
69
+ "multer": "^1.4.4",
70
+ "multer-s3": "^2.10.0",
71
+ "nanoid": "^3.3.1",
72
+ "parse-duration": "^1.0.2",
74
73
  "passport-local": "^1.0.0",
75
74
  "passthrough-counter": "^1.0.0",
76
- "pino": "^6.13.3",
75
+ "picocolors": "^1.0.0",
76
+ "pino": "^6.14.0",
77
77
  "pino-pretty": "^6.0.0",
78
78
  "pluralize": "^8.0.0",
79
79
  "repl": "^0.1.3",
@@ -89,16 +89,16 @@
89
89
  "objection": "^2.2.0"
90
90
  },
91
91
  "devDependencies": {
92
- "@babel/cli": "^7.15.7",
93
- "@babel/preset-env": "^7.15.6",
94
- "@ditojs/babel-preset": "^0.272.0",
92
+ "@babel/cli": "^7.17.6",
93
+ "@babel/preset-env": "^7.16.11",
94
+ "@ditojs/babel-preset": "^0.275.0",
95
95
  "babel-plugin-dynamic-import-node": "^2.3.3",
96
96
  "babel-plugin-module-resolver": "^4.1.0",
97
97
  "knex": "^0.21.21",
98
- "objection": "^2.2.16",
99
- "pg": "^8.7.1",
98
+ "objection": "^2.2.18",
99
+ "pg": "^8.7.3",
100
100
  "rimraf": "^3.0.2",
101
101
  "sqlite3": "^5.0.2"
102
102
  },
103
- "gitHead": "ea5edc5a9bf80fa07dd5db855343d52329ab54f5"
103
+ "gitHead": "b1ee07dad73b0e3408cd90798deb7608c8ef096d"
104
104
  }
@@ -2,7 +2,7 @@ import Koa from 'koa'
2
2
  import Knex from 'knex'
3
3
  import util from 'util'
4
4
  import axios from 'axios'
5
- import chalk from 'chalk'
5
+ import pico from 'picocolors'
6
6
  import zlib from 'zlib'
7
7
  import pino from 'pino'
8
8
  import os from 'os'
@@ -24,7 +24,12 @@ import { Service } from '@/services'
24
24
  import { Storage } from '@/storage'
25
25
  import { convertSchema } from '@/schema'
26
26
  import { formatJson } from '@/utils'
27
- import { ResponseError, ValidationError, AssetError } from '@/errors'
27
+ import {
28
+ ResponseError,
29
+ ValidationError,
30
+ DatabaseError,
31
+ AssetError
32
+ } from '@/errors'
28
33
  import SessionStore from './SessionStore'
29
34
  import { Validator } from './Validator'
30
35
  import {
@@ -158,7 +163,7 @@ export class Application extends Koa {
158
163
  }
159
164
  if (Object.keys(data).length > 0) {
160
165
  console.info(
161
- chalk.yellow.bold(`\n${modelClass.name}:\n`),
166
+ pico.yellow.bold(`\n${modelClass.name}:\n`),
162
167
  util.inspect(data, {
163
168
  colors: true,
164
169
  depth: null,
@@ -473,11 +478,26 @@ export class Application extends Koa {
473
478
  }
474
479
  }
475
480
 
476
- createValidationError({ type, message, errors, options }) {
481
+ createValidationError({ type, message, errors, options, json }) {
477
482
  return new ValidationError({
478
483
  type,
479
484
  message,
480
- errors: this.validator.parseErrors(errors, options)
485
+ errors: this.validator.parseErrors(errors, options),
486
+ // Only include the JSON data in the error if `log.errors.json`is set.
487
+ json: this.config.log.errors?.json ? json : undefined
488
+ })
489
+ }
490
+
491
+ createDatabaseError(error) {
492
+ // Remove knex SQL query and move to separate `sql` property.
493
+ // TODO: Fix this properly in Knex / Objection instead, see:
494
+ // https://gitter.im/Vincit/objection.js?at=5a68728f5a9ebe4f75ca40b0
495
+ const [, sql, message] = error.message.match(/^([\s\S]*) - ([\s\S]*?)$/) ||
496
+ [null, null, error.message]
497
+ return new DatabaseError(error, {
498
+ message,
499
+ // Only include the SQL query in the error if `log.errors.sql`is set.
500
+ sql: this.config.log.errors?.sql ? sql : undefined
481
501
  })
482
502
  }
483
503
 
@@ -608,6 +628,16 @@ export class Application extends Koa {
608
628
  }
609
629
  }
610
630
  this.knex = Knex(knex)
631
+ // Support PostgreSQL type parser mappings in the config.
632
+ if (
633
+ knex.client === 'postgresql' &&
634
+ knex.typeParsers &&
635
+ this.knex.client.driver
636
+ ) {
637
+ for (const [type, parser] of Object.entries(knex.typeParsers)) {
638
+ this.knex.client.driver.types.setTypeParser(type, parser)
639
+ }
640
+ }
611
641
  if (log.sql) {
612
642
  this.setupKnexLogging()
613
643
  }
@@ -826,13 +856,13 @@ export class Application extends Koa {
826
856
  if (!data) {
827
857
  console.info(
828
858
  `${
829
- chalk.red('INFO:')
859
+ pico.red('INFO:')
830
860
  } Asset ${
831
- chalk.green(`'${file.name}'`)
861
+ pico.green(`'${file.name}'`)
832
862
  } is from a foreign source, fetching from ${
833
- chalk.green(`'${file.url}'`)
863
+ pico.green(`'${file.url}'`)
834
864
  } and adding to storage ${
835
- chalk.green(`'${storage.name}'`)
865
+ pico.green(`'${storage.name}'`)
836
866
  }...`
837
867
  )
838
868
  const response = await axios.request({
@@ -1,7 +1,7 @@
1
1
  import repl from 'repl'
2
2
  import path from 'path'
3
3
  import fs from 'fs-extra'
4
- import chalk from 'chalk'
4
+ import pico from 'picocolors'
5
5
  import objection from 'objection'
6
6
  import { isFunction, deindent } from '@ditojs/utils'
7
7
 
@@ -100,11 +100,11 @@ function displayUsage(app, config, details) {
100
100
  Dito Console
101
101
 
102
102
  Available references:
103
- - Dito app: ${chalk.cyan('app')}
103
+ - Dito app: ${pico.cyan('app')}
104
104
  ${
105
105
  modelHandleNames.length > 0
106
106
  ? ` - Dito models: ${
107
- modelHandleNames.map(m => chalk.cyan(m)).join(', ')
107
+ modelHandleNames.map(m => pico.cyan(m)).join(', ')
108
108
  }`
109
109
  : ''
110
110
  }
@@ -1,6 +1,6 @@
1
1
  import path from 'path'
2
2
  import fs from 'fs-extra'
3
- import chalk from 'chalk'
3
+ import pico from 'picocolors'
4
4
  import { getRelationClass, isThroughRelationClass } from '@/schema'
5
5
  import {
6
6
  isObject, isArray, isString, deindent, capitalize
@@ -52,7 +52,7 @@ export async function createMigration(app, name, ...modelNames) {
52
52
  const file = path.join(migrationDir, filename)
53
53
  if (await fs.exists(file)) {
54
54
  // This should never happen, but let's be on the safe side here:
55
- console.info(chalk.red(`Migration '${filename}' already exists.`))
55
+ console.info(pico.red(`Migration '${filename}' already exists.`))
56
56
  return false
57
57
  } else {
58
58
  await fs.writeFile(file, deindent`
@@ -64,7 +64,7 @@ export async function createMigration(app, name, ...modelNames) {
64
64
  ${getCode(dropTables)}
65
65
  }
66
66
  `)
67
- console.info(chalk.cyan(`Migration '${filename}' successfully created.`))
67
+ console.info(pico.cyan(`Migration '${filename}' successfully created.`))
68
68
  return true
69
69
  }
70
70
  }
@@ -1,10 +1,10 @@
1
- import chalk from 'chalk'
1
+ import pico from 'picocolors'
2
2
 
3
3
  export async function migrate(knex) {
4
4
  const [batch, log] = await knex.migrate.latest()
5
5
  console.info(log.length === 0
6
- ? chalk.cyan('Already up to date')
7
- : chalk.green(`Batch ${batch} run: ${log.length} migrations\n`) +
8
- chalk.cyan(log.join('\n')))
6
+ ? pico.cyan('Already up to date')
7
+ : pico.green(`Batch ${batch} run: ${log.length} migrations\n`) +
8
+ pico.cyan(log.join('\n')))
9
9
  return true
10
10
  }
@@ -1,4 +1,4 @@
1
- import chalk from 'chalk'
1
+ import pico from 'picocolors'
2
2
  import { migrate } from './migrate'
3
3
 
4
4
  export async function reset(knex) {
@@ -11,10 +11,10 @@ export async function reset(knex) {
11
11
  migrations.push(...log)
12
12
  }
13
13
  console.info(migrations.length === 0
14
- ? chalk.cyan('Already at the base migration')
15
- : chalk.green(`${batches.length > 1 ? 'Batches' : 'Batch'} ${batches} ` +
14
+ ? pico.cyan('Already at the base migration')
15
+ : pico.green(`${batches.length > 1 ? 'Batches' : 'Batch'} ${batches} ` +
16
16
  `rolled back: ${migrations.length} migrations\n`) +
17
- chalk.cyan(migrations.join('\n')))
17
+ pico.cyan(migrations.join('\n')))
18
18
  await migrate(knex)
19
19
  return true
20
20
  }
@@ -1,10 +1,10 @@
1
- import chalk from 'chalk'
1
+ import pico from 'picocolors'
2
2
 
3
3
  export async function rollback(knex) {
4
4
  const [batch, log] = await knex.migrate.rollback()
5
5
  console.info(log.length === 0
6
- ? chalk.cyan('Already at the base migration')
7
- : chalk.green(`Batch ${batch} rolled back: ${log.length} migrations\n`) +
8
- chalk.cyan(log.join('\n')))
6
+ ? pico.cyan('Already at the base migration')
7
+ : pico.green(`Batch ${batch} rolled back: ${log.length} migrations\n`) +
8
+ pico.cyan(log.join('\n')))
9
9
  return true
10
10
  }
@@ -1,6 +1,6 @@
1
1
  import path from 'path'
2
2
  import fs from 'fs-extra'
3
- import chalk from 'chalk'
3
+ import pico from 'picocolors'
4
4
  import util from 'util'
5
5
  import pluralize from 'pluralize'
6
6
  import { isFunction, isArray, camelize } from '@ditojs/utils'
@@ -57,18 +57,18 @@ async function handleSeed(app, base, seed, modelClass) {
57
57
  }
58
58
  if (isArray(res)) {
59
59
  console.info(
60
- chalk.green(`${base}:`),
61
- chalk.cyan(`${res.length} seed records created.`)
60
+ pico.green(`${base}:`),
61
+ pico.cyan(`${res.length} seed records created.`)
62
62
  )
63
63
  } else {
64
64
  console.info(
65
- chalk.red(`${base}:`),
66
- chalk.cyan('No seed records created.')
65
+ pico.red(`${base}:`),
66
+ pico.cyan('No seed records created.')
67
67
  )
68
68
  }
69
69
  } catch (err) {
70
70
  console.error(
71
- chalk.red(`${base}:`),
71
+ pico.red(`${base}:`),
72
72
  util.inspect(err, {
73
73
  colors: true,
74
74
  depth: null,
@@ -1,9 +1,9 @@
1
- import chalk from 'chalk'
1
+ import pico from 'picocolors'
2
2
 
3
3
  export async function unlock(knex) {
4
4
  await knex.migrate.forceFreeMigrationsLock()
5
5
  console.info(
6
- chalk.green(`Successfully unlocked the migrations lock table`)
6
+ pico.green(`Successfully unlocked the migrations lock table`)
7
7
  )
8
8
  return true
9
9
  }
package/src/cli/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env babel-node
2
2
 
3
3
  import path from 'path'
4
- import chalk from 'chalk'
4
+ import pico from 'picocolors'
5
5
  import Knex from 'knex'
6
6
  import { isPlainObject, isFunction, camelize } from '@ditojs/utils'
7
7
  import * as db from './db'
@@ -47,10 +47,10 @@ async function execute() {
47
47
  } catch (err) {
48
48
  if (err instanceof Error) {
49
49
  console.error(
50
- chalk.red(`${err.detail ? `${err.detail}\n` : ''}${err.stack}`)
50
+ pico.red(`${err.detail ? `${err.detail}\n` : ''}${err.stack}`)
51
51
  )
52
52
  } else {
53
- console.error(chalk.red(err))
53
+ console.error(pico.red(err))
54
54
  }
55
55
  process.exit(1)
56
56
  }
@@ -60,7 +60,7 @@ export class AdminController extends Controller {
60
60
  sendDitoObject(ctx) {
61
61
  // Send back the global dito object as JavaScript code.
62
62
  ctx.type = 'text/javascript'
63
- ctx.body = `window.dito = ${formatJson(this.getDitoObject(), false)}`
63
+ ctx.body = `window.dito = ${formatJson(this.getDitoObject())}`
64
64
  }
65
65
 
66
66
  middleware() {
@@ -1,4 +1,4 @@
1
- import chalk from 'chalk'
1
+ import pico from 'picocolors'
2
2
  import { getOwnProperty, getAllKeys, describeFunction } from '@/utils'
3
3
  import { EventEmitter } from '@/lib'
4
4
  import ControllerAction from './ControllerAction'
@@ -44,11 +44,11 @@ export class Controller {
44
44
  this.url = namespace ? `/${namespace}${url}` : url
45
45
  this.log(
46
46
  `${
47
- namespace ? chalk.green(`/${namespace}/`) : ''
47
+ namespace ? pico.green(`/${namespace}/`) : ''
48
48
  }${
49
- chalk.cyan(path)
49
+ pico.cyan(path)
50
50
  }${
51
- chalk.white(':')
51
+ pico.white(':')
52
52
  }`,
53
53
  this.level
54
54
  )
@@ -92,13 +92,13 @@ export class Controller {
92
92
  setupRoute(verb, url, transacted, authorize, action, handlers) {
93
93
  this.log(
94
94
  `${
95
- chalk.magenta(verb.toUpperCase())
95
+ pico.magenta(verb.toUpperCase())
96
96
  } ${
97
- chalk.green(this.url)
97
+ pico.green(this.url)
98
98
  }${
99
- chalk.cyan(url.slice(this.url.length))
99
+ pico.cyan(url.slice(this.url.length))
100
100
  } ${
101
- chalk.white(this.describeAuthorize(authorize))
101
+ pico.white(this.describeAuthorize(authorize))
102
102
  }`,
103
103
  this.level + 1
104
104
  )
@@ -1,4 +1,3 @@
1
- import { formatJson } from '@/utils'
2
1
  import { isString, isObject, asArray, clone } from '@ditojs/utils'
3
2
 
4
3
  export default class ControllerAction {
@@ -166,8 +165,9 @@ export default class ControllerAction {
166
165
  if (errors.length > 0) {
167
166
  throw this.createValidationError({
168
167
  type: 'ParameterValidation',
169
- message: `The provided data is not valid: ${formatJson(getData())}`,
170
- errors
168
+ message: 'The provided action parameters are not valid',
169
+ errors,
170
+ json: getData()
171
171
  })
172
172
  }
173
173
  }
@@ -188,17 +188,11 @@ export default class ControllerAction {
188
188
  await this.returns.validate(data)
189
189
  return getResult()
190
190
  } catch (error) {
191
- // Include full JSON result in responses during tests and development,
192
- // for easier debugging.
193
- const message =
194
- process.env.NODE_ENV === 'test' ||
195
- process.env.NODE_ENV === 'development'
196
- ? `Invalid result of action: ${formatJson(getResult())}`
197
- : 'Invalid result of action'
198
191
  throw this.createValidationError({
199
192
  type: 'ResultValidation',
200
- message,
201
- errors: error.errors
193
+ message: 'The returned action result is not valid',
194
+ errors: error.errors,
195
+ json: getResult()
202
196
  })
203
197
  }
204
198
  }
@@ -237,7 +231,7 @@ export default class ControllerAction {
237
231
 
238
232
  coerceValue(type, value, modelOptions) {
239
233
  // See if param needs additional coercion:
240
- if (['date', 'datetime', 'timestamp'].includes(type)) {
234
+ if (value && ['date', 'datetime', 'timestamp'].includes(type)) {
241
235
  value = new Date(value)
242
236
  } else {
243
237
  // See if the defined type(s) require coercion to objects:
@@ -1,4 +1,4 @@
1
- import chalk from 'chalk'
1
+ import pico from 'picocolors'
2
2
  import { ControllerError } from '@/errors'
3
3
  import { CollectionController } from './CollectionController'
4
4
  import { setupPropertyInheritance, getScope } from '@/utils'
@@ -32,7 +32,7 @@ export class RelationController extends CollectionController {
32
32
  // Initialize:
33
33
  this.path = this.app.normalizePath(this.name)
34
34
  this.url = `${this.parent.url}/${this.parent.getPath('member', this.path)}`
35
- this.log(`${chalk.blue(this.path)}${chalk.white(':')}`, this.level)
35
+ this.log(`${pico.blue(this.path)}${pico.white(':')}`, this.level)
36
36
  // Copy over all fields in the relation object except the ones that are
37
37
  // going to be inherited in `setup()` (relation, member, allow), for
38
38
  // settings like scope, etc.
@@ -8,7 +8,7 @@ import {
8
8
  } from 'objection'
9
9
 
10
10
  export class DatabaseError extends WrappedError {
11
- constructor(error) {
11
+ constructor(error, overrides) {
12
12
  const status =
13
13
  error instanceof CheckViolationError ? 400
14
14
  : error instanceof NotNullViolationError ? 400
@@ -16,28 +16,7 @@ export class DatabaseError extends WrappedError {
16
16
  : error instanceof DataError ? 400
17
17
  : error instanceof DBError ? 500
18
18
  : 400
19
- // Remove knex SQL query and move to separate `query` property.
20
- // TODO: Fix this properly in Knex / Objection instead, see:
21
- // https://gitter.im/Vincit/objection.js?at=5a68728f5a9ebe4f75ca40b0
22
- const [, sql, message] = error.message.match(/^([\s\S]*) - ([\s\S]*?)$/) ||
23
- [null, null, error.message]
24
- const overrides = {
25
- type: error.constructor.name,
26
- message,
27
- sql,
28
- status
29
- }
19
+ overrides = { type: error.constructor.name, status, ...overrides }
30
20
  super(error, overrides, { message: 'Database error', status })
31
21
  }
32
-
33
- toJSON() {
34
- // Remove SQL query from displayed data in front-end when not in development
35
- // or test.
36
- if (process.env.NODE_ENV !== 'development' ||
37
- process.env.NODE_ENV !== 'test') {
38
- const { sql, ...data } = this.data
39
- return data
40
- }
41
- return this.data
42
- }
43
22
  }
@@ -1,5 +1,4 @@
1
1
  import { isPlainObject, isString } from '@ditojs/utils'
2
- import { formatJson } from '@/utils'
3
2
 
4
3
  export class ResponseError extends Error {
5
4
  constructor(error, defaults = { message: 'Response error', status: 400 }) {
@@ -22,14 +21,7 @@ export class ResponseError extends Error {
22
21
  ? { message: error }
23
22
  : error || {}
24
23
  const { status, ...data } = { ...defaults, ...object }
25
- let { message, code } = data
26
- if (process.env.NODE_ENV === 'test' && error === object) {
27
- // Include full JSON error in message during tests, for better reporting.
28
- const { message: _, ...rest } = data
29
- if (Object.keys(rest).length > 0) {
30
- message = `${message}\nError Data:\n${formatJson(rest)}`
31
- }
32
- }
24
+ const { message, code } = data
33
25
  super(message)
34
26
  this.name = this.constructor.name
35
27
  this.status = status
@@ -2,7 +2,7 @@
2
2
  * Middleware inspired by 'koa-logger'. Adapted and extended to our needs.
3
3
  */
4
4
  import bytes from 'bytes'
5
- import chalk from 'chalk'
5
+ import pico from 'picocolors'
6
6
  import Counter from 'passthrough-counter'
7
7
 
8
8
  export function logRequests({ ignoreUrls } = {}) {
@@ -58,11 +58,11 @@ function logRequest(ctx) {
58
58
  logger.trace(
59
59
  { req: ctx.req },
60
60
  `${
61
- chalk.gray('<--')
61
+ pico.gray('<--')
62
62
  } ${
63
- chalk.bold(ctx.method)
63
+ pico.bold(ctx.method)
64
64
  } ${
65
- chalk.gray(ctx.originalUrl)
65
+ pico.gray(ctx.originalUrl)
66
66
  }`
67
67
  )
68
68
  }
@@ -93,15 +93,15 @@ function logResponse({ ctx, start, length, err }) {
93
93
  logger[level](
94
94
  { req: ctx.req, res: ctx.res },
95
95
  `${
96
- chalk.bold(ctx.method)
96
+ pico.bold(ctx.method)
97
97
  } ${
98
- chalk.gray(ctx.originalUrl)
98
+ pico.gray(ctx.originalUrl)
99
99
  } ${
100
- chalk[statusColor](status)
100
+ pico[statusColor](status)
101
101
  } ${
102
- chalk.gray(formattedTime)
102
+ pico.gray(formattedTime)
103
103
  } ${
104
- chalk.gray(formattedLength)
104
+ pico.gray(formattedLength)
105
105
  }`
106
106
  )
107
107
  }
@@ -5,8 +5,11 @@ import { convertSchema, addRelationSchemas, convertRelations } from '@/schema'
5
5
  import { populateGraph, filterGraph } from '@/graph'
6
6
  import { formatJson } from '@/utils'
7
7
  import {
8
- ResponseError, DatabaseError, GraphError, ModelError, NotFoundError,
9
- RelationError, WrappedError
8
+ ResponseError,
9
+ GraphError, ModelError,
10
+ NotFoundError,
11
+ RelationError,
12
+ WrappedError
10
13
  } from '@/errors'
11
14
  import {
12
15
  isString, isObject, isArray, isFunction, isPromise, asArray, merge, flatten,
@@ -223,7 +226,7 @@ export class Model extends objection.Model {
223
226
  return super.query(trx).onError(err => {
224
227
  // TODO: Shouldn't this wrapping happen on the Controller level?
225
228
  err = err instanceof ResponseError ? err
226
- : err instanceof objection.DBError ? new DatabaseError(err)
229
+ : err instanceof objection.DBError ? this.app.createDatabaseError(err)
227
230
  : new WrappedError(err)
228
231
  return Promise.reject(err)
229
232
  })
@@ -730,12 +733,11 @@ export class Model extends objection.Model {
730
733
  case 'ModelValidation':
731
734
  return this.app.createValidationError({
732
735
  type,
733
- message: message ||
734
- `The provided data for the ${this.name} model is not valid: ${
735
- formatJson(json)
736
- }`,
736
+ message:
737
+ message || `The provided data for the ${this.name} model is not valid`,
737
738
  errors,
738
- options
739
+ options,
740
+ json
739
741
  })
740
742
  case 'RelationExpression':
741
743
  case 'UnallowedRelation':