@tellaro/tql 1.4.3 → 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.
@@ -1,4 +1,52 @@
1
- import { TqlParseError, TqlParseWarning, TqlValidationOptions, TqlValidationResult } from './types';
1
+ import { TqlFieldType, TqlParseError, TqlParseWarning, TqlValidationOptions, TqlValidationResult } from './types';
2
+ /**
3
+ * Maximum regex pattern length TQL will compile, in characters (code points).
4
+ *
5
+ * A pattern longer than this is refused by every TQL runtime. It is flagged here
6
+ * -- in the editor, as the user types -- because the alternative is discovering
7
+ * it after the rule has been saved and deployed: before tql#185 the Python
8
+ * evaluator answered "no match" for an over-long pattern while the query parsed
9
+ * cleanly and reported health green, so a detection rule carrying one never
10
+ * fired and nothing anywhere said so.
11
+ *
12
+ * Mirrored by `MAX_REGEX_PATTERN_LENGTH` in `src/tql/regex_guard.py` and
13
+ * `tql/src/comparator.rs`. Changing it in one place only re-creates the
14
+ * divergence.
15
+ */
16
+ export declare const MAX_REGEX_PATTERN_LENGTH = 1000;
17
+ /**
18
+ * Every field type in the `TqlFieldType` union.
19
+ *
20
+ * Declared once so an operator that genuinely applies to everything says so by NAME rather
21
+ * than by a transcribed twelve-element list — and so a new field type cannot be added to the
22
+ * union while silently falling out of the operators it belongs to.
23
+ * `tests/collectionOperatorTypes.spec.ts` reads the union out of `src/types.ts` and asserts
24
+ * this array is exactly it, so a divergence is a red rather than a quiet narrowing.
25
+ */
26
+ export declare const ALL_FIELD_TYPES: TqlFieldType[];
27
+ /**
28
+ * Every numeric field type in the union — all nine widths, not the five that get typed out.
29
+ */
30
+ export declare const NUMERIC_FIELD_TYPES: TqlFieldType[];
31
+ /**
32
+ * The types an ORDERED comparison (`gt`/`gte`/`lt`/`lte`) is defined over.
33
+ *
34
+ * Numeric and date, plus `keyword`: both evaluators fall back to a lexicographic string
35
+ * comparison when neither side reads as a number, and Python's own validator lists all four
36
+ * operators in its `keyword_ops`. `text` is excluded on that same source's reasoning — a
37
+ * range over an analyzed field compares tokens, not the value the user typed.
38
+ */
39
+ export declare const ORDERED_FIELD_TYPES: TqlFieldType[];
40
+ /**
41
+ * The types `between` / `not_between` are defined over.
42
+ *
43
+ * `ORDERED_FIELD_TYPES` minus `keyword`. The four ordered operators and `between` are NOT
44
+ * the same set, and the difference is Python's, not this package's: `keyword_ops` in
45
+ * `_validate_simple_type_compatibility` lists `gt`/`gte`/`lt`/`lte` and excludes `between`.
46
+ * `tests/collectionAndNullSemantics.spec.ts` pins that exclusion against the Python source,
47
+ * so this constant self-retires if Python ever adds it.
48
+ */
49
+ export declare const BETWEEN_FIELD_TYPES: TqlFieldType[];
2
50
  export declare class TqlValidator {
3
51
  private options;
4
52
  private errors;
@@ -12,6 +60,58 @@ export declare class TqlValidator {
12
60
  * Validate an expression recursively
13
61
  */
14
62
  private validateExpression;
63
+ /**
64
+ * `f any ['a','b']` is ill-typed. Say so, and name `in`.
65
+ *
66
+ * ## The rule, as both engines implement it
67
+ *
68
+ * `any` / `none` apply a comparison to each ELEMENT of an array-valued field. The operand
69
+ * is therefore a SCALAR: `f any 'admin'` asks whether some element equals `'admin'`. A
70
+ * LIST operand asks whether some single element equals the whole two-element list, which
71
+ * no scalar element can. `in` is already the membership operator, and is what the author
72
+ * meant.
73
+ *
74
+ * Both engines now refuse it -- `ab55587` in `tql/src/opensearch/query_builder.rs`
75
+ * (`build_collection_op`) and `_refuse_list_operand` in
76
+ * `src/tql/opensearch_components/query_converter.py` -- raising a type error whose
77
+ * `valid_operators` are `in` / `not in`. Before that it emitted `{"term": {"f": [..]}}`,
78
+ * which OpenSearch answers with HTTP 400 `[term] query does not support array of values`,
79
+ * failing the WHOLE search including every unrelated clause; the intermediate fix emitted
80
+ * `match_none`, which executed cleanly and excluded nothing, silently.
81
+ *
82
+ * ## Three arities, and only the middle one is new
83
+ *
84
+ * The engines do NOT test `is_array`. They unwrap a single-element list FIRST
85
+ * (`unwrap_single` in Rust, `len(value) == 1` in Python) and refuse only what is still a
86
+ * list. Measured against the live Python translator at `ab55587`:
87
+ *
88
+ * | Query | Python translator |
89
+ * |--------------------|------------------------------------------------------|
90
+ * | `f any 'admin'` | `{"term": {"f": "admin"}}` |
91
+ * | `f any ['a']` | `{"term": {"f": "a"}}` -- unwrapped, NOT refused |
92
+ * | `f any ['a','b']` | `TQLTypeError`, valid operators `in`, `not in` |
93
+ *
94
+ * A naive `Array.isArray(value)` check would flag `f any ['a']`, which both engines
95
+ * translate perfectly well -- a red squiggle over a working query, which is the defect
96
+ * this parser keeps being fixed for. Hence `length >= 2`, and hence the two arities are
97
+ * tested separately in `tests/collectionListOperand.spec.ts`.
98
+ *
99
+ * The empty list is not handled here: `f any []` is already an `INVALID_VALUE` error from
100
+ * the parser (`tests/emptyValueList.spec.ts`), and Python does not even parse it
101
+ * (`TQLSyntaxError`). Reporting it twice would be noise.
102
+ *
103
+ * ## Why `ALL` is excluded, and why that is not an oversight
104
+ *
105
+ * `all` / `not_all` are answered by a Painless script that compares each element to
106
+ * `params.value`. A list operand there emits valid DSL that matches nothing -- which is
107
+ * what both in-memory evaluators also answer -- so both engines dispatch `all` BEFORE the
108
+ * refusal and leave it untouched. `f all ['a','b']` translating cleanly is a pinned
109
+ * negative case here, not a gap.
110
+ *
111
+ * Negation is irrelevant: in both engines the array check precedes the quantifier wrapper,
112
+ * so `not any` / `not none` refuse on the same footing as the positive spellings.
113
+ */
114
+ private validateCollectionOperand;
15
115
  /**
16
116
  * Validate a comparison expression
17
117
  */