@ditojs/server 0.271.0 → 0.274.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 (86) hide show
  1. package/lib/app/Application.js +68 -25
  2. package/lib/app/SessionStore.js +3 -1
  3. package/lib/app/Validator.js +16 -2
  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/listAssetConfig.js +4 -2
  9. package/lib/cli/db/migrate.js +3 -3
  10. package/lib/cli/db/reset.js +3 -3
  11. package/lib/cli/db/rollback.js +3 -3
  12. package/lib/cli/db/seed.js +14 -6
  13. package/lib/cli/db/unlock.js +3 -3
  14. package/lib/cli/index.js +9 -5
  15. package/lib/controllers/AdminController.js +18 -6
  16. package/lib/controllers/CollectionController.js +15 -1
  17. package/lib/controllers/Controller.js +28 -4
  18. package/lib/controllers/ControllerAction.js +18 -6
  19. package/lib/controllers/RelationController.js +9 -3
  20. package/lib/controllers/UserController.js +15 -1
  21. package/lib/controllers/index.js +7 -1
  22. package/lib/decorators/index.js +7 -1
  23. package/lib/decorators/parameters.js +4 -2
  24. package/lib/decorators/returns.js +4 -2
  25. package/lib/errors/DatabaseError.js +5 -19
  26. package/lib/errors/ResponseError.js +4 -14
  27. package/lib/errors/index.js +7 -1
  28. package/lib/graph/DitoGraphProcessor.js +5 -1
  29. package/lib/graph/expression.js +5 -1
  30. package/lib/graph/graph.js +18 -2
  31. package/lib/graph/index.js +7 -1
  32. package/lib/index.js +7 -1
  33. package/lib/lib/index.js +7 -1
  34. package/lib/middleware/findRoute.js +7 -1
  35. package/lib/middleware/index.js +7 -1
  36. package/lib/middleware/logRequests.js +4 -4
  37. package/lib/mixins/AssetMixin.js +4 -4
  38. package/lib/mixins/SessionMixin.js +4 -4
  39. package/lib/mixins/TimeStampedMixin.js +4 -4
  40. package/lib/mixins/UserMixin.js +10 -4
  41. package/lib/mixins/index.js +7 -1
  42. package/lib/models/Model.js +41 -22
  43. package/lib/models/definitions/filters.js +9 -1
  44. package/lib/models/definitions/properties.js +7 -1
  45. package/lib/models/definitions/scopes.js +9 -1
  46. package/lib/models/index.js +7 -1
  47. package/lib/query/QueryBuilder.js +11 -1
  48. package/lib/query/QueryFilters.js +11 -1
  49. package/lib/query/index.js +7 -1
  50. package/lib/schema/formats/index.js +7 -1
  51. package/lib/schema/index.js +10 -2
  52. package/lib/schema/keywords/index.js +7 -1
  53. package/lib/schema/properties.js +11 -1
  54. package/lib/schema/relations.js +18 -4
  55. package/lib/services/index.js +7 -1
  56. package/lib/storage/DiskStorage.js +7 -1
  57. package/lib/storage/Storage.js +5 -1
  58. package/lib/storage/index.js +7 -1
  59. package/lib/utils/emitter.js +5 -1
  60. package/lib/utils/index.js +15 -1
  61. package/lib/utils/json.js +9 -0
  62. package/lib/utils/object.js +9 -3
  63. package/package.json +31 -31
  64. package/src/app/Application.js +40 -11
  65. package/src/app/Validator.js +2 -1
  66. package/src/cli/console.js +3 -3
  67. package/src/cli/db/createMigration.js +3 -3
  68. package/src/cli/db/listAssetConfig.js +3 -1
  69. package/src/cli/db/migrate.js +4 -4
  70. package/src/cli/db/reset.js +4 -4
  71. package/src/cli/db/rollback.js +4 -4
  72. package/src/cli/db/seed.js +6 -6
  73. package/src/cli/db/unlock.js +2 -2
  74. package/src/cli/index.js +3 -3
  75. package/src/controllers/AdminController.js +3 -2
  76. package/src/controllers/Controller.js +8 -8
  77. package/src/controllers/ControllerAction.js +7 -5
  78. package/src/controllers/RelationController.js +2 -2
  79. package/src/decorators/parameters.js +2 -2
  80. package/src/decorators/returns.js +2 -2
  81. package/src/errors/DatabaseError.js +2 -23
  82. package/src/errors/ResponseError.js +1 -8
  83. package/src/middleware/logRequests.js +9 -9
  84. package/src/models/Model.js +13 -8
  85. package/src/utils/index.js +1 -0
  86. package/src/utils/json.js +3 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ditojs/server",
