@snowtop/ent 0.1.0-alpha9 → 0.1.0-alpha90

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 (116) hide show
  1. package/action/action.d.ts +36 -31
  2. package/action/action.js +2 -6
  3. package/action/executor.d.ts +3 -3
  4. package/action/executor.js +2 -2
  5. package/action/experimental_action.d.ts +29 -22
  6. package/action/experimental_action.js +29 -6
  7. package/action/orchestrator.d.ts +38 -16
  8. package/action/orchestrator.js +219 -61
  9. package/action/privacy.d.ts +2 -2
  10. package/core/base.d.ts +45 -24
  11. package/core/base.js +7 -1
  12. package/core/clause.d.ts +68 -7
  13. package/core/clause.js +291 -62
  14. package/core/config.d.ts +8 -0
  15. package/core/context.d.ts +5 -3
  16. package/core/context.js +20 -2
  17. package/core/convert.d.ts +1 -1
  18. package/core/db.d.ts +2 -2
  19. package/core/db.js +1 -1
  20. package/core/ent.d.ts +79 -24
  21. package/core/ent.js +520 -168
  22. package/core/loaders/assoc_count_loader.d.ts +2 -2
  23. package/core/loaders/assoc_count_loader.js +6 -1
  24. package/core/loaders/assoc_edge_loader.d.ts +2 -2
  25. package/core/loaders/index.d.ts +1 -1
  26. package/core/loaders/index.js +1 -3
  27. package/core/loaders/index_loader.d.ts +2 -2
  28. package/core/loaders/loader.js +5 -5
  29. package/core/loaders/object_loader.d.ts +6 -5
  30. package/core/loaders/object_loader.js +62 -58
  31. package/core/loaders/query_loader.d.ts +6 -12
  32. package/core/loaders/query_loader.js +47 -10
  33. package/core/loaders/raw_count_loader.d.ts +2 -2
  34. package/core/logger.d.ts +1 -1
  35. package/core/logger.js +1 -0
  36. package/core/privacy.d.ts +26 -25
  37. package/core/privacy.js +21 -25
  38. package/core/query/assoc_query.d.ts +6 -6
  39. package/core/query/custom_clause_query.d.ts +24 -0
  40. package/core/query/custom_clause_query.js +72 -0
  41. package/core/query/custom_query.d.ts +20 -5
  42. package/core/query/custom_query.js +77 -10
  43. package/core/query/index.d.ts +1 -0
  44. package/core/query/index.js +3 -1
  45. package/core/query/query.d.ts +1 -1
  46. package/core/query/query.js +8 -1
  47. package/core/query/shared_assoc_test.d.ts +1 -1
  48. package/core/query/shared_assoc_test.js +17 -5
  49. package/core/query/shared_test.d.ts +3 -0
  50. package/core/query/shared_test.js +211 -30
  51. package/core/viewer.d.ts +3 -3
  52. package/core/viewer.js +1 -1
  53. package/graphql/graphql.js +6 -0
  54. package/graphql/query/edge_connection.d.ts +9 -9
  55. package/graphql/query/page_info.d.ts +1 -1
  56. package/index.d.ts +11 -5
  57. package/index.js +15 -6
  58. package/package.json +1 -1
  59. package/parse_schema/parse.d.ts +12 -3
  60. package/parse_schema/parse.js +70 -11
  61. package/schema/base_schema.js +3 -0
  62. package/schema/field.d.ts +44 -8
  63. package/schema/field.js +125 -9
  64. package/schema/index.d.ts +2 -2
  65. package/schema/json_field.d.ts +13 -1
  66. package/schema/json_field.js +28 -1
  67. package/schema/schema.d.ts +65 -11
  68. package/schema/schema.js +18 -4
  69. package/schema/struct_field.d.ts +11 -1
  70. package/schema/struct_field.js +44 -5
  71. package/scripts/custom_graphql.js +8 -3
  72. package/scripts/{transform_schema.d.ts → migrate_v0.1.d.ts} +0 -0
  73. package/scripts/migrate_v0.1.js +36 -0
  74. package/scripts/read_schema.js +15 -4
  75. package/testutils/builder.d.ts +31 -21
  76. package/testutils/builder.js +83 -29
  77. package/testutils/db/fixture.d.ts +10 -0
  78. package/testutils/db/fixture.js +26 -0
  79. package/testutils/db/{test_db.d.ts → temp_db.d.ts} +15 -3
  80. package/testutils/db/{test_db.js → temp_db.js} +70 -16
  81. package/testutils/db/value.d.ts +6 -0
  82. package/testutils/db/value.js +251 -0
  83. package/testutils/db_time_zone.d.ts +4 -0
  84. package/testutils/db_time_zone.js +41 -0
  85. package/testutils/fake_data/fake_contact.d.ts +5 -4
  86. package/testutils/fake_data/fake_contact.js +14 -6
  87. package/testutils/fake_data/fake_event.d.ts +5 -3
  88. package/testutils/fake_data/fake_event.js +8 -5
  89. package/testutils/fake_data/fake_user.d.ts +4 -4
  90. package/testutils/fake_data/fake_user.js +16 -13
  91. package/testutils/fake_data/test_helpers.d.ts +3 -2
  92. package/testutils/fake_data/test_helpers.js +8 -6
  93. package/testutils/fake_data/user_query.d.ts +8 -6
  94. package/testutils/fake_data/user_query.js +28 -21
  95. package/testutils/fake_log.d.ts +3 -3
  96. package/testutils/parse_sql.d.ts +6 -0
  97. package/testutils/parse_sql.js +16 -2
  98. package/testutils/test_edge_global_schema.d.ts +15 -0
  99. package/testutils/test_edge_global_schema.js +58 -0
  100. package/testutils/write.d.ts +2 -2
  101. package/testutils/write.js +29 -7
  102. package/tsc/ast.d.ts +44 -0
  103. package/tsc/ast.js +267 -0
  104. package/tsc/compilerOptions.d.ts +6 -0
  105. package/tsc/compilerOptions.js +40 -1
  106. package/tsc/move_generated.d.ts +1 -0
  107. package/tsc/move_generated.js +160 -0
  108. package/tsc/transform.d.ts +21 -0
  109. package/tsc/transform.js +167 -0
  110. package/tsc/transform_action.d.ts +22 -0
  111. package/tsc/transform_action.js +179 -0
  112. package/tsc/transform_ent.d.ts +17 -0
  113. package/tsc/transform_ent.js +59 -0
  114. package/tsc/transform_schema.d.ts +27 -0
  115. package/tsc/transform_schema.js +379 -0
  116. package/scripts/transform_schema.js +0 -445
package/core/ent.js CHANGED
@@ -22,13 +22,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
22
22
  return (mod && mod.__esModule) ? mod : { "default": mod };
