objection 3.6.0 → 3.7.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 (35) hide show
  1. package/lib/model/AjvValidator.js +33 -10
  2. package/lib/model/modelClone.js +0 -2
  3. package/lib/queryBuilder/QueryBuilder.js +56 -10
  4. package/lib/queryBuilder/RelationExpression.js +6 -4
  5. package/lib/queryBuilder/graph/GraphOptions.js +27 -55
  6. package/lib/queryBuilder/graph/GraphUpsert.js +22 -0
  7. package/lib/queryBuilder/graph/insert/GraphInsert.js +2 -0
  8. package/lib/queryBuilder/operations/InstanceUpdateOperation.js +6 -1
  9. package/lib/queryBuilder/operations/UpdateOperation.js +11 -0
  10. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeMsSqlOperation.js +3 -1
  11. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeSqliteOperation.js +4 -9
  12. package/lib/queryBuilder/parsers/relationExpressionParser.js +1 -1
  13. package/lib/queryBuilder/parsers/relationExpressionParser.pegjs +1 -1
  14. package/lib/queryBuilder/transformations/WrapMysqlModifySubqueryTransformation.js +4 -7
  15. package/lib/relations/Relation.js +1 -0
  16. package/lib/relations/RelationFindOperation.js +21 -39
  17. package/lib/relations/belongsToOne/BelongsToOneDeleteOperation.js +4 -2
  18. package/lib/relations/manyToMany/ManyToManyModifyMixin.js +39 -15
  19. package/lib/relations/manyToMany/ManyToManyMySqlModifyMixin.js +31 -42
  20. package/lib/relations/manyToMany/ManyToManyRelation.js +8 -16
  21. package/lib/relations/manyToMany/ManyToManySqliteModifyMixin.js +0 -13
  22. package/lib/relations/manyToMany/find/ManyToManyFindOperation.js +2 -3
  23. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateMySqlOperation.js +1 -5
  24. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperation.js +1 -5
  25. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperationBase.js +9 -0
  26. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateSqliteOperation.js +1 -5
  27. package/lib/relations/manyToMany/update/ManyToManyUpdateOperationBase.js +7 -1
  28. package/lib/utils/identifierMapping.js +8 -17
  29. package/lib/utils/objectUtils.js +38 -6
  30. package/lib/utils/parseFieldExpression.js +10 -3
  31. package/package.json +1 -1
  32. package/typings/objection/index.d.ts +46 -26
  33. package/lib/relations/manyToMany/ManyToManyPostgresModifyMixin.js +0 -27
  34. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelatePostgresOperation.js +0 -16
  35. package/lib/relations/manyToMany/update/ManyToManyUpdatePostgresOperation.js +0 -12
@@ -388,11 +388,43 @@ function mergeMaps(map1, map2) {
388
388
  return map;
389
389
  }
390
390
 
