@travetto/model-sql 5.0.0-rc.0 → 5.0.0-rc.10

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/model-sql",
3
- "version": "5.0.0-rc.0",
3
+ "version": "5.0.0-rc.10",
4
4
  "description": "SQL backing for the travetto model module, with real-time modeling support for SQL schemas.",
5
5
  "keywords": [
6
6
  "sql",
@@ -27,14 +27,14 @@
27
27
  "directory": "module/model-sql"
28
28
  },
29
29
  "dependencies": {
30
- "@travetto/config": "^5.0.0-rc.0",
31
- "@travetto/context": "^5.0.0-rc.0",
32
- "@travetto/model": "^5.0.0-rc.0",
33
- "@travetto/model-query": "^5.0.0-rc.0"
30
+ "@travetto/config": "^5.0.0-rc.10",
31
+ "@travetto/context": "^5.0.0-rc.9",
32
+ "@travetto/model": "^5.0.0-rc.10",
33
+ "@travetto/model-query": "^5.0.0-rc.10"
34
34
  },
35
35
  "peerDependencies": {
36
- "@travetto/command": "^5.0.0-rc.0",
37
- "@travetto/test": "^5.0.0-rc.0"
36
+ "@travetto/command": "^5.0.0-rc.9",
37
+ "@travetto/test": "^5.0.0-rc.9"
38
38
  },
39
39
  "peerDependenciesMeta": {
40
40
  "@travetto/command": {
package/src/config.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Config } from '@travetto/config';
2
+ import { asFull } from '@travetto/runtime';
2
3
 
3
4
  /**
4
5
  * SQL Model Config
@@ -40,6 +41,5 @@ export class SQLModelConfig<T extends {} = {}> {
40
41
  /**
41
42
  * Raw client options
42
43
  */
43
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
44
- options: T = {} as T;
44
+ options: T = asFull({});
45
45
  }
@@ -1,4 +1,4 @@
1
- import { Util } from '@travetto/base';
1
+ import { castTo, Util } from '@travetto/runtime';
2
2
  import { AsyncContext } from '@travetto/context';
3
3
 
4
4
  const ContextActiveⲐ: unique symbol = Symbol.for('@travetto/model:sql-active');
