@snowtop/ent 0.1.11 → 0.1.13

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 (47) hide show
  1. package/action/experimental_action.js +2 -2
  2. package/action/operations.js +4 -2
  3. package/core/base.d.ts +12 -10
  4. package/core/clause.d.ts +4 -3
  5. package/core/clause.js +98 -43
  6. package/core/config.d.ts +6 -0
  7. package/core/context.d.ts +6 -14
  8. package/core/context.js +9 -4
  9. package/core/db.js +1 -1
  10. package/core/ent.d.ts +1 -0
  11. package/core/ent.js +30 -11
  12. package/core/loaders/assoc_count_loader.js +1 -1
  13. package/core/loaders/assoc_edge_loader.d.ts +3 -0
  14. package/core/loaders/assoc_edge_loader.js +18 -0
  15. package/core/loaders/object_loader.js +2 -2
  16. package/core/loaders/query_loader.d.ts +3 -3
  17. package/core/loaders/query_loader.js +2 -1
  18. package/core/loaders/raw_count_loader.js +1 -1
  19. package/core/query/assoc_query.d.ts +22 -0
  20. package/core/query/assoc_query.js +101 -2
  21. package/core/query/custom_query.js +2 -9
  22. package/core/query/query.d.ts +1 -0
  23. package/core/query/query.js +72 -11
  24. package/core/query/shared_assoc_test.js +404 -7
  25. package/core/query/shared_test.js +9 -37
  26. package/core/query_impl.d.ts +2 -1
  27. package/core/query_impl.js +25 -7
  28. package/graphql/query/edge_connection.js +2 -2
  29. package/package.json +2 -2
  30. package/parse_schema/parse.d.ts +4 -3
  31. package/parse_schema/parse.js +4 -3
  32. package/schema/base_schema.d.ts +3 -0
  33. package/schema/base_schema.js +2 -0
  34. package/schema/schema.d.ts +1 -0
  35. package/schema/struct_field.d.ts +4 -2
  36. package/schema/struct_field.js +33 -4
  37. package/scripts/custom_graphql.js +1 -1
  38. package/testutils/builder.d.ts +1 -1
  39. package/testutils/builder.js +4 -4
  40. package/testutils/ent-graphql-tests/index.js +2 -2
  41. package/testutils/fake_data/fake_contact.js +1 -1
  42. package/testutils/fake_data/fake_event.js +1 -1
  43. package/testutils/fake_data/test_helpers.js +2 -2
  44. package/testutils/fake_data/user_query.js +1 -1
  45. package/testutils/query.d.ts +9 -0
  46. package/testutils/query.js +45 -0
  47. package/testutils/write.js +3 -3
@@ -64,11 +64,11 @@ class BaseAction {
64
64
  }
65
65
  async save() {
66
66
  await this.builder.save();
67
- return await this.builder.editedEnt();
67
+ return this.builder.editedEnt();
68
68
  }
69
69
  async saveX() {
70
70
  await this.builder.saveX();
71
- return await this.builder.editedEntX();
71
+ return this.builder.editedEntX();
72
72
  }
