@snowtop/ent 0.1.0-alpha99 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/action/action.d.ts +8 -1
  2. package/action/executor.d.ts +16 -3
  3. package/action/executor.js +83 -27
  4. package/action/index.d.ts +2 -1
  5. package/action/operations.d.ts +126 -0
  6. package/action/operations.js +686 -0
  7. package/action/orchestrator.d.ts +22 -8
  8. package/action/orchestrator.js +278 -67
  9. package/core/base.d.ts +34 -24
  10. package/core/clause.d.ts +62 -79
  11. package/core/clause.js +77 -5
  12. package/core/config.d.ts +5 -1
  13. package/core/config.js +3 -0
  14. package/core/const.d.ts +3 -0
  15. package/core/const.js +6 -0
  16. package/core/context.d.ts +4 -3
  17. package/core/context.js +2 -1
  18. package/core/db.d.ts +1 -0
  19. package/core/db.js +7 -7
  20. package/core/ent.d.ts +53 -105
  21. package/core/ent.js +104 -599
  22. package/core/global_schema.d.ts +7 -0
  23. package/core/global_schema.js +51 -0
  24. package/core/loaders/assoc_count_loader.d.ts +4 -2
  25. package/core/loaders/assoc_count_loader.js +10 -2
  26. package/core/loaders/assoc_edge_loader.d.ts +2 -3
  27. package/core/loaders/assoc_edge_loader.js +16 -7
  28. package/core/loaders/index.d.ts +0 -1
  29. package/core/loaders/index.js +1 -3
  30. package/core/loaders/loader.d.ts +3 -3
  31. package/core/loaders/loader.js +3 -20
  32. package/core/loaders/object_loader.d.ts +30 -10
  33. package/core/loaders/object_loader.js +179 -40
  34. package/core/loaders/query_loader.d.ts +4 -4
  35. package/core/loaders/query_loader.js +14 -19
  36. package/core/loaders/raw_count_loader.d.ts +1 -0
  37. package/core/loaders/raw_count_loader.js +3 -2
  38. package/core/privacy.d.ts +19 -10
  39. package/core/privacy.js +47 -26
  40. package/core/query/assoc_query.js +1 -1
  41. package/core/query/custom_clause_query.d.ts +6 -3
  42. package/core/query/custom_clause_query.js +36 -9
  43. package/core/query/custom_query.d.ts +3 -1
  44. package/core/query/custom_query.js +29 -6
  45. package/core/query/query.d.ts +12 -2
  46. package/core/query/query.js +67 -38
  47. package/core/query/shared_assoc_test.js +151 -10
  48. package/core/query/shared_test.d.ts +2 -2
  49. package/core/query/shared_test.js +90 -30
  50. package/core/query_impl.d.ts +8 -0
  51. package/core/query_impl.js +28 -0
  52. package/core/viewer.d.ts +2 -0
  53. package/core/viewer.js +2 -0
  54. package/graphql/graphql.d.ts +103 -19
  55. package/graphql/graphql.js +169 -134
  56. package/graphql/graphql_field_helpers.d.ts +9 -3
  57. package/graphql/graphql_field_helpers.js +22 -2
  58. package/graphql/index.d.ts +2 -1
  59. package/graphql/index.js +5 -2
  60. package/graphql/scalars/orderby_direction.d.ts +2 -0
  61. package/graphql/scalars/orderby_direction.js +15 -0
  62. package/imports/dataz/example1/_auth.js +128 -47
  63. package/imports/dataz/example1/_viewer.js +87 -39
  64. package/imports/index.d.ts +1 -1
  65. package/imports/index.js +2 -2
  66. package/index.d.ts +12 -1
  67. package/index.js +18 -6
  68. package/package.json +20 -17
  69. package/parse_schema/parse.d.ts +10 -4
  70. package/parse_schema/parse.js +70 -24
  71. package/schema/base_schema.d.ts +8 -0
  72. package/schema/base_schema.js +11 -0
  73. package/schema/field.d.ts +6 -3
  74. package/schema/field.js +72 -17
  75. package/schema/index.d.ts +1 -1
  76. package/schema/index.js +2 -1
  77. package/schema/json_field.d.ts +3 -3
  78. package/schema/json_field.js +4 -1
  79. package/schema/schema.d.ts +42 -5
  80. package/schema/schema.js +35 -41
  81. package/schema/struct_field.d.ts +8 -6
  82. package/schema/struct_field.js +67 -8
  83. package/schema/union_field.d.ts +1 -1
  84. package/scripts/custom_compiler.js +4 -4
  85. package/scripts/custom_graphql.js +105 -75
  86. package/scripts/move_types.js +4 -1
  87. package/scripts/read_schema.js +2 -2
  88. package/testutils/action/complex_schemas.d.ts +1 -1
  89. package/testutils/action/complex_schemas.js +10 -3
  90. package/testutils/builder.d.ts +3 -0
  91. package/testutils/builder.js +6 -0
  92. package/testutils/db/temp_db.d.ts +9 -1
  93. package/testutils/db/temp_db.js +82 -14
  94. package/testutils/db_mock.js +1 -3
  95. package/testutils/ent-graphql-tests/index.d.ts +1 -1
  96. package/testutils/ent-graphql-tests/index.js +30 -19
  97. package/testutils/fake_comms.js +1 -1
  98. package/testutils/fake_data/fake_contact.d.ts +1 -1
  99. package/testutils/fake_data/fake_tag.d.ts +1 -1
  100. package/testutils/fake_data/fake_user.d.ts +3 -3
  101. package/testutils/fake_data/fake_user.js +15 -4
  102. package/testutils/fake_data/tag_query.js +8 -3
  103. package/testutils/fake_data/test_helpers.d.ts +3 -2
  104. package/testutils/fake_data/test_helpers.js +4 -4
  105. package/testutils/fake_data/user_query.d.ts +5 -2
  106. package/testutils/fake_data/user_query.js +19 -2
  107. package/testutils/fake_log.js +1 -1
  108. package/tsc/ast.js +2 -1
  109. package/tsc/move_generated.js +2 -2
  110. package/tsc/transform.d.ts +2 -2
  111. package/tsc/transform.js +4 -3
  112. package/tsc/transform_ent.js +2 -1
  113. package/tsc/transform_schema.js +4 -3
  114. package/core/loaders/index_loader.d.ts +0 -14
  115. package/core/loaders/index_loader.js +0 -27
