@entity-access/entity-access 1.0.2 → 1.0.6

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 (69) hide show
  1. package/.github/workflows/node.yml +7 -2
  2. package/.vscode/settings.json +1 -0
  3. package/README.md +57 -27
  4. package/package.json +2 -2
  5. package/src/common/EntityAccessError.ts +10 -0
  6. package/src/common/IDisposable.ts +25 -0
  7. package/src/common/ImmutableObject.ts +53 -0
  8. package/src/common/Logger.ts +59 -0
  9. package/src/common/TypeInfo.ts +3 -0
  10. package/src/common/cache/TimedCache.ts +2 -2
  11. package/src/common/usingAsync.ts +42 -12
  12. package/src/compiler/QueryCompiler.ts +28 -30
  13. package/src/compiler/postgres/PostgreSqlMethodTransformer.ts +23 -0
  14. package/src/compiler/sql-server/SqlServerSqlMethodTransformer.ts +23 -0
  15. package/src/decorators/ForeignKey.ts +1 -1
  16. package/src/decorators/IClassOf.ts +2 -1
  17. package/src/decorators/IColumn.ts +2 -0
  18. package/src/decorators/parser/NameParser.ts +15 -0
  19. package/src/di/di.ts +224 -0
  20. package/src/drivers/base/BaseDriver.ts +28 -3
  21. package/src/drivers/sql-server/ExpressionToSqlServer.ts +34 -9
  22. package/src/drivers/sql-server/SqlServerDriver.ts +7 -16
  23. package/src/entity-query/EntityType.ts +45 -3
  24. package/src/model/EntityContext.ts +167 -22
  25. package/src/model/EntityQuery.ts +118 -59
  26. package/src/model/EntitySource.ts +38 -46
  27. package/src/model/IFilterWithParameter.ts +5 -0
  28. package/src/model/SourceExpression.ts +21 -25
  29. package/src/model/{ChangeEntry.ts → changes/ChangeEntry.ts} +35 -5
  30. package/src/model/{ChangeSet.ts → changes/ChangeSet.ts} +16 -11
  31. package/src/model/events/ContextEvents.ts +26 -0
  32. package/src/model/events/EntityEvents.ts +96 -0
  33. package/src/model/{IdentityService.ts → identity/IdentityService.ts} +9 -1
  34. package/src/model/identity/RelationMapper.ts +71 -0
  35. package/src/model/symbols.ts +1 -0
  36. package/src/model/verification/VerificationSession.ts +173 -0
  37. package/src/query/ast/DebugStringVisitor.ts +175 -0
  38. package/src/query/ast/ExpressionToSql.ts +277 -119
  39. package/src/query/ast/Expressions.ts +130 -13
  40. package/src/query/ast/IStringTransformer.ts +19 -5
  41. package/src/query/ast/ParameterScope.ts +97 -0
  42. package/src/query/ast/ReplaceParameter.ts +40 -0
  43. package/src/query/ast/Types.ts +0 -0
  44. package/src/query/ast/Visitor.ts +26 -5
  45. package/src/query/expander/QueryExpander.ts +147 -0
  46. package/src/query/parser/ArrowToExpression.ts +134 -19
  47. package/src/query/parser/BabelVisitor.ts +31 -43
  48. package/src/query/parser/NotSupportedError.ts +5 -0
  49. package/src/query/parser/Restructure.ts +66 -0
  50. package/src/query/parser/TransformVisitor.ts +83 -0
  51. package/src/sql/ISql.ts +10 -0
  52. package/src/tests/db-tests/tests/select-items.ts +12 -0
  53. package/src/tests/expressions/left-joins/child-joins.ts +54 -34
  54. package/src/tests/expressions/sanitize/sanitize-test.ts +17 -0
  55. package/src/tests/expressions/select/select.ts +24 -0
  56. package/src/tests/expressions/simple/parse-arrow.ts +10 -0
  57. package/src/tests/model/ShoppingContext.ts +7 -3
  58. package/src/tests/model/createContext.ts +68 -17
  59. package/src/tests/security/ShoppingContextEvents.ts +20 -0
  60. package/src/tests/security/events/OrderEvents.ts +72 -0
  61. package/src/tests/security/events/ProductEvents.ts +92 -0
  62. package/src/tests/security/events/UserEvents.ts +28 -0
  63. package/src/tests/security/events/UserInfo.ts +7 -0
  64. package/src/tests/security/tests/include-items.ts +19 -0
  65. package/src/tests/security/tests/place-order.ts +104 -0
  66. package/test.js +11 -4
  67. package/tsconfig.json +2 -0
  68. package/src/decorators/parser/MemberParser.ts +0 -8
  69. package/src/model/EntitySchema.ts +0 -21