3
- "version": "0.271.0",
3
+ "version": "0.274.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.271.0",
34
- "@ditojs/router": "^0.271.0",
35
- "@ditojs/utils": "^0.271.0",
31
+ "@babel/core": "^7.17.5",
32
+ "@babel/runtime": "^7.17.2",
33
+ "@ditojs/admin": "^0.274.0",
34
+ "@ditojs/router": "^0.274.0",
35
+ "@ditojs/utils": "^0.274.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.271.0",
92
+ "@babel/cli": "^7.17.6",
93
+ "@babel/preset-env": "^7.16.11",
94
+ "@ditojs/babel-preset": "^0.274.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": "23245bf234f83049097413475a32d5bd27c5a603"
103
+ "gitHead": "0ebfe0bb8913f9d338bb91a0a507e7a4c6e49ecb"
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'
@@ -23,7 +23,13 @@ import { Controller, AdminController } from '@/controllers'
23
23
  import { Service } from '@/services'
24
24
  import { Storage } from '@/storage'
25
25
  import { convertSchema } from '@/schema'
26
- import { ResponseError, ValidationError, AssetError } from '@/errors'
26
+ import { formatJson } from '@/utils'
27
+ import {
28
+ ResponseError,
29
+ ValidationError,
30
+ DatabaseError,
31
+ AssetError
32
+ } from '@/errors'
27
33
  import SessionStore from './SessionStore'
28
34
  import { Validator } from './Validator'
