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