@abedmurrar/objection 0.0.0-stage → 4.0.0-alpha.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 (190) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +69 -2
  3. package/lib/.eslintrc.json +8 -0
  4. package/lib/initialize.js +14 -0
  5. package/lib/model/AjvValidator.js +325 -0
  6. package/lib/model/Model.js +942 -0
  7. package/lib/model/ModifierNotFoundError.js +12 -0
  8. package/lib/model/NotFoundError.js +23 -0
  9. package/lib/model/RelationDoesNotExistError.js +14 -0
  10. package/lib/model/ValidationError.js +54 -0
  11. package/lib/model/Validator.js +26 -0
  12. package/lib/model/getModel.js +9 -0
  13. package/lib/model/graph/ModelGraph.js +80 -0
  14. package/lib/model/graph/ModelGraphBuilder.js +241 -0
  15. package/lib/model/graph/ModelGraphEdge.js +52 -0
  16. package/lib/model/graph/ModelGraphNode.js +240 -0
  17. package/lib/model/inheritModel.js +28 -0
  18. package/lib/model/modelBindKnex.js +83 -0
  19. package/lib/model/modelClone.js +121 -0
  20. package/lib/model/modelColPropMap.js +34 -0
  21. package/lib/model/modelId.js +62 -0
  22. package/lib/model/modelJsonAttributes.js +92 -0
  23. package/lib/model/modelParseRelations.js +82 -0
  24. package/lib/model/modelQueryProps.js +147 -0
  25. package/lib/model/modelSet.js +136 -0
  26. package/lib/model/modelTableMetadata.js +83 -0
  27. package/lib/model/modelToJson.js +187 -0
  28. package/lib/model/modelUtils.js +47 -0
  29. package/lib/model/modelValidate.js +48 -0
  30. package/lib/model/modelValues.js +126 -0
  31. package/lib/model/modelVisitor.js +41 -0
  32. package/lib/objection.js +114 -0
  33. package/lib/queryBuilder/FunctionBuilder.js +339 -0
  34. package/lib/queryBuilder/InternalOptions.js +29 -0
  35. package/lib/queryBuilder/JoinBuilder.js +157 -0
  36. package/lib/queryBuilder/QueryBuilder.js +1398 -0
  37. package/lib/queryBuilder/QueryBuilderBase.js +884 -0
  38. package/lib/queryBuilder/QueryBuilderContext.js +27 -0
  39. package/lib/queryBuilder/QueryBuilderContextBase.js +35 -0
  40. package/lib/queryBuilder/QueryBuilderOperationSupport.js +558 -0
  41. package/lib/queryBuilder/QueryBuilderUserContext.js +31 -0
  42. package/lib/queryBuilder/RawBuilder.js +93 -0
  43. package/lib/queryBuilder/ReferenceBuilder.js +284 -0
  44. package/lib/queryBuilder/RelationExpression.js +431 -0
  45. package/lib/queryBuilder/StaticHookArguments.js +134 -0
  46. package/lib/queryBuilder/ValueBuilder.js +127 -0
  47. package/lib/queryBuilder/expressions.js +151 -0
  48. package/lib/queryBuilder/functions/mysql.js +817 -0
  49. package/lib/queryBuilder/graph/GraphAction.js +69 -0
  50. package/lib/queryBuilder/graph/GraphData.js +14 -0
  51. package/lib/queryBuilder/graph/GraphFetcher.js +191 -0
  52. package/lib/queryBuilder/graph/GraphNodeDbExistence.js +184 -0
  53. package/lib/queryBuilder/graph/GraphOperation.js +27 -0
  54. package/lib/queryBuilder/graph/GraphOptions.js +207 -0
  55. package/lib/queryBuilder/graph/GraphUpsert.js +305 -0
  56. package/lib/queryBuilder/graph/delete/GraphDelete.js +20 -0
  57. package/lib/queryBuilder/graph/delete/GraphDeleteAction.js +74 -0
  58. package/lib/queryBuilder/graph/insert/GraphInsert.js +145 -0
  59. package/lib/queryBuilder/graph/insert/GraphInsertAction.js +225 -0
  60. package/lib/queryBuilder/graph/insert/JoinRowGraphInsertAction.js +92 -0
  61. package/lib/queryBuilder/graph/patch/GraphPatch.js +20 -0
  62. package/lib/queryBuilder/graph/patch/GraphPatchAction.js +258 -0
  63. package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsert.js +31 -0
  64. package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsertAction.js +126 -0
  65. package/lib/queryBuilder/join/JoinResultColumn.js +23 -0
  66. package/lib/queryBuilder/join/JoinResultParser.js +119 -0
  67. package/lib/queryBuilder/join/RelationJoiner.js +341 -0
  68. package/lib/queryBuilder/join/TableNode.js +163 -0
  69. package/lib/queryBuilder/join/TableTree.js +85 -0
  70. package/lib/queryBuilder/join/utils.js +25 -0
  71. package/lib/queryBuilder/operations/DelegateOperation.js +141 -0
  72. package/lib/queryBuilder/operations/DeleteOperation.js +51 -0
  73. package/lib/queryBuilder/operations/FindByIdOperation.js +37 -0
  74. package/lib/queryBuilder/operations/FindByIdsOperation.js +29 -0
  75. package/lib/queryBuilder/operations/FindOperation.js +164 -0
  76. package/lib/queryBuilder/operations/FirstOperation.js +27 -0
  77. package/lib/queryBuilder/operations/FromOperation.js +110 -0
  78. package/lib/queryBuilder/operations/InsertAndFetchOperation.js +49 -0
  79. package/lib/queryBuilder/operations/InsertGraphAndFetchOperation.js +45 -0
  80. package/lib/queryBuilder/operations/InsertGraphOperation.js +108 -0
  81. package/lib/queryBuilder/operations/InsertOperation.js +206 -0
  82. package/lib/queryBuilder/operations/InstanceDeleteOperation.js +46 -0
  83. package/lib/queryBuilder/operations/InstanceFindOperation.js +21 -0
  84. package/lib/queryBuilder/operations/InstanceInsertOperation.js +24 -0
  85. package/lib/queryBuilder/operations/InstanceUpdateOperation.js +59 -0
  86. package/lib/queryBuilder/operations/JoinRelatedOperation.js +103 -0
  87. package/lib/queryBuilder/operations/KnexOperation.js +14 -0
  88. package/lib/queryBuilder/operations/MergeOperation.js +56 -0
  89. package/lib/queryBuilder/operations/ObjectionToKnexConvertingOperation.js +192 -0
  90. package/lib/queryBuilder/operations/OnBuildKnexOperation.js +29 -0
  91. package/lib/queryBuilder/operations/OnBuildOperation.js +29 -0
  92. package/lib/queryBuilder/operations/OnErrorOperation.js +29 -0
  93. package/lib/queryBuilder/operations/QueryBuilderOperation.js +297 -0
  94. package/lib/queryBuilder/operations/RangeOperation.js +47 -0
  95. package/lib/queryBuilder/operations/RelateOperation.js +27 -0
  96. package/lib/queryBuilder/operations/ReturningOperation.js +30 -0
  97. package/lib/queryBuilder/operations/RunAfterOperation.js +29 -0
  98. package/lib/queryBuilder/operations/RunBeforeOperation.js +29 -0
  99. package/lib/queryBuilder/operations/UnrelateOperation.js +23 -0
  100. package/lib/queryBuilder/operations/UpdateAndFetchOperation.js +80 -0
  101. package/lib/queryBuilder/operations/UpdateOperation.js +155 -0
  102. package/lib/queryBuilder/operations/UpsertGraphAndFetchOperation.js +45 -0
  103. package/lib/queryBuilder/operations/UpsertGraphOperation.js +66 -0
  104. package/lib/queryBuilder/operations/WhereCompositeOperation.js +57 -0
  105. package/lib/queryBuilder/operations/WhereInOperation.js +48 -0
  106. package/lib/queryBuilder/operations/eager/EagerOperation.js +61 -0
  107. package/lib/queryBuilder/operations/eager/JoinEagerOperation.js +51 -0
  108. package/lib/queryBuilder/operations/eager/NaiveEagerOperation.js +13 -0
  109. package/lib/queryBuilder/operations/eager/WhereInEagerOperation.js +285 -0
  110. package/lib/queryBuilder/operations/jsonApi/WhereJsonHasOperation.js +36 -0
  111. package/lib/queryBuilder/operations/jsonApi/WhereJsonNotObjectOperation.js +53 -0
  112. package/lib/queryBuilder/operations/jsonApi/WhereJsonOperation.js +31 -0
  113. package/lib/queryBuilder/operations/jsonApi/WhereJsonOverlapsOperation.js +31 -0
  114. package/lib/queryBuilder/operations/jsonApi/mysqlJsonApi.js +167 -0
  115. package/lib/queryBuilder/operations/jsonApi/postgresJsonApi.js +164 -0
  116. package/lib/queryBuilder/operations/select/SelectOperation.js +66 -0
  117. package/lib/queryBuilder/operations/select/Selection.js +123 -0
  118. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeMsSqlOperation.js +117 -0
  119. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeOperation.js +100 -0
  120. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeSqliteOperation.js +104 -0
  121. package/lib/queryBuilder/parsers/jsonFieldExpressionParser.js +886 -0
  122. package/lib/queryBuilder/parsers/jsonFieldExpressionParser.pegjs +97 -0
  123. package/lib/queryBuilder/parsers/relationExpressionParser.js +1042 -0
  124. package/lib/queryBuilder/parsers/relationExpressionParser.pegjs +141 -0
  125. package/lib/queryBuilder/transformations/CompositeQueryTransformation.js +22 -0
  126. package/lib/queryBuilder/transformations/QueryTransformation.js +11 -0
  127. package/lib/queryBuilder/transformations/WrapMysqlModifySubqueryTransformation.js +71 -0
  128. package/lib/queryBuilder/transformations/index.js +15 -0
  129. package/lib/queryBuilder/whereScopes.js +227 -0
  130. package/lib/relations/Relation.js +401 -0
  131. package/lib/relations/RelationDeleteOperation.js +29 -0
  132. package/lib/relations/RelationFindOperation.js +149 -0
  133. package/lib/relations/RelationInsertOperation.js +33 -0
  134. package/lib/relations/RelationOwner.js +236 -0
  135. package/lib/relations/RelationProperty.js +247 -0
  136. package/lib/relations/RelationUpdateOperation.js +30 -0
  137. package/lib/relations/belongsToOne/BelongsToOneDeleteOperation.js +23 -0
  138. package/lib/relations/belongsToOne/BelongsToOneInsertOperation.js +44 -0
  139. package/lib/relations/belongsToOne/BelongsToOneRelateOperation.js +50 -0
  140. package/lib/relations/belongsToOne/BelongsToOneRelation.js +53 -0
  141. package/lib/relations/belongsToOne/BelongsToOneUnrelateOperation.js +61 -0
  142. package/lib/relations/hasMany/HasManyInsertOperation.js +61 -0
  143. package/lib/relations/hasMany/HasManyRelateOperation.js +64 -0
  144. package/lib/relations/hasMany/HasManyRelation.js +49 -0
  145. package/lib/relations/hasMany/HasManyUnrelateOperation.js +33 -0
  146. package/lib/relations/hasOne/HasOneRelation.js +13 -0
  147. package/lib/relations/hasOneThrough/HasOneThroughRelation.js +13 -0
  148. package/lib/relations/manyToMany/ManyToManyModifyMixin.js +105 -0
  149. package/lib/relations/manyToMany/ManyToManyRelation.js +507 -0
  150. package/lib/relations/manyToMany/ManyToManySqliteModifyMixin.js +36 -0
  151. package/lib/relations/manyToMany/delete/ManyToManyDeleteOperation.js +10 -0
  152. package/lib/relations/manyToMany/delete/ManyToManyDeleteOperationBase.js +26 -0
  153. package/lib/relations/manyToMany/delete/ManyToManyDeleteSqliteOperation.js +12 -0
  154. package/lib/relations/manyToMany/find/ManyToManyFindOperation.js +105 -0
  155. package/lib/relations/manyToMany/insert/ManyToManyInsertOperation.js +52 -0
  156. package/lib/relations/manyToMany/relate/ManyToManyRelateOperation.js +81 -0
  157. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperation.js +14 -0
  158. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperationBase.js +34 -0
  159. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateSqliteOperation.js +16 -0
  160. package/lib/relations/manyToMany/update/ManyToManyUpdateOperation.js +10 -0
  161. package/lib/relations/manyToMany/update/ManyToManyUpdateOperationBase.js +82 -0
  162. package/lib/relations/manyToMany/update/ManyToManyUpdateSqliteOperation.js +12 -0
  163. package/lib/transaction.js +96 -0
  164. package/lib/utils/assert.js +25 -0
  165. package/lib/utils/buildUtils.js +21 -0
  166. package/lib/utils/classUtils.js +13 -0
  167. package/lib/utils/clone.js +2329 -0
  168. package/lib/utils/createModifier.js +42 -0
  169. package/lib/utils/deprecate.js +15 -0
  170. package/lib/utils/identifierMapping.js +227 -0
  171. package/lib/utils/internalPropUtils.js +11 -0
  172. package/lib/utils/knexUtils.js +76 -0
  173. package/lib/utils/mixin.js +25 -0
  174. package/lib/utils/mysqlJsonPath.js +55 -0
  175. package/lib/utils/normalizeIds.js +144 -0
  176. package/lib/utils/objectUtils.js +416 -0
  177. package/lib/utils/parseFieldExpression.js +51 -0
  178. package/lib/utils/postgresJsonPath.js +110 -0
  179. package/lib/utils/promiseUtils/after.js +17 -0
  180. package/lib/utils/promiseUtils/afterReturn.js +17 -0
  181. package/lib/utils/promiseUtils/index.js +17 -0
  182. package/lib/utils/promiseUtils/isPromise.js +11 -0
  183. package/lib/utils/promiseUtils/map.js +110 -0
  184. package/lib/utils/promiseUtils/mapAfterAllReturn.js +29 -0
  185. package/lib/utils/promiseUtils/try.js +22 -0
  186. package/lib/utils/resolveModel.js +107 -0
  187. package/lib/utils/tmpColumnUtils.js +16 -0
  188. package/package.json +86 -4
  189. package/typings/objection/index.d.ts +4104 -0
  190. package/typings/objection/relation-expression.d.ts +237 -0
