@travetto/model-sql 5.0.0-rc.8 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/model-sql",
3
- "version": "5.0.0-rc.8",
3
+ "version": "5.0.0",
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.8",
31
- "@travetto/context": "^5.0.0-rc.8",
32
- "@travetto/model": "^5.0.0-rc.8",
33
- "@travetto/model-query": "^5.0.0-rc.8"
30
+ "@travetto/config": "^5.0.0",
31
+ "@travetto/context": "^5.0.0",
32
+ "@travetto/model": "^5.0.0",
33
+ "@travetto/model-query": "^5.0.0"
34
34
  },
35
35
  "peerDependencies": {
36
- "@travetto/command": "^5.0.0-rc.8",
37
- "@travetto/test": "^5.0.0-rc.8"
36
+ "@travetto/command": "^5.0.0",
37
+ "@travetto/test": "^5.0.0"
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/runtime';
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,5 +1,6 @@
1
+ /* eslint-disable @stylistic/indent */
1
2
  import { DataUtil, SchemaRegistry, FieldConfig, Schema } from '@travetto/schema';
2
- import { Class, AppError, TypedObject, describeFunction } from '@travetto/runtime';
3
+ import { Class, AppError, TypedObject, describeFunction, TimeUtil, castTo, castKey } from '@travetto/runtime';
3
4
  import { SelectClause, Query, SortClause, WhereClause, RetainFields } from '@travetto/model-query';
4
5
  import { BulkResponse, IndexConfig } from '@travetto/model';
5
6
  import { PointImpl } from '@travetto/model-query/src/internal/model/point';
@@ -10,13 +11,6 @@ import { SQLUtil, VisitStack } from '../internal/util';
10
11
  import { DeleteWrapper, InsertWrapper, DialectState } from '../internal/types';
11
12
  import { Connection } from '../connection/base';
12
13
 
13
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
14
- const has$And = (o: unknown): o is ({ $and: WhereClause<unknown>[] }) => !!o && '$and' in (o as object);
15
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
16
- const has$Or = (o: unknown): o is ({ $or: WhereClause<unknown>[] }) => !!o && '$or' in (o as object);
17
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
18
- const has$Not = (o: unknown): o is ({ $not: WhereClause<unknown> }) => !!o && '$not' in (o as object);
19
-
20
14
  interface Alias {
21
15
  alias: string;
22
16
  path: VisitStack[];
@@ -195,16 +189,18 @@ export abstract class SQLDialect implements DialectState {
195
189
  const src = DataUtil.toRegex(value).source.replace(/\\b/g, this.regexWordBoundary);
196
190
  return this.quote(src);
197
191
  } else {
198
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
199
- return this.quote(value as string);
192
+ return this.quote(castTo(value));
200
193
  }
201
194
  } else if (conf.type === Boolean) {
202
195
  return `${value ? 'TRUE' : 'FALSE'}`;
203
196
  } else if (conf.type === Number) {
204
197
  return `${value}`;
205
198
  } else if (conf.type === Date) {
206
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
207
- 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
+ }
208
204
  } else if (conf.type === PointImpl && Array.isArray(value)) {
209
205
  return `point(${value[0]},${value[1]})`;
210
206
  } else if (conf.type === Object) {
@@ -275,8 +271,7 @@ export abstract class SQLDialect implements DialectState {
275
271
  * Delete query and return count removed
276
272
  */
277
273
  async deleteAndGetCount<T>(cls: Class<T>, query: Query<T>): Promise<number> {
278
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
279
- 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));
280
275
  return count;
281
276
  }
282
277
 
