@vireocodedev/query 0.2.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,29 @@
1
+ import { QueryEngineEntityDefinition, QueryEngineEntityKey, QueryEngineEntitySummary, QueryEngineRelationOption } from '../models/queryengine.models';
2
+ import { default as z } from 'zod';
3
+ /** Transport-neutral request options — no dependency on a specific HTTP client. */
4
+ export type QueryEngineRequestOptions = {
5
+ params?: Record<string, unknown>;
6
+ signal?: AbortSignal;
7
+ };
8
+ /**
9
+ * Port the query engine needs from its host application. The app injects an
10
+ * adapter (e.g. wrapping its axios client) so this module stays free of any
11
+ * HTTP/infrastructure dependency.
12
+ */
13
+ export interface QueryEngineHttpClient {
14
+ /** GET a JSON resource relative to the query-engine base path. Returns raw JSON. */
15
+ get(path: string, options?: QueryEngineRequestOptions): Promise<unknown>;
16
+ }
17
+ export interface QueryEngineApi {
18
+ listEntities(options?: QueryEngineRequestOptions): Promise<QueryEngineEntitySummary[]>;
19
+ describeEntity(entityKey: QueryEngineEntityKey, options?: QueryEngineRequestOptions): Promise<QueryEngineEntityDefinition>;
20
+ listRelationOptions(entityKey: QueryEngineEntityKey, fieldPath: string, searchText?: string, options?: QueryEngineRequestOptions): Promise<QueryEngineRelationOption[]>;
21
+ }
22
+ export type CreateQueryEngineApiOptions = {
23
+ /** Consumer-owned schema used to validate/normalize entity keys in responses. Defaults to `z.string()`. */
24
+ entityKeySchema?: z.ZodType<QueryEngineEntityKey>;
25
+ /** Optional legacy path key for back-compat retries (return `undefined` when there is none). */
26
+ legacyEntityKey?: (entityKey: QueryEngineEntityKey) => string | undefined;
27
+ };
28
+ /** Builds a {@link QueryEngineApi} bound to a host-provided HTTP client. */
29
+ export declare function createQueryEngineApi(http: QueryEngineHttpClient, options?: CreateQueryEngineApiOptions): QueryEngineApi;
@@ -0,0 +1,7 @@
1
+ export * from './api/queryengine.api';
2
+ export * from './models/queryengine.models';
3
+ export * from './queryengine.querykeys';
4
+ export * from './sqlite/compileQueryFilterWhere';
5
+ export * from './sqlite/createQueryEngineConfigClient';
6
+ export * from './sqlite/createSqliteQueryExecutor';
7
+ export * from './sqlite/queryEngineConfigSqlite';
package/dist/index.js ADDED
@@ -0,0 +1,517 @@
1
+ import e from "zod";
2
+ //#region src/models/queryengine.models.ts
3
+ var t = [
4
+ "EQUALS",
5
+ "NOT_EQUALS",
6
+ "CONTAINS",
7
+ "STARTS_WITH",
8
+ "ENDS_WITH",
9
+ "IN",
10
+ "GREATER_THAN",
11
+ "GREATER_OR_EQUAL",
12
+ "LESS_THAN",
13
+ "LESS_OR_EQUAL",
14
+ "DATE_RANGE",
15
+ "IS_NULL",
16
+ "IS_NOT_NULL"
17
+ ], n = [
18
+ "STRING",
19
+ "NUMBER",
20
+ "BOOLEAN",
21
+ "DATE",
22
+ "ENUM",
23
+ "RELATION"
24
+ ], r = [
25
+ "CHILD",
26
+ "SELECTION",
27
+ "BOTH"
28
+ ], i = e.enum(t), a = e.enum(n), o = e.enum(r), s = e.object({
29
+ value: e.string().min(1),
30
+ label: e.string().min(1)
31
+ });
32
+ function c(t = e.string().min(1)) {
33
+ let n = e.lazy(() => e.object({
34
+ path: e.string().min(1),
35
+ label: e.string().min(1),
36
+ type: a,
37
+ enumType: e.string().nullable(),
38
+ enumValues: e.array(e.string()),
39
+ operators: e.array(i),
40
+ relation: e.boolean(),
41
+ relationEntityKey: t.nullable(),
42
+ relationMode: o,
43
+ multiple: e.boolean(),
44
+ relationSelectionLabelFields: e.array(e.string()),
45
+ expandable: e.boolean(),
46
+ maxDepth: e.number().int().nonnegative(),
47
+ children: e.array(n)
48
+ }));
49
+ return {
50
+ fieldDefinition: n,
51
+ entityDefinition: e.object({
52
+ key: t,
53
+ title: e.string().min(1),
54
+ fields: e.array(n)
55
+ }).passthrough(),
56
+ entitySummary: e.object({
57
+ key: t,
58
+ filterableFieldCount: e.number().int().nonnegative()
59
+ }).passthrough()
60
+ };
61
+ }
62
+ //#endregion
63
+ //#region src/api/queryengine.api.ts
64
+ function l(e, t) {
65
+ let { entityDefinition: n, entitySummary: r } = c(t?.entityKeySchema), i = t?.legacyEntityKey, a = (e, t) => {
66
+ if (e.trim().length === 0) throw Error(`${t} must be a non-empty string.`);
67
+ return encodeURIComponent(e);
68
+ }, o = (e, t) => t?.signal?.aborted === !0 || typeof DOMException < "u" && e instanceof DOMException && e.name === "AbortError", l = async (t, n, r) => n.parse(await e.get(t, r));
69
+ return {
70
+ listEntities: (e) => l("entities", r.array(), e),
71
+ describeEntity: async (e, t) => {
72
+ let r = a(e, "entityKey");
73
+ try {
74
+ return await l(`entities/${r}`, n, t);
75
+ } catch (r) {
76
+ if (o(r, t)) throw r;
77
+ let s = i?.(e);
78
+ if (s === void 0 || s === e) throw r;
79
+ return await l(`entities/${a(s, "legacyEntityKey")}`, n, t);
80
+ }
81
+ },
82
+ listRelationOptions: async (e, t, n, r) => {
83
+ let c = a(e, "entityKey"), u = a(t, "fieldPath"), d = {
84
+ ...r,
85
+ params: {
86
+ ...r?.params,
87
+ searchText: n
88
+ }
89
+ };
90
+ try {
91
+ return await l(`entities/${c}/fields/${u}/options`, s.array(), d);
92
+ } catch (t) {
93
+ if (o(t, r)) throw t;
94
+ let n = i?.(e);
95
+ if (n === void 0 || n === e) throw t;
96
+ return await l(`entities/${a(n, "legacyEntityKey")}/fields/${u}/options`, s.array(), d);
97
+ }
98
+ }
99
+ };
100
+ }
101
+ //#endregion
102
+ //#region src/queryengine.querykeys.ts
103
+ var u = {
104
+ entities: "queryengineEntities",
105
+ entityDefinition: "queryengineEntityDefinition",
106
+ entityDefinitions: "queryengineEntityDefinitions"
107
+ }, d = e.object({
108
+ kind: e.enum(["leaf", "relation"]).default("leaf"),
109
+ path: e.string().min(1),
110
+ operator: i.optional(),
111
+ value: e.string().optional(),
112
+ parameterized: e.boolean().optional(),
113
+ selectedOptions: e.array(e.object({
114
+ value: e.string(),
115
+ label: e.string()
116
+ })).optional()
117
+ }), f = e.object({
118
+ entity: e.string().optional(),
119
+ rows: e.array(d).optional()
120
+ });
121
+ function p(e, t = {}) {
122
+ let n = { ...t }, r = {}, i = /* @__PURE__ */ new Set();
123
+ for (let { alias: t, expression: a, valueType: o, filterAs: s, sortAs: c } of e) {
124
+ if (t.trim().length === 0) throw Error("SQLite search column alias must be a non-empty string.");
125
+ if (a.trim().length === 0) throw Error(`SQLite search column "${t}" expression must be a non-empty string.`);
126
+ if (i.has(t)) throw Error(`SQLite search column alias "${t}" is registered more than once.`);
127
+ if (i.add(t), s !== !1) {
128
+ let e = s ?? t;
129
+ if (n[e]) throw Error(`SQLite filter field "${e}" is registered more than once.`);
130
+ n[e] = {
131
+ expression: a,
132
+ valueType: o
133
+ };
134
+ }
135
+ c !== !1 && (r[t] = c ?? t);
136
+ }
137
+ return {
138
+ fieldAdapters: n,
139
+ selectColumns: e.map(({ alias: e, expression: t }) => ({
140
+ alias: e,
141
+ expression: t
142
+ })),
143
+ sortExpressionsByKey: r
144
+ };
145
+ }
146
+ function m(e) {
147
+ if (!e?.trim()) return null;
148
+ try {
149
+ return f.parse(JSON.parse(e));
150
+ } catch {
151
+ throw Error("Query filter JSON is invalid.");
152
+ }
153
+ }
154
+ function h(e, t) {
155
+ if (t === "string") return e;
156
+ if (t === "boolean") {
157
+ let t = e.trim().toLowerCase();
158
+ if (t === "true" || t === "1") return 1;
159
+ if (t === "false" || t === "0") return 0;
160
+ throw Error(`Invalid boolean filter value: ${e}`);
161
+ }
162
+ let n = Number(e);
163
+ if (!Number.isFinite(n)) throw Error(`Invalid numeric filter value: ${e}`);
164
+ return n;
165
+ }
166
+ function g(e) {
167
+ return e?.trim() ? e.split(",").map((e) => e.trim()).filter(Boolean) : [];
168
+ }
169
+ function _(e, t) {
170
+ let { expression: n } = t, r = e.operator;
171
+ if (!r || e.parameterized) return null;
172
+ if (r === "IS_NULL") return {
173
+ sql: `${n} IS NULL`,
174
+ params: []
175
+ };
176
+ if (r === "IS_NOT_NULL") return {
177
+ sql: `${n} IS NOT NULL`,
178
+ params: []
179
+ };
180
+ if (r === "DATE_RANGE") {
181
+ let [r = "", i = ""] = String(e.value ?? "").split("|"), a = [], o = [];
182
+ return r.trim() && (a.push(`${n} >= ?`), o.push(h(r.trim(), t.valueType))), i.trim() && (a.push(`${n} <= ?`), o.push(h(i.trim(), t.valueType))), a.length > 0 ? {
183
+ sql: `(${a.join(" AND ")})`,
184
+ params: o
185
+ } : null;
186
+ }
187
+ if (r === "IN") {
188
+ let r = g(e.value);
189
+ return r.length === 0 ? null : {
190
+ sql: `${n} IN (${r.map(() => "?").join(", ")})`,
191
+ params: r.map((e) => h(e, t.valueType))
192
+ };
193
+ }
194
+ let i = e.value?.trim();
195
+ if (!i) return null;
196
+ let a = h(i, t.valueType), o = `LOWER(COALESCE(CAST(${n} AS TEXT), ''))`;
197
+ switch (r) {
198
+ case "EQUALS": return {
199
+ sql: `${n} = ?`,
200
+ params: [a]
201
+ };
202
+ case "NOT_EQUALS": return {
203
+ sql: `${n} <> ?`,
204
+ params: [a]
205
+ };
206
+ case "CONTAINS": return {
207
+ sql: `${o} LIKE ?`,
208
+ params: [`%${i.toLowerCase()}%`]
209
+ };
210
+ case "STARTS_WITH": return {
211
+ sql: `${o} LIKE ?`,
212
+ params: [`${i.toLowerCase()}%`]
213
+ };
214
+ case "ENDS_WITH": return {
215
+ sql: `${o} LIKE ?`,
216
+ params: [`%${i.toLowerCase()}`]
217
+ };
218
+ case "GREATER_THAN": return {
219
+ sql: `${n} > ?`,
220
+ params: [a]
221
+ };
222
+ case "GREATER_OR_EQUAL": return {
223
+ sql: `${n} >= ?`,
224
+ params: [a]
225
+ };
226
+ case "LESS_THAN": return {
227
+ sql: `${n} < ?`,
228
+ params: [a]
229
+ };
230
+ case "LESS_OR_EQUAL": return {
231
+ sql: `${n} <= ?`,
232
+ params: [a]
233
+ };
234
+ default: return null;
235
+ }
236
+ }
237
+ function v(e, t) {
238
+ let n = (e.selectedOptions ?? []).map((e) => e.value?.trim()).filter((e) => !!e);
239
+ return n.length === 0 ? null : {
240
+ sql: `${t.expression} IN (${n.map(() => "?").join(", ")})`,
241
+ params: n.map((e) => h(e, t.valueType))
242
+ };
243
+ }
244
+ function y(e, t) {
245
+ let n = m(t);
246
+ if (n?.entity && n.entity !== e.entity) throw Error(`Invalid filter entity. Expected: ${e.entity}`);
247
+ let r = (Array.isArray(n?.rows) ? n.rows : []).flatMap((t) => {
248
+ if (!t?.path || t.parameterized) return [];
249
+ let n = e.fieldAdapters[t.path];
250
+ if (!n) throw Error(`Unknown SQLite filter field: ${t.path}`);
251
+ let r = t.kind === "relation" ? v(t, n) : _(t, n);
252
+ return r ? [r] : [];
253
+ });
254
+ return {
255
+ sql: [e.baseWhereClause ?? "1 = 1", ...r.map((e) => e.sql)].join(" AND "),
256
+ params: r.flatMap((e) => e.params),
257
+ filterCount: r.length
258
+ };
259
+ }
260
+ function b(e, t) {
261
+ let n = e?.trim().toLocaleLowerCase() ?? "";
262
+ return !n || !t || t.length === 0 ? null : {
263
+ sql: `(${t.map((e) => `LOWER(COALESCE(CAST(${e} AS TEXT), '')) LIKE ?`).join(" OR ")})`,
264
+ params: t.map(() => `%${n}%`)
265
+ };
266
+ }
267
+ //#endregion
268
+ //#region src/sqlite/queryEngineConfigSqlite.ts
269
+ var x = e.object({
270
+ entities: e.array(e.unknown()),
271
+ entityDefinitions: e.record(e.string(), e.unknown())
272
+ });
273
+ function S(e) {
274
+ try {
275
+ return JSON.parse(e);
276
+ } catch {
277
+ throw Error("Stored query-engine config contains malformed JSON.");
278
+ }
279
+ }
280
+ function C(e, t) {
281
+ if (!/^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)?$/.test(e)) throw Error(`${t} must be a valid SQLite identifier.`);
282
+ }
283
+ function w(e) {
284
+ let t = e ?? "singleton";
285
+ if (!t.trim()) throw Error("Query-engine config singleton key must be non-empty.");
286
+ return t;
287
+ }
288
+ function T(e) {
289
+ let t = e ?? {
290
+ replace: "replaceQueryEngineConfig",
291
+ get: "getQueryEngineConfig"
292
+ };
293
+ if (!t.replace.trim() || !t.get.trim()) throw Error("Query-engine config request types must be non-empty.");
294
+ if (t.replace === t.get) throw Error("Query-engine config replace and get request types must be distinct.");
295
+ return t;
296
+ }
297
+ function E(e, t, n) {
298
+ C(n.tableName, "Query-engine config table name");
299
+ let r = x.parse(t), i = e.prepare(`
300
+ INSERT INTO ${n.tableName} (singleton_key, entities_json, entity_definitions_json)
301
+ VALUES (?, ?, ?)
302
+ ON CONFLICT(singleton_key) DO UPDATE SET
303
+ entities_json = excluded.entities_json,
304
+ entity_definitions_json = excluded.entity_definitions_json;
305
+ `);
306
+ try {
307
+ i.bind([
308
+ w(n.singletonKey),
309
+ JSON.stringify(r.entities),
310
+ JSON.stringify(r.entityDefinitions)
311
+ ]), i.step();
312
+ } finally {
313
+ i.finalize();
314
+ }
315
+ }
316
+ function D(e, t) {
317
+ C(t.tableName, "Query-engine config table name");
318
+ let n = e.prepare(`
319
+ SELECT entities_json, entity_definitions_json
320
+ FROM ${t.tableName}
321
+ WHERE singleton_key = ?
322
+ LIMIT 1;
323
+ `);
324
+ try {
325
+ if (n.bind([w(t.singletonKey)]), !n.step()) return null;
326
+ let e = n.get([]), r = t.parseJson ?? S;
327
+ return x.parse({
328
+ entities: r(String(e[0] ?? "[]"), []),
329
+ entityDefinitions: r(String(e[1] ?? "{}"), {})
330
+ });
331
+ } finally {
332
+ n.finalize();
333
+ }
334
+ }
335
+ function O(e) {
336
+ C(e.tableName, "Query-engine config table name"), w(e.singletonKey);
337
+ let t = T(e.requestTypes), n = {
338
+ tableName: e.tableName,
339
+ singletonKey: e.singletonKey
340
+ };
341
+ return {
342
+ [t.replace]: (e, t) => (E(e, x.parse(t.config), n), null),
343
+ [t.get]: (t) => D(t, {
344
+ ...n,
345
+ parseJson: e.parseJson
346
+ })
347
+ };
348
+ }
349
+ //#endregion
350
+ //#region src/sqlite/createQueryEngineConfigClient.ts
351
+ function k(e) {
352
+ let t = e.requestTypes ?? {
353
+ replace: "replaceQueryEngineConfig",
354
+ get: "getQueryEngineConfig"
355
+ };
356
+ if (!t.replace.trim() || !t.get.trim()) throw Error("Query-engine config request types must be non-empty.");
357
+ if (t.replace === t.get) throw Error("Query-engine config replace and get request types must be distinct.");
358
+ let n = null;
359
+ return {
360
+ async replace(r) {
361
+ let i = x.parse(r);
362
+ if (e.runtime.shouldUseInMemoryFallback()) {
363
+ n = i;
364
+ return;
365
+ }
366
+ await e.transport.sendRequest(t.replace, { config: i });
367
+ },
368
+ async get() {
369
+ return e.runtime.shouldUseInMemoryFallback() ? n : await e.transport.sendRequest(t.get);
370
+ },
371
+ setFallback(e) {
372
+ n = e == null ? null : x.parse(e);
373
+ },
374
+ getFallback: () => n,
375
+ dispose: e.runtime.registerInMemoryStore({ clear: () => n = null })
376
+ };
377
+ }
378
+ //#endregion
379
+ //#region src/sqlite/createSqliteQueryExecutor.ts
380
+ function A() {
381
+ return typeof performance < "u" && typeof performance.now == "function" ? performance.now() : Date.now();
382
+ }
383
+ function j(e) {
384
+ return typeof e != "object" || !e ? JSON.stringify(e) ?? String(e) : Array.isArray(e) ? `[${e.map((e) => j(e)).join(",")}]` : `{${Object.entries(e).sort(([e], [t]) => e.localeCompare(t)).map(([e, t]) => `${JSON.stringify(e)}:${j(t)}`).join(",")}}`;
385
+ }
386
+ function M(e) {
387
+ return Object.fromEntries(e.map((e, t) => [e, t]));
388
+ }
389
+ function N(e) {
390
+ return Number.isFinite(e.page) ? Math.max(0, Math.floor(e.page)) : 0;
391
+ }
392
+ function P(e) {
393
+ return Number.isFinite(e.rowsPerPage) ? Math.floor(e.rowsPerPage) : -1;
394
+ }
395
+ function F(e) {
396
+ let t = N(e), n = P(e);
397
+ return n <= 0 ? {
398
+ limit: null,
399
+ offset: null
400
+ } : {
401
+ limit: n,
402
+ offset: t * n
403
+ };
404
+ }
405
+ function I(e, t, n) {
406
+ return e ? n[e] ?? t : t;
407
+ }
408
+ function L(e) {
409
+ let t = e.now ?? A, n = /* @__PURE__ */ new Map(), r = async (r) => {
410
+ let i = j(r), a = {
411
+ ...r,
412
+ clientSentAtMs: t()
413
+ }, o = n.get(i);
414
+ if (o) return await o;
415
+ let s = e.executePagedQuery(a);
416
+ n.set(i, s);
417
+ try {
418
+ return await s;
419
+ } finally {
420
+ n.delete(i);
421
+ }
422
+ };
423
+ return {
424
+ async pagedSearch(n) {
425
+ let i = t(), a = N(n.pageable), o = P(n.pageable), s = y(n.adapter, n.queryFiltersJson), c = b(n.searchText, n.searchExpressions), l = [s.sql, c?.sql].filter(Boolean).join(" AND "), u = [...s.params, ...c?.params ?? []], d = n.selectColumns.map((e) => `${e.expression} AS ${e.alias}`).join(", "), f = I(n.sortBy, n.defaultSortExpression, n.sortExpressionsByKey), p = n.sortDirection?.trim().toLowerCase() === "desc" ? "DESC" : "ASC", { limit: m, offset: h } = F(n.pageable), g = n.includeTotalCount ?? !0, _ = !g && m != null && m > 0 ? m + 1 : m, v = t(), x = await r({
426
+ selectSql: d,
427
+ fromSql: n.adapter.fromClause,
428
+ whereSql: l,
429
+ whereParams: u,
430
+ orderBySql: `${f} ${p}`,
431
+ limit: _,
432
+ offset: h,
433
+ includeTotalCount: g
434
+ }), S = Math.round(t() - v), C = x.rows, w = !1;
435
+ !g && m != null && m > 0 && C.length > m && (C = C.slice(0, m), w = !0);
436
+ let T = t(), E = M(x.columns), D = C.map((e) => n.mapRow(e, E)), O = g ? x.totalElements ?? 0 : o > 0 ? a * o + D.length + +!!w : D.length, k = {
437
+ content: D,
438
+ number: a,
439
+ size: o > 0 ? o : D.length,
440
+ totalElements: O,
441
+ totalPages: o > 0 ? Math.ceil(O / o) : 0
442
+ };
443
+ return e.log?.("paged search done", {
444
+ entity: n.adapter.entity,
445
+ totalMs: Math.round(t() - i),
446
+ sqlExecMs: S,
447
+ queueWaitMs: x.queueWaitMs ?? null,
448
+ workerExecMs: x.workerExecMs ?? null,
449
+ mapRowsMs: Math.round(t() - T),
450
+ rows: D.length,
451
+ totalElements: O,
452
+ includeTotalCount: g,
453
+ hasNextPage: w,
454
+ whereClauses: 1 + s.filterCount + +!!c,
455
+ searchTextLength: n.searchText?.trim().length ?? 0,
456
+ sortBy: n.sortBy ?? null,
457
+ sortDirection: n.sortDirection ?? "asc",
458
+ page: a,
459
+ rowsPerPage: o
460
+ }), k;
461
+ },
462
+ async findMatchingKeys(t, n) {
463
+ let r = y(t, n);
464
+ if (r.filterCount === 0) return null;
465
+ let i = await e.executeQuery({
466
+ sql: `
467
+ SELECT DISTINCT ${t.keyExpression} AS ${t.keyAlias}
468
+ FROM ${t.fromClause}
469
+ WHERE ${r.sql};
470
+ `,
471
+ params: r.params
472
+ }), a = i.columns.indexOf(t.keyAlias);
473
+ return a < 0 ? /* @__PURE__ */ new Set() : new Set(i.rows.map((e) => t.parseKey(e[a])));
474
+ }
475
+ };
476
+ }
477
+ function R(e) {
478
+ let t = e.getColumnNames?.([]) ?? [], n = [];
479
+ for (; e.step();) n.push(e.get([]));
480
+ return {
481
+ columns: t,
482
+ rows: n
483
+ };
484
+ }
485
+ function z(e, t, n) {
486
+ let r = e.prepare(t);
487
+ try {
488
+ return r.bind(n), R(r);
489
+ } finally {
490
+ r.finalize();
491
+ }
492
+ }
493
+ function B(e, t) {
494
+ return z(e, t.sql, t.params);
495
+ }
496
+ function V(e, t, n = A) {
497
+ if (t.limit != null && (!Number.isInteger(t.limit) || t.limit <= 0)) throw Error("SQLite page limit must be a positive integer or null.");
498
+ if (t.offset != null && (!Number.isInteger(t.offset) || t.offset < 0)) throw Error("SQLite page offset must be a non-negative integer or null.");
499
+ let r = n(), i = t.clientSentAtMs == null || !Number.isFinite(t.clientSentAtMs) ? void 0 : Math.max(0, Math.round(r - t.clientSentAtMs)), a = t.limit != null && t.limit > 0 ? `LIMIT ${t.limit}${t.offset != null && t.offset > 0 ? ` OFFSET ${t.offset}` : ""}` : "", o = z(e, `SELECT ${t.selectSql} FROM ${t.fromSql} WHERE ${t.whereSql} ORDER BY ${t.orderBySql} ${a};`, t.whereParams);
500
+ if (!t.includeTotalCount) return {
501
+ ...o,
502
+ totalElements: null,
503
+ queueWaitMs: i,
504
+ workerExecMs: Math.round(n() - r)
505
+ };
506
+ let s = z(e, `SELECT COUNT(*) AS total_elements FROM ${t.fromSql} WHERE ${t.whereSql};`, t.whereParams);
507
+ return {
508
+ ...o,
509
+ totalElements: Number(s.rows[0]?.[0] ?? 0),
510
+ queueWaitMs: i,
511
+ workerExecMs: Math.round(n() - r)
512
+ };
513
+ }
514
+ //#endregion
515
+ export { a as QueryEngineFieldTypeSchema, i as QueryEngineOperatorSchema, u as QueryEngineQueryKey, o as QueryEngineRelationModeSchema, s as QueryEngineRelationOptionSchema, x as SqliteQueryEngineConfigRecordSchema, p as bindSqliteSearchColumns, y as compileQueryFilterWhere, b as compileSearchTextWhere, l as createQueryEngineApi, k as createQueryEngineConfigClient, O as createQueryEngineConfigSqliteRequestHandlers, c as createQueryEngineEntitySchemas, L as createSqliteQueryExecutor, V as executeParameterizedSqlitePagedQuery, B as executeParameterizedSqliteQuery, D as getQueryEngineConfig, E as replaceQueryEngineConfig };
516
+
517
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/models/queryengine.models.ts","../src/api/queryengine.api.ts","../src/queryengine.querykeys.ts","../src/sqlite/compileQueryFilterWhere.ts","../src/sqlite/queryEngineConfigSqlite.ts","../src/sqlite/createQueryEngineConfigClient.ts","../src/sqlite/createSqliteQueryExecutor.ts"],"sourcesContent":["import z from \"zod\";\n\n/**\n * Entity keys are opaque strings at the library level. The consuming app owns\n * its entity-key set and injects a validating/normalizing schema via\n * {@link createQueryEngineEntitySchemas} (see createQueryEngineApi options).\n */\nexport type QueryEngineEntityKey = string;\n\nconst QueryEngineOperators = [\n \"EQUALS\",\n \"NOT_EQUALS\",\n \"CONTAINS\",\n \"STARTS_WITH\",\n \"ENDS_WITH\",\n \"IN\",\n \"GREATER_THAN\",\n \"GREATER_OR_EQUAL\",\n \"LESS_THAN\",\n \"LESS_OR_EQUAL\",\n \"DATE_RANGE\",\n \"IS_NULL\",\n \"IS_NOT_NULL\",\n] as const;\n\nconst QueryEngineFieldTypes = [\"STRING\", \"NUMBER\", \"BOOLEAN\", \"DATE\", \"ENUM\", \"RELATION\"] as const;\nconst QueryEngineRelationModes = [\"CHILD\", \"SELECTION\", \"BOTH\"] as const;\n\nexport const QueryEngineOperatorSchema = z.enum(QueryEngineOperators);\nexport type QueryEngineOperator = z.infer<typeof QueryEngineOperatorSchema>;\n\nexport const QueryEngineFieldTypeSchema = z.enum(QueryEngineFieldTypes);\nexport type QueryEngineFieldType = z.infer<typeof QueryEngineFieldTypeSchema>;\n\nexport const QueryEngineRelationModeSchema = z.enum(QueryEngineRelationModes);\nexport type QueryEngineRelationMode = z.infer<typeof QueryEngineRelationModeSchema>;\n\nexport interface QueryEngineRelationOption {\n value: string;\n label: string;\n}\n\nexport const QueryEngineRelationOptionSchema: z.ZodType<QueryEngineRelationOption> = z.object({\n value: z.string().min(1),\n label: z.string().min(1),\n});\n\nexport interface QueryEngineFieldDefinition {\n path: string;\n label: string;\n type: QueryEngineFieldType;\n enumType: string | null;\n enumValues: string[];\n operators: QueryEngineOperator[];\n relation: boolean;\n relationEntityKey: QueryEngineEntityKey | null;\n relationMode: QueryEngineRelationMode;\n multiple: boolean;\n relationSelectionLabelFields: string[];\n expandable: boolean;\n maxDepth: number;\n children: QueryEngineFieldDefinition[];\n}\n\n/**\n * The published shape of an entity. Backends are free to send more — the parse\n * schemas pass unknown keys through untouched, so a consumer can widen this type\n * with its own backend-specific fields:\n *\n * ```ts\n * type AppEntityDefinition = QueryEngineEntityDefinition & { javaType: string };\n * ```\n */\nexport interface QueryEngineEntityDefinition {\n key: QueryEngineEntityKey;\n title: string;\n fields: QueryEngineFieldDefinition[];\n}\n\nexport interface QueryEngineEntitySummary {\n key: QueryEngineEntityKey;\n filterableFieldCount: number;\n}\n\nexport interface QueryEngineRelationFieldOptionsRequest {\n entityKey: QueryEngineEntityKey;\n fieldPath: string;\n searchText?: string;\n}\n\nexport type QueryEngineEntitySchemas = {\n fieldDefinition: z.ZodType<QueryEngineFieldDefinition>;\n entityDefinition: z.ZodType<QueryEngineEntityDefinition>;\n entitySummary: z.ZodType<QueryEngineEntitySummary>;\n};\n\n/**\n * Builds the entity-key-dependent parse schemas. Pass a consumer-owned\n * `entityKeySchema` (e.g. an enum with legacy normalization) to validate keys;\n * defaults to a non-empty string schema so the engine stays generic over any\n * key set while still rejecting unusable identifiers.\n */\nexport function createQueryEngineEntitySchemas(\n entityKeySchema: z.ZodType<QueryEngineEntityKey> = z.string().min(1),\n): QueryEngineEntitySchemas {\n const fieldDefinition: z.ZodType<QueryEngineFieldDefinition> = z.lazy(() =>\n z.object({\n path: z.string().min(1),\n label: z.string().min(1),\n type: QueryEngineFieldTypeSchema,\n enumType: z.string().nullable(),\n enumValues: z.array(z.string()),\n operators: z.array(QueryEngineOperatorSchema),\n relation: z.boolean(),\n relationEntityKey: entityKeySchema.nullable(),\n relationMode: QueryEngineRelationModeSchema,\n multiple: z.boolean(),\n relationSelectionLabelFields: z.array(z.string()),\n expandable: z.boolean(),\n maxDepth: z.number().int().nonnegative(),\n children: z.array(fieldDefinition),\n }),\n );\n\n const entityDefinition: z.ZodType<QueryEngineEntityDefinition> = z\n .object({\n key: entityKeySchema,\n title: z.string().min(1),\n fields: z.array(fieldDefinition),\n })\n .passthrough();\n\n const entitySummary: z.ZodType<QueryEngineEntitySummary> = z\n .object({\n key: entityKeySchema,\n filterableFieldCount: z.number().int().nonnegative(),\n })\n .passthrough();\n\n return { fieldDefinition, entityDefinition, entitySummary };\n}\n","import {\n createQueryEngineEntitySchemas,\n QueryEngineRelationOptionSchema,\n type QueryEngineEntityDefinition,\n type QueryEngineEntityKey,\n type QueryEngineEntitySummary,\n type QueryEngineRelationOption,\n} from \"../models/queryengine.models\";\nimport type z from \"zod\";\n\n/** Transport-neutral request options — no dependency on a specific HTTP client. */\nexport type QueryEngineRequestOptions = {\n params?: Record<string, unknown>;\n signal?: AbortSignal;\n};\n\n/**\n * Port the query engine needs from its host application. The app injects an\n * adapter (e.g. wrapping its axios client) so this module stays free of any\n * HTTP/infrastructure dependency.\n */\nexport interface QueryEngineHttpClient {\n /** GET a JSON resource relative to the query-engine base path. Returns raw JSON. */\n get(path: string, options?: QueryEngineRequestOptions): Promise<unknown>;\n}\n\nexport interface QueryEngineApi {\n listEntities(options?: QueryEngineRequestOptions): Promise<QueryEngineEntitySummary[]>;\n describeEntity(\n entityKey: QueryEngineEntityKey,\n options?: QueryEngineRequestOptions,\n ): Promise<QueryEngineEntityDefinition>;\n listRelationOptions(\n entityKey: QueryEngineEntityKey,\n fieldPath: string,\n searchText?: string,\n options?: QueryEngineRequestOptions,\n ): Promise<QueryEngineRelationOption[]>;\n}\n\nexport type CreateQueryEngineApiOptions = {\n /** Consumer-owned schema used to validate/normalize entity keys in responses. Defaults to `z.string()`. */\n entityKeySchema?: z.ZodType<QueryEngineEntityKey>;\n /** Optional legacy path key for back-compat retries (return `undefined` when there is none). */\n legacyEntityKey?: (entityKey: QueryEngineEntityKey) => string | undefined;\n};\n\n/** Builds a {@link QueryEngineApi} bound to a host-provided HTTP client. */\nexport function createQueryEngineApi(\n http: QueryEngineHttpClient,\n options?: CreateQueryEngineApiOptions,\n): QueryEngineApi {\n const { entityDefinition, entitySummary } = createQueryEngineEntitySchemas(options?.entityKeySchema);\n const legacyEntityKey = options?.legacyEntityKey;\n\n const pathSegment = (value: string, name: string): string => {\n if (value.trim().length === 0) throw new Error(`${name} must be a non-empty string.`);\n return encodeURIComponent(value);\n };\n\n const isAborted = (error: unknown, requestOptions?: QueryEngineRequestOptions): boolean =>\n requestOptions?.signal?.aborted === true ||\n (typeof DOMException !== \"undefined\" && error instanceof DOMException && error.name === \"AbortError\");\n\n const getParsed = async <TOutput>(\n path: string,\n schema: z.ZodType<TOutput>,\n reqOptions?: QueryEngineRequestOptions,\n ): Promise<TOutput> => schema.parse(await http.get(path, reqOptions));\n\n const describeEntity = async (\n entityKey: QueryEngineEntityKey,\n reqOptions?: QueryEngineRequestOptions,\n ): Promise<QueryEngineEntityDefinition> => {\n const encodedEntityKey = pathSegment(entityKey, \"entityKey\");\n try {\n return await getParsed(`entities/${encodedEntityKey}`, entityDefinition, reqOptions);\n } catch (error) {\n if (isAborted(error, reqOptions)) throw error;\n const legacy = legacyEntityKey?.(entityKey);\n if (legacy === undefined || legacy === entityKey) {\n throw error;\n }\n return await getParsed(`entities/${pathSegment(legacy, \"legacyEntityKey\")}`, entityDefinition, reqOptions);\n }\n };\n\n const listRelationOptions = async (\n entityKey: QueryEngineEntityKey,\n fieldPath: string,\n searchText?: string,\n reqOptions?: QueryEngineRequestOptions,\n ): Promise<QueryEngineRelationOption[]> => {\n const encodedEntityKey = pathSegment(entityKey, \"entityKey\");\n const encodedFieldPath = pathSegment(fieldPath, \"fieldPath\");\n const withSearch: QueryEngineRequestOptions = {\n ...reqOptions,\n params: { ...reqOptions?.params, searchText },\n };\n\n try {\n return await getParsed(\n `entities/${encodedEntityKey}/fields/${encodedFieldPath}/options`,\n QueryEngineRelationOptionSchema.array(),\n withSearch,\n );\n } catch (error) {\n if (isAborted(error, reqOptions)) throw error;\n const legacy = legacyEntityKey?.(entityKey);\n if (legacy === undefined || legacy === entityKey) {\n throw error;\n }\n return await getParsed(\n `entities/${pathSegment(legacy, \"legacyEntityKey\")}/fields/${encodedFieldPath}/options`,\n QueryEngineRelationOptionSchema.array(),\n withSearch,\n );\n }\n };\n\n return {\n listEntities: reqOptions => getParsed(\"entities\", entitySummary.array(), reqOptions),\n describeEntity,\n listRelationOptions,\n };\n}\n","export const QueryEngineQueryKey = {\n entities: \"queryengineEntities\",\n entityDefinition: \"queryengineEntityDefinition\",\n entityDefinitions: \"queryengineEntityDefinitions\",\n} as const;\n","import { QueryEngineOperatorSchema } from \"../models/queryengine.models\";\nimport z from \"zod\";\n\nexport type SqliteQueryFilterValueType = \"string\" | \"number\" | \"boolean\";\n\nexport type SqliteQueryFilterFieldAdapter = {\n expression: string;\n valueType: SqliteQueryFilterValueType;\n};\n\nexport type SqliteQueryFilterAdapter<TKey extends string | number, TEntityKey extends string = string> = {\n entity: TEntityKey;\n fromClause: string;\n keyExpression: string;\n keyAlias: string;\n baseWhereClause?: string;\n fieldAdapters: Record<string, SqliteQueryFilterFieldAdapter>;\n parseKey: (raw: unknown) => TKey;\n};\n\nexport type SqliteSearchSelectColumn = {\n alias: string;\n expression: string;\n};\n\nexport type SqliteSearchColumn = SqliteSearchSelectColumn & {\n valueType: SqliteQueryFilterValueType;\n /** Defaults to the alias; `false` makes the selected column unavailable to filters. */\n filterAs?: string | false;\n /** Defaults to the alias; `false` makes the selected column unavailable to sorting. */\n sortAs?: string | false;\n};\n\nexport type SqliteSearchColumnBindings = {\n fieldAdapters: Record<string, SqliteQueryFilterFieldAdapter>;\n selectColumns: SqliteSearchSelectColumn[];\n sortExpressionsByKey: Record<string, string>;\n};\n\nexport type CompiledQueryFilterWhere = {\n sql: string;\n params: unknown[];\n filterCount: number;\n};\n\ntype CompiledClause = { sql: string; params: unknown[] };\n\nconst QueryFilterRowSchema = z.object({\n kind: z.enum([\"leaf\", \"relation\"]).default(\"leaf\"),\n path: z.string().min(1),\n operator: QueryEngineOperatorSchema.optional(),\n value: z.string().optional(),\n parameterized: z.boolean().optional(),\n selectedOptions: z.array(z.object({ value: z.string(), label: z.string() })).optional(),\n});\n\nconst QueryFilterPayloadSchema = z.object({\n entity: z.string().optional(),\n rows: z.array(QueryFilterRowSchema).optional(),\n});\n\ntype QueryFilterRow = z.output<typeof QueryFilterRowSchema>;\ntype QueryFilterPayload = z.output<typeof QueryFilterPayloadSchema>;\n\nexport function bindSqliteSearchColumns(\n columns: readonly SqliteSearchColumn[],\n filterOnlyFields: Record<string, SqliteQueryFilterFieldAdapter> = {},\n): SqliteSearchColumnBindings {\n const fieldAdapters: Record<string, SqliteQueryFilterFieldAdapter> = { ...filterOnlyFields };\n const sortExpressionsByKey: Record<string, string> = {};\n const aliases = new Set<string>();\n\n for (const { alias, expression, valueType, filterAs, sortAs } of columns) {\n if (alias.trim().length === 0) throw new Error(\"SQLite search column alias must be a non-empty string.\");\n if (expression.trim().length === 0) {\n throw new Error(`SQLite search column \"${alias}\" expression must be a non-empty string.`);\n }\n if (aliases.has(alias)) throw new Error(`SQLite search column alias \"${alias}\" is registered more than once.`);\n aliases.add(alias);\n\n if (filterAs !== false) {\n const filterKey = filterAs ?? alias;\n if (fieldAdapters[filterKey]) {\n throw new Error(`SQLite filter field \"${filterKey}\" is registered more than once.`);\n }\n fieldAdapters[filterKey] = { expression, valueType };\n }\n if (sortAs !== false) sortExpressionsByKey[alias] = sortAs ?? alias;\n }\n\n return {\n fieldAdapters,\n selectColumns: columns.map(({ alias, expression }) => ({ alias, expression })),\n sortExpressionsByKey,\n };\n}\n\nfunction parseQueryFilterPayload(queryFiltersJson: string | null): QueryFilterPayload | null {\n if (!queryFiltersJson?.trim()) return null;\n try {\n return QueryFilterPayloadSchema.parse(JSON.parse(queryFiltersJson));\n } catch {\n throw new Error(\"Query filter JSON is invalid.\");\n }\n}\n\nfunction toBoundValue(rawValue: string, valueType: SqliteQueryFilterValueType): string | number {\n if (valueType === \"string\") return rawValue;\n if (valueType === \"boolean\") {\n const normalized = rawValue.trim().toLowerCase();\n if (normalized === \"true\" || normalized === \"1\") return 1;\n if (normalized === \"false\" || normalized === \"0\") return 0;\n throw new Error(`Invalid boolean filter value: ${rawValue}`);\n }\n\n const numericValue = Number(rawValue);\n if (!Number.isFinite(numericValue)) throw new Error(`Invalid numeric filter value: ${rawValue}`);\n return numericValue;\n}\n\nfunction splitCommaSeparated(value: string | undefined): string[] {\n if (!value?.trim()) return [];\n return value\n .split(\",\")\n .map(part => part.trim())\n .filter(Boolean);\n}\n\nfunction compileLeafClause(row: QueryFilterRow, field: SqliteQueryFilterFieldAdapter): CompiledClause | null {\n const { expression } = field;\n const operator = row.operator;\n if (!operator || row.parameterized) return null;\n if (operator === \"IS_NULL\") return { sql: `${expression} IS NULL`, params: [] };\n if (operator === \"IS_NOT_NULL\") return { sql: `${expression} IS NOT NULL`, params: [] };\n\n if (operator === \"DATE_RANGE\") {\n const [from = \"\", to = \"\"] = String(row.value ?? \"\").split(\"|\");\n const clauses: string[] = [];\n const params: unknown[] = [];\n if (from.trim()) {\n clauses.push(`${expression} >= ?`);\n params.push(toBoundValue(from.trim(), field.valueType));\n }\n if (to.trim()) {\n clauses.push(`${expression} <= ?`);\n params.push(toBoundValue(to.trim(), field.valueType));\n }\n return clauses.length > 0 ? { sql: `(${clauses.join(\" AND \")})`, params } : null;\n }\n\n if (operator === \"IN\") {\n const values = splitCommaSeparated(row.value);\n if (values.length === 0) return null;\n return {\n sql: `${expression} IN (${values.map(() => \"?\").join(\", \")})`,\n params: values.map(value => toBoundValue(value, field.valueType)),\n };\n }\n\n const rawValue = row.value?.trim();\n if (!rawValue) return null;\n const comparableValue = toBoundValue(rawValue, field.valueType);\n const loweredExpression = `LOWER(COALESCE(CAST(${expression} AS TEXT), ''))`;\n\n switch (operator) {\n case \"EQUALS\":\n return { sql: `${expression} = ?`, params: [comparableValue] };\n case \"NOT_EQUALS\":\n return { sql: `${expression} <> ?`, params: [comparableValue] };\n case \"CONTAINS\":\n return { sql: `${loweredExpression} LIKE ?`, params: [`%${rawValue.toLowerCase()}%`] };\n case \"STARTS_WITH\":\n return { sql: `${loweredExpression} LIKE ?`, params: [`${rawValue.toLowerCase()}%`] };\n case \"ENDS_WITH\":\n return { sql: `${loweredExpression} LIKE ?`, params: [`%${rawValue.toLowerCase()}`] };\n case \"GREATER_THAN\":\n return { sql: `${expression} > ?`, params: [comparableValue] };\n case \"GREATER_OR_EQUAL\":\n return { sql: `${expression} >= ?`, params: [comparableValue] };\n case \"LESS_THAN\":\n return { sql: `${expression} < ?`, params: [comparableValue] };\n case \"LESS_OR_EQUAL\":\n return { sql: `${expression} <= ?`, params: [comparableValue] };\n default:\n return null;\n }\n}\n\nfunction compileRelationClause(row: QueryFilterRow, field: SqliteQueryFilterFieldAdapter): CompiledClause | null {\n const values = (row.selectedOptions ?? [])\n .map(option => option.value?.trim())\n .filter((value): value is string => Boolean(value));\n if (values.length === 0) return null;\n return {\n sql: `${field.expression} IN (${values.map(() => \"?\").join(\", \")})`,\n params: values.map(value => toBoundValue(value, field.valueType)),\n };\n}\n\nexport function compileQueryFilterWhere<TKey extends string | number, TEntityKey extends string>(\n adapter: SqliteQueryFilterAdapter<TKey, TEntityKey>,\n queryFiltersJson: string | null,\n): CompiledQueryFilterWhere {\n const payload = parseQueryFilterPayload(queryFiltersJson);\n if (payload?.entity && payload.entity !== adapter.entity) {\n throw new Error(`Invalid filter entity. Expected: ${adapter.entity}`);\n }\n\n const compiled = (Array.isArray(payload?.rows) ? payload.rows : []).flatMap(row => {\n if (!row?.path || row.parameterized) return [];\n const field = adapter.fieldAdapters[row.path];\n if (!field) throw new Error(`Unknown SQLite filter field: ${row.path}`);\n const clause = row.kind === \"relation\" ? compileRelationClause(row, field) : compileLeafClause(row, field);\n return clause ? [clause] : [];\n });\n\n return {\n sql: [adapter.baseWhereClause ?? \"1 = 1\", ...compiled.map(clause => clause.sql)].join(\" AND \"),\n params: compiled.flatMap(clause => clause.params),\n filterCount: compiled.length,\n };\n}\n\nexport function compileSearchTextWhere(\n searchText: string | undefined,\n searchExpressions: readonly string[] | undefined,\n): CompiledClause | null {\n const normalized = searchText?.trim().toLocaleLowerCase() ?? \"\";\n if (!normalized || !searchExpressions || searchExpressions.length === 0) return null;\n return {\n sql: `(${searchExpressions\n .map(expression => `LOWER(COALESCE(CAST(${expression} AS TEXT), '')) LIKE ?`)\n .join(\" OR \")})`,\n params: searchExpressions.map(() => `%${normalized}%`),\n };\n}\n","import z from \"zod\";\n\nexport type SqliteQueryEngineConfigRecord = {\n entities: unknown[];\n entityDefinitions: Record<string, unknown>;\n};\n\nexport const SqliteQueryEngineConfigRecordSchema: z.ZodType<SqliteQueryEngineConfigRecord> = z.object({\n entities: z.array(z.unknown()),\n entityDefinitions: z.record(z.string(), z.unknown()),\n});\n\nexport type QueryEngineConfigSqliteOperationMap = {\n replaceQueryEngineConfig: { request: { config: SqliteQueryEngineConfigRecord }; response: null };\n getQueryEngineConfig: { request: Record<string, never>; response: SqliteQueryEngineConfigRecord | null };\n};\n\nexport type QueryEngineConfigSqliteStatement = {\n bind: (values: readonly unknown[]) => unknown;\n step: () => boolean;\n get: (target: unknown[]) => unknown[];\n finalize: () => unknown;\n};\n\nexport type QueryEngineConfigSqliteDatabase = {\n prepare: (sql: string) => QueryEngineConfigSqliteStatement;\n};\n\nexport type QueryEngineConfigSqliteRequest = Record<string, unknown>;\nexport type QueryEngineConfigSqliteRequestHandler = (\n db: QueryEngineConfigSqliteDatabase,\n request: QueryEngineConfigSqliteRequest,\n) => unknown;\n\nexport type CreateQueryEngineConfigSqliteHandlersConfig = {\n tableName: string;\n singletonKey?: string;\n requestTypes?: { replace: string; get: string };\n parseJson?: <T>(value: string, fallback: T) => T;\n};\n\nfunction defaultParseJson<T>(value: string): T {\n try {\n return JSON.parse(value) as T;\n } catch {\n throw new Error(\"Stored query-engine config contains malformed JSON.\");\n }\n}\n\nfunction assertSqliteIdentifier(value: string, label: string): void {\n if (!/^[A-Za-z_][A-Za-z0-9_]*(?:\\.[A-Za-z_][A-Za-z0-9_]*)?$/.test(value)) {\n throw new Error(`${label} must be a valid SQLite identifier.`);\n }\n}\n\nfunction resolveSingletonKey(value: string | undefined): string {\n const singletonKey = value ?? \"singleton\";\n if (!singletonKey.trim()) throw new Error(\"Query-engine config singleton key must be non-empty.\");\n return singletonKey;\n}\n\nfunction resolveRequestTypes(requestTypes: { replace: string; get: string } | undefined): {\n replace: string;\n get: string;\n} {\n const resolved = requestTypes ?? {\n replace: \"replaceQueryEngineConfig\",\n get: \"getQueryEngineConfig\",\n };\n if (!resolved.replace.trim() || !resolved.get.trim()) {\n throw new Error(\"Query-engine config request types must be non-empty.\");\n }\n if (resolved.replace === resolved.get) {\n throw new Error(\"Query-engine config replace and get request types must be distinct.\");\n }\n return resolved;\n}\n\nexport function replaceQueryEngineConfig(\n db: QueryEngineConfigSqliteDatabase,\n config: SqliteQueryEngineConfigRecord,\n options: { tableName: string; singletonKey?: string },\n): void {\n assertSqliteIdentifier(options.tableName, \"Query-engine config table name\");\n const parsedConfig = SqliteQueryEngineConfigRecordSchema.parse(config);\n const statement = db.prepare(`\n INSERT INTO ${options.tableName} (singleton_key, entities_json, entity_definitions_json)\n VALUES (?, ?, ?)\n ON CONFLICT(singleton_key) DO UPDATE SET\n entities_json = excluded.entities_json,\n entity_definitions_json = excluded.entity_definitions_json;\n `);\n try {\n statement.bind([\n resolveSingletonKey(options.singletonKey),\n JSON.stringify(parsedConfig.entities),\n JSON.stringify(parsedConfig.entityDefinitions),\n ]);\n statement.step();\n } finally {\n statement.finalize();\n }\n}\n\nexport function getQueryEngineConfig(\n db: QueryEngineConfigSqliteDatabase,\n options: {\n tableName: string;\n singletonKey?: string;\n parseJson?: <T>(value: string, fallback: T) => T;\n },\n): SqliteQueryEngineConfigRecord | null {\n assertSqliteIdentifier(options.tableName, \"Query-engine config table name\");\n const statement = db.prepare(`\n SELECT entities_json, entity_definitions_json\n FROM ${options.tableName}\n WHERE singleton_key = ?\n LIMIT 1;\n `);\n try {\n statement.bind([resolveSingletonKey(options.singletonKey)]);\n if (!statement.step()) return null;\n const row = statement.get([]);\n const parseJson = options.parseJson ?? defaultParseJson;\n return SqliteQueryEngineConfigRecordSchema.parse({\n entities: parseJson<unknown[]>(String(row[0] ?? \"[]\"), []),\n entityDefinitions: parseJson<Record<string, unknown>>(String(row[1] ?? \"{}\"), {}),\n });\n } finally {\n statement.finalize();\n }\n}\n\nexport function createQueryEngineConfigSqliteRequestHandlers(\n config: CreateQueryEngineConfigSqliteHandlersConfig,\n): Record<string, QueryEngineConfigSqliteRequestHandler> {\n assertSqliteIdentifier(config.tableName, \"Query-engine config table name\");\n resolveSingletonKey(config.singletonKey);\n const requestTypes = resolveRequestTypes(config.requestTypes);\n const persistenceOptions = {\n tableName: config.tableName,\n singletonKey: config.singletonKey,\n };\n\n return {\n [requestTypes.replace]: (db, request) => {\n replaceQueryEngineConfig(db, SqliteQueryEngineConfigRecordSchema.parse(request.config), persistenceOptions);\n return null;\n },\n [requestTypes.get]: db => {\n return getQueryEngineConfig(db, { ...persistenceOptions, parseJson: config.parseJson });\n },\n };\n}\n","import { SqliteQueryEngineConfigRecordSchema, type SqliteQueryEngineConfigRecord } from \"./queryEngineConfigSqlite\";\n\nexport type QueryEngineConfigClientRuntime = {\n shouldUseInMemoryFallback: () => boolean;\n registerInMemoryStore: (store: { clear: () => void }) => () => void;\n};\n\nexport type QueryEngineConfigClientTransport = {\n sendRequest: <TResponse>(type: string, payload?: Record<string, unknown>) => Promise<TResponse>;\n};\n\nexport type QueryEngineConfigClient = {\n replace: (config: SqliteQueryEngineConfigRecord) => Promise<void>;\n get: () => Promise<SqliteQueryEngineConfigRecord | null>;\n setFallback: (config: SqliteQueryEngineConfigRecord | null) => void;\n getFallback: () => SqliteQueryEngineConfigRecord | null;\n dispose: () => void;\n};\n\nexport function createQueryEngineConfigClient(config: {\n runtime: QueryEngineConfigClientRuntime;\n transport: QueryEngineConfigClientTransport;\n requestTypes?: { replace: string; get: string };\n}): QueryEngineConfigClient {\n const requestTypes = config.requestTypes ?? {\n replace: \"replaceQueryEngineConfig\",\n get: \"getQueryEngineConfig\",\n };\n if (!requestTypes.replace.trim() || !requestTypes.get.trim()) {\n throw new Error(\"Query-engine config request types must be non-empty.\");\n }\n if (requestTypes.replace === requestTypes.get) {\n throw new Error(\"Query-engine config replace and get request types must be distinct.\");\n }\n let fallback: SqliteQueryEngineConfigRecord | null = null;\n const unregister = config.runtime.registerInMemoryStore({ clear: () => (fallback = null) });\n\n return {\n async replace(value) {\n const parsedValue = SqliteQueryEngineConfigRecordSchema.parse(value);\n if (config.runtime.shouldUseInMemoryFallback()) {\n fallback = parsedValue;\n return;\n }\n await config.transport.sendRequest<null>(requestTypes.replace, { config: parsedValue });\n },\n async get() {\n if (config.runtime.shouldUseInMemoryFallback()) return fallback;\n return await config.transport.sendRequest<SqliteQueryEngineConfigRecord | null>(requestTypes.get);\n },\n setFallback(value) {\n fallback = value == null ? null : SqliteQueryEngineConfigRecordSchema.parse(value);\n },\n getFallback: () => fallback,\n dispose: unregister,\n };\n}\n","import {\n compileQueryFilterWhere,\n compileSearchTextWhere,\n type SqliteQueryFilterAdapter,\n type SqliteSearchSelectColumn,\n} from \"./compileQueryFilterWhere\";\n\nexport type SqlitePageableParams = {\n page: number;\n rowsPerPage: number;\n sortBy?: string;\n sortDirection?: string;\n};\n\nexport type SqlitePageableResponse<TRow> = {\n content: TRow[];\n number: number;\n size: number;\n totalElements: number;\n totalPages: number;\n};\n\nexport type ParameterizedSqlitePagedQueryRequest = {\n selectSql: string;\n fromSql: string;\n whereSql: string;\n whereParams: unknown[];\n orderBySql: string;\n limit: number | null;\n offset: number | null;\n includeTotalCount: boolean;\n clientSentAtMs?: number;\n};\n\nexport type ParameterizedSqliteQueryRequest = { sql: string; params: unknown[] };\nexport type ParameterizedSqliteQueryResult = { columns: string[]; rows: unknown[][] };\nexport type ParameterizedSqlitePagedQueryResult = ParameterizedSqliteQueryResult & {\n totalElements: number | null;\n queueWaitMs?: number;\n workerExecMs?: number;\n};\n\nexport type SqlitePagedSearchArgs<TKey extends string | number, TEntityKey extends string, TRow> = {\n adapter: SqliteQueryFilterAdapter<TKey, TEntityKey>;\n queryFiltersJson: string | null;\n pageable: SqlitePageableParams;\n selectColumns: SqliteSearchSelectColumn[];\n sortBy: string | undefined;\n sortDirection: string | undefined;\n defaultSortExpression: string;\n sortExpressionsByKey: Record<string, string>;\n mapRow: (row: unknown[], columnIndexes: Record<string, number>) => TRow;\n searchText?: string;\n searchExpressions?: string[];\n includeTotalCount?: boolean;\n};\n\nexport type CreateSqliteQueryExecutorConfig = {\n executePagedQuery: (request: ParameterizedSqlitePagedQueryRequest) => Promise<ParameterizedSqlitePagedQueryResult>;\n executeQuery: (request: ParameterizedSqliteQueryRequest) => Promise<ParameterizedSqliteQueryResult>;\n now?: () => number;\n log?: (message: string, details: Record<string, unknown>) => void;\n};\n\nexport type SqliteQueryExecutor = {\n pagedSearch: <TKey extends string | number, TEntityKey extends string, TRow>(\n args: SqlitePagedSearchArgs<TKey, TEntityKey, TRow>,\n ) => Promise<SqlitePageableResponse<TRow>>;\n findMatchingKeys: <TKey extends string | number, TEntityKey extends string>(\n adapter: SqliteQueryFilterAdapter<TKey, TEntityKey>,\n queryFiltersJson: string | null,\n ) => Promise<Set<TKey> | null>;\n};\n\nexport type QueryExecutorSqliteStatement = {\n bind: (values: readonly unknown[]) => unknown;\n step: () => boolean;\n get: (target: unknown[]) => unknown[];\n getColumnNames?: (target?: string[]) => string[];\n finalize: () => unknown;\n};\n\nexport type QueryExecutorSqliteDatabase = { prepare: (sql: string) => QueryExecutorSqliteStatement };\n\nfunction defaultNow(): number {\n return typeof performance !== \"undefined\" && typeof performance.now === \"function\" ? performance.now() : Date.now();\n}\n\nfunction stableStringify(value: unknown): string {\n if (value == null || typeof value !== \"object\") return JSON.stringify(value) ?? String(value);\n if (Array.isArray(value)) return `[${value.map(item => stableStringify(item)).join(\",\")}]`;\n const entries = Object.entries(value as Record<string, unknown>).sort(([left], [right]) => left.localeCompare(right));\n return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${stableStringify(item)}`).join(\",\")}}`;\n}\n\nfunction toColumnIndexes(columns: string[]): Record<string, number> {\n return Object.fromEntries(columns.map((column, index) => [column, index]));\n}\n\nfunction normalizePage(pageable: SqlitePageableParams): number {\n return Number.isFinite(pageable.page) ? Math.max(0, Math.floor(pageable.page)) : 0;\n}\n\nfunction normalizeRowsPerPage(pageable: SqlitePageableParams): number {\n return Number.isFinite(pageable.rowsPerPage) ? Math.floor(pageable.rowsPerPage) : -1;\n}\n\nfunction normalizePagination(pageable: SqlitePageableParams): { limit: number | null; offset: number | null } {\n const page = normalizePage(pageable);\n const rowsPerPage = normalizeRowsPerPage(pageable);\n return rowsPerPage <= 0 ? { limit: null, offset: null } : { limit: rowsPerPage, offset: page * rowsPerPage };\n}\n\nfunction getSortExpression(\n sortBy: string | undefined,\n defaultSortExpression: string,\n sortExpressionsByKey: Record<string, string>,\n): string {\n return sortBy ? (sortExpressionsByKey[sortBy] ?? defaultSortExpression) : defaultSortExpression;\n}\n\nexport function createSqliteQueryExecutor(config: CreateSqliteQueryExecutorConfig): SqliteQueryExecutor {\n const now = config.now ?? defaultNow;\n const inFlightPagedQueries = new Map<string, Promise<ParameterizedSqlitePagedQueryResult>>();\n\n const executePagedQuery = async (\n request: ParameterizedSqlitePagedQueryRequest,\n ): Promise<ParameterizedSqlitePagedQueryResult> => {\n const key = stableStringify(request);\n const timedRequest = { ...request, clientSentAtMs: now() };\n const existing = inFlightPagedQueries.get(key);\n if (existing) return await existing;\n const promise = config.executePagedQuery(timedRequest);\n inFlightPagedQueries.set(key, promise);\n try {\n return await promise;\n } finally {\n inFlightPagedQueries.delete(key);\n }\n };\n\n return {\n async pagedSearch(args) {\n const totalStartedAt = now();\n const page = normalizePage(args.pageable);\n const rowsPerPage = normalizeRowsPerPage(args.pageable);\n const compiledFilter = compileQueryFilterWhere(args.adapter, args.queryFiltersJson);\n const compiledSearch = compileSearchTextWhere(args.searchText, args.searchExpressions);\n const whereSql = [compiledFilter.sql, compiledSearch?.sql].filter(Boolean).join(\" AND \");\n const whereParams = [...compiledFilter.params, ...(compiledSearch?.params ?? [])];\n const selectSql = args.selectColumns.map(column => `${column.expression} AS ${column.alias}`).join(\", \");\n const sortExpression = getSortExpression(args.sortBy, args.defaultSortExpression, args.sortExpressionsByKey);\n const sortDirection = args.sortDirection?.trim().toLowerCase() === \"desc\" ? \"DESC\" : \"ASC\";\n const { limit, offset } = normalizePagination(args.pageable);\n const includeTotalCount = args.includeTotalCount ?? true;\n const effectiveLimit = !includeTotalCount && limit != null && limit > 0 ? limit + 1 : limit;\n const sqlStartedAt = now();\n const result = await executePagedQuery({\n selectSql,\n fromSql: args.adapter.fromClause,\n whereSql,\n whereParams,\n orderBySql: `${sortExpression} ${sortDirection}`,\n limit: effectiveLimit,\n offset,\n includeTotalCount,\n });\n const sqlExecMs = Math.round(now() - sqlStartedAt);\n let rows = result.rows;\n let hasNextPage = false;\n if (!includeTotalCount && limit != null && limit > 0 && rows.length > limit) {\n rows = rows.slice(0, limit);\n hasNextPage = true;\n }\n\n const mapStartedAt = now();\n const indexes = toColumnIndexes(result.columns);\n const content = rows.map(row => args.mapRow(row, indexes));\n const totalElements = includeTotalCount\n ? (result.totalElements ?? 0)\n : rowsPerPage > 0\n ? page * rowsPerPage + content.length + (hasNextPage ? 1 : 0)\n : content.length;\n const response = {\n content,\n number: page,\n size: rowsPerPage > 0 ? rowsPerPage : content.length,\n totalElements,\n totalPages: rowsPerPage > 0 ? Math.ceil(totalElements / rowsPerPage) : 0,\n };\n\n config.log?.(\"paged search done\", {\n entity: args.adapter.entity,\n totalMs: Math.round(now() - totalStartedAt),\n sqlExecMs,\n queueWaitMs: result.queueWaitMs ?? null,\n workerExecMs: result.workerExecMs ?? null,\n mapRowsMs: Math.round(now() - mapStartedAt),\n rows: content.length,\n totalElements,\n includeTotalCount,\n hasNextPage,\n whereClauses: 1 + compiledFilter.filterCount + (compiledSearch ? 1 : 0),\n searchTextLength: args.searchText?.trim().length ?? 0,\n sortBy: args.sortBy ?? null,\n sortDirection: args.sortDirection ?? \"asc\",\n page,\n rowsPerPage,\n });\n return response;\n },\n\n async findMatchingKeys(adapter, queryFiltersJson) {\n const compiled = compileQueryFilterWhere(adapter, queryFiltersJson);\n if (compiled.filterCount === 0) return null;\n const result = await config.executeQuery({\n sql: `\n SELECT DISTINCT ${adapter.keyExpression} AS ${adapter.keyAlias}\n FROM ${adapter.fromClause}\n WHERE ${compiled.sql};\n `,\n params: compiled.params,\n });\n const keyIndex = result.columns.indexOf(adapter.keyAlias);\n if (keyIndex < 0) return new Set();\n return new Set(result.rows.map(row => adapter.parseKey(row[keyIndex])));\n },\n };\n}\n\nfunction readRows(statement: QueryExecutorSqliteStatement): ParameterizedSqliteQueryResult {\n const columns = statement.getColumnNames?.([]) ?? [];\n const rows: unknown[][] = [];\n while (statement.step()) rows.push(statement.get([]));\n return { columns, rows };\n}\n\nfunction runBoundQuery(\n db: QueryExecutorSqliteDatabase,\n sql: string,\n params: readonly unknown[],\n): ParameterizedSqliteQueryResult {\n const statement = db.prepare(sql);\n try {\n statement.bind(params);\n return readRows(statement);\n } finally {\n statement.finalize();\n }\n}\n\nexport function executeParameterizedSqliteQuery(\n db: QueryExecutorSqliteDatabase,\n request: ParameterizedSqliteQueryRequest,\n): ParameterizedSqliteQueryResult {\n return runBoundQuery(db, request.sql, request.params);\n}\n\nexport function executeParameterizedSqlitePagedQuery(\n db: QueryExecutorSqliteDatabase,\n request: ParameterizedSqlitePagedQueryRequest,\n now: () => number = defaultNow,\n): ParameterizedSqlitePagedQueryResult {\n if (request.limit != null && (!Number.isInteger(request.limit) || request.limit <= 0)) {\n throw new Error(\"SQLite page limit must be a positive integer or null.\");\n }\n if (request.offset != null && (!Number.isInteger(request.offset) || request.offset < 0)) {\n throw new Error(\"SQLite page offset must be a non-negative integer or null.\");\n }\n\n const startedAt = now();\n const queueWaitMs =\n request.clientSentAtMs == null || !Number.isFinite(request.clientSentAtMs)\n ? undefined\n : Math.max(0, Math.round(startedAt - request.clientSentAtMs));\n const pagination =\n request.limit != null && request.limit > 0\n ? `LIMIT ${request.limit}${request.offset != null && request.offset > 0 ? ` OFFSET ${request.offset}` : \"\"}`\n : \"\";\n const page = runBoundQuery(\n db,\n `SELECT ${request.selectSql} FROM ${request.fromSql} WHERE ${request.whereSql} ORDER BY ${request.orderBySql} ${pagination};`,\n request.whereParams,\n );\n if (!request.includeTotalCount) {\n return { ...page, totalElements: null, queueWaitMs, workerExecMs: Math.round(now() - startedAt) };\n }\n\n const count = runBoundQuery(\n db,\n `SELECT COUNT(*) AS total_elements FROM ${request.fromSql} WHERE ${request.whereSql};`,\n request.whereParams,\n );\n return {\n ...page,\n totalElements: Number(count.rows[0]?.[0] ?? 0),\n queueWaitMs,\n workerExecMs: Math.round(now() - startedAt),\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,94 @@
1
+ import { default as z } from 'zod';
2
+ /**
3
+ * Entity keys are opaque strings at the library level. The consuming app owns
4
+ * its entity-key set and injects a validating/normalizing schema via
5
+ * {@link createQueryEngineEntitySchemas} (see createQueryEngineApi options).
6
+ */
7
+ export type QueryEngineEntityKey = string;
8
+ export declare const QueryEngineOperatorSchema: z.ZodEnum<{
9
+ EQUALS: "EQUALS";
10
+ NOT_EQUALS: "NOT_EQUALS";
11
+ CONTAINS: "CONTAINS";
12
+ STARTS_WITH: "STARTS_WITH";
13
+ ENDS_WITH: "ENDS_WITH";
14
+ IN: "IN";
15
+ GREATER_THAN: "GREATER_THAN";
16
+ GREATER_OR_EQUAL: "GREATER_OR_EQUAL";
17
+ LESS_THAN: "LESS_THAN";
18
+ LESS_OR_EQUAL: "LESS_OR_EQUAL";
19
+ DATE_RANGE: "DATE_RANGE";
20
+ IS_NULL: "IS_NULL";
21
+ IS_NOT_NULL: "IS_NOT_NULL";
22
+ }>;
23
+ export type QueryEngineOperator = z.infer<typeof QueryEngineOperatorSchema>;
24
+ export declare const QueryEngineFieldTypeSchema: z.ZodEnum<{
25
+ STRING: "STRING";
26
+ NUMBER: "NUMBER";
27
+ BOOLEAN: "BOOLEAN";
28
+ DATE: "DATE";
29
+ ENUM: "ENUM";
30
+ RELATION: "RELATION";
31
+ }>;
32
+ export type QueryEngineFieldType = z.infer<typeof QueryEngineFieldTypeSchema>;
33
+ export declare const QueryEngineRelationModeSchema: z.ZodEnum<{
34
+ CHILD: "CHILD";
35
+ SELECTION: "SELECTION";
36
+ BOTH: "BOTH";
37
+ }>;
38
+ export type QueryEngineRelationMode = z.infer<typeof QueryEngineRelationModeSchema>;
39
+ export interface QueryEngineRelationOption {
40
+ value: string;
41
+ label: string;
42
+ }
43
+ export declare const QueryEngineRelationOptionSchema: z.ZodType<QueryEngineRelationOption>;
44
+ export interface QueryEngineFieldDefinition {
45
+ path: string;
46
+ label: string;
47
+ type: QueryEngineFieldType;
48
+ enumType: string | null;
49
+ enumValues: string[];
50
+ operators: QueryEngineOperator[];
51
+ relation: boolean;
52
+ relationEntityKey: QueryEngineEntityKey | null;
53
+ relationMode: QueryEngineRelationMode;
54
+ multiple: boolean;
55
+ relationSelectionLabelFields: string[];
56
+ expandable: boolean;
57
+ maxDepth: number;
58
+ children: QueryEngineFieldDefinition[];
59
+ }
60
+ /**
61
+ * The published shape of an entity. Backends are free to send more — the parse
62
+ * schemas pass unknown keys through untouched, so a consumer can widen this type
63
+ * with its own backend-specific fields:
64
+ *
65
+ * ```ts
66
+ * type AppEntityDefinition = QueryEngineEntityDefinition & { javaType: string };
67
+ * ```
68
+ */
69
+ export interface QueryEngineEntityDefinition {
70
+ key: QueryEngineEntityKey;
71
+ title: string;
72
+ fields: QueryEngineFieldDefinition[];
73
+ }
74
+ export interface QueryEngineEntitySummary {
75
+ key: QueryEngineEntityKey;
76
+ filterableFieldCount: number;
77
+ }
78
+ export interface QueryEngineRelationFieldOptionsRequest {
79
+ entityKey: QueryEngineEntityKey;
80
+ fieldPath: string;
81
+ searchText?: string;
82
+ }
83
+ export type QueryEngineEntitySchemas = {
84
+ fieldDefinition: z.ZodType<QueryEngineFieldDefinition>;
85
+ entityDefinition: z.ZodType<QueryEngineEntityDefinition>;
86
+ entitySummary: z.ZodType<QueryEngineEntitySummary>;
87
+ };
88
+ /**
89
+ * Builds the entity-key-dependent parse schemas. Pass a consumer-owned
90
+ * `entityKeySchema` (e.g. an enum with legacy normalization) to validate keys;
91
+ * defaults to a non-empty string schema so the engine stays generic over any
92
+ * key set while still rejecting unusable identifiers.
93
+ */
94
+ export declare function createQueryEngineEntitySchemas(entityKeySchema?: z.ZodType<QueryEngineEntityKey>): QueryEngineEntitySchemas;
@@ -0,0 +1,5 @@
1
+ export declare const QueryEngineQueryKey: {
2
+ readonly entities: "queryengineEntities";
3
+ readonly entityDefinition: "queryengineEntityDefinition";
4
+ readonly entityDefinitions: "queryengineEntityDefinitions";
5
+ };
@@ -0,0 +1,43 @@
1
+ export type SqliteQueryFilterValueType = "string" | "number" | "boolean";
2
+ export type SqliteQueryFilterFieldAdapter = {
3
+ expression: string;
4
+ valueType: SqliteQueryFilterValueType;
5
+ };
6
+ export type SqliteQueryFilterAdapter<TKey extends string | number, TEntityKey extends string = string> = {
7
+ entity: TEntityKey;
8
+ fromClause: string;
9
+ keyExpression: string;
10
+ keyAlias: string;
11
+ baseWhereClause?: string;
12
+ fieldAdapters: Record<string, SqliteQueryFilterFieldAdapter>;
13
+ parseKey: (raw: unknown) => TKey;
14
+ };
15
+ export type SqliteSearchSelectColumn = {
16
+ alias: string;
17
+ expression: string;
18
+ };
19
+ export type SqliteSearchColumn = SqliteSearchSelectColumn & {
20
+ valueType: SqliteQueryFilterValueType;
21
+ /** Defaults to the alias; `false` makes the selected column unavailable to filters. */
22
+ filterAs?: string | false;
23
+ /** Defaults to the alias; `false` makes the selected column unavailable to sorting. */
24
+ sortAs?: string | false;
25
+ };
26
+ export type SqliteSearchColumnBindings = {
27
+ fieldAdapters: Record<string, SqliteQueryFilterFieldAdapter>;
28
+ selectColumns: SqliteSearchSelectColumn[];
29
+ sortExpressionsByKey: Record<string, string>;
30
+ };
31
+ export type CompiledQueryFilterWhere = {
32
+ sql: string;
33
+ params: unknown[];
34
+ filterCount: number;
35
+ };
36
+ type CompiledClause = {
37
+ sql: string;
38
+ params: unknown[];
39
+ };
40
+ export declare function bindSqliteSearchColumns(columns: readonly SqliteSearchColumn[], filterOnlyFields?: Record<string, SqliteQueryFilterFieldAdapter>): SqliteSearchColumnBindings;
41
+ export declare function compileQueryFilterWhere<TKey extends string | number, TEntityKey extends string>(adapter: SqliteQueryFilterAdapter<TKey, TEntityKey>, queryFiltersJson: string | null): CompiledQueryFilterWhere;
42
+ export declare function compileSearchTextWhere(searchText: string | undefined, searchExpressions: readonly string[] | undefined): CompiledClause | null;
43
+ export {};
@@ -0,0 +1,25 @@
1
+ import { SqliteQueryEngineConfigRecord } from './queryEngineConfigSqlite';
2
+ export type QueryEngineConfigClientRuntime = {
3
+ shouldUseInMemoryFallback: () => boolean;
4
+ registerInMemoryStore: (store: {
5
+ clear: () => void;
6
+ }) => () => void;
7
+ };
8
+ export type QueryEngineConfigClientTransport = {
9
+ sendRequest: <TResponse>(type: string, payload?: Record<string, unknown>) => Promise<TResponse>;
10
+ };
11
+ export type QueryEngineConfigClient = {
12
+ replace: (config: SqliteQueryEngineConfigRecord) => Promise<void>;
13
+ get: () => Promise<SqliteQueryEngineConfigRecord | null>;
14
+ setFallback: (config: SqliteQueryEngineConfigRecord | null) => void;
15
+ getFallback: () => SqliteQueryEngineConfigRecord | null;
16
+ dispose: () => void;
17
+ };
18
+ export declare function createQueryEngineConfigClient(config: {
19
+ runtime: QueryEngineConfigClientRuntime;
20
+ transport: QueryEngineConfigClientTransport;
21
+ requestTypes?: {
22
+ replace: string;
23
+ get: string;
24
+ };
25
+ }): QueryEngineConfigClient;
@@ -0,0 +1,75 @@
1
+ import { SqliteQueryFilterAdapter, SqliteSearchSelectColumn } from './compileQueryFilterWhere';
2
+ export type SqlitePageableParams = {
3
+ page: number;
4
+ rowsPerPage: number;
5
+ sortBy?: string;
6
+ sortDirection?: string;
7
+ };
8
+ export type SqlitePageableResponse<TRow> = {
9
+ content: TRow[];
10
+ number: number;
11
+ size: number;
12
+ totalElements: number;
13
+ totalPages: number;
14
+ };
15
+ export type ParameterizedSqlitePagedQueryRequest = {
16
+ selectSql: string;
17
+ fromSql: string;
18
+ whereSql: string;
19
+ whereParams: unknown[];
20
+ orderBySql: string;
21
+ limit: number | null;
22
+ offset: number | null;
23
+ includeTotalCount: boolean;
24
+ clientSentAtMs?: number;
25
+ };
26
+ export type ParameterizedSqliteQueryRequest = {
27
+ sql: string;
28
+ params: unknown[];
29
+ };
30
+ export type ParameterizedSqliteQueryResult = {
31
+ columns: string[];
32
+ rows: unknown[][];
33
+ };
34
+ export type ParameterizedSqlitePagedQueryResult = ParameterizedSqliteQueryResult & {
35
+ totalElements: number | null;
36
+ queueWaitMs?: number;
37
+ workerExecMs?: number;
38
+ };
39
+ export type SqlitePagedSearchArgs<TKey extends string | number, TEntityKey extends string, TRow> = {
40
+ adapter: SqliteQueryFilterAdapter<TKey, TEntityKey>;
41
+ queryFiltersJson: string | null;
42
+ pageable: SqlitePageableParams;
43
+ selectColumns: SqliteSearchSelectColumn[];
44
+ sortBy: string | undefined;
45
+ sortDirection: string | undefined;
46
+ defaultSortExpression: string;
47
+ sortExpressionsByKey: Record<string, string>;
48
+ mapRow: (row: unknown[], columnIndexes: Record<string, number>) => TRow;
49
+ searchText?: string;
50
+ searchExpressions?: string[];
51
+ includeTotalCount?: boolean;
52
+ };
53
+ export type CreateSqliteQueryExecutorConfig = {
54
+ executePagedQuery: (request: ParameterizedSqlitePagedQueryRequest) => Promise<ParameterizedSqlitePagedQueryResult>;
55
+ executeQuery: (request: ParameterizedSqliteQueryRequest) => Promise<ParameterizedSqliteQueryResult>;
56
+ now?: () => number;
57
+ log?: (message: string, details: Record<string, unknown>) => void;
58
+ };
59
+ export type SqliteQueryExecutor = {
60
+ pagedSearch: <TKey extends string | number, TEntityKey extends string, TRow>(args: SqlitePagedSearchArgs<TKey, TEntityKey, TRow>) => Promise<SqlitePageableResponse<TRow>>;
61
+ findMatchingKeys: <TKey extends string | number, TEntityKey extends string>(adapter: SqliteQueryFilterAdapter<TKey, TEntityKey>, queryFiltersJson: string | null) => Promise<Set<TKey> | null>;
62
+ };
63
+ export type QueryExecutorSqliteStatement = {
64
+ bind: (values: readonly unknown[]) => unknown;
65
+ step: () => boolean;
66
+ get: (target: unknown[]) => unknown[];
67
+ getColumnNames?: (target?: string[]) => string[];
68
+ finalize: () => unknown;
69
+ };
70
+ export type QueryExecutorSqliteDatabase = {
71
+ prepare: (sql: string) => QueryExecutorSqliteStatement;
72
+ };
73
+ export declare function createSqliteQueryExecutor(config: CreateSqliteQueryExecutorConfig): SqliteQueryExecutor;
74
+ export declare function executeParameterizedSqliteQuery(db: QueryExecutorSqliteDatabase, request: ParameterizedSqliteQueryRequest): ParameterizedSqliteQueryResult;
75
+ export declare function executeParameterizedSqlitePagedQuery(db: QueryExecutorSqliteDatabase, request: ParameterizedSqlitePagedQueryRequest, now?: () => number): ParameterizedSqlitePagedQueryResult;
@@ -0,0 +1,48 @@
1
+ import { default as z } from 'zod';
2
+ export type SqliteQueryEngineConfigRecord = {
3
+ entities: unknown[];
4
+ entityDefinitions: Record<string, unknown>;
5
+ };
6
+ export declare const SqliteQueryEngineConfigRecordSchema: z.ZodType<SqliteQueryEngineConfigRecord>;
7
+ export type QueryEngineConfigSqliteOperationMap = {
8
+ replaceQueryEngineConfig: {
9
+ request: {
10
+ config: SqliteQueryEngineConfigRecord;
11
+ };
12
+ response: null;
13
+ };
14
+ getQueryEngineConfig: {
15
+ request: Record<string, never>;
16
+ response: SqliteQueryEngineConfigRecord | null;
17
+ };
18
+ };
19
+ export type QueryEngineConfigSqliteStatement = {
20
+ bind: (values: readonly unknown[]) => unknown;
21
+ step: () => boolean;
22
+ get: (target: unknown[]) => unknown[];
23
+ finalize: () => unknown;
24
+ };
25
+ export type QueryEngineConfigSqliteDatabase = {
26
+ prepare: (sql: string) => QueryEngineConfigSqliteStatement;
27
+ };
28
+ export type QueryEngineConfigSqliteRequest = Record<string, unknown>;
29
+ export type QueryEngineConfigSqliteRequestHandler = (db: QueryEngineConfigSqliteDatabase, request: QueryEngineConfigSqliteRequest) => unknown;
30
+ export type CreateQueryEngineConfigSqliteHandlersConfig = {
31
+ tableName: string;
32
+ singletonKey?: string;
33
+ requestTypes?: {
34
+ replace: string;
35
+ get: string;
36
+ };
37
+ parseJson?: <T>(value: string, fallback: T) => T;
38
+ };
39
+ export declare function replaceQueryEngineConfig(db: QueryEngineConfigSqliteDatabase, config: SqliteQueryEngineConfigRecord, options: {
40
+ tableName: string;
41
+ singletonKey?: string;
42
+ }): void;
43
+ export declare function getQueryEngineConfig(db: QueryEngineConfigSqliteDatabase, options: {
44
+ tableName: string;
45
+ singletonKey?: string;
46
+ parseJson?: <T>(value: string, fallback: T) => T;
47
+ }): SqliteQueryEngineConfigRecord | null;
48
+ export declare function createQueryEngineConfigSqliteRequestHandlers(config: CreateQueryEngineConfigSqliteHandlersConfig): Record<string, QueryEngineConfigSqliteRequestHandler>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vireocodedev/query",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Framework-free query schemas, transport ports, and parameterized SQLite execution for Vireo Starter.",
5
5
  "type": "module",
6
6
  "sideEffects": false,