391
- // Clones `value` deeply, like lodash's `cloneDeep()`: objects keep their
392
- // prototype and get copies of their own enumerable properties, symbols
393
- // included. Arrays, dates, regular expressions, maps, sets, buffers, typed
394
- // arrays and array buffers are copied, circular references are kept.
395
- // Anything else, like functions and errors, is kept as it is.
391
+ // Returns the tag of `value` like `Object.prototype.toString()`, but ignores a
392
+ // `Symbol.toStringTag` that can be overwritten, like lodash's `baseGetTag()`.
393
+ // Objects that define a writable tag, like decimal.js numbers, are plain
394
+ // objects then, while built-ins like `URL` keep their own tag.
395
+ function getRawTag(value) {
396
+ const tag = Object.prototype.toString.call(value);
397
+
398
+ if (!(Symbol.toStringTag in value)) {
399
+ return tag;
400
+ }
401
+
402
+ const isOwn = Object.prototype.hasOwnProperty.call(value, Symbol.toStringTag);
403
+ const ownTag = value[Symbol.toStringTag];
404
+
405
+ try {
406
+ value[Symbol.toStringTag] = undefined;
407
+ } catch (err) {
408
+ return tag;
409
+ }
410
+
411
+ const rawTag = Object.prototype.toString.call(value);
412
+
413
+ if (isOwn) {
414
+ value[Symbol.toStringTag] = ownTag;
415
+ } else {
416
+ delete value[Symbol.toStringTag];
417
+ }
418
+
419
+ return rawTag;
420
+ }
421
+
422
+ // Clones `value` deeply, like lodash's `cloneDeep()`: plain objects and class
423
+ // instances keep their prototype and get copies of their own enumerable
424
+ // properties, symbols included. Arrays, dates, regular expressions, maps,
425
+ // sets, buffers, typed arrays and array buffers are copied, circular
426
+ // references are kept. Anything else, like functions, errors and built-ins
427
+ // with internal state like `URL`, is kept as it is.
396
428
  function cloneDeep(value, clones = new Map()) {
397
429
  if (!isObject(value)) {
398
430
  return value;
@@ -401,7 +433,7 @@ function cloneDeep(value, clones = new Map()) {
401
433
  }
402
434
 
403
435
  const Ctor = value.constructor;
404
- const tag = Object.prototype.toString.call(value);
436
+ const tag = getRawTag(value);
405
437
  let copy;
406
438
 
407
439
  if (tag === '[object Object]') {
@@ -66,10 +66,17 @@ function toPostgresJsonPath(access) {
66
66
  // `whereIn(ref(...), values)`), so the keys need to be escaped instead: single
67
67
  // quotes are doubled, and `?` is escaped so that knex doesn't read it as a
68
68
  // binding placeholder. Backslashes are always doubled by `toPostgresJsonPath()`,
69
- // so they can't escape the closing quote either.
69
+ // so they can't escape the closing quote either. But knex's Postgres dialect
70
+ // drops all backslashes in front of an escaped `?`, so if the path contains any,
71
+ // an escape string literal is used in which backslashes are written as `\134`.
70
72
  function toPostgresJsonPathLiteral(access) {
71
- const path = toPostgresJsonPath(access).replace(/'/g, "''").replace(/\?/g, '\\?');
72
- return `'${path}'`;
73
+ const path = toPostgresJsonPath(access).replace(/'/g, "''");
74
+
75
+ if (path.includes('\\?')) {
76
+ return `E'${path.replace(/\\/g, '\\134').replace(/\?/g, '\\?')}'`;
77
+ } else {
78
+ return `'${path.replace(/\?/g, '\\?')}'`;
79
+ }
73
80
  }
74
81
 
75
82
  module.exports = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "objection",
3
- "version": "3.6.0",
3
+ "version": "3.7.0",
4
4
  "description": "An SQL-friendly ORM for Node.js",
5
5
  "main": "lib/objection.js",
6
6
  "license": "MIT",
@@ -374,7 +374,8 @@ declare namespace Objection {
374
374
 
375
375
  /**
376
376
  * Converts the query builder NQB to the same result kind as QB:
377
- * array, single, maybe-single or page.
377
+ * array, single, maybe-single or page. Any other result type, e.g. after
378
+ * `castTo()` or `patch()`, falls back to QB, without narrowing.
378
379
  */
379
380
  type WithResultKind<QB extends AnyQueryBuilder, NQB extends AnyQueryBuilder> = [
380
381
  ResultType<QB>,
@@ -423,7 +424,9 @@ declare namespace Objection {
423
424
  ? I extends Model
424
425
  ? I[] | PartialModelObject<I>[]
425
426
  : Expression<T[K]>
426
- : Expression<T[K]>;
427
+ : NonNullable<T[K]> extends Model
428
+ ? Expression<T[K]> | PartialModelObject<NonNullable<T[K]>> // nullable to-one relations
429
+ : Expression<T[K]>;
427
430
  };
428
431
 
429
432
  /**
@@ -1000,14 +1003,36 @@ declare namespace Objection {
1000
1003
  includeTriggerModifications?: boolean;
1001
1004
  }
1002
1005
 
1006
+ type ReturningColumn = string | Raw | readonly (string | Raw)[];
1007
+
1008
+ /**
1009
+ * The arguments of `returning()`: any number of columns or column arrays,
1010
+ * optionally followed by an options object.
1011
+ */
1012
+ type ReturningArgs =
1013
+ ReturningColumn[] | [ReturningColumn, ...ReturningColumn[], ReturningOptions | undefined];
1014
+
1003
1015
  interface ReturningMethod {
1004
1016
  <QB extends AnyQueryBuilder>(
1005
1017
  this: QB,
1006
- column: string | Raw | (string | Raw)[] | readonly (string | Raw)[],
1007
- options?: ReturningOptions,
1018
+ ...args: ReturningArgs
1008
1019
  ): QB extends NumberQueryBuilder<QB> ? ArrayQueryBuilder<QB> : QB;
1009
1020
  }
1010
1021
 
1022
+ /**
1023
+ * With `returning()`, `relate()` returns the inserted join rows for
1024
+ * many-to-many relations, the updated related rows for has-many relations
1025
+ * and the updated owner rows for belongs-to-one relations. The typings can't
1026
+ * tell these apart, so the rows are typed as `any`.
1027
+ */
1028
+ interface RelateReturningMethod<M extends Model> {
1029
+ (...args: ReturningArgs): QueryBuilder<M, any[]>;
1030
+ }
1031
+
1032
+ type RelateQueryBuilder<QB extends AnyQueryBuilder> = {
1033
+ returning: RelateReturningMethod<ModelType<QB>>;
1034
+ } & NumberQueryBuilder<QB>;
1035
+
1011
1036
  interface TimeoutOptions {
1012
1037
  cancel: boolean;
1013
1038
  }
@@ -1060,6 +1085,7 @@ declare namespace Objection {
1060
1085
  export interface InsertGraphOptions {
1061
1086
  relate?: boolean | string | string[];
1062
1087
  allowRefs?: boolean;
1088
+ allowMarkers?: boolean;
1063
1089
  }
1064
1090
 
1065
1091
  interface InsertGraphMethod<M extends Model> {
@@ -1087,6 +1113,7 @@ declare namespace Objection {
1087
1113
  noRelate?: boolean | string | string[];
1088
1114
  noUnrelate?: boolean | string | string[];
1089
1115
  allowRefs?: boolean;
1116
+ allowMarkers?: boolean;
1090
1117
  }
1091
1118
 
1092
1119
  interface UpsertGraphMethod<M extends Model> {
@@ -1386,7 +1413,7 @@ declare namespace Objection {
1386
1413
 
1387
1414
  relate(
1388
1415
  ids: MaybeCompositeId | MaybeCompositeId[] | PartialModelObject<M> | PartialModelObject<M>[],
1389
- ): NumberQueryBuilder<this>;
1416
+ ): RelateQueryBuilder<this>;
1390
1417
 
1391
1418
  unrelate(): NumberQueryBuilder<this>;
1392
1419
  for(ids: ForIdValue | ForIdValue[]): this;
@@ -1718,34 +1745,25 @@ declare namespace Objection {
1718
1745
  constructor(config: AjvConfig);
1719
1746
  }
1720
1747
 
1721
- export interface SnakeCaseMappersOptions {
1748
+ /**
1749
+ * The options shared by `snakeCaseMappers` and `knexSnakeCaseMappers`.
1750
+ */
1751
+ export interface SnakeCaseOptions {
1722
1752
  upperCase?: boolean;
1723
1753
  underscoreBeforeDigits?: boolean;
1724
1754
  underscoreBetweenUppercaseLetters?: boolean;
1725
1755
  noDoubleUnderscores?: boolean;
1756
+ }
1757
+
1758
+ export interface SnakeCaseMappersOptions extends SnakeCaseOptions {
1726
1759
  /**
1727
1760
  * Only convert the column part of field expressions like
1728
1761
  * `jsonColumn:someKey` and keep their JSON keys as written.
1729
- * Only used by `snakeCaseMappers`.
1730
1762
  */
1731
1763
  preserveJsonKeys?: boolean;
1732
- /**
1733
- * Also convert the keys of plain objects one level down in the results,
1734
- * e.g. the rows of knex's `nestTables: true` option on MySQL. Also applies
1735
- * to JSON columns parsed by the driver.
1736
- * Only used by `knexSnakeCaseMappers`.
1737
- */
1738
- mapNestedKeys?: boolean;
1739
1764
  }
1740
1765
 
1741
- export interface KnexIdentifierMappingOptions {
1742
- /**
1743
- * Also convert the keys of plain objects one level down in the results,
1744
- * e.g. the rows of knex's `nestTables: true` option on MySQL. Also applies
1745
- * to JSON columns parsed by the driver.
1746
- */
1747
- mapNestedKeys?: boolean;
1748
- }
1766
+ export interface KnexSnakeCaseMappersOptions extends SnakeCaseOptions {}
1749
1767
 
1750
1768
  export interface ColumnNameMappers {
1751
1769
  parse(json: Pojo): Pojo;
@@ -1762,11 +1780,11 @@ declare namespace Objection {
1762
1780
  }
1763
1781
 
1764
1782
  export interface KnexSnakeCaseMappersFactory {
1765
- (options?: SnakeCaseMappersOptions): KnexMappers;
1783
+ (options?: KnexSnakeCaseMappersOptions): KnexMappers;
1766
1784
  }
1767
1785
 
1768
1786
  export interface KnexIdentifierMappingFactory {
1769
- (colToProp: Record<string, string>, options?: KnexIdentifierMappingOptions): KnexMappers;
1787
+ (colToProp: Record<string, string>): KnexMappers;
1770
1788
  }
1771
1789
 
1772
1790
  export type ValidationErrorType =
@@ -2040,14 +2058,16 @@ declare namespace Objection {
2040
2058
  static bindKnex<M>(this: M, trxOrKnex: TransactionOrKnex): M;
2041
2059
  static bindTransaction<M>(this: M, trxOrKnex: TransactionOrKnex): M;
2042
2060
 
2043
- static fetchGraph<M extends Model, const E extends RelationExpression<M>>(
2061
+ // E defaults to `string` so that `fetchGraph<Person>(...)` still compiles,
2062
+ // without narrowing, as TypeScript doesn't infer the remaining arguments.
2063
+ static fetchGraph<M extends Model, const E extends RelationExpression<M> = string>(
2044
2064
  this: ConstructorType<M>,
2045
2065
  modelOrObject: PartialModelObject<M>,
2046
2066
  expression: E,
2047
2067
  options?: FetchGraphOptions,
2048
2068
  ): SingleQueryBuilder<WithGraphModelQueryBuilder<M, E>>;
2049
2069
 
2050
- static fetchGraph<M extends Model, const E extends RelationExpression<M>>(
2070
+ static fetchGraph<M extends Model, const E extends RelationExpression<M> = string>(
2051
2071
  this: ConstructorType<M>,
2052
2072
  modelOrObject: PartialModelObject<M>[],
2053
2073
  expression: E,
@@ -1,27 +0,0 @@
1
- 'use strict';
2
-
3
- const { ManyToManyModifyMixin } = require('./ManyToManyModifyMixin');
4
-
5
- // Like `ManyToManySqliteModifyMixin`, this identifies the join rows to modify
6
- // by their physical row id, so that only the join rows that match the filters
7
- // are modified (#1853). `tableoid` makes `ctid` unique for partitioned tables.
8
- const ManyToManyPostgresModifyMixin = (Operation) => {
9
- return class extends ManyToManyModifyMixin(Operation) {
10
- applyModifyFilterForJoinTable(builder) {
11
- const joinTableOwnerRefs = this.relation.joinTableOwnerProp.refs(builder);
12
- const tableRef = builder.tableRefFor(this.relation.getJoinModelClass(builder));
13
- const rowIdRefs = [`${tableRef}.tableoid`, `${tableRef}.ctid`];
14
-
15
- const ownerValues = this.owner.getProps(this.relation);
16
- const subquery = this.modifyFilterSubquery.clone().select(rowIdRefs);
17
-
18
- return builder
19
- .whereInComposite(rowIdRefs, subquery)
20
- .whereInComposite(joinTableOwnerRefs, ownerValues);
21
- }
22
- };
23
- };
24
-
25
- module.exports = {
26
- ManyToManyPostgresModifyMixin,
27
- };
@@ -1,16 +0,0 @@
1
- 'use strict';
2
-
3
- const { ManyToManyUnrelateOperationBase } = require('./ManyToManyUnrelateOperationBase');
4
- const { ManyToManyPostgresModifyMixin } = require('../ManyToManyPostgresModifyMixin');
5
-
6
- class ManyToManyUnrelatePostgresOperation extends ManyToManyPostgresModifyMixin(
7
- ManyToManyUnrelateOperationBase,
8
- ) {
9
- get modifyMainQuery() {
10
- return false;
11
- }
12
- }
13
-
14
- module.exports = {
15
- ManyToManyUnrelatePostgresOperation,
16
- };
@@ -1,12 +0,0 @@
1
- 'use strict';
2
-
3
- const { ManyToManyUpdateOperationBase } = require('./ManyToManyUpdateOperationBase');
4
- const { ManyToManyPostgresModifyMixin } = require('../ManyToManyPostgresModifyMixin');
5
-
6
- class ManyToManyUpdatePostgresOperation extends ManyToManyPostgresModifyMixin(
7
- ManyToManyUpdateOperationBase,
8
- ) {}
9
-
10
- module.exports = {
11
- ManyToManyUpdatePostgresOperation,
12
- };