@snowtop/ent 0.1.0-alpha16 → 0.1.0-alpha160-test1

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 (176) hide show
  1. package/action/action.d.ts +25 -14
  2. package/action/action.js +22 -7
  3. package/action/executor.d.ts +16 -3
  4. package/action/executor.js +89 -28
  5. package/action/experimental_action.d.ts +25 -16
  6. package/action/experimental_action.js +34 -14
  7. package/action/index.d.ts +4 -1
  8. package/action/index.js +7 -1
  9. package/action/operations.d.ts +126 -0
  10. package/action/operations.js +686 -0
  11. package/action/orchestrator.d.ts +43 -12
  12. package/action/orchestrator.js +461 -101
  13. package/action/relative_value.d.ts +47 -0
  14. package/action/relative_value.js +125 -0
  15. package/action/transaction.d.ts +10 -0
  16. package/action/transaction.js +23 -0
  17. package/auth/auth.d.ts +1 -1
  18. package/core/base.d.ts +56 -23
  19. package/core/base.js +7 -1
  20. package/core/clause.d.ts +103 -39
  21. package/core/clause.js +430 -66
  22. package/core/config.d.ts +13 -3
  23. package/core/config.js +10 -1
  24. package/core/const.d.ts +3 -0
  25. package/core/const.js +6 -0
  26. package/core/context.d.ts +6 -3
  27. package/core/context.js +22 -3
  28. package/core/convert.d.ts +1 -1
  29. package/core/date.js +1 -5
  30. package/core/db.d.ts +12 -8
  31. package/core/db.js +21 -9
  32. package/core/ent.d.ts +99 -95
  33. package/core/ent.js +550 -602
  34. package/core/global_schema.d.ts +7 -0
  35. package/core/global_schema.js +51 -0
  36. package/core/loaders/assoc_count_loader.d.ts +5 -2
  37. package/core/loaders/assoc_count_loader.js +19 -3
  38. package/core/loaders/assoc_edge_loader.d.ts +2 -3
  39. package/core/loaders/assoc_edge_loader.js +23 -17
  40. package/core/loaders/index.d.ts +1 -2
  41. package/core/loaders/index.js +1 -5
  42. package/core/loaders/loader.d.ts +3 -3
  43. package/core/loaders/loader.js +4 -21
  44. package/core/loaders/object_loader.d.ts +30 -9
  45. package/core/loaders/object_loader.js +226 -79
  46. package/core/loaders/query_loader.d.ts +7 -13
  47. package/core/loaders/query_loader.js +60 -24
  48. package/core/loaders/raw_count_loader.d.ts +1 -0
  49. package/core/loaders/raw_count_loader.js +8 -3
  50. package/core/logger.d.ts +1 -1
  51. package/core/logger.js +1 -0
  52. package/core/privacy.d.ts +26 -16
  53. package/core/privacy.js +68 -51
  54. package/core/query/assoc_query.d.ts +3 -2
  55. package/core/query/assoc_query.js +10 -2
  56. package/core/query/custom_clause_query.d.ts +29 -0
  57. package/core/query/custom_clause_query.js +105 -0
  58. package/core/query/custom_query.d.ts +19 -2
  59. package/core/query/custom_query.js +111 -13
  60. package/core/query/index.d.ts +1 -0
  61. package/core/query/index.js +3 -1
  62. package/core/query/query.d.ts +18 -4
  63. package/core/query/query.js +135 -58
  64. package/core/query/shared_assoc_test.d.ts +2 -1
  65. package/core/query/shared_assoc_test.js +186 -55
  66. package/core/query/shared_test.d.ts +9 -2
  67. package/core/query/shared_test.js +529 -236
  68. package/core/query_impl.d.ts +8 -0
  69. package/core/query_impl.js +28 -0
  70. package/core/viewer.d.ts +2 -0
  71. package/core/viewer.js +3 -1
  72. package/graphql/graphql.d.ts +108 -22
  73. package/graphql/graphql.js +183 -137
  74. package/graphql/graphql_field_helpers.d.ts +9 -3
  75. package/graphql/graphql_field_helpers.js +22 -2
  76. package/graphql/index.d.ts +2 -2
  77. package/graphql/index.js +5 -5
  78. package/graphql/query/connection_type.d.ts +9 -9
  79. package/graphql/query/shared_assoc_test.js +1 -1
  80. package/graphql/query/shared_edge_connection.js +1 -19
  81. package/graphql/scalars/orderby_direction.d.ts +2 -0
  82. package/graphql/scalars/orderby_direction.js +15 -0
  83. package/imports/dataz/example1/_auth.js +128 -47
  84. package/imports/dataz/example1/_viewer.js +87 -39
  85. package/imports/index.d.ts +7 -2
  86. package/imports/index.js +20 -5
  87. package/index.d.ts +23 -5
  88. package/index.js +35 -10
  89. package/package.json +19 -19
  90. package/parse_schema/parse.d.ts +33 -9
  91. package/parse_schema/parse.js +182 -33
  92. package/schema/base_schema.d.ts +13 -3
  93. package/schema/base_schema.js +13 -0
  94. package/schema/field.d.ts +78 -21
  95. package/schema/field.js +232 -72
  96. package/schema/index.d.ts +2 -2
  97. package/schema/index.js +7 -2
  98. package/schema/json_field.d.ts +16 -4
  99. package/schema/json_field.js +32 -2
  100. package/schema/schema.d.ts +109 -20
  101. package/schema/schema.js +42 -53
  102. package/schema/struct_field.d.ts +15 -3
  103. package/schema/struct_field.js +117 -22
  104. package/schema/union_field.d.ts +1 -1
  105. package/scripts/custom_compiler.js +12 -8
  106. package/scripts/custom_graphql.js +171 -64
  107. package/scripts/migrate_v0.1.js +36 -0
  108. package/scripts/move_types.js +120 -0
  109. package/scripts/read_schema.js +22 -7
  110. package/testutils/action/complex_schemas.d.ts +69 -0
  111. package/testutils/action/complex_schemas.js +405 -0
  112. package/testutils/builder.d.ts +37 -41
  113. package/testutils/builder.js +66 -46
  114. package/testutils/db/fixture.d.ts +10 -0
  115. package/testutils/db/fixture.js +26 -0
  116. package/testutils/db/{test_db.d.ts → temp_db.d.ts} +32 -8
  117. package/testutils/db/{test_db.js → temp_db.js} +251 -48
  118. package/testutils/db/value.d.ts +7 -0
  119. package/testutils/db/value.js +251 -0
  120. package/testutils/db_mock.d.ts +16 -4
  121. package/testutils/db_mock.js +52 -9
  122. package/testutils/db_time_zone.d.ts +4 -0
  123. package/testutils/db_time_zone.js +41 -0
  124. package/testutils/ent-graphql-tests/index.d.ts +7 -1
  125. package/testutils/ent-graphql-tests/index.js +56 -26
  126. package/testutils/fake_comms.js +1 -1
  127. package/testutils/fake_data/const.d.ts +2 -1
  128. package/testutils/fake_data/const.js +3 -0
  129. package/testutils/fake_data/fake_contact.d.ts +7 -3
  130. package/testutils/fake_data/fake_contact.js +13 -7
  131. package/testutils/fake_data/fake_event.d.ts +4 -1
  132. package/testutils/fake_data/fake_event.js +7 -6
  133. package/testutils/fake_data/fake_tag.d.ts +36 -0
  134. package/testutils/fake_data/fake_tag.js +89 -0
  135. package/testutils/fake_data/fake_user.d.ts +8 -5
  136. package/testutils/fake_data/fake_user.js +31 -19
  137. package/testutils/fake_data/index.js +5 -1
  138. package/testutils/fake_data/internal.d.ts +2 -0
  139. package/testutils/fake_data/internal.js +7 -1
  140. package/testutils/fake_data/tag_query.d.ts +13 -0
  141. package/testutils/fake_data/tag_query.js +48 -0
  142. package/testutils/fake_data/test_helpers.d.ts +14 -6
  143. package/testutils/fake_data/test_helpers.js +31 -15
  144. package/testutils/fake_data/user_query.d.ts +16 -6
  145. package/testutils/fake_data/user_query.js +72 -23
  146. package/testutils/fake_log.js +1 -1
  147. package/testutils/parse_sql.d.ts +6 -0
  148. package/testutils/parse_sql.js +16 -2
  149. package/testutils/test_edge_global_schema.d.ts +15 -0
  150. package/testutils/test_edge_global_schema.js +62 -0
  151. package/testutils/write.d.ts +2 -2
  152. package/testutils/write.js +33 -7
  153. package/tsc/ast.d.ts +15 -3
  154. package/tsc/ast.js +114 -23
  155. package/tsc/compilerOptions.js +5 -1
  156. package/tsc/move_generated.d.ts +1 -0
  157. package/tsc/move_generated.js +164 -0
  158. package/tsc/transform.d.ts +22 -0
  159. package/tsc/transform.js +182 -0
  160. package/tsc/transform_action.d.ts +22 -0
  161. package/tsc/transform_action.js +183 -0
  162. package/tsc/transform_ent.d.ts +17 -0
  163. package/tsc/transform_ent.js +60 -0
  164. package/tsc/transform_schema.d.ts +27 -0
  165. package/{scripts → tsc}/transform_schema.js +146 -117
  166. package/core/loaders/index_loader.d.ts +0 -14
  167. package/core/loaders/index_loader.js +0 -27
  168. package/graphql/enums.d.ts +0 -3
  169. package/graphql/enums.js +0 -25
  170. package/scripts/move_generated.js +0 -141
  171. package/scripts/transform_actions.js +0 -266
  172. package/scripts/transform_code.d.ts +0 -1
  173. package/scripts/transform_code.js +0 -111
  174. package/scripts/transform_schema.d.ts +0 -1
  175. /package/scripts/{move_generated.d.ts → migrate_v0.1.d.ts} +0 -0
  176. /package/scripts/{transform_actions.d.ts → move_types.d.ts} +0 -0
