@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,6 +1,11 @@
1
1
  name: Build
2
2
 
3
- on: push
3
+ on:
4
+ push:
5
+ branches-ignore:
6
+ - 'feature/**'
7
+ tags-ignore:
8
+ - '*'
4
9
 
5
10
  jobs:
6
11
  build:
@@ -38,7 +43,7 @@ jobs:
38
43
  - run: npm install
39
44
  - run: npm install -D typescript
40
45
  - run: tsc
41
- - run: npm run test-db
46
+ - run: npm run test
42
47
  env:
43
48
  POSTGRES_HOST: localhost
44
49
  POSTGRES_PORT: 5432
@@ -33,6 +33,7 @@
33
33
  "PGPASSWORD",
34
34
  "PGPORT",
35
35
  "PGUSER",
36
+ "quasis",
36
37
  "sysdatabases",
37
38
  "varbinary"
38
39
  ]
package/README.md CHANGED
@@ -5,7 +5,7 @@ Inspired from Entity Framework Core, Entity Access is ORM for JavaScript runtime
5
5
 
6
6
  # Project Status
7
7
  1. Beta - Postgres Driver
8
- 2. Alpha - Sql Server Driver
8
+ 2. Beta - Sql Server Driver
9
9
 
10
10
  ## Features
11
11
  1. Unit of Work and Repository Pattern
@@ -15,6 +15,7 @@ Inspired from Entity Framework Core, Entity Access is ORM for JavaScript runtime
15
15
  5. Postgres Driver
16
16
  6. Sql Server Driver
17
17
  7. Automatic parameterization to safeguard sql injection attacks.
18
+ 8. Context Filters - This is a new concept where you can setup filters that will be used against saving/retrieving data.
18
19
 
19
20
  ## Upcoming Features
20
21
  1. Include
