@abedmurrar/objection 0.0.0-stage → 4.0.0-beta.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 +105 -2
  3. package/lib/index.mjs +6 -0
  4. package/lib/initialize.js +11 -0
  5. package/lib/model/AjvValidator.js +278 -0
  6. package/lib/model/Model.js +772 -0
  7. package/lib/model/ModifierNotFoundError.js +10 -0
  8. package/lib/model/NotFoundError.js +19 -0
  9. package/lib/model/RelationDoesNotExistError.js +11 -0
  10. package/lib/model/ValidationError.js +46 -0
  11. package/lib/model/Validator.js +20 -0
  12. package/lib/model/getModel.js +7 -0
  13. package/lib/model/graph/ModelGraph.js +60 -0
  14. package/lib/model/graph/ModelGraphBuilder.js +188 -0
  15. package/lib/model/graph/ModelGraphEdge.js +40 -0
  16. package/lib/model/graph/ModelGraphNode.js +201 -0
  17. package/lib/model/inheritModel.js +19 -0
  18. package/lib/model/modelBindKnex.js +69 -0
  19. package/lib/model/modelClone.js +102 -0
  20. package/lib/model/modelColPropMap.js +24 -0
  21. package/lib/model/modelConcurrency.js +24 -0
  22. package/lib/model/modelId.js +57 -0
  23. package/lib/model/modelJsonAttributes.js +72 -0
  24. package/lib/model/modelParseRelations.js +83 -0
  25. package/lib/model/modelQueryProps.js +116 -0
  26. package/lib/model/modelSet.js +116 -0
  27. package/lib/model/modelTableMetadata.js +71 -0
  28. package/lib/model/modelToJson.js +157 -0
  29. package/lib/model/modelUtils.js +40 -0
  30. package/lib/model/modelValidate.js +40 -0
  31. package/lib/model/modelValues.js +113 -0
  32. package/lib/model/modelVisitor.js +34 -0
  33. package/lib/objection.js +89 -0
  34. package/lib/queryBuilder/FunctionBuilder.js +291 -0
  35. package/lib/queryBuilder/InternalOptions.js +24 -0
  36. package/lib/queryBuilder/JoinBuilder.js +124 -0
  37. package/lib/queryBuilder/QueryBuilder.js +1136 -0
  38. package/lib/queryBuilder/QueryBuilderBase.js +666 -0
  39. package/lib/queryBuilder/QueryBuilderContext.js +20 -0
  40. package/lib/queryBuilder/QueryBuilderContextBase.js +26 -0
  41. package/lib/queryBuilder/QueryBuilderOperationSupport.js +484 -0
  42. package/lib/queryBuilder/QueryBuilderUserContext.js +25 -0
  43. package/lib/queryBuilder/RawBuilder.js +78 -0
  44. package/lib/queryBuilder/ReferenceBuilder.js +246 -0
  45. package/lib/queryBuilder/RelationExpression.js +373 -0
  46. package/lib/queryBuilder/StaticHookArguments.js +122 -0
  47. package/lib/queryBuilder/ValueBuilder.js +115 -0
  48. package/lib/queryBuilder/expressions.js +121 -0
  49. package/lib/queryBuilder/functions/mysql.js +762 -0
  50. package/lib/queryBuilder/graph/GraphAction.js +54 -0
  51. package/lib/queryBuilder/graph/GraphData.js +12 -0
  52. package/lib/queryBuilder/graph/GraphFetcher.js +155 -0
  53. package/lib/queryBuilder/graph/GraphNodeDbExistence.js +146 -0
  54. package/lib/queryBuilder/graph/GraphOperation.js +21 -0
  55. package/lib/queryBuilder/graph/GraphOptions.js +165 -0
  56. package/lib/queryBuilder/graph/GraphUpsert.js +231 -0
  57. package/lib/queryBuilder/graph/delete/GraphDelete.js +15 -0
  58. package/lib/queryBuilder/graph/delete/GraphDeleteAction.js +56 -0
  59. package/lib/queryBuilder/graph/insert/GraphInsert.js +116 -0
  60. package/lib/queryBuilder/graph/insert/GraphInsertAction.js +183 -0
  61. package/lib/queryBuilder/graph/insert/JoinRowGraphInsertAction.js +70 -0
  62. package/lib/queryBuilder/graph/patch/GraphPatch.js +15 -0
  63. package/lib/queryBuilder/graph/patch/GraphPatchAction.js +205 -0
  64. package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsert.js +26 -0
  65. package/lib/queryBuilder/graph/recursiveUpsert/GraphRecursiveUpsertAction.js +96 -0
  66. package/lib/queryBuilder/join/JoinResultColumn.js +19 -0
  67. package/lib/queryBuilder/join/JoinResultParser.js +106 -0
  68. package/lib/queryBuilder/join/RelationJoiner.js +258 -0
  69. package/lib/queryBuilder/join/TableNode.js +136 -0
  70. package/lib/queryBuilder/join/TableTree.js +69 -0
  71. package/lib/queryBuilder/join/utils.js +19 -0
  72. package/lib/queryBuilder/operations/DelegateOperation.js +101 -0
  73. package/lib/queryBuilder/operations/DeleteOperation.js +47 -0
  74. package/lib/queryBuilder/operations/FindByIdOperation.js +29 -0
  75. package/lib/queryBuilder/operations/FindByIdsOperation.js +23 -0
  76. package/lib/queryBuilder/operations/FindOperation.js +147 -0
  77. package/lib/queryBuilder/operations/FirstOperation.js +22 -0
  78. package/lib/queryBuilder/operations/FromOperation.js +91 -0
  79. package/lib/queryBuilder/operations/InsertAndFetchOperation.js +38 -0
  80. package/lib/queryBuilder/operations/InsertGraphAndFetchOperation.js +35 -0
  81. package/lib/queryBuilder/operations/InsertGraphOperation.js +87 -0
  82. package/lib/queryBuilder/operations/InsertOperation.js +173 -0
  83. package/lib/queryBuilder/operations/InstanceDeleteOperation.js +37 -0
  84. package/lib/queryBuilder/operations/InstanceFindOperation.js +17 -0
  85. package/lib/queryBuilder/operations/InstanceInsertOperation.js +20 -0
  86. package/lib/queryBuilder/operations/InstanceUpdateOperation.js +45 -0
  87. package/lib/queryBuilder/operations/JoinRelatedOperation.js +83 -0
  88. package/lib/queryBuilder/operations/KnexOperation.js +11 -0
  89. package/lib/queryBuilder/operations/MergeOperation.js +43 -0
  90. package/lib/queryBuilder/operations/ObjectionToKnexConvertingOperation.js +173 -0
  91. package/lib/queryBuilder/operations/OnBuildKnexOperation.js +23 -0
  92. package/lib/queryBuilder/operations/OnBuildOperation.js +23 -0
  93. package/lib/queryBuilder/operations/OnErrorOperation.js +23 -0
  94. package/lib/queryBuilder/operations/QueryBuilderOperation.js +258 -0
  95. package/lib/queryBuilder/operations/RangeOperation.js +37 -0
  96. package/lib/queryBuilder/operations/RelateOperation.js +20 -0
  97. package/lib/queryBuilder/operations/ReturningOperation.js +24 -0
  98. package/lib/queryBuilder/operations/RunAfterOperation.js +23 -0
  99. package/lib/queryBuilder/operations/RunBeforeOperation.js +23 -0
  100. package/lib/queryBuilder/operations/UnrelateOperation.js +18 -0
  101. package/lib/queryBuilder/operations/UpdateAndFetchOperation.js +63 -0
  102. package/lib/queryBuilder/operations/UpdateOperation.js +130 -0
  103. package/lib/queryBuilder/operations/UpsertGraphAndFetchOperation.js +35 -0
  104. package/lib/queryBuilder/operations/UpsertGraphOperation.js +47 -0
  105. package/lib/queryBuilder/operations/WhereCompositeOperation.js +48 -0
  106. package/lib/queryBuilder/operations/WhereInOperation.js +46 -0
  107. package/lib/queryBuilder/operations/eager/EagerOperation.js +46 -0
  108. package/lib/queryBuilder/operations/eager/JoinEagerOperation.js +41 -0
  109. package/lib/queryBuilder/operations/eager/NaiveEagerOperation.js +10 -0
  110. package/lib/queryBuilder/operations/eager/WhereInEagerOperation.js +227 -0
  111. package/lib/queryBuilder/operations/jsonApi/WhereJsonHasOperation.js +29 -0
  112. package/lib/queryBuilder/operations/jsonApi/WhereJsonNotObjectOperation.js +37 -0
  113. package/lib/queryBuilder/operations/jsonApi/WhereJsonOperation.js +30 -0
  114. package/lib/queryBuilder/operations/jsonApi/WhereJsonOverlapsOperation.js +33 -0
  115. package/lib/queryBuilder/operations/jsonApi/mysqlJsonApi.js +155 -0
  116. package/lib/queryBuilder/operations/jsonApi/postgresJsonApi.js +135 -0
  117. package/lib/queryBuilder/operations/select/SelectOperation.js +50 -0
  118. package/lib/queryBuilder/operations/select/Selection.js +112 -0
  119. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeMsSqlOperation.js +84 -0
  120. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeOperation.js +90 -0
  121. package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeSqliteOperation.js +95 -0
  122. package/lib/queryBuilder/parsers/jsonFieldExpressionParser.js +787 -0
  123. package/lib/queryBuilder/parsers/relationExpressionParser.js +930 -0
  124. package/lib/queryBuilder/transformations/CompositeQueryTransformation.js +17 -0
  125. package/lib/queryBuilder/transformations/QueryTransformation.js +9 -0
  126. package/lib/queryBuilder/transformations/WrapMysqlModifySubqueryTransformation.js +57 -0
  127. package/lib/queryBuilder/transformations/index.js +9 -0
  128. package/lib/queryBuilder/whereScopes.js +199 -0
  129. package/lib/relations/Relation.js +317 -0
  130. package/lib/relations/RelationDeleteOperation.js +23 -0
  131. package/lib/relations/RelationFindOperation.js +116 -0
  132. package/lib/relations/RelationInsertOperation.js +25 -0
  133. package/lib/relations/RelationOwner.js +199 -0
  134. package/lib/relations/RelationProperty.js +207 -0
  135. package/lib/relations/RelationUpdateOperation.js +23 -0
  136. package/lib/relations/belongsToOne/BelongsToOneDeleteOperation.js +18 -0
  137. package/lib/relations/belongsToOne/BelongsToOneInsertOperation.js +33 -0
  138. package/lib/relations/belongsToOne/BelongsToOneRelateOperation.js +39 -0
  139. package/lib/relations/belongsToOne/BelongsToOneRelation.js +45 -0
  140. package/lib/relations/belongsToOne/BelongsToOneUnrelateOperation.js +46 -0
  141. package/lib/relations/hasMany/HasManyInsertOperation.js +45 -0
  142. package/lib/relations/hasMany/HasManyRelateOperation.js +48 -0
  143. package/lib/relations/hasMany/HasManyRelation.js +41 -0
  144. package/lib/relations/hasMany/HasManyUnrelateOperation.js +26 -0
  145. package/lib/relations/hasOne/HasOneRelation.js +10 -0
  146. package/lib/relations/hasOneThrough/HasOneThroughRelation.js +10 -0
  147. package/lib/relations/manyToMany/ManyToManyModifyMixin.js +82 -0
  148. package/lib/relations/manyToMany/ManyToManyRelation.js +405 -0
  149. package/lib/relations/manyToMany/ManyToManySqliteModifyMixin.js +29 -0
  150. package/lib/relations/manyToMany/delete/ManyToManyDeleteOperation.js +8 -0
  151. package/lib/relations/manyToMany/delete/ManyToManyDeleteOperationBase.js +20 -0
  152. package/lib/relations/manyToMany/delete/ManyToManyDeleteSqliteOperation.js +8 -0
  153. package/lib/relations/manyToMany/find/ManyToManyFindOperation.js +82 -0
  154. package/lib/relations/manyToMany/insert/ManyToManyInsertOperation.js +40 -0
  155. package/lib/relations/manyToMany/relate/ManyToManyRelateOperation.js +64 -0
  156. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperation.js +11 -0
  157. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperationBase.js +27 -0
  158. package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateSqliteOperation.js +11 -0
  159. package/lib/relations/manyToMany/update/ManyToManyUpdateOperation.js +8 -0
  160. package/lib/relations/manyToMany/update/ManyToManyUpdateOperationBase.js +65 -0
  161. package/lib/relations/manyToMany/update/ManyToManyUpdateSqliteOperation.js +8 -0
  162. package/lib/transaction.js +66 -0
  163. package/lib/utils/assert.js +19 -0
  164. package/lib/utils/buildUtils.js +19 -0
  165. package/lib/utils/classUtils.js +9 -0
  166. package/lib/utils/cloneDeep.js +365 -0
  167. package/lib/utils/createModifier.js +37 -0
  168. package/lib/utils/deprecate.js +11 -0
  169. package/lib/utils/identifierMapping.js +188 -0
  170. package/lib/utils/internalPropUtils.js +7 -0
  171. package/lib/utils/knexUtils.js +81 -0
  172. package/lib/utils/mixin.js +18 -0
  173. package/lib/utils/mysqlJsonPath.js +70 -0
  174. package/lib/utils/normalizeIds.js +127 -0
  175. package/lib/utils/objectUtils.js +314 -0
  176. package/lib/utils/parseFieldExpression.js +73 -0
  177. package/lib/utils/postgresJsonPath.js +96 -0
  178. package/lib/utils/promiseUtils/after.js +14 -0
  179. package/lib/utils/promiseUtils/afterReturn.js +14 -0
  180. package/lib/utils/promiseUtils/index.js +15 -0
  181. package/lib/utils/promiseUtils/isPromise.js +8 -0
  182. package/lib/utils/promiseUtils/map.js +96 -0
  183. package/lib/utils/promiseUtils/mapAfterAllReturn.js +23 -0
  184. package/lib/utils/promiseUtils/try.js +19 -0
  185. package/lib/utils/resolveModel.js +104 -0
  186. package/lib/utils/tmpColumnUtils.js +7 -0
  187. package/package.json +113 -4
  188. package/typings/objection/index.d.mts +362 -0
  189. package/typings/objection/index.d.ts +4131 -0
  190. package/typings/objection/relation-expression.d.ts +237 -0
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+ class ModifierNotFoundError extends Error {
3
+ constructor(modifierName) {
4
+ super(`Unable to determine modify function from provided value: "${modifierName}".`);
5
+ this.modifierName = modifierName;
6
+ }
7
+ }
8
+ module.exports = {
9
+ ModifierNotFoundError,
10
+ };
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+ class NotFoundError extends Error {
3
+ constructor({ modelClass, data = {}, statusCode = 404, ...rest } = {}) {
4
+ super(rest.message || 'NotFoundError');
5
+ this.type = 'NotFound';
6
+ this.name = this.constructor.name;
7
+ this.data = { ...rest, ...data };
8
+ this.statusCode = statusCode;
9
+ // Add as non-enumerable in case people are passing instances of
10
+ // this error directly to `JSON.stringify`.
11
+ Object.defineProperty(this, 'modelClass', {
12
+ value: modelClass,
13
+ enumerable: false,
14
+ });
15
+ }
16
+ }
17
+ module.exports = {
18
+ NotFoundError,
19
+ };
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+ class RelationDoesNotExistError extends Error {
3
+ constructor(relationName) {
4
+ super(`unknown relation "${relationName}" in a relation expression`);
5
+ this.name = this.constructor.name;
6
+ this.relationName = relationName;
7
+ }
8
+ }
9
+ module.exports = {
10
+ RelationDoesNotExistError,
11
+ };
@@ -0,0 +1,46 @@
1
+ 'use strict';
2
+ const { asArray, isString } = require('../utils/objectUtils');
3
+ const ValidationErrorType = {
4
+ ModelValidation: 'ModelValidation',
5
+ RelationExpression: 'RelationExpression',
6
+ UnallowedRelation: 'UnallowedRelation',
7
+ InvalidGraph: 'InvalidGraph',
8
+ };
9
+ class ValidationError extends Error {
10
+ static get Type() {
11
+ return ValidationErrorType;
12
+ }
13
+ constructor({ type, message, modelClass, data = {}, statusCode = 400 }) {
14
+ super(message || errorsToMessage(data));
15
+ this.name = this.constructor.name;
16
+ this.type = type;
17
+ this.data = data;
18
+ this.statusCode = statusCode;
19
+ // Add as non-enumerable in case people are passing instances of
20
+ // this error directly to `JSON.stringify`.
21
+ Object.defineProperty(this, 'modelClass', {
22
+ value: modelClass,
23
+ enumerable: false,
24
+ });
25
+ }
26
+ }
27
+ function errorsToMessage(data) {
28
+ return Object.keys(data)
29
+ .reduce((messages, key) => {
30
+ messages.push(`${key}: ${asArray(data[key]).map(message).join(', ')}`);
31
+ return messages;
32
+ }, [])
33
+ .join(', ');
34
+ }
35
+ function message(it) {
36
+ if (isString(it)) {
37
+ return it;
38
+ }
39
+ else {
40
+ return it.message;
41
+ }
42
+ }
43
+ module.exports = {
44
+ ValidationError,
45
+ ValidationErrorType,
46
+ };
@@ -0,0 +1,20 @@
1
+ 'use strict';
2
+ class Validator {
3
+ constructor(...args) {
4
+ this.constructor.init(this, ...args);
5
+ }
6
+ static init() { }
7
+ beforeValidate({ model, json, options }) {
8
+ model.$beforeValidate(null, json, options);
9
+ }
10
+ validate() {
11
+ /* istanbul ignore next */
12
+ throw new Error('not implemented');
13
+ }
14
+ afterValidate({ model, json, options }) {
15
+ model.$afterValidate(json, options);
16
+ }
17
+ }
18
+ module.exports = {
19
+ Validator,
20
+ };
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+ // A small helper method for cached lazy-importing of the Model class.
3
+ let Model;
4
+ const getModel = () => Model || (Model = require('./Model').Model);
5
+ module.exports = {
6
+ getModel,
7
+ };
@@ -0,0 +1,60 @@
1
+ 'use strict';
2
+ const { ModelGraphBuilder } = require('./ModelGraphBuilder');
3
+ const NOT_CALCULATED = {};
4
+ class ModelGraph {
5
+ constructor(nodes, edges) {
6
+ this.nodes = nodes;
7
+ this.edges = edges;
8
+ // These are calculated lazily.
9
+ this._nodesByObjects = NOT_CALCULATED;
10
+ this._nodesByIdPathKeys = NOT_CALCULATED;
11
+ }
12
+ static create(rootModelClass, roots) {
13
+ const builder = ModelGraphBuilder.buildGraph(rootModelClass, roots);
14
+ return new ModelGraph(builder.nodes, builder.edges);
15
+ }
16
+ static createEmpty() {
17
+ return new ModelGraph([], []);
18
+ }
19
+ get rootObjects() {
20
+ return this.nodes.filter((node) => !node.parentEdge).map((node) => node.obj);
21
+ }
22
+ nodeForObject(obj) {
23
+ if (!obj) {
24
+ return null;
25
+ }
26
+ if (this._nodesByObjects === NOT_CALCULATED) {
27
+ this._nodesByObjects = createNodesByObjectsMap(this.nodes);
28
+ }
29
+ return this._nodesByObjects.get(obj) || null;
30
+ }
31
+ nodeForNode(node) {
32
+ if (!node) {
33
+ return null;
34
+ }
35
+ if (this._nodesByIdPathKeys === NOT_CALCULATED) {
36
+ this._nodesByIdPathKeys = createNodesByIdPathKeysMap(this.nodes);
37
+ }
38
+ return this._nodesByIdPathKeys.get(node.idPathKey) || null;
39
+ }
40
+ }
41
+ function createNodesByObjectsMap(nodes) {
42
+ const nodesByObjects = new Map();
43
+ for (const node of nodes) {
44
+ nodesByObjects.set(node.obj, node);
45
+ }
46
+ return nodesByObjects;
47
+ }
48
+ function createNodesByIdPathKeysMap(nodes) {
49
+ const nodesByIdPathKeys = new Map();
50
+ for (const node of nodes) {
51
+ const idPathKey = node.idPathKey;
52
+ if (idPathKey !== null) {
53
+ nodesByIdPathKeys.set(idPathKey, node);
54
+ }
55
+ }
56
+ return nodesByIdPathKeys;
57
+ }
58
+ module.exports = {
59
+ ModelGraph,
60
+ };
@@ -0,0 +1,188 @@
1
+ 'use strict';
2
+ const { isObject, isString, asArray, asSingle } = require('../../utils/objectUtils');
3
+ const { ValidationErrorType } = require('../../model/ValidationError');
4
+ const { ModelGraphNode } = require('./ModelGraphNode');
5
+ const { ModelGraphEdge } = require('./ModelGraphEdge');
6
+ class ModelGraphBuilder {
7
+ constructor() {
8
+ this.nodes = [];
9
+ this.edges = [];
10
+ }
11
+ static buildGraph(rootModelClass, roots) {
12
+ const builder = new this();
13
+ builder._buildGraph(rootModelClass, roots);
14
+ return builder;
15
+ }
16
+ _buildGraph(rootModelClass, roots) {
17
+ if (roots) {
18
+ if (Array.isArray(roots)) {
19
+ this._buildNodes(rootModelClass, roots);
20
+ }
21
+ else if (isObject(roots)) {
22
+ this._buildNode(rootModelClass, roots);
23
+ }
24
+ else {
25
+ throw createNotModelError(rootModelClass, roots);
26
+ }
27
+ }
28
+ this._buildReferences();
29
+ }
30
+ _buildNodes(modelClass, objs, parentNode = null, relation = null) {
31
+ objs = asArray(objs);
32
+ objs.forEach((obj, index) => {
33
+ this._buildNode(modelClass, obj, parentNode, relation, index);
34
+ });
35
+ }
36
+ _buildNode(modelClass, obj, parentNode = null, relation = null, index = null) {
37
+ obj = asSingle(obj);
38
+ if (!isObject(obj) || !obj.$isObjectionModel) {
39
+ throw createNotModelError(modelClass, obj);
40
+ }
41
+ const node = new ModelGraphNode(modelClass, obj);
42
+ this.nodes.push(node);
43
+ if (parentNode) {
44
+ const edge = new ModelGraphEdge(ModelGraphEdge.Type.Relation, parentNode, node, relation, index);
45
+ node.parentEdge = edge;
46
+ this._addEdge(parentNode, node, edge);
47
+ }
48
+ this._buildRelationNodes(node);
49
+ }
50
+ _buildRelationNodes(node) {
51
+ for (const relationName of node.modelClass.getRelationNames()) {
52
+ const relatedObjects = node.obj[relationName];
53
+ if (!relatedObjects) {
54
+ continue;
55
+ }
56
+ const relation = node.modelClass.getRelation(relationName);
57
+ if (relation.isOneToOne()) {
58
+ this._buildNode(relation.relatedModelClass, relatedObjects, node, relation);
59
+ }
60
+ else {
61
+ this._buildNodes(relation.relatedModelClass, relatedObjects, node, relation);
62
+ }
63
+ }
64
+ }
65
+ _buildReferences() {
66
+ const nodesByUid = this._nodesByUid();
67
+ this._buildObjectReferences(nodesByUid);
68
+ this._buildPropertyReferences(nodesByUid);
69
+ }
70
+ _nodesByUid() {
71
+ const nodesByUid = new Map();
72
+ for (const node of this.nodes) {
73
+ const uid = node.uid;
74
+ if (uid === undefined) {
75
+ continue;
76
+ }
77
+ nodesByUid.set(uid, node);
78
+ }
79
+ return nodesByUid;
80
+ }
81
+ _buildObjectReferences(nodesByUid) {
82
+ for (const node of this.nodes) {
83
+ const ref = node.reference;
84
+ if (ref === undefined) {
85
+ continue;
86
+ }
87
+ const refNode = nodesByUid.get(ref);
88
+ if (!refNode) {
89
+ throw createReferenceNotFoundError(ref);
90
+ }
91
+ const edge = new ModelGraphEdge(ModelGraphEdge.Type.Reference, node, refNode);
92
+ edge.refType = ModelGraphEdge.ReferenceType.Object;
93
+ this._addEdge(node, refNode, edge);
94
+ }
95
+ }
96
+ _buildPropertyReferences(nodesByUid) {
97
+ for (const node of this.nodes) {
98
+ forEachPropertyReference(node.obj, ({ path, refMatch, ref, refPath }) => {
99
+ const refNode = nodesByUid.get(ref);
100
+ if (!refNode) {
101
+ throw createReferenceNotFoundError(ref);
102
+ }
103
+ const edge = new ModelGraphEdge(ModelGraphEdge.Type.Reference, node, refNode);
104
+ edge.refType = ModelGraphEdge.ReferenceType.Property;
105
+ edge.refMatch = refMatch;
106
+ edge.refOwnerDataPath = path.slice();
107
+ edge.refRelatedDataPath = refPath;
108
+ this._addEdge(node, refNode, edge);
109
+ });
110
+ }
111
+ }
112
+ _addEdge(ownerNode, relatedNode, edge) {
113
+ this.edges.push(edge);
114
+ ownerNode.edges.push(edge);
115
+ relatedNode.edges.push(edge);
116
+ if (edge.type === ModelGraphEdge.Type.Reference) {
117
+ ownerNode.refEdges.push(edge);
118
+ relatedNode.refEdges.push(edge);
119
+ }
120
+ }
121
+ }
122
+ function forEachPropertyReference(obj, callback) {
123
+ const modelClass = obj.constructor;
124
+ const relationNames = modelClass.getRelationNames();
125
+ for (const prop of Object.keys(obj)) {
126
+ if (relationNames.includes(prop)) {
127
+ continue;
128
+ }
129
+ visitStrings(obj[prop], [prop], (str, path) => {
130
+ forEachMatch(modelClass.propRefRegex, str, (match) => {
131
+ const [refMatch, ref, refPath] = match;
132
+ callback({ path, refMatch, ref, refPath: refPath.trim().split('.') });
133
+ });
134
+ });
135
+ }
136
+ }
137
+ function visitStrings(value, path, visit) {
138
+ if (Array.isArray(value)) {
139
+ visitStringsInArray(value, path, visit);
140
+ }
141
+ else if (isObject(value) && !Buffer.isBuffer(value)) {
142
+ visitStringsInObject(value, path, visit);
143
+ }
144
+ else if (isString(value)) {
145
+ visit(value, path);
146
+ }
147
+ }
148
+ function visitStringsInArray(value, path, visit) {
149
+ for (let i = 0; i < value.length; ++i) {
150
+ path.push(i);
151
+ visitStrings(value[i], path, visit);
152
+ path.pop();
153
+ }
154
+ }
155
+ function visitStringsInObject(value, path, visit) {
156
+ for (const prop of Object.keys(value)) {
157
+ path.push(prop);
158
+ visitStrings(value[prop], path, visit);
159
+ path.pop();
160
+ }
161
+ }
162
+ function forEachMatch(regex, str, cb) {
163
+ let matchResult = regex.exec(str);
164
+ while (matchResult) {
165
+ cb(matchResult);
166
+ matchResult = regex.exec(str);
167
+ }
168
+ }
169
+ function createReferenceNotFoundError(ref) {
170
+ return new Error([
171
+ `could not resolve reference ${ref} in a graph.`,
172
+ `If you are sure the #id exist in the same graph,`,
173
+ `this may be due to a limitation that a subgraph under a related item`,
174
+ `cannot reference an item in a subgraph under another related item.`,
175
+ `If you run into this limitation, please open an issue in objection github.`,
176
+ ].join(' '));
177
+ }
178
+ function createNotModelError(modelClass, value) {
179
+ throw modelClass.createValidationError({
180
+ type: ValidationErrorType.InvalidGraph,
181
+ message: `expected value "${value}" to be an instance of ${modelClass.name}`,
182
+ });
183
+ }
184
+ module.exports = {
185
+ ModelGraphBuilder,
186
+ createNotModelError,
187
+ forEachPropertyReference,
188
+ };
@@ -0,0 +1,40 @@
1
+ 'use strict';
2
+ const Type = {
3
+ Relation: 'Relation',
4
+ Reference: 'Reference',
5
+ };
6
+ const ReferenceType = {
7
+ Object: 'Object',
8
+ Property: 'Property',
9
+ };
10
+ class ModelGraphEdge {
11
+ constructor(type, ownerNode, relatedNode, relation = null, relationIndex = null) {
12
+ this.type = type;
13
+ this.ownerNode = ownerNode;
14
+ this.relatedNode = relatedNode;
15
+ this.relation = relation;
16
+ this.relationIndex = relationIndex;
17
+ this.refType = null;
18
+ this.refMatch = null;
19
+ this.refOwnerDataPath = null;
20
+ this.refRelatedDataPath = null;
21
+ }
22
+ static get Type() {
23
+ return Type;
24
+ }
25
+ static get ReferenceType() {
26
+ return ReferenceType;
27
+ }
28
+ getOtherNode(node) {
29
+ return this.isOwnerNode(node) ? this.relatedNode : this.ownerNode;
30
+ }
31
+ isOwnerNode(node) {
32
+ return node === this.ownerNode;
33
+ }
34
+ isRelatedNode(node) {
35
+ return node === this.relatedNode;
36
+ }
37
+ }
38
+ module.exports = {
39
+ ModelGraphEdge,
40
+ };
@@ -0,0 +1,201 @@
1
+ 'use strict';
2
+ const { ModelGraphEdge } = require('./ModelGraphEdge');
3
+ const { isNumber } = require('../../utils/objectUtils');
4
+ const NOT_CALCULATED = {};
5
+ class ModelGraphNode {
6
+ constructor(modelClass, obj) {
7
+ this.modelClass = modelClass;
8
+ this.obj = obj;
9
+ this.edges = [];
10
+ this.userData = {};
11
+ this.hasId = obj.$hasId();
12
+ this.uid = obj[modelClass.uidProp];
13
+ // These are also included in `edges`. These are simply
14
+ // shortcuts for commonly used edges.
15
+ this.refEdges = [];
16
+ this.parentEdge = null;
17
+ // These are calculated lazily.
18
+ this._relationPath = NOT_CALCULATED;
19
+ this._relationPathKey = NOT_CALCULATED;
20
+ this._dataPath = NOT_CALCULATED;
21
+ this._dataPathKey = NOT_CALCULATED;
22
+ this._idPath = NOT_CALCULATED;
23
+ this._idPathKey = NOT_CALCULATED;
24
+ }
25
+ get isReference() {
26
+ return this.reference !== undefined;
27
+ }
28
+ get isDbReference() {
29
+ return this.dbReference !== undefined;
30
+ }
31
+ get reference() {
32
+ return this.obj[this.modelClass.uidRefProp];
33
+ }
34
+ get dbReference() {
35
+ return this.obj[this.modelClass.dbRefProp];
36
+ }
37
+ get parentNode() {
38
+ if (this.parentEdge) {
39
+ return this.parentEdge.ownerNode;
40
+ }
41
+ else {
42
+ return null;
43
+ }
44
+ }
45
+ get indexInRelation() {
46
+ if (this.parentEdge) {
47
+ return this.parentEdge.relationIndex;
48
+ }
49
+ else {
50
+ return null;
51
+ }
52
+ }
53
+ get relationName() {
54
+ if (this.parentEdge) {
55
+ return this.parentEdge.relation.name;
56
+ }
57
+ else {
58
+ return null;
59
+ }
60
+ }
61
+ get relationPath() {
62
+ if (this._relationPath === NOT_CALCULATED) {
63
+ this._relationPath = this._createRelationPath();
64
+ }
65
+ return this._relationPath;
66
+ }
67
+ get relationPathKey() {
68
+ if (this._relationPathKey === NOT_CALCULATED) {
69
+ this._relationPathKey = this._createRelationPathKey();
70
+ }
71
+ return this._relationPathKey;
72
+ }
73
+ get dataPath() {
74
+ if (this._dataPath === NOT_CALCULATED) {
75
+ this._dataPath = this._createDataPath();
76
+ }
77
+ return this._dataPath;
78
+ }
79
+ get dataPathKey() {
80
+ if (this._dataPathKey === NOT_CALCULATED) {
81
+ this._dataPathKey = this._createDataPathKey();
82
+ }
83
+ return this._dataPathKey;
84
+ }
85
+ get idPath() {
86
+ if (this._idPath === NOT_CALCULATED) {
87
+ this._idPath = this._createIdPath();
88
+ }
89
+ return this._idPath;
90
+ }
91
+ get idPathKey() {
92
+ if (this._idPathKey === NOT_CALCULATED) {
93
+ this._idPathKey = this._createIdPathKey();
94
+ }
95
+ return this._idPathKey;
96
+ }
97
+ /**
98
+ * If this node is a reference, returns the referred node.
99
+ */
100
+ get referencedNode() {
101
+ for (const edge of this.refEdges) {
102
+ if (edge.refType === ModelGraphEdge.ReferenceType.Object && edge.isOwnerNode(this)) {
103
+ return edge.relatedNode;
104
+ }
105
+ }
106
+ return null;
107
+ }
108
+ /**
109
+ * Returns all nodes that are references to this node.
110
+ */
111
+ get referencingNodes() {
112
+ const nodes = [];
113
+ for (const edge of this.refEdges) {
114
+ if (edge.refType === ModelGraphEdge.ReferenceType.Object && edge.isRelatedNode(this)) {
115
+ nodes.push(edge.ownerNode);
116
+ }
117
+ }
118
+ return nodes;
119
+ }
120
+ get descendantRelationNodes() {
121
+ return this._collectDescendantRelationNodes([]);
122
+ }
123
+ removeEdge(edge) {
124
+ // Don't allow removing parent edges for now. It would
125
+ // cause all kinds of cache invalidation.
126
+ if (edge === this.parentEdge) {
127
+ throw new Error('cannot remove parent edge');
128
+ }
129
+ this.edges = this.edges.filter((it) => it !== edge);
130
+ this.refEdges = this.refEdges.filter((it) => it !== edge);
131
+ }
132
+ _collectDescendantRelationNodes(nodes) {
133
+ for (const edge of this.edges) {
134
+ if (edge.type === ModelGraphEdge.Type.Relation && edge.isOwnerNode(this)) {
135
+ nodes.push(edge.relatedNode);
136
+ edge.relatedNode._collectDescendantRelationNodes(nodes);
137
+ }
138
+ }
139
+ return nodes;
140
+ }
141
+ _createRelationPath() {
142
+ if (this.parentNode === null) {
143
+ return [];
144
+ }
145
+ else {
146
+ return [...this.parentNode.relationPath, this.relationName];
147
+ }
148
+ }
149
+ _createRelationPathKey() {
150
+ return this.relationPath.join('.');
151
+ }
152
+ _createDataPath() {
153
+ if (this.parentEdge === null) {
154
+ return [];
155
+ }
156
+ else if (this.parentEdge.relation.isOneToOne()) {
157
+ return [...this.parentNode.dataPath, this.relationName];
158
+ }
159
+ else {
160
+ return [...this.parentNode.dataPath, this.relationName, this.indexInRelation];
161
+ }
162
+ }
163
+ _createDataPathKey() {
164
+ const dataPathKey = this.dataPath.reduce((key, it) => {
165
+ if (isNumber(it)) {
166
+ return `${key}[${it}]`;
167
+ }
168
+ else {
169
+ return key ? `${key}.${it}` : it;
170
+ }
171
+ }, '');
172
+ return dataPathKey ? '.' + dataPathKey : dataPathKey;
173
+ }
174
+ _createIdPath() {
175
+ if (!this.obj.$hasId()) {
176
+ return null;
177
+ }
178
+ if (this.parentEdge === null) {
179
+ return [this.obj.$idKey()];
180
+ }
181
+ else {
182
+ const path = this.parentNode.idPath;
183
+ if (path === null) {
184
+ return null;
185
+ }
186
+ return [...path, this.relationName, this.obj.$idKey()];
187
+ }
188
+ }
189
+ _createIdPathKey() {
190
+ const idPath = this.idPath;
191
+ if (idPath) {
192
+ return this.idPath.join('.');
193
+ }
194
+ else {
195
+ return null;
196
+ }
197
+ }
198
+ }
199
+ module.exports = {
200
+ ModelGraphNode,
201
+ };
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+ const cache = new Map();
3
+ function inheritModel(modelClass) {
4
+ let inherit = cache.get(modelClass.name);
5
+ if (!inherit) {
6
+ inherit = createClassInheritor(modelClass.name);
7
+ cache.set(modelClass.name, inherit);
8
+ }
9
+ return inherit(modelClass);
10
+ }
11
+ function createClassInheritor(className) {
12
+ return new Function('BaseClass', `
13
+ 'use strict';
14
+ return class ${className} extends BaseClass {}
15
+ `);
16
+ }
17
+ module.exports = {
18
+ inheritModel,
19
+ };
@@ -0,0 +1,69 @@
1
+ 'use strict';
2
+ const { inheritModel } = require('./inheritModel');
3
+ const { staticHiddenProps } = require('./modelUtils');
4
+ const { defineNonEnumerableProperty } = require('./modelUtils');
5
+ function bindKnex(modelClass, knex) {
6
+ let BoundModelClass = getBoundModelFromCache(modelClass, knex);
7
+ if (BoundModelClass === null) {
8
+ BoundModelClass = inheritModel(modelClass);
9
+ BoundModelClass = copyHiddenProperties(modelClass, BoundModelClass);
10
+ BoundModelClass.knex(knex);
11
+ BoundModelClass = putBoundModelToCache(modelClass, BoundModelClass, knex);
12
+ BoundModelClass = bindRelations(modelClass, BoundModelClass, knex);
13
+ }
14
+ return BoundModelClass;
15
+ }
16
+ function getBoundModelFromCache(modelClass, knex) {
17
+ const cache = getCache(knex);
18
+ const cacheKey = modelClass.uniqueTag();
19
+ return cache.get(cacheKey) || null;
20
+ }
21
+ function getCache(knex) {
22
+ if (!knex.$$objection) {
23
+ createCache(knex);
24
+ }
25
+ return knex.$$objection.boundModels;
26
+ }
27
+ function createCache(knex) {
28
+ defineNonEnumerableProperty(knex, '$$objection', {
29
+ boundModels: new Map(),
30
+ });
31
+ }
32
+ function copyHiddenProperties(modelClass, BoundModelClass) {
33
+ // Bound classes share the validator of the class they are bound from, which
34
+ // is copied below. Create it first, so that a class bound to a new knex
35
+ // instance, like every transaction, doesn't create and compile a validator
36
+ // of its own.
37
+ if (!Object.hasOwn(modelClass, '$$validator')) {
38
+ modelClass.getValidator();
39
+ }
40
+ for (const prop of staticHiddenProps) {
41
+ // $$relations and $$relationArray are handled in separately.
42
+ if (Object.hasOwn(modelClass, prop) && prop !== '$$relations' && prop !== '$$relationArray') {
43
+ defineNonEnumerableProperty(BoundModelClass, prop, modelClass[prop]);
44
+ }
45
+ }
46
+ return BoundModelClass;
47
+ }
48
+ function putBoundModelToCache(modelClass, BoundModelClass, knex) {
49
+ const cache = getCache(knex);
50
+ const cacheKey = modelClass.uniqueTag();
51
+ cache.set(cacheKey, BoundModelClass);
52
+ return BoundModelClass;
53
+ }
54
+ function bindRelations(modelClass, BoundModelClass, knex) {
55
+ const boundRelations = Object.create(null);
56
+ const boundRelationArray = [];
57
+ for (const relationName of modelClass.getRelationNames()) {
58
+ const relation = modelClass.getRelation(relationName);
59
+ const boundRelation = relation.bindKnex(knex);
60
+ boundRelations[relation.name] = boundRelation;
61
+ boundRelationArray.push(boundRelation);
62
+ }
63
+ defineNonEnumerableProperty(BoundModelClass, '$$relations', boundRelations);
64
+ defineNonEnumerableProperty(BoundModelClass, '$$relationArray', boundRelationArray);
65
+ return BoundModelClass;
66
+ }
67
+ module.exports = {
68
+ bindKnex,
69
+ };