29
35
  import {
@@ -157,7 +163,7 @@ export class Application extends Koa {
157
163
  }
158
164
  if (Object.keys(data).length > 0) {
159
165
  console.info(
160
- chalk.yellow.bold(`\n${modelClass.name}:\n`),
166
+ pico.yellow.bold(`\n${modelClass.name}:\n`),
161
167
  util.inspect(data, {
162
168
  colors: true,
163
169
  depth: null,
@@ -472,11 +478,26 @@ export class Application extends Koa {
472
478
  }
473
479
  }
474
480
 
475
- createValidationError({ type, message, errors, options }) {
481
+ createValidationError({ type, message, errors, options, json }) {
476
482
  return new ValidationError({
477
483
  type,
478
484
  message,
479
- 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
480
501
  })
481
502
  }
482
503
 
@@ -485,13 +506,15 @@ export class Application extends Koa {
485
506
 
486
507
  this.use(attachLogger(this.logger))
487
508
 
488
- this.use(handleError())
489
509
  if (app.responseTime !== false) {
490
510
  this.use(responseTime(getOptions(app.responseTime)))
491
511
  }
492
512
  if (log.requests) {
493
513
  this.use(logRequests())
494
514
  }
515
+ // Needs to be positioned after the request logger to log the correct
516
+ // response status.
517
+ this.use(handleError())
495
518
  if (app.helmet !== false) {
496
519
  this.use(helmet(getOptions(app.helmet)))
497
520
  }
@@ -605,6 +628,12 @@ export class Application extends Koa {
605
628
  }
606
629
  }
607
630
  this.knex = Knex(knex)
631
+ // Support PostgreSQL type parser mappings in the config.
632
+ if (knex.client === 'postgresql' && knex.typeParsers) {
633
+ for (const [type, parser] of Object.entries(knex.typeParsers)) {
634
+ this.knex.client.driver.types.setTypeParser(type, parser)
635
+ }
636
+ }
608
637
  if (log.sql) {
609
638
  this.setupKnexLogging()
610
639
  }
@@ -655,7 +684,7 @@ export class Application extends Koa {
655
684
 
656
685
  formatError(err) {
657
686
  const message = err.toJSON
658
- ? JSON.stringify(err.toJSON(), null, 2)
687
+ ? formatJson(err.toJSON())
659
688
  : err.message || err
660
689
  const str = `${err.name}: ${message}`
661
690
  return err.stack && this.config.log.errors?.stack !== false
@@ -823,13 +852,13 @@ export class Application extends Koa {
823
852
  if (!data) {
824
853
  console.info(
825
854
  `${
826
- chalk.red('INFO:')
855
+ pico.red('INFO:')
827
856
  } Asset ${
828
- chalk.green(`'${file.name}'`)
857
+ pico.green(`'${file.name}'`)
829
858
  } is from a foreign source, fetching from ${
830
- chalk.green(`'${file.url}'`)
859
+ pico.green(`'${file.url}'`)
831
860
  } and adding to storage ${
832
- chalk.green(`'${storage.name}'`)
861
+ pico.green(`'${storage.name}'`)
833
862
  }...`
834
863
  )
835
864
  const response = await axios.request({
@@ -2,6 +2,7 @@ import objection from 'objection'
2
2
  import Ajv from 'ajv'
3
3
  import addFormats from 'ajv-formats'
4
4
  import { isArray, isObject, clone, isAsync, isPromise } from '@ditojs/utils'
5
+ import { formatJson } from '@/utils'
5
6
  import * as schema from '@/schema'
6
7
 
7
8
  // Dito does not rely on objection.AjvValidator but instead implements its own
@@ -83,7 +84,7 @@ export class Validator extends objection.Validator {
83
84
  }
84
85
  return opts
85
86
  }, {})
86
- const cacheKey = JSON.stringify(opts)
87
+ const cacheKey = formatJson(opts, false)
87
88
  const { ajv } = this.ajvCache[cacheKey] || (this.ajvCache[cacheKey] = {
88
89
  ajv: this.createAjv(opts),
89
90
  options
@@ -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,8 +1,10 @@
1
+ import { formatJson } from '@/utils'
2
+
1
3
  export async function listAssetConfig(app, ...args) {
2
4
  const assetConfig = app.getAssetConfig({
3
5
  models: args.length > 0 ? args : Object.keys(app.models),
4
6
  normalizeDbNames: true
5
7
  })
6
- console.info(JSON.stringify(assetConfig, null, 2))
8
+ console.info(formatJson(assetConfig))
7
9
  return true
8
10
  }
@@ -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
  }
@@ -6,8 +6,9 @@ import koaWebpack from 'koa-webpack'
6
6
  import historyApiFallback from 'koa-connect-history-api-fallback'
7
7
  import VueService from '@vue/cli-service'
8
8
  import HtmlWebpackTagsPlugin from 'html-webpack-tags-plugin'
9
- import { ControllerError } from '@/errors'
10
9
  import { Controller } from './Controller'
10
+ import { ControllerError } from '@/errors'
11
+ import { formatJson } from '@/utils'
11
12
  import { isString, isObject } from '@ditojs/utils'
12
13
 
13
14
  export class AdminController extends Controller {
@@ -59,7 +60,7 @@ export class AdminController extends Controller {
59
60
  sendDitoObject(ctx) {
60
61
  // Send back the global dito object as JavaScript code.
61
62
  ctx.type = 'text/javascript'
62
- ctx.body = `window.dito = ${JSON.stringify(this.getDitoObject())}`
63
+ ctx.body = `window.dito = ${formatJson(this.getDitoObject())}`
63
64
  }
64
65
 
65
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
  )
@@ -165,8 +165,9 @@ export default class ControllerAction {
165
165
  if (errors.length > 0) {
166
166
  throw this.createValidationError({
167
167
  type: 'ParameterValidation',
168
- message: `The provided data is not valid: ${JSON.stringify(getData())}`,
169
- errors
168
+ message: 'The provided action parameters are not valid',
169
+ errors,
170
+ json: getData()
170
171
  })
171
172
  }
172
173
  }
@@ -189,8 +190,9 @@ export default class ControllerAction {
189
190
  } catch (error) {
190
191
  throw this.createValidationError({
191
192
  type: 'ResultValidation',
192
- message: `Invalid result of action: ${JSON.stringify(getResult())}`,
193
- errors: error.errors
193
+ message: 'The returned action result is not valid',
194
+ errors: error.errors,
195
+ json: getResult()
194
196
  })
195
197
  }
196
198
  }
@@ -229,7 +231,7 @@ export default class ControllerAction {
229
231
 
230
232
  coerceValue(type, value, modelOptions) {
231
233
  // See if param needs additional coercion:
232
- if (['date', 'datetime', 'timestamp'].includes(type)) {
234
+ if (value && ['date', 'datetime', 'timestamp'].includes(type)) {
233
235
  value = new Date(value)
234
236
  } else {
235
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.
@@ -1,5 +1,5 @@
1
1
  import { isArray, isObject } from '@ditojs/utils'
2
- import { createDecorator, deprecate } from '@/utils'
2
+ import { createDecorator, deprecate, formatJson } from '@/utils'
3
3
 
4
4
  export function parameters(parameters, options) {
5
5
  if (isObject(parameters)) {
@@ -7,7 +7,7 @@ export function parameters(parameters, options) {
7
7
  if (!isObject(first)) {
8
8
  deprecate(
9
9
  `@parameters(${
10
- JSON.stringify(parameters)
10
+ formatJson(parameters, false)
11
11
  }) with parameter schema object is deprecated: Schema object should be passed nested inside an array or object definition.`
12
12
  )
13
13
  parameters = [...arguments]
@@ -1,11 +1,11 @@
1
1
  import { isObject } from '@ditojs/utils'
2
- import { createDecorator } from '@/utils'
2
+ import { createDecorator, formatJson } from '@/utils'
3
3
 
4
4
  export function returns(returns, options) {
5
5
  if (!isObject(returns)) {
6
6
  throw new Error(
7
7
  `@returns(${
8
- JSON.stringify(returns)
8
+ formatJson(returns, false)
9
9
  }) needs to be defined using an object parameter definition`
10
10
  )
11
11
  }
@@ -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
  }
@@ -21,14 +21,7 @@ export class ResponseError extends Error {
21
21
  ? { message: error }
22
22
  : error || {}
23
23
  const { status, ...data } = { ...defaults, ...object }
24
- let { message, code } = data
25
- if (process.env.NODE_ENV === 'test' && error === object) {
26
- // Include full JSON error in message during tests, for better reporting.
27
- const { message: _, ...rest } = data
28
- if (Object.keys(rest).length > 0) {
29
- message = `${message}\nError Data:\n${JSON.stringify(rest, null, 2)}`
30
- }
31
- }
24
+ const { message, code } = data
32
25
  super(message)
33
26
  this.name = this.constructor.name
34
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
  }