@travetto/model-query-language 8.0.0-alpha.9 → 8.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/README.md CHANGED
@@ -41,4 +41,4 @@ user.role in ['admin', 'root'] && (user.address.state == 'VA' || user.address.ci
41
41
  ```
42
42
 
43
43
  ### Regular Expression
44
- When querying with regular expressions, patterns can be specified as `'strings'` or as `/patterns/`. The latter allows for the case insensitive modifier: `/pattern/i`. Supporting the insensitive flag is up to the underlying model implementation.
44
+ When querying with regular expressions, patterns can be specified as `'strings'` or as `/patterns/`. The latter allows for the case insensitive modifier: `/pattern/i`. Supporting the insensitive flag is up to the underlying model implementation.
package/__index__.ts CHANGED
@@ -1,3 +1,3 @@
1
+ export * from './src/model-query.ts';
1
2
  export * from './src/parser.ts';
2
3
  export * from './src/tokenizer.ts';
3
- export * from './src/model-query.ts';
package/package.json CHANGED
@@ -1,38 +1,41 @@
1
1
  {
2
2
  "name": "@travetto/model-query-language",
3
- "version": "8.0.0-alpha.9",
4
- "type": "module",
3
+ "version": "8.0.0",
5
4
  "description": "Datastore query language.",
6
5
  "keywords": [
7
6
  "datastore",
7
+ "query-language",
8
8
  "schema",
9
- "typescript",
10
9
  "travetto",
11
- "query-language"
10
+ "typescript"
12
11
  ],
13
12
  "homepage": "https://travetto.io",
14
13
  "license": "MIT",
15
14
  "author": {
16
- "email": "travetto.framework@gmail.com",
17
- "name": "Travetto Framework"
15
+ "name": "Travetto Framework",
16
+ "email": "travetto.framework@gmail.com"
17
+ },
18
+ "repository": {
19
+ "url": "git+https://github.com/travetto/travetto.git",
20
+ "directory": "module/model-query"
18
21
  },
19
22
  "files": [
20
23
  "__index__.ts",
21
24
  "src",
22
25
  "support"
23
26
  ],
27
+ "type": "module",
24
28
  "main": "__index__.ts",
25
- "repository": {
26
- "url": "git+https://github.com/travetto/travetto.git",
27
- "directory": "module/model-query"
29
+ "publishConfig": {
30
+ "access": "public"
28
31
  },
29
32
  "dependencies": {
30
- "@travetto/model": "^8.0.0-alpha.9",
31
- "@travetto/model-query": "^8.0.0-alpha.9",
32
- "@travetto/schema": "^8.0.0-alpha.9"
33
+ "@travetto/model": "^8.0.0",
34
+ "@travetto/model-query": "^8.0.0",
35
+ "@travetto/schema": "^8.0.0"
33
36
  },
34
37
  "peerDependencies": {
35
- "@travetto/test": "^8.0.0-alpha.9"
38
+ "@travetto/test": "^8.0.0"
36
39
  },
37
40
  "peerDependenciesMeta": {
38
41
  "@travetto/test": {
@@ -41,8 +44,5 @@
41
44
  },
42
45
  "travetto": {
43
46
  "displayName": "Data Model Query Language"
44
- },
45
- "publishConfig": {
46
- "access": "public"
47
47
  }
48
48
  }
@@ -1,10 +1,11 @@
1
- import { Schema } from '@travetto/schema';
2
1
  import type { PageableModelQuery } from '@travetto/model-query';
3
2
  import { JSONUtil } from '@travetto/runtime';
3
+ import { Schema } from '@travetto/schema';
4
4
 
5
5
  import { QueryLanguageParser } from './parser.ts';
6
6
 
7
- const parse = <T>(key: string): T | undefined => !key || typeof key !== 'string' || !/^[\{\[]/.test(key) ? undefined : JSONUtil.fromUTF8(key);
7
+ const parse = <T>(key: string): T | undefined =>
8
+ !key || typeof key !== 'string' || !/^[{[]/.test(key) ? undefined : JSONUtil.fromUTF8(key);
8
9
 
9
10
  @Schema()
10
11
  export class QueryLanguageModelQuery {
@@ -13,10 +14,11 @@ export class QueryLanguageModelQuery {
13
14
  ...(self.limit ? { limit: self.limit } : {}),
14
15
  ...(self.offset ? { offset: self.offset } : {}),
15
16
  ...(self.sort ? { sort: parse(self.sort) } : {}),
16
- ...(self.where ? {
17
- where: parse(self.where) ??
18
- QueryLanguageParser.parseToQuery(self.where)
19
- } : {}),
17
+ ...(self.where
18
+ ? {
19
+ where: parse(self.where) ?? QueryLanguageParser.parseToQuery(self.where)
20
+ }
21
+ : {})
20
22
  };
21
23
  }
22
24
 
package/src/parser.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { castTo } from '@travetto/runtime';
2
1
  import type { WhereClauseRaw } from '@travetto/model-query';
2
+ import { castTo } from '@travetto/runtime';
3
3
 
4
4
  import { QueryLanguageTokenizer } from './tokenizer.ts';
5
- import { type Token, type Literal, type GroupNode, OPERATOR_TRANSLATION, type ArrayNode, type AllNode } from './types.ts';
5
+ import { type AllNode, type ArrayNode, type GroupNode, type Literal, OPERATOR_TRANSLATION, type Token } from './types.ts';
6
6
 
7
7
  /**
8
8
  * Determine if a token is boolean
@@ -15,7 +15,6 @@ function isBoolean(value: unknown): value is Token & { type: 'boolean' } {
15
15
  * Language parser
16
16
  */
17
17
  export class QueryLanguageParser {
18
-
19
18
  /**
20
19
  * Handle all clauses
21
20
  */
@@ -101,7 +100,6 @@ export class QueryLanguageParser {
101
100
  * Parse all tokens
102
101
  */
103
102
  static parse(tokens: Token[], position: number = 0): AllNode {
104
-
105
103
  let top: (AllNode | Token)[] = [];
106
104
  const stack: (typeof top)[] = [top];
107
105
  let list: Literal[] | undefined;
@@ -111,7 +109,7 @@ export class QueryLanguageParser {
111
109
  switch (token.type) {
112
110
  case 'grouping':
113
111
  if (token.value === 'start') {
114
- stack.push(top = []);
112
+ stack.push((top = []));
115
113
  } else {
116
114
  const group = stack.pop()!;
117
115
  top = stack.at(-1)!;
@@ -147,7 +145,7 @@ export class QueryLanguageParser {
147
145
  default:
148
146
  top.push(token);
149
147
  }
150
- token = tokens[++position];
148
+ token = tokens[(position += 1)];
151
149
  }
152
150
 
153
151
  this.condense(top, 'or');
@@ -184,7 +182,8 @@ export class QueryLanguageParser {
184
182
  }
185
183
  return top;
186
184
  }
187
- default: throw new Error(`Unexpected node type: ${node.type}`);
185
+ default:
186
+ throw new Error(`Unexpected node type: ${node.type}`);
188
187
  }
189
188
  }
190
189
 
@@ -196,4 +195,4 @@ export class QueryLanguageParser {
196
195
  const node = this.parse(tokens);
197
196
  return this.convert(node);
198
197
  }
199
- }
198
+ }
package/src/tokenizer.ts CHANGED
@@ -1,13 +1,40 @@
1
1
  import { TimeUtil } from '@travetto/runtime';
2
+
2
3
  import type { Token, TokenizeState, TokenType } from './types.ts';
3
4
 
4
- const OPEN_PARENS = 0x28, CLOSE_PARENS = 0x29, OPEN_BRACKET = 0x5b, CLOSE_BRACKET = 0x5d, COMMA = 0x2c;
5
- const GREATER_THAN = 0x3e, LESS_THAN = 0x3c, EQUAL = 0x3d, NOT = 0x21, MODULO = 0x25, TILDE = 0x7e, AND = 0x26, OR = 0x7c;
6
- const SPACE = 0x20, TAB = 0x09;
7
- const DBL_QUOTE = 0x22, SGL_QUOTE = 0x27, FORWARD_SLASH = 0x2f, BACKSLASH = 0x5c;
8
- const PERIOD = 0x2e, UNDERSCORE = 0x54, DOLLAR_SIGN = 0x24, DASH = 0x2d;
9
- const ZERO = 0x30, NINE = 0x39, UPPER_A = 0x41, UPPER_Z = 0x5a, LOWER_A = 0x61, LOWER_Z = 0x7a;
10
- const LOWER_I = 0x69, LOWER_G = 0x67, LOWER_M = 0x6d, LOWER_S = 0x73;
5
+ const OPEN_PARENS = 0x28,
6
+ CLOSE_PARENS = 0x29,
7
+ OPEN_BRACKET = 0x5b,
8
+ CLOSE_BRACKET = 0x5d,
9
+ COMMA = 0x2c;
10
+ const GREATER_THAN = 0x3e,
11
+ LESS_THAN = 0x3c,
12
+ EQUAL = 0x3d,
13
+ NOT = 0x21,
14
+ MODULO = 0x25,
15
+ TILDE = 0x7e,
16
+ AND = 0x26,
17
+ OR = 0x7c;
18
+ const SPACE = 0x20,
19
+ TAB = 0x09;
20
+ const DBL_QUOTE = 0x22,
21
+ SGL_QUOTE = 0x27,
22
+ FORWARD_SLASH = 0x2f,
23
+ BACKSLASH = 0x5c;
24
+ const PERIOD = 0x2e,
25
+ UNDERSCORE = 0x54,
26
+ DOLLAR_SIGN = 0x24,
27
+ DASH = 0x2d;
28
+ const ZERO = 0x30,
29
+ NINE = 0x39,
30
+ UPPER_A = 0x41,
31
+ UPPER_Z = 0x5a,
32
+ LOWER_A = 0x61,
33
+ LOWER_Z = 0x7a;
34
+ const LOWER_I = 0x69,
35
+ LOWER_G = 0x67,
36
+ LOWER_M = 0x6d,
37
+ LOWER_S = 0x73;
11
38
 
12
39
  const ESCAPE: Record<string, string> = {
13
40
  '\\n': '\n',
@@ -26,7 +53,7 @@ const TOKEN_MAPPING: Record<string, Token> = {
26
53
  or: { type: 'boolean', value: 'or' },
27
54
  '||': { type: 'boolean', value: 'or' },
28
55
  in: { type: 'operator', value: 'in' },
29
- ['not-in']: { type: 'operator', value: 'not-in' },
56
+ 'not-in': { type: 'operator', value: 'not-in' },
30
57
  not: { type: 'unary', value: 'not' },
31
58
  '[': { type: 'array', value: 'start' },
32
59
  ']': { type: 'array', value: 'end' },
@@ -34,14 +61,13 @@ const TOKEN_MAPPING: Record<string, Token> = {
34
61
  ')': { type: 'grouping', value: 'end' },
35
62
  null: { type: 'literal', value: null },
36
63
  true: { type: 'literal', value: true },
37
- false: { type: 'literal', value: false },
64
+ false: { type: 'literal', value: false }
38
65
  };
39
66
 
40
67
  /**
41
68
  * Tokenizer for the query language
42
69
  */
43
70
  export class QueryLanguageTokenizer {
44
-
45
71
  /**
46
72
  * Process the next token. Can specify expected type as needed
47
73
  */
@@ -51,8 +77,7 @@ export class QueryLanguageTokenizer {
51
77
  let value: unknown = text;
52
78
  if (!result && state.mode === 'literal') {
53
79
  if (/^["']/.test(text)) {
54
- value = text.substring(1, text.length - 1)
55
- .replace(/\\[.]/g, (a, b) => ESCAPE[a] || b);
80
+ value = text.substring(1, text.length - 1).replace(/\\[.]/g, (a, b) => ESCAPE[a] || b);
56
81
  } else if (/^\//.test(text)) {
57
82
  const start = 1;
58
83
  const end = text.lastIndexOf('/');
@@ -94,13 +119,15 @@ export class QueryLanguageTokenizer {
94
119
  * Determine if valid token identifier
95
120
  */
96
121
  static #isValidIdentToken(ch: number): boolean {
97
- return (ch >= ZERO && ch <= NINE) ||
122
+ return (
123
+ (ch >= ZERO && ch <= NINE) ||
98
124
  (ch >= UPPER_A && ch <= UPPER_Z) ||
99
125
  (ch >= LOWER_A && ch <= LOWER_Z) ||
100
- (ch === UNDERSCORE) ||
101
- (ch === DASH) ||
102
- (ch === DOLLAR_SIGN) ||
103
- (ch === PERIOD);
126
+ ch === UNDERSCORE ||
127
+ ch === DASH ||
128
+ ch === DOLLAR_SIGN ||
129
+ ch === PERIOD
130
+ );
104
131
  }
105
132
 
106
133
  /**
@@ -142,25 +169,39 @@ export class QueryLanguageTokenizer {
142
169
  const ch = text.charCodeAt(state.position);
143
170
  switch (ch) {
144
171
  // Handle punctuation
145
- case OPEN_PARENS: case CLOSE_PARENS: case OPEN_BRACKET: case CLOSE_BRACKET: case COMMA:
172
+ case OPEN_PARENS:
173
+ case CLOSE_PARENS:
174
+ case OPEN_BRACKET:
175
+ case CLOSE_BRACKET:
176
+ case COMMA:
146
177
  this.#flush(state);
147
178
  state.mode = 'punctuation';
148
179
  break;
149
180
  // Handle operator
150
- case GREATER_THAN: case LESS_THAN: case EQUAL:
151
- case MODULO: case NOT: case TILDE: case AND: case OR:
181
+ case GREATER_THAN:
182
+ case LESS_THAN:
183
+ case EQUAL:
184
+ case MODULO:
185
+ case NOT:
186
+ case TILDE:
187
+ case AND:
188
+ case OR:
152
189
  this.#flush(state, 'operator');
153
190
  break;
154
191
  // Handle whitespace
155
- case SPACE: case TAB:
192
+ case SPACE:
193
+ case TAB:
156
194
  this.#flush(state, 'whitespace');
157
195
  break;
158
196
  // Handle quotes and slashes
159
- case DBL_QUOTE: case SGL_QUOTE: case FORWARD_SLASH:
197
+ case DBL_QUOTE:
198
+ case SGL_QUOTE:
199
+ case FORWARD_SLASH:
160
200
  this.#flush(state);
161
201
  state.mode = 'literal';
162
202
  state.position = this.readString(text, state.position) + 1;
163
- if (ch === FORWARD_SLASH) { // Read modifiers, not used by all, but useful in general
203
+ if (ch === FORWARD_SLASH) {
204
+ // Read modifiers, not used by all, but useful in general
164
205
  while (this.#isValidRegexFlag(text.charCodeAt(state.position))) {
165
206
  state.position += 1;
166
207
  }
@@ -182,4 +223,4 @@ export class QueryLanguageTokenizer {
182
223
 
183
224
  return state.out;
184
225
  }
185
- }
226
+ }
package/src/types.ts CHANGED
@@ -1,10 +1,7 @@
1
1
  /**
2
2
  * Supported token types
3
3
  */
4
- export type TokenType =
5
- 'literal' | 'identifier' | 'boolean' |
6
- 'operator' | 'grouping' | 'array' |
7
- 'whitespace' | 'punctuation' | 'unary';
4
+ export type TokenType = 'literal' | 'identifier' | 'boolean' | 'operator' | 'grouping' | 'array' | 'whitespace' | 'punctuation' | 'unary';
8
5
 
9
6
  /**
10
7
  * Tokenization state
@@ -76,11 +73,16 @@ export type AllNode = ArrayNode | UnaryNode | GroupNode | ClauseNode;
76
73
  * Translation of operators to model query keys
77
74
  */
78
75
  export const OPERATOR_TRANSLATION: Record<string, string> = {
79
- '<': '$lt', '<=': '$lte',
80
- '>': '$gt', '>=': '$gte',
81
- '!=': '$ne', '==': '$eq',
82
- '~': '$regex', '!': '$not',
83
- in: '$in', 'not-in': '$nin'
76
+ '<': '$lt',
77
+ '<=': '$lte',
78
+ '>': '$gt',
79
+ '>=': '$gte',
80
+ '!=': '$ne',
81
+ '==': '$eq',
82
+ '~': '$regex',
83
+ '!': '$not',
84
+ in: '$in',
85
+ 'not-in': '$nin'
84
86
  };
85
87
 
86
88
  export const VALID_OPERATORS = new Set(Object.keys(OPERATOR_TRANSLATION));