@pythia-software/query-table-core 0.5.0 → 0.6.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/README.md +51 -0
- package/dist/index.d.ts +561 -281
- package/dist/index.js +1776 -416
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,143 +1,3 @@
|
|
|
1
|
-
/** All filter operators across both source projects, unioned. Every value can
|
|
2
|
-
* be NULL, so `is_null`/`is_not_null` are valid for every field type. */
|
|
3
|
-
type FilterOp = "=" | "!=" | ">" | ">=" | "<" | "<=" | "contains" | "starts_with" | "ends_with" | "matches_regex" | "not_matches_regex" | "length_gt" | "length_lt" | "length_eq" | "includes" | "is_null" | "is_not_null";
|
|
4
|
-
/** A single filter predicate (a "literal" in CNF terms). `value` is always the
|
|
5
|
-
* raw string the UI captured; coercion to number/bool/date happens at
|
|
6
|
-
* apply/compile time based on the field's declared type (never on the value's
|
|
7
|
-
* runtime shape). Unused for the nullary ops (`is_null`/`is_not_null`).
|
|
8
|
-
*
|
|
9
|
-
* `negated` wraps the predicate in a logical NOT. It is only ever set for ops
|
|
10
|
-
* that have no complementary operator (`contains`/`starts_with`/`ends_with`/
|
|
11
|
-
* `includes`); ops with a complement negate by flipping the op itself (`>=`→`<`,
|
|
12
|
-
* `=`→`!=`, `is_null`→`is_not_null`, `matches_regex`→`not_matches_regex`) — see
|
|
13
|
-
* `negateClause` in ops.ts. NOT is null-exclusive: a NULL/empty value satisfies
|
|
14
|
-
* neither the predicate nor its negation, matching `not_matches_regex`. */
|
|
15
|
-
interface WhereClause {
|
|
16
|
-
field: string;
|
|
17
|
-
op: FilterOp;
|
|
18
|
-
value: string;
|
|
19
|
-
negated?: boolean;
|
|
20
|
-
setFilter?: SetFilterMetadata;
|
|
21
|
-
}
|
|
22
|
-
type SetFilterMode = "any" | "all" | "none" | "empty";
|
|
23
|
-
/** Presentation-only identity; execution always uses the accompanying predicates. */
|
|
24
|
-
interface SetFilterMetadata {
|
|
25
|
-
id: string;
|
|
26
|
-
mode: SetFilterMode;
|
|
27
|
-
values: string[];
|
|
28
|
-
}
|
|
29
|
-
/** A disjunction of predicates — the inner OR of conjunctive normal form. Every
|
|
30
|
-
* member is a plain literal; nesting is intentionally not allowed yet (CNF
|
|
31
|
-
* only), but the object shape leaves room to grow. */
|
|
32
|
-
interface OrGroup {
|
|
33
|
-
any: WhereClause[];
|
|
34
|
-
setFilter?: SetFilterMetadata;
|
|
35
|
-
}
|
|
36
|
-
/** One conjunct of the WHERE clause: either a single predicate or an OR group.
|
|
37
|
-
* `QueryState.where` is the AND of these terms, so the whole WHERE is
|
|
38
|
-
* conjunctive normal form — `(a) AND (b OR c) AND (d)`. A bare literal is
|
|
39
|
-
* equivalent to a one-element OR group; normalization flattens singleton groups
|
|
40
|
-
* back to a literal so equality/encoding stay canonical. */
|
|
41
|
-
type WhereTerm = WhereClause | OrGroup;
|
|
42
|
-
/** Narrow a term to an OR group. A literal always carries `field`; a group never
|
|
43
|
-
* does and always carries an `any` array. */
|
|
44
|
-
declare function isOrGroup(term: WhereTerm): term is OrGroup;
|
|
45
|
-
/** The predicate literals a term contributes, in order: the term itself for a
|
|
46
|
-
* literal, or its members for a group. Handy for anything that must visit every
|
|
47
|
-
* predicate regardless of grouping (field allowlisting, SELECT projection). */
|
|
48
|
-
declare function predicatesOf(term: WhereTerm): WhereClause[];
|
|
49
|
-
/** One ORDER BY term. Array order in `QueryState.orderBy` is the sort priority. */
|
|
50
|
-
interface OrderByClause {
|
|
51
|
-
field: string;
|
|
52
|
-
dir: "asc" | "desc";
|
|
53
|
-
/** NULL placement. Omitted = "last" (the historical default both projects used,
|
|
54
|
-
* preserved so old `?q=` links round-trip identically). */
|
|
55
|
-
nulls?: "first" | "last";
|
|
56
|
-
/** Sort by the first capture group of a regex match (or by the whole match
|
|
57
|
-
* when the pattern has no capture group). A non-match produces NULL. Regex
|
|
58
|
-
* syntax is interpreted by the executor (JavaScript locally, PostgreSQL on
|
|
59
|
-
* the bundled Go backend), so portable patterns should use their common
|
|
60
|
-
* syntax subset. */
|
|
61
|
-
extract?: {
|
|
62
|
-
regex: string;
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
/** One column in the SELECT list, plus the view state that travels with it.
|
|
66
|
-
* Order in `QueryState.select` is the left-to-right display order. */
|
|
67
|
-
interface SelectColumn {
|
|
68
|
-
field: string;
|
|
69
|
-
/** Pixel width override. Omitted = FieldDef.select.width, else the type default. */
|
|
70
|
-
width?: number;
|
|
71
|
-
}
|
|
72
|
-
/** A single aggregate operation. `count` is the only op that needs no measure
|
|
73
|
-
* column (it counts rows); the rest reduce one column's values. The server-side
|
|
74
|
-
* matrix (backends/go aggOpAllowed + the TS AGG_OPS_BY_TYPE) decides which ops a
|
|
75
|
-
* field's type permits. */
|
|
76
|
-
type AggOp = "count" | "count_distinct" | "sum" | "avg" | "min" | "max";
|
|
77
|
-
/** One optional "metric" pinned above the table: a single aggregate over a single
|
|
78
|
-
* measure column, optionally broken down by one or more group columns.
|
|
79
|
-
*
|
|
80
|
-
* Scope is deliberately the *whole filtered set* — the same WHERE as the table,
|
|
81
|
-
* but NOT its ORDER BY / LIMIT / OFFSET. It is always evaluated on the server
|
|
82
|
-
* (a real GROUP BY), so it reflects every matching row, not just the visible
|
|
83
|
-
* page. Lives inside QueryState so it round-trips through `?q=`, saved queries,
|
|
84
|
-
* and undo/redo for free — a saved query is a saved dashboard. */
|
|
85
|
-
interface AggregationClause {
|
|
86
|
-
/** Stable id; keys the metric panel and survives a `?q=` round-trip. */
|
|
87
|
-
id: string;
|
|
88
|
-
op: AggOp;
|
|
89
|
-
/** Measure column (FieldDef.name). Omit only for `count` (⇒ COUNT(*)). */
|
|
90
|
-
field?: string;
|
|
91
|
-
/** Group-by columns, in axis order. `[]` = a single grand-total value.
|
|
92
|
-
* One ⇒ a list/bar breakdown; two ⇒ an x/y pivot; three+ ⇒ a flat table. */
|
|
93
|
-
groupBy: string[];
|
|
94
|
-
/** Panel header override; defaults to a derived label like "avg total". */
|
|
95
|
-
label?: string;
|
|
96
|
-
}
|
|
97
|
-
interface QueryState {
|
|
98
|
-
/** Ordered SELECT list + per-column widths. Empty = the schema's default columns. */
|
|
99
|
-
select: SelectColumn[];
|
|
100
|
-
/** WHERE in conjunctive normal form: the AND of these terms, each a single
|
|
101
|
-
* predicate or an OR group. A flat list of literals (no groups) is the common
|
|
102
|
-
* case and the legacy shape, decoded unchanged. */
|
|
103
|
-
where: WhereTerm[];
|
|
104
|
-
/** Multi-sort terms in priority order. Empty = the schema's default sort. */
|
|
105
|
-
orderBy: OrderByClause[];
|
|
106
|
-
/** Page size. */
|
|
107
|
-
limit: number;
|
|
108
|
-
/** Page offset (rows). */
|
|
109
|
-
offset: number;
|
|
110
|
-
/** Optional aggregate metrics shown above the table. Omitted/empty = none.
|
|
111
|
-
* Each runs as its own server GROUP BY over the WHERE-filtered set (no paging). */
|
|
112
|
-
aggregations?: AggregationClause[];
|
|
113
|
-
}
|
|
114
|
-
/** A row's stable identity, used for selection and per-row refresh. */
|
|
115
|
-
type RowId = string | number;
|
|
116
|
-
/** The query a fresh table starts from before schema defaults are layered on.
|
|
117
|
-
* Empty `select`/`orderBy`/`where` mean "defer to the schema", which also keeps
|
|
118
|
-
* encoded `?q=` tokens short (defaults are omitted). */
|
|
119
|
-
declare const EMPTY_QUERY: QueryState;
|
|
120
|
-
/** Structural limits applied whenever query state crosses a trust boundary
|
|
121
|
-
* (URL, storage, an imperative setQuery call, or a server projection). Row
|
|
122
|
-
* limits may use the full safe-integer range; consumers and their backends own
|
|
123
|
-
* any smaller operational cap appropriate for their dataset. */
|
|
124
|
-
declare const MAX_QUERY_LIMIT: number;
|
|
125
|
-
declare const MAX_QUERY_OFFSET = 1000000;
|
|
126
|
-
declare const MAX_SELECT_COLUMNS = 200;
|
|
127
|
-
declare const MAX_WHERE_CLAUSES = 100;
|
|
128
|
-
declare const MAX_ORDER_BY_TERMS = 20;
|
|
129
|
-
declare const MAX_AGGREGATIONS = 20;
|
|
130
|
-
declare const MAX_GROUP_BY_FIELDS = 20;
|
|
131
|
-
declare const MAX_QUERY_TOKEN_LENGTH: number;
|
|
132
|
-
/** Convert unknown input into a bounded, structurally valid QueryState.
|
|
133
|
-
* Invalid members are dropped; invalid scalar paging values use the supplied
|
|
134
|
-
* fallback. This function is intentionally schema-agnostic—field allowlisting
|
|
135
|
-
* still happens in toServerQuery and in the backend compiler. */
|
|
136
|
-
declare function normalizeQueryState(input: unknown, fallback?: QueryState): QueryState;
|
|
137
|
-
/** Structural equality on two queries — used to decide whether a `?q=` write or a
|
|
138
|
-
* refetch is actually needed (avoids history spam / redundant fetches). */
|
|
139
|
-
declare function queriesEqual(a: QueryState, b: QueryState): boolean;
|
|
140
|
-
|
|
141
1
|
type FieldType = "text" | "number" | "enum" | "datetime" | "bool" | "textarray";
|
|
142
2
|
type Align = "left" | "right" | "center";
|
|
143
3
|
/** A cell renderer is resolved by name against a `RenderRegistry` (in
|
|
@@ -257,27 +117,356 @@ interface FieldSchema<Row = any> {
|
|
|
257
117
|
/** Page size for a fresh query. */
|
|
258
118
|
defaultLimit?: number;
|
|
259
119
|
}
|
|
260
|
-
declare function isFilterable(f: FieldDef): boolean;
|
|
261
|
-
declare function isPushdownFilter(f: FieldDef): boolean;
|
|
262
|
-
declare function isSortable(f: FieldDef): boolean;
|
|
263
|
-
declare function isSelectable(f: FieldDef): boolean;
|
|
264
|
-
declare function filterValues(f: FieldDef): FilterValues;
|
|
265
|
-
/** Index fields by name for O(1) lookup. */
|
|
266
|
-
declare function indexFields<Row>(schema: FieldSchema<Row>): Map<string, FieldDef<Row>>;
|
|
267
|
-
/** Resolve the ordered, selectable FieldDefs for a query, falling back to schema
|
|
268
|
-
* defaults, then to every `select.default` field. Unknown/duplicate/unselectable
|
|
269
|
-
* names in `select` are dropped. */
|
|
270
|
-
declare function selectedFields<Row>(schema: FieldSchema<Row>, q: {
|
|
120
|
+
declare function isFilterable(f: FieldDef): boolean;
|
|
121
|
+
declare function isPushdownFilter(f: FieldDef): boolean;
|
|
122
|
+
declare function isSortable(f: FieldDef): boolean;
|
|
123
|
+
declare function isSelectable(f: FieldDef): boolean;
|
|
124
|
+
declare function filterValues(f: FieldDef): FilterValues;
|
|
125
|
+
/** Index fields by name for O(1) lookup. */
|
|
126
|
+
declare function indexFields<Row>(schema: FieldSchema<Row>): Map<string, FieldDef<Row>>;
|
|
127
|
+
/** Resolve the ordered, selectable FieldDefs for a query, falling back to schema
|
|
128
|
+
* defaults, then to every `select.default` field. Unknown/duplicate/unselectable
|
|
129
|
+
* names in `select` are dropped. */
|
|
130
|
+
declare function selectedFields<Row>(schema: FieldSchema<Row>, q: {
|
|
131
|
+
select: SelectColumn[];
|
|
132
|
+
}): FieldDef<Row>[];
|
|
133
|
+
/** Return the canonical name for a query token, or undefined when unresolved. */
|
|
134
|
+
declare function resolveFieldName<Row>(schema: FieldSchema<Row>, field: string): string | undefined;
|
|
135
|
+
/** Read a field's value from a row: derived `accessor` if present, else the
|
|
136
|
+
* backend `path` (dotted) / `name`. Returns null when any path segment is missing. */
|
|
137
|
+
declare function readFieldValue<Row, V = unknown>(field: FieldDef<Row, V>, row: Row): V | null;
|
|
138
|
+
/** Parse + validate a raw JSON schema document into a FieldSchema. The doc is the
|
|
139
|
+
* same one the Go backend loads; here we drop backend `bindings` (the frontend
|
|
140
|
+
* never sees SQL) and leave `render` as a string key the consumer maps to a
|
|
141
|
+
* renderer at runtime. Throws with a readable message on a malformed document. */
|
|
142
|
+
declare function loadSchema<Row = any>(doc: unknown): FieldSchema<Row>;
|
|
143
|
+
|
|
144
|
+
type FormulaType = Exclude<FieldType, "enum"> | "null";
|
|
145
|
+
type FormulaValue = string | number | boolean | string[] | null;
|
|
146
|
+
interface FormulaDiagnostic {
|
|
147
|
+
message: string;
|
|
148
|
+
from: number;
|
|
149
|
+
to: number;
|
|
150
|
+
}
|
|
151
|
+
type FormulaNode = {
|
|
152
|
+
kind: "literal";
|
|
153
|
+
value: FormulaValue;
|
|
154
|
+
from: number;
|
|
155
|
+
to: number;
|
|
156
|
+
} | {
|
|
157
|
+
kind: "field";
|
|
158
|
+
name: string;
|
|
159
|
+
valueType?: FormulaType;
|
|
160
|
+
from: number;
|
|
161
|
+
to: number;
|
|
162
|
+
} | {
|
|
163
|
+
kind: "call";
|
|
164
|
+
name: string;
|
|
165
|
+
args: FormulaNode[];
|
|
166
|
+
from: number;
|
|
167
|
+
to: number;
|
|
168
|
+
};
|
|
169
|
+
interface FormulaPlan {
|
|
170
|
+
ast: FormulaNode;
|
|
171
|
+
dependencies: string[];
|
|
172
|
+
type: FormulaType;
|
|
173
|
+
}
|
|
174
|
+
interface RegexInspection {
|
|
175
|
+
input: string;
|
|
176
|
+
start: number;
|
|
177
|
+
end: number;
|
|
178
|
+
groups: (string | null)[];
|
|
179
|
+
}
|
|
180
|
+
interface FormulaResult {
|
|
181
|
+
value: FormulaValue;
|
|
182
|
+
error?: string;
|
|
183
|
+
regex?: RegexInspection;
|
|
184
|
+
}
|
|
185
|
+
interface FormulaFunction {
|
|
186
|
+
name: string;
|
|
187
|
+
signature: string;
|
|
188
|
+
description: string;
|
|
189
|
+
min: number;
|
|
190
|
+
max: number;
|
|
191
|
+
result: FormulaType | "branch";
|
|
192
|
+
args: FormulaType[] | "any";
|
|
193
|
+
}
|
|
194
|
+
declare const FORMULA_FUNCTIONS: FormulaFunction[];
|
|
195
|
+
declare class FormulaError extends Error {
|
|
196
|
+
from: number;
|
|
197
|
+
to: number;
|
|
198
|
+
constructor(message: string, from?: number, to?: number);
|
|
199
|
+
}
|
|
200
|
+
declare function compileFormula(source: string, fields: readonly {
|
|
201
|
+
name: string;
|
|
202
|
+
type: FieldType;
|
|
203
|
+
}[], resolve?: (name: string) => FormulaPlan | undefined, extension?: (node: FormulaNode, check: (node: FormulaNode) => FormulaType) => FormulaType | undefined): FormulaPlan;
|
|
204
|
+
/** Self-contained runtime: serialized into the browser worker, never user code. */
|
|
205
|
+
declare function formulaRuntime(ast: FormulaNode, inputs: Record<string, FormulaValue>, extension?: (node: Extract<FormulaNode, {
|
|
206
|
+
kind: "call";
|
|
207
|
+
}>) => FormulaResult | undefined): FormulaResult;
|
|
208
|
+
|
|
209
|
+
type MetricValue = number | string | boolean | null;
|
|
210
|
+
type MetricKey = MetricValue;
|
|
211
|
+
type MetricScope = "allMatching" | "shownRows";
|
|
212
|
+
interface MetricValueFormat {
|
|
213
|
+
kind: "number" | "percent" | "duration" | "date" | "time" | "datetime";
|
|
214
|
+
decimals?: number;
|
|
215
|
+
sourceUnit?: "milliseconds" | "seconds" | "minutes" | "hours" | "days" | "iso" | "epochSeconds" | "epochMilliseconds" | "secondsOfDay" | "millisecondsOfDay";
|
|
216
|
+
style?: "human" | "long" | "clock" | "custom" | "iso";
|
|
217
|
+
pattern?: string;
|
|
218
|
+
timeZone?: string;
|
|
219
|
+
}
|
|
220
|
+
/** Bounds use raw result units; omitted bounds are inferred from the data. */
|
|
221
|
+
interface MetricAxisScale {
|
|
222
|
+
mode?: "linear" | "log";
|
|
223
|
+
min?: number;
|
|
224
|
+
max?: number;
|
|
225
|
+
}
|
|
226
|
+
interface MetricDisplay {
|
|
227
|
+
kind: "auto" | "value" | "table" | "list" | "bar-horizontal" | "bar-vertical" | "line" | "pie" | "donut" | "scatter" | "box" | "histogram";
|
|
228
|
+
valueLabel?: string;
|
|
229
|
+
xLabel?: string;
|
|
230
|
+
yLabel?: string;
|
|
231
|
+
format?: MetricValueFormat;
|
|
232
|
+
xFormat?: MetricValueFormat;
|
|
233
|
+
yFormat?: MetricValueFormat;
|
|
234
|
+
legendPosition?: "left" | "right" | "top" | "bottom" | "none";
|
|
235
|
+
list?: {
|
|
236
|
+
showBars?: boolean;
|
|
237
|
+
useGroupColors?: boolean;
|
|
238
|
+
showValues?: boolean;
|
|
239
|
+
};
|
|
240
|
+
pivot?: {
|
|
241
|
+
swap?: boolean;
|
|
242
|
+
rowDir?: "asc" | "desc";
|
|
243
|
+
columnDir?: "asc" | "desc";
|
|
244
|
+
};
|
|
245
|
+
boxMean?: boolean;
|
|
246
|
+
xScale?: MetricAxisScale;
|
|
247
|
+
yScale?: MetricAxisScale;
|
|
248
|
+
}
|
|
249
|
+
interface MetricLayout {
|
|
250
|
+
widthRem: number;
|
|
251
|
+
heightRem: number;
|
|
252
|
+
minWidthRem: number;
|
|
253
|
+
minHeightRem: number;
|
|
254
|
+
}
|
|
255
|
+
interface MetricDistribution {
|
|
256
|
+
kind: "box" | "histogram";
|
|
257
|
+
input: string;
|
|
258
|
+
whiskers?: "minmax" | "tukey";
|
|
259
|
+
bins?: number;
|
|
260
|
+
}
|
|
261
|
+
interface MetricSort {
|
|
262
|
+
key: "value" | "y" | "count" | "samples" | `group${number}`;
|
|
263
|
+
dir: "asc" | "desc";
|
|
264
|
+
nulls?: "first" | "last";
|
|
265
|
+
}
|
|
266
|
+
interface MetricBoxSummary {
|
|
267
|
+
n: number;
|
|
268
|
+
min: number;
|
|
269
|
+
q1: number;
|
|
270
|
+
median: number;
|
|
271
|
+
q3: number;
|
|
272
|
+
max: number;
|
|
273
|
+
mean: number;
|
|
274
|
+
low: number;
|
|
275
|
+
high: number;
|
|
276
|
+
outliers: number[];
|
|
277
|
+
outlierCount: number;
|
|
278
|
+
whiskers: "minmax" | "tukey";
|
|
279
|
+
method: "exact-linear";
|
|
280
|
+
}
|
|
281
|
+
type MetricDistributionResult = {
|
|
282
|
+
kind: "box";
|
|
283
|
+
summary: MetricBoxSummary | null;
|
|
284
|
+
} | {
|
|
285
|
+
kind: "histogram";
|
|
286
|
+
edges: number[];
|
|
287
|
+
counts: number[];
|
|
288
|
+
n: number;
|
|
289
|
+
};
|
|
290
|
+
interface MetricDiagnostic extends FormulaDiagnostic {
|
|
291
|
+
metricId?: string;
|
|
292
|
+
code?: string;
|
|
293
|
+
}
|
|
294
|
+
/** Host must enforce snapshot against the row response; this token alone does not pin data. */
|
|
295
|
+
interface MetricQuery {
|
|
296
|
+
snapshot?: string;
|
|
297
|
+
profile?: string;
|
|
298
|
+
expectedRevisions?: Record<string, string>;
|
|
299
|
+
planToken?: string;
|
|
300
|
+
version: 2;
|
|
301
|
+
where: WhereTerm[];
|
|
302
|
+
orderBy: OrderByClause[];
|
|
303
|
+
limit: number;
|
|
304
|
+
offset: number;
|
|
305
|
+
metrics: AggregationClause[];
|
|
306
|
+
diagnostics: MetricDiagnostic[];
|
|
307
|
+
}
|
|
308
|
+
interface MetricCapabilities {
|
|
309
|
+
version: 2;
|
|
310
|
+
expressions?: boolean;
|
|
311
|
+
shownRows?: boolean;
|
|
312
|
+
distributions?: boolean;
|
|
313
|
+
computedFields?: boolean;
|
|
314
|
+
maxGroups?: number;
|
|
315
|
+
profile?: string;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/** All filter operators across both source projects, unioned. Every value can
|
|
319
|
+
* be NULL, so `is_null`/`is_not_null` are valid for every field type. */
|
|
320
|
+
type FilterOp = "=" | "!=" | ">" | ">=" | "<" | "<=" | "contains" | "starts_with" | "ends_with" | "matches_regex" | "not_matches_regex" | "length_gt" | "length_lt" | "length_eq" | "includes" | "is_null" | "is_not_null";
|
|
321
|
+
/** A single filter predicate (a "literal" in CNF terms). `value` is always the
|
|
322
|
+
* raw string the UI captured; coercion to number/bool/date happens at
|
|
323
|
+
* apply/compile time based on the field's declared type (never on the value's
|
|
324
|
+
* runtime shape). Datetime comparisons also accept signed duration offsets
|
|
325
|
+
* such as "-1h" or "+8d2h10m", resolved relative to execution time.
|
|
326
|
+
* Unused for the nullary ops (`is_null`/`is_not_null`).
|
|
327
|
+
*
|
|
328
|
+
* `negated` wraps the predicate in a logical NOT. It is only ever set for ops
|
|
329
|
+
* that have no complementary operator (`contains`/`starts_with`/`ends_with`/
|
|
330
|
+
* `includes`); ops with a complement negate by flipping the op itself (`>=`→`<`,
|
|
331
|
+
* `=`→`!=`, `is_null`→`is_not_null`, `matches_regex`→`not_matches_regex`) — see
|
|
332
|
+
* `negateClause` in ops.ts. NOT is null-exclusive: a NULL/empty value satisfies
|
|
333
|
+
* neither the predicate nor its negation, matching `not_matches_regex`. */
|
|
334
|
+
interface WhereClause {
|
|
335
|
+
field: string;
|
|
336
|
+
op: FilterOp;
|
|
337
|
+
value: string;
|
|
338
|
+
negated?: boolean;
|
|
339
|
+
setFilter?: SetFilterMetadata;
|
|
340
|
+
}
|
|
341
|
+
type SetFilterMode = "any" | "all" | "none" | "empty";
|
|
342
|
+
/** Presentation-only identity; execution always uses the accompanying predicates. */
|
|
343
|
+
interface SetFilterMetadata {
|
|
344
|
+
id: string;
|
|
345
|
+
mode: SetFilterMode;
|
|
346
|
+
values: string[];
|
|
347
|
+
}
|
|
348
|
+
/** A disjunction of predicates — the inner OR of conjunctive normal form. Every
|
|
349
|
+
* member is a plain literal; nesting is intentionally not allowed yet (CNF
|
|
350
|
+
* only), but the object shape leaves room to grow. */
|
|
351
|
+
interface OrGroup {
|
|
352
|
+
any: WhereClause[];
|
|
353
|
+
setFilter?: SetFilterMetadata;
|
|
354
|
+
}
|
|
355
|
+
/** One conjunct of the WHERE clause: either a single predicate or an OR group.
|
|
356
|
+
* `QueryState.where` is the AND of these terms, so the whole WHERE is
|
|
357
|
+
* conjunctive normal form — `(a) AND (b OR c) AND (d)`. A bare literal is
|
|
358
|
+
* equivalent to a one-element OR group; normalization flattens singleton groups
|
|
359
|
+
* back to a literal so equality/encoding stay canonical. */
|
|
360
|
+
type WhereTerm = WhereClause | OrGroup;
|
|
361
|
+
/** Narrow a term to an OR group. A literal always carries `field`; a group never
|
|
362
|
+
* does and always carries an `any` array. */
|
|
363
|
+
declare function isOrGroup(term: WhereTerm): term is OrGroup;
|
|
364
|
+
/** The predicate literals a term contributes, in order: the term itself for a
|
|
365
|
+
* literal, or its members for a group. Handy for anything that must visit every
|
|
366
|
+
* predicate regardless of grouping (field allowlisting, SELECT projection). */
|
|
367
|
+
declare function predicatesOf(term: WhereTerm): WhereClause[];
|
|
368
|
+
/** One ORDER BY term. Array order in `QueryState.orderBy` is the sort priority. */
|
|
369
|
+
interface OrderByClause {
|
|
370
|
+
field: string;
|
|
371
|
+
dir: "asc" | "desc";
|
|
372
|
+
/** NULL placement. Omitted = "last" (the historical default both projects used,
|
|
373
|
+
* preserved so old `?q=` links round-trip identically). */
|
|
374
|
+
nulls?: "first" | "last";
|
|
375
|
+
/** Sort by the first capture group of a regex match (or by the whole match
|
|
376
|
+
* when the pattern has no capture group). A non-match produces NULL. Regex
|
|
377
|
+
* syntax is interpreted by the executor (JavaScript locally, PostgreSQL on
|
|
378
|
+
* the bundled Go backend), so portable patterns should use their common
|
|
379
|
+
* syntax subset. */
|
|
380
|
+
extract?: {
|
|
381
|
+
regex: string;
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
/** One column in the SELECT list, plus the view state that travels with it.
|
|
385
|
+
* Order in `QueryState.select` is the left-to-right display order. */
|
|
386
|
+
interface SelectColumn {
|
|
387
|
+
field: string;
|
|
388
|
+
/** Pixel width override. Omitted = FieldDef.select.width, else the type default. */
|
|
389
|
+
width?: number;
|
|
390
|
+
}
|
|
391
|
+
/** A single aggregate operation. `count` is the only op that needs no measure
|
|
392
|
+
* column (it counts rows); the rest reduce one column's values. The server-side
|
|
393
|
+
* matrix (backends/go aggOpAllowed + the TS AGG_OPS_BY_TYPE) decides which ops a
|
|
394
|
+
* field's type permits. */
|
|
395
|
+
type AggOp = "count" | "count_distinct" | "sum" | "avg" | "min" | "max";
|
|
396
|
+
/** One optional "metric" pinned above the table: a single aggregate over a single
|
|
397
|
+
* measure column, optionally broken down by one or more group columns.
|
|
398
|
+
*
|
|
399
|
+
* Scope is deliberately the *whole filtered set* — the same WHERE as the table,
|
|
400
|
+
* but NOT its ORDER BY / LIMIT / OFFSET. It is always evaluated on the server
|
|
401
|
+
* (a real GROUP BY), so it reflects every matching row, not just the visible
|
|
402
|
+
* page. Lives inside QueryState so it round-trips through `?q=`, saved queries,
|
|
403
|
+
* and undo/redo for free — a saved query is a saved dashboard. */
|
|
404
|
+
interface AggregationClause {
|
|
405
|
+
expression?: string;
|
|
406
|
+
expressionY?: string;
|
|
407
|
+
scope?: MetricScope;
|
|
408
|
+
sort?: MetricSort[];
|
|
409
|
+
groupLimit?: number;
|
|
410
|
+
display?: MetricDisplay;
|
|
411
|
+
layout?: MetricLayout;
|
|
412
|
+
distribution?: MetricDistribution;
|
|
413
|
+
diagnostics?: string[];
|
|
414
|
+
/** Preserved source operation when this client does not implement it. */
|
|
415
|
+
unsupportedOp?: string;
|
|
416
|
+
/** Stable id; keys the metric panel and survives a `?q=` round-trip. */
|
|
417
|
+
id: string;
|
|
418
|
+
op: AggOp;
|
|
419
|
+
/** Measure column (FieldDef.name). Omit only for `count` (⇒ COUNT(*)). */
|
|
420
|
+
field?: string;
|
|
421
|
+
/** Group-by columns, in axis order. `[]` = a single grand-total value.
|
|
422
|
+
* One ⇒ a list/bar breakdown; two ⇒ an x/y pivot; three+ ⇒ a flat table. */
|
|
423
|
+
groupBy: string[];
|
|
424
|
+
/** Panel header override; defaults to a derived label like "avg total". */
|
|
425
|
+
label?: string;
|
|
426
|
+
}
|
|
427
|
+
interface QueryState {
|
|
428
|
+
/** Ordered SELECT list + per-column widths. Empty = the schema's default columns. */
|
|
271
429
|
select: SelectColumn[];
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
430
|
+
/** WHERE in conjunctive normal form: the AND of these terms, each a single
|
|
431
|
+
* predicate or an OR group. A flat list of literals (no groups) is the common
|
|
432
|
+
* case and the legacy shape, decoded unchanged. */
|
|
433
|
+
where: WhereTerm[];
|
|
434
|
+
/** Multi-sort terms in priority order. Empty = the schema's default sort. */
|
|
435
|
+
orderBy: OrderByClause[];
|
|
436
|
+
/** Page size. */
|
|
437
|
+
limit: number;
|
|
438
|
+
/** Page offset (rows). */
|
|
439
|
+
offset: number;
|
|
440
|
+
/** Optional aggregate metrics shown above the table. Omitted/empty = none.
|
|
441
|
+
* Each runs as its own server GROUP BY over the WHERE-filtered set (no paging). */
|
|
442
|
+
aggregations?: AggregationClause[];
|
|
443
|
+
}
|
|
444
|
+
/** A row's stable identity, used for selection and per-row refresh. */
|
|
445
|
+
type RowId = string | number;
|
|
446
|
+
/** The query a fresh table starts from before schema defaults are layered on.
|
|
447
|
+
* Empty `select`/`orderBy`/`where` mean "defer to the schema", which also keeps
|
|
448
|
+
* encoded `?q=` tokens short (defaults are omitted). */
|
|
449
|
+
declare const EMPTY_QUERY: QueryState;
|
|
450
|
+
/** Structural limits applied whenever query state crosses a trust boundary
|
|
451
|
+
* (URL, storage, an imperative setQuery call, or a server projection). Row
|
|
452
|
+
* limits may use the full safe-integer range; consumers and their backends own
|
|
453
|
+
* any smaller operational cap appropriate for their dataset. */
|
|
454
|
+
declare const MAX_QUERY_LIMIT: number;
|
|
455
|
+
declare const MAX_QUERY_OFFSET = 1000000;
|
|
456
|
+
declare const MAX_SELECT_COLUMNS = 200;
|
|
457
|
+
declare const MAX_WHERE_CLAUSES = 100;
|
|
458
|
+
declare const MAX_ORDER_BY_TERMS = 20;
|
|
459
|
+
declare const MAX_AGGREGATIONS = 20;
|
|
460
|
+
declare const MAX_GROUP_BY_FIELDS = 20;
|
|
461
|
+
declare const MAX_QUERY_TOKEN_LENGTH: number;
|
|
462
|
+
/** Convert unknown input into a bounded, structurally valid QueryState.
|
|
463
|
+
* Invalid members are dropped; invalid scalar paging values use the supplied
|
|
464
|
+
* fallback. This function is intentionally schema-agnostic—field allowlisting
|
|
465
|
+
* still happens in toServerQuery and in the backend compiler. */
|
|
466
|
+
declare function normalizeQueryState(input: unknown, fallback?: QueryState): QueryState;
|
|
467
|
+
/** Structural equality on two queries — used to decide whether a `?q=` write or a
|
|
468
|
+
* refetch is actually needed (avoids history spam / redundant fetches). */
|
|
469
|
+
declare function queriesEqual(a: QueryState, b: QueryState): boolean;
|
|
281
470
|
|
|
282
471
|
/** The logical negation of a predicate. Prefers flipping to the complementary
|
|
283
472
|
* operator (nicer to read, and the server's op allowlist already covers it);
|
|
@@ -320,7 +509,7 @@ declare function opAllowedForType(type: FieldType, op: FilterOp): boolean;
|
|
|
320
509
|
/** Coerce a raw string value to the type the field expects, for client-side
|
|
321
510
|
* comparison. Throws on invalid input (e.g. a non-numeric value on a number
|
|
322
511
|
* field) so callers can surface it rather than silently mis-filtering. */
|
|
323
|
-
declare function coerceValue(type: FieldType, raw: string): string | number | boolean;
|
|
512
|
+
declare function coerceValue(type: FieldType, raw: string, now?: number): string | number | boolean;
|
|
324
513
|
|
|
325
514
|
/** Default aggregate ops per field type. A FieldDef.aggregate.ops overrides this.
|
|
326
515
|
* `count`/`count_distinct` work on anything countable; sum/avg are numeric-only;
|
|
@@ -371,22 +560,40 @@ declare function toServerQuery<Row>(q: QueryState, schema: FieldSchema<Row>): Se
|
|
|
371
560
|
* metrics describe every matching row, not the visible page. Mirrors the Go
|
|
372
561
|
* AggSpec list. */
|
|
373
562
|
interface AggregationRequest {
|
|
563
|
+
diagnostics?: MetricDiagnostic[];
|
|
374
564
|
where: WhereTerm[];
|
|
375
565
|
aggregations: AggregationClause[];
|
|
376
566
|
}
|
|
377
567
|
/** One group's result. `keys` has one entry per AggregationClause.groupBy field,
|
|
378
568
|
* in axis order (`[]` for a grand total); a null key is the NULL/empty bucket. */
|
|
379
569
|
interface AggregationBucket {
|
|
380
|
-
keys:
|
|
570
|
+
keys: MetricKey[];
|
|
571
|
+
error?: string;
|
|
572
|
+
y?: MetricValue;
|
|
573
|
+
yError?: string;
|
|
574
|
+
components?: {
|
|
575
|
+
expression: string;
|
|
576
|
+
value: MetricValue;
|
|
577
|
+
}[];
|
|
578
|
+
nullCount?: number;
|
|
579
|
+
inputErrorCount?: number;
|
|
580
|
+
distribution?: MetricDistributionResult;
|
|
381
581
|
/** The metric: a number for count/sum/avg, or the column's value for min/max
|
|
382
582
|
* (which may be a string for text/datetime). null when undefined (e.g. avg of
|
|
383
583
|
* an all-null column). */
|
|
384
|
-
value:
|
|
584
|
+
value: MetricValue;
|
|
385
585
|
/** COUNT(*) of rows in the group — always present, even when `value` isn't a
|
|
386
586
|
* count, so the panel can show group sizes / shares. */
|
|
387
587
|
count: number;
|
|
388
588
|
}
|
|
389
589
|
interface AggregationResultEntry {
|
|
590
|
+
/** Exact omitted additive quantity; unavailable for ratios/averages/distinct. */
|
|
591
|
+
other?: AggregationBucket;
|
|
592
|
+
error?: string;
|
|
593
|
+
scope?: MetricScope;
|
|
594
|
+
processedRows?: number;
|
|
595
|
+
groupCount?: number;
|
|
596
|
+
coverage?: "exact" | "partial";
|
|
390
597
|
/** Echoes AggregationClause.id. */
|
|
391
598
|
id: string;
|
|
392
599
|
buckets: AggregationBucket[];
|
|
@@ -401,6 +608,145 @@ interface AggregationResult {
|
|
|
401
608
|
* unknown field are dropped — the server has no SQL for them. */
|
|
402
609
|
declare function toAggregationQuery<Row>(q: QueryState, schema: FieldSchema<Row>): AggregationRequest;
|
|
403
610
|
|
|
611
|
+
/** Reserved field token namespace. Query state stores IDs, never formula source. */
|
|
612
|
+
declare const COMPUTED_PREFIX = "@computed/";
|
|
613
|
+
declare const computedFieldName: (id: string) => string;
|
|
614
|
+
declare const isComputedField: (name: string) => boolean;
|
|
615
|
+
interface ComputedColumn {
|
|
616
|
+
id: string;
|
|
617
|
+
label: string;
|
|
618
|
+
expression: {
|
|
619
|
+
language: "qt-expr";
|
|
620
|
+
version: 1;
|
|
621
|
+
source: string;
|
|
622
|
+
};
|
|
623
|
+
/** Optimistic concurrency token issued by the store. */
|
|
624
|
+
revision: string;
|
|
625
|
+
}
|
|
626
|
+
type ComputedColumnDraft = Omit<ComputedColumn, "revision">;
|
|
627
|
+
interface ComputedColumnStore {
|
|
628
|
+
/** Return the complete authorized catalogue for this dataset. */
|
|
629
|
+
list(dataset: string, signal?: AbortSignal): Promise<ComputedColumn[]>;
|
|
630
|
+
/** null creates a new ID; updates must match the current revision. */
|
|
631
|
+
save(dataset: string, column: ComputedColumnDraft, expectedRevision: string | null): Promise<ComputedColumn>;
|
|
632
|
+
/** Optional push invalidation, including changes from other sessions. */
|
|
633
|
+
subscribe?(dataset: string, listener: () => void): () => void;
|
|
634
|
+
}
|
|
635
|
+
declare function validateComputedColumn(input: unknown): ComputedColumn;
|
|
636
|
+
declare function memoryComputedColumnStore(seed?: Record<string, ComputedColumn[]>): ComputedColumnStore;
|
|
637
|
+
/** REST adapter. The implementing server owns authorization and DB persistence.
|
|
638
|
+
* GET base?dataset=… → ComputedColumn[]
|
|
639
|
+
* PUT base?dataset=… → {column, expectedRevision} → ComputedColumn
|
|
640
|
+
* Return HTTP 409 for a stale revision. No formula evaluation happens there. */
|
|
641
|
+
declare function httpComputedColumnStore(base: string, fetcher?: typeof fetch): ComputedColumnStore;
|
|
642
|
+
interface ComputedCellError {
|
|
643
|
+
computedError: string;
|
|
644
|
+
}
|
|
645
|
+
declare function isComputedCellError(value: unknown): value is ComputedCellError;
|
|
646
|
+
interface PreviewGroup {
|
|
647
|
+
inputs: FormulaValue[];
|
|
648
|
+
result: FormulaResult;
|
|
649
|
+
count: number;
|
|
650
|
+
}
|
|
651
|
+
interface ColumnPreview {
|
|
652
|
+
dependencies: string[];
|
|
653
|
+
groups: PreviewGroup[];
|
|
654
|
+
processed: number;
|
|
655
|
+
total: number;
|
|
656
|
+
nulls: number;
|
|
657
|
+
errors: number;
|
|
658
|
+
}
|
|
659
|
+
declare function groupPreview(dependencies: string[], inputs: Record<string, FormulaValue>[], results: FormulaResult[], total: number): ColumnPreview;
|
|
660
|
+
/** Server-issued capabilities are hints for client planning, never authorization. */
|
|
661
|
+
interface ComputedFieldCapabilities {
|
|
662
|
+
type: FieldType;
|
|
663
|
+
select: boolean;
|
|
664
|
+
sort: boolean;
|
|
665
|
+
measure: boolean;
|
|
666
|
+
group: boolean;
|
|
667
|
+
reason?: string;
|
|
668
|
+
}
|
|
669
|
+
interface ComputedExecution {
|
|
670
|
+
profile: string;
|
|
671
|
+
planToken: string;
|
|
672
|
+
resolvedRevisions: Record<string, string>;
|
|
673
|
+
fields: Record<string, ComputedFieldCapabilities>;
|
|
674
|
+
fingerprint?: string;
|
|
675
|
+
snapshot?: string;
|
|
676
|
+
}
|
|
677
|
+
type RowComputedValues = Record<string, {
|
|
678
|
+
value: MetricValue;
|
|
679
|
+
error?: {
|
|
680
|
+
code: string;
|
|
681
|
+
};
|
|
682
|
+
}>;
|
|
683
|
+
|
|
684
|
+
/** Portable datetime/offset bound in milliseconds (also below MAX_SAFE_INTEGER). */
|
|
685
|
+
declare const MAX_RELATIVE_TIME_MS = 8640000000000000;
|
|
686
|
+
/** A signed elapsed offset, e.g. -1h or +8d2h10m. The sign applies to the
|
|
687
|
+
* complete sum. Returns null for absolute values or invalid duration syntax.
|
|
688
|
+
* Units are lowercase; amounts are whole integers. No whitespace, calendar
|
|
689
|
+
* arithmetic, locale, or Date.parse heuristics are used. */
|
|
690
|
+
declare function parseRelativeDuration(value: string): number | null;
|
|
691
|
+
/** Local execution only. The server independently owns its execution clock. */
|
|
692
|
+
interface QueryEvaluationOptions {
|
|
693
|
+
now?: number;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
interface MetricPlan extends FormulaPlan {
|
|
697
|
+
aggregates: Extract<FormulaNode, {
|
|
698
|
+
kind: "call";
|
|
699
|
+
}>[];
|
|
700
|
+
aggregateTypes: FormulaType[];
|
|
701
|
+
}
|
|
702
|
+
declare function compileMetric(expression: string, schema: FieldSchema | readonly FieldDef[], resolve?: (name: string) => FormulaPlan | undefined): MetricPlan;
|
|
703
|
+
/** Row plans for worker evaluation. Null denotes COUNT(), which requires only row count. */
|
|
704
|
+
declare function metricAggregatePlans(plan: MetricPlan): (FormulaPlan | null)[];
|
|
705
|
+
/** Reduce exact per-row worker results; errors are not silently treated as NULL. */
|
|
706
|
+
interface MetricReductionOptions {
|
|
707
|
+
/** Retained distinct entries, independently bounded from row count. */
|
|
708
|
+
maxDistinctValues?: number;
|
|
709
|
+
/** Conservative UTF-16 payload plus per-entry storage estimate. */
|
|
710
|
+
maxDistinctBytes?: number;
|
|
711
|
+
}
|
|
712
|
+
declare function reduceMetricValues(node: Extract<FormulaNode, {
|
|
713
|
+
kind: "call";
|
|
714
|
+
}>, results: readonly FormulaResult[], options?: MetricReductionOptions): FormulaResult;
|
|
715
|
+
/** Evaluate the group AST lazily from reductions indexed like plan.aggregates.
|
|
716
|
+
* A dead IF/COALESCE branch does not observe a failed aggregate in that branch. */
|
|
717
|
+
declare function evaluateMetricReductions(plan: MetricPlan, reductions: readonly FormulaResult[]): FormulaResult;
|
|
718
|
+
declare function metricExpression(clause: AggregationClause): string;
|
|
719
|
+
declare function hasRegex(plan: FormulaPlan): boolean;
|
|
720
|
+
declare function metricPlans(c: AggregationClause, schema: FieldSchema, resolve?: (name: string) => FormulaPlan | undefined): FormulaPlan[];
|
|
721
|
+
declare function validateMetric(c: AggregationClause, schema: FieldSchema, resolve?: (name: string) => FormulaPlan | undefined): FormulaDiagnostic[];
|
|
722
|
+
declare function metricDependencies(c: AggregationClause, schema: FieldSchema, resolve?: (name: string) => FormulaPlan | undefined): string[];
|
|
723
|
+
declare function metricComputationKey(clauses: readonly AggregationClause[]): string;
|
|
724
|
+
declare function toMetricQuery<Row>(q: QueryState, schema: FieldSchema<Row>, execution?: ComputedExecution): MetricQuery;
|
|
725
|
+
/** Safe pre-worker page selection. A supplied committed page needs no query execution.
|
|
726
|
+
* Query regex is never executed here; all-matching regex filtering is unsupported. */
|
|
727
|
+
declare function selectMetricShownRows<Row>(rows: Row[], q: QueryState, schema: FieldSchema<Row>, options?: MetricEvaluationOptions<Row>): Row[];
|
|
728
|
+
interface MetricEvaluationOptions<Row = unknown> extends QueryEvaluationOptions, MetricReductionOptions {
|
|
729
|
+
resolveComputed?: (name: string) => FormulaPlan | undefined;
|
|
730
|
+
/** Called only for regex row ASTs, using cached results from a terminable worker. */
|
|
731
|
+
evaluateInput?: (node: FormulaNode, row: Row, plan: FormulaPlan) => FormulaResult;
|
|
732
|
+
/** Exact committed page supplied by the host after filters/order/window. */
|
|
733
|
+
/** Escaped tuple length bound, in UTF-16 code units (default 65,536). */
|
|
734
|
+
maxGroupKeyLength?: number;
|
|
735
|
+
/** Retained group keys, map/bucket overhead and row references, plus key scratch space (default 16 MB). */
|
|
736
|
+
maxGroupBytes?: number;
|
|
737
|
+
shownRows?: Row[];
|
|
738
|
+
maxRows?: number;
|
|
739
|
+
maxGroups?: number;
|
|
740
|
+
/** Per-metric operation budget (default 10,000,000, allowing simple paired
|
|
741
|
+
* aggregates across the default one-million-row population limit). */
|
|
742
|
+
maxOperations?: number;
|
|
743
|
+
/** @deprecated Regex requires cached evaluateInput; this flag grants no execution permission. */ allowRegex?: boolean;
|
|
744
|
+
signal?: AbortSignal;
|
|
745
|
+
}
|
|
746
|
+
/** Typed lexicographic ordering with null placement independent of direction. */
|
|
747
|
+
declare function compareMetricValues(a: MetricValue, b: MetricValue): number;
|
|
748
|
+
declare function evaluateMetrics<Row>(rows: Row[], q: QueryState, schema: FieldSchema<Row>, options?: MetricEvaluationOptions<Row>): AggregationResult;
|
|
749
|
+
|
|
404
750
|
interface ApplyResult<Row> {
|
|
405
751
|
/** The page slice (after filter + sort + offset/limit). */
|
|
406
752
|
rows: Row[];
|
|
@@ -408,17 +754,42 @@ interface ApplyResult<Row> {
|
|
|
408
754
|
total: number;
|
|
409
755
|
}
|
|
410
756
|
/** Filter + multi-sort + paginate `rows` per `q`, resolving field types/paths
|
|
411
|
-
* from `schema`.
|
|
757
|
+
* from `schema`. Never mutates `rows`. Relative predicates use options.now or
|
|
758
|
+
* one captured Date.now(). Clauses/sorts on unknown fields are
|
|
412
759
|
* ignored (the backend already enforced its own allowlist). */
|
|
413
|
-
declare function applyQuery<Row>(rows: Row[], q: QueryState, schema: FieldSchema<Row
|
|
760
|
+
declare function applyQuery<Row>(rows: Row[], q: QueryState, schema: FieldSchema<Row>, options?: QueryEvaluationOptions): ApplyResult<Row>;
|
|
414
761
|
/** Does one row satisfy one clause? Exposed for the CellMenu preview + tests. */
|
|
415
|
-
declare function matchesClause<Row>(row: Row, clause: WhereClause, schema: FieldSchema<Row
|
|
762
|
+
declare function matchesClause<Row>(row: Row, clause: WhereClause, schema: FieldSchema<Row>, options?: QueryEvaluationOptions): boolean;
|
|
416
763
|
/** Client-side mirror of the backend GROUP BY (the executor for `clientRows`
|
|
417
764
|
* mode / the demo). Scope matches the server contract: the WHERE-filtered set
|
|
418
765
|
* only — ORDER BY / LIMIT / OFFSET are intentionally ignored, so a metric
|
|
419
766
|
* reflects every matching row, not the visible page. Must agree with
|
|
420
767
|
* backends/go's aggregate compile on op semantics. */
|
|
421
|
-
declare function applyAggregations<Row>(rows: Row[], q: QueryState, schema: FieldSchema<Row>): AggregationResult;
|
|
768
|
+
declare function applyAggregations<Row>(rows: Row[], q: QueryState, schema: FieldSchema<Row>, options?: MetricEvaluationOptions<Row>): AggregationResult;
|
|
769
|
+
|
|
770
|
+
/** Versioned row projection keeps server-computed payload separate from host row fields. */
|
|
771
|
+
interface ServerQueryV2 extends ServerQuery {
|
|
772
|
+
version: 2;
|
|
773
|
+
profile: string;
|
|
774
|
+
expectedRevisions: Record<string, string>;
|
|
775
|
+
planToken?: string;
|
|
776
|
+
/** Host must enforce this binding against the row response. */
|
|
777
|
+
snapshot?: string;
|
|
778
|
+
diagnostics: MetricDiagnostic[];
|
|
779
|
+
}
|
|
780
|
+
interface FetchRowsResultV2<Row> {
|
|
781
|
+
version: 2;
|
|
782
|
+
rows: Row[];
|
|
783
|
+
total: number;
|
|
784
|
+
computed: {
|
|
785
|
+
id: RowId;
|
|
786
|
+
values: RowComputedValues;
|
|
787
|
+
}[];
|
|
788
|
+
execution: ComputedExecution;
|
|
789
|
+
}
|
|
790
|
+
/** Project with a previously resolved server capability envelope. This does not
|
|
791
|
+
* authorize definitions or upload their source; the backend revalidates revisions. */
|
|
792
|
+
declare function toServerQueryV2<Row>(q: QueryState, schema: FieldSchema<Row>, execution: ComputedExecution): ServerQueryV2;
|
|
422
793
|
|
|
423
794
|
interface FetchRowsResult<Row> {
|
|
424
795
|
rows: Row[];
|
|
@@ -452,6 +823,9 @@ interface FieldStats {
|
|
|
452
823
|
max?: string | number;
|
|
453
824
|
}
|
|
454
825
|
interface Transport<Row = any> {
|
|
826
|
+
fetchRowsV2?(query: ServerQueryV2, signal?: AbortSignal): Promise<FetchRowsResultV2<Row>>;
|
|
827
|
+
metricCapabilities?: MetricCapabilities;
|
|
828
|
+
fetchMetrics?(query: MetricQuery, signal?: AbortSignal): Promise<AggregationResult>;
|
|
455
829
|
/** Run a server query. The only required method — a purely client-side table
|
|
456
830
|
* can omit a Transport entirely and rely on applyQuery over local rows. */
|
|
457
831
|
fetchRows(query: ServerQuery, signal?: AbortSignal): Promise<FetchRowsResult<Row>>;
|
|
@@ -506,119 +880,6 @@ declare function memoryStorageAdapter(): StorageAdapter;
|
|
|
506
880
|
* datasets. Falls back to no persistence when storage is unavailable. */
|
|
507
881
|
declare function localStorageAdapter(): StorageAdapter;
|
|
508
882
|
|
|
509
|
-
type FormulaType = Exclude<FieldType, "enum"> | "null";
|
|
510
|
-
type FormulaValue = string | number | boolean | string[] | null;
|
|
511
|
-
interface FormulaDiagnostic {
|
|
512
|
-
message: string;
|
|
513
|
-
from: number;
|
|
514
|
-
to: number;
|
|
515
|
-
}
|
|
516
|
-
type FormulaNode = {
|
|
517
|
-
kind: "literal";
|
|
518
|
-
value: FormulaValue;
|
|
519
|
-
from: number;
|
|
520
|
-
to: number;
|
|
521
|
-
} | {
|
|
522
|
-
kind: "field";
|
|
523
|
-
name: string;
|
|
524
|
-
valueType?: FormulaType;
|
|
525
|
-
from: number;
|
|
526
|
-
to: number;
|
|
527
|
-
} | {
|
|
528
|
-
kind: "call";
|
|
529
|
-
name: string;
|
|
530
|
-
args: FormulaNode[];
|
|
531
|
-
from: number;
|
|
532
|
-
to: number;
|
|
533
|
-
};
|
|
534
|
-
interface FormulaPlan {
|
|
535
|
-
ast: FormulaNode;
|
|
536
|
-
dependencies: string[];
|
|
537
|
-
type: FormulaType;
|
|
538
|
-
}
|
|
539
|
-
interface RegexInspection {
|
|
540
|
-
input: string;
|
|
541
|
-
start: number;
|
|
542
|
-
end: number;
|
|
543
|
-
groups: (string | null)[];
|
|
544
|
-
}
|
|
545
|
-
interface FormulaResult {
|
|
546
|
-
value: FormulaValue;
|
|
547
|
-
error?: string;
|
|
548
|
-
regex?: RegexInspection;
|
|
549
|
-
}
|
|
550
|
-
interface FormulaFunction {
|
|
551
|
-
name: string;
|
|
552
|
-
signature: string;
|
|
553
|
-
description: string;
|
|
554
|
-
min: number;
|
|
555
|
-
max: number;
|
|
556
|
-
result: FormulaType | "branch";
|
|
557
|
-
args: FormulaType[] | "any";
|
|
558
|
-
}
|
|
559
|
-
declare const FORMULA_FUNCTIONS: FormulaFunction[];
|
|
560
|
-
declare class FormulaError extends Error {
|
|
561
|
-
from: number;
|
|
562
|
-
to: number;
|
|
563
|
-
constructor(message: string, from?: number, to?: number);
|
|
564
|
-
}
|
|
565
|
-
declare function compileFormula(source: string, fields: readonly {
|
|
566
|
-
name: string;
|
|
567
|
-
type: FieldType;
|
|
568
|
-
}[], resolve?: (name: string) => FormulaPlan | undefined): FormulaPlan;
|
|
569
|
-
/** Self-contained runtime: serialized into the browser worker, never user code. */
|
|
570
|
-
declare function formulaRuntime(ast: FormulaNode, inputs: Record<string, FormulaValue>): FormulaResult;
|
|
571
|
-
|
|
572
|
-
/** Reserved field token namespace. Query state stores IDs, never formula source. */
|
|
573
|
-
declare const COMPUTED_PREFIX = "@computed/";
|
|
574
|
-
declare const computedFieldName: (id: string) => string;
|
|
575
|
-
declare const isComputedField: (name: string) => boolean;
|
|
576
|
-
interface ComputedColumn {
|
|
577
|
-
id: string;
|
|
578
|
-
label: string;
|
|
579
|
-
expression: {
|
|
580
|
-
language: "qt-expr";
|
|
581
|
-
version: 1;
|
|
582
|
-
source: string;
|
|
583
|
-
};
|
|
584
|
-
/** Optimistic concurrency token issued by the store. */
|
|
585
|
-
revision: string;
|
|
586
|
-
}
|
|
587
|
-
type ComputedColumnDraft = Omit<ComputedColumn, "revision">;
|
|
588
|
-
interface ComputedColumnStore {
|
|
589
|
-
/** Return the complete authorized catalogue for this dataset. */
|
|
590
|
-
list(dataset: string, signal?: AbortSignal): Promise<ComputedColumn[]>;
|
|
591
|
-
/** null creates a new ID; updates must match the current revision. */
|
|
592
|
-
save(dataset: string, column: ComputedColumnDraft, expectedRevision: string | null): Promise<ComputedColumn>;
|
|
593
|
-
/** Optional push invalidation, including changes from other sessions. */
|
|
594
|
-
subscribe?(dataset: string, listener: () => void): () => void;
|
|
595
|
-
}
|
|
596
|
-
declare function validateComputedColumn(input: unknown): ComputedColumn;
|
|
597
|
-
declare function memoryComputedColumnStore(seed?: Record<string, ComputedColumn[]>): ComputedColumnStore;
|
|
598
|
-
/** REST adapter. The implementing server owns authorization and DB persistence.
|
|
599
|
-
* GET base?dataset=… → ComputedColumn[]
|
|
600
|
-
* PUT base?dataset=… → {column, expectedRevision} → ComputedColumn
|
|
601
|
-
* Return HTTP 409 for a stale revision. No formula evaluation happens there. */
|
|
602
|
-
declare function httpComputedColumnStore(base: string, fetcher?: typeof fetch): ComputedColumnStore;
|
|
603
|
-
interface ComputedCellError {
|
|
604
|
-
computedError: string;
|
|
605
|
-
}
|
|
606
|
-
declare function isComputedCellError(value: unknown): value is ComputedCellError;
|
|
607
|
-
interface PreviewGroup {
|
|
608
|
-
inputs: FormulaValue[];
|
|
609
|
-
result: FormulaResult;
|
|
610
|
-
count: number;
|
|
611
|
-
}
|
|
612
|
-
interface ColumnPreview {
|
|
613
|
-
dependencies: string[];
|
|
614
|
-
groups: PreviewGroup[];
|
|
615
|
-
processed: number;
|
|
616
|
-
total: number;
|
|
617
|
-
nulls: number;
|
|
618
|
-
errors: number;
|
|
619
|
-
}
|
|
620
|
-
declare function groupPreview(dependencies: string[], inputs: Record<string, FormulaValue>[], results: FormulaResult[], total: number): ColumnPreview;
|
|
621
|
-
|
|
622
883
|
/** Compile an explicitly identified editor into existing CNF predicates. */
|
|
623
884
|
declare function createSetFilter(field: string, mode: SetFilterMode, values: string[], id: string): WhereTerm[];
|
|
624
885
|
/** Remove presentation metadata at transport boundaries, including inner literals. */
|
|
@@ -632,4 +893,23 @@ declare function readSetFilter(where: WhereTerm[], index: number): {
|
|
|
632
893
|
/** Canonicalize consumer-owned value aliases without disconnecting editor metadata. */
|
|
633
894
|
declare function mapSetFilterValues(where: WhereTerm[], canonicalize: (field: string, value: string) => string): WhereTerm[];
|
|
634
895
|
|
|
635
|
-
|
|
896
|
+
declare function percentile(sorted: number[], p: number): number | null;
|
|
897
|
+
declare function boxSummary(values: number[], whiskers?: "minmax" | "tukey"): {
|
|
898
|
+
n: number;
|
|
899
|
+
min: number;
|
|
900
|
+
q1: number;
|
|
901
|
+
median: number;
|
|
902
|
+
q3: number;
|
|
903
|
+
max: number;
|
|
904
|
+
mean: number;
|
|
905
|
+
low: number;
|
|
906
|
+
high: number;
|
|
907
|
+
outliers: number[];
|
|
908
|
+
outlierCount: number;
|
|
909
|
+
whiskers: "minmax" | "tukey";
|
|
910
|
+
method: "exact-linear";
|
|
911
|
+
} | null;
|
|
912
|
+
declare function histogramEdges(values: number[], bins: number): number[];
|
|
913
|
+
declare function histogramCounts(values: number[], edges: number[]): number[];
|
|
914
|
+
|
|
915
|
+
export { AGG_OPS_BY_TYPE, AGG_OPS_NEEDING_FIELD, type AggOp, type AggregateConfig, type AggregationBucket, type AggregationClause, type AggregationRequest, type AggregationResult, type AggregationResultEntry, type Align, type ApplyResult, type BackendField, COMPUTED_PREFIX, type CellRenderer, type ColumnPreview, type ComputedCellError, type ComputedColumn, type ComputedColumnDraft, type ComputedColumnStore, type ComputedExecution, type ComputedFieldCapabilities, type DerivedField, type DistinctValuesQuery, type DistinctValuesResult, EMPTY_QUERY, FORMULA_FUNCTIONS, type FetchRowsResult, type FetchRowsResultV2, type FieldDef, type FieldSchema, type FieldSource, type FieldStats, type FieldType, type FilterConfig, type FilterOp, type FilterValues, type FormulaDiagnostic, FormulaError, type FormulaFunction, type FormulaNode, type FormulaPlan, type FormulaResult, type FormulaType, type FormulaValue, MAX_AGGREGATIONS, MAX_GROUP_BY_FIELDS, MAX_ORDER_BY_TERMS, MAX_QUERY_LIMIT, MAX_QUERY_OFFSET, MAX_QUERY_TOKEN_LENGTH, MAX_RELATIVE_TIME_MS, MAX_SELECT_COLUMNS, MAX_WHERE_CLAUSES, type MetricAxisScale, type MetricBoxSummary, type MetricCapabilities, type MetricDiagnostic, type MetricDisplay, type MetricDistribution, type MetricDistributionResult, type MetricEvaluationOptions, type MetricKey, type MetricLayout, type MetricPlan, type MetricQuery, type MetricReductionOptions, type MetricScope, type MetricSort, type MetricValue, type MetricValueFormat, NULLARY_OPS, OPS_BY_TYPE, type OpChoice, type OpPair, type OrGroup, type OrderByClause, type PreviewGroup, type QueryEvaluationOptions, type QueryState, type RegexInspection, type RowComputedValues, type RowId, type SavedQuery, type SelectColumn, type SelectConfig, type ServerQuery, type ServerQueryV2, type SetFilterMetadata, type SetFilterMode, type SortConfig, type StorageAdapter, type Transport, type WhereClause, type WhereTerm, aggOpAllowedForType, aggOpNeedsField, aggOpsForField, applyAggregations, applyQuery, boxSummary, coerceValue, compareMetricValues, compileFormula, compileMetric, computedFieldName, createSetFilter, decodeQuery, encodeQuery, evaluateMetricReductions, evaluateMetrics, filterValues, formulaRuntime, groupPreview, hasRegex, histogramCounts, histogramEdges, httpComputedColumnStore, indexFields, isComputedCellError, isComputedField, isFilterable, isGroupable, isMeasurable, isNegativePredicate, isOrGroup, isPushdownFilter, isSelectable, isSortable, loadSchema, localStorageAdapter, mapSetFilterValues, matchesClause, memoryComputedColumnStore, memoryStorageAdapter, metricAggregatePlans, metricComputationKey, metricDependencies, metricExpression, metricPlans, negateClause, normalizeQueryState, opAllowedForType, opPairsForField, opsForField, parseRelativeDuration, percentile, predicatesOf, queriesEqual, readFieldValue, readSetFilter, reduceMetricValues, resolveFieldName, selectMetricShownRows, selectedFields, stripSetFilter, toAggregationQuery, toMetricQuery, toServerQuery, toServerQueryV2, validateComputedColumn, validateMetric };
|