package/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Sami Koskimäki
4
+ Copyright (c) 2026 Abed Murrar
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
23
+
package/README.md CHANGED
@@ -1,3 +1,70 @@
1
- # Temporary Holding Version
1
+ [![Tests](https://github.com/abedmurrar/objection.js/actions/workflows/test.yml/badge.svg)](https://github.com/abedmurrar/objection.js/actions/workflows/test.yml)
2
+ [![npm](https://img.shields.io/npm/v/@abedmurrar/objection/next)](https://www.npmjs.com/package/@abedmurrar/objection)
2
3
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
4
+ > **`@abedmurrar/objection`** is a fork of [objection.js](https://github.com/Vincit/objection.js) 3.1.5. It adds:
5
+ >
6
+ > - **Strict TypeScript types.** Query results contain exactly the relations that were loaded, relation expressions and modifier names are checked, and `Model.ref()` only accepts real columns.
7
+ > - **MySQL support** for JSON field expressions and the `whereJson*` methods, `fn.*` helpers for every MySQL 8.4 function, and batch inserts.
8
+ > - **Expressions without raw SQL**, like `Pet.ref('age').gte(3)` and `fn.count().filter(...)`, plus window functions.
9
+ > - **Fixes** for JSON key escaping (SQL injection), `orWhere` escaping relation conditions, and `for()`.
10
+ >
11
+ > See the [changelog](doc/release-notes/changelog.md) and the [migration guide](doc/release-notes/migration.md).
12
+ >
13
+ > ```sh
14
+ > npm install @abedmurrar/objection@next knex
15
+ > ```
16
+ >
17
+ > To keep `import { Model } from 'objection'` working in existing code, install it under the name `objection`:
18
+ >
19
+ > ```sh
20
+ > npm install objection@npm:@abedmurrar/objection@next
21
+ > ```
22
+
23
+ # [Objection.js](https://vincit.github.io/objection.js)
24
+
25
+ Objection.js is an [ORM](https://en.wikipedia.org/wiki/Object-relational_mapping) for [Node.js](https://nodejs.org/) that aims to stay out of your way and make it as easy as possible to use the full power of SQL and the underlying database engine while still making the common stuff easy and enjoyable.
26
+
27
+ Even though ORM is the best commonly known acronym to describe objection, a more accurate description is to call it **a relational query builder**. You get all the benefits of an SQL query builder but also a powerful set of tools for working with relations.
28
+
29
+ Objection.js is built on an SQL query builder called [knex](http://knexjs.org). All databases supported by knex are supported by objection.js. **SQLite3**, **Postgres** and **MySQL** are [thoroughly tested](https://github.com/Vincit/objection.js/actions).
30
+
31
+ What objection.js gives you:
32
+
33
+ - **An easy declarative way of [defining models](https://vincit.github.io/objection.js/guide/models.html) and relationships between them**
34
+ - **Simple and fun way to [fetch, insert, update and delete](https://vincit.github.io/objection.js/guide/query-examples.html) objects using the full power of SQL**
35
+ - **Powerful mechanisms for [eager loading](https://vincit.github.io/objection.js/guide/query-examples.html#eager-loading), [inserting](https://vincit.github.io/objection.js/guide/query-examples.html#graph-inserts) and [upserting](https://vincit.github.io/objection.js/guide/query-examples.html#graph-upserts) object graphs**
36
+ - **Easy to use [transactions](https://vincit.github.io/objection.js/guide/transactions.html)**
37
+ - **Official [TypeScript](https://github.com/Vincit/objection.js/blob/main/typings/objection/index.d.ts) support**
38
+ - **Optional [JSON schema](https://vincit.github.io/objection.js/guide/validation.html) validation**
39
+ - **A way to [store complex documents](https://vincit.github.io/objection.js/guide/documents.html) as single rows**
40
+
41
+ What objection.js **doesn't** give you:
42
+
43
+ - **A fully object oriented view of your database**
44
+ With objection you don't work with entities. You work with queries. Objection doesn't try to wrap every concept with an
45
+ object oriented equivalent. The best attempt to do that (IMO) is Hibernate, which is excellent, but it has 800k lines
46
+ of code and a lot more concepts to learn than SQL itself. The point is, writing a good traditional ORM is borderline
47
+ impossible. Objection attempts to provide a completely different way of working with SQL.
48
+ - **A custom query DSL. SQL is used as a query language.**
49
+ This doesn't mean you have to write SQL strings though. A query builder based on [knex](http://knexjs.org) is
50
+ used to build the SQL. However, if the query builder fails you for some reason, raw SQL strings can be easily
51
+ written using the [raw](https://vincit.github.io/objection.js/api/objection/#raw) helper function.
52
+ - **Automatic database schema creation and migration from model definitions.**
53
+ For simple things it is useful that the database schema is automatically generated from the model definitions,
54
+ but usually just gets in your way when doing anything non-trivial. Objection.js leaves the schema related things
55
+ to you. knex has a great [migration tool](https://knexjs.org/guide/migrations.html) that we recommend for this job. Check
56
+ out the [example project](https://github.com/Vincit/objection.js/tree/main/examples/koa-ts).
57
+
58
+ The best way to get started is to clone our [example project](https://github.com/Vincit/objection.js/tree/main/examples/koa) and start playing with it. There's also a [typescript version](https://github.com/Vincit/objection.js/tree/main/examples/koa-ts) available.
59
+
60
+ Check out [this issue](https://github.com/Vincit/objection.js/issues/1069) to see who is using objection and what they think about it.
61
+
62
+ Shortcuts:
63
+
64
+ - [Who uses objection.js](https://github.com/Vincit/objection.js/discussions/2464)
65
+ - [API reference](https://vincit.github.io/objection.js/api/query-builder/)
66
+ - [Example projects](https://github.com/Vincit/objection.js/tree/main/examples)
67
+ - [Changelog](https://vincit.github.io/objection.js/release-notes/changelog.html)
68
+ - [v1 -> v2 -> v3 migration guide](https://vincit.github.io/objection.js/release-notes/migration.html)
69
+ - [Contribution guide](https://vincit.github.io/objection.js/guide/contributing.html)
70
+ - [Plugins](https://vincit.github.io/objection.js/guide/plugins.html)
@@ -0,0 +1,8 @@
1
+ {
2
+ "parserOptions": {
3
+ "sourceType": "script"
4
+ },
5
+ "rules": {
6
+ "strict": "error"
7
+ }
8
+ }
@@ -0,0 +1,14 @@
1
+ 'use strict';
2
+
3
+ async function initialize(knex, modelClasses) {
4
+ if (!modelClasses) {
5
+ modelClasses = knex;
6
+ knex = modelClasses[0].knex();
7
+ }
8
+
9
+ await Promise.all(modelClasses.map((modelClass) => modelClass.fetchTableMetadata({ knex })));
10
+ }
11
+
12
+ module.exports = {
13
+ initialize,
14
+ };
@@ -0,0 +1,325 @@
1
+ 'use strict';
2
+
3
+ const Ajv = require('ajv');
4
+ const addFormats = require('ajv-formats');
5
+ const { Validator } = require('./Validator');
6
+ const { ValidationErrorType } = require('../model/ValidationError');
7
+ const { isObject, once, cloneDeep: lodashCloneDeep, omit } = require('../utils/objectUtils');
8
+
9
+ class AjvValidator extends Validator {
10
+ static init(self, conf) {
11
+ super.init(self, conf);
12
+
13
+ self.ajvOptions = Object.assign({}, conf.options, {
14
+ allErrors: true,
15
+ });
16
+
17
+ // Create a normal Ajv instance.
18
+ self.ajv = new Ajv(
19
+ Object.assign(
20
+ {
21
+ useDefaults: true,
22
+ },
23
+ self.ajvOptions,
24
+ ),
25
+ );
26
+
27
+ // Create an instance that doesn't set default values. We need this one
28
+ // to validate `patch` objects (objects that have a subset of properties).
29
+ self.ajvNoDefaults = new Ajv(
30
+ Object.assign({}, self.ajvOptions, {
31
+ useDefaults: false,
32
+ }),
33
+ );
34
+
35
+ // A cache for the compiled validator functions.
36
+ self.cache = new Map();
37
+
38
+ const setupAjv = (ajv) => {
39
+ if (conf.onCreateAjv) {
40
+ conf.onCreateAjv(ajv);
41
+ }
42
+ // Only add Ajv formats if they weren't added in user-space already
43
+ if (!ajv.formats['date-time']) {
44
+ addFormats(ajv);
45
+ }
46
+ };
47
+
48
+ setupAjv(self.ajv);
49
+ setupAjv(self.ajvNoDefaults);
50
+ }
51
+
52
+ beforeValidate({ json, model, options, ctx }) {
53
+ ctx.jsonSchema = model.constructor.getJsonSchema();
54
+
55
+ // Objection model's have a `$beforeValidate` hook that is allowed to modify the schema.
56
+ // We need to clone the schema in case the function modifies it. We only do this in the
57
+ // rare case that the given model has implemented the hook.
58
+ if (model.$beforeValidate !== model.$objectionModelClass.prototype.$beforeValidate) {
59
+ ctx.jsonSchema = cloneDeep(ctx.jsonSchema);
60
+ const ret = model.$beforeValidate(ctx.jsonSchema, json, options);
61
+
62
+ if (ret !== undefined) {
63
+ ctx.jsonSchema = ret;
64
+ }
65
+ }
66
+ }
67
+
68
+ validate({ json, model, options, ctx }) {
69
+ if (!ctx.jsonSchema) {
70
+ return json;
71
+ }
72
+
73
+ const modelClass = model.constructor;
74
+ const validator = this.getValidator(modelClass, ctx.jsonSchema, !!options.patch);
75
+
76
+ // We need to clone the input json if we are about to set default values.
77
+ if (!options.mutable && !options.patch && setsDefaultValues(ctx.jsonSchema)) {
78
+ json = cloneDeep(json);
79
+ }
80
+
81
+ validator.call(model, json);
82
+ const error = parseValidationError(validator.errors, modelClass, options, this.ajvOptions);
83
+
84
+ if (error) {
85
+ throw error;
86
+ }
87
+
88
+ return json;
89
+ }
90
+
91
+ getValidator(modelClass, jsonSchema, isPatchObject) {
92
+ // Use the Ajv custom serializer if provided.
93
+ const createCacheKey = this.ajvOptions.serialize || JSON.stringify;
94
+
95
+ // Optimization for the common case where jsonSchema is never modified.
96
+ // In that case we don't need to call the costly createCacheKey function.
97
+ const cacheKey =
98
+ jsonSchema === modelClass.getJsonSchema()
99
+ ? modelClass.uniqueTag()
100
+ : createCacheKey(jsonSchema);
101
+
102
+ let validators = this.cache.get(cacheKey);
103
+ let validator = null;
104
+
105
+ if (!validators) {
106
+ validators = {
107
+ // Validator created for the schema object without `required` properties
108
+ // using the Ajv instance that doesn't set default values.
109
+ patchValidator: null,
110
+
111
+ // Validator created for the unmodified schema.
112
+ normalValidator: null,
113
+ };
114
+
115
+ this.cache.set(cacheKey, validators);
116
+ }
117
+
118
+ if (isPatchObject) {
119
+ validator = validators.patchValidator;
120
+
121
+ if (!validator) {
122
+ validator = this.compilePatchValidator(jsonSchema);
123
+ validators.patchValidator = validator;
124
+ }
125
+ } else {
126
+ validator = validators.normalValidator;
127
+
128
+ if (!validator) {
129
+ validator = this.compileNormalValidator(jsonSchema);
130
+ validators.normalValidator = validator;
131
+ }
132
+ }
133
+
134
+ return validator;
135
+ }
136
+
137
+ compilePatchValidator(jsonSchema) {
138
+ jsonSchema = jsonSchemaWithoutRequired(jsonSchema);
139
+ // We need to use the ajv instance that doesn't set the default values.
140
+ return this.ajvNoDefaults.compile(jsonSchema);
141
+ }
142
+
143
+ compileNormalValidator(jsonSchema) {
144
+ return this.ajv.compile(jsonSchema);
145
+ }
146
+ }
147
+
148
+ function parseValidationError(errors, modelClass, options, ajvOptions) {
149
+ if (!errors) {
150
+ return null;
151
+ }
152
+
153
+ let relationNames = modelClass.getRelationNames();
154
+ let errorHash = {};
155
+ let numErrors = 0;
156
+
157
+ for (const error of errors) {
158
+ // If additionalProperties = false, relations can pop up as additionalProperty
159
+ // errors. Skip those.
160
+ if (
161
+ error.params &&
162
+ error.params.additionalProperty &&
163
+ relationNames.includes(error.params.additionalProperty)
164
+ ) {
165
+ continue;
166
+ }
167
+
168
+ let path = error.instancePath.replace(/\//g, '.');
169
+
170
+ if (error.params) {
171
+ if (error.params.missingProperty) {
172
+ path += `.${error.params.missingProperty}`;
173
+ } else if (error.params.additionalProperty) {
174
+ path += `.${error.params.additionalProperty}`;
175
+ }
176
+ }
177
+
178
+ const key = `${options.dataPath || ''}${path}`.substring(1);
179
+
180
+ // More than one error can occur for the same key in Ajv, merge them in the array:
181
+ const array = errorHash[key] || (errorHash[key] = []);
182
+
183
+ // Prepare error object
184
+ const errorObj = {
185
+ message: error.message,
186
+ keyword: error.keyword,
187
+ params: error.params,
188
+ };
189
+
190
+ // Add data if verbose enabled
191
+ if (ajvOptions.verbose) {
192
+ errorObj.data = error.data;
193
+ }
194
+
195
+ // Use unshift instead of push so that the last error ends up at [0],
196
+ // preserving previous behavior where only the last error was stored.
197
+ array.unshift(errorObj);
198
+
199
+ ++numErrors;
200
+ }
201
+
202
+ if (numErrors === 0) {
203
+ return null;
204
+ }
205
+
206
+ return modelClass.createValidationError({
207
+ type: ValidationErrorType.ModelValidation,
208
+ data: errorHash,
209
+ });
210
+ }
211
+
212
+ function cloneDeep(obj) {
213
+ if (isObject(obj) && obj.$isObjectionModel) {
214
+ return obj.$clone();
215
+ } else {
216
+ return lodashCloneDeep(obj);
217
+ }
218
+ }
219
+
220
+ function setsDefaultValues(jsonSchema) {
221
+ return jsonSchema && jsonSchema.properties && hasDefaults(jsonSchema.properties);
222
+ }
223
+
224
+ function hasDefaults(obj) {
225
+ if (Array.isArray(obj)) {
226
+ return arrayHasDefaults(obj);
227
+ } else {
228
+ return objectHasDefaults(obj);
229
+ }
230
+ }
231
+
232
+ function arrayHasDefaults(arr) {
233
+ for (let i = 0, l = arr.length; i < l; ++i) {
234
+ const val = arr[i];
235
+
236
+ if (isObject(val) && hasDefaults(val)) {
237
+ return true;
238
+ }
239
+ }
240
+
241
+ return false;
242
+ }
243
+
244
+ function objectHasDefaults(obj) {
245
+ const keys = Object.keys(obj);
246
+
247
+ for (let i = 0, l = keys.length; i < l; ++i) {
248
+ const key = keys[i];
249
+
250
+ if (key === 'default') {
251
+ return true;
252
+ } else {
253
+ const val = obj[key];
254
+
255
+ if (isObject(val) && hasDefaults(val)) {
256
+ return true;
257
+ }
258
+ }
259
+ }
260
+
261
+ return false;
262
+ }
263
+
264
+ function jsonSchemaWithoutRequired(jsonSchema) {
265
+ const subSchemaProps = ['anyOf', 'oneOf', 'allOf', 'not', 'then', 'else', 'properties'];
266
+ return Object.assign(
267
+ omit(jsonSchema, ['required', ...subSchemaProps]),
268
+ ...subSchemaProps.map((prop) => subSchemaWithoutRequired(jsonSchema, prop)),
269
+ jsonSchema && jsonSchema.definitions && Object.keys(jsonSchema.definitions).length > 0
270
+ ? {
271
+ definitions: Object.assign(
272
+ ...Object.keys(jsonSchema.definitions).map((prop) => ({
273
+ [prop]: jsonSchemaWithoutRequired(jsonSchema.definitions[prop]),
274
+ })),
275
+ ),
276
+ }
277
+ : {},
278
+ jsonSchema.discriminator && jsonSchema.discriminator.propertyName
279
+ ? { required: [jsonSchema.discriminator.propertyName] }
280
+ : {},
281
+ );
282
+ }
283
+
284
+ function subSchemaWithoutRequired(jsonSchema, prop) {
285
+ if (jsonSchema[prop]) {
286
+ if (Array.isArray(jsonSchema[prop])) {
287
+ const schemaArray = jsonSchemaArrayWithoutRequired(jsonSchema[prop]);
288
+
289
+ if (schemaArray.length !== 0) {
290
+ return {
291
+ [prop]: schemaArray,
292
+ };
293
+ } else {
294
+ return {};
295
+ }
296
+ } else if (jsonSchema.type === 'object' && prop === 'properties') {
297
+ return {
298
+ [prop]: Object.fromEntries(
299
+ Object.entries(jsonSchema[prop]).map(([key, schema]) => [
300
+ key,
301
+ jsonSchemaWithoutRequired(schema),
302
+ ]),
303
+ ),
304
+ };
305
+ } else {
306
+ return {
307
+ [prop]: jsonSchemaWithoutRequired(jsonSchema[prop]),
308
+ };
309
+ }
310
+ } else {
311
+ return {};
312
+ }
313
+ }
314
+
315
+ function jsonSchemaArrayWithoutRequired(jsonSchemaArray) {
316
+ return jsonSchemaArray.map(jsonSchemaWithoutRequired).filter(isNotEmptyObject);
317
+ }
318
+
319
+ function isNotEmptyObject(obj) {
320
+ return Object.keys(obj).length !== 0;
321
+ }
322
+
323
+ module.exports = {
324
+ AjvValidator,
325
+ };