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