23
23
  };
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.getEdgeTypeInGroup = exports.applyPrivacyPolicyForRows = exports.applyPrivacyPolicyForRowX = exports.applyPrivacyPolicyForRow = exports.loadNodesByEdge = exports.loadEdgeForID2 = exports.loadRawEdgeCountX = exports.loadUniqueNode = exports.loadUniqueEdge = exports.loadCustomEdges = exports.loadEdges = exports.defaultEdgeQueryOptions = exports.DefaultLimit = exports.loadEdgeDatas = exports.loadEdgeData = exports.assocEdgeLoader = exports.AssocEdgeData = exports.getCursor = exports.AssocEdge = exports.DeleteNodeOperation = exports.deleteRowsSync = exports.deleteRows = exports.editRowSync = exports.editRow = exports.buildUpdateQuery = exports.createRowSync = exports.createRow = exports.buildInsertQuery = exports.EdgeOperation = exports.EditNodeOperation = exports.buildGroupQuery = exports.buildQuery = exports.loadRows = exports.performRawQuery = exports.loadRow = exports.loadRowX = exports.loadDerivedEntX = exports.loadDerivedEnt = exports.loadCustomData = exports.loadCustomEnts = exports.loadEntsFromClause = exports.loadEntsList = exports.loadEnts = exports.loadEntXFromClause = exports.loadEntFromClause = exports.loadEntXViaKey = exports.loadEntX = exports.loadEntViaKey = exports.loadEnt = void 0;
25
+ exports.loadRawEdgeCountX = exports.loadUniqueNode = exports.loadUniqueEdge = exports.loadCustomEdges = exports.getEdgeClauseAndFields = exports.loadEdges = exports.defaultEdgeQueryOptions = exports.DefaultLimit = exports.loadEdgeDatas = exports.loadEdgeData = exports.assocEdgeLoader = exports.AssocEdgeData = exports.getCursor = exports.AssocEdge = exports.DeleteNodeOperation = exports.deleteRowsSync = exports.deleteRows = exports.editRowSync = exports.editRow = exports.buildUpdateQuery = exports.createRowSync = exports.createRow = exports.buildInsertQuery = exports.EdgeOperation = exports.__hasGlobalSchema = exports.clearGlobalSchema = exports.setGlobalSchema = exports.EditNodeOperation = exports.RawQueryOperation = exports.buildGroupQuery = exports.buildQuery = exports.loadRows = exports.performRawQuery = exports.loadRow = exports.loadRowX = exports.loadDerivedEntX = exports.loadDerivedEnt = exports.loadCustomCount = exports.loadCustomData = exports.loadCustomEnts = exports.loadEntsFromClause = exports.loadEntsList = exports.loadEnts = exports.loadEntXFromClause = exports.loadEntFromClause = exports.loadEntXViaKey = exports.loadEntX = exports.loadEntViaKey = exports.loadEnt = exports.getEntKey = void 0;
26
+ exports.getEdgeTypeInGroup = exports.applyPrivacyPolicyForRows = exports.applyPrivacyPolicyForRow = exports.loadNodesByEdge = exports.loadEdgeForID2 = void 0;
26
27
  const db_1 = __importStar(require("./db"));
27
28
  const privacy_1 = require("./privacy");
28
29
  const clause = __importStar(require("./clause"));
29
30
  const action_1 = require("../action");
30
31
  const logger_1 = require("./logger");
31
32
  const dataloader_1 = __importDefault(require("dataloader"));
33
+ const schema_1 = require("../schema/");
32
34
  // TODO kill this and createDataLoader