@@ -284,7 +279,7 @@ export abstract class SQLDialect implements DialectState {
284
279
  * Get the count for a given query
285
280
  */
286
281
  async getCountForQuery<T>(cls: Class<T>, query: Query<T>): Promise<number> {
287
- const { records } = await this.executeSQL<{ total: number }>(this.getQueryCountSQL(cls, query));
282
+ const { records } = await this.executeSQL<{ total: number }>(this.getQueryCountSQL(cls, query.where));
288
283
  const [record] = records;
289
284
  return Total.from(record).total;
290
285
  }
@@ -301,9 +296,8 @@ export abstract class SQLDialect implements DialectState {
301
296
  * Add a sql column
302
297
  */
303
298
  getAddColumnSQL(stack: VisitStack[]): string {
304
- const field = stack[stack.length - 1];
305
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
306
- 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)};`;
307
301
  }
308
302
 
309
303
  /**
@@ -455,8 +449,7 @@ export abstract class SQLDialect implements DialectState {
455
449
  break;
456
450
  }
457
451
  case '$regex': {
458
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
459
- const re = DataUtil.toRegex(v as string);
452
+ const re = DataUtil.toRegex(castTo(v));
460
453
  const src = re.source;
461
454
  const ins = re.flags && re.flags.includes('i');
462
455
 
@@ -505,8 +498,7 @@ export abstract class SQLDialect implements DialectState {
505
498
  break;
506
499
  }
507
500
  case '$lt': case '$gt': case '$gte': case '$lte': {
508
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
509
- const subItems = TypedObject.keys(top as typeof SQL_OPS)
501
+ const subItems = TypedObject.keys(castTo<typeof SQL_OPS>(top))
510
502
  .map(ssk => `${sPath} ${SQL_OPS[ssk]} ${resolve(top[ssk])}`);
511
503
  items.push(subItems.length > 1 ? `(${subItems.join(` ${SQL_OPS.$and} `)})` : subItems[0]);
512
504
  break;
@@ -536,11 +528,11 @@ export abstract class SQLDialect implements DialectState {
536
528
  getWhereGroupingSQL<T>(cls: Class<T>, o: WhereClause<T>): string {
537
529
  const SQL_OPS = this.SQL_OPS;
538
530
 
539
- if (has$And(o)) {
531
+ if (ModelQueryUtil.has$And(o)) {
540
532
  return `(${o.$and.map(x => this.getWhereGroupingSQL<T>(cls, x)).join(` ${SQL_OPS.$and} `)})`;
541
- } else if (has$Or(o)) {
533
+ } else if (ModelQueryUtil.has$Or(o)) {
542
534
  return `(${o.$or.map(x => this.getWhereGroupingSQL<T>(cls, x)).join(` ${SQL_OPS.$or} `)})`;
543
- } else if (has$Not(o)) {
535
+ } else if (ModelQueryUtil.has$Not(o)) {
544
536
  return `${SQL_OPS.$not} (${this.getWhereGroupingSQL<T>(cls, o.$not)})`;
545
537
  } else {
546
538
  return this.getWhereFieldSQL(SQLUtil.classToStack(cls), o);
@@ -553,8 +545,7 @@ export abstract class SQLDialect implements DialectState {
553
545
  getWhereSQL<T>(cls: Class<T>, where?: WhereClause<T>): string {
554
546
  return !where || !Object.keys(where).length ?
555
547
  '' :
556
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
557
- `WHERE ${this.getWhereGroupingSQL(cls, where as WhereClause<T>)}`;
548
+ `WHERE ${this.getWhereGroupingSQL(cls, castTo(where))}`;
558
549
  }
559
550
 
560
551
  /**
@@ -623,15 +614,13 @@ LEFT OUTER JOIN ${from} ON
623
614
  /**
624
615
  * Generate full query
625
616
  */
626
- getQuerySQL<T>(cls: Class<T>, query: Query<T>): string {
617
+ getQuerySQL<T>(cls: Class<T>, query: Query<T>, where?: WhereClause<T>): string {
627
618
  const sortFields = !query.sort ?
628
619
  '' :
629
620
  SQLUtil.orderBy(cls, query.sort)
630
621
  .map(x => this.resolveName(x.stack))
631
622
  .join(', ');
632
623
 
633
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
634
- const where = query.where as WhereClause<T>;
635
624
  return `
636
625
  ${this.getSelectSQL(cls, query.select)}
637
626
  ${this.getFromSQL(cls)}
@@ -652,8 +641,7 @@ ${this.getLimitSQL(cls, query)}`;
652
641
 
653
642
  const fields = SchemaRegistry.has(config.type) ?
654
643
  [...SQLUtil.getFieldsByLocation(stack).local] :
655
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
656
- (array ? [config as FieldConfig] : []);
644
+ (array ? [castTo<FieldConfig>(config)] : []);
657
645
 
658
646
  if (!parent) {
659
647
  let idField = fields.find(x => x.name === this.idField.name);
@@ -673,7 +661,6 @@ ${this.getLimitSQL(cls, query)}`;
673
661
  .filter(x => !!x.trim())
674
662
  .join(',\n ');
675
663
 
676
- /* eslint-disable @typescript-eslint/indent */
677
664
  const out = `
678
665
  CREATE TABLE IF NOT EXISTS ${this.table(stack)} (
679
666
  ${fieldSql}${fieldSql.length ? ',' : ''}
@@ -733,8 +720,7 @@ CREATE TABLE IF NOT EXISTS ${this.table(stack)} (
733
720
  if (DataUtil.isPlainObject(val)) {
734
721
  throw new Error('Unable to supported nested fields for indices');
735
722
  }
736
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
737
- return [key as string, typeof val === 'number' ? val === 1 : (!!val)];
723
+ return [castTo(key), typeof val === 'number' ? val === 1 : (!!val)];
738
724
  });
739
725
  const constraint = `idx_${table}_${fields.map(([f]) => f).join('_')}`;
740
726
  return `CREATE ${idx.type === 'unique' ? 'UNIQUE ' : ''}INDEX ${constraint} ON ${this.ident(table)} (${fields
@@ -808,8 +794,7 @@ CREATE TABLE IF NOT EXISTS ${this.table(stack)} (
808
794
  return;
809
795
  }
810
796
 
811
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
812
- 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])));
813
798
 
