@marianmeres/condition-parser 1.7.0 → 1.7.2
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/AGENTS.md +201 -0
- package/API.md +355 -0
- package/README.md +19 -14
- package/dist/mod.d.ts +31 -0
- package/dist/mod.js +31 -0
- package/dist/parser.d.ts +37 -8
- package/package.json +10 -4
package/AGENTS.md
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# Agent Context: @marianmeres/condition-parser
|
|
2
|
+
|
|
3
|
+
## Package Overview
|
|
4
|
+
|
|
5
|
+
- **Name**: `@marianmeres/condition-parser`
|
|
6
|
+
- **Version**: 1.7.1
|
|
7
|
+
- **Purpose**: Human-friendly search conditions notation parser (Gmail-style search syntax)
|
|
8
|
+
- **License**: MIT
|
|
9
|
+
- **Runtime**: Deno (primary), Node.js (via NPM distribution)
|
|
10
|
+
|
|
11
|
+
## File Structure
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
src/
|
|
15
|
+
├── mod.ts # Public entry point (re-exports parser.ts)
|
|
16
|
+
└── parser.ts # Main parser implementation (705 lines)
|
|
17
|
+
|
|
18
|
+
tests/
|
|
19
|
+
└── all.test.ts # Test suite (682 lines, 30 tests)
|
|
20
|
+
|
|
21
|
+
scripts/
|
|
22
|
+
└── build-npm.ts # NPM distribution builder
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Public API
|
|
26
|
+
|
|
27
|
+
### Main Export: `ConditionParser`
|
|
28
|
+
|
|
29
|
+
Static class with two public methods:
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
// Primary method - parses search expressions
|
|
33
|
+
ConditionParser.parse(
|
|
34
|
+
input: string,
|
|
35
|
+
options?: Partial<ConditionParserOptions>
|
|
36
|
+
): ConditionParserResult
|
|
37
|
+
|
|
38
|
+
// Error helper (prefixed __ for internal use)
|
|
39
|
+
ConditionParser.__createError(
|
|
40
|
+
input: string,
|
|
41
|
+
pos: number,
|
|
42
|
+
message: string,
|
|
43
|
+
contextRadius?: number
|
|
44
|
+
): Error
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Static Properties
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
ConditionParser.DEFAULT_OPERATOR: string = "eq"
|
|
51
|
+
ConditionParser.DEBUG: boolean = false
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Exported Types
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
interface ConditionParserOptions {
|
|
58
|
+
defaultOperator: string; // default: "eq"
|
|
59
|
+
debug: boolean; // default: false
|
|
60
|
+
transform?: (ctx: ExpressionContext) => ExpressionContext;
|
|
61
|
+
preAddHook?: (ctx: ExpressionContext) => null | undefined | ExpressionContext;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
interface ConditionParserResult {
|
|
65
|
+
parsed: ConditionDump; // from @marianmeres/condition-builder
|
|
66
|
+
unparsed: string;
|
|
67
|
+
meta: Meta;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface Meta {
|
|
71
|
+
keys: string[];
|
|
72
|
+
operators: string[];
|
|
73
|
+
values: any[];
|
|
74
|
+
expressions: ExpressionData[];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
interface ExpressionData {
|
|
78
|
+
key: string;
|
|
79
|
+
operator: string;
|
|
80
|
+
value: string;
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Parser Grammar
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
condition := term (conditionOp term)*
|
|
88
|
+
term := basicExpr | "(" condition ")"
|
|
89
|
+
basicExpr := identifier ":" identifier (":" identifier)?
|
|
90
|
+
identifier := quotedString | unquotedString | parenthesizedValue
|
|
91
|
+
quotedString := ("'" | '"') ... ("'" | '"')
|
|
92
|
+
unquotedString := [^:\s()]+
|
|
93
|
+
parenthesizedValue := "(" ... ")"
|
|
94
|
+
conditionOp := "and" | "or" | "and not" | "or not" | <implicit and>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Expression Formats
|
|
98
|
+
|
|
99
|
+
| Input | Parsed Key | Parsed Operator | Parsed Value |
|
|
100
|
+
|-------|-----------|-----------------|--------------|
|
|
101
|
+
| `key:value` | key | eq (default) | value |
|
|
102
|
+
| `key:op:value` | key | op | value |
|
|
103
|
+
| `"k k":"o o":"v v"` | k k | o o | v v |
|
|
104
|
+
| `key:(complex value)` | key | eq | complex value |
|
|
105
|
+
|
|
106
|
+
## Logical Operators
|
|
107
|
+
|
|
108
|
+
| Syntax | ConditionJoinOperator |
|
|
109
|
+
|--------|----------------------|
|
|
110
|
+
| `and` | "and" |
|
|
111
|
+
| `or` | "or" |
|
|
112
|
+
| `and not` | "andNot" |
|
|
113
|
+
| `or not` | "orNot" |
|
|
114
|
+
| (implicit) | "and" |
|
|
115
|
+
|
|
116
|
+
## Dependencies
|
|
117
|
+
|
|
118
|
+
### Runtime
|
|
119
|
+
|
|
120
|
+
- `@marianmeres/condition-builder` - Type definitions and integration target
|
|
121
|
+
|
|
122
|
+
### Development
|
|
123
|
+
|
|
124
|
+
- `@marianmeres/npmbuild` - NPM distribution builder
|
|
125
|
+
- `@std/assert` - Deno test assertions
|
|
126
|
+
- `@std/fs`, `@std/path` - File system utilities
|
|
127
|
+
|
|
128
|
+
## Build Commands
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
deno task test # Run tests
|
|
132
|
+
deno task test:watch # Run tests in watch mode
|
|
133
|
+
deno task npm:build # Build NPM distribution
|
|
134
|
+
deno task publish # Publish to JSR and NPM
|
|
135
|
+
deno task release # Patch version release
|
|
136
|
+
deno task release minor # Minor version release
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Key Implementation Details
|
|
140
|
+
|
|
141
|
+
1. **Recursive Descent Parser**: Layered parsing methods handle grammar levels
|
|
142
|
+
2. **Fault Tolerance**: Parse errors don't throw; unparsable content preserved in `unparsed`
|
|
143
|
+
3. **Escape Support**: Backslash escapes for `'`, `"`, `:`, `)` within strings
|
|
144
|
+
4. **Case Insensitive**: Operators `and`, `or`, `not` are case-insensitive
|
|
145
|
+
5. **Metadata Collection**: Unique keys, operators, values tracked in `meta`
|
|
146
|
+
6. **Transform Pipeline**: Optional expression transformation before output
|
|
147
|
+
7. **Pre-add Hook**: Optional filtering/routing of expressions
|
|
148
|
+
|
|
149
|
+
## Integration Pattern
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
import { ConditionParser } from "@marianmeres/condition-parser";
|
|
153
|
+
import { Condition } from "@marianmeres/condition-builder";
|
|
154
|
+
|
|
155
|
+
const { parsed, unparsed } = ConditionParser.parse(userInput);
|
|
156
|
+
const condition = Condition.restore(parsed);
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Test Coverage
|
|
160
|
+
|
|
161
|
+
30 tests covering:
|
|
162
|
+
- Basic expression parsing
|
|
163
|
+
- Quoted identifiers (single/double quotes)
|
|
164
|
+
- Escaped characters
|
|
165
|
+
- Logical operators (and, or, and not, or not)
|
|
166
|
+
- Parenthesized grouping
|
|
167
|
+
- Nested conditions
|
|
168
|
+
- Free text handling
|
|
169
|
+
- Transform function
|
|
170
|
+
- Pre-add hook
|
|
171
|
+
- Error handling
|
|
172
|
+
- Metadata collection
|
|
173
|
+
|
|
174
|
+
## Error Handling
|
|
175
|
+
|
|
176
|
+
Parser is fault-tolerant:
|
|
177
|
+
- Malformed input doesn't throw exceptions
|
|
178
|
+
- Partially parsed content preserved in `parsed`
|
|
179
|
+
- Unparsable remainder preserved in `unparsed`
|
|
180
|
+
- Error positions tracked internally for debugging
|
|
181
|
+
|
|
182
|
+
## Common Tasks
|
|
183
|
+
|
|
184
|
+
### Adding new operators
|
|
185
|
+
Operators are strings - no code changes needed. Use `defaultOperator` option or explicit `key:operator:value` syntax.
|
|
186
|
+
|
|
187
|
+
### Custom transformations
|
|
188
|
+
Use `transform` option to normalize keys/values:
|
|
189
|
+
```typescript
|
|
190
|
+
ConditionParser.parse(input, {
|
|
191
|
+
transform: (ctx) => ({ ...ctx, key: ctx.key.toLowerCase() })
|
|
192
|
+
});
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Routing expressions
|
|
196
|
+
Use `preAddHook` to filter or route expressions:
|
|
197
|
+
```typescript
|
|
198
|
+
ConditionParser.parse(input, {
|
|
199
|
+
preAddHook: (ctx) => ctx.key === "special" ? null : ctx
|
|
200
|
+
});
|
|
201
|
+
```
|
package/API.md
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
# API Reference
|
|
2
|
+
|
|
3
|
+
Complete API documentation for `@marianmeres/condition-parser`.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [ConditionParser](#conditionparser)
|
|
8
|
+
- [Static Properties](#static-properties)
|
|
9
|
+
- [Static Methods](#static-methods)
|
|
10
|
+
- [Types](#types)
|
|
11
|
+
- [ConditionParserOptions](#conditionparseroptions)
|
|
12
|
+
- [ConditionParserResult](#conditionparserresult)
|
|
13
|
+
- [Meta](#meta)
|
|
14
|
+
- [ExpressionData](#expressiondata)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## ConditionParser
|
|
19
|
+
|
|
20
|
+
The main parser class. All methods are static.
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { ConditionParser } from "@marianmeres/condition-parser";
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Static Properties
|
|
27
|
+
|
|
28
|
+
#### `DEFAULT_OPERATOR`
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
static DEFAULT_OPERATOR: string = "eq"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Default operator used when none is specified in the expression (e.g., `key:value` uses `"eq"`).
|
|
35
|
+
|
|
36
|
+
#### `DEBUG`
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
static DEBUG: boolean = false
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Global debug flag. When `true`, all parser instances log debug information to the console.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
### Static Methods
|
|
47
|
+
|
|
48
|
+
#### `parse()`
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
static parse(
|
|
52
|
+
input: string,
|
|
53
|
+
options?: Partial<ConditionParserOptions>
|
|
54
|
+
): ConditionParserResult
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Parses a human-friendly search condition string into a structured format.
|
|
58
|
+
|
|
59
|
+
**Parameters:**
|
|
60
|
+
|
|
61
|
+
| Name | Type | Description |
|
|
62
|
+
|------|------|-------------|
|
|
63
|
+
| `input` | `string` | The search expression string to parse |
|
|
64
|
+
| `options` | `Partial<ConditionParserOptions>` | Optional configuration for parsing behavior |
|
|
65
|
+
|
|
66
|
+
**Returns:** [`ConditionParserResult`](#conditionparserresult)
|
|
67
|
+
|
|
68
|
+
**Example:**
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
// Basic parsing
|
|
72
|
+
const { parsed, unparsed } = ConditionParser.parse("foo:bar and baz:bat");
|
|
73
|
+
|
|
74
|
+
// With options
|
|
75
|
+
const result = ConditionParser.parse("FOO:bar", {
|
|
76
|
+
defaultOperator: "contains",
|
|
77
|
+
transform: (ctx) => ({ ...ctx, key: ctx.key.toLowerCase() })
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Handling unparsed content
|
|
81
|
+
const { parsed, unparsed } = ConditionParser.parse(
|
|
82
|
+
"category:books free text search"
|
|
83
|
+
);
|
|
84
|
+
// parsed: [{ expression: { key: "category", operator: "eq", value: "books" }, ... }]
|
|
85
|
+
// unparsed: "free text search"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
#### `__createError()`
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
static __createError(
|
|
94
|
+
input: string,
|
|
95
|
+
pos: number,
|
|
96
|
+
message: string,
|
|
97
|
+
contextRadius?: number
|
|
98
|
+
): Error
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Public helper for creating formatted error messages with position and context information.
|
|
102
|
+
|
|
103
|
+
> **Note:** Prefixed with `__` to indicate this is a special-purpose method not intended for general use. Exposed primarily for testing and advanced use cases.
|
|
104
|
+
|
|
105
|
+
**Parameters:**
|
|
106
|
+
|
|
107
|
+
| Name | Type | Default | Description |
|
|
108
|
+
|------|------|---------|-------------|
|
|
109
|
+
| `input` | `string` | - | The full input string being parsed |
|
|
110
|
+
| `pos` | `number` | - | The position where the error occurred |
|
|
111
|
+
| `message` | `string` | - | The error message |
|
|
112
|
+
| `contextRadius` | `number` | `20` | Number of characters to show before/after error position |
|
|
113
|
+
|
|
114
|
+
**Returns:** `Error` - Error object with formatted message including position and context
|
|
115
|
+
|
|
116
|
+
**Example:**
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
const error = ConditionParser.__createError(
|
|
120
|
+
"foo:bar and baz:bat",
|
|
121
|
+
12,
|
|
122
|
+
"Unexpected character",
|
|
123
|
+
20
|
|
124
|
+
);
|
|
125
|
+
// Error message includes:
|
|
126
|
+
// - The error message
|
|
127
|
+
// - Position: 12
|
|
128
|
+
// - Context snippet with visual marker (^)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Types
|
|
134
|
+
|
|
135
|
+
### ConditionParserOptions
|
|
136
|
+
|
|
137
|
+
Configuration options for the parser.
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
interface ConditionParserOptions {
|
|
141
|
+
defaultOperator: string;
|
|
142
|
+
debug: boolean;
|
|
143
|
+
transform: (context: ExpressionContext) => ExpressionContext;
|
|
144
|
+
preAddHook: (context: ExpressionContext) => null | undefined | ExpressionContext;
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
| Property | Type | Default | Description |
|
|
149
|
+
|----------|------|---------|-------------|
|
|
150
|
+
| `defaultOperator` | `string` | `"eq"` | Default operator when not explicitly specified |
|
|
151
|
+
| `debug` | `boolean` | `false` | Enable debug logging to console |
|
|
152
|
+
| `transform` | `function` | identity | Transform function applied to each parsed expression |
|
|
153
|
+
| `preAddHook` | `function` | - | Hook called before adding each expression; return falsy to skip |
|
|
154
|
+
|
|
155
|
+
**Transform Example:**
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
const result = ConditionParser.parse("FOO:BAR", {
|
|
159
|
+
transform: (ctx) => ({
|
|
160
|
+
...ctx,
|
|
161
|
+
key: ctx.key.toLowerCase(),
|
|
162
|
+
value: ctx.value.toUpperCase()
|
|
163
|
+
})
|
|
164
|
+
});
|
|
165
|
+
// Result: key="foo", value="BAR"
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**PreAddHook Example:**
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
const otherConditions = [];
|
|
172
|
+
|
|
173
|
+
const result = ConditionParser.parse("foo:bar baz:bat", {
|
|
174
|
+
preAddHook: (ctx) => {
|
|
175
|
+
if (ctx.key === "foo") return ctx; // include in parsed
|
|
176
|
+
otherConditions.push(ctx); // route elsewhere
|
|
177
|
+
return null; // skip in parsed
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
### ConditionParserResult
|
|
185
|
+
|
|
186
|
+
Result returned by `ConditionParser.parse()`.
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
interface ConditionParserResult {
|
|
190
|
+
parsed: ConditionDump;
|
|
191
|
+
unparsed: string;
|
|
192
|
+
meta: Meta;
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
| Property | Type | Description |
|
|
197
|
+
|----------|------|-------------|
|
|
198
|
+
| `parsed` | `ConditionDump` | Array of parsed condition expressions (compatible with `@marianmeres/condition-builder`) |
|
|
199
|
+
| `unparsed` | `string` | Any trailing text that couldn't be parsed (useful for free-text search) |
|
|
200
|
+
| `meta` | [`Meta`](#meta) | Metadata about the parsed expressions |
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
### Meta
|
|
205
|
+
|
|
206
|
+
Metadata about the parsed expressions.
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
interface Meta {
|
|
210
|
+
keys: string[];
|
|
211
|
+
operators: string[];
|
|
212
|
+
values: any[];
|
|
213
|
+
expressions: ExpressionData[];
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
| Property | Type | Description |
|
|
218
|
+
|----------|------|-------------|
|
|
219
|
+
| `keys` | `string[]` | Array of unique keys found in parsed expressions |
|
|
220
|
+
| `operators` | `string[]` | Array of unique operators found in parsed expressions |
|
|
221
|
+
| `values` | `any[]` | Array of unique values found in parsed expressions |
|
|
222
|
+
| `expressions` | [`ExpressionData[]`](#expressiondata) | Array of unique expressions as objects |
|
|
223
|
+
|
|
224
|
+
**Example:**
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
const { meta } = ConditionParser.parse("a:eq:1 or b:gt:2 a:eq:1");
|
|
228
|
+
// meta = {
|
|
229
|
+
// keys: ["a", "b"],
|
|
230
|
+
// operators: ["eq", "gt"],
|
|
231
|
+
// values: ["1", "2"],
|
|
232
|
+
// expressions: [
|
|
233
|
+
// { key: "a", operator: "eq", value: "1" },
|
|
234
|
+
// { key: "b", operator: "gt", value: "2" }
|
|
235
|
+
// ]
|
|
236
|
+
// }
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
### ExpressionData
|
|
242
|
+
|
|
243
|
+
Represents a parsed expression with key, operator, and value.
|
|
244
|
+
|
|
245
|
+
```ts
|
|
246
|
+
interface ExpressionData {
|
|
247
|
+
key: string;
|
|
248
|
+
operator: string;
|
|
249
|
+
value: string;
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
| Property | Type | Description |
|
|
254
|
+
|----------|------|-------------|
|
|
255
|
+
| `key` | `string` | The key/field name of the expression |
|
|
256
|
+
| `operator` | `string` | The operator (e.g., `"eq"`, `"gt"`, `"contains"`) |
|
|
257
|
+
| `value` | `string` | The value to compare against |
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Expression Syntax
|
|
262
|
+
|
|
263
|
+
### Basic Expressions
|
|
264
|
+
|
|
265
|
+
```
|
|
266
|
+
key:value -> { key: "key", operator: "eq", value: "value" }
|
|
267
|
+
key:operator:value -> { key: "key", operator: "operator", value: "value" }
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Quoted Identifiers
|
|
271
|
+
|
|
272
|
+
Use single or double quotes for identifiers containing spaces or special characters:
|
|
273
|
+
|
|
274
|
+
```
|
|
275
|
+
"my key":"my value"
|
|
276
|
+
'key with spaces':'value with spaces'
|
|
277
|
+
"key":"operator":"value"
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Escaped Characters
|
|
281
|
+
|
|
282
|
+
Escape special characters with backslash:
|
|
283
|
+
|
|
284
|
+
```
|
|
285
|
+
"value with \" quote" -> value with " quote
|
|
286
|
+
'value with \' quote' -> value with ' quote
|
|
287
|
+
key\:colon:value -> key: "key:colon"
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Parenthesized Values
|
|
291
|
+
|
|
292
|
+
Wrap values in parentheses (useful for complex values):
|
|
293
|
+
|
|
294
|
+
```
|
|
295
|
+
key:(value with spaces)
|
|
296
|
+
key:eq:(complex value)
|
|
297
|
+
key:eq:(value with \) paren)
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Logical Operators
|
|
301
|
+
|
|
302
|
+
```
|
|
303
|
+
a:b and c:d -> AND join
|
|
304
|
+
a:b or c:d -> OR join
|
|
305
|
+
a:b and not c:d -> AND NOT join
|
|
306
|
+
a:b or not c:d -> OR NOT join
|
|
307
|
+
a:b c:d -> implicit AND (same as "a:b and c:d")
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Grouping
|
|
311
|
+
|
|
312
|
+
Use parentheses for logical grouping:
|
|
313
|
+
|
|
314
|
+
```
|
|
315
|
+
(a:b or c:d) and e:f
|
|
316
|
+
a:b and (c:d or (e:f and g:h))
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Free Text
|
|
320
|
+
|
|
321
|
+
Any trailing unparsable content is preserved:
|
|
322
|
+
|
|
323
|
+
```
|
|
324
|
+
category:books the search query
|
|
325
|
+
// parsed: category:books
|
|
326
|
+
// unparsed: "the search query"
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## Integration with condition-builder
|
|
332
|
+
|
|
333
|
+
The parser output is designed to work seamlessly with `@marianmeres/condition-builder`:
|
|
334
|
+
|
|
335
|
+
```ts
|
|
336
|
+
import { ConditionParser } from "@marianmeres/condition-parser";
|
|
337
|
+
import { Condition } from "@marianmeres/condition-builder";
|
|
338
|
+
|
|
339
|
+
const userSearchInput = '(folder:"my projects" or folder:inbox) foo bar';
|
|
340
|
+
|
|
341
|
+
const options = {
|
|
342
|
+
renderKey: (ctx) => `"${ctx.key.replaceAll('"', '""')}"`,
|
|
343
|
+
renderValue: (ctx) => `'${ctx.value.toString().replaceAll("'", "''")}'`,
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
const { parsed, unparsed } = ConditionParser.parse(userSearchInput);
|
|
347
|
+
|
|
348
|
+
const c = new Condition(options);
|
|
349
|
+
c.and("user_id", "eq", 123).and(
|
|
350
|
+
Condition.restore(parsed, options).and("text", "match", unparsed)
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
console.log(c.toString());
|
|
354
|
+
// "user_id"='123' and (("folder"='my projects' or "folder"='inbox') and "text"~*'foo bar')
|
|
355
|
+
```
|
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# @marianmeres/condition-parser
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@marianmeres/condition-parser)
|
|
4
|
+
[](https://jsr.io/@marianmeres/condition-parser)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
3
7
|
Human friendly search conditions notation parser. Somewhat similar to Gmail "Search email" input.
|
|
4
8
|
|
|
5
9
|
The parsed output is designed to match [condition-builder](https://github.com/marianmeres/condition-builder)
|
|
@@ -87,21 +91,15 @@ const result = ConditionParser.parse(
|
|
|
87
91
|
unparsed: "this is free text"
|
|
88
92
|
}
|
|
89
93
|
|
|
90
|
-
//
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
debug: boolean;
|
|
96
|
-
/** If provided, will use the output of this fn as a final parsed expression output. */
|
|
97
|
-
transform: (context: Context) => Context;
|
|
98
|
-
/** Applied as the last step before adding the currently parsed expression.
|
|
99
|
-
* If returns falsey, will skip adding the currently parsed expression. */
|
|
100
|
-
preAddHook: (context: Context) => null | undefined | Context;
|
|
101
|
-
}
|
|
102
|
-
|
|
94
|
+
// ConditionParser.parse options (all optional):
|
|
95
|
+
// - defaultOperator: string (default "eq") - operator when not specified
|
|
96
|
+
// - debug: boolean (default false) - enable debug logging
|
|
97
|
+
// - transform: (ctx) => ctx - transform each parsed expression
|
|
98
|
+
// - preAddHook: (ctx) => ctx|null - filter/route expressions before adding
|
|
103
99
|
```
|
|
104
100
|
|
|
101
|
+
See [API.md](./API.md) for complete API documentation.
|
|
102
|
+
|
|
105
103
|
## In friends harmony with condition-builder
|
|
106
104
|
|
|
107
105
|
See [condition-builder](https://github.com/marianmeres/condition-builder) for more.
|
|
@@ -133,4 +131,11 @@ assertEquals(
|
|
|
133
131
|
|
|
134
132
|
## Related
|
|
135
133
|
|
|
136
|
-
[@marianmeres/condition-builder](https://github.com/marianmeres/condition-builder)
|
|
134
|
+
[@marianmeres/condition-builder](https://github.com/marianmeres/condition-builder)
|
|
135
|
+
|
|
136
|
+
## Package Identity
|
|
137
|
+
|
|
138
|
+
- **Name:** @marianmeres/condition-parser
|
|
139
|
+
- **Author:** Marian Meres
|
|
140
|
+
- **Repository:** https://github.com/marianmeres/condition-parser
|
|
141
|
+
- **License:** MIT
|
package/dist/mod.d.ts
CHANGED
|
@@ -1 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
*
|
|
4
|
+
* Human-friendly search conditions notation parser.
|
|
5
|
+
*
|
|
6
|
+
* Parses expressions like `"key:value"` or `"key:operator:value"` and supports
|
|
7
|
+
* logical operators (`and`, `or`, `and not`, `or not`), parenthesized grouping,
|
|
8
|
+
* quoted strings with escaping, and graceful handling of unparsable content.
|
|
9
|
+
*
|
|
10
|
+
* Designed to work seamlessly with
|
|
11
|
+
* {@link https://github.com/marianmeres/condition-builder | @marianmeres/condition-builder}.
|
|
12
|
+
*
|
|
13
|
+
* @example Basic usage
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { ConditionParser } from "@marianmeres/condition-parser";
|
|
16
|
+
*
|
|
17
|
+
* const result = ConditionParser.parse("foo:bar and baz:bat");
|
|
18
|
+
* // result.parsed contains the parsed conditions
|
|
19
|
+
* // result.unparsed contains any trailing unparsable text
|
|
20
|
+
* // result.meta contains metadata about parsed expressions
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @example With free text
|
|
24
|
+
* ```ts
|
|
25
|
+
* const { parsed, unparsed } = ConditionParser.parse(
|
|
26
|
+
* "category:books free text search"
|
|
27
|
+
* );
|
|
28
|
+
* // parsed: structured conditions
|
|
29
|
+
* // unparsed: "free text search"
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
1
32
|
export * from "./parser.js";
|
package/dist/mod.js
CHANGED
|
@@ -1 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
*
|
|
4
|
+
* Human-friendly search conditions notation parser.
|
|
5
|
+
*
|
|
6
|
+
* Parses expressions like `"key:value"` or `"key:operator:value"` and supports
|
|
7
|
+
* logical operators (`and`, `or`, `and not`, `or not`), parenthesized grouping,
|
|
8
|
+
* quoted strings with escaping, and graceful handling of unparsable content.
|
|
9
|
+
*
|
|
10
|
+
* Designed to work seamlessly with
|
|
11
|
+
* {@link https://github.com/marianmeres/condition-builder | @marianmeres/condition-builder}.
|
|
12
|
+
*
|
|
13
|
+
* @example Basic usage
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { ConditionParser } from "@marianmeres/condition-parser";
|
|
16
|
+
*
|
|
17
|
+
* const result = ConditionParser.parse("foo:bar and baz:bat");
|
|
18
|
+
* // result.parsed contains the parsed conditions
|
|
19
|
+
* // result.unparsed contains any trailing unparsable text
|
|
20
|
+
* // result.meta contains metadata about parsed expressions
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @example With free text
|
|
24
|
+
* ```ts
|
|
25
|
+
* const { parsed, unparsed } = ConditionParser.parse(
|
|
26
|
+
* "category:books free text search"
|
|
27
|
+
* );
|
|
28
|
+
* // parsed: structured conditions
|
|
29
|
+
* // unparsed: "free text search"
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
1
32
|
export * from "./parser.js";
|
package/dist/parser.d.ts
CHANGED
|
@@ -1,15 +1,49 @@
|
|
|
1
1
|
import type { ConditionDump, ExpressionContext } from "@marianmeres/condition-builder";
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Represents a parsed expression with key, operator, and value.
|
|
4
|
+
* This is used in the metadata returned by the parser.
|
|
5
|
+
*/
|
|
6
|
+
export interface ExpressionData {
|
|
7
|
+
/** The key/field name of the expression */
|
|
3
8
|
key: string;
|
|
9
|
+
/** The operator (e.g., "eq", "gt", "contains") */
|
|
4
10
|
operator: string;
|
|
11
|
+
/** The value to compare against */
|
|
5
12
|
value: string;
|
|
6
13
|
}
|
|
7
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Metadata about the parsed expressions.
|
|
16
|
+
* Contains arrays of unique keys, operators, values, and full expressions.
|
|
17
|
+
*/
|
|
18
|
+
export interface Meta {
|
|
19
|
+
/** Array of unique keys found in the parsed expressions */
|
|
8
20
|
keys: string[];
|
|
21
|
+
/** Array of unique operators found in the parsed expressions */
|
|
9
22
|
operators: string[];
|
|
23
|
+
/** Array of unique values found in the parsed expressions */
|
|
10
24
|
values: any[];
|
|
25
|
+
/** Array of unique expressions as {key, operator, value} objects */
|
|
11
26
|
expressions: ExpressionData[];
|
|
12
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Result returned by {@link ConditionParser.parse}.
|
|
30
|
+
*/
|
|
31
|
+
export interface ConditionParserResult {
|
|
32
|
+
/**
|
|
33
|
+
* Array of parsed condition expressions in ConditionDump format.
|
|
34
|
+
* Compatible with {@link https://github.com/marianmeres/condition-builder | @marianmeres/condition-builder}.
|
|
35
|
+
*/
|
|
36
|
+
parsed: ConditionDump;
|
|
37
|
+
/**
|
|
38
|
+
* Any trailing text that couldn't be parsed.
|
|
39
|
+
* Useful for free-text search terms.
|
|
40
|
+
*/
|
|
41
|
+
unparsed: string;
|
|
42
|
+
/**
|
|
43
|
+
* Metadata about the parsed expressions (unique keys, operators, values).
|
|
44
|
+
*/
|
|
45
|
+
meta: Meta;
|
|
46
|
+
}
|
|
13
47
|
/**
|
|
14
48
|
* Configuration options for the ConditionParser.
|
|
15
49
|
*/
|
|
@@ -164,10 +198,5 @@ export declare class ConditionParser {
|
|
|
164
198
|
* // unparsed: "free text search"
|
|
165
199
|
* ```
|
|
166
200
|
*/
|
|
167
|
-
static parse(input: string, options?: Partial<ConditionParserOptions>):
|
|
168
|
-
parsed: ConditionDump;
|
|
169
|
-
unparsed: string;
|
|
170
|
-
meta: Meta;
|
|
171
|
-
};
|
|
201
|
+
static parse(input: string, options?: Partial<ConditionParserOptions>): ConditionParserResult;
|
|
172
202
|
}
|
|
173
|
-
export {};
|
package/package.json
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marianmeres/condition-parser",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/mod.js",
|
|
6
6
|
"types": "dist/mod.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/mod.d.ts",
|
|
10
|
+
"import": "./dist/mod.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
7
13
|
"author": "Marian Meres",
|
|
8
14
|
"license": "MIT",
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@marianmeres/condition-builder": "^1.9.3"
|
|
17
|
+
},
|
|
9
18
|
"repository": {
|
|
10
19
|
"type": "git",
|
|
11
20
|
"url": "git+https://github.com/marianmeres/condition-parser.git"
|
|
12
21
|
},
|
|
13
22
|
"bugs": {
|
|
14
23
|
"url": "https://github.com/marianmeres/condition-parser/issues"
|
|
15
|
-
},
|
|
16
|
-
"dependencies": {
|
|
17
|
-
"@marianmeres/condition-builder": "^1.9.0"
|
|
18
24
|
}
|
|
19
25
|
}
|