@dalgo/dalgo2typesense 0.0.0-bootstrap.0 → 0.2.1

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,159 @@
1
+ import { TypesenseHttpError, TypesenseRequestError, TypesenseResponseSizeError } from "./errors.js";
2
+ import { isObject } from "./transport.js";
3
+ const FIELD_TYPE = /^[A-Za-z][A-Za-z0-9[\]_]*$/u;
4
+ function failureFor(error) {
5
+ if (error instanceof TypesenseHttpError) {
6
+ return {
7
+ reason: error.status === 401 ? "unauthorized" : error.status === 403 ? "forbidden" : error.status === 404 ? "not-found" : "unavailable",
8
+ httpStatus: error.status,
9
+ };
10
+ }
11
+ if (error instanceof TypesenseRequestError || error instanceof TypesenseResponseSizeError)
12
+ return { reason: "unavailable" };
13
+ if (error instanceof TypeError)
14
+ return { reason: "malformed" };
15
+ return { reason: "unavailable" };
16
+ }
17
+ export function metadataFailure(error) {
18
+ return failureFor(error);
19
+ }
20
+ export function supportedDeclaredType(type) {
21
+ return type === "string" || type === "string[]"
22
+ || type === "int32" || type === "int32[]"
23
+ || type === "int64" || type === "int64[]"
24
+ || type === "float" || type === "float[]"
25
+ || type === "bool" || type === "bool[]"
26
+ || type === "object" || type === "object[]"
27
+ || type === "geopoint" || type === "geopoint[]"
28
+ || type === "geopolygon" || type === "auto";
29
+ }
30
+ function requiredString(value, label, maximum = 1_024) {
31
+ if (typeof value !== "string" || value.length === 0 || value.length > maximum || !value.isWellFormed()) {
32
+ throw new TypeError(`malformed Typesense ${label}`);
33
+ }
34
+ for (let index = 0; index < value.length; index += 1) {
35
+ const code = value.charCodeAt(index);
36
+ if (code < 0x20 || code === 0x7f)
37
+ throw new TypeError(`malformed Typesense ${label}`);
38
+ }
39
+ return value;
40
+ }
41
+ function optionalBoolean(value, label, fallback) {
42
+ if (value === undefined)
43
+ return fallback;
44
+ if (typeof value !== "boolean")
45
+ throw new TypeError(`malformed Typesense ${label}`);
46
+ return value;
47
+ }
48
+ /**
49
+ * The official 30.2 `Collection.get_summary_json` always includes
50
+ * `default_sorting_field`, and a collection created without one carries the
51
+ * empty string. An empty sentinel is valid; a non-string, non-well-formed or
52
+ * control-bearing value is still malformed.
53
+ */
54
+ function defaultSortingField(value) {
55
+ if (value === undefined)
56
+ return undefined;
57
+ if (typeof value !== "string" || value.length > 256 || !value.isWellFormed()) {
58
+ throw new TypeError("malformed Typesense default_sorting_field");
59
+ }
60
+ for (let index = 0; index < value.length; index += 1) {
61
+ const code = value.charCodeAt(index);
62
+ if (code < 0x20 || code === 0x7f)
63
+ throw new TypeError("malformed Typesense default_sorting_field");
64
+ }
65
+ return value;
66
+ }
67
+ function optionalNumber(value, label) {
68
+ if (value === undefined)
69
+ return undefined;
70
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
71
+ throw new TypeError(`malformed Typesense ${label}`);
72
+ }
73
+ return value;
74
+ }
75
+ /** Parses the real collection schema. Arrays keep their declared native type. */
76
+ export function parseCollectionSchema(value, expectedName, maxFields) {
77
+ if (!isObject(value))
78
+ throw new TypeError("malformed Typesense collection schema");
79
+ const name = requiredString(value.name, "collection name", 256);
80
+ if (name !== expectedName)
81
+ throw new TypeError("Typesense collection schema name does not match the configured collection");
82
+ if (!Array.isArray(value.fields))
83
+ throw new TypeError("malformed Typesense collection fields");
84
+ if (value.fields.length > maxFields)
85
+ throw new RangeError("Typesense collection fields exceed maxColumns");
86
+ const seen = new Set();
87
+ const fields = [];
88
+ for (const raw of value.fields) {
89
+ if (!isObject(raw))
90
+ throw new TypeError("malformed Typesense collection field");
91
+ const fieldName = requiredString(raw.name, "field name", 256);
92
+ if (fieldName.startsWith("$") || /[,\t\r\n]/u.test(fieldName))
93
+ throw new TypeError("unsupported Typesense field name");
94
+ if (seen.has(fieldName))
95
+ throw new TypeError("duplicate Typesense field name");
96
+ seen.add(fieldName);
97
+ const type = requiredString(raw.type, "field type", 64);
98
+ if (!FIELD_TYPE.test(type) || !supportedDeclaredType(type))
99
+ throw new TypeError("unsupported Typesense field type");
100
+ if (fieldName === "id" && type !== "string")
101
+ throw new TypeError("explicit Typesense id field type must be string");
102
+ fields.push(Object.freeze({
103
+ name: fieldName,
104
+ type,
105
+ facet: optionalBoolean(raw.facet, "field facet", false),
106
+ // Typesense always returns the id string and assigns one when omitted, so
107
+ // an explicitly optional id is normalised to non-null. It is not invented
108
+ // twice when declared.
109
+ optional: fieldName === "id" ? false : optionalBoolean(raw.optional, "field optional", false),
110
+ index: optionalBoolean(raw.index, "field index", true),
111
+ sort: optionalBoolean(raw.sort, "field sort", false),
112
+ infix: optionalBoolean(raw.infix, "field infix", false),
113
+ }));
114
+ }
115
+ if (!seen.has("id")) {
116
+ fields.push(Object.freeze({
117
+ name: "id",
118
+ type: "string",
119
+ facet: false,
120
+ optional: false,
121
+ index: true,
122
+ sort: false,
123
+ infix: false,
124
+ }));
125
+ }
126
+ const defaultSortingFieldValue = defaultSortingField(value.default_sorting_field);
127
+ const numDocuments = optionalNumber(value.num_documents, "num_documents");
128
+ return Object.freeze({
129
+ name,
130
+ fields: Object.freeze(fields),
131
+ ...(defaultSortingFieldValue === undefined ? {} : { defaultSortingField: defaultSortingFieldValue }),
132
+ ...(numDocuments === undefined ? {} : { numDocuments }),
133
+ });
134
+ }
135
+ /** Parses `GET /collections` and never turns a denial into an empty list. */
136
+ export function parseCollectionList(value, maximum) {
137
+ if (!Array.isArray(value))
138
+ throw new TypeError("malformed Typesense collection list");
139
+ const truncated = value.length > maximum;
140
+ const names = [];
141
+ const seen = new Set();
142
+ for (const entry of value.slice(0, maximum)) {
143
+ if (!isObject(entry))
144
+ throw new TypeError("malformed Typesense collection entry");
145
+ const name = requiredString(entry.name, "collection name", 256);
146
+ if (seen.has(name))
147
+ throw new TypeError("duplicate Typesense collection name");
148
+ seen.add(name);
149
+ names.push(name);
150
+ }
151
+ return { collections: Object.freeze(names.sort()), truncated };
152
+ }
153
+ export function schemaFieldTypes(schema) {
154
+ const result = new Map();
155
+ for (const field of schema.fields)
156
+ result.set(field.name, field.type);
157
+ return result;
158
+ }
159
+ //# sourceMappingURL=metadata.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata.js","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ export interface TypesenseSort {
2
+ readonly field: string;
3
+ readonly direction?: "asc" | "desc";
4
+ }
5
+ /** One bounded, typed native Typesense search. No arbitrary option bag. */
6
+ export interface TypesenseNativeSearchRequest {
7
+ /** Native query text. `*` is the wildcard query. */
8
+ readonly q: string;
9
+ /** Full-text fields. Required unless `q` is `*`. */
10
+ readonly queryBy?: readonly string[];
11
+ /** Bounded native Typesense filter expression; negative filters are retained here. */
12
+ readonly filterBy?: string;
13
+ readonly sortBy?: readonly TypesenseSort[];
14
+ readonly facetBy?: readonly string[];
15
+ readonly maxFacetValues?: number;
16
+ readonly limit: number;
17
+ readonly offset?: number;
18
+ }
19
+ export interface TypesenseNativeSearchHit {
20
+ readonly id: string;
21
+ /** The complete native document, including its string id. */
22
+ readonly document: Readonly<Record<string, unknown>>;
23
+ /**
24
+ * The server's native full-text ranking score as its **exact decimal
25
+ * string**. Typesense serialises the uint64 `text_match` wrapper, which
26
+ * routinely exceeds Number.MAX_SAFE_INTEGER, so a JavaScript number would
27
+ * silently round the ranking. The string is never converted to a number and
28
+ * is cross-checked against `text_match_info.score` when the server sends it.
29
+ */
30
+ readonly textMatch?: string;
31
+ readonly curated: boolean;
32
+ }
33
+ export interface TypesenseFacetValue {
34
+ readonly value: string;
35
+ readonly count: number;
36
+ readonly highlighted?: string;
37
+ }
38
+ export interface TypesenseFacetStats {
39
+ readonly min?: number;
40
+ readonly max?: number;
41
+ readonly sum?: number;
42
+ readonly avg?: number;
43
+ readonly totalValues?: number;
44
+ }
45
+ export interface TypesenseFacet {
46
+ readonly field: string;
47
+ readonly counts: readonly TypesenseFacetValue[];
48
+ readonly stats?: TypesenseFacetStats;
49
+ }
50
+ export interface TypesenseNativeSearchResult {
51
+ readonly collection: string;
52
+ readonly hits: readonly TypesenseNativeSearchHit[];
53
+ readonly facets: readonly TypesenseFacet[];
54
+ readonly found: number;
55
+ readonly outOf: number;
56
+ readonly page: number;
57
+ readonly returned: number;
58
+ readonly searchTimeMs: number;
59
+ readonly partial: boolean;
60
+ readonly complete: boolean;
61
+ readonly truncated: boolean;
62
+ readonly provenance: "typesense-native-search";
63
+ }
64
+ export interface DecodedSearch {
65
+ readonly hits: readonly TypesenseNativeSearchHit[];
66
+ readonly facets: readonly TypesenseFacet[];
67
+ readonly found: number;
68
+ readonly outOf: number;
69
+ readonly page: number;
70
+ readonly searchTimeMs: number;
71
+ readonly cutoff: boolean;
72
+ }
73
+ export interface SearchDecodeOptions {
74
+ readonly limit: number;
75
+ readonly offset: number;
76
+ readonly facetFields: readonly string[];
77
+ readonly maxFacetValues: number;
78
+ readonly maxRowBytes: number;
79
+ }
80
+ /** Decodes one search response strictly. No array, id or count is inferred. */
81
+ export declare function decodeSearchResponse(value: unknown, options: SearchDecodeOptions): DecodedSearch;
82
+ export declare function buildNativeResult(collection: string, decoded: DecodedSearch, limit: number, offset: number): TypesenseNativeSearchResult;
package/dist/search.js ADDED
@@ -0,0 +1,238 @@
1
+ import { isExactUint64, isLosslessNumber } from "./json.js";
2
+ import { isObject, isPlainObject } from "./transport.js";
3
+ function requiredInteger(value, label, allowZero = true) {
4
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < (allowZero ? 0 : 1)) {
5
+ throw new TypeError(`malformed Typesense ${label}`);
6
+ }
7
+ return value;
8
+ }
9
+ function optionalInteger(value, label, allowZero = true) {
10
+ if (value === undefined)
11
+ return undefined;
12
+ return requiredInteger(value, label, allowZero);
13
+ }
14
+ function rowBytes(document, maximum) {
15
+ let text;
16
+ try {
17
+ text = JSON.stringify(document);
18
+ }
19
+ catch {
20
+ throw new TypeError("Typesense document is not JSON-encodable");
21
+ }
22
+ if (new TextEncoder().encode(text).byteLength > maximum) {
23
+ throw new RangeError("Typesense document exceeds maxRowBytes");
24
+ }
25
+ }
26
+ /**
27
+ * Reads the native score at its known protocol location. An unsafe uint64
28
+ * `text_match` token was already proven to be exactly this path by the raw
29
+ * scanner, so the parsed value is its exact decimal string. A safe integer is
30
+ * normalised to the same string representation. If the server also emits
31
+ * `text_match_info.score`, the two exact decimals must agree.
32
+ */
33
+ function nativeTextMatch(raw) {
34
+ let exact;
35
+ const value = raw.text_match;
36
+ if (value !== undefined) {
37
+ if (typeof value === "number") {
38
+ if (!Number.isSafeInteger(value) || value < 0)
39
+ throw new TypeError("malformed Typesense text_match");
40
+ exact = String(value);
41
+ }
42
+ else if (typeof value === "string") {
43
+ if (!isExactUint64(value))
44
+ throw new TypeError("malformed Typesense text_match");
45
+ exact = value;
46
+ }
47
+ else {
48
+ throw new TypeError("malformed Typesense text_match");
49
+ }
50
+ }
51
+ const info = raw.text_match_info;
52
+ if (info !== undefined) {
53
+ if (!isPlainObject(info))
54
+ throw new TypeError("malformed Typesense text_match_info");
55
+ const score = info.score;
56
+ if (score !== undefined) {
57
+ let counterpart;
58
+ if (typeof score === "number") {
59
+ if (!Number.isSafeInteger(score) || score < 0)
60
+ throw new TypeError("malformed Typesense text_match_info.score");
61
+ counterpart = String(score);
62
+ }
63
+ else if (typeof score === "string") {
64
+ if (!isExactUint64(score))
65
+ throw new TypeError("malformed Typesense text_match_info.score");
66
+ counterpart = score;
67
+ }
68
+ else {
69
+ throw new TypeError("malformed Typesense text_match_info.score");
70
+ }
71
+ if (exact === undefined)
72
+ exact = counterpart;
73
+ else if (exact !== counterpart)
74
+ throw new TypeError("Typesense text_match and text_match_info.score disagree");
75
+ }
76
+ }
77
+ return exact;
78
+ }
79
+ /** Decodes one search response strictly. No array, id or count is inferred. */
80
+ export function decodeSearchResponse(value, options) {
81
+ if (!isObject(value))
82
+ throw new TypeError("malformed Typesense search response");
83
+ const found = requiredInteger(value.found, "found");
84
+ const outOf = requiredInteger(value.out_of, "out_of");
85
+ const page = optionalInteger(value.page, "page") ?? 0;
86
+ const searchTimeMs = optionalInteger(value.search_time_ms, "search_time_ms") ?? 0;
87
+ let cutoff = false;
88
+ if (value.search_cutoff !== undefined) {
89
+ if (typeof value.search_cutoff !== "boolean")
90
+ throw new TypeError("malformed Typesense search_cutoff");
91
+ cutoff = value.search_cutoff;
92
+ }
93
+ if (!Array.isArray(value.hits))
94
+ throw new TypeError("malformed Typesense hits");
95
+ if (value.hits.length > options.limit)
96
+ throw new TypeError("Typesense returned more hits than requested");
97
+ const hits = [];
98
+ const seen = new Set();
99
+ for (const raw of value.hits) {
100
+ if (!isObject(raw) || !isPlainObject(raw.document))
101
+ throw new TypeError("malformed Typesense search hit");
102
+ const id = raw.document.id;
103
+ if (typeof id !== "string" || id.length === 0 || id.length > 1_024 || !id.isWellFormed()) {
104
+ throw new TypeError("Typesense search hit document id must be a non-empty bounded string");
105
+ }
106
+ for (let index = 0; index < id.length; index += 1) {
107
+ const code = id.charCodeAt(index);
108
+ if (code < 0x20 || code === 0x7f)
109
+ throw new TypeError("Typesense search hit document id is not supported");
110
+ }
111
+ if (seen.has(id))
112
+ throw new TypeError("Typesense search response contains a duplicate document id");
113
+ seen.add(id);
114
+ rowBytes(raw.document, options.maxRowBytes);
115
+ const textMatch = nativeTextMatch(raw);
116
+ const curated = raw.curated === undefined ? false : raw.curated;
117
+ if (typeof curated !== "boolean")
118
+ throw new TypeError("malformed Typesense curated flag");
119
+ hits.push(Object.freeze({
120
+ id,
121
+ document: Object.freeze({ ...raw.document }),
122
+ curated,
123
+ ...(textMatch === undefined ? {} : { textMatch }),
124
+ }));
125
+ }
126
+ if ((hits.length > 0 && found < options.offset + hits.length) || outOf < found) {
127
+ throw new TypeError("Typesense search counts are inconsistent with the requested offset");
128
+ }
129
+ const facets = decodeFacets(value.facet_counts, options.facetFields, options.maxFacetValues);
130
+ return Object.freeze({
131
+ hits: Object.freeze(hits),
132
+ facets,
133
+ found,
134
+ outOf,
135
+ page,
136
+ searchTimeMs,
137
+ cutoff,
138
+ });
139
+ }
140
+ function decodeFacets(value, requested, maximum) {
141
+ if (value === undefined || value === null) {
142
+ if (requested.length > 0)
143
+ throw new TypeError("Typesense facet_counts is missing");
144
+ return Object.freeze([]);
145
+ }
146
+ if (!Array.isArray(value))
147
+ throw new TypeError("malformed Typesense facet_counts");
148
+ if (requested.length === 0) {
149
+ if (value.length > 0)
150
+ throw new TypeError("Typesense returned facet_counts that were not requested");
151
+ return Object.freeze([]);
152
+ }
153
+ if (value.length !== requested.length)
154
+ throw new TypeError("Typesense facet_counts does not match the requested facets");
155
+ const wanted = new Set(requested);
156
+ const seen = new Set();
157
+ const facets = [];
158
+ for (const raw of value) {
159
+ if (!isObject(raw) || typeof raw.field_name !== "string" || raw.field_name.length === 0) {
160
+ throw new TypeError("malformed Typesense facet");
161
+ }
162
+ if (!wanted.has(raw.field_name) || seen.has(raw.field_name))
163
+ throw new TypeError("Typesense returned an unrequested or duplicate facet");
164
+ seen.add(raw.field_name);
165
+ if (!Array.isArray(raw.counts) || raw.counts.length > maximum)
166
+ throw new TypeError("malformed Typesense facet counts");
167
+ const counts = [];
168
+ const values = new Set();
169
+ for (const count of raw.counts) {
170
+ if (!isObject(count) || typeof count.value !== "string")
171
+ throw new TypeError("malformed Typesense facet value");
172
+ if (values.has(count.value))
173
+ throw new TypeError("Typesense facet contains a duplicate value");
174
+ values.add(count.value);
175
+ const amount = requiredInteger(count.count, "facet count");
176
+ const highlighted = count.highlighted === undefined ? undefined : count.highlighted;
177
+ if (highlighted !== undefined && typeof highlighted !== "string")
178
+ throw new TypeError("malformed Typesense facet highlight");
179
+ counts.push(Object.freeze({
180
+ value: count.value,
181
+ count: amount,
182
+ ...(highlighted === undefined ? {} : { highlighted }),
183
+ }));
184
+ }
185
+ const stats = raw.stats === undefined || raw.stats === null ? undefined : decodeStats(raw.stats);
186
+ facets.push(Object.freeze({
187
+ field: raw.field_name,
188
+ counts: Object.freeze(counts),
189
+ ...(stats === undefined ? {} : { stats }),
190
+ }));
191
+ }
192
+ return Object.freeze(facets);
193
+ }
194
+ function decodeStats(value) {
195
+ if (!isObject(value))
196
+ throw new TypeError("malformed Typesense facet stats");
197
+ const numeric = (name) => {
198
+ const raw = value[name];
199
+ if (raw === undefined)
200
+ return undefined;
201
+ if (typeof raw !== "number" || !isLosslessNumber(raw))
202
+ throw new TypeError("malformed Typesense facet statistic");
203
+ return raw;
204
+ };
205
+ const min = numeric("min");
206
+ const max = numeric("max");
207
+ const sum = numeric("sum");
208
+ const avg = numeric("avg");
209
+ const totalValues = value.total_values === undefined ? undefined : requiredInteger(value.total_values, "facet total_values");
210
+ return Object.freeze({
211
+ ...(min === undefined ? {} : { min }),
212
+ ...(max === undefined ? {} : { max }),
213
+ ...(sum === undefined ? {} : { sum }),
214
+ ...(avg === undefined ? {} : { avg }),
215
+ ...(totalValues === undefined ? {} : { totalValues }),
216
+ });
217
+ }
218
+ export function buildNativeResult(collection, decoded, limit, offset) {
219
+ const returned = decoded.hits.length;
220
+ const truncated = decoded.found > offset + returned;
221
+ const partial = decoded.cutoff || (returned < limit && decoded.found > offset + returned);
222
+ const complete = !partial && !truncated && !decoded.cutoff;
223
+ return Object.freeze({
224
+ collection,
225
+ hits: decoded.hits,
226
+ facets: decoded.facets,
227
+ found: decoded.found,
228
+ outOf: decoded.outOf,
229
+ page: decoded.page,
230
+ returned,
231
+ searchTimeMs: decoded.searchTimeMs,
232
+ partial,
233
+ complete,
234
+ truncated,
235
+ provenance: "typesense-native-search",
236
+ });
237
+ }
238
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ import { type StructuredQuery, type TugQLCondition } from "@dalgo/core";
2
+ /** A core `StructuredQuery` plus the approved provider-local additions. */
3
+ export interface TypesenseQuery<T> extends StructuredQuery<T> {
4
+ /** Provider-local exact predicate, ANDed with the flat `filters` array. */
5
+ readonly where?: TugQLCondition;
6
+ /** Exact top-level fields to return. The native id is fetched but not returned unless named. */
7
+ readonly projection?: readonly string[];
8
+ }
9
+ export type Truth = "true" | "false" | "unknown";
10
+ export type LiteralClass = "string" | "number" | "boolean";
11
+ export interface FilterLiteral {
12
+ readonly class: LiteralClass;
13
+ readonly value: string | number | boolean;
14
+ readonly wire: string;
15
+ }
16
+ export interface ComparisonNode {
17
+ readonly kind: "comparison";
18
+ readonly field: string;
19
+ readonly operator: "==" | "<" | "<=" | ">" | ">=" | "in";
20
+ readonly literal?: FilterLiteral;
21
+ readonly literals?: readonly FilterLiteral[];
22
+ }
23
+ export interface GroupNode {
24
+ readonly kind: "group";
25
+ readonly operator: "and" | "or";
26
+ readonly children: readonly ConditionNode[];
27
+ }
28
+ export type ConditionNode = ComparisonNode | GroupNode;
29
+ export interface CompileOptions {
30
+ readonly maxFilterBytes: number;
31
+ readonly maxDepth?: number;
32
+ readonly maxConditions?: number;
33
+ }
34
+ export declare function structuredField(value: unknown): string;
35
+ export declare function projectionFields(value: readonly string[] | undefined, maximum: number): readonly string[] | undefined;
36
+ export declare function orderFields(orders: StructuredQuery<unknown>["orders"], maximum: number): readonly {
37
+ readonly field: string;
38
+ readonly direction: "asc" | "desc";
39
+ }[];
40
+ export declare function isNumericSchemaType(type: string | undefined): boolean;
41
+ export declare function isNumericField(fields: ReadonlyMap<string, string>, field: string): boolean;
42
+ export declare function filterLiteral(value: unknown): FilterLiteral;
43
+ /** Renders the final filter after the database has supplied the real schema. */
44
+ export declare function renderPredicate(node: ConditionNode, numeric: ReadonlyMap<string, string>, maxBytes: number): string;
45
+ /**
46
+ * Builds and validates the complete admitted condition AST before any request.
47
+ * Numeric ranges are rendered later, once the real collection schema proves
48
+ * that the field is a scalar numeric type.
49
+ */
50
+ export declare function predicateRoot<T>(query: TypesenseQuery<T>, options: CompileOptions): ConditionNode | undefined;
51
+ export declare function collectPredicate(node: ConditionNode): {
52
+ readonly fields: readonly string[];
53
+ readonly ranges: readonly string[];
54
+ };
55
+ /**
56
+ * A deterministic, secret-free serialisation of the admitted predicate that
57
+ * retains the original operators and literal classes.
58
+ *
59
+ * The rendered native filter is deliberately not enough to identify a query:
60
+ * Typesense candidate normalisation widens `>`/`<` to `>=`/`<=` and two
61
+ * different exact predicates can therefore render one identical filter string.
62
+ * Cursor identity must bind the exact validated AST so a stricter handle can
63
+ * never be replayed as its widened neighbour.
64
+ */
65
+ export declare function canonicalPredicate(node: ConditionNode | undefined): unknown;
66
+ /** Mutable work budget shared by every row verified for one operation/window. */
67
+ export interface VerificationState {
68
+ work: number;
69
+ }
70
+ export declare function createVerifierState(): VerificationState;
71
+ /**
72
+ * Creates an exact verifier. The work budget is shared across all rows of one
73
+ * operation, so a query that verifies many small rows still reaches the
74
+ * macrotask cadence: a bounded per-row counter could never yield for trees
75
+ * smaller than the old 256-node threshold.
76
+ */
77
+ export declare function createVerifier(node: ConditionNode, state?: VerificationState): (data: Readonly<Record<string, unknown>>, check: () => void) => Promise<boolean>;
78
+ /** Yields an actual macrotask, not merely a promise microtask. */
79
+ export declare function yieldToEventLoop(): Promise<void>;