search-input-query-parser 0.1.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.
Files changed (58) hide show
  1. package/dist/cjs/first-pass-parser.js +77 -0
  2. package/dist/cjs/lexer.js +322 -0
  3. package/dist/cjs/parse-in-values.js +65 -0
  4. package/dist/cjs/parse-primary.js +154 -0
  5. package/dist/cjs/parse-range-expression.js +174 -0
  6. package/dist/cjs/parser.js +85 -0
  7. package/dist/cjs/search-query-to-sql.js +346 -0
  8. package/dist/cjs/transform-to-expression.js +130 -0
  9. package/dist/cjs/validate-expression-fields.js +244 -0
  10. package/dist/cjs/validate-in-expression.js +33 -0
  11. package/dist/cjs/validate-string.js +65 -0
  12. package/dist/cjs/validate-wildcard.js +40 -0
  13. package/dist/cjs/validator.js +34 -0
  14. package/dist/esm/first-pass-parser.js +73 -0
  15. package/dist/esm/lexer.js +315 -0
  16. package/dist/esm/parse-in-values.js +61 -0
  17. package/dist/esm/parse-primary.js +147 -0
  18. package/dist/esm/parse-range-expression.js +170 -0
  19. package/dist/esm/parser.js +81 -0
  20. package/dist/esm/search-query-to-sql.js +341 -0
  21. package/dist/esm/transform-to-expression.js +126 -0
  22. package/dist/esm/validate-expression-fields.js +240 -0
  23. package/dist/esm/validate-in-expression.js +29 -0
  24. package/dist/esm/validate-string.js +61 -0
  25. package/dist/esm/validate-wildcard.js +36 -0
  26. package/dist/esm/validator.js +30 -0
  27. package/dist/types/first-pass-parser.d.ts +40 -0
  28. package/dist/types/lexer.d.ts +27 -0
  29. package/dist/types/parse-in-values.d.ts +3 -0
  30. package/dist/types/parse-primary.d.ts +6 -0
  31. package/dist/types/parse-range-expression.d.ts +2 -0
  32. package/dist/types/parser.d.ts +68 -0
  33. package/dist/types/search-query-to-sql.d.ts +18 -0
  34. package/dist/types/transform-to-expression.d.ts +3 -0
  35. package/dist/types/validate-expression-fields.d.ts +4 -0
  36. package/dist/types/validate-in-expression.d.ts +3 -0
  37. package/dist/types/validate-string.d.ts +3 -0
  38. package/dist/types/validate-wildcard.d.ts +3 -0
  39. package/dist/types/validator.d.ts +8 -0
  40. package/package.json +52 -0
  41. package/src/first-pass-parser.test.ts +441 -0
  42. package/src/first-pass-parser.ts +144 -0
  43. package/src/lexer.test.ts +439 -0
  44. package/src/lexer.ts +387 -0
  45. package/src/parse-in-values.ts +74 -0
  46. package/src/parse-primary.ts +179 -0
  47. package/src/parse-range-expression.ts +187 -0
  48. package/src/parser.test.ts +982 -0
  49. package/src/parser.ts +219 -0
  50. package/src/search-query-to-sql.test.ts +503 -0
  51. package/src/search-query-to-sql.ts +506 -0
  52. package/src/transform-to-expression.ts +153 -0
  53. package/src/validate-expression-fields.ts +296 -0
  54. package/src/validate-in-expression.ts +36 -0
  55. package/src/validate-string.ts +73 -0
  56. package/src/validate-wildcard.ts +45 -0
  57. package/src/validator.test.ts +192 -0
  58. package/src/validator.ts +53 -0
