@ditojs/server 1.1.0 → 1.3.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ditojs/server",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "type": "module",
5
5
  "description": "Dito.js Server – Dito.js is a declarative and modern web framework, based on Objection.js, Koa.js and Vue.js",
6
6
  "repository": "https://github.com/ditojs/dito/tree/master/packages/server",
@@ -21,9 +21,9 @@
21
21
  "node >= 14"
22
22
  ],
23
23
  "dependencies": {
24
- "@ditojs/admin": "^1.1.0",
25
- "@ditojs/router": "^1.1.0",
26
- "@ditojs/utils": "^1.1.0",
24
+ "@ditojs/admin": "^1.3.0",
25
+ "@ditojs/router": "^1.3.0",
26
+ "@ditojs/utils": "^1.3.0",
27
27
  "@koa/cors": "^3.2.0",
28
28
  "@koa/multer": "^3.0.0",
29
29
  "@originjs/vite-plugin-commonjs": "^1.0.3",
@@ -53,7 +53,6 @@
53
53
  "koa-session": "^6.2.0",
54
54
  "koa-static": "^5.0.0",
55
55
  "mime-types": "^2.1.35",
56
- "minimatch": "^5.0.1",
57
56
  "multer": "^1.4.4",
58
57
  "multer-s3": "^2.10.0",
59
58
  "nanoid": "^3.3.1",
@@ -82,5 +81,5 @@
82
81
  "pg": "^8.7.3",
83
82
  "sqlite3": "^5.0.2"
84
83
  },
85
- "gitHead": "32b276bd5d9eaf468352a69f361bdb5df5ed373c"
84
+ "gitHead": "23aac9eddfc17e3e9367db2b7ac73595bc2bad37"
86
85
  }
@@ -122,6 +122,11 @@ export class Application extends Koa {
122
122
  controller,
123
123
  action
124
124
  }
125
+ if (!(method in this.router)) {
126
+ throw new Error(
127
+ `Unsupported HTTP method '${method}' in route '${path}'`
128
+ )
129
+ }
125
130
  this.router[method](path, route)
126
131
  }
127
132
 
