@ditojs/server 2.0.4 → 2.1.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 (54) hide show
  1. package/package.json +11 -13
  2. package/src/app/Application.js +228 -212
  3. package/src/app/Validator.js +53 -43
  4. package/src/cli/console.js +6 -4
  5. package/src/cli/db/createMigration.js +59 -30
  6. package/src/cli/db/migrate.js +6 -4
  7. package/src/cli/db/reset.js +8 -5
  8. package/src/cli/db/rollback.js +6 -4
  9. package/src/cli/db/seed.js +2 -1
  10. package/src/cli/index.js +1 -1
  11. package/src/controllers/AdminController.js +100 -84
  12. package/src/controllers/CollectionController.js +37 -30
  13. package/src/controllers/Controller.js +83 -43
  14. package/src/controllers/ControllerAction.js +27 -15
  15. package/src/controllers/ModelController.js +4 -1
  16. package/src/controllers/RelationController.js +19 -21
  17. package/src/controllers/UsersController.js +3 -4
  18. package/src/decorators/parameters.js +3 -1
  19. package/src/decorators/scope.js +1 -1
  20. package/src/errors/ControllerError.js +2 -1
  21. package/src/errors/DatabaseError.js +20 -11
  22. package/src/graph/DitoGraphProcessor.js +48 -40
  23. package/src/graph/expression.js +6 -8
  24. package/src/graph/graph.js +20 -11
  25. package/src/lib/EventEmitter.js +12 -12
  26. package/src/middleware/handleConnectMiddleware.js +16 -10
  27. package/src/middleware/handleError.js +6 -5
  28. package/src/middleware/handleSession.js +78 -0
  29. package/src/middleware/handleUser.js +2 -2
  30. package/src/middleware/index.js +2 -0
  31. package/src/middleware/logRequests.js +3 -3
  32. package/src/middleware/setupRequestStorage.js +14 -0
  33. package/src/mixins/AssetMixin.js +62 -58
  34. package/src/mixins/SessionMixin.js +13 -10
  35. package/src/mixins/TimeStampedMixin.js +33 -29
  36. package/src/mixins/UserMixin.js +130 -116
  37. package/src/models/Model.js +245 -194
  38. package/src/models/definitions/filters.js +14 -13
  39. package/src/query/QueryBuilder.js +252 -195
  40. package/src/query/QueryFilters.js +3 -3
  41. package/src/query/QueryParameters.js +2 -2
  42. package/src/query/Registry.js +8 -10
  43. package/src/schema/keywords/_validate.js +10 -8
  44. package/src/schema/properties.test.js +247 -206
  45. package/src/schema/relations.js +42 -20
  46. package/src/schema/relations.test.js +36 -19
  47. package/src/services/Service.js +8 -14
  48. package/src/storage/S3Storage.js +5 -3
  49. package/src/storage/Storage.js +16 -14
  50. package/src/utils/function.js +7 -4
  51. package/src/utils/function.test.js +30 -6
  52. package/src/utils/object.test.js +5 -1
  53. package/types/index.d.ts +244 -257
  54. package/src/app/SessionStore.js +0 -31
