tempest-db-js 0.3.0 → 0.5.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.
@@ -37,10 +37,14 @@ var OPERATORS = [
37
37
  "lte",
38
38
  "like",
39
39
  "ilike",
40
+ "ieq",
40
41
  "in",
41
42
  "notIn",
42
43
  "between",
43
- "isNull"
44
+ "isNull",
45
+ "contains",
46
+ "containedBy",
47
+ "overlaps"
44
48
  ];
45
49
  var Agg = class {
46
50
  constructor(fn, column2) {
@@ -124,7 +128,56 @@ var SelectBuilder = class _SelectBuilder {
124
128
  offset(n) {
125
129
  return this.with({ offset: n });
126
130
  }
131
+ /**
132
+ * Lock the selected rows for update (`SELECT ... FOR UPDATE`), à la
133
+ * SQLAlchemy's `with_for_update()`.
134
+ *
135
+ * `{ skipLocked: true }` is the job-queue claim: competing workers each take a
136
+ * disjoint batch instead of blocking on — or worse, double-processing — the
137
+ * same rows.
138
+ *
139
+ * PostgreSQL and MySQL 8.0+ only. SQLite has no row-level locking, and its
140
+ * dialect throws rather than emitting a `SELECT` that silently locks nothing —
141
+ * a lock that does not exist only fails under production concurrency.
142
+ *
143
+ * @param options `skipLocked` / `noWait` wait behavior, and `of` to restrict
144
+ * the lock to specific tables.
145
+ * @returns A builder carrying the locking clause.
146
+ * @throws Error When both `skipLocked` and `noWait` are set.
147
+ *
148
+ * @example
149
+ * ```ts
150
+ * const batch = await session.execute(
151
+ * select(Outbound)
152
+ * .where({ status: "queued" })
153
+ * .orderBy("nextAttemptAt")
154
+ * .limit(10)
155
+ * .forUpdate({ skipLocked: true }),
156
+ * ).all();
157
+ * ```
158
+ */
159
+ forUpdate(options) {
160
+ return this.with({ lock: buildLock("update", options) });
161
+ }
162
+ /**
163
+ * Take a shared read lock on the selected rows (`SELECT ... FOR SHARE`), the
164
+ * weaker counterpart of {@link SelectBuilder.forUpdate}.
165
+ *
166
+ * @param options `skipLocked` / `noWait` wait behavior, and `of` tables.
167
+ * @returns A builder carrying the locking clause.
168
+ * @throws Error When both `skipLocked` and `noWait` are set.
169
+ */
170
+ forShare(options) {
171
+ return this.with({ lock: buildLock("share", options) });
172
+ }
127
173
  };
174
+ function buildLock(strength, options) {
175
+ if (options?.skipLocked && options?.noWait) {
176
+ throw new Error("forUpdate/forShare accept skipLocked or noWait, not both.");
177
+ }
178
+ const wait = options?.skipLocked ? "skipLocked" : options?.noWait ? "noWait" : "block";
179
+ return { strength, wait, of: options?.of ?? [] };
180
+ }
128
181
  function select(model, columns) {
129
182
  return new SelectBuilder(
130
183
  {
@@ -137,13 +190,223 @@ function select(model, columns) {
137
190
  where: void 0,
138
191
  orderBy: [],
139
192
  limit: void 0,
140
- offset: void 0
193
+ offset: void 0,
194
+ names: columnNamesOf(model) ?? void 0
141
195
  },
142
196
  model
143
197
  );
144
198
  }
145
199
 
200
+ // src/serialize.ts
201
+ var ValidationError = class extends Error {
202
+ constructor(table, issues) {
203
+ super(`Validation failed for ${table}:
204
+ - ${issues.join("\n - ")}`);
205
+ this.table = table;
206
+ this.issues = issues;
207
+ this.name = "ValidationError";
208
+ }
209
+ table;
210
+ issues;
211
+ };
212
+ function toBase64(bytes) {
213
+ if (typeof Buffer !== "undefined") return Buffer.from(bytes).toString("base64");
214
+ let binary = "";
215
+ for (const byte of bytes) binary += String.fromCharCode(byte);
216
+ return btoa(binary);
217
+ }
218
+ function fromBase64(value) {
219
+ if (typeof Buffer !== "undefined") return new Uint8Array(Buffer.from(value, "base64"));
220
+ const binary = atob(value);
221
+ const bytes = new Uint8Array(binary.length);
222
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
223
+ return bytes;
224
+ }
225
+ function encodeValue(column2, value) {
226
+ if (value === null || value === void 0) return null;
227
+ switch (column2.type.kind) {
228
+ case "bigint":
229
+ return typeof value === "bigint" ? value.toString() : value;
230
+ case "date":
231
+ case "datetime":
232
+ case "timestamp":
233
+ return value instanceof Date ? value.toISOString() : value;
234
+ case "blob":
235
+ return value instanceof Uint8Array ? toBase64(value) : value;
236
+ default:
237
+ return value;
238
+ }
239
+ }
240
+ function decodeValue(column2, value) {
241
+ if (value === null || value === void 0) return null;
242
+ switch (column2.type.kind) {
243
+ case "bigint":
244
+ return typeof value === "bigint" ? value : BigInt(value);
245
+ case "date":
246
+ case "datetime":
247
+ case "timestamp":
248
+ return value instanceof Date ? value : new Date(value);
249
+ case "blob":
250
+ return value instanceof Uint8Array ? value : fromBase64(value);
251
+ case "json":
252
+ return typeof value === "string" ? JSON.parse(value) : value;
253
+ case "array": {
254
+ const element = column2.type.meta.element;
255
+ const items = typeof value === "string" ? JSON.parse(value) : value;
256
+ if (!Array.isArray(items)) return items;
257
+ if (!element) return items;
258
+ return items.map((item) => decodeValue({ type: element }, item));
259
+ }
260
+ case "numeric":
261
+ return typeof value === "string" ? value : String(value);
262
+ case "boolean":
263
+ return typeof value === "boolean" ? value : value === 1 || value === "true";
264
+ case "smallint":
265
+ case "integer":
266
+ case "real":
267
+ case "double":
268
+ return typeof value === "number" ? value : Number(value);
269
+ default:
270
+ return value;
271
+ }
272
+ }
273
+ function toDict(model, row) {
274
+ const columns = columnsOf(model);
275
+ const out = {};
276
+ for (const name of Object.keys(columns)) {
277
+ out[name] = row[name] ?? null;
278
+ }
279
+ return out;
280
+ }
281
+ function toJSON(model, row) {
282
+ const columns = columnsOf(model);
283
+ const out = {};
284
+ for (const [name, col] of Object.entries(columns)) {
285
+ out[name] = encodeValue(col, row[name] ?? null);
286
+ }
287
+ return out;
288
+ }
289
+ function stringify(model, row) {
290
+ return JSON.stringify(toJSON(model, row));
291
+ }
292
+ function fromDict(model, data) {
293
+ const columns = columnsOf(model);
294
+ const out = {};
295
+ const issues = [];
296
+ for (const [name, col] of Object.entries(columns)) {
297
+ const present = name in data && data[name] !== void 0 && data[name] !== null;
298
+ if (!present) {
299
+ const required = col.flags.notNull && !col.flags.hasDefault;
300
+ if (required) {
301
+ issues.push(`missing required column "${name}"`);
302
+ continue;
303
+ }
304
+ out[name] = null;
305
+ continue;
306
+ }
307
+ try {
308
+ out[name] = decodeValue(col, data[name]);
309
+ } catch (error) {
310
+ issues.push(`column "${name}": ${error.message}`);
311
+ }
312
+ }
313
+ if (issues.length > 0) {
314
+ throw new ValidationError(model.tablename, issues);
315
+ }
316
+ return out;
317
+ }
318
+ function parse(model, json) {
319
+ return fromDict(model, JSON.parse(json));
320
+ }
321
+ function decoderFor(type) {
322
+ switch (type.kind) {
323
+ case "bigint":
324
+ return (v) => v == null ? null : typeof v === "bigint" ? v : BigInt(v);
325
+ case "date":
326
+ case "datetime":
327
+ case "timestamp":
328
+ return (v) => v == null ? null : v instanceof Date ? v : new Date(v);
329
+ case "blob":
330
+ return (v) => v == null ? null : v instanceof Uint8Array ? v : fromBase64(v);
331
+ case "json":
332
+ return (v) => v == null ? null : typeof v === "string" ? JSON.parse(v) : v;
333
+ case "array": {
334
+ const element = type.meta.element;
335
+ const inner = element ? decoderFor(element) : null;
336
+ if (!inner) return null;
337
+ return (v) => v == null ? null : Array.isArray(v) ? v.map(inner) : v;
338
+ }
339
+ case "numeric":
340
+ return (v) => v == null ? null : typeof v === "string" ? v : String(v);
341
+ case "boolean":
342
+ return (v) => v == null ? null : typeof v === "boolean" ? v : v === 1 || v === "true";
343
+ case "smallint":
344
+ case "integer":
345
+ case "real":
346
+ case "double":
347
+ return (v) => v == null ? null : typeof v === "number" ? v : Number(v);
348
+ default:
349
+ return null;
350
+ }
351
+ }
352
+ var mapperCache = /* @__PURE__ */ new WeakMap();
353
+ function mapperFor(model) {
354
+ const cached = mapperCache.get(model);
355
+ if (cached) return cached;
356
+ const props = columnPropsOf(model);
357
+ const names = props ? Object.fromEntries(Object.entries(props).map(([db, prop]) => [prop, db])) : null;
358
+ const decoders = /* @__PURE__ */ new Map();
359
+ for (const [prop, col] of Object.entries(columnsOf(model))) {
360
+ const decoder = decoderFor(col.type);
361
+ if (decoder) decoders.set(names?.[prop] ?? prop, decoder);
362
+ }
363
+ const mapper = { props, decoders };
364
+ mapperCache.set(model, mapper);
365
+ return mapper;
366
+ }
367
+ function coerceRow(model, raw) {
368
+ const { props, decoders } = mapperFor(model);
369
+ const out = {};
370
+ for (const name of Object.keys(raw)) {
371
+ const decode2 = decoders.get(name);
372
+ out[props?.[name] ?? name] = decode2 ? decode2(raw[name]) : raw[name];
373
+ }
374
+ return out;
375
+ }
376
+
146
377
  // src/mutations.ts
378
+ var STRUCTURED_KINDS = /* @__PURE__ */ new Set(["json", "array", "blob"]);
379
+ function isBindableScalar(value) {
380
+ if (value === null || value === void 0) return true;
381
+ const type = typeof value;
382
+ if (type === "string" || type === "number" || type === "bigint" || type === "boolean") {
383
+ return true;
384
+ }
385
+ return value instanceof Date || value instanceof Uint8Array;
386
+ }
387
+ function assertWritableValues(model, values, clause) {
388
+ const columns = columnsOf(model);
389
+ const issues = [];
390
+ for (const [key, value] of Object.entries(values)) {
391
+ const col = columns[key];
392
+ if (!col) {
393
+ issues.push(`${clause}: "${key}" is not a column of ${model.tablename}`);
394
+ continue;
395
+ }
396
+ if (isSqlExpression(value) || isBindableScalar(value)) continue;
397
+ if (typeof value === "object" && STRUCTURED_KINDS.has(col.type.kind)) continue;
398
+ issues.push(
399
+ `${clause}: "${key}" got ${describeValue(value)}, which cannot be bound to a ${col.type.kind} column \u2014 use sql.raw()/sql.expr\`...\` for a SQL expression`
400
+ );
401
+ }
402
+ if (issues.length > 0) throw new ValidationError(model.tablename, issues);
403
+ }
404
+ function describeValue(value) {
405
+ if (typeof value === "function") return "a function";
406
+ if (Array.isArray(value)) return "an array";
407
+ if (typeof value === "symbol") return "a symbol";
408
+ return "an object";
409
+ }
147
410
  var InsertBuilder = class _InsertBuilder {
148
411
  constructor(node, source) {
149
412
  this.node = node;
@@ -154,28 +417,66 @@ var InsertBuilder = class _InsertBuilder {
154
417
  with(patch) {
155
418
  return new _InsertBuilder({ ...this.node, ...patch }, this.source);
156
419
  }
157
- /** Provide one row or many rows to insert, typed by the insert shape. */
420
+ /**
421
+ * Provide one row or many rows to insert, typed by the insert shape.
422
+ *
423
+ * @param rows One row, or an array of rows.
424
+ * @returns A builder carrying the rows.
425
+ * @throws ValidationError When a value is not a column value the dialect can
426
+ * bind (see the `sql` helpers for writing an expression instead).
427
+ */
158
428
  values(rows) {
159
429
  const list = Array.isArray(rows) ? rows : [rows];
430
+ for (const row of list) assertWritableValues(this.source, row, "values");
160
431
  return this.with({ values: list });
161
432
  }
162
433
  /**
163
434
  * On a unique/PK conflict on `target`, do nothing (skip the row).
164
435
  *
165
436
  * @param target The conflicting column(s) — a unique or primary key.
437
+ * @param options Pass `where` to name the predicate of a **partial** unique
438
+ * index, which PostgreSQL requires in order to match it as a conflict target.
439
+ * @returns A builder carrying the conflict clause.
440
+ *
441
+ * @example
442
+ * ```ts
443
+ * insert(Outbound)
444
+ * .values(data)
445
+ * .onConflictDoNothing(["consumer", "idempotencyKey"], {
446
+ * where: { idempotencyKey: { isNull: false } },
447
+ * })
448
+ * .returning();
449
+ * ```
166
450
  */
167
- onConflictDoNothing(target) {
168
- return this.with({ onConflict: { target, update: "nothing" } });
451
+ onConflictDoNothing(target, options) {
452
+ return this.with({
453
+ onConflict: {
454
+ target,
455
+ update: "nothing",
456
+ targetWhere: options?.where ? toCondNode(options.where) : void 0
457
+ }
458
+ });
169
459
  }
170
460
  /**
171
461
  * On a unique/PK conflict on `target`, overwrite the given columns (upsert).
172
462
  *
173
463
  * @param target The conflicting column(s) — a unique or primary key.
174
464
  * @param set The columns to update with new values.
465
+ * @param options `indexWhere` names the predicate of a partial unique index
466
+ * (the conflict target); `updateWhere` further restricts which conflicting
467
+ * rows are rewritten.
468
+ * @returns A builder carrying the conflict clause.
469
+ * @throws ValidationError When a `set` value cannot be bound.
175
470
  */
176
- onConflictDoUpdate(target, set) {
471
+ onConflictDoUpdate(target, set, options) {
472
+ assertWritableValues(this.source, set, "set");
177
473
  return this.with({
178
- onConflict: { target, update: set }
474
+ onConflict: {
475
+ target,
476
+ update: set,
477
+ targetWhere: options?.indexWhere ? toCondNode(options.indexWhere) : void 0,
478
+ updateWhere: options?.updateWhere ? toCondNode(options.updateWhere) : void 0
479
+ }
179
480
  });
180
481
  }
181
482
  returning(columns) {
@@ -188,7 +489,8 @@ function insert(model) {
188
489
  kind: "insert",
189
490
  table: model.tablename,
190
491
  values: [],
191
- returning: null
492
+ returning: null,
493
+ names: columnNamesOf(model) ?? void 0
192
494
  },
193
495
  model
194
496
  );
@@ -203,8 +505,27 @@ var UpdateBuilder = class _UpdateBuilder {
203
505
  with(patch) {
204
506
  return new _UpdateBuilder({ ...this.node, ...patch }, this.source);
205
507
  }
206
- /** The columns to write. Partial — only the given columns change. */
508
+ /**
509
+ * The columns to write. Partial — only the given columns change.
510
+ *
511
+ * A value is bound as a parameter unless it is a {@link sql} expression, which
512
+ * is rendered inline instead — that is how a counter is written without a
513
+ * read-modify-write race.
514
+ *
515
+ * @param values The column → value map.
516
+ * @returns A builder carrying the assignments.
517
+ * @throws ValidationError When a value is not a column value the dialect can
518
+ * bind (a bare object, an array on a scalar column, a function).
519
+ *
520
+ * @example
521
+ * ```ts
522
+ * update(Outbound)
523
+ * .set({ attempts: sql.raw("attempts + 1"), updatedAt: sql.now() })
524
+ * .where({ id });
525
+ * ```
526
+ */
207
527
  set(values) {
528
+ assertWritableValues(this.source, values, "set");
208
529
  return this.with({ set: values });
209
530
  }
210
531
  /** Restrict the rows to update. Marks the builder safe to execute. */
@@ -230,7 +551,8 @@ function update(model) {
230
551
  set: {},
231
552
  where: void 0,
232
553
  guarded: false,
233
- returning: null
554
+ returning: null,
555
+ names: columnNamesOf(model) ?? void 0
234
556
  },
235
557
  model
236
558
  );
@@ -267,7 +589,8 @@ function del(model) {
267
589
  table: model.tablename,
268
590
  where: void 0,
269
591
  guarded: false,
270
- returning: null
592
+ returning: null,
593
+ names: columnNamesOf(model) ?? void 0
271
594
  },
272
595
  model
273
596
  );
@@ -320,215 +643,70 @@ function parseSqlite(raw, driver, rest) {
320
643
  driver,
321
644
  host: null,
322
645
  port: null,
323
- user: null,
324
- password: null,
325
- database: decode(database),
326
- options: {},
327
- raw
328
- };
329
- }
330
- function parseNetworkUrl(raw, driver, rest, dialect) {
331
- let parsed;
332
- try {
333
- parsed = new URL(`${dialect}:${rest}`);
334
- } catch {
335
- throw new InvalidDatabaseUrl(raw, "could not parse host/credentials");
336
- }
337
- const database = decode(parsed.pathname.replace(/^\//, "")) || null;
338
- const options = {};
339
- for (const [key, value] of parsed.searchParams) options[key] = value;
340
- return {
341
- dialect,
342
- driver,
343
- host: parsed.hostname || null,
344
- port: parsed.port ? Number(parsed.port) : null,
345
- user: parsed.username ? decode(parsed.username) : null,
346
- password: parsed.password ? decode(parsed.password) : null,
347
- database,
348
- options,
349
- raw
350
- };
351
- }
352
- function parseDatabaseUrl(url) {
353
- const schemeEnd = url.indexOf(":");
354
- if (schemeEnd === -1) {
355
- throw new InvalidDatabaseUrl(
356
- url,
357
- "missing scheme (expected e.g. sqlite:// or postgresql://)"
358
- );
359
- }
360
- const { base, driver } = splitScheme(url.slice(0, schemeEnd));
361
- const dialect = DIALECT_ALIASES[base];
362
- if (!dialect) {
363
- throw new InvalidDatabaseUrl(url, `unknown dialect ${JSON.stringify(base)}`);
364
- }
365
- const rest = url.slice(schemeEnd + 1);
366
- if (dialect === "sqlite") return parseSqlite(url, driver, rest);
367
- return parseNetworkUrl(url, driver, rest, dialect);
368
- }
369
- function detectDialect(url) {
370
- return parseDatabaseUrl(url).dialect;
371
- }
372
-
373
- // src/serialize.ts
374
- var ValidationError = class extends Error {
375
- constructor(table, issues) {
376
- super(`Validation failed for ${table}:
377
- - ${issues.join("\n - ")}`);
378
- this.table = table;
379
- this.issues = issues;
380
- this.name = "ValidationError";
381
- }
382
- table;
383
- issues;
384
- };
385
- function toBase64(bytes) {
386
- if (typeof Buffer !== "undefined") return Buffer.from(bytes).toString("base64");
387
- let binary = "";
388
- for (const byte of bytes) binary += String.fromCharCode(byte);
389
- return btoa(binary);
390
- }
391
- function fromBase64(value) {
392
- if (typeof Buffer !== "undefined") return new Uint8Array(Buffer.from(value, "base64"));
393
- const binary = atob(value);
394
- const bytes = new Uint8Array(binary.length);
395
- for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
396
- return bytes;
397
- }
398
- function encodeValue(column2, value) {
399
- if (value === null || value === void 0) return null;
400
- switch (column2.type.kind) {
401
- case "bigint":
402
- return typeof value === "bigint" ? value.toString() : value;
403
- case "date":
404
- case "datetime":
405
- case "timestamp":
406
- return value instanceof Date ? value.toISOString() : value;
407
- case "blob":
408
- return value instanceof Uint8Array ? toBase64(value) : value;
409
- default:
410
- return value;
411
- }
412
- }
413
- function decodeValue(column2, value) {
414
- if (value === null || value === void 0) return null;
415
- switch (column2.type.kind) {
416
- case "bigint":
417
- return typeof value === "bigint" ? value : BigInt(value);
418
- case "date":
419
- case "datetime":
420
- case "timestamp":
421
- return value instanceof Date ? value : new Date(value);
422
- case "blob":
423
- return value instanceof Uint8Array ? value : fromBase64(value);
424
- case "json":
425
- return typeof value === "string" ? JSON.parse(value) : value;
426
- case "numeric":
427
- return typeof value === "string" ? value : String(value);
428
- case "boolean":
429
- return typeof value === "boolean" ? value : value === 1 || value === "true";
430
- case "smallint":
431
- case "integer":
432
- case "real":
433
- case "double":
434
- return typeof value === "number" ? value : Number(value);
435
- default:
436
- return value;
437
- }
438
- }
439
- function toDict(model, row) {
440
- const columns = columnsOf(model);
441
- const out = {};
442
- for (const name of Object.keys(columns)) {
443
- out[name] = row[name] ?? null;
444
- }
445
- return out;
446
- }
447
- function toJSON(model, row) {
448
- const columns = columnsOf(model);
449
- const out = {};
450
- for (const [name, col] of Object.entries(columns)) {
451
- out[name] = encodeValue(col, row[name] ?? null);
452
- }
453
- return out;
454
- }
455
- function stringify(model, row) {
456
- return JSON.stringify(toJSON(model, row));
457
- }
458
- function fromDict(model, data) {
459
- const columns = columnsOf(model);
460
- const out = {};
461
- const issues = [];
462
- for (const [name, col] of Object.entries(columns)) {
463
- const present = name in data && data[name] !== void 0 && data[name] !== null;
464
- if (!present) {
465
- const required = col.flags.notNull && !col.flags.hasDefault;
466
- if (required) {
467
- issues.push(`missing required column "${name}"`);
468
- continue;
469
- }
470
- out[name] = null;
471
- continue;
472
- }
473
- try {
474
- out[name] = decodeValue(col, data[name]);
475
- } catch (error) {
476
- issues.push(`column "${name}": ${error.message}`);
477
- }
478
- }
479
- if (issues.length > 0) {
480
- throw new ValidationError(model.tablename, issues);
481
- }
482
- return out;
483
- }
484
- function parse(model, json) {
485
- return fromDict(model, JSON.parse(json));
646
+ user: null,
647
+ password: null,
648
+ database: decode(database),
649
+ options: {},
650
+ raw
651
+ };
486
652
  }
487
- function decoderForKind(kind) {
488
- switch (kind) {
489
- case "bigint":
490
- return (v) => v == null ? null : typeof v === "bigint" ? v : BigInt(v);
491
- case "date":
492
- case "datetime":
493
- case "timestamp":
494
- return (v) => v == null ? null : v instanceof Date ? v : new Date(v);
495
- case "blob":
496
- return (v) => v == null ? null : v instanceof Uint8Array ? v : fromBase64(v);
497
- case "json":
498
- return (v) => v == null ? null : typeof v === "string" ? JSON.parse(v) : v;
499
- case "numeric":
500
- return (v) => v == null ? null : typeof v === "string" ? v : String(v);
501
- case "boolean":
502
- return (v) => v == null ? null : typeof v === "boolean" ? v : v === 1 || v === "true";
503
- case "smallint":
504
- case "integer":
505
- case "real":
506
- case "double":
507
- return (v) => v == null ? null : typeof v === "number" ? v : Number(v);
508
- default:
509
- return null;
653
+ function parseNetworkUrl(raw, driver, rest, dialect) {
654
+ let parsed;
655
+ try {
656
+ parsed = new URL(`${dialect}:${rest}`);
657
+ } catch {
658
+ throw new InvalidDatabaseUrl(raw, "could not parse host/credentials");
510
659
  }
660
+ const database = decode(parsed.pathname.replace(/^\//, "")) || null;
661
+ const options = {};
662
+ for (const [key, value] of parsed.searchParams) options[key] = value;
663
+ return {
664
+ dialect,
665
+ driver,
666
+ host: parsed.hostname || null,
667
+ port: parsed.port ? Number(parsed.port) : null,
668
+ user: parsed.username ? decode(parsed.username) : null,
669
+ password: parsed.password ? decode(parsed.password) : null,
670
+ database,
671
+ options,
672
+ raw
673
+ };
511
674
  }
512
- var decoderCache = /* @__PURE__ */ new WeakMap();
513
- function decodersFor(model) {
514
- const cached = decoderCache.get(model);
515
- if (cached) return cached;
516
- const map = /* @__PURE__ */ new Map();
517
- for (const [name, col] of Object.entries(columnsOf(model))) {
518
- const decoder = decoderForKind(col.type.kind);
519
- if (decoder) map.set(name, decoder);
675
+ function parseDatabaseUrl(url) {
676
+ const schemeEnd = url.indexOf(":");
677
+ if (schemeEnd === -1) {
678
+ throw new InvalidDatabaseUrl(
679
+ url,
680
+ "missing scheme (expected e.g. sqlite:// or postgresql://)"
681
+ );
520
682
  }
521
- decoderCache.set(model, map);
522
- return map;
683
+ const { base, driver } = splitScheme(url.slice(0, schemeEnd));
684
+ const dialect = DIALECT_ALIASES[base];
685
+ if (!dialect) {
686
+ throw new InvalidDatabaseUrl(url, `unknown dialect ${JSON.stringify(base)}`);
687
+ }
688
+ const rest = url.slice(schemeEnd + 1);
689
+ if (dialect === "sqlite") return parseSqlite(url, driver, rest);
690
+ return parseNetworkUrl(url, driver, rest, dialect);
523
691
  }
524
- function coerceRow(model, raw) {
525
- const decoders = decodersFor(model);
526
- const out = {};
527
- for (const name of Object.keys(raw)) {
528
- const decode2 = decoders.get(name);
529
- out[name] = decode2 ? decode2(raw[name]) : raw[name];
692
+ function detectDialect(url) {
693
+ return parseDatabaseUrl(url).dialect;
694
+ }
695
+
696
+ // src/expressions.ts
697
+ function renderPortableToken(token, dialect) {
698
+ switch (token) {
699
+ case "now":
700
+ return dialect === "postgresql" ? "now()" : "CURRENT_TIMESTAMP";
701
+ case "current_date":
702
+ return "CURRENT_DATE";
703
+ case "current_time":
704
+ return "CURRENT_TIME";
705
+ case "uuidv4":
706
+ if (dialect === "postgresql") return "gen_random_uuid()";
707
+ if (dialect === "mysql") return "(UUID())";
708
+ return "(lower(hex(randomblob(16))))";
530
709
  }
531
- return out;
532
710
  }
533
711
 
534
712
  // src/dialect.ts
@@ -551,6 +729,20 @@ var Params = class {
551
729
  return this.placeholder(this.values.length);
552
730
  }
553
731
  };
732
+ function insertHasExpression(node) {
733
+ for (const row of node.values) {
734
+ for (const value of Object.values(row)) {
735
+ if (isSqlExpression(value)) return true;
736
+ }
737
+ }
738
+ const update2 = node.onConflict?.update;
739
+ if (update2 && update2 !== "nothing") {
740
+ for (const value of Object.values(update2)) {
741
+ if (isSqlExpression(value)) return true;
742
+ }
743
+ }
744
+ return false;
745
+ }
554
746
  var BaseDialect = class _BaseDialect {
555
747
  /**
556
748
  * INSERT SQL templates keyed by structure (dialect|table|columns|rowCount|
@@ -560,6 +752,21 @@ var BaseDialect = class _BaseDialect {
560
752
  static insertTemplates = /* @__PURE__ */ new Map();
561
753
  /** Quoted-identifier cache (see {@link quoteId}). Shared across dialects. */
562
754
  static quotedIds = /* @__PURE__ */ new Map();
755
+ /**
756
+ * The SQL operator for an array containment/overlap test.
757
+ *
758
+ * Only PostgreSQL has native arrays; the other dialects throw rather than
759
+ * emitting an operator that means something else there.
760
+ *
761
+ * @param op The array operator name.
762
+ * @returns The SQL operator text.
763
+ * @throws Error On a dialect without native array support.
764
+ */
765
+ arrayOperator(op) {
766
+ throw new Error(
767
+ `The "${op}" operator needs native array support, which ${this.name} does not have.`
768
+ );
769
+ }
563
770
  /**
564
771
  * Quote an identifier (column/table) for the active dialect.
565
772
  *
@@ -598,51 +805,175 @@ var BaseDialect = class _BaseDialect {
598
805
  }
599
806
  return { sql: sql2, params: params.values };
600
807
  }
601
- /** Render a qualified `alias.column` ref as `"alias"."column"`. */
602
- qualify(ref) {
808
+ /**
809
+ * Render a qualified `alias.column` ref as `"alias"."column"`, translating the
810
+ * property name to the real column name for that alias's model.
811
+ *
812
+ * @param ref The `alias.property` reference (a bare name is left unqualified).
813
+ * @param names The node's per-alias name maps, if any source renames columns.
814
+ * @returns The quoted, qualified identifier.
815
+ */
816
+ qualify(ref, names) {
603
817
  const dot = ref.indexOf(".");
604
818
  if (dot === -1) return this.quoteId(ref);
605
- return `${this.quoteId(ref.slice(0, dot))}.${this.quoteId(ref.slice(dot + 1))}`;
819
+ const alias = ref.slice(0, dot);
820
+ const prop = ref.slice(dot + 1);
821
+ return `${this.quoteId(alias)}.${this.columnId(prop, names?.[alias])}`;
822
+ }
823
+ /**
824
+ * Quote a column identifier, translating the model property name to the real
825
+ * database column name first.
826
+ *
827
+ * `names` is `undefined` for a model that renames nothing — the overwhelmingly
828
+ * common case — so this stays a single lookup plus the memoized quote.
829
+ *
830
+ * @param prop The model property name as written in the builder.
831
+ * @param names The node's property → column map, if any.
832
+ * @returns The quoted database identifier.
833
+ */
834
+ columnId(prop, names) {
835
+ return this.quoteId(names?.[prop] ?? prop);
836
+ }
837
+ /**
838
+ * Render a {@link SqlExpression} inline, binding the parameters it carries.
839
+ *
840
+ * This is what keeps `set({ attempts: sql.raw("attempts + 1") })` an expression
841
+ * instead of a bound object: the fragment goes into the statement text, and
842
+ * only a `sql.expr` template's interpolations become parameters.
843
+ *
844
+ * @param expr The branded expression.
845
+ * @param params The parameter collector for the statement being compiled.
846
+ * @returns The SQL text of the expression.
847
+ */
848
+ renderExpression(expr, params) {
849
+ const token = expr.expression;
850
+ if (typeof token === "string") return renderPortableToken(token, this.name);
851
+ if ("raw" in token) return token.raw;
852
+ const parts = token.parts;
853
+ let sql2 = parts[0] ?? "";
854
+ for (let i = 1; i < parts.length; i++) {
855
+ sql2 += `${params.bind(expr.params[i - 1])}${parts[i]}`;
856
+ }
857
+ return sql2;
858
+ }
859
+ /** Render one write value: a SQL expression inline, anything else as a parameter. */
860
+ renderValue(value, params) {
861
+ return isSqlExpression(value) ? this.renderExpression(value, params) : params.bind(value);
862
+ }
863
+ /**
864
+ * Render a row-level locking clause (`FOR UPDATE ...`).
865
+ *
866
+ * Standard on PostgreSQL and MySQL 8.0+; SQLite overrides it to throw.
867
+ *
868
+ * @param lock The locking clause from the node.
869
+ * @returns The SQL text, leading space included.
870
+ */
871
+ renderLock(lock) {
872
+ const strength = lock.strength === "update" ? "FOR UPDATE" : "FOR SHARE";
873
+ const of = lock.of.length > 0 ? ` OF ${lock.of.map((t) => this.quoteId(t)).join(", ")}` : "";
874
+ const wait = lock.wait === "skipLocked" ? " SKIP LOCKED" : lock.wait === "noWait" ? " NOWAIT" : "";
875
+ return ` ${strength}${of}${wait}`;
606
876
  }
607
877
  // ---- statements -------------------------------------------------------
608
878
  compileSelect(node, params) {
879
+ const names = node.names;
609
880
  let cols;
610
881
  if (node.aggregates.length > 0) {
611
- const groupSel = node.groupBy.map((c) => this.quoteId(c));
882
+ const groupSel = node.groupBy.map((c) => this.columnId(c, names));
612
883
  const aggSel = node.aggregates.map((a) => {
613
- const inner = a.column === "*" ? "*" : this.quoteId(a.column);
884
+ const inner = a.column === "*" ? "*" : this.columnId(a.column, names);
614
885
  return `${a.fn.toUpperCase()}(${inner}) AS ${this.quoteId(a.alias)}`;
615
886
  });
616
887
  cols = [...groupSel, ...aggSel].join(", ");
617
888
  } else {
618
- cols = node.columns === "*" ? "*" : node.columns.map((c) => this.quoteId(c)).join(", ");
889
+ cols = node.columns === "*" ? "*" : node.columns.map((c) => this.columnId(c, names)).join(", ");
619
890
  }
620
891
  let sql2 = `SELECT ${node.distinct ? "DISTINCT " : ""}${cols} FROM ${this.quoteId(node.table)}`;
621
- const where = this.compileCondition(node.where, params, (k) => this.quoteId(k));
892
+ const where = this.compileCondition(
893
+ node.where,
894
+ params,
895
+ (k) => this.columnId(k, names)
896
+ );
622
897
  if (where) sql2 += ` WHERE ${where}`;
623
898
  if (node.groupBy.length > 0) {
624
- sql2 += ` GROUP BY ${node.groupBy.map((c) => this.quoteId(c)).join(", ")}`;
899
+ sql2 += ` GROUP BY ${node.groupBy.map((c) => this.columnId(c, names)).join(", ")}`;
625
900
  }
626
901
  if (node.orderBy.length > 0) {
627
902
  const terms = node.orderBy.map(
628
- (t) => `${this.quoteId(t.column)} ${t.direction === "desc" ? "DESC" : "ASC"}`
903
+ (t) => `${this.columnId(t.column, names)} ${t.direction === "desc" ? "DESC" : "ASC"}`
629
904
  ).join(", ");
630
905
  sql2 += ` ORDER BY ${terms}`;
631
906
  }
632
907
  if (node.limit !== void 0) sql2 += ` LIMIT ${params.bind(node.limit)}`;
633
908
  if (node.offset !== void 0) sql2 += ` OFFSET ${params.bind(node.offset)}`;
909
+ if (node.lock) {
910
+ if (node.distinct || node.groupBy.length > 0 || node.aggregates.length > 0) {
911
+ throw new Error(
912
+ "FOR UPDATE / FOR SHARE cannot be combined with DISTINCT or an aggregate query \u2014 lock the underlying rows in a separate SELECT."
913
+ );
914
+ }
915
+ sql2 += this.renderLock(node.lock);
916
+ }
634
917
  return sql2;
635
918
  }
919
+ /**
920
+ * Compile an INSERT.
921
+ *
922
+ * Takes the cached fast path only when the statement text is a pure function of
923
+ * its structure. A SQL expression among the values, or a conflict predicate,
924
+ * makes the text depend on the values themselves — those compile uncached, in
925
+ * SQL order, so placeholder positions stay correct.
926
+ */
636
927
  compileInsert(node, params) {
637
928
  const columns = node.values.length > 0 ? Object.keys(node.values[0]) : [];
929
+ const conflict = node.onConflict;
930
+ const cacheable = conflict?.targetWhere === void 0 && conflict?.updateWhere === void 0 && !insertHasExpression(node);
931
+ if (!cacheable) return this.compileInsertDirect(node, columns, params);
638
932
  for (const row of node.values) {
639
933
  for (const c of columns) params.bind(row[c] ?? null);
640
934
  }
641
- const conflictCols = node.onConflict && node.onConflict.update !== "nothing" ? Object.keys(node.onConflict.update) : [];
935
+ const conflictCols = conflict && conflict.update !== "nothing" ? Object.keys(conflict.update) : [];
642
936
  for (const c of conflictCols) {
643
- params.bind((node.onConflict?.update)[c]);
937
+ params.bind((conflict?.update)[c]);
644
938
  }
645
- return this.insertTemplate(node, columns, conflictCols);
939
+ return this.insertTemplate(node, columns, conflictCols, params);
940
+ }
941
+ /**
942
+ * Compile an INSERT without the template cache, rendering clauses in statement
943
+ * order so every parameter is bound at the position it appears.
944
+ *
945
+ * @param node The insert node.
946
+ * @param columns The column keys shared by every row.
947
+ * @param params The parameter collector.
948
+ * @returns The SQL text.
949
+ */
950
+ compileInsertDirect(node, columns, params) {
951
+ const names = node.names;
952
+ const colSql = columns.map((c) => this.columnId(c, names)).join(", ");
953
+ const rowsSql = node.values.map((row) => {
954
+ const cells = columns.map(
955
+ (c) => this.renderValue(row[c] ?? null, params)
956
+ );
957
+ return `(${cells.join(", ")})`;
958
+ }).join(", ");
959
+ let sql2 = `INSERT INTO ${this.quoteId(node.table)} (${colSql}) VALUES ${rowsSql}`;
960
+ if (node.onConflict) {
961
+ const update2 = node.onConflict.update;
962
+ const conflictCols = update2 === "nothing" ? [] : Object.keys(update2);
963
+ let cursor = 0;
964
+ sql2 += this.renderConflict(
965
+ node.onConflict,
966
+ conflictCols,
967
+ () => {
968
+ const key = conflictCols[cursor++];
969
+ return this.renderValue(update2[key], params);
970
+ },
971
+ names,
972
+ params
973
+ );
974
+ }
975
+ sql2 += this.compileReturning(node.returning, names);
976
+ return sql2;
646
977
  }
647
978
  /**
648
979
  * The INSERT SQL template for a given structure, cached across calls.
@@ -652,13 +983,14 @@ var BaseDialect = class _BaseDialect {
652
983
  * deterministic from the counts (a fresh statement always starts binding at 1).
653
984
  * So a per-row insert loop compiles the string once and reuses it every row.
654
985
  */
655
- insertTemplate(node, columns, conflictCols) {
986
+ insertTemplate(node, columns, conflictCols, params) {
656
987
  const returningKey = node.returning === null ? "" : node.returning === "*" ? "*" : node.returning.join(",");
657
988
  const conflictKey = node.onConflict ? `${node.onConflict.target.join(",")}>${node.onConflict.update === "nothing" ? "nothing" : conflictCols.join(",")}` : "";
658
989
  const key = `${this.name}|${node.table}|${columns.join(",")}|${node.values.length}|${returningKey}|${conflictKey}`;
659
990
  const cached = _BaseDialect.insertTemplates.get(key);
660
991
  if (cached !== void 0) return cached;
661
- const colSql = columns.map((c) => this.quoteId(c)).join(", ");
992
+ const names = node.names;
993
+ const colSql = columns.map((c) => this.columnId(c, names)).join(", ");
662
994
  let position = 0;
663
995
  const rowsSql = node.values.map(() => `(${columns.map(() => this.placeholder(++position)).join(", ")})`).join(", ");
664
996
  let sql2 = `INSERT INTO ${this.quoteId(node.table)} (${colSql}) VALUES ${rowsSql}`;
@@ -666,57 +998,91 @@ var BaseDialect = class _BaseDialect {
666
998
  sql2 += this.renderConflict(
667
999
  node.onConflict,
668
1000
  conflictCols,
669
- () => this.placeholder(++position)
1001
+ () => this.placeholder(++position),
1002
+ names,
1003
+ params
670
1004
  );
671
1005
  }
672
- sql2 += this.compileReturning(node.returning);
1006
+ sql2 += this.compileReturning(node.returning, names);
673
1007
  _BaseDialect.insertTemplates.set(key, sql2);
674
1008
  return sql2;
675
1009
  }
676
1010
  /**
677
1011
  * Render the conflict-handling clause. Standard SQL (SQLite/PostgreSQL) uses
678
- * `ON CONFLICT (...) DO NOTHING | DO UPDATE SET ...`; MySQL overrides this.
1012
+ * `ON CONFLICT (...) [WHERE predicate] DO NOTHING | DO UPDATE SET ... [WHERE ...]`;
1013
+ * MySQL overrides this.
1014
+ *
1015
+ * The index predicate is rendered before the `DO UPDATE` assignments because
1016
+ * that is where it sits in the statement, so its parameters bind first.
679
1017
  *
680
1018
  * @param onConflict The conflict clause from the node.
681
1019
  * @param conflictCols The columns to overwrite on `DO UPDATE` (empty for nothing).
682
- * @param nextPlaceholder Yields the next positional placeholder (advances the count).
1020
+ * @param nextValue Yields the SQL for the next `DO UPDATE` assignment value.
1021
+ * @param names The node's property → column map, if any.
1022
+ * @param params The parameter collector, for the predicates.
1023
+ * @returns The SQL text, leading space included.
683
1024
  */
684
- renderConflict(onConflict, conflictCols, nextPlaceholder) {
685
- const target = onConflict.target.map((c) => this.quoteId(c)).join(", ");
686
- if (onConflict.update === "nothing") return ` ON CONFLICT (${target}) DO NOTHING`;
687
- const assignments = conflictCols.map((c) => `${this.quoteId(c)} = ${nextPlaceholder()}`).join(", ");
688
- return ` ON CONFLICT (${target}) DO UPDATE SET ${assignments}`;
1025
+ renderConflict(onConflict, conflictCols, nextValue, names, params) {
1026
+ const idFor = (key) => this.columnId(key, names);
1027
+ const target = onConflict.target.map(idFor).join(", ");
1028
+ const indexWhere = this.compileCondition(onConflict.targetWhere, params, idFor);
1029
+ const targetSql = indexWhere ? `(${target}) WHERE ${indexWhere}` : `(${target})`;
1030
+ if (onConflict.update === "nothing") return ` ON CONFLICT ${targetSql} DO NOTHING`;
1031
+ const assignments = conflictCols.map((c) => `${idFor(c)} = ${nextValue()}`).join(", ");
1032
+ let sql2 = ` ON CONFLICT ${targetSql} DO UPDATE SET ${assignments}`;
1033
+ const updateWhere = this.compileCondition(onConflict.updateWhere, params, idFor);
1034
+ if (updateWhere) sql2 += ` WHERE ${updateWhere}`;
1035
+ return sql2;
689
1036
  }
690
1037
  compileUpdate(node, params) {
691
- const sets = Object.entries(node.set).map(([col, value]) => `${this.quoteId(col)} = ${params.bind(value)}`).join(", ");
1038
+ const names = node.names;
1039
+ const sets = Object.entries(node.set).map(
1040
+ ([col, value]) => `${this.columnId(col, names)} = ${this.renderValue(value, params)}`
1041
+ ).join(", ");
692
1042
  let sql2 = `UPDATE ${this.quoteId(node.table)} SET ${sets}`;
693
- const where = this.compileCondition(node.where, params, (k) => this.quoteId(k));
1043
+ const where = this.compileCondition(
1044
+ node.where,
1045
+ params,
1046
+ (k) => this.columnId(k, names)
1047
+ );
694
1048
  if (where) sql2 += ` WHERE ${where}`;
695
- sql2 += this.compileReturning(node.returning);
1049
+ sql2 += this.compileReturning(node.returning, names);
696
1050
  return sql2;
697
1051
  }
698
1052
  compileDelete(node, params) {
1053
+ const names = node.names;
699
1054
  let sql2 = `DELETE FROM ${this.quoteId(node.table)}`;
700
- const where = this.compileCondition(node.where, params, (k) => this.quoteId(k));
1055
+ const where = this.compileCondition(
1056
+ node.where,
1057
+ params,
1058
+ (k) => this.columnId(k, names)
1059
+ );
701
1060
  if (where) sql2 += ` WHERE ${where}`;
702
- sql2 += this.compileReturning(node.returning);
1061
+ sql2 += this.compileReturning(node.returning, names);
703
1062
  return sql2;
704
1063
  }
705
1064
  compileJoin(node, params) {
1065
+ const names = node.names;
706
1066
  const cols = node.selections.map((s) => {
707
1067
  const ref = `${s.alias}.${s.column}`;
708
- return `${this.qualify(ref)} AS ${this.quoteId(ref)}`;
1068
+ return `${this.qualify(ref, names)} AS ${this.quoteId(ref)}`;
709
1069
  }).join(", ");
710
1070
  let sql2 = `SELECT ${cols} FROM ${this.quoteId(node.base.table)} AS ${this.quoteId(node.base.alias)}`;
711
1071
  for (const j of node.joins) {
712
1072
  const kw = j.kind === "left" ? "LEFT JOIN" : "INNER JOIN";
713
- const on = j.on.map(([l, r]) => `${this.qualify(l)} = ${this.qualify(r)}`).join(" AND ");
1073
+ const on = j.on.map(([l, r]) => `${this.qualify(l, names)} = ${this.qualify(r, names)}`).join(" AND ");
714
1074
  sql2 += ` ${kw} ${this.quoteId(j.table)} AS ${this.quoteId(j.alias)} ON ${on}`;
715
1075
  }
716
- const where = this.compileCondition(node.where, params, (k) => this.qualify(k));
1076
+ const where = this.compileCondition(
1077
+ node.where,
1078
+ params,
1079
+ (k) => this.qualify(k, names)
1080
+ );
717
1081
  if (where) sql2 += ` WHERE ${where}`;
718
1082
  if (node.orderBy.length > 0) {
719
- const terms = node.orderBy.map((t) => `${this.qualify(t.ref)} ${t.direction === "desc" ? "DESC" : "ASC"}`).join(", ");
1083
+ const terms = node.orderBy.map(
1084
+ (t) => `${this.qualify(t.ref, names)} ${t.direction === "desc" ? "DESC" : "ASC"}`
1085
+ ).join(", ");
720
1086
  sql2 += ` ORDER BY ${terms}`;
721
1087
  }
722
1088
  if (node.limit !== void 0) sql2 += ` LIMIT ${params.bind(node.limit)}`;
@@ -724,10 +1090,10 @@ var BaseDialect = class _BaseDialect {
724
1090
  return sql2;
725
1091
  }
726
1092
  // ---- clauses ----------------------------------------------------------
727
- compileReturning(returning) {
1093
+ compileReturning(returning, names) {
728
1094
  if (returning === null) return "";
729
1095
  if (returning === "*") return " RETURNING *";
730
- return ` RETURNING ${returning.map((c) => this.quoteId(c)).join(", ")}`;
1096
+ return ` RETURNING ${returning.map((c) => this.columnId(c, names)).join(", ")}`;
731
1097
  }
732
1098
  /**
733
1099
  * Compile a condition tree (fields / and / or / not) to SQL. `idFor` renders a
@@ -784,6 +1150,14 @@ var BaseDialect = class _BaseDialect {
784
1150
  return `${id} LIKE ${params.bind(operand)}`;
785
1151
  case "ilike":
786
1152
  return this.ilike(id, params.bind(operand));
1153
+ case "ieq":
1154
+ return operand === null ? `${id} IS NULL` : `lower(${id}) = lower(${params.bind(operand)})`;
1155
+ case "contains":
1156
+ return `${id} ${this.arrayOperator("contains")} ${params.bind(operand)}`;
1157
+ case "containedBy":
1158
+ return `${id} ${this.arrayOperator("containedBy")} ${params.bind(operand)}`;
1159
+ case "overlaps":
1160
+ return `${id} ${this.arrayOperator("overlaps")} ${params.bind(operand)}`;
787
1161
  case "in":
788
1162
  return this.compileIn(id, operand, params, false);
789
1163
  case "notIn":
@@ -814,6 +1188,16 @@ var SqliteDialect = class extends BaseDialect {
814
1188
  ilike(column2, param) {
815
1189
  return `${column2} LIKE ${param}`;
816
1190
  }
1191
+ /**
1192
+ * SQLite has no row-level locking, so a lock request is an error rather than a
1193
+ * silently unlocked `SELECT` — a lock that does not exist only shows up as
1194
+ * duplicated work under production concurrency.
1195
+ */
1196
+ renderLock() {
1197
+ throw new Error(
1198
+ "SQLite has no row-level locking \u2014 FOR UPDATE / FOR SHARE is unsupported. Serialize the claim inside a transaction instead."
1199
+ );
1200
+ }
817
1201
  };
818
1202
  var PostgresDialect = class extends BaseDialect {
819
1203
  name = "postgresql";
@@ -823,6 +1207,11 @@ var PostgresDialect = class extends BaseDialect {
823
1207
  ilike(column2, param) {
824
1208
  return `${column2} ILIKE ${param}`;
825
1209
  }
1210
+ arrayOperator(op) {
1211
+ if (op === "contains") return "@>";
1212
+ if (op === "containedBy") return "<@";
1213
+ return "&&";
1214
+ }
826
1215
  };
827
1216
  var mysqlQuotedIds = /* @__PURE__ */ new Map();
828
1217
  var MysqlDialect = class extends BaseDialect {
@@ -840,12 +1229,17 @@ var MysqlDialect = class extends BaseDialect {
840
1229
  mysqlQuotedIds.set(name, quoted);
841
1230
  return quoted;
842
1231
  }
843
- renderConflict(onConflict, conflictCols, nextPlaceholder) {
1232
+ renderConflict(onConflict, conflictCols, nextValue, names) {
1233
+ if (onConflict.targetWhere || onConflict.updateWhere) {
1234
+ throw new Error(
1235
+ "MySQL's ON DUPLICATE KEY UPDATE has no conflict-target predicate \u2014 a partial unique index is PostgreSQL/SQLite only."
1236
+ );
1237
+ }
844
1238
  if (onConflict.update === "nothing") {
845
- const col = this.quoteId(onConflict.target[0] ?? "id");
1239
+ const col = this.columnId(onConflict.target[0] ?? "id", names);
846
1240
  return ` ON DUPLICATE KEY UPDATE ${col} = ${col}`;
847
1241
  }
848
- const assignments = conflictCols.map((c) => `${this.quoteId(c)} = ${nextPlaceholder()}`).join(", ");
1242
+ const assignments = conflictCols.map((c) => `${this.columnId(c, names)} = ${nextValue()}`).join(", ");
849
1243
  return ` ON DUPLICATE KEY UPDATE ${assignments}`;
850
1244
  }
851
1245
  compileReturning(returning) {
@@ -870,6 +1264,11 @@ function getDialect(name) {
870
1264
  function selectionsFor(alias, model) {
871
1265
  return Object.keys(columnsOf(model)).map((column2) => ({ alias, column: column2 }));
872
1266
  }
1267
+ function withAliasNames(current, alias, model) {
1268
+ const names = columnNamesOf(model);
1269
+ if (!names) return current;
1270
+ return { ...current ?? {}, [alias]: names };
1271
+ }
873
1272
  var JoinBuilder = class _JoinBuilder {
874
1273
  constructor(node, sources) {
875
1274
  this.node = node;
@@ -882,7 +1281,8 @@ var JoinBuilder = class _JoinBuilder {
882
1281
  {
883
1282
  ...this.node,
884
1283
  joins: [...this.node.joins, clause],
885
- selections: [...this.node.selections, ...selectionsFor(clause.alias, model)]
1284
+ selections: [...this.node.selections, ...selectionsFor(clause.alias, model)],
1285
+ names: withAliasNames(this.node.names, clause.alias, model)
886
1286
  },
887
1287
  { ...this.sources, [clause.alias]: model }
888
1288
  );
@@ -943,7 +1343,8 @@ function join(model, alias) {
943
1343
  where: void 0,
944
1344
  orderBy: [],
945
1345
  limit: void 0,
946
- offset: void 0
1346
+ offset: void 0,
1347
+ names: withAliasNames(void 0, alias, model)
947
1348
  },
948
1349
  { [alias]: model }
949
1350
  );
@@ -1256,11 +1657,12 @@ function splitJoinRow(node, sources, raw) {
1256
1657
  const out = {};
1257
1658
  for (const [alias, model] of Object.entries(sources)) {
1258
1659
  const sub = {};
1660
+ const names = columnNamesOf(model);
1259
1661
  let allNull = true;
1260
1662
  for (const colName of Object.keys(columnsOf(model))) {
1261
1663
  const value = raw[`${alias}.${colName}`];
1262
1664
  if (value !== null && value !== void 0) allNull = false;
1263
- sub[colName] = value;
1665
+ sub[names?.[colName] ?? colName] = value;
1264
1666
  }
1265
1667
  out[alias] = leftAliases.has(alias) && allNull ? null : coerceRow(model, sub);
1266
1668
  }
@@ -1382,6 +1784,13 @@ var AsyncResult = class {
1382
1784
  }
1383
1785
  };
1384
1786
  var savepointCounter = 0;
1787
+ function assertRawParams(params) {
1788
+ if (!Array.isArray(params)) {
1789
+ throw new TypeError(
1790
+ "session.raw(sql, params) takes an array of bound parameters \u2014 never interpolate values into the SQL string."
1791
+ );
1792
+ }
1793
+ }
1385
1794
  var SyncSession = class {
1386
1795
  constructor(driver, dialect, logger) {
1387
1796
  this.driver = driver;
@@ -1400,6 +1809,44 @@ var SyncSession = class {
1400
1809
  throw new QueryExecutionError(error, sql2, params);
1401
1810
  }
1402
1811
  }
1812
+ /**
1813
+ * Run a raw, parameterized SQL statement (synchronous) — the runtime counterpart of the
1814
+ * migrations' `Op.execute`.
1815
+ *
1816
+ * A query builder never covers all of SQL, and without an escape hatch a single
1817
+ * unsupported query forces a whole second database stack alongside this one. Use
1818
+ * it for what the builder cannot yet express, and keep everything else typed.
1819
+ *
1820
+ * The statement goes through the same path as a compiled one: it is logged via
1821
+ * `onQuery`, wrapped in {@link QueryExecutionError} on failure, and runs on the
1822
+ * reserved connection inside `transaction()`.
1823
+ *
1824
+ * @param sql The statement text. Placeholders only (`$1` / `?` per dialect) —
1825
+ * never interpolate a value into this string.
1826
+ * @param params The bound parameters, in placeholder order.
1827
+ * @param options Pass `as` to coerce the returned rows with a model's column
1828
+ * types (and its column-name mapping).
1829
+ * @returns The result view over the returned rows.
1830
+ * @throws Error When `params` is not an array — the guard against calling this
1831
+ * with an interpolated string and no parameters by mistake.
1832
+ *
1833
+ * @example
1834
+ * ```ts
1835
+ * const claimed = await session.raw<OutboundRow>(
1836
+ * `UPDATE outbound_messages SET status = 'sending'
1837
+ * WHERE id = ANY($1) RETURNING *`,
1838
+ * [ids],
1839
+ * { as: Outbound },
1840
+ * ).all();
1841
+ * ```
1842
+ */
1843
+ raw(sql2, params = [], options) {
1844
+ assertRawParams(params);
1845
+ const result = this.exec(sql2, params);
1846
+ const model = options?.as;
1847
+ const rows = model ? result.rows.map((row) => coerceRow(model, row)) : result.rows;
1848
+ return new SyncResult(rows, result.changes);
1849
+ }
1403
1850
  /** Compile, run, and coerce a builder into a result. */
1404
1851
  execute(builder) {
1405
1852
  const node = builder.node;
@@ -1482,6 +1929,46 @@ var AsyncSession = class _AsyncSession {
1482
1929
  throw new QueryExecutionError(error, sql2, params);
1483
1930
  }
1484
1931
  }
1932
+ /**
1933
+ * Run a raw, parameterized SQL statement — the runtime counterpart of the
1934
+ * migrations' `Op.execute`.
1935
+ *
1936
+ * A query builder never covers all of SQL, and without an escape hatch a single
1937
+ * unsupported query forces a whole second database stack alongside this one. Use
1938
+ * it for what the builder cannot yet express, and keep everything else typed.
1939
+ *
1940
+ * The statement goes through the same path as a compiled one: it is logged via
1941
+ * `onQuery`, wrapped in {@link QueryExecutionError} on failure, and runs on the
1942
+ * reserved connection inside `transaction()`.
1943
+ *
1944
+ * @param sql The statement text. Placeholders only (`$1` / `?` per dialect) —
1945
+ * never interpolate a value into this string.
1946
+ * @param params The bound parameters, in placeholder order.
1947
+ * @param options Pass `as` to coerce the returned rows with a model's column
1948
+ * types (and its column-name mapping).
1949
+ * @returns The result view over the returned rows.
1950
+ * @throws Error When `params` is not an array — the guard against calling this
1951
+ * with an interpolated string and no parameters by mistake.
1952
+ *
1953
+ * @example
1954
+ * ```ts
1955
+ * const claimed = await session.raw<OutboundRow>(
1956
+ * `UPDATE outbound_messages SET status = 'sending'
1957
+ * WHERE id = ANY($1) RETURNING *`,
1958
+ * [ids],
1959
+ * { as: Outbound },
1960
+ * ).all();
1961
+ * ```
1962
+ */
1963
+ raw(sql2, params = [], options) {
1964
+ assertRawParams(params);
1965
+ const model = options?.as;
1966
+ const inner = this.exec(sql2, params).then((result) => {
1967
+ const rows = model ? result.rows.map((row) => coerceRow(model, row)) : result.rows;
1968
+ return new SyncResult(rows, result.changes);
1969
+ });
1970
+ return new AsyncResult(inner);
1971
+ }
1485
1972
  execute(builder) {
1486
1973
  const node = builder.node;
1487
1974
  const { sql: sql2, params } = this.dialect.compile(node);
@@ -1748,73 +2235,190 @@ function createPostgresDriver(url, pool) {
1748
2235
  var DEFAULT_FLAGS = {
1749
2236
  primaryKey: false,
1750
2237
  notNull: false,
1751
- hasDefault: false
2238
+ hasDefault: false,
2239
+ unique: false
1752
2240
  };
2241
+ var EXPRESSION = /* @__PURE__ */ Symbol.for("tempest-db-js.expression");
2242
+ function expression(token, params = []) {
2243
+ return { [EXPRESSION]: true, kind: "expression", expression: token, params };
2244
+ }
2245
+ function isSqlExpression(value) {
2246
+ return typeof value === "object" && value !== null && value[EXPRESSION] === true;
2247
+ }
1753
2248
  var sql = {
1754
2249
  /** Current timestamp at insert (`CURRENT_TIMESTAMP` / `now()`). */
1755
- now: () => ({ kind: "expression", expression: "now" }),
2250
+ now: () => expression("now"),
1756
2251
  /** Current date. */
1757
- currentDate: () => ({ kind: "expression", expression: "current_date" }),
2252
+ currentDate: () => expression("current_date"),
1758
2253
  /** Current time. */
1759
- currentTime: () => ({ kind: "expression", expression: "current_time" }),
2254
+ currentTime: () => expression("current_time"),
1760
2255
  /** A freshly generated UUID v4 (`gen_random_uuid()` / portable fallback). */
1761
- uuidv4: () => ({ kind: "expression", expression: "uuidv4" }),
1762
- /** Escape hatch: a verbatim SQL expression rendered as-is. */
1763
- raw: (expression) => ({
1764
- kind: "expression",
1765
- expression: { raw: expression }
1766
- })
2256
+ uuidv4: () => expression("uuidv4"),
2257
+ /**
2258
+ * Escape hatch: a verbatim SQL expression rendered as-is, with no parameters.
2259
+ *
2260
+ * The fragment is interpolated into the statement untouched, so it must never
2261
+ * carry user input — use {@link sql.expr} when a value has to be bound.
2262
+ *
2263
+ * @param fragment The SQL text (e.g. `"attempts + 1"`).
2264
+ * @returns The expression, usable as a default and as a write value.
2265
+ */
2266
+ raw: (fragment) => expression({ raw: fragment }),
2267
+ /**
2268
+ * A parameterized SQL expression, written as a tagged template. Static text is
2269
+ * SQL; every `${...}` interpolation becomes a bound parameter, so the fragment
2270
+ * is injection-safe by construction.
2271
+ *
2272
+ * Cannot be used as a column default — a `DEFAULT` clause has nowhere to bind
2273
+ * parameters; use {@link sql.raw} there.
2274
+ *
2275
+ * @param parts The static SQL segments supplied by the template tag.
2276
+ * @param values The interpolated values, bound in order.
2277
+ * @returns The expression, usable as a write value.
2278
+ *
2279
+ * @example
2280
+ * ```ts
2281
+ * update(Account).set({ balance: sql.expr`balance - ${amount}` }).where({ id });
2282
+ * // UPDATE "accounts" SET "balance" = balance - $1 WHERE "id" = $2
2283
+ * ```
2284
+ */
2285
+ expr: (parts, ...values) => expression({ parts: Array.from(parts) }, values)
1767
2286
  };
1768
2287
  function isDefaultValue(value) {
1769
2288
  return typeof value === "object" && value !== null && "kind" in value && (value.kind === "literal" || value.kind === "expression");
1770
2289
  }
2290
+ function bindsParameters(value) {
2291
+ return value.kind === "expression" && typeof value.expression === "object" && "parts" in value.expression;
2292
+ }
2293
+ function parseReference(ref, options) {
2294
+ const dot = ref.lastIndexOf(".");
2295
+ if (dot <= 0 || dot === ref.length - 1) {
2296
+ throw new Error(`Invalid foreign key reference "${ref}"; expected "table.column".`);
2297
+ }
2298
+ return {
2299
+ table: ref.slice(0, dot),
2300
+ column: ref.slice(dot + 1),
2301
+ onDelete: options?.onDelete,
2302
+ onUpdate: options?.onUpdate
2303
+ };
2304
+ }
1771
2305
  var Column = class _Column {
1772
- constructor(type, flags, defaultValue = null, onUpdateValue = null) {
2306
+ constructor(type, flags, defaultValue = null, onUpdateValue = null, reference = null, dbName = null) {
1773
2307
  this.type = type;
1774
2308
  this.flags = flags;
1775
2309
  this.defaultValue = defaultValue;
1776
2310
  this.onUpdateValue = onUpdateValue;
2311
+ this.reference = reference;
2312
+ this.dbName = dbName;
1777
2313
  }
1778
2314
  type;
1779
2315
  flags;
1780
2316
  defaultValue;
1781
2317
  onUpdateValue;
1782
- primaryKey() {
2318
+ reference;
2319
+ dbName;
2320
+ /** Clone this column with one facet replaced, carrying every other over. */
2321
+ derive(patch) {
1783
2322
  return new _Column(
1784
2323
  this.type,
1785
- { ...this.flags, primaryKey: true, hasDefault: true },
1786
- this.defaultValue,
1787
- this.onUpdateValue
2324
+ patch.flags ?? this.flags,
2325
+ patch.defaultValue !== void 0 ? patch.defaultValue : this.defaultValue,
2326
+ patch.onUpdateValue !== void 0 ? patch.onUpdateValue : this.onUpdateValue,
2327
+ patch.reference !== void 0 ? patch.reference : this.reference,
2328
+ patch.dbName !== void 0 ? patch.dbName : this.dbName
1788
2329
  );
1789
2330
  }
2331
+ primaryKey() {
2332
+ return this.derive({ flags: { ...this.flags, primaryKey: true, hasDefault: true } });
2333
+ }
1790
2334
  notNull() {
1791
- return new _Column(
1792
- this.type,
1793
- { ...this.flags, notNull: true },
1794
- this.defaultValue,
1795
- this.onUpdateValue
1796
- );
2335
+ return this.derive({ flags: { ...this.flags, notNull: true } });
2336
+ }
2337
+ /**
2338
+ * Add a `UNIQUE` constraint to the column (mirrors SQLAlchemy's
2339
+ * `mapped_column(unique=True)`). DDL-only — does not change the inferred type.
2340
+ */
2341
+ unique() {
2342
+ return this.derive({ flags: { ...this.flags, unique: true } });
2343
+ }
2344
+ /**
2345
+ * Map this property to a differently-named database column, à la SQLAlchemy's
2346
+ * `mapped_column("consumer_name")` (Django's `db_column`, Prisma's `@map`).
2347
+ *
2348
+ * The override applies everywhere the name reaches SQL — select, insert,
2349
+ * update, delete, where, order by, group by, returning, conflict targets, the
2350
+ * migration IR and the drift check — while the TypeScript row keeps the
2351
+ * property name. Use it to keep a `snake_case` schema behind a `camelCase`
2352
+ * model; {@link Model.naming} does the same for a whole table at once.
2353
+ *
2354
+ * @param dbName The real column name in the database.
2355
+ * @returns A new column bound to that name.
2356
+ * @throws Error When `dbName` is empty.
2357
+ *
2358
+ * @example
2359
+ * ```ts
2360
+ * class ApiKey extends Model {
2361
+ * static tablename = "api_keys";
2362
+ * consumerName = column.text().name("consumer_name").notNull();
2363
+ * }
2364
+ * ```
2365
+ */
2366
+ name(dbName) {
2367
+ if (dbName.length === 0) {
2368
+ throw new Error("column.name() requires a non-empty database column name.");
2369
+ }
2370
+ return this.derive({ dbName });
2371
+ }
2372
+ /**
2373
+ * Declare a foreign-key reference to another table's column, à la SQLAlchemy's
2374
+ * `mapped_column(ForeignKey("table.column", ondelete=...))`. DDL-only — does
2375
+ * not change the inferred type.
2376
+ *
2377
+ * @param ref The target as `"table.column"` (e.g. `"users.id"`).
2378
+ * @param options Optional `onDelete` / `onUpdate` referential actions.
2379
+ * @returns A new column carrying the reference.
2380
+ * @throws Error When `ref` is not a valid `"table.column"` string.
2381
+ */
2382
+ references(ref, options) {
2383
+ return this.derive({ reference: parseReference(ref, options) });
1797
2384
  }
1798
2385
  /**
1799
2386
  * Set the insert-time default: a constant value of type `T`, or a portable
1800
2387
  * server-side expression from {@link sql} (e.g. `sql.now()`, `sql.uuidv4()`).
2388
+ *
2389
+ * @param value The literal default, or a {@link sql} expression.
2390
+ * @returns A new column carrying the default.
2391
+ * @throws Error When given a `sql.expr` fragment — a `DEFAULT` clause has
2392
+ * nowhere to bind parameters; use `sql.raw()` for a verbatim expression.
1801
2393
  */
1802
2394
  default(value) {
1803
2395
  const resolved = isDefaultValue(value) ? value : { kind: "literal", value };
1804
- return new _Column(
1805
- this.type,
1806
- { ...this.flags, hasDefault: true },
1807
- resolved,
1808
- this.onUpdateValue
1809
- );
2396
+ if (bindsParameters(resolved)) {
2397
+ throw new Error(
2398
+ "sql.expr`...` binds parameters and cannot be a column default \u2014 use sql.raw() for a verbatim DEFAULT expression."
2399
+ );
2400
+ }
2401
+ return this.derive({
2402
+ flags: { ...this.flags, hasDefault: true },
2403
+ defaultValue: resolved
2404
+ });
1810
2405
  }
1811
2406
  /**
1812
2407
  * Re-apply a value whenever the row is updated (e.g. an `updated_at` column
1813
2408
  * with `sql.now()`). Mirrors SQLAlchemy's `onupdate`.
2409
+ *
2410
+ * @param value The literal value, or a {@link sql} expression.
2411
+ * @returns A new column carrying the on-update value.
2412
+ * @throws Error When given a `sql.expr` fragment (see {@link Column.default}).
1814
2413
  */
1815
2414
  onUpdate(value) {
1816
2415
  const resolved = isDefaultValue(value) ? value : { kind: "literal", value };
1817
- return new _Column(this.type, this.flags, this.defaultValue, resolved);
2416
+ if (bindsParameters(resolved)) {
2417
+ throw new Error(
2418
+ "sql.expr`...` binds parameters and cannot be an onUpdate default \u2014 use sql.raw() for a verbatim expression."
2419
+ );
2420
+ }
2421
+ return this.derive({ onUpdateValue: resolved });
1818
2422
  }
1819
2423
  };
1820
2424
  function makeColumn(kind, meta = {}) {
@@ -1866,10 +2470,67 @@ var column = {
1866
2470
  /** `UUID` → `string`. */
1867
2471
  uuid: () => makeColumn("uuid"),
1868
2472
  /** `ENUM(...values)` → a string-literal union of the given values. */
1869
- enum: (...values) => makeColumn("enum", { values })
2473
+ enum: (...values) => makeColumn("enum", { values }),
2474
+ /**
2475
+ * A PostgreSQL array column (`text[]`, `integer[]`) → `T[]`.
2476
+ *
2477
+ * PostgreSQL only: SQLite and MySQL have no native array type, and rendering
2478
+ * one as JSON there would give the same model different semantics per dialect
2479
+ * (`@>` and `&&` work on one and not the other), so the DDL renderer throws
2480
+ * for those dialects instead of falling back silently.
2481
+ *
2482
+ * @param element The element column (its type, not its flags, is what is used).
2483
+ * @returns A column whose inferred type is an array of the element's type.
2484
+ *
2485
+ * @example
2486
+ * ```ts
2487
+ * class ApiKey extends Model {
2488
+ * static tablename = "api_keys";
2489
+ * scopes = column.array(column.text()).notNull().default(["send"]);
2490
+ * }
2491
+ * ```
2492
+ */
2493
+ array: (element) => makeColumn("array", { element: element.type })
1870
2494
  };
2495
+ function unique(...columns) {
2496
+ if (columns.length === 0) {
2497
+ throw new Error("unique() requires at least one column.");
2498
+ }
2499
+ return { kind: "unique", columns };
2500
+ }
2501
+ function foreignKey(columns, refTable, refColumns, options) {
2502
+ if (columns.length === 0 || columns.length !== refColumns.length) {
2503
+ throw new Error(
2504
+ "foreignKey() requires matching, non-empty local and referenced column lists."
2505
+ );
2506
+ }
2507
+ return {
2508
+ kind: "foreignKey",
2509
+ name: options?.name,
2510
+ columns,
2511
+ refTable,
2512
+ refColumns,
2513
+ onDelete: options?.onDelete,
2514
+ onUpdate: options?.onUpdate
2515
+ };
2516
+ }
2517
+ function toSnakeCase(name) {
2518
+ return name.replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
2519
+ }
1871
2520
  var Model = class {
1872
2521
  static tablename;
2522
+ /**
2523
+ * Optional table-level constraints (composite unique / foreign keys), returned
2524
+ * by a thunk so forward references resolve lazily. Mirrors SQLAlchemy's
2525
+ * `__table_args__`.
2526
+ */
2527
+ static tableArgs;
2528
+ /**
2529
+ * How to derive column names from property names (default `"preserve"`). Set
2530
+ * `"snake_case"` to keep a `snake_case` schema behind a `camelCase` model
2531
+ * without annotating every column; {@link Column.name} overrides it per column.
2532
+ */
2533
+ static naming;
1873
2534
  };
1874
2535
  var columnsCache = /* @__PURE__ */ new WeakMap();
1875
2536
  function columnsOf(model) {
@@ -1885,7 +2546,48 @@ function columnsOf(model) {
1885
2546
  columnsCache.set(model, out);
1886
2547
  return out;
1887
2548
  }
2549
+ var nameMapCache = /* @__PURE__ */ new WeakMap();
2550
+ var propMapCache = /* @__PURE__ */ new WeakMap();
2551
+ function columnNamesOf(model) {
2552
+ const cached = nameMapCache.get(model);
2553
+ if (cached !== void 0) return cached;
2554
+ const strategy = model.naming ?? "preserve";
2555
+ const map = {};
2556
+ const seen = /* @__PURE__ */ new Map();
2557
+ let renamed = false;
2558
+ for (const [prop, col] of Object.entries(columnsOf(model))) {
2559
+ const dbName = col.dbName ?? (strategy === "snake_case" ? toSnakeCase(prop) : prop);
2560
+ const collision = seen.get(dbName);
2561
+ if (collision !== void 0) {
2562
+ throw new Error(
2563
+ `${model.tablename}: properties "${collision}" and "${prop}" both map to column "${dbName}".`
2564
+ );
2565
+ }
2566
+ seen.set(dbName, prop);
2567
+ map[prop] = dbName;
2568
+ if (dbName !== prop) renamed = true;
2569
+ }
2570
+ const result = renamed ? map : null;
2571
+ nameMapCache.set(model, result);
2572
+ return result;
2573
+ }
2574
+ function columnPropsOf(model) {
2575
+ const cached = propMapCache.get(model);
2576
+ if (cached !== void 0) return cached;
2577
+ const forward = columnNamesOf(model);
2578
+ let result = null;
2579
+ if (forward) {
2580
+ const inverse = {};
2581
+ for (const [prop, dbName] of Object.entries(forward)) inverse[dbName] = prop;
2582
+ result = inverse;
2583
+ }
2584
+ propMapCache.set(model, result);
2585
+ return result;
2586
+ }
2587
+ function dbColumn(names, prop) {
2588
+ return names?.[prop] ?? prop;
2589
+ }
1888
2590
 
1889
- export { ActiveRecord, Agg, AsyncEngine, AsyncResult, AsyncSession, BaseDialect, BaseRepository, Column, DeleteBuilder, InsertBuilder, InvalidDatabaseUrl, JoinBuilder, Model, MysqlDialect, NoResultError, NodeSqliteDriver, OPERATORS, PostgresDialect, QueryExecutionError, RecordNotFound, SelectBuilder, SqliteDialect, SyncEngine, SyncResult, SyncSession, UpdateBuilder, ValidationError, activeRecord, and, avg, belongsTo, column, columnsOf, count, createEngine, createSyncEngine, del, detectDialect, fromDict, getDialect, hasMany, insert, isCondition, join, loadRelations, max, min, not, or, parse, parseDatabaseUrl, select, sql, stringify, sum, toCondNode, toDict, toJSON, update };
1890
- //# sourceMappingURL=chunk-Q32CBI2A.js.map
1891
- //# sourceMappingURL=chunk-Q32CBI2A.js.map
2591
+ export { ActiveRecord, Agg, AsyncEngine, AsyncResult, AsyncSession, BaseDialect, BaseRepository, Column, DeleteBuilder, InsertBuilder, InvalidDatabaseUrl, JoinBuilder, Model, MysqlDialect, NoResultError, NodeSqliteDriver, OPERATORS, Params, PostgresDialect, QueryExecutionError, RecordNotFound, SelectBuilder, SqliteDialect, SyncEngine, SyncResult, SyncSession, UpdateBuilder, ValidationError, activeRecord, and, avg, belongsTo, column, columnNamesOf, columnPropsOf, columnsOf, count, createEngine, createSyncEngine, dbColumn, del, detectDialect, foreignKey, fromDict, getDialect, hasMany, insert, isCondition, isSqlExpression, join, loadRelations, max, min, not, or, parse, parseDatabaseUrl, renderPortableToken, select, sql, stringify, sum, toCondNode, toDict, toJSON, toSnakeCase, unique, update };
2592
+ //# sourceMappingURL=chunk-5QQMVTS5.js.map
2593
+ //# sourceMappingURL=chunk-5QQMVTS5.js.map