@@ -193,12 +193,14 @@ export class Validator extends objection.Validator {
193
193
  for (const error of errors) {
194
194
  // Adjust dataPaths to reflect nested validation in Objection.
195
195
  // NOTE: As of Ajv 8, `error.dataPath` is now called `error.instancePath`,
196
- // but we stick to `error.dataPath` in Dito.js
197
- const dataPath = `${options?.dataPath || ''}${error.instancePath}`
196
+ // but we stick to `error.dataPath` in Dito.js, and support both in errors
197
+ // passed in here.
198
+ const instancePath = (error.instancePath ?? error.dataPath) || ''
199
+ const dataPath = `${options?.dataPath || ''}${instancePath}`
198
200
  // Unknown properties are reported in `['propertyName']` notation,
199
201
  // so replace those with dot-notation, see:
200
202
  // https://github.com/epoberezkin/ajv/issues/671
201
- const key = dataPath.replace(/\['([^']*)'\]/g, '.$1').slice(1)
203
+ const key = dataPath.replace(/\['([^']*)'\]/g, '/$1').slice(1)
202
204
  const { message, keyword, params } = error
203
205
  const definition = keyword === 'format'
204
206
  ? this.getFormat(params.format)
@@ -13,7 +13,7 @@ import { merge } from '@ditojs/utils'
13
13
  import { Controller } from './Controller.js'
14
14
  import { handleConnectMiddleware } from '../middleware/index.js'
15
15
  import { ControllerError } from '../errors/index.js'
16
- import { formatJson } from '../utils/index.js'
16
+ import { formatJson, deprecate } from '../utils/index.js'
17
17
 
18
18
  export class AdminController extends Controller {
19
19
  // @override
@@ -34,11 +34,19 @@ export class AdminController extends Controller {
34
34
  }
35
35
 
36
36
  getPath(name) {
37
- const str = this.config[name]?.path
37
+ const { config } = this
38
+ let str = config[name]
39
+ if (config.build?.path || config.dist?.path) {
40
+ deprecate(`config.admin.build.path and config.admin.dist.path are deprecated. Use config.admin.root and config.admin.dist instead.`)
41
+
42
+ str = name === 'root' ? config.build?.path
43
+ : name === 'dist' ? config.dist?.path
44
+ : null
45
+ }
38
46
  if (!str) {
39
47
  throw new ControllerError(
40
48
  this,
41
- `Missing admin.${name}.path configuration.`
49
+ `Missing \`config.admin.${name}\` configuration.`
42
50
  )
43
51
  }
44
52
  return path.resolve(str)
@@ -85,8 +93,8 @@ export class AdminController extends Controller {
85
93
  this.koa = new Koa()
86
94
  this.koa.use(this.middleware())
87
95
  if (this.mode === 'development') {
88
- // Calling getPath() throws exception if admin.build.path is not defined:
89
- if (this.getPath('build')) {
96
+ // Calling getPath() throws exception if config.admin.root is not defined:
97
+ if (this.getPath('root')) {
90
98
  this.app.once('after:start', () => this.setupViteServer())
91
99
  }
92
100
  } else {
@@ -128,14 +136,14 @@ export class AdminController extends Controller {
128
136
  const development = this.mode === 'development'
129
137
 
130
138
  const cwd = path.resolve('.')
131
- const root = this.getPath('build')
139
+ const root = this.getPath('root')
132
140
  const base = `${this.url}/`
133
141
  const views = path.join(root, 'views')
134
142
 
135
143
  // Read `package.json` from the closest package.json, so we can emulate
136
144
  // ESM-style imports mappings in rollup / vite.
137
145
  const pkg = findUpSync('package.json', { cwd: root })
138
- const { imports } = JSON.parse(fs.readFileSync(pkg, 'utf8'))
146
+ const { imports = {} } = JSON.parse(fs.readFileSync(pkg, 'utf8'))
139
147
 
140
148
  return defineConfig(merge({
141
149
  root,
@@ -209,7 +217,7 @@ export class AdminController extends Controller {
209
217
  replacement: '#',
210
218
  customResolver(id) {
211
219
  for (const [find, replacement] of Object.entries(imports)) {
212
- picomatch.isMatch(id, find, {
220
+ picomatch.isMatch(id, find.replace('*', '**'), {
213
221
  capture: true,
214
222
  onMatch({ input, regex }) {
215
223
  const replacementPath = path.resolve(replacement)
@@ -176,7 +176,7 @@ export class CollectionController extends Controller {
176
176
  )
177
177
  }
178
178
 
179
- toCoreActions(actions) {
179
+ convertToCoreActions(actions) {
180
180
  // Mark action object and methods as core, so `Controller.processValues()`
181
181
  // can filter correctly.
182
182
  for (const action of Object.values(actions)) {
@@ -188,7 +188,7 @@ export class CollectionController extends Controller {
188
188
  return actions
189
189
  }
190
190
 
191
- collection = this.toCoreActions({
191
+ collection = this.convertToCoreActions({
192
192
  async get(ctx, modify) {
193
193
  const result = await this.execute(ctx, (query, trx) => {
194
194
  query.find(ctx.query, this.allowParam).modify(getModify(modify, trx))
@@ -235,7 +235,7 @@ export class CollectionController extends Controller {
235
235
  }
236
236
  })
237
237
 
238
- member = this.toCoreActions({
238
+ member = this.convertToCoreActions({
239
239
  async get(ctx, modify) {
240
240
  return this.execute(ctx, (query, trx) => query
241
241
  .findById(ctx.memberId)
@@ -6,8 +6,8 @@ import {
6
6
  ResponseError, WrappedError, ControllerError, AuthorizationError
7
7
  } from '../errors/index.js'
8
8
  import {
9
- getOwnProperty, getAllKeys, processHandlerParameters, describeFunction,
10
- formatJson, deprecate
9
+ getOwnProperty, getOwnKeys, getAllKeys, processHandlerParameters,
10
+ describeFunction, formatJson, deprecate
11
11
  } from '../utils/index.js'
12
12
  import {
13
13
  isObject, isString, isArray, isBoolean, isFunction, asArray, equals,
@@ -312,43 +312,44 @@ export class Controller {
312
312
  // NOTE: `handleAllow()` and `handleAuthorize()` are applied in sequence of
313
313
  // the `values` inheritance, from sub-class to base-class.
314
314
 
315
- const mergedAllow = {}
316
- const mergedAuthorize = {}
317
- let hasOwnAllow = false
315
+ let allowMap = {}
316
+ const authorizeMap = {}
318
317
 
319
- const excludeKey = key => ['allow', 'authorize'].includes(key)
318
+ const includeKey = key => !['allow', 'authorize'].includes(key)
320
319
 
321
320
  const handleAllow = (allow, current) => {
321
+ const getFilteredMap = keys =>
322
+ Object.fromEntries(keys.filter(includeKey).map(key => [key, true]))
323
+
322
324
  if (allow) {
323
- allow = asArray(allow)
324
- hasOwnAllow = true
325
- } else if (!hasOwnAllow) {
326
- // Only keep adding to the merged `allow` if we didn't already encounter
327
- // an own `allow` object further up the chain.
328
- allow = Object.keys(current)
329
- }
330
- if (allow) {
331
- if (allow.includes('*')) {
332
- allow = getAllKeys(current)
333
- }
334
- for (const key of allow) {
335
- if (!excludeKey(key)) {
336
- mergedAllow[key] = true
337
- }
325
+ // The controller action object provides its own allow setting:
326
+ // - Clear whatever has been collected in `mergedAllow` so far
327
+ // - Merge the `allow` setting with all the own keys of the object,
328
+ // unless:
329
+ // - If the allow setting includes '*', allow all keys of the object,
330
+ // even the inherited ones.
331
+ let keys = asArray(allow)
332
+ if (keys.includes('*')) {
333
+ keys = getAllKeys(current)
334
+ } else {
335
+ keys = [
336
+ ...keys,
337
+ ...getOwnKeys(current)
338
+ ]
338
339
  }
340
+ allowMap = getFilteredMap(keys) // Clear previous keys by overriding.
341
+ } else {
342
+ // The controller action object does not provide its own allow setting,
343
+ // so add its own keys to the already allowed inherited keys so far.
344
+ Object.assign(allowMap, getFilteredMap(getOwnKeys(current)))
339
345
  }
346
+ // console.log('allow', Object.keys(allowMap))
340
347
  }
341
348
 
342
349
  const handleAuthorize = authorize => {
343
350
  const add = (key, value) => {
344
- // Since we're walking up in the inheritance chain, only take on an
345
- // authorize setting for a given key if it wasn't already defined before
346
- if (
347
- key in values &&
348
- !(key in mergedAuthorize) &&
349
- !excludeKey(key)
350
- ) {
351
- mergedAuthorize[key] = value
351
+ if (key in values && includeKey(key)) {
352
+ authorizeMap[key] = value
352
353
  }
353
354
  }
354
355
 
@@ -365,20 +366,23 @@ export class Controller {
365
366
  }
366
367
  }
367
368
 
368
- // Process the `allow` and `authorize` settings in sequence of the `values`
369
- // inheritance, from sub-class to base-class.
369
+ // Process the `allow` and `authorize` settings in reversed sequence of the
370
+ // `values` inheritance, from base-class to sub-class.
371
+ const chain = []
370
372
  let current = values
371
373
  while (current !== Object.prototype && !current.hasOwnProperty('$core')) {
372
- handleAllow(getOwnProperty(current, 'allow'), current)
373
- handleAuthorize(getOwnProperty(current, 'authorize'))
374
+ chain.unshift(current)
374
375
  current = Object.getPrototypeOf(current)
375
376
  }
376
377
 
377
- // At the end of the chain, also support both settings on the controller-
378
- // level, and thus applied to all action objects in the controller.
379
- if (this.allow) {
380
- handleAllow(this.allow, values)
378
+ for (const current of chain) {
379
+ handleAllow(getOwnProperty(current, 'allow'), current)
380
+ handleAuthorize(getOwnProperty(current, 'authorize'))
381
381
  }
382
+
383
+ // At the end of the chain, also support authorize settings on the
384
+ // controller-level, and thus applied to all action objects in the
385
+ // controller.
382
386
  if (this.authorize) {
383
387
  handleAuthorize(this.authorize)
384
388
  }
@@ -387,7 +391,7 @@ export class Controller {
387
391
  // Create a filtered `values` object that only contains the allowed fields
388
392
  values: getAllKeys(values).reduce(
389
393
  (result, key) => {
390
- if (mergedAllow[key]) {
394
+ if (allowMap[key]) {
391
395
  result[key] = values[key]
392
396
  }
393
397
  return result
@@ -397,8 +401,8 @@ export class Controller {
397
401
  // `super` in handler functions.
398
402
  Object.create(Object.getPrototypeOf(values))
399
403
  ),
400
- allow: Object.keys(mergedAllow),
401
- authorize: mergedAuthorize
404
+ allow: Object.keys(allowMap),
405
+ authorize: authorizeMap
402
406
  }
403
407
  }
404
408
 
@@ -1,7 +1,9 @@
1
1
  import { asArray } from '@ditojs/utils'
2
2
 
3
+ export const getOwnKeys = Object.keys
4
+
3
5
  export function getAllKeys(object) {
4
- // Unlike `Object.keys()`, this returns all enumerable keys not just own ones.
6
+ // Unlike `getOwnKeys()`, this returns all enumerable keys not just own ones.
5
7
  const keys = []
6
8
  for (const key in object) {
7
9
  keys.push(key)