33
35
  class cacheMap {
34
36
  constructor(options) {
@@ -38,7 +40,7 @@ class cacheMap {
38
40
  get(key) {
39
41
  const ret = this.m.get(key);
40
42
  if (ret) {
41
- (0, logger_1.log)("query", {
43
+ (0, logger_1.log)("cache", {
42
44
  "dataloader-cache-hit": key,
43
45
  "tableName": this.options.tableName,
44
46
  });
@@ -55,12 +57,41 @@ class cacheMap {
55
57
  return this.m.clear();
56
58
  }
57
59
  }
60
+ class entCacheMap {
61
+ constructor(viewer, options) {
62
+ this.viewer = viewer;
63
+ this.options = options;
64
+ this.m = new Map();
65
+ this.logEnabled = false;
66
+ this.logEnabled = (0, logger_1.logEnabled)("cache");
67
+ }
68
+ get(id) {
69
+ const ret = this.m.get(id);
70
+ if (this.logEnabled && ret) {
71
+ const key = getEntKey(this.viewer, id, this.options);
72
+ (0, logger_1.log)("cache", {
73
+ "ent-cache-hit": key,
74
+ });
75
+ }
76
+ return ret;
77
+ }
78
+ set(key, value) {
79
+ return this.m.set(key, value);
80
+ }
81
+ delete(key) {
82
+ return this.m.delete(key);
83
+ }
84
+ clear() {
85
+ return this.m.clear();
86
+ }
87
+ }
58
88
  function createDataLoader(options) {
59
89
  const loaderOptions = {};
60
90
  // if query logging is enabled, we should log what's happening with loader
61
91
  if ((0, logger_1.logEnabled)("query")) {
62
92
  loaderOptions.cacheMap = new cacheMap(options);
63
93
  }
94
+ // something here brokwn with strict:true
64
95
  return new dataloader_1.default(async (ids) => {
65
96
  if (!ids.length) {
66
97
  return [];
@@ -84,28 +115,145 @@ function createDataLoader(options) {
84
115
  return result;
85
116
  }, loaderOptions);
86
117
  }
87
- // Ent accessors
118
+ // used to wrap errors that would eventually be thrown in ents
119
+ // not an Error because DataLoader automatically rejects that
120
+ class ErrorWrapper {
121
+ constructor(error) {
122
+ this.error = error;
123
+ }
124
+ }
125
+ function createEntLoader(viewer, options, map) {
126
+ // share the cache across loaders even if we create a new instance
127
+ const loaderOptions = {};
128
+ loaderOptions.cacheMap = map;
129
+ return new dataloader_1.default(async (ids) => {
130
+ if (!ids.length) {
131
+ return [];
132
+ }
133
+ let result = [];
134
+ const loader = options.loaderFactory.createLoader(viewer.context);
135
+ const rows = await loader.loadMany(ids);
136
+ // this is a loader which should return the same order based on passed-in ids
137
+ // so let's depend on that...
138
+ for (let idx = 0; idx < rows.length; idx++) {
139
+ const row = rows[idx];
140
+ // db error
141
+ if (row instanceof Error) {
142
+ result[idx] = row;
143
+ continue;
144
+ }
145
+ else if (!row) {
146
+ result[idx] = new ErrorWrapper(new Error(`couldn't find row for value ${ids[idx]}`));
147
+ }
148
+ else {
149
+ const r = await applyPrivacyPolicyForRowImpl(viewer, options, row);
150
+ if (r instanceof Error) {
151
+ result[idx] = new ErrorWrapper(r);
152
+ }
153
+ else {
154
+ result[idx] = r;
155
+ }
156
+ }
157
+ }
158
+ return result;
159
+ }, loaderOptions);
160
+ }
161
+ class EntLoader {
162
+ constructor(viewer, options) {
163
+ this.viewer = viewer;
164
+ this.options = options;
165
+ this.map = new entCacheMap(viewer, options);
166
+ this.loader = createEntLoader(this.viewer, this.options, this.map);
167
+ }
168
+ getMap() {
169
+ return this.map;
170
+ }
171
+ async load(id) {
172
+ return this.loader.load(id);
173
+ }
174
+ async loadMany(ids) {
175
+ return this.loader.loadMany(ids);
176
+ }
177
+ prime(id, ent) {
178
+ this.loader.prime(id, ent);
179
+ }
180
+ clear(id) {
181
+ this.loader.clear(id);
182
+ }
183
+ clearAll() {
184
+ this.loader.clearAll();
185
+ }
186
+ }
187
+ function getEntLoader(viewer, options) {
188
+ if (!viewer.context?.cache) {
189
+ return new EntLoader(viewer, options);
190
+ }
191
+ const name = `ent-loader:${viewer.instanceKey()}:${options.loaderFactory.name}`;
192
+ return viewer.context.cache.getLoaderWithLoadMany(name, () => new EntLoader(viewer, options));
193
+ }
194
+ function getEntKey(viewer, id, options) {
195
+ return `${viewer.instanceKey()}:${options.loaderFactory.name}:${id}`;
196
+ }
197
+ exports.getEntKey = getEntKey;
88
198
  async function loadEnt(viewer, id, options) {
89
- const row = await options.loaderFactory.createLoader(viewer.context).load(id);
90
- return await applyPrivacyPolicyForRow(viewer, options, row);
199
+ if (typeof id !== "string" &&
200
+ typeof id !== "number" &&
201
+ typeof id !== "bigint") {
202
+ throw new Error(`invalid id ${id} passed to loadEnt`);
203
+ }
204
+ const r = await getEntLoader(viewer, options).load(id);
205
+ return r instanceof ErrorWrapper ? null : r;
91
206
  }
92
207
  exports.loadEnt = loadEnt;
208
+ async function applyPrivacyPolicyForRowAndStoreInEntLoader(viewer, row, options,
209
+ // can pass in loader when calling this for multi-id cases...
210
+ loader) {
211
+ if (!loader) {
212
+ loader = getEntLoader(viewer, options);
213
+ }
214
+ // TODO every row.id needs to be audited...
215
+ // https://github.com/lolopinto/ent/issues/1064
216
+ const id = row.id;
217
+ // we should check the ent loader cache to see if this is already there
218
+ // TODO hmm... we eventually need a custom data-loader for this too so that it's all done correctly if there's a complicated fetch deep down in graphql
219
+ const result = loader.getMap().get(id);
220
+ if (result !== undefined) {
221
+ return result;
222
+ }
223
+ const r = await applyPrivacyPolicyForRowImpl(viewer, options, row);
224
+ if (r instanceof Error) {
225
+ loader.prime(id, new ErrorWrapper(r));
226
+ return new ErrorWrapper(r);
227
+ }
228
+ else {
229
+ loader.prime(id, r);
230
+ return r;
231
+ }
232
+ }
93
233
  // this is the same implementation-wise (right now) as loadEnt. it's just clearer that it's not loaded via ID.
94
234
  // used for load via email address etc
95
235
  async function loadEntViaKey(viewer, key, options) {
96
236
  const row = await options.loaderFactory
97
237
  .createLoader(viewer.context)
98
238
  .load(key);
99
- return await applyPrivacyPolicyForRow(viewer, options, row);
239
+ if (!row) {
240
+ return null;
241
+ }
242
+ const r = await applyPrivacyPolicyForRowAndStoreInEntLoader(viewer, row, options);
243
+ return r instanceof ErrorWrapper ? null : r;
100
244
  }
101
245
  exports.loadEntViaKey = loadEntViaKey;
102
246
  async function loadEntX(viewer, id, options) {
103
- const row = await options.loaderFactory.createLoader(viewer.context).load(id);
104
- if (!row) {
105
- // todo make this better
106
- throw new Error(`${options.loaderFactory.name}: couldn't find row for value ${id}`);
247
+ if (typeof id !== "string" &&
248
+ typeof id !== "number" &&
249
+ typeof id !== "bigint") {
250
+ throw new Error(`invalid id ${id} passed to loadEntX`);
107
251
  }
108
- return await applyPrivacyPolicyForRowX(viewer, options, row);
252
+ const r = await getEntLoader(viewer, options).load(id);
253
+ if (r instanceof ErrorWrapper) {
254
+ throw r.error;
255
+ }
256
+ return r;
109
257
  }
110
258
  exports.loadEntX = loadEntX;
111
259
  async function loadEntXViaKey(viewer, key, options) {
@@ -116,9 +264,16 @@ async function loadEntXViaKey(viewer, key, options) {
116
264
  // todo make this better
117
265
  throw new Error(`${options.loaderFactory.name}: couldn't find row for value ${key}`);
118
266
  }
119
- return await applyPrivacyPolicyForRowX(viewer, options, row);
267
+ const r = await applyPrivacyPolicyForRowAndStoreInEntLoader(viewer, row, options);
268
+ if (r instanceof ErrorWrapper) {
269
+ throw r.error;
270
+ }
271
+ return r;
120
272
  }
121
273
  exports.loadEntXViaKey = loadEntXViaKey;
274
+ /**
275
+ * @deprecated use loadCustomEnts
276
+ */
122
277
  async function loadEntFromClause(viewer, options, clause) {
123
278
  const rowOptions = {
124
279
  ...options,
@@ -126,12 +281,18 @@ async function loadEntFromClause(viewer, options, clause) {
126
281
  context: viewer.context,
127
282
  };
128
283
  const row = await loadRow(rowOptions);
129
- return await applyPrivacyPolicyForRow(viewer, options, row);
284
+ if (row === null) {
285
+ return null;
286
+ }
287
+ return applyPrivacyPolicyForRow(viewer, options, row);
130
288
  }
131
289
  exports.loadEntFromClause = loadEntFromClause;
132
290
  // same as loadEntFromClause
133
291
  // only works for ents where primary key is "id"
134
292
  // use loadEnt with a loaderFactory if different
293
+ /**
294
+ * @deprecated use loadCustomEnts
295
+ */
135
296
  async function loadEntXFromClause(viewer, options, clause) {
136
297
  const rowOptions = {
137
298
  ...options,
@@ -146,37 +307,19 @@ async function loadEnts(viewer, options, ...ids) {
146
307
  if (!ids.length) {
147
308
  return new Map();
148
309
  }
149
- let loaded = false;
150
- let rows = [];
151
- // TODO loadMany everywhere
152
- const l = options.loaderFactory.createLoader(viewer.context);
153
- if (l.loadMany) {
154
- loaded = true;
155
- rows = await l.loadMany(ids);
156
- }
157
- // TODO rewrite all of this
310
+ // result
158
311
  let m = new Map();
159
- if (loaded) {
160
- let rows2 = [];
161
- for (const row of rows) {
162
- if (!row) {
163
- continue;
164
- }
165
- if (row instanceof Error) {
166
- throw row;
167
- }
168
- rows2.push(row);
312
+ const ret = await getEntLoader(viewer, options).loadMany(ids);
313
+ for (const r of ret) {
314
+ if (r instanceof Error) {
315
+ throw r;
169
316
  }
170
- m = await applyPrivacyPolicyForRows(viewer, rows2, options);
171
- }
172
- else {
173
- m = await loadEntsFromClause(viewer,
174
- // this is always "id" if not using an ObjectLoaderFactory
175
- clause.In("id", ...ids), options);
317
+ if (r instanceof ErrorWrapper) {
318
+ continue;
319
+ }
320
+ m.set(r.id, r);
176
321
  }
177
322
  return m;
178
- // TODO do we want to change this to be a map not a list so that it's easy to check for existence?
179
- // TODO eventually this should be doing a cache then db queyr and maybe depend on dataloader to get all the results at once
180
323
  }
181
324
  exports.loadEnts = loadEnts;
182
325
  // calls loadEnts and returns the results sorted in the order they were passed in
@@ -195,6 +338,9 @@ async function loadEntsList(viewer, options, ...ids) {
195
338
  exports.loadEntsList = loadEntsList;
196
339
  // we return a map here so that any sorting for queries that exist
197
340
  // can be done in O(N) time
341
+ /**
342
+ * @deperecated use loadCustomEnts
343
+ */
198
344
  async function loadEntsFromClause(viewer, clause, options) {
199
345
  const rowOptions = {
200
346
  ...options,
@@ -202,65 +348,135 @@ async function loadEntsFromClause(viewer, clause, options) {
202
348
  context: viewer.context,
203
349
  };
204
350
  const rows = await loadRows(rowOptions);
205
- return await applyPrivacyPolicyForRows(viewer, rows, options);
351
+ return applyPrivacyPolicyForRowsDeprecated(viewer, rows, options);
206
352
  }
207
353
  exports.loadEntsFromClause = loadEntsFromClause;
208
354
  async function loadCustomEnts(viewer, options, query) {
209
355
  const rows = await loadCustomData(options, query, viewer.context);
210
- const result = new Array(rows.length);
211
- await Promise.all(rows.map(async (row, idx) => {
212
- const ent = new options.ent(viewer, row);
213
- let privacyEnt = await applyPrivacyPolicyForEnt(viewer, ent, row, options);
214
- if (privacyEnt) {
215
- result[idx] = privacyEnt;
216
- }
217
- }));
218
- // filter ents that aren't visible because of privacy
219
- return result.filter((r) => r !== undefined);
356
+ return applyPrivacyPolicyForRows(viewer, rows, options);
220
357
  }
221
358
  exports.loadCustomEnts = loadCustomEnts;
222
359
  function isClause(opts) {
223
360
  const cls = opts;
224
361
  return cls.clause !== undefined && cls.values !== undefined;
225
362
  }
226
- function isRawQuery(opts) {
363
+ function isParameterizedQuery(opts) {
227
364
  return opts.query !== undefined;
228
365
  }
366
+ /**
367
+ * Note that if there's default read transformations (e.g. soft delete) and a clause is passed in
368
+ * either as Clause or QueryDataOptions without {disableTransformations: true}, the default transformation
369
+ * (e.g. soft delete) is applied.
370
+ *
371
+ * Passing a full SQL string or Paramterized SQL string doesn't apply it and the given string is sent to the
372
+ * database as written.
373
+ *
374
+ * e.g.
375
+ * Foo.loadCustom(opts, 'SELECT * FROM foo') // doesn't change the query
376
+ * Foo.loadCustom(opts, { query: 'SELECT * FROM foo WHERE id = ?', values: [1]}) // doesn't change the query
377
+ * Foo.loadCustom(opts, query.Eq('time', Date.now())) // changes the query
378
+ * Foo.loadCustom(opts, {
379
+ * clause: query.LessEq('time', Date.now()),
380
+ * limit: 100,
381
+ * orderby: 'time',
382
+ * }) // changes the query
383
+ * Foo.loadCustom(opts, {
384
+ * clause: query.LessEq('time', Date.now()),
385
+ * limit: 100,
386
+ * orderby: 'time',
387
+ * disableTransformations: false
388
+ * }) // doesn't change the query
389
+ */
229
390
  async function loadCustomData(options, query, context) {
391
+ const rows = await loadCustomDataImpl(options, query, context);
392
+ // prime the data so that subsequent fetches of the row with this id are a cache hit.
393
+ if (options.prime) {
394
+ const loader = options.loaderFactory.createLoader(context);
395
+ if (isPrimableLoader(loader) && loader.primeAll !== undefined) {
396
+ for (const row of rows) {
397
+ loader.primeAll(row);
398
+ }
399
+ }
400
+ }
401
+ return rows;
402
+ }
403
+ exports.loadCustomData = loadCustomData;
404
+ // NOTE: if you use a raw query or paramterized query with this,
405
+ // you should use `SELECT count(*) as count...`
406
+ async function loadCustomCount(options, query, context) {
407
+ // TODO also need to loaderify this in case we're querying for this a lot...
408
+ const rows = await loadCustomDataImpl({
409
+ ...options,
410
+ fields: ["count(1) as count"],
411
+ }, query, context);
412
+ if (rows.length) {
413
+ return parseInt(rows[0].count);
414
+ }
415
+ return 0;
416
+ }
417
+ exports.loadCustomCount = loadCustomCount;
418
+ function isPrimableLoader(loader) {
419
+ return loader != undefined;
420
+ }
421
+ async function loadCustomDataImpl(options, query, context) {
422
+ function getClause(cls) {
423
+ let optClause = options.loaderFactory?.options?.clause;
424
+ if (typeof optClause === "function") {
425
+ optClause = optClause();
426
+ }
427
+ if (!optClause) {
428
+ return cls;
429
+ }
430
+ return clause.And(cls, optClause);
431
+ }
230
432
  if (typeof query === "string") {
231
433
  // no caching, perform raw query
232
- return await performRawQuery(query, [], []);
434
+ return performRawQuery(query, [], []);
233
435
  }
234
436
  else if (isClause(query)) {
437
+ // if a Clause is passed in and we have a default clause
438
+ // associated with the query, pass that in
439
+ // if we want to disableTransformations, need to indicate that with
440
+ // disableTransformations option
235
441
  // this will have rudimentary caching but nothing crazy
236
- return await loadRows({
442
+ return loadRows({
237
443
  ...options,
238
- clause: query,
444
+ clause: getClause(query),
239
445
  context: context,
240
446
  });
241
447
  }
242
- else if (isRawQuery(query)) {
448
+ else if (isParameterizedQuery(query)) {
243
449
  // no caching, perform raw query
244
- return await performRawQuery(query.query, query.values || [], query.logValues);
450
+ return performRawQuery(query.query, query.values || [], query.logValues);
245
451
  }
246
452
  else {
453
+ let cls = query.clause;
454
+ if (!query.disableTransformations) {
455
+ cls = getClause(cls);
456
+ }
247
457
  // this will have rudimentary caching but nothing crazy
248
- return await loadRows({
458
+ return loadRows({
249
459
  ...query,
250
460
  ...options,
251
461
  context: context,
462
+ clause: cls,
252
463
  });
253
464
  }
254
465
  }
255
- exports.loadCustomData = loadCustomData;
256
466
  // Derived ents
467
+ // no ent caching
257
468
  async function loadDerivedEnt(viewer, data, loader) {
258
469
  const ent = new loader(viewer, data);
259
- return await applyPrivacyPolicyForEnt(viewer, ent, data, {
470
+ const r = await applyPrivacyPolicyForEnt(viewer, ent, data, {
260
471
  ent: loader,
261
472
  });
473
+ if (r instanceof Error) {
474
+ return null;
475
+ }
476
+ return r;
262
477
  }
263
478
  exports.loadDerivedEnt = loadDerivedEnt;
479
+ // won't have caching yet either
264
480
  async function loadDerivedEntX(viewer, data, loader) {
265
481
  const ent = new loader(viewer, data);
266
482
  return await applyPrivacyPolicyForEntX(viewer, ent, data, { ent: loader });
@@ -269,19 +485,21 @@ exports.loadDerivedEntX = loadDerivedEntX;
269
485
  // everything calls into this two so should be fine
270
486
  // TODO is there a smarter way to not instantiate two objects here?
271
487
  async function applyPrivacyPolicyForEnt(viewer, ent, data, fieldPrivacyOptions) {
272
- if (ent) {
273
- const visible = await (0, privacy_1.applyPrivacyPolicy)(viewer, ent.privacyPolicy, ent);
274
- if (!visible) {
275
- return null;
276
- }
488
+ const error = await (0, privacy_1.applyPrivacyPolicyImpl)(viewer, ent.getPrivacyPolicy(), ent);
489
+ if (error === null) {
277
490
  return doFieldPrivacy(viewer, ent, data, fieldPrivacyOptions);
278
491
  }
279
- return null;
492
+ return error;
280
493
  }
281
494
  async function applyPrivacyPolicyForEntX(viewer, ent, data, options) {
282
- // this will throw
283
- await (0, privacy_1.applyPrivacyPolicyX)(viewer, ent.privacyPolicy, ent);
284
- return doFieldPrivacy(viewer, ent, data, options);
495
+ const r = await applyPrivacyPolicyForEnt(viewer, ent, data, options);
496
+ if (r instanceof Error) {
497
+ throw r;
498
+ }
499
+ if (r === null) {
500
+ throw new Error(`couldn't apply privacyPoliy for ent ${ent.id}`);
501
+ }
502
+ return r;
285
503
  }
286
504
  async function doFieldPrivacy(viewer, ent, data, options) {
287
505
  if (!options.fieldPrivacy) {
@@ -290,12 +508,12 @@ async function doFieldPrivacy(viewer, ent, data, options) {
290
508
  const promises = [];
291
509
  let somethingChanged = false;
292
510
  for (const [k, policy] of options.fieldPrivacy) {
511
+ const curr = data[k];
512
+ if (curr === null || curr === undefined) {
513
+ continue;
514
+ }
293
515
  promises.push((async () => {
294
516
  // don't do anything if key is null or for some reason missing
295
- const curr = data[k];
296
- if (curr === null || curr === undefined) {
297
- return;
298
- }
299
517
  const r = await (0, privacy_1.applyPrivacyPolicy)(viewer, policy, ent);
300
518
  if (!r) {
301
519
  data[k] = null;
@@ -339,42 +557,27 @@ async function loadRow(options) {
339
557
  }
340
558
  const query = buildQuery(options);
341
559
  logQuery(query, options.clause.logValues());
342
- try {
343
- const pool = db_1.default.getInstance().getPool();
344
- const res = await pool.query(query, options.clause.values());
345
- if (res.rowCount != 1) {
346
- if (res.rowCount > 1) {
347
- (0, logger_1.log)("error", "got more than one row for query " + query);
348
- }
349
- return null;
350
- }
351
- // put the row in the cache...
352
- if (cache) {
353
- cache.primeCache(options, res.rows[0]);
560
+ const pool = db_1.default.getInstance().getPool();
561
+ const res = await pool.query(query, options.clause.values());
562
+ if (res.rowCount != 1) {
563
+ if (res.rowCount > 1) {
564
+ (0, logger_1.log)("error", "got more than one row for query " + query);
354
565
  }
355
- return res.rows[0];
356
- }
357
- catch (e) {
358
- // an example of an error being suppressed
359
- // another one. TODO https://github.com/lolopinto/ent/issues/862
360
- (0, logger_1.log)("error", e);
361
566
  return null;
362
567
  }
568
+ // put the row in the cache...
569
+ if (cache) {
570
+ cache.primeCache(options, res.rows[0]);
571
+ }
572
+ return res.rows[0];
363
573
  }
364
574
  exports.loadRow = loadRow;
365
575
  // this always goes to the db, no cache, nothing
366
576
  async function performRawQuery(query, values, logValues) {
367
577
  const pool = db_1.default.getInstance().getPool();
368
578
  logQuery(query, logValues || []);
369
- try {
370
- const res = await pool.queryAll(query, values);
371
- return res.rows;
372
- }
373
- catch (e) {
374
- // TODO need to change every query to catch an error!
375
- (0, logger_1.log)("error", e);
376
- return [];
377
- }
579
+ const res = await pool.queryAll(query, values);
580
+ return res.rows;
378
581
  }
379
582
  exports.performRawQuery = performRawQuery;
380
583
  // TODO this should throw, we can't be hiding errors here
@@ -432,10 +635,41 @@ function buildGroupQuery(options) {
432
635
  ];
433
636
  }
434
637
  exports.buildGroupQuery = buildGroupQuery;
638
+ class RawQueryOperation {
639
+ constructor(queries) {
640
+ this.queries = queries;
641
+ }
642
+ async performWrite(queryer, context) {
643
+ for (const q of this.queries) {
644
+ if (typeof q === "string") {
645
+ logQuery(q, []);
646
+ await queryer.query(q);
647
+ }
648
+ else {
649
+ logQuery(q.query, q.logValues || []);
650
+ await queryer.query(q.query, q.values);
651
+ }
652
+ }
653
+ }
654
+ performWriteSync(queryer, context) {
655
+ for (const q of this.queries) {
656
+ if (typeof q === "string") {
657
+ logQuery(q, []);
658
+ queryer.execSync(q);
659
+ }
660
+ else {
661
+ logQuery(q.query, q.logValues || []);
662
+ queryer.execSync(q.query, q.values);
663
+ }
664
+ }
665
+ }
666
+ }
667
+ exports.RawQueryOperation = RawQueryOperation;
435
668
  class EditNodeOperation {
436
669
  constructor(options, existingEnt = null) {
437
670
  this.options = options;
438
671
  this.existingEnt = existingEnt;
672
+ this.row = null;
439
673
  this.placeholderID = options.placeholderID;
440
674
  }
441
675
  resolve(executor) {
@@ -471,9 +705,10 @@ class EditNodeOperation {
471
705
  if (this.hasData(options.fields)) {
472
706
  // even this with returning * may not always work if transformed...
473
707
  // we can have a transformed flag to see if it should be returned?
474
- this.row = await editRow(queryer, options, this.existingEnt.id, "RETURNING *");
708
+ this.row = await editRow(queryer, options, "RETURNING *");
475
709
  }
476
710
  else {
711
+ // @ts-ignore
477
712
  this.row = this.existingEnt["data"];
478
713
  }
479
714
  }
@@ -496,7 +731,7 @@ class EditNodeOperation {
496
731
  optionClause = opts.clause;
497
732
  }
498
733
  if (optionClause) {
499
- cls = clause.And(optionClause, cls);
734
+ cls = clause.And(cls, optionClause);
500
735
  }
501
736
  }
502
737
  const query = buildQuery({
@@ -520,10 +755,11 @@ class EditNodeOperation {
520
755
  };
521
756
  if (this.existingEnt) {
522
757
  if (this.hasData(this.options.fields)) {
523
- editRowSync(queryer, options, this.existingEnt.id, "RETURNING *");
758
+ editRowSync(queryer, options, "RETURNING *");
524
759
  this.reloadRow(queryer, this.existingEnt.id, options);
525
760
  }
526
761
  else {
762
+ // @ts-ignore
527
763
  this.row = this.existingEnt["data"];
528
764
  }
529
765
  }
@@ -544,8 +780,23 @@ class EditNodeOperation {
544
780
  }
545
781
  }
546
782
  exports.EditNodeOperation = EditNodeOperation;
783
+ let globalSchema;
784
+ function setGlobalSchema(val) {
785
+ globalSchema = val;
786
+ }
787
+ exports.setGlobalSchema = setGlobalSchema;
788
+ function clearGlobalSchema() {
789
+ globalSchema = undefined;
790
+ }
791
+ exports.clearGlobalSchema = clearGlobalSchema;
792
+ // used by tests. no guarantee will always exist
793
+ function __hasGlobalSchema() {
794
+ return globalSchema !== undefined;
795
+ }
796
+ exports.__hasGlobalSchema = __hasGlobalSchema;
547
797
  class EdgeOperation {
548
- constructor(edgeInput, options) {
798
+ constructor(builder, edgeInput, options) {
799
+ this.builder = builder;
549
800
  this.edgeInput = edgeInput;
550
801
  this.options = options;
551
802
  }
@@ -581,7 +832,31 @@ class EdgeOperation {
581
832
  }
582
833
  }
583
834
  getDeleteRowParams(edgeData, edge, context) {
835
+ let transformed = null;
836
+ let op = schema_1.SQLStatementOperation.Delete;
837
+ let updateData = null;
838
+ // TODO respect disableTransformations
839
+ if (globalSchema?.transformEdgeWrite) {
840
+ transformed = globalSchema.transformEdgeWrite({
841
+ op: schema_1.SQLStatementOperation.Delete,
842
+ edge,
843
+ });
844
+ if (transformed) {
845
+ op = transformed.op;
846
+ if (transformed.op === schema_1.SQLStatementOperation.Insert) {
847
+ throw new Error(`cannot currently transform a delete into an insert`);
848
+ }
849
+ if (transformed.op === schema_1.SQLStatementOperation.Update) {
850
+ if (!transformed.data) {
851
+ throw new Error(`cannot transform a delete into an update without providing data`);
852
+ }
853
+ updateData = transformed.data;
854
+ }
855
+ }
856
+ }
584
857
  return {
858
+ op,
859
+ updateData,
585
860
  options: {
586
861
  tableName: edgeData.edgeTable,
587
862
  context,
@@ -591,11 +866,35 @@ class EdgeOperation {
591
866
  }
592
867
  async performDeleteWrite(q, edgeData, edge, context) {
593
868
  const params = this.getDeleteRowParams(edgeData, edge, context);
594
- return deleteRows(q, params.options, params.clause);
869
+ if (params.op === schema_1.SQLStatementOperation.Delete) {
870
+ return deleteRows(q, params.options, params.clause);
871
+ }
872
+ else {
873
+ if (params.op !== schema_1.SQLStatementOperation.Update) {
874
+ throw new Error(`invalid operation ${params.op}`);
875
+ }
876
+ await editRow(q, {
877
+ tableName: params.options.tableName,
878
+ whereClause: params.clause,
879
+ fields: params.updateData,
880
+ });
881
+ }
595
882
  }
596
883
  performDeleteWriteSync(q, edgeData, edge, context) {
597
884
  const params = this.getDeleteRowParams(edgeData, edge, context);
598
- return deleteRowsSync(q, params.options, params.clause);
885
+ if (params.op === schema_1.SQLStatementOperation.Delete) {
886
+ return deleteRowsSync(q, params.options, params.clause);
887
+ }
888
+ else {
889
+ if (params.op !== schema_1.SQLStatementOperation.Update) {
890
+ throw new Error(`invalid operation ${params.op}`);
891
+ }
892
+ editRowSync(q, {
893
+ tableName: params.options.tableName,
894
+ whereClause: params.clause,
895
+ fields: params.updateData,
896
+ });
897
+ }
599
898
  }
600
899
  getInsertRowParams(edgeData, edge, context) {
601
900
  const fields = {
@@ -614,6 +913,30 @@ class EdgeOperation {
614
913
  // maybe when actions exist?
615
914
  fields["time"] = new Date().toISOString();
616
915
  }
916
+ const onConflictFields = ["data"];
917
+ if (globalSchema?.extraEdgeFields) {
918
+ for (const name in globalSchema.extraEdgeFields) {
919
+ const f = globalSchema.extraEdgeFields[name];
920
+ if (f.defaultValueOnCreate) {
921
+ const storageKey = (0, schema_1.getStorageKey)(f, name);
922
+ fields[storageKey] = f.defaultValueOnCreate(this.builder, {});
923
+ // onconflict make sure we override the default values
924
+ // e.g. setting deleted_at = null for soft delete
925
+ onConflictFields.push(storageKey);
926
+ }
927
+ }
928
+ }
929
+ // TODO respect disableTransformations
930
+ let transformed = null;
931
+ if (globalSchema?.transformEdgeWrite) {
932
+ transformed = globalSchema.transformEdgeWrite({
933
+ op: schema_1.SQLStatementOperation.Insert,
934
+ edge,
935
+ });
936
+ if (transformed) {
937
+ throw new Error(`transforming an insert edge not currently supported`);
938
+ }
939
+ }
617
940
  return [
618
941
  {
619
942
  tableName: edgeData.edgeTable,
@@ -621,7 +944,9 @@ class EdgeOperation {
621
944
  fieldsToLog: fields,
622
945
  context,
623
946
  },
624
- "ON CONFLICT(id1, edge_type, id2) DO UPDATE SET data = EXCLUDED.data",
947
+ `ON CONFLICT(id1, edge_type, id2) DO UPDATE SET ${onConflictFields
948
+ .map((f) => `${f} = EXCLUDED.${f}`)
949
+ .join(", ")}`,
625
950
  ];
626
951
  }
627
952
  async performInsertWrite(q, edgeData, edge, context) {
@@ -658,7 +983,7 @@ class EdgeOperation {
658
983
  }
659
984
  }
660
985
  symmetricEdge() {
661
- return new EdgeOperation({
986
+ return new EdgeOperation(this.builder, {
662
987
  id1: this.edgeInput.id2,
663
988
  id1Type: this.edgeInput.id2Type,
664
989
  id2: this.edgeInput.id1,
@@ -674,7 +999,7 @@ class EdgeOperation {
674
999
  });
675
1000
  }
676
1001
  inverseEdge(edgeData) {
677
- return new EdgeOperation({
1002
+ return new EdgeOperation(this.builder, {
678
1003
  id1: this.edgeInput.id2,
679
1004
  id1Type: this.edgeInput.id2Type,
680
1005
  id2: this.edgeInput.id1,
@@ -742,7 +1067,7 @@ class EdgeOperation {
742
1067
  if (data) {
743
1068
  edge.data = data;
744
1069
  }
745
- return new EdgeOperation(edge, {
1070
+ return new EdgeOperation(builder, edge, {
746
1071
  operation: action_1.WriteOperation.Insert,
747
1072
  id2Placeholder,
748
1073
  id1Placeholder,
@@ -763,7 +1088,7 @@ class EdgeOperation {
763
1088
  if (data) {
764
1089
  edge.data = data;
765
1090
  }
766
- return new EdgeOperation(edge, {
1091
+ return new EdgeOperation(builder, edge, {
767
1092
  operation: action_1.WriteOperation.Insert,
768
1093
  id1Placeholder,
769
1094
  id2Placeholder,
@@ -781,7 +1106,7 @@ class EdgeOperation {
781
1106
  id2Type: "",
782
1107
  id1Type: "",
783
1108
  };
784
- return new EdgeOperation(edge, {
1109
+ return new EdgeOperation(builder, edge, {
785
1110
  operation: action_1.WriteOperation.Delete,
786
1111
  });
787
1112
  }
@@ -796,7 +1121,7 @@ class EdgeOperation {
796
1121
  id2Type: "",
797
1122
  id1Type: "",
798
1123
  };
799
- return new EdgeOperation(edge, {
1124
+ return new EdgeOperation(builder, edge, {
800
1125
  operation: action_1.WriteOperation.Delete,
801
1126
  });
802
1127
  }
@@ -808,40 +1133,26 @@ function isSyncQueryer(queryer) {
808
1133
  async function mutateRow(queryer, query, values, logValues, options) {
809
1134
  logQuery(query, logValues);
810
1135
  let cache = options.context?.cache;
811
- try {
812
- let res;
813
- if (isSyncQueryer(queryer)) {
814
- res = queryer.execSync(query, values);
815
- }
816
- else {
817
- res = await queryer.exec(query, values);
818
- }
819
- if (cache) {
820
- cache.clearCache();
821
- }
822
- return res;
1136
+ let res;
1137
+ if (isSyncQueryer(queryer)) {
1138
+ res = queryer.execSync(query, values);
823
1139
  }
824
- catch (err) {
825
- // TODO:::why is this not rethrowing?
826
- (0, logger_1.log)("error", err);
827
- throw err;
1140
+ else {
1141
+ res = await queryer.exec(query, values);
1142
+ }
1143
+ if (cache) {
1144
+ cache.clearCache();
828
1145
  }
1146
+ return res;
829
1147
  }
830
1148
  function mutateRowSync(queryer, query, values, logValues, options) {
831
1149
  logQuery(query, logValues);
832
1150
  let cache = options.context?.cache;
833
- try {
834
- const res = queryer.execSync(query, values);
835
- if (cache) {
836
- cache.clearCache();
837
- }
838
- return res;
839
- }
840
- catch (err) {
841
- // TODO:::why is this not rethrowing?
842
- (0, logger_1.log)("error", err);
843
- throw err;
1151
+ const res = queryer.execSync(query, values);
1152
+ if (cache) {
1153
+ cache.clearCache();
844
1154
  }
1155
+ return res;
845
1156
  }
846
1157
  function buildInsertQuery(options, suffix) {
847
1158
  let fields = [];
@@ -893,32 +1204,36 @@ function createRowSync(queryer, options, suffix) {
893
1204
  return null;
894
1205
  }
895
1206
  exports.createRowSync = createRowSync;
896
- function buildUpdateQuery(options, id, suffix) {
1207
+ function buildUpdateQuery(options, suffix) {
897
1208
  let valsString = [];
898
1209
  let values = [];
899
1210
  let logValues = [];
900
1211
  const dialect = db_1.default.getDialect();
901
1212
  let idx = 1;
902
1213
  for (const key in options.fields) {
903
- values.push(options.fields[key]);
1214
+ const val = options.fields[key];
1215
+ values.push(val);
904
1216
  if (options.fieldsToLog) {
905
1217
  logValues.push(options.fieldsToLog[key]);
906
1218
  }
1219
+ // TODO would be nice to use clause here. need update version of the queries so that
1220
+ // we don't have to handle dialect specifics here
1221
+ // can't use clause because of IS NULL
1222
+ // valsString.push(clause.Eq(key, val).clause(idx));
907
1223
  if (dialect === db_1.Dialect.Postgres) {
908
1224
  valsString.push(`${key} = $${idx}`);
909
- idx++;
910
1225
  }
911
1226
  else {
912
1227
  valsString.push(`${key} = ?`);
913
1228
  }
1229
+ idx++;
914
1230
  }
915
1231
  const vals = valsString.join(", ");
916
1232
  let query = `UPDATE ${options.tableName} SET ${vals} WHERE `;
917
- if (dialect === db_1.Dialect.Postgres) {
918
- query = query + `${options.key} = $${idx}`;
919
- }
920
- else {
921
- query = query + `${options.key} = ?`;
1233
+ query = query + options.whereClause.clause(idx);
1234
+ values.push(...options.whereClause.values());
1235
+ if (options.fieldsToLog) {
1236
+ logValues.push(...options.whereClause.logValues());
922
1237
  }
923
1238
  if (suffix) {
924
1239
  query = query + " " + suffix;
@@ -926,10 +1241,8 @@ function buildUpdateQuery(options, id, suffix) {
926
1241
  return [query, values, logValues];
927
1242
  }
928
1243
  exports.buildUpdateQuery = buildUpdateQuery;
929
- async function editRow(queryer, options, id, suffix) {
930
- const [query, values, logValues] = buildUpdateQuery(options, id, suffix);
931
- // add id as value to prepared query
932
- values.push(id);
1244
+ async function editRow(queryer, options, suffix) {
1245
+ const [query, values, logValues] = buildUpdateQuery(options, suffix);
933
1246
  const res = await mutateRow(queryer, query, values, logValues, options);
934
1247
  if (res?.rowCount == 1) {
935
1248
  // for now assume id primary key
@@ -940,10 +1253,8 @@ async function editRow(queryer, options, id, suffix) {
940
1253
  return null;
941
1254
  }
942
1255
  exports.editRow = editRow;
943
- function editRowSync(queryer, options, id, suffix) {
944
- const [query, values, logValues] = buildUpdateQuery(options, id, suffix);
945
- // add id as value to prepared query
946
- values.push(id);
1256
+ function editRowSync(queryer, options, suffix) {
1257
+ const [query, values, logValues] = buildUpdateQuery(options, suffix);
947
1258
  const res = mutateRowSync(queryer, query, values, logValues, options);
948
1259
  if (res?.rowCount == 1) {
949
1260
  // for now assume id primary key
@@ -994,6 +1305,12 @@ class AssocEdge {
994
1305
  this.edgeType = data.edge_type;
995
1306
  this.time = data.time;
996
1307
  this.data = data.data;
1308
+ this.rawData = data;
1309
+ }
1310
+ __getRawData() {
1311
+ // incase there's extra db fields. useful for tests
1312
+ // in production, a subclass of this should be in use so we won't need this...
1313
+ return this.rawData;
997
1314
  }
998
1315
  getCursor() {
999
1316
  return getCursor({
@@ -1100,6 +1417,21 @@ async function loadEdges(options) {
1100
1417
  return loadCustomEdges({ ...options, ctr: AssocEdge });
1101
1418
  }
1102
1419
  exports.loadEdges = loadEdges;
1420
+ function getEdgeClauseAndFields(cls, options) {
1421
+ let fields = edgeFields;
1422
+ if (globalSchema?.transformEdgeRead) {
1423
+ const transformClause = globalSchema.transformEdgeRead();
1424
+ if (!options.disableTransformations) {
1425
+ cls = clause.And(cls, transformClause);
1426
+ }
1427
+ fields = edgeFields.concat(transformClause.columns());
1428
+ }
1429
+ return {
1430
+ cls,
1431
+ fields,
1432
+ };
1433
+ }
1434
+ exports.getEdgeClauseAndFields = getEdgeClauseAndFields;
1103
1435
  async function loadCustomEdges(options) {
1104
1436
  const { id1, edgeType, context } = options;
1105
1437
  const edgeData = await loadEdgeData(edgeType);
@@ -1111,10 +1443,11 @@ async function loadCustomEdges(options) {
1111
1443
  if (options.queryOptions?.clause) {
1112
1444
  cls = clause.And(cls, options.queryOptions.clause);
1113
1445
  }
1446
+ const { cls: actualClause, fields } = getEdgeClauseAndFields(cls, options);
1114
1447
  const rows = await loadRows({
1115
1448
  tableName: edgeData.edgeTable,
1116
- fields: edgeFields,
1117
- clause: cls,
1449
+ fields: fields,
1450
+ clause: actualClause,
1118
1451
  orderby: options.queryOptions?.orderby || defaultOptions.orderby,
1119
1452
  limit: options.queryOptions?.limit || defaultOptions.limit,
1120
1453
  context,
@@ -1130,10 +1463,11 @@ async function loadUniqueEdge(options) {
1130
1463
  if (!edgeData) {
1131
1464
  throw new Error(`error loading edge data for ${edgeType}`);
1132
1465
  }
1466
+ const { cls, fields } = getEdgeClauseAndFields(clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)), options);
1133
1467
  const row = await loadRow({
1134
1468
  tableName: edgeData.edgeTable,
1135
- fields: edgeFields,
1136
- clause: clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)),
1469
+ fields: fields,
1470
+ clause: cls,
1137
1471
  context,
1138
1472
  });
1139
1473
  if (!row) {
@@ -1160,11 +1494,12 @@ async function loadRawEdgeCountX(options) {
1160
1494
  if (!edgeData) {
1161
1495
  throw new Error(`error loading edge data for ${edgeType}`);
1162
1496
  }
1497
+ const { cls } = getEdgeClauseAndFields(clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)), options);
1163
1498
  const row = await loadRowX({
1164
1499
  tableName: edgeData.edgeTable,
1165
1500
  // sqlite needs as count otherwise it returns count(1)
1166
1501
  fields: ["count(1) as count"],
1167
- clause: clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType)),
1502
+ clause: cls,
1168
1503
  context,
1169
1504
  });
1170
1505
  return parseInt(row["count"], 10) || 0;
@@ -1190,19 +1525,20 @@ async function loadNodesByEdge(viewer, id1, edgeType, options) {
1190
1525
  }
1191
1526
  exports.loadNodesByEdge = loadNodesByEdge;
1192
1527
  async function applyPrivacyPolicyForRow(viewer, options, row) {
1193
- if (!row) {
1194
- return null;
1195
- }
1196
- const ent = new options.ent(viewer, row);
1197
- return await applyPrivacyPolicyForEnt(viewer, ent, row, options);
1528
+ const r = await applyPrivacyPolicyForRowImpl(viewer, options, row);
1529
+ return r instanceof Error ? null : r;
1198
1530
  }
1199
1531
  exports.applyPrivacyPolicyForRow = applyPrivacyPolicyForRow;
1532
+ async function applyPrivacyPolicyForRowImpl(viewer, options, row) {
1533
+ const ent = new options.ent(viewer, row);
1534
+ return applyPrivacyPolicyForEnt(viewer, ent, row, options);
1535
+ }
1200
1536
  async function applyPrivacyPolicyForRowX(viewer, options, row) {
1201
1537
  const ent = new options.ent(viewer, row);
1202
1538
  return await applyPrivacyPolicyForEntX(viewer, ent, row, options);
1203
1539
  }
1204
- exports.applyPrivacyPolicyForRowX = applyPrivacyPolicyForRowX;
1205
- async function applyPrivacyPolicyForRows(viewer, rows, options) {
1540
+ // deprecated. doesn't use entcache
1541
+ async function applyPrivacyPolicyForRowsDeprecated(viewer, rows, options) {
1206
1542
  let m = new Map();
1207
1543
  // apply privacy logic
1208
1544
  await Promise.all(rows.map(async (row) => {
@@ -1213,6 +1549,22 @@ async function applyPrivacyPolicyForRows(viewer, rows, options) {
1213
1549
  }));
1214
1550
  return m;
1215
1551
  }
1552
+ async function applyPrivacyPolicyForRows(viewer, rows, options) {
1553
+ const result = new Array(rows.length);
1554
+ if (!rows.length) {
1555
+ return [];
1556
+ }
1557
+ const entLoader = getEntLoader(viewer, options);
1558
+ await Promise.all(rows.map(async (row, idx) => {
1559
+ const r = await applyPrivacyPolicyForRowAndStoreInEntLoader(viewer, row, options, entLoader);
1560
+ if (r instanceof ErrorWrapper) {
1561
+ return;
1562
+ }
1563
+ result[idx] = r;
1564
+ }));
1565
+ // filter ents that aren't visible because of privacy
1566
+ return result.filter((r) => r !== undefined);
1567
+ }
1216
1568
  exports.applyPrivacyPolicyForRows = applyPrivacyPolicyForRows;
1217
1569
  async function loadEdgeWithConst(viewer, id1, id2, edgeEnum, edgeType) {
1218
1570
  const edge = await loadEdgeForID2({