@dxos/echo-query 0.12.0-next-73daef452f1464b62efdbca75fb0067c1066f264 → 0.14.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.
- package/dist/lib/index.mjs +234 -6
- package/dist/lib/index.mjs.map +1 -1
- package/dist/types/src/filter/enum-terms.d.ts +36 -0
- package/dist/types/src/filter/enum-terms.d.ts.map +1 -0
- package/dist/types/src/filter/enum-terms.test.d.ts +2 -0
- package/dist/types/src/filter/enum-terms.test.d.ts.map +1 -0
- package/dist/types/src/filter/index.d.ts +3 -0
- package/dist/types/src/filter/index.d.ts.map +1 -0
- package/dist/types/src/filter/match-filter.d.ts +22 -0
- package/dist/types/src/filter/match-filter.d.ts.map +1 -0
- package/dist/types/src/filter/match-filter.test.d.ts +2 -0
- package/dist/types/src/filter/match-filter.test.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +2 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/parser/gen/index.d.ts +1 -1
- package/dist/types/src/parser/gen/index.d.ts.map +1 -1
- package/dist/types/src/parser/index.d.ts +2 -2
- package/dist/types/src/parser/index.d.ts.map +1 -1
- package/dist/types/src/parser/query-builder.d.ts +10 -0
- package/dist/types/src/parser/query-builder.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -19
- package/src/filter/enum-terms.test.ts +144 -0
- package/src/filter/enum-terms.ts +181 -0
- package/src/filter/index.ts +6 -0
- package/src/filter/match-filter.test.ts +188 -0
- package/src/filter/match-filter.ts +178 -0
- package/src/index.ts +2 -5
- package/src/parser/gen/index.ts +2 -2
- package/src/parser/index.ts +2 -2
- package/src/parser/query-builder.ts +27 -8
- package/src/parser/query.test.ts +23 -2
- package/dist/lib/sandbox.mjs +0 -137
- package/dist/lib/sandbox.mjs.map +0 -1
- package/dist/query-lite/index.d.ts +0 -8
- package/dist/query-lite/index.d.ts.map +0 -1
- package/dist/query-lite/index.js +0 -728
- package/dist/query-lite/index.js.map +0 -1
- package/dist/types/src/query-lite/index.d.ts +0 -2
- package/dist/types/src/query-lite/index.d.ts.map +0 -1
- package/dist/types/src/query-lite/query-lite.d.ts +0 -8
- package/dist/types/src/query-lite/query-lite.d.ts.map +0 -1
- package/dist/types/src/sandbox/index.d.ts +0 -2
- package/dist/types/src/sandbox/index.d.ts.map +0 -1
- package/dist/types/src/sandbox/query-sandbox.d.ts +0 -21
- package/dist/types/src/sandbox/query-sandbox.d.ts.map +0 -1
- package/dist/types/src/sandbox/query-sandbox.test.d.ts +0 -2
- package/dist/types/src/sandbox/query-sandbox.test.d.ts.map +0 -1
- package/dist/types/src/sandbox/quickjs.d.ts +0 -8
- package/dist/types/src/sandbox/quickjs.d.ts.map +0 -1
- package/dist/types/src/sandbox/quickjs.test.d.ts +0 -2
- package/dist/types/src/sandbox/quickjs.test.d.ts.map +0 -1
- package/src/env.d.ts +0 -8
- package/src/query-lite/index.ts +0 -5
- package/src/query-lite/query-lite.ts +0 -1028
- package/src/sandbox/index.ts +0 -5
- package/src/sandbox/query-sandbox.test.ts +0 -54
- package/src/sandbox/query-sandbox.ts +0 -72
- package/src/sandbox/quickjs.test.ts +0 -67
- package/src/sandbox/quickjs.ts +0 -33
package/dist/lib/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { Filter, Obj } from "@dxos/echo";
|
|
1
2
|
import { LRParser } from "@lezer/lr";
|
|
2
|
-
import { Filter } from "@dxos/echo";
|
|
3
3
|
import { invariant } from "@dxos/invariant";
|
|
4
4
|
//#region \0rolldown/runtime.js
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
@@ -13,6 +13,221 @@ var __exportAll = (all, no_symbols) => {
|
|
|
13
13
|
return target;
|
|
14
14
|
};
|
|
15
15
|
//#endregion
|
|
16
|
+
//#region src/filter/enum-terms.ts
|
|
17
|
+
var NOT_KEYWORDS = /* @__PURE__ */ new Set(["NOT", "!"]);
|
|
18
|
+
/**
|
|
19
|
+
* Splits the terms on one enumerated property out of a query, so a picker for that property and the
|
|
20
|
+
* query text can be two views over one string.
|
|
21
|
+
*
|
|
22
|
+
* Only top-level terms a picker could have written are read — `prop:x`, `NOT prop:x` (or `!prop:x`)
|
|
23
|
+
* and `(prop:a OR prop:b)` — and they are intersected, as the query's implicit AND intersects them.
|
|
24
|
+
* A query with a top-level `OR` is left whole: its terms are not all required, so no value set
|
|
25
|
+
* describes it. A value outside the set (`status:sta` while it is being typed) stays in the text,
|
|
26
|
+
* where it matches as the substring it is.
|
|
27
|
+
*/
|
|
28
|
+
var parseEnumTerms = (text, { property, values }) => {
|
|
29
|
+
const tokens = tokenize(text);
|
|
30
|
+
if (hasTopLevelOr(tokens)) return { rest: text.trim() };
|
|
31
|
+
const valueOf = (token) => {
|
|
32
|
+
const value = termPattern(property).exec(token)?.[2].toLowerCase();
|
|
33
|
+
return value === void 0 ? void 0 : values.find((candidate) => candidate.toLowerCase() === value);
|
|
34
|
+
};
|
|
35
|
+
const groupOf = (token) => {
|
|
36
|
+
if (!token.startsWith("(") || !token.endsWith(")")) return;
|
|
37
|
+
const parts = token.slice(1, -1).trim().split(/\s+/);
|
|
38
|
+
const group = /* @__PURE__ */ new Set();
|
|
39
|
+
for (let index = 0; index < parts.length; index++) {
|
|
40
|
+
if (index % 2 === 1) {
|
|
41
|
+
if (parts[index].toUpperCase() !== "OR") return;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
const value = valueOf(parts[index]);
|
|
45
|
+
if (value === void 0) return;
|
|
46
|
+
group.add(value);
|
|
47
|
+
}
|
|
48
|
+
return parts.length % 2 === 1 ? group : void 0;
|
|
49
|
+
};
|
|
50
|
+
let kept;
|
|
51
|
+
const narrow = (keep) => {
|
|
52
|
+
kept = new Set((kept ? [...kept] : values).filter(keep));
|
|
53
|
+
};
|
|
54
|
+
const rest = [];
|
|
55
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
56
|
+
const token = tokens[index];
|
|
57
|
+
const next = tokens[index + 1];
|
|
58
|
+
const negated = NOT_KEYWORDS.has(token.toUpperCase()) && next !== void 0 ? valueOf(next) : void 0;
|
|
59
|
+
if (negated !== void 0) {
|
|
60
|
+
narrow((value) => value !== negated);
|
|
61
|
+
index++;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const inline = token.startsWith("!") ? valueOf(token.slice(1)) : void 0;
|
|
65
|
+
if (inline !== void 0) {
|
|
66
|
+
narrow((value) => value !== inline);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const single = valueOf(token);
|
|
70
|
+
if (single !== void 0) {
|
|
71
|
+
narrow((value) => value === single);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const group = groupOf(token);
|
|
75
|
+
if (group) {
|
|
76
|
+
narrow((value) => group.has(value));
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
rest.push(token);
|
|
80
|
+
}
|
|
81
|
+
const result = kept;
|
|
82
|
+
return result ? {
|
|
83
|
+
values: values.filter((value) => result.has(value)),
|
|
84
|
+
rest: rest.join(" ")
|
|
85
|
+
} : { rest: rest.join(" ") };
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* The query with its terms on the property replaced by the one term that says `selected`, placed
|
|
89
|
+
* first.
|
|
90
|
+
*
|
|
91
|
+
* Written in whichever of the two forms is shorter — the values kept (`prop:a`, `(prop:a OR prop:b)`)
|
|
92
|
+
* or the values hidden (`NOT prop:x NOT prop:y`) — so hiding two of nine reads as that, not as a list
|
|
93
|
+
* of the seven left. Every value selected writes no term. A query with a top-level `OR` comes back
|
|
94
|
+
* unchanged: no term on the property describes it, so a written term could not be read back, and
|
|
95
|
+
* each write would stack another term in front of the last.
|
|
96
|
+
*/
|
|
97
|
+
var writeEnumTerms = (text, spec, selected) => {
|
|
98
|
+
const { property, values } = spec;
|
|
99
|
+
if (hasTopLevelOr(tokenize(text))) return text;
|
|
100
|
+
const { rest } = parseEnumTerms(text, spec);
|
|
101
|
+
const kept = values.filter((value) => selected.includes(value));
|
|
102
|
+
const hidden = values.filter((value) => !selected.includes(value));
|
|
103
|
+
return [hidden.length === 0 ? "" : kept.length > 0 && kept.length <= hidden.length ? kept.length === 1 ? `${property}:${kept[0]}` : `(${kept.map((value) => `${property}:${value}`).join(" OR ")})` : hidden.map((value) => `NOT ${property}:${value}`).join(" "), rest].filter((part) => part.length > 0).join(" ");
|
|
104
|
+
};
|
|
105
|
+
var hasTopLevelOr = (tokens) => tokens.some((token) => token.toUpperCase() === "OR");
|
|
106
|
+
/** `prop:value`, the value optionally quoted; the property name matched literally, in any case. */
|
|
107
|
+
var termPattern = (property) => new RegExp(`^${property.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}:(["']?)([\\w-]+)\\1$`, "i");
|
|
108
|
+
/** Top-level terms: whitespace-separated, with parenthesized groups and quoted strings kept whole. */
|
|
109
|
+
var tokenize = (text) => {
|
|
110
|
+
const tokens = [];
|
|
111
|
+
let current = "";
|
|
112
|
+
let depth = 0;
|
|
113
|
+
let quote;
|
|
114
|
+
let previous = "";
|
|
115
|
+
for (const char of text) {
|
|
116
|
+
const prior = previous;
|
|
117
|
+
previous = char;
|
|
118
|
+
if (quote) {
|
|
119
|
+
current += char;
|
|
120
|
+
if (char === quote) quote = void 0;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (char === "\"" || char === "'" && !/[a-zA-Z0-9_]/.test(prior)) quote = char;
|
|
124
|
+
else if (char === "(") depth++;
|
|
125
|
+
else if (char === ")") depth = Math.max(0, depth - 1);
|
|
126
|
+
else if (/\s/.test(char) && depth === 0) {
|
|
127
|
+
if (current.length > 0) tokens.push(current);
|
|
128
|
+
current = "";
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
current += char;
|
|
132
|
+
}
|
|
133
|
+
if (current.length > 0) tokens.push(current);
|
|
134
|
+
return tokens;
|
|
135
|
+
};
|
|
136
|
+
//#endregion
|
|
137
|
+
//#region src/filter/match-filter.ts
|
|
138
|
+
/**
|
|
139
|
+
* Evaluates a filter the {@link QueryBuilder} built against one object in memory.
|
|
140
|
+
*
|
|
141
|
+
* For a host that cannot hand the filter to a query: one that renders a tree and must keep the
|
|
142
|
+
* ancestors of a match, or reads a feed the query engine does not index. Semantics follow what a
|
|
143
|
+
* reader typed rather than the query engine's equality: a string property matches as a
|
|
144
|
+
* case-insensitive substring (`status:start` finds `started`), an array on any element, and an
|
|
145
|
+
* object on any field (`assignee:rich` finds an actor's name or email). An unknown node fails
|
|
146
|
+
* closed, so a filter the evaluator does not understand never broadens the result.
|
|
147
|
+
*/
|
|
148
|
+
var matchesFilter = (filter, object, options = {}) => matchesAst(filter.ast, object, options);
|
|
149
|
+
var matchesAst = (ast, object, options) => {
|
|
150
|
+
switch (ast.type) {
|
|
151
|
+
case "and": return ast.filters.every((sub) => matchesAst(sub, object, options));
|
|
152
|
+
case "or": return ast.filters.some((sub) => matchesAst(sub, object, options));
|
|
153
|
+
case "not": return !matchesAst(ast.filter, object, options);
|
|
154
|
+
case "tag": return (options.tags ?? metaTags)(object).includes(ast.tag);
|
|
155
|
+
case "text-search": return matchesText(ast.text, (options.text ?? stringProperties)(object));
|
|
156
|
+
case "object":
|
|
157
|
+
if (ast.typename && !typeMatches(ast.typename, typeUri(object))) return false;
|
|
158
|
+
if (ast.id && !ast.id.includes(object.id)) return false;
|
|
159
|
+
return Object.entries(ast.props).every(([key, predicate]) => matchesPredicate(predicate, getPath(object, key)));
|
|
160
|
+
default: return false;
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
/** A property's value against the predicate a `key:value` term or an object literal built. */
|
|
164
|
+
var matchesPredicate = (predicate, value) => {
|
|
165
|
+
switch (predicate.type) {
|
|
166
|
+
case "compare": switch (predicate.operator) {
|
|
167
|
+
case "eq": return matchesValue(value, predicate.value);
|
|
168
|
+
case "neq": return !matchesValue(value, predicate.value);
|
|
169
|
+
default: return compareOrdered(value, predicate.value, predicate.operator);
|
|
170
|
+
}
|
|
171
|
+
case "object": return isRecord(value) && Object.entries(predicate.props).every(([key, sub]) => matchesPredicate(sub, getPath(value, key)));
|
|
172
|
+
case "and": return predicate.filters.every((sub) => matchesPredicate(sub, value));
|
|
173
|
+
case "or": return predicate.filters.some((sub) => matchesPredicate(sub, value));
|
|
174
|
+
case "not": return !matchesPredicate(predicate.filter, value);
|
|
175
|
+
default: return false;
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* Strings match as case-insensitive substrings, arrays on any element, objects on any field; any
|
|
180
|
+
* other value only by identity.
|
|
181
|
+
*/
|
|
182
|
+
var matchesValue = (value, query) => {
|
|
183
|
+
if (value === query) return true;
|
|
184
|
+
if (typeof query === "string" && typeof value === "string") return value.toLowerCase().includes(query.toLowerCase());
|
|
185
|
+
if (Array.isArray(value)) return value.some((entry) => matchesValue(entry, query));
|
|
186
|
+
if (typeof query === "string" && isRecord(value)) return Object.values(value).some((entry) => matchesValue(entry, query));
|
|
187
|
+
return false;
|
|
188
|
+
};
|
|
189
|
+
/** Ordering holds only between two numbers or two strings; anything else matches nothing. */
|
|
190
|
+
var compareOrdered = (value, query, operator) => {
|
|
191
|
+
if (typeof value === "number" && typeof query === "number") return compare(value, query, operator);
|
|
192
|
+
if (typeof value === "string" && typeof query === "string") return compare(value, query, operator);
|
|
193
|
+
return false;
|
|
194
|
+
};
|
|
195
|
+
var compare = (value, query, operator) => {
|
|
196
|
+
switch (operator) {
|
|
197
|
+
case "gt": return value > query;
|
|
198
|
+
case "gte": return value >= query;
|
|
199
|
+
case "lt": return value < query;
|
|
200
|
+
case "lte": return value <= query;
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
var matchesText = (needle, haystacks) => {
|
|
204
|
+
if (!needle) return true;
|
|
205
|
+
const lower = needle.toLowerCase();
|
|
206
|
+
return haystacks.some((haystack) => haystack?.toLowerCase().includes(lower));
|
|
207
|
+
};
|
|
208
|
+
var isRecord = (value) => typeof value === "object" && value !== null;
|
|
209
|
+
var getPath = (object, path) => path.split(".").reduce((current, key) => isRecord(current) ? current[key] : void 0, object);
|
|
210
|
+
var stringProperties = (object) => Object.entries(object).flatMap(([key, value]) => key !== "id" && typeof value === "string" ? [value] : []);
|
|
211
|
+
var metaTags = (object) => {
|
|
212
|
+
try {
|
|
213
|
+
return Obj.getMeta(object).tags.map((tag) => tag.uri);
|
|
214
|
+
} catch {
|
|
215
|
+
return [];
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
/**
|
|
219
|
+
* A `type:` term names a type without a version (`dxn:org.example.person`), while an object's type
|
|
220
|
+
* carries one (`dxn:org.example.person:0.1.0`), so an unversioned name matches every version.
|
|
221
|
+
*/
|
|
222
|
+
var typeMatches = (typename, uri) => uri !== void 0 && (uri === typename || uri.startsWith(`${typename}:`));
|
|
223
|
+
var typeUri = (object) => {
|
|
224
|
+
try {
|
|
225
|
+
return Obj.getTypeURI(object)?.toString();
|
|
226
|
+
} catch {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
//#endregion
|
|
16
231
|
//#region src/parser/gen/query.ts
|
|
17
232
|
var spec_Identifier = {
|
|
18
233
|
__proto__: null,
|
|
@@ -89,6 +304,20 @@ var QueryDSL;
|
|
|
89
304
|
//#region src/parser/query-builder.ts
|
|
90
305
|
var __dxlog_file = "/__w/dxos/dxos/packages/core/echo/echo-query/src/parser/query-builder.ts";
|
|
91
306
|
/**
|
|
307
|
+
* The `#tag` token for a tag label. The grammar's `Tag` token admits only `[a-zA-Z0-9_-]`, so every
|
|
308
|
+
* other run of characters becomes one `-` — otherwise `#Needs reply` parses as tag `needs` plus text.
|
|
309
|
+
*/
|
|
310
|
+
var formatTag = (label) => "#" + label.trim().replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
311
|
+
/** The key and tag a `#tag` token names, matched against each label's {@link formatTag} form. */
|
|
312
|
+
var findTagByToken = (tags, token) => {
|
|
313
|
+
const normalized = token.toLowerCase();
|
|
314
|
+
const entry = Object.entries(tags ?? {}).find(([, tag]) => formatTag(tag.label).toLowerCase() === normalized);
|
|
315
|
+
return entry && {
|
|
316
|
+
key: entry[0],
|
|
317
|
+
tag: entry[1]
|
|
318
|
+
};
|
|
319
|
+
};
|
|
320
|
+
/**
|
|
92
321
|
* Stateless query builder that parses DSL trees into filters.
|
|
93
322
|
*
|
|
94
323
|
* NOTE: QueryBuilder was largely developed using Claude Sonnet 4.5 (in Windsurf)..
|
|
@@ -403,13 +632,12 @@ var QueryBuilder = class {
|
|
|
403
632
|
invariant(this._tags, void 0, {
|
|
404
633
|
"~LogMeta": "~LogMeta",
|
|
405
634
|
F: __dxlog_file,
|
|
406
|
-
L:
|
|
635
|
+
L: 496,
|
|
407
636
|
S: this,
|
|
408
637
|
A: ["this._tags", ""]
|
|
409
638
|
});
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
return key ? Filter.tag(key) : void 0;
|
|
639
|
+
const match = findTagByToken(this._tags, this._getNodeText(cursor, input));
|
|
640
|
+
return match ? Filter.tag(match.key) : void 0;
|
|
413
641
|
}
|
|
414
642
|
/**
|
|
415
643
|
* Parse a Value node.
|
|
@@ -636,6 +864,6 @@ var unescapeStringLiteral = (literalBody) => {
|
|
|
636
864
|
return out;
|
|
637
865
|
};
|
|
638
866
|
//#endregion
|
|
639
|
-
export { QueryBuilder, QueryDSL, normalizeInput };
|
|
867
|
+
export { QueryBuilder, QueryDSL, findTagByToken, formatTag, matchesFilter, normalizeInput, parseEnumTerms, writeEnumTerms };
|
|
640
868
|
|
|
641
869
|
//# sourceMappingURL=index.mjs.map
|
package/dist/lib/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/parser/gen/query.ts","../../src/parser/gen/query.terms.ts","../../src/parser/gen/index.ts","../../src/parser/query-builder.ts"],"sourcesContent":["// This file was generated by lezer-generator. You probably shouldn't edit it.\nimport {LRParser} from \"@lezer/lr\"\nconst spec_Identifier = {__proto__:null,type:26, NOT:86, not:86, \"!\":86, AND:88, and:88, OR:90, or:90}\nexport const parser = LRParser.deserialize({\n version: 14,\n states: \"(jOVQPOOOnQPO'#ClOOQO'#Cd'#CdOOQO'#Cf'#CfOyQPO'#ChO!OQPO'#CrO!WQPO'#CkOOQO'#Cc'#CcOOQO'#Cz'#CzOOQO'#DW'#DWOVQPO'#DWQ!]QPOOOVQPO'#DWO!mQPO,58xO!rQPO'#DQO!wQPO,59WO#PQPO,59SO#UQPO'#CtOOQO,59^,59^O#ZQPO,59^O#cQQO,59VO#wQPO,59rOOQO'#C{'#C{OOQO'#C|'#C|OOQO'#C}'#C}OVQPO,59rOVQPO,59rOVQPO,59rO$rQPO,59rOVQPO1G.dOOQO,59l,59lOOQO-E7O-E7OOOQO1G.n1G.nO#cQQO,59`O%VQPO'#DRO%[QPO1G.xOOQO1G.x1G.xO%dQQO'#CwOOQO'#Cn'#CnOOQO1G.q1G.qO%kQPO1G/^O&RQPO1G/^O&iQPO1G/^OOQO1G/^1G/^O'PQPO7+$OOOQO1G.z1G.zOOQO,59m,59mOOQO-E7P-E7POOQO7+$d7+$dOOQO,59c,59cO'dQPO,59cOOQO<<Gj<<GjO#cQQO'#DSO'lQPO1G.}OOQO1G.}1G.}OOQO,59n,59nOOQO-E7Q-E7QOOQO7+$i7+$i\",\n stateData: \"'t~OyOS~ORPOT[OXQOZRO]SOgTO{WO~OS]Oa^O^`X~O^`O~ORaOjbO~O^dO~OrhOshO|fO}gO~PVOTmO~ORnO~Oa^O^`a~ORpO~O^qO~OirOjtO~OZvOcvOdvOevOgTOluO~O{WORzaTzaXzaZza]zagzarzaszawza|za}zaUza~OU{OrhOshO|fO}gO~PVORaO~OirOj!QO~Om!RO~P#cOrzisziwzi|zi}ziUzi~PVO|fOrzisziwzi}ziUzi~PVO|fO}gOrzisziwziUzi~PVOU!TOrhOshO|fO}gO~PVOi!UOm!WO~Oi!UOm!ZO~O\",\n goto: \"%h{PP|PPPP|!]P!]P!]PP!]!lP!{PPP#XP#nPP#hPP#t$U$]$cPP$h$n$tPPP$zkXOYZ[ijklmxyz|kVOYZ[ijklmxyz|kUOYZ[ijklmxyz|QwdQ}qQ!SuR!X!UjVOYZ[ijklmxyz|Xvdqu!UQcTR!OrmYOYZ[eijklmxyz|ZiZlyz|XjZlz|VkZl|Q_PRo_QscR!PsQ!V!SR!Y!VQZO`eYZelxyz|Ql[QxiQyjQzkR|m\",\n nodeNames: \"⚠ Query Assignment Identifier = ( ) Filter TagFilter Tag TextFilter String TypeFilter TypeKeyword : PropertyFilter PropertyPath . Value Number Boolean Null ObjectLiteral { ObjectProperty , } ArrayLiteral [ ] Not And Or Relation ArrowRight ArrowLeft\",\n maxTerm: 45,\n skippedNodes: [0],\n repeatNodeCount: 3,\n tokenData: \"3P~RsX^#`pq#`rs$Tst%qwx&ixy(Qyz(V|}([}!O(a!O!P*Q!Q![(j![!]*V!^!_*[!_!`*g!c!}*l!}#O+W#P#Q+]#R#S*l#T#Y*l#Y#Z+b#Z#b*l#b#c.q#c#h*l#h#i1c#i#o*l#o#p2u#q#r2z#y#z#`$f$g#`#BY#BZ#`$IS$I_#`$I|$JO#`$JT$JU#`$KV$KW#`&FU&FV#`~#eYy~X^#`pq#`#y#z#`$f$g#`#BY#BZ#`$IS$I_#`$I|$JO#`$JT$JU#`$KV$KW#`&FU&FV#`~$WVOr$Trs$ms#O$T#O#P$r#P;'S$T;'S;=`%k<%lO$T~$rOZ~~$uRO;'S$T;'S;=`%O;=`O$T~%RWOr$Trs$ms#O$T#O#P$r#P;'S$T;'S;=`%k;=`<%l$T<%lO$T~%nP;=`<%l$T~%tT}!O&T!Q![&T!c!}&T#R#S&T#T#o&T~&YTX~}!O&T!Q![&T!c!}&T#R#S&T#T#o&T~&lVOw&iwx$mx#O&i#O#P'R#P;'S&i;'S;=`'z<%lO&i~'URO;'S&i;'S;=`'_;=`O&i~'bWOw&iwx$mx#O&i#O#P'R#P;'S&i;'S;=`'z;=`<%l&i<%lO&i~'}P;=`<%l&i~(VOT~~([OU~~(aOi~~(dQ!Q![(j!`!a){~(oSc~!O!P({!Q![(j!g!h)a#X#Y)a~)OP!Q![)R~)WRc~!Q![)R!g!h)a#X#Y)a~)dR{|)m}!O)m!Q![)s~)pP!Q![)s~)xPc~!Q![)s~*QOr~~*VOa~~*[O^~~*_P}!O*b~*gOs~~*lOS~P*qVRP}!O*l!O!P*l!P!Q*l!Q![*l!c!}*l#R#S*l#T#o*l~+]Ol~~+bOm~R+gWRP}!O*l!O!P*l!P!Q*l!Q![*l!c!}*l#R#S*l#T#U,P#U#o*lR,UXRP}!O*l!O!P*l!P!Q*l!Q![*l!c!}*l#R#S*l#T#`*l#`#a,q#a#o*lR,vXRP}!O*l!O!P*l!P!Q*l!Q![*l!c!}*l#R#S*l#T#g*l#g#h-c#h#o*lR-hXRP}!O*l!O!P*l!P!Q*l!Q![*l!c!}*l#R#S*l#T#X*l#X#Y.T#Y#o*lR.[VdQRP}!O*l!O!P*l!P!Q*l!Q![*l!c!}*l#R#S*l#T#o*lR.vXRP}!O*l!O!P*l!P!Q*l!Q![*l!c!}*l#R#S*l#T#i*l#i#j/c#j#o*lR/hXRP}!O*l!O!P*l!P!Q*l!Q![*l!c!}*l#R#S*l#T#`*l#`#a0T#a#o*lR0YXRP}!O*l!O!P*l!P!Q*l!Q![*l!c!}*l#R#S*l#T#`*l#`#a0u#a#o*lR0|VRPeQ}!O*l!O!P*l!P!Q*l!Q![*l!c!}*l#R#S*l#T#o*lR1hXRP}!O*l!O!P*l!P!Q*l!Q![*l!c!}*l#R#S*l#T#f*l#f#g2T#g#o*lR2YXRP}!O*l!O!P*l!P!Q*l!Q![*l!c!}*l#R#S*l#T#i*l#i#j-c#j#o*l~2zOg~~3POj~\",\n tokenizers: [0, 1],\n topRules: {\"Query\":[0,1]},\n specialized: [{term: 3, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}],\n tokenPrec: 0\n})\n","// This file was generated by lezer-generator. You probably shouldn't edit it.\nexport const\n Query = 1,\n Assignment = 2,\n Identifier = 3,\n Filter = 7,\n TagFilter = 8,\n Tag = 9,\n TextFilter = 10,\n String = 11,\n TypeFilter = 12,\n TypeKeyword = 13,\n PropertyFilter = 15,\n PropertyPath = 16,\n Value = 18,\n Number = 19,\n Boolean = 20,\n Null = 21,\n ObjectLiteral = 22,\n ObjectProperty = 24,\n ArrayLiteral = 27,\n Not = 30,\n And = 31,\n Or = 32,\n Relation = 33,\n ArrowRight = 34,\n ArrowLeft = 35\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { LRParser } from '@lezer/lr';\nimport { parser } from './query';\nimport * as terms from './query.terms';\n\nexport namespace QueryDSL {\n export const Parser: LRParser = parser;\n export const Node = terms;\n export const Tokens = ['type:', 'AND', 'OR', 'NOT'];\n}\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { type Parser, type Tree, type TreeCursor } from '@lezer/common';\n\nimport { Filter, type Tag } from '@dxos/echo';\nimport { invariant } from '@dxos/invariant';\nimport { type URI } from '@dxos/keys';\n\nimport { QueryDSL } from './gen';\n\n// TODO(burdon): Return Query AST.\nexport type BuildResult = { filter?: Filter.Any; name?: string };\n\n/**\n * Stateless query builder that parses DSL trees into filters.\n *\n * NOTE: QueryBuilder was largely developed using Claude Sonnet 4.5 (in Windsurf)..\n * To modify the functionality, create a minimal breaking test and direct the LLM to fix either the grammar or builder.\n */\nexport class QueryBuilder {\n private readonly _parser: Parser = QueryDSL.Parser.configure({ strict: true });\n\n constructor(private readonly _tags?: Tag.Map) {}\n\n /**\n * Check valid input.\n */\n validate(input: string): boolean {\n try {\n const tree = this._parser.parse(normalizeInput(input));\n return tree.cursor().node.name === 'Query';\n } catch {\n return false;\n }\n }\n\n /**\n * Build a query from the input string.\n */\n build(input: string): BuildResult {\n try {\n const normalized = normalizeInput(input);\n const tree = this._parser.parse(normalized);\n return this.buildQuery(tree, normalized);\n } catch {\n return {};\n }\n }\n\n /**\n * Build a query from a parsed DSL tree.\n */\n buildQuery(tree: Tree, input: string): BuildResult {\n const cursor = tree.cursor();\n\n // Start at root (Query node).\n if (cursor.node.name !== 'Query') {\n return {};\n }\n\n // Check if Query has multiple children (binary expression).\n const children: Array<{ name: string; from: number; to: number }> = [];\n if (cursor.firstChild()) {\n do {\n children.push({ name: cursor.node.name, from: cursor.from, to: cursor.to });\n } while (cursor.nextSibling());\n cursor.parent();\n }\n\n // Check if this is an assignment.\n const hasAssignment = children.some((child) => child.name === 'Assignment');\n if (hasAssignment) {\n return this._parseAssignment(cursor, input);\n }\n\n // If we have an operator in the children, or multiple expressions (implicit AND), parse as binary expression.\n const hasOperator = children.some((child) => child.name === 'And' || child.name === 'Or');\n const hasMultipleExpressions =\n children.filter((child) => child.name === 'Filter' || child.name === 'Not' || child.name === '(').length > 1;\n if (hasOperator || hasMultipleExpressions) {\n const filter = this._parseBinaryExpression(cursor, input);\n return { filter };\n }\n\n // Otherwise, parse the single expression.\n if (!cursor.firstChild()) {\n return { filter: Filter.nothing() };\n }\n\n const filter = this._parseExpression(cursor, input);\n return { filter };\n }\n\n /**\n * Parse an assignment node.\n */\n private _parseAssignment(cursor: TreeCursor, input: string): BuildResult {\n if (!cursor.firstChild()) {\n return {};\n }\n\n let name: string | undefined;\n let filter: Filter.Any | undefined;\n\n // Find the Assignment node\n do {\n if (cursor.node.name === 'Assignment') {\n // Get the full assignment text first\n const assignmentText = this._getNodeText(cursor, input);\n\n if (cursor.firstChild()) {\n // First child should be the variable name (Identifier)\n name = this._getNodeText(cursor, input);\n\n // Find the parentheses in the assignment text and extract the content\n const openParenIndex = assignmentText.indexOf('(');\n const closeParenIndex = assignmentText.lastIndexOf(')');\n\n if (openParenIndex !== -1 && closeParenIndex !== -1 && closeParenIndex > openParenIndex) {\n const subInput = assignmentText.slice(openParenIndex + 1, closeParenIndex).trim();\n const subTree = this._parser.parse(subInput);\n const subResult = this.buildQuery(subTree, subInput);\n filter = subResult.filter;\n }\n\n cursor.parent(); // Back to Assignment\n }\n break;\n }\n } while (cursor.nextSibling());\n\n cursor.parent(); // Back to Query\n return { filter, name };\n }\n\n /**\n * Parse an expression node.\n */\n private _parseExpression(cursor: TreeCursor, input: string): Filter.Any | undefined {\n const nodeName = cursor.node.name;\n\n switch (nodeName) {\n case 'Filter':\n return this._parseFilter(cursor, input);\n\n case 'Not': {\n // Move past NOT token to the expression.\n cursor.nextSibling();\n const notFilter = this._parseExpression(cursor, input);\n return notFilter ? Filter.not(notFilter) : undefined;\n }\n\n case 'And':\n case 'Or':\n // This is the operator node, we need to handle the binary expression.\n // The cursor is positioned at the operator, we need to go back to parent.\n cursor.parent();\n return this._parseBinaryExpression(cursor, input);\n\n case '(': {\n // Skip opening paren.\n cursor.nextSibling();\n const parenFilter = this._parseExpression(cursor, input);\n // Skip closing paren.\n cursor.nextSibling();\n return parenFilter;\n }\n\n default: {\n // Check if this is a binary expression (has And/Or as a child).\n const savedPos = cursor.from;\n if (cursor.firstChild()) {\n // Look for And/Or operators or multiple expressions (implicit AND).\n let hasOperator = false;\n let expressionCount = 0;\n do {\n if (cursor.node.name === 'And' || cursor.node.name === 'Or') {\n hasOperator = true;\n break;\n }\n if (cursor.node.name === 'Filter' || cursor.node.name === 'Not' || cursor.node.name === '(') {\n expressionCount++;\n }\n } while (cursor.nextSibling());\n hasOperator = hasOperator || expressionCount > 1;\n\n // Reset cursor to the saved position.\n cursor.parent();\n cursor.firstChild();\n while (cursor.from !== savedPos && cursor.nextSibling()) {}\n\n if (hasOperator) {\n cursor.parent();\n return this._parseBinaryExpression(cursor, input);\n } else {\n const result = this._parseExpression(cursor, input);\n cursor.parent();\n return result;\n }\n }\n return Filter.nothing();\n }\n }\n }\n\n /**\n * Parse a binary expression (AND/OR).\n */\n private _parseBinaryExpression(cursor: TreeCursor, input: string): Filter.Any {\n const filters: Filter.Any[] = [];\n let operator: 'and' | 'or' | null = null;\n\n // Collect all filters and operators.\n if (cursor.firstChild()) {\n do {\n const nodeName = cursor.node.name;\n\n if (nodeName === 'And' || nodeName === 'Or') {\n operator = nodeName.toLowerCase() as 'and' | 'or';\n } else if (nodeName === '(') {\n // Handle parenthesized expression.\n // Look ahead to see if this is a binary expression.\n const savedPos = cursor.from;\n cursor.nextSibling(); // Move past '('\n\n // Check if the parentheses contain a binary expression.\n let hasBinaryOp = false;\n do {\n if (cursor.node.name === 'And' || cursor.node.name === 'Or') {\n hasBinaryOp = true;\n break;\n }\n } while (cursor.nextSibling() && cursor.node.name !== ')');\n\n // Reset cursor to start of parenthesized content.\n cursor.parent();\n cursor.firstChild();\n while (cursor.from !== savedPos && cursor.nextSibling()) {}\n cursor.nextSibling(); // Move past '(' again.\n\n if (hasBinaryOp) {\n // Find the matching closing parenthesis.\n let depth = 1;\n const exprStart = cursor.from;\n let exprEnd = cursor.to;\n\n while (cursor.nextSibling() && depth > 0) {\n if (cursor.node.name === '(') {\n depth++;\n } else if (cursor.node.name === ')') {\n depth--;\n if (depth === 0) {\n exprEnd = cursor.from;\n }\n }\n }\n\n // Parse the expression inside parentheses as a subtree.\n const subInput = input.slice(exprStart, exprEnd);\n const subTree = this._parser.parse(subInput);\n const subResult = this.buildQuery(subTree, subInput);\n if (subResult.filter) {\n filters.push(subResult.filter);\n }\n } else {\n // Simple parenthesized expression.\n const subFilter = this._parseExpression(cursor, input);\n if (subFilter) {\n filters.push(subFilter);\n }\n\n // Skip until we find the closing parenthesis.\n while (cursor.nextSibling() && cursor.node.name !== ')') {}\n }\n } else if (nodeName !== ')') {\n const subFilter = this._parseExpression(cursor, input);\n if (subFilter) {\n filters.push(subFilter);\n }\n }\n } while (cursor.nextSibling());\n\n cursor.parent();\n }\n\n if (filters.length === 0) {\n return Filter.nothing();\n }\n\n if (filters.length === 1) {\n return filters[0];\n }\n\n return operator === 'or' ? Filter.or(...filters) : Filter.and(...filters);\n }\n\n /**\n * Parse a Filter node.\n */\n private _parseFilter(cursor: TreeCursor, input: string): Filter.Any | undefined {\n if (!cursor.firstChild()) {\n return Filter.nothing();\n }\n\n let result: Filter.Any | undefined = undefined;\n const filterType = cursor.node.name;\n switch (filterType) {\n case 'TagFilter':\n if (this._tags) {\n result = this._parseTagFilter(cursor, input);\n }\n break;\n\n case 'TextFilter':\n result = this._parseTextFilter(cursor, input);\n break;\n\n case 'TypeFilter':\n result = this._parseTypeFilter(cursor, input);\n break;\n\n case 'ObjectLiteral':\n result = this._parseObjectLiteral(cursor, input);\n break;\n\n case 'PropertyFilter':\n result = this._parsePropertyFilter(cursor, input);\n break;\n\n default:\n result = Filter.nothing();\n }\n\n cursor.parent();\n return result;\n }\n\n /**\n * Parse a TypeFilter node (type:typename).\n */\n private _parseTypeFilter(cursor: TreeCursor, input: string): Filter.Any {\n // Skip TypeKeyword.\n cursor.firstChild();\n cursor.nextSibling(); // Skip ':'\n cursor.nextSibling(); // Move to Identifier\n\n const typename = this._getNodeText(cursor, input);\n cursor.parent(); // Go back to TypeFilter.\n // Inline the URI construction to keep runtime `@dxos/keys` values out of the query-lite bundle\n // (which runs in a QuickJS sandbox without that dep). Callers may pass a bare typename\n // (e.g. `com.example.task`) or a canonical URI (`dxn:…` / `echo:…`); prepend `dxn:` only for\n // bare names detected by the absence of a URI scheme.\n const uri = (/^[a-z][a-z0-9+.-]*:/i.test(typename) ? typename : `dxn:${typename}`) as URI.URI;\n return Filter.type(uri);\n }\n\n /**\n * Parse a TextFilter node (quoted string).\n */\n private _parseTextFilter(cursor: TreeCursor, input: string): Filter.Any {\n cursor.firstChild(); // Move to String node.\n const text = this._getNodeText(cursor, input);\n cursor.parent(); // Go back to TextFilter.\n // Remove quotes and decode escapes.\n return Filter.text(unescapeStringLiteral(text.slice(1, -1)));\n }\n\n /**\n * Parse an ObjectLiteral node.\n */\n private _parseObjectLiteral(cursor: TreeCursor, input: string): Filter.Any {\n const props: Record<string, any> = {};\n\n if (cursor.firstChild()) {\n do {\n if (cursor.node.name === 'ObjectProperty') {\n const { key, value } = this._parseObjectProperty(cursor, input);\n if (key) {\n // Convert simple values to Filter.eq for compatibility with Filter.props.\n props[key] = Filter.eq(value);\n }\n }\n } while (cursor.nextSibling());\n\n cursor.parent();\n }\n\n return Filter.props(props);\n }\n\n /**\n * Parse an ObjectProperty node.\n */\n private _parseObjectProperty(cursor: TreeCursor, input: string): { key: string | null; value: any } {\n let key: string | null = null;\n let value: any = null;\n\n if (cursor.firstChild()) {\n // First child should be the property name (Identifier).\n if (cursor.node.name === 'Identifier') {\n key = this._getNodeText(cursor, input);\n }\n\n // Skip ':' and move to Value.\n cursor.nextSibling();\n cursor.nextSibling();\n\n if (cursor.node.name === 'Value' && cursor.firstChild()) {\n value = this._parseValue(cursor, input);\n cursor.parent();\n }\n\n cursor.parent();\n }\n\n return { key, value };\n }\n\n /**\n * Parse a PropertyFilter node (property:value).\n */\n private _parsePropertyFilter(cursor: TreeCursor, input: string): Filter.Any {\n let path: string | null = null;\n let value: any = null;\n\n if (cursor.firstChild()) {\n // First child is PropertyPath.\n if (cursor.node.name === 'PropertyPath') {\n path = this._parsePropertyPath(cursor, input);\n }\n\n // Skip ':' and move to Value.\n cursor.nextSibling();\n cursor.nextSibling();\n\n if (cursor.node.name === 'Value' && cursor.firstChild()) {\n value = this._parseValue(cursor, input);\n cursor.parent();\n }\n\n cursor.parent();\n }\n\n if (!path) {\n return Filter.nothing();\n }\n\n return Filter.props({ [path]: value });\n }\n\n /**\n * Parse a PropertyPath node (supports dot notation).\n */\n private _parsePropertyPath(cursor: TreeCursor, input: string): string {\n const parts: string[] = [];\n\n if (cursor.firstChild()) {\n do {\n if (cursor.node.name === 'Identifier') {\n parts.push(this._getNodeText(cursor, input));\n }\n } while (cursor.nextSibling());\n\n cursor.parent();\n }\n\n return parts.join('.');\n }\n\n /**\n * Parse a TagFilter node (#tag).\n */\n private _parseTagFilter(cursor: TreeCursor, input: string): Filter.Any | undefined {\n invariant(this._tags);\n const str = this._getNodeText(cursor, input).slice(1).toLowerCase();\n const [key] = Object.entries(this._tags!).find(([, value]) => value.label.toLowerCase() === str) ?? [];\n return key ? Filter.tag(key) : undefined;\n }\n\n /**\n * Parse a Value node.\n */\n private _parseValue(cursor: TreeCursor, input: string): any {\n const valueType = cursor.node.name;\n\n switch (valueType) {\n case 'String': {\n // Remove quotes and decode escapes.\n const str = this._getNodeText(cursor, input);\n return unescapeStringLiteral(str.slice(1, -1));\n }\n\n case 'Number':\n return Number(this._getNodeText(cursor, input));\n\n case 'Boolean':\n return this._getNodeText(cursor, input) === 'true';\n\n case 'Null':\n return null;\n\n case 'ObjectLiteral': {\n // For nested objects, parse recursively.\n const props: Record<string, any> = {};\n if (cursor.firstChild()) {\n do {\n if (cursor.node.name === 'ObjectProperty') {\n const { key, value } = this._parseObjectProperty(cursor, input);\n if (key) {\n props[key] = value;\n }\n }\n } while (cursor.nextSibling());\n cursor.parent();\n }\n return props;\n }\n\n case 'ArrayLiteral': {\n // Parse array values\n const array: any[] = [];\n if (cursor.firstChild()) {\n do {\n if (cursor.node.name === 'Value' && cursor.firstChild()) {\n array.push(this._parseValue(cursor, input));\n cursor.parent();\n }\n } while (cursor.nextSibling());\n cursor.parent();\n }\n return array;\n }\n\n default:\n return null;\n }\n }\n\n /**\n * Get the text content of the current node.\n */\n private _getNodeText(cursor: TreeCursor, input: string): string {\n return input.slice(cursor.from, cursor.to);\n }\n}\n\nconst KEYWORDS = new Set(['AND', 'OR', 'NOT']);\nconst VALUE_LITERALS = new Set(['true', 'false', 'null']);\nconst SPECIAL_CHARS = /[\\s(){}[\\],\"']/;\nconst PROPERTY_KEY = /^[a-zA-Z_][a-zA-Z0-9_.]*$/;\nconst NUMBER_LITERAL = /^-?\\d+(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$/;\n\n// Common URL schemes — `http://...`, `mailto:rich@...` etc. should be searched as text,\n// not auto-promoted to property filters.\nconst URL_SCHEMES = new Set([\n 'http',\n 'https',\n 'ftp',\n 'ftps',\n 'file',\n 'mailto',\n 'tel',\n 'sms',\n 'data',\n 'javascript',\n 'ws',\n 'wss',\n 'ssh',\n 'git',\n]);\n\n/**\n * Normalize raw user input into a form the lezer grammar can parse.\n * - Bare text fragments (e.g. `foo`) are wrapped in quotes so they parse as TextFilter.\n * - Property values that aren't already quoted/numeric/boolean (e.g. `from:rich@dxos.org`) are quoted.\n * - Tags, type filters, operators, parens, braces, quoted strings, and assignments pass through unchanged.\n */\nexport const normalizeInput = (input: string): string => {\n const out: string[] = [];\n let pos = 0;\n while (pos < input.length) {\n const currentChar = input[pos];\n\n // Whitespace.\n if (/\\s/.test(currentChar)) {\n out.push(currentChar);\n pos++;\n continue;\n }\n\n // Quoted string (already a valid TextFilter / Value).\n // Single-quote only opens a string if the previous char isn't a word-char,\n // so apostrophes inside barewords (e.g. `don't`, `O'Connor`) stay attached.\n const prevChar = pos > 0 ? input[pos - 1] : '';\n const isStringOpener =\n currentChar === '\"' || (currentChar === \"'\" && (pos === 0 || !/[a-zA-Z0-9_]/.test(prevChar)));\n if (isStringOpener) {\n const quoteChar = currentChar;\n let scanIndex = pos + 1;\n while (scanIndex < input.length && input[scanIndex] !== quoteChar) {\n if (input[scanIndex] === '\\\\' && scanIndex + 1 < input.length) {\n scanIndex += 2;\n } else {\n scanIndex++;\n }\n }\n out.push(input.slice(pos, Math.min(scanIndex + 1, input.length)));\n pos = scanIndex + 1;\n continue;\n }\n\n // Object/array literals: pass through (contents already structured).\n if (currentChar === '{' || currentChar === '[') {\n const closeChar = currentChar === '{' ? '}' : ']';\n let depth = 1;\n let scanIndex = pos + 1;\n while (scanIndex < input.length && depth > 0) {\n const innerChar = input[scanIndex];\n if (innerChar === '\"' || innerChar === \"'\") {\n const quoteChar = innerChar;\n scanIndex++;\n while (scanIndex < input.length && input[scanIndex] !== quoteChar) {\n if (input[scanIndex] === '\\\\' && scanIndex + 1 < input.length) {\n scanIndex += 2;\n } else {\n scanIndex++;\n }\n }\n scanIndex++;\n } else if (innerChar === currentChar) {\n depth++;\n scanIndex++;\n } else if (innerChar === closeChar) {\n depth--;\n scanIndex++;\n } else {\n scanIndex++;\n }\n }\n out.push(input.slice(pos, scanIndex));\n pos = scanIndex;\n continue;\n }\n\n // Single-char structural tokens. `}` / `]` only reach here when unmatched —\n // pass them through so the lezer parser can produce a clear error rather than spinning.\n if (\n currentChar === '(' ||\n currentChar === ')' ||\n currentChar === '=' ||\n currentChar === ',' ||\n currentChar === '}' ||\n currentChar === ']'\n ) {\n out.push(currentChar);\n pos++;\n continue;\n }\n\n // Relations.\n if ((currentChar === '-' && input[pos + 1] === '>') || (currentChar === '<' && input[pos + 1] === '-')) {\n out.push(input.slice(pos, pos + 2));\n pos += 2;\n continue;\n }\n\n // NOT prefix (`!`) — single token.\n if (currentChar === '!') {\n out.push(currentChar);\n pos++;\n continue;\n }\n\n // Tag.\n if (currentChar === '#') {\n let scanIndex = pos + 1;\n while (scanIndex < input.length && /[a-zA-Z0-9_-]/.test(input[scanIndex])) {\n scanIndex++;\n }\n out.push(input.slice(pos, scanIndex));\n pos = scanIndex;\n continue;\n }\n\n // Bareword: scan until next whitespace or special char.\n let scanIndex = pos;\n while (scanIndex < input.length) {\n const innerChar = input[scanIndex];\n if (innerChar === '\"') {\n break;\n }\n if (innerChar === \"'\" && scanIndex > pos && !/[a-zA-Z0-9_]/.test(input[scanIndex - 1])) {\n break;\n }\n if (innerChar === \"'\" && scanIndex === pos) {\n break;\n }\n if (innerChar !== \"'\" && SPECIAL_CHARS.test(innerChar)) {\n break;\n }\n if (innerChar === '-' && input[scanIndex + 1] === '>') {\n break;\n }\n if (innerChar === '<' && input[scanIndex + 1] === '-') {\n break;\n }\n scanIndex++;\n }\n // Defensive: if no characters were consumed, advance one to avoid infinite loops.\n if (scanIndex === pos) {\n out.push(currentChar);\n pos++;\n continue;\n }\n const token = input.slice(pos, scanIndex);\n pos = scanIndex;\n\n // Operators.\n if (KEYWORDS.has(token.toUpperCase())) {\n out.push(token);\n continue;\n }\n\n // Property/type filter (`key:value`).\n const colonIdx = token.indexOf(':');\n if (colonIdx > 0) {\n const key = token.slice(0, colonIdx);\n const rest = token.slice(colonIdx + 1);\n\n // type:typename — leave for grammar's TypeFilter (Identifier value).\n if (key === 'type') {\n out.push(token);\n continue;\n }\n\n // URLs (`http://...`, `mailto:rich@...`) and URL-paths starting with `//`\n // are searched as text rather than auto-promoted to property filters.\n const isUrlScheme = URL_SCHEMES.has(key.toLowerCase()) || rest.startsWith('//');\n if (!isUrlScheme && PROPERTY_KEY.test(key)) {\n if (rest.length === 0) {\n // Trailing colon while typing — pass through.\n out.push(token);\n continue;\n }\n const firstValueChar = rest[0];\n if (firstValueChar === '\"' || firstValueChar === \"'\") {\n // Already quoted.\n out.push(token);\n continue;\n }\n if (firstValueChar === '{' || firstValueChar === '[') {\n // Object/array literal value.\n out.push(token);\n continue;\n }\n if (VALUE_LITERALS.has(rest) || NUMBER_LITERAL.test(rest)) {\n // Boolean / null / number literal.\n out.push(token);\n continue;\n }\n out.push(`${key}:\"${escapeStringLiteral(rest)}\"`);\n continue;\n }\n\n // URL or unknown key shape — fall through to text.\n }\n\n // Identifier followed by `=` is the LHS of an Assignment — keep as-is.\n if (PROPERTY_KEY.test(token)) {\n let lookahead = pos;\n while (lookahead < input.length && /\\s/.test(input[lookahead])) {\n lookahead++;\n }\n if (input[lookahead] === '=') {\n out.push(token);\n continue;\n }\n }\n\n // Bare text fragment: quote so it parses as TextFilter.\n out.push(`\"${escapeStringLiteral(token)}\"`);\n }\n\n return out.join('');\n};\n\n/** Escape a raw value into a string literal body (without surrounding quotes). */\nconst escapeStringLiteral = (value: string): string => value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n\n/** Decode `\\\\` and `\\\"` escapes inside a string literal body. */\nconst unescapeStringLiteral = (literalBody: string): string => {\n let out = '';\n for (let i = 0; i < literalBody.length; i++) {\n const ch = literalBody[i];\n if (ch === '\\\\' && i + 1 < literalBody.length) {\n out += literalBody[i + 1];\n i++;\n } else {\n out += ch;\n }\n }\n return out;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/filter/enum-terms.ts","../../src/filter/match-filter.ts","../../src/parser/gen/query.ts","../../src/parser/gen/query.terms.ts","../../src/parser/gen/index.ts","../../src/parser/query-builder.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n/** A property that takes one of a closed set of values — a task's `status`, a message's `folder`. */\nexport type EnumProperty<T extends string> = {\n /** The property name as a term writes it (`status` in `status:done`). */\n property: string;\n /** Every value, in the order a written term lists them. */\n values: readonly T[];\n};\n\nexport type EnumTerms<T extends string> = {\n /** The values the query's terms on the property keep; absent when the query names none. */\n values?: readonly T[];\n /** The query with those terms taken out. */\n rest: string;\n};\n\nconst NOT_KEYWORDS = new Set(['NOT', '!']);\n\n/**\n * Splits the terms on one enumerated property out of a query, so a picker for that property and the\n * query text can be two views over one string.\n *\n * Only top-level terms a picker could have written are read — `prop:x`, `NOT prop:x` (or `!prop:x`)\n * and `(prop:a OR prop:b)` — and they are intersected, as the query's implicit AND intersects them.\n * A query with a top-level `OR` is left whole: its terms are not all required, so no value set\n * describes it. A value outside the set (`status:sta` while it is being typed) stays in the text,\n * where it matches as the substring it is.\n */\nexport const parseEnumTerms = <T extends string>(text: string, { property, values }: EnumProperty<T>): EnumTerms<T> => {\n const tokens = tokenize(text);\n if (hasTopLevelOr(tokens)) {\n return { rest: text.trim() };\n }\n\n const valueOf = (token: string): T | undefined => {\n const match = termPattern(property).exec(token);\n const value = match?.[2].toLowerCase();\n return value === undefined ? undefined : values.find((candidate) => candidate.toLowerCase() === value);\n };\n const groupOf = (token: string): ReadonlySet<T> | undefined => {\n if (!token.startsWith('(') || !token.endsWith(')')) {\n return undefined;\n }\n const parts = token.slice(1, -1).trim().split(/\\s+/);\n const group = new Set<T>();\n for (let index = 0; index < parts.length; index++) {\n if (index % 2 === 1) {\n if (parts[index].toUpperCase() !== 'OR') {\n return undefined;\n }\n continue;\n }\n const value = valueOf(parts[index]);\n if (value === undefined) {\n return undefined;\n }\n group.add(value);\n }\n return parts.length % 2 === 1 ? group : undefined;\n };\n\n let kept: Set<T> | undefined;\n const narrow = (keep: (value: T) => boolean) => {\n kept = new Set((kept ? [...kept] : values).filter(keep));\n };\n\n const rest: string[] = [];\n for (let index = 0; index < tokens.length; index++) {\n const token = tokens[index];\n const next = tokens[index + 1];\n const negated = NOT_KEYWORDS.has(token.toUpperCase()) && next !== undefined ? valueOf(next) : undefined;\n if (negated !== undefined) {\n narrow((value) => value !== negated);\n index++;\n continue;\n }\n const inline = token.startsWith('!') ? valueOf(token.slice(1)) : undefined;\n if (inline !== undefined) {\n narrow((value) => value !== inline);\n continue;\n }\n const single = valueOf(token);\n if (single !== undefined) {\n narrow((value) => value === single);\n continue;\n }\n const group = groupOf(token);\n if (group) {\n narrow((value) => group.has(value));\n continue;\n }\n rest.push(token);\n }\n\n const result: ReadonlySet<T> | undefined = kept;\n return result\n ? { values: values.filter((value) => result.has(value)), rest: rest.join(' ') }\n : { rest: rest.join(' ') };\n};\n\n/**\n * The query with its terms on the property replaced by the one term that says `selected`, placed\n * first.\n *\n * Written in whichever of the two forms is shorter — the values kept (`prop:a`, `(prop:a OR prop:b)`)\n * or the values hidden (`NOT prop:x NOT prop:y`) — so hiding two of nine reads as that, not as a list\n * of the seven left. Every value selected writes no term. A query with a top-level `OR` comes back\n * unchanged: no term on the property describes it, so a written term could not be read back, and\n * each write would stack another term in front of the last.\n */\nexport const writeEnumTerms = <T extends string>(\n text: string,\n spec: EnumProperty<T>,\n selected: readonly T[],\n): string => {\n const { property, values } = spec;\n if (hasTopLevelOr(tokenize(text))) {\n return text;\n }\n const { rest } = parseEnumTerms(text, spec);\n const kept = values.filter((value) => selected.includes(value));\n const hidden = values.filter((value) => !selected.includes(value));\n const term =\n hidden.length === 0\n ? ''\n : kept.length > 0 && kept.length <= hidden.length\n ? kept.length === 1\n ? `${property}:${kept[0]}`\n : `(${kept.map((value) => `${property}:${value}`).join(' OR ')})`\n : hidden.map((value) => `NOT ${property}:${value}`).join(' ');\n\n return [term, rest].filter((part) => part.length > 0).join(' ');\n};\n\nconst hasTopLevelOr = (tokens: readonly string[]): boolean => tokens.some((token) => token.toUpperCase() === 'OR');\n\n/** `prop:value`, the value optionally quoted; the property name matched literally, in any case. */\nconst termPattern = (property: string): RegExp =>\n new RegExp(`^${property.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}:([\"']?)([\\\\w-]+)\\\\1$`, 'i');\n\n/** Top-level terms: whitespace-separated, with parenthesized groups and quoted strings kept whole. */\nconst tokenize = (text: string): string[] => {\n const tokens: string[] = [];\n let current = '';\n let depth = 0;\n let quote: string | undefined;\n let previous = '';\n for (const char of text) {\n const prior = previous;\n previous = char;\n if (quote) {\n current += char;\n if (char === quote) {\n quote = undefined;\n }\n continue;\n }\n // An apostrophe inside a word (`don't`) is not a quote, as `normalizeInput` reads it.\n if (char === '\"' || (char === \"'\" && !/[a-zA-Z0-9_]/.test(prior))) {\n quote = char;\n } else if (char === '(') {\n depth++;\n } else if (char === ')') {\n depth = Math.max(0, depth - 1);\n } else if (/\\s/.test(char) && depth === 0) {\n if (current.length > 0) {\n tokens.push(current);\n }\n current = '';\n continue;\n }\n current += char;\n }\n if (current.length > 0) {\n tokens.push(current);\n }\n return tokens;\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type Filter, Obj, type QueryAST } from '@dxos/echo';\n\nexport type MatchFilterOptions<T extends Obj.Unknown> = {\n /**\n * The strings a free-text term searches. Defaults to the object's top-level string properties;\n * a host narrows it to what a reader sees (a task's title and description, say).\n */\n text?: (object: T) => readonly (string | undefined)[];\n /** The object's tag ids — the uri space `#tag` terms carry. Defaults to `Obj.getMeta(object).tags`. */\n tags?: (object: T) => readonly string[];\n};\n\n/**\n * Evaluates a filter the {@link QueryBuilder} built against one object in memory.\n *\n * For a host that cannot hand the filter to a query: one that renders a tree and must keep the\n * ancestors of a match, or reads a feed the query engine does not index. Semantics follow what a\n * reader typed rather than the query engine's equality: a string property matches as a\n * case-insensitive substring (`status:start` finds `started`), an array on any element, and an\n * object on any field (`assignee:rich` finds an actor's name or email). An unknown node fails\n * closed, so a filter the evaluator does not understand never broadens the result.\n */\nexport const matchesFilter = <T extends Obj.Unknown>(\n filter: Filter.Any,\n object: T,\n options: MatchFilterOptions<T> = {},\n): boolean => matchesAst(filter.ast, object, options);\n\nconst matchesAst = <T extends Obj.Unknown>(\n ast: QueryAST.Filter,\n object: T,\n options: MatchFilterOptions<T>,\n): boolean => {\n switch (ast.type) {\n case 'and':\n return ast.filters.every((sub) => matchesAst(sub, object, options));\n case 'or':\n return ast.filters.some((sub) => matchesAst(sub, object, options));\n case 'not':\n return !matchesAst(ast.filter, object, options);\n case 'tag':\n return (options.tags ?? metaTags)(object).includes(ast.tag);\n case 'text-search':\n return matchesText(ast.text, (options.text ?? stringProperties)(object));\n case 'object': {\n // `typename` is null for `Filter.everything()`/`Filter.props()`, which match any type.\n if (ast.typename && !typeMatches(ast.typename, typeUri(object))) {\n return false;\n }\n // An `id:` term lands here rather than in `props`, so a filter naming one id would otherwise\n // match every object.\n if (ast.id && !ast.id.includes(object.id)) {\n return false;\n }\n return Object.entries(ast.props).every(([key, predicate]) => matchesPredicate(predicate, getPath(object, key)));\n }\n default:\n return false;\n }\n};\n\n/** A property's value against the predicate a `key:value` term or an object literal built. */\nconst matchesPredicate = (predicate: QueryAST.Filter, value: unknown): boolean => {\n switch (predicate.type) {\n case 'compare':\n switch (predicate.operator) {\n case 'eq':\n return matchesValue(value, predicate.value);\n case 'neq':\n return !matchesValue(value, predicate.value);\n default:\n return compareOrdered(value, predicate.value, predicate.operator);\n }\n case 'object':\n // A nested literal (`{ address: { city: … } }`) narrows the value's own fields.\n return (\n isRecord(value) &&\n Object.entries(predicate.props).every(([key, sub]) => matchesPredicate(sub, getPath(value, key)))\n );\n case 'and':\n return predicate.filters.every((sub) => matchesPredicate(sub, value));\n case 'or':\n return predicate.filters.some((sub) => matchesPredicate(sub, value));\n case 'not':\n return !matchesPredicate(predicate.filter, value);\n default:\n return false;\n }\n};\n\n/**\n * Strings match as case-insensitive substrings, arrays on any element, objects on any field; any\n * other value only by identity.\n */\nconst matchesValue = (value: unknown, query: unknown): boolean => {\n if (value === query) {\n return true;\n }\n if (typeof query === 'string' && typeof value === 'string') {\n return value.toLowerCase().includes(query.toLowerCase());\n }\n if (Array.isArray(value)) {\n return value.some((entry) => matchesValue(entry, query));\n }\n if (typeof query === 'string' && isRecord(value)) {\n return Object.values(value).some((entry) => matchesValue(entry, query));\n }\n return false;\n};\n\n/** Ordering holds only between two numbers or two strings; anything else matches nothing. */\nconst compareOrdered = (value: unknown, query: unknown, operator: 'gt' | 'gte' | 'lt' | 'lte'): boolean => {\n if (typeof value === 'number' && typeof query === 'number') {\n return compare(value, query, operator);\n }\n if (typeof value === 'string' && typeof query === 'string') {\n return compare(value, query, operator);\n }\n return false;\n};\n\nconst compare = <V extends number | string>(value: V, query: V, operator: 'gt' | 'gte' | 'lt' | 'lte'): boolean => {\n switch (operator) {\n case 'gt':\n return value > query;\n case 'gte':\n return value >= query;\n case 'lt':\n return value < query;\n case 'lte':\n return value <= query;\n }\n};\n\nconst matchesText = (needle: string, haystacks: readonly (string | undefined)[]): boolean => {\n if (!needle) {\n return true;\n }\n const lower = needle.toLowerCase();\n return haystacks.some((haystack) => haystack?.toLowerCase().includes(lower));\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null;\n\nconst getPath = (object: unknown, path: string): unknown =>\n path.split('.').reduce<unknown>((current, key) => (isRecord(current) ? current[key] : undefined), object);\n\n// The id is not text a reader wrote, so a word that happens to occur in one does not match.\nconst stringProperties = (object: Obj.Unknown): string[] =>\n Object.entries(object).flatMap(([key, value]) => (key !== 'id' && typeof value === 'string' ? [value] : []));\n\nconst metaTags = (object: Obj.Unknown): string[] => {\n try {\n return Obj.getMeta(object).tags.map((tag) => tag.uri);\n } catch {\n // An object outside a database carries no meta; a tag term then matches nothing rather than throwing.\n return [];\n }\n};\n\n/**\n * A `type:` term names a type without a version (`dxn:org.example.person`), while an object's type\n * carries one (`dxn:org.example.person:0.1.0`), so an unversioned name matches every version.\n */\nconst typeMatches = (typename: string, uri: string | undefined): boolean =>\n uri !== undefined && (uri === typename || uri.startsWith(`${typename}:`));\n\nconst typeUri = (object: Obj.Unknown): string | undefined => {\n try {\n return Obj.getTypeURI(object)?.toString();\n } catch {\n return undefined;\n }\n};\n","// This file was generated by lezer-generator. You probably shouldn't edit it.\nimport {LRParser} from \"@lezer/lr\"\nconst spec_Identifier = {__proto__:null,type:26, NOT:86, not:86, \"!\":86, AND:88, and:88, OR:90, or:90}\nexport const parser = LRParser.deserialize({\n version: 14,\n states: \"(jOVQPOOOnQPO'#ClOOQO'#Cd'#CdOOQO'#Cf'#CfOyQPO'#ChO!OQPO'#CrO!WQPO'#CkOOQO'#Cc'#CcOOQO'#Cz'#CzOOQO'#DW'#DWOVQPO'#DWQ!]QPOOOVQPO'#DWO!mQPO,58xO!rQPO'#DQO!wQPO,59WO#PQPO,59SO#UQPO'#CtOOQO,59^,59^O#ZQPO,59^O#cQQO,59VO#wQPO,59rOOQO'#C{'#C{OOQO'#C|'#C|OOQO'#C}'#C}OVQPO,59rOVQPO,59rOVQPO,59rO$rQPO,59rOVQPO1G.dOOQO,59l,59lOOQO-E7O-E7OOOQO1G.n1G.nO#cQQO,59`O%VQPO'#DRO%[QPO1G.xOOQO1G.x1G.xO%dQQO'#CwOOQO'#Cn'#CnOOQO1G.q1G.qO%kQPO1G/^O&RQPO1G/^O&iQPO1G/^OOQO1G/^1G/^O'PQPO7+$OOOQO1G.z1G.zOOQO,59m,59mOOQO-E7P-E7POOQO7+$d7+$dOOQO,59c,59cO'dQPO,59cOOQO<<Gj<<GjO#cQQO'#DSO'lQPO1G.}OOQO1G.}1G.}OOQO,59n,59nOOQO-E7Q-E7QOOQO7+$i7+$i\",\n stateData: \"'t~OyOS~ORPOT[OXQOZRO]SOgTO{WO~OS]Oa^O^`X~O^`O~ORaOjbO~O^dO~OrhOshO|fO}gO~PVOTmO~ORnO~Oa^O^`a~ORpO~O^qO~OirOjtO~OZvOcvOdvOevOgTOluO~O{WORzaTzaXzaZza]zagzarzaszawza|za}zaUza~OU{OrhOshO|fO}gO~PVORaO~OirOj!QO~Om!RO~P#cOrzisziwzi|zi}ziUzi~PVO|fOrzisziwzi}ziUzi~PVO|fO}gOrzisziwziUzi~PVOU!TOrhOshO|fO}gO~PVOi!UOm!WO~Oi!UOm!ZO~O\",\n goto: \"%h{PP|PPPP|!]P!]P!]PP!]!lP!{PPP#XP#nPP#hPP#t$U$]$cPP$h$n$tPPP$zkXOYZ[ijklmxyz|kVOYZ[ijklmxyz|kUOYZ[ijklmxyz|QwdQ}qQ!SuR!X!UjVOYZ[ijklmxyz|Xvdqu!UQcTR!OrmYOYZ[eijklmxyz|ZiZlyz|XjZlz|VkZl|Q_PRo_QscR!PsQ!V!SR!Y!VQZO`eYZelxyz|Ql[QxiQyjQzkR|m\",\n nodeNames: \"⚠ Query Assignment Identifier = ( ) Filter TagFilter Tag TextFilter String TypeFilter TypeKeyword : PropertyFilter PropertyPath . Value Number Boolean Null ObjectLiteral { ObjectProperty , } ArrayLiteral [ ] Not And Or Relation ArrowRight ArrowLeft\",\n maxTerm: 45,\n skippedNodes: [0],\n repeatNodeCount: 3,\n tokenData: \"3P~RsX^#`pq#`rs$Tst%qwx&ixy(Qyz(V|}([}!O(a!O!P*Q!Q![(j![!]*V!^!_*[!_!`*g!c!}*l!}#O+W#P#Q+]#R#S*l#T#Y*l#Y#Z+b#Z#b*l#b#c.q#c#h*l#h#i1c#i#o*l#o#p2u#q#r2z#y#z#`$f$g#`#BY#BZ#`$IS$I_#`$I|$JO#`$JT$JU#`$KV$KW#`&FU&FV#`~#eYy~X^#`pq#`#y#z#`$f$g#`#BY#BZ#`$IS$I_#`$I|$JO#`$JT$JU#`$KV$KW#`&FU&FV#`~$WVOr$Trs$ms#O$T#O#P$r#P;'S$T;'S;=`%k<%lO$T~$rOZ~~$uRO;'S$T;'S;=`%O;=`O$T~%RWOr$Trs$ms#O$T#O#P$r#P;'S$T;'S;=`%k;=`<%l$T<%lO$T~%nP;=`<%l$T~%tT}!O&T!Q![&T!c!}&T#R#S&T#T#o&T~&YTX~}!O&T!Q![&T!c!}&T#R#S&T#T#o&T~&lVOw&iwx$mx#O&i#O#P'R#P;'S&i;'S;=`'z<%lO&i~'URO;'S&i;'S;=`'_;=`O&i~'bWOw&iwx$mx#O&i#O#P'R#P;'S&i;'S;=`'z;=`<%l&i<%lO&i~'}P;=`<%l&i~(VOT~~([OU~~(aOi~~(dQ!Q![(j!`!a){~(oSc~!O!P({!Q![(j!g!h)a#X#Y)a~)OP!Q![)R~)WRc~!Q![)R!g!h)a#X#Y)a~)dR{|)m}!O)m!Q![)s~)pP!Q![)s~)xPc~!Q![)s~*QOr~~*VOa~~*[O^~~*_P}!O*b~*gOs~~*lOS~P*qVRP}!O*l!O!P*l!P!Q*l!Q![*l!c!}*l#R#S*l#T#o*l~+]Ol~~+bOm~R+gWRP}!O*l!O!P*l!P!Q*l!Q![*l!c!}*l#R#S*l#T#U,P#U#o*lR,UXRP}!O*l!O!P*l!P!Q*l!Q![*l!c!}*l#R#S*l#T#`*l#`#a,q#a#o*lR,vXRP}!O*l!O!P*l!P!Q*l!Q![*l!c!}*l#R#S*l#T#g*l#g#h-c#h#o*lR-hXRP}!O*l!O!P*l!P!Q*l!Q![*l!c!}*l#R#S*l#T#X*l#X#Y.T#Y#o*lR.[VdQRP}!O*l!O!P*l!P!Q*l!Q![*l!c!}*l#R#S*l#T#o*lR.vXRP}!O*l!O!P*l!P!Q*l!Q![*l!c!}*l#R#S*l#T#i*l#i#j/c#j#o*lR/hXRP}!O*l!O!P*l!P!Q*l!Q![*l!c!}*l#R#S*l#T#`*l#`#a0T#a#o*lR0YXRP}!O*l!O!P*l!P!Q*l!Q![*l!c!}*l#R#S*l#T#`*l#`#a0u#a#o*lR0|VRPeQ}!O*l!O!P*l!P!Q*l!Q![*l!c!}*l#R#S*l#T#o*lR1hXRP}!O*l!O!P*l!P!Q*l!Q![*l!c!}*l#R#S*l#T#f*l#f#g2T#g#o*lR2YXRP}!O*l!O!P*l!P!Q*l!Q![*l!c!}*l#R#S*l#T#i*l#i#j-c#j#o*l~2zOg~~3POj~\",\n tokenizers: [0, 1],\n topRules: {\"Query\":[0,1]},\n specialized: [{term: 3, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}],\n tokenPrec: 0\n})\n","// This file was generated by lezer-generator. You probably shouldn't edit it.\nexport const\n Query = 1,\n Assignment = 2,\n Identifier = 3,\n Filter = 7,\n TagFilter = 8,\n Tag = 9,\n TextFilter = 10,\n String = 11,\n TypeFilter = 12,\n TypeKeyword = 13,\n PropertyFilter = 15,\n PropertyPath = 16,\n Value = 18,\n Number = 19,\n Boolean = 20,\n Null = 21,\n ObjectLiteral = 22,\n ObjectProperty = 24,\n ArrayLiteral = 27,\n Not = 30,\n And = 31,\n Or = 32,\n Relation = 33,\n ArrowRight = 34,\n ArrowLeft = 35\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { LRParser } from '@lezer/lr';\nimport { parser } from './query.ts';\nimport * as terms from './query.terms.ts';\n\nexport namespace QueryDSL {\n export const Parser: LRParser = parser;\n export const Node = terms;\n export const Tokens = ['type:', 'AND', 'OR', 'NOT'];\n}\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { type Parser, type Tree, type TreeCursor } from '@lezer/common';\n\nimport { Filter, type Tag } from '@dxos/echo';\nimport { invariant } from '@dxos/invariant';\nimport { type URI } from '@dxos/keys';\n\nimport { QueryDSL } from './gen/index.ts';\n\n// TODO(burdon): Return Query AST.\nexport type BuildResult = { filter?: Filter.Any; name?: string };\n\n/**\n * The `#tag` token for a tag label. The grammar's `Tag` token admits only `[a-zA-Z0-9_-]`, so every\n * other run of characters becomes one `-` — otherwise `#Needs reply` parses as tag `needs` plus text.\n */\nexport const formatTag = (label: string): string =>\n '#' +\n label\n .trim()\n .replace(/[^a-zA-Z0-9_-]+/g, '-')\n .replace(/^-+|-+$/g, '');\n\n/** The key and tag a `#tag` token names, matched against each label's {@link formatTag} form. */\nexport const findTagByToken = (\n tags: Tag.Map | undefined,\n token: string,\n): { key: string; tag: Tag.Map[string] } | undefined => {\n const normalized = token.toLowerCase();\n const entry = Object.entries(tags ?? {}).find(([, tag]) => formatTag(tag.label).toLowerCase() === normalized);\n return entry && { key: entry[0], tag: entry[1] };\n};\n\n/**\n * Stateless query builder that parses DSL trees into filters.\n *\n * NOTE: QueryBuilder was largely developed using Claude Sonnet 4.5 (in Windsurf)..\n * To modify the functionality, create a minimal breaking test and direct the LLM to fix either the grammar or builder.\n */\nexport class QueryBuilder {\n private readonly _parser: Parser = QueryDSL.Parser.configure({ strict: true });\n\n constructor(private readonly _tags?: Tag.Map) {}\n\n /**\n * Check valid input.\n */\n validate(input: string): boolean {\n try {\n const tree = this._parser.parse(normalizeInput(input));\n return tree.cursor().node.name === 'Query';\n } catch {\n return false;\n }\n }\n\n /**\n * Build a query from the input string.\n */\n build(input: string): BuildResult {\n try {\n const normalized = normalizeInput(input);\n const tree = this._parser.parse(normalized);\n return this.buildQuery(tree, normalized);\n } catch {\n return {};\n }\n }\n\n /**\n * Build a query from a parsed DSL tree.\n */\n buildQuery(tree: Tree, input: string): BuildResult {\n const cursor = tree.cursor();\n\n // Start at root (Query node).\n if (cursor.node.name !== 'Query') {\n return {};\n }\n\n // Check if Query has multiple children (binary expression).\n const children: Array<{ name: string; from: number; to: number }> = [];\n if (cursor.firstChild()) {\n do {\n children.push({ name: cursor.node.name, from: cursor.from, to: cursor.to });\n } while (cursor.nextSibling());\n cursor.parent();\n }\n\n // Check if this is an assignment.\n const hasAssignment = children.some((child) => child.name === 'Assignment');\n if (hasAssignment) {\n return this._parseAssignment(cursor, input);\n }\n\n // If we have an operator in the children, or multiple expressions (implicit AND), parse as binary expression.\n const hasOperator = children.some((child) => child.name === 'And' || child.name === 'Or');\n const hasMultipleExpressions =\n children.filter((child) => child.name === 'Filter' || child.name === 'Not' || child.name === '(').length > 1;\n if (hasOperator || hasMultipleExpressions) {\n const filter = this._parseBinaryExpression(cursor, input);\n return { filter };\n }\n\n // Otherwise, parse the single expression.\n if (!cursor.firstChild()) {\n return { filter: Filter.nothing() };\n }\n\n const filter = this._parseExpression(cursor, input);\n return { filter };\n }\n\n /**\n * Parse an assignment node.\n */\n private _parseAssignment(cursor: TreeCursor, input: string): BuildResult {\n if (!cursor.firstChild()) {\n return {};\n }\n\n let name: string | undefined;\n let filter: Filter.Any | undefined;\n\n // Find the Assignment node\n do {\n if (cursor.node.name === 'Assignment') {\n // Get the full assignment text first\n const assignmentText = this._getNodeText(cursor, input);\n\n if (cursor.firstChild()) {\n // First child should be the variable name (Identifier)\n name = this._getNodeText(cursor, input);\n\n // Find the parentheses in the assignment text and extract the content\n const openParenIndex = assignmentText.indexOf('(');\n const closeParenIndex = assignmentText.lastIndexOf(')');\n\n if (openParenIndex !== -1 && closeParenIndex !== -1 && closeParenIndex > openParenIndex) {\n const subInput = assignmentText.slice(openParenIndex + 1, closeParenIndex).trim();\n const subTree = this._parser.parse(subInput);\n const subResult = this.buildQuery(subTree, subInput);\n filter = subResult.filter;\n }\n\n cursor.parent(); // Back to Assignment\n }\n break;\n }\n } while (cursor.nextSibling());\n\n cursor.parent(); // Back to Query\n return { filter, name };\n }\n\n /**\n * Parse an expression node.\n */\n private _parseExpression(cursor: TreeCursor, input: string): Filter.Any | undefined {\n const nodeName = cursor.node.name;\n\n switch (nodeName) {\n case 'Filter':\n return this._parseFilter(cursor, input);\n\n case 'Not': {\n // Move past NOT token to the expression.\n cursor.nextSibling();\n const notFilter = this._parseExpression(cursor, input);\n return notFilter ? Filter.not(notFilter) : undefined;\n }\n\n case 'And':\n case 'Or':\n // This is the operator node, we need to handle the binary expression.\n // The cursor is positioned at the operator, we need to go back to parent.\n cursor.parent();\n return this._parseBinaryExpression(cursor, input);\n\n case '(': {\n // Skip opening paren.\n cursor.nextSibling();\n const parenFilter = this._parseExpression(cursor, input);\n // Skip closing paren.\n cursor.nextSibling();\n return parenFilter;\n }\n\n default: {\n // Check if this is a binary expression (has And/Or as a child).\n const savedPos = cursor.from;\n if (cursor.firstChild()) {\n // Look for And/Or operators or multiple expressions (implicit AND).\n let hasOperator = false;\n let expressionCount = 0;\n do {\n if (cursor.node.name === 'And' || cursor.node.name === 'Or') {\n hasOperator = true;\n break;\n }\n if (cursor.node.name === 'Filter' || cursor.node.name === 'Not' || cursor.node.name === '(') {\n expressionCount++;\n }\n } while (cursor.nextSibling());\n hasOperator = hasOperator || expressionCount > 1;\n\n // Reset cursor to the saved position.\n cursor.parent();\n cursor.firstChild();\n while (cursor.from !== savedPos && cursor.nextSibling()) {}\n\n if (hasOperator) {\n cursor.parent();\n return this._parseBinaryExpression(cursor, input);\n } else {\n const result = this._parseExpression(cursor, input);\n cursor.parent();\n return result;\n }\n }\n return Filter.nothing();\n }\n }\n }\n\n /**\n * Parse a binary expression (AND/OR).\n */\n private _parseBinaryExpression(cursor: TreeCursor, input: string): Filter.Any {\n const filters: Filter.Any[] = [];\n let operator: 'and' | 'or' | null = null;\n\n // Collect all filters and operators.\n if (cursor.firstChild()) {\n do {\n const nodeName = cursor.node.name;\n\n if (nodeName === 'And' || nodeName === 'Or') {\n operator = nodeName.toLowerCase() as 'and' | 'or';\n } else if (nodeName === '(') {\n // Handle parenthesized expression.\n // Look ahead to see if this is a binary expression.\n const savedPos = cursor.from;\n cursor.nextSibling(); // Move past '('\n\n // Check if the parentheses contain a binary expression.\n let hasBinaryOp = false;\n do {\n if (cursor.node.name === 'And' || cursor.node.name === 'Or') {\n hasBinaryOp = true;\n break;\n }\n } while (cursor.nextSibling() && cursor.node.name !== ')');\n\n // Reset cursor to start of parenthesized content.\n cursor.parent();\n cursor.firstChild();\n while (cursor.from !== savedPos && cursor.nextSibling()) {}\n cursor.nextSibling(); // Move past '(' again.\n\n if (hasBinaryOp) {\n // Find the matching closing parenthesis.\n let depth = 1;\n const exprStart = cursor.from;\n let exprEnd = cursor.to;\n\n while (cursor.nextSibling() && depth > 0) {\n if (cursor.node.name === '(') {\n depth++;\n } else if (cursor.node.name === ')') {\n depth--;\n if (depth === 0) {\n exprEnd = cursor.from;\n }\n }\n }\n\n // Parse the expression inside parentheses as a subtree.\n const subInput = input.slice(exprStart, exprEnd);\n const subTree = this._parser.parse(subInput);\n const subResult = this.buildQuery(subTree, subInput);\n if (subResult.filter) {\n filters.push(subResult.filter);\n }\n } else {\n // Simple parenthesized expression.\n const subFilter = this._parseExpression(cursor, input);\n if (subFilter) {\n filters.push(subFilter);\n }\n\n // Skip until we find the closing parenthesis.\n while (cursor.nextSibling() && cursor.node.name !== ')') {}\n }\n } else if (nodeName !== ')') {\n const subFilter = this._parseExpression(cursor, input);\n if (subFilter) {\n filters.push(subFilter);\n }\n }\n } while (cursor.nextSibling());\n\n cursor.parent();\n }\n\n if (filters.length === 0) {\n return Filter.nothing();\n }\n\n if (filters.length === 1) {\n return filters[0];\n }\n\n return operator === 'or' ? Filter.or(...filters) : Filter.and(...filters);\n }\n\n /**\n * Parse a Filter node.\n */\n private _parseFilter(cursor: TreeCursor, input: string): Filter.Any | undefined {\n if (!cursor.firstChild()) {\n return Filter.nothing();\n }\n\n let result: Filter.Any | undefined = undefined;\n const filterType = cursor.node.name;\n switch (filterType) {\n case 'TagFilter':\n if (this._tags) {\n result = this._parseTagFilter(cursor, input);\n }\n break;\n\n case 'TextFilter':\n result = this._parseTextFilter(cursor, input);\n break;\n\n case 'TypeFilter':\n result = this._parseTypeFilter(cursor, input);\n break;\n\n case 'ObjectLiteral':\n result = this._parseObjectLiteral(cursor, input);\n break;\n\n case 'PropertyFilter':\n result = this._parsePropertyFilter(cursor, input);\n break;\n\n default:\n result = Filter.nothing();\n }\n\n cursor.parent();\n return result;\n }\n\n /**\n * Parse a TypeFilter node (type:typename).\n */\n private _parseTypeFilter(cursor: TreeCursor, input: string): Filter.Any {\n // Skip TypeKeyword.\n cursor.firstChild();\n cursor.nextSibling(); // Skip ':'\n cursor.nextSibling(); // Move to Identifier\n\n const typename = this._getNodeText(cursor, input);\n cursor.parent(); // Go back to TypeFilter.\n // Callers may pass a bare typename (e.g. `com.example.task`) or a canonical URI\n // (`dxn:…` / `echo:…`); prepend `dxn:` only for bare names, detected by the absence of a\n // URI scheme.\n const uri = (/^[a-z][a-z0-9+.-]*:/i.test(typename) ? typename : `dxn:${typename}`) as URI.URI;\n return Filter.type(uri);\n }\n\n /**\n * Parse a TextFilter node (quoted string).\n */\n private _parseTextFilter(cursor: TreeCursor, input: string): Filter.Any {\n cursor.firstChild(); // Move to String node.\n const text = this._getNodeText(cursor, input);\n cursor.parent(); // Go back to TextFilter.\n // Remove quotes and decode escapes.\n return Filter.text(unescapeStringLiteral(text.slice(1, -1)));\n }\n\n /**\n * Parse an ObjectLiteral node.\n */\n private _parseObjectLiteral(cursor: TreeCursor, input: string): Filter.Any {\n const props: Record<string, any> = {};\n\n if (cursor.firstChild()) {\n do {\n if (cursor.node.name === 'ObjectProperty') {\n const { key, value } = this._parseObjectProperty(cursor, input);\n if (key) {\n // Convert simple values to Filter.eq for compatibility with Filter.props.\n props[key] = Filter.eq(value);\n }\n }\n } while (cursor.nextSibling());\n\n cursor.parent();\n }\n\n return Filter.props(props);\n }\n\n /**\n * Parse an ObjectProperty node.\n */\n private _parseObjectProperty(cursor: TreeCursor, input: string): { key: string | null; value: any } {\n let key: string | null = null;\n let value: any = null;\n\n if (cursor.firstChild()) {\n // First child should be the property name (Identifier).\n if (cursor.node.name === 'Identifier') {\n key = this._getNodeText(cursor, input);\n }\n\n // Skip ':' and move to Value.\n cursor.nextSibling();\n cursor.nextSibling();\n\n if (cursor.node.name === 'Value' && cursor.firstChild()) {\n value = this._parseValue(cursor, input);\n cursor.parent();\n }\n\n cursor.parent();\n }\n\n return { key, value };\n }\n\n /**\n * Parse a PropertyFilter node (property:value).\n */\n private _parsePropertyFilter(cursor: TreeCursor, input: string): Filter.Any {\n let path: string | null = null;\n let value: any = null;\n\n if (cursor.firstChild()) {\n // First child is PropertyPath.\n if (cursor.node.name === 'PropertyPath') {\n path = this._parsePropertyPath(cursor, input);\n }\n\n // Skip ':' and move to Value.\n cursor.nextSibling();\n cursor.nextSibling();\n\n if (cursor.node.name === 'Value' && cursor.firstChild()) {\n value = this._parseValue(cursor, input);\n cursor.parent();\n }\n\n cursor.parent();\n }\n\n if (!path) {\n return Filter.nothing();\n }\n\n return Filter.props({ [path]: value });\n }\n\n /**\n * Parse a PropertyPath node (supports dot notation).\n */\n private _parsePropertyPath(cursor: TreeCursor, input: string): string {\n const parts: string[] = [];\n\n if (cursor.firstChild()) {\n do {\n if (cursor.node.name === 'Identifier') {\n parts.push(this._getNodeText(cursor, input));\n }\n } while (cursor.nextSibling());\n\n cursor.parent();\n }\n\n return parts.join('.');\n }\n\n /**\n * Parse a TagFilter node (#tag).\n */\n private _parseTagFilter(cursor: TreeCursor, input: string): Filter.Any | undefined {\n invariant(this._tags);\n const match = findTagByToken(this._tags, this._getNodeText(cursor, input));\n return match ? Filter.tag(match.key) : undefined;\n }\n\n /**\n * Parse a Value node.\n */\n private _parseValue(cursor: TreeCursor, input: string): any {\n const valueType = cursor.node.name;\n\n switch (valueType) {\n case 'String': {\n // Remove quotes and decode escapes.\n const str = this._getNodeText(cursor, input);\n return unescapeStringLiteral(str.slice(1, -1));\n }\n\n case 'Number':\n return Number(this._getNodeText(cursor, input));\n\n case 'Boolean':\n return this._getNodeText(cursor, input) === 'true';\n\n case 'Null':\n return null;\n\n case 'ObjectLiteral': {\n // For nested objects, parse recursively.\n const props: Record<string, any> = {};\n if (cursor.firstChild()) {\n do {\n if (cursor.node.name === 'ObjectProperty') {\n const { key, value } = this._parseObjectProperty(cursor, input);\n if (key) {\n props[key] = value;\n }\n }\n } while (cursor.nextSibling());\n cursor.parent();\n }\n return props;\n }\n\n case 'ArrayLiteral': {\n // Parse array values\n const array: any[] = [];\n if (cursor.firstChild()) {\n do {\n if (cursor.node.name === 'Value' && cursor.firstChild()) {\n array.push(this._parseValue(cursor, input));\n cursor.parent();\n }\n } while (cursor.nextSibling());\n cursor.parent();\n }\n return array;\n }\n\n default:\n return null;\n }\n }\n\n /**\n * Get the text content of the current node.\n */\n private _getNodeText(cursor: TreeCursor, input: string): string {\n return input.slice(cursor.from, cursor.to);\n }\n}\n\nconst KEYWORDS = new Set(['AND', 'OR', 'NOT']);\nconst VALUE_LITERALS = new Set(['true', 'false', 'null']);\nconst SPECIAL_CHARS = /[\\s(){}[\\],\"']/;\nconst PROPERTY_KEY = /^[a-zA-Z_][a-zA-Z0-9_.]*$/;\nconst NUMBER_LITERAL = /^-?\\d+(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$/;\n\n// Common URL schemes — `http://...`, `mailto:rich@...` etc. should be searched as text,\n// not auto-promoted to property filters.\nconst URL_SCHEMES = new Set([\n 'http',\n 'https',\n 'ftp',\n 'ftps',\n 'file',\n 'mailto',\n 'tel',\n 'sms',\n 'data',\n 'javascript',\n 'ws',\n 'wss',\n 'ssh',\n 'git',\n]);\n\n/**\n * Normalize raw user input into a form the lezer grammar can parse.\n * - Bare text fragments (e.g. `foo`) are wrapped in quotes so they parse as TextFilter.\n * - Property values that aren't already quoted/numeric/boolean (e.g. `from:rich@dxos.org`) are quoted.\n * - Tags, type filters, operators, parens, braces, quoted strings, and assignments pass through unchanged.\n */\nexport const normalizeInput = (input: string): string => {\n const out: string[] = [];\n let pos = 0;\n while (pos < input.length) {\n const currentChar = input[pos];\n\n // Whitespace.\n if (/\\s/.test(currentChar)) {\n out.push(currentChar);\n pos++;\n continue;\n }\n\n // Quoted string (already a valid TextFilter / Value).\n // Single-quote only opens a string if the previous char isn't a word-char,\n // so apostrophes inside barewords (e.g. `don't`, `O'Connor`) stay attached.\n const prevChar = pos > 0 ? input[pos - 1] : '';\n const isStringOpener =\n currentChar === '\"' || (currentChar === \"'\" && (pos === 0 || !/[a-zA-Z0-9_]/.test(prevChar)));\n if (isStringOpener) {\n const quoteChar = currentChar;\n let scanIndex = pos + 1;\n while (scanIndex < input.length && input[scanIndex] !== quoteChar) {\n if (input[scanIndex] === '\\\\' && scanIndex + 1 < input.length) {\n scanIndex += 2;\n } else {\n scanIndex++;\n }\n }\n out.push(input.slice(pos, Math.min(scanIndex + 1, input.length)));\n pos = scanIndex + 1;\n continue;\n }\n\n // Object/array literals: pass through (contents already structured).\n if (currentChar === '{' || currentChar === '[') {\n const closeChar = currentChar === '{' ? '}' : ']';\n let depth = 1;\n let scanIndex = pos + 1;\n while (scanIndex < input.length && depth > 0) {\n const innerChar = input[scanIndex];\n if (innerChar === '\"' || innerChar === \"'\") {\n const quoteChar = innerChar;\n scanIndex++;\n while (scanIndex < input.length && input[scanIndex] !== quoteChar) {\n if (input[scanIndex] === '\\\\' && scanIndex + 1 < input.length) {\n scanIndex += 2;\n } else {\n scanIndex++;\n }\n }\n scanIndex++;\n } else if (innerChar === currentChar) {\n depth++;\n scanIndex++;\n } else if (innerChar === closeChar) {\n depth--;\n scanIndex++;\n } else {\n scanIndex++;\n }\n }\n out.push(input.slice(pos, scanIndex));\n pos = scanIndex;\n continue;\n }\n\n // Single-char structural tokens. `}` / `]` only reach here when unmatched —\n // pass them through so the lezer parser can produce a clear error rather than spinning.\n if (\n currentChar === '(' ||\n currentChar === ')' ||\n currentChar === '=' ||\n currentChar === ',' ||\n currentChar === '}' ||\n currentChar === ']'\n ) {\n out.push(currentChar);\n pos++;\n continue;\n }\n\n // Relations.\n if ((currentChar === '-' && input[pos + 1] === '>') || (currentChar === '<' && input[pos + 1] === '-')) {\n out.push(input.slice(pos, pos + 2));\n pos += 2;\n continue;\n }\n\n // NOT prefix (`!`) — single token.\n if (currentChar === '!') {\n out.push(currentChar);\n pos++;\n continue;\n }\n\n // Tag.\n if (currentChar === '#') {\n let scanIndex = pos + 1;\n while (scanIndex < input.length && /[a-zA-Z0-9_-]/.test(input[scanIndex])) {\n scanIndex++;\n }\n out.push(input.slice(pos, scanIndex));\n pos = scanIndex;\n continue;\n }\n\n // Bareword: scan until next whitespace or special char.\n let scanIndex = pos;\n while (scanIndex < input.length) {\n const innerChar = input[scanIndex];\n if (innerChar === '\"') {\n break;\n }\n if (innerChar === \"'\" && scanIndex > pos && !/[a-zA-Z0-9_]/.test(input[scanIndex - 1])) {\n break;\n }\n if (innerChar === \"'\" && scanIndex === pos) {\n break;\n }\n if (innerChar !== \"'\" && SPECIAL_CHARS.test(innerChar)) {\n break;\n }\n if (innerChar === '-' && input[scanIndex + 1] === '>') {\n break;\n }\n if (innerChar === '<' && input[scanIndex + 1] === '-') {\n break;\n }\n scanIndex++;\n }\n // Defensive: if no characters were consumed, advance one to avoid infinite loops.\n if (scanIndex === pos) {\n out.push(currentChar);\n pos++;\n continue;\n }\n const token = input.slice(pos, scanIndex);\n pos = scanIndex;\n\n // Operators.\n if (KEYWORDS.has(token.toUpperCase())) {\n out.push(token);\n continue;\n }\n\n // Property/type filter (`key:value`).\n const colonIdx = token.indexOf(':');\n if (colonIdx > 0) {\n const key = token.slice(0, colonIdx);\n const rest = token.slice(colonIdx + 1);\n\n // type:typename — leave for grammar's TypeFilter (Identifier value).\n if (key === 'type') {\n out.push(token);\n continue;\n }\n\n // URLs (`http://...`, `mailto:rich@...`) and URL-paths starting with `//`\n // are searched as text rather than auto-promoted to property filters.\n const isUrlScheme = URL_SCHEMES.has(key.toLowerCase()) || rest.startsWith('//');\n if (!isUrlScheme && PROPERTY_KEY.test(key)) {\n if (rest.length === 0) {\n // Trailing colon while typing — pass through.\n out.push(token);\n continue;\n }\n const firstValueChar = rest[0];\n if (firstValueChar === '\"' || firstValueChar === \"'\") {\n // Already quoted.\n out.push(token);\n continue;\n }\n if (firstValueChar === '{' || firstValueChar === '[') {\n // Object/array literal value.\n out.push(token);\n continue;\n }\n if (VALUE_LITERALS.has(rest) || NUMBER_LITERAL.test(rest)) {\n // Boolean / null / number literal.\n out.push(token);\n continue;\n }\n out.push(`${key}:\"${escapeStringLiteral(rest)}\"`);\n continue;\n }\n\n // URL or unknown key shape — fall through to text.\n }\n\n // Identifier followed by `=` is the LHS of an Assignment — keep as-is.\n if (PROPERTY_KEY.test(token)) {\n let lookahead = pos;\n while (lookahead < input.length && /\\s/.test(input[lookahead])) {\n lookahead++;\n }\n if (input[lookahead] === '=') {\n out.push(token);\n continue;\n }\n }\n\n // Bare text fragment: quote so it parses as TextFilter.\n out.push(`\"${escapeStringLiteral(token)}\"`);\n }\n\n return out.join('');\n};\n\n/** Escape a raw value into a string literal body (without surrounding quotes). */\nconst escapeStringLiteral = (value: string): string => value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n\n/** Decode `\\\\` and `\\\"` escapes inside a string literal body. */\nconst unescapeStringLiteral = (literalBody: string): string => {\n let out = '';\n for (let i = 0; i < literalBody.length; i++) {\n const ch = literalBody[i];\n if (ch === '\\\\' && i + 1 < literalBody.length) {\n out += literalBody[i + 1];\n i++;\n } else {\n out += ch;\n }\n }\n return out;\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** A property that takes one of a closed set of values — a task's `status`, a message's `folder`. */
|
|
2
|
+
export type EnumProperty<T extends string> = {
|
|
3
|
+
/** The property name as a term writes it (`status` in `status:done`). */
|
|
4
|
+
property: string;
|
|
5
|
+
/** Every value, in the order a written term lists them. */
|
|
6
|
+
values: readonly T[];
|
|
7
|
+
};
|
|
8
|
+
export type EnumTerms<T extends string> = {
|
|
9
|
+
/** The values the query's terms on the property keep; absent when the query names none. */
|
|
10
|
+
values?: readonly T[];
|
|
11
|
+
/** The query with those terms taken out. */
|
|
12
|
+
rest: string;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Splits the terms on one enumerated property out of a query, so a picker for that property and the
|
|
16
|
+
* query text can be two views over one string.
|
|
17
|
+
*
|
|
18
|
+
* Only top-level terms a picker could have written are read — `prop:x`, `NOT prop:x` (or `!prop:x`)
|
|
19
|
+
* and `(prop:a OR prop:b)` — and they are intersected, as the query's implicit AND intersects them.
|
|
20
|
+
* A query with a top-level `OR` is left whole: its terms are not all required, so no value set
|
|
21
|
+
* describes it. A value outside the set (`status:sta` while it is being typed) stays in the text,
|
|
22
|
+
* where it matches as the substring it is.
|
|
23
|
+
*/
|
|
24
|
+
export declare const parseEnumTerms: <T extends string>(text: string, { property, values }: EnumProperty<T>) => EnumTerms<T>;
|
|
25
|
+
/**
|
|
26
|
+
* The query with its terms on the property replaced by the one term that says `selected`, placed
|
|
27
|
+
* first.
|
|
28
|
+
*
|
|
29
|
+
* Written in whichever of the two forms is shorter — the values kept (`prop:a`, `(prop:a OR prop:b)`)
|
|
30
|
+
* or the values hidden (`NOT prop:x NOT prop:y`) — so hiding two of nine reads as that, not as a list
|
|
31
|
+
* of the seven left. Every value selected writes no term. A query with a top-level `OR` comes back
|
|
32
|
+
* unchanged: no term on the property describes it, so a written term could not be read back, and
|
|
33
|
+
* each write would stack another term in front of the last.
|
|
34
|
+
*/
|
|
35
|
+
export declare const writeEnumTerms: <T extends string>(text: string, spec: EnumProperty<T>, selected: readonly T[]) => string;
|
|
36
|
+
//# sourceMappingURL=enum-terms.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enum-terms.d.ts","sourceRoot":"","sources":["../../../../src/filter/enum-terms.ts"],"names":[],"mappings":"AAIA,qGAAqG;AACrG,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,MAAM,IAAI;IAC3C,yEAAyE;IACzE,QAAQ,EAAE,MAAM,CAAC;IACjB,2DAA2D;IAC3D,MAAM,EAAE,SAAS,CAAC,EAAE,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,MAAM,IAAI;IACxC,2FAA2F;IAC3F,MAAM,CAAC,EAAE,SAAS,CAAC,EAAE,CAAC;IACtB,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAIF;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,SAAS,MAAM,QAAQ,MAAM,wBAAwB,YAAY,CAAC,CAAC,CAAC,KAAG,SAAS,CAAC,CAAC,CAsEjH,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,SAAS,MAAM,QACvC,MAAM,QACN,YAAY,CAAC,CAAC,CAAC,YACX,SAAS,CAAC,EAAE,KACrB,MAkBF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"enum-terms.test.d.ts","sourceRoot":"","sources":["../../../../src/filter/enum-terms.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/filter/index.ts"],"names":[],"mappings":"AAIA,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type Filter, Obj } from '@dxos/echo';
|
|
2
|
+
export type MatchFilterOptions<T extends Obj.Unknown> = {
|
|
3
|
+
/**
|
|
4
|
+
* The strings a free-text term searches. Defaults to the object's top-level string properties;
|
|
5
|
+
* a host narrows it to what a reader sees (a task's title and description, say).
|
|
6
|
+
*/
|
|
7
|
+
text?: (object: T) => readonly (string | undefined)[];
|
|
8
|
+
/** The object's tag ids — the uri space `#tag` terms carry. Defaults to `Obj.getMeta(object).tags`. */
|
|
9
|
+
tags?: (object: T) => readonly string[];
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Evaluates a filter the {@link QueryBuilder} built against one object in memory.
|
|
13
|
+
*
|
|
14
|
+
* For a host that cannot hand the filter to a query: one that renders a tree and must keep the
|
|
15
|
+
* ancestors of a match, or reads a feed the query engine does not index. Semantics follow what a
|
|
16
|
+
* reader typed rather than the query engine's equality: a string property matches as a
|
|
17
|
+
* case-insensitive substring (`status:start` finds `started`), an array on any element, and an
|
|
18
|
+
* object on any field (`assignee:rich` finds an actor's name or email). An unknown node fails
|
|
19
|
+
* closed, so a filter the evaluator does not understand never broadens the result.
|
|
20
|
+
*/
|
|
21
|
+
export declare const matchesFilter: <T extends Obj.Unknown>(filter: Filter.Any, object: T, options?: MatchFilterOptions<T>) => boolean;
|
|
22
|
+
//# sourceMappingURL=match-filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"match-filter.d.ts","sourceRoot":"","sources":["../../../../src/filter/match-filter.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,MAAM,EAAE,GAAG,EAAiB,MAAM,YAAY,CAAC;AAE7D,MAAM,MAAM,kBAAkB,CAAC,CAAC,SAAS,GAAG,CAAC,OAAO,IAAI;IACtD;;;OAGG;IACH,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAAC;IACtD,uGAAuG;IACvG,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,SAAS,MAAM,EAAE,CAAC;CACzC,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa,GAAI,CAAC,SAAS,GAAG,CAAC,OAAO,UACzC,MAAM,CAAC,GAAG,UACV,CAAC,YACA,kBAAkB,CAAC,CAAC,CAAC,KAC7B,OAAkD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"match-filter.test.d.ts","sourceRoot":"","sources":["../../../../src/filter/match-filter.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC"}
|