@pythia-software/query-table-core 0.1.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/LICENSE +21 -0
- package/README.md +44 -0
- package/dist/index.d.ts +422 -0
- package/dist/index.js +859 -0
- package/dist/index.js.map +1 -0
- package/package.json +35 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,859 @@
|
|
|
1
|
+
// src/query.ts
|
|
2
|
+
var EMPTY_QUERY = {
|
|
3
|
+
select: [],
|
|
4
|
+
where: [],
|
|
5
|
+
orderBy: [],
|
|
6
|
+
limit: 100,
|
|
7
|
+
offset: 0
|
|
8
|
+
};
|
|
9
|
+
var MAX_QUERY_LIMIT = 1e3;
|
|
10
|
+
var MAX_QUERY_OFFSET = 1e6;
|
|
11
|
+
var MAX_SELECT_COLUMNS = 200;
|
|
12
|
+
var MAX_WHERE_CLAUSES = 100;
|
|
13
|
+
var MAX_ORDER_BY_TERMS = 20;
|
|
14
|
+
var MAX_AGGREGATIONS = 20;
|
|
15
|
+
var MAX_GROUP_BY_FIELDS = 20;
|
|
16
|
+
var MAX_QUERY_TOKEN_LENGTH = 2 * 1024 * 1024;
|
|
17
|
+
var MAX_FIELD_NAME_LENGTH = 256;
|
|
18
|
+
var MAX_FILTER_VALUE_LENGTH = 1e4;
|
|
19
|
+
var MAX_LABEL_LENGTH = 1e3;
|
|
20
|
+
var MIN_COLUMN_WIDTH = 24;
|
|
21
|
+
var MAX_COLUMN_WIDTH = 2e3;
|
|
22
|
+
var FILTER_OPS = /* @__PURE__ */ new Set([
|
|
23
|
+
"=",
|
|
24
|
+
"!=",
|
|
25
|
+
">",
|
|
26
|
+
">=",
|
|
27
|
+
"<",
|
|
28
|
+
"<=",
|
|
29
|
+
"contains",
|
|
30
|
+
"starts_with",
|
|
31
|
+
"ends_with",
|
|
32
|
+
"includes",
|
|
33
|
+
"is_null",
|
|
34
|
+
"is_not_null"
|
|
35
|
+
]);
|
|
36
|
+
var AGG_OPS = /* @__PURE__ */ new Set(["count", "count_distinct", "sum", "avg", "min", "max"]);
|
|
37
|
+
function isRecord(value) {
|
|
38
|
+
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
39
|
+
}
|
|
40
|
+
function boundedString(value, maxLength) {
|
|
41
|
+
return typeof value === "string" && value.length > 0 && value.length <= maxLength ? value : null;
|
|
42
|
+
}
|
|
43
|
+
function boundedInteger(value, fallback, min, max) {
|
|
44
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
|
|
45
|
+
return Math.max(min, Math.min(max, Math.round(value)));
|
|
46
|
+
}
|
|
47
|
+
function normalizeQueryState(input, fallback = EMPTY_QUERY) {
|
|
48
|
+
const raw = isRecord(input) ? input : {};
|
|
49
|
+
const select = [];
|
|
50
|
+
if (Array.isArray(raw.select)) {
|
|
51
|
+
for (const item of raw.select.slice(0, MAX_SELECT_COLUMNS)) {
|
|
52
|
+
if (!isRecord(item)) continue;
|
|
53
|
+
const field = boundedString(item.field, MAX_FIELD_NAME_LENGTH);
|
|
54
|
+
if (!field) continue;
|
|
55
|
+
const column = { field };
|
|
56
|
+
if (typeof item.width === "number" && Number.isFinite(item.width)) {
|
|
57
|
+
column.width = boundedInteger(item.width, MIN_COLUMN_WIDTH, MIN_COLUMN_WIDTH, MAX_COLUMN_WIDTH);
|
|
58
|
+
}
|
|
59
|
+
select.push(column);
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
select.push(...fallback.select.map((column) => ({ ...column })));
|
|
63
|
+
}
|
|
64
|
+
const where = [];
|
|
65
|
+
if (Array.isArray(raw.where)) {
|
|
66
|
+
for (const item of raw.where.slice(0, MAX_WHERE_CLAUSES)) {
|
|
67
|
+
if (!isRecord(item)) continue;
|
|
68
|
+
const field = boundedString(item.field, MAX_FIELD_NAME_LENGTH);
|
|
69
|
+
const value = typeof item.value === "string" && item.value.length <= MAX_FILTER_VALUE_LENGTH ? item.value : null;
|
|
70
|
+
if (!field || typeof item.op !== "string" || !FILTER_OPS.has(item.op) || value == null) continue;
|
|
71
|
+
where.push({ field, op: item.op, value });
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
where.push(...fallback.where.map((clause) => ({ ...clause })));
|
|
75
|
+
}
|
|
76
|
+
const orderBy = [];
|
|
77
|
+
if (Array.isArray(raw.orderBy)) {
|
|
78
|
+
for (const item of raw.orderBy.slice(0, MAX_ORDER_BY_TERMS)) {
|
|
79
|
+
if (!isRecord(item)) continue;
|
|
80
|
+
const field = boundedString(item.field, MAX_FIELD_NAME_LENGTH);
|
|
81
|
+
if (!field || item.dir !== "asc" && item.dir !== "desc") continue;
|
|
82
|
+
const term = { field, dir: item.dir };
|
|
83
|
+
if (item.nulls === "first" || item.nulls === "last") term.nulls = item.nulls;
|
|
84
|
+
orderBy.push(term);
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
orderBy.push(...fallback.orderBy.map((term) => ({ ...term })));
|
|
88
|
+
}
|
|
89
|
+
const out = {
|
|
90
|
+
select,
|
|
91
|
+
where,
|
|
92
|
+
orderBy,
|
|
93
|
+
limit: boundedInteger(raw.limit, fallback.limit, 1, MAX_QUERY_LIMIT),
|
|
94
|
+
offset: boundedInteger(raw.offset, fallback.offset, 0, MAX_QUERY_OFFSET)
|
|
95
|
+
};
|
|
96
|
+
if (Array.isArray(raw.aggregations)) {
|
|
97
|
+
const aggregations = [];
|
|
98
|
+
for (const item of raw.aggregations.slice(0, MAX_AGGREGATIONS)) {
|
|
99
|
+
if (!isRecord(item)) continue;
|
|
100
|
+
const id = boundedString(item.id, MAX_FIELD_NAME_LENGTH);
|
|
101
|
+
if (!id || typeof item.op !== "string" || !AGG_OPS.has(item.op) || !Array.isArray(item.groupBy)) continue;
|
|
102
|
+
const groupBy = item.groupBy.slice(0, MAX_GROUP_BY_FIELDS).map((field2) => boundedString(field2, MAX_FIELD_NAME_LENGTH)).filter((field2) => field2 != null);
|
|
103
|
+
const aggregation = { id, op: item.op, groupBy };
|
|
104
|
+
const field = boundedString(item.field, MAX_FIELD_NAME_LENGTH);
|
|
105
|
+
const label = boundedString(item.label, MAX_LABEL_LENGTH);
|
|
106
|
+
if (field) aggregation.field = field;
|
|
107
|
+
if (label) aggregation.label = label;
|
|
108
|
+
aggregations.push(aggregation);
|
|
109
|
+
}
|
|
110
|
+
if (aggregations.length > 0) out.aggregations = aggregations;
|
|
111
|
+
} else if (fallback.aggregations?.length) {
|
|
112
|
+
out.aggregations = fallback.aggregations.map((aggregation) => ({ ...aggregation, groupBy: [...aggregation.groupBy] }));
|
|
113
|
+
}
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
function queriesEqual(a, b) {
|
|
117
|
+
if (a === b) return true;
|
|
118
|
+
if (a.limit !== b.limit || a.offset !== b.offset) return false;
|
|
119
|
+
if (!sameArray(a.where, b.where)) return false;
|
|
120
|
+
if (!sameArray(a.orderBy, b.orderBy)) return false;
|
|
121
|
+
if (!sameArray(a.select, b.select)) return false;
|
|
122
|
+
if (!sameArray(a.aggregations ?? [], b.aggregations ?? [])) return false;
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
function sameArray(a, b) {
|
|
126
|
+
if (a.length !== b.length) return false;
|
|
127
|
+
for (let i = 0; i < a.length; i++) {
|
|
128
|
+
if (JSON.stringify(a[i]) !== JSON.stringify(b[i])) return false;
|
|
129
|
+
}
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// src/schema.ts
|
|
134
|
+
function isFilterable(f) {
|
|
135
|
+
return f.filter?.enabled ?? f.source.kind === "backend";
|
|
136
|
+
}
|
|
137
|
+
function isPushdownFilter(f) {
|
|
138
|
+
return isFilterable(f) && (f.filter?.pushdown ?? f.source.kind === "backend");
|
|
139
|
+
}
|
|
140
|
+
function isSortable(f) {
|
|
141
|
+
return f.sort?.enabled ?? f.source.kind === "backend";
|
|
142
|
+
}
|
|
143
|
+
function isSelectable(f) {
|
|
144
|
+
return f.select?.enabled ?? true;
|
|
145
|
+
}
|
|
146
|
+
function filterValues(f) {
|
|
147
|
+
return f.filter?.values ?? { source: "autocomplete" };
|
|
148
|
+
}
|
|
149
|
+
function indexFields(schema) {
|
|
150
|
+
return new Map(schema.fields.map((f) => [f.name, f]));
|
|
151
|
+
}
|
|
152
|
+
function selectedFields(schema, q) {
|
|
153
|
+
const order = q.select.length > 0 ? q.select.map((c) => c.field) : (schema.defaultSelect ?? schema.fields.filter((f) => f.select?.default).map((f) => ({ field: f.name }))).map(
|
|
154
|
+
(c) => c.field
|
|
155
|
+
);
|
|
156
|
+
const seen = /* @__PURE__ */ new Set();
|
|
157
|
+
const out = [];
|
|
158
|
+
for (const name of order) {
|
|
159
|
+
if (seen.has(name)) continue;
|
|
160
|
+
const f = resolveField(schema, name);
|
|
161
|
+
if (!f || !isSelectable(f)) continue;
|
|
162
|
+
seen.add(name);
|
|
163
|
+
seen.add(f.name);
|
|
164
|
+
out.push(f);
|
|
165
|
+
}
|
|
166
|
+
return out;
|
|
167
|
+
}
|
|
168
|
+
function resolveField(schema, field) {
|
|
169
|
+
const token = field.trim();
|
|
170
|
+
if (!token) return void 0;
|
|
171
|
+
const byName = indexFields(schema);
|
|
172
|
+
const byExactName = byName.get(token);
|
|
173
|
+
if (byExactName) return byExactName;
|
|
174
|
+
const needle = token.toLowerCase();
|
|
175
|
+
for (const f of schema.fields) {
|
|
176
|
+
if (f.name.toLowerCase() === needle) return f;
|
|
177
|
+
if (aliasMatches(f, needle)) return f;
|
|
178
|
+
}
|
|
179
|
+
return void 0;
|
|
180
|
+
}
|
|
181
|
+
function resolveFieldName(schema, field) {
|
|
182
|
+
return resolveField(schema, field)?.name;
|
|
183
|
+
}
|
|
184
|
+
function aliasTermsFor(field) {
|
|
185
|
+
const out = [];
|
|
186
|
+
const seen = /* @__PURE__ */ new Set();
|
|
187
|
+
const add = (raw) => {
|
|
188
|
+
if (typeof raw !== "string") return;
|
|
189
|
+
const value = raw.trim();
|
|
190
|
+
if (!value) return;
|
|
191
|
+
const key = value.toLowerCase();
|
|
192
|
+
if (seen.has(key)) return;
|
|
193
|
+
seen.add(key);
|
|
194
|
+
out.push(value);
|
|
195
|
+
};
|
|
196
|
+
add(field.alias);
|
|
197
|
+
if (field.aliases) {
|
|
198
|
+
for (const alias of field.aliases) add(alias);
|
|
199
|
+
}
|
|
200
|
+
return out;
|
|
201
|
+
}
|
|
202
|
+
function aliasMatches(field, needle) {
|
|
203
|
+
for (const alias of aliasTermsFor(field)) {
|
|
204
|
+
if (alias.toLowerCase() === needle) return true;
|
|
205
|
+
}
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
function readFieldValue(field, row) {
|
|
209
|
+
if (row == null) return null;
|
|
210
|
+
if (field.source.kind === "derived" && field.source.accessor) {
|
|
211
|
+
return field.source.accessor(row);
|
|
212
|
+
}
|
|
213
|
+
const path = field.source.kind === "backend" && field.source.path || field.name;
|
|
214
|
+
const r = row;
|
|
215
|
+
if (path in r) return r[path] ?? null;
|
|
216
|
+
let cur = row;
|
|
217
|
+
for (const part of path.split(".")) {
|
|
218
|
+
if (cur == null || typeof cur !== "object") return null;
|
|
219
|
+
cur = cur[part];
|
|
220
|
+
}
|
|
221
|
+
return cur ?? null;
|
|
222
|
+
}
|
|
223
|
+
function loadSchema(doc) {
|
|
224
|
+
const d = doc;
|
|
225
|
+
if (!d || typeof d !== "object") throw new Error("schema: document is not an object");
|
|
226
|
+
if (typeof d.name !== "string") throw new Error("schema: missing `name`");
|
|
227
|
+
if (typeof d.idField !== "string") throw new Error("schema: missing `idField`");
|
|
228
|
+
if (!Array.isArray(d.fields) || d.fields.length === 0) throw new Error("schema: `fields` must be a non-empty array");
|
|
229
|
+
const fields = d.fields.map((raw, i) => projectField(raw, i));
|
|
230
|
+
return {
|
|
231
|
+
name: d.name,
|
|
232
|
+
idField: d.idField,
|
|
233
|
+
fields,
|
|
234
|
+
defaultSort: d.defaultSort,
|
|
235
|
+
defaultSelect: d.defaultSelect,
|
|
236
|
+
defaultLimit: d.defaultLimit
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
function projectField(raw, i) {
|
|
240
|
+
if (!raw || typeof raw.name !== "string") throw new Error(`schema: field[${i}] missing \`name\``);
|
|
241
|
+
if (typeof raw.label !== "string") throw new Error(`schema: field ${raw.name} missing \`label\``);
|
|
242
|
+
if (typeof raw.type !== "string") throw new Error(`schema: field ${raw.name} missing \`type\``);
|
|
243
|
+
const rs = raw.source ?? (raw.bindings ? "backend" : "derived");
|
|
244
|
+
let source;
|
|
245
|
+
if (typeof rs === "string") {
|
|
246
|
+
source = rs === "derived" ? { kind: "derived" } : { kind: "backend" };
|
|
247
|
+
} else if (rs.kind === "derived") {
|
|
248
|
+
source = { kind: "derived" };
|
|
249
|
+
} else {
|
|
250
|
+
source = { kind: "backend", path: rs.path, synthetic: rs.synthetic };
|
|
251
|
+
}
|
|
252
|
+
return {
|
|
253
|
+
name: raw.name,
|
|
254
|
+
label: raw.label,
|
|
255
|
+
type: raw.type,
|
|
256
|
+
source,
|
|
257
|
+
filter: raw.filter,
|
|
258
|
+
sort: raw.sort,
|
|
259
|
+
select: raw.select,
|
|
260
|
+
aggregate: raw.aggregate,
|
|
261
|
+
group: raw.group,
|
|
262
|
+
alias: raw.alias,
|
|
263
|
+
aliases: normalizeAliases(raw.aliases, raw.alias),
|
|
264
|
+
render: raw.render
|
|
265
|
+
// string key from JSON; consumer may override with a fn
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
function normalizeAliases(rawAliases, rawAlias) {
|
|
269
|
+
const aliases = [];
|
|
270
|
+
const seen = /* @__PURE__ */ new Set();
|
|
271
|
+
const add = (raw) => {
|
|
272
|
+
if (typeof raw !== "string") return;
|
|
273
|
+
const value = raw.trim();
|
|
274
|
+
if (!value) return;
|
|
275
|
+
const key = value.toLowerCase();
|
|
276
|
+
if (seen.has(key)) return;
|
|
277
|
+
seen.add(key);
|
|
278
|
+
aliases.push(value);
|
|
279
|
+
};
|
|
280
|
+
add(rawAlias);
|
|
281
|
+
if (Array.isArray(rawAliases)) {
|
|
282
|
+
for (const item of rawAliases) add(item);
|
|
283
|
+
}
|
|
284
|
+
return aliases;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// src/ops.ts
|
|
288
|
+
var NULLITY = ["is_null", "is_not_null"];
|
|
289
|
+
var OPS_BY_TYPE = {
|
|
290
|
+
text: ["=", "!=", "contains", "starts_with", "ends_with", ...NULLITY],
|
|
291
|
+
enum: ["=", "!=", ...NULLITY],
|
|
292
|
+
number: ["=", "!=", ">", ">=", "<", "<=", ...NULLITY],
|
|
293
|
+
datetime: ["=", "!=", ">", ">=", "<", "<=", ...NULLITY],
|
|
294
|
+
bool: ["=", "!=", ...NULLITY],
|
|
295
|
+
textarray: ["includes", ...NULLITY]
|
|
296
|
+
};
|
|
297
|
+
var NULLARY_OPS = new Set(NULLITY);
|
|
298
|
+
function opsForField(field) {
|
|
299
|
+
return field.filter?.ops ?? OPS_BY_TYPE[field.type];
|
|
300
|
+
}
|
|
301
|
+
function opAllowedForType(type, op) {
|
|
302
|
+
return OPS_BY_TYPE[type].includes(op);
|
|
303
|
+
}
|
|
304
|
+
function coerceValue(type, raw) {
|
|
305
|
+
switch (type) {
|
|
306
|
+
case "number": {
|
|
307
|
+
const n = Number(raw);
|
|
308
|
+
if (raw.trim() === "" || Number.isNaN(n)) throw new Error(`not a number: ${JSON.stringify(raw)}`);
|
|
309
|
+
return n;
|
|
310
|
+
}
|
|
311
|
+
case "bool": {
|
|
312
|
+
const v = raw.toLowerCase();
|
|
313
|
+
if (v === "true" || v === "1" || v === "t") return true;
|
|
314
|
+
if (v === "false" || v === "0" || v === "f") return false;
|
|
315
|
+
throw new Error(`not a bool: ${JSON.stringify(raw)}`);
|
|
316
|
+
}
|
|
317
|
+
case "datetime": {
|
|
318
|
+
const ms = Date.parse(raw);
|
|
319
|
+
if (Number.isNaN(ms)) throw new Error(`not a datetime: ${JSON.stringify(raw)}`);
|
|
320
|
+
return ms;
|
|
321
|
+
}
|
|
322
|
+
default:
|
|
323
|
+
return raw;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// src/agg.ts
|
|
328
|
+
var AGG_OPS_BY_TYPE = {
|
|
329
|
+
number: ["count", "count_distinct", "sum", "avg", "min", "max"],
|
|
330
|
+
datetime: ["count", "count_distinct", "min", "max"],
|
|
331
|
+
enum: ["count", "count_distinct", "min", "max"],
|
|
332
|
+
text: ["count", "count_distinct", "min", "max"],
|
|
333
|
+
bool: ["count", "count_distinct"],
|
|
334
|
+
textarray: ["count"]
|
|
335
|
+
};
|
|
336
|
+
var AGG_OPS_NEEDING_FIELD = /* @__PURE__ */ new Set([
|
|
337
|
+
"count_distinct",
|
|
338
|
+
"sum",
|
|
339
|
+
"avg",
|
|
340
|
+
"min",
|
|
341
|
+
"max"
|
|
342
|
+
]);
|
|
343
|
+
function aggOpNeedsField(op) {
|
|
344
|
+
return AGG_OPS_NEEDING_FIELD.has(op);
|
|
345
|
+
}
|
|
346
|
+
function aggOpsForField(field) {
|
|
347
|
+
return field.aggregate?.ops ?? AGG_OPS_BY_TYPE[field.type];
|
|
348
|
+
}
|
|
349
|
+
function aggOpAllowedForType(type, op) {
|
|
350
|
+
return AGG_OPS_BY_TYPE[type].includes(op);
|
|
351
|
+
}
|
|
352
|
+
function isMeasurable(field) {
|
|
353
|
+
if (field.source.kind !== "backend") return false;
|
|
354
|
+
if (field.aggregate?.measure != null) return field.aggregate.measure;
|
|
355
|
+
return aggOpsForField(field).length > 0;
|
|
356
|
+
}
|
|
357
|
+
function isGroupable(field) {
|
|
358
|
+
if (field.source.kind !== "backend") return false;
|
|
359
|
+
if (field.aggregate?.groupable != null) return field.aggregate.groupable;
|
|
360
|
+
return field.type === "enum" || field.type === "text" || field.type === "bool";
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// src/encode.ts
|
|
364
|
+
function toBase64Url(json) {
|
|
365
|
+
let b64;
|
|
366
|
+
if (typeof Buffer !== "undefined") {
|
|
367
|
+
b64 = Buffer.from(json, "utf8").toString("base64");
|
|
368
|
+
} else {
|
|
369
|
+
const bytes = new TextEncoder().encode(json);
|
|
370
|
+
let bin = "";
|
|
371
|
+
for (const b of bytes) bin += String.fromCharCode(b);
|
|
372
|
+
b64 = btoa(bin);
|
|
373
|
+
}
|
|
374
|
+
return b64.replace(/=+$/, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
375
|
+
}
|
|
376
|
+
function fromBase64Url(s) {
|
|
377
|
+
const b64 = s.replace(/-/g, "+").replace(/_/g, "/") + "===".slice((s.length + 3) % 4);
|
|
378
|
+
if (typeof Buffer !== "undefined") return Buffer.from(b64, "base64").toString("utf8");
|
|
379
|
+
const bin = atob(b64);
|
|
380
|
+
const bytes = Uint8Array.from(bin, (c) => c.charCodeAt(0));
|
|
381
|
+
return new TextDecoder().decode(bytes);
|
|
382
|
+
}
|
|
383
|
+
function encodeQuery(q) {
|
|
384
|
+
q = normalizeQueryState(q);
|
|
385
|
+
const c = {};
|
|
386
|
+
if (q.select.length)
|
|
387
|
+
c.s = q.select.map((col) => col.width != null ? [col.field, col.width] : [col.field]);
|
|
388
|
+
if (q.where.length) c.w = q.where;
|
|
389
|
+
if (q.orderBy.length) c.o = q.orderBy;
|
|
390
|
+
c.l = q.limit;
|
|
391
|
+
if (q.offset) c.f = q.offset;
|
|
392
|
+
if (q.aggregations?.length) c.g = q.aggregations;
|
|
393
|
+
if (Object.keys(c).length === 0) return "";
|
|
394
|
+
return toBase64Url(JSON.stringify(c));
|
|
395
|
+
}
|
|
396
|
+
function decodeQuery(token) {
|
|
397
|
+
if (!token) return { ...EMPTY_QUERY };
|
|
398
|
+
if (token.length > MAX_QUERY_TOKEN_LENGTH) return { ...EMPTY_QUERY };
|
|
399
|
+
try {
|
|
400
|
+
const c = JSON.parse(fromBase64Url(token));
|
|
401
|
+
const out = normalizeQueryState({
|
|
402
|
+
select: normalizeSelect(c.s ?? c.c),
|
|
403
|
+
where: Array.isArray(c.w) ? c.w : [],
|
|
404
|
+
orderBy: normalizeOrderBy(c.o),
|
|
405
|
+
limit: c.l,
|
|
406
|
+
offset: c.f,
|
|
407
|
+
aggregations: c.g
|
|
408
|
+
});
|
|
409
|
+
return out;
|
|
410
|
+
} catch {
|
|
411
|
+
return { ...EMPTY_QUERY };
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
function normalizeSelect(s) {
|
|
415
|
+
if (!Array.isArray(s)) return [];
|
|
416
|
+
return s.map((item) => {
|
|
417
|
+
if (typeof item === "string") return { field: item };
|
|
418
|
+
if (Array.isArray(item) && typeof item[0] === "string") {
|
|
419
|
+
return typeof item[1] === "number" ? { field: item[0], width: item[1] } : { field: item[0] };
|
|
420
|
+
}
|
|
421
|
+
return null;
|
|
422
|
+
}).filter((x) => x != null);
|
|
423
|
+
}
|
|
424
|
+
function normalizeOrderBy(o) {
|
|
425
|
+
if (!o) return [];
|
|
426
|
+
if (Array.isArray(o)) return o;
|
|
427
|
+
return [o];
|
|
428
|
+
}
|
|
429
|
+
function toServerQuery(q, schema) {
|
|
430
|
+
q = normalizeQueryState(q);
|
|
431
|
+
const byName = indexFields(schema);
|
|
432
|
+
const resolveField2 = (name) => resolveFieldName(schema, name) ?? name;
|
|
433
|
+
const where = q.where.filter((cl) => {
|
|
434
|
+
const field = resolveField2(cl.field);
|
|
435
|
+
const f = byName.get(field);
|
|
436
|
+
return f != null && isPushdownFilter(f);
|
|
437
|
+
});
|
|
438
|
+
const orderBy = [];
|
|
439
|
+
for (const term of q.orderBy) {
|
|
440
|
+
const field = resolveField2(term.field);
|
|
441
|
+
const f = byName.get(field);
|
|
442
|
+
if (!f || !isSortable(f) || f.source.kind !== "backend") continue;
|
|
443
|
+
orderBy.push({ ...term, field: f.sort?.field ?? f.name });
|
|
444
|
+
}
|
|
445
|
+
const select = new Set(
|
|
446
|
+
selectedFields(schema, q).filter((f) => f.source.kind === "backend").map((f) => f.name)
|
|
447
|
+
);
|
|
448
|
+
select.add(schema.idField);
|
|
449
|
+
for (const cl of q.where) {
|
|
450
|
+
const field = resolveField2(cl.field);
|
|
451
|
+
const f = byName.get(field);
|
|
452
|
+
if (f && isFilterable(f) && !isPushdownFilter(f) && f.source.kind === "backend") select.add(field);
|
|
453
|
+
}
|
|
454
|
+
return { select: [...select], where, orderBy, limit: q.limit, offset: q.offset };
|
|
455
|
+
}
|
|
456
|
+
function toAggregationQuery(q, schema) {
|
|
457
|
+
q = normalizeQueryState(q);
|
|
458
|
+
const byName = indexFields(schema);
|
|
459
|
+
const resolveField2 = (name) => resolveFieldName(schema, name) ?? name;
|
|
460
|
+
const where = q.where.filter((cl) => {
|
|
461
|
+
const f = byName.get(resolveField2(cl.field));
|
|
462
|
+
return f != null && isPushdownFilter(f);
|
|
463
|
+
});
|
|
464
|
+
const isBackend = (name) => {
|
|
465
|
+
if (name == null) return true;
|
|
466
|
+
const field = resolveField2(name);
|
|
467
|
+
const f = byName.get(field);
|
|
468
|
+
return f != null && f.source.kind === "backend";
|
|
469
|
+
};
|
|
470
|
+
const aggregations = (q.aggregations ?? []).filter(
|
|
471
|
+
(a) => isBackend(a.field) && a.groupBy.every((g) => isBackend(g))
|
|
472
|
+
);
|
|
473
|
+
return { where, aggregations };
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// src/apply.ts
|
|
477
|
+
function applyQuery(rows, q, schema) {
|
|
478
|
+
const byName = indexFields(schema);
|
|
479
|
+
const resolveField2 = (name) => resolveFieldName(schema, name) ?? name;
|
|
480
|
+
const whereClauses = q.where.map((clause) => ({ ...clause, field: resolveField2(clause.field) }));
|
|
481
|
+
const orderBy = q.orderBy.map((term) => ({ ...term, field: resolveField2(term.field) }));
|
|
482
|
+
let out = rows.filter((row) => whereClauses.every((cl) => matchesWith(byName, row, cl)));
|
|
483
|
+
const total = out.length;
|
|
484
|
+
if (orderBy.length) {
|
|
485
|
+
const terms = orderBy.map((t) => ({ field: byName.get(t.field), dir: t.dir, nullsLast: (t.nulls ?? "last") === "last" })).filter((t) => t.field != null);
|
|
486
|
+
out = [...out].sort((a, b) => {
|
|
487
|
+
for (const t of terms) {
|
|
488
|
+
const av = readFieldValue(t.field, a);
|
|
489
|
+
const bv = readFieldValue(t.field, b);
|
|
490
|
+
const aNull = av == null;
|
|
491
|
+
const bNull = bv == null;
|
|
492
|
+
if (aNull || bNull) {
|
|
493
|
+
if (aNull && bNull) continue;
|
|
494
|
+
return (aNull ? 1 : -1) * (t.nullsLast ? 1 : -1);
|
|
495
|
+
}
|
|
496
|
+
const cmp = compare(av, bv);
|
|
497
|
+
if (cmp !== 0) return t.dir === "asc" ? cmp : -cmp;
|
|
498
|
+
}
|
|
499
|
+
return 0;
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
const start = q.offset > 0 ? q.offset : 0;
|
|
503
|
+
const end = q.limit > 0 ? start + q.limit : out.length;
|
|
504
|
+
return { rows: out.slice(start, end), total };
|
|
505
|
+
}
|
|
506
|
+
function matchesClause(row, clause, schema) {
|
|
507
|
+
return matchesWith(indexFields(schema), row, clause);
|
|
508
|
+
}
|
|
509
|
+
function applyAggregations(rows, q, schema) {
|
|
510
|
+
const byName = indexFields(schema);
|
|
511
|
+
const resolveField2 = (name) => resolveFieldName(schema, name) ?? name;
|
|
512
|
+
const whereClauses = q.where.map((clause) => ({ ...clause, field: resolveField2(clause.field) }));
|
|
513
|
+
const filtered = rows.filter((row) => whereClauses.every((cl) => matchesWith(byName, row, cl)));
|
|
514
|
+
const metrics = (q.aggregations ?? []).map((agg) => ({
|
|
515
|
+
id: agg.id,
|
|
516
|
+
buckets: computeBuckets(filtered, agg, byName, resolveField2)
|
|
517
|
+
}));
|
|
518
|
+
return { metrics };
|
|
519
|
+
}
|
|
520
|
+
function computeBuckets(rows, agg, byName, resolveField2) {
|
|
521
|
+
const groupFields = agg.groupBy.map((n) => byName.get(resolveField2(n)));
|
|
522
|
+
const measure = agg.field ? byName.get(resolveField2(agg.field)) : void 0;
|
|
523
|
+
const groups = /* @__PURE__ */ new Map();
|
|
524
|
+
for (const row of rows) {
|
|
525
|
+
const keys = groupFields.map((f) => {
|
|
526
|
+
if (!f) return null;
|
|
527
|
+
const v = readFieldValue(f, row);
|
|
528
|
+
return v == null || v === "" ? null : String(v);
|
|
529
|
+
});
|
|
530
|
+
const k = JSON.stringify(keys);
|
|
531
|
+
let g = groups.get(k);
|
|
532
|
+
if (!g) {
|
|
533
|
+
g = { keys, rows: [] };
|
|
534
|
+
groups.set(k, g);
|
|
535
|
+
}
|
|
536
|
+
g.rows.push(row);
|
|
537
|
+
}
|
|
538
|
+
const buckets = [...groups.values()].map((g) => ({
|
|
539
|
+
keys: g.keys,
|
|
540
|
+
count: g.rows.length,
|
|
541
|
+
value: aggValue(agg.op, measure, g.rows)
|
|
542
|
+
}));
|
|
543
|
+
return sortBuckets(buckets);
|
|
544
|
+
}
|
|
545
|
+
function aggValue(op, measure, rows) {
|
|
546
|
+
if (op === "count") {
|
|
547
|
+
if (!measure) return rows.length;
|
|
548
|
+
let n = 0;
|
|
549
|
+
for (const r of rows) if (readFieldValue(measure, r) != null) n++;
|
|
550
|
+
return n;
|
|
551
|
+
}
|
|
552
|
+
if (!measure) return null;
|
|
553
|
+
const values = rows.map((r) => readFieldValue(measure, r)).filter((v) => v != null);
|
|
554
|
+
switch (op) {
|
|
555
|
+
case "count_distinct":
|
|
556
|
+
return new Set(values.map((v) => String(v))).size;
|
|
557
|
+
case "sum":
|
|
558
|
+
case "avg": {
|
|
559
|
+
let sum = 0;
|
|
560
|
+
let n = 0;
|
|
561
|
+
for (const v of values) {
|
|
562
|
+
const num = typeof v === "number" ? v : Number(v);
|
|
563
|
+
if (Number.isFinite(num)) {
|
|
564
|
+
sum += num;
|
|
565
|
+
n++;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
if (op === "avg") return n ? sum / n : null;
|
|
569
|
+
return n ? sum : null;
|
|
570
|
+
}
|
|
571
|
+
case "min":
|
|
572
|
+
case "max": {
|
|
573
|
+
let best;
|
|
574
|
+
for (const v of values) {
|
|
575
|
+
if (best === void 0) {
|
|
576
|
+
best = v;
|
|
577
|
+
continue;
|
|
578
|
+
}
|
|
579
|
+
const cmp = compare(v, best);
|
|
580
|
+
if (op === "min" ? cmp < 0 : cmp > 0) best = v;
|
|
581
|
+
}
|
|
582
|
+
return best ?? null;
|
|
583
|
+
}
|
|
584
|
+
default:
|
|
585
|
+
return null;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
function sortBuckets(buckets) {
|
|
589
|
+
return [...buckets].sort((a, b) => {
|
|
590
|
+
const av = typeof a.value === "number" ? a.value : a.count;
|
|
591
|
+
const bv = typeof b.value === "number" ? b.value : b.count;
|
|
592
|
+
if (av !== bv) return bv - av;
|
|
593
|
+
return JSON.stringify(a.keys).localeCompare(JSON.stringify(b.keys));
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
function matchesWith(byName, row, clause) {
|
|
597
|
+
const field = byName.get(clause.field);
|
|
598
|
+
if (!field) return true;
|
|
599
|
+
const v = readFieldValue(field, row);
|
|
600
|
+
if (clause.op === "is_null") return Array.isArray(v) ? v.length === 0 : v == null || v === "";
|
|
601
|
+
if (clause.op === "is_not_null") return Array.isArray(v) ? v.length > 0 : v != null && v !== "";
|
|
602
|
+
if (clause.value === "" && clause.op !== "=" && clause.op !== "!=") return true;
|
|
603
|
+
if (clause.op === "includes") {
|
|
604
|
+
const arr = Array.isArray(v) ? v : [];
|
|
605
|
+
const needle = clause.value.toLowerCase();
|
|
606
|
+
return arr.some((x) => String(x).toLowerCase() === needle);
|
|
607
|
+
}
|
|
608
|
+
if (Array.isArray(v)) {
|
|
609
|
+
const needle = clause.value.toLowerCase();
|
|
610
|
+
const hay = v.map((x) => String(x).toLowerCase());
|
|
611
|
+
switch (clause.op) {
|
|
612
|
+
case "contains":
|
|
613
|
+
return hay.some((s) => s.includes(needle));
|
|
614
|
+
case "=":
|
|
615
|
+
return hay.includes(needle);
|
|
616
|
+
case "!=":
|
|
617
|
+
return !hay.includes(needle);
|
|
618
|
+
default:
|
|
619
|
+
return true;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
switch (clause.op) {
|
|
623
|
+
case "=":
|
|
624
|
+
return String(v ?? "") === String(coerceSafe(field, clause.value));
|
|
625
|
+
case "!=":
|
|
626
|
+
return String(v ?? "") !== String(coerceSafe(field, clause.value));
|
|
627
|
+
case ">":
|
|
628
|
+
return compare(v, coerceSafe(field, clause.value)) > 0;
|
|
629
|
+
case ">=":
|
|
630
|
+
return compare(v, coerceSafe(field, clause.value)) >= 0;
|
|
631
|
+
case "<":
|
|
632
|
+
return compare(v, coerceSafe(field, clause.value)) < 0;
|
|
633
|
+
case "<=":
|
|
634
|
+
return compare(v, coerceSafe(field, clause.value)) <= 0;
|
|
635
|
+
case "contains":
|
|
636
|
+
return String(v ?? "").toLowerCase().includes(clause.value.toLowerCase());
|
|
637
|
+
case "starts_with":
|
|
638
|
+
return String(v ?? "").toLowerCase().startsWith(clause.value.toLowerCase());
|
|
639
|
+
case "ends_with":
|
|
640
|
+
return String(v ?? "").toLowerCase().endsWith(clause.value.toLowerCase());
|
|
641
|
+
default:
|
|
642
|
+
return true;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
function coerceSafe(field, raw) {
|
|
646
|
+
try {
|
|
647
|
+
return coerceValue(field.type, raw);
|
|
648
|
+
} catch {
|
|
649
|
+
return raw;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
function compare(a, b) {
|
|
653
|
+
if (a == null && b == null) return 0;
|
|
654
|
+
if (a == null) return -1;
|
|
655
|
+
if (b == null) return 1;
|
|
656
|
+
if (typeof a === "number" && typeof b === "number") return a - b;
|
|
657
|
+
if (typeof a === "boolean" && typeof b === "boolean") return a === b ? 0 : a ? 1 : -1;
|
|
658
|
+
return String(a).localeCompare(String(b));
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// src/adapters.ts
|
|
662
|
+
var LAST_PREFIX = "query-table:last:";
|
|
663
|
+
var SAVED_PREFIX = "query-table:saved:";
|
|
664
|
+
var DEFAULT_PREFIX = "query-table:default:";
|
|
665
|
+
var MAX_SAVED_QUERIES = 100;
|
|
666
|
+
var MAX_SAVED_NAME_LENGTH = 200;
|
|
667
|
+
function normalizeSavedQuery(value) {
|
|
668
|
+
if (value == null || typeof value !== "object" || Array.isArray(value)) return null;
|
|
669
|
+
const raw = value;
|
|
670
|
+
if (typeof raw.id !== "string" || !raw.id || raw.id.length > 256 || typeof raw.name !== "string" || !raw.name || raw.name.length > MAX_SAVED_NAME_LENGTH || typeof raw.savedAt !== "number" || !Number.isFinite(raw.savedAt) || raw.query == null || typeof raw.query !== "object") {
|
|
671
|
+
return null;
|
|
672
|
+
}
|
|
673
|
+
return { id: raw.id, name: raw.name, savedAt: raw.savedAt, query: normalizeQueryState(raw.query) };
|
|
674
|
+
}
|
|
675
|
+
function normalizeSavedQueries(value) {
|
|
676
|
+
if (!Array.isArray(value)) return [];
|
|
677
|
+
return value.slice(0, MAX_SAVED_QUERIES).map(normalizeSavedQuery).filter((item) => item != null);
|
|
678
|
+
}
|
|
679
|
+
function memoryStorageAdapter() {
|
|
680
|
+
const last = /* @__PURE__ */ new Map();
|
|
681
|
+
const saved = /* @__PURE__ */ new Map();
|
|
682
|
+
const defaults = /* @__PURE__ */ new Map();
|
|
683
|
+
return {
|
|
684
|
+
async loadLast(key) {
|
|
685
|
+
const query = last.get(key);
|
|
686
|
+
return query ? normalizeQueryState(query) : null;
|
|
687
|
+
},
|
|
688
|
+
async saveLast(key, query) {
|
|
689
|
+
last.set(key, normalizeQueryState(query));
|
|
690
|
+
},
|
|
691
|
+
async listSaved(key) {
|
|
692
|
+
return (saved.get(key) ?? []).map((item) => ({ ...item, query: normalizeQueryState(item.query) }));
|
|
693
|
+
},
|
|
694
|
+
async loadDefaultSaved(key) {
|
|
695
|
+
const id = defaults.get(key);
|
|
696
|
+
return id ? (saved.get(key) ?? []).find((item) => item.id === id) ?? null : null;
|
|
697
|
+
},
|
|
698
|
+
async setDefaultSaved(key, id) {
|
|
699
|
+
if (id == null) {
|
|
700
|
+
defaults.delete(key);
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
if (!(saved.get(key) ?? []).some((item) => item.id === id)) throw new Error(`Saved query "${id}" does not exist.`);
|
|
704
|
+
defaults.set(key, id);
|
|
705
|
+
},
|
|
706
|
+
async saveNamed(key, name, query, savedAt) {
|
|
707
|
+
const normalizedName = name.trim();
|
|
708
|
+
if (!normalizedName || normalizedName.length > MAX_SAVED_NAME_LENGTH) {
|
|
709
|
+
throw new Error(`Saved query name must be between 1 and ${MAX_SAVED_NAME_LENGTH} characters.`);
|
|
710
|
+
}
|
|
711
|
+
const items = saved.get(key) ?? [];
|
|
712
|
+
if (items.length >= MAX_SAVED_QUERIES) throw new Error(`At most ${MAX_SAVED_QUERIES} saved queries are allowed.`);
|
|
713
|
+
if (items.some((item2) => item2.name === normalizedName)) throw new Error(`Saved query "${normalizedName}" already exists.`);
|
|
714
|
+
const item = {
|
|
715
|
+
id: `${savedAt}-${Math.round((savedAt * 9301 + 49297) % 233280)}`,
|
|
716
|
+
name: normalizedName,
|
|
717
|
+
savedAt,
|
|
718
|
+
query: normalizeQueryState(query)
|
|
719
|
+
};
|
|
720
|
+
saved.set(key, [...items, item]);
|
|
721
|
+
return item;
|
|
722
|
+
},
|
|
723
|
+
async deleteSaved(key, id) {
|
|
724
|
+
saved.set(key, (saved.get(key) ?? []).filter((item) => item.id !== id));
|
|
725
|
+
if (defaults.get(key) === id) defaults.delete(key);
|
|
726
|
+
}
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
function localStorageAdapter() {
|
|
730
|
+
const ls = (() => {
|
|
731
|
+
try {
|
|
732
|
+
return typeof localStorage !== "undefined" ? localStorage : null;
|
|
733
|
+
} catch {
|
|
734
|
+
return null;
|
|
735
|
+
}
|
|
736
|
+
})();
|
|
737
|
+
const readSaved = (key) => {
|
|
738
|
+
if (!ls) return [];
|
|
739
|
+
try {
|
|
740
|
+
const raw = ls.getItem(SAVED_PREFIX + key);
|
|
741
|
+
return normalizeSavedQueries(raw ? JSON.parse(raw) : []);
|
|
742
|
+
} catch {
|
|
743
|
+
return [];
|
|
744
|
+
}
|
|
745
|
+
};
|
|
746
|
+
const writeSaved = (key, items) => {
|
|
747
|
+
if (ls) ls.setItem(SAVED_PREFIX + key, JSON.stringify(items));
|
|
748
|
+
};
|
|
749
|
+
const readDefaultId = (key) => {
|
|
750
|
+
if (!ls) return null;
|
|
751
|
+
return ls.getItem(DEFAULT_PREFIX + key);
|
|
752
|
+
};
|
|
753
|
+
const writeDefaultId = (key, id) => {
|
|
754
|
+
if (!ls) return;
|
|
755
|
+
if (id) ls.setItem(DEFAULT_PREFIX + key, id);
|
|
756
|
+
else ls.removeItem(DEFAULT_PREFIX + key);
|
|
757
|
+
};
|
|
758
|
+
return {
|
|
759
|
+
async loadLast(key) {
|
|
760
|
+
if (!ls) return null;
|
|
761
|
+
try {
|
|
762
|
+
const raw = ls.getItem(LAST_PREFIX + key);
|
|
763
|
+
return raw ? normalizeQueryState(JSON.parse(raw)) : null;
|
|
764
|
+
} catch {
|
|
765
|
+
return null;
|
|
766
|
+
}
|
|
767
|
+
},
|
|
768
|
+
async saveLast(key, query) {
|
|
769
|
+
if (ls) ls.setItem(LAST_PREFIX + key, JSON.stringify(normalizeQueryState(query)));
|
|
770
|
+
},
|
|
771
|
+
async listSaved(key) {
|
|
772
|
+
return readSaved(key).sort((a, b) => b.savedAt - a.savedAt);
|
|
773
|
+
},
|
|
774
|
+
async loadDefaultSaved(key) {
|
|
775
|
+
const id = readDefaultId(key);
|
|
776
|
+
if (!id) return null;
|
|
777
|
+
const found = readSaved(key).find((q) => q.id === id);
|
|
778
|
+
if (!found) writeDefaultId(key, null);
|
|
779
|
+
return found ?? null;
|
|
780
|
+
},
|
|
781
|
+
async setDefaultSaved(key, id) {
|
|
782
|
+
if (id && !readSaved(key).some((q) => q.id === id)) {
|
|
783
|
+
throw new Error(`Saved query "${id}" does not exist.`);
|
|
784
|
+
}
|
|
785
|
+
writeDefaultId(key, id);
|
|
786
|
+
},
|
|
787
|
+
async saveNamed(key, name, query, savedAt) {
|
|
788
|
+
const normalizedName = name.trim();
|
|
789
|
+
if (!normalizedName || normalizedName.length > MAX_SAVED_NAME_LENGTH) {
|
|
790
|
+
throw new Error(`Saved query name must be between 1 and ${MAX_SAVED_NAME_LENGTH} characters.`);
|
|
791
|
+
}
|
|
792
|
+
const items = readSaved(key);
|
|
793
|
+
if (items.length >= MAX_SAVED_QUERIES) throw new Error(`At most ${MAX_SAVED_QUERIES} saved queries are allowed.`);
|
|
794
|
+
if (items.some((q) => q.name === normalizedName)) {
|
|
795
|
+
throw new Error(`Saved query "${normalizedName}" already exists.`);
|
|
796
|
+
}
|
|
797
|
+
const item = {
|
|
798
|
+
id: `${savedAt}-${Math.round((savedAt * 9301 + 49297) % 233280)}`,
|
|
799
|
+
name: normalizedName,
|
|
800
|
+
savedAt,
|
|
801
|
+
query: normalizeQueryState(query)
|
|
802
|
+
};
|
|
803
|
+
items.push(item);
|
|
804
|
+
writeSaved(key, items);
|
|
805
|
+
return item;
|
|
806
|
+
},
|
|
807
|
+
async deleteSaved(key, id) {
|
|
808
|
+
writeSaved(
|
|
809
|
+
key,
|
|
810
|
+
readSaved(key).filter((q) => q.id !== id)
|
|
811
|
+
);
|
|
812
|
+
if (readDefaultId(key) === id) writeDefaultId(key, null);
|
|
813
|
+
}
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
export {
|
|
817
|
+
AGG_OPS_BY_TYPE,
|
|
818
|
+
AGG_OPS_NEEDING_FIELD,
|
|
819
|
+
EMPTY_QUERY,
|
|
820
|
+
MAX_AGGREGATIONS,
|
|
821
|
+
MAX_GROUP_BY_FIELDS,
|
|
822
|
+
MAX_ORDER_BY_TERMS,
|
|
823
|
+
MAX_QUERY_LIMIT,
|
|
824
|
+
MAX_QUERY_OFFSET,
|
|
825
|
+
MAX_QUERY_TOKEN_LENGTH,
|
|
826
|
+
MAX_SELECT_COLUMNS,
|
|
827
|
+
MAX_WHERE_CLAUSES,
|
|
828
|
+
NULLARY_OPS,
|
|
829
|
+
OPS_BY_TYPE,
|
|
830
|
+
aggOpAllowedForType,
|
|
831
|
+
aggOpNeedsField,
|
|
832
|
+
aggOpsForField,
|
|
833
|
+
applyAggregations,
|
|
834
|
+
applyQuery,
|
|
835
|
+
coerceValue,
|
|
836
|
+
decodeQuery,
|
|
837
|
+
encodeQuery,
|
|
838
|
+
filterValues,
|
|
839
|
+
indexFields,
|
|
840
|
+
isFilterable,
|
|
841
|
+
isGroupable,
|
|
842
|
+
isMeasurable,
|
|
843
|
+
isPushdownFilter,
|
|
844
|
+
isSelectable,
|
|
845
|
+
isSortable,
|
|
846
|
+
loadSchema,
|
|
847
|
+
localStorageAdapter,
|
|
848
|
+
matchesClause,
|
|
849
|
+
memoryStorageAdapter,
|
|
850
|
+
normalizeQueryState,
|
|
851
|
+
opAllowedForType,
|
|
852
|
+
opsForField,
|
|
853
|
+
queriesEqual,
|
|
854
|
+
readFieldValue,
|
|
855
|
+
selectedFields,
|
|
856
|
+
toAggregationQuery,
|
|
857
|
+
toServerQuery
|
|
858
|
+
};
|
|
859
|
+
//# sourceMappingURL=index.js.map
|