@@ -38,6 +39,22 @@ db.orders.add({
38
39
 
39
40
  // save all in single transaction
40
41
  await db.saveChanges();
42
+
43
+
44
+ const existingOrderItem1: OrderItem;
45
+ const existingOrderItem2: OrderItem;
46
+
47
+ existingOrderItem2.status = "cancelled";
48
+ existingOrderItem1.status = "cancelled";
49
+ // executes update statements in single transaction
50
+ await db.saveChanges();
51
+
52
+
53
+ db.orderItems.delete(existingOrderItem1);
54
+ db.orderItems.delete(existingOrderItem2);
55
+ // executes delete statements in single transaction
56
+ await db.saveChanges();
57
+
41
58
  ```
42
59
 
43
60
  ### Arrow function based query features
@@ -52,11 +69,34 @@ Simple Query
52
69
  ```typescript
53
70
  const db = new ShoppingContext();
54
71
 
55
- /// first parameter is set of parameters to pass to the query
56
- /// the reason it is first, is to help in intellisense.
57
-
58
- /// second parameter is an arrow function which returns a filter
59
- const q = db.orders.where({ userID }, (params) => (order) => order.userID === p.userID);
72
+ // find customer from orderID
73
+ const q = db.customers
74
+ // first we will send parameters
75
+ .where({ orderID },
76
+ // second we will write an arrow
77
+ // accepting parameters
78
+ (p) =>
79
+ // this is the arrow which will
80
+ // be converted to SQL
81
+ // you can write very limited set of
82
+ // expressions in this arrow function
83
+ (x) => x.orders.some(
84
+ // This will results in exists or join
85
+ // based on relations declared
86
+ (order) => order.orderID === p.orderID );
87
+ const customer = await q.first();
88
+ ```
89
+ Above expression will result in following filter expression
90
+ ```sql
91
+ SELECT c.firstName, c.lastName, c.customerID
92
+ FROM customers as c
93
+ EXISTS (
94
+ SELECT 1
95
+ FROM Orders as o1
96
+ WHERE x.customerID = o1.orderID
97
+ AND o1.orderID = $1
98
+ )
99
+ LIMIT 1;
60
100
  ```
61
101
 
62
102
  Query with Like operator
@@ -124,42 +164,32 @@ class OrderItem {
124
164
 
125
165
  ### Compare operators
126
166
 
167
+ Only handful of operators are supported as of now.
168
+ 1. Equality Both `==`, `===`, will result in simple `=` operator in SQL. There is no type check performed and no conversion is performed to speed up
169
+ execution. However, typescript will give you warning and compilation for mismatch of operands and you can convert them as needed. But for conversion
170
+ use only `Sql.*` functions.
171
+ 2. Above also applies for operators `!=` and `!==`, they will result in `<>` in SQL.
172
+ 3. `+`, `-`, `*`, `/` operators will be sent to SQL as it is.
173
+ 4. For precedence, we recommend using brackets in the arrow functions as there might be differences in underlying database engine's preferences and you may not get correct results.
174
+ 5. Template Literals, will be sent to SQL as concat, however, please use
175
+ conversion of non string to string type if underlying provider does not support direct conversion.
176
+ 6. Conversion methods, `Sql.cast.as*` methods will provide conversion from any type to desired type. `Sql.cast.asText` will convert to number to text.
177
+
127
178
  #### Equality
128
179
  Both strict and non strict equality will result in
129
180
  simple equality comparison in SQL. Database provider
130
181
  may or may not convert them correctly, so we recommend
131
182
  using helper functions to convert before comparison.
132
183
  ```typescript
133
- // find all customer from orderID
134
184
  const q = db.customers
135
- // first we will send parameters
136
185
  .where({ orderID },
137
- // second we will write an arrow
138
- // accepting parameters
139
186
  (p) =>
140
- // this is the arrow which will
141
- // be converted to SQL
142
- // you can write very limited set of
143
- // expressions in this arrow function
144
187
  (x) => x.orders.some(
145
- // This will results in exists or join
146
- // based on what level of nested
147
- // foreign key references are available
148
188
  (order) => order.orderID === p.orderID )
149
189
  )
150
190
 
151
191
  ```
152
192
 
153
- Above expression will result in following filter expression
154
- ```sql
155
- EXISTS (
156
- SELECT 1
157
- FROM Orders as o1
158
- WHERE x.customerID = o1.orderID
159
- AND o1.orderID = $1
160
- )
161
- ```
162
-
163
193
  #### Like
164
194
 
165
195
  To use `LIKE` operator, `Sql.text.like` method must be used
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@entity-access/entity-access",
3
- "version": "1.0.2",
3
+ "version": "1.0.6",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
+ "test-plain": "node --enable-source-maps ./test.js no-db",
7
8
  "test": "node --enable-source-maps ./test.js",
8
- "test-db": "node --enable-source-maps ./test.js test-db",
9
9
  "postversion": "git push --follow-tags"
10
10
  },
11
11
  "type": "module",
@@ -0,0 +1,10 @@
1
+ export default class EntityAccessError extends Error {
2
+
3
+ static throw(message: string = "Access denied") {
4
+ throw new EntityAccessError(message);
5
+ }
6
+
7
+ constructor(message: string = "Access denied") {
8
+ super(message);
9
+ }
10
+ }
@@ -0,0 +1,25 @@
1
+ /* eslint-disable no-console */
2
+ /* eslint-disable @typescript-eslint/naming-convention */
3
+ declare global {
4
+ interface SymbolConstructor {
5
+ readonly disposable?: unique symbol;
6
+ readonly asyncDisposable?: unique symbol;
7
+ }
8
+ }
9
+
10
+ // @ts-expect-error readonly
11
+ Symbol.disposable ??= Symbol("@@disposable");
12
+ // @ts-expect-error readonly
13
+ Symbol.asyncDisposable ??= Symbol("@@asyncDisposable");
14
+
15
+
16
+ export interface IDisposable {
17
+ dispose?();
18
+ [Symbol.disposable]?();
19
+ [Symbol.asyncDisposable]?();
20
+ }
21
+
22
+ export function disposeDisposable(d: IDisposable) {
23
+ const f = d[Symbol.disposable] ?? d[Symbol.asyncDisposable];
24
+ f?.()?.catch((error) => console.error(error));
25
+ }
@@ -0,0 +1,53 @@
1
+ import { IClassOf } from "../decorators/IClassOf.js";
2
+
3
+ function clone() {
4
+ Object.getPrototypeOf(this).constructor.create({ ... this });
5
+ }
6
+
7
+ export default class ImmutableObject {
8
+
9
+ public static create<T extends ImmutableObject>(this:IClassOf<T>, p: Partial<T>): T {
10
+ (p as any).type = this.name;
11
+ for(const [key, value] of Object.entries(p)) {
12
+ if (!value) {
13
+ continue;
14
+ }
15
+ // if it is array, we should make it immutable
16
+ if (Array.isArray(value)) {
17
+ const array = [];
18
+ let index = 0;
19
+ for (const iterator of value) {
20
+ Object.defineProperty(array, index++, {
21
+ value: iterator,
22
+ writable: false,
23
+ enumerable: true
24
+ });
25
+ }
26
+ Object.defineProperty(array, "clone", {
27
+ value: clone,
28
+ enumerable: false,
29
+ writable: false
30
+ });
31
+ Object.defineProperty(p, key, {
32
+ value: array,
33
+ enumerable: true,
34
+ writable: false
35
+ });
36
+ continue;
37
+ }
38
+ Object.defineProperty(p, key, {
39
+ value,
40
+ enumerable: true,
41
+ writable: false
42
+ });
43
+ }
44
+ Object.setPrototypeOf(p, Object.getPrototypeOf(this));
45
+ Object.defineProperty(p, "clone", {
46
+ value: clone,
47
+ enumerable: false,
48
+ writable: false
49
+ });
50
+ return p as T;
51
+ }
52
+
53
+ }
@@ -0,0 +1,59 @@
1
+ import { RegisterSingleton } from "../di/di.js";
2
+ import { IDisposable } from "./IDisposable.js";
3
+
4
+ /* eslint-disable no-console */
5
+ @RegisterSingleton
6
+ export default class Logger implements IDisposable {
7
+
8
+ public static instance = new Logger();
9
+
10
+ log(a) {
11
+ console.log(a);
12
+ return this;
13
+ }
14
+
15
+ error(a) {
16
+ console.error(a);
17
+ return this;
18
+ }
19
+
20
+ newSession() {
21
+ return new SessionLogger(this);
22
+ }
23
+
24
+ dispose() {
25
+ // do nothing...
26
+ }
27
+ }
28
+
29
+ class SessionLogger extends Logger {
30
+ private items = [];
31
+
32
+ constructor(private parent: Logger) {
33
+ super();
34
+ }
35
+
36
+ log(a) {
37
+ this.items.push({ log: a});
38
+ return this;
39
+ }
40
+
41
+ error(a: any) {
42
+ this.items.push({ error: a});
43
+ return this;
44
+ }
45
+
46
+ dispose(): void {
47
+ for (const { log, error } of this.items) {
48
+ if (log) {
49
+ this.parent.log(log);
50
+ continue;
51
+ }
52
+ if (error) {
53
+ this.parent.error(error);
54
+ continue;
55
+ }
56
+ }
57
+ }
58
+
59
+ }
@@ -0,0 +1,3 @@
1
+ export const TypeInfo = {
2
+ nameOfType: (t: any) => t.name ?? t.toString()
3
+ };
@@ -11,10 +11,10 @@ export default class TimedCache<TKey = any, T = any> {
11
11
  this.map.delete(key);
12
12
  }
13
13
 
14
- getOrCreate(key: TKey, factory: (k: TKey) => T, ttl: number = 15000) {
14
+ getOrCreate<TP>(key: TKey, p1: TP, factory: (k: TKey,p: TP) => T, ttl: number = 15000) {
15
15
  let item = this.map.get(key);
16
16
  if (!item) {
17
- item = { value: factory(key), ttl, expire: Date.now() + ttl };
17
+ item = { value: factory(key, p1), ttl, expire: Date.now() + ttl };
18
18
  this.map.set(key, item);
19
19
  } else {
20
20
  item.expire = Date.now() + ttl;
@@ -1,24 +1,54 @@
1
+ import { IDisposable } from "./IDisposable.js";
1
2
 
2
- export interface IDisposable {
3
+ export type IDisposableObject = IDisposable & { end?(): any; close?():any };
3
4
 
4
- end?():any;
5
- dispose?(): any;
6
- close?(): any;
5
+ export type IDisposableObjectType = IDisposableObject | IDisposableObject[];
7
6
 
8
- }
9
7
 
8
+ const disposeAll = async (all: (IDisposable & { end?(): any; close?():any })[]) => {
9
+ let error = null;
10
+ // we need to dispose in reverse order...
11
+ while (all.length) {
12
+ const d = all.pop();
13
+ try {
14
+ const f = d.dispose ?? d.end ?? d.close ?? d[Symbol.disposable] ?? d[Symbol.asyncDisposable];
15
+ const r = f?.apply(d);
16
+ if (r?.then) {
17
+ await r;
18
+ }
19
+ } catch (e) {
20
+ error = error
21
+ ? new Error(`${error.stack ?? error}\r\n${e.stack ?? e}`)
22
+ : error;
23
+ }
24
+ }
25
+ if (error) {
26
+ throw error;
27
+ }
28
+ };
10
29
 
11
- export default async function usingAsync<T extends IDisposable>(d: T, fx: (p:T) => Promise<any>) {
30
+ export default async function usingAsync<T>(fx: (registry: IDisposableObject[]) => T | Promise<T>) {
31
+ const registry = [] as IDisposableObject[];
12
32
  try {
13
- const r = fx(d);
33
+ const r = fx(registry) as any;
14
34
  if (r?.then) {
15
35
  await r;
16
36
  }
17
- return r;
18
37
  } finally {
19
- const r = d.dispose?.() ?? d.end?.() ?? d.close?.();
20
- if (r?.then) {
21
- await r;
22
- }
38
+ await disposeAll(registry);
23
39
  }
24
40
  }
41
+
42
+ export class DisposableScope {
43
+
44
+ private disposables: IDisposableObject[] = [];
45
+
46
+ public register(d: IDisposableObject) {
47
+ this.disposables.push(d);
48
+ }
49
+
50
+ async dispose() {
51
+ await disposeAll(this.disposables);
52
+ }
53
+
54
+ }
@@ -1,14 +1,19 @@
1
- import { IClassOf } from "../decorators/IClassOf.js";
2
1
  import ExpressionToSql from "../query/ast/ExpressionToSql.js";
3
- import { ISqlMethodTransformer, IStringTransformer, ITextOrFunctionArray } from "../query/ast/IStringTransformer.js";
4
- import { Expression, PlaceholderExpression } from "../query/ast/Expressions.js";
2
+ import { ISqlMethodTransformer, IStringTransformer, ITextQuery } from "../query/ast/IStringTransformer.js";
3
+ import { Expression, ParameterExpression, SelectStatement } from "../query/ast/Expressions.js";
5
4
  import SqlLiteral from "../query/ast/SqlLiteral.js";
6
5
  import ArrowToExpression from "../query/parser/ArrowToExpression.js";
7
6
  import PostgreSqlMethodTransformer from "./postgres/PostgreSqlMethodTransformer.js";
8
- import EntityType from "../entity-query/EntityType.js";
9
- import { EntitySource } from "../model/EntitySource.js";
10
- import { IEntityQuery } from "../model/IFilterWithParameter.js";
11
- import { SourceExpression } from "../model/SourceExpression.js";
7
+ import EntityQuery from "../model/EntityQuery.js";
8
+ import ReplaceParameter from "../query/ast/ReplaceParameter.js";
9
+
10
+
11
+ export class CompiledQuery {
12
+ constructor(
13
+ public root: ParameterExpression,
14
+ public target: ParameterExpression,
15
+ public textQuery: ITextQuery) {}
16
+ }
12
17
 
13
18
  export default class QueryCompiler {
14
19
 
@@ -38,47 +43,40 @@ export default class QueryCompiler {
38
43
  this.sqlMethodTransformer = sqlMethodTransformer;
39
44
  }
40
45
 
41
- public execute<P = any, T = any>(parameters: P, fx: (p: P) => (x: T) => any, source?: SourceExpression) {
42
- const { param, target , body } = this.arrowToExpression.transform(fx);
43
- const exp = new this.expressionToSql(source, param, target, this);
46
+ public execute<P = any, T = any>(parameters: P, fx: (p: P) => (x: T) => any, source?: EntityQuery) {
47
+ const { params, target , body } = this.arrowToExpression.transform(fx, source?.selectStatement.as);
48
+ const exp = new this.expressionToSql(source, params[0], target, this);
44
49
  const query = exp.visit(body);
45
50
  return this.invoke(query, parameters);
46
51
  }
47
52
 
48
- public compileToExpression(source: SourceExpression, p: any, fx: (p1) => (x) => any) {
49
- const { param, target , body } = this.arrowToExpression.transform(fx);
50
- const exp = new this.expressionToSql(source, param, target, this);
51
- const visited = exp.visit(body);
52
- return PlaceholderExpression.create({
53
- expression: () => visited.map((x) => typeof x === "function" ? () => x(p) : x)
54
- });
53
+ public compile(source: EntityQuery, fx: (p) => (x) => any) {
54
+ const { params, target , body } = this.arrowToExpression.transform(fx, source?.selectStatement.as);
55
+ return { params, target, body };
55
56
  }
56
57
 
57
- public compile( source: SourceExpression , fx: (p) => (x) => any) {
58
- const { param, target , body } = this.arrowToExpression.transform(fx);
59
- const exp = new this.expressionToSql(source, param, target, this);
60
- return exp.visit(body);
58
+ public compileToSql( source: EntityQuery , fx: (p) => (x) => any) {
59
+ const { params, target , body } = this.arrowToExpression.transform(fx, source?.selectStatement.as);
60
+ const exp = new this.expressionToSql(source, params[0], target, this);
61
+ const textQuery = exp.visit(body);
62
+ return new CompiledQuery(exp.root,exp.target,textQuery);
61
63
  }
62
64
 
63
- public compileExpression(exp: Expression) {
64
- const toSql = new this.expressionToSql(null, void 0, void 0, this);
65
+ public compileExpression(source: EntityQuery, exp: Expression) {
66
+ const toSql = new this.expressionToSql(source ?? null, null, (exp as SelectStatement)?.as ?? source?.selectStatement?.as, this);
65
67
  const query = toSql.visit(exp);
66
68
  return this.invoke(query);
67
69
  }
68
70
 
69
- private invoke(query: ITextOrFunctionArray, p: any = {}) {
71
+ private invoke(query: ITextQuery, p: any = {}) {
70
72
  let text = "";
71
73
  const values = [];
72
74
  for (const iterator of query) {
73
- if (typeof iterator === "string") {
75
+ if (typeof iterator !== "function") {
74
76
  text += iterator;
75
77
  continue;
76
78
  }
77
- let value = iterator;
78
- if (typeof iterator === "function") {
79
- value = iterator(p);
80
- }
81
-
79
+ const value = iterator(p);
82
80
  values.push(value);
83
81
  text += "$" + values.length;
84
82
  }
@@ -6,6 +6,29 @@ export const PostgreSqlHelper: ISqlHelpers = {
6
6
  in(a, array) {
7
7
  return prepareAny `${a} IN ${array}`;
8
8
  },
9
+ cast: {
10
+ asBigInt(a) {
11
+ return prepareAny `(${a} ::bigint)`;
12
+ },
13
+ asDate(a) {
14
+ return prepareAny `(${a} ::date)`;
15
+ },
16
+ asDateTime(a) {
17
+ return prepareAny `(${a} ::timestamp)`;
18
+ },
19
+ asInteger(a) {
20
+ return prepareAny `(${a} ::int)`;
21
+ },
22
+ asNumber(a) {
23
+ return prepareAny `(${a} ::double)`;
24
+ },
25
+ asText(a) {
26
+ return prepareAny `(${a} ::text)`;
27
+ },
28
+ asDecimal(a) {
29
+ return prepareAny `(${a} ::decimal(18,2))`;
30
+ },
31
+ },
9
32
  date: {
10
33
  addDays(d, n) {
11
34
  return prepareAny `(${d} + (${n} * interval '1 day'))`;
@@ -6,6 +6,29 @@ export const SqlServerSqlHelper: ISqlHelpers = {
6
6
  in(a, array) {
7
7
  return prepareAny `${a} IN ${array}`;
8
8
  },
9
+ cast: {
10
+ asBigInt(a) {
11
+ return prepareAny `CAST(${a} as bigint)`;
12
+ },
13
+ asDate(a) {
14
+ return prepareAny `CAST(${a} as date)`;
15
+ },
16
+ asDateTime(a) {
17
+ return prepareAny `CAST(${a} as datetime2)`;
18
+ },
19
+ asInteger(a) {
20
+ return prepareAny `CAST(${a} as int)`;
21
+ },
22
+ asNumber(a) {
23
+ return prepareAny `CAST(${a} as double)`;
24
+ },
25
+ asText(a) {
26
+ return prepareAny `CAST(${a} as varchar(max))`;
27
+ },
28
+ asDecimal(a) {
29
+ return prepareAny `CAST(${a} as decimal(18,2))`;
30
+ },
31
+ },
9
32
  date: {
10
33
  addDays(d, n) {
11
34
  return prepareAny `DateAdd(DAY, ${d}, ${n})`;
@@ -1,6 +1,6 @@
1
1
  import type { IClassOf } from "./IClassOf.js";
2
2
  import SchemaRegistry from "./SchemaRegistry.js";
3
- import NameParser from "./parser/MemberParser.js";
3
+ import NameParser from "./parser/NameParser.js";
4
4
 
5
5
 
6
6
  export default function ForeignKey<T, TRelated>(
@@ -1 +1,2 @@
1
- export type IClassOf<T> = abstract new (... a: any[]) => T;
1
+ export type IClassOf<T> = new (... a: any[]) => T;
2
+ export type IAbstractClassOf<T> = abstract new (... a: any[]) => T;
@@ -45,6 +45,8 @@ export interface IEntityRelation {
45
45
  */
46
46
  name: string;
47
47
 
48
+ isInverseRelation?: boolean;
49
+
48
50
  isCollection?: boolean;
49
51
 
50
52
 
@@ -0,0 +1,15 @@
1
+ const parsedName = Symbol("parsedName");
2
+
3
+ export default class NameParser {
4
+ public static parseMember(text: ((a: any) => any)) {
5
+ let name = text[parsedName];
6
+ if (!name) {
7
+ const t: string = text.toString();
8
+
9
+ const index = t.lastIndexOf(".");
10
+ name = t.substring(index + 1);
11
+ text[parsedName] = name;
12
+ }
13
+ return name;
14
+ }
15
+ }