objection 3.9.1 → 4.0.0-rc.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.
Files changed (250) hide show
  1. package/lib/dbErrors/LICENSE +21 -0
  2. package/lib/dbErrors/errorCodes/mssql.js +19 -0
  3. package/lib/dbErrors/errorCodes/mysql.js +805 -0
  4. package/lib/dbErrors/errorCodes/postgres.js +241 -0
  5. package/lib/dbErrors/errors/CheckViolationError.js +10 -0
  6. package/lib/dbErrors/errors/ConstraintViolationError.js +3 -0
  7. package/lib/dbErrors/errors/DBError.js +9 -0
  8. package/lib/dbErrors/errors/DataError.js +3 -0
  9. package/lib/dbErrors/errors/ForeignKeyViolationError.js +11 -0
  10. package/lib/dbErrors/errors/NotNullViolationError.js +12 -0
  11. package/lib/dbErrors/errors/UniqueViolationError.js +12 -0
  12. package/lib/dbErrors/index.js +59 -0
  13. package/lib/dbErrors/parsers/mssql/DBError/CheckViolationError/parser.js +28 -0
  14. package/lib/dbErrors/parsers/mssql/DBError/DataError/parser.js +25 -0
  15. package/lib/dbErrors/parsers/mssql/DBError/ForeignKeyViolationError/parser.js +39 -0
  16. package/lib/dbErrors/parsers/mssql/DBError/NotNullViolationError/parser.js +30 -0
  17. package/lib/dbErrors/parsers/mssql/DBError/UniqueViolationError/parser.js +53 -0
  18. package/lib/dbErrors/parsers/mssql/DBError/parser.js +51 -0
  19. package/lib/dbErrors/parsers/mssql/DBError/util.js +16 -0
  20. package/lib/dbErrors/parsers/mysql/DBError/ConstraintViolationError/ForeignKeyViolationError/parser.js +43 -0
  21. package/lib/dbErrors/parsers/mysql/DBError/ConstraintViolationError/NotNullViolationError/parser.js +29 -0
  22. package/lib/dbErrors/parsers/mysql/DBError/ConstraintViolationError/UniqueViolationError/parser.js +27 -0
  23. package/lib/dbErrors/parsers/mysql/DBError/ConstraintViolationError/parser.js +23 -0
  24. package/lib/dbErrors/parsers/mysql/DBError/DataError/parser.js +24 -0
  25. package/lib/dbErrors/parsers/mysql/DBError/parser.js +28 -0
  26. package/lib/dbErrors/parsers/postgres/DBError/ConstraintViolationError/CheckViolationError/parser.js +18 -0
  27. package/lib/dbErrors/parsers/postgres/DBError/ConstraintViolationError/ForeignKeyViolationError/parser.js +18 -0
  28. package/lib/dbErrors/parsers/postgres/DBError/ConstraintViolationError/NotNullViolationError/parser.js +18 -0
  29. package/lib/dbErrors/parsers/postgres/DBError/ConstraintViolationError/UniqueViolationError/parser.js +40 -0
  30. package/lib/dbErrors/parsers/postgres/DBError/ConstraintViolationError/parser.js +25 -0
  31. package/lib/dbErrors/parsers/postgres/DBError/DataError/parser.js +16 -0
  32. package/lib/dbErrors/parsers/postgres/DBError/parser.js +27 -0
  33. package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/CheckViolationError/parser.js +20 -0
  34. package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/ForeignKeyViolationError/parser.js +20 -0
  35. package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/NotNullViolationError/parser.js +27 -0
  36. package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/UniqueViolationError/parser.js +28 -0
  37. package/lib/dbErrors/parsers/sqlite/DBError/ConstraintViolationError/parser.js +24 -0
  38. package/lib/dbErrors/parsers/sqlite/DBError/parser.js +23 -0
  39. package/lib/dbErrors/utils/sqlState.js +3 -0
  40. package/lib/initialize.js +1 -7
  41. package/lib/model/AjvValidator.js +51 -117
  42. package/lib/model/Model.js +84 -59
  43. package/lib/model/ModifierNotFoundError.js +1 -7
  44. package/lib/model/NotFoundError.js +1 -7
  45. package/lib/model/RelationDoesNotExistError.js +1 -7
  46. package/lib/model/ValidationError.js +3 -10
  47. package/lib/model/Validator.js +1 -7
  48. package/lib/model/getModel.js +7 -6
  49. package/lib/model/inheritModel.js +1 -7
  50. package/lib/model/modelBindKnex.js +4 -10
  51. package/lib/model/modelClone.js +5 -11
  52. package/lib/model/modelColPropMap.js +3 -10
  53. package/lib/model/modelId.js +3 -10
  54. package/lib/model/modelJsonAttributes.js +35 -16
  55. package/lib/model/modelParseRelations.js +2 -8
  56. package/lib/model/modelQueryProps.js +11 -23
  57. package/lib/model/modelSet.js +89 -41
  58. package/lib/model/modelTableMetadata.js +4 -11
  59. package/lib/model/modelToJson.js +6 -13
  60. package/lib/model/modelUtils.js +14 -13
  61. package/lib/model/modelValidate.js +3 -9
  62. package/lib/model/modelValues.js +5 -14
  63. package/lib/model/modelVisitor.js +1 -7
  64. package/lib/objection.js +33 -71
  65. package/lib/queryBuilder/FunctionBuilder.js +4 -11
  66. package/lib/queryBuilder/InternalOptions.js +4 -9
  67. package/lib/queryBuilder/JoinBuilder.js +5 -8
  68. package/lib/queryBuilder/QueryBuilder.js +126 -95
  69. package/lib/queryBuilder/QueryBuilderBase.js +41 -123
  70. package/lib/queryBuilder/QueryBuilderContext.js +2 -8
  71. package/lib/queryBuilder/QueryBuilderContextBase.js +2 -8
  72. package/lib/queryBuilder/QueryBuilderOperationSupport.js +7 -21
  73. package/lib/queryBuilder/QueryBuilderUserContext.js +1 -7
  74. package/lib/queryBuilder/RawBuilder.js +5 -13
  75. package/lib/queryBuilder/ReferenceBuilder.js +20 -48
  76. package/lib/queryBuilder/RelationExpression.js +18 -16
  77. package/lib/queryBuilder/StaticHookArguments.js +74 -16
  78. package/lib/queryBuilder/ValueBuilder.js +11 -13
  79. package/lib/queryBuilder/getJoinBuilder.js +10 -0
  80. package/lib/queryBuilder/graph/GraphLoader.js +314 -0
  81. package/lib/queryBuilder/graph/GraphNode.js +241 -0
  82. package/lib/queryBuilder/graph/GraphOptions.js +55 -248
  83. package/lib/queryBuilder/graph/GraphPlan.js +431 -0
  84. package/lib/queryBuilder/graph/GraphSchedule.js +513 -0
  85. package/lib/queryBuilder/graph/GraphUpsert.js +519 -412
  86. package/lib/queryBuilder/graph/GraphWriter.js +508 -0
  87. package/lib/queryBuilder/graph/graphKeys.js +929 -0
  88. package/lib/queryBuilder/join/JoinResultColumn.js +1 -7
  89. package/lib/queryBuilder/join/JoinResultParser.js +6 -11
  90. package/lib/queryBuilder/join/RelationJoiner.js +66 -84
  91. package/lib/queryBuilder/join/TableNode.js +2 -8
  92. package/lib/queryBuilder/join/TableTree.js +3 -9
  93. package/lib/queryBuilder/join/utils.js +5 -15
  94. package/lib/queryBuilder/jsonDialects/JsonDialect.js +51 -0
  95. package/lib/queryBuilder/jsonDialects/MySqlJsonDialect.js +81 -0
  96. package/lib/queryBuilder/jsonDialects/PostgresJsonDialect.js +45 -0
  97. package/lib/queryBuilder/jsonDialects/SqliteJsonDialect.js +67 -0
  98. package/lib/queryBuilder/jsonDialects/index.js +44 -0
  99. package/lib/queryBuilder/operations/DelegateOperation.js +2 -8
  100. package/lib/queryBuilder/operations/DeleteOperation.js +10 -39
  101. package/lib/queryBuilder/operations/FindByIdOperation.js +7 -12
  102. package/lib/queryBuilder/operations/FindByIdsOperation.js +4 -8
  103. package/lib/queryBuilder/operations/FindOperation.js +14 -34
  104. package/lib/queryBuilder/operations/FirstOperation.js +6 -10
  105. package/lib/queryBuilder/operations/FromOperation.js +3 -9
  106. package/lib/queryBuilder/operations/InsertAndFetchOperation.js +13 -12
  107. package/lib/queryBuilder/operations/InsertGraphAndFetchOperation.js +4 -10
  108. package/lib/queryBuilder/operations/InsertGraphOperation.js +7 -18
  109. package/lib/queryBuilder/operations/InsertOperation.js +118 -45
  110. package/lib/queryBuilder/operations/InstanceDeleteOperation.js +5 -12
  111. package/lib/queryBuilder/operations/InstanceFindOperation.js +4 -11
  112. package/lib/queryBuilder/operations/InstanceInsertOperation.js +2 -8
  113. package/lib/queryBuilder/operations/InstanceUpdateOperation.js +7 -14
  114. package/lib/queryBuilder/operations/JoinRelatedOperation.js +5 -11
  115. package/lib/queryBuilder/operations/KnexOperation.js +2 -8
  116. package/lib/queryBuilder/operations/LimitPerParentOperation.js +7 -69
  117. package/lib/queryBuilder/operations/MergeOperation.js +5 -11
  118. package/lib/queryBuilder/operations/NoneOperation.js +2 -8
  119. package/lib/queryBuilder/operations/ObjectionToKnexConvertingOperation.js +20 -49
  120. package/lib/queryBuilder/operations/OnBuildKnexOperation.js +2 -8
  121. package/lib/queryBuilder/operations/OnBuildOperation.js +2 -8
  122. package/lib/queryBuilder/operations/OnErrorOperation.js +2 -8
  123. package/lib/queryBuilder/operations/QueryBuilderOperation.js +1 -7
  124. package/lib/queryBuilder/operations/RangeOperation.js +11 -26
  125. package/lib/queryBuilder/operations/RelateOperation.js +2 -8
  126. package/lib/queryBuilder/operations/ReturningOperation.js +3 -9
  127. package/lib/queryBuilder/operations/RunAfterOperation.js +2 -12
  128. package/lib/queryBuilder/operations/RunBeforeOperation.js +2 -8
  129. package/lib/queryBuilder/operations/UnrelateOperation.js +2 -8
  130. package/lib/queryBuilder/operations/UpdateAndFetchOperation.js +14 -17
  131. package/lib/queryBuilder/operations/UpdateOperation.js +32 -132
  132. package/lib/queryBuilder/operations/UpsertGraphAndFetchOperation.js +8 -11
  133. package/lib/queryBuilder/operations/UpsertGraphOperation.js +6 -12
  134. package/lib/queryBuilder/operations/WhereCompositeOperation.js +3 -9
  135. package/lib/queryBuilder/operations/WhereGroupOperation.js +2 -8
  136. package/lib/queryBuilder/operations/eager/EagerOperation.js +3 -9
  137. package/lib/queryBuilder/operations/eager/JoinEagerOperation.js +72 -10
  138. package/lib/queryBuilder/operations/eager/NaiveEagerOperation.js +2 -8
  139. package/lib/queryBuilder/operations/eager/WhereInEagerOperation.js +23 -44
  140. package/lib/queryBuilder/operations/fieldExpressions.js +122 -0
  141. package/lib/queryBuilder/operations/jsonApi/WhereJsonHasOperation.js +20 -0
  142. package/lib/queryBuilder/operations/jsonApi/WhereJsonOperation.js +44 -0
  143. package/lib/queryBuilder/operations/jsonApi/WhereJsonTypeOperation.js +14 -0
  144. package/lib/queryBuilder/operations/select/SelectOperation.js +11 -21
  145. package/lib/queryBuilder/operations/select/Selection.js +19 -17
  146. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeMsSqlOperation.js +4 -10
  147. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeOperation.js +4 -10
  148. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeSqliteOperation.js +4 -10
  149. package/lib/queryBuilder/operations/windowOrder.js +56 -0
  150. package/lib/queryBuilder/parsers/jsonFieldExpressionParser.js +3 -7
  151. package/lib/queryBuilder/parsers/relationExpressionParser.js +42 -43
  152. package/lib/queryBuilder/parsers/relationExpressionParser.pegjs +39 -36
  153. package/lib/queryBuilder/transformations/CompositeQueryTransformation.js +2 -8
  154. package/lib/queryBuilder/transformations/QueryTransformation.js +1 -7
  155. package/lib/queryBuilder/transformations/WrapMysqlModifySubqueryTransformation.js +3 -9
  156. package/lib/queryBuilder/transformations/index.js +3 -11
  157. package/lib/queryBuilder/whereGroups.js +9 -24
  158. package/lib/queryBuilder/withoutOrder.js +4 -11
  159. package/lib/relations/Relation.js +47 -28
  160. package/lib/relations/RelationDeleteOperation.js +4 -14
  161. package/lib/relations/RelationFindOperation.js +119 -90
  162. package/lib/relations/RelationInsertOperation.js +2 -8
  163. package/lib/relations/RelationOwner.js +5 -11
  164. package/lib/relations/RelationProperty.js +4 -10
  165. package/lib/relations/RelationUpdateOperation.js +4 -14
  166. package/lib/relations/RelationWriteResultOperation.js +15 -0
  167. package/lib/relations/belongsToMany/BelongsToManyDeleteOperation.js +23 -0
  168. package/lib/relations/belongsToMany/BelongsToManyFindOperation.js +35 -0
  169. package/lib/relations/belongsToMany/BelongsToManyInsertOperation.js +28 -0
  170. package/lib/relations/belongsToMany/BelongsToManyRelateOperation.js +16 -0
  171. package/lib/relations/belongsToMany/BelongsToManyRelation.js +276 -0
  172. package/lib/relations/belongsToMany/BelongsToManyUnrelateOperation.js +11 -0
  173. package/lib/relations/belongsToOne/BelongsToOneDeleteOperation.js +2 -8
  174. package/lib/relations/belongsToOne/BelongsToOneInsertOperation.js +6 -11
  175. package/lib/relations/belongsToOne/BelongsToOneRelateOperation.js +3 -9
  176. package/lib/relations/belongsToOne/BelongsToOneRelation.js +8 -19
  177. package/lib/relations/belongsToOne/BelongsToOneUnrelateOperation.js +53 -27
  178. package/lib/relations/hasMany/HasManyInsertOperation.js +5 -10
  179. package/lib/relations/hasMany/HasManyRelateOperation.js +5 -11
  180. package/lib/relations/hasMany/HasManyRelation.js +9 -11
  181. package/lib/relations/hasMany/HasManyUnrelateOperation.js +3 -9
  182. package/lib/relations/hasOne/HasOneRelation.js +2 -8
  183. package/lib/relations/hasOneThrough/HasOneThroughRelation.js +2 -8
  184. package/lib/relations/manyToMany/ManyToManyModifyMixin.js +10 -15
  185. package/lib/relations/manyToMany/ManyToManyMySqlModifyMixin.js +2 -8
  186. package/lib/relations/manyToMany/ManyToManyRelation.js +82 -50
  187. package/lib/relations/manyToMany/ManyToManySqliteModifyMixin.js +2 -8
  188. package/lib/relations/manyToMany/delete/ManyToManyDeleteOperation.js +5 -9
  189. package/lib/relations/manyToMany/delete/ManyToManyDeleteOperationBase.js +2 -8
  190. package/lib/relations/manyToMany/delete/ManyToManyDeleteSqliteOperation.js +3 -9
  191. package/lib/relations/manyToMany/find/ManyToManyFindOperation.js +20 -59
  192. package/lib/relations/manyToMany/insert/ManyToManyInsertOperation.js +6 -11
  193. package/lib/relations/manyToMany/relate/ManyToManyRelateOperation.js +4 -10
  194. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateMySqlOperation.js +3 -9
  195. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperation.js +5 -9
  196. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperationBase.js +3 -9
  197. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateSqliteOperation.js +3 -9
  198. package/lib/relations/manyToMany/update/ManyToManyUpdateMySqlOperation.js +3 -9
  199. package/lib/relations/manyToMany/update/ManyToManyUpdateOperation.js +5 -9
  200. package/lib/relations/manyToMany/update/ManyToManyUpdateOperationBase.js +64 -72
  201. package/lib/relations/manyToMany/update/ManyToManyUpdateSqliteOperation.js +3 -9
  202. package/lib/transaction.js +3 -9
  203. package/lib/utils/assert.js +3 -11
  204. package/lib/utils/buildUtils.js +2 -8
  205. package/lib/utils/classUtils.js +1 -7
  206. package/lib/utils/createModifier.js +2 -8
  207. package/lib/utils/identifierMapping.js +23 -27
  208. package/lib/utils/internalPropUtils.js +1 -7
  209. package/lib/utils/knexUtils.js +84 -40
  210. package/lib/utils/mixin.js +3 -10
  211. package/lib/utils/normalizeIds.js +2 -8
  212. package/lib/utils/objectUtils.js +41 -60
  213. package/lib/utils/parseFieldExpression.js +16 -16
  214. package/lib/utils/promiseUtils/after.js +2 -8
  215. package/lib/utils/promiseUtils/afterReturn.js +2 -8
  216. package/lib/utils/promiseUtils/index.js +7 -16
  217. package/lib/utils/promiseUtils/isPromise.js +2 -8
  218. package/lib/utils/promiseUtils/map.js +2 -8
  219. package/lib/utils/promiseUtils/mapAfterAllReturn.js +2 -8
  220. package/lib/utils/promiseUtils/try.js +2 -8
  221. package/lib/utils/resolveModel.js +10 -17
  222. package/lib/utils/tmpColumnUtils.js +2 -9
  223. package/lib/utils/warnings.js +2 -14
  224. package/package.json +25 -17
  225. package/typings/objection/index.d.ts +3217 -2179
  226. package/lib/.eslintrc.json +0 -8
  227. package/lib/model/graph/ModelGraph.js +0 -80
  228. package/lib/model/graph/ModelGraphBuilder.js +0 -241
  229. package/lib/model/graph/ModelGraphEdge.js +0 -52
  230. package/lib/model/graph/ModelGraphNode.js +0 -238
  231. package/lib/queryBuilder/graph/GraphAction.js +0 -69
  232. package/lib/queryBuilder/graph/GraphData.js +0 -53
  233. package/lib/queryBuilder/graph/GraphFetcher.js +0 -197
  234. package/lib/queryBuilder/graph/GraphNodeDbExistence.js +0 -184
  235. package/lib/queryBuilder/graph/GraphOperation.js +0 -27
  236. package/lib/queryBuilder/graph/delete/GraphDelete.js +0 -73
  237. package/lib/queryBuilder/graph/delete/GraphDeleteAction.js +0 -88
  238. package/lib/queryBuilder/graph/insert/DeferredRelationGraphInsertAction.js +0 -58
  239. package/lib/queryBuilder/graph/insert/GraphInsert.js +0 -296
  240. package/lib/queryBuilder/graph/insert/GraphInsertAction.js +0 -233
  241. package/lib/queryBuilder/graph/insert/JoinRowGraphInsertAction.js +0 -92
  242. package/lib/queryBuilder/graph/patch/GraphPatch.js +0 -20
  243. package/lib/queryBuilder/graph/patch/GraphPatchAction.js +0 -289
  244. package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsert.js +0 -31
  245. package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsertAction.js +0 -126
  246. package/lib/queryBuilder/operations/jsonApi/WhereJsonHasPostgresOperation.js +0 -30
  247. package/lib/queryBuilder/operations/jsonApi/WhereJsonNotObjectPostgresOperation.js +0 -37
  248. package/lib/queryBuilder/operations/jsonApi/WhereJsonPostgresOperation.js +0 -62
  249. package/lib/queryBuilder/operations/jsonApi/postgresJsonApi.js +0 -192
  250. package/lib/utils/wrapDbError.js +0 -64