package/core/ent.js CHANGED
@@ -26,41 +26,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
26
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.loadUniqueNode = exports.loadUniqueEdge = exports.loadCustomEdges = exports.getEdgeClauseAndFields = exports.loadEdges = 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.___setLogQueryErrorWithError = exports.loadRow = exports.loadRowX = exports.logQuery = 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;
30
- exports.getEdgeTypeInGroup = exports.applyPrivacyPolicyForRows = exports.applyPrivacyPolicyForRow = exports.loadNodesByEdge = exports.loadEdgeForID2 = exports.loadRawEdgeCountX = void 0;
29
+ exports.applyPrivacyPolicyForRow = exports.loadNodesByEdge = exports.loadEdgeForID2 = exports.loadRawEdgeCountX = exports.loadUniqueNode = exports.loadUniqueEdge = exports.loadCustomEdges = exports.getEdgeClauseAndFields = exports.loadEdges = exports.getDefaultLimit = exports.setDefaultLimit = exports.loadEdgeDatas = exports.loadEdgeData = exports.assocEdgeLoader = exports.AssocEdgeData = exports.getCursor = exports.AssocEdge = exports.deleteRowsSync = exports.deleteRows = exports.editRowSync = exports.editRow = exports.buildUpdateQuery = exports.createRowSync = exports.createRow = exports.buildInsertQuery = exports.buildGroupQuery = exports.buildQuery = exports.loadRows = exports.performRawQuery = exports.___setLogQueryErrorWithError = exports.loadRow = exports.loadRowX = exports.logQuery = 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 = exports.getEntLoader = exports.rowIsError = void 0;
30
+ exports.getEdgeTypeInGroup = exports.applyPrivacyPolicyForRows = void 0;
31
31
  const db_1 = __importStar(require("./db"));
32
32
  const privacy_1 = require("./privacy");
33
33
  const clause = __importStar(require("./clause"));
34
- const action_1 = require("../action");
35
34
  const logger_1 = require("./logger");
36
35
  const dataloader_1 = __importDefault(require("dataloader"));
