search-input-query-parser 0.2.1 → 0.4.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 +4 -2
- package/dist/cjs/first-pass-parser.js +1 -1
- package/dist/cjs/lexer.js +3 -3
- package/dist/cjs/parse-in-values.js +4 -4
- package/dist/cjs/parse-primary.js +4 -4
- package/dist/cjs/parser.js +1 -1
- package/dist/cjs/validate-expression-fields.js +39 -16
- package/dist/cjs/validate-in-expression.js +3 -3
- package/dist/cjs/validate-string.js +5 -5
- package/dist/cjs/validate-wildcard.js +3 -3
- package/dist/cjs/validator.js +28 -27
- package/dist/esm/first-pass-parser.js +1 -1
- package/dist/esm/lexer.js +3 -3
- package/dist/esm/parse-in-values.js +4 -4
- package/dist/esm/parse-primary.js +4 -4
- package/dist/esm/parser.js +1 -1
- package/dist/esm/validate-expression-fields.js +39 -16
- package/dist/esm/validate-in-expression.js +3 -3
- package/dist/esm/validate-string.js +5 -5
- package/dist/esm/validate-wildcard.js +3 -3
- package/dist/esm/validator.js +28 -27
- package/dist/types/validator.d.ts +28 -27
- package/package.json +1 -1
- package/src/first-pass-parser.ts +1 -2
- package/src/lexer.ts +3 -3
- package/src/parse-in-values.ts +4 -4
- package/src/parse-primary.ts +4 -4
- package/src/parser.test.ts +102 -67
- package/src/parser.ts +1 -1
- package/src/validate-expression-fields.ts +40 -16
- package/src/validate-in-expression.ts +3 -3
- package/src/validate-string.ts +5 -5
- package/src/validate-wildcard.ts +3 -3
- package/src/validator.test.ts +15 -15
- package/src/validator.ts +28 -27
package/README.md
CHANGED
|
@@ -20,7 +20,8 @@ import {
|
|
|
20
20
|
const schemas: FieldSchema[] = [
|
|
21
21
|
{ name: 'title', type: 'string' },
|
|
22
22
|
{ name: 'price', type: 'number' },
|
|
23
|
-
{ name: 'date', type: 'date' }
|
|
23
|
+
{ name: 'date', type: 'date' },
|
|
24
|
+
{ name: 'in_stock', type: 'boolean' }
|
|
24
25
|
];
|
|
25
26
|
|
|
26
27
|
// Parse a search query
|
|
@@ -58,7 +59,8 @@ const searchableColumns = ['title', 'description'];
|
|
|
58
59
|
const schemas: FieldSchema[] = [
|
|
59
60
|
{ name: 'title', type: 'string' },
|
|
60
61
|
{ name: 'price', type: 'number' },
|
|
61
|
-
{ name: 'date', type: 'date' }
|
|
62
|
+
{ name: 'date', type: 'date' },
|
|
63
|
+
{ name: 'in_stock', type: 'boolean' }
|
|
62
64
|
];
|
|
63
65
|
|
|
64
66
|
// Convert a search query to SQL
|
|
@@ -34,7 +34,7 @@ const parseExpression = (stream, minPrecedence = 0) => {
|
|
|
34
34
|
if (nextToken.type === lexer_1.TokenType.EOF) {
|
|
35
35
|
throw {
|
|
36
36
|
message: `Unexpected token: ${token.value}`,
|
|
37
|
-
code: validator_1.SearchQueryErrorCode.
|
|
37
|
+
code: validator_1.SearchQueryErrorCode.SYNTAX_TOKEN_UNEXPECTED,
|
|
38
38
|
value: token.value,
|
|
39
39
|
position: token.position,
|
|
40
40
|
length: token.length,
|
package/dist/cjs/lexer.js
CHANGED
|
@@ -99,7 +99,7 @@ const tokenizeQuotedString = (input, position) => {
|
|
|
99
99
|
}
|
|
100
100
|
throw {
|
|
101
101
|
message: "Unterminated quoted string",
|
|
102
|
-
code: validator_1.SearchQueryErrorCode.
|
|
102
|
+
code: validator_1.SearchQueryErrorCode.SYNTAX_QUOTE_UNTERMINATED,
|
|
103
103
|
position,
|
|
104
104
|
length,
|
|
105
105
|
};
|
|
@@ -266,7 +266,7 @@ const tokenize = (input) => {
|
|
|
266
266
|
prevToken.type === TokenType.STRING)) {
|
|
267
267
|
throw {
|
|
268
268
|
message: "Invalid syntax: Missing operator or whitespace between terms",
|
|
269
|
-
code: validator_1.SearchQueryErrorCode.
|
|
269
|
+
code: validator_1.SearchQueryErrorCode.SYNTAX_OPERATOR_OR_SPACE_MISSING,
|
|
270
270
|
position: position,
|
|
271
271
|
length: 1,
|
|
272
272
|
};
|
|
@@ -279,7 +279,7 @@ const tokenize = (input) => {
|
|
|
279
279
|
!isSpecialChar(input[newPos])) {
|
|
280
280
|
throw {
|
|
281
281
|
message: "Invalid syntax: Missing operator or whitespace between terms",
|
|
282
|
-
code: validator_1.SearchQueryErrorCode.
|
|
282
|
+
code: validator_1.SearchQueryErrorCode.SYNTAX_OPERATOR_OR_SPACE_MISSING,
|
|
283
283
|
position: newPos,
|
|
284
284
|
length: 1,
|
|
285
285
|
};
|
|
@@ -10,7 +10,7 @@ const parseInValues = (stream, inValuePosition) => {
|
|
|
10
10
|
if ((0, lexer_1.currentToken)(currentStream).type !== lexer_1.TokenType.LPAREN) {
|
|
11
11
|
throw {
|
|
12
12
|
message: "Expected '(' after IN",
|
|
13
|
-
code: validator_1.SearchQueryErrorCode.
|
|
13
|
+
code: validator_1.SearchQueryErrorCode.IN_LPAREN_MISSING,
|
|
14
14
|
position: inValuePosition, // Use the position passed from the caller
|
|
15
15
|
length: 1,
|
|
16
16
|
};
|
|
@@ -22,7 +22,7 @@ const parseInValues = (stream, inValuePosition) => {
|
|
|
22
22
|
if (values.length === 0) {
|
|
23
23
|
throw {
|
|
24
24
|
message: "IN operator requires at least one value",
|
|
25
|
-
code: validator_1.SearchQueryErrorCode.
|
|
25
|
+
code: validator_1.SearchQueryErrorCode.IN_LIST_EMPTY,
|
|
26
26
|
position: token.position,
|
|
27
27
|
length: 1,
|
|
28
28
|
};
|
|
@@ -39,7 +39,7 @@ const parseInValues = (stream, inValuePosition) => {
|
|
|
39
39
|
token.type !== lexer_1.TokenType.COMMA)) {
|
|
40
40
|
throw {
|
|
41
41
|
message: "Expected ',' or ')' after IN value",
|
|
42
|
-
code: validator_1.SearchQueryErrorCode.
|
|
42
|
+
code: validator_1.SearchQueryErrorCode.IN_SEPARATOR_MISSING,
|
|
43
43
|
position: token.position,
|
|
44
44
|
length: 1,
|
|
45
45
|
};
|
|
@@ -59,7 +59,7 @@ const parseInValues = (stream, inValuePosition) => {
|
|
|
59
59
|
}
|
|
60
60
|
throw {
|
|
61
61
|
message: "Expected ',' or ')' after IN value",
|
|
62
|
-
code: validator_1.SearchQueryErrorCode.
|
|
62
|
+
code: validator_1.SearchQueryErrorCode.IN_SEPARATOR_MISSING,
|
|
63
63
|
position: nextToken.position,
|
|
64
64
|
length: 1,
|
|
65
65
|
};
|
|
@@ -10,7 +10,7 @@ const expectToken = (stream, type, message) => {
|
|
|
10
10
|
if (token.type !== type) {
|
|
11
11
|
throw {
|
|
12
12
|
message: message ? message : `Expected ${type}`,
|
|
13
|
-
code: validator_1.SearchQueryErrorCode.
|
|
13
|
+
code: validator_1.SearchQueryErrorCode.SYNTAX_TOKEN_MISSING,
|
|
14
14
|
value: type,
|
|
15
15
|
position: token.position,
|
|
16
16
|
length: token.length,
|
|
@@ -137,7 +137,7 @@ const parsePrimary = (stream) => {
|
|
|
137
137
|
case lexer_1.TokenType.OR:
|
|
138
138
|
throw {
|
|
139
139
|
message: `${token.value} is a reserved word`,
|
|
140
|
-
code: validator_1.SearchQueryErrorCode.
|
|
140
|
+
code: validator_1.SearchQueryErrorCode.SYNTAX_KEYWORD_RESERVED,
|
|
141
141
|
value: token.value,
|
|
142
142
|
position: token.position,
|
|
143
143
|
length: token.length,
|
|
@@ -145,14 +145,14 @@ const parsePrimary = (stream) => {
|
|
|
145
145
|
case lexer_1.TokenType.RPAREN:
|
|
146
146
|
throw {
|
|
147
147
|
message: 'Unexpected ")"',
|
|
148
|
-
code: validator_1.SearchQueryErrorCode.
|
|
148
|
+
code: validator_1.SearchQueryErrorCode.SYNTAX_PARENTHESIS_UNEXPECTED,
|
|
149
149
|
position: token.position,
|
|
150
150
|
length: token.length,
|
|
151
151
|
};
|
|
152
152
|
default:
|
|
153
153
|
throw {
|
|
154
154
|
message: "Unexpected token",
|
|
155
|
-
code: validator_1.SearchQueryErrorCode.
|
|
155
|
+
code: validator_1.SearchQueryErrorCode.SYNTAX_TOKEN_UNEXPECTED,
|
|
156
156
|
position: token.position,
|
|
157
157
|
length: token.length,
|
|
158
158
|
};
|
package/dist/cjs/parser.js
CHANGED
|
@@ -48,7 +48,7 @@ const parseSearchInputQuery = (input, fieldSchemas = []) => {
|
|
|
48
48
|
if (finalToken.type !== lexer_1.TokenType.EOF) {
|
|
49
49
|
throw {
|
|
50
50
|
message: 'Unexpected ")"',
|
|
51
|
-
code: validator_1.SearchQueryErrorCode.
|
|
51
|
+
code: validator_1.SearchQueryErrorCode.SYNTAX_PARENTHESIS_UNEXPECTED,
|
|
52
52
|
position: finalToken.position,
|
|
53
53
|
length: finalToken.length,
|
|
54
54
|
};
|
|
@@ -9,7 +9,7 @@ const validateNumber = (value, position, errors) => {
|
|
|
9
9
|
if (isNaN(Number(value))) {
|
|
10
10
|
errors.push({
|
|
11
11
|
message: "Invalid numeric value",
|
|
12
|
-
code: validator_1.SearchQueryErrorCode.
|
|
12
|
+
code: validator_1.SearchQueryErrorCode.VALUE_NUMERIC_INVALID,
|
|
13
13
|
position,
|
|
14
14
|
length: value.length,
|
|
15
15
|
});
|
|
@@ -34,7 +34,7 @@ const validateNumericRange = (start, end, basePosition, errors) => {
|
|
|
34
34
|
if (startNum > endNum) {
|
|
35
35
|
errors.push({
|
|
36
36
|
message: "Range start must be less than or equal to range end",
|
|
37
|
-
code: validator_1.SearchQueryErrorCode.
|
|
37
|
+
code: validator_1.SearchQueryErrorCode.VALUE_RANGE_START_EXCEEDS_END,
|
|
38
38
|
position: basePosition,
|
|
39
39
|
length: start.length + 2 + end.length,
|
|
40
40
|
});
|
|
@@ -55,7 +55,7 @@ const validateFieldValue = (expr, allowedFields, errors, schemas) => {
|
|
|
55
55
|
if (!allowedFields.has(expr.field.toLowerCase())) {
|
|
56
56
|
errors.push({
|
|
57
57
|
message: `Invalid field: "${expr.field}"`,
|
|
58
|
-
code: validator_1.SearchQueryErrorCode.
|
|
58
|
+
code: validator_1.SearchQueryErrorCode.FIELD_NAME_INVALID,
|
|
59
59
|
value: expr.field,
|
|
60
60
|
position: expr.position,
|
|
61
61
|
length: expr.field.length,
|
|
@@ -70,7 +70,7 @@ const validateFieldValue = (expr, allowedFields, errors, schemas) => {
|
|
|
70
70
|
if (isNaN(Number(value))) {
|
|
71
71
|
errors.push({
|
|
72
72
|
message: "Invalid numeric value",
|
|
73
|
-
code: validator_1.SearchQueryErrorCode.
|
|
73
|
+
code: validator_1.SearchQueryErrorCode.VALUE_NUMERIC_INVALID,
|
|
74
74
|
position: expr.position +
|
|
75
75
|
expr.field.length +
|
|
76
76
|
4 +
|
|
@@ -83,7 +83,20 @@ const validateFieldValue = (expr, allowedFields, errors, schemas) => {
|
|
|
83
83
|
if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
84
84
|
errors.push({
|
|
85
85
|
message: "Invalid date format",
|
|
86
|
-
code: validator_1.SearchQueryErrorCode.
|
|
86
|
+
code: validator_1.SearchQueryErrorCode.VALUE_DATE_FORMAT_INVALID,
|
|
87
|
+
position: expr.position +
|
|
88
|
+
expr.field.length +
|
|
89
|
+
3 +
|
|
90
|
+
index * (value.length + 1),
|
|
91
|
+
length: value.length,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
break;
|
|
95
|
+
case "boolean":
|
|
96
|
+
if (!["true", "false"].includes(value.toLowerCase())) {
|
|
97
|
+
errors.push({
|
|
98
|
+
message: "Invalid boolean value",
|
|
99
|
+
code: validator_1.SearchQueryErrorCode.VALUE_BOOLEAN_INVALID,
|
|
87
100
|
position: expr.position +
|
|
88
101
|
expr.field.length +
|
|
89
102
|
3 +
|
|
@@ -100,9 +113,9 @@ const validateFieldValue = (expr, allowedFields, errors, schemas) => {
|
|
|
100
113
|
case "WILDCARD": {
|
|
101
114
|
// For wildcard patterns, validate against field type constraints
|
|
102
115
|
const schema = schemas.get(expr.prefix.toLowerCase());
|
|
103
|
-
if ((schema === null || schema === void 0 ? void 0 : schema.type) === "number" || (schema === null || schema === void 0 ? void 0 : schema.type) === "date") {
|
|
116
|
+
if ((schema === null || schema === void 0 ? void 0 : schema.type) === "number" || (schema === null || schema === void 0 ? void 0 : schema.type) === "date" || (schema === null || schema === void 0 ? void 0 : schema.type) === "boolean") {
|
|
104
117
|
errors.push({
|
|
105
|
-
code: validator_1.SearchQueryErrorCode.
|
|
118
|
+
code: validator_1.SearchQueryErrorCode.VALUE_WILDCARD_NOT_PERMITTED,
|
|
106
119
|
value: schema.type,
|
|
107
120
|
message: `Wildcards are not allowed for ${schema.type} fields`,
|
|
108
121
|
position: expr.position,
|
|
@@ -120,7 +133,7 @@ const validateFieldValue = (expr, allowedFields, errors, schemas) => {
|
|
|
120
133
|
if (!allowedFields.has(fieldName.toLowerCase()) && colonIndex > 0) {
|
|
121
134
|
errors.push({
|
|
122
135
|
message: `Invalid field: "${fieldName}"`,
|
|
123
|
-
code: validator_1.SearchQueryErrorCode.
|
|
136
|
+
code: validator_1.SearchQueryErrorCode.FIELD_NAME_INVALID,
|
|
124
137
|
value: fieldName,
|
|
125
138
|
position: expr.position,
|
|
126
139
|
length: colonIndex,
|
|
@@ -129,7 +142,7 @@ const validateFieldValue = (expr, allowedFields, errors, schemas) => {
|
|
|
129
142
|
if (!value) {
|
|
130
143
|
errors.push({
|
|
131
144
|
message: "Expected field value",
|
|
132
|
-
code: validator_1.SearchQueryErrorCode.
|
|
145
|
+
code: validator_1.SearchQueryErrorCode.SYNTAX_VALUE_MISSING,
|
|
133
146
|
position: expr.position,
|
|
134
147
|
length: colonIndex + 1,
|
|
135
148
|
});
|
|
@@ -138,7 +151,7 @@ const validateFieldValue = (expr, allowedFields, errors, schemas) => {
|
|
|
138
151
|
if (value.startsWith(":")) {
|
|
139
152
|
errors.push({
|
|
140
153
|
message: "Missing field name",
|
|
141
|
-
code: validator_1.SearchQueryErrorCode.
|
|
154
|
+
code: validator_1.SearchQueryErrorCode.SYNTAX_FIELD_NAME_MISSING,
|
|
142
155
|
position: expr.position,
|
|
143
156
|
length: value.length + colonIndex + 1,
|
|
144
157
|
});
|
|
@@ -153,7 +166,7 @@ const validateFieldValue = (expr, allowedFields, errors, schemas) => {
|
|
|
153
166
|
if (value === ".." || value.includes("...")) {
|
|
154
167
|
errors.push({
|
|
155
168
|
message: "Invalid range format",
|
|
156
|
-
code: validator_1.SearchQueryErrorCode.
|
|
169
|
+
code: validator_1.SearchQueryErrorCode.VALUE_RANGE_FORMAT_INVALID,
|
|
157
170
|
position: valueStartPosition,
|
|
158
171
|
length: value.length,
|
|
159
172
|
});
|
|
@@ -170,7 +183,7 @@ const validateFieldValue = (expr, allowedFields, errors, schemas) => {
|
|
|
170
183
|
if (invalidOp.test(value)) {
|
|
171
184
|
errors.push({
|
|
172
185
|
message: "Invalid range operator",
|
|
173
|
-
code: validator_1.SearchQueryErrorCode.
|
|
186
|
+
code: validator_1.SearchQueryErrorCode.VALUE_RANGE_OPERATOR_INVALID,
|
|
174
187
|
position: valueStartPosition,
|
|
175
188
|
length: 3,
|
|
176
189
|
});
|
|
@@ -179,7 +192,7 @@ const validateFieldValue = (expr, allowedFields, errors, schemas) => {
|
|
|
179
192
|
if (!compValue) {
|
|
180
193
|
errors.push({
|
|
181
194
|
message: "Expected range value",
|
|
182
|
-
code: validator_1.SearchQueryErrorCode.
|
|
195
|
+
code: validator_1.SearchQueryErrorCode.VALUE_RANGE_MISSING,
|
|
183
196
|
position: valueStartPosition + operator.length,
|
|
184
197
|
length: 0,
|
|
185
198
|
});
|
|
@@ -207,7 +220,7 @@ const validateFieldValue = (expr, allowedFields, errors, schemas) => {
|
|
|
207
220
|
if (!dateValidator(start) || !dateValidator(end)) {
|
|
208
221
|
errors.push({
|
|
209
222
|
message: "Invalid date format",
|
|
210
|
-
code: validator_1.SearchQueryErrorCode.
|
|
223
|
+
code: validator_1.SearchQueryErrorCode.VALUE_DATE_FORMAT_INVALID,
|
|
211
224
|
position: valueStartPosition,
|
|
212
225
|
length: value.length,
|
|
213
226
|
});
|
|
@@ -221,7 +234,7 @@ const validateFieldValue = (expr, allowedFields, errors, schemas) => {
|
|
|
221
234
|
if (!dateValidator(dateStr)) {
|
|
222
235
|
errors.push({
|
|
223
236
|
message: "Invalid date format",
|
|
224
|
-
code: validator_1.SearchQueryErrorCode.
|
|
237
|
+
code: validator_1.SearchQueryErrorCode.VALUE_DATE_FORMAT_INVALID,
|
|
225
238
|
position: valueStartPosition,
|
|
226
239
|
length: value.length,
|
|
227
240
|
});
|
|
@@ -231,7 +244,7 @@ const validateFieldValue = (expr, allowedFields, errors, schemas) => {
|
|
|
231
244
|
else if (!dateValidator(value)) {
|
|
232
245
|
errors.push({
|
|
233
246
|
message: "Invalid date format",
|
|
234
|
-
code: validator_1.SearchQueryErrorCode.
|
|
247
|
+
code: validator_1.SearchQueryErrorCode.VALUE_DATE_FORMAT_INVALID,
|
|
235
248
|
position: valueStartPosition,
|
|
236
249
|
length: value.length,
|
|
237
250
|
});
|
|
@@ -239,6 +252,16 @@ const validateFieldValue = (expr, allowedFields, errors, schemas) => {
|
|
|
239
252
|
}
|
|
240
253
|
}
|
|
241
254
|
}
|
|
255
|
+
if (schema.type === "boolean") {
|
|
256
|
+
if (!["true", "false"].includes(value.toLowerCase())) {
|
|
257
|
+
errors.push({
|
|
258
|
+
message: "Invalid boolean value",
|
|
259
|
+
code: validator_1.SearchQueryErrorCode.VALUE_BOOLEAN_INVALID,
|
|
260
|
+
position: valueStartPosition,
|
|
261
|
+
length: value.length,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
242
265
|
break;
|
|
243
266
|
}
|
|
244
267
|
}
|
|
@@ -7,7 +7,7 @@ const validateInExpression = (expr, errors) => {
|
|
|
7
7
|
if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(expr.field)) {
|
|
8
8
|
errors.push({
|
|
9
9
|
message: "Invalid characters in field name",
|
|
10
|
-
code: validator_1.SearchQueryErrorCode.
|
|
10
|
+
code: validator_1.SearchQueryErrorCode.FIELD_CHARS_INVALID,
|
|
11
11
|
position: expr.position,
|
|
12
12
|
length: expr.field.length,
|
|
13
13
|
});
|
|
@@ -16,7 +16,7 @@ const validateInExpression = (expr, errors) => {
|
|
|
16
16
|
if (validator_1.reservedWords.has(expr.field.toUpperCase())) {
|
|
17
17
|
errors.push({
|
|
18
18
|
message: `${expr.field} is a reserved word`,
|
|
19
|
-
code: validator_1.SearchQueryErrorCode.
|
|
19
|
+
code: validator_1.SearchQueryErrorCode.FIELD_NAME_RESERVED,
|
|
20
20
|
value: expr.field,
|
|
21
21
|
position: expr.position,
|
|
22
22
|
length: expr.field.length,
|
|
@@ -27,7 +27,7 @@ const validateInExpression = (expr, errors) => {
|
|
|
27
27
|
if (value.includes(",")) {
|
|
28
28
|
errors.push({
|
|
29
29
|
message: "Invalid character in IN value",
|
|
30
|
-
code: validator_1.SearchQueryErrorCode.
|
|
30
|
+
code: validator_1.SearchQueryErrorCode.IN_VALUE_INVALID,
|
|
31
31
|
position: expr.position + expr.field.length + 3 + index * (value.length + 1),
|
|
32
32
|
length: value.length,
|
|
33
33
|
});
|
|
@@ -16,7 +16,7 @@ const validateString = (expr, errors) => {
|
|
|
16
16
|
if (expr.value.endsWith(":")) {
|
|
17
17
|
errors.push({
|
|
18
18
|
message: "Expected field value",
|
|
19
|
-
code: validator_1.SearchQueryErrorCode.
|
|
19
|
+
code: validator_1.SearchQueryErrorCode.SYNTAX_FIELD_VALUE_MISSING,
|
|
20
20
|
position: expr.position,
|
|
21
21
|
length: expr.length,
|
|
22
22
|
});
|
|
@@ -26,7 +26,7 @@ const validateString = (expr, errors) => {
|
|
|
26
26
|
if (expr.value.startsWith(":")) {
|
|
27
27
|
errors.push({
|
|
28
28
|
message: "Missing field name",
|
|
29
|
-
code: validator_1.SearchQueryErrorCode.
|
|
29
|
+
code: validator_1.SearchQueryErrorCode.SYNTAX_FIELD_NAME_MISSING,
|
|
30
30
|
position: expr.position,
|
|
31
31
|
length: expr.length,
|
|
32
32
|
});
|
|
@@ -39,7 +39,7 @@ const validateString = (expr, errors) => {
|
|
|
39
39
|
if (validator_1.reservedWords.has(fieldName.toUpperCase())) {
|
|
40
40
|
errors.push({
|
|
41
41
|
message: `${fieldName} is a reserved word`,
|
|
42
|
-
code: validator_1.SearchQueryErrorCode.
|
|
42
|
+
code: validator_1.SearchQueryErrorCode.FIELD_NAME_RESERVED,
|
|
43
43
|
value: fieldName,
|
|
44
44
|
position: expr.position,
|
|
45
45
|
length: fieldName.length,
|
|
@@ -50,7 +50,7 @@ const validateString = (expr, errors) => {
|
|
|
50
50
|
if (!/^[a-zA-Z0-9_-]+$/.test(fieldName)) {
|
|
51
51
|
errors.push({
|
|
52
52
|
message: "Invalid characters in field name",
|
|
53
|
-
code: validator_1.SearchQueryErrorCode.
|
|
53
|
+
code: validator_1.SearchQueryErrorCode.FIELD_CHARS_INVALID,
|
|
54
54
|
position: expr.position,
|
|
55
55
|
length: fieldName.length,
|
|
56
56
|
});
|
|
@@ -62,7 +62,7 @@ const validateString = (expr, errors) => {
|
|
|
62
62
|
validator_1.reservedWords.has(expr.value.toUpperCase())) {
|
|
63
63
|
errors.push({
|
|
64
64
|
message: `${expr.value} is a reserved word`,
|
|
65
|
-
code: validator_1.SearchQueryErrorCode.
|
|
65
|
+
code: validator_1.SearchQueryErrorCode.FIELD_NAME_RESERVED,
|
|
66
66
|
value: expr.value,
|
|
67
67
|
position: expr.position,
|
|
68
68
|
length: expr.length,
|
|
@@ -14,7 +14,7 @@ const validateWildcard = (expr, errors) => {
|
|
|
14
14
|
const secondStar = value.indexOf("*", firstStar + 1);
|
|
15
15
|
errors.push({
|
|
16
16
|
message: "Only one trailing wildcard (*) is allowed",
|
|
17
|
-
code: validator_1.SearchQueryErrorCode.
|
|
17
|
+
code: validator_1.SearchQueryErrorCode.WILDCARD_MULTIPLE_NOT_PERMITTED,
|
|
18
18
|
position: expr.position + secondStar,
|
|
19
19
|
length: 1,
|
|
20
20
|
});
|
|
@@ -22,7 +22,7 @@ const validateWildcard = (expr, errors) => {
|
|
|
22
22
|
if ((firstStar !== -1 && firstStar !== value.length - 1) && !value.endsWith("**")) {
|
|
23
23
|
errors.push({
|
|
24
24
|
message: "Wildcard (*) can only appear at the end of a term",
|
|
25
|
-
code: validator_1.SearchQueryErrorCode.
|
|
25
|
+
code: validator_1.SearchQueryErrorCode.WILDCARD_POSITION_INVALID,
|
|
26
26
|
position: expr.position + firstStar,
|
|
27
27
|
length: 1,
|
|
28
28
|
});
|
|
@@ -34,7 +34,7 @@ const validateWildcard = (expr, errors) => {
|
|
|
34
34
|
if (value.endsWith("**")) {
|
|
35
35
|
errors.push({
|
|
36
36
|
message: "Only one trailing wildcard (*) is allowed",
|
|
37
|
-
code: validator_1.SearchQueryErrorCode.
|
|
37
|
+
code: validator_1.SearchQueryErrorCode.WILDCARD_MULTIPLE_NOT_PERMITTED,
|
|
38
38
|
position: expr.position + value.length - 1,
|
|
39
39
|
length: 1,
|
|
40
40
|
});
|
package/dist/cjs/validator.js
CHANGED
|
@@ -5,38 +5,39 @@ const validate_in_expression_1 = require("./validate-in-expression");
|
|
|
5
5
|
const validate_string_1 = require("./validate-string");
|
|
6
6
|
var SearchQueryErrorCode;
|
|
7
7
|
(function (SearchQueryErrorCode) {
|
|
8
|
-
SearchQueryErrorCode[SearchQueryErrorCode["
|
|
8
|
+
SearchQueryErrorCode[SearchQueryErrorCode["UNKNOWN"] = 0] = "UNKNOWN";
|
|
9
9
|
// Syntax Errors (1000-1999)
|
|
10
|
-
SearchQueryErrorCode[SearchQueryErrorCode["
|
|
11
|
-
SearchQueryErrorCode[SearchQueryErrorCode["
|
|
12
|
-
SearchQueryErrorCode[SearchQueryErrorCode["
|
|
13
|
-
SearchQueryErrorCode[SearchQueryErrorCode["
|
|
14
|
-
SearchQueryErrorCode[SearchQueryErrorCode["
|
|
15
|
-
SearchQueryErrorCode[SearchQueryErrorCode["
|
|
16
|
-
SearchQueryErrorCode[SearchQueryErrorCode["
|
|
17
|
-
SearchQueryErrorCode[SearchQueryErrorCode["
|
|
18
|
-
SearchQueryErrorCode[SearchQueryErrorCode["
|
|
19
|
-
SearchQueryErrorCode[SearchQueryErrorCode["
|
|
10
|
+
SearchQueryErrorCode[SearchQueryErrorCode["SYNTAX_QUOTE_UNTERMINATED"] = 1001] = "SYNTAX_QUOTE_UNTERMINATED";
|
|
11
|
+
SearchQueryErrorCode[SearchQueryErrorCode["SYNTAX_VALUE_MISSING"] = 1002] = "SYNTAX_VALUE_MISSING";
|
|
12
|
+
SearchQueryErrorCode[SearchQueryErrorCode["SYNTAX_FIELD_NAME_MISSING"] = 1003] = "SYNTAX_FIELD_NAME_MISSING";
|
|
13
|
+
SearchQueryErrorCode[SearchQueryErrorCode["SYNTAX_FIELD_VALUE_MISSING"] = 1004] = "SYNTAX_FIELD_VALUE_MISSING";
|
|
14
|
+
SearchQueryErrorCode[SearchQueryErrorCode["SYNTAX_PARENTHESIS_UNEXPECTED"] = 1005] = "SYNTAX_PARENTHESIS_UNEXPECTED";
|
|
15
|
+
SearchQueryErrorCode[SearchQueryErrorCode["SYNTAX_PARENTHESIS_MISSING"] = 1006] = "SYNTAX_PARENTHESIS_MISSING";
|
|
16
|
+
SearchQueryErrorCode[SearchQueryErrorCode["SYNTAX_TOKEN_UNEXPECTED"] = 1007] = "SYNTAX_TOKEN_UNEXPECTED";
|
|
17
|
+
SearchQueryErrorCode[SearchQueryErrorCode["SYNTAX_TOKEN_MISSING"] = 1008] = "SYNTAX_TOKEN_MISSING";
|
|
18
|
+
SearchQueryErrorCode[SearchQueryErrorCode["SYNTAX_OPERATOR_OR_SPACE_MISSING"] = 1009] = "SYNTAX_OPERATOR_OR_SPACE_MISSING";
|
|
19
|
+
SearchQueryErrorCode[SearchQueryErrorCode["SYNTAX_KEYWORD_RESERVED"] = 1010] = "SYNTAX_KEYWORD_RESERVED";
|
|
20
20
|
// Field Validation Errors (2000-2999)
|
|
21
|
-
SearchQueryErrorCode[SearchQueryErrorCode["
|
|
22
|
-
SearchQueryErrorCode[SearchQueryErrorCode["
|
|
23
|
-
SearchQueryErrorCode[SearchQueryErrorCode["
|
|
21
|
+
SearchQueryErrorCode[SearchQueryErrorCode["FIELD_NAME_INVALID"] = 2001] = "FIELD_NAME_INVALID";
|
|
22
|
+
SearchQueryErrorCode[SearchQueryErrorCode["FIELD_CHARS_INVALID"] = 2002] = "FIELD_CHARS_INVALID";
|
|
23
|
+
SearchQueryErrorCode[SearchQueryErrorCode["FIELD_NAME_RESERVED"] = 2003] = "FIELD_NAME_RESERVED";
|
|
24
24
|
// Value Validation Errors (3000-3999)
|
|
25
|
-
SearchQueryErrorCode[SearchQueryErrorCode["
|
|
26
|
-
SearchQueryErrorCode[SearchQueryErrorCode["
|
|
27
|
-
SearchQueryErrorCode[SearchQueryErrorCode["
|
|
28
|
-
SearchQueryErrorCode[SearchQueryErrorCode["
|
|
29
|
-
SearchQueryErrorCode[SearchQueryErrorCode["
|
|
30
|
-
SearchQueryErrorCode[SearchQueryErrorCode["
|
|
31
|
-
SearchQueryErrorCode[SearchQueryErrorCode["
|
|
25
|
+
SearchQueryErrorCode[SearchQueryErrorCode["VALUE_NUMERIC_INVALID"] = 3001] = "VALUE_NUMERIC_INVALID";
|
|
26
|
+
SearchQueryErrorCode[SearchQueryErrorCode["VALUE_DATE_FORMAT_INVALID"] = 3002] = "VALUE_DATE_FORMAT_INVALID";
|
|
27
|
+
SearchQueryErrorCode[SearchQueryErrorCode["VALUE_RANGE_FORMAT_INVALID"] = 3003] = "VALUE_RANGE_FORMAT_INVALID";
|
|
28
|
+
SearchQueryErrorCode[SearchQueryErrorCode["VALUE_RANGE_OPERATOR_INVALID"] = 3004] = "VALUE_RANGE_OPERATOR_INVALID";
|
|
29
|
+
SearchQueryErrorCode[SearchQueryErrorCode["VALUE_RANGE_MISSING"] = 3005] = "VALUE_RANGE_MISSING";
|
|
30
|
+
SearchQueryErrorCode[SearchQueryErrorCode["VALUE_RANGE_START_EXCEEDS_END"] = 3006] = "VALUE_RANGE_START_EXCEEDS_END";
|
|
31
|
+
SearchQueryErrorCode[SearchQueryErrorCode["VALUE_WILDCARD_NOT_PERMITTED"] = 3007] = "VALUE_WILDCARD_NOT_PERMITTED";
|
|
32
|
+
SearchQueryErrorCode[SearchQueryErrorCode["VALUE_BOOLEAN_INVALID"] = 3008] = "VALUE_BOOLEAN_INVALID";
|
|
32
33
|
// Wildcard Errors (4000-4999)
|
|
33
|
-
SearchQueryErrorCode[SearchQueryErrorCode["
|
|
34
|
-
SearchQueryErrorCode[SearchQueryErrorCode["
|
|
34
|
+
SearchQueryErrorCode[SearchQueryErrorCode["WILDCARD_POSITION_INVALID"] = 4001] = "WILDCARD_POSITION_INVALID";
|
|
35
|
+
SearchQueryErrorCode[SearchQueryErrorCode["WILDCARD_MULTIPLE_NOT_PERMITTED"] = 4002] = "WILDCARD_MULTIPLE_NOT_PERMITTED";
|
|
35
36
|
// IN Expression Errors (5000-5999)
|
|
36
|
-
SearchQueryErrorCode[SearchQueryErrorCode["
|
|
37
|
-
SearchQueryErrorCode[SearchQueryErrorCode["
|
|
38
|
-
SearchQueryErrorCode[SearchQueryErrorCode["
|
|
39
|
-
SearchQueryErrorCode[SearchQueryErrorCode["
|
|
37
|
+
SearchQueryErrorCode[SearchQueryErrorCode["IN_LIST_EMPTY"] = 5001] = "IN_LIST_EMPTY";
|
|
38
|
+
SearchQueryErrorCode[SearchQueryErrorCode["IN_VALUE_INVALID"] = 5002] = "IN_VALUE_INVALID";
|
|
39
|
+
SearchQueryErrorCode[SearchQueryErrorCode["IN_SEPARATOR_MISSING"] = 5003] = "IN_SEPARATOR_MISSING";
|
|
40
|
+
SearchQueryErrorCode[SearchQueryErrorCode["IN_LPAREN_MISSING"] = 5004] = "IN_LPAREN_MISSING";
|
|
40
41
|
})(SearchQueryErrorCode || (exports.SearchQueryErrorCode = SearchQueryErrorCode = {}));
|
|
41
42
|
exports.reservedWords = new Set(["AND", "OR"]);
|
|
42
43
|
const walkExpression = (expr, errors) => {
|
|
@@ -31,7 +31,7 @@ export const parseExpression = (stream, minPrecedence = 0) => {
|
|
|
31
31
|
if (nextToken.type === TokenType.EOF) {
|
|
32
32
|
throw {
|
|
33
33
|
message: `Unexpected token: ${token.value}`,
|
|
34
|
-
code: SearchQueryErrorCode.
|
|
34
|
+
code: SearchQueryErrorCode.SYNTAX_TOKEN_UNEXPECTED,
|
|
35
35
|
value: token.value,
|
|
36
36
|
position: token.position,
|
|
37
37
|
length: token.length,
|
package/dist/esm/lexer.js
CHANGED
|
@@ -93,7 +93,7 @@ const tokenizeQuotedString = (input, position) => {
|
|
|
93
93
|
}
|
|
94
94
|
throw {
|
|
95
95
|
message: "Unterminated quoted string",
|
|
96
|
-
code: SearchQueryErrorCode.
|
|
96
|
+
code: SearchQueryErrorCode.SYNTAX_QUOTE_UNTERMINATED,
|
|
97
97
|
position,
|
|
98
98
|
length,
|
|
99
99
|
};
|
|
@@ -260,7 +260,7 @@ export const tokenize = (input) => {
|
|
|
260
260
|
prevToken.type === TokenType.STRING)) {
|
|
261
261
|
throw {
|
|
262
262
|
message: "Invalid syntax: Missing operator or whitespace between terms",
|
|
263
|
-
code: SearchQueryErrorCode.
|
|
263
|
+
code: SearchQueryErrorCode.SYNTAX_OPERATOR_OR_SPACE_MISSING,
|
|
264
264
|
position: position,
|
|
265
265
|
length: 1,
|
|
266
266
|
};
|
|
@@ -273,7 +273,7 @@ export const tokenize = (input) => {
|
|
|
273
273
|
!isSpecialChar(input[newPos])) {
|
|
274
274
|
throw {
|
|
275
275
|
message: "Invalid syntax: Missing operator or whitespace between terms",
|
|
276
|
-
code: SearchQueryErrorCode.
|
|
276
|
+
code: SearchQueryErrorCode.SYNTAX_OPERATOR_OR_SPACE_MISSING,
|
|
277
277
|
position: newPos,
|
|
278
278
|
length: 1,
|
|
279
279
|
};
|
|
@@ -7,7 +7,7 @@ export const parseInValues = (stream, inValuePosition) => {
|
|
|
7
7
|
if (currentToken(currentStream).type !== TokenType.LPAREN) {
|
|
8
8
|
throw {
|
|
9
9
|
message: "Expected '(' after IN",
|
|
10
|
-
code: SearchQueryErrorCode.
|
|
10
|
+
code: SearchQueryErrorCode.IN_LPAREN_MISSING,
|
|
11
11
|
position: inValuePosition, // Use the position passed from the caller
|
|
12
12
|
length: 1,
|
|
13
13
|
};
|
|
@@ -19,7 +19,7 @@ export const parseInValues = (stream, inValuePosition) => {
|
|
|
19
19
|
if (values.length === 0) {
|
|
20
20
|
throw {
|
|
21
21
|
message: "IN operator requires at least one value",
|
|
22
|
-
code: SearchQueryErrorCode.
|
|
22
|
+
code: SearchQueryErrorCode.IN_LIST_EMPTY,
|
|
23
23
|
position: token.position,
|
|
24
24
|
length: 1,
|
|
25
25
|
};
|
|
@@ -36,7 +36,7 @@ export const parseInValues = (stream, inValuePosition) => {
|
|
|
36
36
|
token.type !== TokenType.COMMA)) {
|
|
37
37
|
throw {
|
|
38
38
|
message: "Expected ',' or ')' after IN value",
|
|
39
|
-
code: SearchQueryErrorCode.
|
|
39
|
+
code: SearchQueryErrorCode.IN_SEPARATOR_MISSING,
|
|
40
40
|
position: token.position,
|
|
41
41
|
length: 1,
|
|
42
42
|
};
|
|
@@ -56,7 +56,7 @@ export const parseInValues = (stream, inValuePosition) => {
|
|
|
56
56
|
}
|
|
57
57
|
throw {
|
|
58
58
|
message: "Expected ',' or ')' after IN value",
|
|
59
|
-
code: SearchQueryErrorCode.
|
|
59
|
+
code: SearchQueryErrorCode.IN_SEPARATOR_MISSING,
|
|
60
60
|
position: nextToken.position,
|
|
61
61
|
length: 1,
|
|
62
62
|
};
|
|
@@ -7,7 +7,7 @@ export const expectToken = (stream, type, message) => {
|
|
|
7
7
|
if (token.type !== type) {
|
|
8
8
|
throw {
|
|
9
9
|
message: message ? message : `Expected ${type}`,
|
|
10
|
-
code: SearchQueryErrorCode.
|
|
10
|
+
code: SearchQueryErrorCode.SYNTAX_TOKEN_MISSING,
|
|
11
11
|
value: type,
|
|
12
12
|
position: token.position,
|
|
13
13
|
length: token.length,
|
|
@@ -131,7 +131,7 @@ export const parsePrimary = (stream) => {
|
|
|
131
131
|
case TokenType.OR:
|
|
132
132
|
throw {
|
|
133
133
|
message: `${token.value} is a reserved word`,
|
|
134
|
-
code: SearchQueryErrorCode.
|
|
134
|
+
code: SearchQueryErrorCode.SYNTAX_KEYWORD_RESERVED,
|
|
135
135
|
value: token.value,
|
|
136
136
|
position: token.position,
|
|
137
137
|
length: token.length,
|
|
@@ -139,14 +139,14 @@ export const parsePrimary = (stream) => {
|
|
|
139
139
|
case TokenType.RPAREN:
|
|
140
140
|
throw {
|
|
141
141
|
message: 'Unexpected ")"',
|
|
142
|
-
code: SearchQueryErrorCode.
|
|
142
|
+
code: SearchQueryErrorCode.SYNTAX_PARENTHESIS_UNEXPECTED,
|
|
143
143
|
position: token.position,
|
|
144
144
|
length: token.length,
|
|
145
145
|
};
|
|
146
146
|
default:
|
|
147
147
|
throw {
|
|
148
148
|
message: "Unexpected token",
|
|
149
|
-
code: SearchQueryErrorCode.
|
|
149
|
+
code: SearchQueryErrorCode.SYNTAX_TOKEN_UNEXPECTED,
|
|
150
150
|
position: token.position,
|
|
151
151
|
length: token.length,
|
|
152
152
|
};
|
package/dist/esm/parser.js
CHANGED
|
@@ -43,7 +43,7 @@ export const parseSearchInputQuery = (input, fieldSchemas = []) => {
|
|
|
43
43
|
if (finalToken.type !== TokenType.EOF) {
|
|
44
44
|
throw {
|
|
45
45
|
message: 'Unexpected ")"',
|
|
46
|
-
code: SearchQueryErrorCode.
|
|
46
|
+
code: SearchQueryErrorCode.SYNTAX_PARENTHESIS_UNEXPECTED,
|
|
47
47
|
position: finalToken.position,
|
|
48
48
|
length: finalToken.length,
|
|
49
49
|
};
|