bun-query-builder 0.1.24 → 0.1.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/src/index.js CHANGED
@@ -11694,7 +11694,7 @@ function setConfig(userConfig) {
11694
11694
  Object.assign(_config, config5);
11695
11695
  }
11696
11696
  }
11697
- var defaultConfig4, config5, _config = null, _lastConfiguredDialect = null, _warnedDialectConflicts;
11697
+ var defaultConfig4, CONFIG_SINGLETON_KEY, config5, _config = null, _lastConfiguredDialect = null, _warnedDialectConflicts;
11698
11698
  var init_config = __esm(() => {
11699
11699
  init_dist();
11700
11700
  defaultConfig4 = {
@@ -11754,14 +11754,14 @@ var init_config = __esm(() => {
11754
11754
  defaultFilter: true
11755
11755
  }
11756
11756
  };
11757
- config5 = { ...defaultConfig4 };
11757
+ CONFIG_SINGLETON_KEY = Symbol.for("bun-query-builder.config");
11758
+ config5 = globalThis[CONFIG_SINGLETON_KEY] ??= { ...defaultConfig4 };
11758
11759
  _warnedDialectConflicts = new Set;
11759
11760
  });
11760
11761
 
11761
11762
  // src/db.ts
11762
11763
  var {SQL } = globalThis.Bun;
11763
11764
  import { Database } from "bun:sqlite";
11764
- import process19 from "process";
11765
11765
 
11766
11766
  class SQLiteWrapper {
11767
11767
  db;
@@ -11993,6 +11993,21 @@ function createConnectionString(dialect, dbConfig) {
11993
11993
  throw new Error(`Unsupported dialect: ${dialect}`);
11994
11994
  }
11995
11995
  }
11996
+ function resolvePoolOptions(pool) {
11997
+ if (!pool)
11998
+ return {};
11999
+ const out = {};
12000
+ const toSeconds = (ms) => Math.max(0, Math.round(ms / 1000));
12001
+ if (typeof pool.max === "number" && Number.isFinite(pool.max))
12002
+ out.max = pool.max;
12003
+ if (typeof pool.idleTimeoutMs === "number" && Number.isFinite(pool.idleTimeoutMs))
12004
+ out.idleTimeout = toSeconds(pool.idleTimeoutMs);
12005
+ if (typeof pool.acquireTimeoutMs === "number" && Number.isFinite(pool.acquireTimeoutMs))
12006
+ out.connectionTimeout = toSeconds(pool.acquireTimeoutMs);
12007
+ if (typeof pool.maxLifetimeMs === "number" && Number.isFinite(pool.maxLifetimeMs))
12008
+ out.maxLifetime = toSeconds(pool.maxLifetimeMs);
12009
+ return out;
12010
+ }
11996
12011
  function getBunSql() {
11997
12012
  const dialect = config5.dialect;
11998
12013
  const connectionString = createConnectionString(dialect, config5.database);
@@ -12000,43 +12015,42 @@ function getBunSql() {
12000
12015
  if (dialect === "sqlite") {
12001
12016
  return createSQLiteSQL(connectionString);
12002
12017
  }
12003
- const sql = new SQL(connectionString);
12004
- if (sql && typeof sql.catch === "function") {
12005
- sql.catch((error) => {
12006
- if (config5.verbose && !error.message.includes("database") && !error.message.includes("does not exist")) {
12007
- console.warn(`[query-builder] Database connection error: ${error.message}`);
12008
- }
12009
- });
12010
- }
12018
+ const poolOptions = resolvePoolOptions(config5.database.pool);
12019
+ const sql = Object.keys(poolOptions).length > 0 ? new SQL(connectionString, poolOptions) : new SQL(connectionString);
12011
12020
  return sql;
12012
12021
  } catch (error) {
12013
- if (config5.verbose) {
12014
- console.warn(`[query-builder] Failed to create connection: ${error.message}`);
12015
- }
12016
- try {
12022
+ console.error(`[query-builder] Failed to create database connection for dialect '${dialect}': ${error.message}`);
12023
+ if (dialect === "sqlite") {
12017
12024
  return createSQLiteSQL(":memory:");
12018
- } catch {
12019
- return {
12020
- query: () => Promise.resolve([]),
12021
- execute: () => Promise.resolve([]),
12022
- close: () => Promise.resolve()
12023
- };
12024
12025
  }
12026
+ throw error;
12025
12027
  }
12026
12028
  }
12029
+ function connectionSignature() {
12030
+ const d = config5.database;
12031
+ return JSON.stringify({
12032
+ dialect: config5.dialect,
12033
+ database: d.database,
12034
+ username: d.username,
12035
+ password: d.password,
12036
+ host: d.host,
12037
+ port: d.port,
12038
+ url: d.url,
12039
+ pool: resolvePoolOptions(d.pool)
12040
+ });
12041
+ }
12027
12042
  function getOrCreateBunSql(forceNew = false) {
12028
- const configChanged = _bunSqlInstance !== null && (_currentDialect !== config5.dialect || _currentDatabase !== config5.database.database);
12043
+ const signature = connectionSignature();
12044
+ const configChanged = _bunSqlInstance !== null && _currentSignature !== signature;
12029
12045
  if (forceNew || configChanged || !_bunSqlInstance) {
12030
12046
  _bunSqlInstance = getBunSql();
12031
- _currentDialect = config5.dialect;
12032
- _currentDatabase = config5.database.database;
12047
+ _currentSignature = signature;
12033
12048
  }
12034
12049
  return _bunSqlInstance;
12035
12050
  }
12036
12051
  function resetConnection() {
12037
12052
  _bunSqlInstance = null;
12038
- _currentDialect = null;
12039
- _currentDatabase = null;
12053
+ _currentSignature = null;
12040
12054
  }
12041
12055
  async function withFreshConnection(fn) {
12042
12056
  try {
@@ -12051,7 +12065,7 @@ async function withFreshConnection(fn) {
12051
12065
  }
12052
12066
  }
12053
12067
  function createLazyBunSql() {
12054
- return new Proxy({}, {
12068
+ return new Proxy(function lazyBunSql() {}, {
12055
12069
  get(_target, prop) {
12056
12070
  const sql = getOrCreateBunSql();
12057
12071
  const value = sql[prop];
@@ -12066,20 +12080,10 @@ function createLazyBunSql() {
12066
12080
  }
12067
12081
  });
12068
12082
  }
12069
- var _bunSqlInstance = null, _currentDialect = null, _currentDatabase = null, bunSql;
12083
+ var _bunSqlInstance = null, _currentSignature = null, bunSql;
12070
12084
  var init_db = __esm(() => {
12071
12085
  init_config();
12072
12086
  bunSql = createLazyBunSql();
12073
- if (typeof process19 !== "undefined" && process19.on) {
12074
- const existingHandler = process19.listeners("unhandledRejection").find((h) => h.name === "sqlConnectionErrorHandler");
12075
- if (!existingHandler) {
12076
- let sqlConnectionErrorHandler = function(reason) {
12077
- if (reason && (reason.message?.includes("database") || reason.message?.includes("does not exist") || reason.code === "ERR_POSTGRES_SERVER_ERROR" || reason.code === "3D000")) {}
12078
- };
12079
- Object.defineProperty(sqlConnectionErrorHandler, "name", { value: "sqlConnectionErrorHandler" });
12080
- process19.on("unhandledRejection", sqlConnectionErrorHandler);
12081
- }
12082
- }
12083
12087
  });
12084
12088
 
12085
12089
  // src/actions/benchmark.ts
@@ -12329,6 +12333,47 @@ function* iterateAllPivots(meta, options = {}) {
12329
12333
  function isRawExpression(expr) {
12330
12334
  return typeof expr === "object" && expr !== null && "raw" in expr && typeof expr.raw === "string";
12331
12335
  }
12336
+ function quoteInsertIdent(id) {
12337
+ return config5.dialect === "mysql" ? `\`${id.replace(/`/g, "``")}\`` : `"${id.replace(/"/g, '""')}"`;
12338
+ }
12339
+ function buildInsertClause(rows, startIndex = 1) {
12340
+ const cols = Object.keys(rows[0] ?? {});
12341
+ const params = [];
12342
+ let idx = startIndex;
12343
+ const tuples = rows.map((row) => {
12344
+ const phs = cols.map((c) => {
12345
+ params.push(row[c]);
12346
+ return getPlaceholder(idx++);
12347
+ });
12348
+ return `(${phs.join(", ")})`;
12349
+ });
12350
+ return {
12351
+ colsSql: cols.map(quoteInsertIdent).join(", "),
12352
+ valuesSql: tuples.join(", "),
12353
+ params,
12354
+ nextIndex: idx
12355
+ };
12356
+ }
12357
+ function hasSlowQueryHook(h) {
12358
+ return Boolean(h && (h.onSlowQuery || h.slowQueryThresholdMs != null && h.slowQueryThresholdMs >= 0));
12359
+ }
12360
+ function renderSelectColumn(col) {
12361
+ if (typeof col === "string")
12362
+ return col;
12363
+ if (isRawExpression(col))
12364
+ return col.raw;
12365
+ if (col && typeof col === "object") {
12366
+ const anyCol = col;
12367
+ if (typeof anyCol.raw === "function")
12368
+ return String(anyCol.raw());
12369
+ if (typeof anyCol.sql === "string")
12370
+ return anyCol.sql;
12371
+ const str = String(col);
12372
+ if (str !== "[object Object]")
12373
+ return str;
12374
+ }
12375
+ throw new TypeError(`[query-builder] select(): unsupported column ${String(col)} \u2014 pass a column name, a string[], or a SQL fragment (e.g. sql\`count(*) as c\`)`);
12376
+ }
12332
12377
  function validateIdentifier(name, context) {
12333
12378
  if (!SQL_PATTERNS.IDENTIFIER.test(name)) {
12334
12379
  const contextMsg = context ? ` in ${context}` : "";
@@ -12543,20 +12588,35 @@ function createQueryBuilder(state) {
12543
12588
  config5.debug.captureText = prev;
12544
12589
  return s;
12545
12590
  }
12591
+ function computeParams(q) {
12592
+ if (!q || typeof q !== "object")
12593
+ return;
12594
+ if (Array.isArray(q.values))
12595
+ return q.values;
12596
+ if (Array.isArray(q.parameters))
12597
+ return q.parameters;
12598
+ if (Array.isArray(q.params))
12599
+ return q.params;
12600
+ return;
12601
+ }
12546
12602
  function runWithHooks(q, kind, opts) {
12547
12603
  const hooks = config5.hooks;
12548
- const hasHooks = hooks && (hooks.onQueryStart || hooks.onQueryEnd || hooks.onQueryError || hooks.startSpan);
12604
+ const slowMs = hooks?.slowQueryThresholdMs;
12605
+ const slowEnabled = slowMs != null && slowMs >= 0;
12606
+ const hasSlowQuery = Boolean(hooks?.onSlowQuery || slowEnabled);
12607
+ const hasHooks = hooks && (hooks.onQueryStart || hooks.onQueryEnd || hooks.onQueryError || hooks.startSpan || hasSlowQuery);
12549
12608
  const hasTimeoutOrSignal = opts?.timeoutMs && opts.timeoutMs > 0 || opts?.signal;
12550
12609
  if (!hasHooks && !hasTimeoutOrSignal) {
12551
12610
  return q.execute();
12552
12611
  }
12553
12612
  const text = computeSqlText(q);
12613
+ const params = computeParams(q);
12554
12614
  const startAt = Date.now();
12555
12615
  let span;
12556
12616
  try {
12557
- hooks?.onQueryStart?.({ sql: text, kind });
12617
+ hooks?.onQueryStart?.({ sql: text, params, kind });
12558
12618
  if (hooks?.startSpan)
12559
- span = hooks.startSpan({ sql: text, kind });
12619
+ span = hooks.startSpan({ sql: text, params, kind });
12560
12620
  } catch {}
12561
12621
  let finished = false;
12562
12622
  const finish = (err, rowCount) => {
@@ -12566,9 +12626,15 @@ function createQueryBuilder(state) {
12566
12626
  const durationMs = Date.now() - startAt;
12567
12627
  try {
12568
12628
  if (err) {
12569
- hooks?.onQueryError?.({ sql: text, error: err, durationMs, kind });
12629
+ hooks?.onQueryError?.({ sql: text, params, error: err, durationMs, kind });
12570
12630
  } else {
12571
- hooks?.onQueryEnd?.({ sql: text, durationMs, rowCount, kind });
12631
+ hooks?.onQueryEnd?.({ sql: text, params, durationMs, rowCount, kind });
12632
+ if (slowEnabled && durationMs >= slowMs) {
12633
+ if (hooks?.onSlowQuery)
12634
+ hooks.onSlowQuery({ sql: text, params, durationMs, kind });
12635
+ else
12636
+ console.warn(`[query-builder] slow query (${durationMs}ms >= ${slowMs}ms): ${text}`);
12637
+ }
12572
12638
  }
12573
12639
  } catch {}
12574
12640
  try {
@@ -12648,6 +12714,36 @@ function createQueryBuilder(state) {
12648
12714
  const p = hasWhere ? prefix : "WHERE";
12649
12715
  text = `${text} ${p} ${clause}`;
12650
12716
  };
12717
+ const appendSetOp = (op, other) => {
12718
+ const st = other.__rawState?.();
12719
+ if (st) {
12720
+ const offset = whereParams.length;
12721
+ const otherSql = config5.dialect === "postgres" ? st.sql.replace(/\$(\d+)/g, (_m, n) => `$${Number(n) + offset}`) : st.sql;
12722
+ text += ` ${op} ${otherSql}`;
12723
+ whereParams.push(...st.params);
12724
+ } else {
12725
+ text += ` ${op} ${String(other.toSQL())}`;
12726
+ }
12727
+ built = null;
12728
+ };
12729
+ const insertJoin = (joinClause) => {
12730
+ const re = /\(|\)|\b(?:WHERE|GROUP BY|HAVING|ORDER BY|LIMIT|OFFSET|UNION)\b/gi;
12731
+ let depth = 0;
12732
+ let cut = -1;
12733
+ let mm;
12734
+ while (mm = re.exec(text)) {
12735
+ if (mm[0] === "(") {
12736
+ depth++;
12737
+ } else if (mm[0] === ")") {
12738
+ depth = Math.max(0, depth - 1);
12739
+ } else if (depth === 0) {
12740
+ cut = mm.index;
12741
+ break;
12742
+ }
12743
+ }
12744
+ text = cut >= 0 ? `${text.slice(0, cut)}${joinClause} ${text.slice(cut)}` : `${text} ${joinClause}`;
12745
+ built = null;
12746
+ };
12651
12747
  const joinedTables = new Set;
12652
12748
  let timeoutMs;
12653
12749
  let abortSignal;
@@ -12732,6 +12828,18 @@ function createQueryBuilder(state) {
12732
12828
  }
12733
12829
  }
12734
12830
  };
12831
+ const buildOverClause = (partitionBy, orderBy) => {
12832
+ const cols = Array.isArray(partitionBy) ? partitionBy : partitionBy ? [partitionBy] : [];
12833
+ const parts = [];
12834
+ if (cols.length)
12835
+ parts.push(`PARTITION BY ${cols.join(", ")}`);
12836
+ if (orderBy && orderBy.length)
12837
+ parts.push(`ORDER BY ${orderBy.map(([c, d]) => `${c} ${d === "desc" ? "DESC" : "ASC"}`).join(", ")}`);
12838
+ return parts.length ? `OVER (${parts.join(" ")})` : "OVER ()";
12839
+ };
12840
+ const addWindowFunction = (fnExpr, alias, partitionBy, orderBy) => {
12841
+ addToSelectClause(`${fnExpr} ${buildOverClause(partitionBy, orderBy)} AS ${alias}`);
12842
+ };
12735
12843
  function assertSafeWhereOperator(op, context) {
12736
12844
  if (typeof op !== "string")
12737
12845
  throw new TypeError(`[query-builder] ${context}: operator must be a string, got ${typeof op}`);
@@ -12871,6 +12979,46 @@ function createQueryBuilder(state) {
12871
12979
  validateIdentifier(fkA, "withCount (foreign key)");
12872
12980
  return `(SELECT COUNT(*) FROM ${pivot} WHERE ${pivot}.${fkA} = ${parentTable}.${pk})`;
12873
12981
  };
12982
+ const applyRelationAggregate = (fn, relation, column) => {
12983
+ if (!meta)
12984
+ return;
12985
+ validateIdentifier(column, `with${fn[0]}${fn.slice(1).toLowerCase()} (column)`);
12986
+ const parentTable = String(table);
12987
+ const rels = meta.relations?.[parentTable];
12988
+ if (!rels)
12989
+ return;
12990
+ const found = Object.entries(rels).find(([_t, relMap2]) => relMap2 && typeof relMap2 === "object" && (relation in relMap2));
12991
+ if (!found)
12992
+ return;
12993
+ const [type, relMap] = found;
12994
+ const entry = relMap[relation];
12995
+ const targetModel = typeof entry === "string" ? entry : entry?.model || entry?.target || entry;
12996
+ const targetTable = meta.modelToTable[targetModel] || targetModel;
12997
+ const pk = meta.primaryKeys[parentTable] ?? "id";
12998
+ validateIdentifier(targetTable, `with${fn} (target table)`);
12999
+ const aggExpr = `${fn}(${targetTable}.${column})`;
13000
+ let sub;
13001
+ if (type === "hasMany" || type === "hasOne") {
13002
+ const fk = `${parentTable.endsWith("s") ? parentTable.slice(0, -1) : parentTable}_id`;
13003
+ validateIdentifier(fk, `with${fn} (foreign key)`);
13004
+ sub = `(SELECT ${aggExpr} FROM ${targetTable} WHERE ${targetTable}.${fk} = ${parentTable}.${pk})`;
13005
+ } else if (type === "belongsToMany") {
13006
+ const resolved = meta ? resolvePivot(meta, parentTable, relation, { singularize, models: meta.models }) : null;
13007
+ const a = singularize(parentTable);
13008
+ const b = singularize(targetTable);
13009
+ const pivot = resolved?.pivotTable ?? [a, b].sort().join("_");
13010
+ const fkA = resolved?.fkParent ?? `${a}_id`;
13011
+ const fkB = resolved?.fkRelated ?? `${b}_id`;
13012
+ const targetPk = meta.primaryKeys[targetTable] ?? "id";
13013
+ validateIdentifier(pivot, `with${fn} (pivot table)`);
13014
+ validateIdentifier(fkA, `with${fn} (foreign key)`);
13015
+ validateIdentifier(fkB, `with${fn} (related key)`);
13016
+ sub = `(SELECT ${aggExpr} FROM ${pivot} JOIN ${targetTable} ON ${targetTable}.${targetPk} = ${pivot}.${fkB} WHERE ${pivot}.${fkA} = ${parentTable}.${pk})`;
13017
+ } else {
13018
+ return;
13019
+ }
13020
+ addToSelectClause(`${sub} AS ${relation}_${fn.toLowerCase()}_${column}`);
13021
+ };
12874
13022
  const applyPivotColumnsToQuery = () => {
12875
13023
  if (pivotColumns.size === 0)
12876
13024
  return;
@@ -12965,6 +13113,52 @@ function createQueryBuilder(state) {
12965
13113
  built = null;
12966
13114
  return this;
12967
13115
  },
13116
+ over(expression, alias, opts = {}) {
13117
+ addWindowFunction(expression, alias, opts.partitionBy, opts.orderBy);
13118
+ return this;
13119
+ },
13120
+ lag(column, opts = {}) {
13121
+ const args = [column, String(opts.offset ?? 1)];
13122
+ if (opts.defaultValue !== undefined)
13123
+ args.push(String(opts.defaultValue));
13124
+ addWindowFunction(`LAG(${args.join(", ")})`, opts.alias ?? `${column}_lag`, opts.partitionBy, opts.orderBy);
13125
+ return this;
13126
+ },
13127
+ lead(column, opts = {}) {
13128
+ const args = [column, String(opts.offset ?? 1)];
13129
+ if (opts.defaultValue !== undefined)
13130
+ args.push(String(opts.defaultValue));
13131
+ addWindowFunction(`LEAD(${args.join(", ")})`, opts.alias ?? `${column}_lead`, opts.partitionBy, opts.orderBy);
13132
+ return this;
13133
+ },
13134
+ sumOver(column, opts = {}) {
13135
+ addWindowFunction(`SUM(${column})`, opts.alias ?? `${column}_sum`, opts.partitionBy, opts.orderBy);
13136
+ return this;
13137
+ },
13138
+ avgOver(column, opts = {}) {
13139
+ addWindowFunction(`AVG(${column})`, opts.alias ?? `${column}_avg`, opts.partitionBy, opts.orderBy);
13140
+ return this;
13141
+ },
13142
+ countOver(column = "*", opts = {}) {
13143
+ addWindowFunction(`COUNT(${column})`, opts.alias ?? "count_over", opts.partitionBy, opts.orderBy);
13144
+ return this;
13145
+ },
13146
+ minOver(column, opts = {}) {
13147
+ addWindowFunction(`MIN(${column})`, opts.alias ?? `${column}_min`, opts.partitionBy, opts.orderBy);
13148
+ return this;
13149
+ },
13150
+ maxOver(column, opts = {}) {
13151
+ addWindowFunction(`MAX(${column})`, opts.alias ?? `${column}_max`, opts.partitionBy, opts.orderBy);
13152
+ return this;
13153
+ },
13154
+ firstValue(column, opts = {}) {
13155
+ addWindowFunction(`FIRST_VALUE(${column})`, opts.alias ?? `${column}_first`, opts.partitionBy, opts.orderBy);
13156
+ return this;
13157
+ },
13158
+ lastValue(column, opts = {}) {
13159
+ addWindowFunction(`LAST_VALUE(${column})`, opts.alias ?? `${column}_last`, opts.partitionBy, opts.orderBy);
13160
+ return this;
13161
+ },
12968
13162
  selectAll() {
12969
13163
  return this;
12970
13164
  },
@@ -12974,22 +13168,24 @@ function createQueryBuilder(state) {
12974
13168
  const cols = Array.isArray(columns2) ? columns2 : [columns2];
12975
13169
  if (cols.length === 0)
12976
13170
  return this;
13171
+ const rendered = cols.map(renderSelectColumn);
12977
13172
  const fromIndex = text.indexOf(" FROM ");
12978
13173
  if (fromIndex !== -1) {
12979
- text = `SELECT ${cols.join(", ")}${text.substring(fromIndex)}`;
13174
+ text = `SELECT ${rendered.join(", ")}${text.substring(fromIndex)}`;
12980
13175
  } else {
12981
- text = `SELECT ${cols.join(", ")} FROM ${table}`;
13176
+ text = `SELECT ${rendered.join(", ")} FROM ${table}`;
12982
13177
  }
12983
13178
  return this;
12984
13179
  },
12985
13180
  addSelect(...columns2) {
12986
13181
  if (!columns2.length)
12987
13182
  return this;
13183
+ const rendered = columns2.map(renderSelectColumn);
12988
13184
  const fromIdx = text.indexOf(" FROM ");
12989
13185
  if (fromIdx !== -1) {
12990
- text = `${text.substring(0, fromIdx)}, ${columns2.join(", ")}${text.substring(fromIdx)}`;
13186
+ text = `${text.substring(0, fromIdx)}, ${rendered.join(", ")}${text.substring(fromIdx)}`;
12991
13187
  } else {
12992
- text += `, ${columns2.join(", ")}`;
13188
+ text += `, ${rendered.join(", ")}`;
12993
13189
  }
12994
13190
  built = null;
12995
13191
  return this;
@@ -13406,6 +13602,22 @@ function createQueryBuilder(state) {
13406
13602
  }
13407
13603
  return this;
13408
13604
  },
13605
+ withSum(relation, column) {
13606
+ applyRelationAggregate("SUM", relation, column);
13607
+ return this;
13608
+ },
13609
+ withAvg(relation, column) {
13610
+ applyRelationAggregate("AVG", relation, column);
13611
+ return this;
13612
+ },
13613
+ withMax(relation, column) {
13614
+ applyRelationAggregate("MAX", relation, column);
13615
+ return this;
13616
+ },
13617
+ withMin(relation, column) {
13618
+ applyRelationAggregate("MIN", relation, column);
13619
+ return this;
13620
+ },
13409
13621
  applyPivotColumns() {
13410
13622
  applyPivotColumnsToQuery();
13411
13623
  return this;
@@ -13617,8 +13829,9 @@ function createQueryBuilder(state) {
13617
13829
  },
13618
13830
  whereBetween(column, start, end) {
13619
13831
  const keyword = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
13832
+ const i = whereParams.length + 1;
13833
+ text = `${text} ${keyword} ${String(column)} BETWEEN ${getPlaceholder(i)} AND ${getPlaceholder(i + 1)}`;
13620
13834
  whereParams.push(start, end);
13621
- text = `${text} ${keyword} ${String(column)} BETWEEN ? AND ?`;
13622
13835
  built = null;
13623
13836
  return this;
13624
13837
  },
@@ -13630,9 +13843,30 @@ function createQueryBuilder(state) {
13630
13843
  },
13631
13844
  whereJsonContains(column, json) {
13632
13845
  const keyword = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
13846
+ const dialect = config5.dialect;
13633
13847
  const idx = whereParams.length + 1;
13634
- text += ` ${keyword} ${column} @> ${getPlaceholder(idx)}`;
13635
- whereParams.push(JSON.stringify(json));
13848
+ if (dialect === "postgres") {
13849
+ if (config5.sql?.jsonContainsMode === "function")
13850
+ text += ` ${keyword} jsonb_contains(${column}, ${getPlaceholder(idx)})`;
13851
+ else
13852
+ text += ` ${keyword} ${column} @> ${getPlaceholder(idx)}`;
13853
+ whereParams.push(JSON.stringify(json));
13854
+ } else if (dialect === "mysql") {
13855
+ text += ` ${keyword} JSON_CONTAINS(${column}, ${getPlaceholder(idx)})`;
13856
+ whereParams.push(JSON.stringify(json));
13857
+ } else {
13858
+ if (Array.isArray(json)) {
13859
+ const conds = json.map((_, i) => `EXISTS (SELECT 1 FROM json_each(${column}) WHERE json_each.value = ${getPlaceholder(idx + i)})`);
13860
+ text += ` ${keyword} (${conds.join(" AND ")})`;
13861
+ for (const v of json)
13862
+ whereParams.push(v);
13863
+ } else if (json !== null && typeof json === "object") {
13864
+ throw new Error("[query-builder] whereJsonContains: object containment is not supported on SQLite \u2014 pass a scalar or array, or use whereJsonPath.");
13865
+ } else {
13866
+ text += ` ${keyword} EXISTS (SELECT 1 FROM json_each(${column}) WHERE json_each.value = ${getPlaceholder(idx)})`;
13867
+ whereParams.push(json);
13868
+ }
13869
+ }
13636
13870
  built = null;
13637
13871
  return this;
13638
13872
  },
@@ -13659,69 +13893,85 @@ function createQueryBuilder(state) {
13659
13893
  whereLike(column, pattern, caseSensitive = false) {
13660
13894
  const expr = caseSensitive ? sql`${sql(String(column))} LIKE ${pattern}` : sql`LOWER(${sql(String(column))}) LIKE LOWER(${pattern})`;
13661
13895
  built = sql`${ensureBuilt()} WHERE ${expr}`;
13662
- addWhereText("WHERE", `${caseSensitive ? String(column) : `LOWER(${String(column)})`} LIKE ${caseSensitive ? "?" : "LOWER(?)"}`);
13896
+ const ph = getPlaceholder(whereParams.length + 1);
13897
+ addWhereText("WHERE", `${caseSensitive ? String(column) : `LOWER(${String(column)})`} LIKE ${caseSensitive ? ph : `LOWER(${ph})`}`);
13898
+ whereParams.push(pattern);
13663
13899
  return this;
13664
13900
  },
13665
13901
  whereILike(column, pattern) {
13902
+ const ph = getPlaceholder(whereParams.length + 1);
13666
13903
  if (config5.dialect === "postgres") {
13667
13904
  built = sql`${ensureBuilt()} WHERE ${sql(String(column))} ILIKE ${pattern}`;
13668
- addWhereText("WHERE", `${String(column)} ILIKE ?`);
13905
+ addWhereText("WHERE", `${String(column)} ILIKE ${ph}`);
13669
13906
  } else {
13670
13907
  const expr = sql`LOWER(${sql(String(column))}) LIKE LOWER(${pattern})`;
13671
13908
  built = sql`${ensureBuilt()} WHERE ${expr}`;
13672
- addWhereText("WHERE", `LOWER(${String(column)}) LIKE LOWER(?)`);
13909
+ addWhereText("WHERE", `LOWER(${String(column)}) LIKE LOWER(${ph})`);
13673
13910
  }
13911
+ whereParams.push(pattern);
13674
13912
  return this;
13675
13913
  },
13676
13914
  orWhereLike(column, pattern, caseSensitive = false) {
13677
13915
  const expr = caseSensitive ? sql`${sql(String(column))} LIKE ${pattern}` : sql`LOWER(${sql(String(column))}) LIKE LOWER(${pattern})`;
13678
13916
  built = sql`${ensureBuilt()} OR ${expr}`;
13679
- addWhereText("OR", `${caseSensitive ? String(column) : `LOWER(${String(column)})`} LIKE ${caseSensitive ? "?" : "LOWER(?)"}`);
13917
+ const ph = getPlaceholder(whereParams.length + 1);
13918
+ addWhereText("OR", `${caseSensitive ? String(column) : `LOWER(${String(column)})`} LIKE ${caseSensitive ? ph : `LOWER(${ph})`}`);
13919
+ whereParams.push(pattern);
13680
13920
  return this;
13681
13921
  },
13682
13922
  orWhereILike(column, pattern) {
13923
+ const ph = getPlaceholder(whereParams.length + 1);
13683
13924
  if (config5.dialect === "postgres") {
13684
13925
  built = sql`${ensureBuilt()} OR ${sql(String(column))} ILIKE ${pattern}`;
13685
- addWhereText("OR", `${String(column)} ILIKE ?`);
13926
+ addWhereText("OR", `${String(column)} ILIKE ${ph}`);
13686
13927
  } else {
13687
13928
  const expr = sql`LOWER(${sql(String(column))}) LIKE LOWER(${pattern})`;
13688
13929
  built = sql`${ensureBuilt()} OR ${expr}`;
13689
- addWhereText("OR", `LOWER(${String(column)}) LIKE LOWER(?)`);
13930
+ addWhereText("OR", `LOWER(${String(column)}) LIKE LOWER(${ph})`);
13690
13931
  }
13932
+ whereParams.push(pattern);
13691
13933
  return this;
13692
13934
  },
13693
13935
  whereNotLike(column, pattern, caseSensitive = false) {
13694
13936
  const expr = caseSensitive ? sql`${sql(String(column))} NOT LIKE ${pattern}` : sql`LOWER(${sql(String(column))}) NOT LIKE LOWER(${pattern})`;
13695
13937
  built = sql`${ensureBuilt()} WHERE ${expr}`;
13696
- addWhereText("WHERE", `${caseSensitive ? String(column) : `LOWER(${String(column)})`} NOT LIKE ${caseSensitive ? "?" : "LOWER(?)"}`);
13938
+ const ph = getPlaceholder(whereParams.length + 1);
13939
+ addWhereText("WHERE", `${caseSensitive ? String(column) : `LOWER(${String(column)})`} NOT LIKE ${caseSensitive ? ph : `LOWER(${ph})`}`);
13940
+ whereParams.push(pattern);
13697
13941
  return this;
13698
13942
  },
13699
13943
  whereNotILike(column, pattern) {
13944
+ const ph = getPlaceholder(whereParams.length + 1);
13700
13945
  if (config5.dialect === "postgres") {
13701
13946
  built = sql`${ensureBuilt()} WHERE ${sql(String(column))} NOT ILIKE ${pattern}`;
13702
- addWhereText("WHERE", `${String(column)} NOT ILIKE ?`);
13947
+ addWhereText("WHERE", `${String(column)} NOT ILIKE ${ph}`);
13703
13948
  } else {
13704
13949
  const expr = sql`LOWER(${sql(String(column))}) NOT LIKE LOWER(${pattern})`;
13705
13950
  built = sql`${ensureBuilt()} WHERE ${expr}`;
13706
- addWhereText("WHERE", `LOWER(${String(column)}) NOT LIKE LOWER(?)`);
13951
+ addWhereText("WHERE", `LOWER(${String(column)}) NOT LIKE LOWER(${ph})`);
13707
13952
  }
13953
+ whereParams.push(pattern);
13708
13954
  return this;
13709
13955
  },
13710
13956
  orWhereNotLike(column, pattern, caseSensitive = false) {
13711
13957
  const expr = caseSensitive ? sql`${sql(String(column))} NOT LIKE ${pattern}` : sql`LOWER(${sql(String(column))}) NOT LIKE LOWER(${pattern})`;
13712
13958
  built = sql`${ensureBuilt()} OR ${expr}`;
13713
- addWhereText("OR", `${caseSensitive ? String(column) : `LOWER(${String(column)})`} NOT LIKE ${caseSensitive ? "?" : "LOWER(?)"}`);
13959
+ const ph = getPlaceholder(whereParams.length + 1);
13960
+ addWhereText("OR", `${caseSensitive ? String(column) : `LOWER(${String(column)})`} NOT LIKE ${caseSensitive ? ph : `LOWER(${ph})`}`);
13961
+ whereParams.push(pattern);
13714
13962
  return this;
13715
13963
  },
13716
13964
  orWhereNotILike(column, pattern) {
13965
+ const ph = getPlaceholder(whereParams.length + 1);
13717
13966
  if (config5.dialect === "postgres") {
13718
13967
  built = sql`${ensureBuilt()} OR ${sql(String(column))} NOT ILIKE ${pattern}`;
13719
- addWhereText("OR", `${String(column)} NOT ILIKE ?`);
13968
+ addWhereText("OR", `${String(column)} NOT ILIKE ${ph}`);
13720
13969
  } else {
13721
13970
  const expr = sql`LOWER(${sql(String(column))}) NOT LIKE LOWER(${pattern})`;
13722
13971
  built = sql`${ensureBuilt()} OR ${expr}`;
13723
- addWhereText("OR", `LOWER(${String(column)}) NOT LIKE LOWER(?)`);
13972
+ addWhereText("OR", `LOWER(${String(column)}) NOT LIKE LOWER(${ph})`);
13724
13973
  }
13974
+ whereParams.push(pattern);
13725
13975
  return this;
13726
13976
  },
13727
13977
  whereAny(cols, op, value) {
@@ -13762,7 +14012,8 @@ function createQueryBuilder(state) {
13762
14012
  },
13763
14013
  whereNotBetween(column, start, end) {
13764
14014
  const keyword = SQL_PATTERNS.WHERE.test(text) ? "AND" : "WHERE";
13765
- text += ` ${keyword} ${column} NOT BETWEEN ? AND ?`;
14015
+ const i = whereParams.length + 1;
14016
+ text += ` ${keyword} ${column} NOT BETWEEN ${getPlaceholder(i)} AND ${getPlaceholder(i + 1)}`;
13766
14017
  whereParams.push(start, end);
13767
14018
  built = null;
13768
14019
  return this;
@@ -14030,7 +14281,7 @@ function createQueryBuilder(state) {
14030
14281
  return this;
14031
14282
  },
14032
14283
  join(table2, onLeft, operator, onRight) {
14033
- text = `${text} JOIN ${table2} ON ${onLeft} ${operator} ${onRight}`;
14284
+ insertJoin(`JOIN ${table2} ON ${onLeft} ${operator} ${onRight}`);
14034
14285
  joinedTables.add(table2);
14035
14286
  return this;
14036
14287
  },
@@ -14039,44 +14290,37 @@ function createQueryBuilder(state) {
14039
14290
  validateQualifiedIdentifier(onLeft, "joinSub(onLeft)");
14040
14291
  validateQualifiedIdentifier(onRight, "joinSub(onRight)");
14041
14292
  assertSafeWhereOperator(operator, "joinSub(operator)");
14042
- text += ` JOIN (${String(sub.toSQL())}) AS ${alias} ON ${onLeft} ${operator} ${onRight}`;
14043
- built = null;
14293
+ insertJoin(`JOIN (${String(sub.toSQL())}) AS ${alias} ON ${onLeft} ${operator} ${onRight}`);
14044
14294
  joinedTables.add(alias);
14045
14295
  return this;
14046
14296
  },
14047
14297
  innerJoin(table2, onLeft, operator, onRight) {
14048
- text = `${text} INNER JOIN ${table2} ON ${onLeft} ${operator} ${onRight}`;
14049
- built = null;
14298
+ insertJoin(`INNER JOIN ${table2} ON ${onLeft} ${operator} ${onRight}`);
14050
14299
  joinedTables.add(table2);
14051
14300
  return this;
14052
14301
  },
14053
14302
  leftJoin(table2, onLeft, operator, onRight) {
14054
- text = `${text} LEFT JOIN ${table2} ON ${onLeft} ${operator} ${onRight}`;
14055
- built = null;
14303
+ insertJoin(`LEFT JOIN ${table2} ON ${onLeft} ${operator} ${onRight}`);
14056
14304
  joinedTables.add(table2);
14057
14305
  return this;
14058
14306
  },
14059
14307
  leftJoinSub(sub, alias, onLeft, operator, onRight) {
14060
- text += ` LEFT JOIN (${String(sub.toSQL())}) AS ${alias} ON ${onLeft} ${operator} ${onRight}`;
14061
- built = null;
14308
+ insertJoin(`LEFT JOIN (${String(sub.toSQL())}) AS ${alias} ON ${onLeft} ${operator} ${onRight}`);
14062
14309
  joinedTables.add(alias);
14063
14310
  return this;
14064
14311
  },
14065
14312
  rightJoin(table2, onLeft, operator, onRight) {
14066
- text = `${text} RIGHT JOIN ${table2} ON ${onLeft} ${operator} ${onRight}`;
14067
- built = null;
14313
+ insertJoin(`RIGHT JOIN ${table2} ON ${onLeft} ${operator} ${onRight}`);
14068
14314
  joinedTables.add(table2);
14069
14315
  return this;
14070
14316
  },
14071
14317
  crossJoin(table2) {
14072
- text = `${text} CROSS JOIN ${table2}`;
14073
- built = null;
14318
+ insertJoin(`CROSS JOIN ${table2}`);
14074
14319
  joinedTables.add(table2);
14075
14320
  return this;
14076
14321
  },
14077
14322
  crossJoinSub(sub, alias) {
14078
- text += ` CROSS JOIN (${String(sub.toSQL())}) AS ${alias}`;
14079
- built = null;
14323
+ insertJoin(`CROSS JOIN (${String(sub.toSQL())}) AS ${alias}`);
14080
14324
  joinedTables.add(alias);
14081
14325
  return this;
14082
14326
  },
@@ -14129,9 +14373,10 @@ function createQueryBuilder(state) {
14129
14373
  return this;
14130
14374
  },
14131
14375
  having(expr) {
14376
+ const kw = /\bHAVING\b/i.test(text) ? "AND" : "HAVING";
14132
14377
  if (Array.isArray(expr)) {
14133
14378
  const paramIdx = whereParams.length + 1;
14134
- text = `${text} HAVING ${expr[0]} ${expr[1]} ${getPlaceholder(paramIdx)}`;
14379
+ text = `${text} ${kw} ${expr[0]} ${expr[1]} ${getPlaceholder(paramIdx)}`;
14135
14380
  whereParams.push(expr[2]);
14136
14381
  built = null;
14137
14382
  } else if (expr && typeof expr === "object" && !("raw" in expr)) {
@@ -14145,18 +14390,19 @@ function createQueryBuilder(state) {
14145
14390
  conditions[i] = `${key} = ${getPlaceholder(baseIdx + i + 1)}`;
14146
14391
  whereParams.push(expr[key]);
14147
14392
  }
14148
- text = `${text} HAVING ${conditions.join(" AND ")}`;
14393
+ text = `${text} ${kw} ${conditions.join(" AND ")}`;
14149
14394
  built = null;
14150
14395
  }
14151
14396
  } else if (expr && typeof expr.raw !== "undefined") {
14152
- text += ` HAVING ${expr.raw}`;
14397
+ text += ` ${kw} ${expr.raw}`;
14153
14398
  built = null;
14154
14399
  }
14155
14400
  return this;
14156
14401
  },
14157
14402
  havingRaw(fragment) {
14158
14403
  assertSqlFragment(fragment, "havingRaw(fragment)");
14159
- text += ` HAVING ${String(fragment)}`;
14404
+ const kw = /\bHAVING\b/i.test(text) ? "AND" : "HAVING";
14405
+ text += ` ${kw} ${String(fragment)}`;
14160
14406
  built = null;
14161
14407
  return this;
14162
14408
  },
@@ -14167,13 +14413,27 @@ function createQueryBuilder(state) {
14167
14413
  return this;
14168
14414
  },
14169
14415
  union(other) {
14170
- text += ` UNION ${String(other.toSQL())}`;
14171
- built = null;
14416
+ appendSetOp("UNION", other);
14172
14417
  return this;
14173
14418
  },
14174
14419
  unionAll(other) {
14175
- text += ` UNION ALL ${String(other.toSQL())}`;
14176
- built = null;
14420
+ appendSetOp("UNION ALL", other);
14421
+ return this;
14422
+ },
14423
+ intersect(other) {
14424
+ appendSetOp("INTERSECT", other);
14425
+ return this;
14426
+ },
14427
+ intersectAll(other) {
14428
+ appendSetOp("INTERSECT ALL", other);
14429
+ return this;
14430
+ },
14431
+ except(other) {
14432
+ appendSetOp("EXCEPT", other);
14433
+ return this;
14434
+ },
14435
+ exceptAll(other) {
14436
+ appendSetOp("EXCEPT ALL", other);
14177
14437
  return this;
14178
14438
  },
14179
14439
  forPage(page, perPage) {
@@ -14217,11 +14477,22 @@ function createQueryBuilder(state) {
14217
14477
  const e = await this.exists();
14218
14478
  return !e;
14219
14479
  },
14220
- async paginate(perPage, page = 1) {
14480
+ async paginate(perPage, page = 1, opts = {}) {
14221
14481
  if (!Number.isFinite(perPage) || perPage <= 0 || !Number.isInteger(perPage))
14222
14482
  throw new TypeError(`[query-builder] paginate(perPage): expected positive integer, got ${perPage}`);
14223
14483
  if (!Number.isFinite(page) || page < 1 || !Number.isInteger(page))
14224
14484
  throw new TypeError(`[query-builder] paginate(page): expected integer >= 1, got ${page}`);
14485
+ if (opts.tx) {
14486
+ const baseSql = reorderSelectClauses(text);
14487
+ const baseParams = [...whereParams];
14488
+ const cRows2 = await opts.tx.unsafe(`SELECT COUNT(*) as c FROM (${baseSql}) as sub`, baseParams);
14489
+ const total2 = Number(cRows2?.[0]?.c ?? 0);
14490
+ const lastPage2 = Math.max(1, Math.ceil(total2 / perPage));
14491
+ const p2 = Math.max(1, Math.min(page, lastPage2));
14492
+ const offset2 = (p2 - 1) * perPage;
14493
+ const data2 = await opts.tx.unsafe(`${baseSql} LIMIT ${perPage} OFFSET ${offset2}`, baseParams);
14494
+ return { data: data2, meta: { perPage, page: p2, total: total2, lastPage: lastPage2 } };
14495
+ }
14225
14496
  const countQ = sql`SELECT COUNT(*) as c FROM (${ensureBuilt()}) as sub`;
14226
14497
  const cRows = await runWithHooks(countQ, "select", { signal: abortSignal, timeoutMs });
14227
14498
  const [cRow] = cRows;
@@ -14378,7 +14649,7 @@ function createQueryBuilder(state) {
14378
14649
  },
14379
14650
  async get() {
14380
14651
  const hooks = config5.hooks;
14381
- const hasQueryHooks = hooks && (hooks.onQueryStart || hooks.onQueryEnd || hooks.onQueryError || hooks.startSpan);
14652
+ const hasQueryHooks = hooks && (hooks.onQueryStart || hooks.onQueryEnd || hooks.onQueryError || hooks.startSpan || hasSlowQueryHook(hooks));
14382
14653
  if (!config5.softDeletes?.enabled && !useCache && !timeoutMs && !abortSignal && !hasQueryHooks) {
14383
14654
  const prepareFn = _sql._prepareStatement;
14384
14655
  if (prepareFn) {
@@ -14433,7 +14704,7 @@ function createQueryBuilder(state) {
14433
14704
  },
14434
14705
  async first() {
14435
14706
  const fHooks = config5.hooks;
14436
- const fHasQueryHooks = fHooks && (fHooks.onQueryStart || fHooks.onQueryEnd || fHooks.onQueryError || fHooks.startSpan);
14707
+ const fHasQueryHooks = fHooks && (fHooks.onQueryStart || fHooks.onQueryEnd || fHooks.onQueryError || fHooks.startSpan || hasSlowQueryHook(fHooks));
14437
14708
  if (!config5.softDeletes?.enabled && !useCache && !timeoutMs && !abortSignal && !fHasQueryHooks) {
14438
14709
  const prepareFn = _sql._prepareStatement;
14439
14710
  if (prepareFn) {
@@ -14515,7 +14786,7 @@ function createQueryBuilder(state) {
14515
14786
  countText = `SELECT COUNT(*) as c FROM ${table}`;
14516
14787
  }
14517
14788
  const cHooks = config5.hooks;
14518
- const cHasHooks = cHooks && (cHooks.onQueryStart || cHooks.onQueryEnd || cHooks.onQueryError || cHooks.startSpan);
14789
+ const cHasHooks = cHooks && (cHooks.onQueryStart || cHooks.onQueryEnd || cHooks.onQueryError || cHooks.startSpan || hasSlowQueryHook(cHooks));
14519
14790
  if (!config5.softDeletes?.enabled && !useCache && !timeoutMs && !abortSignal && !cHasHooks) {
14520
14791
  const prepareFn = _sql._prepareStatement;
14521
14792
  if (prepareFn) {
@@ -14533,7 +14804,7 @@ function createQueryBuilder(state) {
14533
14804
  const fromIdx = text.indexOf(" FROM ");
14534
14805
  const avgText = fromIdx !== -1 ? `SELECT AVG(${column}) as a${text.substring(fromIdx)}` : `SELECT AVG(${column}) as a FROM ${table}`;
14535
14806
  const aHooks = config5.hooks;
14536
- const aHasHooks = aHooks && (aHooks.onQueryStart || aHooks.onQueryEnd || aHooks.onQueryError || aHooks.startSpan);
14807
+ const aHasHooks = aHooks && (aHooks.onQueryStart || aHooks.onQueryEnd || aHooks.onQueryError || aHooks.startSpan || hasSlowQueryHook(aHooks));
14537
14808
  if (!config5.softDeletes?.enabled && !useCache && !timeoutMs && !abortSignal && !aHasHooks) {
14538
14809
  const prepareFn = _sql._prepareStatement;
14539
14810
  if (prepareFn) {
@@ -14603,6 +14874,9 @@ function createQueryBuilder(state) {
14603
14874
  toParams() {
14604
14875
  return ensureBuilt().values?.() ?? [];
14605
14876
  },
14877
+ __rawState() {
14878
+ return { sql: reorderSelectClauses(text), params: [...whereParams] };
14879
+ },
14606
14880
  raw() {
14607
14881
  return ensureBuilt().raw();
14608
14882
  },
@@ -14961,15 +15235,15 @@ function createQueryBuilder(state) {
14961
15235
  params.length = totalParams;
14962
15236
  if (rowCount === 1) {
14963
15237
  if (!isPostgres) {
14964
- let cols = keys[0];
15238
+ let cols = quoteId(keys[0]);
14965
15239
  let placeholders = "?";
14966
15240
  params[0] = firstRow[keys[0]];
14967
15241
  for (let c = 1;c < colCount; c++) {
14968
- cols += `,${keys[c]}`;
15242
+ cols += `,${quoteId(keys[c])}`;
14969
15243
  placeholders += ",?";
14970
15244
  params[c] = firstRow[keys[c]];
14971
15245
  }
14972
- sqlText = `INSERT INTO ${table}(${cols})VALUES(${placeholders})`;
15246
+ sqlText = `INSERT INTO ${quoteId(table)}(${cols})VALUES(${placeholders})`;
14973
15247
  } else {
14974
15248
  const columnList = keys.map((k) => quoteId(k)).join(",");
14975
15249
  sqlText = `INSERT INTO ${quoteId(table)}(${columnList})VALUES(`;
@@ -15027,7 +15301,7 @@ function createQueryBuilder(state) {
15027
15301
  },
15028
15302
  execute() {
15029
15303
  const hooks = config5.hooks;
15030
- const hasHooks = hooks && (hooks.onQueryStart || hooks.onQueryEnd || hooks.onQueryError || hooks.startSpan || hooks.beforeCreate || hooks.afterCreate);
15304
+ const hasHooks = hooks && (hooks.onQueryStart || hooks.onQueryEnd || hooks.onQueryError || hooks.startSpan || hooks.beforeCreate || hooks.afterCreate || hasSlowQueryHook(hooks));
15031
15305
  if (!hasHooks) {
15032
15306
  const prepareFn = _sql._prepareStatement;
15033
15307
  if (prepareFn) {
@@ -15501,55 +15775,72 @@ function createQueryBuilder(state) {
15501
15775
  };
15502
15776
  },
15503
15777
  async insertOrIgnore(table, values) {
15504
- if (config5.dialect === "mysql") {
15505
- const built2 = bunSql`INSERT IGNORE INTO ${bunSql(String(table))} ${bunSql(values)}`;
15506
- return built2.execute();
15507
- }
15508
- const built = bunSql`INSERT INTO ${bunSql(String(table))} ${bunSql(values)} ON CONFLICT DO NOTHING`;
15509
- return built.execute();
15778
+ const rows = Array.isArray(values) ? values : [values];
15779
+ if (!rows.length)
15780
+ return;
15781
+ const { colsSql, valuesSql, params } = buildInsertClause(rows);
15782
+ const tbl = quoteInsertIdent(String(table));
15783
+ const sqlText = config5.dialect === "mysql" ? `INSERT IGNORE INTO ${tbl} (${colsSql}) VALUES ${valuesSql}` : `INSERT INTO ${tbl} (${colsSql}) VALUES ${valuesSql} ON CONFLICT DO NOTHING`;
15784
+ return bunSql.unsafe(sqlText, params).execute();
15510
15785
  },
15511
15786
  async insertGetId(table, values, idColumn = "id") {
15787
+ const { colsSql, valuesSql, params } = buildInsertClause([values]);
15788
+ const tbl = quoteInsertIdent(String(table));
15512
15789
  if (config5.dialect === "mysql") {
15513
- const insertQuery = bunSql`INSERT INTO ${bunSql(String(table))} ${bunSql(values)}`;
15514
- const result = await insertQuery.execute();
15515
- if (result && typeof result === "object" && "insertId" in result) {
15516
- return result.insertId;
15517
- }
15518
- const [lastIdResult] = await bunSql`SELECT LAST_INSERT_ID() as id`.execute();
15519
- return lastIdResult?.id;
15520
- } else {
15521
- const q = bunSql`INSERT INTO ${bunSql(String(table))} ${bunSql(values)} RETURNING ${bunSql(String(idColumn))} as id`;
15522
- const [row] = await q.execute();
15523
- return row?.id;
15524
- }
15790
+ await bunSql.unsafe(`INSERT INTO ${tbl} (${colsSql}) VALUES ${valuesSql}`, params).execute();
15791
+ const [row2] = await bunSql.unsafe(`SELECT LAST_INSERT_ID() as id`).execute();
15792
+ return row2?.id;
15793
+ }
15794
+ if (config5.dialect === "sqlite") {
15795
+ const res = await bunSql.unsafe(`INSERT INTO ${tbl} (${colsSql}) VALUES ${valuesSql}`, params).execute();
15796
+ if (res?.lastInsertRowid != null)
15797
+ return res.lastInsertRowid;
15798
+ const [row2] = await bunSql.unsafe(`SELECT last_insert_rowid() as id`).execute();
15799
+ return row2?.id;
15800
+ }
15801
+ const [row] = await bunSql.unsafe(`INSERT INTO ${tbl} (${colsSql}) VALUES ${valuesSql} RETURNING ${quoteInsertIdent(String(idColumn))} as id`, params).execute();
15802
+ return row?.id;
15525
15803
  },
15526
15804
  async updateOrInsert(table, match, values) {
15527
- const whereParts = Object.keys(match).map((k) => bunSql`${bunSql(String(k))} = ${bunSql(match[k])}`);
15528
- const existsQ = bunSql`SELECT 1 FROM ${bunSql(String(table))} WHERE ${bunSql(whereParts)} LIMIT 1`;
15529
- const existsRows = await existsQ.execute();
15805
+ const tbl = quoteInsertIdent(String(table));
15806
+ const matchKeys = Object.keys(match);
15807
+ let idx = 1;
15808
+ const whereSql = matchKeys.map((k) => `${quoteInsertIdent(k)} = ${getPlaceholder(idx++)}`).join(" AND ");
15809
+ const whereParams = matchKeys.map((k) => match[k]);
15810
+ const existsRows = await bunSql.unsafe(`SELECT 1 FROM ${tbl} WHERE ${whereSql} LIMIT 1`, whereParams).execute();
15530
15811
  if (existsRows.length) {
15531
- const upd = bunSql`UPDATE ${bunSql(String(table))} SET ${bunSql(values)} WHERE ${bunSql(whereParts)}`;
15532
- await upd.execute();
15533
- return true;
15534
- } else {
15535
- const ins = bunSql`INSERT INTO ${bunSql(String(table))} ${bunSql({ ...match, ...values })}`;
15536
- await ins.execute();
15812
+ const setKeys = Object.keys(values);
15813
+ let i = 1;
15814
+ const setSql = setKeys.map((k) => `${quoteInsertIdent(k)} = ${getPlaceholder(i++)}`).join(", ");
15815
+ const whereSql2 = matchKeys.map((k) => `${quoteInsertIdent(k)} = ${getPlaceholder(i++)}`).join(" AND ");
15816
+ const params2 = [...setKeys.map((k) => values[k]), ...matchKeys.map((k) => match[k])];
15817
+ await bunSql.unsafe(`UPDATE ${tbl} SET ${setSql} WHERE ${whereSql2}`, params2).execute();
15537
15818
  return true;
15538
15819
  }
15820
+ const { colsSql, valuesSql, params } = buildInsertClause([{ ...match, ...values }]);
15821
+ await bunSql.unsafe(`INSERT INTO ${tbl} (${colsSql}) VALUES ${valuesSql}`, params).execute();
15822
+ return true;
15539
15823
  },
15540
15824
  async upsert(table, rows, conflictColumns, mergeColumns) {
15541
15825
  const targetCols = conflictColumns.map((c) => String(c));
15542
15826
  const setCols = (mergeColumns ?? []).map((c) => String(c));
15827
+ const list = rows;
15828
+ if (!list.length)
15829
+ return;
15830
+ const { colsSql, valuesSql, params } = buildInsertClause(list);
15831
+ const tbl = quoteInsertIdent(String(table));
15832
+ const insert = `INSERT INTO ${tbl} (${colsSql}) VALUES ${valuesSql}`;
15543
15833
  if (config5.dialect === "mysql") {
15544
- const updateList2 = setCols.map((c) => `\`${c.replace(/`/g, "``")}\` = VALUES(\`${c.replace(/`/g, "``")}\`)`).join(", ");
15545
- const built2 = bunSql`INSERT INTO ${bunSql(String(table))} ${bunSql(rows)} ON DUPLICATE KEY UPDATE ${bunSql.unsafe(updateList2)}`;
15546
- return built2.execute();
15547
- }
15548
- const isPostgres = config5.dialect === "postgres";
15549
- const quoteCol = (column) => isPostgres ? `"${column.replace(/"/g, '""')}"` : `"${column.replace(/"/g, '""')}"`;
15550
- const updateList = setCols.map((column) => `${quoteCol(column)} = EXCLUDED.${quoteCol(column)}`).join(", ");
15551
- const built = bunSql`INSERT INTO ${bunSql(String(table))} ${bunSql(rows)} ON CONFLICT (${bunSql(targetCols)}) DO UPDATE SET ${bunSql.unsafe(updateList)}`;
15552
- return built.execute();
15834
+ if (setCols.length === 0)
15835
+ return bunSql.unsafe(`INSERT IGNORE INTO ${tbl} (${colsSql}) VALUES ${valuesSql}`, params).execute();
15836
+ const updateList2 = setCols.map((c) => `${quoteInsertIdent(c)} = VALUES(${quoteInsertIdent(c)})`).join(", ");
15837
+ return bunSql.unsafe(`${insert} ON DUPLICATE KEY UPDATE ${updateList2}`, params).execute();
15838
+ }
15839
+ const targets = targetCols.map(quoteInsertIdent).join(", ");
15840
+ if (setCols.length === 0)
15841
+ return bunSql.unsafe(`${insert} ON CONFLICT (${targets}) DO NOTHING`, params).execute();
15842
+ const updateList = setCols.map((c) => `${quoteInsertIdent(c)} = EXCLUDED.${quoteInsertIdent(c)}`).join(", ");
15843
+ return bunSql.unsafe(`${insert} ON CONFLICT (${targets}) DO UPDATE SET ${updateList}`, params).execute();
15553
15844
  },
15554
15845
  async save(table, values) {
15555
15846
  const pk = meta?.primaryKeys[String(table)] ?? "id";
@@ -15652,7 +15943,8 @@ function createQueryBuilder(state) {
15652
15943
  const colCount = keys.length;
15653
15944
  const rowCount = rows.length;
15654
15945
  const params = Array.from({ length: rowCount * colCount });
15655
- let sql = `INSERT INTO ${table}(${keys.join(",")})VALUES`;
15946
+ const quoteId = config5.dialect === "mysql" ? (id) => `\`${String(id).replace(/`/g, "``")}\`` : (id) => `"${String(id).replace(/"/g, '""')}"`;
15947
+ let sql = `INSERT INTO ${quoteId(String(table))}(${keys.map(quoteId).join(",")})VALUES`;
15656
15948
  let pidx = 0;
15657
15949
  for (let r = 0;r < rowCount; r++) {
15658
15950
  if (r > 0)
@@ -15959,7 +16251,7 @@ var init_cache = __esm(() => {
15959
16251
  });
15960
16252
 
15961
16253
  // src/actions/console.ts
15962
- import process21 from "process";
16254
+ import process19 from "process";
15963
16255
  import { createInterface } from "readline";
15964
16256
  async function startConsole() {
15965
16257
  console.log("-- Query Builder Interactive Console");
@@ -15968,8 +16260,8 @@ async function startConsole() {
15968
16260
  console.log();
15969
16261
  const qb = createQueryBuilder();
15970
16262
  const rl = createInterface({
15971
- input: process21.stdin,
15972
- output: process21.stdout,
16263
+ input: process19.stdin,
16264
+ output: process19.stdout,
15973
16265
  prompt: "qb> "
15974
16266
  });
15975
16267
  let multilineBuffer = "";
@@ -16020,7 +16312,7 @@ Tips:
16020
16312
  if (cmd === ".exit" || cmd === ".quit") {
16021
16313
  console.log("Goodbye!");
16022
16314
  rl.close();
16023
- process21.exit(0);
16315
+ process19.exit(0);
16024
16316
  } else if (cmd === ".help") {
16025
16317
  console.log(helpText);
16026
16318
  } else if (cmd === ".clear") {
@@ -16091,7 +16383,7 @@ Tips:
16091
16383
  rl.on("close", () => {
16092
16384
  console.log(`
16093
16385
  Goodbye!`);
16094
- process21.exit(0);
16386
+ process19.exit(0);
16095
16387
  });
16096
16388
  rl.prompt();
16097
16389
  }
@@ -16469,9 +16761,9 @@ var init_db_info = __esm(() => {
16469
16761
  });
16470
16762
 
16471
16763
  // src/actions/db-optimize.ts
16472
- import process22 from "process";
16764
+ import process21 from "process";
16473
16765
  async function dbOptimize(options = {}) {
16474
- const dialect = options.dialect || process22.env.DB_DIALECT || "postgres";
16766
+ const dialect = options.dialect || process21.env.DB_DIALECT || "postgres";
16475
16767
  const aggressive = options.aggressive || false;
16476
16768
  if (options.verbose) {
16477
16769
  console.log(`Optimizing ${dialect} database${aggressive ? " (aggressive mode)" : ""}...`);
@@ -16511,7 +16803,7 @@ async function dbOptimize(options = {}) {
16511
16803
  await bunSql`ANALYZE TABLE ${bunSql(table)}`;
16512
16804
  }
16513
16805
  } else {
16514
- const dbName = process22.env.DB_NAME || "test";
16806
+ const dbName = process21.env.DB_NAME || "test";
16515
16807
  const tables = await bunSql`
16516
16808
  SELECT table_name
16517
16809
  FROM information_schema.tables
@@ -16563,9 +16855,9 @@ var init_db_optimize = __esm(() => {
16563
16855
  });
16564
16856
 
16565
16857
  // src/actions/db-wipe.ts
16566
- import process23 from "process";
16858
+ import process22 from "process";
16567
16859
  async function dbWipe(options = {}) {
16568
- const dialect = options.dialect || process23.env.DB_DIALECT || "postgres";
16860
+ const dialect = options.dialect || process22.env.DB_DIALECT || "postgres";
16569
16861
  if (options.verbose) {
16570
16862
  console.log(`Wiping all tables from ${dialect} database...`);
16571
16863
  }
@@ -16579,7 +16871,7 @@ async function dbWipe(options = {}) {
16579
16871
  `;
16580
16872
  tables = result.map((row) => row.tablename);
16581
16873
  } else if (dialect === "mysql") {
16582
- const dbName = process23.env.DB_NAME || "test";
16874
+ const dbName = process22.env.DB_NAME || "test";
16583
16875
  const result = await bunSql`
16584
16876
  SELECT table_name
16585
16877
  FROM information_schema.tables
@@ -16872,10 +17164,130 @@ var init_introspect = __esm(() => {
16872
17164
  init_src2();
16873
17165
  });
16874
17166
 
17167
+ // src/actions/introspect-db.ts
17168
+ function sqlTypeToAttr(sqlType) {
17169
+ const t = sqlType.toLowerCase();
17170
+ if (/^bool|boolean|tinyint\(1\)/.test(t))
17171
+ return "boolean";
17172
+ if (/int|serial|numeric|decimal|double|real|float|money/.test(t))
17173
+ return "number";
17174
+ if (/timestamp|datetime|date|time/.test(t))
17175
+ return "datetime";
17176
+ if (/json/.test(t))
17177
+ return "json";
17178
+ return "string";
17179
+ }
17180
+ function singularize(name) {
17181
+ if (/ies$/i.test(name))
17182
+ return name.replace(/ies$/i, "y");
17183
+ if (/ses$/i.test(name))
17184
+ return name.replace(/es$/i, "");
17185
+ if (/s$/i.test(name) && !/ss$/i.test(name))
17186
+ return name.replace(/s$/i, "");
17187
+ return name;
17188
+ }
17189
+ function pascalCase(name) {
17190
+ return name.replace(/[-_\s]+/g, " ").split(" ").filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
17191
+ }
17192
+ function modelNameForTable(table) {
17193
+ return pascalCase(singularize(table));
17194
+ }
17195
+ async function listTables(qb, dialect) {
17196
+ if (dialect === "postgres") {
17197
+ const rows2 = await qb.unsafe(`SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_type = 'BASE TABLE' ORDER BY table_name`);
17198
+ return rows2.map((r) => r.table_name);
17199
+ }
17200
+ if (dialect === "mysql") {
17201
+ const rows2 = await qb.unsafe(`SELECT table_name FROM information_schema.tables WHERE table_schema = DATABASE() AND table_type = 'BASE TABLE' ORDER BY table_name`);
17202
+ return rows2.map((r) => r.table_name ?? r.TABLE_NAME);
17203
+ }
17204
+ const rows = await qb.unsafe(`SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY name`);
17205
+ return rows.map((r) => r.name);
17206
+ }
17207
+ async function readColumns(qb, dialect, table) {
17208
+ if (dialect === "sqlite") {
17209
+ const rows2 = await qb.unsafe(`PRAGMA table_info(${table})`);
17210
+ return rows2.map((r) => ({
17211
+ name: r.name,
17212
+ sqlType: String(r.type || ""),
17213
+ nullable: Number(r.notnull) === 0,
17214
+ isPrimaryKey: Number(r.pk) > 0
17215
+ }));
17216
+ }
17217
+ if (dialect === "postgres") {
17218
+ const rows2 = await qb.unsafe(`SELECT c.column_name, c.data_type, c.is_nullable,
17219
+ (SELECT COUNT(*) FROM information_schema.table_constraints tc
17220
+ JOIN information_schema.key_column_usage k ON k.constraint_name = tc.constraint_name
17221
+ WHERE tc.constraint_type = 'PRIMARY KEY' AND tc.table_name = c.table_name AND k.column_name = c.column_name) AS is_pk
17222
+ FROM information_schema.columns c
17223
+ WHERE c.table_name = $1 AND c.table_schema = 'public'
17224
+ ORDER BY c.ordinal_position`, [table]);
17225
+ return rows2.map((r) => ({
17226
+ name: r.column_name,
17227
+ sqlType: String(r.data_type || ""),
17228
+ nullable: String(r.is_nullable).toUpperCase() === "YES",
17229
+ isPrimaryKey: Number(r.is_pk) > 0
17230
+ }));
17231
+ }
17232
+ const rows = await qb.unsafe(`SELECT column_name, data_type, is_nullable, column_key
17233
+ FROM information_schema.columns
17234
+ WHERE table_name = ? AND table_schema = DATABASE()
17235
+ ORDER BY ordinal_position`, [table]);
17236
+ return rows.map((r) => ({
17237
+ name: r.column_name ?? r.COLUMN_NAME,
17238
+ sqlType: String(r.data_type ?? r.DATA_TYPE ?? ""),
17239
+ nullable: String(r.is_nullable ?? r.IS_NULLABLE).toUpperCase() === "YES",
17240
+ isPrimaryKey: String(r.column_key ?? r.COLUMN_KEY).toUpperCase() === "PRI"
17241
+ }));
17242
+ }
17243
+ function generateModelSource(table, columns) {
17244
+ const modelName = modelNameForTable(table);
17245
+ const pk = columns.find((c) => c.isPrimaryKey)?.name ?? "id";
17246
+ const attrLines = columns.map((c) => {
17247
+ const parts = [`type: '${sqlTypeToAttr(c.sqlType)}'`];
17248
+ if (!c.nullable && !c.isPrimaryKey)
17249
+ parts.push("required: true");
17250
+ return ` ${c.name}: { ${parts.join(", ")} },`;
17251
+ }).join(`
17252
+ `);
17253
+ return `export const ${modelName} = defineModel({
17254
+ name: '${modelName}',
17255
+ table: '${table}',
17256
+ primaryKey: '${pk}',
17257
+ attributes: {
17258
+ ${attrLines}
17259
+ },
17260
+ })
17261
+ `;
17262
+ }
17263
+ async function introspectDatabase(opts = {}) {
17264
+ const dialect = config5.dialect || "postgres";
17265
+ const qb = createQueryBuilder();
17266
+ const tables = opts.tables?.length ? opts.tables : await listTables(qb, dialect);
17267
+ const out = [];
17268
+ for (const table of tables) {
17269
+ const columns = await readColumns(qb, dialect, table);
17270
+ if (!columns.length)
17271
+ continue;
17272
+ out.push({
17273
+ table,
17274
+ modelName: modelNameForTable(table),
17275
+ primaryKey: columns.find((c) => c.isPrimaryKey)?.name ?? "id",
17276
+ columns,
17277
+ source: generateModelSource(table, columns)
17278
+ });
17279
+ }
17280
+ return out;
17281
+ }
17282
+ var init_introspect_db = __esm(() => {
17283
+ init_config();
17284
+ init_src2();
17285
+ });
17286
+
16875
17287
  // src/actions/make-model.ts
16876
17288
  import { existsSync as existsSync14, mkdirSync as mkdirSync5, writeFileSync as writeFileSync10 } from "fs";
16877
17289
  import { dirname as dirname5, join as join7 } from "path";
16878
- import process24 from "process";
17290
+ import process23 from "process";
16879
17291
  function findWorkspaceRoot(startPath) {
16880
17292
  let currentPath = startPath;
16881
17293
  while (currentPath !== dirname5(currentPath)) {
@@ -16884,10 +17296,10 @@ function findWorkspaceRoot(startPath) {
16884
17296
  }
16885
17297
  currentPath = dirname5(currentPath);
16886
17298
  }
16887
- return process24.cwd();
17299
+ return process23.cwd();
16888
17300
  }
16889
17301
  async function makeModel(name, options = {}) {
16890
- const workspaceRoot = findWorkspaceRoot(process24.cwd());
17302
+ const workspaceRoot = findWorkspaceRoot(process23.cwd());
16891
17303
  const modelsDir = options.dir || join7(workspaceRoot, "app/Models");
16892
17304
  if (!existsSync14(modelsDir)) {
16893
17305
  mkdirSync5(modelsDir, { recursive: true });
@@ -17444,7 +17856,7 @@ __export(exports_migrate, {
17444
17856
  import { existsSync as existsSync16, mkdirSync as mkdirSync7, mkdtempSync, readdirSync as readdirSync6, readFileSync as readFileSync3, rmSync, unlinkSync, writeFileSync as writeFileSync11 } from "fs";
17445
17857
  import { tmpdir } from "os";
17446
17858
  import { join as join9 } from "path";
17447
- import process25 from "process";
17859
+ import process24 from "process";
17448
17860
  function info(message) {
17449
17861
  if (config5.verbose)
17450
17862
  console.log(message);
@@ -17491,7 +17903,7 @@ function savePlanSnapshot(workspaceRoot, dialect, plan) {
17491
17903
  info(`-- Model snapshot saved to ${snapshotPath}`);
17492
17904
  }
17493
17905
  function getWorkspaceRoot() {
17494
- return process25.cwd();
17906
+ return process24.cwd();
17495
17907
  }
17496
17908
  function ensureSqlDirectory(workspaceRoot) {
17497
17909
  const sqlDir = getSqlDirectory(workspaceRoot);
@@ -17503,7 +17915,7 @@ function ensureSqlDirectory(workspaceRoot) {
17503
17915
  }
17504
17916
  async function generateMigration(dir, opts = {}) {
17505
17917
  if (!dir) {
17506
- dir = join9(process25.cwd(), "app/Models");
17918
+ dir = join9(process24.cwd(), "app/Models");
17507
17919
  }
17508
17920
  const dialect = opts.dialect || config5.dialect || "postgres";
17509
17921
  const workspaceRoot = getWorkspaceRoot();
@@ -17558,7 +17970,7 @@ async function generateMigration(dir, opts = {}) {
17558
17970
  }
17559
17971
  async function executeMigration(dir) {
17560
17972
  if (!dir) {
17561
- dir = join9(process25.cwd(), "app/Models");
17973
+ dir = join9(process24.cwd(), "app/Models");
17562
17974
  }
17563
17975
  const workspaceRoot = getWorkspaceRoot();
17564
17976
  const sqlDir = ensureSqlDirectory(workspaceRoot);
@@ -17623,7 +18035,7 @@ async function executeMigration(dir) {
17623
18035
  }
17624
18036
  async function resetDatabase(dir, opts = {}) {
17625
18037
  if (!dir) {
17626
- dir = join9(process25.cwd(), "app/Models");
18038
+ dir = join9(process24.cwd(), "app/Models");
17627
18039
  }
17628
18040
  const dialect = opts.dialect || "postgres";
17629
18041
  const driver = getDialectDriver(dialect);
@@ -17716,7 +18128,7 @@ async function resetDatabase(dir, opts = {}) {
17716
18128
  }
17717
18129
  async function deleteMigrationFiles(dir, workspaceRoot, opts = {}) {
17718
18130
  if (!dir) {
17719
- dir = join9(process25.cwd(), "app/Models");
18131
+ dir = join9(process24.cwd(), "app/Models");
17720
18132
  }
17721
18133
  if (!workspaceRoot) {
17722
18134
  workspaceRoot = getWorkspaceRoot();
@@ -17814,9 +18226,51 @@ var init_migrate_generate = __esm(() => {
17814
18226
  });
17815
18227
 
17816
18228
  // src/actions/migrate-rollback.ts
17817
- import { existsSync as existsSync17, unlinkSync as unlinkSync2 } from "fs";
18229
+ import { existsSync as existsSync17, readFileSync as readFileSync4, unlinkSync as unlinkSync2 } from "fs";
17818
18230
  import { dirname as dirname7, join as join10 } from "path";
17819
- import process26 from "process";
18231
+ import process25 from "process";
18232
+ function splitSqlStatements2(sql) {
18233
+ const out = [];
18234
+ let buf = "";
18235
+ let inString = false;
18236
+ const lines = sql.split(`
18237
+ `).filter((l) => !/^\s*--/.test(l));
18238
+ const text = lines.join(`
18239
+ `);
18240
+ for (let i = 0;i < text.length; i++) {
18241
+ const ch = text[i];
18242
+ if (ch === "'")
18243
+ inString = !inString;
18244
+ if (ch === ";" && !inString) {
18245
+ if (buf.trim())
18246
+ out.push(buf.trim());
18247
+ buf = "";
18248
+ } else {
18249
+ buf += ch;
18250
+ }
18251
+ }
18252
+ if (buf.trim())
18253
+ out.push(buf.trim());
18254
+ return out;
18255
+ }
18256
+ function deriveDownStatements(forwardSql, dialect = config5.dialect) {
18257
+ const q = (id) => dialect === "mysql" ? `\`${id}\`` : `"${id}"`;
18258
+ const down = [];
18259
+ const skipped = [];
18260
+ for (const stmt of splitSqlStatements2(forwardSql)) {
18261
+ let m;
18262
+ if (m = /^CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?["`']?(\w+)["`']?/i.exec(stmt)) {
18263
+ down.push(`DROP TABLE IF EXISTS ${q(m[1])}`);
18264
+ } else if (m = /^ALTER\s+TABLE\s+["`']?(\w+)["`']?\s+ADD\s+(?:COLUMN\s+)?["`']?(\w+)["`']?/i.exec(stmt)) {
18265
+ down.push(`ALTER TABLE ${q(m[1])} DROP COLUMN ${q(m[2])}`);
18266
+ } else if (m = /^CREATE\s+(?:UNIQUE\s+)?INDEX\s+(?:IF\s+NOT\s+EXISTS\s+)?["`']?(\w+)["`']?(?:\s+ON\s+["`']?(\w+)["`']?)?/i.exec(stmt)) {
18267
+ down.push(dialect === "mysql" && m[2] ? `DROP INDEX ${q(m[1])} ON ${q(m[2])}` : `DROP INDEX IF EXISTS ${q(m[1])}`);
18268
+ } else {
18269
+ skipped.push(stmt);
18270
+ }
18271
+ }
18272
+ return { down: down.reverse(), skipped };
18273
+ }
17820
18274
  function findWorkspaceRoot2(startPath) {
17821
18275
  let currentPath = startPath;
17822
18276
  while (currentPath !== dirname7(currentPath)) {
@@ -17825,16 +18279,17 @@ function findWorkspaceRoot2(startPath) {
17825
18279
  }
17826
18280
  currentPath = dirname7(currentPath);
17827
18281
  }
17828
- return process26.cwd();
18282
+ return process25.cwd();
17829
18283
  }
17830
18284
  function getSqlDirectory2(workspaceRoot) {
17831
18285
  if (!workspaceRoot) {
17832
- workspaceRoot = findWorkspaceRoot2(process26.cwd());
18286
+ workspaceRoot = findWorkspaceRoot2(process25.cwd());
17833
18287
  }
17834
18288
  return join10(workspaceRoot, "database", "migrations");
17835
18289
  }
17836
18290
  async function migrateRollback(options = {}) {
17837
18291
  const steps = options.steps || 1;
18292
+ const reverseSchema = options.reverseSchema !== false;
17838
18293
  console.log("-- Rolling back migrations");
17839
18294
  console.log(`-- Steps: ${steps}`);
17840
18295
  console.log();
@@ -17864,13 +18319,25 @@ async function migrateRollback(options = {}) {
17864
18319
  console.log(` - ${migration.migration}`);
17865
18320
  }
17866
18321
  console.log();
18322
+ const sqlDir = getSqlDirectory2();
18323
+ let reversedAny = false;
17867
18324
  for (const migration of migrationsToRollback) {
17868
18325
  try {
18326
+ const filePath = join10(sqlDir, migration.migration);
18327
+ if (reverseSchema && existsSync17(filePath)) {
18328
+ const forwardSql = readFileSync4(filePath, "utf8");
18329
+ const { down, skipped } = deriveDownStatements(forwardSql);
18330
+ for (const stmt of down) {
18331
+ await qb.unsafe(stmt);
18332
+ console.log(`-- \u21A9 ${stmt}`);
18333
+ reversedAny = true;
18334
+ }
18335
+ if (skipped.length > 0)
18336
+ console.log(`-- \u26A0\uFE0F ${skipped.length} statement(s) in ${migration.migration} could not be auto-reversed (data/complex DDL) \u2014 reverse manually.`);
18337
+ }
17869
18338
  const deleteSql = `DELETE FROM migrations WHERE migration = $1`;
17870
18339
  await qb.unsafe(deleteSql, [migration.migration]);
17871
18340
  console.log(`-- \u2713 Removed migration record: ${migration.migration}`);
17872
- const sqlDir = getSqlDirectory2();
17873
- const filePath = join10(sqlDir, migration.migration);
17874
18341
  if (existsSync17(filePath)) {
17875
18342
  unlinkSync2(filePath);
17876
18343
  console.log(`-- \uD83D\uDDD1\uFE0F Deleted migration file: ${migration.migration}`);
@@ -17881,24 +18348,25 @@ async function migrateRollback(options = {}) {
17881
18348
  }
17882
18349
  }
17883
18350
  console.log();
17884
- console.log("-- \u26A0\uFE0F Important: Rollback only removes migration records.");
17885
- console.log("-- To reverse schema changes:");
17886
- console.log("-- 1. Revert your model changes");
17887
- console.log("-- 2. Run `qb migrate:fresh` to rebuild the database");
17888
- console.log("-- or manually write and execute reverse SQL");
18351
+ if (reverseSchema) {
18352
+ console.log(reversedAny ? "-- \u2713 Reverse DDL executed for the rolled-back migration(s)." : "-- \u26A0\uFE0F No reversible DDL found; only migration records were removed.");
18353
+ } else {
18354
+ console.log("-- \u26A0\uFE0F reverseSchema disabled: only migration records were removed.");
18355
+ }
17889
18356
  } catch (err) {
17890
18357
  console.error("-- Rollback failed:", err);
17891
18358
  throw err;
17892
18359
  }
17893
18360
  }
17894
18361
  var init_migrate_rollback = __esm(() => {
18362
+ init_config();
17895
18363
  init_src2();
17896
18364
  });
17897
18365
 
17898
18366
  // src/actions/migrate-status.ts
17899
18367
  import { existsSync as existsSync18, readdirSync as readdirSync8 } from "fs";
17900
18368
  import { dirname as dirname8, join as join11 } from "path";
17901
- import process27 from "process";
18369
+ import process26 from "process";
17902
18370
  function findWorkspaceRoot3(startPath) {
17903
18371
  let currentPath = startPath;
17904
18372
  while (currentPath !== dirname8(currentPath)) {
@@ -17907,11 +18375,11 @@ function findWorkspaceRoot3(startPath) {
17907
18375
  }
17908
18376
  currentPath = dirname8(currentPath);
17909
18377
  }
17910
- return process27.cwd();
18378
+ return process26.cwd();
17911
18379
  }
17912
18380
  function getSqlDirectory3(workspaceRoot) {
17913
18381
  if (!workspaceRoot) {
17914
- workspaceRoot = findWorkspaceRoot3(process27.cwd());
18382
+ workspaceRoot = findWorkspaceRoot3(process26.cwd());
17915
18383
  }
17916
18384
  return join11(workspaceRoot, "database", "migrations");
17917
18385
  }
@@ -18005,9 +18473,9 @@ var init_migrate_status = __esm(() => {
18005
18473
  // src/actions/model-show.ts
18006
18474
  import { readdirSync as readdirSync9 } from "fs";
18007
18475
  import { extname as extname2, join as join12 } from "path";
18008
- import process28 from "process";
18476
+ import process27 from "process";
18009
18477
  async function modelShow(modelName, options = {}) {
18010
- const dir = options.dir || join12(process28.cwd(), "app/Models");
18478
+ const dir = options.dir || join12(process27.cwd(), "app/Models");
18011
18479
  try {
18012
18480
  const files = readdirSync9(dir);
18013
18481
  const modelFile = files.find((f) => {
@@ -18126,7 +18594,7 @@ var init_ping = __esm(() => {
18126
18594
  });
18127
18595
 
18128
18596
  // src/actions/query-explain-all.ts
18129
- import { readdirSync as readdirSync10, readFileSync as readFileSync4, statSync as statSync3 } from "fs";
18597
+ import { readdirSync as readdirSync10, readFileSync as readFileSync5, statSync as statSync3 } from "fs";
18130
18598
  import { extname as extname3, join as join13 } from "path";
18131
18599
  async function queryExplainAll(path, options = {}) {
18132
18600
  const results = [];
@@ -18152,7 +18620,7 @@ async function queryExplainAll(path, options = {}) {
18152
18620
  }
18153
18621
  for (const file2 of files) {
18154
18622
  try {
18155
- const query = readFileSync4(file2, "utf8").trim();
18623
+ const query = readFileSync5(file2, "utf8").trim();
18156
18624
  if (!query) {
18157
18625
  if (options.verbose) {
18158
18626
  console.log(`\u2298 ${file2}: empty file`);
@@ -18189,7 +18657,7 @@ async function queryExplainAll(path, options = {}) {
18189
18657
  } catch (error) {
18190
18658
  results.push({
18191
18659
  file: file2,
18192
- query: readFileSync4(file2, "utf8").trim(),
18660
+ query: readFileSync5(file2, "utf8").trim(),
18193
18661
  plan: [],
18194
18662
  error: error.message
18195
18663
  });
@@ -18221,9 +18689,9 @@ var init_query_explain_all = __esm(() => {
18221
18689
  // src/actions/relation-diagram.ts
18222
18690
  import { writeFileSync as writeFileSync12 } from "fs";
18223
18691
  import { join as join14 } from "path";
18224
- import process29 from "process";
18692
+ import process28 from "process";
18225
18693
  async function relationDiagram(options = {}) {
18226
- const dir = options.dir || join14(process29.cwd(), "app/Models");
18694
+ const dir = options.dir || join14(process28.cwd(), "app/Models");
18227
18695
  const format = options.format || "mermaid";
18228
18696
  try {
18229
18697
  const models = await loadModels({ modelsDir: dir });
@@ -18345,7 +18813,7 @@ var init_relation_diagram = __esm(() => {
18345
18813
  // src/actions/seed.ts
18346
18814
  import { existsSync as existsSync19, mkdirSync as mkdirSync8, readdirSync as readdirSync11, writeFileSync as writeFileSync13 } from "fs";
18347
18815
  import { dirname as dirname9, join as join15 } from "path";
18348
- import process30 from "process";
18816
+ import process29 from "process";
18349
18817
  function findWorkspaceRoot4(startPath) {
18350
18818
  let currentPath = startPath;
18351
18819
  while (currentPath !== dirname9(currentPath)) {
@@ -18354,7 +18822,7 @@ function findWorkspaceRoot4(startPath) {
18354
18822
  }
18355
18823
  currentPath = dirname9(currentPath);
18356
18824
  }
18357
- return process30.cwd();
18825
+ return process29.cwd();
18358
18826
  }
18359
18827
  async function loadSeeders(seedersDir) {
18360
18828
  if (!existsSync19(seedersDir)) {
@@ -18386,7 +18854,7 @@ async function loadSeeders(seedersDir) {
18386
18854
  return seeders;
18387
18855
  }
18388
18856
  async function runSeeders(config6 = {}) {
18389
- const workspaceRoot = findWorkspaceRoot4(process30.cwd());
18857
+ const workspaceRoot = findWorkspaceRoot4(process29.cwd());
18390
18858
  const seedersDir = config6.seedersDir || join15(workspaceRoot, "database/seeders");
18391
18859
  const verbose = config6.verbose ?? true;
18392
18860
  if (verbose) {
@@ -18421,7 +18889,7 @@ async function runSeeders(config6 = {}) {
18421
18889
  }
18422
18890
  }
18423
18891
  async function runSeeder(className, options = {}) {
18424
- const workspaceRoot = findWorkspaceRoot4(process30.cwd());
18892
+ const workspaceRoot = findWorkspaceRoot4(process29.cwd());
18425
18893
  const seedersDir = join15(workspaceRoot, "database/seeders");
18426
18894
  const verbose = options.verbose ?? true;
18427
18895
  if (verbose) {
@@ -18445,7 +18913,7 @@ async function runSeeder(className, options = {}) {
18445
18913
  }
18446
18914
  }
18447
18915
  async function makeSeeder(name) {
18448
- const workspaceRoot = findWorkspaceRoot4(process30.cwd());
18916
+ const workspaceRoot = findWorkspaceRoot4(process29.cwd());
18449
18917
  const seedersDir = join15(workspaceRoot, "database/seeders");
18450
18918
  if (!existsSync19(seedersDir)) {
18451
18919
  mkdirSync8(seedersDir, { recursive: true });
@@ -18509,7 +18977,7 @@ export default class ${className} extends Seeder {
18509
18977
  console.log(`-- \u2713 Created seeder: ${filePath}`);
18510
18978
  }
18511
18979
  async function freshDatabase(options = {}) {
18512
- const workspaceRoot = findWorkspaceRoot4(process30.cwd());
18980
+ const workspaceRoot = findWorkspaceRoot4(process29.cwd());
18513
18981
  const modelsDir = options.modelsDir || join15(workspaceRoot, "app/Models");
18514
18982
  const seedersDir = options.seedersDir || join15(workspaceRoot, "database/seeders");
18515
18983
  const verbose = options.verbose ?? true;
@@ -18571,7 +19039,7 @@ var init_unsafe = __esm(() => {
18571
19039
  // src/actions/validate.ts
18572
19040
  import { existsSync as existsSync20 } from "fs";
18573
19041
  import { dirname as dirname10, join as join16 } from "path";
18574
- import process31 from "process";
19042
+ import process30 from "process";
18575
19043
  function findWorkspaceRoot5(startPath) {
18576
19044
  let currentPath = startPath;
18577
19045
  while (currentPath !== dirname10(currentPath)) {
@@ -18580,11 +19048,11 @@ function findWorkspaceRoot5(startPath) {
18580
19048
  }
18581
19049
  currentPath = dirname10(currentPath);
18582
19050
  }
18583
- return process31.cwd();
19051
+ return process30.cwd();
18584
19052
  }
18585
19053
  async function validateSchema(dir) {
18586
19054
  if (!dir) {
18587
- dir = join16(findWorkspaceRoot5(process31.cwd()), "app/Models");
19055
+ dir = join16(findWorkspaceRoot5(process30.cwd()), "app/Models");
18588
19056
  }
18589
19057
  const dialect = config5.dialect || "postgres";
18590
19058
  console.log("-- Validating Schema");
@@ -18803,6 +19271,7 @@ var init_actions = __esm(() => {
18803
19271
  init_file();
18804
19272
  init_inspect();
18805
19273
  init_introspect();
19274
+ init_introspect_db();
18806
19275
  init_make_model();
18807
19276
  init_migrate();
18808
19277
  init_migrate_generate();
@@ -23273,6 +23742,10 @@ var init_src = __esm(() => {
23273
23742
 
23274
23743
  // src/orm.ts
23275
23744
  import { Database as Database2 } from "bun:sqlite";
23745
+ function formatNow() {
23746
+ const iso = new Date().toISOString();
23747
+ return config5.dialect === "mysql" ? iso.slice(0, 19).replace("T", " ") : iso;
23748
+ }
23276
23749
  function assertValidIdentifier(name, context) {
23277
23750
  if (typeof name !== "string" || name.length === 0)
23278
23751
  throw new TypeError(`[bun-query-builder] ${context}: identifier must be a non-empty string, got ${typeof name}`);
@@ -23293,19 +23766,138 @@ function getModelFromRegistry(name) {
23293
23766
  }
23294
23767
  return _getModel(name);
23295
23768
  }
23769
+ function toPostgresPlaceholders(sql2) {
23770
+ let i2 = 0;
23771
+ return sql2.replace(/\?/g, () => `$${++i2}`);
23772
+ }
23773
+ function extractChanges(res) {
23774
+ if (res == null)
23775
+ return 0;
23776
+ if (typeof res.affectedRows === "number")
23777
+ return res.affectedRows;
23778
+ if (typeof res.count === "number")
23779
+ return res.count;
23780
+ if (Array.isArray(res))
23781
+ return res.length;
23782
+ return 0;
23783
+ }
23784
+ function extractInsertId(res) {
23785
+ if (res == null || typeof res !== "object")
23786
+ return null;
23787
+ if ("insertId" in res && res.insertId != null)
23788
+ return res.insertId;
23789
+ if ("lastInsertRowid" in res && res.lastInsertRowid != null)
23790
+ return res.lastInsertRowid;
23791
+ return null;
23792
+ }
23793
+
23794
+ class SqliteExecutor {
23795
+ sqliteDb;
23796
+ dialect = "sqlite";
23797
+ constructor(sqliteDb) {
23798
+ this.sqliteDb = sqliteDb;
23799
+ }
23800
+ all(sql2, params) {
23801
+ return Promise.resolve(this.sqliteDb.query(sql2).all(...params));
23802
+ }
23803
+ get(sql2, params) {
23804
+ return Promise.resolve(this.sqliteDb.query(sql2).get(...params) ?? undefined);
23805
+ }
23806
+ run(sql2, params) {
23807
+ const r2 = this.sqliteDb.run(sql2, params);
23808
+ return Promise.resolve({ changes: r2.changes, lastInsertId: r2.lastInsertRowid });
23809
+ }
23810
+ insert(sql2, params) {
23811
+ return this.run(sql2, params);
23812
+ }
23813
+ }
23814
+
23815
+ class DriverExecutor {
23816
+ dialect;
23817
+ constructor(dialect) {
23818
+ this.dialect = dialect;
23819
+ }
23820
+ conn() {
23821
+ return getOrCreateBunSql();
23822
+ }
23823
+ text(sql2) {
23824
+ return this.dialect === "postgres" ? toPostgresPlaceholders(sql2) : sql2;
23825
+ }
23826
+ async all(sql2, params) {
23827
+ const rows = await this.conn().unsafe(this.text(sql2), params);
23828
+ return Array.isArray(rows) ? rows : [];
23829
+ }
23830
+ async get(sql2, params) {
23831
+ const rows = await this.conn().unsafe(this.text(sql2), params);
23832
+ if (Array.isArray(rows))
23833
+ return rows[0] ?? undefined;
23834
+ return rows ?? undefined;
23835
+ }
23836
+ async run(sql2, params) {
23837
+ const res = await this.conn().unsafe(this.text(sql2), params);
23838
+ return { changes: extractChanges(res), lastInsertId: extractInsertId(res) };
23839
+ }
23840
+ async insert(sql2, params, primaryKey) {
23841
+ if (this.dialect === "postgres") {
23842
+ const rows = await this.conn().unsafe(`${toPostgresPlaceholders(sql2)} RETURNING ${primaryKey}`, params);
23843
+ const row = Array.isArray(rows) ? rows[0] : rows;
23844
+ return { changes: 1, lastInsertId: row ? row[primaryKey] ?? null : null };
23845
+ }
23846
+ const res = await this.conn().unsafe(sql2, params);
23847
+ let id = extractInsertId(res);
23848
+ if (id == null) {
23849
+ const rows = await this.conn().unsafe("SELECT LAST_INSERT_ID() as id", []);
23850
+ const row = Array.isArray(rows) ? rows[0] : rows;
23851
+ id = row?.id ?? null;
23852
+ }
23853
+ return { changes: extractChanges(res) || 1, lastInsertId: id };
23854
+ }
23855
+ }
23296
23856
  function configureOrm(options) {
23297
23857
  if (options.database instanceof Database2) {
23298
23858
  globalDb = options.database;
23299
23859
  } else {
23300
23860
  globalDb = new Database2(options.database || ":memory:", { create: true });
23301
23861
  }
23302
- preparedStatementCache.clear();
23862
+ _executor = null;
23863
+ _executorForDb = null;
23303
23864
  }
23304
- function getDatabase() {
23305
- if (!globalDb) {
23306
- globalDb = new Database2(":memory:", { create: true });
23865
+ function getExecutor() {
23866
+ if (globalDb) {
23867
+ if (!_executor || _executorForDb !== globalDb) {
23868
+ _executor = new SqliteExecutor(globalDb);
23869
+ _executorForDb = globalDb;
23870
+ _executorDialect = "sqlite";
23871
+ _executorDatabase = null;
23872
+ }
23873
+ return _executor;
23307
23874
  }
23308
- return globalDb;
23875
+ const dialect = config5.dialect;
23876
+ const database = config5.database?.database ?? null;
23877
+ if (_executor && _executorForDb === null && _executorDialect === dialect && _executorDatabase === database)
23878
+ return _executor;
23879
+ _executorForDb = null;
23880
+ _executorDialect = dialect;
23881
+ _executorDatabase = database;
23882
+ if (dialect === "sqlite")
23883
+ _executor = new SqliteExecutor(new Database2(database || ":memory:", { create: true }));
23884
+ else
23885
+ _executor = new DriverExecutor(dialect);
23886
+ return _executor;
23887
+ }
23888
+ function getDatabase() {
23889
+ const exec = getExecutor();
23890
+ if (exec.sqliteDb)
23891
+ return exec.sqliteDb;
23892
+ throw new Error(`[bun-query-builder] getDatabase() is only available for the sqlite dialect; ` + `the configured dialect is '${exec.dialect}'. Use the async model API instead.`);
23893
+ }
23894
+ function softDeletesEnabled(definition) {
23895
+ const t2 = definition.traits;
23896
+ return Boolean(t2?.useSoftDeletes || t2?.softDeletable);
23897
+ }
23898
+ function timestampsEnabled(definition) {
23899
+ const t2 = definition.traits;
23900
+ return Boolean(t2?.useTimestamps || t2?.timestampable);
23309
23901
  }
23310
23902
  function collectBelongsToManyKeys(definition) {
23311
23903
  const keys = new Set;
@@ -23459,8 +24051,8 @@ class ModelInstance {
23459
24051
  Object.assign(this._attributes, data);
23460
24052
  return this;
23461
24053
  }
23462
- save() {
23463
- const db = getDatabase();
24054
+ async save() {
24055
+ const exec = getExecutor();
23464
24056
  const pk = this._definition.primaryKey || "id";
23465
24057
  const hooks = this._definition.hooks;
23466
24058
  const setters = this._definition.set || {};
@@ -23470,88 +24062,108 @@ class ModelInstance {
23470
24062
  }
23471
24063
  }
23472
24064
  if (this._attributes[pk]) {
23473
- hooks?.beforeUpdate?.(this, this.getChanges());
24065
+ await hooks?.beforeUpdate?.(this, this.getChanges());
23474
24066
  const changes = this.getChanges();
23475
24067
  const changeKeys = Object.keys(changes);
23476
24068
  if (changeKeys.length > 0) {
23477
24069
  const sets = changeKeys.map((k2) => `${k2} = ?`).join(", ");
23478
24070
  const values = [...Object.values(changes), this._attributes[pk]];
23479
- if (this._definition.traits?.useTimestamps) {
23480
- const now = new Date().toISOString();
23481
- db.run(`UPDATE ${this._definition.table} SET ${sets}, updated_at = ? WHERE ${pk} = ?`, [...Object.values(changes), now, this._attributes[pk]]);
24071
+ if (timestampsEnabled(this._definition)) {
24072
+ const now = formatNow();
24073
+ await exec.run(`UPDATE ${this._definition.table} SET ${sets}, updated_at = ? WHERE ${pk} = ?`, [...Object.values(changes), now, this._attributes[pk]]);
23482
24074
  } else {
23483
- db.run(`UPDATE ${this._definition.table} SET ${sets} WHERE ${pk} = ?`, values);
24075
+ await exec.run(`UPDATE ${this._definition.table} SET ${sets} WHERE ${pk} = ?`, values);
23484
24076
  }
23485
24077
  }
23486
- hooks?.afterUpdate?.(this);
24078
+ await hooks?.afterUpdate?.(this);
23487
24079
  } else {
23488
24080
  const attrs = this._definition.attributes;
23489
24081
  const data = {};
23490
24082
  for (const [key, attr] of Object.entries(attrs)) {
23491
- if (attr.fillable && this._attributes[key] !== undefined) {
24083
+ if (attr.guarded)
24084
+ continue;
24085
+ if (this._attributes[key] !== undefined) {
23492
24086
  data[key] = this._attributes[key];
23493
24087
  }
23494
24088
  }
23495
- if (this._definition.traits?.useTimestamps) {
23496
- const now = new Date().toISOString();
24089
+ if (timestampsEnabled(this._definition)) {
24090
+ const now = formatNow();
23497
24091
  data.created_at = now;
23498
24092
  data.updated_at = now;
23499
24093
  }
23500
24094
  if (this._definition.traits?.useUuid && !data.uuid) {
23501
24095
  data.uuid = crypto.randomUUID();
23502
24096
  }
23503
- hooks?.beforeCreate?.(data);
24097
+ await hooks?.beforeCreate?.(data);
23504
24098
  const columns = Object.keys(data);
23505
24099
  const placeholders = columns.map(() => "?").join(", ");
23506
- const result = db.run(`INSERT INTO ${this._definition.table} (${columns.join(", ")}) VALUES (${placeholders})`, Object.values(data));
24100
+ const result = await exec.insert(`INSERT INTO ${this._definition.table} (${columns.join(", ")}) VALUES (${placeholders})`, Object.values(data), pk);
23507
24101
  for (const [key, value] of Object.entries(data)) {
23508
24102
  this._attributes[key] = value;
23509
24103
  }
23510
- this._attributes[pk] = result.lastInsertRowid;
23511
- hooks?.afterCreate?.(this);
24104
+ if (result.lastInsertId != null)
24105
+ this._attributes[pk] = result.lastInsertId;
24106
+ await hooks?.afterCreate?.(this);
23512
24107
  }
23513
24108
  this._original = { ...this._attributes };
23514
24109
  this._hasSaved = true;
23515
24110
  return this;
23516
24111
  }
23517
- update(data) {
24112
+ async update(data) {
23518
24113
  this.fill(data);
23519
24114
  return this.save();
23520
24115
  }
23521
- fresh() {
23522
- const db = getDatabase();
24116
+ async fresh() {
24117
+ const exec = getExecutor();
23523
24118
  const pk = this._definition.primaryKey || "id";
23524
24119
  const id = this._attributes[pk];
23525
24120
  if (id == null)
23526
24121
  return null;
23527
- const row = db.query(`SELECT * FROM ${this._definition.table} WHERE ${pk} = ?`).get(id);
24122
+ const row = await exec.get(`SELECT * FROM ${this._definition.table} WHERE ${pk} = ?`, [id]);
23528
24123
  if (!row)
23529
24124
  return null;
23530
24125
  return new ModelInstance(this._definition, row);
23531
24126
  }
23532
- delete() {
23533
- const db = getDatabase();
24127
+ async delete() {
24128
+ const exec = getExecutor();
23534
24129
  const pk = this._definition.primaryKey || "id";
23535
24130
  const pkValue = this._attributes[pk];
23536
24131
  const hooks = this._definition.hooks;
23537
24132
  if (!pkValue)
23538
24133
  throw new Error("Cannot delete a model without a primary key");
23539
- hooks?.beforeDelete?.(this);
23540
- if (this._definition.traits?.useSoftDeletes) {
23541
- db.run(`UPDATE ${this._definition.table} SET deleted_at = ? WHERE ${pk} = ?`, [new Date().toISOString(), pkValue]);
24134
+ await hooks?.beforeDelete?.(this);
24135
+ if (softDeletesEnabled(this._definition)) {
24136
+ const now = formatNow();
24137
+ await exec.run(`UPDATE ${this._definition.table} SET deleted_at = ? WHERE ${pk} = ?`, [now, pkValue]);
24138
+ this._attributes[SOFT_DELETE_COLUMN] = now;
23542
24139
  } else {
23543
- db.run(`DELETE FROM ${this._definition.table} WHERE ${pk} = ?`, [pkValue]);
24140
+ await exec.run(`DELETE FROM ${this._definition.table} WHERE ${pk} = ?`, [pkValue]);
23544
24141
  }
23545
- hooks?.afterDelete?.(this);
24142
+ await hooks?.afterDelete?.(this);
23546
24143
  return true;
23547
24144
  }
23548
- refresh() {
23549
- const db = getDatabase();
24145
+ async restore() {
24146
+ if (!softDeletesEnabled(this._definition))
24147
+ throw new Error(`[orm] restore() requires soft deletes on '${this._definition.name}'`);
24148
+ const pk = this._definition.primaryKey || "id";
24149
+ const pkValue = this._attributes[pk];
24150
+ if (!pkValue)
24151
+ throw new Error("Cannot restore a model without a primary key");
24152
+ await getExecutor().run(`UPDATE ${this._definition.table} SET ${SOFT_DELETE_COLUMN} = ? WHERE ${pk} = ?`, [null, pkValue]);
24153
+ this._attributes[SOFT_DELETE_COLUMN] = null;
24154
+ this._original = null;
24155
+ return this;
24156
+ }
24157
+ trashed() {
24158
+ return this._attributes[SOFT_DELETE_COLUMN] != null;
24159
+ }
24160
+ async refresh() {
24161
+ const exec = getExecutor();
23550
24162
  const pk = this._definition.primaryKey || "id";
23551
24163
  const pkValue = this._attributes[pk];
23552
24164
  if (!pkValue)
23553
24165
  throw new Error("Cannot refresh a model without a primary key");
23554
- const row = db.query(`SELECT * FROM ${this._definition.table} WHERE ${pk} = ?`).get(pkValue);
24166
+ const row = await exec.get(`SELECT * FROM ${this._definition.table} WHERE ${pk} = ?`, [pkValue]);
23555
24167
  if (!row)
23556
24168
  return null;
23557
24169
  this._attributes = row;
@@ -23849,9 +24461,23 @@ class BelongsToManyRelationBuilder {
23849
24461
  this._offset = n2;
23850
24462
  return this;
23851
24463
  }
24464
+ relatedSelectColumns() {
24465
+ const cols = new Set([this.relatedPk, ...Object.keys(this._relatedDef.attributes ?? {})]);
24466
+ const t2 = this._relatedDef.traits;
24467
+ if (t2?.useTimestamps || t2?.timestampable) {
24468
+ cols.add("created_at");
24469
+ cols.add("updated_at");
24470
+ }
24471
+ if (t2?.useSoftDeletes || t2?.softDeletable)
24472
+ cols.add("deleted_at");
24473
+ if (t2?.useUuid)
24474
+ cols.add("uuid");
24475
+ return [...cols];
24476
+ }
23852
24477
  buildSelect() {
23853
24478
  const params = [];
23854
- let sql2 = `SELECT ${this.relatedTable}.*, ${this.pivotTable}.* FROM ${this.relatedTable}`;
24479
+ const relatedSelect = this.relatedSelectColumns().map((c2) => `${this.relatedTable}.${c2} AS ${BTM_RELATED_ALIAS}${c2}`).join(", ");
24480
+ let sql2 = `SELECT ${relatedSelect}, ${this.pivotTable}.* FROM ${this.relatedTable}`;
23855
24481
  sql2 += ` INNER JOIN ${this.pivotTable} ON ${this.pivotTable}.${this.fkRelated} = ${this.relatedTable}.${this.relatedPk}`;
23856
24482
  sql2 += ` WHERE ${this.pivotTable}.${this.fkParent} = ?`;
23857
24483
  params.push(this.parentId);
@@ -23872,58 +24498,51 @@ class BelongsToManyRelationBuilder {
23872
24498
  return { sql: sql2, params };
23873
24499
  }
23874
24500
  hydrateRows(rows) {
23875
- const relatedAttrs = new Set(Object.keys(this._relatedDef.attributes ?? {}));
23876
- relatedAttrs.add(this.relatedPk);
23877
- relatedAttrs.add("created_at");
23878
- relatedAttrs.add("updated_at");
23879
- relatedAttrs.add("deleted_at");
23880
24501
  const fkParent = this.fkParent;
23881
24502
  const fkRelated = this.fkRelated;
23882
24503
  return rows.map((raw) => {
23883
24504
  const relatedRow = {};
23884
24505
  const pivotExtras = {};
23885
24506
  for (const [k2, v2] of Object.entries(raw)) {
23886
- if (k2 === fkParent || k2 === fkRelated)
23887
- continue;
23888
- if (relatedAttrs.has(k2))
23889
- relatedRow[k2] = v2;
23890
- else
24507
+ if (k2.startsWith(BTM_RELATED_ALIAS))
24508
+ relatedRow[k2.slice(BTM_RELATED_ALIAS.length)] = v2;
24509
+ else if (k2 !== fkParent && k2 !== fkRelated)
23891
24510
  pivotExtras[k2] = v2;
23892
24511
  }
23893
- relatedRow[this.relatedPk] = raw[this.relatedPk];
23894
24512
  const inst = new ModelInstance(this._relatedDef, relatedRow);
23895
24513
  inst.pivot = pivotExtras;
23896
24514
  return inst;
23897
24515
  });
23898
24516
  }
23899
- get() {
23900
- const db = getDatabase();
24517
+ async get() {
24518
+ const exec = getExecutor();
23901
24519
  const { sql: sql2, params } = this.buildSelect();
23902
- const rows = db.query(sql2).all(...params);
24520
+ const rows = await exec.all(sql2, params);
23903
24521
  return this.hydrateRows(rows);
23904
24522
  }
23905
- first() {
24523
+ async first() {
23906
24524
  this._limit = 1;
23907
- return this.get()[0];
24525
+ return (await this.get())[0];
23908
24526
  }
23909
- count() {
23910
- const db = getDatabase();
24527
+ async count() {
24528
+ const exec = getExecutor();
23911
24529
  let sql2 = `SELECT COUNT(*) as count FROM ${this.pivotTable} WHERE ${this.fkParent} = ?`;
23912
24530
  const params = [this.parentId];
23913
24531
  for (const w of this._pivotWheres) {
23914
24532
  sql2 += ` AND ${w.sql}`;
23915
24533
  params.push(...w.params);
23916
24534
  }
23917
- return db.query(sql2).get(...params).count;
24535
+ const row = await exec.get(sql2, params);
24536
+ return Number(row?.count ?? 0);
23918
24537
  }
23919
- exists() {
23920
- return this.count() > 0;
24538
+ async exists() {
24539
+ return await this.count() > 0;
23921
24540
  }
23922
24541
  now() {
23923
- return new Date().toISOString();
24542
+ return formatNow();
23924
24543
  }
23925
- attach(idOrIds, extras = {}) {
23926
- const db = getDatabase();
24544
+ async attach(idOrIds, extras = {}) {
24545
+ const exec = getExecutor();
23927
24546
  const ids = Array.isArray(idOrIds) ? idOrIds : [idOrIds];
23928
24547
  if (ids.length === 0)
23929
24548
  return 0;
@@ -23945,13 +24564,13 @@ class BelongsToManyRelationBuilder {
23945
24564
  const placeholders = cols.map(() => "?").join(", ");
23946
24565
  const sql2 = `INSERT INTO ${this.pivotTable} (${cols.join(", ")}) VALUES (${placeholders})`;
23947
24566
  const params = cols.map((c2) => row[c2]);
23948
- db.run(sql2, params);
24567
+ await exec.run(sql2, params);
23949
24568
  inserted++;
23950
24569
  }
23951
24570
  return inserted;
23952
24571
  }
23953
- detach(idOrIds) {
23954
- const db = getDatabase();
24572
+ async detach(idOrIds) {
24573
+ const exec = getExecutor();
23955
24574
  let sql2 = `DELETE FROM ${this.pivotTable} WHERE ${this.fkParent} = ?`;
23956
24575
  const params = [this.parentId];
23957
24576
  if (idOrIds !== undefined && idOrIds !== null) {
@@ -23962,10 +24581,10 @@ class BelongsToManyRelationBuilder {
23962
24581
  sql2 += ` AND ${this.fkRelated} IN (${placeholders})`;
23963
24582
  params.push(...ids);
23964
24583
  }
23965
- return db.run(sql2, params).changes;
24584
+ return (await exec.run(sql2, params)).changes;
23966
24585
  }
23967
- updateExistingPivot(relatedId, extras) {
23968
- const db = getDatabase();
24586
+ async updateExistingPivot(relatedId, extras) {
24587
+ const exec = getExecutor();
23969
24588
  const updates = { ...extras };
23970
24589
  if (this._resolved.pivotTimestamps && updates.updated_at === undefined) {
23971
24590
  updates.updated_at = this.now();
@@ -23976,10 +24595,10 @@ class BelongsToManyRelationBuilder {
23976
24595
  const setClause = cols.map((c2) => `${c2} = ?`).join(", ");
23977
24596
  const sql2 = `UPDATE ${this.pivotTable} SET ${setClause} WHERE ${this.fkParent} = ? AND ${this.fkRelated} = ?`;
23978
24597
  const params = [...cols.map((c2) => updates[c2]), this.parentId, relatedId];
23979
- return db.run(sql2, params).changes;
24598
+ return (await exec.run(sql2, params)).changes;
23980
24599
  }
23981
- sync(items) {
23982
- const db = getDatabase();
24600
+ async sync(items) {
24601
+ const exec = getExecutor();
23983
24602
  const desired = new Map;
23984
24603
  for (const item of items) {
23985
24604
  if (item != null && typeof item === "object" && "id" in item) {
@@ -23989,41 +24608,41 @@ class BelongsToManyRelationBuilder {
23989
24608
  desired.set(item, {});
23990
24609
  }
23991
24610
  }
23992
- const current = db.query(`SELECT * FROM ${this.pivotTable} WHERE ${this.fkParent} = ?`).all(this.parentId);
24611
+ const current = await exec.all(`SELECT * FROM ${this.pivotTable} WHERE ${this.fkParent} = ?`, [this.parentId]);
23993
24612
  const currentIds = new Set(current.map((r2) => r2[this.fkRelated]));
23994
24613
  const attached = [];
23995
24614
  const detached = [];
23996
24615
  const updated = [];
23997
24616
  const toDetach = [...currentIds].filter((id) => !desired.has(id));
23998
24617
  if (toDetach.length > 0) {
23999
- this.detach(toDetach);
24618
+ await this.detach(toDetach);
24000
24619
  detached.push(...toDetach);
24001
24620
  }
24002
24621
  for (const [id, extras] of desired) {
24003
24622
  if (!currentIds.has(id)) {
24004
- this.attach(id, extras);
24623
+ await this.attach(id, extras);
24005
24624
  attached.push(id);
24006
24625
  } else if (Object.keys(extras).length > 0) {
24007
- this.updateExistingPivot(id, extras);
24626
+ await this.updateExistingPivot(id, extras);
24008
24627
  updated.push(id);
24009
24628
  }
24010
24629
  }
24011
24630
  return { attached, detached, updated };
24012
24631
  }
24013
- toggle(idOrIds) {
24014
- const db = getDatabase();
24632
+ async toggle(idOrIds) {
24633
+ const exec = getExecutor();
24015
24634
  const ids = Array.isArray(idOrIds) ? idOrIds : [idOrIds];
24016
24635
  if (ids.length === 0)
24017
24636
  return { attached: [], detached: [] };
24018
24637
  const placeholders = ids.map(() => "?").join(", ");
24019
- const present = db.query(`SELECT ${this.fkRelated} FROM ${this.pivotTable} WHERE ${this.fkParent} = ? AND ${this.fkRelated} IN (${placeholders})`).all(this.parentId, ...ids);
24638
+ const present = await exec.all(`SELECT ${this.fkRelated} FROM ${this.pivotTable} WHERE ${this.fkParent} = ? AND ${this.fkRelated} IN (${placeholders})`, [this.parentId, ...ids]);
24020
24639
  const presentIds = new Set(present.map((r2) => r2[this.fkRelated]));
24021
24640
  const toAttach = ids.filter((id) => !presentIds.has(id));
24022
24641
  const toDetach = ids.filter((id) => presentIds.has(id));
24023
24642
  if (toAttach.length > 0)
24024
- this.attach(toAttach);
24643
+ await this.attach(toAttach);
24025
24644
  if (toDetach.length > 0)
24026
- this.detach(toDetach);
24645
+ await this.detach(toDetach);
24027
24646
  return { attached: toAttach, detached: toDetach };
24028
24647
  }
24029
24648
  }
@@ -24036,9 +24655,18 @@ class ModelQueryBuilder {
24036
24655
  _offset;
24037
24656
  _select = ["*"];
24038
24657
  _withRelations = [];
24658
+ _trashed = "exclude";
24039
24659
  constructor(definition) {
24040
24660
  this._definition = definition;
24041
24661
  }
24662
+ withTrashed() {
24663
+ this._trashed = "include";
24664
+ return this;
24665
+ }
24666
+ onlyTrashed() {
24667
+ this._trashed = "only";
24668
+ return this;
24669
+ }
24042
24670
  where(column, operatorOrValue, value) {
24043
24671
  if (value === undefined) {
24044
24672
  this._wheres.push({ column, operator: "=", value: operatorOrValue, boolean: "and" });
@@ -24216,11 +24844,24 @@ class ModelQueryBuilder {
24216
24844
  }
24217
24845
  return clauses.join(" ");
24218
24846
  }
24847
+ softDeleteClause() {
24848
+ if (this._trashed === "include" || !softDeletesEnabled(this._definition))
24849
+ return "";
24850
+ return this._trashed === "only" ? `${SOFT_DELETE_COLUMN} IS NOT NULL` : `${SOFT_DELETE_COLUMN} IS NULL`;
24851
+ }
24852
+ composeWhere(params) {
24853
+ const userClause = this._wheres.length > 0 ? this.buildWhereClauses(params) : "";
24854
+ const sd = this.softDeleteClause();
24855
+ if (userClause && sd)
24856
+ return `(${userClause}) AND ${sd}`;
24857
+ return userClause || sd;
24858
+ }
24219
24859
  buildQuery() {
24220
24860
  const params = [];
24221
24861
  let sql2 = `SELECT ${this._select.join(", ")} FROM ${this._definition.table}`;
24222
- if (this._wheres.length > 0) {
24223
- sql2 += ` WHERE ${this.buildWhereClauses(params)}`;
24862
+ const whereBody = this.composeWhere(params);
24863
+ if (whereBody) {
24864
+ sql2 += ` WHERE ${whereBody}`;
24224
24865
  }
24225
24866
  if (this._orderBy.length > 0) {
24226
24867
  sql2 += ` ORDER BY ${this._orderBy.map((o2) => `${o2.column} ${o2.direction.toUpperCase()}`).join(", ")}`;
@@ -24234,10 +24875,10 @@ class ModelQueryBuilder {
24234
24875
  toSql() {
24235
24876
  return this.buildQuery();
24236
24877
  }
24237
- eagerLoadRelations(instances) {
24878
+ async eagerLoadRelations(instances) {
24238
24879
  if (instances.length === 0 || this._withRelations.length === 0)
24239
24880
  return;
24240
- const db = getDatabase();
24881
+ const exec = getExecutor();
24241
24882
  const pk = this._definition.primaryKey || "id";
24242
24883
  for (const relationName of this._withRelations) {
24243
24884
  const cacheKey = `${this._definition.name}:${relationName}`;
@@ -24253,7 +24894,7 @@ class ModelQueryBuilder {
24253
24894
  if (parentIds.length === 0)
24254
24895
  continue;
24255
24896
  const placeholders = parentIds.map(() => "?").join(", ");
24256
- const rows = db.query(`SELECT * FROM ${rel.relatedTable} WHERE ${rel.foreignKey} IN (${placeholders})`).all(...parentIds);
24897
+ const rows = await exec.all(`SELECT * FROM ${rel.relatedTable} WHERE ${rel.foreignKey} IN (${placeholders})`, parentIds);
24257
24898
  const relatedModelDef = getModelFromRegistry(rel.relatedModelName);
24258
24899
  const relDef = relatedModelDef?.getDefinition?.() || relatedModelDef?.definition || this._definition;
24259
24900
  if (rel.type === "hasMany") {
@@ -24285,7 +24926,7 @@ class ModelQueryBuilder {
24285
24926
  if (uniqueFkValues.length === 0)
24286
24927
  continue;
24287
24928
  const placeholders = uniqueFkValues.map(() => "?").join(", ");
24288
- const rows = db.query(`SELECT * FROM ${rel.relatedTable} WHERE ${rel.localKey} IN (${placeholders})`).all(...uniqueFkValues);
24929
+ const rows = await exec.all(`SELECT * FROM ${rel.relatedTable} WHERE ${rel.localKey} IN (${placeholders})`, uniqueFkValues);
24289
24930
  const relatedModelDef = getModelFromRegistry(rel.relatedModelName);
24290
24931
  const relDef = relatedModelDef?.getDefinition?.() || relatedModelDef?.definition || this._definition;
24291
24932
  const byPk = new Map;
@@ -24305,7 +24946,7 @@ class ModelQueryBuilder {
24305
24946
  if (!rel.pivotTable || !rel.pivotFkParent || !rel.pivotFkRelated)
24306
24947
  continue;
24307
24948
  const pivotPlaceholders = parentIds.map(() => "?").join(", ");
24308
- const pivotRows = db.query(`SELECT * FROM ${rel.pivotTable} WHERE ${rel.pivotFkParent} IN (${pivotPlaceholders})`).all(...parentIds);
24949
+ const pivotRows = await exec.all(`SELECT * FROM ${rel.pivotTable} WHERE ${rel.pivotFkParent} IN (${pivotPlaceholders})`, parentIds);
24309
24950
  if (pivotRows.length === 0) {
24310
24951
  for (const instance of instances)
24311
24952
  instance.setRelation(relationName, []);
@@ -24318,7 +24959,7 @@ class ModelQueryBuilder {
24318
24959
  let relatedRows = [];
24319
24960
  if (relatedIds.length > 0) {
24320
24961
  const relPlaceholders = relatedIds.map(() => "?").join(", ");
24321
- relatedRows = db.query(`SELECT * FROM ${rel.relatedTable} WHERE ${relatedPk} IN (${relPlaceholders})`).all(...relatedIds);
24962
+ relatedRows = await exec.all(`SELECT * FROM ${rel.relatedTable} WHERE ${relatedPk} IN (${relPlaceholders})`, relatedIds);
24322
24963
  }
24323
24964
  const relatedByPk = new Map;
24324
24965
  for (const r2 of relatedRows)
@@ -24352,75 +24993,77 @@ class ModelQueryBuilder {
24352
24993
  }
24353
24994
  }
24354
24995
  }
24355
- get() {
24356
- const db = getDatabase();
24996
+ async get() {
24997
+ const exec = getExecutor();
24357
24998
  const { sql: sql2, params } = this.buildQuery();
24358
- const rows = db.query(sql2).all(...params);
24999
+ const rows = await exec.all(sql2, params);
24359
25000
  const instances = rows.map((row) => new ModelInstance(this._definition, row));
24360
25001
  if (this._withRelations.length > 0) {
24361
- this.eagerLoadRelations(instances);
25002
+ await this.eagerLoadRelations(instances);
24362
25003
  }
24363
25004
  return instances;
24364
25005
  }
24365
- first() {
25006
+ async first() {
24366
25007
  this._limit = 1;
24367
- return this.get()[0];
25008
+ return (await this.get())[0];
24368
25009
  }
24369
- firstOrFail() {
24370
- const result = this.first();
25010
+ async firstOrFail() {
25011
+ const result = await this.first();
24371
25012
  if (!result)
24372
25013
  throw new Error(`No ${this._definition.name} found`);
24373
25014
  return result;
24374
25015
  }
24375
- last() {
25016
+ async last() {
24376
25017
  const pk = this._definition.primaryKey || "id";
24377
25018
  this._orderBy = [{ column: pk, direction: "desc" }];
24378
25019
  this._limit = 1;
24379
- return this.get()[0];
25020
+ return (await this.get())[0];
24380
25021
  }
24381
- count() {
24382
- const db = getDatabase();
25022
+ async count() {
25023
+ const exec = getExecutor();
24383
25024
  const params = [];
24384
25025
  let sql2 = `SELECT COUNT(*) as count FROM ${this._definition.table}`;
24385
- if (this._wheres.length > 0) {
24386
- sql2 += ` WHERE ${this.buildWhereClauses(params)}`;
25026
+ const whereBody = this.composeWhere(params);
25027
+ if (whereBody) {
25028
+ sql2 += ` WHERE ${whereBody}`;
24387
25029
  }
24388
- return db.query(sql2).get(...params).count;
25030
+ const row = await exec.get(sql2, params);
25031
+ return Number(row?.count ?? 0);
24389
25032
  }
24390
- exists() {
24391
- return this.count() > 0;
25033
+ async exists() {
25034
+ return await this.count() > 0;
24392
25035
  }
24393
- doesntExist() {
24394
- return this.count() === 0;
25036
+ async doesntExist() {
25037
+ return await this.count() === 0;
24395
25038
  }
24396
- sole() {
25039
+ async sole() {
24397
25040
  this._limit = 2;
24398
- const results = this.get();
25041
+ const results = await this.get();
24399
25042
  if (results.length === 0)
24400
25043
  throw new Error(`No ${this._definition.name} found`);
24401
25044
  if (results.length > 1)
24402
25045
  throw new Error(`Expected one ${this._definition.name}, found multiple`);
24403
25046
  return results[0];
24404
25047
  }
24405
- increment(column, amount = 1) {
25048
+ async increment(column, amount = 1) {
24406
25049
  assertValidIdentifier(column, "increment(column)");
24407
- const db = getDatabase();
25050
+ const exec = getExecutor();
24408
25051
  const params = [amount];
24409
25052
  let sql2 = `UPDATE ${this._definition.table} SET ${column} = ${column} + ?`;
24410
- if (this._definition.traits?.useTimestamps) {
25053
+ if (timestampsEnabled(this._definition)) {
24411
25054
  sql2 += `, updated_at = ?`;
24412
- params.push(new Date().toISOString());
25055
+ params.push(formatNow());
24413
25056
  }
24414
25057
  if (this._wheres.length > 0) {
24415
25058
  const clauses = this.buildWhereClauses(params);
24416
25059
  sql2 += ` WHERE ${clauses}`;
24417
25060
  }
24418
- return db.run(sql2, params).changes;
25061
+ return (await exec.run(sql2, params)).changes;
24419
25062
  }
24420
25063
  decrement(column, amount = 1) {
24421
25064
  return this.increment(column, -amount);
24422
25065
  }
24423
- chunk(size, callback) {
25066
+ async chunk(size, callback) {
24424
25067
  let page = 0;
24425
25068
  while (true) {
24426
25069
  const builder = new ModelQueryBuilder(this._definition);
@@ -24428,12 +25071,13 @@ class ModelQueryBuilder {
24428
25071
  builder._orderBy = [...this._orderBy];
24429
25072
  builder._select = [...this._select];
24430
25073
  builder._withRelations = [...this._withRelations];
25074
+ builder._trashed = this._trashed;
24431
25075
  builder._limit = size;
24432
25076
  builder._offset = page * size;
24433
- const results = builder.get();
25077
+ const results = await builder.get();
24434
25078
  if (results.length === 0)
24435
25079
  break;
24436
- const result = callback(results);
25080
+ const result = await callback(results);
24437
25081
  if (result === false)
24438
25082
  break;
24439
25083
  if (results.length < size)
@@ -24441,12 +25085,12 @@ class ModelQueryBuilder {
24441
25085
  page++;
24442
25086
  }
24443
25087
  }
24444
- paginate(page = 1, perPage = 15) {
24445
- const total = this.count();
25088
+ async paginate(page = 1, perPage = 15) {
25089
+ const total = await this.count();
24446
25090
  const lastPage = Math.ceil(total / perPage);
24447
25091
  this._limit = perPage;
24448
25092
  this._offset = (page - 1) * perPage;
24449
- const data = this.get();
25093
+ const data = await this.get();
24450
25094
  return {
24451
25095
  data,
24452
25096
  total,
@@ -24459,13 +25103,14 @@ class ModelQueryBuilder {
24459
25103
  to: data.length > 0 ? (page - 1) * perPage + data.length : null
24460
25104
  };
24461
25105
  }
24462
- pluck(column) {
25106
+ async pluck(column) {
24463
25107
  assertValidIdentifier(column, "pluck(column)");
24464
- const db = getDatabase();
25108
+ const exec = getExecutor();
24465
25109
  const params = [];
24466
25110
  let sql2 = `SELECT ${column} FROM ${this._definition.table}`;
24467
- if (this._wheres.length > 0) {
24468
- sql2 += ` WHERE ${this.buildWhereClauses(params)}`;
25111
+ const whereBody = this.composeWhere(params);
25112
+ if (whereBody) {
25113
+ sql2 += ` WHERE ${whereBody}`;
24469
25114
  }
24470
25115
  if (this._orderBy.length > 0) {
24471
25116
  sql2 += ` ORDER BY ${this._orderBy.map((o2) => `${o2.column} ${o2.direction.toUpperCase()}`).join(", ")}`;
@@ -24474,19 +25119,20 @@ class ModelQueryBuilder {
24474
25119
  sql2 += ` LIMIT ${this._limit}`;
24475
25120
  if (this._offset !== undefined)
24476
25121
  sql2 += ` OFFSET ${this._offset}`;
24477
- const rows = db.query(sql2).all(...params);
25122
+ const rows = await exec.all(sql2, params);
24478
25123
  return rows.map((r2) => r2[column]);
24479
25124
  }
24480
- aggregate(fn, column) {
25125
+ async aggregate(fn, column) {
24481
25126
  assertValidIdentifier(column, `${fn}(column)`);
24482
- const db = getDatabase();
25127
+ const exec = getExecutor();
24483
25128
  const params = [];
24484
25129
  let sql2 = `SELECT ${fn}(${column}) as v FROM ${this._definition.table}`;
24485
- if (this._wheres.length > 0) {
24486
- sql2 += ` WHERE ${this.buildWhereClauses(params)}`;
25130
+ const whereBody = this.composeWhere(params);
25131
+ if (whereBody) {
25132
+ sql2 += ` WHERE ${whereBody}`;
24487
25133
  }
24488
- const row = db.query(sql2).get(...params);
24489
- return row?.v ?? null;
25134
+ const row = await exec.get(sql2, params);
25135
+ return row?.v == null ? null : Number(row.v);
24490
25136
  }
24491
25137
  max(column) {
24492
25138
  return this.aggregate("MAX", column);
@@ -24494,34 +25140,34 @@ class ModelQueryBuilder {
24494
25140
  min(column) {
24495
25141
  return this.aggregate("MIN", column);
24496
25142
  }
24497
- avg(column) {
24498
- return this.aggregate("AVG", column) || 0;
25143
+ async avg(column) {
25144
+ return await this.aggregate("AVG", column) || 0;
24499
25145
  }
24500
- sum(column) {
24501
- return this.aggregate("SUM", column) || 0;
25146
+ async sum(column) {
25147
+ return await this.aggregate("SUM", column) || 0;
24502
25148
  }
24503
- delete() {
24504
- const db = getDatabase();
25149
+ async delete() {
25150
+ const exec = getExecutor();
24505
25151
  const params = [];
24506
25152
  let sql2 = `DELETE FROM ${this._definition.table}`;
24507
25153
  if (this._wheres.length > 0) {
24508
25154
  sql2 += ` WHERE ${this.buildWhereClauses(params)}`;
24509
25155
  }
24510
- return db.run(sql2, params).changes;
25156
+ return (await exec.run(sql2, params)).changes;
24511
25157
  }
24512
- update(data) {
24513
- const db = getDatabase();
25158
+ async update(data) {
25159
+ const exec = getExecutor();
24514
25160
  const entries = Object.entries(data);
24515
25161
  const sets = entries.map(([k2]) => `${k2} = ?`).join(", ");
24516
25162
  const params = entries.map(([, v2]) => v2);
24517
- if (this._definition.traits?.useTimestamps) {
24518
- params.push(new Date().toISOString());
25163
+ if (timestampsEnabled(this._definition)) {
25164
+ params.push(formatNow());
24519
25165
  }
24520
- let sql2 = `UPDATE ${this._definition.table} SET ${sets}${this._definition.traits?.useTimestamps ? ", updated_at = ?" : ""}`;
25166
+ let sql2 = `UPDATE ${this._definition.table} SET ${sets}${timestampsEnabled(this._definition) ? ", updated_at = ?" : ""}`;
24521
25167
  if (this._wheres.length > 0) {
24522
25168
  sql2 += ` WHERE ${this.buildWhereClauses(params)}`;
24523
25169
  }
24524
- return db.run(sql2, params).changes;
25170
+ return (await exec.run(sql2, params)).changes;
24525
25171
  }
24526
25172
  }
24527
25173
  function createModel(definition) {
@@ -24563,28 +25209,26 @@ function createModel(definition) {
24563
25209
  limit: (count) => new ModelQueryBuilder(definition).limit(count),
24564
25210
  take: (count) => new ModelQueryBuilder(definition).take(count),
24565
25211
  skip: (count) => new ModelQueryBuilder(definition).skip(count),
24566
- find(id) {
24567
- const db = getDatabase();
25212
+ withTrashed: () => new ModelQueryBuilder(definition).withTrashed(),
25213
+ onlyTrashed: () => new ModelQueryBuilder(definition).onlyTrashed(),
25214
+ async find(id) {
25215
+ const exec = getExecutor();
24568
25216
  const pk = definition.primaryKey || "id";
24569
- const sql2 = `SELECT * FROM ${definition.table} WHERE ${pk} = ?`;
24570
- let stmt = preparedStatementCache.get(sql2);
24571
- if (!stmt) {
24572
- stmt = db.prepare(sql2);
24573
- preparedStatementCache.set(sql2, stmt);
24574
- }
24575
- const row = stmt.get(id);
25217
+ const sd = softDeletesEnabled(definition) ? ` AND ${SOFT_DELETE_COLUMN} IS NULL` : "";
25218
+ const row = await exec.get(`SELECT * FROM ${definition.table} WHERE ${pk} = ?${sd}`, [id]);
24576
25219
  return row ? new ModelInstance(definition, row) : undefined;
24577
25220
  },
24578
- findOrFail(id) {
24579
- const result = model.find(id);
25221
+ async findOrFail(id) {
25222
+ const result = await model.find(id);
24580
25223
  if (!result)
24581
25224
  throw new Error(`${definition.name} with id ${id} not found`);
24582
25225
  return result;
24583
25226
  },
24584
- findMany(ids) {
24585
- const db = getDatabase();
25227
+ async findMany(ids) {
25228
+ const exec = getExecutor();
24586
25229
  const pk = definition.primaryKey || "id";
24587
- const rows = db.query(`SELECT * FROM ${definition.table} WHERE ${pk} IN (${ids.map(() => "?").join(", ")})`).all(...ids);
25230
+ const sd = softDeletesEnabled(definition) ? ` AND ${SOFT_DELETE_COLUMN} IS NULL` : "";
25231
+ const rows = await exec.all(`SELECT * FROM ${definition.table} WHERE ${pk} IN (${ids.map(() => "?").join(", ")})${sd}`, ids);
24588
25232
  return rows.map((row) => new ModelInstance(definition, row));
24589
25233
  },
24590
25234
  all: () => new ModelQueryBuilder(definition).get(),
@@ -24601,44 +25245,47 @@ function createModel(definition) {
24601
25245
  whereNotBetween(column, range) {
24602
25246
  return new ModelQueryBuilder(definition).whereNotBetween(column, range);
24603
25247
  },
24604
- create(data) {
25248
+ async create(data) {
24605
25249
  const instance = new ModelInstance(definition, data);
24606
- instance.save();
25250
+ await instance.save();
24607
25251
  return instance;
24608
25252
  },
24609
- createMany(items) {
24610
- return items.map((data) => this.create(data));
25253
+ async createMany(items) {
25254
+ const out = [];
25255
+ for (const data of items)
25256
+ out.push(await this.create(data));
25257
+ return out;
24611
25258
  },
24612
- updateOrCreate(search, data) {
25259
+ async updateOrCreate(search, data) {
24613
25260
  let query = new ModelQueryBuilder(definition);
24614
25261
  for (const [key, value] of Object.entries(search)) {
24615
25262
  query = query.where(key, value);
24616
25263
  }
24617
- const existing = query.first();
25264
+ const existing = await query.first();
24618
25265
  if (existing) {
24619
- existing.update(data);
25266
+ await existing.update(data);
24620
25267
  return existing;
24621
25268
  }
24622
25269
  return this.create({ ...search, ...data });
24623
25270
  },
24624
- firstOrCreate(search, data) {
25271
+ async firstOrCreate(search, data) {
24625
25272
  let query = new ModelQueryBuilder(definition);
24626
25273
  for (const [key, value] of Object.entries(search)) {
24627
25274
  query = query.where(key, value);
24628
25275
  }
24629
- const existing = query.first();
25276
+ const existing = await query.first();
24630
25277
  return existing || this.create({ ...search, ...data });
24631
25278
  },
24632
- destroy(id) {
24633
- const db = getDatabase();
25279
+ async destroy(id) {
25280
+ const exec = getExecutor();
24634
25281
  const pk = definition.primaryKey || "id";
24635
- return db.run(`DELETE FROM ${definition.table} WHERE ${pk} = ?`, [id]).changes > 0;
25282
+ return (await exec.run(`DELETE FROM ${definition.table} WHERE ${pk} = ?`, [id])).changes > 0;
24636
25283
  },
24637
25284
  remove(id) {
24638
25285
  return this.destroy(id);
24639
25286
  },
24640
- truncate() {
24641
- getDatabase().run(`DELETE FROM ${definition.table}`);
25287
+ async truncate() {
25288
+ await getExecutor().run(`DELETE FROM ${definition.table}`, []);
24642
25289
  },
24643
25290
  getDefinition: () => definition,
24644
25291
  getTable: () => definition.table,
@@ -24668,8 +25315,8 @@ function createModel(definition) {
24668
25315
  }
24669
25316
  });
24670
25317
  }
24671
- function createTableFromModel(definition) {
24672
- const db = getDatabase();
25318
+ async function createTableFromModel(definition) {
25319
+ const exec = getExecutor();
24673
25320
  const pk = definition.primaryKey || "id";
24674
25321
  const columns = [];
24675
25322
  columns.push(definition.autoIncrement !== false ? `${pk} INTEGER PRIMARY KEY AUTOINCREMENT` : `${pk} INTEGER PRIMARY KEY`);
@@ -24696,13 +25343,13 @@ function createTableFromModel(definition) {
24696
25343
  }
24697
25344
  columns.push(colDef);
24698
25345
  }
24699
- if (definition.traits?.useTimestamps) {
25346
+ if (timestampsEnabled(definition)) {
24700
25347
  columns.push("created_at TEXT", "updated_at TEXT");
24701
25348
  }
24702
- if (definition.traits?.useSoftDeletes) {
25349
+ if (softDeletesEnabled(definition)) {
24703
25350
  columns.push("deleted_at TEXT");
24704
25351
  }
24705
- db.run(`CREATE TABLE IF NOT EXISTS ${definition.table} (${columns.join(", ")})`);
25352
+ await exec.run(`CREATE TABLE IF NOT EXISTS ${definition.table} (${columns.join(", ")})`, []);
24706
25353
  }
24707
25354
  function createFakerCompatLayer(underlying) {
24708
25355
  return new Proxy(underlying, {
@@ -24726,7 +25373,7 @@ function createFakerCompatLayer(underlying) {
24726
25373
  });
24727
25374
  }
24728
25375
  async function seedModel(definition, count, faker) {
24729
- const db = getDatabase();
25376
+ const exec = getExecutor();
24730
25377
  const seeder = definition.traits?.useSeeder;
24731
25378
  const seedCount = count ?? (typeof seeder === "object" && seeder ? seeder.count : 10);
24732
25379
  if (!faker) {
@@ -24744,24 +25391,25 @@ async function seedModel(definition, count, faker) {
24744
25391
  if (attr.factory)
24745
25392
  data[name] = attr.factory(faker);
24746
25393
  }
24747
- if (definition.traits?.useTimestamps) {
24748
- const now = new Date().toISOString();
25394
+ if (timestampsEnabled(definition)) {
25395
+ const now = formatNow();
24749
25396
  data.created_at = now;
24750
25397
  data.updated_at = now;
24751
25398
  }
24752
25399
  if (definition.traits?.useUuid)
24753
25400
  data.uuid = crypto.randomUUID();
24754
25401
  const columns = Object.keys(data);
24755
- db.run(`INSERT INTO ${definition.table} (${columns.join(", ")}) VALUES (${columns.map(() => "?").join(", ")})`, Object.values(data));
25402
+ await exec.run(`INSERT INTO ${definition.table} (${columns.join(", ")}) VALUES (${columns.map(() => "?").join(", ")})`, Object.values(data));
24756
25403
  }
24757
25404
  }
24758
- var SAFE_SQL_IDENTIFIER, _getModel = null, globalDb = null, snakeCaseCache, tableNameCache, relationCache, preparedStatementCache;
25405
+ var SAFE_SQL_IDENTIFIER, _getModel = null, globalDb = null, _executor = null, _executorForDb = null, _executorDialect = null, _executorDatabase = null, SOFT_DELETE_COLUMN = "deleted_at", BTM_RELATED_ALIAS = "__btm_rel__", snakeCaseCache, tableNameCache, relationCache;
24759
25406
  var init_orm = __esm(() => {
25407
+ init_config();
25408
+ init_db();
24760
25409
  SAFE_SQL_IDENTIFIER = /^[A-Z_][A-Z0-9_]*$/i;
24761
25410
  snakeCaseCache = new Map;
24762
25411
  tableNameCache = new Map;
24763
25412
  relationCache = new Map;
24764
- preparedStatementCache = new Map;
24765
25413
  });
24766
25414
 
24767
25415
  // src/model.ts
@@ -26210,9 +26858,9 @@ function buildDatabaseSchema(models) {
26210
26858
  // src/loader.ts
26211
26859
  import { readdirSync as readdirSync12, statSync as statSync5 } from "fs";
26212
26860
  import { basename, extname as extname4 } from "path";
26213
- import process33 from "process";
26861
+ import process31 from "process";
26214
26862
  async function loadModels(options) {
26215
- const cwd = options.cwd ?? process33.cwd();
26863
+ const cwd = options.cwd ?? process31.cwd();
26216
26864
  const dir = options.modelsDir.startsWith("/") ? options.modelsDir : `${cwd}/${options.modelsDir}`;
26217
26865
  const result = {};
26218
26866
  const entries = readdirSync12(dir);
@@ -26239,6 +26887,23 @@ async function loadModels(options) {
26239
26887
  }
26240
26888
  var init_loader = () => {};
26241
26889
 
26890
+ // src/relation-utils.ts
26891
+ function normalizeRelationEntry(entry) {
26892
+ if (typeof entry === "string")
26893
+ return { model: entry };
26894
+ if (entry && typeof entry === "object" && typeof entry.model === "string") {
26895
+ const e2 = entry;
26896
+ return { model: e2.model, foreignKey: e2.foreignKey, onDelete: e2.onDelete };
26897
+ }
26898
+ return null;
26899
+ }
26900
+ function normalizeRelationList(rel) {
26901
+ if (!rel)
26902
+ return [];
26903
+ const entries = Array.isArray(rel) ? rel : typeof rel === "object" ? Object.values(rel) : [];
26904
+ return entries.map(normalizeRelationEntry).filter((x2) => x2 !== null);
26905
+ }
26906
+
26242
26907
  // src/meta.ts
26243
26908
  function buildSchemaMeta(models) {
26244
26909
  const modelToTable = {};
@@ -26256,13 +26921,24 @@ function buildSchemaMeta(models) {
26256
26921
  const toRecord = (v2) => {
26257
26922
  if (!v2)
26258
26923
  return {};
26924
+ const rec = {};
26259
26925
  if (Array.isArray(v2)) {
26260
- const rec = {};
26261
- for (const relName of v2)
26262
- rec[relName] = relName;
26926
+ for (const item of v2) {
26927
+ const n2 = normalizeRelationEntry(item);
26928
+ if (n2)
26929
+ rec[n2.model] = n2.model;
26930
+ }
26263
26931
  return rec;
26264
26932
  }
26265
- return v2;
26933
+ if (typeof v2 === "object") {
26934
+ for (const [key, val] of Object.entries(v2)) {
26935
+ const n2 = normalizeRelationEntry(val);
26936
+ if (n2)
26937
+ rec[key] = n2.model;
26938
+ }
26939
+ return rec;
26940
+ }
26941
+ return {};
26266
26942
  };
26267
26943
  const toBelongsToManyRecord = (v2) => {
26268
26944
  if (!v2)
@@ -26309,11 +26985,12 @@ function buildSchemaMeta(models) {
26309
26985
  }
26310
26986
  return { modelToTable, tableToModel, primaryKeys, relations, scopes: scopesByTable, models };
26311
26987
  }
26988
+ var init_meta = () => {};
26312
26989
 
26313
26990
  // src/migrations.ts
26314
26991
  import { existsSync as existsSync21, mkdirSync as mkdirSync9, writeFileSync as writeFileSync14 } from "fs";
26315
26992
  import { dirname as dirname11, join as join17 } from "path";
26316
- import process35 from "process";
26993
+ import process33 from "process";
26317
26994
  function info2(message) {
26318
26995
  if (config5.verbose)
26319
26996
  console.log(message);
@@ -26329,10 +27006,10 @@ function findWorkspaceRoot6(startPath) {
26329
27006
  }
26330
27007
  currentPath = dirname11(currentPath);
26331
27008
  }
26332
- return process35.cwd();
27009
+ return process33.cwd();
26333
27010
  }
26334
27011
  function ensureSqlDirectory2() {
26335
- const workspaceRoot = findWorkspaceRoot6(process35.cwd());
27012
+ const workspaceRoot = findWorkspaceRoot6(process33.cwd());
26336
27013
  const sqlDir = join17(workspaceRoot, "database", "migrations");
26337
27014
  if (!existsSync21(sqlDir)) {
26338
27015
  mkdirSync9(sqlDir, { recursive: true });
@@ -26471,15 +27148,6 @@ function detectTypeFromValidationRule(rule) {
26471
27148
  }
26472
27149
  return;
26473
27150
  }
26474
- function normalizeBelongsTo(belongsTo) {
26475
- if (!belongsTo)
26476
- return [];
26477
- if (Array.isArray(belongsTo))
26478
- return belongsTo;
26479
- if (typeof belongsTo === "object")
26480
- return Object.values(belongsTo);
26481
- return [];
26482
- }
26483
27151
  function buildMigrationPlan2(models, options) {
26484
27152
  const meta = buildSchemaMeta(models);
26485
27153
  const tables = [];
@@ -26568,12 +27236,12 @@ function buildMigrationPlan2(models, options) {
26568
27236
  }
26569
27237
  columns.push(col);
26570
27238
  }
26571
- const belongsToModels = normalizeBelongsTo(model.belongsTo);
26572
- for (const relatedModel of belongsToModels) {
26573
- const fkColumnName = `${snakeCase(relatedModel)}_id`;
27239
+ const belongsToRelations = normalizeRelationList(model.belongsTo);
27240
+ for (const rel of belongsToRelations) {
27241
+ const fkColumnName = rel.foreignKey ?? `${snakeCase(rel.model)}_id`;
26574
27242
  if (columns.some((c2) => c2.name === fkColumnName))
26575
27243
  continue;
26576
- const refTable = meta.modelToTable[relatedModel];
27244
+ const refTable = meta.modelToTable[rel.model];
26577
27245
  if (!refTable)
26578
27246
  continue;
26579
27247
  const refPk = meta.primaryKeys[refTable] ?? "id";
@@ -26584,7 +27252,7 @@ function buildMigrationPlan2(models, options) {
26584
27252
  isUnique: false,
26585
27253
  isNullable: true,
26586
27254
  hasDefault: false,
26587
- references: { table: refTable, column: refPk }
27255
+ references: { table: refTable, column: refPk, onDelete: rel.onDelete }
26588
27256
  });
26589
27257
  }
26590
27258
  const traits = model.traits;
@@ -26901,6 +27569,13 @@ function columnsAreDifferent(col1, col2) {
26901
27569
  }
26902
27570
  return false;
26903
27571
  }
27572
+ function referencesAreDifferent(r1, r2) {
27573
+ if (Boolean(r1) !== Boolean(r2))
27574
+ return true;
27575
+ if (!r1 || !r2)
27576
+ return false;
27577
+ return r1.table !== r2.table || r1.column !== r2.column || r1.onDelete !== r2.onDelete || r1.onUpdate !== r2.onUpdate;
27578
+ }
26904
27579
  function mapIndexesByKey(indexes) {
26905
27580
  const map = {};
26906
27581
  for (const i2 of indexes) {
@@ -27044,6 +27719,13 @@ function generateDiffSql(previous, next) {
27044
27719
  info2(`-- Detected column type change: ${curr.table}.${colName} (${prevCol.type} -> ${currCol.type})`);
27045
27720
  hasChanges = true;
27046
27721
  }
27722
+ if (referencesAreDifferent(prevCol.references, currCol.references) && currCol.references) {
27723
+ const addFkStatement = driver.addForeignKey(curr.table, currCol.name, currCol.references.table, currCol.references.column, currCol.references.onDelete, currCol.references.onUpdate);
27724
+ tableChanges.push(addFkStatement);
27725
+ chunks.push(addFkStatement);
27726
+ info2(`-- Detected foreign-key change: ${curr.table}.${colName} -> ${currCol.references.table}(${currCol.references.column})${currCol.references.onDelete ? ` ON DELETE ${currCol.references.onDelete}` : ""}`);
27727
+ hasChanges = true;
27728
+ }
27047
27729
  }
27048
27730
  }
27049
27731
  for (const colName of Object.keys(currCols)) {
@@ -27097,6 +27779,7 @@ var migrationCounter = 0, migrationsCreatedCount = 0, migrationsUpdatedCount = 0
27097
27779
  var init_migrations = __esm(() => {
27098
27780
  init_config();
27099
27781
  init_drivers();
27782
+ init_meta();
27100
27783
  });
27101
27784
 
27102
27785
  // src/schema.ts
@@ -27131,6 +27814,7 @@ var init_src2 = __esm(async () => {
27131
27814
  init_dynamodb_tooling_adapter();
27132
27815
  init_dynamodb();
27133
27816
  init_loader();
27817
+ init_meta();
27134
27818
  init_migrations();
27135
27819
  init_orm();
27136
27820
  });
@@ -27148,6 +27832,7 @@ export {
27148
27832
  tableInfo,
27149
27833
  startConsole,
27150
27834
  stacksModelToEntity,
27835
+ sqlTypeToAttr,
27151
27836
  sql,
27152
27837
  modelShow as showModel,
27153
27838
  setQueryCacheMaxSize,
@@ -27168,6 +27853,7 @@ export {
27168
27853
  parseStacksModels,
27169
27854
  dbOptimize as optimizeDatabase,
27170
27855
  modelShow,
27856
+ modelNameForTable,
27171
27857
  migrateStatus,
27172
27858
  migrateRollback,
27173
27859
  migrateModels,
@@ -27180,6 +27866,7 @@ export {
27180
27866
  iterateAllPivots,
27181
27867
  isDefinitionEqual,
27182
27868
  isBrowser,
27869
+ introspectDatabase,
27183
27870
  introspect,
27184
27871
  inspectTable,
27185
27872
  importData,
@@ -27197,6 +27884,7 @@ export {
27197
27884
  getAllModels,
27198
27885
  generateSqlString,
27199
27886
  generateSql,
27887
+ generateModelSource,
27200
27888
  generateMigration,
27201
27889
  generateDiffSqlString,
27202
27890
  generateDiffSql,
@@ -27206,6 +27894,8 @@ export {
27206
27894
  file,
27207
27895
  extractTableDefinition,
27208
27896
  extractModelSchema,
27897
+ extractInsertId,
27898
+ extractChanges,
27209
27899
  exportData,
27210
27900
  queryExplainAll as explainAllQueries,
27211
27901
  explain,