@@ -1,7 +1,8 @@
1
- import { IColumn } from "../../decorators/IColumn.js";
2
1
  import { IClassOf } from "../../decorators/IClassOf.js";
3
- import { ITextOrFunctionArray } from "./IStringTransformer.js";
2
+ import { ITextQuery } from "./IStringTransformer.js";
4
3
  import type EntityType from "../../entity-query/EntityType.js";
4
+ import DebugStringVisitor from "./DebugStringVisitor.js";
5
+ import { IEntityRelation } from "../../decorators/IColumn.js";
5
6
 
6
7
  const flattenedSelf = Symbol("flattenedSelf");
7
8
 
@@ -24,9 +25,68 @@ const flattenedSelf = Symbol("flattenedSelf");
24
25
 
25
26
  export abstract class Expression {
26
27
 
28
+ static array(elements: Expression[]) {
29
+ return ArrayExpression.create({ elements });
30
+ }
31
+
32
+ static as(expression: Expression, alias: QuotedLiteral | string) {
33
+ if (typeof alias === "string") {
34
+ alias = Expression.quotedLiteral(alias);
35
+ }
36
+ return ExpressionAs.create({
37
+ expression,
38
+ alias
39
+ });
40
+ }
41
+
42
+ static templateLiteral(value: Expression[]) {
43
+ return TemplateLiteral.create({ value });
44
+ }
45
+
46
+
47
+ static constant(value: string) {
48
+ return Constant.create({ value });
49
+ }
50
+
51
+ static quotedLiteral(name: string) {
52
+ return QuotedLiteral.create({ literal: name });
53
+ }
54
+
55
+ static parameter(name: string) {
56
+ return ParameterExpression.create({ name });
57
+ }
58
+
59
+ static identifier(name: string) {
60
+ return Identifier.create({ value: name });
61
+ }
62
+
63
+ static logicalAnd(left: Expression, right: Expression): BinaryExpression {
64
+ return BinaryExpression.create({ left, operator: "AND", right});
65
+ }
66
+
67
+ static member(target: Expression, identifier: string |Expression): MemberExpression {
68
+ return MemberExpression.create({
69
+ target,
70
+ property: typeof identifier === "string"
71
+ ? this.identifier(identifier)
72
+ : identifier
73
+ });
74
+ }
75
+
76
+ static equal(left: Expression, right: Expression) {
77
+ return BinaryExpression.create({ left, right, operator: "="});
78
+ }
79
+
27
80
  static create<T extends Expression>(this: IClassOf<T>, p: Partial<Omit<T, "type">>) {
28
81
  (p as any).type = (this as any).name;
29
82
  Object.setPrototypeOf(p, this.prototype);
83
+ Object.defineProperty(p, "debugView", {
84
+ get() {
85
+ return DebugStringVisitor.expressionToString(this);
86
+ },
87
+ enumerable: true,
88
+ configurable: true
89
+ });
30
90
  return p as T;
31
91
  }
32
92
 
@@ -68,9 +128,14 @@ export abstract class Expression {
68
128
 
69
129
  }
70
130
 
71
- export class PlaceholderExpression extends Expression {
72
- readonly type = "PlaceholderExpression";
73
- expression: () => ITextOrFunctionArray;
131
+ export class ArrayExpression extends Expression {
132
+ readonly type = "ArrayExpression";
133
+ elements: Expression[];
134
+ }
135
+
136
+ export class PartialExpression extends Expression {
137
+ readonly type = "PartialExpression";
138
+ query: ITextQuery;
74
139
  }
75
140
 
76
141
  export class BinaryExpression extends Expression {
@@ -112,6 +177,26 @@ export class CallExpression extends Expression {
112
177
  arguments: Expression[];
113
178
  }
114
179
 
180
+ export class ParameterExpression extends Expression {
181
+ readonly type = "ParameterExpression";
182
+ name: string;
183
+ /**
184
+ * Default value if any...
185
+ */
186
+ value: any;
187
+
188
+ /**
189
+ * Entity Model representing the variable
190
+ */
191
+ model: EntityType;
192
+
193
+ quotedLiteral: (x: string) => string;
194
+
195
+ toString() {
196
+ return this.quotedLiteral?.(this.name) ?? this.name;
197
+ }
198
+ }
199
+
115
200
  export class MemberExpression extends Expression {
116
201
  readonly type = "MemberExpression";
117
202
  target: Expression;
@@ -121,19 +206,21 @@ export class MemberExpression extends Expression {
121
206
 
122
207
  export class ArrowFunctionExpression extends Expression {
123
208
  readonly type = "ArrowFunctionExpression";
124
- params: Expression[];
209
+ params: ParameterExpression[];
125
210
  body: Expression;
126
211
  }
127
212
 
128
213
  export type TableSource = SelectStatement | QuotedLiteral | ExpressionAs | TableLiteral;
129
214
 
215
+ export type Expand = { [key: string]: string | Expand };
216
+
130
217
  export class SelectStatement extends Expression {
131
218
 
132
219
  readonly type = "SelectStatement";
133
220
 
134
- source: TableSource;
221
+ source: TableSource | ValuesStatement;
135
222
 
136
- as: QuotedLiteral;
223
+ as: ParameterExpression;
137
224
 
138
225
  fields: (Expression | QuotedLiteral | ExpressionAs)[];
139
226
 
@@ -147,13 +234,30 @@ export class SelectStatement extends Expression {
147
234
 
148
235
  offset: number;
149
236
 
237
+ model: EntityType;
238
+
239
+ // include relations...
240
+ include: SelectStatement[];
241
+
242
+ }
243
+
244
+ export class NewObjectExpression extends Expression {
245
+ readonly type = "NewObjectExpression";
246
+ properties: ExpressionAs[];
247
+ }
248
+
249
+ export class ConditionalExpression extends Expression {
250
+ readonly type = "ConditionalExpression";
251
+ test: Expression;
252
+ consequent: Expression;
253
+ alternate: Expression;
150
254
  }
151
255
 
152
256
  export class JoinExpression extends Expression {
153
257
  readonly type = "JoinExpression";
154
258
  joinType: "LEFT" | "INNER";
155
- source: SelectStatement | QuotedLiteral | ExpressionAs;
156
- as: QuotedLiteral;
259
+ source: SelectStatement | QuotedLiteral | ExpressionAs | TableLiteral;
260
+ as: QuotedLiteral | ParameterExpression;
157
261
  where: Expression;
158
262
  model: EntityType;
159
263
  }
@@ -200,8 +304,17 @@ export class BigIntLiteral extends Expression {
200
304
  public value: bigint;
201
305
  }
202
306
 
307
+ export class TemplateElement extends Expression {
308
+ readonly type = "TemplateElement";
309
+ public value: {
310
+ raw: string;
311
+ cooked: string;
312
+ };
313
+ }
314
+
203
315
  export class TemplateLiteral extends Expression {
204
316
  readonly type = "TemplateLiteral";
317
+ public quasis: TemplateElement[];
205
318
  public value: Expression[];
206
319
  }
207
320
 
@@ -260,7 +373,7 @@ export class UpdateStatement extends Expression {
260
373
 
261
374
  export class DeleteStatement extends Expression {
262
375
  readonly type = "DeleteStatement";
263
- table: TableLiteral;
376
+ table: TableLiteral | QuotedLiteral;
264
377
  where: Expression;
265
378
  }
266
379
 
@@ -289,6 +402,10 @@ export type ExpressionType =
289
402
  CoalesceExpression|
290
403
  ExistsExpression|
291
404
  Identifier |
292
- PlaceholderExpression|
293
- ArrowFunctionExpression
405
+ ArrowFunctionExpression |
406
+ ConditionalExpression |
407
+ NewObjectExpression |
408
+ ParameterExpression |
409
+ ArrayExpression |
410
+ TemplateElement
294
411
  ;
@@ -1,10 +1,22 @@
1
- export type ITextOrFunction = string | ((p: any) => any);
2
- export type ITextOrFunctionArray = ITextOrFunction[];
1
+ export type ITextQueryFragment = string | ((p: any) => any) | { toString(): string };
2
+ export type ITextQuery = ITextQueryFragment[];
3
3
 
4
4
  export type IStringTransformer = (s: string) => string;
5
5
 
6
6
  export type ISqlMethodTransformer = (callee: string, args: string[]) => string;
7
7
 
8
+ export class QueryParameter {
9
+
10
+ static create(name: () => string, quotedLiteral: (n: string) => string ) {
11
+ return new QueryParameter(name, quotedLiteral);
12
+ }
13
+
14
+ constructor(public name: () => string, public quotedLiteral: (n: string) => string) {}
15
+
16
+ toString() {
17
+ return this.quotedLiteral(this.name());
18
+ }
19
+ }
8
20
 
9
21
  export const prepareAny = (a: TemplateStringsArray, ... p: any[]): any => {
10
22
  const r = [];
@@ -46,7 +58,7 @@ const addNonEmptyFlat = (array: any[], item: any) => {
46
58
  array.push(item);
47
59
  };
48
60
 
49
- export const prepare = (a: TemplateStringsArray, ... p: (ITextOrFunction | ITextOrFunctionArray)[]): ITextOrFunctionArray => {
61
+ export const prepare = (a: TemplateStringsArray, ... p: (ITextQueryFragment | ITextQuery)[]): ITextQuery => {
50
62
 
51
63
  const r = [];
52
64
  for (let index = 0; index < a.length; index++) {
@@ -60,12 +72,14 @@ export const prepare = (a: TemplateStringsArray, ... p: (ITextOrFunction | IText
60
72
  return r.flat(2);
61
73
  };
62
74
 
63
- export const prepareJoin = (a: (ITextOrFunction | ITextOrFunctionArray)[], sep: string = ","): ITextOrFunctionArray => {
75
+ export const prepareJoin = (a: (ITextQueryFragment | ITextQuery)[], sep: string = ","): ITextQuery => {
64
76
  const r = [];
65
77
  let first = true;
66
78
  for (const iterator of a) {
67
79
  if (!first) {
68
- r.push(",");
80
+ if (sep) {
81
+ r.push(sep);
82
+ }
69
83
  }
70
84
  first = false;
71
85
  addNonEmptyFlat(r, iterator);
@@ -0,0 +1,97 @@
1
+ import type EntityType from "../../entity-query/EntityType.js";
2
+ import type { ParameterExpression, SelectStatement } from "./Expressions.js";
3
+
4
+
5
+ export interface IMappingModel {
6
+ parameter: ParameterExpression;
7
+ selectStatement?: SelectStatement;
8
+ name?: string;
9
+ model?: EntityType;
10
+ replace?: ParameterExpression;
11
+ isRuntimeParam?: boolean;
12
+ }
13
+
14
+ /**
15
+ * This class will provide scope for ever parameter along with type mapping, unique name and
16
+ * and replace parameter
17
+ */
18
+ export default class ParameterScope {
19
+
20
+ private map:Map<ParameterExpression, IMappingModel> = new Map();
21
+
22
+ private names: Set<string> = new Set();
23
+
24
+ public get(p: ParameterExpression) {
25
+ const model = this.map.get(p);
26
+ return model;
27
+ }
28
+
29
+ public nameOf(p: ParameterExpression) {
30
+ const model = this.map.get(p);
31
+ return model.name;
32
+ }
33
+
34
+ public alias(
35
+ originalParameter: ParameterExpression,
36
+ alias: ParameterExpression,
37
+ selectStatement: SelectStatement
38
+ ) {
39
+ const model = this.map.get(originalParameter);
40
+ if (!model.selectStatement) {
41
+ model.selectStatement = selectStatement;
42
+ } else {
43
+ selectStatement = model.selectStatement;
44
+ }
45
+ return this.create({ parameter: alias, selectStatement, name: model.name, replace: originalParameter });
46
+ }
47
+
48
+ create(model: IMappingModel) {
49
+
50
+ const existing = this.map.get(model.parameter);
51
+ if (existing) {
52
+ existing.replace = model.replace;
53
+ existing.selectStatement = model.selectStatement;
54
+ existing.model ??= existing.selectStatement.model;
55
+ return;
56
+ }
57
+
58
+ if (!model.replace) {
59
+ // create name...
60
+ model.name = this.createName(model.parameter.model?.name?.[0] ?? model.parameter.name, model.parameter.name );
61
+ }
62
+
63
+ if (!model.model) {
64
+ if (model.parameter.model) {
65
+ model.model = model.parameter.model;
66
+ } else if (model.selectStatement){
67
+ model.model = model.selectStatement.model;
68
+ }
69
+ }
70
+
71
+ this.map.set(model.parameter, model);
72
+ }
73
+ createName(prefix: string, name?: string): string {
74
+ if (name) {
75
+ name = name.toLocaleLowerCase();
76
+ if (!this.names.has(name)) {
77
+ this.names.add(name);
78
+ return name;
79
+ }
80
+ }
81
+ let index = 1;
82
+ prefix = prefix.toLocaleLowerCase();
83
+ while(true) {
84
+ name = `${prefix}${index++}`;
85
+ if (this.names.has(name)) {
86
+ continue;
87
+ }
88
+ this.names.add(name);
89
+ return name;
90
+ }
91
+ }
92
+
93
+ delete(param1: ParameterExpression) {
94
+ // this.map.delete(param1);
95
+ }
96
+
97
+ }
@@ -0,0 +1,40 @@
1
+ import { Expression } from "@babel/types";
2
+
3
+ export default class ReplaceParameter {
4
+
5
+ public static replace(tree: Expression | Expression[], from: Expression, to: Expression) {
6
+ if (!tree) {
7
+ return tree;
8
+ }
9
+ if (tree === from) {
10
+ return to;
11
+ }
12
+ if (Array.isArray(tree)) {
13
+ const copy = [];
14
+ let index = 0;
15
+ for (const iterator of tree) {
16
+ Object.defineProperty(copy, index++, {
17
+ value: this.replace(iterator, from, to),
18
+ writable: false,
19
+ enumerable: true
20
+ });
21
+ }
22
+ return tree;
23
+ }
24
+ if (!(tree as any).type) {
25
+ return tree;
26
+ }
27
+ const treeCopy = {};
28
+ for (const key in tree) {
29
+ if (Object.prototype.hasOwnProperty.call(tree, key)) {
30
+ const element = tree[key];
31
+ Object.defineProperty(treeCopy, key, {
32
+ value: this.replace(element, from, to)
33
+ });
34
+ }
35
+ }
36
+ Object.setPrototypeOf(treeCopy, Object.getPrototypeOf(tree));
37
+ return treeCopy;
38
+ }
39
+
40
+ }
File without changes
@@ -1,4 +1,5 @@
1
- import { ArrowFunctionExpression, BigIntLiteral, BinaryExpression, BooleanLiteral, CallExpression, CoalesceExpression, Constant, DeleteStatement, ExistsExpression, Expression, ExpressionAs, ExpressionType, Identifier, InsertStatement, JoinExpression, MemberExpression, NullExpression, NumberLiteral, OrderByExpression, PlaceholderExpression, QuotedLiteral, ReturnUpdated, SelectStatement, StringLiteral, TableLiteral, TemplateLiteral, UpdateStatement, ValuesStatement } from "./Expressions.js";
1
+ import { NotSupportedError } from "../parser/NotSupportedError.js";
2
+ import { ArrayExpression, ArrowFunctionExpression, BigIntLiteral, BinaryExpression, BooleanLiteral, CallExpression, CoalesceExpression, ConditionalExpression, Constant, DeleteStatement, ExistsExpression, Expression, ExpressionAs, ExpressionType, Identifier, InsertStatement, JoinExpression, MemberExpression, NewObjectExpression, NullExpression, NumberLiteral, OrderByExpression, ParameterExpression, QuotedLiteral, ReturnUpdated, SelectStatement, StringLiteral, TableLiteral, TemplateElement, TemplateLiteral, UpdateStatement, ValuesStatement } from "./Expressions.js";
2
3
 
3
4
 
4
5
  export default abstract class Visitor<T = any> {
@@ -56,18 +57,38 @@ export default abstract class Visitor<T = any> {
56
57
  return this.visitCoalesceExpression(e);
57
58
  case "ExistsExpression":
58
59
  return this.visitExistsExpression(e);
59
- case "PlaceholderExpression":
60
- return this.visitPlaceholderExpression(e);
61
60
  case "ArrowFunctionExpression":
62
61
  return this.visitArrowFunctionExpression(e);
62
+ case "ConditionalExpression":
63
+ return this.visitConditionalExpression(e);
64
+ case "NewObjectExpression":
65
+ return this.visitNewObjectExpression(e);
66
+ case "TemplateElement":
67
+ return this.visitTemplateElement(e);
68
+ case "ParameterExpression":
69
+ return this.visitParameterExpression(e);
70
+ case "ArrayExpression":
71
+ return this.visitArrayExpression(e);
63
72
  }
64
73
  const c: never = e;
65
74
  throw new Error(`${e1.type} Not implemented`);
66
75
  }
67
- visitArrowFunctionExpression(e: ArrowFunctionExpression): T {
76
+ visitArrayExpression(e: ArrayExpression): T {
77
+ throw new NotSupportedError("Array Expression");
78
+ }
79
+ visitParameterExpression(e: ParameterExpression): T {
68
80
  return;
69
81
  }
70
- visitPlaceholderExpression(e: PlaceholderExpression): T {
82
+ visitTemplateElement(e: TemplateElement): T {
83
+ return;
84
+ }
85
+ visitNewObjectExpression(e: NewObjectExpression): T {
86
+ return;
87
+ }
88
+ visitConditionalExpression(e: ConditionalExpression): T {
89
+ return;
90
+ }
91
+ visitArrowFunctionExpression(e: ArrowFunctionExpression): T {
71
92
  return;
72
93
  }
73
94
  visitExistsExpression(e: ExistsExpression): T {
@@ -0,0 +1,147 @@
1
+ import EntityType from "../../entity-query/EntityType.js";
2
+ import EntityContext from "../../model/EntityContext.js";
3
+ import EntityQuery from "../../model/EntityQuery.js";
4
+ import { ArrowFunctionExpression, ExistsExpression, Expression, ExpressionType, JoinExpression, ParameterExpression, SelectStatement, TableSource } from "../ast/Expressions.js";
5
+ import ArrowToExpression from "../parser/ArrowToExpression.js";
6
+ import { NotSupportedError } from "../parser/NotSupportedError.js";
7
+
8
+ export class QueryExpander {
9
+ static expand(context: EntityContext, select: SelectStatement, p) {
10
+ const qe = new QueryExpander(context, select);
11
+ const expression = ArrowToExpression.transform(`(_____________________x) => ${p}` as any);
12
+ qe.expandNode(select, select.model, expression.body as ExpressionType);
13
+ return select;
14
+ }
15
+
16
+ constructor(
17
+ private context: EntityContext,
18
+ private select: SelectStatement
19
+ ) {
20
+
21
+ }
22
+
23
+ expandNode(parent: SelectStatement, model: EntityType, node: ExpressionType): [SelectStatement, EntityType] {
24
+
25
+ if (node.type === "ArrayExpression") {
26
+ for (const iterator of node.elements) {
27
+ this.expandNode(parent, model, iterator as ExpressionType);
28
+ }
29
+ return;
30
+ }
31
+
32
+ if(node.type === "CallExpression") {
33
+ const callee = node.callee as ExpressionType;
34
+ if (callee.type !== "MemberExpression") {
35
+ throw new NotSupportedError(callee.type);
36
+ }
37
+ const property = callee.property as ExpressionType;
38
+ if (property.type !== "Identifier") {
39
+ throw new NotSupportedError(property.type);
40
+ }
41
+ if (property.value !== "forEach") {
42
+ throw new NotSupportedError(property.value);
43
+ }
44
+ const [expandedSelect, expandedType] = this.expandNode(parent, model, callee.target as ExpressionType);
45
+
46
+ const arrow = node.arguments[0];
47
+ if (!arrow || arrow.type !== "ArrowFunctionExpression") {
48
+ throw new NotSupportedError(arrow?.type ?? "Empty Expression");
49
+ }
50
+ this.expandNode(expandedSelect, expandedType, (arrow as ArrowFunctionExpression).body as ExpressionType);
51
+ return [expandedSelect, expandedType];
52
+ }
53
+
54
+ if (node.type !== "MemberExpression") {
55
+ throw new NotSupportedError(node.type);
56
+ }
57
+
58
+ const p = node.property as ExpressionType;
59
+ if (p.type !== "Identifier") {
60
+ throw new NotSupportedError(p.type);
61
+ }
62
+
63
+ const target = node.target as ExpressionType;
64
+ if (target.type === "MemberExpression") {
65
+ const [mepSelect, mepType] = this.expandNode(parent, model, target);
66
+ parent = mepSelect;
67
+ model = mepType;
68
+ }
69
+
70
+ const mp = model.getProperty(p.value);
71
+ if (!mp.relation) {
72
+ throw new NotSupportedError(`No relation found ${p.value} in ${model.name}`);
73
+ }
74
+ const { relation } = mp;
75
+ const { relatedTypeClass: propertyType } = relation;
76
+
77
+ let query = this.context.query(propertyType);
78
+ const events = this.context.eventsFor(propertyType, false);
79
+ if (events) {
80
+ query = events.includeFilter(query, model, p.value) ?? query;
81
+ }
82
+ const select = { ... (query as EntityQuery).selectStatement };
83
+
84
+ let where: Expression;
85
+ let joinWhere: Expression;
86
+
87
+ const fk = relation.fkColumn ?? relation.relatedRelation.fkColumn;
88
+
89
+
90
+ if(relation.isInverseRelation) {
91
+
92
+ joinWhere = Expression.equal(
93
+ Expression.member(
94
+ parent.as,
95
+ Expression.quotedLiteral(fk.columnName)
96
+ ),
97
+ Expression.member(
98
+ select.as,
99
+ Expression.quotedLiteral(model.keys[0].columnName)
100
+ )
101
+ );
102
+ // load parent..
103
+ where = parent.where
104
+ ? Expression.logicalAnd(joinWhere, parent.where)
105
+ : joinWhere;
106
+
107
+ const joins = (select.joins ??= []);
108
+ joins.push(JoinExpression.create({
109
+ source: parent.source as TableSource,
110
+ as: parent.as,
111
+ model,
112
+ where
113
+ }));
114
+ (this.select.include ??= []).push(select);
115
+ return [select, relation.relatedEntity];
116
+ }
117
+
118
+ joinWhere = Expression.equal(
119
+ Expression.member(
120
+ parent.as,
121
+ Expression.quotedLiteral(fk.columnName)
122
+ ),
123
+ Expression.member(
124
+ select.as,
125
+ Expression.quotedLiteral(relation.relatedEntity.keys[0].columnName)
126
+ )
127
+ );
128
+
129
+ parent = { ... parent };
130
+
131
+ parent.where = parent.where
132
+ ? Expression.logicalAnd(parent.where, joinWhere)
133
+ : joinWhere;
134
+
135
+ const existsWhere = ExistsExpression.create({
136
+ target: parent
137
+ });
138
+
139
+ select.where = select.where
140
+ ? Expression.logicalAnd(select.where, existsWhere)
141
+ : existsWhere;
142
+
143
+ (this.select.include ??= []).push(select);
144
+
145
+ return [select, relation.relatedEntity];
146
+ }
147
+ }