@travetto/model-sql 2.1.4 → 2.2.1

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/README.md CHANGED
@@ -20,7 +20,7 @@ or
20
20
  npm install pg
21
21
  ```
22
22
 
23
- This module provides a [SQL](https://en.wikipedia.org/wiki/SQL)-based implementation for the [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") module. This source allows the [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") module to read, write and query against [SQL](https://en.wikipedia.org/wiki/SQL) databases. In development mode, the [SQLModelService](https://github.com/travetto/travetto/tree/main/module/model-sql/src/service.ts#L36) will also modify the database schema in real time to minimize impact to development.
23
+ This module provides a [SQL](https://en.wikipedia.org/wiki/SQL)-based implementation for the [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") module. This source allows the [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") module to read, write and query against [SQL](https://en.wikipedia.org/wiki/SQL) databases. In development mode, the [SQLModelService](https://github.com/travetto/travetto/tree/main/module/model-sql/src/service.ts#L38) will also modify the database schema in real time to minimize impact to development.
24
24
 
25
25
  The schema generated will not generally map to existing tables as it is attempting to produce a document store like experience on top of
26
26
  a [SQL](https://en.wikipedia.org/wiki/SQL) database. Every table generated will have a `path_id` which determines it's location in the document hierarchy as well as sub tables will have a `parent_path_id` to associate records with the parent values.
@@ -34,7 +34,7 @@ The current SQL client support stands at:
34
34
 
35
35
  **Note**: Wider client support will roll out as usage increases.
36
36
 
37
- Supported featrues:
37
+ Supported features:
38
38
 
39
39
  * [CRUD](https://github.com/travetto/travetto/tree/main/module/model/src/service/crud.ts#L11)
40
40
  * [Bulk](https://github.com/travetto/travetto/tree/main/module/model/src/service/bulk.ts#L23)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@travetto/model-sql",
3
3
  "displayName": "SQL Model Service",
4
- "version": "2.1.4",
4
+ "version": "2.2.1",
5
5
  "description": "SQL backing for the travetto model module, with real-time modeling support for SQL schemas.",
6
6
  "keywords": [
7
7
  "sql",
@@ -28,13 +28,13 @@
28
28
  "directory": "module/model-sql"
29
29
  },
30
30
  "dependencies": {
31
- "@travetto/config": "^2.1.4",
32
- "@travetto/context": "^2.1.4",
33
- "@travetto/model": "^2.1.4",
34
- "@travetto/model-query": "2.1.4"
31
+ "@travetto/config": "^2.2.1",
32
+ "@travetto/context": "^2.2.1",
33
+ "@travetto/model": "^2.2.1",
34
+ "@travetto/model-query": "2.2.1"
35
35
  },
36
36
  "devDependencies": {
37
- "@travetto/app": "^2.1.4"
37
+ "@travetto/app": "^2.2.1"
38
38
  },
39
39
  "optionalPeerDependencies": {
40
40
  "@types/mysql": "^2.15.21",
@@ -24,14 +24,14 @@ export abstract class Connection<C = unknown> {
24
24
  * Get active connection
25
25
  */
26
26
  get active(): C {
27
- return this.context.get(ContextActiveⲐ) as C;
27
+ return this.context.get<C>(ContextActiveⲐ);
28
28
  }
29
29
 
30
30
  /**
31
31
  * Get active tx state
32
32
  */
33
- get activeTx() {
34
- return !!this.context.get(TxActiveⲐ) as boolean;
33
+ get activeTx(): boolean {
34
+ return !!this.context.get<boolean>(TxActiveⲐ);
35
35
  }
36
36
 
37
37
  /**
@@ -85,7 +85,7 @@ export abstract class Connection<C = unknown> {
85
85
  * @param op
86
86
  * @param args
87
87
  */
88
- async * iterateWithActive<R>(op: () => AsyncGenerator<R>) {
88
+ async * iterateWithActive<R>(op: () => AsyncGenerator<R>): AsyncIterable<R> {
89
89
  if (this.active) {
90
90
  yield* op();
91
91
  }
@@ -134,7 +134,7 @@ export abstract class Connection<C = unknown> {
134
134
  /**
135
135
  * Start a transaction
136
136
  */
137
- async startTx(conn: C, transactionId?: string) {
137
+ async startTx(conn: C, transactionId?: string): Promise<void> {
138
138
  if (transactionId) {
139
139
  if (this.nestedTransactions) {
140
140
  await this.execute(conn, `SAVEPOINT ${transactionId};`);
@@ -150,7 +150,7 @@ export abstract class Connection<C = unknown> {
150
150
  /**
151
151
  * Commit active transaction
152
152
  */
153
- async commitTx(conn: C, transactionId?: string) {
153
+ async commitTx(conn: C, transactionId?: string): Promise<void> {
154
154
  if (transactionId) {
155
155
  if (this.nestedTransactions) {
156
156
  await this.execute(conn, `RELEASE SAVEPOINT ${transactionId};`);
@@ -163,7 +163,7 @@ export abstract class Connection<C = unknown> {
163
163
  /**
164
164
  * Rollback active transaction
165
165
  */
166
- async rollbackTx(conn: C, transactionId?: string) {
166
+ async rollbackTx(conn: C, transactionId?: string): Promise<void> {
167
167
  if (transactionId) {
168
168
  if (this.isolatedTransactions) {
169
169
  await this.execute(conn, `ROLLBACK TO ${transactionId};`);
@@ -12,12 +12,12 @@ export interface ConnectionAware<C = unknown> {
12
12
  * Decorator to ensure a method runs with a valid connection
13
13
  */
14
14
  export function Connected<T extends ConnectionAware>() {
15
- return function (target: T, prop: string | symbol, desc: MethodDescriptor) {
15
+ return function (target: T, prop: string | symbol, desc: MethodDescriptor): void {
16
16
  const og = desc.value!;
17
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
18
- desc.value = async function (this: any, ...args: any[]) {
17
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
18
+ desc.value = async function (this: T, ...args: unknown[]) {
19
19
  return this.conn.runWithActive(() => og.call(this, ...args));
20
- };
20
+ } as typeof og;
21
21
  };
22
22
  }
23
23
 
@@ -25,12 +25,12 @@ export function Connected<T extends ConnectionAware>() {
25
25
  * Decorator to ensure a method runs with a valid connection
26
26
  */
27
27
  export function ConnectedIterator<T extends ConnectionAware>() {
28
- return function (target: T, prop: string | symbol, desc: MethodDescriptor) {
28
+ return function (target: T, prop: string | symbol, desc: MethodDescriptor): void {
29
29
  const og = desc.value!;
30
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
- desc.value = async function* (this: any, ...args: any[]) {
30
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
31
+ desc.value = async function* (this: T, ...args: unknown[]) {
32
32
  yield* this.conn.iterateWithActive(() => og.call(this, ...args));
33
- };
33
+ } as typeof og;
34
34
  };
35
35
  }
36
36
 
@@ -38,11 +38,11 @@ export function ConnectedIterator<T extends ConnectionAware>() {
38
38
  * Decorator to ensure a method runs with a valid transaction
39
39
  */
40
40
  export function Transactional<T extends ConnectionAware>(mode: TransactionType = 'required') {
41
- return function (target: T, prop: string | symbol, desc: MethodDescriptor) {
41
+ return function (target: T, prop: string | symbol, desc: MethodDescriptor): void {
42
42
  const og = desc.value!;
43
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
- desc.value = function (this: any, ...args: any[]) {
43
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
44
+ desc.value = function (this: T, ...args: unknown[]) {
45
45
  return this.conn.runWithTransaction(mode, () => og.call(this, ...args));
46
- };
46
+ } as typeof og;
47
47
  };
48
48
  }
@@ -1,6 +1,6 @@
1
1
  import { SchemaRegistry, FieldConfig, Schema } from '@travetto/schema';
2
2
  import { Class, Util, AppError } from '@travetto/base';
3
- import { SelectClause, Query, SortClause, WhereClause } from '@travetto/model-query';
3
+ import { SelectClause, Query, SortClause, WhereClause, RetainFields } from '@travetto/model-query';
4
4
  import { BulkResponse, IndexConfig } from '@travetto/model';
5
5
  import { PointImpl } from '@travetto/model-query/src/internal/model/point';
6
6
  import { ModelType } from '@travetto/model/src/types/model';
@@ -10,8 +10,11 @@ import { SQLUtil, VisitStack } from '../internal/util';
10
10
  import { DeleteWrapper, InsertWrapper, DialectState } from '../internal/types';
11
11
  import { Connection } from '../connection/base';
12
12
 
13
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
13
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
14
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
15
18
  const has$Not = (o: unknown): o is ({ $not: WhereClause<unknown> }) => !!o && '$not' in (o as object);
16
19
 
17
20
  interface Alias {
@@ -24,15 +27,14 @@ class Total {
24
27
  total: number;
25
28
  }
26
29
 
27
- function makeField(name: string, type: Class, required: boolean, extra: Partial<FieldConfig>) {
30
+ function makeField(name: string, type: Class, required: boolean, extra: Partial<FieldConfig>): FieldConfig {
28
31
  return {
29
32
  name,
30
- owner: null,
31
33
  type,
32
34
  array: false,
33
35
  ...(required ? { required: { active: true } } : {}),
34
36
  ...extra
35
- } as FieldConfig;
37
+ };
36
38
  }
37
39
 
38
40
  /**
@@ -81,7 +83,7 @@ export abstract class SQLDialect implements DialectState {
81
83
  BOOLEAN: 'BOOLEAN',
82
84
  TINYINT: 'TINYINT',
83
85
  SMALLINT: 'SMALLINT',
84
- MEDIUMINIT: 'MEDIUMINT',
86
+ MEDIUMINT: 'MEDIUMINT',
85
87
  INT: 'INT',
86
88
  BIGINT: 'BIGINT',
87
89
  TIMESTAMP: 'TIMESTAMP',
@@ -91,7 +93,7 @@ export abstract class SQLDialect implements DialectState {
91
93
  /**
92
94
  * Column types with inputs
93
95
  */
94
- PARAMETERIZED_COLUMN_TYPES: Record<'VARCHAR' | 'DECIMAL', (...nums: number[]) => string> = {
96
+ PARAMETERIZED_COLUMN_TYPES: Record<'VARCHAR' | 'DECIMAL', (...values: number[]) => string> = {
95
97
  VARCHAR: n => `VARCHAR(${n})`,
96
98
  DECIMAL: (d, p) => `DECIMAL(${d},${p})`
97
99
  };
@@ -153,7 +155,7 @@ export abstract class SQLDialect implements DialectState {
153
155
  */
154
156
  abstract hash(inp: string): string;
155
157
 
156
- executeSQL<T>(sql: string) {
158
+ executeSQL<T>(sql: string): Promise<{ records: T[], count: number }> {
157
159
  return this.conn.execute<T>(this.conn.active, sql);
158
160
  }
159
161
 
@@ -171,15 +173,15 @@ export abstract class SQLDialect implements DialectState {
171
173
  * @param value
172
174
  * @returns
173
175
  */
174
- resolveDateValue(value: Date) {
175
- const [day, time] = (value as Date).toISOString().split(/[TZ]/);
176
+ resolveDateValue(value: Date): string {
177
+ const [day, time] = value.toISOString().split(/[TZ]/);
176
178
  return this.quote(`${day} ${time}`);
177
179
  }
178
180
 
179
181
  /**
180
182
  * Convert value to SQL valid representation
181
183
  */
182
- resolveValue(conf: FieldConfig, value: unknown) {
184
+ resolveValue(conf: FieldConfig, value: unknown): string {
183
185
  if (value === undefined || value === null) {
184
186
  return 'NULL';
185
187
  } else if (conf.type === String) {
@@ -187,6 +189,7 @@ export abstract class SQLDialect implements DialectState {
187
189
  const src = Util.toRegex(value).source.replace(/\\b/g, this.regexWordBoundary);
188
190
  return this.quote(src);
189
191
  } else {
192
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
190
193
  return this.quote(value as string);
191
194
  }
192
195
  } else if (conf.type === Boolean) {
@@ -194,6 +197,7 @@ export abstract class SQLDialect implements DialectState {
194
197
  } else if (conf.type === Number) {
195
198
  return `${value}`;
196
199
  } else if (conf.type === Date) {
200
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
197
201
  return this.resolveDateValue(ModelQueryUtil.resolveComparator(value) as Date);
198
202
  } else if (conf.type === PointImpl && Array.isArray(value)) {
199
203
  return `point(${value[0]},${value[1]})`;
@@ -206,7 +210,7 @@ export abstract class SQLDialect implements DialectState {
206
210
  /**
207
211
  * Get column type from field config
208
212
  */
209
- getColumnType(conf: FieldConfig) {
213
+ getColumnType(conf: FieldConfig): string {
210
214
  let type: string = '';
211
215
 
212
216
  if (conf.type === Number) {
@@ -221,7 +225,7 @@ export abstract class SQLDialect implements DialectState {
221
225
  } else if (digits < 5) {
222
226
  type = this.COLUMN_TYPES.SMALLINT;
223
227
  } else if (digits < 7) {
224
- type = this.COLUMN_TYPES.MEDIUMINIT;
228
+ type = this.COLUMN_TYPES.MEDIUMINT;
225
229
  } else if (digits < 10) {
226
230
  type = this.COLUMN_TYPES.INT;
227
231
  } else {
@@ -253,7 +257,7 @@ export abstract class SQLDialect implements DialectState {
253
257
  /**
254
258
  * FieldConfig to Column definition
255
259
  */
256
- getColumnDefinition(conf: FieldConfig) {
260
+ getColumnDefinition(conf: FieldConfig): string | undefined {
257
261
  const type = this.getColumnType(conf);
258
262
  if (!type) {
259
263
  return;
@@ -264,7 +268,8 @@ export abstract class SQLDialect implements DialectState {
264
268
  /**
265
269
  * Delete query and return count removed
266
270
  */
267
- async deleteAndGetCount<T>(cls: Class<T>, query: Query<T>) {
271
+ async deleteAndGetCount<T>(cls: Class<T>, query: Query<T>): Promise<number> {
272
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
268
273
  const { count } = await this.executeSQL(this.getDeleteSQL(SQLUtil.classToStack(cls), query.where as WhereClause<T>));
269
274
  return count;
270
275
  }
@@ -272,7 +277,7 @@ export abstract class SQLDialect implements DialectState {
272
277
  /**
273
278
  * Get the count for a given query
274
279
  */
275
- async getCountForQuery<T>(cls: Class<T>, query: Query<T>) {
280
+ async getCountForQuery<T>(cls: Class<T>, query: Query<T>): Promise<number> {
276
281
  const { records } = await this.executeSQL<{ total: number }>(this.getQueryCountSQL(cls, query));
277
282
  const [record] = records;
278
283
  return Total.from(record).total;
@@ -281,7 +286,7 @@ export abstract class SQLDialect implements DialectState {
281
286
  /**
282
287
  * Remove a sql column
283
288
  */
284
- getDropColumnSQL(stack: VisitStack[]) {
289
+ getDropColumnSQL(stack: VisitStack[]): string {
285
290
  const field = stack[stack.length - 1];
286
291
  return `ALTER TABLE ${this.parentTable(stack)} DROP COLUMN ${this.ident(field.name)};`;
287
292
  }
@@ -289,8 +294,9 @@ export abstract class SQLDialect implements DialectState {
289
294
  /**
290
295
  * Add a sql column
291
296
  */
292
- getAddColumnSQL(stack: VisitStack[]) {
297
+ getAddColumnSQL(stack: VisitStack[]): string {
293
298
  const field = stack[stack.length - 1];
299
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
294
300
  return `ALTER TABLE ${this.parentTable(stack)} ADD COLUMN ${this.getColumnDefinition(field as FieldConfig)};`;
295
301
  }
296
302
 
@@ -309,49 +315,49 @@ export abstract class SQLDialect implements DialectState {
309
315
  /**
310
316
  * Determine table/field namespace for a given stack location
311
317
  */
312
- namespace(stack: VisitStack[]) {
318
+ namespace(stack: VisitStack[]): string {
313
319
  return `${this.ns}${SQLUtil.buildTable(stack)}`;
314
320
  }
315
321
 
316
322
  /**
317
323
  * Determine namespace for a given stack location - 1
318
324
  */
319
- namespaceParent(stack: VisitStack[]) {
325
+ namespaceParent(stack: VisitStack[]): string {
320
326
  return this.namespace(stack.slice(0, stack.length - 1));
321
327
  }
322
328
 
323
329
  /**
324
330
  * Determine table name for a given stack location
325
331
  */
326
- table(stack: VisitStack[]) {
332
+ table(stack: VisitStack[]): string {
327
333
  return this.ident(this.namespace(stack));
328
334
  }
329
335
 
330
336
  /**
331
337
  * Determine parent table name for a given stack location
332
338
  */
333
- parentTable(stack: VisitStack[]) {
339
+ parentTable(stack: VisitStack[]): string {
334
340
  return this.table(stack.slice(0, stack.length - 1));
335
341
  }
336
342
 
337
343
  /**
338
344
  * Get lookup key for cls and name
339
345
  */
340
- getKey(cls: Class, name: string) {
346
+ getKey(cls: Class, name: string): string {
341
347
  return `${cls.name}:${name}`;
342
348
  }
343
349
 
344
350
  /**
345
351
  * Alias a field for usage
346
352
  */
347
- alias(field: string | FieldConfig, alias: string = this.rootAlias) {
353
+ alias(field: string | FieldConfig, alias: string = this.rootAlias): string {
348
354
  return `${alias}.${this.ident(field)}`;
349
355
  }
350
356
 
351
357
  /**
352
358
  * Get alias cache for the stack
353
359
  */
354
- getAliasCache(stack: VisitStack[], resolve: (path: VisitStack[]) => string) {
360
+ getAliasCache(stack: VisitStack[], resolve: (path: VisitStack[]) => string): Map<string, Alias> {
355
361
  const cls = stack[0].type;
356
362
 
357
363
  if (this.aliasCache.has(cls)) {
@@ -402,7 +408,7 @@ export abstract class SQLDialect implements DialectState {
402
408
  const { foreignMap, localMap } = SQLUtil.getFieldsByLocation(stack);
403
409
  const SQL_OPS = this.SQL_OPS;
404
410
 
405
- for (const key of Object.keys(o) as ((keyof (typeof o)))[]) {
411
+ for (const key of Object.keys(o)) {
406
412
  const top = o[key];
407
413
  const field = localMap[key] ?? foreignMap[key];
408
414
  if (!field) {
@@ -437,18 +443,19 @@ export abstract class SQLDialect implements DialectState {
437
443
  const arr = [...new Set(Array.isArray(v) ? v : [v])].map(el => resolve(el));
438
444
  const valueTable = this.parentTable(sStack);
439
445
  const alias = `_all_${sStack.length}`;
440
- const ppath = this.ident(this.parentPathField.name);
441
- const rppath = this.resolveName([...sStack, field, this.parentPathField]);
446
+ const pPath = this.ident(this.parentPathField.name);
447
+ const rpPath = this.resolveName([...sStack, field, this.parentPathField]);
442
448
 
443
449
  items.push(`${arr.length} = (
444
450
  SELECT COUNT(DISTINCT ${alias}.${this.ident(field.name)})
445
451
  FROM ${valueTable} ${alias}
446
- WHERE ${alias}.${ppath} = ${rppath}
452
+ WHERE ${alias}.${pPath} = ${rpPath}
447
453
  AND ${alias}.${this.ident(field.name)} IN (${arr.join(',')})
448
454
  )`);
449
455
  break;
450
456
  }
451
457
  case '$regex': {
458
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
452
459
  const re = Util.toRegex(v as string);
453
460
  const src = re.source;
454
461
  const ins = re.flags && re.flags.includes('i');
@@ -482,7 +489,8 @@ export abstract class SQLDialect implements DialectState {
482
489
  break;
483
490
  }
484
491
  case '$lt': case '$gt': case '$gte': case '$lte': {
485
- const subItems = (Object.keys(top) as (keyof typeof SQL_OPS)[])
492
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
493
+ const subItems = Object.keys(top as typeof SQL_OPS)
486
494
  .map(ssk => `${sPath} ${SQL_OPS[ssk]} ${resolve(top[ssk])}`);
487
495
  items.push(subItems.length > 1 ? `(${subItems.join(` ${SQL_OPS.$and} `)})` : subItems[0]);
488
496
  break;
@@ -529,6 +537,7 @@ export abstract class SQLDialect implements DialectState {
529
537
  getWhereSQL<T>(cls: Class<T>, where?: WhereClause<T>): string {
530
538
  return !where || !Object.keys(where).length ?
531
539
  '' :
540
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
532
541
  `WHERE ${this.getWhereGroupingSQL(cls, where as WhereClause<T>)}`;
533
542
  }
534
543
 
@@ -598,16 +607,19 @@ LEFT OUTER JOIN ${from} ON
598
607
  /**
599
608
  * Generate full query
600
609
  */
601
- getQuerySQL<T>(cls: Class<T>, query: Query<T>) {
610
+ getQuerySQL<T>(cls: Class<T>, query: Query<T>): string {
602
611
  const sortFields = !query.sort ?
603
612
  '' :
604
613
  SQLUtil.orderBy(cls, query.sort)
605
614
  .map(x => this.resolveName(x.stack))
606
615
  .join(', ');
616
+
617
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
618
+ const where = query.where as WhereClause<T>;
607
619
  return `