@@ -55,7 +55,9 @@ export class CollectionController extends Controller {
55
55
  // @override
56
56
  getPath(type, path) {
57
57
  return type === 'member'
58
- ? path ? `:${this.idParam}/${path}` : `:${this.idParam}`
58
+ ? path
59
+ ? `:${this.idParam}/${path}`
60
+ : `:${this.idParam}`
59
61
  : path
60
62
  }
61
63
 
@@ -91,9 +93,11 @@ export class CollectionController extends Controller {
91
93
  // Returns the model ids that this request concerns, read from the param
92
94
  // for member ids, and from the payload for collection ids:
93
95
  const { type } = ctx.action
94
- return type === 'member' ? [this.getMemberId(ctx)]
95
- : type === 'collection' ? this.getCollectionIds(ctx)
96
- : []
96
+ return type === 'member'
97
+ ? [this.getMemberId(ctx)]
98
+ : type === 'collection'
99
+ ? this.getCollectionIds(ctx)
100
+ : []
97
101
  }
98
102
 
99
103
  validateId(id) {
@@ -142,7 +146,7 @@ export class CollectionController extends Controller {
142
146
  const { scope } = base
143
147
  const { allowScope, allowFilter } = this
144
148
 
145
- const asAllowArray = value => value === false ? [] : asArray(value)
149
+ const asAllowArray = value => (value === false ? [] : asArray(value))
146
150
 
147
151
  if (allowScope !== undefined && allowScope !== true) {
148
152
  query.allowScope(
@@ -168,8 +172,7 @@ export class CollectionController extends Controller {
168
172
  async executeAndFetch(action, ctx, modify, body = ctx.request.body) {
169
173
  const name = `${action}${this.graph ? 'DitoGraph' : ''}AndFetch`
170
174
  return this.execute(ctx, (query, trx) =>
171
- query[name](body)
172
- .modify(getModify(modify, trx))
175
+ query[name](body).modify(getModify(modify, trx))
173
176
  )
174
177
  }
175
178
 
@@ -207,23 +210,25 @@ export class CollectionController extends Controller {
207
210
  },
208
211
 
209
212
  async delete(ctx, modify) {
210
- const count = await this.execute(ctx, (query, trx) => query
211
- .ignoreScope()
212
- .find(ctx.query, this.allowParam)
213
- .modify(query => this.isOneToOne && query.throwIfNotFound())
214
- .modify(getModify(modify, trx))
215
- .modify(query => this.unrelate ? query.unrelate() : query.delete())
213
+ const count = await this.execute(ctx, (query, trx) =>
214
+ query
215
+ .ignoreScope()
216
+ .find(ctx.query, this.allowParam)
217
+ .modify(query => this.isOneToOne && query.throwIfNotFound())
218
+ .modify(getModify(modify, trx))
219
+ .modify(query => (this.unrelate ? query.unrelate() : query.delete()))
216
220
  )
217
221
  return { count }
218
222
  },
219
223
 
220
224
  async post(ctx, modify) {
221
225
  const result = this.relate
222
- // Use patchDitoGraphAndFetch() to handle relates for us.
223
- ? await this.execute(ctx, (query, trx) => query
224
- .patchDitoGraphAndFetch(ctx.request.body, { relate: true })
225
- .modify(getModify(modify, trx))
226
- )
226
+ ? // Use patchDitoGraphAndFetch() to handle relates for us.
227
+ await this.execute(ctx, (query, trx) =>
228
+ query
229
+ .patchDitoGraphAndFetch(ctx.request.body, { relate: true })
230
+ .modify(getModify(modify, trx))
231
+ )
227
232
  : await this.executeAndFetch('insert', ctx, modify)
228
233
  ctx.status = 201 // Created
229
234
  if (isObject(result)) {
@@ -243,22 +248,24 @@ export class CollectionController extends Controller {
243
248
 
244
249
  member = this.convertToCoreActions({
245
250
  async get(ctx, modify) {
246
- return this.execute(ctx, (query, trx) => query
247
- .findById(ctx.memberId)
248
- .find(ctx.query, this.allowParam)
249
- .throwIfNotFound()
250
- .modify(getModify(modify, trx))
251
+ return this.execute(ctx, (query, trx) =>
252
+ query
253
+ .findById(ctx.memberId)
254
+ .find(ctx.query, this.allowParam)
255
+ .throwIfNotFound()
256
+ .modify(getModify(modify, trx))
251
257
  )
252
258
  },
253
259
 
254
260
  async delete(ctx, modify) {
255
- const count = await this.execute(ctx, (query, trx) => query
256
- .ignoreScope()
257
- .findById(ctx.memberId)
258
- .find(ctx.query, this.allowParam)
259
- .throwIfNotFound()
260
- .modify(getModify(modify, trx))
261
- .modify(query => this.unrelate ? query.unrelate() : query.delete())
261
+ const count = await this.execute(ctx, (query, trx) =>
262
+ query
263
+ .ignoreScope()
264
+ .findById(ctx.memberId)
265
+ .find(ctx.query, this.allowParam)
266
+ .throwIfNotFound()
267
+ .modify(getModify(modify, trx))
268
+ .modify(query => (this.unrelate ? query.unrelate() : query.delete()))
262
269
  )
263
270
  return { count }
264
271
  },
@@ -3,18 +3,30 @@ import { EventEmitter } from '../lib/index.js'
3
3
  import ControllerAction from './ControllerAction.js'
4
4
  import MemberAction from './MemberAction.js'
5
5
  import {
6
- ResponseError, ControllerError, AuthorizationError
6
+ ResponseError,
7
+ ControllerError,
8
+ AuthorizationError
7
9
  } from '../errors/index.js'
8
10
  import {
9
- getOwnProperty, getOwnKeys, getAllKeys, getInheritanceChain
11
+ getOwnProperty,
12
+ getOwnKeys,
13
+ getAllKeys,
14
+ getInheritanceChain
10
15
  } from '../utils/object.js'
11
16
  import { processHandlerParameters } from '../utils/handler.js'
12
17
  import { describeFunction } from '../utils/function.js'
13
18
  import { formatJson } from '../utils/json.js'
14
19
  import { deprecate } from '../utils/deprecate.js'
15
20
  import {
16
- isObject, isString, isArray, isBoolean, isFunction, asArray, equals,
17
- parseDataPath, normalizeDataPath
21
+ isObject,
22
+ isString,
23
+ isArray,
24
+ isBoolean,
25
+ isFunction,
26
+ asArray,
27
+ equals,
28
+ parseDataPath,
29
+ normalizeDataPath
18
30
  } from '@ditojs/utils'
19
31
 
20
32
  export class Controller {
@@ -42,10 +54,14 @@ export class Controller {
42
54
  // the callbacks from base classes are also run. And reverse the chain so
43
55
  // that the base class callbacks are run first.
44
56
  const chain = getInheritanceChain(hooks).reverse()
57
+ const keys = Object.keys(Object.assign({}, hooks, ...chain))
45
58
  const events = Object.fromEntries(
46
- Object.keys(hooks || {}).map(
47
- event => [event, chain.map(hooks => hooks[event]).filter(Boolean)]
48
- )
59
+ keys.map(event => [
60
+ event,
61
+ chain
62
+ .map(hooks => (hooks.hasOwnProperty(event) ? hooks[event] : null))
63
+ .filter(Boolean)
64
+ ])
49
65
  )
50
66
  this._configureEmitter(events, {
51
67
  // Support wildcard hooks only on controllers:
@@ -174,7 +190,11 @@ export class Controller {
174
190
  // Replace the action object with the converted action handler, so they
175
191
  // too can benefit from prototypal inheritance:
176
192
  actions[name] = this.setupAction(
177
- type, actions, name, action, authorize[name]
193
+ type,
194
+ actions,
195
+ name,
196
+ action,
197
+ authorize[name]
178
198
  )
179
199
  }
180
200
  // Expose a direct reference to the controller on the action object, but
@@ -190,9 +210,11 @@ export class Controller {
190
210
  }
191
211
 
192
212
  setupAction(type, actions, name, action, authorize) {
193
- const handler = isFunction(action) ? action
194
- : isObject(action) ? convertActionObject(name, action, actions)
195
- : null
213
+ const handler = isFunction(action)
214
+ ? action
215
+ : isObject(action)
216
+ ? convertActionObject(name, action, actions)
217
+ : null
196
218
  // Action naming convention: `'<method> <path>'`, or just `'<method>'` for
197
219
  // the default methods.
198
220
  let [method, path = ''] = name.split(' ')
@@ -206,7 +228,14 @@ export class Controller {
206
228
  type,
207
229
  // eslint-disable-next-line new-cap
208
230
  new actionClass(
209
- this, actions, handler, type, name, method, path, authorize
231
+ this,
232
+ actions,
233
+ handler,
234
+ type,
235
+ name,
236
+ method,
237
+ path,
238
+ authorize
210
239
  )
211
240
  )
212
241
  return handler
@@ -249,7 +278,8 @@ export class Controller {
249
278
  } = config
250
279
  const storage = this.app.getStorage(storageName)
251
280
  if (!storage) {
252
- throw new ControllerError(this,
281
+ throw new ControllerError(
282
+ this,
253
283
  `Unknown storage configuration: '${storageName}'`
254
284
  )
255
285
  }
@@ -258,9 +288,10 @@ export class Controller {
258
288
 
259
289
  const normalizedPath = getDataPath(
260
290
  // Router supports both shallow & deep wildcards, no normalization needed.
261
- token => token === '*' || token === '**'
262
- ? token
263
- : this.app.normalizePath(token)
291
+ token =>
292
+ token === '*' || token === '**'
293
+ ? token
294
+ : this.app.normalizePath(token)
264
295
  )
265
296
 
266
297
  // Convert `dataPath` to a regular expression to match field names
@@ -270,11 +301,12 @@ export class Controller {
270
301
  `^${
271
302
  getDataPath(
272
303
  // Use the exact same regexps as in `Router`:
273
- token => token === '*'
274
- ? '[^/]+' // shallow wildcard
275
- : token === '**'
276
- ? '.+?' // deep wildcard
277
- : token
304
+ token =>
305
+ token === '*'
306
+ ? '[^/]+' // shallow wildcard
307
+ : token === '**'
308
+ ? '.+?' // deep wildcard
309
+ : token
278
310
  )
279
311
  }$`
280
312
  )
@@ -525,17 +557,17 @@ export class Controller {
525
557
  member = await this.getMember(ctx)
526
558
  }
527
559
  return !!values.find(
528
- // Support 3 scenarios:
529
- // - '$self': The requested member is checked against `ctx.state.user`
530
- // and the action is only authorized if it matches the member.
531
- // - '$owner': The member is asked if it is owned by `ctx.state.user`
532
- // through the optional `Model.$hasOwner()` method.
533
- // - any string: `ctx.state.user` is checked for this role through
534
- // the overridable `UserModel.hasRole()` method.
560
+ // Support 3 scenarios:
561
+ // - '$self': The requested member is checked against `ctx.state.user`
562
+ // and the action is only authorized if it matches the member.
563
+ // - '$owner': The member is asked if it is owned by `ctx.state.user`
564
+ // through the optional `Model.$hasOwner()` method.
565
+ // - any string: `ctx.state.user` is checked for this role through
566
+ // the overridable `UserModel.hasRole()` method.
535
567
  value => {
536
568
  return value === '$self'
537
569
  ? user.constructor === this.modelClass &&
538
- equals(user.$id(), ctx.memberId)
570
+ equals(user.$id(), ctx.memberId)
539
571
  : value === '$owner'
540
572
  ? member?.$hasOwner?.(user)
541
573
  : user.$hasRole(value)
@@ -543,7 +575,8 @@ export class Controller {
543
575
  )
544
576
  }
545
577
  } else {
546
- throw new ControllerError(this,
578
+ throw new ControllerError(
579
+ this,
547
580
  `Unsupported authorize setting: '${authorize}'`
548
581
  )
549
582
  }
@@ -588,14 +621,18 @@ function convertActionObject(name, object, actions) {
588
621
  Object.setPrototypeOf(object, Object.getPrototypeOf(actions))
589
622
 
590
623
  if (action) {
591
- deprecate(`action.action is deprecated. Use action.method and action.path instead.`)
624
+ deprecate(
625
+ `action.action is deprecated. Use action.method and action.path instead.`
626
+ )
592
627
  const [method, path] = asArray(action)
593
628
  handler.method = method
594
629
  handler.path = path
595
630
  }
596
631
 
597
632
  if (!handler) {
598
- throw new Error(`Missing handler in '${name}' action: ${formatJson(object)}`)
633
+ throw new Error(
634
+ `Missing handler in '${name}' action: ${formatJson(object)}`
635
+ )
599
636
  }
600
637
 
601
638
  handler.authorize = authorize ?? null
@@ -609,15 +646,18 @@ function convertActionObject(name, object, actions) {
609
646
  }
610
647
 
611
648
  function isMethodAction(name) {
612
- return {
613
- get: true,
614
- delete: true,
615
- post: true,
616
- put: true,
617
- patch: true,
618
- head: true,
619
- options: true,
620
- trace: true,
621
- connect: true
622
- }[name] || false
649
+ return (
650
+ {
651
+ get: true,
652
+ delete: true,
653
+ post: true,
654
+ put: true,
655
+ patch: true,
656
+ head: true,
657
+ options: true,
658
+ trace: true,
659
+ connect: true
660
+ }[name] ||
661
+ false
662
+ )
623
663
  }
@@ -2,7 +2,14 @@ import { isString, isObject, asArray, clone } from '@ditojs/utils'
2
2
 
3
3
  export default class ControllerAction {
4
4
  constructor(
5
- controller, actions, handler, type, name, _method, _path, _authorize
5
+ controller,
6
+ actions,
7
+ handler,
8
+ type,
9
+ name,
10
+ _method,
11
+ _path,
12
+ _authorize
6
13
  ) {
7
14
  const {
8
15
  core = false,
@@ -33,10 +40,12 @@ export default class ControllerAction {
33
40
  this.authorize = authorize || _authorize
34
41
  this.transacted = !!(
35
42
  transacted ||
36
- controller.transacted || (
37
- // Core graph and assets operations are always transacted, unless the
38
- // method is 'get':
39
- core && method !== 'get' && (
43
+ controller.transacted ||
44
+ // Core graph and assets operations are always transacted, unless the
45
+ // method is 'get':
46
+ (
47
+ core &&
48
+ method !== 'get' && (
40
49
  controller.graph ||
41
50
  controller.assets
42
51
  )
@@ -176,7 +185,7 @@ export default class ControllerAction {
176
185
  }
177
186
  }
178
187
 
179
- const getData = () => unwrapRoot ? params[dataName] : params
188
+ const getData = () => (unwrapRoot ? params[dataName] : params)
180
189
  try {
181
190
  await this.parameters.validate(params)
182
191
  return getData()
@@ -208,7 +217,7 @@ export default class ControllerAction {
208
217
 
209
218
  // If a named result is defined, return the data wrapped,
210
219
  // otherwise return the original unwrapped result object.
211
- const getResult = () => returnsName ? data : result
220
+ const getResult = () => (returnsName ? data : result)
212
221
  try {
213
222
  await this.returns.validate(data)
214
223
  return getResult()
@@ -279,14 +288,17 @@ export default class ControllerAction {
279
288
  } else {
280
289
  // A simple version of named key/value pairs, values can be
281
290
  // strings or numbers.
282
- value = Object.fromEntries(value.split(/\s*,\s*/g).map(entry => {
283
- let [key, val] = entry.split(/\s*:\s*/)
284
- try {
285
- // Try parsing basic types, but fall back to unquoted string.
286
- val = JSON.parse(val)
287
- } catch {}
288
- return [key, val]
289
- }))
291
+ value = Object.fromEntries(
292
+ value.split(/\s*,\s*/g).map(entry => {
293
+ let [key, val] = entry.split(/\s*:\s*/)
294
+ try {
295
+ // Try parsing basic types, but fall back to unquoted
296
+ // string.
297
+ val = JSON.parse(val)
298
+ } catch {}
299
+ return [key, val]
300
+ })
301
+ )
290
302
  }
291
303
  } else {
292
304
  value = JSON.parse(value)
@@ -41,7 +41,10 @@ export class ModelController extends CollectionController {
41
41
  throw new ControllerError(this, `Relation '${name}' not found.`)
42
42
  }
43
43
  const relation = new RelationController(
44
- this, object, relationInstance, relationDefinition
44
+ this,
45
+ object,
46
+ relationInstance,
47
+ relationDefinition
45
48
  )
46
49
  // RelationController instances are not registered with the app, but are
47
50
  // managed by their parent controller instead.
@@ -9,8 +9,10 @@ export class RelationController extends CollectionController {
9
9
  constructor(parent, object, relationInstance, relationDefinition) {
10
10
  super(parent.app, null)
11
11
  if (parent.modelClass !== relationInstance.ownerModelClass) {
12
- throw new ControllerError(parent,
13
- `Invalid parent controller for relation '${relationInstance.name}'.`)
12
+ throw new ControllerError(
13
+ parent,
14
+ `Invalid parent controller for relation '${relationInstance.name}'.`
15
+ )
14
16
  }
15
17
  this.parent = parent
16
18
  this.object = object
@@ -81,26 +83,22 @@ export class RelationController extends CollectionController {
81
83
  // @override
82
84
  async execute(ctx, execute) {
83
85
  const id = this.parent.getMemberId(ctx)
84
- return this.parent.execute(ctx,
85
- async (parentQuery, trx) => {
86
- const model = await parentQuery
87
- .ignoreScope()
88
- .findById(id)
89
- .throwIfNotFound()
90
- // Explicitly only select the foreign key ids for more efficiency.
91
- .select(...this.relationInstance.ownerProp.props)
92
- // This is the same as `ModelController.execute()`, except for the use
93
- // of `model.$relatedQuery()` instead of `modelClass.query()`:
94
- const query = model.$relatedQuery(this.relationInstance.name, trx)
95
- this.setupQuery(query)
96
- return execute(query, trx)
97
- }
98
- )
86
+ return this.parent.execute(ctx, async (parentQuery, trx) => {
87
+ const model = await parentQuery
88
+ .ignoreScope()
89
+ .findById(id)
90
+ .throwIfNotFound()
91
+ // Explicitly only select the foreign key ids for more efficiency.
92
+ .select(...this.relationInstance.ownerProp.props)
93
+ // This is the same as `ModelController.execute()`, except for the use
94
+ // of `model.$relatedQuery()` instead of `modelClass.query()`:
95
+ const query = model.$relatedQuery(this.relationInstance.name, trx)
96
+ this.setupQuery(query)
97
+ return execute(query, trx)
98
+ })
99
99
  }
100
100
 
101
- collection = this.toCoreActions({
102
- })
101
+ collection = this.toCoreActions({})
103
102
 
104
- member = this.toCoreActions({
105
- })
103
+ member = this.toCoreActions({})
106
104
  }
@@ -40,16 +40,15 @@ export class UsersController extends ModelController {
40
40
  return {
41
41
  authenticated,
42
42
  user: authenticated ? ctx.state.user : null
43
-
44
43
  }
45
44
  },
46
45
 
47
46
  'get self'(ctx) {
48
47
  return this.isAuthenticated(ctx)
49
48
  ? this.member.get.call(
50
- this,
51
- this.getContextWithMemberId(ctx, ctx.state.user.$id())
52
- )
49
+ this,
50
+ this.getContextWithMemberId(ctx, ctx.state.user.$id())
51
+ )
53
52
  : null
54
53
  }
55
54
  }
@@ -16,7 +16,9 @@ export function parameters(parameters, options) {
16
16
  }
17
17
  }
18
18
  if (!isArray(parameters) && !isObject(parameters)) {
19
- throw new Error(`@parameters() need to be defined using array or object definitions`)
19
+ throw new Error(
20
+ `@parameters() need to be defined using array or object definitions`
21
+ )
20
22
  }
21
23
 
22
24
  return createDecorator(value => {
@@ -2,7 +2,7 @@ import { createDecorator } from '../utils/decorator.js'
2
2
 
3
3
  export function scope(...scopes) {
4
4
  return createDecorator(value => {
5
- const scope = value.scope = value.scope || []
5
+ const scope = (value.scope ||= [])
6
6
  scope.push(...scopes)
7
7
  })
8
8
  }
@@ -3,7 +3,8 @@ import { ResponseError } from './ResponseError.js'
3
3
 
4
4
  export class ControllerError extends ResponseError {
5
5
  constructor(controller, error) {
6
- const { name } = isFunction(controller) ? controller
6
+ const { name } = isFunction(controller)
7
+ ? controller
7
8
  : controller.constructor
8
9
  super(`Controller ${name}: ${error}`, {
9
10
  message: `Controller ${name}: Controller error`,
@@ -18,19 +18,28 @@ const {
18
18
 
19
19
  export class DatabaseError extends ResponseError {
20
20
  constructor(error, overrides) {
21
- super(error, {
22
- type: error.constructor.name,
23
- message: 'Database error',
24
- status: getStatus(error)
25
- }, overrides)
21
+ super(
22
+ error,
23
+ {
24
+ type: error.constructor.name,
25
+ message: 'Database error',
26
+ status: getStatus(error)
27
+ },
28
+ overrides
29
+ )
26
30
  }
27
31
  }
28
32
 
29
33
  function getStatus(error) {
30
- return error instanceof CheckViolationError ? 400
31
- : error instanceof NotNullViolationError ? 400
32
- : error instanceof ConstraintViolationError ? 409
33
- : error instanceof DataError ? 400
34
- : error instanceof DBError ? 500
35
- : 400
34
+ return error instanceof CheckViolationError
35
+ ? 400
36
+ : error instanceof NotNullViolationError
37
+ ? 400
38
+ : error instanceof ConstraintViolationError
39
+ ? 409
40
+ : error instanceof DataError
41
+ ? 400
42
+ : error instanceof DBError
43
+ ? 500
44
+ : 400
36
45
  }