@@ -1,4 +1,27 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
27
  };
@@ -6,11 +29,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
29
  exports.EntChangeset = exports.Orchestrator = exports.edgeDirection = void 0;
7
30
  const ent_1 = require("../core/ent");
8
31
  const schema_1 = require("../schema/schema");
32
+ const operations_1 = require("./operations");
9
33
  const action_1 = require("../action");
10
34
  const privacy_1 = require("../core/privacy");
11
35
  const executor_1 = require("./executor");
12
36
  const logger_1 = require("../core/logger");
13
37
  const memoizee_1 = __importDefault(require("memoizee"));
38
+ const clause = __importStar(require("../core/clause"));
39
+ const types_1 = require("util/types");
40
+ const operations_2 = require("./operations");
14
41
  var edgeDirection;
15
42
  (function (edgeDirection) {
16
43
  edgeDirection[edgeDirection["inboundEdge"] = 0] = "inboundEdge";
@@ -24,31 +51,38 @@ class edgeInputData {
24
51
  return id.placeholderID !== undefined;
25
52
  }
26
53
  }
27
- function getViewer(action) {
28
- if (!action.viewer.viewerID) {
54
+ function getViewer(viewer) {
55
+ if (!viewer.viewerID) {
29
56
  return "Logged out Viewer";
30
57
  }
31
58
  else {
32
- return `Viewer with ID ${action.viewer.viewerID}`;
59
+ return `Viewer with ID ${viewer.viewerID}`;
33
60
  }
34
61
  }
35
62
  class EntCannotCreateEntError extends Error {
36
63
  constructor(privacyPolicy, action) {
37
- let msg = `${getViewer(action)} does not have permission to create ${action.builder.ent.name}`;
64
+ let msg = `${getViewer(action.viewer)} does not have permission to create ${action.builder.ent.name}`;
38
65
  super(msg);
39
66
  this.privacyPolicy = privacyPolicy;
40
67
  }
41
68
  }
42
69
  class EntCannotEditEntError extends Error {
43
70
  constructor(privacyPolicy, action, ent) {
44
- let msg = `${getViewer(action)} does not have permission to edit ${ent.constructor.name}`;
71
+ let msg = `${getViewer(action.viewer)} does not have permission to edit ${ent.constructor.name}`;
72
+ super(msg);
73
+ this.privacyPolicy = privacyPolicy;
74
+ }
75
+ }
76
+ class EntCannotEditEntFieldError extends Error {
77
+ constructor(privacyPolicy, viewer, field, ent) {
78
+ let msg = `${getViewer(viewer)} does not have permission to edit field ${field} in ${ent.constructor.name}`;
45
79
  super(msg);
46
80
  this.privacyPolicy = privacyPolicy;
47
81
  }
48
82
  }
49
83
  class EntCannotDeleteEntError extends Error {
50
84
  constructor(privacyPolicy, action, ent) {
51
- let msg = `${getViewer(action)} does not have permission to delete ${ent.constructor.name}`;
85
+ let msg = `${getViewer(action.viewer)} does not have permission to delete ${ent.constructor.name}`;
52
86
  super(msg);
53
87
  this.privacyPolicy = privacyPolicy;
54
88
  }
@@ -58,6 +92,7 @@ class Orchestrator {
58
92
  this.options = options;
59
93
  this.edgeSet = new Set();
60
94
  this.edges = new Map();
95
+ this.conditionalEdges = new Map();
61
96
  this.changesets = [];
62
97
  this.dependencies = new Map();
63
98
  this.fieldsToResolve = [];
@@ -68,7 +103,11 @@ class Orchestrator {
68
103
  this.existingEnt = this.options.builder.existingEnt;
69
104
  this.memoizedGetFields = (0, memoizee_1.default)(this.getFieldsInfo.bind(this));
70
105
  }
71
- addEdge(edge, op) {
106
+ // don't type this because we don't care
107
+ __getOptions() {
108
+ return this.options;
109
+ }
110
+ addEdge(edge, op, conditional) {
72
111
  this.edgeSet.add(edge.edgeType);
73
112
  let m1 = this.edges.get(edge.edgeType) || new Map();
74
113
  let m2 = m1.get(op) || new Map();
@@ -83,11 +122,22 @@ class Orchestrator {
83
122
  // set or overwrite the new edge data for said id
84
123
  m2.set(id, edge);
85
124
  m1.set(op, m2);
86
- this.edges.set(edge.edgeType, m1);
125
+ if (conditional && this.onConflict) {
126
+ this.conditionalEdges.set(edge.edgeType, m1);
127
+ }
128
+ else {
129
+ this.edges.set(edge.edgeType, m1);
130
+ }
87
131
  }
88
132
  setDisableTransformations(val) {
89
133
  this.disableTransformations = val;
90
134
  }
135
+ setOnConflictOptions(onConflict) {
136
+ if (onConflict?.onConflictConstraint && !onConflict.updateCols) {
137
+ throw new Error(`cannot set onConflictConstraint without updateCols`);
138
+ }
139
+ this.onConflict = onConflict;
140
+ }
91
141
  addInboundEdge(id1, edgeType, nodeType, options) {
92
142
  this.addEdge(new edgeInputData({
93
143
  id: id1,
@@ -95,7 +145,7 @@ class Orchestrator {
95
145
  nodeType,
96
146
  options,
97
147
  direction: edgeDirection.inboundEdge,
98
- }), action_1.WriteOperation.Insert);
148
+ }), action_1.WriteOperation.Insert, options?.conditional);
99
149
  }
100
150
  addOutboundEdge(id2, edgeType, nodeType, options) {
101
151
  this.addEdge(new edgeInputData({
@@ -104,21 +154,23 @@ class Orchestrator {
104
154
  nodeType,
105
155
  options,
106
156
  direction: edgeDirection.outboundEdge,
107
- }), action_1.WriteOperation.Insert);
157
+ }), action_1.WriteOperation.Insert, options?.conditional);
108
158
  }
109
- removeInboundEdge(id1, edgeType) {
159
+ removeInboundEdge(id1, edgeType, options) {
110
160
  this.addEdge(new edgeInputData({
111
161
  id: id1,
112
162
  edgeType,
113
163
  direction: edgeDirection.inboundEdge,
114
- }), action_1.WriteOperation.Delete);
164
+ options,
165
+ }), action_1.WriteOperation.Delete, options?.conditional);
115
166
  }
116
- removeOutboundEdge(id2, edgeType) {
167
+ removeOutboundEdge(id2, edgeType, options) {
117
168
  this.addEdge(new edgeInputData({
118
169
  id: id2,
119
170
  edgeType,
120
171
  direction: edgeDirection.outboundEdge,
121
- }), action_1.WriteOperation.Delete);
172
+ options,
173
+ }), action_1.WriteOperation.Delete, options?.conditional);
122
174
  }
