@tellaro/tql 1.5.0 → 2.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.
@@ -11,6 +11,28 @@ export declare class SemanticPreview {
11
11
  private extractFromBinary;
12
12
  private extractFromUnary;
13
13
  private extractFromGroup;
14
+ /**
15
+ * `field [not] ANY value` -- the collection operator IS the operator this preview reports.
16
+ *
17
+ * Recursing straight into `expr.condition` (what this did) hands the wrapped comparison to
18
+ * `extractFromComparison`, which reports the INNER operator -- `eq` by default, because
19
+ * both grammars default the optional inner operator to `eq`. So `f any true`, `f not any
20
+ * true` and `f none true` all reported `eq`: the collection operator vanished and, on the
21
+ * negated spellings, the preview stated the logical opposite of the query.
22
+ */
23
+ private extractFromCollection;
24
+ /**
25
+ * The operator string a collection expression renders as.
26
+ *
27
+ * The shape is the one `not contains` and `not in` already use: negation lives in the
28
+ * operator NAME (`not_contains`, `not_in`), not in a separate flag the formatter has to
29
+ * remember to read. `collectionOperatorSpelling` derives `any` / `not_any` from the
30
+ * parser's own spelling table, so this cannot drift from what the parser accepts.
31
+ *
32
+ * An inner operator other than the implicit `eq` is appended (`any gt`), because
33
+ * `f any gt 5` and `f any 5` are different queries.
34
+ */
35
+ private collectionOperatorLabel;
14
36
  private extractFromComparison;
15
37
  /**
16
38
  * Get field name and mutators from field reference or mutator expression
@@ -31,6 +53,7 @@ export declare class SemanticPreview {
31
53
  private buildBinaryNode;
32
54
  private buildUnaryNode;
33
55
  private buildComparisonNode;
56
+ private buildCollectionNode;
34
57
  private buildGroupNode;
35
58
  }
36
59
  /**
@@ -1,11 +1,11 @@
1
- import { parseTql as f } from "./parser.js";
1
+ import { parseTql as f, collectionOperatorSpelling as g } from "./parser.js";
2
2
  import { analyzeComplexity as m } from "./complexityAnalyzer.js";
3
- class u {
3
+ class o {
4
4
  /**
5
5
  * Generate a complete preview for a TQL query
6
6
  */
7
7
  generatePreview(t) {
8
- const r = f(t), i = m(t), n = [], s = {};
8
+ const r = f(t), i = m(t), n = [], c = {};
9
9
  if (!r.ast)
10
10
  return {
11
11
  fields: [],
@@ -15,15 +15,15 @@ class u {
15
15
  uniqueFields: [],
16
16
  operatorSummary: {}
17
17
  };
18
- this.extractFieldUsage(r.ast, n, s);
19
- const a = this.buildLogicTree(r.ast), c = [...new Set(n.map((o) => o.name))];
18
+ this.extractFieldUsage(r.ast, n, c);
19
+ const a = this.buildLogicTree(r.ast), s = [...new Set(n.map((u) => u.name))];
20
20
  return {
21
21
  fields: n,
22
22
  logicTree: a,
23
23
  complexity: i,
24
24
  fieldCount: n.length,
25
- uniqueFields: c,
26
- operatorSummary: s
25
+ uniqueFields: s,
26
+ operatorSummary: c
27
27
  };
28
28
  }
29
29
  /**
@@ -40,6 +40,12 @@ class u {
40
40
  case "comparison":
41
41
  this.extractFromComparison(t, r, i);
42
42
  break;
43
+ // `field ANY value` -- the field and operator this preview describes are inside the
44
+ // wrapped comparison. Without this arm the `default` below swallowed it and the
45
+ // preview reported a collection query as touching no fields at all.
46
+ case "collection":
47
+ this.extractFromCollection(t, r, i);
48
+ break;
43
49
  case "group":
44
50
  this.extractFromGroup(t, r, i);
45
51
  break;
@@ -54,16 +60,60 @@ class u {
54
60
  extractFromGroup(t, r, i) {
55
61
  this.extractFieldUsage(t.expression, r, i);
56
62
  }
63
+ /**
64
+ * `field [not] ANY value` -- the collection operator IS the operator this preview reports.
65
+ *
66
+ * Recursing straight into `expr.condition` (what this did) hands the wrapped comparison to
67
+ * `extractFromComparison`, which reports the INNER operator -- `eq` by default, because
68
+ * both grammars default the optional inner operator to `eq`. So `f any true`, `f not any
69
+ * true` and `f none true` all reported `eq`: the collection operator vanished and, on the
70
+ * negated spellings, the preview stated the logical opposite of the query.
71
+ */
72
+ extractFromCollection(t, r, i) {
73
+ if (t.condition.type !== "comparison") {
74
+ this.extractFieldUsage(t.condition, r, i);
75
+ return;
76
+ }
77
+ const n = t.condition, c = this.collectionOperatorLabel(t, n);
78
+ i[c] = (i[c] || 0) + 1;
79
+ const a = this.getFieldInfo(n.field);
80
+ r.push({
81
+ name: a.name,
82
+ operator: c,
83
+ value: this.extractValue(n.value),
84
+ // The inner operator decides HOW each element is compared; the collection operator
85
+ // decides WHICH elements have to match. `f any gt 5` is a range match over elements.
86
+ matchType: this.determineMatchType(n.operator.toLowerCase(), n.value),
87
+ mutators: a.mutators,
88
+ start: t.start,
89
+ end: t.end
90
+ });
91
+ }
92
+ /**
93
+ * The operator string a collection expression renders as.
94
+ *
95
+ * The shape is the one `not contains` and `not in` already use: negation lives in the
96
+ * operator NAME (`not_contains`, `not_in`), not in a separate flag the formatter has to
97
+ * remember to read. `collectionOperatorSpelling` derives `any` / `not_any` from the
98
+ * parser's own spelling table, so this cannot drift from what the parser accepts.
99
+ *
100
+ * An inner operator other than the implicit `eq` is appended (`any gt`), because
101
+ * `f any gt 5` and `f any 5` are different queries.
102
+ */
103
+ collectionOperatorLabel(t, r) {
104
+ const i = g(t.operator, t.negated), n = r.operator.toLowerCase();
105
+ return n === "eq" ? i : `${i} ${n}`;
106
+ }
57
107
  extractFromComparison(t, r, i) {
58
108
  const n = t.operator.toLowerCase();
59
109
  i[n] = (i[n] || 0) + 1;
60
- const s = this.getFieldInfo(t.field), a = this.determineMatchType(n, t.value), c = this.extractValue(t.value);
110
+ const c = this.getFieldInfo(t.field), a = this.determineMatchType(n, t.value), s = this.extractValue(t.value);
61
111
  r.push({
62
- name: s.name,
112
+ name: c.name,
63
113
  operator: n,
64
- value: c,
114
+ value: s,
65
115
  matchType: a,
66
- mutators: s.mutators,
116
+ mutators: c.mutators,
67
117
  start: t.start,
68
118
  end: t.end
69
119
  });
@@ -123,6 +173,12 @@ class u {
123
173
  return this.buildUnaryNode(t);
124
174
  case "comparison":
125
175
  return this.buildComparisonNode(t);
176
+ // `field ANY value` -- one leaf, carrying the COLLECTION operator. Returning
177
+ // `buildLogicTree(expr.condition)` (what this did) returned the wrapped comparison,
178
+ // whose operator is the inner `eq` -- so the preview rendered `f not any true` as
179
+ // `f eq true`, the logical opposite of the query it was describing.
180
+ case "collection":
181
+ return this.buildCollectionNode(t);
126
182
  case "group":
127
183
  return this.buildGroupNode(t);
128
184
  default:
@@ -164,6 +220,20 @@ class u {
164
220
  end: t.end
165
221
  };
166
222
  }
223
+ buildCollectionNode(t) {
224
+ if (t.condition.type !== "comparison")
225
+ return this.buildLogicTree(t.condition);
226
+ const r = t.condition, i = this.getFieldInfo(r.field);
227
+ return {
228
+ type: "comparison",
229
+ field: i.name,
230
+ operator: this.collectionOperatorLabel(t, r),
231
+ value: this.extractValue(r.value),
232
+ mutators: i.mutators,
233
+ start: t.start,
234
+ end: t.end
235
+ };
236
+ }
167
237
  buildGroupNode(t) {
168
238
  return {
169
239
  type: "group",
@@ -173,30 +243,30 @@ class u {
173
243
  };
174
244
  }
175
245
  }
246
+ function b(e) {
247
+ return new o().generatePreview(e);
248
+ }
176
249
  function T(e) {
177
- return new u().generatePreview(e);
250
+ return new o().generatePreview(e).fields;
178
251
  }
179
- function v(e) {
180
- return new u().generatePreview(e).fields;
252
+ function F(e) {
253
+ return new o().generatePreview(e).logicTree;
181
254
  }
182
255
  function $(e) {
183
- return new u().generatePreview(e).logicTree;
256
+ return new o().generatePreview(e).uniqueFields;
184
257
  }
185
- function b(e) {
186
- return new u().generatePreview(e).uniqueFields;
187
- }
188
- function F(e) {
189
- return new u().generatePreview(e).operatorSummary;
258
+ function L(e) {
259
+ return new o().generatePreview(e).operatorSummary;
190
260
  }
191
261
  function h(e) {
192
262
  if (e.type === "comparison") {
193
- const t = e.mutators?.length ? ` | ${e.mutators.join(" | ")}` : "", r = g(e.value);
263
+ const t = e.mutators?.length ? ` | ${e.mutators.join(" | ")}` : "", r = d(e.value);
194
264
  return `${e.field}${t} ${e.operator} ${r}`;
195
265
  }
196
266
  return e.type === "NOT" && e.children?.length === 1 ? `NOT (${h(e.children[0])})` : e.type === "group" && e.children?.length === 1 ? `(${h(e.children[0])})` : (e.type === "AND" || e.type === "OR") && e.children ? e.children.map(h).join(` ${e.type} `) : "";
197
267
  }
198
- function g(e) {
199
- return e === null ? "null" : e === void 0 ? "" : typeof e == "string" ? `"${e}"` : Array.isArray(e) ? `[${e.map(g).join(", ")}]` : String(e);
268
+ function d(e) {
269
+ return e === null ? "null" : e === void 0 ? "" : typeof e == "string" ? `"${e}"` : Array.isArray(e) ? `[${e.map(d).join(", ")}]` : String(e);
200
270
  }
201
271
  function N(e, t = "") {
202
272
  if (!e)
@@ -206,35 +276,35 @@ function N(e, t = "") {
206
276
  `);
207
277
  }
208
278
  function l(e, t, r, i) {
209
- const n = i ? "└── " : "├── ", s = i ? " " : "│ ";
279
+ const n = i ? "└── " : "├── ", c = i ? " " : "│ ";
210
280
  if (e.type === "comparison") {
211
- const a = e.mutators?.length ? ` | ${e.mutators.join(" | ")}` : "", c = g(e.value);
212
- t.push(`${r}${n}${e.field}${a} ${e.operator} ${c}`);
213
- } else e.type === "NOT" ? (t.push(`${r}${n}NOT`), e.children?.length && l(e.children[0], t, r + s, !0)) : e.type === "group" ? (t.push(`${r}${n}GROUP`), e.children?.length && l(e.children[0], t, r + s, !0)) : e.children && (t.push(`${r}${n}${e.type}`), e.children.forEach((a, c) => {
214
- const o = c === e.children.length - 1;
215
- l(a, t, r + s, o);
281
+ const a = e.mutators?.length ? ` | ${e.mutators.join(" | ")}` : "", s = d(e.value);
282
+ t.push(`${r}${n}${e.field}${a} ${e.operator} ${s}`);
283
+ } else e.type === "NOT" ? (t.push(`${r}${n}NOT`), e.children?.length && l(e.children[0], t, r + c, !0)) : e.type === "group" ? (t.push(`${r}${n}GROUP`), e.children?.length && l(e.children[0], t, r + c, !0)) : e.children && (t.push(`${r}${n}${e.type}`), e.children.forEach((a, s) => {
284
+ const u = s === e.children.length - 1;
285
+ l(a, t, r + c, u);
216
286
  }));
217
287
  }
218
- function d(e) {
219
- return e ? e.type === "comparison" ? [e] : e.children ? e.children.flatMap(d) : [] : [];
220
- }
221
288
  function y(e) {
289
+ return e ? e.type === "comparison" ? [e] : e.children ? e.children.flatMap(y) : [] : [];
290
+ }
291
+ function p(e) {
222
292
  if (!e)
223
293
  return 0;
224
294
  if (!e.children || e.children.length === 0)
225
295
  return 1;
226
- const t = e.children.map(y);
296
+ const t = e.children.map(p);
227
297
  return 1 + Math.max(...t);
228
298
  }
229
299
  export {
230
- u as SemanticPreview,
231
- d as flattenLogicTree,
300
+ o as SemanticPreview,
301
+ y as flattenLogicTree,
232
302
  N as formatLogicTreeAscii,
233
303
  h as formatLogicTreeNode,
234
- T as generatePreview,
235
- v as getFieldUsage,
236
- $ as getLogicTree,
237
- y as getLogicTreeDepth,
238
- F as getOperatorSummary,
239
- b as getUniqueFields
304
+ b as generatePreview,
305
+ T as getFieldUsage,
306
+ F as getLogicTree,
307
+ p as getLogicTreeDepth,
308
+ L as getOperatorSummary,
309
+ $ as getUniqueFields
240
310
  };
@@ -2,10 +2,25 @@ import { TqlAggregationFunction, TqlCollectionOperator, TqlComparisonOperator, T
2
2
  /**
3
3
  * All comparison operators supported by TQL.
4
4
  * Includes negated variants using both `not_` prefix and `!` prefix.
5
+ *
6
+ * Checked against both engines by `tests/catalogParity.spec.ts`, which derives the operator
7
+ * sets from `tql/src/parser/grammar.pest` and `src/tql/parser_components/grammar.py` at test
8
+ * time. Adding a name here that neither engine accepts fails that test.
5
9
  */
6
10
  export declare const COMPARISON_OPERATORS: TqlComparisonOperator[];
7
11
  /**
8
12
  * Symbol-based operators mapped to their word equivalents.
13
+ *
14
+ * `==` is deliberately absent. TQL's equality symbol is a SINGLE `=`
15
+ * (`eq_op = { ^"eq" | "=" }` in tql/src/parser/grammar.pest), and the Python parser
16
+ * rejects `==` outright with "Invalid operator '==' ... Use '=' for equality"
17
+ * (src/tql/parser_components/error_analyzer.py). Mapping it to `eq` here made the editor
18
+ * green-light a query that fails the moment it is run — the inverse, and worse, of a false
19
+ * red squiggle: the user gets no warning until execution.
20
+ *
21
+ * The tokenizer still lexes `==` as one operator token (see `scanSymbolOperator`) so the
22
+ * parser can name it in a single targeted error rather than splintering it into `=` + `=`
23
+ * and emitting a cascade.
9
24
  */
10
25
  export declare const SYMBOL_OPERATORS: Record<string, TqlComparisonOperator>;
11
26
  /**
@@ -13,23 +28,137 @@ export declare const SYMBOL_OPERATORS: Record<string, TqlComparisonOperator>;
13
28
  */
14
29
  export declare const NEGATION_SYMBOL_OPERATORS: Record<string, TqlComparisonOperator>;
15
30
  export declare const LOGICAL_OPERATORS: TqlLogicalOperator[];
31
+ /**
32
+ * Symbolic spellings of the three logical operators, mapped to their canonical words.
33
+ *
34
+ * All three are declared in the engine grammars alongside the words, in the SAME rule:
35
+ * `and_op = @{ ^"and" ~ word_end | "&&" }`, `or_op = @{ ^"or" ~ word_end | "||" }` and
36
+ * `not_op = @{ ^"not" ~ word_end | "!" }` (`tql/src/parser/grammar.pest`); Python's
37
+ * `_setup_operators` builds the same alternations. So `a eq 1 && b eq 2` is not a
38
+ * convenience this package invented — it is one of two spellings of one operator, and both
39
+ * engines produce an IDENTICAL AST for the two.
40
+ *
41
+ * This package accepted neither. `&&` lexed as two `unknown` tokens and `||` as two `pipe`
42
+ * tokens, so a query both engines run was reported as `Unexpected token '&'` — a false red
43
+ * on syntax the product documents.
44
+ *
45
+ * Single `&` and single `|` are deliberately NOT here. Neither engine accepts them
46
+ * (`a eq 1 & b eq 2` raises `TQLSyntaxError at position 7` in Python, measured), and `|` is
47
+ * TQL's mutator pipe, so folding it into OR would silently reinterpret `f | lowercase`.
48
+ */
49
+ export declare const SYMBOL_LOGICAL_OPERATORS: Record<string, TqlLogicalOperator>;
16
50
  export declare const COLLECTION_OPERATORS: TqlCollectionOperator[];
17
51
  /**
18
- * All mutators supported by TQL.
19
- * Synchronized with Python/Rust implementations.
52
+ * The mutator names this package offers for autocomplete.
53
+ *
54
+ * This list is NOT self-evidently in agreement with the engines, and for most of its life
55
+ * it was not: the docstring said "Synchronized with Python/Rust implementations" while it
56
+ * was synchronized with Rust only, and four names here — `geoip`, `is_loopback`,
57
+ * `is_multicast`, `is_link_local` — are rejected by the Python evaluator, which is what the
58
+ * backend query API runs.
59
+ *
60
+ * That claim is now checked rather than asserted. `tests/registryParity.spec.ts` reads the
61
+ * Rust dispatch table, Rust's `MUTATOR_NAMES`, Python's `ALLOWED_MUTATORS` and the
62
+ * `TqlMutator` union FROM SOURCE and asserts set equality across all five surfaces, with
63
+ * the four known divergences pinned by name so they fail if they change in either
64
+ * direction. Editing this list without editing the engines fails that test.
65
+ *
66
+ * Comparison operators and type hints get the same treatment in
67
+ * `tests/catalogParity.spec.ts`. Do not restore a bare "synchronized with X" comment
68
+ * anywhere here: an invariant nothing enforces reads exactly like one that is enforced.
20
69
  */
21
70
  export declare const MUTATORS: TqlMutator[];
71
+ /**
72
+ * The mutators that answer a BOOLEAN, so `field | <name>` with no operator is a FILTER.
73
+ *
74
+ * ## Why this is a named set and not four characters of `startsWith("is_")`
75
+ *
76
+ * `1c1d74d` made `ip | is_private` mean `ip | is_private eq true` in both engines. Before
77
+ * it, the bare form meant `exists` — it matched every record that HAS an `ip` field at all,
78
+ * while reading as a filter. That is the worst shape in this codebase: a clause that cannot
79
+ * fail loudly, it just quietly stops filtering and keeps returning plausible results.
80
+ * `1c1d74d` retires it as a documented breaking change.
81
+ *
82
+ * Both engines DERIVE the set rather than listing it, and both got there after being burned
83
+ * by a list:
84
+ *
85
+ * - **Rust** — `Mutator::returns_boolean` is a trait method defaulting to `false`; the
86
+ * parser asks `mutators::returns_boolean(name)` and enumerates nothing.
87
+ * - **Python** — `BOOLEAN_PREDICATE_MUTATORS` in `src/tql/mutator_classification.py`, one
88
+ * frozenset. It used to be a literal `["is_private", "is_global"]` written out in THREE
89
+ * places in `parser.py`, and when `is_loopback` / `is_multicast` / `is_link_local` were
90
+ * added they reached none of them — so two of five predicates filtered and three
91
+ * silently did not.
92
+ *
93
+ * This package cannot import either, so it holds the only hand-written copy in the product.
94
+ * That is a liability, and `tests/booleanPredicateSugar.spec.ts` is the mitigation: it reads
95
+ * Rust's `returns_boolean` overrides and Python's frozenset FROM SOURCE and asserts all
96
+ * three name the same five. Adding a predicate to an engine without adding it here fails
97
+ * that spec, and so does the reverse. Do not edit this list on its own.
98
+ *
99
+ * Note that `any` and `all` also yield booleans but are NOT here: they are collection
100
+ * operators, this package lexes them as `collectionOp`, and neither engine's predicate set
101
+ * contains them. Their output type is declared separately in `completionProvider.ts`.
102
+ */
103
+ export declare const BOOLEAN_PREDICATE_MUTATORS: TqlMutator[];
104
+ /** Membership form of {@link BOOLEAN_PREDICATE_MUTATORS}, for parser hot paths. */
105
+ export declare const BOOLEAN_PREDICATE_MUTATOR_SET: ReadonlySet<string>;
22
106
  /**
23
107
  * All aggregation functions supported by TQL.
24
- * Synchronized with Python/Rust implementations.
108
+ *
109
+ * Unlike MUTATORS, COMPARISON_OPERATORS and TYPE_HINTS, this one is NOT derived from the
110
+ * engines: neither exposes an aggregation registry to read — the surface is spread across
111
+ * `stats_evaluator` branches on both sides. `tests/catalogParity.spec.ts` asserts only that
112
+ * this list covers a transcribed core set, which is a floor and not a parity guarantee.
25
113
  */
26
114
  export declare const AGGREGATION_FUNCTIONS: TqlAggregationFunction[];
27
115
  /**
28
- * Type hints for field type specification.
116
+ * Type hints for field type specification: `field::ip eq "10.0.0.1"`.
117
+ *
118
+ * Derived-and-checked, not transcribed. `tests/catalogParity.spec.ts` reads the Rust
119
+ * `type_name` rule out of `grammar.pest` and Python's `KNOWN_TYPE_HINTS` out of
120
+ * `src/tql/evaluator_components/field_access.py` — the set Python's grammar interpolates —
121
+ * and asserts exact three-way equality minus an explicit pin list. Adding a name here that
122
+ * neither engine accepts fails that test, and so does dropping one that both accept.
123
+ *
124
+ * The two engines now accept the SAME sixteen names, so this list is that set rather than a
125
+ * union papering over a disagreement. It was a union until `a12d842`: Rust's `type_name`
126
+ * omitted `date`, `geo`, `object` and `ip`, deliberately, because Rust parsed a type hint
127
+ * and read it nowhere and adding names to a layer that consumes none turns a loud parse
128
+ * error into a silent no-op. That commit made the Rust evaluator honour hints and then
129
+ * widened the rule, and the four pins recording the gap went red and were deleted.
130
+ *
131
+ * `ip` is the one to know about: a fully-implemented Python coercion branch — format
132
+ * validation, CIDR handling for the `cidr` operator, its own error message — that no query
133
+ * could reach in either engine until `b338476`, and which this package could not classify.
29
134
  */
30
135
  export declare const TYPE_HINTS: TqlTypeHint[];
31
136
  /**
32
- * Visualization types for stats pipeline.
137
+ * Chart types accepted after `=>` in a stats pipeline: `| stats count() by host => bar`.
138
+ *
139
+ * ## What enforces this list, and what does not
140
+ *
141
+ * It is a REAL contract, and against exactly one engine. The Python grammar declares
142
+ * `self.viz_types = one_of("bar barchart horizontal_bar ...", caseless=True)`
143
+ * (`src/tql/parser_components/grammar.py`) — a CLOSED set, so the backend query API rejects
144
+ * a chart type that is not in it (`=> zzznotaviz` raises `TQLSyntaxError`, measured). A name
145
+ * added here and not there is a name the editor completes and the API refuses, which is the
146
+ * same defect `field::geo` and `not_any` were.
147
+ *
148
+ * Rust is permissive by comparison: `viz_hint = { "=>" ~ identifier ~ viz_params? }`
149
+ * (`tql/src/parser/grammar.pest`) accepts ANY identifier, so it validates nothing and cannot
150
+ * be the authority here. That asymmetry is a live Rust/Python divergence, pinned in
151
+ * `tests/catalogParity.spec.ts` rather than resolved in this package.
152
+ *
153
+ * Within `js/` the list drives two things and neither validates: `classifyIdentifier` uses it
154
+ * for the `visualization` token class (syntax highlighting), and `getVizCompletions` for
155
+ * autocomplete. `parseVizHintString` in `builder.ts` casts whatever it is given to
156
+ * `TqlVisualizationType` without checking membership — see its own note. So a wrong name is
157
+ * caught by the Python parser at execution and by nothing before it.
158
+ *
159
+ * Do not restore a bare "Visualization types for stats pipeline" comment here. A list whose
160
+ * enforcement is unstated reads exactly like a list with none, and this one has real
161
+ * enforcement in a place a reader of this file would not think to look.
33
162
  */
34
163
  export declare const VISUALIZATION_TYPES: TqlVisualizationType[];
35
164
  /**
@@ -52,7 +52,6 @@ const l = [
52
52
  "not_all",
53
53
  "not_none"
54
54
  ], h = {
55
- "==": "eq",
56
55
  "=": "eq",
57
56
  "!=": "ne",
58
57
  ">": "gt",
@@ -78,7 +77,11 @@ const l = [
78
77
  "!any": "not_any",
79
78
  "!all": "not_all",
80
79
  "!none": "not_none"
81
- }, d = ["AND", "OR", "NOT"], p = ["ANY", "ALL", "NONE"], k = [
80
+ }, p = ["AND", "OR", "NOT"], b = {
81
+ "&&": "AND",
82
+ "||": "OR",
83
+ "!": "NOT"
84
+ }, d = ["ANY", "ALL", "NONE"], k = [
82
85
  // String mutators
83
86
  "lowercase",
84
87
  "uppercase",
@@ -117,6 +120,14 @@ const l = [
117
120
  "min",
118
121
  "max"
119
122
  ], _ = [
123
+ "is_private",
124
+ "is_global",
125
+ "is_loopback",
126
+ "is_multicast",
127
+ "is_link_local"
128
+ ], A = new Set(
129
+ _
130
+ ), f = [
120
131
  // Counting
121
132
  "count",
122
133
  "unique_count",
@@ -145,7 +156,7 @@ const l = [
145
156
  "values",
146
157
  "unique",
147
158
  "distinct"
148
- ], f = [
159
+ ], m = [
149
160
  "string",
150
161
  "str",
151
162
  "number",
@@ -160,8 +171,9 @@ const l = [
160
171
  "array",
161
172
  "list",
162
173
  "object",
163
- "geo"
164
- ], m = [
174
+ "geo",
175
+ "ip"
176
+ ], T = [
165
177
  "bar",
166
178
  "barchart",
167
179
  "horizontal_bar",
@@ -184,8 +196,8 @@ const l = [
184
196
  "nested_donut",
185
197
  "chord",
186
198
  "auto"
187
- ], w = ["by", "as", "null", "true", "false", "stats", "top", "bottom"];
188
- class g {
199
+ ], g = ["by", "as", "null", "true", "false", "stats", "top", "bottom"];
200
+ class w {
189
201
  input;
190
202
  position;
191
203
  line;
@@ -211,6 +223,8 @@ class g {
211
223
  if (this.isWhitespace(this.peek()))
212
224
  return this.scanWhitespace();
213
225
  const e = this.position, n = this.line, i = this.column, t = this.advance();
226
+ if ((t === "&" || t === "|") && this.peek() === t)
227
+ return this.advance(), this.makeToken("logicalOp", t + t, e, n, i);
214
228
  switch (t) {
215
229
  case "(":
216
230
  case ")":
@@ -332,7 +346,7 @@ class g {
332
346
  this.position = s, this.column = i + 1;
333
347
  }
334
348
  }
335
- return t in h || t === "==" || t === "=" || t === "!=" || t === ">" || t === ">=" || t === "<" || t === "<=" ? this.makeToken("operator", t, e, n, i) : this.makeToken("unknown", t, e, n, i);
349
+ return t in h || t === "==" || t === "=" || t === "!=" || t === ">" || t === ">=" || t === "<" || t === "<=" ? this.makeToken("operator", t, e, n, i) : t === "!" ? this.makeToken("logicalOp", t, e, n, i) : this.makeToken("unknown", t, e, n, i);
336
350
  }
337
351
  /**
338
352
  * Scan an identifier (field name, operator keyword, etc.)
@@ -359,7 +373,7 @@ class g {
359
373
  */
360
374
  classifyIdentifier(e) {
361
375
  const n = e.toUpperCase(), i = e.toLowerCase();
362
- return d.includes(n) ? "logicalOp" : p.includes(n) ? "collectionOp" : i === "true" || i === "false" ? "boolean" : i === "null" ? "null" : i === "stats" ? "keyword" : l.includes(i) ? "operator" : _.includes(i) ? "aggregation" : k.includes(i) ? "mutator" : f.includes(i) ? "typeHint" : m.includes(i) ? "visualization" : w.includes(i) ? "keyword" : "field";
376
+ return p.includes(n) ? "logicalOp" : d.includes(n) ? "collectionOp" : i === "true" || i === "false" ? "boolean" : i === "null" ? "null" : i === "stats" ? "keyword" : l.includes(i) ? "operator" : f.includes(i) ? "aggregation" : k.includes(i) ? "mutator" : m.includes(i) ? "typeHint" : T.includes(i) ? "visualization" : g.includes(i) ? "keyword" : "field";
363
377
  }
364
378
  // ============================================================================
365
379
  // Helper Methods
@@ -409,22 +423,22 @@ class g {
409
423
  return e === "=" || e === "!" || e === ">" || e === "<";
410
424
  }
411
425
  }
412
- function T(o) {
413
- return new g(o).tokenize();
426
+ function O(o) {
427
+ return new w(o).tokenize();
414
428
  }
415
- function b(o) {
416
- return T(o).filter((e) => e.type !== "whitespace");
429
+ function v(o) {
430
+ return O(o).filter((e) => e.type !== "whitespace");
417
431
  }
418
- function v(o, e) {
432
+ function E(o, e) {
419
433
  for (const n of o)
420
434
  if (e >= n.start && e < n.end)
421
435
  return n;
422
436
  return null;
423
437
  }
424
- function O(o, e, n) {
438
+ function S(o, e, n) {
425
439
  return o.filter((i) => i.start < n && i.end > e);
426
440
  }
427
- function A(o, e) {
441
+ function I(o, e) {
428
442
  let n = null;
429
443
  for (const i of o)
430
444
  if (i.end <= e)
@@ -433,7 +447,7 @@ function A(o, e) {
433
447
  break;
434
448
  return n;
435
449
  }
436
- function E(o, e, n = !0) {
450
+ function N(o, e, n = !0) {
437
451
  const i = n ? o.filter((s) => s.type !== "whitespace") : o;
438
452
  let t = null;
439
453
  for (const s of i)
@@ -444,21 +458,24 @@ function E(o, e, n = !0) {
444
458
  return t;
445
459
  }
446
460
  export {
447
- _ as AGGREGATION_FUNCTIONS,
448
- p as COLLECTION_OPERATORS,
461
+ f as AGGREGATION_FUNCTIONS,
462
+ _ as BOOLEAN_PREDICATE_MUTATORS,
463
+ A as BOOLEAN_PREDICATE_MUTATOR_SET,
464
+ d as COLLECTION_OPERATORS,
449
465
  l as COMPARISON_OPERATORS,
450
- w as KEYWORDS,
451
- d as LOGICAL_OPERATORS,
466
+ g as KEYWORDS,
467
+ p as LOGICAL_OPERATORS,
452
468
  k as MUTATORS,
453
469
  u as NEGATION_SYMBOL_OPERATORS,
470
+ b as SYMBOL_LOGICAL_OPERATORS,
454
471
  h as SYMBOL_OPERATORS,
455
- f as TYPE_HINTS,
456
- g as TqlTokenizer,
457
- m as VISUALIZATION_TYPES,
458
- E as findPreviousToken,
459
- v as findTokenAtPosition,
460
- A as findTokenBefore,
461
- O as findTokensInRange,
462
- T as tokenizeTql,
463
- b as tokenizeTqlNoWhitespace
472
+ m as TYPE_HINTS,
473
+ w as TqlTokenizer,
474
+ T as VISUALIZATION_TYPES,
475
+ N as findPreviousToken,
476
+ E as findTokenAtPosition,
477
+ I as findTokenBefore,
478
+ S as findTokensInRange,
479
+ O as tokenizeTql,
480
+ v as tokenizeTqlNoWhitespace
464
481
  };