@@ -0,0 +1,241 @@
1
+ import { asArray, asSingle, isNumber, isObject, isString } from '../../utils/objectUtils.js';
2
+ import { ValidationErrorType } from '../../model/ValidationError.js';
3
+
4
+ // A model in a graph that is inserted or upserted, or in the current graph that
5
+ // is fetched from the database for it.
6
+ export class GraphNode {
7
+ constructor(modelClass, obj, { parent = null, relation = null, index = null, relationPath }) {
8
+ this.modelClass = modelClass;
9
+ this.obj = obj;
10
+ this.parent = parent;
11
+ this.relation = relation;
12
+ // The index in the array of a to-many relation, or of array input:
13
+ this.index = index;
14
+ this.children = [];
15
+ this.relationPath = parent ? [...parent.relationPath, relation.name] : relationPath;
16
+ this.relationPathKey = this.relationPath.join('.');
17
+ // The id state of the input, before the writes complete ids with keys:
18
+ this.hasId = obj.$hasId();
19
+
20
+ // Read the markers of the graph, which `removeMarkers()` removes from the
21
+ // models once all places of a model are read:
22
+ const { uidProp, uidRefProp, dbRefProp, graphUnrelateProp, graphDeleteProp } = modelClass;
23
+ this.uid = obj[uidProp];
24
+ this.reference = obj[uidRefProp];
25
+ this.dbReference = obj[dbRefProp];
26
+ this.toBeUnrelated = obj[graphUnrelateProp] === true;
27
+ this.toBeDeleted = obj[graphDeleteProp] === true;
28
+
29
+ // The node of a `#ref` model, and the `#ref{}` properties of this model:
30
+ this.referencedNode = null;
31
+ this.propertyReferences = [];
32
+ // The nodes whose `#ref` or `#ref{}` point to this node:
33
+ this.referencingNodes = [];
34
+ this.propertyReferencingNodes = [];
35
+
36
+ // How the graph and the current graph pair up, see `GraphLoader`: The
37
+ // current node at the place of an input node and the other way around, and
38
+ // the current node of a related model's row fetched from elsewhere.
39
+ this.current = null;
40
+ this.node = null;
41
+ this.row = null;
42
+ this.isMissing = false;
43
+
44
+ // Set by `GraphPlan`:
45
+ this.isPruned = false;
46
+ }
47
+
48
+ get isReference() {
49
+ return this.reference !== undefined;
50
+ }
51
+
52
+ get isDbReference() {
53
+ return this.dbReference !== undefined;
54
+ }
55
+
56
+ get toBeUnrelatedOrDeleted() {
57
+ return this.toBeUnrelated || this.toBeDeleted;
58
+ }
59
+
60
+ get isRoot() {
61
+ return !this.parent;
62
+ }
63
+
64
+ // The current node of the row of this node, at its place or where a relate
65
+ // moves it from.
66
+ get existing() {
67
+ return this.current || this.row;
68
+ }
69
+
70
+ get dataPath() {
71
+ return this.parent
72
+ ? [
73
+ ...this.parent.dataPath,
74
+ this.relation.name,
75
+ ...(this.relation.isOneToOne() ? [] : [this.index]),
76
+ ]
77
+ : this.index !== null
78
+ ? [this.index]
79
+ : [];
80
+ }
81
+
82
+ // The data path in the form of validation errors, like `.pets[0].owner` or
83
+ // `[1].pets[0].owner` for array input.
84
+ get dataPathKey() {
85
+ return this.dataPath.map((key) => (isNumber(key) ? `[${key}]` : `.${key}`)).join('');
86
+ }
87
+
88
+ get descendants() {
89
+ return this.children.flatMap((child) => [child, ...child.descendants]);
90
+ }
91
+
92
+ get activeChildren() {
93
+ return this.children.filter((child) => !child.isPruned);
94
+ }
95
+
96
+ get rowKey() {
97
+ return `${this.modelClass.getTableName()}:${this.obj.$idKey()}`;
98
+ }
99
+
100
+ // Build the nodes of `roots` and their related models in depth-first order.
101
+ // `relationPath` is the place of the roots in the graph that a current graph
102
+ // is fetched for. The roots of array input know their index in it.
103
+ static buildGraph(modelClass, roots, relationPath = []) {
104
+ const nodes = [];
105
+
106
+ const addNode = (modelClass, obj, place) => {
107
+ if (!isObject(obj) || !obj.$isObjectionModel) {
108
+ throw createNotModelError(modelClass, obj);
109
+ }
110
+
111
+ const node = new GraphNode(modelClass, obj, place);
112
+ nodes.push(node);
113
+ place.parent?.children.push(node);
114
+
115
+ for (const name of modelClass.getRelationNames()) {
116
+ const related = obj[name];
117
+
118
+ if (related) {
119
+ const relation = modelClass.getRelation(name);
120
+ const items = relation.isOneToOne() ? [asSingle(related)] : asArray(related);
121
+
122
+ items.forEach((item, index) => {
123
+ addNode(relation.relatedModelClass, item, { parent: node, relation, index });
124
+ });
125
+ }
126
+ }
127
+ };
128
+
129
+ asArray(roots).forEach((root, index) => {
130
+ addNode(modelClass, root, { relationPath, index: Array.isArray(roots) ? index : null });
131
+ });
132
+
133
+ return nodes;
134
+ }
135
+
136
+ // Remove the markers from the models, as they're neither written nor
137
+ // returned, and return a function that puts them back if the writes fail.
138
+ static removeMarkers(nodes) {
139
+ const markers = [];
140
+
141
+ for (const { modelClass, obj } of nodes) {
142
+ for (const prop of [
143
+ modelClass.uidProp,
144
+ modelClass.uidRefProp,
145
+ modelClass.dbRefProp,
146
+ modelClass.graphUnrelateProp,
147
+ modelClass.graphDeleteProp,
148
+ ]) {
149
+ if (Object.hasOwn(obj, prop)) {
150
+ markers.push([obj, prop, obj[prop]]);
151
+ delete obj[prop];
152
+ }
153
+ }
154
+ }
155
+
156
+ return () => {
157
+ for (const [obj, prop, value] of markers) {
158
+ obj[prop] = value;
159
+ }
160
+ };
161
+ }
162
+
163
+ // Link the `#ref` and `#ref{}` references of the nodes to the nodes with the
164
+ // `#id` that they refer to.
165
+ static linkReferences(nodes) {
166
+ const nodesByUid = new Map();
167
+
168
+ for (const node of nodes) {
169
+ if (node.uid !== undefined) {
170
+ nodesByUid.set(node.uid, node);
171
+ }
172
+ }
173
+
174
+ const getNode = (uid) => {
175
+ const node = nodesByUid.get(uid);
176
+
177
+ if (!node) {
178
+ throw new Error(`could not resolve reference ${uid} in a graph.`);
179
+ }
180
+
181
+ return node;
182
+ };
183
+
184
+ for (const node of nodes) {
185
+ if (node.isReference) {
186
+ node.referencedNode = getNode(node.reference);
187
+ node.referencedNode.referencingNodes.push(node);
188
+ }
189
+
190
+ forEachPropertyReference(node.obj, ({ path, match, uid, refPath, isWholeValue }) => {
191
+ const referencedNode = getNode(uid);
192
+ node.propertyReferences.push({ path, match, refPath, isWholeValue, referencedNode });
193
+ referencedNode.propertyReferencingNodes.push(node);
194
+ });
195
+ }
196
+ }
197
+ }
198
+
199
+ function forEachPropertyReference(obj, callback) {
200
+ const modelClass = obj.constructor;
201
+ const relationNames = modelClass.getRelationNames();
202
+
203
+ const visit = (value, path) => {
204
+ if (Array.isArray(value)) {
205
+ value.forEach((item, index) => visit(item, [...path, index]));
206
+ } else if (isObject(value) && !Buffer.isBuffer(value)) {
207
+ for (const key of Object.keys(value)) {
208
+ visit(value[key], [...path, key]);
209
+ }
210
+ } else if (isString(value)) {
211
+ for (const [match, uid, refPath] of value.matchAll(modelClass.propRefRegex)) {
212
+ callback({
213
+ path,
214
+ match,
215
+ uid,
216
+ refPath: refPath.trim().split('.'),
217
+ isWholeValue: match === value,
218
+ });
219
+ }
220
+ }
221
+ };
222
+
223
+ for (const key of Object.keys(obj)) {
224
+ if (!relationNames.includes(key)) {
225
+ visit(obj[key], [key]);
226
+ }
227
+ }
228
+ }
229
+
230
+ export function createNotModelError(modelClass, value) {
231
+ return modelClass.createValidationError({
232
+ type: ValidationErrorType.InvalidGraph,
233
+ message: `expected value "${value}" to be an instance of ${modelClass.name}`,
234
+ });
235
+ }
236
+
237
+ // Name the place of a model in error messages, by its data path.
238
+ export function formatPath(dataPathKey) {
239
+ const path = dataPathKey.replace(/^\./, '');
240
+ return path ? `'${path}'` : 'the root';
241
+ }
@@ -1,7 +1,5 @@
1
- 'use strict';
2
-
3
- const { RelationExpression } = require('../RelationExpression');
4
- const { isString } = require('../../utils/objectUtils');
1
+ import { RelationExpression } from '../RelationExpression.js';
2
+ import { isString } from '../../utils/objectUtils.js';
5
3
 