608
620
  ${this.getSelectSQL(cls, query.select)}
609
621
  ${this.getFromSQL(cls)}
610
- ${this.getWhereSQL(cls, query.where as WhereClause<T>)}
622
+ ${this.getWhereSQL(cls, where)}
611
623
  ${this.getGroupBySQL(cls, query)}${sortFields ? `, ${sortFields}` : ''}
612
624
  ${this.getOrderBySQL(cls, query.sort)}
613
625
  ${this.getLimitSQL(cls, query)}`;
@@ -624,6 +636,7 @@ ${this.getLimitSQL(cls, query)}`;
624
636
 
625
637
  const fields = SchemaRegistry.has(config.type) ?
626
638
  [...SQLUtil.getFieldsByLocation(stack).local] :
639
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
627
640
  (array ? [config as FieldConfig] : []);
628
641
 
629
642
  if (!parent) {
@@ -660,19 +673,19 @@ CREATE TABLE IF NOT EXISTS ${this.table(stack)} (
660
673
  /**
661
674
  * Generate drop SQL
662
675
  */
663
- getDropTableSQL(stack: VisitStack[]) {
676
+ getDropTableSQL(stack: VisitStack[]): string {
664
677
  return `DROP TABLE IF EXISTS ${this.table(stack)}; `;
665
678
  }
666
679
 
667
680
  /**
668
681
  * Generate truncate SQL
669
682
  */
670
- getTruncateTableSQL(stack: VisitStack[]) {
683
+ getTruncateTableSQL(stack: VisitStack[]): string {
671
684
  return `TRUNCATE ${this.table(stack)}; `;
672
685
  }
673
686
 
674
687
  /**
675
- * Get all table creat queries for a class
688
+ * Get all table create queries for a class
676
689
  */
677
690
  getCreateAllTablesSQL(cls: Class): string[] {
678
691
  const out: string[] = [];
@@ -697,11 +710,12 @@ CREATE TABLE IF NOT EXISTS ${this.table(stack)} (
697
710
  getCreateIndexSQL<T extends ModelType>(cls: Class<T>, idx: IndexConfig<T>): string {
698
711
  const table = this.namespace(SQLUtil.classToStack(cls));
699
712
  const fields: [string, boolean][] = idx.fields.map(x => {
700
- const key = Object.keys(x)[0] as keyof typeof x;
713
+ const key = Object.keys(x)[0];
701
714
  const val = x[key];
702
715
  if (Util.isPlainObject(val)) {
703
716
  throw new Error('Unable to supported nested fields for indices');
704
717
  }
718
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
705
719
  return [key as string, typeof val === 'number' ? val === 1 : (!!val)];
706
720
  });
707
721
  const constraint = `idx_${table}_${fields.map(([f]) => f).join('_')}`;
@@ -751,7 +765,7 @@ CREATE TABLE IF NOT EXISTS ${this.table(stack)} (
751
765
  const isArray = !!config.array;
752
766
 
753
767
  if (isArray) {
754
- const newInstances = [] as InsertWrapper['records'];
768
+ const newInstances: typeof instances = [];
755
769
  for (const el of instances) {
756
770
  if (el.value === null || el.value === undefined) {
757
771
  continue;
@@ -776,6 +790,7 @@ CREATE TABLE IF NOT EXISTS ${this.table(stack)} (
776
790
  return;
777
791
  }
778
792
 
793
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
779
794
  const matrix = instances.map(inst => columns.map(c => this.resolveValue(c, (inst.value as Record<string, string>)[c.name])));
780
795
 
781
796
  columnNames.push(this.pathField.name);
@@ -812,7 +827,7 @@ ${matrix.map(row => `(${row.join(', ')})`).join(',\n')};`;
812
827
  */
813
828
  getAllInsertSQL<T extends ModelType>(cls: Class<T>, instance: T): string[] {
814
829
  const out: string[] = [];
815
- const add = (text?: string) => text && out.push(text);
830
+ const add = (text?: string): void => { text && out.push(text); };
816
831
  SQLUtil.visitSchemaInstance(cls, instance, {
817
832
  onRoot: ({ value, path }) => add(this.getInsertSQL(path, [{ stack: path, value }])),
818
833
  onSub: ({ value, path }) => add(this.getInsertSQL(path, [{ stack: path, value }])),
@@ -824,7 +839,7 @@ ${matrix.map(row => `(${row.join(', ')})`).join(',\n')};`;
824
839
  /**
825
840
  * Simple data base updates
826
841
  */
827
- getUpdateSQL(stack: VisitStack[], data: Record<string, unknown>, where?: WhereClause<unknown>) {
842
+ getUpdateSQL(stack: VisitStack[], data: Record<string, unknown>, where?: WhereClause<unknown>): string {
828
843
  const { type } = stack[stack.length - 1];
829
844
  const { localMap } = SQLUtil.getFieldsByLocation(stack);
830
845
  return `
@@ -866,18 +881,21 @@ ${orderBy};`;
866
881
  /**
867
882
  * Get COUNT(1) query
868
883
  */
869
- getQueryCountSQL<T>(cls: Class<T>, query: Query<T>) {
884
+ getQueryCountSQL<T>(cls: Class<T>, query: Query<T>): string {
885
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
886
+ const where = query.where as WhereClause<T>;
870
887
  return `
871
888
  SELECT COUNT(DISTINCT ${this.rootAlias}.id) as total
872
889
  ${this.getFromSQL(cls)}
873
- ${this.getWhereSQL(cls, query.where as WhereClause<T>)}`;
890
+ ${this.getWhereSQL(cls, where)}`;
874
891
  }
875
892
 
876
893
  async fetchDependents<T>(cls: Class<T>, items: T[], select?: SelectClause<T>): Promise<T[]> {
877
894
  const stack: Record<string, unknown>[] = [];
878
895
  const selectStack: (SelectClause<T> | undefined)[] = [];
879
896
 
880
- const buildSet = (children: unknown[], field?: FieldConfig) => SQLUtil.collectDependents(this, stack[stack.length - 1], children, field);
897
+ const buildSet = (children: unknown[], field?: FieldConfig): Record<string, unknown> =>
898
+ SQLUtil.collectDependents(this, stack[stack.length - 1], children, field);
881
899
 
882
900
  await SQLUtil.visitSchema(SchemaRegistry.get(cls), {
883
901
  onRoot: async (config) => {
@@ -890,12 +908,15 @@ ${this.getWhereSQL(cls, query.where as WhereClause<T>)}`;
890
908
  const top = stack[stack.length - 1];
891
909
  const ids = Object.keys(top);
892
910
  const selectTop = selectStack[selectStack.length - 1];
893
- // @ts-ignore
894
- const subSelectTop: SelectClause<T> | undefined = selectTop?.[config.name];
911
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
912
+ const fieldKey = config.name as keyof RetainFields<T>;
913
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
914
+ const subSelectTop: SelectClause<T> | undefined = selectTop?.[fieldKey] as SelectClause<T> | undefined;
895
915
 
896
916
  // See if a selection exists at all
897
917
  const sel: FieldConfig[] = subSelectTop ? fields
898
- .filter(f => subSelectTop[f.name as keyof SelectClause<T>] === 1)
918
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
919
+ .filter(f => typeof subSelectTop === 'object' && subSelectTop[f.name as typeof fieldKey] === 1)
899
920
  : [];
900
921
 
901
922
  if (sel.length) {
@@ -924,7 +945,7 @@ ${this.getWhereSQL(cls, query.where as WhereClause<T>)}`;
924
945
  }
925
946
  }
926
947
  },
927
- onSimple: async ({ config, path }) => {
948
+ onSimple: async ({ config, path }): Promise<void> => {
928
949
  const top = stack[stack.length - 1];
929
950
  const ids = Object.keys(top);
930
951
  if (ids.length) {
@@ -943,7 +964,7 @@ ${this.getWhereSQL(cls, query.where as WhereClause<T>)}`;
943
964
  /**
944
965
  * Delete all ids
945
966
  */
946
- async deleteByIds(stack: VisitStack[], ids: string[]) {
967
+ async deleteByIds(stack: VisitStack[], ids: string[]): Promise<number> {
947
968
  return this.deleteAndGetCount<ModelType>(stack[stack.length - 1].type, {
948
969
  where: {
949
970
  [stack.length === 1 ? this.idField.name : this.pathField.name]: {
@@ -956,10 +977,10 @@ ${this.getWhereSQL(cls, query.where as WhereClause<T>)}`;
956
977
  /**
957
978
  * Do bulk process
958
979
  */
959
- async bulkProcess(dels: DeleteWrapper[], inserts: InsertWrapper[], upserts: InsertWrapper[], updates: InsertWrapper[]): Promise<BulkResponse> {
980
+ async bulkProcess(deletes: DeleteWrapper[], inserts: InsertWrapper[], upserts: InsertWrapper[], updates: InsertWrapper[]): Promise<BulkResponse> {
960
981
  const out = {
961
982
  counts: {
962
- delete: dels.reduce((acc, el) => acc + el.ids.length, 0),
983
+ delete: deletes.reduce((acc, el) => acc + el.ids.length, 0),
963
984
  error: 0,
964
985
  insert: inserts.filter(x => x.stack.length === 1).reduce((acc, el) => acc + el.records.length, 0),
965
986
  update: updates.filter(x => x.stack.length === 1).reduce((acc, el) => acc + el.records.length, 0),
@@ -970,7 +991,7 @@ ${this.getWhereSQL(cls, query.where as WhereClause<T>)}`;
970
991
  };
971
992
 
972
993
  // Full removals
973
- await Promise.all(dels.map(el => this.deleteByIds(el.stack, el.ids)));
994
+ await Promise.all(deletes.map(el => this.deleteByIds(el.stack, el.ids)));
974
995
 
975
996
  // Adding deletes
976
997
  if (upserts.length || updates.length) {
@@ -978,9 +999,11 @@ ${this.getWhereSQL(cls, query.where as WhereClause<T>)}`;
978
999
 
979
1000
  await Promise.all([
980
1001
  ...upserts.filter(x => x.stack.length === 1).map(i =>
1002
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
981
1003
  this.deleteByIds(i.stack, i.records.map(v => (v.value as Record<string, string>)[idx]))
982
1004
  ),
983
1005
  ...updates.filter(x => x.stack.length === 1).map(i =>
1006
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
984
1007
  this.deleteByIds(i.stack, i.records.map(v => (v.value as Record<string, string>)[idx]))
985
1008
  ),
986
1009
  ]);
@@ -24,7 +24,7 @@ export class MySQLConnection extends Connection<mysql.PoolConnection> {
24
24
  this.#config = config;
25
25
  }
26
26
 
27
- async init() {
27
+ async init(): Promise<void> {
28
28
  this.#pool = mysql.createPool({
29
29
  user: this.#config.user,
30
30
  password: this.#config.password,
@@ -43,7 +43,7 @@ export class MySQLConnection extends Connection<mysql.PoolConnection> {
43
43
  /**
44
44
  * Support some basic type support for JSON data
45
45
  */
46
- typeCast(field: Parameters<Exclude<mysql.TypeCast, boolean>>[0], next: () => unknown) {
46
+ typeCast(field: Parameters<Exclude<mysql.TypeCast, boolean>>[0], next: () => unknown): unknown {
47
47
  const res = next();
48
48
  if (typeof res === 'string' && (field.type === 'JSON' || field.type === 'BLOB')) {
49
49
  if (res.charAt(0) === '{' && res.charAt(res.length - 1) === '}') {
@@ -67,19 +67,19 @@ export class MySQLConnection extends Connection<mysql.PoolConnection> {
67
67
  rej(err);
68
68
  }
69
69
  } else {
70
- const records = Array.isArray(results) ? [...results].map(v => ({ ...v })) : [{ ...results }] as T[];
70
+ const records: T[] = Array.isArray(results) ? [...results].map(v => ({ ...v })) : [{ ...results }];
71
71
  res({ records, count: results.affectedRows });
72
72
  }
73
73
  });
74
74
  });
75
75
  }
76
76
 
77
- acquire() {
77
+ acquire(): Promise<mysql.PoolConnection> {
78
78
  return new Promise<mysql.PoolConnection>((res, rej) =>
79
79
  this.#pool.getConnection((err, conn) => err ? rej(err) : res(conn)));
80
80
  }
81
81
 
82
- release(conn: mysql.PoolConnection) {
82
+ release(conn: mysql.PoolConnection): void {
83
83
  conn.release();
84
84
  }
85
85
  }