37
- const schema_1 = require("../schema/");
38
- // TODO kill this and createDataLoader
39
- class cacheMap {
40
- constructor(options) {
41
- this.options = options;
42
- this.m = new Map();
43
- }
44
- get(key) {
45
- const ret = this.m.get(key);
46
- if (ret) {
47
- (0, logger_1.log)("cache", {
48
- "dataloader-cache-hit": key,
49
- "tableName": this.options.tableName,
50
- });
51
- }
52
- return ret;
53
- }
54
- set(key, value) {
55
- return this.m.set(key, value);
56
- }
57
- delete(key) {
58
- return this.m.delete(key);
59
- }
60
- clear() {
61
- return this.m.clear();
62
- }
63
- }
36
+ const global_schema_1 = require("./global_schema");
37
+ const query_impl_1 = require("./query_impl");
38
+ const loader_1 = require("./loaders/loader");
64
39
  class entCacheMap {
65
40
  constructor(viewer, options) {
66
41
  this.viewer = viewer;
@@ -89,11 +64,11 @@ class entCacheMap {
89
64
  return this.m.clear();
90
65
  }
91
66
  }
92
- function createDataLoader(options) {
67
+ function createAssocEdgeConfigLoader(options) {
93
68
  const loaderOptions = {};
94
69
  // if query logging is enabled, we should log what's happening with loader
95
70
  if ((0, logger_1.logEnabled)("query")) {
96
- loaderOptions.cacheMap = new cacheMap(options);
71
+ loaderOptions.cacheMap = new loader_1.CacheMap(options);
97
72
  }
98
73
  // something here brokwn with strict:true
99
74
  return new dataloader_1.default(async (ids) => {
@@ -101,9 +76,11 @@ function createDataLoader(options) {
101
76
  return [];
102
77
  }
103
78
  let col = options.key;
79
+ // defaults to uuid
80
+ let typ = options.keyType || "uuid";
104
81
  const rowOptions = {
105
82
  ...options,
106
- clause: clause.In(col, ...ids),
83
+ clause: clause.DBTypeIn(col, ids, typ),
107
84
  };
108
85
  // TODO is there a better way of doing this?
109
86
  // context not needed because we're creating a loader which has its own cache which is being used here
@@ -126,6 +103,13 @@ class ErrorWrapper {
126
103
  this.error = error;
127
104
  }
128
105
  }
106
+ // note if storing the result of this in something that checks instanceof Error e.g. DataLoader, we need to check instanceof at that callsite
107
+ function rowIsError(row) {
108
+ // jest does things that break instanceof checks
109
+ // so we need to check the name as well for native error SqliteError
110
+ return row instanceof Error || row?.constructor?.name === "SqliteError";
111
+ }
112
+ exports.rowIsError = rowIsError;
129
113
  function createEntLoader(viewer, options, map) {
130
114
  // share the cache across loaders even if we create a new instance
131
115
  const loaderOptions = {};
@@ -143,8 +127,14 @@ function createEntLoader(viewer, options, map) {
143
127
  for (let idx = 0; idx < rows.length; idx++) {
144
128
  const row = rows[idx];
145
129
  // db error
146
- if (row instanceof Error) {
147
- result[idx] = row;
130
+ if (rowIsError(row)) {
131
+ if (row instanceof Error) {
132
+ result[idx] = row;
133
+ }
134
+ else {
135
+ // @ts-ignore SqliteError
136
+ result[idx] = new Error(row.message);
137
+ }
148
138
  continue;
149
139
  }
150
140
  else if (!row) {
@@ -157,7 +147,7 @@ function createEntLoader(viewer, options, map) {
157
147
  }
158
148
  else {
159
149
  const r = await applyPrivacyPolicyForRowImpl(viewer, options, row);
160
- if (r instanceof Error) {
150
+ if (rowIsError(r)) {
161
151
  result[idx] = new ErrorWrapper(r);
162
152
  }
163
153
  else {
@@ -201,6 +191,7 @@ function getEntLoader(viewer, options) {
201
191
  const name = `ent-loader:${viewer.instanceKey()}:${options.loaderFactory.name}`;
202
192
  return viewer.context.cache.getLoaderWithLoadMany(name, () => new EntLoader(viewer, options));
203
193
  }
194
+ exports.getEntLoader = getEntLoader;
204
195
  function getEntKey(viewer, id, options) {
205
196
  return `${viewer.instanceKey()}:${options.loaderFactory.name}:${id}`;
206
197
  }
@@ -231,7 +222,7 @@ loader) {
231
222
  return result;
232
223
  }
233
224
  const r = await applyPrivacyPolicyForRowImpl(viewer, options, row);
234
- if (r instanceof Error) {
225
+ if (rowIsError(r)) {
235
226
  loader.prime(id, new ErrorWrapper(r));
236
227
  return new ErrorWrapper(r);
237
228
  }
@@ -321,7 +312,7 @@ async function loadEnts(viewer, options, ...ids) {
321
312
  let m = new Map();
322
313
  const ret = await getEntLoader(viewer, options).loadMany(ids);
323
314
  for (const r of ret) {
324
- if (r instanceof Error) {
315
+ if (rowIsError(r)) {
325
316
  throw r;
326
317
  }
327
318
  if (r instanceof ErrorWrapper) {
@@ -396,6 +387,12 @@ function isParameterizedQuery(opts) {
396
387
  * orderby: 'time',
397
388
  * disableTransformations: false
398
389
  * }) // doesn't change the query
390
+ *
391
+ * For queries that pass in a clause, we batch them with an underlying dataloader so that multiple queries with the same clause
392
+ * or parallel queries with the same clause are batched together.
393
+ *
394
+ * If a raw or parameterized query is passed in, we don't attempt to batch them together and they're executed as is.
395
+ * If you end up with a scenario where you may need to coalesce or batch (non-clause) queries here, you should use some kind of memoization here.
399
396
  */
400
397
  async function loadCustomData(options, query, context) {
401
398
  const rows = await loadCustomDataImpl(options, query, context);
@@ -414,7 +411,10 @@ exports.loadCustomData = loadCustomData;
414
411
  // NOTE: if you use a raw query or paramterized query with this,
415
412
  // you should use `SELECT count(*) as count...`
416
413
  async function loadCustomCount(options, query, context) {
417
- // TODO also need to loaderify this in case we're querying for this a lot...
414
+ // if clause, we'll use the loader and strong typing/coalescing it provides
415
+ if (typeof query !== "string" && isClause(query)) {
416
+ return options.loaderFactory.createCountLoader(context).load(query);
417
+ }
418
418
  const rows = await loadCustomDataImpl({
419
419
  ...options,
420
420
  fields: ["count(1) as count"],
@@ -429,46 +429,31 @@ function isPrimableLoader(loader) {
429
429
  return loader != undefined;
430
430
  }
431
431
  async function loadCustomDataImpl(options, query, context) {
432
- function getClause(cls) {
433
- let optClause = options.loaderFactory?.options?.clause;
434
- if (typeof optClause === "function") {
435
- optClause = optClause();
436
- }
437
- if (!optClause) {
438
- return cls;
439
- }
440
- return clause.And(cls, optClause);
441
- }
442
432
  if (typeof query === "string") {
443
433
  // no caching, perform raw query
444
434
  return performRawQuery(query, [], []);
445
435
  }
446
436
  else if (isClause(query)) {
447
- // if a Clause is passed in and we have a default clause
448
- // associated with the query, pass that in
449
- // if we want to disableTransformations, need to indicate that with
450
- // disableTransformations option
451
- // this will have rudimentary caching but nothing crazy
452
- return loadRows({
453
- ...options,
454
- clause: getClause(query),
455
- context: context,
456
- });
437
+ const r = await options.loaderFactory
438
+ .createTypedLoader(context)
439
+ .load(query);
440
+ return r;
457
441
  }
458
442
  else if (isParameterizedQuery(query)) {
459
443
  // no caching, perform raw query
460
444
  return performRawQuery(query.query, query.values || [], query.logValues);
461
445
  }
462
446
  else {
447
+ // this will have rudimentary caching but nothing crazy
463
448
  let cls = query.clause;
464
449
  if (!query.disableTransformations) {
465
- cls = getClause(cls);
450
+ cls = clause.getCombinedClause(options.loaderFactory.options, query.clause);
466
451
  }
467
- // this will have rudimentary caching but nothing crazy
468
452
  return loadRows({
469
453
  ...query,
470
454
  ...options,
471
455
  context: context,
456
+ // @ts-expect-error
472
457
  clause: cls,
473
458
  });
474
459
  }
@@ -480,7 +465,7 @@ async function loadDerivedEnt(viewer, data, loader) {
480
465
  const r = await applyPrivacyPolicyForEnt(viewer, ent, data, {
481
466
  ent: loader,
482
467
  });
483
- if (r instanceof Error) {
468
+ if (rowIsError(r)) {
484
469
  return null;
485
470
  }
486
471
  return r;
@@ -503,7 +488,7 @@ async function applyPrivacyPolicyForEnt(viewer, ent, data, fieldPrivacyOptions)
503
488
  }
504
489
  async function applyPrivacyPolicyForEntX(viewer, ent, data, options) {
505
490
  const r = await applyPrivacyPolicyForEnt(viewer, ent, data, options);
506
- if (r instanceof Error) {
491
+ if (rowIsError(r)) {
507
492
  throw r;
508
493
  }
509
494
  if (r === null) {
@@ -517,11 +502,12 @@ async function doFieldPrivacy(viewer, ent, data, options) {
517
502
  }
518
503
  const promises = [];
519
504
  let somethingChanged = false;
505
+ const clone = { ...data };
520
506
  const origData = {
521
507
  ...data,
522
508
  };
523
509
  for (const [k, policy] of options.fieldPrivacy) {
524
- const curr = data[k];
510
+ const curr = clone[k];
525
511
  if (curr === null || curr === undefined) {
526
512
  continue;
527
513
  }
@@ -529,7 +515,7 @@ async function doFieldPrivacy(viewer, ent, data, options) {
529
515
  // don't do anything if key is null or for some reason missing
530
516
  const r = await (0, privacy_1.applyPrivacyPolicy)(viewer, policy, ent);
531
517
  if (!r) {
532
- data[k] = null;
518
+ clone[k] = null;
533
519
  somethingChanged = true;
534
520
  }
535
521
  })());
@@ -537,7 +523,7 @@ async function doFieldPrivacy(viewer, ent, data, options) {
537
523
  await Promise.all(promises);
538
524
  if (somethingChanged) {
539
525
  // have to create new instance
540
- const ent = new options.ent(viewer, data);
526
+ const ent = new options.ent(viewer, clone);
541
527
  ent.__setRawDBData(origData);
542
528
  return ent;
543
529
  }
@@ -605,7 +591,7 @@ async function performRawQuery(query, values, logValues) {
605
591
  catch (e) {
606
592
  if (_logQueryWithError) {
607
593
  const msg = e.message;
608
- throw new Error(`error \`${msg}\` running query: \`${query}\``);
594
+ throw new Error(`error \`${msg}\` running query: \`${query}\` with values: \`${logValues}\``);
609
595
  }
610
596
  throw e;
611
597
  }
@@ -634,17 +620,18 @@ function buildQuery(options) {
634
620
  const fields = options.fields.join(", ");
635
621
  // always start at 1
636
622
  const whereClause = options.clause.clause(1);
637
- let query = `SELECT ${fields} FROM ${options.tableName} WHERE ${whereClause}`;
623
+ const parts = [];
624
+ parts.push(`SELECT ${fields} FROM ${options.tableName} WHERE ${whereClause}`);
638
625
  if (options.groupby) {
639
- query = `${query} GROUP BY ${options.groupby}`;
626
+ parts.push(`GROUP BY ${options.groupby}`);
640
627
  }
641
628
  if (options.orderby) {
642
- query = `${query} ORDER BY ${options.orderby}`;
629
+ parts.push(`ORDER BY ${(0, query_impl_1.getOrderByPhrase)(options.orderby)}`);
643
630
  }
644
631
  if (options.limit) {
645
- query = `${query} LIMIT ${options.limit}`;
632
+ parts.push(`LIMIT ${options.limit}`);
646
633
  }
647
- return query;
634
+ return parts.join(" ");
648
635
  }
649
636
  exports.buildQuery = buildQuery;
650
637
  // this is used for queries when we select multiple ids at once
@@ -656,7 +643,7 @@ function buildGroupQuery(options) {
656
643
  }
657
644
  let orderby = "";
658
645
  if (options.orderby) {
659
- orderby = `ORDER BY ${options.orderby}`;
646
+ orderby = `ORDER BY ${(0, query_impl_1.getOrderByPhrase)(options.orderby)}`;
660
647
  }
661
648
  // window functions work in sqlite!
662
649
  // https://www.sqlite.org/windowfunctions.html
@@ -666,499 +653,6 @@ function buildGroupQuery(options) {
666
653
  ];
667
654
  }
668
655
  exports.buildGroupQuery = buildGroupQuery;
669
- class RawQueryOperation {
670
- constructor(queries) {
671
- this.queries = queries;
672
- }
673
- async performWrite(queryer, context) {
674
- for (const q of this.queries) {
675
- if (typeof q === "string") {
676
- logQuery(q, []);
677
- await queryer.query(q);
678
- }
679
- else {
680
- logQuery(q.query, q.logValues || []);
681
- await queryer.query(q.query, q.values);
682
- }
683
- }
684
- }
685
- performWriteSync(queryer, context) {
686
- for (const q of this.queries) {
687
- if (typeof q === "string") {
688
- logQuery(q, []);
689
- queryer.execSync(q);
690
- }
691
- else {
692
- logQuery(q.query, q.logValues || []);
693
- queryer.execSync(q.query, q.values);
694
- }
695
- }
696
- }
697
- }
698
- exports.RawQueryOperation = RawQueryOperation;
699
- class EditNodeOperation {
700
- constructor(options, existingEnt = null) {
701
- this.options = options;
702
- this.existingEnt = existingEnt;
703
- this.row = null;
704
- this.placeholderID = options.placeholderID;
705
- }
706
- resolve(executor) {
707
- if (!this.options.fieldsToResolve.length) {
708
- return;
709
- }
710
- let fields = this.options.fields;
711
- this.options.fieldsToResolve.forEach((fieldName) => {
712
- let value = fields[fieldName];
713
- if (!value) {
714
- throw new Error(`trying to resolve field ${fieldName} but not a valid field`);
715
- }
716
- let ent = executor.resolveValue(value.placeholderID);
717
- if (!ent) {
718
- throw new Error(`couldn't resolve field \`${fieldName}\` with value ${value.placeholderID}`);
719
- }
720
- fields[fieldName] = ent.id;
721
- });
722
- this.options.fields = fields;
723
- }
724
- hasData(data) {
725
- for (const _k in data) {
726
- return true;
727
- }
728
- return false;
729
- }
730
- async performWrite(queryer, context) {
731
- let options = {
732
- ...this.options,
733
- context,
734
- };
735
- if (this.existingEnt) {
736
- if (this.hasData(options.fields)) {
737
- // even this with returning * may not always work if transformed...
738
- // we can have a transformed flag to see if it should be returned?
739
- this.row = await editRow(queryer, options, "RETURNING *");
740
- }
741
- else {
742
- // @ts-ignore
743
- this.row = this.existingEnt["data"];
744
- }
745
- }
746
- else {
747
- this.row = await createRow(queryer, options, "RETURNING *");
748
- }
749
- }
750
- reloadRow(queryer, id, options) {
751
- // TODO this isn't always an ObjectLoader. should throw or figure out a way to get query
752
- // and run this on its own...
753
- const loader = this.options.loadEntOptions.loaderFactory.createLoader(options.context);
754
- const opts = loader.getOptions();
755
- let cls = clause.Eq(options.key, id);
756
- if (opts.clause) {
757
- let optionClause;
758
- if (typeof opts.clause === "function") {
759
- optionClause = opts.clause();
760
- }
761
- else {
762
- optionClause = opts.clause;
763
- }
764
- if (optionClause) {
765
- cls = clause.And(cls, optionClause);
766
- }
767
- }
768
- const query = buildQuery({
769
- fields: opts.fields.length ? opts.fields : ["*"],
770
- tableName: options.tableName,
771
- clause: cls,
772
- });
773
- // special case log here because we're not going through any of the normal
774
- // methods here because those are async and this is sync
775
- // this is the only place we're doing this so only handling here
776
- logQuery(query, [id]);
777
- const r = queryer.querySync(query, [id]);
778
- if (r.rows.length === 1) {
779
- this.row = r.rows[0];
780
- }
781
- }
782
- performWriteSync(queryer, context) {
783
- let options = {
784
- ...this.options,
785
- context,
786
- };
787
- if (this.existingEnt) {
788
- if (this.hasData(this.options.fields)) {
789
- editRowSync(queryer, options, "RETURNING *");
790
- this.reloadRow(queryer, this.existingEnt.id, options);
791
- }
792
- else {
793
- // @ts-ignore
794
- this.row = this.existingEnt["data"];
795
- }
796
- }
797
- else {
798
- createRowSync(queryer, options, "RETURNING *");
799
- const id = options.fields[options.key];
800
- this.reloadRow(queryer, id, options);
801
- }
802
- }
803
- returnedRow() {
804
- return this.row;
805
- }
806
- createdEnt(viewer) {
807
- if (!this.row) {
808
- return null;
809
- }
810
- return new this.options.loadEntOptions.ent(viewer, this.row);
811
- }
812
- }
813
- exports.EditNodeOperation = EditNodeOperation;
814
- let globalSchema;
815
- function setGlobalSchema(val) {
816
- globalSchema = val;
817
- }
818
- exports.setGlobalSchema = setGlobalSchema;
819
- function clearGlobalSchema() {
820
- globalSchema = undefined;
821
- }
822
- exports.clearGlobalSchema = clearGlobalSchema;
823
- // used by tests. no guarantee will always exist
824
- function __hasGlobalSchema() {
825
- return globalSchema !== undefined;
826
- }
827
- exports.__hasGlobalSchema = __hasGlobalSchema;
828
- class EdgeOperation {
829
- constructor(builder, edgeInput, options) {
830
- this.builder = builder;
831
- this.edgeInput = edgeInput;
832
- this.options = options;
833
- }
834
- async preFetch(queryer, context) {
835
- let edgeData = await loadEdgeData(this.edgeInput.edgeType);
836
- if (!edgeData) {
837
- throw new Error(`error loading edge data for ${this.edgeInput.edgeType}`);
838
- }
839
- this.edgeData = edgeData;
840
- }
841
- async performWrite(queryer, context) {
842
- if (!this.edgeData) {
843
- throw new Error(`error fetching edgeData for type ${this.edgeInput.edgeType}`);
844
- }
845
- switch (this.options.operation) {
846
- case action_1.WriteOperation.Delete:
847
- return this.performDeleteWrite(queryer, this.edgeData, this.edgeInput, context);
848
- case action_1.WriteOperation.Insert:
849
- case action_1.WriteOperation.Edit:
850
- return this.performInsertWrite(queryer, this.edgeData, this.edgeInput, context);
851
- }
852
- }
853
- performWriteSync(queryer, context) {
854
- if (!this.edgeData) {
855
- throw new Error(`error fetching edgeData for type ${this.edgeInput.edgeType}`);
856
- }
857
- switch (this.options.operation) {
858
- case action_1.WriteOperation.Delete:
859
- return this.performDeleteWriteSync(queryer, this.edgeData, this.edgeInput, context);
860
- case action_1.WriteOperation.Insert:
861
- case action_1.WriteOperation.Edit:
862
- return this.performInsertWriteSync(queryer, this.edgeData, this.edgeInput, context);
863
- }
864
- }
865
- getDeleteRowParams(edgeData, edge, context) {
866
- let transformed = null;
867
- let op = schema_1.SQLStatementOperation.Delete;
868
- let updateData = null;
869
- // TODO respect disableTransformations
870
- if (globalSchema?.transformEdgeWrite) {
871
- transformed = globalSchema.transformEdgeWrite({
872
- op: schema_1.SQLStatementOperation.Delete,
873
- edge,
874
- });
875
- if (transformed) {
876
- op = transformed.op;
877
- if (transformed.op === schema_1.SQLStatementOperation.Insert) {
878
- throw new Error(`cannot currently transform a delete into an insert`);
879
- }
880
- if (transformed.op === schema_1.SQLStatementOperation.Update) {
881
- if (!transformed.data) {
882
- throw new Error(`cannot transform a delete into an update without providing data`);
883
- }
884
- updateData = transformed.data;
885
- }
886
- }
887
- }
888
- return {
889
- op,
890
- updateData,
891
- options: {
892
- tableName: edgeData.edgeTable,
893
- context,
894
- },
895
- clause: clause.And(clause.Eq("id1", edge.id1), clause.Eq("id2", edge.id2), clause.Eq("edge_type", edge.edgeType)),
896
- };
897
- }
898
- async performDeleteWrite(q, edgeData, edge, context) {
899
- const params = this.getDeleteRowParams(edgeData, edge, context);
900
- if (params.op === schema_1.SQLStatementOperation.Delete) {
901
- return deleteRows(q, params.options, params.clause);
902
- }
903
- else {
904
- if (params.op !== schema_1.SQLStatementOperation.Update) {
905
- throw new Error(`invalid operation ${params.op}`);
906
- }
907
- await editRow(q, {
908
- tableName: params.options.tableName,
909
- whereClause: params.clause,
910
- fields: params.updateData,
911
- fieldsToLog: params.updateData,
912
- });
913
- }
914
- }
915
- performDeleteWriteSync(q, edgeData, edge, context) {
916
- const params = this.getDeleteRowParams(edgeData, edge, context);
917
- if (params.op === schema_1.SQLStatementOperation.Delete) {
918
- return deleteRowsSync(q, params.options, params.clause);
919
- }
920
- else {
921
- if (params.op !== schema_1.SQLStatementOperation.Update) {
922
- throw new Error(`invalid operation ${params.op}`);
923
- }
924
- editRowSync(q, {
925
- tableName: params.options.tableName,
926
- whereClause: params.clause,
927
- fields: params.updateData,
928
- });
929
- }
930
- }
931
- getInsertRowParams(edgeData, edge, context) {
932
- const fields = {
933
- id1: edge.id1,
934
- id2: edge.id2,
935
- id1_type: edge.id1Type,
936
- id2_type: edge.id2Type,
937
- edge_type: edge.edgeType,
938
- data: edge.data || null,
939
- };
940
- if (edge.time) {
941
- fields["time"] = edge.time.toISOString();
942
- }
943
- else {
944
- // todo make this a schema field like what we do in generated base files...
945
- // maybe when actions exist?
946
- fields["time"] = new Date().toISOString();
947
- }
948
- const onConflictFields = ["data"];
949
- if (globalSchema?.extraEdgeFields) {
950
- for (const name in globalSchema.extraEdgeFields) {
951
- const f = globalSchema.extraEdgeFields[name];
952
- if (f.defaultValueOnCreate) {
953
- const storageKey = (0, schema_1.getStorageKey)(f, name);
954
- fields[storageKey] = f.defaultValueOnCreate(this.builder, {});
955
- // onconflict make sure we override the default values
956
- // e.g. setting deleted_at = null for soft delete
957
- onConflictFields.push(storageKey);
958
- }
959
- }
960
- }
961
- // TODO respect disableTransformations
962
- let transformed = null;
963
- if (globalSchema?.transformEdgeWrite) {
964
- transformed = globalSchema.transformEdgeWrite({
965
- op: schema_1.SQLStatementOperation.Insert,
966
- edge,
967
- });
968
- if (transformed) {
969
- throw new Error(`transforming an insert edge not currently supported`);
970
- }
971
- }
972
- return [
973
- {
974
- tableName: edgeData.edgeTable,
975
- fields: fields,
976
- fieldsToLog: fields,
977
- context,
978
- },
979
- `ON CONFLICT(id1, edge_type, id2) DO UPDATE SET ${onConflictFields
980
- .map((f) => `${f} = EXCLUDED.${f}`)
981
- .join(", ")}`,
982
- ];
983
- }
984
- async performInsertWrite(q, edgeData, edge, context) {
985
- const [options, suffix] = this.getInsertRowParams(edgeData, edge, context);
986
- await createRow(q, options, suffix);
987
- }
988
- performInsertWriteSync(q, edgeData, edge, context) {
989
- const [options, suffix] = this.getInsertRowParams(edgeData, edge, context);
990
- createRowSync(q, options, suffix);
991
- }
992
- resolveImpl(executor, placeholder, desc) {
993
- let ent = executor.resolveValue(placeholder);
994
- if (!ent) {
995
- throw new Error(`could not resolve placeholder value ${placeholder} for ${desc} for edge ${this.edgeInput.edgeType}`);
996
- }
997
- if (ent.id === undefined) {
998
- throw new Error(`id of resolved ent is not defined`);
999
- }
1000
- return [ent.id, ent.nodeType];
1001
- }
1002
- resolve(executor) {
1003
- if (this.options.id1Placeholder) {
1004
- [this.edgeInput.id1, this.edgeInput.id1Type] = this.resolveImpl(executor, this.edgeInput.id1, "id1");
1005
- }
1006
- if (this.options.id2Placeholder) {
1007
- [this.edgeInput.id2, this.edgeInput.id2Type] = this.resolveImpl(executor, this.edgeInput.id2, "id2");
1008
- }
1009
- if (this.options.dataPlaceholder) {
1010
- if (!this.edgeInput.data) {
1011
- throw new Error(`data placeholder set but edgeInput data undefined`);
1012
- }
1013
- let [data, _] = this.resolveImpl(executor, this.edgeInput.data.toString(), "data");
1014
- this.edgeInput.data = data.toString();
1015
- }
1016
- }
1017
- symmetricEdge() {
1018
- return new EdgeOperation(this.builder, {
1019
- id1: this.edgeInput.id2,
1020
- id1Type: this.edgeInput.id2Type,
1021
- id2: this.edgeInput.id1,
1022
- id2Type: this.edgeInput.id1Type,
1023
- edgeType: this.edgeInput.edgeType,
1024
- time: this.edgeInput.time,
1025
- data: this.edgeInput.data,
1026
- }, {
1027
- operation: this.options.operation,
1028
- id1Placeholder: this.options.id2Placeholder,
1029
- id2Placeholder: this.options.id1Placeholder,
1030
- dataPlaceholder: this.options.dataPlaceholder,
1031
- });
1032
- }
1033
- inverseEdge(edgeData) {
1034
- return new EdgeOperation(this.builder, {
1035
- id1: this.edgeInput.id2,
1036
- id1Type: this.edgeInput.id2Type,
1037
- id2: this.edgeInput.id1,
1038
- id2Type: this.edgeInput.id1Type,
1039
- edgeType: edgeData.inverseEdgeType,
1040
- time: this.edgeInput.time,
1041
- data: this.edgeInput.data,
1042
- }, {
1043
- operation: this.options.operation,
1044
- id1Placeholder: this.options.id2Placeholder,
1045
- id2Placeholder: this.options.id1Placeholder,
1046
- dataPlaceholder: this.options.dataPlaceholder,
1047
- });
1048
- }
1049
- static resolveIDs(srcBuilder, // id1
1050
- destID) {
1051
- let destIDVal;
1052
- let destPlaceholder = false;
1053
- if (this.isBuilder(destID)) {
1054
- destIDVal = destID.placeholderID;
1055
- destPlaceholder = true;
1056
- }
1057
- else {
1058
- destIDVal = destID;
1059
- }
1060
- let srcIDVal;
1061
- let srcType;
1062
- let srcPlaceholder = false;
1063
- if (srcBuilder.existingEnt) {
1064
- srcIDVal = srcBuilder.existingEnt.id;
1065
- srcType = srcBuilder.existingEnt.nodeType;
1066
- }
1067
- else {
1068
- srcPlaceholder = true;
1069
- // get placeholder.
1070
- srcIDVal = srcBuilder.placeholderID;
1071
- // expected to be filled later
1072
- srcType = "";
1073
- }
1074
- return [srcIDVal, srcType, srcPlaceholder, destIDVal, destPlaceholder];
1075
- }
1076
- static isBuilder(val) {
1077
- return val.placeholderID !== undefined;
1078
- }
1079
- static resolveData(data) {
1080
- if (!data) {
1081
- return [undefined, false];
1082
- }
1083
- if (this.isBuilder(data)) {
1084
- return [data.placeholderID.toString(), true];
1085
- }
1086
- return [data, false];
1087
- }
1088
- static inboundEdge(builder, edgeType, id1, nodeType, options) {
1089
- let [id2Val, id2Type, id2Placeholder, id1Val, id1Placeholder] = EdgeOperation.resolveIDs(builder, id1);
1090
- let [data, dataPlaceholder] = EdgeOperation.resolveData(options?.data);
1091
- const edge = {
1092
- id1: id1Val,
1093
- edgeType: edgeType,
1094
- id2: id2Val,
1095
- id2Type: id2Type,
1096
- id1Type: nodeType,
1097
- ...options,
1098
- };
1099
- if (data) {
1100
- edge.data = data;
1101
- }
1102
- return new EdgeOperation(builder, edge, {
1103
- operation: action_1.WriteOperation.Insert,
1104
- id2Placeholder,
1105
- id1Placeholder,
1106
- dataPlaceholder,
1107
- });
1108
- }
1109
- static outboundEdge(builder, edgeType, id2, nodeType, options) {
1110
- let [id1Val, id1Type, id1Placeholder, id2Val, id2Placeholder] = EdgeOperation.resolveIDs(builder, id2);
1111
- let [data, dataPlaceholder] = EdgeOperation.resolveData(options?.data);
1112
- const edge = {
1113
- id1: id1Val,
1114
- edgeType: edgeType,
1115
- id2: id2Val,
1116
- id2Type: nodeType,
1117
- id1Type: id1Type,
1118
- ...options,
1119
- };
1120
- if (data) {
1121
- edge.data = data;
1122
- }
1123
- return new EdgeOperation(builder, edge, {
1124
- operation: action_1.WriteOperation.Insert,
1125
- id1Placeholder,
1126
- id2Placeholder,
1127
- dataPlaceholder,
1128
- });
1129
- }
1130
- static removeInboundEdge(builder, edgeType, id1) {
1131
- if (!builder.existingEnt) {
1132
- throw new Error("cannot remove an edge from a non-existing ent");
1133
- }
1134
- const edge = {
1135
- id1: id1,
1136
- edgeType: edgeType,
1137
- id2: builder.existingEnt.id,
1138
- id2Type: "",
1139
- id1Type: "",
1140
- };
1141
- return new EdgeOperation(builder, edge, {
1142
- operation: action_1.WriteOperation.Delete,
1143
- });
1144
- }
1145
- static removeOutboundEdge(builder, edgeType, id2) {
1146
- if (!builder.existingEnt) {
1147
- throw new Error("cannot remove an edge from a non-existing ent");
1148
- }
1149
- const edge = {
1150
- id2: id2,
1151
- edgeType: edgeType,
1152
- id1: builder.existingEnt.id,
1153
- id2Type: "",
1154
- id1Type: "",
1155
- };
1156
- return new EdgeOperation(builder, edge, {
1157
- operation: action_1.WriteOperation.Delete,
1158
- });
1159
- }
1160
- }
1161
- exports.EdgeOperation = EdgeOperation;
1162
656
  function isSyncQueryer(queryer) {
1163
657
  return queryer.execSync !== undefined;
1164
658
  }
@@ -1228,8 +722,26 @@ function buildInsertQuery(options, suffix) {
1228
722
  const cols = fields.join(", ");
1229
723
  const vals = valsString.join(", ");
1230
724
  let query = `INSERT INTO ${options.tableName} (${cols}) VALUES (${vals})`;
725
+ if (options.onConflict) {
726
+ let onConflict = "";
727
+ if (options.onConflict.onConflictConstraint) {
728
+ onConflict = `ON CONFLICT ON CONSTRAINT ${options.onConflict.onConflictConstraint}`;
729
+ }
730
+ else {
731
+ onConflict = `ON CONFLICT(${options.onConflict.onConflictCols.join(", ")})`;
732
+ }
733
+ if (options.onConflict.updateCols?.length) {
734
+ onConflict += ` DO UPDATE SET ${options.onConflict.updateCols
735
+ .map((f) => `${f} = EXCLUDED.${f}`)
736
+ .join(", ")}`;
737
+ }
738
+ else {
739
+ onConflict += ` DO NOTHING`;
740
+ }
741
+ query = query + " " + onConflict;
742
+ }
1231
743
  if (suffix) {
1232
- query = query + " " + suffix;
744
+ query += " " + suffix;
1233
745
  }
1234
746
  return [query, values, logValues];
1235
747
  }
@@ -1336,27 +848,6 @@ function deleteRowsSync(queryer, options, cls) {
1336
848
  mutateRowSync(queryer, query, cls.values(), cls.logValues(), options);
1337
849
  }
1338
850
  exports.deleteRowsSync = deleteRowsSync;
1339
- class DeleteNodeOperation {
1340
- constructor(id, options) {
1341
- this.id = id;
1342
- this.options = options;
1343
- }
1344
- async performWrite(queryer, context) {
1345
- let options = {
1346
- ...this.options,
1347
- context,
1348
- };
1349
- return deleteRows(queryer, options, clause.Eq("id", this.id));
1350
- }
1351
- performWriteSync(queryer, context) {
1352
- let options = {
1353
- ...this.options,
1354
- context,
1355
- };
1356
- return deleteRowsSync(queryer, options, clause.Eq("id", this.id));
1357
- }
1358
- }
1359
- exports.DeleteNodeOperation = DeleteNodeOperation;
1360
851
  class AssocEdge {
1361
852
  constructor(data) {
1362
853
  this.id1 = data.id1;
@@ -1417,10 +908,11 @@ const assocEdgeFields = [
1417
908
  "inverse_edge_type",
1418
909
  "edge_table",
1419
910
  ];
1420
- exports.assocEdgeLoader = createDataLoader({
911
+ exports.assocEdgeLoader = createAssocEdgeConfigLoader({
1421
912
  tableName: "assoc_edge_config",
1422
913
  fields: assocEdgeFields,
1423
914
  key: "edge_type",
915
+ keyType: "uuid",
1424
916
  });
1425
917
  // we don't expect assoc_edge_config information to change
1426
918
  // so not using ContextCache but just caching it as needed once per server
@@ -1442,7 +934,7 @@ async function loadEdgeDatas(...edgeTypes) {
1442
934
  if (!row) {
1443
935
  return;
1444
936
  }
1445
- if (row instanceof Error) {
937
+ if (rowIsError(row)) {
1446
938
  throw row;
1447
939
  }
1448
940
  m.set(row["edge_type"], new AssocEdgeData(row));
@@ -1459,8 +951,15 @@ const edgeFields = [
1459
951
  "time",
1460
952
  "data",
1461
953
  ];
1462
- exports.DefaultLimit = 1000;
1463
- // TODO default limit from somewhere
954
+ let defaultLimit = 1000;
955
+ function setDefaultLimit(limit) {
956
+ defaultLimit = limit;
957
+ }
958
+ exports.setDefaultLimit = setDefaultLimit;
959
+ function getDefaultLimit() {
960
+ return defaultLimit;
961
+ }
962
+ exports.getDefaultLimit = getDefaultLimit;
1464
963
  function defaultEdgeQueryOptions(id1, edgeType, id2) {
1465
964
  let cls = clause.And(clause.Eq("id1", id1), clause.Eq("edge_type", edgeType));
1466
965
  if (id2) {
@@ -1468,8 +967,13 @@ function defaultEdgeQueryOptions(id1, edgeType, id2) {
1468
967
  }
1469
968
  return {
1470
969
  clause: cls,
1471
- orderby: "time DESC",
1472
- limit: exports.DefaultLimit,
970
+ orderby: [
971
+ {
972
+ column: "time",
973
+ direction: "DESC",
974
+ },
975
+ ],
976
+ limit: defaultLimit,
1473
977
  };
1474
978
  }
1475
979
  async function loadEdges(options) {
@@ -1478,9 +982,10 @@ async function loadEdges(options) {
1478
982
  exports.loadEdges = loadEdges;
1479
983
  function getEdgeClauseAndFields(cls, options) {
1480
984
  let fields = edgeFields;
1481
- if (globalSchema?.transformEdgeRead) {
1482
- const transformClause = globalSchema.transformEdgeRead();
1483
- if (!options.disableTransformations) {
985
+ const transformEdgeRead = (0, global_schema_1.__getGlobalSchema)()?.transformEdgeRead;
986
+ if (transformEdgeRead) {
987
+ const transformClause = transformEdgeRead();
988
+ if (!options.queryOptions?.disableTransformations) {
1484
989
  cls = clause.And(cls, transformClause);
1485
990
  }
1486
991
  fields = edgeFields.concat(transformClause.columns());
@@ -1598,7 +1103,7 @@ async function loadNodesByEdge(viewer, id1, edgeType, options) {
1598
1103
  exports.loadNodesByEdge = loadNodesByEdge;
1599
1104
  async function applyPrivacyPolicyForRow(viewer, options, row) {
1600
1105
  const r = await applyPrivacyPolicyForRowImpl(viewer, options, row);
1601
- return r instanceof Error ? null : r;
1106
+ return rowIsError(r) ? null : r;
1602
1107
  }
1603
1108
  exports.applyPrivacyPolicyForRow = applyPrivacyPolicyForRow;
1604
1109
  async function applyPrivacyPolicyForRowImpl(viewer, options, row) {
@@ -1656,7 +1161,7 @@ async function getEdgeTypeInGroup(viewer, id1, id2, m) {
1656
1161
  tableToEdgeEnumMap.forEach((edgeEnums, tableName) => {
1657
1162
  promises.push((async () => {
1658
1163
  const edgeTypes = edgeEnums.map((edgeEnum) => m.get(edgeEnum));
1659
- const { cls, fields } = getEdgeClauseAndFields(clause.And(clause.Eq("id1", id1), clause.In("edge_type", edgeTypes), clause.Eq("id2", id2)), {});
1164
+ const { cls, fields } = getEdgeClauseAndFields(clause.And(clause.Eq("id1", id1), clause.UuidIn("edge_type", edgeTypes), clause.Eq("id2", id2)), {});
1660
1165
  const rows = await loadRows({
1661
1166
  tableName,
1662
1167
  fields,