objection 3.1.2 → 3.1.4

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.
@@ -219,27 +219,27 @@ class Model {
219
219
  return this.constructor.traverseAsync(filterConstructor, this, callback);
220
220
  }
221
221
 
222
- $omitFromJson(props) {
222
+ $omitFromJson(...props) {
223
223
  if (arguments.length === 0) {
224
224
  return this.$$omitFromJson;
225
225
  } else {
226
226
  if (!this.hasOwnProperty('$$omitFromJson')) {
227
- defineNonEnumerableProperty(this, '$$omitFromJson', props);
228
- } else {
229
- this.$$omitFromJson = this.$$omitFromJson.concat(props);
227
+ defineNonEnumerableProperty(this, '$$omitFromJson', []);
230
228
  }
229
+ this.$$omitFromJson = this.$$omitFromJson.concat(asPropsArray(props));
230
+ return this;
231
231
  }
232
232
  }
233
233
 
234
- $omitFromDatabaseJson(props) {
234
+ $omitFromDatabaseJson(...props) {
235
235
  if (arguments.length === 0) {
236
236
  return this.$$omitFromDatabaseJson;
237
237
  } else {
238
238
  if (!this.hasOwnProperty('$$omitFromDatabaseJson')) {
239
- defineNonEnumerableProperty(this, '$$omitFromDatabaseJson', props);
240
- } else {
241
- this.$$omitFromDatabaseJson = this.$$omitFromDatabaseJson.concat(props);
239
+ defineNonEnumerableProperty(this, '$$omitFromDatabaseJson', []);
242
240
  }
241
+ this.$$omitFromDatabaseJson = this.$$omitFromDatabaseJson.concat(asPropsArray(props));
242
+ return this;
243
243
  }
244
244
  }
245
245
 
@@ -907,6 +907,20 @@ function getTraverseArgs(filterConstructor, models, traverser) {
907
907
  };
908
908
  }
909
909
 
910
+ function asPropsArray(props) {
911
+ if (props.length === 1) {
912
+ const arg = props[0];
913
+ if (Array.isArray(arg)) {
914
+ return arg;
915
+ } else if (arg && typeof arg === 'object') {
916
+ return Object.entries(arg)
917
+ .filter(([, value]) => value)
918
+ .map(([key]) => key);
919
+ }
920
+ }
921
+ return props;
922
+ }
923
+
910
924
  module.exports = {
911
925
  Model,
912
926
  };
@@ -4,6 +4,7 @@ class InternalOptions {
4
4
  constructor() {
5
5
  this.skipUndefined = false;
6
6
  this.keepImplicitJoinProps = false;
7
+ this.returnImmediatelyValue = undefined;
7
8
  this.isInternalQuery = false;
8
9
  this.debug = false;
9
10
  this.schema = undefined;
@@ -14,6 +15,7 @@ class InternalOptions {
14
15
 
15
16
  copy.skipUndefined = this.skipUndefined;
16
17
  copy.keepImplicitJoinProps = this.keepImplicitJoinProps;
18
+ copy.returnImmediatelyValue = this.returnImmediatelyValue;
17
19
  copy.isInternalQuery = this.isInternalQuery;
18
20
  copy.debug = this.debug;
19
21
  copy.schema = this.schema;
@@ -1119,7 +1119,24 @@ function afterExecute(builder, result) {
1119
1119
  return promise;
1120
1120
  }
1121
1121
 
1122
+ class ReturnImmediatelyException {
1123
+ constructor(value) {
1124
+ this.value = value;
1125
+ }
1126
+ }
1127
+
1128
+ function handleReturnImmediatelyValue(builder) {
1129
+ const { returnImmediatelyValue } = builder.internalOptions();
1130
+ if (returnImmediatelyValue !== undefined) {
1131
+ throw new ReturnImmediatelyException(returnImmediatelyValue);
1132
+ }
1133
+ }
1134
+
1122
1135
  function handleExecuteError(builder, err) {
1136
+ if (err instanceof ReturnImmediatelyException) {
1137
+ return Promise.resolve(err.value);
1138
+ }
1139
+
1123
1140
  let promise = Promise.reject(wrapError(err));
1124
1141
 
1125
1142
  builder.forEachOperation(true, (op) => {
@@ -1139,9 +1156,11 @@ function chainOperationHooks(promise, builder, hookName) {
1139
1156
 
1140
1157
  builder.forEachOperation(true, (op) => {
1141
1158
  if (op.hasHook(hookName)) {
1142
- promise = promise.then((result) =>
1143
- builder.callAsyncOperationMethod(op, hookName, [builder, result]),
1144
- );
1159
+ promise = promise.then((result) => {
1160
+ const res = builder.callAsyncOperationMethod(op, hookName, [builder, result]);
1161
+ handleReturnImmediatelyValue(builder);
1162
+ return res;
1163
+ });
1145
1164
  }
1146
1165
  });
1147
1166
 
@@ -1261,7 +1280,11 @@ async function chainHooks(promise, builder, func) {
1261
1280
  let promise = Promise.resolve(result);
1262
1281
 
1263
1282
  if (isFunction(func)) {
1264
- promise = promise.then((result) => func.call(builder, result, builder));
1283
+ promise = promise.then((result) => {
1284
+ const res = func.call(builder, result, builder);
1285
+ handleReturnImmediatelyValue(builder);
1286
+ return res;
1287
+ });
1265
1288
  } else if (Array.isArray(func)) {
1266
1289
  func.forEach((func) => {
1267
1290
  promise = chainHooks(promise, builder, func);
@@ -76,8 +76,8 @@ class ReferenceBuilder {
76
76
  const table = this.tableName
77
77
  ? this.tableName
78
78
  : this.modelClass
79
- ? builder.tableRefFor(this.modelClass)
80
- : null;
79
+ ? builder.tableRefFor(this.modelClass)
80
+ : null;
81
81
 
82
82
  if (table) {
83
83
  return `${table}.${this.column}`;
@@ -3,7 +3,7 @@
3
3
  const { getModel } = require('../../../model/getModel');
4
4
  const { GraphAction } = require('../GraphAction');
5
5
  const { isInternalProp } = require('../../../utils/internalPropUtils');
6
- const { union, difference, isObject, jsonEquals } = require('../../../utils/objectUtils');
6
+ const { difference, isObject, jsonEquals } = require('../../../utils/objectUtils');
7
7
  const promiseUtils = require('../../../utils/promiseUtils');
8
8
 
9
9
  class GraphPatchAction extends GraphAction {
@@ -33,7 +33,7 @@ class GraphPatchAction extends GraphAction {
33
33
 
34
34
  const handleUpdate = () => {
35
35
  const { changedProps, unchangedProps } = this._findChanges(node);
36
- const allProps = union(changedProps, unchangedProps);
36
+ const allProps = [...changedProps, ...unchangedProps];
37
37
 
38
38
  const propsToUpdate = difference(
39
39
  shouldPatch || shouldUpdate
@@ -59,12 +59,16 @@ class GraphPatchAction extends GraphAction {
59
59
  // See if the model defines a beforeUpdate or $beforeUpdate hook. If it does
60
60
  // not, we can check for updated properties now and drop out immediately if
61
61
  // there is nothing to update. Otherwise, we need to wait for the hook to be
62
- // called before calling handleUpdate. See issue #2233.
62
+ // called before calling handleUpdate, but only if the node contains changes
63
+ // that aren't id properties (relates). See issues #2233, #2605.
63
64
  const hasBeforeUpdate =
64
65
  node.obj.constructor.beforeUpdate !== Model.beforeUpdate ||
65
66
  node.obj.$beforeUpdate !== Model.prototype.$beforeUpdate;
66
67
 
67
- if (!hasBeforeUpdate && !handleUpdate()) {
68
+ if (
69
+ (hasBeforeUpdate && !this._hasNonIdPropertyChanges(node)) ||
70
+ (!hasBeforeUpdate && !handleUpdate())
71
+ ) {
68
72
  return null;
69
73
  }
70
74
 
@@ -82,24 +86,15 @@ class GraphPatchAction extends GraphAction {
82
86
  .copyFrom(builder, GraphAction.ReturningAllSelector);
83
87
 
84
88
  if (hasBeforeUpdate) {
85
- updateBuilder
86
- .context({
87
- runBefore: () => {
88
- // Call handleUpdate in the runBefore hook which runs after the
89
- // $beforeUpdate hook, allowing it to modify the object before the
90
- // updated properties are determined. See issue #2233.
91
- if (hasBeforeUpdate && !handleUpdate()) {
92
- throw new ReturnNullException();
93
- }
94
- },
95
- })
96
- .onError((error) => {
97
- if (error instanceof ReturnNullException) {
98
- return null;
99
- } else {
100
- return Promise.reject(error);
101
- }
102
- });
89
+ updateBuilder.internalContext().runBefore.push((result, builder) => {
90
+ // Call handleUpdate in the runBefore hook which runs after the
91
+ // $beforeUpdate hook, allowing it to modify the object before the
92
+ // updated properties are determined. See issue #2233.
93
+ if (hasBeforeUpdate && !handleUpdate()) {
94
+ builder.internalOptions().returnImmediatelyValue = null;
95
+ }
96
+ return result;
97
+ });
103
98
  }
104
99
 
105
100
  if (shouldPatch) {
@@ -216,6 +211,11 @@ class GraphPatchAction extends GraphAction {
216
211
  };
217
212
  }
218
213
 
214
+ _hasNonIdPropertyChanges(node) {
215
+ const idProps = node.modelClass.getIdPropertyArray();
216
+ return this._findChanges(node).changedProps.some((prop) => !idProps.includes(prop));
217
+ }
218
+
219
219
  _createBuilder(node) {
220
220
  if (node.parentEdge && !node.parentEdge.relation.isObjectionHasManyRelation) {
221
221
  return this._createRelatedBuilder(node);
@@ -235,8 +235,6 @@ class GraphPatchAction extends GraphAction {
235
235
  }
236
236
  }
237
237
 
238
- class ReturnNullException {}
239
-
240
238
  function childQueryOptions() {
241
239
  return {
242
240
  fork: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "objection",
3
- "version": "3.1.2",
3
+ "version": "3.1.4",
4
4
  "description": "An SQL-friendly ORM for Node.js",
5
5
  "main": "lib/objection.js",
6
6
  "license": "MIT",
@@ -53,7 +53,7 @@
53
53
  ],
54
54
  "types": "./typings/objection/index.d.ts",
55
55
  "dependencies": {
56
- "ajv": "^8.6.2",
56
+ "ajv": "^8.12.0",
57
57
  "ajv-formats": "^2.1.1",
58
58
  "db-errors": "^0.2.3"
59
59
  },
@@ -61,20 +61,20 @@
61
61
  "knex": ">=1.0.1"
62
62
  },
63
63
  "devDependencies": {
64
- "@types/node": "^20.1.7",
65
- "chai": "^4.3.4",
64
+ "@types/node": "^20.10.0",
65
+ "chai": "^4.3.10",
66
66
  "chai-subset": "^1.6.0",
67
- "eslint": "^8.1.0",
67
+ "eslint": "^8.54.0",
68
68
  "eslint-formatter-codeframe": "^7.32.1",
69
- "eslint-plugin-prettier": "^5.0.0",
69
+ "eslint-plugin-prettier": "^5.0.1",
70
70
  "expect.js": "^0.3.1",
71
- "knex": ">=1.0.1",
71
+ "knex": "^3.0.1",
72
72
  "mocha": "^10.2.0",
73
73
  "mysql": "^2.18.1",
74
- "pg": "^8.7.1",
75
- "prettier": "3.0.3",
76
- "sqlite3": "^5.0.2",
77
- "typescript": ">=4.5.4",
74
+ "pg": "^8.11.3",
75
+ "prettier": "3.2.5",
76
+ "sqlite3": "^5.1.6",
77
+ "typescript": ">=5.3.2",
78
78
  "vuepress": "1.9.10"
79
79
  }
80
80
  }
@@ -203,10 +203,10 @@ declare namespace Objection {
203
203
  [K in DataPropertyNames<T>]?: Defined<T[K]> extends Model
204
204
  ? T[K]
205
205
  : Defined<T[K]> extends Array<infer I>
206
- ? I extends Model
207
- ? I[]
208
- : Expression<T[K]>
209
- : Expression<T[K]>;
206
+ ? I extends Model
207
+ ? I[]
208
+ : Expression<T[K]>
209
+ : Expression<T[K]>;
210
210
  };
211
211
 
212
212
  /**
@@ -229,13 +229,14 @@ declare namespace Objection {
229
229
  }
230
230
  : never;
231
231
 
232
- type PartialModelGraphField<F> = Defined<F> extends Model
233
- ? PartialModelGraph<Defined<F>>
234
- : Defined<F> extends Array<infer I>
235
- ? I extends Model
236
- ? PartialModelGraph<I>[]
237
- : Expression<F>
238
- : Expression<F>;
232
+ type PartialModelGraphField<F> =
233
+ Defined<F> extends Model
234
+ ? PartialModelGraph<Defined<F>>
235
+ : Defined<F> extends Array<infer I>
236
+ ? I extends Model
237
+ ? PartialModelGraph<I>[]
238
+ : Expression<F>
239
+ : Expression<F>;
239
240
 
240
241
  /**
241
242
  * Extracts the property names (excluding relations) of a model class.
@@ -245,12 +246,12 @@ declare namespace Objection {
245
246
  [K in keyof T]?: Defined<T[K]> extends Model
246
247
  ? never
247
248
  : Defined<T[K]> extends Array<infer I>
248
- ? I extends Model
249
- ? never
250
- : K
251
- : T[K] extends Function
252
- ? never
253
- : K;
249
+ ? I extends Model
250
+ ? never
251
+ : K
252
+ : T[K] extends Function
253
+ ? never
254
+ : K;
254
255
  }[keyof T],
255
256
  undefined | 'QueryBuilderType'
256
257
  >;
@@ -263,10 +264,10 @@ declare namespace Objection {
263
264
  [K in keyof T]?: Defined<T[K]> extends Model
264
265
  ? K
265
266
  : Defined<T[K]> extends Array<infer I>
266
- ? I extends Model
267
- ? K
268
- : never
269
- : never;
267
+ ? I extends Model
268
+ ? K
269
+ : never
270
+ : never;
270
271
  }[keyof T]
271
272
  >;
272
273
 
@@ -277,10 +278,10 @@ declare namespace Objection {
277
278
  type RelatedQueryBuilder<T> = T extends Model
278
279
  ? SingleQueryBuilder<QueryBuilderType<T>>
279
280
  : T extends Array<infer I>
280
- ? I extends Model
281
- ? QueryBuilderType<I>
282
- : never
283
- : never;
281
+ ? I extends Model
282
+ ? QueryBuilderType<I>
283
+ : never
284
+ : never;
284
285
 
285
286
  /**
286
287
  * Just like RelatedQueryBuilder but always returns an array
@@ -290,10 +291,10 @@ declare namespace Objection {
290
291
  type ArrayRelatedQueryBuilder<T> = T extends Model
291
292
  ? QueryBuilderType<T>
292
293
  : T extends Array<infer I>
293
- ? I extends Model
294
- ? QueryBuilderType<I>
295
- : never
296
- : never;
294
+ ? I extends Model
295
+ ? QueryBuilderType<I>
296
+ : never
297
+ : never;
297
298
 
298
299
  /**
299
300
  * Gets the query builder type for a model type.
@@ -1418,9 +1419,9 @@ declare namespace Objection {
1418
1419
  table?: string;
1419
1420
  }
1420
1421
 
1421
- export type Constructor<TResult, TParams extends any[] = any[]> = new (
1422
- ...params: TParams
1423
- ) => TResult;
1422
+ export interface Constructor<T> {
1423
+ new (): T;
1424
+ }
1424
1425
 
1425
1426
  export interface ModelConstructor<M extends Model> extends Constructor<M> {}
1426
1427
 
@@ -1699,13 +1700,13 @@ declare namespace Objection {
1699
1700
  ): this;
1700
1701
 
1701
1702
  $set(obj: Pojo): this;
1702
- $omit(keys: string | string[] | { [key: string]: boolean }): this;
1703
- $pick(keys: string | string[] | { [key: string]: boolean }): this;
1704
1703
  $clone(opt?: CloneOptions): this;
1705
1704
  $traverse(filterConstructor: typeof Model, traverser: TraverserFunction): this;
1706
1705
  $traverse(traverser: TraverserFunction): this;
1707
1706
  $traverseAsync(filterConstructor: typeof Model, traverser: TraverserFunction): Promise<this>;
1708
1707
  $traverseAsync(traverser: TraverserFunction): Promise<this>;
1708
+ $omitFromJson(keys: string | string[] | { [key: string]: boolean }): this;
1709
+ $omitFromDatabaseJson(keys: string | string[] | { [key: string]: boolean }): this;
1709
1710
 
1710
1711
  $knex(): Knex;
1711
1712
  $transaction(): Knex;