@@ -0,0 +1,240 @@
1
+ // Helper to validate numeric values
2
+ const validateNumber = (value, position, errors) => {
3
+ if (value === "")
4
+ return false;
5
+ if (isNaN(Number(value))) {
6
+ errors.push({
7
+ message: "Invalid numeric value",
8
+ position,
9
+ length: value.length,
10
+ });
11
+ return false;
12
+ }
13
+ return true;
14
+ };
15
+ // Helper to validate range values for numeric fields
16
+ const validateNumericRange = (start, end, basePosition, errors) => {
17
+ let isValid = true;
18
+ const startPos = basePosition;
19
+ const endPos = basePosition + start.length + 2; // +2 for the '..'
20
+ if (start && !validateNumber(start, startPos, errors)) {
21
+ isValid = false;
22
+ }
23
+ if (end && !validateNumber(end, endPos, errors)) {
24
+ isValid = false;
25
+ }
26
+ if (isValid && start && end) {
27
+ const startNum = Number(start);
28
+ const endNum = Number(end);
29
+ if (startNum > endNum) {
30
+ errors.push({
31
+ message: "Range start must be less than or equal to range end",
32
+ position: basePosition,
33
+ length: start.length + 2 + end.length,
34
+ });
35
+ isValid = false;
36
+ }
37
+ }
38
+ return isValid;
39
+ };
40
+ // Helper to validate numeric comparison operators
41
+ const validateNumericComparison = (operator, value, basePosition, errors) => {
42
+ const valuePosition = basePosition + operator.length;
43
+ return validateNumber(value, valuePosition, errors);
44
+ };
45
+ // Field validation helpers
46
+ const validateFieldValue = (expr, allowedFields, errors, schemas) => {
47
+ switch (expr.type) {
48
+ case "IN": {
49
+ if (!allowedFields.has(expr.field.toLowerCase())) {
50
+ errors.push({
51
+ message: `Invalid field: "${expr.field}"`,
52
+ position: expr.position,
53
+ length: expr.field.length,
54
+ });
55
+ }
56
+ // Get schema for type validation
57
+ const schema = schemas.get(expr.field.toLowerCase());
58
+ if (schema) {
59
+ expr.values.forEach((value, index) => {
60
+ switch (schema.type) {
61
+ case "number":
62
+ if (isNaN(Number(value))) {
63
+ errors.push({
64
+ message: "Invalid numeric value",
65
+ position: expr.position +
66
+ expr.field.length +
67
+ 4 +
68
+ index * (value.length + 1),
69
+ length: value.length,
70
+ });
71
+ }
72
+ break;
73
+ case "date":
74
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) {
75
+ errors.push({
76
+ message: "Invalid date format",
77
+ position: expr.position +
78
+ expr.field.length +
79
+ 3 +
80
+ index * (value.length + 1),
81
+ length: value.length,
82
+ });
83
+ }
84
+ break;
85
+ }
86
+ });
87
+ }
88
+ break;
89
+ }
90
+ case "WILDCARD": {
91
+ // For wildcard patterns, validate against field type constraints
92
+ const schema = schemas.get(expr.prefix.toLowerCase());
93
+ if ((schema === null || schema === void 0 ? void 0 : schema.type) === "number" || (schema === null || schema === void 0 ? void 0 : schema.type) === "date") {
94
+ errors.push({
95
+ message: `Wildcards are not allowed for ${schema.type} fields`,
96
+ position: expr.position,
97
+ length: expr.length,
98
+ });
99
+ }
100
+ break;
101
+ }
102
+ case "STRING": {
103
+ const colonIndex = expr.value.indexOf(":");
104
+ if (colonIndex === -1)
105
+ return;
106
+ const fieldName = expr.value.substring(0, colonIndex).trim();
107
+ const value = expr.value.substring(colonIndex + 1).trim();
108
+ if (!allowedFields.has(fieldName.toLowerCase()) && colonIndex > 0) {
109
+ errors.push({
110
+ message: `Invalid field: "${fieldName}"`,
111
+ position: expr.position,
112
+ length: colonIndex,
113
+ });
114
+ }
115
+ if (!value) {
116
+ errors.push({
117
+ message: "Expected field value",
118
+ position: expr.position,
119
+ length: colonIndex + 1,
120
+ });
121
+ return;
122
+ }
123
+ if (value.startsWith(":")) {
124
+ errors.push({
125
+ message: "Missing field name",
126
+ position: expr.position,
127
+ length: value.length + colonIndex + 1,
128
+ });
129
+ return;
130
+ }
131
+ const schema = schemas.get(fieldName.toLowerCase());
132
+ if (!schema)
133
+ return;
134
+ const valueStartPosition = expr.position + colonIndex + 1;
135
+ if (schema.type === "number") {
136
+ if (value.includes("..")) {
137
+ if (value === ".." || value.includes("...")) {
138
+ errors.push({
139
+ message: "Invalid range format",
140
+ position: valueStartPosition,
141
+ length: value.length,
142
+ });
143
+ return;
144
+ }
145
+ const [start, end] = value.split("..");
146
+ validateNumericRange(start, end, valueStartPosition, errors);
147
+ return;
148
+ }
149
+ const comparisonMatch = value.match(/^(>=|>|<=|<)(.*)$/);
150
+ if (comparisonMatch) {
151
+ const [, operator, compValue] = comparisonMatch;
152
+ const invalidOp = /^[<>]{2,}|>=>/;
153
+ if (invalidOp.test(value)) {
154
+ errors.push({
155
+ message: "Invalid range operator",
156
+ position: valueStartPosition,
157
+ length: 3,
158
+ });
159
+ return;
160
+ }
161
+ if (!compValue) {
162
+ errors.push({
163
+ message: "Expected range value",
164
+ position: valueStartPosition + operator.length,
165
+ length: 0,
166
+ });
167
+ return;
168
+ }
169
+ validateNumericComparison(operator, compValue, valueStartPosition, errors);
170
+ return;
171
+ }
172
+ validateNumber(value, valueStartPosition, errors);
173
+ return;
174
+ }
175
+ if (schema.type === "date") {
176
+ const dateValidator = (dateStr) => {
177
+ if (!dateStr)
178
+ return true;
179
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) {
180
+ return false;
181
+ }
182
+ const date = new Date(dateStr);
183
+ return (!isNaN(date.getTime()) &&
184
+ dateStr === date.toISOString().split("T")[0]);
185
+ };
186
+ if (value.includes("..")) {
187
+ const [start, end] = value.split("..");
188
+ if (!dateValidator(start) || !dateValidator(end)) {
189
+ errors.push({
190
+ message: "Invalid date format",
191
+ position: valueStartPosition,
192
+ length: value.length,
193
+ });
194
+ return;
195
+ }
196
+ }
197
+ else {
198
+ const comparisonMatch = value.match(/^(>=|>|<=|<)(.*)$/);
199
+ if (comparisonMatch) {
200
+ const [, , dateStr] = comparisonMatch;
201
+ if (!dateValidator(dateStr)) {
202
+ errors.push({
203
+ message: "Invalid date format",
204
+ position: valueStartPosition,
205
+ length: value.length,
206
+ });
207
+ return;
208
+ }
209
+ }
210
+ else if (!dateValidator(value)) {
211
+ errors.push({
212
+ message: "Invalid date format",
213
+ position: valueStartPosition,
214
+ length: value.length,
215
+ });
216
+ return;
217
+ }
218
+ }
219
+ }
220
+ break;
221
+ }
222
+ }
223
+ };
224
+ export const validateExpressionFields = (expr, allowedFields, errors, schemas) => {
225
+ switch (expr.type) {
226
+ case "STRING":
227
+ case "WILDCARD":
228
+ case "IN":
229
+ validateFieldValue(expr, allowedFields, errors, schemas);
230
+ break;
231
+ case "AND":
232
+ case "OR":
233
+ validateExpressionFields(expr.left, allowedFields, errors, schemas);
234
+ validateExpressionFields(expr.right, allowedFields, errors, schemas);
235
+ break;
236
+ case "NOT":
237
+ validateExpressionFields(expr.expression, allowedFields, errors, schemas);
238
+ break;
239
+ }
240
+ };
@@ -0,0 +1,29 @@
1
+ import { reservedWords } from "./validator";
2
+ export const validateInExpression = (expr, errors) => {
3
+ // Validate field name pattern
4
+ if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(expr.field)) {
5
+ errors.push({
6
+ message: "Invalid characters in field name",
7
+ position: expr.position,
8
+ length: expr.field.length,
9
+ });
10
+ }
11
+ // Check for reserved words
12
+ if (reservedWords.has(expr.field.toUpperCase())) {
13
+ errors.push({
14
+ message: `${expr.field} is a reserved word`,
15
+ position: expr.position,
16
+ length: expr.field.length,
17
+ });
18
+ }
19
+ // Validate value format based on field type
20
+ expr.values.forEach((value, index) => {
21
+ if (value.includes(",")) {
22
+ errors.push({
23
+ message: "Invalid character in IN value",
24
+ position: expr.position + expr.field.length + 3 + index * (value.length + 1),
25
+ length: value.length,
26
+ });
27
+ }
28
+ });
29
+ };
@@ -0,0 +1,61 @@
1
+ import { reservedWords } from "./validator";
2
+ import { validateWildcard } from "./validate-wildcard";
3
+ // Validate individual strings (field:value pairs or plain terms)
4
+ export const validateString = (expr, errors) => {
5
+ // Validate wildcard usage
6
+ validateWildcard(expr, errors);
7
+ // For wildcard patterns, no additional validation needed
8
+ if (expr.type === "WILDCARD") {
9
+ return;
10
+ }
11
+ // Handle STRING type
12
+ // Check for empty field values
13
+ if (expr.value.endsWith(":")) {
14
+ errors.push({
15
+ message: "Expected field value",
16
+ position: expr.position,
17
+ length: expr.length,
18
+ });
19
+ return;
20
+ }
21
+ // Check for field values that start with colon
22
+ if (expr.value.startsWith(":")) {
23
+ errors.push({
24
+ message: "Missing field name",
25
+ position: expr.position,
26
+ length: expr.length,
27
+ });
28
+ return;
29
+ }
30
+ // For field:value patterns, validate the field name
31
+ if (expr.value.includes(":")) {
32
+ const [fieldName] = expr.value.split(":");
33
+ // Check for reserved words used as field names
34
+ if (reservedWords.has(fieldName.toUpperCase())) {
35
+ errors.push({
36
+ message: `${fieldName} is a reserved word`,
37
+ position: expr.position,
38
+ length: fieldName.length,
39
+ });
40
+ return;
41
+ }
42
+ // Check for invalid characters in field names
43
+ if (!/^[a-zA-Z0-9_-]+$/.test(fieldName)) {
44
+ errors.push({
45
+ message: "Invalid characters in field name",
46
+ position: expr.position,
47
+ length: fieldName.length,
48
+ });
49
+ return;
50
+ }
51
+ }
52
+ // Handle standalone reserved words (not in field:value pattern)
53
+ if (!expr.value.includes(":") &&
54
+ reservedWords.has(expr.value.toUpperCase())) {
55
+ errors.push({
56
+ message: `${expr.value} is a reserved word`,
57
+ position: expr.position,
58
+ length: expr.length,
59
+ });
60
+ }
61
+ };
@@ -0,0 +1,36 @@
1
+ // Validates wildcard patterns
2
+ export const validateWildcard = (expr, errors) => {
3
+ const value = expr.type === "STRING" ? expr.value : expr.prefix + "*";
4
+ const starCount = (value.match(/\*/g) || []).length;
5
+ const isQuoted = expr.quoted;
6
+ // For unquoted strings
7
+ if (!isQuoted) {
8
+ const firstStar = value.indexOf("*");
9
+ if (starCount > 1) {
10
+ const secondStar = value.indexOf("*", firstStar + 1);
11
+ errors.push({
12
+ message: "Only one trailing wildcard (*) is allowed",
13
+ position: expr.position + secondStar,
14
+ length: 1,
15
+ });
16
+ }
17
+ if ((firstStar !== -1 && firstStar !== value.length - 1) && !value.endsWith("**")) {
18
+ errors.push({
19
+ message: "Wildcard (*) can only appear at the end of a term",
20
+ position: expr.position + firstStar,
21
+ length: 1,
22
+ });
23
+ }
24
+ }
25
+ // For quoted strings
26
+ else {
27
+ // Handle multiple wildcards or internal wildcards in quoted strings
28
+ if (value.endsWith("**")) {
29
+ errors.push({
30
+ message: "Only one trailing wildcard (*) is allowed",
31
+ position: expr.position + value.length - 1,
32
+ length: 1,
33
+ });
34
+ }
35
+ }
36
+ };
@@ -0,0 +1,30 @@
1
+ import { validateInExpression } from "./validate-in-expression";
2
+ import { validateString } from "./validate-string";
3
+ export const reservedWords = new Set(["AND", "OR"]);
4
+ const walkExpression = (expr, errors) => {
5
+ switch (expr.type) {
6
+ case "STRING":
7
+ case "WILDCARD":
8
+ validateString(expr, errors);
9
+ break;
10
+ case "AND":
11
+ case "OR":
12
+ walkExpression(expr.left, errors);
13
+ walkExpression(expr.right, errors);
14
+ break;
15
+ case "NOT":
16
+ walkExpression(expr.expression, errors);
17
+ break;
18
+ case "IN":
19
+ validateInExpression(expr, errors);
20
+ break;
21
+ }
22
+ };
23
+ export const validateSearchQuery = (expression) => {
24
+ const errors = [];
25
+ if (expression === null) {
26
+ return errors;
27
+ }
28
+ walkExpression(expression, errors);
29
+ return errors;
30
+ };
@@ -0,0 +1,40 @@
1
+ import { TokenStream } from "./lexer";
2
+ export type PositionLength = {
3
+ position: number;
4
+ length: number;
5
+ };
6
+ export type StringLiteral = {
7
+ readonly type: "STRING";
8
+ readonly value: string;
9
+ readonly quoted: boolean;
10
+ } & PositionLength;
11
+ export type WildcardPattern = {
12
+ readonly type: "WILDCARD";
13
+ readonly prefix: string;
14
+ readonly quoted: boolean;
15
+ } & PositionLength;
16
+ export type AndExpression = {
17
+ readonly type: "AND";
18
+ readonly left: FirstPassExpression;
19
+ readonly right: FirstPassExpression;
20
+ } & PositionLength;
21
+ export type OrExpression = {
22
+ readonly type: "OR";
23
+ readonly left: FirstPassExpression;
24
+ readonly right: FirstPassExpression;
25
+ } & PositionLength;
26
+ export type NotExpression = {
27
+ readonly type: "NOT";
28
+ readonly expression: FirstPassExpression;
29
+ } & PositionLength;
30
+ export type InExpression = {
31
+ readonly type: "IN";
32
+ readonly field: string;
33
+ readonly values: string[];
34
+ } & PositionLength;
35
+ export type FirstPassExpression = StringLiteral | WildcardPattern | AndExpression | OrExpression | NotExpression | InExpression;
36
+ export interface ParseResult<T> {
37
+ readonly result: T;
38
+ readonly stream: TokenStream;
39
+ }
40
+ export declare const parseExpression: (stream: TokenStream, minPrecedence?: number) => ParseResult<FirstPassExpression>;
@@ -0,0 +1,27 @@
1
+ export declare enum TokenType {
2
+ STRING = "STRING",
3
+ QUOTED_STRING = "QUOTED_STRING",
4
+ LPAREN = "LPAREN",
5
+ RPAREN = "RPAREN",
6
+ AND = "AND",
7
+ OR = "OR",
8
+ NOT = "NOT",
9
+ EOF = "EOF",
10
+ IN = "IN",
11
+ COMMA = "COMMA",
12
+ NUMBER = "NUMBER"
13
+ }
14
+ export interface Token {
15
+ type: TokenType;
16
+ value: string;
17
+ position: number;
18
+ length: number;
19
+ }
20
+ export interface TokenStream {
21
+ readonly tokens: Token[];
22
+ readonly position: number;
23
+ }
24
+ export declare const createStream: (tokens: Token[]) => TokenStream;
25
+ export declare const currentToken: (stream: TokenStream) => Token;
26
+ export declare const advanceStream: (stream: TokenStream) => TokenStream;
27
+ export declare const tokenize: (input: string) => Token[];
@@ -0,0 +1,3 @@
1
+ import { ParseResult } from "./first-pass-parser";
2
+ import { TokenStream } from "./lexer";
3
+ export declare const parseInValues: (stream: TokenStream, inValuePosition: number) => ParseResult<string[]>;
@@ -0,0 +1,6 @@
1
+ import { ParseResult, FirstPassExpression } from "./first-pass-parser";
2
+ import { TokenStream, TokenType } from "./lexer";
3
+ export declare const expectToken: (stream: TokenStream, type: TokenType, message?: string) => TokenStream;
4
+ export declare const isFieldValuePattern: (value: string) => boolean;
5
+ export declare const extractFieldValue: (value: string) => [string, string];
6
+ export declare const parsePrimary: (stream: TokenStream) => ParseResult<FirstPassExpression>;
@@ -0,0 +1,2 @@
1
+ import { FieldSchema, RangeExpression, FieldValue } from "./parser";
2
+ export declare const parseRangeExpression: (fieldName: string, value: string, schema: FieldSchema | undefined, position: number, colonIndex: number) => RangeExpression | FieldValue;
@@ -0,0 +1,68 @@
1
+ import { PositionLength } from "./first-pass-parser";
2
+ import { ValidationError } from "./validator";
3
+ interface FieldSchema {
4
+ name: string;
5
+ type: "string" | "number" | "date" | "boolean";
6
+ }
7
+ type SearchTerm = {
8
+ readonly type: "SEARCH_TERM";
9
+ readonly value: string;
10
+ } & PositionLength;
11
+ type WildcardPattern = {
12
+ readonly type: "WILDCARD";
13
+ readonly prefix: string;
14
+ readonly quoted: boolean;
15
+ } & PositionLength;
16
+ type Field = {
17
+ readonly type: "FIELD";
18
+ readonly value: string;
19
+ } & PositionLength;
20
+ type Value = {
21
+ readonly type: "VALUE";
22
+ readonly value: string;
23
+ } & PositionLength;
24
+ type RangeOperator = ">=" | ">" | "<=" | "<" | "BETWEEN";
25
+ type RangeExpression = {
26
+ readonly type: "RANGE";
27
+ readonly field: Field;
28
+ readonly operator: RangeOperator;
29
+ readonly value: Value;
30
+ readonly value2?: Value;
31
+ } & PositionLength;
32
+ export type FieldValue = {
33
+ readonly type: "FIELD_VALUE";
34
+ readonly field: Field;
35
+ readonly value: Value;
36
+ };
37
+ type And = {
38
+ readonly type: "AND";
39
+ readonly left: Expression;
40
+ readonly right: Expression;
41
+ } & PositionLength;
42
+ type Or = {
43
+ readonly type: "OR";
44
+ readonly left: Expression;
45
+ readonly right: Expression;
46
+ } & PositionLength;
47
+ type Not = {
48
+ readonly type: "NOT";
49
+ readonly expression: Expression;
50
+ } & PositionLength;
51
+ type InExpression = {
52
+ readonly type: "IN";
53
+ readonly field: Field;
54
+ readonly values: Value[];
55
+ } & PositionLength;
56
+ type Expression = SearchTerm | WildcardPattern | FieldValue | RangeExpression | And | Or | Not | InExpression;
57
+ type SearchQuery = {
58
+ readonly type: "SEARCH_QUERY";
59
+ readonly expression: Expression | null;
60
+ };
61
+ type SearchQueryError = {
62
+ readonly type: "SEARCH_QUERY_ERROR";
63
+ readonly expression: null;
64
+ readonly errors: ValidationError[];
65
+ };
66
+ declare const stringify: (expr: Expression) => string;
67
+ export declare const parseSearchInputQuery: (input: string, fieldSchemas?: FieldSchema[]) => SearchQuery | SearchQueryError;
68
+ export { type SearchQuery, type SearchQueryError, type Expression, type ValidationError, type FieldSchema, type RangeOperator, type RangeExpression, type WildcardPattern, type Value, stringify, };
@@ -0,0 +1,18 @@
1
+ import { SearchQuery, FieldSchema } from "./parser";
2
+ export interface SqlQueryResult {
3
+ text: string;
4
+ values: any[];
5
+ }
6
+ export type SearchType = "ilike" | "tsvector" | "paradedb";
7
+ export interface SearchQueryOptions {
8
+ searchType?: SearchType;
9
+ language?: string;
10
+ }
11
+ /**
12
+ * Convert a SearchQuery to a SQL WHERE clause with specified search type
13
+ */
14
+ export declare const searchQueryToSql: (query: SearchQuery, searchableColumns: string[], schemas?: FieldSchema[], options?: SearchQueryOptions) => SqlQueryResult;
15
+ /**
16
+ * Convert a search string directly to SQL
17
+ */
18
+ export declare const searchStringToSql: (searchString: string, searchableColumns: string[], schemas?: FieldSchema[], options?: SearchQueryOptions) => SqlQueryResult;
@@ -0,0 +1,3 @@
1
+ import { FirstPassExpression } from "./first-pass-parser";
2
+ import { FieldSchema, Expression } from "./parser";
3
+ export declare const transformToExpression: (expr: FirstPassExpression, schemas: Map<string, FieldSchema>) => Expression;
@@ -0,0 +1,4 @@
1
+ import { FieldSchema } from "./parser";
2
+ import { FirstPassExpression } from "./first-pass-parser";
3
+ import { ValidationError } from "./validator";
4
+ export declare const validateExpressionFields: (expr: FirstPassExpression, allowedFields: Set<string>, errors: ValidationError[], schemas: Map<string, FieldSchema>) => void;
@@ -0,0 +1,3 @@
1
+ import { InExpression } from "./first-pass-parser";
2
+ import { ValidationError } from "./validator";
3
+ export declare const validateInExpression: (expr: InExpression, errors: ValidationError[]) => void;
@@ -0,0 +1,3 @@
1
+ import { StringLiteral, WildcardPattern } from "./first-pass-parser";
2
+ import { ValidationError } from "./validator";
3
+ export declare const validateString: (expr: StringLiteral | WildcardPattern, errors: ValidationError[]) => void;
@@ -0,0 +1,3 @@
1
+ import { StringLiteral, WildcardPattern } from "./first-pass-parser";
2
+ import { ValidationError } from "./validator";
3
+ export declare const validateWildcard: (expr: StringLiteral | WildcardPattern, errors: ValidationError[]) => void;
@@ -0,0 +1,8 @@
1
+ import { FirstPassExpression } from "./first-pass-parser";
2
+ export type ValidationError = {
3
+ message: string;
4
+ position: number;
5
+ length: number;
6
+ };
7
+ export declare const reservedWords: Set<string>;
8
+ export declare const validateSearchQuery: (expression: FirstPassExpression) => ValidationError[];
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "search-input-query-parser",
3
+ "version": "0.1.0",
4
+ "description": "A parser for advanced search query syntax with field:value support",
5
+ "keywords": [
6
+ "search",
7
+ "parser",
8
+ "query",
9
+ "typescript"
10
+ ],
11
+ "author": "William Cotton",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/williamcotton/search-input-query"
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "src"
19
+ ],
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/types/parser.d.ts",
23
+ "import": "./dist/esm/parser.js",
24
+ "require": "./dist/cjs/parser.js"
25
+ }
26
+ },
27
+ "scripts": {
28
+ "test": "jest",
29
+ "test:watch": "jest --watch",
30
+ "type-check": "tsc --noEmit",
31
+ "build": "npm run build:esm && npm run build:cjs && npm run build:types",
32
+ "build:esm": "tsc -p tsconfig.esm.json",
33
+ "build:cjs": "tsc -p tsconfig.cjs.json",
34
+ "build:types": "tsc -p tsconfig.types.json",
35
+ "prepublishOnly": "npm run build"
36
+ },
37
+ "jest": {
38
+ "preset": "ts-jest",
39
+ "testEnvironment": "node",
40
+ "testMatch": [
41
+ "**/*.test.ts"
42
+ ]
43
+ },
44
+ "license": "ISC",
45
+ "devDependencies": {
46
+ "@types/jest": "^29.5.14",
47
+ "@types/node": "^20.11.0",
48
+ "jest": "^29.7.0",
49
+ "ts-jest": "^29.2.5",
50
+ "typescript": "^5.3.3"
51
+ }
52
+ }