objection 3.6.0 → 3.8.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 (60) hide show
  1. package/lib/model/AjvValidator.js +132 -52
  2. package/lib/model/modelClone.js +0 -2
  3. package/lib/queryBuilder/QueryBuilder.js +128 -24
  4. package/lib/queryBuilder/QueryBuilderOperationSupport.js +15 -3
  5. package/lib/queryBuilder/RelationExpression.js +6 -4
  6. package/lib/queryBuilder/StaticHookArguments.js +4 -1
  7. package/lib/queryBuilder/graph/GraphOptions.js +36 -79
  8. package/lib/queryBuilder/graph/GraphUpsert.js +35 -7
  9. package/lib/queryBuilder/graph/insert/GraphInsert.js +2 -0
  10. package/lib/queryBuilder/join/RelationJoiner.js +104 -12
  11. package/lib/queryBuilder/join/utils.js +2 -2
  12. package/lib/queryBuilder/operations/DelegateOperation.js +22 -0
  13. package/lib/queryBuilder/operations/FirstOperation.js +5 -3
  14. package/lib/queryBuilder/operations/InsertGraphOperation.js +1 -0
  15. package/lib/queryBuilder/operations/InstanceDeleteOperation.js +2 -1
  16. package/lib/queryBuilder/operations/InstanceFindOperation.js +2 -1
  17. package/lib/queryBuilder/operations/InstanceUpdateOperation.js +8 -2
  18. package/lib/queryBuilder/operations/LimitPerParentOperation.js +145 -0
  19. package/lib/queryBuilder/operations/QueryBuilderOperation.js +31 -0
  20. package/lib/queryBuilder/operations/UpdateAndFetchOperation.js +22 -5
  21. package/lib/queryBuilder/operations/UpdateOperation.js +40 -3
  22. package/lib/queryBuilder/operations/WhereGroupOperation.js +2 -8
  23. package/lib/queryBuilder/operations/eager/JoinEagerOperation.js +5 -0
  24. package/lib/queryBuilder/operations/eager/WhereInEagerOperation.js +28 -2
  25. package/lib/queryBuilder/operations/jsonApi/postgresJsonApi.js +2 -2
  26. package/lib/queryBuilder/operations/select/SelectOperation.js +8 -4
  27. package/lib/queryBuilder/operations/select/Selection.js +21 -20
  28. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeMsSqlOperation.js +3 -1
  29. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeSqliteOperation.js +4 -9
  30. package/lib/queryBuilder/parsers/relationExpressionParser.js +6 -4
  31. package/lib/queryBuilder/parsers/relationExpressionParser.pegjs +7 -3
  32. package/lib/queryBuilder/transformations/WrapMysqlModifySubqueryTransformation.js +4 -7
  33. package/lib/queryBuilder/whereGroups.js +35 -40
  34. package/lib/queryBuilder/withoutOrder.js +33 -0
  35. package/lib/relations/Relation.js +4 -0
  36. package/lib/relations/RelationFindOperation.js +30 -43
  37. package/lib/relations/belongsToOne/BelongsToOneDeleteOperation.js +4 -2
  38. package/lib/relations/manyToMany/ManyToManyModifyMixin.js +65 -19
  39. package/lib/relations/manyToMany/ManyToManyMySqlModifyMixin.js +31 -42
  40. package/lib/relations/manyToMany/ManyToManyRelation.js +15 -16
  41. package/lib/relations/manyToMany/ManyToManySqliteModifyMixin.js +0 -13
  42. package/lib/relations/manyToMany/find/ManyToManyFindOperation.js +9 -5
  43. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateMySqlOperation.js +1 -5
  44. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperation.js +1 -5
  45. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperationBase.js +9 -0
  46. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateSqliteOperation.js +1 -5
  47. package/lib/relations/manyToMany/update/ManyToManyUpdateMySqlOperation.js +16 -1
  48. package/lib/relations/manyToMany/update/ManyToManyUpdateOperationBase.js +92 -16
  49. package/lib/utils/identifierMapping.js +10 -19
  50. package/lib/utils/knexUtils.js +14 -0
  51. package/lib/utils/objectUtils.js +38 -6
  52. package/lib/utils/parseFieldExpression.js +22 -3
  53. package/lib/utils/warnings.js +26 -0
  54. package/lib/utils/wrapDbError.js +34 -20
  55. package/package.json +5 -4
  56. package/typings/objection/index.d.ts +54 -26
  57. package/lib/relations/manyToMany/ManyToManyPostgresModifyMixin.js +0 -27
  58. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelatePostgresOperation.js +0 -16
  59. package/lib/relations/manyToMany/update/ManyToManyUpdatePostgresOperation.js +0 -12
  60. package/lib/utils/deprecate.js +0 -21
