@travetto/model-query 2.1.5 → 2.2.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/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
  npm install @travetto/model-query
9
9
  ```
10
10
 
11
- This module provides an enhanced query contract for [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") implementations. This contract has been externalized due to it being more complex than many implemenations can natively support. In addition to the contract, this module provides support for textual query language that can be checked and parsed into the proper query structure.
11
+ This module provides an enhanced query contract for [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") implementations. This contract has been externalized due to it being more complex than many implementations can natively support. In addition to the contract, this module provides support for textual query language that can be checked and parsed into the proper query structure.
12
12
 
13
13
  ## Contracts
14
14
 
@@ -41,7 +41,7 @@ export interface ModelQuerySupport {
41
41
  ```
42
42
 
43
43
  ### [Query Crud](https://github.com/travetto/travetto/tree/main/module/model-query/src/service/crud.ts#L11)
44
- Reinforcing the complexity provided in these contracts, the [Query Crud](https://github.com/travetto/travetto/tree/main/module/model-query/src/service/crud.ts#L11) contract allows for bulk update/deletion by query. This requires the underlying implentation to support these operations.
44
+ Reinforcing the complexity provided in these contracts, the [Query Crud](https://github.com/travetto/travetto/tree/main/module/model-query/src/service/crud.ts#L11) contract allows for bulk update/deletion by query. This requires the underlying implementation to support these operations.
45
45
 
46
46
  **Code: Query Crud**
47
47
  ```typescript
@@ -79,7 +79,7 @@ export interface ModelQueryFacetSupport extends ModelQuerySupport {
79
79
  ```
80
80
 
81
81
  ### [Suggest](https://github.com/travetto/travetto/tree/main/module/model-query/src/service/suggest.ts#L12)
82
- Additionally, this same pattern avails it self in a set of suggestion methods that allow for powering auto completion and typeahead functionalities.
82
+ Additionally, this same pattern avails it self in a set of suggestion methods that allow for powering auto completion and type-ahead functionalities.
83
83
 
84
84
  **Code: Suggest**
85
85
  ```typescript
@@ -129,7 +129,7 @@ One of the complexities of abstracting multiple storage mechanisms, is providing
129
129
  * `field: { $in: T[] }` to see if a record's value appears in the array provided to `$in`
130
130
  * `field: { $nin: T[] }` to see if a record's value does not appear in the array provided to `$in`
131
131
 
132
- ### Ordered Numberic Fields
132
+ ### Ordered Numeric Fields
133
133
 
134
134
  * `field: { $lt: number }` checks if value is less than
135
135
  * `field: { $lte: number }` checks if value is less than or equal to
@@ -142,7 +142,7 @@ One of the complexities of abstracting multiple storage mechanisms, is providing
142
142
  * `field: { $gt: Date | RelativeTime }` checks if value is greater than
143
143
  * `field: { $gte: Date | RelativeTime }` checks if value is greater than or equal to
144
144
 
145
- **Note**: Relative times are strings consisting of a number and a unit. e.g. -1w or 30d. These times are always relative to Date.now, but should make building quieries more natural.
145
+ **Note**: Relative times are strings consisting of a number and a unit. e.g. -1w or 30d. These times are always relative to Date.now, but should make building queries more natural.
146
146
 
147
147
  ### Array Fields
148
148
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@travetto/model-query",
3
3
  "displayName": "Data Model Querying",
4
- "version": "2.1.5",
4
+ "version": "2.2.0",
5
5
  "description": "Datastore abstraction for advanced query support.",
6
6
  "keywords": [
7
7
  "datastore",
@@ -28,9 +28,9 @@
28
28
  "directory": "module/model-query"
29
29
  },
30
30
  "dependencies": {
31
- "@travetto/di": "^2.1.5",
32
- "@travetto/model": "^2.1.5",
33
- "@travetto/schema": "^2.1.5"
31
+ "@travetto/di": "^2.2.0",
32
+ "@travetto/model": "^2.2.0",
33
+ "@travetto/schema": "^2.2.0"
34
34
  },
35
35
  "docDependencies": {
36
36
  "@travetto/model-mongo": true,
@@ -1,12 +1,13 @@
1
1
  import { Util } from '@travetto/base';
2
2
 
3
3
  export class PointImpl {
4
- static validateSchema(input: unknown) {
4
+ static validateSchema(input: unknown): 'type' | undefined {
5
5
  const ret = this.bindSchema(input);
6
6
  return ret && !isNaN(ret[0]) && !isNaN(ret[1]) ? undefined : 'type';
7
7
  }
8
8
  static bindSchema(input: unknown): [number, number] | undefined {
9
9
  if (Array.isArray(input) && input.length === 2) {
10
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
10
11
  return input.map(x => Util.coerceType(x, Number, false)) as [number, number];
11
12
  }
12
13
  }
@@ -1,11 +1,12 @@
1
1
  import { WhereClauseRaw } from '../../model/where-clause';
2
2
  import { QueryLanguageTokenizer } from './tokenizer';
3
- import { Node, Token, ClauseNode, UnaryNode, Literal, GroupNode, OP_TRANSLATION, ArrayNode } from './types';
3
+ import { Token, Literal, GroupNode, OP_TRANSLATION, ArrayNode, AllNode } from './types';
4
4
 
5
5
  /**
6
6
  * Determine if a token is boolean
7
7
  */
8
8
  function isBoolean(o: unknown): o is Token & { type: 'boolean' } {
9
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
9
10
  return !!o && (o as { type: string }).type === 'boolean';
10
11
  }
11
12
 
@@ -17,9 +18,12 @@ export class QueryLanguageParser {
17
18
  /**
18
19
  * Handle all clauses
19
20
  */
20
- static handleClause(nodes: (Node | Token)[]) {
21
+ static handleClause(nodes: (AllNode | Token)[]): void {
22
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
21
23
  const val = nodes.pop()! as Token | ArrayNode;
24
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
22
25
  const op = nodes.pop()! as Token;
26
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
23
27
  const ident = nodes.pop()! as Token;
24
28
 
25
29
  // value isn't a literal or a list, bail
@@ -33,6 +37,7 @@ export class QueryLanguageParser {
33
37
  }
34
38
 
35
39
  // If operator is not known, bail
40
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
36
41
  const finalOp = OP_TRANSLATION[op.value as string];
37
42
  if (!finalOp) {
38
43
  throw new Error(`Unexpected operator: ${op.value}`);
@@ -40,10 +45,11 @@ export class QueryLanguageParser {
40
45
 
41
46
  nodes.push({
42
47
  type: 'clause',
48
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
43
49
  field: ident.value as string,
44
50
  op: finalOp,
45
51
  value: val.value
46
- } as ClauseNode);
52
+ });
47
53
 
48
54
  // Handle unary support
49
55
  this.unary(nodes);
@@ -55,13 +61,16 @@ export class QueryLanguageParser {
55
61
  * Condense nodes to remove unnecessary groupings
56
62
  * (a AND (b AND (c AND d))) => (a AND b AND c)
57
63
  */
58
- static condense(nodes: (Node | Token)[], op: string) {
64
+ static condense(nodes: (AllNode | Token)[], op: 'and' | 'or'): void {
59
65
  let second = nodes[nodes.length - 2];
60
66
 
61
67
  while (isBoolean(second) && second.value === op) {
62
- const right = nodes.pop()!;
68
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
69
+ const right = nodes.pop()! as AllNode;
63
70
  nodes.pop()!;
64
- const left = nodes.pop()!;
71
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
72
+ const left = nodes.pop()! as AllNode;
73
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
65
74
  const rg = right as GroupNode;
66
75
  if (rg.type === 'group' && rg.op === op) {
67
76
  rg.value.unshift(left);
@@ -71,7 +80,7 @@ export class QueryLanguageParser {
71
80
  type: 'group',
72
81
  op,
73
82
  value: [left, right]
74
- } as GroupNode);
83
+ });
75
84
  }
76
85
  second = nodes[nodes.length - 2];
77
86
  }
@@ -81,25 +90,27 @@ export class QueryLanguageParser {
81
90
  * Remove unnecessary unary nodes
82
91
  * (((5))) => 5
83
92
  */
84
- static unary(nodes: (Node | Token)[]) {
93
+ static unary(nodes: (AllNode | Token)[]): void {
94
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
85
95
  const second = nodes[nodes.length - 2] as Token;
86
96
  if (second && second.type === 'unary' && second.value === 'not') {
87
- const node = nodes.pop()!;
97
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
98
+ const node = nodes.pop()! as AllNode;
88
99
  nodes.pop();
89
100
  nodes.push({
90
101
  type: 'unary',
91
102
  op: 'not',
92
103
  value: node
93
- } as UnaryNode);
104
+ });
94
105
  }
95
106
  }
96
107
 
97
108
  /**
98
109
  * Parse all tokens
99
110
  */
100
- static parse(tokens: Token[], pos: number = 0): Node {
111
+ static parse(tokens: Token[], pos: number = 0): AllNode {
101
112
 
102
- let top: (Node | Token)[] = [];
113
+ let top: (AllNode | Token)[] = [];
103
114
  const stack: (typeof top)[] = [top];
104
115
  let arr: Literal[] | undefined;
105
116
 
@@ -122,7 +133,8 @@ export class QueryLanguageParser {
122
133
  if (token.value === 'start') {
123
134
  arr = [];
124
135
  } else {
125
- top.push({ type: 'list', value: arr! } as ArrayNode);
136
+ const arrNode: ArrayNode = { type: 'list', value: arr! };
137
+ top.push(arrNode);
126
138
  arr = undefined;
127
139
  this.handleClause(top);
128
140
  }
@@ -148,38 +160,38 @@ export class QueryLanguageParser {
148
160
 
149
161
  this.condense(top, 'or');
150
162
 
151
- return top[0];
163
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
164
+ return top[0] as AllNode;
152
165
  }
153
166
 
154
167
  /**
155
168
  * Convert Query AST to output
156
169
  */
157
- static convert<T = unknown>(node: Node): WhereClauseRaw<T> {
170
+ static convert<T = unknown>(node: AllNode): WhereClauseRaw<T> {
158
171
  switch (node.type) {
159
172
  case 'unary': {
160
- const un = node as UnaryNode;
161
- return { [`$${un.op!}`]: this.convert(un.value) } as WhereClauseRaw<T>;
173
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
174
+ return { [`$${node.op!}`]: this.convert(node.value) } as WhereClauseRaw<T>;
162
175
  }
163
176
  case 'group': {
164
- const gn = node as GroupNode;
165
- return { [`$${gn.op!}`]: gn.value.map(x => this.convert(x)) } as WhereClauseRaw<T>;
177
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
178
+ return { [`$${node.op!}`]: node.value.map(x => this.convert(x)) } as WhereClauseRaw<T>;
166
179
  }
167
180
  case 'clause': {
168
- const cn = node as ClauseNode;
169
- const parts = cn.field!.split('.');
181
+ const parts = node.field!.split('.');
170
182
  const top: WhereClauseRaw<T> = {};
171
- let sub = top as Record<string, unknown>;
183
+ let sub: Record<string, unknown> = top;
172
184
  for (const p of parts) {
173
185
  sub = sub[p] = {};
174
186
  }
175
- if (cn.op === '$regex' && typeof cn.value === 'string') {
176
- sub[cn.op!] = new RegExp(`^${cn.value}`);
177
- } else if ((cn.op === '$eq' || cn.op === '$ne') && cn.value === null) {
178
- sub.$exists = cn.op !== '$eq';
179
- } else if ((cn.op === '$in' || cn.op === '$nin') && !Array.isArray(cn.value)) {
180
- throw new Error(`Expected array literal for ${cn.op}`);
187
+ if (node.op === '$regex' && typeof node.value === 'string') {
188
+ sub[node.op!] = new RegExp(`^${node.value}`);
189
+ } else if ((node.op === '$eq' || node.op === '$ne') && node.value === null) {
190
+ sub.$exists = node.op !== '$eq';
191
+ } else if ((node.op === '$in' || node.op === '$nin') && !Array.isArray(node.value)) {
192
+ throw new Error(`Expected array literal for ${node.op}`);
181
193
  } else {
182
- sub[cn.op!] = cn.value;
194
+ sub[node.op!] = node.value;
183
195
  }
184
196
  return top;
185
197
  }
@@ -5,7 +5,7 @@ const OPEN_PARENS = 0x28, CLOSE_PARENS = 0x29, OPEN_BRACKET = 0x5b, CLOSE_BRACKE
5
5
  const GREATER_THAN = 0x3e, LESS_THAN = 0x3c, EQUAL = 0x3d, NOT = 0x21, MODULO = 0x25, TILDE = 0x7e, AND = 0x26, OR = 0x7c;
6
6
  const SPACE = 0x20, TAB = 0x09;
7
7
  const DBL_QUOTE = 0x22, SGL_QUOTE = 0x27, FORWARD_SLASH = 0x2f, BACKSLASH = 0x5c;
8
- const PERIOD = 0x2e, UNDERSCORE = 0x54, DOLLARSIGN = 0x24, DASH = 0x2d;
8
+ const PERIOD = 0x2e, UNDERSCORE = 0x54, DOLLAR_SIGN = 0x24, DASH = 0x2d;
9
9
  const ZERO = 0x30, NINE = 0x39, UPPER_A = 0x41, UPPER_Z = 0x5a, LOWER_A = 0x61, LOWER_Z = 0x7a;
10
10
  const LOWER_I = 0x69, LOWER_G = 0x67, LOWER_M = 0x6d, LOWER_S = 0x73;
11
11
 
@@ -45,7 +45,7 @@ export class QueryLanguageTokenizer {
45
45
  /**
46
46
  * Process the next token. Can specify expected type as needed
47
47
  */
48
- static #processToken(state: TokenizeState, mode?: TokenType) {
48
+ static #processToken(state: TokenizeState, mode?: TokenType): Token {
49
49
  const text = state.text.substring(state.start, state.pos);
50
50
  const res = TOKEN_MAPPING[text.toLowerCase()];
51
51
  let value: unknown = text;
@@ -73,7 +73,7 @@ export class QueryLanguageTokenizer {
73
73
  /**
74
74
  * Flush state to output
75
75
  */
76
- static #flush(state: TokenizeState, mode?: TokenType) {
76
+ static #flush(state: TokenizeState, mode?: TokenType): void {
77
77
  if ((!mode || !state.mode || mode !== state.mode) && state.start !== state.pos) {
78
78
  if (state.mode !== 'whitespace') {
79
79
  state.out.push(this.#processToken(state, mode));
@@ -86,27 +86,27 @@ export class QueryLanguageTokenizer {
86
86
  /**
87
87
  * Determine if valid regex flag
88
88
  */
89
- static #isValidRegexFlag(ch: number) {
89
+ static #isValidRegexFlag(ch: number): boolean {
90
90
  return ch === LOWER_I || ch === LOWER_G || ch === LOWER_M || ch === LOWER_S;
91
91
  }
92
92
 
93
93
  /**
94
94
  * Determine if valid token identifier
95
95
  */
96
- static #isValidIdentToken(ch: number) {
96
+ static #isValidIdentToken(ch: number): boolean {
97
97
  return (ch >= ZERO && ch <= NINE) ||
98
98
  (ch >= UPPER_A && ch <= UPPER_Z) ||
99
99
  (ch >= LOWER_A && ch <= LOWER_Z) ||
100
100
  (ch === UNDERSCORE) ||
101
101
  (ch === DASH) ||
102
- (ch === DOLLARSIGN) ||
102
+ (ch === DOLLAR_SIGN) ||
103
103
  (ch === PERIOD);
104
104
  }
105
105
 
106
106
  /**
107
107
  * Read string until quote
108
108
  */
109
- static readString(text: string, pos: number) {
109
+ static readString(text: string, pos: number): number {
110
110
  const len = text.length;
111
111
  const ch = text.charCodeAt(pos);
112
112
  const q = ch;
@@ -130,10 +130,11 @@ export class QueryLanguageTokenizer {
130
130
  */
131
131
  static tokenize(text: string): Token[] {
132
132
  const state: TokenizeState = {
133
- out: [] as Token[],
133
+ out: [],
134
134
  pos: 0,
135
135
  start: 0,
136
136
  text,
137
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
137
138
  mode: undefined! as TokenType
138
139
  };
139
140
  const len = text.length;
@@ -33,15 +33,14 @@ export interface Token {
33
33
  /**
34
34
  * Base AST Node
35
35
  */
36
- export interface Node {
37
- type: string;
36
+ export interface Node<T extends string = string> {
37
+ type: T;
38
38
  }
39
39
 
40
40
  /**
41
41
  * Simple clause
42
42
  */
43
- export interface ClauseNode extends Node {
44
- type: 'clause';
43
+ export interface ClauseNode extends Node<'clause'> {
45
44
  field?: string;
46
45
  op?: string;
47
46
  value?: Literal | Literal[];
@@ -50,30 +49,29 @@ export interface ClauseNode extends Node {
50
49
  /**
51
50
  * Grouping
52
51
  */
53
- export interface GroupNode extends Node {
54
- type: 'group';
52
+ export interface GroupNode extends Node<'group'> {
55
53
  op?: 'and' | 'or';
56
- value: Node[];
54
+ value: AllNode[];
57
55
  }
58
56
 
59
57
  /**
60
58
  * Unary node
61
59
  */
62
- export interface UnaryNode extends Node {
63
- type: 'unary';
60
+ export interface UnaryNode extends Node<'unary'> {
64
61
  op?: 'not';
65
- value: Node;
62
+ value: AllNode;
66
63
  }
67
64
 
68
65
  /**
69
66
  * Array node
70
67
  */
71
- export interface ArrayNode extends Node {
72
- type: 'list';
68
+ export interface ArrayNode extends Node<'list'> {
73
69
  op?: 'not';
74
70
  value: Literal[];
75
71
  }
76
72
 
73
+ export type AllNode = ArrayNode | UnaryNode | GroupNode | ClauseNode;
74
+
77
75
  /**
78
76
  * Translation of operators to model query keys
79
77
  */
@@ -51,7 +51,7 @@ class $QueryVerifier {
51
51
  /**
52
52
  * Handle generic clauses
53
53
  */
54
- processGenericClause<T>(state: State, cls: Class<T>, val: object, handler: ProcessingHandler) {
54
+ processGenericClause<T>(state: State, cls: Class<T>, val: object, handler: ProcessingHandler): void {
55
55
  const view = SchemaRegistry.getViewSchema(cls);
56
56
 
57
57
  if (val === undefined || val === null) {
@@ -102,14 +102,14 @@ class $QueryVerifier {
102
102
  /**
103
103
  * Ensure types match
104
104
  */
105
- typesMatch(declared: string, actual: string | undefined) {
105
+ typesMatch(declared: string, actual: string | undefined): boolean {
106
106
  return declared === actual;
107
107
  }
108
108
 
109
109
  /**
110
110
  * Check operator clause
111
111
  */
112
- checkOperatorClause(state: State, declaredType: SimpleType, value: unknown, allowed: Record<string, Set<string>>, isArray: boolean) {
112
+ checkOperatorClause(state: State, declaredType: SimpleType, value: unknown, allowed: Record<string, Set<string>>, isArray: boolean): void {
113
113
  if (isArray) {
114
114
  if (Array.isArray(value)) {
115
115
  // Handle array literal
@@ -173,7 +173,7 @@ class $QueryVerifier {
173
173
  /**
174
174
  * Process where clause
175
175
  */
176
- processWhereClause<T>(st: State, cls: Class<T>, passed: object) {
176
+ processWhereClause<T>(st: State, cls: Class<T>, passed: object): void {
177
177
  return this.processGenericClause(st, cls, passed, {
178
178
  preMember: (state: State, value: Record<string, unknown>) => {
179
179
  const keys = Object.keys(value);
@@ -215,14 +215,14 @@ class $QueryVerifier {
215
215
  /**
216
216
  * Handle group by clause
217
217
  */
218
- processGroupByClause(state: State, value: object) {
218
+ processGroupByClause(state: State, value: object): void {
219
219
 
220
220
  }
221
221
 
222
222
  /**
223
223
  * Handle sort clause
224
224
  */
225
- processSortClause<T>(st: State, cls: Class<T>, passed: object) {
225
+ processSortClause<T>(st: State, cls: Class<T>, passed: object): void {
226
226
  return this.processGenericClause(st, cls, passed, {
227
227
  onSimpleType: (state, type, value) => {
228
228
  if (value === 1 || value === -1 || typeof value === 'boolean') {
@@ -236,7 +236,7 @@ class $QueryVerifier {
236
236
  /**
237
237
  * Handle select clause
238
238
  */
239
- processSelectClause<T>(st: State, cls: Class<T>, passed: object) {
239
+ processSelectClause<T>(st: State, cls: Class<T>, passed: object): void {
240
240
  return this.processGenericClause(st, cls, passed, {
241
241
  onSimpleType: (state, type, value) => {
242
242
  const actual = TypeUtil.getActualType(value);
@@ -269,32 +269,35 @@ class $QueryVerifier {
269
269
  /**
270
270
  * Verify the query
271
271
  */
272
- verify<T>(cls: Class<T>, query: ModelQuery<T> | Query<T> | PageableModelQuery<T>) {
272
+ verify<T>(cls: Class<T>, query: ModelQuery<T> | Query<T> | PageableModelQuery<T>): void {
273
273
  const errors: ValidationError[] = [];
274
274
 
275
275
  const state = {
276
276
  path: '',
277
- collect(path: string, message: string) {
277
+ collect(path: string, message: string): void {
278
278
  errors.push({ message: `${path}: ${message}`, path, kind: 'model' });
279
279
  },
280
- log(err: string) {
280
+ log(err: string): void {
281
281
  this.collect(this.path, err);
282
282
  },
283
- extend(sub: string) {
283
+ extend<S extends { path: string }>(this: S, sub: string): S {
284
284
  return { ...this, path: !this.path ? sub : `${this.path}.${sub}` };
285
285
  }
286
286
  };
287
287
 
288
288
  // Check all the clauses
289
289
  for (const [key, fn] of this.#mapping) {
290
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
291
+ const queryKey = key as keyof typeof query;
292
+
290
293
  if (!(key in query)
291
- || query[key as keyof typeof query] === undefined
292
- || query[key as keyof typeof query] === null
294
+ || query[queryKey] === undefined
295
+ || query[queryKey] === null
293
296
  ) {
294
297
  continue;
295
298
  }
296
299
 
297
- const val = (query as Query<unknown>)[key];
300
+ const val = query[queryKey];
298
301
  const subState = state.extend(key);
299
302
 
300
303
  if (Array.isArray(val)) {
@@ -302,6 +305,7 @@ class $QueryVerifier {
302
305
  this[fn](subState, cls, el);
303
306
  }
304
307
  } else {
308
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
305
309
  this[fn](subState, cls, val as object);
306
310
  }
307
311
  }
@@ -13,6 +13,7 @@ export class ModelQuerySuggestSupportTarget { }
13
13
  * @param o
14
14
  */
15
15
  export function isQuerySupported(o: unknown): o is ModelQuerySupport {
16
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
16
17
  return !!o && !!(o as Record<string, unknown>)['query'];
17
18
  }
18
19
 
@@ -21,6 +22,7 @@ export function isQuerySupported(o: unknown): o is ModelQuerySupport {
21
22
  * @param o
22
23
  */
23
24
  export function isQueryCrudSupported(o: unknown): o is ModelQueryCrudSupport {
25
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
24
26
  return !!o && !!(o as Record<string, unknown>)['deleteByQuery'];
25
27
  }
26
28
 
@@ -29,6 +31,7 @@ export function isQueryCrudSupported(o: unknown): o is ModelQueryCrudSupport {
29
31
  * @param o
30
32
  */
31
33
  export function isQueryFacetSupported(o: unknown): o is ModelQueryFacetSupport {
34
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
32
35
  return !!o && !!(o as Record<string, unknown>)['facet'];
33
36
  }
34
37
 
@@ -37,5 +40,6 @@ export function isQueryFacetSupported(o: unknown): o is ModelQueryFacetSupport {
37
40
  * @param o
38
41
  */
39
42
  export function isQuerySuggestSupported(o: unknown): o is ModelQuerySuggestSupport {
43
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
40
44
  return !!o && !!(o as Record<string, unknown>)['suggest'];
41
45
  }
@@ -12,7 +12,7 @@ export class ModelQueryExpiryUtil {
12
12
  /**
13
13
  * Delete all expired
14
14
  */
15
- static async deleteExpired<T extends ModelType>(svc: ModelQueryCrudSupport & ModelCrudSupport, cls: Class<T>) {
15
+ static async deleteExpired<T extends ModelType>(svc: ModelQueryCrudSupport & ModelCrudSupport, cls: Class<T>): Promise<number> {
16
16
  const expiry = await ModelRegistry.getExpiry(cls);
17
17
  const res = await svc.deleteByQuery<ModelType>(cls, {
18
18
  where: {
@@ -18,8 +18,9 @@ export class ModelQueryUtil {
18
18
  * @param val
19
19
  * @returns
20
20
  */
21
- static resolveComparator(val: unknown) {
21
+ static resolveComparator(val: unknown): unknown {
22
22
  if (typeof val === 'string') {
23
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
23
24
  return Util.timeFromNow(val as '1m');
24
25
  } else {
25
26
  return val;
@@ -29,10 +30,10 @@ export class ModelQueryUtil {
29
30
  /**
30
31
  * Verify result set is singular, and decide if failing on many should happen
31
32
  */
32
- static verifyGetSingleCounts<T>(cls: Class<T>, res?: T[], failOnMany = true) {
33
+ static verifyGetSingleCounts<T>(cls: Class<T>, res?: T[], failOnMany = true): T {
33
34
  res = res ?? [];
34
35
  if (res.length === 1 || res.length > 1 && !failOnMany) {
35
- return res[0] as T;
36
+ return res[0]!;
36
37
  }
37
38
  throw res.length === 0 ? new NotFoundError(cls, 'none') : new AppError(`Invalid number of results for find by id: ${res.length}`, 'data');
38
39
  }
@@ -41,14 +42,16 @@ export class ModelQueryUtil {
41
42
  * Get a where clause with type
42
43
  */
43
44
  static getWhereClause<T extends ModelType>(cls: Class<T>, o: WhereClause<T> | string | undefined, checkExpiry = true): WhereClause<T> {
44
- let q = o ? (typeof o === 'string' ? QueryLanguageParser.parseToQuery(o) as WhereClause<T> : o) : undefined;
45
- const clauses = (q ? [q] : []) as WhereClauseRaw<T>[];
45
+ let q: WhereClause<T> | undefined = o ? (typeof o === 'string' ? QueryLanguageParser.parseToQuery(o) : o) : undefined;
46
+ const clauses: WhereClauseRaw<T>[] = (q ? [q] : []);
46
47
 
47
48
  const conf = ModelRegistry.get(cls);
48
49
  if (conf.subType) {
50
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
49
51
  clauses.push({ type: SchemaRegistry.getSubTypeName(cls) } as WhereClauseRaw<T>);
50
52
  }
51
53
  if (checkExpiry && conf.expiresAt) {
54
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
52
55
  clauses.push({
53
56
  $or: [
54
57
  { [conf.expiresAt]: { $exists: false } },
@@ -67,9 +70,12 @@ export class ModelQueryUtil {
67
70
  /**
68
71
  * Enrich query where clause, and verify query is correct
69
72
  */
70
- static getQueryAndVerify<T extends ModelType, U extends Query<T> | ModelQuery<T>>(cls: Class<T>, query: U, checkExpiry = true) {
73
+ static getQueryAndVerify<T extends ModelType, U extends Query<T> | ModelQuery<T>>(
74
+ cls: Class<T>, query: U, checkExpiry = true
75
+ ): U & { where: WhereClause<T> } {
71
76
  query.where = this.getWhereClause(cls, query.where, checkExpiry);
72
77
  QueryVerifier.verify(cls, query);
78
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
73
79
  return query as U & { where: WhereClause<T> };
74
80
  }
75
81
  }
@@ -13,14 +13,14 @@ export class ModelQuerySuggestUtil {
13
13
  /**
14
14
  * Build regex for suggesting
15
15
  */
16
- static getSuggestRegex(prefix?: string) {
16
+ static getSuggestRegex(prefix?: string): RegExp {
17
17
  return prefix ? new RegExp(`\\b${prefix}.*`, 'i') : /./;
18
18
  }
19
19
 
20
20
  /**
21
21
  * Build suggest query on top of query language
22
22
  */
23
- static getSuggestQuery<T extends ModelType>(cls: Class<T>, field: ValidStringFields<T>, prefix?: string, query?: Query<T>) {
23
+ static getSuggestQuery<T extends ModelType>(cls: Class<T>, field: ValidStringFields<T>, prefix?: string, query?: Query<T>): Query<T> {
24
24
  const config = ModelRegistry.get(cls);
25
25
  const limit = query?.limit ?? 10;
26
26
  const clauses: WhereClauseRaw<ModelType>[] = prefix ? [{ [field]: { $regex: this.getSuggestRegex(prefix) } }] : [];
@@ -34,6 +34,7 @@ export class ModelQuerySuggestUtil {
34
34
  }
35
35
 
36
36
  if (query?.where) {
37
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
37
38
  clauses.push(query.where! as WhereClauseRaw<ModelType>);
38
39
  }
39
40
 
@@ -41,15 +42,17 @@ export class ModelQuerySuggestUtil {
41
42
  where: clauses.length ? (clauses.length > 1 ? { $and: clauses } : clauses[0]) : {},
42
43
  limit,
43
44
  select: query?.select
44
- } as Query<T>;
45
+ };
45
46
  }
46
47
 
47
48
  /**
48
49
  * Join suggestion results
49
50
  */
50
51
  static combineSuggestResults<T extends ModelType, U>(
51
- cls: Class<T>, field: ValidStringFields<T>,
52
- prefix: string = '', results: T[],
52
+ cls: Class<T>,
53
+ field: ValidStringFields<T>,
54
+ prefix: string = '',
55
+ results: T[],
53
56
  transform: (value: string, entity: T) => U,
54
57
  limit?: number
55
58
  ): U[] {
@@ -59,7 +62,8 @@ export class ModelQuerySuggestUtil {
59
62
  for (const r of results) {
60
63
  const val = r[field];
61
64
  if (Array.isArray(val)) {
62
- out.push(...val.filter(f => pattern.test(f)).map(f => [f, transform(f, r)] as [string, U]));
65
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
66
+ out.push(...val.filter(f => pattern.test(f)).map((f: string) => [f, transform(f, r)] as [string, U]));
63
67
  } else if (typeof val === 'string') {
64
68
  out.push([val, transform(val, r)]);
65
69
  }
@@ -74,11 +78,12 @@ export class ModelQuerySuggestUtil {
74
78
  /**
75
79
  * Build suggestion query
76
80
  */
77
- static getSuggestFieldQuery<T extends ModelType>(cls: Class<T>, field: ValidStringFields<T>, prefix?: string, query?: PageableModelQuery<T>) {
81
+ static getSuggestFieldQuery<T extends ModelType>(cls: Class<T>, field: ValidStringFields<T>, prefix?: string, query?: PageableModelQuery<T>): Query<T> {
78
82
  const config = ModelRegistry.get(cls);
83
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
79
84
  return this.getSuggestQuery<ModelType>(cls, field as ValidStringFields<ModelType>, prefix, {
80
85
  ...(query ?? {}),
81
86
  select: { [field]: true, ...(config.subType ? { type: true } : {}) }
82
- });
87
+ }) as Query<T>;
83
88
  }
84
89
  }
@@ -3,14 +3,14 @@ import { Class } from '@travetto/base';
3
3
 
4
4
  import { PointImpl } from '../model/point';
5
5
 
6
- const st = (t: string | string[], arr: boolean = false) =>
6
+ const st = (t: string | string[], arr: boolean = false): Set<string> =>
7
7
  new Set((Array.isArray(t) ? t : [t]).map(v => arr ? `${v}[]` : v));
8
8
 
9
- const basic = (types: Set<string>) => ({ $ne: types, $eq: types, $exists: st('boolean') });
10
- const scalar = (types: Set<string>) => ({ $in: types, $nin: types });
11
- const str = () => ({ $regex: st(['RegExp', 'string']) });
12
- const comp = (types: Set<string>) => ({ $lt: types, $lte: types, $gt: types, $gte: types });
13
- const geo = (type: string) => ({
9
+ const basic = (types: Set<string>): Record<string, Set<string>> => ({ $ne: types, $eq: types, $exists: st('boolean') });
10
+ const scalar = (types: Set<string>): Record<string, Set<string>> => ({ $in: types, $nin: types });
11
+ const str = (): Record<string, Set<string>> => ({ $regex: st(['RegExp', 'string']) });
12
+ const comp = (types: Set<string>): Record<string, Set<string>> => ({ $lt: types, $lte: types, $gt: types, $gte: types });
13
+ const geo = (type: string): Record<string, Set<string>> => ({
14
14
  $near: st(type),
15
15
  $maxDistance: st('number'),
16
16
  $unit: st('string'),
@@ -26,11 +26,11 @@ export class TypeUtil {
26
26
  * Mapping types to various operators
27
27
  */
28
28
  static OPERATORS = {
29
- string: { ...basic(st('string')), ...scalar(st('string', true)), ...str() } as Record<string, Set<string>>,
30
- number: { ...basic(st('number')), ...scalar(st('number', true)), ...comp(st('number')) } as Record<string, Set<string>>,
31
- boolean: { ...basic(st('boolean')), ...scalar(st('boolean', true)) } as Record<string, Set<string>>,
32
- Date: { ...basic(st('Date')), ...scalar(st('Date', true)), ...comp(st(['string', 'Date'])) } as Record<string, Set<string>>,
33
- Point: { ...basic(st('Point')), ...geo('Point') } as Record<string, Set<string>>,
29
+ string: { ...basic(st('string')), ...scalar(st('string', true)), ...str() },
30
+ number: { ...basic(st('number')), ...scalar(st('number', true)), ...comp(st('number')) },
31
+ boolean: { ...basic(st('boolean')), ...scalar(st('boolean', true)) },
32
+ Date: { ...basic(st('Date')), ...scalar(st('Date', true)), ...comp(st(['string', 'Date'])) },
33
+ Point: { ...basic(st('Point')), ...geo('Point') }
34
34
  };
35
35
 
36
36
  /**
@@ -86,7 +86,7 @@ export type WhereClause<T> = WhereClauseRaw<RetainFields<T>>;
86
86
  * Provides all the valid string type fields from a given type T
87
87
  */
88
88
  export type ValidStringFields<T> = {
89
- [K in keyof T]:
89
+ [K in Extract<keyof T, string>]:
90
90
  (T[K] extends (String | string | string[] | String[] | undefined) ? K : never) // eslint-disable-line @typescript-eslint/ban-types
91
- }[keyof T];
91
+ }[Extract<keyof T, string>];
92
92