@ditojs/server 2.32.1 → 2.32.2

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": "2.32.1",
3
+ "version": "2.32.2",
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",
@@ -25,22 +25,22 @@
25
25
  "node >= 18"
26
26
  ],
27
27
  "dependencies": {
28
- "@ditojs/admin": "^2.32.1",
29
- "@ditojs/build": "^2.32.0",
30
- "@ditojs/router": "^2.32.0",
31
- "@ditojs/utils": "^2.32.0",
28
+ "@ditojs/admin": "^2.32.2",
29
+ "@ditojs/build": "^2.32.2",
30
+ "@ditojs/router": "^2.32.2",
31
+ "@ditojs/utils": "^2.32.2",
32
32
  "@koa/cors": "^5.0.0",
33
33
  "@koa/multer": "^3.0.2",
34
34
  "@originjs/vite-plugin-commonjs": "^1.0.3",
35
- "@vitejs/plugin-vue": "^5.1.3",
36
- "@vue/compiler-sfc": "3.5.2",
35
+ "@vitejs/plugin-vue": "^5.1.4",
36
+ "@vue/compiler-sfc": "^3.5.6",
37
37
  "ajv": "^8.17.1",
38
38
  "ajv-formats": "^2.1.1",
39
39
  "bcryptjs": "^2.4.3",
40
40
  "bytes": "^3.1.2",
41
41
  "data-uri-to-buffer": "^6.0.2",
42
42
  "eventemitter2": "^6.4.9",
43
- "file-type": "^19.4.1",
43
+ "file-type": "^19.5.0",
44
44
  "koa": "^2.15.3",
45
45
  "koa-bodyparser": "^4.4.1",
46
46
  "koa-compose": "^4.1.0",
@@ -67,10 +67,10 @@
67
67
  "pino-pretty": "^11.2.2",
68
68
  "pluralize": "^8.0.0",
69
69
  "repl": "^0.1.3",
70
- "type-fest": "^4.26.0",
70
+ "type-fest": "^4.26.1",
71
71
  "uuid": "^10.0.0",
72
- "vite": "^5.4.3",
73
- "vue": "^3.5.2"
72
+ "vite": "^5.4.6",
73
+ "vue": "^3.5.6"
74
74
  },
75
75
  "peerDependencies": {
76
76
  "@aws-sdk/client-s3": "^3.0.0",
@@ -87,11 +87,11 @@
87
87
  "@types/koa-session": "^6.4.5",
88
88
  "@types/koa-static": "^4.0.4",
89
89
  "@types/koa__cors": "^5.0.0",
90
- "@types/node": "^22.5.4",
90
+ "@types/node": "^22.5.5",
91
91
  "knex": "^3.1.0",
92
92
  "objection": "^3.1.4",
93
- "typescript": "^5.5.4"
93
+ "typescript": "^5.6.2"
94
94
  },
95
95
  "types": "types",
96
- "gitHead": "22008a15e26380fa7b226ca115f0643bfa88334d"
96
+ "gitHead": "e39e6be34ced2fea17083c0d8a2cb90d41aaf435"
97
97
  }