@@ -2,35 +2,49 @@
2
2
 
3
3
  const { wrapError, DBError, UniqueViolationError } = require('db-errors');
4
4
 
5
- // db-errors only recognizes mssql unique key violations, but the same error
6
- // number (2627) is also used for primary key violations (#2688).
5
+ // db-errors only recognizes mssql unique key violations, but primary key
6
+ // violations have the same error number, with a different message (#2688).
7
7
  const MSSQL_PRIMARY_KEY_REGEX =
8
8
  /Violation of PRIMARY KEY constraint '(.+)'\. Cannot insert duplicate key in object '(.+)\.(.+)'\. The duplicate key value is \((.+)\)/;
9
9
 
10
10
  function wrapDbError(err) {
11
- const wrapped = wrapError(err);
12
-
13
- if (wrapped.constructor === DBError && wrapped.client === 'mssql') {
14
- const { nativeError } = wrapped;
15
- const match =
16
- nativeError.info &&
17
- nativeError.info.number === 2627 &&
18
- MSSQL_PRIMARY_KEY_REGEX.exec(nativeError.message);
19
-
20
- if (match) {
21
- return new UniqueViolationError({
22
- nativeError,
23
- client: 'mssql',
24
- table: match[3],
25
- schema: match[2],
26
- constraint: match[1],
27
- });
28
- }
11
+ if (!isTediousError(err)) {
12
+ return wrapError(err);
13
+ }
14
+
15
+ // db-errors parses the errors of the `mssql` package, which wraps the errors
16
+ // of tedious and keeps their number and class in `originalError.info`. knex
17
+ // uses tedious directly, so its errors are passed on in that shape.
18
+ const wrapped = wrapError({
19
+ code: err.code,
20
+ originalError: { message: err.message, info: { number: err.number, class: err.class } },
21
+ });
22
+
23
+ if (!(wrapped instanceof DBError)) {
24
+ return err;
25
+ }
26
+
27
+ wrapped.nativeError = err;
28
+
29
+ const match = wrapped.constructor === DBError && MSSQL_PRIMARY_KEY_REGEX.exec(err.message);
30
+
31
+ if (match) {
32
+ return new UniqueViolationError({
33
+ nativeError: err,
34
+ client: 'mssql',
35
+ table: match[3],
36
+ schema: match[2],
37
+ constraint: match[1],
38
+ });
29
39
  }
30
40
 
31
41
  return wrapped;
32
42
  }
33
43
 
44
+ function isTediousError(err) {
45
+ return err?.code === 'EREQUEST' && Number.isInteger(err.number);
46
+ }
47
+
34
48
  module.exports = {
35
49
  wrapDbError,
36
50
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "objection",
3
- "version": "3.6.0",
3
+ "version": "3.8.0",
4
4
  "description": "An SQL-friendly ORM for Node.js",
5
5
  "main": "lib/objection.js",
6
6
  "license": "MIT",
@@ -9,7 +9,7 @@
9
9
  "test:fast": "mocha --slow 10 --timeout 15000 --reporter spec --recursive tests --bail --exclude \"tests/unit/relations/files/**\"",
10
10
  "test:typings": "tsc && prettier --check \"typings/**/*.ts\" \"tests/ts/**/*.ts\"",
11
11
  "db:up": "docker compose up -d --wait",
12
- "db:down": "docker compose down -v",
12
+ "db:down": "docker compose --profile mssql down -v",
13
13
  "build:parsers": "npx --yes peggy@5.1.0 --format commonjs -o lib/queryBuilder/parsers/jsonFieldExpressionParser.js lib/queryBuilder/parsers/jsonFieldExpressionParser.pegjs && npx --yes peggy@5.1.0 --format commonjs -o lib/queryBuilder/parsers/relationExpressionParser.js lib/queryBuilder/parsers/relationExpressionParser.pegjs && prettier --write \"lib/queryBuilder/parsers/*Parser.js\"",
14
14
  "prettier": "prettier --write \"{examples,lib,tests,testUtils,typings,doc}/**/*.{js,ts}\"",
15
15
  "eslint": "eslint --format codeframe \"examples/**/*.js\" \"lib/**/*.js\" \"tests/**/*.js\" \"testUtils/**/*.js\" \"*.js\"",
@@ -66,7 +66,7 @@
66
66
  "db-errors": "^0.2.3"
67
67
  },
68
68
  "peerDependencies": {
69
- "knex": ">=2.3.0"
69
+ "knex": ">=2.3.0 <4"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/node": "^22.15.3",
@@ -82,7 +82,8 @@
82
82
  "pg": "^8.15.6",
83
83
  "prettier": "3.9.9",
84
84
  "sqlite3": "^5.1.7",
85
- "typescript": ">=5.8.3",
85
+ "tedious": "^20.3.7",
86
+ "typescript": "^5.8.3",
86
87
  "vitepress": "1.6.4"
87
88
  }
88
89
  }