73
73
  getInput() {
74
74
  return this.input;
@@ -358,7 +358,7 @@ class EdgeOperation {
358
358
  async performDeleteWrite(q, edgeData, edge, context) {
359
359
  const params = this.getDeleteRowParams(edgeData, edge, context);
360
360
  if (params.op === schema_1.SQLStatementOperation.Delete) {
361
- return (0, ent_1.deleteRows)(q, params.options, params.clause);
361
+ return (0, ent_1.deleteRows)(q, { ...params.options, context }, params.clause);
362
362
  }
363
363
  else {
364
364
  if (params.op !== schema_1.SQLStatementOperation.Update) {
@@ -369,13 +369,14 @@ class EdgeOperation {
369
369
  whereClause: params.clause,
370
370
  fields: params.updateData,
371
371
  fieldsToLog: params.updateData,
372
+ context,
372
373
  });
373
374
  }
374
375
  }
375
376
  performDeleteWriteSync(q, edgeData, edge, context) {
376
377
  const params = this.getDeleteRowParams(edgeData, edge, context);
377
378
  if (params.op === schema_1.SQLStatementOperation.Delete) {
378
- return (0, ent_1.deleteRowsSync)(q, params.options, params.clause);
379
+ return (0, ent_1.deleteRowsSync)(q, { ...params.options, context }, params.clause);
379
380
  }
380
381
  else {
381
382
  if (params.op !== schema_1.SQLStatementOperation.Update) {
@@ -385,6 +386,7 @@ class EdgeOperation {
385
386
  tableName: params.options.tableName,
386
387
  whereClause: params.clause,
387
388
  fields: params.updateData,
389
+ context,
388
390
  });
389
391
  }
390
392
  }
package/core/base.d.ts CHANGED
@@ -26,21 +26,16 @@ export interface PrimableLoader<K, V> extends Loader<K, V> {
26
26
  prime(d: V): void;
27
27
  primeAll?(d: V): void;
28
28
  }
29
+ export type QueryOptions = Required<Pick<LoadRowsOptions, "clause" | "fields" | "tableName">> & Pick<LoadRowsOptions, "orderby" | "join">;
29
30
  interface cache {
30
31
  getLoader<K, V>(name: string, create: () => Loader<K, V>): Loader<K, V>;
31
32
  getLoaderWithLoadMany<K, V>(name: string, create: () => LoaderWithLoadMany<K, V>): LoaderWithLoadMany<K, V>;
32
- getCachedRows(options: queryOptions): Data[] | null;
33
- getCachedRow(options: queryOptions): Data | null;
34
- primeCache(options: queryOptions, rows: Data[]): void;
35
- primeCache(options: queryOptions, rows: Data): void;
33
+ getCachedRows(options: QueryOptions): Data[] | null;
34
+ getCachedRow(options: QueryOptions): Data | null;
35
+ primeCache(options: QueryOptions, rows: Data[]): void;
36
+ primeCache(options: QueryOptions, rows: Data): void;
36
37
  clearCache(): void;
37
38
  }
38
- interface queryOptions {
39
- fields: string[];
40
- tableName: string;
41
- clause: clause.Clause;
42
- orderby?: OrderBy;
43
- }
44
39
  export interface Context<TViewer extends Viewer = Viewer> {
45
40
  getViewer(): TViewer;
46
41
  cache?: cache;
@@ -67,6 +62,7 @@ export interface EntConstructor<TEnt extends Ent, TViewer extends Viewer = Viewe
67
62
  export type ID = string | number;
68
63
  export interface DataOptions {
69
64
  tableName: string;
65
+ alias?: string;
70
66
  context?: Context;
71
67
  }
72
68
  export interface SelectBaseDataOptions extends DataOptions {
@@ -79,6 +75,11 @@ export interface SelectDataOptions extends SelectBaseDataOptions {
79
75
  }
80
76
  export interface QueryableDataOptions extends SelectBaseDataOptions, QueryDataOptions {
81
77
  }
78
+ interface JoinOptions<T2 extends Data = Data, K2 = keyof T2> {
79
+ tableName: string;
80
+ alias?: string;
81
+ clause: clause.Clause<T2, K2>;
82
+ }
82
83
  export interface QueryDataOptions<T extends Data = Data, K = keyof T> {
83
84
  distinct?: boolean;
84
85
  clause: clause.Clause<T, K>;
@@ -86,6 +87,7 @@ export interface QueryDataOptions<T extends Data = Data, K = keyof T> {
86
87
  groupby?: K;
87
88
  limit?: number;
88
89
  disableTransformations?: boolean;
90
+ join?: JoinOptions;
89
91
  }
90
92
  export interface LoadRowOptions extends QueryableDataOptions {
91
93
  }
package/core/clause.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Data, ID, SelectDataOptions } from "./base";
2
2
  export interface Clause<T extends Data = Data, K = keyof T> {
3
- clause(idx: number): string;
3
+ clause(idx: number, alias?: string): string;
4
4
  columns(): K[];
5
5
  values(): any[];
6
6
  instanceKey(): string;
@@ -19,7 +19,7 @@ export declare class inClause<T extends Data, K = keyof T> implements Clause<T,
19
19
  protected op: InClauseOperator;
20
20
  static getPostgresInClauseValuesThreshold(): number;
21
21
  constructor(col: K, value: any[], type?: string);
22
- clause(idx: number): string;
22
+ clause(idx: number, alias?: string): string;
23
23
  columns(): K[];
24
24
  values(): any[];
25
25
  logValues(): any[];
@@ -124,5 +124,6 @@ export declare function Subtract<T extends Data, K = keyof T>(col: K, value: any
124
124
  export declare function Multiply<T extends Data, K = keyof T>(col: K, value: any): Clause<T, K>;
125
125
  export declare function Divide<T extends Data, K = keyof T>(col: K, value: any): Clause<T, K>;
126
126
  export declare function Modulo<T extends Data, K = keyof T>(col: K, value: any): Clause<T, K>;
127
- export declare function getCombinedClause<V extends Data = Data, K = keyof V>(options: Omit<SelectDataOptions, "key">, cls: Clause<V, K>): Clause<V, K>;
127
+ export declare function getCombinedClause<V extends Data = Data, K = keyof V>(options: Pick<SelectDataOptions, "clause">, cls: Clause<V, K>, checkIntersection?: boolean): Clause<V, K>;
128
+ export declare function Expression<T extends Data, K = keyof T>(expression: string): Clause<T, K>;
128
129
  export {};
package/core/clause.js CHANGED
@@ -24,7 +24,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.Modulo = exports.Divide = exports.Multiply = exports.Subtract = exports.Add = exports.PaginationMultipleColsSubQuery = exports.JSONKeyInList = exports.JSONBKeyInList = exports.JSONKeyExists = exports.JSONPathValuePredicate = exports.JSONObjectFieldKeyAsText = exports.JSONObjectFieldKeyASJSON = exports.sensitiveValue = exports.TsVectorWebsearchToTsQuery = exports.TsVectorPhraseToTsQuery = exports.TsVectorPlainToTsQuery = exports.TsVectorColTsQuery = exports.WebsearchToTsQuery = exports.PhraseToTsQuery = exports.PlainToTsQuery = exports.TsQuery = exports.DBTypeNotIn = exports.TextNotIn = exports.IntegerNotIn = exports.UuidNotIn = exports.DBTypeIn = exports.TextIn = exports.IntegerIn = exports.UuidIn = exports.In = exports.OrOptional = exports.Or = exports.AndOptional = exports.And = exports.LessEq = exports.GreaterEq = exports.Less = exports.Greater = exports.NotEq = exports.Eq = exports.ArrayNotEq = exports.ArrayEq = exports.PostgresArrayNotOverlaps = exports.PostgresArrayOverlaps = exports.PostgresArrayNotContains = exports.PostgresArrayNotContainsValue = exports.PostgresArrayContains = exports.PostgresArrayContainsValue = exports.notInClause = exports.inClause = void 0;
27
- exports.getCombinedClause = void 0;
27
+ exports.Expression = exports.getCombinedClause = void 0;
28
28
  const db_1 = __importStar(require("./db"));
29
29
  const query_impl_1 = require("./query_impl");
30
30
  function isSensitive(val) {
@@ -38,6 +38,12 @@ function rawValue(val) {
38
38
  }
39
39
  return val;
40
40
  }
41
+ function renderCol(col, alias) {
42
+ if (alias) {
43
+ return `${alias}.${col}`;
44
+ }
45
+ return col;
46
+ }
41
47
  class simpleClause {
42
48
  constructor(col, value, op, handleNull) {
43
49
  this.col = col;
@@ -45,15 +51,15 @@ class simpleClause {
45
51
  this.op = op;
46
52
  this.handleNull = handleNull;
47
53
  }
48
- clause(idx) {
54
+ clause(idx, alias) {
49
55
  const nullClause = this.nullClause();
50
56
  if (nullClause) {
51
- return nullClause.clause(idx);
57
+ return nullClause.clause(idx, alias);
52
58
  }
53
59
  if (db_1.default.getDialect() === db_1.Dialect.Postgres) {
54
- return `${this.col} ${this.op} $${idx}`;
60
+ return `${renderCol(this.col, alias)} ${this.op} $${idx}`;
55
61
  }
56
- return `${this.col} ${this.op} ?`;
62
+ return `${renderCol(this.col, alias)} ${this.op} ?`;
57
63
  }
58
64
  nullClause() {
59
65
  if (!this.handleNull || this.value !== null) {
@@ -92,13 +98,17 @@ class simpleClause {
92
98
  return `${this.col}${this.op}${rawValue(this.value)}`;
93
99
  }
94
100
  }
101
+ // NB: we're not using alias in this class in clause method
102
+ // if we end up with a subclass that does, we need to handle it
95
103
  class queryClause {
96
- constructor(dependentQueryOptions) {
104
+ constructor(dependentQueryOptions, // private value: any, // private op: string, // private handleNull?: Clause<T, K>,
105
+ prefix) {
97
106
  this.dependentQueryOptions = dependentQueryOptions;
107
+ this.prefix = prefix;
98
108
  }
99
- clause(idx) {
109
+ clause(idx, alias) {
100
110
  const q = (0, query_impl_1.buildQuery)(this.dependentQueryOptions);
101
- return `EXISTS (${q})`;
111
+ return `${this.prefix} (${q})`;
102
112
  }
103
113
  columns() {
104
114
  // @ts-ignore
@@ -111,15 +121,21 @@ class queryClause {
111
121
  return this.dependentQueryOptions.clause.logValues();
112
122
  }
113
123
  instanceKey() {
114
- return `exists:${this.dependentQueryOptions.tableName}:${this.dependentQueryOptions.clause.instanceKey()}`;
124
+ return `${this.prefix.toLowerCase()}:${this.dependentQueryOptions.tableName}:${this.dependentQueryOptions.clause.instanceKey()}`;
125
+ }
126
+ }
127
+ class existsQueryClause extends queryClause {
128
+ constructor(dependentQueryOptions) {
129
+ super(dependentQueryOptions, "EXISTS");
130
+ this.dependentQueryOptions = dependentQueryOptions;
115
131
  }
116
132
  }
117
133
  class isNullClause {
118
134
  constructor(col) {
119
135
  this.col = col;
120
136
  }
121
- clause(_idx) {
122
- return `${this.col} IS NULL`;
137
+ clause(_idx, alias) {
138
+ return `${renderCol(this.col, alias)} IS NULL`;
123
139
  }
124
140
  columns() {
125
141
  return [];
@@ -138,8 +154,8 @@ class isNotNullClause {
138
154
  constructor(col) {
139
155
  this.col = col;
140
156
  }
141
- clause(idx) {
142
- return `${this.col} IS NOT NULL`;
157
+ clause(idx, alias) {
158
+ return `${renderCol(this.col, alias)} IS NOT NULL`;
143
159
  }
144
160
  columns() {
145
161
  return [];
@@ -154,17 +170,37 @@ class isNotNullClause {
154
170
  return `${this.col} IS NOT NULL`;
155
171
  }
156
172
  }
173
+ class simpleExpression {
174
+ constructor(expression) {
175
+ this.expression = expression;
176
+ }
177
+ clause(idx, alias) {
178
+ return this.expression;
179
+ }
180
+ columns() {
181
+ return [];
182
+ }
183
+ values() {
184
+ return [];
185
+ }
186
+ logValues() {
187
+ return [];
188
+ }
189
+ instanceKey() {
190
+ return `${this.expression}`;
191
+ }
192
+ }
157
193
  class arraySimpleClause {
158
194
  constructor(col, value, op) {
159
195
  this.col = col;
160
196
  this.value = value;
161
197
  this.op = op;
162
198
  }
163
- clause(idx) {
199
+ clause(idx, alias) {
164
200
  if (db_1.default.getDialect() === db_1.Dialect.Postgres) {
165
- return `$${idx} ${this.op} ANY(${this.col})`;
201
+ return `$${idx} ${this.op} ANY(${renderCol(this.col, alias)})`;
166
202
  }
167
- return `${this.col} ${this.op} ?`;
203
+ return `${renderCol(this.col, alias)} ${this.op} ?`;
168
204
  }
169
205
  columns() {
170
206
  return [this.col];
@@ -192,12 +228,12 @@ class postgresArrayOperator {
192
228
  this.op = op;
193
229
  this.not = not;
194
230
  }
195
- clause(idx) {
231
+ clause(idx, alias) {
196
232
  if (db_1.default.getDialect() === db_1.Dialect.Postgres) {
197
233
  if (this.not) {
198
- return `NOT ${this.col} ${this.op} $${idx}`;
234
+ return `NOT ${renderCol(this.col, alias)} ${this.op} $${idx}`;
199
235
  }
200
- return `${this.col} ${this.op} $${idx}`;
236
+ return `${renderCol(this.col, alias)} ${this.op} $${idx}`;
201
237
  }
202
238
  throw new Error(`not supported`);
203
239
  }
@@ -262,14 +298,14 @@ class inClause {
262
298
  this.type = type;
263
299
  this.op = "IN";
264
300
  }
265
- clause(idx) {
301
+ clause(idx, alias) {
266
302
  // do a simple = when only one item
267
303
  if (this.value.length === 1) {
268
304
  if (this.op === "IN") {
269
- return new simpleClause(this.col, this.value[0], "=").clause(idx);
305
+ return new simpleClause(this.col, this.value[0], "=").clause(idx, alias);
270
306
  }
271
307
  else {
272
- return new simpleClause(this.col, this.value[0], "!=").clause(idx);
308
+ return new simpleClause(this.col, this.value[0], "!=").clause(idx, alias);
273
309
  }
274
310
  }
275
311
  const postgres = db_1.default.getDialect() === db_1.Dialect.Postgres;
@@ -302,7 +338,7 @@ class inClause {
302
338
  if (postgresValuesList) {
303
339
  inValue = `VALUES${inValue}`;
304
340
  }
305
- return `${this.col} ${this.op} (${inValue})`;
341
+ return `${renderCol(this.col, alias)} ${this.op} (${inValue})`;
306
342
  // TODO we need to return idx at end to query builder...
307
343
  // or anything that's doing a composite query so next clause knows where to start
308
344
  // or change to a sqlx.Rebind format
@@ -343,10 +379,10 @@ class compositeClause {
343
379
  this.sep = sep;
344
380
  this.compositeOp = this.sep;
345
381
  }
346
- clause(idx) {
382
+ clause(idx, alias) {
347
383
  let clauses = [];
348
384
  for (const clause of this.clauses) {
349
- let cls = clause.clause(idx);
385
+ let cls = clause.clause(idx, alias);
350
386
  // if composite clause and a different op, add parens so that we enforce order of precedence
351
387
  if (clause.compositeOp && clause.compositeOp !== this.sep) {
352
388
  cls = `(${cls})`;
@@ -408,16 +444,16 @@ class tsQueryClause {
408
444
  value: this.val,
409
445
  };
410
446
  }
411
- clause(idx) {
447
+ clause(idx, alias) {
412
448
  const { language } = this.getInfo();
413
449
  if (db_1.Dialect.Postgres === db_1.default.getDialect()) {
414
450
  if (this.tsVectorCol) {
415
- return `to_tsvector(${this.col}) @@ ${this.getFunction()}('${language}', $${idx})`;
451
+ return `to_tsvector(${renderCol(this.col, alias)}) @@ ${this.getFunction()}('${language}', $${idx})`;
416
452
  }
417
- return `${this.col} @@ ${this.getFunction()}('${language}', $${idx})`;
453
+ return `${renderCol(this.col, alias)} @@ ${this.getFunction()}('${language}', $${idx})`;
418
454
  }
419
455
  // FYI this doesn't actually work for sqlite since different
420
- return `${this.col} @@ ${this.getFunction()}('${language}', ?)`;
456
+ return `${renderCol(this.col, alias)} @@ ${this.getFunction()}('${language}', ?)`;
421
457
  }
422
458
  columns() {
423
459
  return [this.col];
@@ -716,11 +752,11 @@ class jSONPathValuePredicateClause {
716
752
  this.value = value;
717
753
  this.pred = pred;
718
754
  }
719
- clause(idx) {
755
+ clause(idx, alias) {
720
756
  if (db_1.default.getDialect() !== db_1.Dialect.Postgres) {
721
757
  throw new Error(`not supported`);
722
758
  }
723
- return `${this.col} @@ $${idx}`;
759
+ return `${renderCol(this.col, alias)} @@ $${idx}`;
724
760
  }
725
761
  columns() {
726
762
  return [this.col];
@@ -762,7 +798,7 @@ function JSONBKeyInList(dbCol, jsonCol, val) {
762
798
  // @ts-ignore
763
799
  Eq(JSONObjectFieldKeyAsText("json_element", jsonCol), val)),
764
800
  };
765
- return new queryClause(opts);
801
+ return new existsQueryClause(opts);
766
802
  }
767
803
  exports.JSONBKeyInList = JSONBKeyInList;
768
804
  function JSONKeyInList(dbCol, jsonCol, val) {
@@ -774,7 +810,7 @@ function JSONKeyInList(dbCol, jsonCol, val) {
774
810
  // @ts-ignore
775
811
  Eq(JSONObjectFieldKeyAsText("json_element", jsonCol), val)),
776
812
  };
777
- return new queryClause(opts);
813
+ return new existsQueryClause(opts);
778
814
  }
779
815
  exports.JSONKeyInList = JSONKeyInList;
780
816
  // TODO need a better name for this lol
@@ -787,15 +823,15 @@ class paginationMultipleColumnsSubQueryClause {
787
823
  this.uniqueCol = uniqueCol;
788
824
  this.val = val;
789
825
  }
790
- buildSimpleQuery(clause, idx) {
791
- return `SELECT ${this.col} FROM ${this.tableName} WHERE ${clause.clause(idx)}`;
826
+ buildSimpleQuery(clause, idx, alias) {
827
+ return `SELECT ${renderCol(this.col, alias)} FROM ${this.tableName} WHERE ${clause.clause(idx, alias)}`;
792
828
  }
793
- clause(idx) {
794
- const eq1 = this.buildSimpleQuery(Eq(this.uniqueCol, this.val), idx);
795
- const eq2 = this.buildSimpleQuery(Eq(this.uniqueCol, this.val), idx + 1);
796
- const op = new simpleClause(this.uniqueCol, this.val, this.op).clause(idx + 2);
829
+ clause(idx, alias) {
830
+ const eq1 = this.buildSimpleQuery(Eq(this.uniqueCol, this.val), idx, alias);
831
+ const eq2 = this.buildSimpleQuery(Eq(this.uniqueCol, this.val), idx + 1, alias);
832
+ const op = new simpleClause(this.uniqueCol, this.val, this.op).clause(idx + 2, alias);
797
833
  // nest in () to make sure it's scoped correctly
798
- return `(${this.col} ${this.op} (${eq1}) OR (${this.col} = (${eq2}) AND ${op}))`;
834
+ return `(${renderCol(this.col, alias)} ${this.op} (${eq1}) OR (${renderCol(this.col, alias)} = (${eq2}) AND ${op}))`;
799
835
  }
800
836
  columns() {
801
837
  return [this.col];
@@ -836,7 +872,7 @@ function Modulo(col, value) {
836
872
  return new simpleClause(col, value, "%", new isNullClause(col));
837
873
  }
838
874
  exports.Modulo = Modulo;
839
- function getCombinedClause(options, cls) {
875
+ function getCombinedClause(options, cls, checkIntersection = false) {
840
876
  if (options.clause) {
841
877
  let optionClause;
842
878
  if (typeof options.clause === "function") {
@@ -846,10 +882,29 @@ function getCombinedClause(options, cls) {
846
882
  optionClause = options.clause;
847
883
  }
848
884
  if (optionClause) {
849
- // @ts-expect-error different types
850
- cls = And(cls, optionClause);
885
+ let and = true;
886
+ if (checkIntersection) {
887
+ // this should be the smaller one
888
+ const transformedCols = new Set(optionClause.columns());
889
+ const queriedCols = cls.columns();
890
+ const has = new Set();
891
+ for (const col of queriedCols) {
892
+ if (transformedCols.has(col)) {
893
+ has.add(col);
894
+ }
895
+ }
896
+ and = transformedCols.size > 0 && has.size !== transformedCols.size;
897
+ }
898
+ if (and) {
899
+ // @ts-expect-error different types
900
+ cls = And(cls, optionClause);
901
+ }
851
902
  }
852
903
  }
853
904
  return cls;
854
905
  }
855
906
  exports.getCombinedClause = getCombinedClause;
907
+ function Expression(expression) {
908
+ return new simpleExpression(expression);
909
+ }
910
+ exports.Expression = Expression;
package/core/config.d.ts CHANGED
@@ -23,10 +23,15 @@ export interface Config {
23
23
  }
24
24
  export interface ConfigWithCodegen extends Config {
25
25
  codegen?: CodegenConfig;
26
+ databaseMigration?: DatabaseMigrationConfig;
26
27
  customGraphQLJSONPath?: string;
27
28
  dynamicScriptCustomGraphQLJSONPath?: string;
28
29
  globalSchemaPath?: string;
29
30
  }
31
+ interface DatabaseMigrationConfig {
32
+ custom_sql_include?: string[];
33
+ custom_sql_exclude?: string[];
34
+ }
30
35
  interface CodegenConfig {
31
36
  defaultEntPolicy?: PrivacyConfig;
32
37
  defaultActionPolicy?: PrivacyConfig;
@@ -45,6 +50,7 @@ interface CodegenConfig {
45
50
  customAssocEdgePath?: importedObject;
46
51
  globalImportPath?: string;
47
52
  userOveriddenFiles?: string[];
53
+ transformLoadMethod?: string;
48
54
  transformDeleteMethod?: string;
49
55
  }
50
56
  interface PrettierConfig {
package/core/context.d.ts CHANGED
@@ -1,9 +1,7 @@
1
1
  /// <reference types="node" />
2
- import { Viewer, Data, Loader, LoaderWithLoadMany } from "./base";
2
+ import { Viewer, Data, Loader, LoaderWithLoadMany, QueryOptions } from "./base";
3
3
  import { IncomingMessage, ServerResponse } from "http";
4
- import * as clause from "./clause";
5
4
  import { Context } from "./base";
6
- import { OrderBy } from "./query_impl";
7
5
  export interface RequestContext<TViewer extends Viewer = Viewer> extends Context<TViewer> {
8
6
  authViewer(viewer: TViewer): Promise<void>;
9
7
  logout(): Promise<void>;
@@ -13,21 +11,15 @@ export interface RequestContext<TViewer extends Viewer = Viewer> extends Context
13
11
  export declare class ContextCache {
14
12
  loaders: Map<string, Loader<any, any>>;
15
13
  loaderWithLoadMany: Map<string, LoaderWithLoadMany<any, any>>;
14
+ discardedLoaders: Loader<any, any>[];
16
15
  getLoader<K, V>(name: string, create: () => Loader<K, V>): Loader<K, V>;
17
16
  getLoaderWithLoadMany<K, V>(name: string, create: () => LoaderWithLoadMany<K, V>): LoaderWithLoadMany<K, V>;
18
17
  private itemMap;
19
18
  private listMap;
20
19
  private getkey;
21
- getCachedRows(options: queryOptions): Data[] | null;
22
- getCachedRow(options: queryOptions): Data | null;
23
- primeCache(options: queryOptions, rows: Data[]): void;
24
- primeCache(options: queryOptions, rows: Data): void;
20
+ getCachedRows(options: QueryOptions): Data[] | null;
21
+ getCachedRow(options: QueryOptions): Data | null;
22
+ primeCache(options: QueryOptions, rows: Data[]): void;
23
+ primeCache(options: QueryOptions, rows: Data): void;
25
24
  clearCache(): void;
26
25
  }
27
- interface queryOptions {
28
- fields: string[];
29
- tableName: string;
30
- clause: clause.Clause;
31
- orderby?: OrderBy;
32
- }
33
- export {};
package/core/context.js CHANGED
@@ -8,6 +8,9 @@ class ContextCache {
8
8
  this.loaders = new Map();
9
9
  // we should eventually combine the two but better for typing to be separate for now
10
10
  this.loaderWithLoadMany = new Map();
11
+ // keep track of discarded loaders in case someone ends up holding onto a reference
12
+ // so that we can call clearAll() on them
13
+ this.discardedLoaders = [];
11
14
  // we have a per-table map to make it easier to purge and have less things to compare with
12
15
  this.itemMap = new Map();
13
16
  this.listMap = new Map();
@@ -42,6 +45,9 @@ class ContextCache {
42
45
  if (options.orderby) {
43
46
  parts.push((0, query_impl_1.getOrderByPhrase)(options.orderby));
44
47
  }
48
+ if (options.join) {
49
+ parts.push((0, query_impl_1.getJoinPhrase)(options.join));
50
+ }
45
51
  return parts.join(",");
46
52
  }
47
53
  getCachedRows(options) {
@@ -87,15 +93,14 @@ class ContextCache {
87
93
  }
88
94
  }
89
95
  clearCache() {
96
+ this.discardedLoaders.forEach((l) => l.clearAll());
90
97
  for (const [_key, loader] of this.loaders) {
91
- // may not need this since we're clearing the loaders themselves...
92
- // but may have some benefits by explicitily doing so?
93
98
  loader.clearAll();
99
+ this.discardedLoaders.push(loader);
94
100
  }
95
101
  for (const [_key, loader] of this.loaderWithLoadMany) {
96
- // may not need this since we're clearing the loaders themselves...
97
- // but may have some benefits by explicitily doing so?
98
102
  loader.clearAll();
103
+ this.discardedLoaders.push(loader);
99
104
  }
100
105
  this.loaders.clear();
101
106
  this.loaderWithLoadMany.clear();
package/core/db.js CHANGED
@@ -324,7 +324,7 @@ class Postgres {
324
324
  };
325
325
  }
326
326
  async close() {
327
- return await this.pool.end();
327
+ return this.pool.end();
328
328
  }
329
329
  }
330
330
  exports.Postgres = Postgres;
package/core/ent.d.ts CHANGED
@@ -175,6 +175,7 @@ interface loadEdgeForIDOptions<T extends AssocEdge> extends loadCustomEdgesOptio
175
175
  id2: ID;
176
176
  }
177
177
  export declare function loadEdgeForID2<T extends AssocEdge>(options: loadEdgeForIDOptions<T>): Promise<T | undefined>;
178
+ export declare function loadTwoWayEdges<T extends AssocEdge>(opts: loadCustomEdgesOptions<T>): Promise<T[]>;
178
179
  export declare function loadNodesByEdge<T extends Ent>(viewer: Viewer, id1: ID, edgeType: string, options: LoadEntOptions<T>): Promise<T[]>;
179
180
  export declare function applyPrivacyPolicyForRow<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, options: LoadEntOptions<TEnt, TViewer>, row: Data): Promise<TEnt | null>;
180
181
  export declare function applyPrivacyPolicyForRows<TEnt extends Ent<TViewer>, TViewer extends Viewer>(viewer: TViewer, rows: Data[], options: LoadEntOptions<TEnt, TViewer>): Promise<TEnt[]>;
package/core/ent.js CHANGED
@@ -26,8 +26,8 @@ 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.applyPrivacyPolicyForRows = 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.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 = void 0;
29
+ exports.applyPrivacyPolicyForRow = exports.loadNodesByEdge = exports.loadTwoWayEdges = 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.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"));
@@ -301,7 +301,7 @@ async function loadEntXFromClause(viewer, options, clause) {
301
301
  context: viewer.context,
302
302
  };
303
303
  const row = await loadRowX(rowOptions);
304
- return await applyPrivacyPolicyForRowX(viewer, options, row);
304
+ return applyPrivacyPolicyForRowX(viewer, options, row);
305
305
  }
306
306
  exports.loadEntXFromClause = loadEntXFromClause;
307
307
  async function loadEnts(viewer, options, ...ids) {
@@ -447,7 +447,7 @@ async function loadCustomDataImpl(options, query, context) {
447
447
  // this will have rudimentary caching but nothing crazy
448
448
  let cls = query.clause;
449
449
  if (!query.disableTransformations) {
450
- cls = clause.getCombinedClause(options.loaderFactory.options, query.clause);
450
+ cls = clause.getCombinedClause(options.loaderFactory.options, query.clause, true);
451
451
  }
452
452
  return loadRows({
453
453
  ...query,
@@ -474,7 +474,7 @@ exports.loadDerivedEnt = loadDerivedEnt;
474
474
  // won't have caching yet either
475
475
  async function loadDerivedEntX(viewer, data, loader) {
476
476
  const ent = new loader(viewer, data);
477
- return await applyPrivacyPolicyForEntX(viewer, ent, data, { ent: loader });
477
+ return applyPrivacyPolicyForEntX(viewer, ent, data, { ent: loader });
478
478
  }
479
479
  exports.loadDerivedEntX = loadDerivedEntX;
480
480
  // everything calls into this two so should be fine
@@ -978,7 +978,7 @@ function getEdgeClauseAndFields(cls, options) {
978
978
  }
979
979
  exports.getEdgeClauseAndFields = getEdgeClauseAndFields;
980
980
  async function loadCustomEdges(options) {
981
- const { cls: actualClause, fields, defaultOptions, tableName, } = await loadEgesInfo(options);
981
+ const { cls: actualClause, fields, defaultOptions, tableName, } = await loadEdgesInfo(options);
982
982
  const rows = await loadRows({
983
983
  tableName,
984
984
  fields: fields,
@@ -992,7 +992,7 @@ async function loadCustomEdges(options) {
992
992
  });
993
993
  }
994
994
  exports.loadCustomEdges = loadCustomEdges;
995
- async function loadEgesInfo(options, id2) {
995
+ async function loadEdgesInfo(options, id2) {
996
996
  const { id1, edgeType } = options;
997
997
  const edgeData = await loadEdgeData(edgeType);
998
998
  if (!edgeData) {
@@ -1037,7 +1037,7 @@ async function loadUniqueNode(viewer, id1, edgeType, options) {
1037
1037
  if (!edge) {
1038
1038
  return null;
1039
1039
  }
1040
- return await loadEnt(viewer, edge.id2, options);
1040
+ return loadEnt(viewer, edge.id2, options);
1041
1041
  }
1042
1042
  exports.loadUniqueNode = loadUniqueNode;
1043
1043
  async function loadRawEdgeCountX(options) {
@@ -1058,10 +1058,10 @@ async function loadRawEdgeCountX(options) {
1058
1058
  }
1059
1059
  exports.loadRawEdgeCountX = loadRawEdgeCountX;
1060
1060
  async function loadEdgeForID2(options) {
1061
- const { cls: actualClause, fields, tableName, } = await loadEgesInfo(options, options.id2);
1061
+ const { cls: actualClause, fields, tableName, } = await loadEdgesInfo(options, options.id2);
1062
1062
  const row = await loadRow({
1063
1063
  tableName,
1064
- fields: fields,
1064
+ fields,
1065
1065
  clause: actualClause,
1066
1066
  context: options.context,
1067
1067
  });
@@ -1070,6 +1070,25 @@ async function loadEdgeForID2(options) {
1070
1070
  }
1071
1071
  }
1072
1072
  exports.loadEdgeForID2 = loadEdgeForID2;
1073
+ async function loadTwoWayEdges(opts) {
1074
+ const { cls: actualClause, fields, tableName } = await loadEdgesInfo(opts);
1075
+ const rows = await loadRows({
1076
+ tableName,
1077
+ alias: "t1",
1078
+ fields,
1079
+ clause: actualClause,
1080
+ context: opts.context,
1081
+ join: {
1082
+ tableName,
1083
+ alias: "t2",
1084
+ clause: clause.And(
1085
+ // these are not values so need this to not think they're values...
1086
+ clause.Expression("t1.id1 = t2.id2"), clause.Expression("t1.id2 = t2.id1")),
1087
+ },
1088
+ });
1089
+ return rows;
1090
+ }
1091
+ exports.loadTwoWayEdges = loadTwoWayEdges;
1073
1092
  async function loadNodesByEdge(viewer, id1, edgeType, options) {
1074
1093
  // load edges
1075
1094
  const rows = await loadEdges({
@@ -1093,7 +1112,7 @@ async function applyPrivacyPolicyForRowImpl(viewer, options, row) {
1093
1112
  }
1094
1113
  async function applyPrivacyPolicyForRowX(viewer, options, row) {
1095
1114
  const ent = new options.ent(viewer, row);
1096
- return await applyPrivacyPolicyForEntX(viewer, ent, row, options);
1115
+ return applyPrivacyPolicyForEntX(viewer, ent, row, options);
1097
1116
  }
1098
1117
  // deprecated. doesn't use entcache
1099
1118
  async function applyPrivacyPolicyForRowsDeprecated(viewer, rows, options) {