814
799
  columnNames.push(this.pathField.name);
815
800
  if (hasParent) {
@@ -899,13 +884,11 @@ ${orderBy};`;
899
884
  /**
900
885
  * Get COUNT(1) query
901
886
  */
902
- getQueryCountSQL<T>(cls: Class<T>, query: Query<T>): string {
903
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
904
- const where = query.where as WhereClause<T>;
887
+ getQueryCountSQL<T>(cls: Class<T>, where?: WhereClause<T>): string {
905
888
  return `
906
889
  SELECT COUNT(DISTINCT ${this.rootAlias}.id) as total
907
890
  ${this.getFromSQL(cls)}
908
- ${this.getWhereSQL(cls, where)}`;
891
+ ${this.getWhereSQL(cls, where!)}`;
909
892
  }
910
893
 
911
894
  async fetchDependents<T>(cls: Class<T>, items: T[], select?: SelectClause<T>): Promise<T[]> {
@@ -926,15 +909,12 @@ ${this.getWhereSQL(cls, where)}`;
926
909
  const top = stack[stack.length - 1];
927
910
  const ids = Object.keys(top);
928
911
  const selectTop = selectStack[selectStack.length - 1];
929
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
930
- const fieldKey = config.name as keyof RetainFields<T>;
931
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
932
- 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]);
933
914
 
934
915
  // See if a selection exists at all
935
916
  const sel: FieldConfig[] = subSelectTop ? fields
936
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
937
- .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)
938
918
  : [];
939
919
 
940
920
  if (sel.length) {
@@ -1017,12 +997,10 @@ ${this.getWhereSQL(cls, where)}`;
1017
997
 
1018
998
  await Promise.all([
1019
999
  ...upserts.filter(x => x.stack.length === 1).map(i =>
1020
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
1021
- 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]))
1022
1001
  ),
1023
1002
  ...updates.filter(x => x.stack.length === 1).map(i =>
1024
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
1025
- 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]))
1026
1004
  ),
1027
1005
  ]);
1028
1006
  }
@@ -1,4 +1,4 @@
1
- import { Class, TypedObject } from '@travetto/runtime';
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/runtime';
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}`,
@@ -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