@@ -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,39 @@ 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. MySQL has no
1026
+ * `returning`: there, many-to-many relations return the join rows built from
1027
+ * the input, without columns generated by the database, and the other
1028
+ * relations still return the number of affected rows. The typings can't tell
1029
+ * these apart, so the rows are typed as `any`.
1030
+ */
1031
+ interface RelateReturningMethod<M extends Model> {
1032
+ (...args: ReturningArgs): QueryBuilder<M, any[]>;
1033
+ }
1034
+
1035
+ type RelateQueryBuilder<QB extends AnyQueryBuilder> = {
1036
+ returning: RelateReturningMethod<ModelType<QB>>;
1037
+ } & NumberQueryBuilder<QB>;
1038
+
1011
1039
  interface TimeoutOptions {
1012
1040
  cancel: boolean;
1013
1041
  }
@@ -1060,6 +1088,11 @@ declare namespace Objection {
1060
1088
  export interface InsertGraphOptions {
1061
1089
  relate?: boolean | string | string[];
1062
1090
  allowRefs?: boolean;
1091
+ /**
1092
+ * @deprecated Has no effect, since `insertGraph()` rejects `#unrelate` and
1093
+ * `#delete` markers. Removed in objection 4.0.
1094
+ */
1095
+ allowMarkers?: boolean;
1063
1096
  }
1064
1097
 
1065
1098
  interface InsertGraphMethod<M extends Model> {
@@ -1087,6 +1120,7 @@ declare namespace Objection {
1087
1120
  noRelate?: boolean | string | string[];
1088
1121
  noUnrelate?: boolean | string | string[];
1089
1122
  allowRefs?: boolean;
1123
+ allowMarkers?: boolean;
1090
1124
  }
1091
1125
 
1092
1126
  interface UpsertGraphMethod<M extends Model> {
@@ -1113,6 +1147,7 @@ declare namespace Objection {
1113
1147
  aliases?: { [key: string]: string };
1114
1148
  joinOperation?: string;
1115
1149
  maxBatchSize?: number;
1150
+ limitPerParent?: boolean;
1116
1151
  }
1117
1152
 
1118
1153
  export interface WithGraphOptions extends GraphOptions {
@@ -1386,7 +1421,7 @@ declare namespace Objection {
1386
1421
 
1387
1422
  relate(
1388
1423
  ids: MaybeCompositeId | MaybeCompositeId[] | PartialModelObject<M> | PartialModelObject<M>[],
1389
- ): NumberQueryBuilder<this>;
1424
+ ): RelateQueryBuilder<this>;
1390
1425
 
1391
1426
  unrelate(): NumberQueryBuilder<this>;
1392
1427
  for(ids: ForIdValue | ForIdValue[]): this;
@@ -1718,34 +1753,25 @@ declare namespace Objection {
1718
1753
  constructor(config: AjvConfig);
1719
1754
  }
1720
1755
 
1721
- export interface SnakeCaseMappersOptions {
1756
+ /**
1757
+ * The options shared by `snakeCaseMappers` and `knexSnakeCaseMappers`.
1758
+ */
1759
+ export interface SnakeCaseOptions {
1722
1760
  upperCase?: boolean;
1723
1761
  underscoreBeforeDigits?: boolean;
1724
1762
  underscoreBetweenUppercaseLetters?: boolean;
1725
1763
  noDoubleUnderscores?: boolean;
1764
+ }
1765
+
1766
+ export interface SnakeCaseMappersOptions extends SnakeCaseOptions {
1726
1767
  /**
1727
1768
  * Only convert the column part of field expressions like
1728
1769
  * `jsonColumn:someKey` and keep their JSON keys as written.
1729
- * Only used by `snakeCaseMappers`.
1730
1770
  */
1731
1771
  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
1772
  }
1740
1773
 
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
- }
1774
+ export interface KnexSnakeCaseMappersOptions extends SnakeCaseOptions {}
1749
1775
 
1750
1776
  export interface ColumnNameMappers {
1751
1777
  parse(json: Pojo): Pojo;
@@ -1762,11 +1788,11 @@ declare namespace Objection {
1762
1788
  }
1763
1789
 
1764
1790
  export interface KnexSnakeCaseMappersFactory {
1765
- (options?: SnakeCaseMappersOptions): KnexMappers;
1791
+ (options?: KnexSnakeCaseMappersOptions): KnexMappers;
1766
1792
  }
1767
1793
 
1768
1794
  export interface KnexIdentifierMappingFactory {
1769
- (colToProp: Record<string, string>, options?: KnexIdentifierMappingOptions): KnexMappers;
1795
+ (colToProp: Record<string, string>): KnexMappers;
1770
1796
  }
1771
1797
 
1772
1798
  export type ValidationErrorType =
@@ -2040,14 +2066,16 @@ declare namespace Objection {
2040
2066
  static bindKnex<M>(this: M, trxOrKnex: TransactionOrKnex): M;
2041
2067
  static bindTransaction<M>(this: M, trxOrKnex: TransactionOrKnex): M;
2042
2068
 
2043
- static fetchGraph<M extends Model, const E extends RelationExpression<M>>(
2069
+ // E defaults to `string` so that `fetchGraph<Person>(...)` still compiles,
2070
+ // without narrowing, as TypeScript doesn't infer the remaining arguments.
2071
+ static fetchGraph<M extends Model, const E extends RelationExpression<M> = string>(
2044
2072
  this: ConstructorType<M>,
2045
2073
  modelOrObject: PartialModelObject<M>,
2046
2074
  expression: E,
2047
2075
  options?: FetchGraphOptions,
2048
2076
  ): SingleQueryBuilder<WithGraphModelQueryBuilder<M, E>>;
2049
2077
 
2050
- static fetchGraph<M extends Model, const E extends RelationExpression<M>>(
2078
+ static fetchGraph<M extends Model, const E extends RelationExpression<M> = string>(
2051
2079
  this: ConstructorType<M>,
2052
2080
  modelOrObject: PartialModelObject<M>[],
2053
2081
  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
- };
@@ -1,21 +0,0 @@
1
- 'use strict';
2
-
3
- const LOGGED_DEPRECATIONS = new Set();
4
-
5
- function deprecate(message) {
6
- // Only log deprecation messages once.
7
- if (!LOGGED_DEPRECATIONS.has(message)) {
8
- LOGGED_DEPRECATIONS.add(message);
9
- console.warn(message);
10
- }
11
- }
12
-
13
- // Only meant for tests that need to see a deprecation message again.
14
- function resetDeprecations() {
15
- LOGGED_DEPRECATIONS.clear();
16
- }
17
-
18
- module.exports = {
19
- deprecate,
20
- resetDeprecations,
21
- };