@tellaro/tql 0.1.0 → 0.1.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.
@@ -0,0 +1,209 @@
1
+ var d = Object.defineProperty;
2
+ var p = (i, e, t) => e in i ? d(i, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : i[e] = t;
3
+ var h = (i, e, t) => p(i, typeof e != "symbol" ? e + "" : e, t);
4
+ import { parseTql as g } from "./parser.js";
5
+ const c = {
6
+ maxDepth: 5,
7
+ maxRegex: 3,
8
+ maxInListSize: 100,
9
+ maxOrChainLength: 10,
10
+ maxLeadingWildcards: 2
11
+ }, u = /* @__PURE__ */ new Set([
12
+ "geoip_lookup",
13
+ "geoip",
14
+ "geo",
15
+ "nslookup"
16
+ ]), y = /\*/g, f = /^\*/;
17
+ class x {
18
+ constructor(e) {
19
+ h(this, "thresholds");
20
+ this.thresholds = { ...c, ...e };
21
+ }
22
+ /**
23
+ * Analyze a TQL query for complexity
24
+ */
25
+ analyze(e) {
26
+ const t = g(e), a = {
27
+ depth: 0,
28
+ operatorCount: 0,
29
+ wildcardCount: 0,
30
+ leadingWildcardCount: 0,
31
+ regexCount: 0,
32
+ mutatorCount: 0,
33
+ postProcessingMutatorCount: 0,
34
+ inListMaxSize: 0,
35
+ orChainLength: 0,
36
+ andChainLength: 0,
37
+ negationDepth: 0
38
+ }, n = [], s = [];
39
+ return t.ast ? (this.analyzeExpression(t.ast, a, n, 0, 0), a.orChainLength = this.calculateOrChainLength(t.ast), a.andChainLength = this.calculateAndChainLength(t.ast), this.generateWarnings(a, n), this.generateSuggestions(n, s, a), {
40
+ score: this.calculateScore(a, n),
41
+ warnings: n,
42
+ suggestions: s,
43
+ metrics: a
44
+ }) : {
45
+ score: "low",
46
+ warnings: [],
47
+ suggestions: [],
48
+ metrics: a
49
+ };
50
+ }
51
+ /**
52
+ * Recursively analyze an expression and collect metrics
53
+ */
54
+ analyzeExpression(e, t, a, n, s) {
55
+ switch (t.depth = Math.max(t.depth, n), t.negationDepth = Math.max(t.negationDepth, s), e.type) {
56
+ case "binary":
57
+ this.analyzeBinaryExpression(e, t, a, n, s);
58
+ break;
59
+ case "unary":
60
+ this.analyzeUnaryExpression(e, t, a, n, s);
61
+ break;
62
+ case "comparison":
63
+ this.analyzeComparisonExpression(e, t, a);
64
+ break;
65
+ case "group":
66
+ this.analyzeGroupExpression(e, t, a, n, s);
67
+ break;
68
+ }
69
+ }
70
+ analyzeBinaryExpression(e, t, a, n, s) {
71
+ t.operatorCount++, this.analyzeExpression(e.left, t, a, n + 1, s), this.analyzeExpression(e.right, t, a, n + 1, s);
72
+ }
73
+ analyzeUnaryExpression(e, t, a, n, s) {
74
+ t.operatorCount++;
75
+ const o = e.operator === "NOT" ? s + 1 : s;
76
+ this.analyzeExpression(e.operand, t, a, n + 1, o);
77
+ }
78
+ analyzeGroupExpression(e, t, a, n, s) {
79
+ this.analyzeExpression(e.expression, t, a, n + 1, s);
80
+ }
81
+ analyzeComparisonExpression(e, t, a) {
82
+ t.operatorCount++;
83
+ const n = e.field;
84
+ if (n.type === "mutator")
85
+ t.mutatorCount++, u.has(n.mutator) && (t.postProcessingMutatorCount++, a.push({
86
+ type: "post_processing_mutator",
87
+ message: `Mutator '${n.mutator}' requires post-processing and may be slow`,
88
+ severity: "warning",
89
+ location: { start: n.start, end: n.end }
90
+ }));
91
+ else if (n.type === "field" && n.mutators) {
92
+ t.mutatorCount += n.mutators.length;
93
+ for (const o of n.mutators)
94
+ u.has(o) && (t.postProcessingMutatorCount++, a.push({
95
+ type: "post_processing_mutator",
96
+ message: `Mutator '${o}' requires post-processing and may be slow`,
97
+ severity: "warning",
98
+ location: { start: n.start, end: n.end }
99
+ }));
100
+ }
101
+ const s = e.operator.toLowerCase();
102
+ if ((s === "regexp" || s === "regex" || s === "matches" || s === "not_regexp" || s === "not_matches") && t.regexCount++, this.analyzeValue(e.value, t, a, e), (s === "in" || s === "not_in") && Array.isArray(e.value)) {
103
+ const o = e.value.length;
104
+ t.inListMaxSize = Math.max(t.inListMaxSize, o);
105
+ }
106
+ }
107
+ analyzeValue(e, t, a, n) {
108
+ const s = Array.isArray(e) ? e : [e];
109
+ for (const o of s)
110
+ if (o.valueType === "string" && typeof o.value == "string") {
111
+ const l = o.value.match(y);
112
+ l && (t.wildcardCount += l.length, f.test(o.value) && (t.leadingWildcardCount++, t.leadingWildcardCount <= this.thresholds.maxLeadingWildcards && a.push({
113
+ type: "leading_wildcard",
114
+ message: `Leading wildcard in '${o.value}' is expensive - consider using 'endswith' if possible`,
115
+ severity: "warning",
116
+ location: { start: n.start, end: n.end }
117
+ })));
118
+ }
119
+ }
120
+ /**
121
+ * Calculate the length of OR chains (consecutive ORs)
122
+ */
123
+ calculateOrChainLength(e) {
124
+ if (e.type !== "binary" || e.operator !== "OR")
125
+ return 0;
126
+ let t = 1, a = e;
127
+ for (; a.type === "binary" && a.operator === "OR"; )
128
+ t++, a = a.right;
129
+ const n = this.calculateOrChainLength(e.left);
130
+ return Math.max(t, n);
131
+ }
132
+ /**
133
+ * Calculate the length of AND chains (consecutive ANDs)
134
+ */
135
+ calculateAndChainLength(e) {
136
+ if (e.type !== "binary" || e.operator !== "AND")
137
+ return 0;
138
+ let t = 1, a = e;
139
+ for (; a.type === "binary" && a.operator === "AND"; )
140
+ t++, a = a.right;
141
+ const n = this.calculateAndChainLength(e.left);
142
+ return Math.max(t, n);
143
+ }
144
+ /**
145
+ * Generate warnings based on collected metrics
146
+ */
147
+ generateWarnings(e, t) {
148
+ e.depth > this.thresholds.maxDepth && t.push({
149
+ type: "deep_nesting",
150
+ message: `Query depth of ${e.depth} exceeds recommended maximum of ${this.thresholds.maxDepth}`,
151
+ severity: "warning"
152
+ }), e.regexCount > this.thresholds.maxRegex && t.push({
153
+ type: "multiple_regex",
154
+ message: `Query contains ${e.regexCount} regex operators which may impact performance`,
155
+ severity: "warning"
156
+ }), e.inListMaxSize > this.thresholds.maxInListSize && t.push({
157
+ type: "large_in_list",
158
+ message: `IN list with ${e.inListMaxSize} values exceeds recommended maximum of ${this.thresholds.maxInListSize}`,
159
+ severity: "warning"
160
+ }), e.orChainLength > this.thresholds.maxOrChainLength && t.push({
161
+ type: "complex_or_chain",
162
+ message: `OR chain with ${e.orChainLength} conditions may be slow`,
163
+ severity: "info"
164
+ }), e.leadingWildcardCount > this.thresholds.maxLeadingWildcards && t.push({
165
+ type: "multiple_wildcards",
166
+ message: `Query contains ${e.leadingWildcardCount} leading wildcards which prevent index usage`,
167
+ severity: "warning"
168
+ }), e.negationDepth > 2 && t.push({
169
+ type: "nested_negation",
170
+ message: `Deeply nested negations (depth ${e.negationDepth}) may be hard to understand and optimize`,
171
+ severity: "info"
172
+ });
173
+ }
174
+ /**
175
+ * Generate optimization suggestions based on warnings
176
+ */
177
+ generateSuggestions(e, t, a) {
178
+ const n = new Set(e.map((s) => s.type));
179
+ (n.has("leading_wildcard") || n.has("multiple_wildcards")) && t.push("Leading wildcards (`*foo`) are expensive - use `endswith` if possible"), n.has("deep_nesting") && t.push(`Query depth of ${a.depth} may impact performance - consider simplifying`), n.has("multiple_regex") && t.push("Multiple regex operators are slow - consider using `contains` or `startswith` where possible"), n.has("large_in_list") && t.push("Large IN lists can be slow - consider splitting into multiple queries or using a lookup table"), n.has("complex_or_chain") && t.push("Consider using `in` instead of multiple `OR` conditions for the same field"), n.has("post_processing_mutator") && t.push("Mutators like `geoip_lookup` and `nslookup` require post-processing and cannot be optimized by the database"), n.has("nested_negation") && t.push("Simplify nested NOT expressions for better readability and potential optimization");
180
+ }
181
+ /**
182
+ * Calculate overall complexity score based on metrics and warnings
183
+ */
184
+ calculateScore(e, t) {
185
+ let a = 0;
186
+ e.depth > this.thresholds.maxDepth ? a += 2 : e.depth > 3 && (a += 1), e.regexCount > this.thresholds.maxRegex ? a += 2 : e.regexCount > 1 && (a += 1), e.leadingWildcardCount > this.thresholds.maxLeadingWildcards ? a += 2 : e.leadingWildcardCount > 0 && (a += 1), e.inListMaxSize > this.thresholds.maxInListSize ? a += 2 : e.inListMaxSize > 50 && (a += 1), e.postProcessingMutatorCount > 0 && (a += e.postProcessingMutatorCount), e.orChainLength > this.thresholds.maxOrChainLength && (a += 1);
187
+ const n = t.filter((s) => s.severity === "error").length;
188
+ return a += n * 2, a >= 4 ? "high" : a >= 2 ? "medium" : "low";
189
+ }
190
+ }
191
+ function r(i, e) {
192
+ return new x(e).analyze(i);
193
+ }
194
+ function L(i, e) {
195
+ return r(i, e).score === "high";
196
+ }
197
+ function z(i, e) {
198
+ return r(i, e).warnings;
199
+ }
200
+ function _(i) {
201
+ return r(i).metrics;
202
+ }
203
+ export {
204
+ x as ComplexityAnalyzer,
205
+ r as analyzeComplexity,
206
+ _ as getComplexityMetrics,
207
+ z as getComplexityWarnings,
208
+ L as isHighComplexity
209
+ };
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Fuzzy Matcher for TQL Autocompletion
3
+ *
4
+ * Provides fuzzy string matching for better autocomplete suggestions.
5
+ * Supports subsequence matching and calculates match scores.
6
+ */
7
+ /**
8
+ * Match result with score and highlight positions
9
+ */
10
+ export interface FuzzyMatchResult {
11
+ /** Whether the pattern matched */
12
+ matched: boolean;
13
+ /** Match score (higher is better). 0 = no match */
14
+ score: number;
15
+ /** Indices in the target string that matched */
16
+ matchedIndices: number[];
17
+ }
18
+ /**
19
+ * Options for fuzzy matching
20
+ */
21
+ export interface FuzzyMatchOptions {
22
+ /** Bonus for consecutive character matches */
23
+ consecutiveBonus?: number;
24
+ /** Bonus for matching at word boundaries (after . or _) */
25
+ wordBoundaryBonus?: number;
26
+ /** Bonus for matching the first character */
27
+ firstCharBonus?: number;
28
+ /** Bonus for matching case exactly */
29
+ caseMatchBonus?: number;
30
+ /** Penalty for gaps between matches */
31
+ gapPenalty?: number;
32
+ /** Minimum score to consider a match */
33
+ minScore?: number;
34
+ }
35
+ /**
36
+ * Perform fuzzy matching of a pattern against a target string.
37
+ *
38
+ * Uses subsequence matching - all characters in the pattern must appear
39
+ * in the target in the same order, but not necessarily consecutively.
40
+ *
41
+ * @example
42
+ * fuzzyMatch("srcip", "source_ip") // matches: s-r-c-e_-i-p
43
+ * fuzzyMatch("hst", "hostname") // matches: h-o-s-t-name
44
+ */
45
+ export declare function fuzzyMatch(pattern: string, target: string, options?: FuzzyMatchOptions): FuzzyMatchResult;
46
+ /**
47
+ * Calculate Levenshtein (edit) distance between two strings.
48
+ * Useful for typo detection and suggestions.
49
+ */
50
+ export declare function levenshteinDistance(a: string, b: string): number;
51
+ /**
52
+ * Find the closest match for a string from a list of candidates
53
+ * using Levenshtein distance.
54
+ */
55
+ export declare function findClosestMatch(input: string, candidates: string[], maxDistance?: number): {
56
+ match: string;
57
+ distance: number;
58
+ } | null;
59
+ /**
60
+ * Filter and sort candidates by fuzzy match score
61
+ */
62
+ export declare function fuzzyFilter<T>(pattern: string, candidates: T[], getText: (item: T) => string, options?: FuzzyMatchOptions): Array<{
63
+ item: T;
64
+ score: number;
65
+ matchedIndices: number[];
66
+ }>;
67
+ /**
68
+ * Generate highlighted HTML for a fuzzy match result.
69
+ * Wraps matched characters in a span with a highlight class.
70
+ */
71
+ export declare function highlightMatch(text: string, matchedIndices: number[], highlightClass?: string): string;
@@ -0,0 +1,110 @@
1
+ const d = {
2
+ consecutiveBonus: 15,
3
+ wordBoundaryBonus: 10,
4
+ firstCharBonus: 8,
5
+ caseMatchBonus: 3,
6
+ gapPenalty: 1,
7
+ minScore: 0
8
+ };
9
+ function g(n, o, r = {}) {
10
+ const c = { ...d, ...r };
11
+ if (n.length === 0)
12
+ return { matched: !0, score: 0, matchedIndices: [] };
13
+ if (n.length > o.length)
14
+ return { matched: !1, score: 0, matchedIndices: [] };
15
+ const t = n.toLowerCase(), e = o.toLowerCase();
16
+ if (e.startsWith(t)) {
17
+ const h = Array.from({ length: n.length }, (f, u) => u);
18
+ return { matched: !0, score: m(n, o, c), matchedIndices: h };
19
+ }
20
+ const s = B(t, e, n, o, c);
21
+ return s.matchedIndices.length === n.length && s.score >= c.minScore ? { matched: !0, ...s } : { matched: !1, score: 0, matchedIndices: [] };
22
+ }
23
+ function m(n, o, r) {
24
+ let c = n.length * 10;
25
+ c += r.firstCharBonus, c += (n.length - 1) * r.consecutiveBonus;
26
+ for (let t = 0; t < n.length; t++)
27
+ n[t] === o[t] && (c += r.caseMatchBonus);
28
+ return n.toLowerCase() === o.toLowerCase() && (c += 50), c;
29
+ }
30
+ function B(n, o, r, c, t) {
31
+ const e = n.length, s = o.length, h = [];
32
+ let i = 0, f = 0, u = -1;
33
+ for (let l = 0; l < s && f < e; l++)
34
+ n[f] === o[l] && (h.push(l), i += 10, f === 0 && l === 0 && (i += t.firstCharBonus), u !== -1 && l === u + 1 ? i += t.consecutiveBonus : u !== -1 && (i -= (l - u - 1) * t.gapPenalty), (l === 0 || c[l - 1] === "." || c[l - 1] === "_") && (i += t.wordBoundaryBonus), r[f] === c[l] && (i += t.caseMatchBonus), u = l, f++);
35
+ return { score: Math.max(0, i), matchedIndices: h };
36
+ }
37
+ function a(n, o) {
38
+ const r = n.toLowerCase(), c = o.toLowerCase(), t = [];
39
+ for (let e = 0; e <= c.length; e++)
40
+ t[e] = [e];
41
+ for (let e = 0; e <= r.length; e++)
42
+ t[0][e] = e;
43
+ for (let e = 1; e <= c.length; e++)
44
+ for (let s = 1; s <= r.length; s++)
45
+ c.charAt(e - 1) === r.charAt(s - 1) ? t[e][s] = t[e - 1][s - 1] : t[e][s] = Math.min(
46
+ t[e - 1][s - 1] + 1,
47
+ // substitution
48
+ t[e][s - 1] + 1,
49
+ // insertion
50
+ t[e - 1][s] + 1
51
+ // deletion
52
+ );
53
+ return t[c.length][r.length];
54
+ }
55
+ function I(n, o, r = 3) {
56
+ if (o.length === 0)
57
+ return null;
58
+ let c = o[0], t = a(n, c);
59
+ for (let e = 1; e < o.length; e++) {
60
+ const s = a(n, o[e]);
61
+ s < t && (t = s, c = o[e]);
62
+ }
63
+ return t <= r ? { match: c, distance: t } : null;
64
+ }
65
+ function M(n, o, r, c = {}) {
66
+ const t = [];
67
+ for (const e of o) {
68
+ const s = r(e), h = g(n, s, c);
69
+ h.matched && t.push({
70
+ item: e,
71
+ score: h.score,
72
+ matchedIndices: h.matchedIndices
73
+ });
74
+ }
75
+ return t.sort((e, s) => s.score - e.score), t;
76
+ }
77
+ function C(n, o, r = "tql-fuzzy-highlight") {
78
+ if (o.length === 0)
79
+ return n;
80
+ const c = new Set(o);
81
+ let t = "", e = !1;
82
+ for (let s = 0; s < n.length; s++) {
83
+ const h = c.has(s);
84
+ h && !e ? (t += `<span class="${r}">`, e = !0) : !h && e && (t += "</span>", e = !1), t += w(n[s]);
85
+ }
86
+ return e && (t += "</span>"), t;
87
+ }
88
+ function w(n) {
89
+ switch (n) {
90
+ case "&":
91
+ return "&amp;";
92
+ case "<":
93
+ return "&lt;";
94
+ case ">":
95
+ return "&gt;";
96
+ case '"':
97
+ return "&quot;";
98
+ case "'":
99
+ return "&#39;";
100
+ default:
101
+ return n;
102
+ }
103
+ }
104
+ export {
105
+ I as findClosestMatch,
106
+ M as fuzzyFilter,
107
+ g as fuzzyMatch,
108
+ C as highlightMatch,
109
+ a as levenshteinDistance
110
+ };
@@ -11,3 +11,8 @@ export { TqlTokenizer, tokenizeTql, tokenizeTqlNoWhitespace, findTokenAtPosition
11
11
  export { TqlParser, parseTql, isValidTql, getTqlErrors, formatParseError } from './parser';
12
12
  export { TqlValidator, validateTql, isValidTqlSyntax, getTqlIssues, formatValidationResult } from './validator';
13
13
  export { getCompletions, getWordAtPosition, getCompletionTriggerCharacters, shouldTriggerCompletion } from './completionProvider';
14
+ export { fuzzyMatch, levenshteinDistance, findClosestMatch, fuzzyFilter, highlightMatch, type FuzzyMatchResult, type FuzzyMatchOptions } from './fuzzyMatcher';
15
+ export { SchemaCache, getSchemaCache, createSchemaCache, getCachedFieldNames, getCachedFieldType, getCachedField, type CachedSchema } from './schemaCache';
16
+ export { ComplexityAnalyzer, analyzeComplexity, isHighComplexity, getComplexityWarnings, getComplexityMetrics } from './complexityAnalyzer';
17
+ export { SemanticPreview, generatePreview, getFieldUsage, getLogicTree, getUniqueFields, getOperatorSummary, formatLogicTreeNode, formatLogicTreeAscii, flattenLogicTree, getLogicTreeDepth } from './semanticPreview';
18
+ export type { ComplexityScore, ComplexityWarning, ComplexityWarningType, ComplexityMetrics, ComplexityResult, ComplexityThresholds, MatchType, FieldUsage, LogicTreeNodeType, LogicTreeNode, PreviewResult, QueryHistoryEntry, UseQueryHistoryOptions } from './types';
@@ -62,6 +62,16 @@ export declare class TqlParser {
62
62
  private matchLogical;
63
63
  private addError;
64
64
  private addWarning;
65
+ /**
66
+ * Synchronize parser state after an error by advancing to a recovery point.
67
+ * Recovery points are: logical operators (AND, OR, NOT), closing parens, or EOF.
68
+ */
69
+ private synchronize;
70
+ /**
71
+ * Create a placeholder expression for error recovery.
72
+ * This allows the parser to continue and find additional errors.
73
+ */
74
+ private createPlaceholderExpression;
65
75
  }
66
76
  /**
67
77
  * Parse a TQL query string