123
175
  // this doesn't take a direction as that's an implementation detail
124
176
  // it doesn't make any sense to use the same edgeType for inbound and outbound edges
@@ -141,29 +193,39 @@ class Orchestrator {
141
193
  m.clear();
142
194
  }
143
195
  }
144
- buildMainOp() {
196
+ buildMainOp(conditionalBuilder) {
145
197
  // this assumes we have validated fields
146
198
  switch (this.actualOperation) {
147
199
  case action_1.WriteOperation.Delete:
148
- return new ent_1.DeleteNodeOperation(this.existingEnt.id, {
200
+ return new operations_1.DeleteNodeOperation(this.existingEnt.id, this.options.builder, {
149
201
  tableName: this.options.tableName,
150
202
  });
151
203
  default:
152
204
  if (this.actualOperation === action_1.WriteOperation.Edit && !this.existingEnt) {
153
205
  throw new Error(`existing ent required with operation ${this.actualOperation}`);
154
206
  }
207
+ if (this.options.expressions &&
208
+ this.actualOperation !== action_1.WriteOperation.Edit) {
209
+ throw new Error(`expressions are only supported in edit operations for now`);
210
+ }
155
211
  const opts = {
156
212
  fields: this.validatedFields,
157
213
  tableName: this.options.tableName,
158
214
  fieldsToResolve: this.fieldsToResolve,
159
215
  key: this.options.key,
160
216
  loadEntOptions: this.options.loaderOptions,
161
- placeholderID: this.options.builder.placeholderID,
217
+ whereClause: clause.Eq(this.options.key, this.existingEnt?.id),
218
+ expressions: this.options.expressions,
219
+ onConflict: this.onConflict,
220
+ builder: this.options.builder,
162
221
  };
163
222
  if (this.logValues) {
164
223
  opts.fieldsToLog = this.logValues;
165
224
  }
166
- this.mainOp = new ent_1.EditNodeOperation(opts, this.existingEnt);
225
+ this.mainOp = new operations_1.EditNodeOperation(opts, this.existingEnt);
226
+ if (conditionalBuilder) {
227
+ this.mainOp = new operations_1.ConditionalNodeOperation(this.mainOp, conditionalBuilder);
228
+ }
167
229
  return this.mainOp;
168
230
  }
169
231
  }
@@ -183,10 +245,10 @@ class Orchestrator {
183
245
  throw new Error(`no nodeType for edge when adding outboundEdge`);
184
246
  }
185
247
  if (edge.direction === edgeDirection.outboundEdge) {
186
- return ent_1.EdgeOperation.outboundEdge(this.options.builder, edgeType, edge.id, edge.nodeType, edge.options);
248
+ return operations_1.EdgeOperation.outboundEdge(this.options.builder, edgeType, edge.id, edge.nodeType, edge.options);
187
249
  }
188
250
  else {
189
- return ent_1.EdgeOperation.inboundEdge(this.options.builder, edgeType, edge.id, edge.nodeType, edge.options);
251
+ return operations_1.EdgeOperation.inboundEdge(this.options.builder, edgeType, edge.id, edge.nodeType, edge.options);
190
252
  }
191
253
  }
192
254
  else if (op === action_1.WriteOperation.Delete) {
@@ -195,30 +257,55 @@ class Orchestrator {
195
257
  }
196
258
  let id2 = edge.id;
197
259
  if (edge.direction === edgeDirection.outboundEdge) {
198
- return ent_1.EdgeOperation.removeOutboundEdge(this.options.builder, edgeType, id2);
260
+ return operations_1.EdgeOperation.removeOutboundEdge(this.options.builder, edgeType, id2, edge.options);
199
261
  }
200
262
  else {
201
- return ent_1.EdgeOperation.removeInboundEdge(this.options.builder, edgeType, id2);
263
+ return operations_1.EdgeOperation.removeInboundEdge(this.options.builder, edgeType, id2, edge.options);
202
264
  }
203
265
  }
204
266
  throw new Error("could not find an edge operation from the given parameters");
205
267
  }
206
- async buildEdgeOps(ops) {
268
+ async buildEdgeOps(ops, conditionalBuilder, conditionalOverride) {
207
269
  const edgeDatas = await (0, ent_1.loadEdgeDatas)(...Array.from(this.edgeSet.values()));
208
- for (const [edgeType, m] of this.edges) {
209
- for (const [op, m2] of m) {
210
- for (const [_, edge] of m2) {
211
- let edgeOp = this.getEdgeOperation(edgeType, op, edge);
212
- ops.push(edgeOp);
213
- const edgeData = edgeDatas.get(edgeType);
214
- if (!edgeData) {
215
- throw new Error(`could not load edge data for ${edgeType}`);
216
- }
217
- if (edgeData.symmetricEdge) {
218
- ops.push(edgeOp.symmetricEdge());
219
- }
220
- if (edgeData.inverseEdgeType) {
221
- ops.push(edgeOp.inverseEdge(edgeData));
270
+ const edges = [
271
+ [this.edges, false],
272
+ [this.conditionalEdges, true],
273
+ ];
274
+ // conditional should only apply if onconflict...
275
+ // if no upsert and just create, nothing to do here
276
+ for (const edgeInfo of edges) {
277
+ const [edges, conditionalEdge] = edgeInfo;
278
+ const conditional = conditionalOverride || conditionalEdge;
279
+ for (const [edgeType, m] of edges) {
280
+ for (const [op, m2] of m) {
281
+ for (const [_, edge] of m2) {
282
+ let edgeOp = this.getEdgeOperation(edgeType, op, edge);
283
+ if (conditional) {
284
+ ops.push(new operations_1.ConditionalOperation(edgeOp, conditionalBuilder));
285
+ }
286
+ else {
287
+ ops.push(edgeOp);
288
+ }
289
+ const edgeData = edgeDatas.get(edgeType);
290
+ if (!edgeData) {
291
+ throw new Error(`could not load edge data for '${edgeType}'`);
292
+ }
293
+ // similar logic in EntChangeset.changesetFromEdgeOp
294
+ // doesn't support conditional edges
295
+ if (edgeData.symmetricEdge) {
296
+ let symmetric = edgeOp.symmetricEdge();
297
+ if (conditional) {
298
+ symmetric = new operations_1.ConditionalOperation(symmetric, conditionalBuilder);
299
+ }
300
+ ops.push(symmetric);
301
+ }
302
+ if (edgeData.inverseEdgeType) {
303
+ let inverse = edgeOp.inverseEdge(edgeData);
304
+ if (conditional) {
305
+ inverse = new operations_1.ConditionalOperation(inverse, conditionalBuilder);
306
+ }
307
+ ops.push(inverse);
308
+ }
222
309
  }
223
310
  }
224
311
  }
@@ -238,12 +325,50 @@ class Orchestrator {
238
325
  }
239
326
  return new EntCannotDeleteEntError(privacyPolicy, action, this.existingEnt);
240
327
  }
241
- getEntForPrivacyPolicyImpl(editedData) {
328
+ async getRowForPrivacyPolicyImpl(schemaFields, editedData) {
329
+ // need to format fields if possible because ent constructors expect data that's
330
+ // in the format that's coming from the db
331
+ // required for object fields...
332
+ const formatted = { ...editedData };
333
+ for (const [fieldName, field] of schemaFields) {
334
+ if (!field.format) {
335
+ continue;
336
+ }
337
+ let dbKey = this.getStorageKey(fieldName);
338
+ let val = formatted[dbKey];
339
+ if (!val) {
340
+ continue;
341
+ }
342
+ if (field.valid) {
343
+ let valid = field.valid(val);
344
+ if ((0, types_1.isPromise)(valid)) {
345
+ valid = await valid;
346
+ }
347
+ // if not valid, don't format and don't pass to ent?
348
+ // or just early throw here
349
+ if (!valid) {
350
+ continue;
351
+ // throw new Error(`invalid field ${fieldName} with value ${val}`);
352
+ }
353
+ }
354
+ // nested so it's not JSON stringified or anything like that
355
+ val = field.format(formatted[dbKey], true);
356
+ if ((0, types_1.isPromise)(val)) {
357
+ val = await val;
358
+ }
359
+ formatted[dbKey] = val;
360
+ }
361
+ return formatted;
362
+ }
363
+ async getEntForPrivacyPolicyImpl(schemaFields, editedData, viewerToUse, rowToUse) {
242
364
  if (this.actualOperation !== action_1.WriteOperation.Insert) {
243
365
  return this.existingEnt;
244
366
  }
367
+ if (!rowToUse) {
368
+ rowToUse = await this.getRowForPrivacyPolicyImpl(schemaFields, editedData);
369
+ }
245
370
  // we create an unsafe ent to be used for privacy policies
246
- return new this.options.builder.ent(this.options.builder.viewer, editedData);
371
+ return new this.options.builder.ent(viewerToUse, rowToUse);
247
372
  }
248
373
  getSQLStatementOperation() {
249
374
  switch (this.actualOperation) {
@@ -273,8 +398,8 @@ class Orchestrator {
273
398
  if (this.actualOperation !== action_1.WriteOperation.Insert) {
274
399
  return this.existingEnt;
275
400
  }
276
- const { editedData } = await this.memoizedGetFields();
277
- return this.getEntForPrivacyPolicyImpl(editedData);
401
+ const { schemaFields, editedData } = await this.memoizedGetFields();
402
+ return this.getEntForPrivacyPolicyImpl(schemaFields, editedData, this.options.viewer);
278
403
  }
279
404
  // this gets the fields that were explicitly set plus any default or transformed values
280
405
  // mainly exists to get default fields e.g. default id to be used in triggers
@@ -284,15 +409,41 @@ class Orchestrator {
284
409
  const { editedData } = await this.memoizedGetFields();
285
410
  return editedData;
286
411
  }
412
+ /**
413
+ * @returns validated and formatted fields that would be written to the db
414
+ * throws an error if called before valid() or validX() has been called
415
+ */
416
+ getValidatedFields() {
417
+ if (this.validatedFields === null) {
418
+ throw new Error(`trying to call getValidatedFields before validating fields`);
419
+ }
420
+ return this.validatedFields;
421
+ }
287
422
  // Note: this is memoized. call memoizedGetFields instead
288
423
  async getFieldsInfo() {
289
424
  const action = this.options.action;
290
425
  const builder = this.options.builder;
291
426
  // future optimization: can get schemaFields to memoize based on different values
292
427
  const schemaFields = (0, schema_1.getFields)(this.options.schema);
428
+ // also future optimization, no need to go through the list of fields multiple times
429
+ let editPrivacyFields = new Map();
430
+ switch (this.actualOperation) {
431
+ case action_1.WriteOperation.Edit:
432
+ editPrivacyFields = (0, schema_1.getFieldsWithEditPrivacy)(this.options.schema, this.options.fieldInfo);
433
+ break;
434
+ case action_1.WriteOperation.Insert:
435
+ editPrivacyFields = (0, schema_1.getFieldsForCreateAction)(this.options.schema, this.options.fieldInfo);
436
+ break;
437
+ }
293
438
  const editedFields = await this.options.editedFields();
294
- let editedData = await this.getFieldsWithDefaultValues(builder, schemaFields, editedFields, action);
295
- return { editedData, editedFields, schemaFields };
439
+ let { data: editedData, userDefinedKeys } = await this.getFieldsWithDefaultValues(builder, schemaFields, editedFields, action);
440
+ return {
441
+ editedData,
442
+ editedFields,
443
+ schemaFields,
444
+ userDefinedKeys,
445
+ editPrivacyFields,
446
+ };
296
447
  }
297
448
  async validate() {
298
449
  // existing ent required for edit or delete operations
@@ -303,7 +454,7 @@ class Orchestrator {
303
454
  throw new Error(`existing ent required with operation ${this.actualOperation}`);
304
455
  }
305
456
  }
306
- const { schemaFields, editedData } = await this.memoizedGetFields();
457
+ const { schemaFields, editedData, userDefinedKeys, editPrivacyFields } = await this.memoizedGetFields();
307
458
  const action = this.options.action;
308
459
  const builder = this.options.builder;
309
460
  // this runs in following phases:
@@ -312,8 +463,40 @@ class Orchestrator {
312
463
  // * triggers
313
464
  // * validators
314
465
  let privacyPolicy = action?.getPrivacyPolicy();
466
+ const errors = [];
315
467
  if (privacyPolicy) {
316
- await (0, privacy_1.applyPrivacyPolicyX)(this.options.viewer, privacyPolicy, this.getEntForPrivacyPolicyImpl(editedData), this.throwError.bind(this));
468
+ const ent = await this.getEntForPrivacyPolicyImpl(schemaFields, editedData, this.options.viewer);
469
+ try {
470
+ await (0, privacy_1.applyPrivacyPolicyX)(this.options.viewer, privacyPolicy, ent, () => this.throwError());
471
+ }
472
+ catch (err) {
473
+ errors.push(err);
474
+ }
475
+ }
476
+ // we have edit privacy fields, so we need to apply privacy policy on those
477
+ const promises = [];
478
+ if (editPrivacyFields.size) {
479
+ // get row based on edited data
480
+ const row = await this.getRowForPrivacyPolicyImpl(schemaFields, editedData);
481
+ // get viewer for ent load based on formatted row
482
+ const viewer = await this.viewerForEntLoad(row);
483
+ const ent = await this.getEntForPrivacyPolicyImpl(schemaFields, editedData, viewer, row);
484
+ for (const [k, policy] of editPrivacyFields) {
485
+ if (editedData[k] === undefined || !userDefinedKeys.has(k)) {
486
+ continue;
487
+ }
488
+ promises.push((async () => {
489
+ const r = await (0, privacy_1.applyPrivacyPolicy)(viewer, policy, ent);
490
+ if (!r) {
491
+ errors.push(new EntCannotEditEntFieldError(policy, viewer, k, ent));
492
+ }
493
+ })());
494
+ }
495
+ await Promise.all(promises);
496
+ }
497
+ // privacy or field errors should return first so it's less confusing
498
+ if (errors.length) {
499
+ return errors;
317
500
  }
318
501
  // have to run triggers which update fields first before field and other validators
319
502
  // so running this first to build things up
@@ -327,38 +510,70 @@ class Orchestrator {
327
510
  // not ideal we're calling this twice. fix...
328
511
  // needed for now. may need to rewrite some of this?
329
512
  const editedFields2 = await this.options.editedFields();
330
- await Promise.all([
513
+ const [errs2, errs3] = await Promise.all([
331
514
  this.formatAndValidateFields(schemaFields, editedFields2),
332
515
  this.validators(validators, action, builder),
333
516
  ]);
517
+ errors.push(...errs2);
518
+ errors.push(...errs3);
519
+ return errors;
334
520
  }
335
521
  async triggers(action, builder, triggers) {
336
- await Promise.all(triggers.map(async (trigger) => {
337
- let ret = await trigger.changeset(builder, action.getInput());
338
- if (Array.isArray(ret)) {
339
- ret = await Promise.all(ret);
340
- }
341
- if (Array.isArray(ret)) {
342
- for (const v of ret) {
343
- if (typeof v === "object") {
344
- this.changesets.push(v);
345
- }
522
+ let groups = [];
523
+ let lastArray = 0;
524
+ let prevWasArray = false;
525
+ for (let i = 0; i < triggers.length; i++) {
526
+ let t = triggers[i];
527
+ if (Array.isArray(t)) {
528
+ if (!prevWasArray) {
529
+ // @ts-ignore
530
+ groups.push(triggers.slice(lastArray, i));
346
531
  }
532
+ groups.push(t);
533
+ prevWasArray = true;
534
+ lastArray++;
347
535
  }
348
- else if (ret) {
349
- this.changesets.push(ret);
536
+ else {
537
+ if (i === triggers.length - 1) {
538
+ // @ts-ignore
539
+ groups.push(triggers.slice(lastArray, i + 1));
540
+ }
541
+ prevWasArray = false;
350
542
  }
351
- }));
543
+ }
544
+ for (const triggers of groups) {
545
+ await Promise.all(triggers.map(async (trigger) => {
546
+ let ret = await trigger.changeset(builder, action.getInput());
547
+ if (Array.isArray(ret)) {
548
+ ret = await Promise.all(ret);
549
+ }
550
+ if (Array.isArray(ret)) {
551
+ for (const v of ret) {
552
+ if (typeof v === "object") {
553
+ this.changesets.push(v);
554
+ }
555
+ }
556
+ }
557
+ else if (ret) {
558
+ this.changesets.push(ret);
559
+ }
560
+ }));
561
+ }
352
562
  }
353
563
  async validators(validators, action, builder) {
354
- let promises = [];
355
- validators.forEach((validator) => {
356
- let res = validator.validate(builder, action.getInput());
357
- if (res) {
358
- promises.push(res);
564
+ const errors = [];
565
+ await Promise.all(validators.map(async (v) => {
566
+ try {
567
+ const r = await v.validate(builder, action.getInput());
568
+ if (r instanceof Error) {
569
+ errors.push(r);
570
+ }
571
+ }
572
+ catch (err) {
573
+ errors.push(err);
359
574
  }
360
- });
361
- await Promise.all(promises);
575
+ }));
576
+ return errors;
362
577
  }
363
578
  isBuilder(val) {
364
579
  return val.placeholderID !== undefined;
@@ -379,23 +594,31 @@ class Orchestrator {
379
594
  // if disable transformations set, don't do schema transform and just do the right thing
380
595
  // else apply schema tranformation if it exists
381
596
  let transformed = null;
597
+ const sqlOp = this.getSQLStatementOperation();
598
+ // why is transform write technically different from upsert?
599
+ // it's create -> update just at the db level...
382
600
  if (action?.transformWrite) {
383
601
  transformed = await action.transformWrite({
384
- viewer: builder.viewer,
385
- op: this.getSQLStatementOperation(),
602
+ builder,
603
+ input,
604
+ op: sqlOp,
386
605
  data: editedFields,
387
- existingEnt: this.existingEnt,
388
606
  });
389
607
  }
390
608
  else if (!this.disableTransformations) {
391
609
  transformed = (0, schema_1.getTransformedUpdateOp)(this.options.schema, {
392
- viewer: builder.viewer,
393
- op: this.getSQLStatementOperation(),
610
+ builder,
611
+ input,
612
+ op: sqlOp,
394
613
  data: editedFields,
395
- existingEnt: this.existingEnt,
396
614
  });
397
615
  }
398
616
  if (transformed) {
617
+ if (sqlOp === schema_1.SQLStatementOperation.Insert && sqlOp !== transformed.op) {
618
+ if (!transformed.existingEnt) {
619
+ throw new Error(`cannot transform an insert operation without providing an existing ent`);
620
+ }
621
+ }
399
622
  if (transformed.data) {
400
623
  updateInput = true;
401
624
  for (const k in transformed.data) {
@@ -414,18 +637,29 @@ class Orchestrator {
414
637
  // this.defaultFieldsByFieldName[k] = val;
415
638
  }
416
639
  }
640
+ if (transformed.changeset) {
641
+ const changeset = await transformed.changeset();
642
+ this.changesets.push(changeset);
643
+ }
417
644
  this.actualOperation = this.getWriteOpForSQLStamentOp(transformed.op);
418
645
  if (transformed.existingEnt) {
419
646
  // @ts-ignore
420
647
  this.existingEnt = transformed.existingEnt;
648
+ // modify existing ent in builder. it's readonly in generated ents but doesn't apply here
649
+ builder.existingEnt = transformed.existingEnt;
421
650
  }
422
651
  }
423
652
  // transforming before doing default fields so that we don't create a new id
424
653
  // and anything that depends on the type of operations knows what it is
654
+ const userDefinedKeys = new Set();
425
655
  for (const [fieldName, field] of schemaFields) {
426
656
  let value = editedFields.get(fieldName);
427
657
  let defaultValue = undefined;
428
658
  let dbKey = this.getStorageKey(fieldName);
659
+ let updateOnlyIfOther = field.onlyUpdateIfOtherFieldsBeingSet_BETA;
660
+ if (value !== undefined) {
661
+ userDefinedKeys.add(dbKey);
662
+ }
429
663
  if (value === undefined) {
430
664
  if (this.actualOperation === action_1.WriteOperation.Insert) {
431
665
  if (field.defaultToViewerOnCreate && field.defaultValueOnCreate) {
@@ -439,11 +673,17 @@ class Orchestrator {
439
673
  if (defaultValue === undefined) {
440
674
  throw new Error(`defaultValueOnCreate() returned undefined for field ${fieldName}`);
441
675
  }
676
+ if ((0, types_1.isPromise)(defaultValue)) {
677
+ defaultValue = await defaultValue;
678
+ }
442
679
  }
443
680
  }
444
681
  if (field.defaultValueOnEdit &&
445
682
  this.actualOperation === action_1.WriteOperation.Edit) {
446
683
  defaultValue = field.defaultValueOnEdit(builder, input);
684
+ if ((0, types_1.isPromise)(defaultValue)) {
685
+ defaultValue = await defaultValue;
686
+ }
447
687
  }
448
688
  }
449
689
  if (value !== undefined) {
@@ -451,7 +691,12 @@ class Orchestrator {
451
691
  }
452
692
  if (defaultValue !== undefined) {
453
693
  updateInput = true;
454
- defaultData[dbKey] = defaultValue;
694
+ if (updateOnlyIfOther) {
695
+ defaultData[dbKey] = defaultValue;
696
+ }
697
+ else {
698
+ data[dbKey] = defaultValue;
699
+ }
455
700
  this.defaultFieldsByFieldName[fieldName] = defaultValue;
456
701
  this.defaultFieldsByTSName[this.getInputKey(fieldName)] = defaultValue;
457
702
  }
@@ -467,7 +712,7 @@ class Orchestrator {
467
712
  this.options.updateInput(this.defaultFieldsByTSName);
468
713
  }
469
714
  }
470
- return data;
715
+ return { data, userDefinedKeys };
471
716
  }
472
717
  hasData(data) {
473
718
  for (const _k in data) {
@@ -479,7 +724,7 @@ class Orchestrator {
479
724
  // now format and validate...
480
725
  if (value === null) {
481
726
  if (!field.nullable) {
482
- throw new Error(`field ${fieldName} set to null for non-nullable field`);
727
+ return new Error(`field ${fieldName} set to null for non-nullable field`);
483
728
  }
484
729
  }
485
730
  else if (value === undefined) {
@@ -490,14 +735,17 @@ class Orchestrator {
490
735
  // server default allowed
491
736
  field.serverDefault === undefined &&
492
737
  this.actualOperation === action_1.WriteOperation.Insert) {
493
- throw new Error(`required field ${fieldName} not set`);
738
+ return new Error(`required field ${fieldName} not set`);
494
739
  }
495
740
  }
496
741
  else if (this.isBuilder(value)) {
497
742
  if (field.valid) {
498
- const valid = await field.valid(value);
743
+ let valid = field.valid(value);
744
+ if ((0, types_1.isPromise)(valid)) {
745
+ valid = await valid;
746
+ }
499
747
  if (!valid) {
500
- throw new Error(`invalid field ${fieldName} with value ${value}`);
748
+ return new Error(`invalid field ${fieldName} with value ${value}`);
501
749
  }
502
750
  }
503
751
  // keep track of dependencies to resolve
@@ -507,10 +755,12 @@ class Orchestrator {
507
755
  }
508
756
  else {
509
757
  if (field.valid) {
510
- // TODO this could be async. handle this better
511
- const valid = await field.valid(value);
758
+ let valid = field.valid(value);
759
+ if ((0, types_1.isPromise)(valid)) {
760
+ valid = await valid;
761
+ }
512
762
  if (!valid) {
513
- throw new Error(`invalid field ${fieldName} with value ${value}`);
763
+ return new Error(`invalid field ${fieldName} with value ${value}`);
514
764
  }
515
765
  }
516
766
  if (field.format) {
@@ -520,26 +770,52 @@ class Orchestrator {
520
770
  return value;
521
771
  }
522
772
  async formatAndValidateFields(schemaFields, editedFields) {
773
+ const errors = [];
523
774
  const op = this.actualOperation;
524
775
  if (op === action_1.WriteOperation.Delete) {
525
- return;
776
+ return [];
526
777
  }
527
778
  // build up data to be saved...
528
779
  let data = {};
529
780
  let logValues = {};
781
+ let needsFullDataChecks = [];
530
782
  for (const [fieldName, field] of schemaFields) {
531
783
  let value = editedFields.get(fieldName);
784
+ if (field.validateWithFullData) {
785
+ needsFullDataChecks.push(fieldName);
786
+ }
532
787
  if (value === undefined && op === action_1.WriteOperation.Insert) {
533
788
  // null allowed
534
789
  value = this.defaultFieldsByFieldName[fieldName];
535
790
  }
536
791
  let dbKey = this.getStorageKey(fieldName);
537
- value = await this.transformFieldValue(fieldName, field, dbKey, value);
792
+ let ret = await this.transformFieldValue(fieldName, field, dbKey, value);
793
+ if (ret instanceof Error) {
794
+ errors.push(ret);
795
+ }
796
+ else {
797
+ value = ret;
798
+ }
538
799
  if (value !== undefined) {
539
800
  data[dbKey] = value;
540
801
  logValues[dbKey] = field.logValue(value);
541
802
  }
542
803
  }
804
+ for (const fieldName of needsFullDataChecks) {
805
+ const field = schemaFields.get(fieldName);
806
+ let value = editedFields.get(fieldName);
807
+ // @ts-ignore...
808
+ // type hackery because it's hard
809
+ const v = await field.validateWithFullData(value, this.options.builder);
810
+ if (!v) {
811
+ if (value === undefined) {
812
+ errors.push(new Error(`field ${fieldName} set to undefined when it can't be nullable`));
813
+ }
814
+ else {
815
+ errors.push(new Error(`field ${fieldName} set to null when it can't be nullable`));
816
+ }
817
+ }
818
+ }
543
819
  // we ignored default values while editing.
544
820
  // if we're editing and there's data, add default values
545
821
  if (op === action_1.WriteOperation.Edit && this.hasData(data)) {
@@ -549,41 +825,73 @@ class Orchestrator {
549
825
  let dbKey = this.getStorageKey(fieldName);
550
826
  // no value, let's just default
551
827
  if (data[dbKey] === undefined) {
552
- const value = await this.transformFieldValue(fieldName, field, dbKey, defaultValue);
553
- data[dbKey] = value;
554
- logValues[dbKey] = field.logValue(value);
828
+ const ret = await this.transformFieldValue(fieldName, field, dbKey, defaultValue);
829
+ if (ret instanceof Error) {
830
+ errors.push(ret);
831
+ }
832
+ else {
833
+ data[dbKey] = ret;
834
+ logValues[dbKey] = field.logValue(ret);
835
+ }
555
836
  }
556
837
  }
557
838
  }
558
839
  this.validatedFields = data;
559
840
  this.logValues = logValues;
841
+ return errors;
560
842
  }
561
843
  async valid() {
562
- try {
563
- await this.validate();
564
- }
565
- catch (e) {
566
- (0, logger_1.log)("error", e);
844
+ const errors = await this.validate();
845
+ if (errors.length) {
846
+ errors.map((err) => (0, logger_1.log)("error", err));
567
847
  return false;
568
848
  }
569
849
  return true;
570
850
  }
571
851
  async validX() {
852
+ const errors = await this.validate();
853
+ if (errors.length) {
854
+ // just throw the first one...
855
+ // TODO we should ideally throw all of them
856
+ throw errors[0];
857
+ }
858
+ }
859
+ /**
860
+ * @experimental API that's not guaranteed to remain in the future which returns
861
+ * a list of errors encountered
862
+ * 0 errors indicates valid
863
+ * NOTE that this currently doesn't catch errors returned by validators().
864
+ * If those throws, this still throws and doesn't return them
865
+ */
866
+ async validWithErrors() {
572
867
  return this.validate();
573
868
  }
574
- async build() {
869
+ async buildPlusChangeset(conditionalBuilder, conditionalOverride) {
575
870
  // validate everything first
576
871
  await this.validX();
577
- let ops = [this.buildMainOp()];
578
- await this.buildEdgeOps(ops);
579
- return new EntChangeset(this.options.viewer, this.options.builder.placeholderID, this.options.loaderOptions.ent, ops, this.dependencies, this.changesets, this.options);
872
+ let ops = [
873
+ this.buildMainOp(conditionalOverride ? conditionalBuilder : undefined),
874
+ ];
875
+ await this.buildEdgeOps(ops, conditionalBuilder, conditionalOverride);
876
+ // TODO throw if we try and create a new changeset after previously creating one
877
+ // TODO test actualOperation value
878
+ // observers is fine since they're run after and we have the actualOperation value...
879
+ return new EntChangeset(this.options.viewer, this.options.builder, this.options.builder.placeholderID, conditionalOverride, ops, this.dependencies, this.changesets, this.options);
880
+ }
881
+ async build() {
882
+ return this.buildPlusChangeset(this.options.builder, false);
883
+ }
884
+ async buildWithOptions_BETA(options) {
885
+ // set as dependency so that we do the right order of operations
886
+ this.dependencies.set(options.conditionalBuilder.placeholderID, options.conditionalBuilder);
887
+ return this.buildPlusChangeset(options.conditionalBuilder, true);
580
888
  }
581
889
  async viewerForEntLoad(data) {
582
890
  const action = this.options.action;
583
891
  if (!action || !action.viewerForEntLoad) {
584
892
  return this.options.viewer;
585
893
  }
586
- return action.viewerForEntLoad(data);
894
+ return action.viewerForEntLoad(data, action.builder.viewer.context);
587
895
  }
588
896
  async returnedRow() {
589
897
  if (this.mainOp && this.mainOp.returnedRow) {
@@ -618,16 +926,62 @@ class Orchestrator {
618
926
  }
619
927
  }
620
928
  exports.Orchestrator = Orchestrator;
929
+ function randomNum() {
930
+ return Math.random().toString(10).substring(2);
931
+ }
932
+ // each changeset is required to have a unique placeholderID
933
+ // used in executor. if we end up creating multiple changesets from a builder, we need
934
+ // different placeholders
935
+ // in practice, only applies to Entchangeset::changesetFrom()
621
936
  class EntChangeset {
622
- constructor(viewer, placeholderID, ent, operations, dependencies, changesets, options) {
937
+ constructor(viewer, builder, placeholderID, conditionalOverride, operations, dependencies, changesets, options) {
623
938
  this.viewer = viewer;
939
+ this.builder = builder;
624
940
  this.placeholderID = placeholderID;
625
- this.ent = ent;
941
+ this.conditionalOverride = conditionalOverride;
626
942
  this.operations = operations;
627
943
  this.dependencies = dependencies;
628
944
  this.changesets = changesets;
629
945
  this.options = options;
630
946
  }
947
+ static changesetFrom(builder, ops) {
948
+ return new EntChangeset(builder.viewer, builder,
949
+ // need unique placeholderID different from the builder. see comment above EntChangeset
950
+ `$ent.idPlaceholderID$ ${randomNum()}-${builder.ent.name}`, false, ops);
951
+ }
952
+ static changesetFromQueries(builder, queries) {
953
+ return EntChangeset.changesetFrom(builder, [
954
+ new operations_2.RawQueryOperation(builder, queries),
955
+ ]);
956
+ }
957
+ static async changesetFromEdgeOp(builder, op, edgeType) {
958
+ const edgeData = await (0, ent_1.loadEdgeData)(edgeType);
959
+ const ops = [op];
960
+ if (!edgeData) {
961
+ throw new Error(`could not load edge data for '${edgeType}'`);
962
+ }
963
+ // similar logic in Orchestrator.buildEdgeOps
964
+ // doesn't support conditional edges
965
+ if (edgeData.symmetricEdge) {
966
+ ops.push(op.symmetricEdge());
967
+ }
968
+ if (edgeData.inverseEdgeType) {
969
+ ops.push(op.inverseEdge(edgeData));
970
+ }
971
+ return EntChangeset.changesetFrom(builder, ops);
972
+ }
973
+ static async changesetFromOutboundEdge(builder, edgeType, id2, nodeType, options) {
974
+ return EntChangeset.changesetFromEdgeOp(builder, operations_1.EdgeOperation.outboundEdge(builder, edgeType, id2, nodeType, options), edgeType);
975
+ }
976
+ static async changesetFromInboundEdge(builder, edgeType, id1, nodeType, options) {
977
+ return EntChangeset.changesetFromEdgeOp(builder, operations_1.EdgeOperation.inboundEdge(builder, edgeType, id1, nodeType, options), edgeType);
978
+ }
979
+ static changesetRemoveFromOutboundEdge(builder, edgeType, id2, options) {
980
+ return EntChangeset.changesetFromEdgeOp(builder, operations_1.EdgeOperation.removeOutboundEdge(builder, edgeType, id2, options), edgeType);
981
+ }
982
+ static changesetRemoveFromInboundEdge(builder, edgeType, id1, options) {
983
+ return EntChangeset.changesetFromEdgeOp(builder, operations_1.EdgeOperation.removeInboundEdge(builder, edgeType, id1, options), edgeType);
984
+ }
631
985
  executor() {
632
986
  if (this._executor) {
633
987
  return this._executor;
@@ -637,9 +991,15 @@ class EntChangeset {
637
991
  // executor and depend on something else in the stack to handle this correctly
638
992
  // ComplexExecutor which could be a parent of this should make sure the dependency
639
993
  // is resolved beforehand
640
- return (this._executor = new executor_1.ListBasedExecutor(this.viewer, this.placeholderID, this.operations, this.options));
641
- }
642
- return (this._executor = new executor_1.ComplexExecutor(this.viewer, this.placeholderID, this.operations, this.dependencies || new Map(), this.changesets || [], this.options));
994
+ return (this._executor = new executor_1.ListBasedExecutor(this.viewer, this.placeholderID, this.operations, this.options, {
995
+ conditionalOverride: this.conditionalOverride,
996
+ builder: this.builder,
997
+ }));
998
+ }
999
+ return (this._executor = new executor_1.ComplexExecutor(this.viewer, this.placeholderID, this.operations, this.dependencies || new Map(), this.changesets || [], this.options, {
1000
+ conditionalOverride: this.conditionalOverride,
1001
+ builder: this.builder,
1002
+ }));
643
1003
  }
644
1004
  }
645
1005
  exports.EntChangeset = EntChangeset;