bun-query-builder 0.1.58 → 0.1.60

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/bin/cli.js CHANGED
@@ -12062,6 +12062,13 @@ function createSQLiteSQL(filename) {
12062
12062
  sqlFunction._wrapper = wrapper;
12063
12063
  return sqlFunction;
12064
12064
  }
12065
+ function sqliteFileFromUrl(url) {
12066
+ const stripped = url.replace(/^(?:sqlite|sqlite3|file):/i, "");
12067
+ if (stripped === url)
12068
+ return url;
12069
+ const path = stripped.startsWith("///") ? stripped.slice(2) : stripped.startsWith("//") ? stripped.slice(2) : stripped;
12070
+ return path || ":memory:";
12071
+ }
12065
12072
  function createConnectionString(dialect, dbConfig) {
12066
12073
  if ((dialect === "postgres" || dialect === "mysql" || dialect === "singlestore") && process19.env.DB_CONNECTION === dialect) {
12067
12074
  const e = process19.env;
@@ -12075,7 +12082,7 @@ function createConnectionString(dialect, dbConfig) {
12075
12082
  return `${scheme}://${envUser}:${envPass}@${envHost}${envPort ? `:${envPort}` : ""}/${envDb}${ssl2}`;
12076
12083
  }
12077
12084
  if (dbConfig.url) {
12078
- return dbConfig.url;
12085
+ return dialect === "sqlite" ? sqliteFileFromUrl(dbConfig.url) : dbConfig.url;
12079
12086
  }
12080
12087
  const { database, username, password, host = "localhost", port, ssl } = dbConfig;
12081
12088
  const sslQ = ssl || process19.env.DB_SSL === "true" || process19.env.DB_SSL === "1" ? "?ssl=true" : "";
@@ -12351,9 +12358,37 @@ var init_benchmark = __esm(() => {
12351
12358
  init_src2();
12352
12359
  });
12353
12360
 
12361
+ // src/inflect.ts
12362
+ function stripTrailingS(name) {
12363
+ return name.endsWith("s") ? name.slice(0, -1) : name;
12364
+ }
12365
+ function inflectSingular(name) {
12366
+ if (name.endsWith("ies") && name.length > 3) {
12367
+ const stem = name.slice(0, -3);
12368
+ const prev = stem[stem.length - 1];
12369
+ if (prev && !"aeiou".includes(prev))
12370
+ return `${stem}y`;
12371
+ }
12372
+ if (name.endsWith("es") && name.length > 2) {
12373
+ const stem = name.slice(0, -2);
12374
+ if (stem.endsWith("s") || stem.endsWith("x") || stem.endsWith("ch") || stem.endsWith("sh"))
12375
+ return stem;
12376
+ }
12377
+ if (name.endsWith("s") && !name.endsWith("ss") && !name.endsWith("us") && !name.endsWith("is"))
12378
+ return name.slice(0, -1);
12379
+ return name;
12380
+ }
12381
+ function singularizerFor(strategy) {
12382
+ if (strategy === "none")
12383
+ return (name) => name;
12384
+ if (strategy === "inflect")
12385
+ return inflectSingular;
12386
+ return stripTrailingS;
12387
+ }
12388
+
12354
12389
  // src/pivot.ts
12355
12390
  function defaultSingularize(name) {
12356
- return name.endsWith("s") ? name.slice(0, -1) : name;
12391
+ return singularizerFor(config5.relations?.singularizeStrategy)(name);
12357
12392
  }
12358
12393
  function resolvePivot(meta, parentTable, relationKey, options = {}) {
12359
12394
  const singularize = options.singularize ?? defaultSingularize;
@@ -12430,6 +12465,9 @@ function* iterateAllPivots(meta, options = {}) {
12430
12465
  }
12431
12466
  }
12432
12467
  }
12468
+ var init_pivot = __esm(() => {
12469
+ init_config();
12470
+ });
12433
12471
 
12434
12472
  // src/client.ts
12435
12473
  function isRawExpression(expr) {
@@ -12610,7 +12648,13 @@ class QueryCache {
12610
12648
  this.cache.clear();
12611
12649
  }
12612
12650
  setMaxSize(size) {
12613
- this.maxSize = size;
12651
+ this.maxSize = Math.max(0, Math.floor(size));
12652
+ while (this.cache.size > this.maxSize) {
12653
+ const lruKey = this.cache.keys().next().value;
12654
+ if (lruKey === undefined)
12655
+ break;
12656
+ this.cache.delete(lruKey);
12657
+ }
12614
12658
  }
12615
12659
  }
12616
12660
  function isRetriableTxError(err) {
@@ -12715,8 +12759,7 @@ function computeBackoffMs(attempt, cfg) {
12715
12759
  const max = Math.max(base, cfg?.maxMs ?? 2000);
12716
12760
  let ms = Math.min(max, base * factor ** Math.max(0, attempt - 1));
12717
12761
  if (cfg?.jitter) {
12718
- const jitter = Math.random() * ms * 0.2;
12719
- ms = ms - jitter / 2;
12762
+ ms = Math.min(max, ms * (0.9 + Math.random() * 0.2));
12720
12763
  }
12721
12764
  return Math.floor(ms);
12722
12765
  }
@@ -12968,11 +13011,7 @@ function createQueryBuilder(state) {
12968
13011
  const pivotColumns = new Map;
12969
13012
  const pivotConfigRelations = new Set;
12970
13013
  let cacheTtl = 60000;
12971
- const singularize = (name) => {
12972
- if (config5.relations.singularizeStrategy === "none")
12973
- return name;
12974
- return name.endsWith("s") ? name.slice(0, -1) : name;
12975
- };
13014
+ const singularize = (name) => singularizerFor(config5.relations.singularizeStrategy)(name);
12976
13015
  const resolvePivotLocal = (relationKey) => {
12977
13016
  if (!meta)
12978
13017
  return null;
@@ -15448,6 +15487,7 @@ function createQueryBuilder(state) {
15448
15487
  const colCount = keys.length;
15449
15488
  const totalParams = rowCount * colCount;
15450
15489
  params.length = totalParams;
15490
+ const columnList = isPostgres ? keys.map((k) => quoteId(k)).join(",") : keys.join(",");
15451
15491
  if (rowCount === 1) {
15452
15492
  if (!isPostgres) {
15453
15493
  let cols = quoteId(keys[0]);
@@ -15460,7 +15500,6 @@ function createQueryBuilder(state) {
15460
15500
  }
15461
15501
  sqlText = `INSERT INTO ${quoteId(table)}(${cols})VALUES(${placeholders})`;
15462
15502
  } else {
15463
- const columnList = keys.map((k) => quoteId(k)).join(",");
15464
15503
  sqlText = `INSERT INTO ${quoteId(table)}(${columnList})VALUES(`;
15465
15504
  for (let c = 0;c < colCount; c++) {
15466
15505
  if (c > 0)
@@ -15471,8 +15510,8 @@ function createQueryBuilder(state) {
15471
15510
  sqlText += ")";
15472
15511
  }
15473
15512
  } else {
15474
- const columnList = keys.map((k) => quoteId(k)).join(",");
15475
- sqlText = `INSERT INTO ${quoteId(table)}(${columnList})VALUES`;
15513
+ const columnList2 = keys.map((k) => quoteId(k)).join(",");
15514
+ sqlText = `INSERT INTO ${quoteId(table)}(${columnList2})VALUES`;
15476
15515
  let pidx = 0;
15477
15516
  for (let r = 0;r < rowCount; r++) {
15478
15517
  const row = rows[r];
@@ -16028,11 +16067,13 @@ function createQueryBuilder(state) {
16028
16067
  }
16029
16068
  },
16030
16069
  async savepoint(fn) {
16070
+ if (state?.inTransaction !== true)
16071
+ throw new Error("savepoint() must be called inside a transaction");
16031
16072
  const s = _sql;
16032
16073
  if (!s || typeof s.savepoint !== "function")
16033
- throw new Error("savepoint() must be called inside a transaction");
16074
+ throw new Error("savepoint() is not supported by the active connection");
16034
16075
  return await s.savepoint(async (sp) => {
16035
- const qb = createQueryBuilder({ sql: sp, meta, schema });
16076
+ const qb = createQueryBuilder({ sql: sp, meta, schema, inTransaction: true });
16036
16077
  return await fn(qb);
16037
16078
  });
16038
16079
  },
@@ -16449,6 +16490,7 @@ var SQL_PATTERNS, SAFE_WHERE_OPERATORS, warnedSqlFragmentContexts, queryCache, r
16449
16490
  var init_client = __esm(() => {
16450
16491
  init_config();
16451
16492
  init_db();
16493
+ init_pivot();
16452
16494
  SQL_PATTERNS = {
16453
16495
  SELECT_STAR: /^SELECT\s+\*/i,
16454
16496
  SELECT: /^SELECT\s+/i,
@@ -17049,7 +17091,7 @@ var init_db_info = __esm(() => {
17049
17091
  // src/actions/db-optimize.ts
17050
17092
  import process22 from "process";
17051
17093
  async function dbOptimize(options = {}) {
17052
- const dialect = options.dialect || process22.env.DB_DIALECT || "postgres";
17094
+ const dialect = options.dialect || process22.env.DB_DIALECT || config5.dialect || "postgres";
17053
17095
  const aggressive = options.aggressive || false;
17054
17096
  if (options.verbose) {
17055
17097
  console.log(`Optimizing ${dialect} database${aggressive ? " (aggressive mode)" : ""}...`);
@@ -17144,7 +17186,7 @@ var init_db_optimize = __esm(() => {
17144
17186
  // src/actions/db-wipe.ts
17145
17187
  import process23 from "process";
17146
17188
  async function dbWipe(options = {}) {
17147
- const dialect = options.dialect || process23.env.DB_DIALECT || "postgres";
17189
+ const dialect = options.dialect || process23.env.DB_DIALECT || config5.dialect || "postgres";
17148
17190
  if (options.verbose) {
17149
17191
  console.log(`Wiping all tables from ${dialect} database...`);
17150
17192
  }
@@ -18762,7 +18804,7 @@ async function resetDatabase(dir, opts = {}) {
18762
18804
  if (!dir) {
18763
18805
  dir = join9(process25.cwd(), "app/Models");
18764
18806
  }
18765
- const dialect = opts.dialect || "postgres";
18807
+ const dialect = opts.dialect || config5.dialect || "postgres";
18766
18808
  const driver = getDialectDriver(dialect);
18767
18809
  const workspaceRoot = getWorkspaceRoot();
18768
18810
  try {
@@ -18862,7 +18904,7 @@ async function deleteMigrationFiles(dir, workspaceRoot, opts = {}) {
18862
18904
  if (!workspaceRoot) {
18863
18905
  workspaceRoot = getWorkspaceRoot();
18864
18906
  }
18865
- const dialect = String(opts.dialect || "postgres");
18907
+ const dialect = String(opts.dialect || config5.dialect || "postgres");
18866
18908
  const snapshotPath = getSnapshotPath(workspaceRoot, dialect);
18867
18909
  if (existsSync16(snapshotPath)) {
18868
18910
  unlinkSync(snapshotPath);
@@ -20018,6 +20060,23 @@ var init_actions = __esm(() => {
20018
20060
  init_wait_ready();
20019
20061
  });
20020
20062
 
20063
+ // src/relation-utils.ts
20064
+ function normalizeRelationEntry(entry) {
20065
+ if (typeof entry === "string")
20066
+ return { model: entry };
20067
+ if (entry && typeof entry === "object" && typeof entry.model === "string") {
20068
+ const e = entry;
20069
+ return { model: e.model, foreignKey: e.foreignKey, onDelete: e.onDelete };
20070
+ }
20071
+ return null;
20072
+ }
20073
+ function normalizeRelationList(rel) {
20074
+ if (!rel)
20075
+ return [];
20076
+ const entries = Array.isArray(rel) ? rel : typeof rel === "object" ? Object.values(rel) : [];
20077
+ return entries.map(normalizeRelationEntry).filter((x) => x !== null);
20078
+ }
20079
+
20021
20080
  // ../../node_modules/@stacksjs/ts-faker/dist/chunk-aggdkeg4.js
20022
20081
  import { existsSync as zS } from "fs";
20023
20082
  import { join as YS } from "path";
@@ -24676,6 +24735,13 @@ function normalizeAttributeKeys(data) {
24676
24735
  }
24677
24736
  return result;
24678
24737
  }
24738
+ function normalizeAttributeKeysInPlace(data) {
24739
+ for (const key in data) {
24740
+ if (toSnakeCase(key) !== key)
24741
+ return normalizeAttributeKeys(data);
24742
+ }
24743
+ return data;
24744
+ }
24679
24745
  function findAttributeDef(attrs, key) {
24680
24746
  if (attrs[key])
24681
24747
  return attrs[key];
@@ -24697,7 +24763,7 @@ class ModelInstance {
24697
24763
  _relations = {};
24698
24764
  constructor(definition, attributes = {}) {
24699
24765
  this._definition = definition;
24700
- this._attributes = normalizeAttributeKeys({ ...attributes });
24766
+ this._attributes = normalizeAttributeKeysInPlace({ ...attributes });
24701
24767
  this._original = null;
24702
24768
  const btmKeys = collectBelongsToManyKeys(definition);
24703
24769
  if (btmKeys.size === 0)
@@ -24753,12 +24819,12 @@ class ModelInstance {
24753
24819
  only(keys) {
24754
24820
  const out = {};
24755
24821
  for (const k2 of keys) {
24756
- out[k2] = this._attributes[k2];
24822
+ out[k2] = this._attributes[toSnakeCase(k2)];
24757
24823
  }
24758
24824
  return out;
24759
24825
  }
24760
24826
  except(keys) {
24761
- const drop = new Set(keys.map((k2) => k2));
24827
+ const drop = new Set(keys.map((k2) => toSnakeCase(k2)));
24762
24828
  const out = {};
24763
24829
  for (const k2 of Object.keys(this._attributes)) {
24764
24830
  if (!drop.has(k2))
@@ -24786,7 +24852,8 @@ class ModelInstance {
24786
24852
  if (this._original === null)
24787
24853
  return false;
24788
24854
  if (column) {
24789
- return this._attributes[column] !== this._original[column];
24855
+ const key = toSnakeCase(column);
24856
+ return this._attributes[key] !== this._original[key];
24790
24857
  }
24791
24858
  return Object.keys(this._attributes).some((k2) => this._attributes[k2] !== this._original[k2]);
24792
24859
  }
@@ -24795,7 +24862,7 @@ class ModelInstance {
24795
24862
  }
24796
24863
  getOriginal(column) {
24797
24864
  const orig = this.getOriginalAttributes();
24798
- return orig[column];
24865
+ return orig[toSnakeCase(column)];
24799
24866
  }
24800
24867
  getChanges() {
24801
24868
  if (this._original === null)
@@ -24834,7 +24901,7 @@ class ModelInstance {
24834
24901
  const setters = this._definition.set || {};
24835
24902
  for (const [key, setter] of Object.entries(setters)) {
24836
24903
  if (this.isDirty(key)) {
24837
- this._attributes[key] = setter(this._attributes);
24904
+ this._attributes[toSnakeCase(key)] = setter(this._attributes);
24838
24905
  }
24839
24906
  }
24840
24907
  if (this._attributes[pk]) {
@@ -24997,6 +25064,9 @@ class ModelInstance {
24997
25064
  return json;
24998
25065
  }
24999
25066
  }
25067
+ function sqlColumn(name) {
25068
+ return toSnakeCase(name);
25069
+ }
25000
25070
  function toSnakeCase(str) {
25001
25071
  let cached = snakeCaseCache.get(str);
25002
25072
  if (cached !== undefined)
@@ -25045,6 +25115,34 @@ function resolveRelation(definition, relationName) {
25045
25115
  }
25046
25116
  return null;
25047
25117
  }
25118
+ function findBelongsToEntry() {
25119
+ const rel = definition.belongsTo;
25120
+ if (!rel)
25121
+ return null;
25122
+ const lower = relationName.toLowerCase();
25123
+ const unwrap = (entry) => {
25124
+ if (typeof entry === "string")
25125
+ return { model: entry };
25126
+ if (entry && typeof entry === "object" && typeof entry.model === "string")
25127
+ return { model: entry.model, foreignKey: entry.foreignKey };
25128
+ return null;
25129
+ };
25130
+ if (Array.isArray(rel)) {
25131
+ for (const item of rel) {
25132
+ const e2 = unwrap(item);
25133
+ if (e2 && e2.model.toLowerCase() === lower)
25134
+ return e2;
25135
+ }
25136
+ return null;
25137
+ }
25138
+ if (typeof rel === "object") {
25139
+ for (const [key, value] of Object.entries(rel)) {
25140
+ if (key === relationName || key.toLowerCase() === lower)
25141
+ return unwrap(value);
25142
+ }
25143
+ }
25144
+ return null;
25145
+ }
25048
25146
  function findBelongsToManyEntry() {
25049
25147
  const rel = definition.belongsToMany;
25050
25148
  if (!rel)
@@ -25084,12 +25182,13 @@ function resolveRelation(definition, relationName) {
25084
25182
  const foreignKey = toSnakeCase(parentName) + "_id";
25085
25183
  return { type: "hasOne", relatedModelName: hasOneModel, relatedTable, foreignKey, localKey: parentPk };
25086
25184
  }
25087
- const belongsToModel = findModelName(definition.belongsTo);
25088
- if (belongsToModel) {
25185
+ const belongsToEntry = findBelongsToEntry();
25186
+ if (belongsToEntry) {
25187
+ const belongsToModel = belongsToEntry.model;
25089
25188
  const relatedModel = getModelFromRegistry(belongsToModel);
25090
25189
  const relatedTable = relatedModel?.getTable?.() || toTableName(belongsToModel);
25091
25190
  const relatedPk = relatedModel?.getDefinition?.()?.primaryKey || "id";
25092
- const foreignKey = toSnakeCase(belongsToModel) + "_id";
25191
+ const foreignKey = belongsToEntry.foreignKey || `${toSnakeCase(belongsToModel)}_id`;
25093
25192
  return { type: "belongsTo", relatedModelName: belongsToModel, relatedTable, foreignKey, localKey: relatedPk };
25094
25193
  }
25095
25194
  const found = findBelongsToManyEntry();
@@ -25111,12 +25210,12 @@ function resolveRelation(definition, relationName) {
25111
25210
  }
25112
25211
  pivotTable = throughModel.getTable?.() || throughModel.getDefinition?.()?.table || toTableName(config6.through);
25113
25212
  } else {
25114
- const a2 = parentTable.endsWith("s") ? parentTable.slice(0, -1) : parentTable;
25115
- const b2 = relatedTable.endsWith("s") ? relatedTable.slice(0, -1) : relatedTable;
25213
+ const a2 = singularizeWord(parentTable);
25214
+ const b2 = singularizeWord(relatedTable);
25116
25215
  pivotTable = [a2, b2].sort().join("_");
25117
25216
  }
25118
- const pivotFkParent = config6?.foreignKey || `${parentTable.endsWith("s") ? parentTable.slice(0, -1) : parentTable}_id`;
25119
- const pivotFkRelated = config6?.relatedKey || `${relatedTable.endsWith("s") ? relatedTable.slice(0, -1) : relatedTable}_id`;
25217
+ const pivotFkParent = config6?.foreignKey || `${singularizeWord(parentTable)}_id`;
25218
+ const pivotFkRelated = config6?.relatedKey || `${singularizeWord(relatedTable)}_id`;
25120
25219
  const pivotColumns = [];
25121
25220
  if (config6?.pivot?.columns) {
25122
25221
  for (const k2 of Object.keys(config6.pivot.columns))
@@ -25184,8 +25283,17 @@ function resolveRelation(definition, relationName) {
25184
25283
  return hot;
25185
25284
  return null;
25186
25285
  }
25286
+ function distinctParentIds(instances, pk) {
25287
+ const seen = new Set;
25288
+ for (const i2 of instances) {
25289
+ const v2 = i2.get(pk);
25290
+ if (v2 != null)
25291
+ seen.add(v2);
25292
+ }
25293
+ return [...seen];
25294
+ }
25187
25295
  function singularizeWord(table) {
25188
- return table.endsWith("s") ? table.slice(0, -1) : table;
25296
+ return singularizerFor(config5.relations?.singularizeStrategy)(table);
25189
25297
  }
25190
25298
 
25191
25299
  class BelongsToManyRelationBuilder {
@@ -25588,7 +25696,7 @@ class ModelQueryBuilder {
25588
25696
  }
25589
25697
  whereNotBetween(column, range) {
25590
25698
  assertValidIdentifier(column, "whereNotBetween(column)");
25591
- const col = column;
25699
+ const col = sqlColumn(column);
25592
25700
  this._wheres.push({
25593
25701
  raw: `(${col} < ? OR ${col} > ?)`,
25594
25702
  rawParams: [range[0], range[1]],
@@ -25634,7 +25742,10 @@ class ModelQueryBuilder {
25634
25742
  return this;
25635
25743
  }
25636
25744
  with(...relations) {
25637
- this._withRelations = relations;
25745
+ for (const r2 of relations) {
25746
+ if (!this._withRelations.includes(r2))
25747
+ this._withRelations.push(r2);
25748
+ }
25638
25749
  return this;
25639
25750
  }
25640
25751
  getWithRelations() {
@@ -25650,13 +25761,14 @@ class ModelQueryBuilder {
25650
25761
  if (w.rawParams && w.rawParams.length > 0)
25651
25762
  params.push(...w.rawParams);
25652
25763
  } else if (w.value === null) {
25653
- clause = w.operator === "=" ? `${w.column} IS NULL` : `${w.column} IS NOT NULL`;
25764
+ const col = sqlColumn(w.column);
25765
+ clause = w.operator === "=" ? `${col} IS NULL` : `${col} IS NOT NULL`;
25654
25766
  } else if (w.operator === "in" || w.operator === "not in") {
25655
25767
  const arr = w.value;
25656
- clause = `${w.column} ${w.operator.toUpperCase()} (${arr.map(() => "?").join(", ")})`;
25768
+ clause = `${sqlColumn(w.column)} ${w.operator.toUpperCase()} (${arr.map(() => "?").join(", ")})`;
25657
25769
  params.push(...arr);
25658
25770
  } else {
25659
- clause = `${w.column} ${w.operator} ?`;
25771
+ clause = `${sqlColumn(w.column)} ${w.operator} ?`;
25660
25772
  params.push(w.value);
25661
25773
  }
25662
25774
  clauses.push(i2 === 0 ? clause : `${w.boolean.toUpperCase()} ${clause}`);
@@ -25677,13 +25789,14 @@ class ModelQueryBuilder {
25677
25789
  }
25678
25790
  buildQuery() {
25679
25791
  const params = [];
25680
- let sql2 = `SELECT ${this._select.join(", ")} FROM ${this._definition.table}`;
25792
+ const cols = this._select.map((c2) => c2 === "*" ? c2 : sqlColumn(c2));
25793
+ let sql2 = `SELECT ${cols.join(", ")} FROM ${this._definition.table}`;
25681
25794
  const whereBody = this.composeWhere(params);
25682
25795
  if (whereBody) {
25683
25796
  sql2 += ` WHERE ${whereBody}`;
25684
25797
  }
25685
25798
  if (this._orderBy.length > 0) {
25686
- sql2 += ` ORDER BY ${this._orderBy.map((o2) => `${o2.column} ${o2.direction.toUpperCase()}`).join(", ")}`;
25799
+ sql2 += ` ORDER BY ${this._orderBy.map((o2) => `${sqlColumn(o2.column)} ${o2.direction.toUpperCase()}`).join(", ")}`;
25687
25800
  }
25688
25801
  if (this._limit !== undefined)
25689
25802
  sql2 += ` LIMIT ${this._limit}`;
@@ -25697,165 +25810,168 @@ class ModelQueryBuilder {
25697
25810
  async eagerLoadRelations(instances) {
25698
25811
  if (instances.length === 0 || this._withRelations.length === 0)
25699
25812
  return;
25813
+ await Promise.all(this._withRelations.map((r2) => this.eagerLoadRelation(r2, instances)));
25814
+ }
25815
+ async eagerLoadRelation(relationName, instances) {
25700
25816
  const exec = getExecutor();
25701
25817
  const pk = this._definition.primaryKey || "id";
25702
- for (const relationName of this._withRelations) {
25703
- const cacheKey = `${this._definition.name}:${relationName}`;
25704
- let rel = relationCache.get(cacheKey);
25705
- if (rel === undefined) {
25706
- rel = resolveRelation(this._definition, relationName);
25707
- if (relationCache.size >= RELATION_CACHE_MAX)
25708
- relationCache.clear();
25709
- relationCache.set(cacheKey, rel);
25710
- }
25711
- if (!rel)
25712
- continue;
25713
- if (rel.type === "hasMany" || rel.type === "hasOne") {
25714
- const parentIds = instances.map((i2) => i2.get(pk)).filter((id) => id != null);
25715
- if (parentIds.length === 0)
25716
- continue;
25717
- const placeholders = parentIds.map(() => "?").join(", ");
25718
- const rows = await exec.all(`SELECT * FROM ${rel.relatedTable} WHERE ${rel.foreignKey} IN (${placeholders})`, parentIds);
25719
- const relatedModelDef = getModelFromRegistry(rel.relatedModelName);
25720
- const relDef = relatedModelDef?.getDefinition?.() || relatedModelDef?.definition || this._definition;
25721
- if (rel.type === "hasMany") {
25722
- const grouped = new Map;
25723
- for (const row of rows) {
25724
- const fkVal = row[rel.foreignKey];
25725
- if (!grouped.has(fkVal))
25726
- grouped.set(fkVal, []);
25727
- grouped.get(fkVal).push(row);
25728
- }
25729
- for (const instance of instances) {
25730
- const related = grouped.get(instance.get(pk)) || [];
25731
- instance.setRelation(relationName, related.map((r2) => new ModelInstance(relDef, r2)));
25732
- }
25733
- } else {
25734
- const byFk = new Map;
25735
- for (const row of rows) {
25736
- byFk.set(row[rel.foreignKey], row);
25737
- }
25738
- for (const instance of instances) {
25739
- const row = byFk.get(instance.get(pk));
25740
- instance.setRelation(relationName, row ? new ModelInstance(relDef, row) : null);
25741
- }
25818
+ const cacheKey = `${this._definition.name}:${relationName}`;
25819
+ let rel = relationCache.get(cacheKey);
25820
+ if (rel === undefined) {
25821
+ rel = resolveRelation(this._definition, relationName);
25822
+ if (relationCache.size >= RELATION_CACHE_MAX)
25823
+ relationCache.clear();
25824
+ relationCache.set(cacheKey, rel);
25825
+ }
25826
+ if (!rel)
25827
+ return;
25828
+ if (rel.type === "hasMany" || rel.type === "hasOne") {
25829
+ const parentIds = distinctParentIds(instances, pk);
25830
+ if (parentIds.length === 0)
25831
+ return;
25832
+ const placeholders = parentIds.map(() => "?").join(", ");
25833
+ const rows = await exec.all(`SELECT * FROM ${rel.relatedTable} WHERE ${rel.foreignKey} IN (${placeholders})`, parentIds);
25834
+ const relatedModelDef = getModelFromRegistry(rel.relatedModelName);
25835
+ const relDef = relatedModelDef?.getDefinition?.() || relatedModelDef?.definition || this._definition;
25836
+ if (rel.type === "hasMany") {
25837
+ const grouped = new Map;
25838
+ for (const row of rows) {
25839
+ const fkVal = row[rel.foreignKey];
25840
+ if (!grouped.has(fkVal))
25841
+ grouped.set(fkVal, []);
25842
+ grouped.get(fkVal).push(row);
25742
25843
  }
25743
- }
25744
- if (rel.type === "belongsTo") {
25745
- const fkSet = new Set;
25746
- for (const i2 of instances) {
25747
- const v2 = i2._attributes[rel.foreignKey];
25748
- if (v2 != null)
25749
- fkSet.add(v2);
25844
+ for (const instance of instances) {
25845
+ const related = grouped.get(instance.get(pk)) || [];
25846
+ instance.setRelation(relationName, related.map((r2) => new ModelInstance(relDef, r2)));
25750
25847
  }
25751
- const uniqueFkValues = [...fkSet];
25752
- if (uniqueFkValues.length === 0)
25753
- continue;
25754
- const placeholders = uniqueFkValues.map(() => "?").join(", ");
25755
- const rows = await exec.all(`SELECT * FROM ${rel.relatedTable} WHERE ${rel.localKey} IN (${placeholders})`, uniqueFkValues);
25756
- const relatedModelDef = getModelFromRegistry(rel.relatedModelName);
25757
- const relDef = relatedModelDef?.getDefinition?.() || relatedModelDef?.definition || this._definition;
25758
- const byPk = new Map;
25848
+ } else {
25849
+ const byFk = new Map;
25759
25850
  for (const row of rows) {
25760
- byPk.set(row[rel.localKey], row);
25851
+ const k2 = row[rel.foreignKey];
25852
+ if (!byFk.has(k2))
25853
+ byFk.set(k2, row);
25761
25854
  }
25762
25855
  for (const instance of instances) {
25763
- const fkVal = instance._attributes[rel.foreignKey];
25764
- const row = byPk.get(fkVal);
25856
+ const row = byFk.get(instance.get(pk));
25765
25857
  instance.setRelation(relationName, row ? new ModelInstance(relDef, row) : null);
25766
25858
  }
25767
25859
  }
25768
- if (rel.type === "belongsToMany") {
25769
- const parentIds = instances.map((i2) => i2.get(pk)).filter((id) => id != null);
25770
- if (parentIds.length === 0)
25771
- continue;
25772
- if (!rel.pivotTable || !rel.pivotFkParent || !rel.pivotFkRelated)
25773
- continue;
25774
- const pivotPlaceholders = parentIds.map(() => "?").join(", ");
25775
- const pivotRows = await exec.all(`SELECT * FROM ${rel.pivotTable} WHERE ${rel.pivotFkParent} IN (${pivotPlaceholders})`, parentIds);
25776
- if (pivotRows.length === 0) {
25777
- for (const instance of instances)
25778
- instance.setRelation(relationName, []);
25779
- continue;
25780
- }
25781
- const relatedIdSet = new Set;
25782
- for (const p2 of pivotRows) {
25783
- const v2 = p2[rel.pivotFkRelated];
25784
- if (v2 != null)
25785
- relatedIdSet.add(v2);
25786
- }
25787
- const relatedIds = [...relatedIdSet];
25788
- const relatedModelDef = getModelFromRegistry(rel.relatedModelName);
25789
- const relDef = relatedModelDef?.getDefinition?.() || relatedModelDef?.definition || this._definition;
25790
- const relatedPk = relDef?.primaryKey || "id";
25791
- let relatedRows = [];
25792
- if (relatedIds.length > 0) {
25793
- const relPlaceholders = relatedIds.map(() => "?").join(", ");
25794
- relatedRows = await exec.all(`SELECT * FROM ${rel.relatedTable} WHERE ${relatedPk} IN (${relPlaceholders})`, relatedIds);
25795
- }
25796
- const relatedByPk = new Map;
25797
- for (const r2 of relatedRows)
25798
- relatedByPk.set(r2[relatedPk], r2);
25799
- const pivotByParent = new Map;
25800
- for (const p2 of pivotRows) {
25801
- const key = p2[rel.pivotFkParent];
25802
- if (!pivotByParent.has(key))
25803
- pivotByParent.set(key, []);
25804
- pivotByParent.get(key).push(p2);
25805
- }
25806
- const pivotKnownKeys = new Set([rel.pivotFkParent, rel.pivotFkRelated]);
25807
- for (const instance of instances) {
25808
- const parentVal = instance.get(pk);
25809
- const myPivots = pivotByParent.get(parentVal) || [];
25810
- const relatedInstances = myPivots.map((p2) => {
25811
- const relRow = relatedByPk.get(p2[rel.pivotFkRelated]);
25812
- if (!relRow)
25813
- return null;
25814
- const inst = new ModelInstance(relDef, relRow);
25815
- const extras = {};
25816
- for (const [k2, v2] of Object.entries(p2)) {
25817
- if (!pivotKnownKeys.has(k2))
25818
- extras[k2] = v2;
25819
- }
25820
- inst.pivot = extras;
25821
- return inst;
25822
- }).filter((x2) => x2 !== null);
25823
- instance.setRelation(relationName, relatedInstances);
25824
- }
25860
+ }
25861
+ if (rel.type === "belongsTo") {
25862
+ const fkSet = new Set;
25863
+ for (const i2 of instances) {
25864
+ const v2 = i2._attributes[rel.foreignKey];
25865
+ if (v2 != null)
25866
+ fkSet.add(v2);
25825
25867
  }
25826
- if (rel.type === "hasManyThrough" || rel.type === "hasOneThrough") {
25827
- const parentIds = instances.map((i2) => i2.get(pk)).filter((id) => id != null);
25828
- if (parentIds.length === 0)
25829
- continue;
25830
- const throughPk = rel.throughLocalKey || "id";
25831
- const throughPh = parentIds.map(() => "?").join(", ");
25832
- const throughRows = await exec.all(`SELECT ${throughPk}, ${rel.throughForeignKey} FROM ${rel.throughTable} WHERE ${rel.throughForeignKey} IN (${throughPh})`, parentIds);
25833
- if (throughRows.length === 0) {
25834
- for (const instance of instances)
25835
- instance.setRelation(relationName, rel.type === "hasManyThrough" ? [] : null);
25868
+ const uniqueFkValues = [...fkSet];
25869
+ if (uniqueFkValues.length === 0)
25870
+ return;
25871
+ const placeholders = uniqueFkValues.map(() => "?").join(", ");
25872
+ const rows = await exec.all(`SELECT * FROM ${rel.relatedTable} WHERE ${rel.localKey} IN (${placeholders})`, uniqueFkValues);
25873
+ const relatedModelDef = getModelFromRegistry(rel.relatedModelName);
25874
+ const relDef = relatedModelDef?.getDefinition?.() || relatedModelDef?.definition || this._definition;
25875
+ const byPk = new Map;
25876
+ for (const row of rows) {
25877
+ byPk.set(row[rel.localKey], row);
25878
+ }
25879
+ for (const instance of instances) {
25880
+ const fkVal = instance._attributes[rel.foreignKey];
25881
+ const row = byPk.get(fkVal);
25882
+ instance.setRelation(relationName, row ? new ModelInstance(relDef, row) : null);
25883
+ }
25884
+ }
25885
+ if (rel.type === "belongsToMany") {
25886
+ const parentIds = distinctParentIds(instances, pk);
25887
+ if (parentIds.length === 0)
25888
+ return;
25889
+ if (!rel.pivotTable || !rel.pivotFkParent || !rel.pivotFkRelated)
25890
+ return;
25891
+ const pivotPlaceholders = parentIds.map(() => "?").join(", ");
25892
+ const pivotRows = await exec.all(`SELECT * FROM ${rel.pivotTable} WHERE ${rel.pivotFkParent} IN (${pivotPlaceholders})`, parentIds);
25893
+ if (pivotRows.length === 0) {
25894
+ for (const instance of instances)
25895
+ instance.setRelation(relationName, []);
25896
+ return;
25897
+ }
25898
+ const relatedIdSet = new Set;
25899
+ for (const p2 of pivotRows) {
25900
+ const v2 = p2[rel.pivotFkRelated];
25901
+ if (v2 != null)
25902
+ relatedIdSet.add(v2);
25903
+ }
25904
+ const relatedIds = [...relatedIdSet];
25905
+ const relatedModelDef = getModelFromRegistry(rel.relatedModelName);
25906
+ const relDef = relatedModelDef?.getDefinition?.() || relatedModelDef?.definition || this._definition;
25907
+ const relatedPk = relDef?.primaryKey || "id";
25908
+ let relatedRows = [];
25909
+ if (relatedIds.length > 0) {
25910
+ const relPlaceholders = relatedIds.map(() => "?").join(", ");
25911
+ relatedRows = await exec.all(`SELECT * FROM ${rel.relatedTable} WHERE ${relatedPk} IN (${relPlaceholders})`, relatedIds);
25912
+ }
25913
+ const relatedByPk = new Map;
25914
+ for (const r2 of relatedRows)
25915
+ relatedByPk.set(r2[relatedPk], r2);
25916
+ const pivotByParent = new Map;
25917
+ for (const p2 of pivotRows) {
25918
+ const key = p2[rel.pivotFkParent];
25919
+ if (!pivotByParent.has(key))
25920
+ pivotByParent.set(key, []);
25921
+ pivotByParent.get(key).push(p2);
25922
+ }
25923
+ const pivotKnownKeys = new Set([rel.pivotFkParent, rel.pivotFkRelated]);
25924
+ for (const instance of instances) {
25925
+ const parentVal = instance.get(pk);
25926
+ const myPivots = pivotByParent.get(parentVal) || [];
25927
+ const relatedInstances = myPivots.map((p2) => {
25928
+ const relRow = relatedByPk.get(p2[rel.pivotFkRelated]);
25929
+ if (!relRow)
25930
+ return null;
25931
+ const inst = new ModelInstance(relDef, relRow);
25932
+ const extras = {};
25933
+ for (const [k2, v2] of Object.entries(p2)) {
25934
+ if (!pivotKnownKeys.has(k2))
25935
+ extras[k2] = v2;
25936
+ }
25937
+ inst.pivot = extras;
25938
+ return inst;
25939
+ }).filter((x2) => x2 !== null);
25940
+ instance.setRelation(relationName, relatedInstances);
25941
+ }
25942
+ }
25943
+ if (rel.type === "hasManyThrough" || rel.type === "hasOneThrough") {
25944
+ const parentIds = distinctParentIds(instances, pk);
25945
+ if (parentIds.length === 0)
25946
+ return;
25947
+ const throughPk = rel.throughLocalKey || "id";
25948
+ const throughPh = parentIds.map(() => "?").join(", ");
25949
+ const throughRows = await exec.all(`SELECT ${throughPk}, ${rel.throughForeignKey} FROM ${rel.throughTable} WHERE ${rel.throughForeignKey} IN (${throughPh})`, parentIds);
25950
+ if (throughRows.length === 0) {
25951
+ for (const instance of instances)
25952
+ instance.setRelation(relationName, rel.type === "hasManyThrough" ? [] : null);
25953
+ return;
25954
+ }
25955
+ const throughToParent = new Map;
25956
+ for (const t2 of throughRows)
25957
+ throughToParent.set(t2[throughPk], t2[rel.throughForeignKey]);
25958
+ const throughIds = [...new Set(throughRows.map((t2) => t2[throughPk]))];
25959
+ const targetPh = throughIds.map(() => "?").join(", ");
25960
+ const targetRows = await exec.all(`SELECT * FROM ${rel.relatedTable} WHERE ${rel.targetForeignKey} IN (${targetPh})`, throughIds);
25961
+ const relatedModelDef = getModelFromRegistry(rel.relatedModelName);
25962
+ const relDef = relatedModelDef?.getDefinition?.() || relatedModelDef?.definition || this._definition;
25963
+ const byParent = new Map;
25964
+ for (const row of targetRows) {
25965
+ const parentVal = throughToParent.get(row[rel.targetForeignKey]);
25966
+ if (parentVal == null)
25836
25967
  continue;
25837
- }
25838
- const throughToParent = new Map;
25839
- for (const t2 of throughRows)
25840
- throughToParent.set(t2[throughPk], t2[rel.throughForeignKey]);
25841
- const throughIds = [...new Set(throughRows.map((t2) => t2[throughPk]))];
25842
- const targetPh = throughIds.map(() => "?").join(", ");
25843
- const targetRows = await exec.all(`SELECT * FROM ${rel.relatedTable} WHERE ${rel.targetForeignKey} IN (${targetPh})`, throughIds);
25844
- const relatedModelDef = getModelFromRegistry(rel.relatedModelName);
25845
- const relDef = relatedModelDef?.getDefinition?.() || relatedModelDef?.definition || this._definition;
25846
- const byParent = new Map;
25847
- for (const row of targetRows) {
25848
- const parentVal = throughToParent.get(row[rel.targetForeignKey]);
25849
- if (parentVal == null)
25850
- continue;
25851
- if (!byParent.has(parentVal))
25852
- byParent.set(parentVal, []);
25853
- byParent.get(parentVal).push(new ModelInstance(relDef, row));
25854
- }
25855
- for (const instance of instances) {
25856
- const group = byParent.get(instance.get(pk)) || [];
25857
- instance.setRelation(relationName, rel.type === "hasManyThrough" ? group : group[0] ?? null);
25858
- }
25968
+ if (!byParent.has(parentVal))
25969
+ byParent.set(parentVal, []);
25970
+ byParent.get(parentVal).push(new ModelInstance(relDef, row));
25971
+ }
25972
+ for (const instance of instances) {
25973
+ const group = byParent.get(instance.get(pk)) || [];
25974
+ instance.setRelation(relationName, rel.type === "hasManyThrough" ? group : group[0] ?? null);
25859
25975
  }
25860
25976
  }
25861
25977
  }
@@ -25915,7 +26031,8 @@ class ModelQueryBuilder {
25915
26031
  assertValidIdentifier(column, "increment(column)");
25916
26032
  const exec = getExecutor();
25917
26033
  const params = [amount];
25918
- let sql2 = `UPDATE ${this._definition.table} SET ${column} = ${column} + ?`;
26034
+ const incCol = sqlColumn(column);
26035
+ let sql2 = `UPDATE ${this._definition.table} SET ${incCol} = ${incCol} + ?`;
25919
26036
  if (timestampsEnabled(this._definition)) {
25920
26037
  sql2 += `, updated_at = ?`;
25921
26038
  params.push(formatNow());
@@ -25973,26 +26090,27 @@ class ModelQueryBuilder {
25973
26090
  assertValidIdentifier(column, "pluck(column)");
25974
26091
  const exec = getExecutor();
25975
26092
  const params = [];
25976
- let sql2 = `SELECT ${column} FROM ${this._definition.table}`;
26093
+ const pluckCol = sqlColumn(column);
26094
+ let sql2 = `SELECT ${pluckCol} FROM ${this._definition.table}`;
25977
26095
  const whereBody = this.composeWhere(params);
25978
26096
  if (whereBody) {
25979
26097
  sql2 += ` WHERE ${whereBody}`;
25980
26098
  }
25981
26099
  if (this._orderBy.length > 0) {
25982
- sql2 += ` ORDER BY ${this._orderBy.map((o2) => `${o2.column} ${o2.direction.toUpperCase()}`).join(", ")}`;
26100
+ sql2 += ` ORDER BY ${this._orderBy.map((o2) => `${sqlColumn(o2.column)} ${o2.direction.toUpperCase()}`).join(", ")}`;
25983
26101
  }
25984
26102
  if (this._limit !== undefined)
25985
26103
  sql2 += ` LIMIT ${this._limit}`;
25986
26104
  if (this._offset !== undefined)
25987
26105
  sql2 += ` OFFSET ${this._offset}`;
25988
26106
  const rows = await exec.all(sql2, params);
25989
- return rows.map((r2) => r2[column]);
26107
+ return rows.map((r2) => r2[pluckCol]);
25990
26108
  }
25991
26109
  async aggregate(fn, column) {
25992
26110
  assertValidIdentifier(column, `${fn}(column)`);
25993
26111
  const exec = getExecutor();
25994
26112
  const params = [];
25995
- let sql2 = `SELECT ${fn}(${column}) as v FROM ${this._definition.table}`;
26113
+ let sql2 = `SELECT ${fn}(${sqlColumn(column)}) as v FROM ${this._definition.table}`;
25996
26114
  const whereBody = this.composeWhere(params);
25997
26115
  if (whereBody) {
25998
26116
  sql2 += ` WHERE ${whereBody}`;
@@ -26031,7 +26149,7 @@ class ModelQueryBuilder {
26031
26149
  async update(data) {
26032
26150
  const exec = getExecutor();
26033
26151
  const entries = Object.entries(data);
26034
- const sets = entries.map(([k2]) => `${k2} = ?`).join(", ");
26152
+ const sets = entries.map(([k2]) => `${sqlColumn(k2)} = ?`).join(", ");
26035
26153
  const params = entries.map(([, v2]) => v2);
26036
26154
  if (timestampsEnabled(this._definition)) {
26037
26155
  params.push(formatNow());
@@ -26193,12 +26311,19 @@ function createModel(definition) {
26193
26311
  }
26194
26312
  async function createTableFromModel(definition) {
26195
26313
  const exec = getExecutor();
26196
- const pk = definition.primaryKey || "id";
26314
+ const pk = toSnakeCase(definition.primaryKey || "id");
26197
26315
  const columns = [];
26316
+ const emitted = new Set([pk]);
26198
26317
  columns.push(definition.autoIncrement !== false ? `${pk} INTEGER PRIMARY KEY AUTOINCREMENT` : `${pk} INTEGER PRIMARY KEY`);
26199
- if (definition.traits?.useUuid)
26318
+ if (definition.traits?.useUuid) {
26200
26319
  columns.push("uuid TEXT UNIQUE");
26201
- for (const [name, attr] of Object.entries(definition.attributes)) {
26320
+ emitted.add("uuid");
26321
+ }
26322
+ for (const [attrName, attr] of Object.entries(definition.attributes)) {
26323
+ const name = toSnakeCase(attrName);
26324
+ if (emitted.has(name))
26325
+ continue;
26326
+ emitted.add(name);
26202
26327
  let colType = "TEXT";
26203
26328
  if (attr.type === "number")
26204
26329
  colType = "INTEGER";
@@ -26219,10 +26344,23 @@ async function createTableFromModel(definition) {
26219
26344
  }
26220
26345
  columns.push(colDef);
26221
26346
  }
26347
+ for (const rel of normalizeRelationList(definition.belongsTo)) {
26348
+ const name = rel.foreignKey ?? `${toSnakeCase(rel.model)}_id`;
26349
+ if (emitted.has(name))
26350
+ continue;
26351
+ emitted.add(name);
26352
+ columns.push(`${name} INTEGER`);
26353
+ }
26222
26354
  if (timestampsEnabled(definition)) {
26223
- columns.push("created_at TEXT", "updated_at TEXT");
26355
+ for (const c2 of ["created_at", "updated_at"]) {
26356
+ if (emitted.has(c2))
26357
+ continue;
26358
+ emitted.add(c2);
26359
+ columns.push(`${c2} TEXT`);
26360
+ }
26224
26361
  }
26225
- if (softDeletesEnabled(definition)) {
26362
+ if (softDeletesEnabled(definition) && !emitted.has("deleted_at")) {
26363
+ emitted.add("deleted_at");
26226
26364
  columns.push("deleted_at TEXT");
26227
26365
  }
26228
26366
  await exec.run(`CREATE TABLE IF NOT EXISTS ${definition.table} (${columns.join(", ")})`, []);
@@ -26265,7 +26403,7 @@ async function seedModel(definition, count, faker) {
26265
26403
  const data = {};
26266
26404
  for (const [name, attr] of Object.entries(definition.attributes)) {
26267
26405
  if (attr.factory)
26268
- data[name] = attr.factory(faker);
26406
+ data[toSnakeCase(name)] = attr.factory(faker);
26269
26407
  }
26270
26408
  if (timestampsEnabled(definition)) {
26271
26409
  const now = formatNow();
@@ -27766,23 +27904,6 @@ async function loadModels(options) {
27766
27904
  }
27767
27905
  var init_loader = () => {};
27768
27906
 
27769
- // src/relation-utils.ts
27770
- function normalizeRelationEntry(entry) {
27771
- if (typeof entry === "string")
27772
- return { model: entry };
27773
- if (entry && typeof entry === "object" && typeof entry.model === "string") {
27774
- const e2 = entry;
27775
- return { model: e2.model, foreignKey: e2.foreignKey, onDelete: e2.onDelete };
27776
- }
27777
- return null;
27778
- }
27779
- function normalizeRelationList(rel) {
27780
- if (!rel)
27781
- return [];
27782
- const entries = Array.isArray(rel) ? rel : typeof rel === "object" ? Object.values(rel) : [];
27783
- return entries.map(normalizeRelationEntry).filter((x2) => x2 !== null);
27784
- }
27785
-
27786
27907
  // src/meta.ts
27787
27908
  function buildSchemaMeta(models) {
27788
27909
  const modelToTable = {};
@@ -27877,6 +27998,9 @@ function info2(message) {
27877
27998
  function snakeCase(str) {
27878
27999
  return str.replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").replace(/([a-z\d])([A-Z])/g, "$1_$2").replace(/(\d)([A-Z])/gi, "$1_$2").toLowerCase();
27879
28000
  }
28001
+ function singularizeTable(table) {
28002
+ return singularizerFor(config5.relations?.singularizeStrategy)(table);
28003
+ }
27880
28004
  function findWorkspaceRoot6(startPath) {
27881
28005
  let currentPath = startPath;
27882
28006
  while (currentPath !== dirname12(currentPath)) {
@@ -28265,7 +28389,7 @@ function buildMigrationPlan2(models, options) {
28265
28389
  const model = models[modelName];
28266
28390
  const traits = model?.traits ?? {};
28267
28391
  const modelTable = model.table || `${String(model.name).toLowerCase()}s`;
28268
- const singular = modelTable.replace(/s$/, "");
28392
+ const singular = singularizeTable(modelTable);
28269
28393
  if (traits.likeable) {
28270
28394
  const opts = typeof traits.likeable === "object" ? traits.likeable : {};
28271
28395
  const likeTable = opts.table || `${modelTable}_likes`;
@@ -28332,7 +28456,7 @@ function buildMigrationPlan2(models, options) {
28332
28456
  if (!btm || typeof btm !== "object" || Array.isArray(btm))
28333
28457
  continue;
28334
28458
  const parentTable = model.table || `${String(model.name).toLowerCase()}s`;
28335
- const parentSingular = parentTable.replace(/s$/, "");
28459
+ const parentSingular = singularizeTable(parentTable);
28336
28460
  for (const [relKey, value] of Object.entries(btm)) {
28337
28461
  const cfg = value;
28338
28462
  if (!cfg || typeof cfg !== "object")
@@ -28345,7 +28469,7 @@ function buildMigrationPlan2(models, options) {
28345
28469
  const relatedModelName = cfg.model;
28346
28470
  const relatedRaw = models[relatedModelName];
28347
28471
  const relatedTable = relatedRaw?.table || `${String(relatedModelName).toLowerCase()}s`;
28348
- const relatedSingular = relatedTable.replace(/s$/, "");
28472
+ const relatedSingular = singularizeTable(relatedTable);
28349
28473
  const pivotTable = cfg.table || [parentSingular, relatedSingular].sort().join("_");
28350
28474
  const fkParent = cfg.foreignKey || `${parentSingular}_id`;
28351
28475
  const fkRelated = cfg.relatedKey || `${relatedSingular}_id`;
@@ -28979,6 +29103,7 @@ var init_src2 = __esm(() => {
28979
29103
  init_loader();
28980
29104
  init_meta();
28981
29105
  init_migrations();
29106
+ init_pivot();
28982
29107
  init_orm();
28983
29108
  init_sqlite_pragmas();
28984
29109
  });
@@ -31022,7 +31147,7 @@ function getPrefix() {
31022
31147
  }
31023
31148
  var prefix = getPrefix();
31024
31149
  // package.json
31025
- var version2 = "0.1.58";
31150
+ var version2 = "0.1.60";
31026
31151
 
31027
31152
  // bin/cli.ts
31028
31153
  init_actions();