@snowtop/ent 0.1.0-alpha160-test7 → 0.1.0-alpha161-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 (317) hide show
  1. package/{dist/core → core}/query/assoc_query.d.ts +3 -0
  2. package/{dist/core → core}/query/assoc_query.js +71 -0
  3. package/{dist/core → core}/query/query.d.ts +3 -0
  4. package/{dist/core → core}/query/query.js +20 -6
  5. package/core/query/shared_assoc_test.d.ts +2 -0
  6. package/core/query/shared_assoc_test.js +1089 -0
  7. package/core/query/shared_test.d.ts +21 -0
  8. package/core/query/shared_test.js +736 -0
  9. package/graphql/query/shared_assoc_test.d.ts +1 -0
  10. package/graphql/query/shared_assoc_test.js +203 -0
  11. package/package.json +8 -53
  12. package/{dist/testutils → testutils}/fake_data/test_helpers.js +2 -1
  13. package/dist/package.json +0 -64
  14. package/src/action/action.ts +0 -330
  15. package/src/action/executor.ts +0 -453
  16. package/src/action/experimental_action.ts +0 -277
  17. package/src/action/index.ts +0 -31
  18. package/src/action/operations.ts +0 -967
  19. package/src/action/orchestrator.ts +0 -1527
  20. package/src/action/privacy.ts +0 -37
  21. package/src/action/relative_value.ts +0 -242
  22. package/src/action/transaction.ts +0 -38
  23. package/src/auth/auth.ts +0 -77
  24. package/src/auth/index.ts +0 -8
  25. package/src/core/base.ts +0 -367
  26. package/src/core/clause.ts +0 -1065
  27. package/src/core/config.ts +0 -219
  28. package/src/core/const.ts +0 -5
  29. package/src/core/context.ts +0 -135
  30. package/src/core/convert.ts +0 -106
  31. package/src/core/date.ts +0 -23
  32. package/src/core/db.ts +0 -498
  33. package/src/core/ent.ts +0 -1740
  34. package/src/core/global_schema.ts +0 -49
  35. package/src/core/loaders/assoc_count_loader.ts +0 -99
  36. package/src/core/loaders/assoc_edge_loader.ts +0 -250
  37. package/src/core/loaders/index.ts +0 -12
  38. package/src/core/loaders/loader.ts +0 -66
  39. package/src/core/loaders/object_loader.ts +0 -489
  40. package/src/core/loaders/query_loader.ts +0 -314
  41. package/src/core/loaders/raw_count_loader.ts +0 -175
  42. package/src/core/logger.ts +0 -49
  43. package/src/core/privacy.ts +0 -660
  44. package/src/core/query/assoc_query.ts +0 -240
  45. package/src/core/query/custom_clause_query.ts +0 -174
  46. package/src/core/query/custom_query.ts +0 -302
  47. package/src/core/query/index.ts +0 -9
  48. package/src/core/query/query.ts +0 -674
  49. package/src/core/query_impl.ts +0 -32
  50. package/src/core/viewer.ts +0 -52
  51. package/src/ent.code-workspace +0 -73
  52. package/src/graphql/builtins/connection.ts +0 -25
  53. package/src/graphql/builtins/edge.ts +0 -16
  54. package/src/graphql/builtins/node.ts +0 -12
  55. package/src/graphql/graphql.ts +0 -891
  56. package/src/graphql/graphql_field_helpers.ts +0 -221
  57. package/src/graphql/index.ts +0 -42
  58. package/src/graphql/mutations/union.ts +0 -39
  59. package/src/graphql/node_resolver.ts +0 -122
  60. package/src/graphql/query/connection_type.ts +0 -113
  61. package/src/graphql/query/edge_connection.ts +0 -171
  62. package/src/graphql/query/page_info.ts +0 -34
  63. package/src/graphql/query/shared_edge_connection.ts +0 -287
  64. package/src/graphql/scalars/orderby_direction.ts +0 -13
  65. package/src/graphql/scalars/time.ts +0 -38
  66. package/src/imports/dataz/example1/_auth.ts +0 -51
  67. package/src/imports/dataz/example1/_viewer.ts +0 -35
  68. package/src/imports/index.ts +0 -213
  69. package/src/index.ts +0 -145
  70. package/src/parse_schema/parse.ts +0 -585
  71. package/src/schema/base_schema.ts +0 -224
  72. package/src/schema/field.ts +0 -1087
  73. package/src/schema/index.ts +0 -53
  74. package/src/schema/json_field.ts +0 -94
  75. package/src/schema/schema.ts +0 -1028
  76. package/src/schema/struct_field.ts +0 -234
  77. package/src/schema/union_field.ts +0 -105
  78. package/src/scripts/custom_compiler.ts +0 -331
  79. package/src/scripts/custom_graphql.ts +0 -550
  80. package/src/scripts/migrate_v0.1.ts +0 -41
  81. package/src/scripts/move_types.ts +0 -131
  82. package/src/scripts/read_schema.ts +0 -67
  83. package/src/setupPackage.js +0 -42
  84. package/src/testutils/action/complex_schemas.ts +0 -517
  85. package/src/testutils/builder.ts +0 -422
  86. package/src/testutils/context/test_context.ts +0 -25
  87. package/src/testutils/db/fixture.ts +0 -32
  88. package/src/testutils/db/temp_db.ts +0 -941
  89. package/src/testutils/db/value.ts +0 -294
  90. package/src/testutils/db_mock.ts +0 -351
  91. package/src/testutils/db_time_zone.ts +0 -40
  92. package/src/testutils/ent-graphql-tests/index.ts +0 -653
  93. package/src/testutils/fake_comms.ts +0 -50
  94. package/src/testutils/fake_data/const.ts +0 -64
  95. package/src/testutils/fake_data/events_query.ts +0 -145
  96. package/src/testutils/fake_data/fake_contact.ts +0 -150
  97. package/src/testutils/fake_data/fake_event.ts +0 -150
  98. package/src/testutils/fake_data/fake_tag.ts +0 -139
  99. package/src/testutils/fake_data/fake_user.ts +0 -232
  100. package/src/testutils/fake_data/index.ts +0 -1
  101. package/src/testutils/fake_data/internal.ts +0 -8
  102. package/src/testutils/fake_data/tag_query.ts +0 -56
  103. package/src/testutils/fake_data/test_helpers.ts +0 -388
  104. package/src/testutils/fake_data/user_query.ts +0 -524
  105. package/src/testutils/fake_log.ts +0 -52
  106. package/src/testutils/mock_date.ts +0 -10
  107. package/src/testutils/mock_log.ts +0 -39
  108. package/src/testutils/parse_sql.ts +0 -685
  109. package/src/testutils/test_edge_global_schema.ts +0 -49
  110. package/src/testutils/write.ts +0 -70
  111. package/src/tsc/ast.ts +0 -351
  112. package/src/tsc/compilerOptions.ts +0 -85
  113. package/src/tsc/move_generated.ts +0 -191
  114. package/src/tsc/transform.ts +0 -226
  115. package/src/tsc/transform_action.ts +0 -224
  116. package/src/tsc/transform_ent.ts +0 -66
  117. package/src/tsc/transform_schema.ts +0 -546
  118. package/tsconfig.json +0 -20
  119. /package/{dist/action → action}/action.d.ts +0 -0
  120. /package/{dist/action → action}/action.js +0 -0
  121. /package/{dist/action → action}/executor.d.ts +0 -0
  122. /package/{dist/action → action}/executor.js +0 -0
  123. /package/{dist/action → action}/experimental_action.d.ts +0 -0
  124. /package/{dist/action → action}/experimental_action.js +0 -0
  125. /package/{dist/action → action}/index.d.ts +0 -0
  126. /package/{dist/action → action}/index.js +0 -0
  127. /package/{dist/action → action}/operations.d.ts +0 -0
  128. /package/{dist/action → action}/operations.js +0 -0
  129. /package/{dist/action → action}/orchestrator.d.ts +0 -0
  130. /package/{dist/action → action}/orchestrator.js +0 -0
  131. /package/{dist/action → action}/privacy.d.ts +0 -0
  132. /package/{dist/action → action}/privacy.js +0 -0
  133. /package/{dist/action → action}/relative_value.d.ts +0 -0
  134. /package/{dist/action → action}/relative_value.js +0 -0
  135. /package/{dist/action → action}/transaction.d.ts +0 -0
  136. /package/{dist/action → action}/transaction.js +0 -0
  137. /package/{dist/auth → auth}/auth.d.ts +0 -0
  138. /package/{dist/auth → auth}/auth.js +0 -0
  139. /package/{dist/auth → auth}/index.d.ts +0 -0
  140. /package/{dist/auth → auth}/index.js +0 -0
  141. /package/{dist/core → core}/base.d.ts +0 -0
  142. /package/{dist/core → core}/base.js +0 -0
  143. /package/{dist/core → core}/clause.d.ts +0 -0
  144. /package/{dist/core → core}/clause.js +0 -0
  145. /package/{dist/core → core}/config.d.ts +0 -0
  146. /package/{dist/core → core}/config.js +0 -0
  147. /package/{dist/core → core}/const.d.ts +0 -0
  148. /package/{dist/core → core}/const.js +0 -0
  149. /package/{dist/core → core}/context.d.ts +0 -0
  150. /package/{dist/core → core}/context.js +0 -0
  151. /package/{dist/core → core}/convert.d.ts +0 -0
  152. /package/{dist/core → core}/convert.js +0 -0
  153. /package/{dist/core → core}/date.d.ts +0 -0
  154. /package/{dist/core → core}/date.js +0 -0
  155. /package/{dist/core → core}/db.d.ts +0 -0
  156. /package/{dist/core → core}/db.js +0 -0
  157. /package/{dist/core → core}/ent.d.ts +0 -0
  158. /package/{dist/core → core}/ent.js +0 -0
  159. /package/{dist/core → core}/global_schema.d.ts +0 -0
  160. /package/{dist/core → core}/global_schema.js +0 -0
  161. /package/{dist/core → core}/loaders/assoc_count_loader.d.ts +0 -0
  162. /package/{dist/core → core}/loaders/assoc_count_loader.js +0 -0
  163. /package/{dist/core → core}/loaders/assoc_edge_loader.d.ts +0 -0
  164. /package/{dist/core → core}/loaders/assoc_edge_loader.js +0 -0
  165. /package/{dist/core → core}/loaders/index.d.ts +0 -0
  166. /package/{dist/core → core}/loaders/index.js +0 -0
  167. /package/{dist/core → core}/loaders/loader.d.ts +0 -0
  168. /package/{dist/core → core}/loaders/loader.js +0 -0
  169. /package/{dist/core → core}/loaders/object_loader.d.ts +0 -0
  170. /package/{dist/core → core}/loaders/object_loader.js +0 -0
  171. /package/{dist/core → core}/loaders/query_loader.d.ts +0 -0
  172. /package/{dist/core → core}/loaders/query_loader.js +0 -0
  173. /package/{dist/core → core}/loaders/raw_count_loader.d.ts +0 -0
  174. /package/{dist/core → core}/loaders/raw_count_loader.js +0 -0
  175. /package/{dist/core → core}/logger.d.ts +0 -0
  176. /package/{dist/core → core}/logger.js +0 -0
  177. /package/{dist/core → core}/privacy.d.ts +0 -0
  178. /package/{dist/core → core}/privacy.js +0 -0
  179. /package/{dist/core → core}/query/custom_clause_query.d.ts +0 -0
  180. /package/{dist/core → core}/query/custom_clause_query.js +0 -0
  181. /package/{dist/core → core}/query/custom_query.d.ts +0 -0
  182. /package/{dist/core → core}/query/custom_query.js +0 -0
  183. /package/{dist/core → core}/query/index.d.ts +0 -0
  184. /package/{dist/core → core}/query/index.js +0 -0
  185. /package/{dist/core → core}/query_impl.d.ts +0 -0
  186. /package/{dist/core → core}/query_impl.js +0 -0
  187. /package/{dist/core → core}/viewer.d.ts +0 -0
  188. /package/{dist/core → core}/viewer.js +0 -0
  189. /package/{dist/graphql → graphql}/builtins/connection.d.ts +0 -0
  190. /package/{dist/graphql → graphql}/builtins/connection.js +0 -0
  191. /package/{dist/graphql → graphql}/builtins/edge.d.ts +0 -0
  192. /package/{dist/graphql → graphql}/builtins/edge.js +0 -0
  193. /package/{dist/graphql → graphql}/builtins/node.d.ts +0 -0
  194. /package/{dist/graphql → graphql}/builtins/node.js +0 -0
  195. /package/{dist/graphql → graphql}/graphql.d.ts +0 -0
  196. /package/{dist/graphql → graphql}/graphql.js +0 -0
  197. /package/{dist/graphql → graphql}/graphql_field_helpers.d.ts +0 -0
  198. /package/{dist/graphql → graphql}/graphql_field_helpers.js +0 -0
  199. /package/{dist/graphql → graphql}/index.d.ts +0 -0
  200. /package/{dist/graphql → graphql}/index.js +0 -0
  201. /package/{dist/graphql → graphql}/mutations/union.d.ts +0 -0
  202. /package/{dist/graphql → graphql}/mutations/union.js +0 -0
  203. /package/{dist/graphql → graphql}/node_resolver.d.ts +0 -0
  204. /package/{dist/graphql → graphql}/node_resolver.js +0 -0
  205. /package/{dist/graphql → graphql}/query/connection_type.d.ts +0 -0
  206. /package/{dist/graphql → graphql}/query/connection_type.js +0 -0
  207. /package/{dist/graphql → graphql}/query/edge_connection.d.ts +0 -0
  208. /package/{dist/graphql → graphql}/query/edge_connection.js +0 -0
  209. /package/{dist/graphql → graphql}/query/page_info.d.ts +0 -0
  210. /package/{dist/graphql → graphql}/query/page_info.js +0 -0
  211. /package/{dist/graphql → graphql}/query/shared_edge_connection.d.ts +0 -0
  212. /package/{dist/graphql → graphql}/query/shared_edge_connection.js +0 -0
  213. /package/{dist/graphql → graphql}/scalars/orderby_direction.d.ts +0 -0
  214. /package/{dist/graphql → graphql}/scalars/orderby_direction.js +0 -0
  215. /package/{dist/graphql → graphql}/scalars/time.d.ts +0 -0
  216. /package/{dist/graphql → graphql}/scalars/time.js +0 -0
  217. /package/{dist/imports → imports}/dataz/example1/_auth.d.ts +0 -0
  218. /package/{dist/imports → imports}/dataz/example1/_auth.js +0 -0
  219. /package/{dist/imports → imports}/dataz/example1/_viewer.d.ts +0 -0
  220. /package/{dist/imports → imports}/dataz/example1/_viewer.js +0 -0
  221. /package/{dist/imports → imports}/index.d.ts +0 -0
  222. /package/{dist/imports → imports}/index.js +0 -0
  223. /package/{dist/index.d.ts → index.d.ts} +0 -0
  224. /package/{dist/index.js → index.js} +0 -0
  225. /package/{dist/parse_schema → parse_schema}/parse.d.ts +0 -0
  226. /package/{dist/parse_schema → parse_schema}/parse.js +0 -0
  227. /package/{dist/schema → schema}/base_schema.d.ts +0 -0
  228. /package/{dist/schema → schema}/base_schema.js +0 -0
  229. /package/{dist/schema → schema}/field.d.ts +0 -0
  230. /package/{dist/schema → schema}/field.js +0 -0
  231. /package/{dist/schema → schema}/index.d.ts +0 -0
  232. /package/{dist/schema → schema}/index.js +0 -0
  233. /package/{dist/schema → schema}/json_field.d.ts +0 -0
  234. /package/{dist/schema → schema}/json_field.js +0 -0
  235. /package/{dist/schema → schema}/schema.d.ts +0 -0
  236. /package/{dist/schema → schema}/schema.js +0 -0
  237. /package/{dist/schema → schema}/struct_field.d.ts +0 -0
  238. /package/{dist/schema → schema}/struct_field.js +0 -0
  239. /package/{dist/schema → schema}/union_field.d.ts +0 -0
  240. /package/{dist/schema → schema}/union_field.js +0 -0
  241. /package/{dist/scripts → scripts}/custom_compiler.d.ts +0 -0
  242. /package/{dist/scripts → scripts}/custom_compiler.js +0 -0
  243. /package/{dist/scripts → scripts}/custom_graphql.d.ts +0 -0
  244. /package/{dist/scripts → scripts}/custom_graphql.js +0 -0
  245. /package/{dist/scripts → scripts}/migrate_v0.1.d.ts +0 -0
  246. /package/{dist/scripts → scripts}/migrate_v0.1.js +0 -0
  247. /package/{dist/scripts → scripts}/move_types.d.ts +0 -0
  248. /package/{dist/scripts → scripts}/move_types.js +0 -0
  249. /package/{dist/scripts → scripts}/read_schema.d.ts +0 -0
  250. /package/{dist/scripts → scripts}/read_schema.js +0 -0
  251. /package/{dist/testutils → testutils}/action/complex_schemas.d.ts +0 -0
  252. /package/{dist/testutils → testutils}/action/complex_schemas.js +0 -0
  253. /package/{dist/testutils → testutils}/builder.d.ts +0 -0
  254. /package/{dist/testutils → testutils}/builder.js +0 -0
  255. /package/{dist/testutils → testutils}/context/test_context.d.ts +0 -0
  256. /package/{dist/testutils → testutils}/context/test_context.js +0 -0
  257. /package/{dist/testutils → testutils}/db/fixture.d.ts +0 -0
  258. /package/{dist/testutils → testutils}/db/fixture.js +0 -0
  259. /package/{dist/testutils → testutils}/db/temp_db.d.ts +0 -0
  260. /package/{dist/testutils → testutils}/db/temp_db.js +0 -0
  261. /package/{dist/testutils → testutils}/db/value.d.ts +0 -0
  262. /package/{dist/testutils → testutils}/db/value.js +0 -0
  263. /package/{dist/testutils → testutils}/db_mock.d.ts +0 -0
  264. /package/{dist/testutils → testutils}/db_mock.js +0 -0
  265. /package/{dist/testutils → testutils}/db_time_zone.d.ts +0 -0
  266. /package/{dist/testutils → testutils}/db_time_zone.js +0 -0
  267. /package/{dist/testutils → testutils}/ent-graphql-tests/index.d.ts +0 -0
  268. /package/{dist/testutils → testutils}/ent-graphql-tests/index.js +0 -0
  269. /package/{dist/testutils → testutils}/fake_comms.d.ts +0 -0
  270. /package/{dist/testutils → testutils}/fake_comms.js +0 -0
  271. /package/{dist/testutils → testutils}/fake_data/const.d.ts +0 -0
  272. /package/{dist/testutils → testutils}/fake_data/const.js +0 -0
  273. /package/{dist/testutils → testutils}/fake_data/events_query.d.ts +0 -0
  274. /package/{dist/testutils → testutils}/fake_data/events_query.js +0 -0
  275. /package/{dist/testutils → testutils}/fake_data/fake_contact.d.ts +0 -0
  276. /package/{dist/testutils → testutils}/fake_data/fake_contact.js +0 -0
  277. /package/{dist/testutils → testutils}/fake_data/fake_event.d.ts +0 -0
  278. /package/{dist/testutils → testutils}/fake_data/fake_event.js +0 -0
  279. /package/{dist/testutils → testutils}/fake_data/fake_tag.d.ts +0 -0
  280. /package/{dist/testutils → testutils}/fake_data/fake_tag.js +0 -0
  281. /package/{dist/testutils → testutils}/fake_data/fake_user.d.ts +0 -0
  282. /package/{dist/testutils → testutils}/fake_data/fake_user.js +0 -0
  283. /package/{dist/testutils → testutils}/fake_data/index.d.ts +0 -0
  284. /package/{dist/testutils → testutils}/fake_data/index.js +0 -0
  285. /package/{dist/testutils → testutils}/fake_data/internal.d.ts +0 -0
  286. /package/{dist/testutils → testutils}/fake_data/internal.js +0 -0
  287. /package/{dist/testutils → testutils}/fake_data/tag_query.d.ts +0 -0
  288. /package/{dist/testutils → testutils}/fake_data/tag_query.js +0 -0
  289. /package/{dist/testutils → testutils}/fake_data/test_helpers.d.ts +0 -0
  290. /package/{dist/testutils → testutils}/fake_data/user_query.d.ts +0 -0
  291. /package/{dist/testutils → testutils}/fake_data/user_query.js +0 -0
  292. /package/{dist/testutils → testutils}/fake_log.d.ts +0 -0
  293. /package/{dist/testutils → testutils}/fake_log.js +0 -0
  294. /package/{dist/testutils → testutils}/mock_date.d.ts +0 -0
  295. /package/{dist/testutils → testutils}/mock_date.js +0 -0
  296. /package/{dist/testutils → testutils}/mock_log.d.ts +0 -0
  297. /package/{dist/testutils → testutils}/mock_log.js +0 -0
  298. /package/{dist/testutils → testutils}/parse_sql.d.ts +0 -0
  299. /package/{dist/testutils → testutils}/parse_sql.js +0 -0
  300. /package/{dist/testutils → testutils}/test_edge_global_schema.d.ts +0 -0
  301. /package/{dist/testutils → testutils}/test_edge_global_schema.js +0 -0
  302. /package/{dist/testutils → testutils}/write.d.ts +0 -0
  303. /package/{dist/testutils → testutils}/write.js +0 -0
  304. /package/{dist/tsc → tsc}/ast.d.ts +0 -0
  305. /package/{dist/tsc → tsc}/ast.js +0 -0
  306. /package/{dist/tsc → tsc}/compilerOptions.d.ts +0 -0
  307. /package/{dist/tsc → tsc}/compilerOptions.js +0 -0
  308. /package/{dist/tsc → tsc}/move_generated.d.ts +0 -0
  309. /package/{dist/tsc → tsc}/move_generated.js +0 -0
  310. /package/{dist/tsc → tsc}/transform.d.ts +0 -0
  311. /package/{dist/tsc → tsc}/transform.js +0 -0
  312. /package/{dist/tsc → tsc}/transform_action.d.ts +0 -0
  313. /package/{dist/tsc → tsc}/transform_action.js +0 -0
  314. /package/{dist/tsc → tsc}/transform_ent.d.ts +0 -0
  315. /package/{dist/tsc → tsc}/transform_ent.js +0 -0
  316. /package/{dist/tsc → tsc}/transform_schema.d.ts +0 -0
  317. /package/{dist/tsc → tsc}/transform_schema.js +0 -0