@@ -197,7 +197,9 @@ export class CollectionController extends Controller {
197
197
  collection = this.convertToCoreActions({
198
198
  async get(ctx, modify) {
199
199
  const result = await this.execute(ctx, (query, trx) => {
200
- query.find(ctx.query, this.allowParam).modify(getModify(modify, trx))
200
+ query
201
+ .find(ctx.filteredQuery, this.allowParam)
202
+ .modify(getModify(modify, trx))
201
203
  return this.isOneToOne ? query.first() : query
202
204
  })
203
205
  // This method doesn't always return an array:
@@ -210,7 +212,7 @@ export class CollectionController extends Controller {
210
212
  const count = await this.execute(ctx, (query, trx) =>
211
213
  query
212
214
  .ignoreScope()
213
- .find(ctx.query, this.allowParam)
215
+ .find(ctx.filteredQuery, this.allowParam)
214
216
  .modify(query => this.isOneToOne && query.throwIfNotFound())
215
217
  .modify(getModify(modify, trx))
216
218
  .modify(query => (this.unrelate ? query.unrelate() : query.delete()))
@@ -248,7 +250,7 @@ export class CollectionController extends Controller {
248
250
  return this.execute(ctx, (query, trx) =>
249
251
  query
250
252
  .findById(ctx.memberId)
251
- .find(ctx.query, this.allowParam)
253
+ .find(ctx.filteredQuery, this.allowParam)
252
254
  .throwIfNotFound()
253
255
  .modify(getModify(modify, trx))
254
256
  )
@@ -259,7 +261,7 @@ export class CollectionController extends Controller {
259
261
  query
260
262
  .ignoreScope()
261
263
  .findById(ctx.memberId)
262
- .find(ctx.query, this.allowParam)
264
+ .find(ctx.filteredQuery, this.allowParam)
263
265
  .throwIfNotFound()
264
266
  .modify(getModify(modify, trx))
265
267
  .modify(query => (this.unrelate ? query.unrelate() : query.delete()))
@@ -84,20 +84,33 @@ export default class ControllerAction {
84
84
  // - 'query': Use `ctx.request.query`, regardless of the action's method.
85
85
  // - 'body': Use `ctx.request.body`, regardless of the action's method.
86
86
  getParams(ctx, from = this.paramsName) {
87
- const value = from === 'path' ? ctx.params : ctx.request[from]
87
+ const params = from === 'path' ? ctx.params : ctx.request[from]
88
88
  // koa-bodyparser always sets an object, even when there is no body.
89
89
  // Detect this here and return null instead.
90
90
  const isNull = (
91
91
  from === 'body' &&
92
92
  ctx.request.headers['content-length'] === '0' &&
93
- Object.keys(value).length === 0
93
+ Object.keys(params).length === 0
94
94
  )
95
- return isNull ? null : value
95
+ return isNull ? null : params
96
96
  }
97
97
 
98
98
  async callAction(ctx) {
99
99
  const params = await this.validateParameters(ctx)
100
- const { args, member } = await this.collectArguments(ctx, params)
100
+ const { args, member } = await this.collectArguments(
101
+ ctx,
102
+ params
103
+ )
104
+ let filteredQuery = null
105
+ Object.defineProperty(ctx, 'filteredQuery', {
106
+ get: () => {
107
+ filteredQuery ??=
108
+ this.paramsName === 'query' ? this.filterParameters(params) : params
109
+ return filteredQuery
110
+ },
111
+ enumerable: false,
112
+ configurable: true
113
+ })
101
114
  await this.controller.handleAuthorization(this.authorization, ctx, member)
102
115
  const { identifier } = this
103
116
  await this.controller.emitHook(`before:${identifier}`, false, ctx, ...args)
@@ -119,11 +132,12 @@ export default class ControllerAction {
119
132
  if (!this.parameters.validate) {
120
133
  return null
121
134
  }
122
- // Since validation also performs coercion, create a clone of the params
123
- // so that this doesn't modify the data on `ctx`.
135
+ // Since validation also performs coercion, create a shallow clone of the
136
+ // params so that this doesn't modify the data on `ctx`.Actually used values
137
+ // themselves are deep cloned below.
124
138
  // NOTE: The data can be either an object or an array.
125
- const data = clone(this.getParams(ctx))
126
- let params = data || {}
139
+ const data = { ...this.getParams(ctx) }
140
+ let params = {}
127
141
  const { dataName } = this.parameters
128
142
  let unwrapRoot = false
129
143
  const errors = []
@@ -152,12 +166,14 @@ export default class ControllerAction {
152
166
  params = {}
153
167
  }
154
168
  params[paramName] = data
169
+ } else {
170
+ params[paramName] = clone(data[paramName])
155
171
  }
156
172
  if (from) {
157
173
  // Allow parameters to be 'borrowed' from other objects.
158
- const data = this.getParams(ctx, from)
174
+ const source = this.getParams(ctx, from)
159
175
  // See above for an explanation of `clone()`:
160
- params[paramName] = clone(wrapRoot ? data : data?.[paramName])
176
+ params[paramName] = clone(wrapRoot ? source : source?.[paramName])
161
177
  }
162
178
  try {
163
179
  const value = params[paramName]
@@ -263,6 +279,21 @@ export default class ControllerAction {
263
279
  return { args, member }
264
280
  }
265
281
 
282
+ filterParameters(params) {
283
+ const filtered = {}
284
+ const consumedNames = Object.fromEntries(
285
+ this.parameters.list
286
+ .filter(param => !!param.name)
287
+ .map(param => [param.name, true])
288
+ )
289
+ for (const [key, value] of Object.entries(params)) {
290
+ if (!consumedNames[key]) {
291
+ filtered[key] = value
292
+ }
293
+ }
294
+ return filtered
295
+ }
296
+
266
297
  coerceValue(type, value, modelOptions) {
267
298
  // See if param needs additional coercion:
268
299
  if (value && ['date', 'datetime', 'timestamp'].includes(type)) {