@@ -95,13 +95,12 @@ export abstract class Connection<C = unknown> {
95
95
  * @param op
96
96
  * @param args
97
97
  */
98
- async * iterateWithActive<R>(op: () => AsyncGenerator<R>): AsyncIterable<R> {
98
+ async * iterateWithActive<R>(op: () => AsyncIterable<R>): AsyncIterable<R> {
99
99
  if (this.active) {
100
100
  yield* op();
101
101
  }
102
102
 
103
- // eslint-disable-next-line @typescript-eslint/no-this-alias
104
- const self = this;
103
+ const self = castTo<Connection>(this);
105
104
  yield* this.context.iterate(async function* () {
106
105
  try {
107
106
  self.context.set(ContextActiveⲐ, await self.acquire());
@@ -117,7 +116,7 @@ export abstract class Connection<C = unknown> {
117
116
  /**
118
117
  * Run a function within a valid sql transaction. Relies on @travetto/context.
119
118
  */
120
- async runWithTransaction<R>(mode: TransactionType, op: () => R): Promise<R> {
119
+ async runWithTransaction<R>(mode: TransactionType, op: () => Promise<R>): Promise<R> {
121
120
  if (this.activeTx) {
122
121
  if (mode === 'isolated' || mode === 'force') {
123
122
  const txId = mode === 'isolated' ? `tx${Util.uuid()}` : undefined;
@@ -1,3 +1,4 @@
1
+ import { AsyncItrMethodDescriptor, AsyncMethodDescriptor } from '@travetto/runtime';
1
2
  import { Connection, TransactionType } from './base';
2
3
 
3
4
  /**
@@ -10,38 +11,41 @@ export interface ConnectionAware<C = unknown> {
10
11
  /**
11
12
  * Decorator to ensure a method runs with a valid connection
12
13
  */
13
- export function Connected<T extends ConnectionAware>() {
14
- return function (target: T, prop: string | symbol, desc: TypedPropertyDescriptor<(...params: unknown[]) => Promise<unknown>>): void {
14
+ export function Connected() {
15
+ return function <T extends { conn?: Connection }>(
16
+ target: T, prop: string | symbol, desc: AsyncMethodDescriptor<T>
17
+ ): void {
15
18
  const og = desc.value!;
16
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
17
- desc.value = async function (this: T, ...args: unknown[]) {
18
- return this.conn.runWithActive(() => og.call(this, ...args));
19
- } as typeof og;
19
+ desc.value = function (...args: unknown[]): ReturnType<typeof og> {
20
+ return this.conn!.runWithActive(() => og.call(this, ...args));
21
+ };
20
22
  };
21
23
  }
22
24
 
23
25
  /**
24
26
  * Decorator to ensure a method runs with a valid connection
25
27
  */
26
- export function ConnectedIterator<T extends ConnectionAware>() {
27
- return function (target: T, prop: string | symbol, desc: TypedPropertyDescriptor<(...params: unknown[]) => AsyncGenerator<unknown>>): void {
28
+ export function ConnectedIterator() {
29
+ return function <T extends { conn?: Connection }>(
30
+ target: T, prop: string | symbol, desc: AsyncItrMethodDescriptor<T>
31
+ ): void {
28
32
  const og = desc.value!;
29
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
30
- desc.value = async function* (this: T, ...args: unknown[]) {
31
- yield* this.conn.iterateWithActive(() => og.call(this, ...args));
32
- } as typeof og;
33
+ desc.value = async function* (...args: unknown[]): ReturnType<typeof og> {
34
+ yield* this.conn!.iterateWithActive(() => og.call(this, ...args));
35
+ };
33
36
  };
34
37
  }
35
38
 
36
39
  /**
37
40
  * Decorator to ensure a method runs with a valid transaction
38
41
  */
39
- export function Transactional<T extends ConnectionAware>(mode: TransactionType = 'required') {
40
- return function (target: T, prop: string | symbol, desc: TypedPropertyDescriptor<(...params: unknown[]) => Promise<unknown>>): void {
42
+ export function Transactional(mode: TransactionType = 'required') {
43
+ return function <T extends { conn?: Connection }>(
44
+ target: unknown, prop: string | symbol, desc: AsyncMethodDescriptor<T>
45
+ ): void {
41
46
  const og = desc.value!;
42
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
43
- desc.value = function (this: T, ...args: unknown[]) {
44
- return this.conn.runWithTransaction(mode, () => og.call(this, ...args));
45
- } as typeof og;
47
+ desc.value = function (...args: unknown[]): ReturnType<typeof og> {
48
+ return this.conn!.runWithTransaction(mode, () => og.call(this, ...args));
49
+ };
46
50
  };
47
- }
51
+ }
@@ -1,6 +1,6 @@
1
- import { RuntimeIndex } from '@travetto/manifest';
1
+ /* eslint-disable @stylistic/indent */
2
2
  import { DataUtil, SchemaRegistry, FieldConfig, Schema } from '@travetto/schema';
3
- import { Class, AppError, TypedObject } from '@travetto/base';
3
+ import { Class, AppError, TypedObject, describeFunction, TimeUtil, castTo, castKey } from '@travetto/runtime';
4
4
  import { SelectClause, Query, SortClause, WhereClause, RetainFields } from '@travetto/model-query';
5
5
  import { BulkResponse, IndexConfig } from '@travetto/model';
6
6
  import { PointImpl } from '@travetto/model-query/src/internal/model/point';
@@ -11,13 +11,6 @@ import { SQLUtil, VisitStack } from '../internal/util';
11
11
  import { DeleteWrapper, InsertWrapper, DialectState } from '../internal/types';
12
12
  import { Connection } from '../connection/base';
13
13
 
14
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
15
- const has$And = (o: unknown): o is ({ $and: WhereClause<unknown>[] }) => !!o && '$and' in (o as object);
16
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
17
- const has$Or = (o: unknown): o is ({ $or: WhereClause<unknown>[] }) => !!o && '$or' in (o as object);
18
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
19
- const has$Not = (o: unknown): o is ({ $not: WhereClause<unknown> }) => !!o && '$not' in (o as object);
20
-
21
14
  interface Alias {
22
15
  alias: string;
23
16
  path: VisitStack[];
@@ -196,16 +189,18 @@ export abstract class SQLDialect implements DialectState {
196
189
  const src = DataUtil.toRegex(value).source.replace(/\\b/g, this.regexWordBoundary);
197
190
  return this.quote(src);
198
191
  } else {
199
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
200
- return this.quote(value as string);
192
+ return this.quote(castTo(value));
201
193
  }
202
194
  } else if (conf.type === Boolean) {
203
195
  return `${value ? 'TRUE' : 'FALSE'}`;
204
196
  } else if (conf.type === Number) {
205
197
  return `${value}`;
206
198
  } else if (conf.type === Date) {
207
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
208
- return this.resolveDateValue(ModelQueryUtil.resolveComparator(value) as Date);
199
+ if (typeof value === 'string' && TimeUtil.isTimeSpan(value)) {
200
+ return this.resolveDateValue(TimeUtil.fromNow(value));
201
+ } else {
202
+ return this.resolveDateValue(DataUtil.coerceType(value, Date, true));
203
+ }
209
204
  } else if (conf.type === PointImpl && Array.isArray(value)) {
210
205
  return `point(${value[0]},${value[1]})`;
211
206
  } else if (conf.type === Object) {
@@ -276,8 +271,7 @@ export abstract class SQLDialect implements DialectState {
276
271
  * Delete query and return count removed
277
272
  */
278
273
  async deleteAndGetCount<T>(cls: Class<T>, query: Query<T>): Promise<number> {
279
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
280
- const { count } = await this.executeSQL(this.getDeleteSQL(SQLUtil.classToStack(cls), query.where as WhereClause<T>));
274
+ const { count } = await this.executeSQL<T>(this.getDeleteSQL(SQLUtil.classToStack(cls), query.where));
281
275
  return count;
282
276
  }
283
277
 
@@ -285,7 +279,7 @@ export abstract class SQLDialect implements DialectState {
285
279
  * Get the count for a given query
286
280
  */
287
281
  async getCountForQuery<T>(cls: Class<T>, query: Query<T>): Promise<number> {
288
- const { records } = await this.executeSQL<{ total: number }>(this.getQueryCountSQL(cls, query));
282
+ const { records } = await this.executeSQL<{ total: number }>(this.getQueryCountSQL(cls, query.where));
289
283
  const [record] = records;
290
284
  return Total.from(record).total;
291
285
  }
@@ -302,9 +296,8 @@ export abstract class SQLDialect implements DialectState {
302
296
  * Add a sql column
303
297
  */
304
298
  getAddColumnSQL(stack: VisitStack[]): string {
305
- const field = stack[stack.length - 1];
306
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
307
- return `ALTER TABLE ${this.parentTable(stack)} ADD COLUMN ${this.getColumnDefinition(field as FieldConfig)};`;
299
+ const field: FieldConfig = castTo(stack[stack.length - 1]);
300
+ return `ALTER TABLE ${this.parentTable(stack)} ADD COLUMN ${this.getColumnDefinition(field)};`;
308
301
  }
309
302
 
310
303
  /**
@@ -456,8 +449,7 @@ export abstract class SQLDialect implements DialectState {
456
449
  break;
457
450
  }
458
451
  case '$regex': {
459
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
460
- const re = DataUtil.toRegex(v as string);
452
+ const re = DataUtil.toRegex(castTo(v));
461
453
  const src = re.source;
462
454
  const ins = re.flags && re.flags.includes('i');
463
455
 
@@ -506,8 +498,7 @@ export abstract class SQLDialect implements DialectState {
506
498
  break;
507
499
  }
508
500
  case '$lt': case '$gt': case '$gte': case '$lte': {
509
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
510
- const subItems = TypedObject.keys(top as typeof SQL_OPS)
501
+ const subItems = TypedObject.keys(castTo<typeof SQL_OPS>(top))
511
502
  .map(ssk => `${sPath} ${SQL_OPS[ssk]} ${resolve(top[ssk])}`);
512
503
  items.push(subItems.length > 1 ? `(${subItems.join(` ${SQL_OPS.$and} `)})` : subItems[0]);
513
504
  break;
@@ -537,11 +528,11 @@ export abstract class SQLDialect implements DialectState {
537
528
  getWhereGroupingSQL<T>(cls: Class<T>, o: WhereClause<T>): string {
538
529
  const SQL_OPS = this.SQL_OPS;
539
530
 
540
- if (has$And(o)) {
531
+ if (ModelQueryUtil.has$And(o)) {
541
532
  return `(${o.$and.map(x => this.getWhereGroupingSQL<T>(cls, x)).join(` ${SQL_OPS.$and} `)})`;
542
- } else if (has$Or(o)) {
533
+ } else if (ModelQueryUtil.has$Or(o)) {
543
534
  return `(${o.$or.map(x => this.getWhereGroupingSQL<T>(cls, x)).join(` ${SQL_OPS.$or} `)})`;
544
- } else if (has$Not(o)) {
535
+ } else if (ModelQueryUtil.has$Not(o)) {
545
536
  return `${SQL_OPS.$not} (${this.getWhereGroupingSQL<T>(cls, o.$not)})`;
546
537
  } else {
547
538
  return this.getWhereFieldSQL(SQLUtil.classToStack(cls), o);
@@ -554,8 +545,7 @@ export abstract class SQLDialect implements DialectState {
554
545
  getWhereSQL<T>(cls: Class<T>, where?: WhereClause<T>): string {
555
546
  return !where || !Object.keys(where).length ?
556
547
  '' :
557
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
558
- `WHERE ${this.getWhereGroupingSQL(cls, where as WhereClause<T>)}`;
548
+ `WHERE ${this.getWhereGroupingSQL(cls, castTo(where))}`;
559
549
  }
560
550
 
561
551
  /**
@@ -624,15 +614,13 @@ LEFT OUTER JOIN ${from} ON
624
614
  /**
625
615
  * Generate full query
626
616
  */
627
- getQuerySQL<T>(cls: Class<T>, query: Query<T>): string {
617
+ getQuerySQL<T>(cls: Class<T>, query: Query<T>, where?: WhereClause<T>): string {
628
618
  const sortFields = !query.sort ?
629
619
  '' :
630
620
  SQLUtil.orderBy(cls, query.sort)
631
621
  .map(x => this.resolveName(x.stack))
632
622
  .join(', ');
633
623
 
634
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
635
- const where = query.where as WhereClause<T>;
636
624
  return `
637
625
  ${this.getSelectSQL(cls, query.select)}
638
626
  ${this.getFromSQL(cls)}
@@ -647,14 +635,13 @@ ${this.getLimitSQL(cls, query)}`;
647
635
  const parent = stack.length > 1;
648
636
  const array = parent && config.array;
649
637
 
650
- if (config.type && RuntimeIndex.getFunctionMetadata(config.type)?.synthetic) {
638
+ if (config.type && describeFunction(config.type)?.synthetic) {
651
639
  throw new AppError(`Cannot create SQL tables for synthetic types, please convert ${SQLUtil.buildPath(stack)} to a concrete class`);
652
640
  }
653
641
 
654
642
  const fields = SchemaRegistry.has(config.type) ?
655
643
  [...SQLUtil.getFieldsByLocation(stack).local] :
656
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
657
- (array ? [config as FieldConfig] : []);
644
+ (array ? [castTo<FieldConfig>(config)] : []);
658
645
 
659
646
  if (!parent) {
660
647
  let idField = fields.find(x => x.name === this.idField.name);
@@ -674,7 +661,6 @@ ${this.getLimitSQL(cls, query)}`;
674
661
  .filter(x => !!x.trim())
675
662
  .join(',\n ');
676
663
 
677
- /* eslint-disable @typescript-eslint/indent */
678
664
  const out = `
679
665
  CREATE TABLE IF NOT EXISTS ${this.table(stack)} (
680
666
  ${fieldSql}${fieldSql.length ? ',' : ''}
@@ -734,8 +720,7 @@ CREATE TABLE IF NOT EXISTS ${this.table(stack)} (
734
720
  if (DataUtil.isPlainObject(val)) {
735
721
  throw new Error('Unable to supported nested fields for indices');
736
722
  }
737
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
738
- return [key as string, typeof val === 'number' ? val === 1 : (!!val)];
723
+ return [castTo(key), typeof val === 'number' ? val === 1 : (!!val)];
739
724
  });
740
725
  const constraint = `idx_${table}_${fields.map(([f]) => f).join('_')}`;
741
726
  return `CREATE ${idx.type === 'unique' ? 'UNIQUE ' : ''}INDEX ${constraint} ON ${this.ident(table)} (${fields
@@ -809,8 +794,7 @@ CREATE TABLE IF NOT EXISTS ${this.table(stack)} (
809
794
  return;
810
795
  }
811
796
 
812
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
813
- const matrix = instances.map(inst => columns.map(c => this.resolveValue(c, (inst.value as Record<string, string>)[c.name])));
797
+ const matrix = instances.map(inst => columns.map(c => this.resolveValue(c, castTo<Record<string, unknown>>(inst.value)[c.name])));
814
798
 
815
799
  columnNames.push(this.pathField.name);
816
800
  if (hasParent) {
@@ -900,13 +884,11 @@ ${orderBy};`;
900
884
  /**
901
885
  * Get COUNT(1) query
902
886
  */
903
- getQueryCountSQL<T>(cls: Class<T>, query: Query<T>): string {
904
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
905
- const where = query.where as WhereClause<T>;
887
+ getQueryCountSQL<T>(cls: Class<T>, where?: WhereClause<T>): string {
906
888
  return `
907
889
  SELECT COUNT(DISTINCT ${this.rootAlias}.id) as total
908
890
  ${this.getFromSQL(cls)}
909
- ${this.getWhereSQL(cls, where)}`;
891
+ ${this.getWhereSQL(cls, where!)}`;
910
892
  }
911
893
 
912
894
  async fetchDependents<T>(cls: Class<T>, items: T[], select?: SelectClause<T>): Promise<T[]> {
@@ -927,15 +909,12 @@ ${this.getWhereSQL(cls, where)}`;
927
909
  const top = stack[stack.length - 1];
928
910
  const ids = Object.keys(top);
929
911
  const selectTop = selectStack[selectStack.length - 1];
930
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
931
- const fieldKey = config.name as keyof RetainFields<T>;
932
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
933
- const subSelectTop: SelectClause<T> | undefined = selectTop?.[fieldKey] as SelectClause<T> | undefined;
912
+ const fieldKey = castKey<RetainFields<T>>(config.name);
913
+ const subSelectTop: SelectClause<T> | undefined = castTo(selectTop?.[fieldKey]);
934
914
 
935
915
  // See if a selection exists at all
936
916
  const sel: FieldConfig[] = subSelectTop ? fields
937
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
938
- .filter(f => typeof subSelectTop === 'object' && subSelectTop[f.name as typeof fieldKey] === 1)
917
+ .filter(f => typeof subSelectTop === 'object' && subSelectTop[castTo<typeof fieldKey>(f.name)] === 1)
939
918
  : [];
940
919
 
941
920
  if (sel.length) {
@@ -1018,12 +997,10 @@ ${this.getWhereSQL(cls, where)}`;
1018
997
 
1019
998
  await Promise.all([
1020
999
  ...upserts.filter(x => x.stack.length === 1).map(i =>
1021
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
1022
- this.deleteByIds(i.stack, i.records.map(v => (v.value as Record<string, string>)[idx]))
1000
+ this.deleteByIds(i.stack, i.records.map(v => castTo<Record<string, string>>(v.value)[idx]))
1023
1001
  ),
1024
1002
  ...updates.filter(x => x.stack.length === 1).map(i =>
1025
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
1026
- this.deleteByIds(i.stack, i.records.map(v => (v.value as Record<string, string>)[idx]))
1003
+ this.deleteByIds(i.stack, i.records.map(v => castTo<Record<string, string>>(v.value)[idx]))
1027
1004
  ),
1028
1005
  ]);
1029
1006
  }
@@ -1,4 +1,4 @@
1
- import { Class, TypedObject } from '@travetto/base';
1
+ import { castKey, castTo, Class, TypedObject } from '@travetto/runtime';
2
2
  import { SelectClause, SortClause } from '@travetto/model-query';
3
3
  import { ModelRegistry, ModelType, OptionalId } from '@travetto/model';
4
4
  import { SchemaRegistry, ClassConfig, FieldConfig, DataUtil } from '@travetto/schema';
@@ -53,11 +53,9 @@ export class SQLUtil {
53
53
  o[k] = this.cleanResults(dct, o[k]);
54
54
  }
55
55
  }
56
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
57
- return { ...o } as unknown as U[];
56
+ return castTo({ ...o });
58
57
  } else {
59
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
60
- return o as unknown as U;
58
+ return castTo(o);
61
59
  }
62
60
  }
63
61
 
@@ -73,8 +71,7 @@ export class SQLUtil {
73
71
  }
74
72
 
75
73
  if (!cls) { // If a simple type, it is it's own field
76
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
77
- const field = { ...top } as FieldConfig;
74
+ const field: FieldConfig = castTo({ ...top });
78
75
  return {
79
76
  local: [field], localMap: { [field.name]: field },
80
77
  foreign: [], foreignMap: {}
@@ -184,8 +181,7 @@ export class SQLUtil {
184
181
  },
185
182
  onSub: (config) => {
186
183
  const { config: field } = config;
187
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
188
- const topObj = pathObj[pathObj.length - 1] as Record<string, unknown>;
184
+ const topObj: Record<string, unknown> = castTo(pathObj[pathObj.length - 1]);
189
185
  const top = config.path[config.path.length - 1];
190
186
 
191
187
  if (field.name in topObj) {
@@ -213,8 +209,7 @@ export class SQLUtil {
213
209
  },
214
210
  onSimple: (config) => {
215
211
  const { config: field } = config;
216
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
217
- const topObj = pathObj[pathObj.length - 1] as Record<string, unknown>;
212
+ const topObj: Record<string, unknown> = castTo(pathObj[pathObj.length - 1]);
218
213
  const value = topObj[field.name];
219
214
  return handler.onSimple({ ...config, value });
220
215
  }
@@ -234,16 +229,14 @@ export class SQLUtil {
234
229
  let toGet = new Set<string>();
235
230
 
236
231
  for (const [k, v] of TypedObject.entries(select)) {
237
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
238
- const sk = k as string;
239
- if (!DataUtil.isPlainObject(select[k]) && localMap[sk]) {
232
+ if (typeof k === 'string' && !DataUtil.isPlainObject(select[k]) && localMap[k]) {
240
233
  if (!v) {
241
234
  if (toGet.size === 0) {
242
235
  toGet = new Set(SchemaRegistry.get(cls).views[AllViewⲐ].fields);
243
236
  }
244
- toGet.delete(sk);
237
+ toGet.delete(k);
245
238
  } else {
246
- toGet.add(sk);
239
+ toGet.add(k);
247
240
  }
248
241
  }
249
242
  }
@@ -268,8 +261,7 @@ export class SQLUtil {
268
261
  } else {
269
262
  stack.push(field);
270
263
  schema = SchemaRegistry.get(field.type);
271
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
272
- cl = val as Record<string, unknown>;
264
+ cl = castTo(val);
273
265
  }
274
266
  }
275
267
  return found;
@@ -283,29 +275,24 @@ export class SQLUtil {
283
275
  if (field) {
284
276
  const isSimple = SchemaRegistry.has(field.type);
285
277
  for (const el of v) {
286
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
287
- const parentKey = el[dct.parentPathField.name as keyof T] as unknown as string;
288
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
289
- const root = (parent as Record<string, Record<string, unknown>>)[parentKey];
278
+ const parentKey: string = castTo(el[castKey<T>(dct.parentPathField.name)]);
279
+ const root = castTo<Record<string, Record<string, unknown>>>(parent)[parentKey];
280
+ const fieldKey = castKey<(typeof root) | T>(field.name);
290
281
  if (field.array) {
291
- if (!root[field.name]) {
292
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
293
- root[field.name] = [isSimple ? el : el[field.name as keyof T]];
294
- } else {
295
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
296
- (root[field.name] as unknown[]).push(isSimple ? el : el[field.name as keyof T]);
282
+ if (!root[fieldKey]) {
283
+ root[fieldKey] = [isSimple ? el : el[fieldKey]];
284
+ } else if (Array.isArray(root[fieldKey])) {
285
+ root[fieldKey].push(isSimple ? el : el[fieldKey]);
297
286
  }
298
287
  } else {
299
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
300
- root[field.name] = isSimple ? el : el[field.name as keyof T];
288
+ root[fieldKey] = isSimple ? el : el[fieldKey];
301
289
  }
302
290
  }
303
291
  }
304
292
 
305
293
  const mapping: Record<string, T> = {};
306
294
  for (const el of v) {
307
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
308
- const key = el[dct.pathField.name as keyof T];
295
+ const key = el[castKey<T>(dct.pathField.name)];
309
296
  if (typeof key === 'string') {
310
297
  mapping[key] = el;
311
298
  }
package/src/service.ts CHANGED
@@ -4,13 +4,13 @@ import {
4
4
  NotFoundError, ModelRegistry, ExistsError, OptionalId,
5
5
  ModelIdSource
6
6
  } from '@travetto/model';
7
- import { Class } from '@travetto/base';
7
+ import { castTo, Class } from '@travetto/runtime';
8
8
  import { DataUtil, SchemaChange } from '@travetto/schema';
9
9
  import { AsyncContext } from '@travetto/context';
10
10
  import { Injectable } from '@travetto/di';
11
11
  import {
12
12
  ModelQuery, ModelQueryCrudSupport, ModelQueryFacetSupport, ModelQuerySupport,
13
- PageableModelQuery, ValidStringFields, WhereClause, WhereClauseRaw,
13
+ PageableModelQuery, ValidStringFields, WhereClauseRaw, QueryVerifier
14
14
  } from '@travetto/model-query';
15
15
 
16
16
  import { ModelQueryUtil } from '@travetto/model-query/src/internal/service/query';
@@ -95,8 +95,7 @@ export class SQLModelService implements
95
95
  }
96
96
 
97
97
  async #deleteRaw<T extends ModelType>(cls: Class<T>, id: string, where?: WhereClauseRaw<T>, checkExpiry = true): Promise<void> {
98
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
99
- ((where ??= {}) as WhereClauseRaw<ModelType>).id = id;
98
+ castTo<WhereClauseRaw<ModelType>>(where ??= {}).id = id;
100
99
 
101
100
  const count = await this.#dialect.deleteAndGetCount<ModelType>(cls, {
102
101
  where: ModelQueryUtil.getWhereClause(cls, where, checkExpiry)
@@ -191,8 +190,7 @@ export class SQLModelService implements
191
190
 
192
191
  @Connected()
193
192
  async get<T extends ModelType>(cls: Class<T>, id: string): Promise<T> {
194
- const where: WhereClauseRaw<ModelType> = { id };
195
- const res = await this.query(cls, { where });
193
+ const res = await this.query(cls, { where: castTo({ id }) });
196
194
  if (res.length === 1) {
197
195
  return await ModelCrudUtil.load(cls, res[0]);
198
196
  }
@@ -250,7 +248,8 @@ export class SQLModelService implements
250
248
 
251
249
  @Connected()
252
250
  async query<T extends ModelType>(cls: Class<T>, query: PageableModelQuery<T>): Promise<T[]> {
253
- const { records: res } = await this.#exec<T>(this.#dialect.getQuerySQL(cls, ModelQueryUtil.getQueryAndVerify(cls, query)));
251
+ await QueryVerifier.verify(cls, query);
252
+ const { records: res } = await this.#exec<T>(this.#dialect.getQuerySQL(cls, query, ModelQueryUtil.getWhereClause(cls, query.where)));
254
253
  if (ModelRegistry.has(cls)) {
255
254
  await this.#dialect.fetchDependents(cls, res, query && query.select);
256
255
  }
@@ -267,35 +266,40 @@ export class SQLModelService implements
267
266
 
268
267
  @Connected()
269
268
  async queryCount<T extends ModelType>(cls: Class<T>, query: ModelQuery<T>): Promise<number> {
270
- const { records } = await this.#exec<{ total: string | number }>(this.#dialect.getQueryCountSQL(cls, ModelQueryUtil.getQueryAndVerify(cls, query)));
269
+ await QueryVerifier.verify(cls, query);
270
+ const { records } = await this.#exec<{ total: string | number }>(this.#dialect.getQueryCountSQL(cls, ModelQueryUtil.getWhereClause(cls, query.where)));
271
271
  return +records[0].total;
272
272
  }
273
273
 
274
274
  @Connected()
275
275
  @Transactional()
276
276
  async updateOneWithQuery<T extends ModelType>(cls: Class<T>, item: T, query: ModelQuery<T>): Promise<T> {
277
- query = ModelQueryUtil.getQueryWithId(cls, item, query);
278
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
279
- await this.#deleteRaw(cls, item.id, query.where as WhereClause<T>, true);
277
+ await QueryVerifier.verify(cls, query);
278
+ const where = ModelQueryUtil.getWhereClause(cls, query.where);
279
+ where.id = item.id;
280
+ await this.#deleteRaw(cls, item.id, where, true);
280
281
  return await this.create(cls, item);
281
282
  }
282
283
 
283
284
  @Connected()
284
285
  @Transactional()
285
286
  async updateByQuery<T extends ModelType>(cls: Class<T>, query: ModelQuery<T>, data: Partial<T>): Promise<number> {
286
- const { count } = await this.#exec(this.#dialect.getUpdateSQL(SQLUtil.classToStack(cls), data, ModelQueryUtil.getQueryAndVerify(cls, query).where));
287
+ await QueryVerifier.verify(cls, query);
288
+ const { count } = await this.#exec(this.#dialect.getUpdateSQL(SQLUtil.classToStack(cls), data, ModelQueryUtil.getWhereClause(cls, query.where)));
287
289
  return count;
288
290
  }
289
291
 
290
292
  @Connected()
291
293
  @Transactional()
292
294
  async deleteByQuery<T extends ModelType>(cls: Class<T>, query: ModelQuery<T>): Promise<number> {
293
- const { count } = await this.#exec(this.#dialect.getDeleteSQL(SQLUtil.classToStack(cls), ModelQueryUtil.getQueryAndVerify(cls, query, false).where));
295
+ await QueryVerifier.verify(cls, query);
296
+ const { count } = await this.#exec(this.#dialect.getDeleteSQL(SQLUtil.classToStack(cls), ModelQueryUtil.getWhereClause(cls, query.where, false)));
294
297
  return count;
295
298
  }
296
299
 
297
300
  @Connected()
298
301
  async suggest<T extends ModelType>(cls: Class<T>, field: ValidStringFields<T>, prefix?: string, query?: PageableModelQuery<T>): Promise<T[]> {
302
+ await QueryVerifier.verify(cls, query);
299
303
  const q = ModelQuerySuggestUtil.getSuggestQuery<T>(cls, field, prefix, query);
300
304
  const results = await this.query<T>(cls, q);
301
305
  return ModelQuerySuggestUtil.combineSuggestResults(cls, field, prefix, results, (a, b) => b, query?.limit);
@@ -303,16 +307,17 @@ export class SQLModelService implements
303
307
 
304
308
  @Connected()
305
309
  async suggestValues<T extends ModelType>(cls: Class<T>, field: ValidStringFields<T>, prefix?: string, query?: PageableModelQuery<T>): Promise<string[]> {
310
+ await QueryVerifier.verify(cls, query);
306
311
  const q = ModelQuerySuggestUtil.getSuggestFieldQuery(cls, field, prefix, query);
307
312
  const results = await this.query(cls, q);
308
313
 
309
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
310
- const modelTypeField = field as ValidStringFields<ModelType>;
314
+ const modelTypeField: ValidStringFields<ModelType> = castTo(field);
311
315
  return ModelQuerySuggestUtil.combineSuggestResults(cls, modelTypeField, prefix, results, x => x, query?.limit);
312
316
  }
313
317
 
314
318
  @Connected()
315
319
  async facet<T extends ModelType>(cls: Class<T>, field: ValidStringFields<T>, query?: ModelQuery<T>): Promise<{ key: string, count: number }[]> {
320
+ await QueryVerifier.verify(cls, query);
316
321
  const col = this.#dialect.ident(field);
317
322
  const ttl = this.#dialect.ident('count');
318
323
  const key = this.#dialect.ident('key');
@@ -321,7 +326,7 @@ export class SQLModelService implements
321
326
  this.#dialect.getFromSQL(cls),
322
327
  ];
323
328
  q.push(
324
- this.#dialect.getWhereSQL(cls, ModelQueryUtil.getQueryAndVerify(cls, query ?? {}).where)
329
+ this.#dialect.getWhereSQL(cls, ModelQueryUtil.getWhereClause(cls, query?.where))
325
330
  );
326
331
  q.push(
327
332
  `GROUP BY ${col}`,
@@ -1,6 +1,6 @@
1
1
  import { AsyncContext, WithAsyncContext } from '@travetto/context';
2
2
  import { ModelRegistry } from '@travetto/model';
3
- import { Class } from '@travetto/base';
3
+ import { Class } from '@travetto/runtime';
4
4
  import { ChangeEvent } from '@travetto/registry';
5
5
  import { SchemaChange } from '@travetto/schema';
6
6
 
@@ -6,6 +6,7 @@ import { BaseModelSuite } from '@travetto/model/support/test/base';
6
6
 
7
7
  import { VisitStack } from '../../src/internal/util';
8
8
  import { SQLModelService } from '../../src/service';
9
+ import { castTo } from '@travetto/runtime';
9
10
 
10
11
  @Schema()
11
12
  class User {
@@ -66,8 +67,8 @@ export abstract class BaseSQLTest extends BaseModelSuite<SQLModelService> {
66
67
 
67
68
  const dct = await this.dialect;
68
69
  dct.resolveName = (stack: VisitStack[]) => {
69
- const field = stack[stack.length - 1] as FieldConfig;
70
- const parent = stack[stack.length - 2] as FieldConfig;
70
+ const field: FieldConfig = castTo(stack[stack.length - 1]);
71
+ const parent: FieldConfig = castTo(stack[stack.length - 2]);
71
72
  return `${field.owner ? field.owner.name : parent.name}.${field.name}`;
72
73
  };
73
74
 
@@ -79,7 +80,7 @@ export abstract class BaseSQLTest extends BaseModelSuite<SQLModelService> {
79
80
  async testRegEx() {
80
81
  const dct = await this.dialect;
81
82
  dct.resolveName = (stack: VisitStack[]) => {
82
- const field = stack[stack.length - 1] as FieldConfig;
83
+ const field: FieldConfig = castTo(stack[stack.length - 1]);
83
84
  return `${field.owner?.name}.${field.name}`;
84
85
  };
85
86