@supalive/core 1.15.0 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index-B2nE1y9E.d.ts +2384 -0
- package/dist/index-B2nE1y9E.d.ts.map +1 -0
- package/dist/index-B4UxN1ZL.d.ts +2365 -0
- package/dist/index-B4UxN1ZL.d.ts.map +1 -0
- package/dist/index-B5hAqW3O.d.ts +2402 -0
- package/dist/index-B5hAqW3O.d.ts.map +1 -0
- package/dist/index-B8JFAyw4.d.ts +2377 -0
- package/dist/index-B8JFAyw4.d.ts.map +1 -0
- package/dist/index-BI3MfAyI.d.ts +2399 -0
- package/dist/index-BI3MfAyI.d.ts.map +1 -0
- package/dist/index-BL90lDw7.d.ts +2400 -0
- package/dist/index-BL90lDw7.d.ts.map +1 -0
- package/dist/index-BvryqYUm.d.ts +2394 -0
- package/dist/index-BvryqYUm.d.ts.map +1 -0
- package/dist/index-CfcrWLLm.d.ts +2377 -0
- package/dist/index-CfcrWLLm.d.ts.map +1 -0
- package/dist/index-DYkOBnw_.d.ts +2378 -0
- package/dist/index-DYkOBnw_.d.ts.map +1 -0
- package/dist/index-DcM2uKpT.d.ts +2393 -0
- package/dist/index-DcM2uKpT.d.ts.map +1 -0
- package/dist/index-DsvdhOKL.d.ts +2400 -0
- package/dist/index-DsvdhOKL.d.ts.map +1 -0
- package/dist/index-ggi1DrSG.d.ts +2336 -0
- package/dist/index-ggi1DrSG.d.ts.map +1 -0
- package/dist/mysql-07ROfbBZ.d.ts +114 -0
- package/dist/mysql-07ROfbBZ.d.ts.map +1 -0
- package/dist/mysql-BJaQhVGE.d.ts +114 -0
- package/dist/mysql-BJaQhVGE.d.ts.map +1 -0
- package/dist/mysql-BKs5PTSR.d.ts +114 -0
- package/dist/mysql-BKs5PTSR.d.ts.map +1 -0
- package/dist/mysql-BTigT6mP.d.ts +114 -0
- package/dist/mysql-BTigT6mP.d.ts.map +1 -0
- package/dist/mysql-C7Km_lgc.d.ts +114 -0
- package/dist/mysql-C7Km_lgc.d.ts.map +1 -0
- package/dist/mysql-CXymz1Eu.d.ts +114 -0
- package/dist/mysql-CXymz1Eu.d.ts.map +1 -0
- package/dist/mysql-ClUK7S2j.js +623 -0
- package/dist/mysql-ClUK7S2j.js.map +1 -0
- package/dist/mysql-CtpWqOhO.d.ts +114 -0
- package/dist/mysql-CtpWqOhO.d.ts.map +1 -0
- package/dist/mysql-Cxro4f6T.d.ts +114 -0
- package/dist/mysql-Cxro4f6T.d.ts.map +1 -0
- package/dist/mysql-DNiYIZCK.d.ts +114 -0
- package/dist/mysql-DNiYIZCK.d.ts.map +1 -0
- package/dist/mysql-DS1s1c5M.d.ts +114 -0
- package/dist/mysql-DS1s1c5M.d.ts.map +1 -0
- package/dist/mysql-fTKZ1zaF.js +623 -0
- package/dist/mysql-fTKZ1zaF.js.map +1 -0
- package/dist/mysql-qTszEKAU.d.ts +114 -0
- package/dist/mysql-qTszEKAU.d.ts.map +1 -0
- package/dist/mysql-qW13LcGQ.d.ts +114 -0
- package/dist/mysql-qW13LcGQ.d.ts.map +1 -0
- package/dist/overlap-checker-1TwhkAwy.js +259 -0
- package/dist/overlap-checker-1TwhkAwy.js.map +1 -0
- package/dist/overlap-checker-CGlxWcyb.js +259 -0
- package/dist/overlap-checker-CGlxWcyb.js.map +1 -0
- package/dist/postgres-B0fqJe1X.d.ts +118 -0
- package/dist/postgres-B0fqJe1X.d.ts.map +1 -0
- package/dist/postgres-B5-5vgLJ.d.ts +118 -0
- package/dist/postgres-B5-5vgLJ.d.ts.map +1 -0
- package/dist/postgres-BATDLPml.d.ts +118 -0
- package/dist/postgres-BATDLPml.d.ts.map +1 -0
- package/dist/postgres-BUVYjnaT.d.ts +118 -0
- package/dist/postgres-BUVYjnaT.d.ts.map +1 -0
- package/dist/postgres-Bk_8PFRT.d.ts +118 -0
- package/dist/postgres-Bk_8PFRT.d.ts.map +1 -0
- package/dist/postgres-Bx79_JEy.d.ts +118 -0
- package/dist/postgres-Bx79_JEy.d.ts.map +1 -0
- package/dist/postgres-C3w741VH.d.ts +118 -0
- package/dist/postgres-C3w741VH.d.ts.map +1 -0
- package/dist/postgres-CAZYYgbZ.d.ts +118 -0
- package/dist/postgres-CAZYYgbZ.d.ts.map +1 -0
- package/dist/postgres-CN03Espg.js +868 -0
- package/dist/postgres-CN03Espg.js.map +1 -0
- package/dist/postgres-CRPJdZx2.js +868 -0
- package/dist/postgres-CRPJdZx2.js.map +1 -0
- package/dist/postgres-DCXnKaJr.d.ts +118 -0
- package/dist/postgres-DCXnKaJr.d.ts.map +1 -0
- package/dist/postgres-DgCYPf0j.d.ts +118 -0
- package/dist/postgres-DgCYPf0j.d.ts.map +1 -0
- package/dist/postgres-Dnshux6k.d.ts +118 -0
- package/dist/postgres-Dnshux6k.d.ts.map +1 -0
- package/dist/postgres-bhVzeRf2.d.ts +118 -0
- package/dist/postgres-bhVzeRf2.d.ts.map +1 -0
- package/dist/query-LJranz0c.js +795 -0
- package/dist/query-LJranz0c.js.map +1 -0
- package/dist/query-q1ZvKrZO.js +762 -0
- package/dist/query-q1ZvKrZO.js.map +1 -0
- package/dist/realtime_db-B0Tin87E.js +312 -0
- package/dist/realtime_db-B0Tin87E.js.map +1 -0
- package/dist/realtime_db-DVhhasJj.js +312 -0
- package/dist/realtime_db-DVhhasJj.js.map +1 -0
- package/dist/router-Bk0eLfmB.js +285 -0
- package/dist/router-Bk0eLfmB.js.map +1 -0
- package/dist/router-DP2ThAwh.js.map +1 -1
- package/dist/src/client/index.d.ts +2 -2
- package/dist/src/client/index.js +95 -20
- package/dist/src/client/index.js.map +1 -1
- package/dist/src/exports/mysql.d.ts +1 -1
- package/dist/src/exports/mysql.js +1 -1
- package/dist/src/exports/postgres.d.ts +1 -1
- package/dist/src/exports/postgres.js +1 -1
- package/dist/src/exports/procedure.d.ts +2 -2
- package/dist/src/exports/procedure.js +1 -1
- package/dist/src/exports/schema-sql.d.ts +1 -1
- package/dist/src/exports/server.d.ts +13 -6
- package/dist/src/exports/server.d.ts.map +1 -1
- package/dist/src/exports/server.js +802 -252
- package/dist/src/exports/server.js.map +1 -1
- package/dist/src/exports/sub-manager-worker-entry.js +1 -1
- package/dist/src/exports/types.d.ts +2 -2
- package/dist/src/exports/types.js +1 -1
- package/dist/sub-worker-dispatch-D7jfw6ys.js +826 -0
- package/dist/sub-worker-dispatch-D7jfw6ys.js.map +1 -0
- package/dist/sub-worker-dispatch-qiSs5Xkm.js +826 -0
- package/dist/sub-worker-dispatch-qiSs5Xkm.js.map +1 -0
- package/dist/types_client_rpc-CWZmlWmM.js +145 -0
- package/dist/types_client_rpc-CWZmlWmM.js.map +1 -0
- package/dist/types_client_rpc-D4td5W0Y.js +141 -0
- package/dist/types_client_rpc-D4td5W0Y.js.map +1 -0
- package/dist/types_server-AXVa_2mF.d.ts +684 -0
- package/dist/types_server-AXVa_2mF.d.ts.map +1 -0
- package/dist/types_server-BhC19R2a.js.map +1 -1
- package/dist/types_server-BmIgDd8d.d.ts +614 -0
- package/dist/types_server-BmIgDd8d.d.ts.map +1 -0
- package/dist/types_server-C6B9_b1R.d.ts +662 -0
- package/dist/types_server-C6B9_b1R.d.ts.map +1 -0
- package/dist/types_server-CUPM08l2.d.ts +684 -0
- package/dist/types_server-CUPM08l2.d.ts.map +1 -0
- package/dist/types_server-CtYRmHdO.d.ts +684 -0
- package/dist/types_server-CtYRmHdO.d.ts.map +1 -0
- package/dist/types_server-DHRi8bGO.d.ts +684 -0
- package/dist/types_server-DHRi8bGO.d.ts.map +1 -0
- package/dist/types_server-DRr5yKy1.d.ts +655 -0
- package/dist/types_server-DRr5yKy1.d.ts.map +1 -0
- package/dist/types_server-DmCbve3w.d.ts +684 -0
- package/dist/types_server-DmCbve3w.d.ts.map +1 -0
- package/dist/types_server-DoeybfmY.d.ts +684 -0
- package/dist/types_server-DoeybfmY.d.ts.map +1 -0
- package/dist/types_server-Gs_vSVaS.d.ts +655 -0
- package/dist/types_server-Gs_vSVaS.d.ts.map +1 -0
- package/dist/types_server-ILUmPFvk.d.ts +614 -0
- package/dist/types_server-ILUmPFvk.d.ts.map +1 -0
- package/dist/types_server-JK14NE0j.d.ts +684 -0
- package/dist/types_server-JK14NE0j.d.ts.map +1 -0
- package/dist/types_server-hYv2--1M.d.ts +684 -0
- package/dist/types_server-hYv2--1M.d.ts.map +1 -0
- package/dist/types_server-kkQJDSjA.d.ts +684 -0
- package/dist/types_server-kkQJDSjA.d.ts.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,795 @@
|
|
|
1
|
+
import { i as normalizeCodec } from "./table-id-DyrJBKAQ.js";
|
|
2
|
+
import { i as bytesEqual } from "./helper-zdJT5FUc.js";
|
|
3
|
+
import { z as z$1 } from "zod";
|
|
4
|
+
import { SpanStatusCode, context, trace } from "@opentelemetry/api";
|
|
5
|
+
//#region src/db/sqlcommenter.ts
|
|
6
|
+
function comment(input) {
|
|
7
|
+
if (!input) return "";
|
|
8
|
+
if (typeof input === "string") return `/*${input}*/`;
|
|
9
|
+
const entries = Object.entries(input).filter(([_, v]) => v != null);
|
|
10
|
+
if (entries.length === 0) return "";
|
|
11
|
+
return `/*${entries.map(([k, v]) => `${encodeURIComponent(k)}='${encodeURIComponent(v)}'`).sort().join(",")}*/`;
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/db/db-trace.ts
|
|
15
|
+
const tracer = trace.getTracer("@supalive/core");
|
|
16
|
+
/**
|
|
17
|
+
* Run `fn` inside a `db.<op> <table>` span when a trace is being recorded;
|
|
18
|
+
* otherwise run it directly. `system` is the DB flavor (`mysql`/`postgres`).
|
|
19
|
+
* Attributes follow OTel DB semantic conventions and stay low-cardinality —
|
|
20
|
+
* table + operation only, never ids or parameter values.
|
|
21
|
+
*/
|
|
22
|
+
function traceDbCall(op, table, system, fn) {
|
|
23
|
+
const active = trace.getActiveSpan();
|
|
24
|
+
if (!active || !active.isRecording()) return fn();
|
|
25
|
+
const span = tracer.startSpan(table ? `db.${op} ${table}` : `db.${op}`, { attributes: {
|
|
26
|
+
"db.system": system,
|
|
27
|
+
"db.operation": op,
|
|
28
|
+
...table ? { "db.sql.table": table } : {}
|
|
29
|
+
} });
|
|
30
|
+
return context.with(trace.setSpan(context.active(), span), async () => {
|
|
31
|
+
try {
|
|
32
|
+
const res = await fn();
|
|
33
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
34
|
+
return res;
|
|
35
|
+
} catch (err) {
|
|
36
|
+
if (err instanceof Error) span.recordException(err);
|
|
37
|
+
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
38
|
+
throw err;
|
|
39
|
+
} finally {
|
|
40
|
+
span.end();
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Run a query-cache operation (`cache.<op>`) inside a span when a trace is being
|
|
46
|
+
* recorded; otherwise run it directly. Mirrors {@link traceDbCall} so a trace
|
|
47
|
+
* shows the cache work — the metadata fetch, freshness scan, and data read — the
|
|
48
|
+
* same way it shows DB reads. Without this a cache HIT is invisible (no DB span,
|
|
49
|
+
* no query span), so you can't see hit/miss or cache latency in a trace.
|
|
50
|
+
*
|
|
51
|
+
* Same zero-overhead guard: no active RECORDING span → just run, no allocation.
|
|
52
|
+
* `setHit` lets the caller stamp `cache.hit` on the span (a hit-rate signal)
|
|
53
|
+
* without paying for it when tracing is off.
|
|
54
|
+
*/
|
|
55
|
+
function traceCacheCall(op, fn) {
|
|
56
|
+
const active = trace.getActiveSpan();
|
|
57
|
+
if (!active || !active.isRecording()) return fn(() => {});
|
|
58
|
+
const span = tracer.startSpan(`cache.${op}`, { attributes: {
|
|
59
|
+
"cache.system": "redis",
|
|
60
|
+
"cache.operation": op
|
|
61
|
+
} });
|
|
62
|
+
const setHit = (hit) => span.setAttribute("cache.hit", hit);
|
|
63
|
+
return context.with(trace.setSpan(context.active(), span), async () => {
|
|
64
|
+
try {
|
|
65
|
+
const res = await fn(setHit);
|
|
66
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
67
|
+
return res;
|
|
68
|
+
} catch (err) {
|
|
69
|
+
if (err instanceof Error) span.recordException(err);
|
|
70
|
+
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
71
|
+
throw err;
|
|
72
|
+
} finally {
|
|
73
|
+
span.end();
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region src/db/query.ts
|
|
79
|
+
function jsonKeysToPath(keys) {
|
|
80
|
+
let out = "$";
|
|
81
|
+
for (const k of keys) out += `."${k.replace(/"/g, "\\\"")}"`;
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
function resolveJsonPath(opts) {
|
|
85
|
+
if (opts?.path !== void 0 && opts?.pathRaw !== void 0) throw new Error("jsonContains: pass either `path` or `pathRaw`, not both");
|
|
86
|
+
if (opts?.pathRaw !== void 0) return opts.pathRaw;
|
|
87
|
+
if (opts?.path !== void 0) return jsonKeysToPath(Array.isArray(opts.path) ? opts.path : [opts.path]);
|
|
88
|
+
}
|
|
89
|
+
var QueryBuilder = class {
|
|
90
|
+
ctx;
|
|
91
|
+
model;
|
|
92
|
+
constructor(ctx, model) {
|
|
93
|
+
this.ctx = ctx;
|
|
94
|
+
this.model = model;
|
|
95
|
+
}
|
|
96
|
+
getColumnName(key) {
|
|
97
|
+
return this.model.columns[key] ?? key;
|
|
98
|
+
}
|
|
99
|
+
select(...columns) {
|
|
100
|
+
const selectedProps = columns.length > 0 ? columns : Object.keys(this.model.columns);
|
|
101
|
+
return new SelectQueryBuilder(this.ctx, this.model, [], null, null, null, selectedProps);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
var SelectQueryBuilder = class SelectQueryBuilder {
|
|
105
|
+
ctx;
|
|
106
|
+
model;
|
|
107
|
+
filters = [];
|
|
108
|
+
paginationClause = null;
|
|
109
|
+
orderByClause = null;
|
|
110
|
+
limitVal = null;
|
|
111
|
+
offsetVal = null;
|
|
112
|
+
selectedProps = [];
|
|
113
|
+
computedFields = [];
|
|
114
|
+
commentStr = "";
|
|
115
|
+
constructor(ctx, model, filters = [], orderByClause = null, limitVal = null, offsetVal = null, selectedProps = [], computedFields = [], commentStr = "") {
|
|
116
|
+
this.ctx = ctx;
|
|
117
|
+
this.model = model;
|
|
118
|
+
this.filters = filters;
|
|
119
|
+
this.orderByClause = orderByClause;
|
|
120
|
+
this.limitVal = limitVal;
|
|
121
|
+
this.offsetVal = offsetVal;
|
|
122
|
+
this.selectedProps = selectedProps;
|
|
123
|
+
this.computedFields = computedFields;
|
|
124
|
+
this.commentStr = commentStr;
|
|
125
|
+
}
|
|
126
|
+
getColumnName(key) {
|
|
127
|
+
return this.model.columns[key] ?? key;
|
|
128
|
+
}
|
|
129
|
+
getPropertyName(column) {
|
|
130
|
+
return this.model.reverseColumns[column] ?? this.computedFields.find((f) => f.alias === column)?.field ?? column;
|
|
131
|
+
}
|
|
132
|
+
mapRowToSchema(row) {
|
|
133
|
+
const result = {};
|
|
134
|
+
for (const [col, value] of Object.entries(row)) {
|
|
135
|
+
const prop = this.getPropertyName(col);
|
|
136
|
+
const codec = this.model.codecs[prop] ?? this.computedFields.find((f) => f.alias === col)?.codec;
|
|
137
|
+
result[prop] = codec?.decode && value !== void 0 && value !== null ? codec.decode(value) : value;
|
|
138
|
+
}
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
where(filterFn) {
|
|
142
|
+
this.filters.push(filterFn);
|
|
143
|
+
return this;
|
|
144
|
+
}
|
|
145
|
+
paginate(clause) {
|
|
146
|
+
this.paginationClause = clause;
|
|
147
|
+
return this;
|
|
148
|
+
}
|
|
149
|
+
orderBy(column, directionOrOptions = "ASC") {
|
|
150
|
+
const sql = this.buildOrderBySql(column, directionOrOptions);
|
|
151
|
+
if (!this.orderByClause) this.orderByClause = [];
|
|
152
|
+
this.orderByClause.push(sql);
|
|
153
|
+
return this;
|
|
154
|
+
}
|
|
155
|
+
buildOrderBySql(column, directionOrOptions = "ASC") {
|
|
156
|
+
const options = typeof directionOrOptions === "string" ? { direction: directionOrOptions } : directionOrOptions;
|
|
157
|
+
let sql = this.getColumnName(column);
|
|
158
|
+
if (options?.collate) sql += ` COLLATE ${options.collate}`;
|
|
159
|
+
sql += ` ${(options?.direction ?? "ASC").toUpperCase()}`;
|
|
160
|
+
if (this.ctx.db.dbType === "postgres") {
|
|
161
|
+
if (options?.nulls) sql += ` NULLS ${options.nulls.toUpperCase()}`;
|
|
162
|
+
}
|
|
163
|
+
return sql;
|
|
164
|
+
}
|
|
165
|
+
orderByRaw(sql) {
|
|
166
|
+
if (!this.orderByClause) this.orderByClause = [];
|
|
167
|
+
this.orderByClause.push(sql);
|
|
168
|
+
return this;
|
|
169
|
+
}
|
|
170
|
+
limit(n) {
|
|
171
|
+
this.limitVal = n;
|
|
172
|
+
return this;
|
|
173
|
+
}
|
|
174
|
+
offset(n) {
|
|
175
|
+
this.offsetVal = n;
|
|
176
|
+
return this;
|
|
177
|
+
}
|
|
178
|
+
computed(config) {
|
|
179
|
+
const computed = {
|
|
180
|
+
field: config.field,
|
|
181
|
+
sql: config.sql,
|
|
182
|
+
alias: config.alias ?? config.field,
|
|
183
|
+
codec: normalizeCodec(config.codec, config.type)
|
|
184
|
+
};
|
|
185
|
+
this.computedFields.push(computed);
|
|
186
|
+
return new SelectQueryBuilder(this.ctx, this.model, this.filters, this.orderByClause, this.limitVal, this.offsetVal, [...this.selectedProps, config.field], this.computedFields, this.commentStr);
|
|
187
|
+
}
|
|
188
|
+
comment(input) {
|
|
189
|
+
this.commentStr = comment(input);
|
|
190
|
+
return this;
|
|
191
|
+
}
|
|
192
|
+
async first() {
|
|
193
|
+
return (await this.limit(1).get())[0] ?? null;
|
|
194
|
+
}
|
|
195
|
+
async exists() {
|
|
196
|
+
return await this.count() > 0;
|
|
197
|
+
}
|
|
198
|
+
static defaultCountType = z$1.coerce.number();
|
|
199
|
+
async count(parser) {
|
|
200
|
+
const predicate = this.buildPredicate(false) ?? {
|
|
201
|
+
kind: "leaf",
|
|
202
|
+
column: "1",
|
|
203
|
+
operator: "=",
|
|
204
|
+
value: "1"
|
|
205
|
+
};
|
|
206
|
+
const table = this.model.table;
|
|
207
|
+
const querySpec = {
|
|
208
|
+
kind: "range",
|
|
209
|
+
table,
|
|
210
|
+
predicate,
|
|
211
|
+
columns: ["count"],
|
|
212
|
+
orderBy: null,
|
|
213
|
+
limit: null,
|
|
214
|
+
offset: null
|
|
215
|
+
};
|
|
216
|
+
const params = [];
|
|
217
|
+
const sql = `SELECT COUNT(*) as count FROM ${table} WHERE (${buildPredicateSql(predicate, params, this.ctx.db.sqlBuilder)})${this.commentStr ? " " + this.commentStr : ""}`;
|
|
218
|
+
const raw = (await traceDbCall("count", table, this.ctx.db.dbType, () => this.ctx.db.query(sql, params))).rows[0]?.count ?? "0";
|
|
219
|
+
this.ctx.internalGetReadSet().push({ ...querySpec });
|
|
220
|
+
if (parser) return parser.parse(raw);
|
|
221
|
+
else return SelectQueryBuilder.defaultCountType.parse(raw);
|
|
222
|
+
}
|
|
223
|
+
async find(id) {
|
|
224
|
+
if (this.filters.length > 0) throw new Error(`Cannot use find() after where() - find() is meant for primary key lookups without additional filters`);
|
|
225
|
+
const table = this.model.table;
|
|
226
|
+
const tableId = this.model.tableId;
|
|
227
|
+
const pendingWrite = this.ctx.internalGetWriteSet().find((w) => w.id === id && bytesEqual(w.tableId, tableId));
|
|
228
|
+
if (pendingWrite) {
|
|
229
|
+
if (pendingWrite.op === "delete") return null;
|
|
230
|
+
return this.mapRowToSchema({
|
|
231
|
+
...pendingWrite.data,
|
|
232
|
+
id,
|
|
233
|
+
commit_ts: this.ctx.beginTs
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
const computedCols = this.computedFields.map((f) => `${f.sql} as ${f.alias}`);
|
|
237
|
+
const selectPropsWithComputed = this.selectedProps.slice(0, this.selectedProps.length - this.computedFields.length);
|
|
238
|
+
let sql = `SELECT ${selectPropsWithComputed.length > 0 ? [
|
|
239
|
+
"id",
|
|
240
|
+
...selectPropsWithComputed.map((p) => this.getColumnName(p)),
|
|
241
|
+
...computedCols,
|
|
242
|
+
"commit_ts"
|
|
243
|
+
].join(", ") : "*"} FROM ${table} WHERE id = ${this.ctx.db.sqlBuilder.placeholder(1)}`;
|
|
244
|
+
if (this.commentStr) sql += ` ${this.commentStr}`;
|
|
245
|
+
const row = (await traceDbCall("find", table, this.ctx.db.dbType, () => this.ctx.db.query(sql, [id]))).rows[0] ?? null;
|
|
246
|
+
const expectedCommitTs = row ? BigInt(row.commit_ts) : null;
|
|
247
|
+
this.ctx.internalGetReadSet().push({
|
|
248
|
+
kind: "point",
|
|
249
|
+
table,
|
|
250
|
+
id,
|
|
251
|
+
expectedCommitTs
|
|
252
|
+
});
|
|
253
|
+
return row ? this.mapRowToSchema(row) : null;
|
|
254
|
+
}
|
|
255
|
+
async get() {
|
|
256
|
+
const predicate = this.buildPredicate(true) ?? {
|
|
257
|
+
kind: "leaf",
|
|
258
|
+
column: "1",
|
|
259
|
+
operator: "=",
|
|
260
|
+
value: "1"
|
|
261
|
+
};
|
|
262
|
+
const table = this.model.table;
|
|
263
|
+
const querySpec = {
|
|
264
|
+
kind: "range",
|
|
265
|
+
table,
|
|
266
|
+
predicate,
|
|
267
|
+
columns: this.selectedProps,
|
|
268
|
+
orderBy: this.orderByClause,
|
|
269
|
+
limit: this.limitVal,
|
|
270
|
+
offset: this.offsetVal
|
|
271
|
+
};
|
|
272
|
+
const params = [];
|
|
273
|
+
const whereSql = buildPredicateSql(predicate, params, this.ctx.db.sqlBuilder);
|
|
274
|
+
const computedCols = this.computedFields.map((f) => `${f.sql} as ${f.alias}`);
|
|
275
|
+
const selectPropsWithComputed = this.selectedProps.slice(0, this.selectedProps.length - this.computedFields.length);
|
|
276
|
+
const selectColsNames = selectPropsWithComputed.length > 0 ? [
|
|
277
|
+
"id",
|
|
278
|
+
...selectPropsWithComputed.map((p) => this.getColumnName(p)),
|
|
279
|
+
...computedCols,
|
|
280
|
+
"commit_ts"
|
|
281
|
+
] : "*";
|
|
282
|
+
let sql = `SELECT ${Array.from(new Set(selectColsNames).values()).join(", ")} FROM ${table} WHERE (${whereSql})`;
|
|
283
|
+
let orderByClause;
|
|
284
|
+
const paginateClause = this.paginationClause;
|
|
285
|
+
if (paginateClause) {
|
|
286
|
+
const idField = paginateClause.idCol ?? "id";
|
|
287
|
+
const orderDir = paginateClause.direction ?? "DESC";
|
|
288
|
+
orderByClause = [this.buildOrderBySql(paginateClause.timeCol, orderDir), this.buildOrderBySql(idField, orderDir)];
|
|
289
|
+
if (this.orderByClause) orderByClause.push(...this.orderByClause);
|
|
290
|
+
} else if (this.orderByClause) orderByClause = this.orderByClause;
|
|
291
|
+
if (orderByClause) sql += ` ORDER BY ${orderByClause.join(", ")}`;
|
|
292
|
+
const limit = paginateClause?.limit ?? this.limitVal;
|
|
293
|
+
if (limit !== null) sql += ` LIMIT ${limit}`;
|
|
294
|
+
if (this.offsetVal !== null && !this.paginationClause) sql += ` OFFSET ${this.offsetVal}`;
|
|
295
|
+
if (this.commentStr) sql += ` ${this.commentStr}`;
|
|
296
|
+
const result = await traceDbCall("select", table, this.ctx.db.dbType, () => this.ctx.db.query(sql, params));
|
|
297
|
+
this.ctx.internalGetReadSet().push({ ...querySpec });
|
|
298
|
+
return result.rows.map((row) => this.mapRowToSchema(row));
|
|
299
|
+
}
|
|
300
|
+
buildPredicate(includePagination) {
|
|
301
|
+
if (this.filters.length === 0) return null;
|
|
302
|
+
const predicates = [];
|
|
303
|
+
const fb = new FilterBuilder(this.model.columns, this.model.codecs);
|
|
304
|
+
let paginateFilter;
|
|
305
|
+
const clause = this.paginationClause;
|
|
306
|
+
const cursor = clause?.cursor;
|
|
307
|
+
if (clause && cursor != null) {
|
|
308
|
+
const descending = (clause.direction ?? "DESC") === "DESC";
|
|
309
|
+
const idField = clause.idCol ?? "id";
|
|
310
|
+
paginateFilter = (f) => {
|
|
311
|
+
f.or([(f1) => {
|
|
312
|
+
return descending ? f1.lt(clause.timeCol, cursor.time) : f1.gt(clause.timeCol, cursor.time);
|
|
313
|
+
}, (f2) => {
|
|
314
|
+
const q = f2.eq(clause.timeCol, cursor.time);
|
|
315
|
+
return descending ? q.lt(idField, cursor.id) : q.gt(idField, cursor.id);
|
|
316
|
+
}]);
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
const count = this.filters.length + (paginateFilter ? 1 : 0);
|
|
320
|
+
for (let i = 0; i < count; i++) {
|
|
321
|
+
(i == this.filters.length ? paginateFilter : this.filters[i])(fb);
|
|
322
|
+
if (fb.predicates.length > 0) {
|
|
323
|
+
if (fb.predicates.some((p) => p.kind === "or")) predicates.push(...fb.predicates);
|
|
324
|
+
else predicates.push({
|
|
325
|
+
kind: "and",
|
|
326
|
+
children: fb.predicates
|
|
327
|
+
});
|
|
328
|
+
fb.predicates = [];
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
if (predicates.length === 0) return null;
|
|
332
|
+
if (predicates.length === 1) return predicates[0] ?? null;
|
|
333
|
+
return {
|
|
334
|
+
kind: "and",
|
|
335
|
+
children: predicates
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
var FilterBuilder = class FilterBuilder {
|
|
340
|
+
columns;
|
|
341
|
+
codecs;
|
|
342
|
+
predicates = [];
|
|
343
|
+
constructor(columns, codecs) {
|
|
344
|
+
this.columns = columns;
|
|
345
|
+
this.codecs = codecs;
|
|
346
|
+
}
|
|
347
|
+
add(column, operator, value, value2, raw) {
|
|
348
|
+
const leaf = {
|
|
349
|
+
kind: "leaf",
|
|
350
|
+
column,
|
|
351
|
+
operator,
|
|
352
|
+
value,
|
|
353
|
+
value2
|
|
354
|
+
};
|
|
355
|
+
if (raw) leaf.raw = true;
|
|
356
|
+
this.predicates.push(leaf);
|
|
357
|
+
}
|
|
358
|
+
addJsonContains(operator, key, value, opts) {
|
|
359
|
+
const leaf = {
|
|
360
|
+
kind: "leaf",
|
|
361
|
+
column: this.getColumnName(key),
|
|
362
|
+
operator,
|
|
363
|
+
value: this.encodeJson(value, opts?.raw)
|
|
364
|
+
};
|
|
365
|
+
if (opts?.raw) leaf.raw = true;
|
|
366
|
+
const path = resolveJsonPath(opts);
|
|
367
|
+
if (path !== void 0) leaf.path = path;
|
|
368
|
+
this.predicates.push(leaf);
|
|
369
|
+
}
|
|
370
|
+
getColumnName(key) {
|
|
371
|
+
return this.columns[key] ?? key;
|
|
372
|
+
}
|
|
373
|
+
encode(key, value) {
|
|
374
|
+
const codec = this.codecs[key];
|
|
375
|
+
if (!codec?.encode || value === void 0 || value === null) return value;
|
|
376
|
+
return codec.encode(value);
|
|
377
|
+
}
|
|
378
|
+
encodeJson(value, raw) {
|
|
379
|
+
if (value === null || value === void 0) return value;
|
|
380
|
+
if (raw) return value;
|
|
381
|
+
return JSON.stringify(value);
|
|
382
|
+
}
|
|
383
|
+
eq(key, value) {
|
|
384
|
+
this.add(this.getColumnName(key), "=", this.encode(key, value));
|
|
385
|
+
return this;
|
|
386
|
+
}
|
|
387
|
+
neq(key, value) {
|
|
388
|
+
this.add(this.getColumnName(key), "!=", this.encode(key, value));
|
|
389
|
+
return this;
|
|
390
|
+
}
|
|
391
|
+
gt(key, value) {
|
|
392
|
+
this.add(this.getColumnName(key), ">", this.encode(key, value));
|
|
393
|
+
return this;
|
|
394
|
+
}
|
|
395
|
+
gte(key, value) {
|
|
396
|
+
this.add(this.getColumnName(key), ">=", this.encode(key, value));
|
|
397
|
+
return this;
|
|
398
|
+
}
|
|
399
|
+
lt(key, value) {
|
|
400
|
+
this.add(this.getColumnName(key), "<", this.encode(key, value));
|
|
401
|
+
return this;
|
|
402
|
+
}
|
|
403
|
+
lte(key, value) {
|
|
404
|
+
this.add(this.getColumnName(key), "<=", this.encode(key, value));
|
|
405
|
+
return this;
|
|
406
|
+
}
|
|
407
|
+
like(key, pattern) {
|
|
408
|
+
this.add(this.getColumnName(key), "like", this.encode(key, pattern));
|
|
409
|
+
return this;
|
|
410
|
+
}
|
|
411
|
+
ilike(key, pattern) {
|
|
412
|
+
this.add(this.getColumnName(key), "ilike", this.encode(key, pattern));
|
|
413
|
+
return this;
|
|
414
|
+
}
|
|
415
|
+
contains(key, value) {
|
|
416
|
+
this.add(this.getColumnName(key), "contains", this.encode(key, value));
|
|
417
|
+
return this;
|
|
418
|
+
}
|
|
419
|
+
icontains(key, value) {
|
|
420
|
+
this.add(this.getColumnName(key), "icontains", this.encode(key, value));
|
|
421
|
+
return this;
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* JSON containment. Targets a JSON/JSONB column and asserts the document
|
|
425
|
+
* contains the given candidate (object-key subset for objects,
|
|
426
|
+
* element-subset for arrays — see dialect-specific docs for edge cases).
|
|
427
|
+
* Pass `{ raw: true }` if `value` is already a JSON-encoded string.
|
|
428
|
+
* Pass `{ path: ... }` to scope the check to a sub-document of the
|
|
429
|
+
* target (jsonb_path_query_first on PG, JSON_CONTAINS path arg on MySQL).
|
|
430
|
+
*/
|
|
431
|
+
jsonContains(key, value, opts) {
|
|
432
|
+
this.addJsonContains("jsonContains", key, value, opts);
|
|
433
|
+
return this;
|
|
434
|
+
}
|
|
435
|
+
/** Inverse of {@link jsonContains}: column is contained in the given value. */
|
|
436
|
+
jsonContainedBy(key, value, opts) {
|
|
437
|
+
this.addJsonContains("jsonContainedBy", key, value, opts);
|
|
438
|
+
return this;
|
|
439
|
+
}
|
|
440
|
+
jsonHasKey(key, value, opts) {
|
|
441
|
+
const col = this.getColumnName(key);
|
|
442
|
+
if (opts?.mode && Array.isArray(value)) {
|
|
443
|
+
const leaf = {
|
|
444
|
+
kind: "leaf",
|
|
445
|
+
column: col,
|
|
446
|
+
operator: "jsonHasKey",
|
|
447
|
+
value: value.map((p) => opts.raw && typeof p === "string" ? p : jsonKeysToPath(Array.isArray(p) ? p : [p])),
|
|
448
|
+
mode: opts.mode
|
|
449
|
+
};
|
|
450
|
+
this.predicates.push(leaf);
|
|
451
|
+
return this;
|
|
452
|
+
}
|
|
453
|
+
if (Array.isArray(value)) this.add(col, "jsonHasKey", jsonKeysToPath(value), void 0, true);
|
|
454
|
+
else if (opts?.raw) this.add(col, "jsonHasKey", value, void 0, true);
|
|
455
|
+
else this.add(col, "jsonHasKey", value, void 0, false);
|
|
456
|
+
return this;
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Array element containment: target column is a JSON array; the given
|
|
460
|
+
* `value` (any JSON-encodable type) must appear as one of its elements.
|
|
461
|
+
* Pass `{ raw: true }` if `value` is already a JSON-encoded string.
|
|
462
|
+
* Pass `{ path: ... }` to scope the check to a sub-array of the column.
|
|
463
|
+
*/
|
|
464
|
+
jsonArrayContains(key, value, opts) {
|
|
465
|
+
this.addJsonContains("jsonArrayContains", key, value, opts);
|
|
466
|
+
return this;
|
|
467
|
+
}
|
|
468
|
+
between(key, lo, hi) {
|
|
469
|
+
this.add(this.getColumnName(key), "between", this.encode(key, lo), this.encode(key, hi));
|
|
470
|
+
return this;
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Set membership: `key` equals any of `values` (SQL `IN`). Each value is
|
|
474
|
+
* codec-encoded like `eq`. An empty `values` matches no rows. Bound as one
|
|
475
|
+
* parameter per element, so keep the list within the driver's parameter
|
|
476
|
+
* limit (chunk very large sets at the call site).
|
|
477
|
+
*/
|
|
478
|
+
in(key, values) {
|
|
479
|
+
const col = this.getColumnName(key);
|
|
480
|
+
this.add(col, "in", values.map((v) => this.encode(key, v)));
|
|
481
|
+
return this;
|
|
482
|
+
}
|
|
483
|
+
isNull(key) {
|
|
484
|
+
this.add(this.getColumnName(key), "isNull", null);
|
|
485
|
+
return this;
|
|
486
|
+
}
|
|
487
|
+
notNull(key) {
|
|
488
|
+
this.add(this.getColumnName(key), "isNotNull", null);
|
|
489
|
+
return this;
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Compose an OR clause. Each callback fills a fresh sub-FilterBuilder
|
|
493
|
+
* with predicates that are AND-ed together within that branch; the
|
|
494
|
+
* branches themselves are OR-ed. Example:
|
|
495
|
+
* f.or([
|
|
496
|
+
* b => b.eq("tag", "a"),
|
|
497
|
+
* b => b.eq("tag", "b").gt("qty", 3), // (tag=b AND qty>3)
|
|
498
|
+
* ])
|
|
499
|
+
* produces (tag=a) OR (tag=b AND qty>3).
|
|
500
|
+
*/
|
|
501
|
+
or(branches) {
|
|
502
|
+
const children = [];
|
|
503
|
+
for (const fn of branches) {
|
|
504
|
+
const sub = new FilterBuilder(this.columns, this.codecs);
|
|
505
|
+
fn(sub);
|
|
506
|
+
if (sub.predicates.length === 1) children.push(sub.predicates[0]);
|
|
507
|
+
else if (sub.predicates.length > 1) children.push({
|
|
508
|
+
kind: "and",
|
|
509
|
+
children: sub.predicates
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
if (children.length > 0) this.predicates.push({
|
|
513
|
+
kind: "or",
|
|
514
|
+
children
|
|
515
|
+
});
|
|
516
|
+
return this;
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
function buildPredicateSql(predicate, params, sql) {
|
|
520
|
+
switch (predicate.kind) {
|
|
521
|
+
case "and": return `(${predicate.children.map((c) => buildPredicateSql(c, params, sql)).join(" AND ")})`;
|
|
522
|
+
case "or": return `(${predicate.children.map((c) => buildPredicateSql(c, params, sql)).join(" OR ")})`;
|
|
523
|
+
case "leaf": return buildLeafSql(predicate, params, sql);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
function buildLeafSql(leaf, params, sql) {
|
|
527
|
+
const col = leaf.column;
|
|
528
|
+
switch (leaf.operator) {
|
|
529
|
+
case "=":
|
|
530
|
+
case "!=":
|
|
531
|
+
case ">":
|
|
532
|
+
case ">=":
|
|
533
|
+
case "<":
|
|
534
|
+
case "<=":
|
|
535
|
+
params.push(leaf.value);
|
|
536
|
+
return `${col} ${leaf.operator} ${sql.placeholder(params.length)}`;
|
|
537
|
+
case "between": {
|
|
538
|
+
params.push(leaf.value);
|
|
539
|
+
const p1 = sql.placeholder(params.length);
|
|
540
|
+
params.push(leaf.value2);
|
|
541
|
+
return `${col} BETWEEN ${p1} AND ${sql.placeholder(params.length)}`;
|
|
542
|
+
}
|
|
543
|
+
case "in": {
|
|
544
|
+
const values = leaf.value ?? [];
|
|
545
|
+
if (values.length === 0) return "1 = 0";
|
|
546
|
+
return `${col} IN (${values.map((v) => {
|
|
547
|
+
params.push(v);
|
|
548
|
+
return sql.placeholder(params.length);
|
|
549
|
+
}).join(", ")})`;
|
|
550
|
+
}
|
|
551
|
+
case "like":
|
|
552
|
+
params.push(leaf.value);
|
|
553
|
+
return `${col} LIKE ${sql.placeholder(params.length)}`;
|
|
554
|
+
case "ilike":
|
|
555
|
+
params.push(leaf.value);
|
|
556
|
+
return sql.ilike(col, params.length);
|
|
557
|
+
case "contains":
|
|
558
|
+
params.push(`%${leaf.value}%`);
|
|
559
|
+
return `${col} LIKE ${sql.placeholder(params.length)}`;
|
|
560
|
+
case "icontains":
|
|
561
|
+
params.push(`%${leaf.value}%`);
|
|
562
|
+
return sql.icontains(col, params.length);
|
|
563
|
+
case "jsonContains": {
|
|
564
|
+
params.push(leaf.value);
|
|
565
|
+
const valIdx = params.length;
|
|
566
|
+
let pathIndex;
|
|
567
|
+
if (leaf.path !== void 0) {
|
|
568
|
+
params.push(leaf.path);
|
|
569
|
+
pathIndex = params.length;
|
|
570
|
+
}
|
|
571
|
+
return sql.jsonContains(col, valIdx, {
|
|
572
|
+
raw: leaf.raw,
|
|
573
|
+
pathIndex
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
case "jsonContainedBy": {
|
|
577
|
+
params.push(leaf.value);
|
|
578
|
+
const valIdx = params.length;
|
|
579
|
+
let pathIndex;
|
|
580
|
+
if (leaf.path !== void 0) {
|
|
581
|
+
params.push(leaf.path);
|
|
582
|
+
pathIndex = params.length;
|
|
583
|
+
}
|
|
584
|
+
return sql.jsonContainedBy(col, valIdx, {
|
|
585
|
+
raw: leaf.raw,
|
|
586
|
+
pathIndex
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
case "jsonHasKey":
|
|
590
|
+
if (leaf.mode && Array.isArray(leaf.value)) {
|
|
591
|
+
const paths = leaf.value;
|
|
592
|
+
const startIdx = params.length + 1;
|
|
593
|
+
for (const p of paths) params.push(p);
|
|
594
|
+
return sql.jsonHasKey(col, startIdx, {
|
|
595
|
+
raw: true,
|
|
596
|
+
mode: leaf.mode,
|
|
597
|
+
pathCount: paths.length
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
params.push(leaf.value);
|
|
601
|
+
return sql.jsonHasKey(col, params.length, { raw: leaf.raw });
|
|
602
|
+
case "jsonArrayContains": {
|
|
603
|
+
params.push(leaf.value);
|
|
604
|
+
const valIdx = params.length;
|
|
605
|
+
let pathIndex;
|
|
606
|
+
if (leaf.path !== void 0) {
|
|
607
|
+
params.push(leaf.path);
|
|
608
|
+
pathIndex = params.length;
|
|
609
|
+
}
|
|
610
|
+
return sql.jsonArrayContains(col, valIdx, {
|
|
611
|
+
raw: leaf.raw,
|
|
612
|
+
pathIndex
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
case "isNull": return `${col} IS NULL`;
|
|
616
|
+
case "isNotNull": return `${col} IS NOT NULL`;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
function matchesPredicate(row, predicate) {
|
|
620
|
+
switch (predicate.kind) {
|
|
621
|
+
case "and": return predicate.children.every((c) => matchesPredicate(row, c));
|
|
622
|
+
case "or": return predicate.children.some((c) => matchesPredicate(row, c));
|
|
623
|
+
case "leaf": return matchesLeaf(row, predicate);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
function matchesLeaf(row, leaf) {
|
|
627
|
+
const val = row[leaf.column];
|
|
628
|
+
switch (leaf.operator) {
|
|
629
|
+
case "=": return val === leaf.value;
|
|
630
|
+
case "!=": return val !== leaf.value;
|
|
631
|
+
case ">": return val > leaf.value;
|
|
632
|
+
case ">=": return val >= leaf.value;
|
|
633
|
+
case "<": return val < leaf.value;
|
|
634
|
+
case "<=": return val <= leaf.value;
|
|
635
|
+
case "between": return val >= leaf.value && val <= leaf.value2;
|
|
636
|
+
case "in": return Array.isArray(leaf.value) && leaf.value.includes(val);
|
|
637
|
+
case "like":
|
|
638
|
+
case "contains":
|
|
639
|
+
case "ilike":
|
|
640
|
+
case "icontains": {
|
|
641
|
+
if (typeof val !== "string" || typeof leaf.value !== "string") return false;
|
|
642
|
+
const haystack = leaf.operator === "ilike" || leaf.operator === "icontains" ? val.toLowerCase() : val;
|
|
643
|
+
const raw = leaf.value;
|
|
644
|
+
const needle = leaf.operator === "ilike" || leaf.operator === "icontains" ? raw.toLowerCase() : raw;
|
|
645
|
+
const pattern = leaf.operator === "contains" || leaf.operator === "icontains" ? `%${needle}%` : needle;
|
|
646
|
+
return new RegExp("^" + pattern.replace(/%/g, ".*").replace(/_/g, ".") + "$").test(haystack);
|
|
647
|
+
}
|
|
648
|
+
case "jsonContains": {
|
|
649
|
+
const candidate = decodeJsonLeafValue(leaf);
|
|
650
|
+
const target = leaf.path !== void 0 ? jsonPathExtract(val, leaf.path) : val;
|
|
651
|
+
if (target === void 0) return false;
|
|
652
|
+
return jsonContains(target, candidate);
|
|
653
|
+
}
|
|
654
|
+
case "jsonContainedBy": {
|
|
655
|
+
const candidate = decodeJsonLeafValue(leaf);
|
|
656
|
+
const target = leaf.path !== void 0 ? jsonPathExtract(val, leaf.path) : val;
|
|
657
|
+
if (target === void 0) return false;
|
|
658
|
+
return jsonContains(candidate, target);
|
|
659
|
+
}
|
|
660
|
+
case "jsonHasKey": {
|
|
661
|
+
if (leaf.mode && Array.isArray(leaf.value)) {
|
|
662
|
+
const paths = leaf.value;
|
|
663
|
+
if (paths.length === 0) return false;
|
|
664
|
+
return leaf.mode === "all" ? paths.every((p) => jsonPathExists(val, p)) : paths.some((p) => jsonPathExists(val, p));
|
|
665
|
+
}
|
|
666
|
+
if (leaf.raw) return jsonPathExists(val, leaf.value);
|
|
667
|
+
const key = leaf.value;
|
|
668
|
+
if (val === null || typeof val !== "object") return false;
|
|
669
|
+
if (Array.isArray(val)) return val.includes(key);
|
|
670
|
+
return key in val;
|
|
671
|
+
}
|
|
672
|
+
case "jsonArrayContains": {
|
|
673
|
+
const candidate = decodeJsonLeafValue(leaf);
|
|
674
|
+
const target = leaf.path !== void 0 ? jsonPathExtract(val, leaf.path) : val;
|
|
675
|
+
if (!Array.isArray(target)) return false;
|
|
676
|
+
return target.some((el) => jsonContains(el, candidate));
|
|
677
|
+
}
|
|
678
|
+
case "isNull": return val === null;
|
|
679
|
+
case "isNotNull": return val !== null;
|
|
680
|
+
default: return false;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
function decodeJsonLeafValue(leaf) {
|
|
684
|
+
if (!leaf.raw) return leaf.value;
|
|
685
|
+
if (typeof leaf.value !== "string") return leaf.value;
|
|
686
|
+
try {
|
|
687
|
+
return JSON.parse(leaf.value);
|
|
688
|
+
} catch {
|
|
689
|
+
return leaf.value;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
function jsonContains(target, candidate) {
|
|
693
|
+
if (candidate === target) return true;
|
|
694
|
+
if (target === null || candidate === null) return target === candidate;
|
|
695
|
+
if (target === void 0 || candidate === void 0) return false;
|
|
696
|
+
const tArr = Array.isArray(target);
|
|
697
|
+
const cArr = Array.isArray(candidate);
|
|
698
|
+
const tObj = !tArr && typeof target === "object";
|
|
699
|
+
const cObj = !cArr && typeof candidate === "object";
|
|
700
|
+
if (!cArr && !cObj && tArr) return target.some((el) => jsonContains(el, candidate));
|
|
701
|
+
if (cArr && tArr) {
|
|
702
|
+
const t = target;
|
|
703
|
+
return candidate.every((c) => t.some((el) => jsonContains(el, c)));
|
|
704
|
+
}
|
|
705
|
+
if (cObj && tObj) {
|
|
706
|
+
const t = target;
|
|
707
|
+
const c = candidate;
|
|
708
|
+
for (const k of Object.keys(c)) {
|
|
709
|
+
if (!(k in t)) return false;
|
|
710
|
+
if (!jsonContains(t[k], c[k])) return false;
|
|
711
|
+
}
|
|
712
|
+
return true;
|
|
713
|
+
}
|
|
714
|
+
return false;
|
|
715
|
+
}
|
|
716
|
+
function jsonPathExtract(val, path) {
|
|
717
|
+
let p = path;
|
|
718
|
+
if (p.startsWith("$")) p = p.slice(1);
|
|
719
|
+
let cur = val;
|
|
720
|
+
while (p.length > 0) if (p.startsWith(".\"")) {
|
|
721
|
+
let end = 2;
|
|
722
|
+
while (end < p.length) {
|
|
723
|
+
if (p[end] === "\\" && end + 1 < p.length) {
|
|
724
|
+
end += 2;
|
|
725
|
+
continue;
|
|
726
|
+
}
|
|
727
|
+
if (p[end] === "\"") break;
|
|
728
|
+
end++;
|
|
729
|
+
}
|
|
730
|
+
if (end >= p.length) return void 0;
|
|
731
|
+
const key = p.slice(2, end).replace(/\\"/g, "\"").replace(/\\\\/g, "\\");
|
|
732
|
+
if (cur === null || typeof cur !== "object" || Array.isArray(cur)) return void 0;
|
|
733
|
+
if (!(key in cur)) return void 0;
|
|
734
|
+
cur = cur[key];
|
|
735
|
+
p = p.slice(end + 1);
|
|
736
|
+
} else if (p.startsWith(".")) {
|
|
737
|
+
const m = p.slice(1).match(/^([^.[]+)/);
|
|
738
|
+
if (!m) return void 0;
|
|
739
|
+
const key = m[1];
|
|
740
|
+
if (cur === null || typeof cur !== "object" || Array.isArray(cur)) return void 0;
|
|
741
|
+
if (!(key in cur)) return void 0;
|
|
742
|
+
cur = cur[key];
|
|
743
|
+
p = p.slice(1 + key.length);
|
|
744
|
+
} else if (p.startsWith("[")) {
|
|
745
|
+
const end = p.indexOf("]");
|
|
746
|
+
if (end === -1) return void 0;
|
|
747
|
+
const idx = parseInt(p.slice(1, end), 10);
|
|
748
|
+
if (!Array.isArray(cur) || idx < 0 || idx >= cur.length) return void 0;
|
|
749
|
+
cur = cur[idx];
|
|
750
|
+
p = p.slice(end + 1);
|
|
751
|
+
} else return;
|
|
752
|
+
return cur;
|
|
753
|
+
}
|
|
754
|
+
function jsonPathExists(val, path) {
|
|
755
|
+
let p = path;
|
|
756
|
+
if (p.startsWith("$")) p = p.slice(1);
|
|
757
|
+
let cur = val;
|
|
758
|
+
while (p.length > 0) if (p.startsWith(".\"")) {
|
|
759
|
+
let end = 2;
|
|
760
|
+
while (end < p.length) {
|
|
761
|
+
if (p[end] === "\\" && end + 1 < p.length) {
|
|
762
|
+
end += 2;
|
|
763
|
+
continue;
|
|
764
|
+
}
|
|
765
|
+
if (p[end] === "\"") break;
|
|
766
|
+
end++;
|
|
767
|
+
}
|
|
768
|
+
if (end >= p.length) return false;
|
|
769
|
+
const key = p.slice(2, end).replace(/\\"/g, "\"").replace(/\\\\/g, "\\");
|
|
770
|
+
if (cur === null || typeof cur !== "object" || Array.isArray(cur)) return false;
|
|
771
|
+
if (!(key in cur)) return false;
|
|
772
|
+
cur = cur[key];
|
|
773
|
+
p = p.slice(end + 1);
|
|
774
|
+
} else if (p.startsWith(".")) {
|
|
775
|
+
const m = p.slice(1).match(/^([^.[]+)/);
|
|
776
|
+
if (!m) return false;
|
|
777
|
+
const key = m[1];
|
|
778
|
+
if (cur === null || typeof cur !== "object" || Array.isArray(cur)) return false;
|
|
779
|
+
if (!(key in cur)) return false;
|
|
780
|
+
cur = cur[key];
|
|
781
|
+
p = p.slice(1 + key.length);
|
|
782
|
+
} else if (p.startsWith("[")) {
|
|
783
|
+
const end = p.indexOf("]");
|
|
784
|
+
if (end === -1) return false;
|
|
785
|
+
const idx = parseInt(p.slice(1, end), 10);
|
|
786
|
+
if (!Array.isArray(cur) || idx < 0 || idx >= cur.length) return false;
|
|
787
|
+
cur = cur[idx];
|
|
788
|
+
p = p.slice(end + 1);
|
|
789
|
+
} else return false;
|
|
790
|
+
return cur !== void 0;
|
|
791
|
+
}
|
|
792
|
+
//#endregion
|
|
793
|
+
export { jsonPathExtract as a, traceDbCall as c, jsonPathExists as i, buildPredicateSql as n, matchesPredicate as o, jsonContains as r, traceCacheCall as s, QueryBuilder as t };
|
|
794
|
+
|
|
795
|
+
//# sourceMappingURL=query-LJranz0c.js.map
|