@@ -1,1527 +0,0 @@
1
- import {
2
- ID,
3
- Data,
4
- Ent,
5
- Viewer,
6
- LoadEntOptions,
7
- PrivacyError,
8
- PrivacyPolicy,
9
- CreateRowOptions,
10
- } from "../core/base";
11
- import {
12
- loadEdgeDatas,
13
- applyPrivacyPolicyForRow,
14
- parameterizedQueryOptions,
15
- loadEdgeData,
16
- } from "../core/ent";
17
- import {
18
- getFields,
19
- SchemaInputType,
20
- Field,
21
- getTransformedUpdateOp,
22
- SQLStatementOperation,
23
- TransformedUpdateOperation,
24
- FieldInfoMap,
25
- getFieldsWithEditPrivacy,
26
- getFieldsForCreateAction,
27
- } from "../schema/schema";
28
- import {
29
- Changeset,
30
- ChangesetOptions,
31
- Executor,
32
- Validator,
33
- } from "../action/action";
34
- import {
35
- AssocEdgeInputOptions,
36
- DataOperation,
37
- EdgeOperation,
38
- EditNodeOperation,
39
- DeleteNodeOperation,
40
- EditNodeOptions,
41
- AssocEdgeOptions,
42
- ConditionalOperation,
43
- ConditionalNodeOperation,
44
- } from "./operations";
45
- import { WriteOperation, Builder, Action } from "../action";
46
- import { applyPrivacyPolicy, applyPrivacyPolicyX } from "../core/privacy";
47
- import { ListBasedExecutor, ComplexExecutor } from "./executor";
48
- import { log } from "../core/logger";
49
- import { Trigger } from "./action";
50
- import memoize from "memoizee";
51
- import * as clause from "../core/clause";
52
- import { isPromise } from "util/types";
53
- import { RawQueryOperation } from "./operations";
54
-
55
- type MaybeNull<T extends Ent> = T | null;
56
- type TMaybleNullableEnt<T extends Ent> = T | MaybeNull<T>;
57
-
58
- export interface OrchestratorOptions<
59
- TEnt extends Ent<TViewer>,
60
- TInput extends Data,
61
- TViewer extends Viewer,
62
- TExistingEnt extends TMaybleNullableEnt<TEnt> = MaybeNull<TEnt>,
63
- > {
64
- viewer: TViewer;
65
- operation: WriteOperation;
66
- tableName: string;
67
- // should we make it nullable for delete?
68
- loaderOptions: LoadEntOptions<TEnt, TViewer>;
69
- // key, usually 'id' that's being updated
70
- key: string;
71
-
72
- builder: Builder<TEnt, TViewer, TExistingEnt>;
73
- action?: Action<TEnt, Builder<TEnt, TViewer>, TViewer, TInput>;
74
- schema: SchemaInputType;
75
- editedFields(): Map<string, any> | Promise<Map<string, any>>;
76
- // this is called with fields with defaultValueOnCreate|Edit
77
- updateInput?: (data: TInput) => void;
78
-
79
- // mapping of column to expressions to use
80
- // if set and a column exists, we use the expression here instead of the given expression in the sql query
81
- // for now, only works in an `UPDATE` query i.e. with operation === WriteOperation.Insert
82
- // if passed with a different operation type, it throws an Error
83
- // any value provided in editedFields for this value is ignored and we assume the right thing is done with said expression
84
-
85
- // TODO ability to get expression value, parse it and update it
86
- // e.g. if somehow there's a promotion if you play your 1000th game (which costs 5 tokens),
87
- // we increase your balance by 1000000 after the cost of the ticket
88
- // or we completely use your balance or something
89
- expressions?: Map<string, clause.Clause>;
90
- fieldInfo: FieldInfoMap;
91
- }
92
-
93
- interface edgeInputDataOpts {
94
- edgeType: string;
95
- id: Builder<Ent> | ID; // when an OutboundEdge, this is the id2, when an inbound edge, this is the id1
96
- nodeType?: string; // expected to be set for WriteOperation.Insert and undefined for WriteOperation.Delete
97
- options?: AssocEdgeInputOptions;
98
- }
99
-
100
- // hmm is it worth having multiple types here or just having one?
101
- // we have one type here instead
102
- export interface EdgeInputData extends edgeInputDataOpts {
103
- isBuilder(id: Builder<Ent> | ID): id is Builder<Ent>;
104
- }
105
-
106
- export enum edgeDirection {
107
- inboundEdge,
108
- outboundEdge,
109
- }
110
-
111
- interface internalEdgeInputData extends edgeInputDataOpts {
112
- direction: edgeDirection;
113
- }
114
-
115
- class edgeInputData implements EdgeInputData {
116
- direction: edgeDirection;
117
- edgeType: string;
118
- id: Builder<Ent> | ID;
119
- nodeType?: string;
120
- options?: AssocEdgeInputOptions;
121
-
122
- constructor(opts: internalEdgeInputData) {
123
- Object.assign(this, opts);
124
- }
125
-
126
- isBuilder(id: Builder<Ent> | ID): id is Builder<Ent> {
127
- return (id as Builder<Ent>).placeholderID !== undefined;
128
- }
129
- }
130
-
131
- type IDMap = Map<ID, edgeInputData>;
132
- type OperationMap = Map<WriteOperation, IDMap>;
133
- // this is a map of
134
- // edgeType : {
135
- // WriteOperation: {
136
- // id: {
137
- // id input
138
- // }
139
- // }
140
- // }
141
- type EdgeMap = Map<string, OperationMap>;
142
-
143
- function getViewer(viewer: Viewer) {
144
- if (!viewer.viewerID) {
145
- return "Logged out Viewer";
146
- } else {
147
- return `Viewer with ID ${viewer.viewerID}`;
148
- }
149
- }
150
-
151
- class EntCannotCreateEntError extends Error implements PrivacyError {
152
- privacyPolicy: PrivacyPolicy;
153
- constructor(privacyPolicy: PrivacyPolicy, action: Action<Ent, Builder<Ent>>) {
154
- let msg = `${getViewer(action.viewer)} does not have permission to create ${
155
- action.builder.ent.name
156
- }`;
157
- super(msg);
158
- this.privacyPolicy = privacyPolicy;
159
- }
160
- }
161
-
162
- class EntCannotEditEntError extends Error implements PrivacyError {
163
- privacyPolicy: PrivacyPolicy;
164
- constructor(
165
- privacyPolicy: PrivacyPolicy,
166
- action: Action<Ent, Builder<Ent>>,
167
- ent: Ent,
168
- ) {
169
- let msg = `${getViewer(action.viewer)} does not have permission to edit ${
170
- ent.constructor.name
171
- }`;
172
- super(msg);
173
- this.privacyPolicy = privacyPolicy;
174
- }
175
- }
176
-
177
- class EntCannotEditEntFieldError extends Error implements PrivacyError {
178
- privacyPolicy: PrivacyPolicy;
179
- constructor(
180
- privacyPolicy: PrivacyPolicy,
181
- viewer: Viewer,
182
- field: string,
183
- ent: Ent,
184
- ) {
185
- let msg = `${getViewer(
186
- viewer,
187
- )} does not have permission to edit field ${field} in ${
188
- ent.constructor.name
189
- }`;
190
- super(msg);
191
- this.privacyPolicy = privacyPolicy;
192
- }
193
- }
194
-
195
- class EntCannotDeleteEntError extends Error implements PrivacyError {
196
- privacyPolicy: PrivacyPolicy;
197
- constructor(
198
- privacyPolicy: PrivacyPolicy,
199
- action: Action<Ent, Builder<Ent>>,
200
- ent: Ent,
201
- ) {
202
- let msg = `${getViewer(action.viewer)} does not have permission to delete ${
203
- ent.constructor.name
204
- }`;
205
- super(msg);
206
- this.privacyPolicy = privacyPolicy;
207
- }
208
- }
209
-
210
- interface fieldsInfo {
211
- editedData: Data;
212
- editedFields: Map<string, any>;
213
- schemaFields: Map<string, Field>;
214
- userDefinedKeys: Set<string>;
215
- editPrivacyFields: Map<string, PrivacyPolicy>;
216
- }
217
-
218
- export class Orchestrator<
219
- TEnt extends Ent<TViewer>,
220
- TInput extends Data,
221
- TViewer extends Viewer,
222
- TExistingEnt extends TMaybleNullableEnt<TEnt> = MaybeNull<TEnt>,
223
- > {
224
- private edgeSet: Set<string> = new Set<string>();
225
- private edges: EdgeMap = new Map();
226
- private conditionalEdges: EdgeMap = new Map();
227
- private validatedFields: Data | null;
228
- private logValues: Data | null;
229
- private changesets: Changeset[] = [];
230
- private dependencies: Map<ID, Builder<TEnt>> = new Map();
231
- private fieldsToResolve: string[] = [];
232
- private mainOp: DataOperation<TEnt> | null;
233
- viewer: Viewer;
234
- private defaultFieldsByFieldName: Data = {};
235
- private defaultFieldsByTSName: Data = {};
236
- // we can transform from one update to another so we wanna differentiate
237
- // btw the beginning op and the transformed one we end up using
238
- private actualOperation: WriteOperation;
239
- // same with existingEnt. can transform so we wanna know what we started with and now where we are.
240
- private existingEnt: TExistingEnt;
241
- private disableTransformations: boolean;
242
- private onConflict: CreateRowOptions["onConflict"] | undefined;
243
- private memoizedGetFields: () => Promise<fieldsInfo>;
244
-
245
- constructor(
246
- private options: OrchestratorOptions<TEnt, TInput, TViewer, TExistingEnt>,
247
- ) {
248
- this.viewer = options.viewer;
249
- this.actualOperation = this.options.operation;
250
- this.existingEnt = this.options.builder.existingEnt;
251
- this.memoizedGetFields = memoize(this.getFieldsInfo.bind(this));
252
- }
253
-
254
- // don't type this because we don't care
255
- __getOptions(): OrchestratorOptions<any, any, any, any> {
256
- return this.options;
257
- }
258
-
259
- private addEdge(
260
- edge: edgeInputData,
261
- op: WriteOperation,
262
- conditional?: boolean,
263
- ) {
264
- this.edgeSet.add(edge.edgeType);
265
-
266
- let m1: OperationMap = this.edges.get(edge.edgeType) || new Map();
267
- let m2: IDMap = m1.get(op) || new Map();
268
- let id: ID;
269
- if (edge.isBuilder(edge.id)) {
270
- id = edge.id.placeholderID;
271
- } else {
272
- id = edge.id;
273
- }
274
- // let id = edge.id.toString(); // TODO confirm that toString for builder is placeholderID. if not, add it or change this...
275
- // set or overwrite the new edge data for said id
276
- m2.set(id, edge);
277
- m1.set(op, m2);
278
- if (conditional && this.onConflict) {
279
- this.conditionalEdges.set(edge.edgeType, m1);
280
- } else {
281
- this.edges.set(edge.edgeType, m1);
282
- }
283
- }
284
-
285
- setDisableTransformations(val: boolean) {
286
- this.disableTransformations = val;
287
- }
288
-
289
- setOnConflictOptions(onConflict: CreateRowOptions["onConflict"]) {
290
- if (onConflict?.onConflictConstraint && !onConflict.updateCols) {
291
- throw new Error(`cannot set onConflictConstraint without updateCols`);
292
- }
293
- this.onConflict = onConflict;
294
- }
295
-
296
- addInboundEdge<T2 extends Ent>(
297
- id1: ID | Builder<T2, any>,
298
- edgeType: string,
299
- nodeType: string,
300
- options?: AssocEdgeInputOptions,
301
- ) {
302
- this.addEdge(
303
- new edgeInputData({
304
- id: id1,
305
- edgeType,
306
- nodeType,
307
- options,
308
- direction: edgeDirection.inboundEdge,
309
- }),
310
- WriteOperation.Insert,
311
- options?.conditional,
312
- );
313
- }
314
-
315
- addOutboundEdge<T2 extends Ent>(
316
- id2: ID | Builder<T2, any>,
317
- edgeType: string,
318
- nodeType: string,
319
- options?: AssocEdgeInputOptions,
320
- ) {
321
- this.addEdge(
322
- new edgeInputData({
323
- id: id2,
324
- edgeType,
325
- nodeType,
326
- options,
327
- direction: edgeDirection.outboundEdge,
328
- }),
329
- WriteOperation.Insert,
330
- options?.conditional,
331
- );
332
- }
333
-
334
- removeInboundEdge(id1: ID, edgeType: string, options?: AssocEdgeOptions) {
335
- this.addEdge(
336
- new edgeInputData({
337
- id: id1,
338
- edgeType,
339
- direction: edgeDirection.inboundEdge,
340
- options,
341
- }),
342
- WriteOperation.Delete,
343
- options?.conditional,
344
- );
345
- }
346
-
347
- removeOutboundEdge(id2: ID, edgeType: string, options?: AssocEdgeOptions) {
348
- this.addEdge(
349
- new edgeInputData({
350
- id: id2,
351
- edgeType,
352
- direction: edgeDirection.outboundEdge,
353
- options,
354
- }),
355
- WriteOperation.Delete,
356
- options?.conditional,
357
- );
358
- }
359
-
360
- // this doesn't take a direction as that's an implementation detail
361
- // it doesn't make any sense to use the same edgeType for inbound and outbound edges
362
- // so no need for that
363
- getInputEdges(edgeType: string, op: WriteOperation): EdgeInputData[] {
364
- let m: IDMap = this.edges.get(edgeType)?.get(op) || new Map();
365
- // want a list and not an IterableIterator
366
- let ret: edgeInputData[] = [];
367
- m.forEach((v) => ret.push(v));
368
-
369
- return ret;
370
- }
371
-
372
- // this privides a way to clear data if needed
373
- // we don't have a great API for this yet
374
- clearInputEdges(edgeType: string, op: WriteOperation, id?: ID) {
375
- let m: IDMap = this.edges.get(edgeType)?.get(op) || new Map();
376
- if (id) {
377
- m.delete(id);
378
- } else {
379
- m.clear();
380
- }
381
- }
382
-
383
- private buildMainOp(conditionalBuilder?: Builder<any>): DataOperation {
384
- // this assumes we have validated fields
385
- switch (this.actualOperation) {
386
- case WriteOperation.Delete:
387
- return new DeleteNodeOperation(
388
- this.existingEnt!.id,
389
- this.options.builder,
390
- {
391
- tableName: this.options.tableName,
392
- },
393
- );
394
- default:
395
- if (this.actualOperation === WriteOperation.Edit && !this.existingEnt) {
396
- throw new Error(
397
- `existing ent required with operation ${this.actualOperation}`,
398
- );
399
- }
400
- if (
401
- this.options.expressions &&
402
- this.actualOperation !== WriteOperation.Edit
403
- ) {
404
- throw new Error(
405
- `expressions are only supported in edit operations for now`,
406
- );
407
- }
408
- const opts: EditNodeOptions<TEnt> = {
409
- fields: this.validatedFields!,
410
- tableName: this.options.tableName,
411
- fieldsToResolve: this.fieldsToResolve,
412
- key: this.options.key,
413
- loadEntOptions: this.options.loaderOptions,
414
- whereClause: clause.Eq(this.options.key, this.existingEnt?.id),
415
- expressions: this.options.expressions,
416
- onConflict: this.onConflict,
417
- builder: this.options.builder,
418
- };
419
- if (this.logValues) {
420
- opts.fieldsToLog = this.logValues;
421
- }
422
- this.mainOp = new EditNodeOperation(opts, this.existingEnt);
423
- if (conditionalBuilder) {
424
- this.mainOp = new ConditionalNodeOperation(
425
- this.mainOp,
426
- conditionalBuilder,
427
- );
428
- }
429
- return this.mainOp;
430
- }
431
- }
432
-
433
- // edgeType e.g. EdgeType.OrganizationToArchivedMembers
434
- // add | remove
435
- // operation e.g. WriteOperation.Insert or WriteOperation.Delete
436
- // and then what's the format to return and how do we deal with placeholders?
437
- // { id: ID | Builder<Ent>}
438
- // or we push the resolving to the end and return the raw data?
439
- // seems like the best approach...
440
- // so if you pass a builder, you get it back
441
- // and can pass it to the other e.g. removeEdge
442
- //
443
- private getEdgeOperation(
444
- edgeType: string,
445
- op: WriteOperation,
446
- edge: internalEdgeInputData,
447
- ): EdgeOperation {
448
- if (op === WriteOperation.Insert) {
449
- if (!edge.nodeType) {
450
- throw new Error(`no nodeType for edge when adding outboundEdge`);
451
- }
452
- if (edge.direction === edgeDirection.outboundEdge) {
453
- return EdgeOperation.outboundEdge(
454
- this.options.builder,
455
- edgeType,
456
- edge.id,
457
- edge.nodeType,
458
- edge.options,
459
- );
460
- } else {
461
- return EdgeOperation.inboundEdge(
462
- this.options.builder,
463
- edgeType,
464
- edge.id,
465
- edge.nodeType,
466
- edge.options,
467
- );
468
- }
469
- } else if (op === WriteOperation.Delete) {
470
- if (this.isBuilder(edge.id)) {
471
- throw new Error("removeEdge APIs don't take a builder as an argument");
472
- }
473
- let id2 = edge.id as ID;
474
-
475
- if (edge.direction === edgeDirection.outboundEdge) {
476
- return EdgeOperation.removeOutboundEdge(
477
- this.options.builder,
478
- edgeType,
479
- id2,
480
- edge.options,
481
- );
482
- } else {
483
- return EdgeOperation.removeInboundEdge(
484
- this.options.builder,
485
- edgeType,
486
- id2,
487
- edge.options,
488
- );
489
- }
490
- }
491
- throw new Error(
492
- "could not find an edge operation from the given parameters",
493
- );
494
- }
495
-
496
- private async buildEdgeOps(
497
- ops: DataOperation[],
498
- conditionalBuilder: Builder<any>,
499
- conditionalOverride: boolean,
500
- ): Promise<void> {
501
- const edgeDatas = await loadEdgeDatas(...Array.from(this.edgeSet.values()));
502
- const edges: [EdgeMap, boolean][] = [
503
- [this.edges, false],
504
- [this.conditionalEdges, true],
505
- ];
506
- // conditional should only apply if onconflict...
507
- // if no upsert and just create, nothing to do here
508
- for (const edgeInfo of edges) {
509
- const [edges, conditionalEdge] = edgeInfo;
510
- const conditional = conditionalOverride || conditionalEdge;
511
- for (const [edgeType, m] of edges) {
512
- for (const [op, m2] of m) {
513
- for (const [_, edge] of m2) {
514
- let edgeOp = this.getEdgeOperation(edgeType, op, edge);
515
- if (conditional) {
516
- ops.push(new ConditionalOperation(edgeOp, conditionalBuilder));
517
- } else {
518
- ops.push(edgeOp);
519
- }
520
- const edgeData = edgeDatas.get(edgeType);
521
- if (!edgeData) {
522
- throw new Error(`could not load edge data for '${edgeType}'`);
523
- }
524
- // similar logic in EntChangeset.changesetFromEdgeOp
525
- // doesn't support conditional edges
526
-
527
- if (edgeData.symmetricEdge) {
528
- let symmetric: DataOperation = edgeOp.symmetricEdge();
529
- if (conditional) {
530
- symmetric = new ConditionalOperation(
531
- symmetric,
532
- conditionalBuilder,
533
- );
534
- }
535
- ops.push(symmetric);
536
- }
537
-
538
- if (edgeData.inverseEdgeType) {
539
- let inverse: DataOperation = edgeOp.inverseEdge(edgeData);
540
- if (conditional) {
541
- inverse = new ConditionalOperation(inverse, conditionalBuilder);
542
- }
543
- ops.push(inverse);
544
- }
545
- }
546
- }
547
- }
548
- }
549
- }
550
-
551
- private throwError(): PrivacyError {
552
- const action = this.options.action;
553
- let privacyPolicy = action?.getPrivacyPolicy();
554
- if (!privacyPolicy || !action) {
555
- throw new Error(`shouldn't get here if no privacyPolicy for action`);
556
- }
557
-
558
- if (this.actualOperation === WriteOperation.Insert) {
559
- return new EntCannotCreateEntError(privacyPolicy, action);
560
- } else if (this.actualOperation === WriteOperation.Edit) {
561
- return new EntCannotEditEntError(
562
- privacyPolicy,
563
- action,
564
- this.existingEnt!,
565
- );
566
- }
567
- return new EntCannotDeleteEntError(
568
- privacyPolicy,
569
- action,
570
- this.existingEnt!,
571
- );
572
- }
573
-
574
- private async getRowForPrivacyPolicyImpl(
575
- schemaFields: Map<string, Field>,
576
- editedData: Data,
577
- ): Promise<Data> {
578
- // need to format fields if possible because ent constructors expect data that's
579
- // in the format that's coming from the db
580
- // required for object fields...
581
-
582
- const formatted = { ...editedData };
583
- for (const [fieldName, field] of schemaFields) {
584
- if (!field.format) {
585
- continue;
586
- }
587
-
588
- let dbKey = this.getStorageKey(fieldName);
589
- let val = formatted[dbKey];
590
- if (!val) {
591
- continue;
592
- }
593
-
594
- if (field.valid) {
595
- let valid = field.valid(val);
596
- if (isPromise(valid)) {
597
- valid = await valid;
598
- }
599
- // if not valid, don't format and don't pass to ent?
600
- // or just early throw here
601
- if (!valid) {
602
- continue;
603
- // throw new Error(`invalid field ${fieldName} with value ${val}`);
604
- }
605
- }
606
-
607
- // nested so it's not JSON stringified or anything like that
608
- val = field.format(formatted[dbKey], true);
609
- if (isPromise(val)) {
610
- val = await val;
611
- }
612
-
613
- formatted[dbKey] = val;
614
- }
615
- return formatted;
616
- }
617
-
618
- private async getEntForPrivacyPolicyImpl(
619
- schemaFields: Map<string, Field>,
620
- editedData: Data,
621
- viewerToUse: TViewer,
622
- rowToUse?: Data,
623
- ): Promise<TEnt> {
624
- if (this.actualOperation !== WriteOperation.Insert) {
625
- return this.existingEnt!;
626
- }
627
-
628
- if (!rowToUse) {
629
- rowToUse = await this.getRowForPrivacyPolicyImpl(
630
- schemaFields,
631
- editedData,
632
- );
633
- }
634
-
635
- // we create an unsafe ent to be used for privacy policies
636
- return new this.options.builder.ent(viewerToUse, rowToUse);
637
- }
638
-
639
- private getSQLStatementOperation(): SQLStatementOperation {
640
- switch (this.actualOperation) {
641
- case WriteOperation.Edit:
642
- return SQLStatementOperation.Update;
643
- case WriteOperation.Insert:
644
- return SQLStatementOperation.Insert;
645
- case WriteOperation.Delete:
646
- return SQLStatementOperation.Delete;
647
- }
648
- }
649
-
650
- private getWriteOpForSQLStamentOp(op: SQLStatementOperation): WriteOperation {
651
- switch (op) {
652
- case SQLStatementOperation.Update:
653
- return WriteOperation.Edit;
654
- case SQLStatementOperation.Insert:
655
- return WriteOperation.Insert;
656
- case SQLStatementOperation.Update:
657
- return WriteOperation.Delete;
658
- default:
659
- throw new Error("invalid path");
660
- }
661
- }
662
-
663
- // if you're doing custom privacy within an action and want to
664
- // get either the unsafe ent or the existing ent that's being edited
665
- async getPossibleUnsafeEntForPrivacy(): Promise<TEnt> {
666
- if (this.actualOperation !== WriteOperation.Insert) {
667
- return this.existingEnt!;
668
- }
669
- const { schemaFields, editedData } = await this.memoizedGetFields();
670
- return this.getEntForPrivacyPolicyImpl(
671
- schemaFields,
672
- editedData,
673
- this.options.viewer,
674
- );
675
- }
676
-
677
- // this gets the fields that were explicitly set plus any default or transformed values
678
- // mainly exists to get default fields e.g. default id to be used in triggers
679
- // NOTE: this API may change in the future
680
- // doesn't work to get ids for autoincrement keys
681
- async getEditedData() {
682
- const { editedData } = await this.memoizedGetFields();
683
- return editedData;
684
- }
685
-
686
- /**
687
- * @returns validated and formatted fields that would be written to the db
688
- * throws an error if called before valid() or validX() has been called
689
- */
690
- getValidatedFields() {
691
- if (this.validatedFields === null) {
692
- throw new Error(
693
- `trying to call getValidatedFields before validating fields`,
694
- );
695
- }
696
- return this.validatedFields;
697
- }
698
-
699
- // Note: this is memoized. call memoizedGetFields instead
700
- private async getFieldsInfo(): Promise<fieldsInfo> {
701
- const action = this.options.action;
702
- const builder = this.options.builder;
703
-
704
- // future optimization: can get schemaFields to memoize based on different values
705
- const schemaFields = getFields(this.options.schema);
706
-
707
- // also future optimization, no need to go through the list of fields multiple times
708
- let editPrivacyFields = new Map<string, PrivacyPolicy>();
709
- switch (this.actualOperation) {
710
- case WriteOperation.Edit:
711
- editPrivacyFields = getFieldsWithEditPrivacy(
712
- this.options.schema,
713
- this.options.fieldInfo,
714
- );
715
- break;
716
-
717
- case WriteOperation.Insert:
718
- editPrivacyFields = getFieldsForCreateAction(
719
- this.options.schema,
720
- this.options.fieldInfo,
721
- );
722
- break;
723
- }
724
-
725
- const editedFields = await this.options.editedFields();
726
-
727
- let { data: editedData, userDefinedKeys } =
728
- await this.getFieldsWithDefaultValues(
729
- builder,
730
- schemaFields,
731
- editedFields,
732
- action,
733
- );
734
-
735
- return {
736
- editedData,
737
- editedFields,
738
- schemaFields,
739
- userDefinedKeys,
740
- editPrivacyFields,
741
- };
742
- }
743
-
744
- private async validate(): Promise<Error[]> {
745
- // existing ent required for edit or delete operations
746
- switch (this.actualOperation) {
747
- case WriteOperation.Delete:
748
- case WriteOperation.Edit:
749
- if (!this.existingEnt) {
750
- throw new Error(
751
- `existing ent required with operation ${this.actualOperation}`,
752
- );
753
- }
754
- }
755
-
756
- const { schemaFields, editedData, userDefinedKeys, editPrivacyFields } =
757
- await this.memoizedGetFields();
758
- const action = this.options.action;
759
- const builder = this.options.builder;
760
-
761
- // this runs in following phases:
762
- // * set default fields and pass to builder so the value can be checked by triggers/observers/validators
763
- // * privacy policy (use unsafe ent if we have it)
764
- // * triggers
765
- // * validators
766
- let privacyPolicy = action?.getPrivacyPolicy();
767
-
768
- const errors: Error[] = [];
769
-
770
- if (privacyPolicy) {
771
- const ent = await this.getEntForPrivacyPolicyImpl(
772
- schemaFields,
773
- editedData,
774
- this.options.viewer,
775
- );
776
-
777
- try {
778
- await applyPrivacyPolicyX(this.options.viewer, privacyPolicy, ent, () =>
779
- this.throwError(),
780
- );
781
- } catch (err) {
782
- errors.push(err);
783
- }
784
- }
785
-
786
- // we have edit privacy fields, so we need to apply privacy policy on those
787
- const promises: Promise<void>[] = [];
788
- if (editPrivacyFields.size) {
789
- // get row based on edited data
790
- const row = await this.getRowForPrivacyPolicyImpl(
791
- schemaFields,
792
- editedData,
793
- );
794
- // get viewer for ent load based on formatted row
795
- const viewer = await this.viewerForEntLoad(row);
796
-
797
- const ent = await this.getEntForPrivacyPolicyImpl(
798
- schemaFields,
799
- editedData,
800
- viewer,
801
- row,
802
- );
803
-
804
- for (const [k, policy] of editPrivacyFields) {
805
- if (editedData[k] === undefined || !userDefinedKeys.has(k)) {
806
- continue;
807
- }
808
- promises.push(
809
- (async () => {
810
- const r = await applyPrivacyPolicy(viewer, policy, ent);
811
- if (!r) {
812
- errors.push(
813
- new EntCannotEditEntFieldError(policy, viewer, k, ent!),
814
- );
815
- }
816
- })(),
817
- );
818
- }
819
- await Promise.all(promises);
820
- }
821
-
822
- // privacy or field errors should return first so it's less confusing
823
- if (errors.length) {
824
- return errors;
825
- }
826
-
827
- // have to run triggers which update fields first before field and other validators
828
- // so running this first to build things up
829
- if (action?.getTriggers) {
830
- await this.triggers(action!, builder, action.getTriggers());
831
- }
832
-
833
- let validators: Validator<TEnt, Builder<TEnt, TViewer>, TViewer, TInput>[] =
834
- [];
835
- if (action?.getValidators) {
836
- validators = action.getValidators();
837
- }
838
-
839
- // not ideal we're calling this twice. fix...
840
- // needed for now. may need to rewrite some of this?
841
- const editedFields2 = await this.options.editedFields();
842
- const [errs2, errs3] = await Promise.all([
843
- this.formatAndValidateFields(schemaFields, editedFields2),
844
- this.validators(validators, action!, builder),
845
- ]);
846
- errors.push(...errs2);
847
- errors.push(...errs3);
848
- return errors;
849
- }
850
-
851
- private async triggers(
852
- action: Action<TEnt, Builder<TEnt, TViewer>, TViewer, TInput>,
853
- builder: Builder<TEnt, TViewer>,
854
- triggers: Array<
855
- | Trigger<TEnt, Builder<TEnt, TViewer>>
856
- | Array<Trigger<TEnt, Builder<TEnt, TViewer>>>
857
- >,
858
- ): Promise<void> {
859
- let groups: Trigger<TEnt, Builder<TEnt, TViewer>>[][] = [];
860
- let lastArray = 0;
861
- let prevWasArray = false;
862
- for (let i = 0; i < triggers.length; i++) {
863
- let t = triggers[i];
864
- if (Array.isArray(t)) {
865
- if (!prevWasArray) {
866
- // @ts-ignore
867
- groups.push(triggers.slice(lastArray, i));
868
- }
869
- groups.push(t);
870
-
871
- prevWasArray = true;
872
- lastArray++;
873
- } else {
874
- if (i === triggers.length - 1) {
875
- // @ts-ignore
876
- groups.push(triggers.slice(lastArray, i + 1));
877
- }
878
- prevWasArray = false;
879
- }
880
- }
881
-
882
- for (const triggers of groups) {
883
- await Promise.all(
884
- triggers.map(async (trigger) => {
885
- let ret = await trigger.changeset(builder, action.getInput());
886
- if (Array.isArray(ret)) {
887
- ret = await Promise.all(ret);
888
- }
889
-
890
- if (Array.isArray(ret)) {
891
- for (const v of ret) {
892
- if (typeof v === "object") {
893
- this.changesets.push(v);
894
- }
895
- }
896
- } else if (ret) {
897
- this.changesets.push(ret);
898
- }
899
- }),
900
- );
901
- }
902
- }
903
-
904
- private async validators(
905
- validators: Validator<TEnt, Builder<TEnt, TViewer>, TViewer, TInput>[],
906
- action: Action<TEnt, Builder<TEnt, TViewer>, TViewer, TInput>,
907
- builder: Builder<TEnt, TViewer>,
908
- ): Promise<Error[]> {
909
- const errors: Error[] = [];
910
- await Promise.all(
911
- validators.map(async (v) => {
912
- try {
913
- const r = await v.validate(builder, action.getInput());
914
- if (r instanceof Error) {
915
- errors.push(r);
916
- }
917
- } catch (err) {
918
- errors.push(err as Error);
919
- }
920
- }),
921
- );
922
- return errors;
923
- }
924
-
925
- private isBuilder(val: Builder<TEnt> | any): val is Builder<TEnt> {
926
- return (val as Builder<TEnt>).placeholderID !== undefined;
927
- }
928
-
929
- private getInputKey(k: string) {
930
- return this.options.fieldInfo[k].inputKey;
931
- }
932
-
933
- private getStorageKey(k: string) {
934
- return this.options.fieldInfo[k].dbCol;
935
- }
936
-
937
- private async getFieldsWithDefaultValues(
938
- builder: Builder<TEnt, TViewer>,
939
- schemaFields: Map<string, Field>,
940
- editedFields: Map<string, any>,
941
- action?: Action<TEnt, Builder<TEnt, TViewer>, TViewer, TInput> | undefined,
942
- ): Promise<Data> {
943
- let data: Data = {};
944
- let defaultData: Data = {};
945
-
946
- let input: Data = action?.getInput() || {};
947
-
948
- let updateInput = false;
949
-
950
- // transformations
951
- // if action transformations. always do it
952
- // if disable transformations set, don't do schema transform and just do the right thing
953
- // else apply schema tranformation if it exists
954
- let transformed: TransformedUpdateOperation<TEnt, TViewer> | null = null;
955
-
956
- const sqlOp = this.getSQLStatementOperation();
957
- // why is transform write technically different from upsert?
958
- // it's create -> update just at the db level...
959
- if (action?.transformWrite) {
960
- transformed = await action.transformWrite({
961
- builder,
962
- input,
963
- op: sqlOp,
964
- data: editedFields,
965
- });
966
- } else if (!this.disableTransformations) {
967
- transformed = getTransformedUpdateOp<TEnt, TViewer>(this.options.schema, {
968
- builder,
969
- input,
970
- op: sqlOp,
971
- data: editedFields,
972
- });
973
- }
974
- if (transformed) {
975
- if (sqlOp === SQLStatementOperation.Insert && sqlOp !== transformed.op) {
976
- if (!transformed.existingEnt) {
977
- throw new Error(
978
- `cannot transform an insert operation without providing an existing ent`,
979
- );
980
- }
981
- }
982
- if (transformed.data) {
983
- updateInput = true;
984
- for (const k in transformed.data) {
985
- let field = schemaFields.get(k);
986
- if (!field) {
987
- throw new Error(`tried to transform field with unknown field ${k}`);
988
- }
989
- let val = transformed.data[k];
990
- if (field.format) {
991
- val = field.format(transformed.data[k]);
992
- }
993
- data[this.getStorageKey(k)] = val;
994
- this.defaultFieldsByTSName[this.getInputKey(k)] = val;
995
- // hmm do we need this?
996
- // TODO how to do this for local tests?
997
- // this.defaultFieldsByFieldName[k] = val;
998
- }
999
- }
1000
- if (transformed.changeset) {
1001
- const changeset = await transformed.changeset();
1002
- this.changesets.push(changeset);
1003
- }
1004
- this.actualOperation = this.getWriteOpForSQLStamentOp(transformed.op);
1005
- if (transformed.existingEnt) {
1006
- // @ts-ignore
1007
- this.existingEnt = transformed.existingEnt;
1008
- // modify existing ent in builder. it's readonly in generated ents but doesn't apply here
1009
- builder.existingEnt = transformed.existingEnt;
1010
- }
1011
- }
1012
- // transforming before doing default fields so that we don't create a new id
1013
- // and anything that depends on the type of operations knows what it is
1014
-
1015
- const userDefinedKeys = new Set<string>();
1016
- for (const [fieldName, field] of schemaFields) {
1017
- let value = editedFields.get(fieldName);
1018
- let defaultValue: any = undefined;
1019
- let dbKey = this.getStorageKey(fieldName);
1020
-
1021
- let updateOnlyIfOther = field.onlyUpdateIfOtherFieldsBeingSet_BETA;
1022
-
1023
- if (value !== undefined) {
1024
- userDefinedKeys.add(dbKey);
1025
- }
1026
-
1027
- if (value === undefined) {
1028
- if (this.actualOperation === WriteOperation.Insert) {
1029
- if (field.defaultToViewerOnCreate && field.defaultValueOnCreate) {
1030
- throw new Error(
1031
- `cannot set both defaultToViewerOnCreate and defaultValueOnCreate`,
1032
- );
1033
- }
1034
- if (field.defaultToViewerOnCreate) {
1035
- defaultValue = builder.viewer.viewerID;
1036
- }
1037
- if (field.defaultValueOnCreate) {
1038
- defaultValue = field.defaultValueOnCreate(builder, input);
1039
- if (defaultValue === undefined) {
1040
- throw new Error(
1041
- `defaultValueOnCreate() returned undefined for field ${fieldName}`,
1042
- );
1043
- }
1044
- if (isPromise(defaultValue)) {
1045
- defaultValue = await defaultValue;
1046
- }
1047
- }
1048
- }
1049
-
1050
- if (
1051
- field.defaultValueOnEdit &&
1052
- this.actualOperation === WriteOperation.Edit
1053
- ) {
1054
- defaultValue = field.defaultValueOnEdit(builder, input);
1055
- if (isPromise(defaultValue)) {
1056
- defaultValue = await defaultValue;
1057
- }
1058
- }
1059
- }
1060
-
1061
- if (value !== undefined) {
1062
- data[dbKey] = value;
1063
- }
1064
-
1065
- if (defaultValue !== undefined) {
1066
- updateInput = true;
1067
-
1068
- if (updateOnlyIfOther) {
1069
- defaultData[dbKey] = defaultValue;
1070
- } else {
1071
- data[dbKey] = defaultValue;
1072
- }
1073
-
1074
- this.defaultFieldsByFieldName[fieldName] = defaultValue;
1075
- this.defaultFieldsByTSName[this.getInputKey(fieldName)] = defaultValue;
1076
- }
1077
- }
1078
-
1079
- // if there's data changing, add data
1080
- if (this.hasData(data)) {
1081
- data = {
1082
- ...data,
1083
- ...defaultData,
1084
- };
1085
- if (updateInput && this.options.updateInput) {
1086
- // this basically fixes #605. just needs to be exposed correctly
1087
- this.options.updateInput(this.defaultFieldsByTSName as TInput);
1088
- }
1089
- }
1090
-
1091
- return { data, userDefinedKeys };
1092
- }
1093
-
1094
- private hasData(data: Data) {
1095
- for (const _k in data) {
1096
- return true;
1097
- }
1098
- return false;
1099
- }
1100
-
1101
- private async transformFieldValue(
1102
- fieldName: string,
1103
- field: Field,
1104
- dbKey: string,
1105
- value: any,
1106
- ): Promise<Error | any> {
1107
- // now format and validate...
1108
- if (value === null) {
1109
- if (!field.nullable) {
1110
- return new Error(
1111
- `field ${fieldName} set to null for non-nullable field`,
1112
- );
1113
- }
1114
- } else if (value === undefined) {
1115
- if (
1116
- !field.nullable &&
1117
- // required field can be skipped if server default set
1118
- // not checking defaultValueOnCreate() or defaultValueOnEdit() as that's set above
1119
- // not setting server default as we're depending on the database handling that.
1120
- // server default allowed
1121
- field.serverDefault === undefined &&
1122
- this.actualOperation === WriteOperation.Insert
1123
- ) {
1124
- return new Error(`required field ${fieldName} not set`);
1125
- }
1126
- } else if (this.isBuilder(value)) {
1127
- if (field.valid) {
1128
- let valid = field.valid(value);
1129
- if (isPromise(valid)) {
1130
- valid = await valid;
1131
- }
1132
- if (!valid) {
1133
- return new Error(`invalid field ${fieldName} with value ${value}`);
1134
- }
1135
- }
1136
- // keep track of dependencies to resolve
1137
- this.dependencies.set(value.placeholderID, value);
1138
- // keep track of fields to resolve
1139
- this.fieldsToResolve.push(dbKey);
1140
- } else {
1141
- if (field.valid) {
1142
- let valid = field.valid(value);
1143
- if (isPromise(valid)) {
1144
- valid = await valid;
1145
- }
1146
- if (!valid) {
1147
- return new Error(`invalid field ${fieldName} with value ${value}`);
1148
- }
1149
- }
1150
-
1151
- if (field.format) {
1152
- value = await field.format(value);
1153
- }
1154
- }
1155
- return value;
1156
- }
1157
-
1158
- private async formatAndValidateFields(
1159
- schemaFields: Map<string, Field>,
1160
- editedFields: Map<string, any>,
1161
- ): Promise<Error[]> {
1162
- const errors: Error[] = [];
1163
- const op = this.actualOperation;
1164
- if (op === WriteOperation.Delete) {
1165
- return [];
1166
- }
1167
-
1168
- // build up data to be saved...
1169
- let data = {};
1170
- let logValues = {};
1171
-
1172
- let needsFullDataChecks: string[] = [];
1173
- for (const [fieldName, field] of schemaFields) {
1174
- let value = editedFields.get(fieldName);
1175
-
1176
- if (field.validateWithFullData) {
1177
- needsFullDataChecks.push(fieldName);
1178
- }
1179
-
1180
- if (value === undefined && op === WriteOperation.Insert) {
1181
- // null allowed
1182
- value = this.defaultFieldsByFieldName[fieldName];
1183
- }
1184
- let dbKey = this.getStorageKey(fieldName);
1185
-
1186
- let ret = await this.transformFieldValue(fieldName, field, dbKey, value);
1187
- if (ret instanceof Error) {
1188
- errors.push(ret);
1189
- } else {
1190
- value = ret;
1191
- }
1192
-
1193
- if (value !== undefined) {
1194
- data[dbKey] = value;
1195
- logValues[dbKey] = field.logValue(value);
1196
- }
1197
- }
1198
-
1199
- for (const fieldName of needsFullDataChecks) {
1200
- const field = schemaFields.get(fieldName)!;
1201
- let value = editedFields.get(fieldName);
1202
-
1203
- // @ts-ignore...
1204
- // type hackery because it's hard
1205
- const v = await field.validateWithFullData(value, this.options.builder);
1206
- if (!v) {
1207
- if (value === undefined) {
1208
- errors.push(
1209
- new Error(
1210
- `field ${fieldName} set to undefined when it can't be nullable`,
1211
- ),
1212
- );
1213
- } else {
1214
- errors.push(
1215
- new Error(
1216
- `field ${fieldName} set to null when it can't be nullable`,
1217
- ),
1218
- );
1219
- }
1220
- }
1221
- }
1222
-
1223
- // we ignored default values while editing.
1224
- // if we're editing and there's data, add default values
1225
- if (op === WriteOperation.Edit && this.hasData(data)) {
1226
- for (const fieldName in this.defaultFieldsByFieldName) {
1227
- const defaultValue = this.defaultFieldsByFieldName[fieldName];
1228
- let field = schemaFields.get(fieldName)!;
1229
-
1230
- let dbKey = this.getStorageKey(fieldName);
1231
-
1232
- // no value, let's just default
1233
- if (data[dbKey] === undefined) {
1234
- const ret = await this.transformFieldValue(
1235
- fieldName,
1236
- field,
1237
- dbKey,
1238
- defaultValue,
1239
- );
1240
- if (ret instanceof Error) {
1241
- errors.push(ret);
1242
- } else {
1243
- data[dbKey] = ret;
1244
- logValues[dbKey] = field.logValue(ret);
1245
- }
1246
- }
1247
- }
1248
- }
1249
-
1250
- this.validatedFields = data;
1251
- this.logValues = logValues;
1252
- return errors;
1253
- }
1254
-
1255
- async valid(): Promise<boolean> {
1256
- const errors = await this.validate();
1257
- if (errors.length) {
1258
- errors.map((err) => log("error", err));
1259
- return false;
1260
- }
1261
- return true;
1262
- }
1263
-
1264
- async validX(): Promise<void> {
1265
- const errors = await this.validate();
1266
- if (errors.length) {
1267
- // just throw the first one...
1268
- // TODO we should ideally throw all of them
1269
- throw errors[0];
1270
- }
1271
- }
1272
-
1273
- /**
1274
- * @experimental API that's not guaranteed to remain in the future which returns
1275
- * a list of errors encountered
1276
- * 0 errors indicates valid
1277
- * NOTE that this currently doesn't catch errors returned by validators().
1278
- * If those throws, this still throws and doesn't return them
1279
- */
1280
- async validWithErrors(): Promise<Error[]> {
1281
- return this.validate();
1282
- }
1283
-
1284
- private async buildPlusChangeset(
1285
- conditionalBuilder: Builder<any>,
1286
- conditionalOverride: boolean,
1287
- ): Promise<EntChangeset<TEnt>> {
1288
- // validate everything first
1289
- await this.validX();
1290
-
1291
- let ops: DataOperation[] = [
1292
- this.buildMainOp(conditionalOverride ? conditionalBuilder : undefined),
1293
- ];
1294
-
1295
- await this.buildEdgeOps(ops, conditionalBuilder, conditionalOverride);
1296
-
1297
- // TODO throw if we try and create a new changeset after previously creating one
1298
-
1299
- // TODO test actualOperation value
1300
- // observers is fine since they're run after and we have the actualOperation value...
1301
-
1302
- return new EntChangeset(
1303
- this.options.viewer,
1304
- this.options.builder,
1305
- this.options.builder.placeholderID,
1306
- conditionalOverride,
1307
- ops,
1308
- this.dependencies,
1309
- this.changesets,
1310
- this.options,
1311
- );
1312
- }
1313
-
1314
- async build(): Promise<EntChangeset<TEnt>> {
1315
- return this.buildPlusChangeset(this.options.builder, false);
1316
- }
1317
-
1318
- async buildWithOptions_BETA(
1319
- options: ChangesetOptions,
1320
- ): Promise<EntChangeset<TEnt>> {
1321
- // set as dependency so that we do the right order of operations
1322
- this.dependencies.set(
1323
- options.conditionalBuilder.placeholderID,
1324
- options.conditionalBuilder,
1325
- );
1326
- return this.buildPlusChangeset(options.conditionalBuilder, true);
1327
- }
1328
-
1329
- private async viewerForEntLoad(data: Data) {
1330
- const action = this.options.action;
1331
- if (!action || !action.viewerForEntLoad) {
1332
- return this.options.viewer;
1333
- }
1334
- return action.viewerForEntLoad(data, action.builder.viewer.context);
1335
- }
1336
-
1337
- async returnedRow(): Promise<Data | null> {
1338
- if (this.mainOp && this.mainOp.returnedRow) {
1339
- return this.mainOp.returnedRow();
1340
- }
1341
- return null;
1342
- }
1343
-
1344
- async editedEnt(): Promise<TEnt | null> {
1345
- const row = await this.returnedRow();
1346
- if (!row) {
1347
- return null;
1348
- }
1349
- const viewer = await this.viewerForEntLoad(row);
1350
- return applyPrivacyPolicyForRow(viewer, this.options.loaderOptions, row);
1351
- }
1352
-
1353
- async editedEntX(): Promise<TEnt> {
1354
- const row = await this.returnedRow();
1355
- if (!row) {
1356
- throw new Error(`ent was not created`);
1357
- }
1358
- const viewer = await this.viewerForEntLoad(row);
1359
- const ent = await applyPrivacyPolicyForRow(
1360
- viewer,
1361
- this.options.loaderOptions,
1362
- row,
1363
- );
1364
-
1365
- if (!ent) {
1366
- if (this.actualOperation == WriteOperation.Insert) {
1367
- throw new Error(`was able to create ent but not load it`);
1368
- } else {
1369
- throw new Error(`was able to edit ent but not load it`);
1370
- }
1371
- }
1372
- return ent;
1373
- }
1374
- }
1375
-
1376
- function randomNum(): string {
1377
- return Math.random().toString(10).substring(2);
1378
- }
1379
-
1380
- // each changeset is required to have a unique placeholderID
1381
- // used in executor. if we end up creating multiple changesets from a builder, we need
1382
- // different placeholders
1383
- // in practice, only applies to Entchangeset::changesetFrom()
1384
- export class EntChangeset<T extends Ent> implements Changeset {
1385
- private _executor: Executor | null;
1386
- constructor(
1387
- public viewer: Viewer,
1388
- private builder: Builder<T>,
1389
- public readonly placeholderID: ID,
1390
- private conditionalOverride: boolean,
1391
- public operations: DataOperation[],
1392
- public dependencies?: Map<ID, Builder<Ent>>,
1393
- public changesets?: Changeset[],
1394
- private options?: OrchestratorOptions<T, Data, Viewer>,
1395
- ) {}
1396
-
1397
- static changesetFrom(builder: Builder<any, any, any>, ops: DataOperation[]) {
1398
- return new EntChangeset(
1399
- builder.viewer,
1400
- builder,
1401
- // need unique placeholderID different from the builder. see comment above EntChangeset
1402
- `$ent.idPlaceholderID$ ${randomNum()}-${builder.ent.name}`,
1403
- false,
1404
- ops,
1405
- );
1406
- }
1407
-
1408
- static changesetFromQueries(
1409
- builder: Builder<any, any, any>,
1410
- queries: Array<string | parameterizedQueryOptions>,
1411
- ) {
1412
- return EntChangeset.changesetFrom(builder, [
1413
- new RawQueryOperation(builder, queries),
1414
- ]);
1415
- }
1416
-
1417
- private static async changesetFromEdgeOp(
1418
- builder: Builder<any, any, any>,
1419
- op: EdgeOperation,
1420
- edgeType: string,
1421
- ) {
1422
- const edgeData = await loadEdgeData(edgeType);
1423
- const ops: DataOperation[] = [op];
1424
- if (!edgeData) {
1425
- throw new Error(`could not load edge data for '${edgeType}'`);
1426
- }
1427
- // similar logic in Orchestrator.buildEdgeOps
1428
- // doesn't support conditional edges
1429
- if (edgeData.symmetricEdge) {
1430
- ops.push(op.symmetricEdge());
1431
- }
1432
- if (edgeData.inverseEdgeType) {
1433
- ops.push(op.inverseEdge(edgeData));
1434
- }
1435
- return EntChangeset.changesetFrom(builder, ops);
1436
- }
1437
-
1438
- static async changesetFromOutboundEdge(
1439
- builder: Builder<any, any, any>,
1440
- edgeType: string,
1441
- id2: Builder<any> | ID,
1442
- nodeType: string,
1443
- options?: AssocEdgeInputOptions,
1444
- ) {
1445
- return EntChangeset.changesetFromEdgeOp(
1446
- builder,
1447
- EdgeOperation.outboundEdge(builder, edgeType, id2, nodeType, options),
1448
- edgeType,
1449
- );
1450
- }
1451
-
1452
- static async changesetFromInboundEdge(
1453
- builder: Builder<any, any, any>,
1454
- edgeType: string,
1455
- id1: Builder<any> | ID,
1456
- nodeType: string,
1457
- options?: AssocEdgeInputOptions,
1458
- ) {
1459
- return EntChangeset.changesetFromEdgeOp(
1460
- builder,
1461
- EdgeOperation.inboundEdge(builder, edgeType, id1, nodeType, options),
1462
- edgeType,
1463
- );
1464
- }
1465
-
1466
- static changesetRemoveFromOutboundEdge(
1467
- builder: Builder<any, any, any>,
1468
- edgeType: string,
1469
- id2: ID,
1470
- options?: AssocEdgeInputOptions,
1471
- ) {
1472
- return EntChangeset.changesetFromEdgeOp(
1473
- builder,
1474
- EdgeOperation.removeOutboundEdge(builder, edgeType, id2, options),
1475
- edgeType,
1476
- );
1477
- }
1478
-
1479
- static changesetRemoveFromInboundEdge(
1480
- builder: Builder<any, any, any>,
1481
- edgeType: string,
1482
- id1: ID,
1483
- options?: AssocEdgeInputOptions,
1484
- ) {
1485
- return EntChangeset.changesetFromEdgeOp(
1486
- builder,
1487
- EdgeOperation.removeInboundEdge(builder, edgeType, id1, options),
1488
- edgeType,
1489
- );
1490
- }
1491
-
1492
- executor(): Executor {
1493
- if (this._executor) {
1494
- return this._executor;
1495
- }
1496
-
1497
- if (!this.changesets?.length) {
1498
- // if we have dependencies but no changesets, we just need a simple
1499
- // executor and depend on something else in the stack to handle this correctly
1500
- // ComplexExecutor which could be a parent of this should make sure the dependency
1501
- // is resolved beforehand
1502
- return (this._executor = new ListBasedExecutor(
1503
- this.viewer,
1504
- this.placeholderID,
1505
- this.operations,
1506
- this.options,
1507
- {
1508
- conditionalOverride: this.conditionalOverride,
1509
- builder: this.builder,
1510
- },
1511
- ));
1512
- }
1513
-
1514
- return (this._executor = new ComplexExecutor(
1515
- this.viewer,
1516
- this.placeholderID,
1517
- this.operations,
1518
- this.dependencies || new Map(),
1519
- this.changesets || [],
1520
- this.options,
1521
- {
1522
- conditionalOverride: this.conditionalOverride,
1523
- builder: this.builder,
1524
- },
1525
- ));
1526
- }
1527
- }