6
4
  const NO_RELATE = 'noRelate';
7
5
  const NO_UNRELATE = 'noUnrelate';
@@ -18,7 +16,7 @@ const ALLOW_REFS = 'allowRefs';
18
16
  const ALLOW_MARKERS = 'allowMarkers';
19
17
 
20
18
  // The options that are matched against the relation path of each node, see
21
- // `_hasOption()`.
19
+ // `has()`.
22
20
  const NODE_OPTION_NAMES = [
23
21
  NO_RELATE,
24
22
  NO_UNRELATE,
@@ -33,27 +31,31 @@ const NODE_OPTION_NAMES = [
33
31
 
34
32
  const OPTION_NAMES = [...NODE_OPTION_NAMES, FETCH_STRATEGY, ALLOW_REFS, ALLOW_MARKERS];
35
33
 
36
- const FetchStrategy = {
34
+ // `insertGraph` never fetches, updates or removes existing rows, so only the
35
+ // options about inserting and relating apply to it, and the others are set:
36
+ const INSERT_OPTION_NAMES = [RELATE, NO_RELATE, NO_INSERT, ALLOW_REFS];
37
+ const INSERT_ONLY_OPTIONS = {
38
+ [NO_UPDATE]: true,
39
+ [NO_DELETE]: true,
40
+ [NO_UNRELATE]: true,
41
+ [INSERT_MISSING]: true,
42
+ };
43
+
44
+ export const FetchStrategy = {
37
45
  OnlyIdentifiers: 'OnlyIdentifiers',
38
46
  Everything: 'Everything',
39
47
  OnlyNeeded: 'OnlyNeeded',
40
48
  };
41
49
 
42
- class GraphOptions {
43
- // `rootRelationPath` is the relation path of the root node of a recursive
44
- // upsert in the original graph, against which the relation paths and
45
- // expressions of the options match. `insertOnly` is only set by
46
- // `insertGraph`, which never fetches, updates or removes existing rows.
47
- constructor(options, rootRelationPath = [], insertOnly = false) {
48
- if (options instanceof GraphOptions) {
49
- this.options = options.options;
50
- this.rootRelationPath = options.rootRelationPath;
51
- this.insertOnly = options.insertOnly;
52
- } else {
53
- this.options = parseRelationExpressions(options);
54
- this.rootRelationPath = rootRelationPath;
55
- this.insertOnly = insertOnly;
56
- }
50
+ export class GraphOptions {
51
+ // `insertOnly` is only set by `insertGraph`, which never fetches, updates or
52
+ // removes existing rows.
53
+ constructor(options, insertOnly = false) {
54
+ checkForUnknownOptions(options, insertOnly);
55
+ this.options = parseRelationExpressions(
56
+ insertOnly ? { ...options, ...INSERT_ONLY_OPTIONS } : options,
57
+ );
58
+ this.insertOnly = insertOnly;
57
59
  }
58
60
 
59
61
  isFetchStrategy(strategy) {
@@ -72,136 +74,15 @@ class GraphOptions {
72
74
  return this.insertOnly;
73
75
  }
74
76
 
75
- // Like `shouldRelate` but ignores settings that explicitly disable relate operations.
76
- shouldRelateIgnoreDisable(node, graphData) {
77
- if (node.toBeUnrelatedOrDeleted) {
78
- return false;
79
- }
80
-
81
- if (node.isReference || node.isDbReference) {
82
- return true;
83
- }
84
-
85
- return (
86
- this._hasOption(node, RELATE) &&
87
- !getCurrentNode(node, graphData) &&
88
- !!node.parentEdge &&
89
- !!node.parentEdge.relation &&
90
- hasRelateProp(node) &&
91
- graphData.nodeDbExistence.doesNodeExistInDb(node)
92
- );
93
- }
94
-
95
- shouldRelate(node, graphData) {
96
- return !this._hasOption(node, NO_RELATE) && this.shouldRelateIgnoreDisable(node, graphData);
97
- }
98
-
99
- // Like `shouldInsert` but ignores settings that explicitly disable insert operations.
100
- shouldInsertIgnoreDisable(node, graphData) {
101
- if (node.toBeUnrelatedOrDeleted) {
102
- return false;
103
- }
104
-
105
- return (
106
- !getCurrentNode(node, graphData) &&
107
- !this.shouldRelateIgnoreDisable(node, graphData) &&
108
- (!node.hasId || this.shouldInsertMissing(node))
109
- );
110
- }
111
-
112
- shouldInsert(node, graphData) {
113
- return !this._hasOption(node, NO_INSERT) && this.shouldInsertIgnoreDisable(node, graphData);
114
- }
115
-
116
- shouldInsertMissing(node) {
117
- return this._hasOption(node, INSERT_MISSING);
118
- }
119
-
120
- // Like `shouldPatch() || shouldUpdate()` but ignores settings that explicitly disable
121
- // update or patch operations.
122
- shouldPatchOrUpdateIgnoreDisable(node, graphData) {
123
- if (node.toBeUnrelatedOrDeleted) {
124
- return false;
125
- }
126
-
127
- if (this.shouldRelate(node, graphData)) {
128
- // We should update all nodes that are going to be related. Note that
129
- // we don't actually update anything unless there is something to update
130
- // so this is just a preliminary test.
131
- return true;
132
- }
133
-
134
- return !!getCurrentNode(node, graphData);
135
- }
136
-
137
- shouldPatch(node, graphData) {
138
- return (
139
- this.shouldPatchOrUpdateIgnoreDisable(node, graphData) &&
140
- !this._hasOption(node, NO_UPDATE) &&
141
- !this._hasOption(node, UPDATE)
142
- );
143
- }
144
-
145
- shouldUpdate(node, graphData) {
146
- return (
147
- this.shouldPatchOrUpdateIgnoreDisable(node, graphData) &&
148
- !this._hasOption(node, NO_UPDATE) &&
149
- this._hasOption(node, UPDATE)
150
- );
151
- }
152
-
153
- // Like `shouldUnrelate` but ignores settings that explicitly disable unrelate operations.
154
- shouldUnrelateIgnoreDisable(currentNode) {
155
- return this._hasOption(currentNode, UNRELATE);
156
- }
157
-
158
- shouldUnrelate(currentNode, graphData) {
159
- const node = getNode(currentNode, graphData.graph);
160
- if (node && node.toBeUnrelated) return true;
161
-
162
- return (
163
- !node &&
164
- !hasIgnoredOtherModel(currentNode, graphData) &&
165
- !this._hasOption(currentNode, NO_UNRELATE) &&
166
- (this.shouldUnrelateIgnoreDisable(currentNode) ||
167
- // The unrelate replaces a delete, so `noDelete` disables it too.
168
- (!this._hasOption(currentNode, NO_DELETE) && shouldUnrelateKeptRow(currentNode, graphData)))
169
- );
170
- }
171
-
172
- shouldDelete(currentNode, graphData) {
173
- const node = getNode(currentNode, graphData.graph);
174
- // `#unrelate` takes precedence over `#delete` if both are set.
175
- if (node && node.toBeDeleted && !node.toBeUnrelated) return true;
176
-
177
- return (
178
- !node &&
179
- !hasIgnoredOtherModel(currentNode, graphData) &&
180
- !this._hasOption(currentNode, NO_DELETE) &&
181
- !this.shouldUnrelateIgnoreDisable(currentNode) &&
182
- // Rows that the graph keeps in another place must not be deleted (#214).
183
- !graphData.keptNodeForRow(currentNode)
184
- );
185
- }
186
-
187
- shouldInsertOrRelate(node, graphData) {
188
- return this.shouldInsert(node, graphData) || this.shouldRelate(node, graphData);
77
+ // Whether a node that the graph relates or patches is written with `patch()`
78
+ // or `update()`. With `noUpdate`, it's neither, and only the foreign keys of
79
+ // its relates and BelongsToOneRelations are patched.
80
+ shouldPatch(node) {
81
+ return !this.has(node, NO_UPDATE) && !this.has(node, UPDATE);
189
82
  }
190
83
 
191
- // The graph ignores models that it neither inserts, relates nor patches, like
192
- // models marked with `#unrelate` or `#delete` and models skipped by `noInsert`
193
- // or `noRelate`. Inserted models have ids and can look like models to relate
194
- // afterwards, so we check that they were inserted too.
195
- shouldIgnore(node, graphData) {
196
- return (
197
- !node.userData.inserted &&
198
- !this.shouldInsertOrRelate(node, graphData) &&
199
- !this.shouldPatchOrUpdateIgnoreDisable(node, graphData)
200
- );
201
- }
202
-
203
- shouldDeleteOrUnrelate(currentNode, graphData) {
204
- return this.shouldDelete(currentNode, graphData) || this.shouldUnrelate(currentNode, graphData);
84
+ shouldUpdate(node) {
85
+ return !this.has(node, NO_UPDATE) && this.has(node, UPDATE);
205
86
  }
206
87
 
207
88
  allowRefs() {
@@ -212,38 +93,37 @@ class GraphOptions {
212
93
  return !!this.options[ALLOW_MARKERS];
213
94
  }
214
95
 
215
- // The options of recursive upserts keep matching against the relation paths
216
- // of the original graph, which the relation paths of the new root lead to.
217
- rebasedOptions(newRoot) {
218
- return new GraphOptions(this.options, [...this.rootRelationPath, ...newRoot.relationPath]);
96
+ // Whether the option applies to the relation path of `node`.
97
+ has(node, optionName) {
98
+ const option = this.options[optionName];
99
+
100
+ return isRelationExpression(option)
101
+ ? matchesRelationExpression(option, node.relationPath)
102
+ : Array.isArray(option)
103
+ ? option.includes(node.relationPathKey)
104
+ : !!option;
219
105
  }
106
+ }
220
107
 
221
- _hasOption(node, optionName) {
222
- const option = this.options[optionName];
108
+ // Reject misspelled options and options of `upsertGraph` passed to
109
+ // `insertGraph`, which would otherwise be ignored silently (#84). Options set
110
+ // to `undefined` count as not set.
111
+ function checkForUnknownOptions(options, insertOnly) {
112
+ const [method, optionNames] = insertOnly
113
+ ? ['insertGraph', INSERT_OPTION_NAMES]
114
+ : ['upsertGraph', OPTION_NAMES];
223
115
 
224
- if (isRelationExpression(option)) {
225
- return matchesRelationExpression(option, this._relationPath(node));
226
- } else if (Array.isArray(option)) {
227
- return option.includes(this._relationPath(node).join('.'));
228
- } else if (typeof option === 'boolean') {
229
- return option;
230
- } else if (option === undefined) {
231
- return false;
232
- } else {
116
+ for (const [name, value] of Object.entries(options)) {
117
+ if (value !== undefined && !optionNames.includes(name)) {
233
118
  throw new Error(
234
- `expected ${optionName} option value "${option}" to be a boolean, an array of relation paths or a relation expression`,
119
+ `"${name}" is not an option of ${method}(). Valid options are: ${optionNames.join(', ')}.`,
235
120
  );
236
121
  }
237
122
  }
238
-
239
- // The relation path of the node in the original graph, see `rebasedOptions()`.
240
- _relationPath(node) {
241
- return [...this.rootRelationPath, ...node.relationPath];
242
- }
243
123
  }
244
124
 
245
125
  // Parses the string values of the node options into relation expressions,
246
- // once per operation.
126
+ // once per operation, and checks the types of the other values.
247
127
  function parseRelationExpressions(options) {
248
128
  let parsedOptions = options;
249
129
 
@@ -256,6 +136,10 @@ function parseRelationExpressions(options) {
256
136
  }
257
137
 
258
138
  parsedOptions[optionName] = parseRelationExpression(optionName, option);
139
+ } else if (!(option === undefined || typeof option === 'boolean' || Array.isArray(option))) {
140
+ throw new Error(
141
+ `expected ${optionName} option value "${option}" to be a boolean, an array of relation paths or a relation expression`,
142
+ );
259
143
  }
260
144
  }
261
145
 
@@ -288,14 +172,6 @@ function parseRelationExpression(optionName, expression) {
288
172
  throw createError(`aliases like "${alias.$relation} as ${alias.$name}" are not supported`);
289
173
  }
290
174
 
291
- // `*` and `^` only have a meaning at the end of a relation path. Anywhere else
292
- // they are parsed as relation names that would never match.
293
- const token = findNode(parsedExpression.node, (node) => /^(\*|\^\d*)$/.test(node.$relation));
294
-
295
- if (token) {
296
- throw createError(`"${token.$relation}" is only supported at the end of a relation path`);
297
- }
298
-
299
175
  return parsedExpression;
300
176
  }
301
177
 
@@ -342,72 +218,3 @@ function matchesRelationExpression(expression, relationPath) {
342
218
  function isRelationExpression(value) {
343
219
  return !!value && value.isObjectionRelationExpression === true;
344
220
  }
345
-
346
- function getCurrentNode(node, graphData) {
347
- if (!graphData || !node) {
348
- return null;
349
- }
350
-
351
- return graphData.currentGraph.nodeForNode(node);
352
- }
353
-
354
- // For `HasMany` and `HasOne` relations, the relate prop is the id of the
355
- // related model. Use the id state of the input graph, since the parent's
356
- // insert copies the foreign key into the related model, which can complete
357
- // a composite id that contains it and would turn an insert into a relate (#2544).
358
- function hasRelateProp(node) {
359
- const relation = node.parentEdge.relation;
360
-
361
- if (relation.isObjectionHasManyRelation) {
362
- return node.hasId;
363
- } else {
364
- return relation.hasRelateProp(node.obj);
365
- }
366
- }
367
-
368
- // An ignored model that isn't the current model of a BelongsToOneRelation
369
- // doesn't replace it, so we keep the current one instead of removing it as
370
- // missing.
371
- function hasIgnoredOtherModel(currentNode, graphData) {
372
- const { relation } = currentNode.parentEdge;
373
- const parentNode = getNode(currentNode.parentNode, graphData.graph);
374
-
375
- return (
376
- relation.isObjectionBelongsToOneRelation &&
377
- !!parentNode &&
378
- parentNode.edges.some(
379
- (edge) =>
380
- edge.relation === relation &&
381
- edge.isOwnerNode(parentNode) &&
382
- graphData.graphOptions.shouldIgnore(edge.relatedNode, graphData),
383
- )
384
- );
385
- }
386
-
387
- // A row that the graph keeps in another place is unrelated here instead of
388
- // being deleted, unless it's a has-many row that the graph relates through the
389
- // same relation: That relate moves it by overwriting the foreign key, which
390
- // may not be nullable.
391
- function shouldUnrelateKeptRow(currentNode, graphData) {
392
- const keptNode = graphData.keptNodeForRow(currentNode);
393
- const { relation } = currentNode.parentEdge;
394
-
395
- return (
396
- !!keptNode &&
397
- !(relation.isObjectionHasManyRelation && keptNode.parentEdge?.relation === relation)
398
- );
399
- }
400
-
401
- function getNode(currentNode, graph) {
402
- if (!graph || !currentNode) {
403
- return null;
404
- }
405
-
406
- return graph.nodeForNode(currentNode);
407
- }
408
-
409
- module.exports = {
410
- GraphOptions,
411
- OPTION_NAMES,
412
- FetchStrategy,
413
- };