@type32/tauri-sqlite-orm 0.1.3 → 0.1.4

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/index.mjs CHANGED
@@ -1,17 +1,3 @@
1
- // src/connection.ts
2
- import Database from "@tauri-apps/plugin-sql";
3
- var db = null;
4
- async function initDb(dbPath) {
5
- db = await Database.load(dbPath);
6
- return db;
7
- }
8
- function getDb() {
9
- if (!db) {
10
- throw new Error("Database not initialized. Please call initDb() first.");
11
- }
12
- return db;
13
- }
14
-
15
1
  // src/schema-builder.ts
16
2
  function sql(strings, ...values) {
17
3
  const raw = strings.reduce(
@@ -40,6 +26,18 @@ function createColumn(params) {
40
26
  col.defaultFn = fn;
41
27
  return col;
42
28
  };
29
+ col.$default = (fn) => {
30
+ col.defaultFn = fn;
31
+ return col;
32
+ };
33
+ col.$onUpdate = (fn) => {
34
+ col.onUpdateFn = fn;
35
+ return col;
36
+ };
37
+ col.$onUpdateFn = (fn) => {
38
+ col.onUpdateFn = fn;
39
+ return col;
40
+ };
43
41
  col.references = (target, actions) => {
44
42
  const t = target();
45
43
  col.references = {
@@ -52,34 +50,43 @@ function createColumn(params) {
52
50
  };
53
51
  return col;
54
52
  }
55
- function text(name, config) {
53
+ function text(nameOrConfig, maybeConfig) {
54
+ const name = typeof nameOrConfig === "string" ? nameOrConfig : "";
55
+ const config = typeof nameOrConfig === "string" ? maybeConfig : nameOrConfig;
56
56
  const col = createColumn({
57
57
  name,
58
58
  type: "TEXT",
59
- isPrimaryKey: config?.isPrimaryKey,
60
59
  _dataType: ""
61
60
  });
62
61
  if (config?.enum) col.enumValues = config.enum;
62
+ if (config?.mode) col.mode = config.mode;
63
63
  return col;
64
64
  }
65
- function integer(name, config) {
65
+ function integer(nameOrConfig, maybeConfig) {
66
+ const name = typeof nameOrConfig === "string" ? nameOrConfig : "";
67
+ const config = typeof nameOrConfig === "string" ? maybeConfig : nameOrConfig;
66
68
  let dt = 0;
67
69
  if (config?.mode === "boolean") dt = false;
68
- if (config?.mode === "timestamp") dt = /* @__PURE__ */ new Date();
70
+ if (config?.mode === "timestamp" || config?.mode === "timestamp_ms")
71
+ dt = /* @__PURE__ */ new Date();
69
72
  const col = createColumn({
70
73
  name,
71
74
  type: "INTEGER",
72
- isPrimaryKey: config?.isPrimaryKey,
73
- autoIncrement: config?.autoIncrement,
74
75
  mode: config?.mode ?? "number",
75
76
  _dataType: dt
76
77
  });
77
78
  return col;
78
79
  }
79
80
  function real(name) {
80
- return createColumn({ name, type: "REAL", _dataType: 0 });
81
+ return createColumn({
82
+ name: name ?? "",
83
+ type: "REAL",
84
+ _dataType: 0
85
+ });
81
86
  }
82
- function blob(name, config) {
87
+ function blob(nameOrConfig, maybeConfig) {
88
+ const name = typeof nameOrConfig === "string" ? nameOrConfig : "";
89
+ const config = typeof nameOrConfig === "string" ? maybeConfig : nameOrConfig;
83
90
  let dt = new Uint8Array();
84
91
  if (config?.mode === "bigint") dt = 0n;
85
92
  if (config?.mode === "json") dt = void 0;
@@ -90,7 +97,9 @@ function blob(name, config) {
90
97
  _dataType: dt
91
98
  });
92
99
  }
93
- function numeric(name, config) {
100
+ function numeric(nameOrConfig, maybeConfig) {
101
+ const name = typeof nameOrConfig === "string" ? nameOrConfig : "";
102
+ const config = typeof nameOrConfig === "string" ? maybeConfig : nameOrConfig;
94
103
  let dt = "";
95
104
  if (config?.mode === "number") dt = 0;
96
105
  if (config?.mode === "bigint") dt = 0n;
@@ -101,22 +110,32 @@ function numeric(name, config) {
101
110
  _dataType: dt
102
111
  });
103
112
  }
104
- var boolean = (name) => createColumn({
105
- name,
106
- type: "INTEGER",
107
- _dataType: false,
108
- mode: "boolean"
109
- });
110
- var timestamp = (name) => createColumn({
111
- name,
112
- type: "INTEGER",
113
- _dataType: /* @__PURE__ */ new Date(),
114
- mode: "timestamp"
115
- });
113
+ function boolean(name) {
114
+ return createColumn({
115
+ name: name ?? "",
116
+ type: "INTEGER",
117
+ _dataType: false,
118
+ mode: "boolean"
119
+ });
120
+ }
121
+ function timestamp(name) {
122
+ return createColumn({
123
+ name: name ?? "",
124
+ type: "INTEGER",
125
+ _dataType: /* @__PURE__ */ new Date(),
126
+ mode: "timestamp"
127
+ });
128
+ }
129
+ function increments(name) {
130
+ return integer(name ?? "").primaryKey({
131
+ autoIncrement: true
132
+ });
133
+ }
116
134
  function defineTable(tableName, schema) {
117
135
  const finalizedSchema = { ...schema };
118
136
  for (const key of Object.keys(finalizedSchema)) {
119
137
  const col = finalizedSchema[key];
138
+ if (!col.name || col.name === "") col.name = key;
120
139
  col.tableName = tableName;
121
140
  }
122
141
  const table = {
@@ -125,7 +144,7 @@ function defineTable(tableName, schema) {
125
144
  // The Drizzle-like type inference properties
126
145
  $inferSelect: {},
127
146
  $inferInsert: {}
128
- // Example: omit 'id' for inserts
147
+ // omit PK columns
129
148
  };
130
149
  for (const [key, col] of Object.entries(finalizedSchema)) {
131
150
  table[key] = col;
@@ -133,6 +152,45 @@ function defineTable(tableName, schema) {
133
152
  return table;
134
153
  }
135
154
 
155
+ // src/orm.ts
156
+ import Database from "@tauri-apps/plugin-sql";
157
+
158
+ // src/sql-helpers.ts
159
+ function isColumn(value) {
160
+ return typeof value === "object" && value !== null && "_dataType" in value;
161
+ }
162
+ function getQualifiedName(column) {
163
+ if (column.tableName) return `${column.tableName}.${column.name}`;
164
+ return column.name;
165
+ }
166
+ function comparison(operator, column, value) {
167
+ return {
168
+ toSQL: () => {
169
+ if (isColumn(value)) {
170
+ return {
171
+ clause: `${getQualifiedName(column)} ${operator} ${getQualifiedName(
172
+ value
173
+ )}`,
174
+ bindings: []
175
+ };
176
+ }
177
+ return {
178
+ clause: `${getQualifiedName(column)} ${operator} ?`,
179
+ bindings: [value]
180
+ };
181
+ }
182
+ };
183
+ }
184
+ var eq = (column, value) => comparison("=", column, value);
185
+ var ne = (column, value) => comparison("!=", column, value);
186
+ var gt = (column, value) => comparison(">", column, value);
187
+ var gte = (column, value) => comparison(">=", column, value);
188
+ var lt = (column, value) => comparison("<", column, value);
189
+ var lte = (column, value) => comparison("<=", column, value);
190
+ var like = (column, value) => comparison("LIKE", column, value);
191
+ var asc = (column) => `${getQualifiedName(column)} ASC`;
192
+ var desc = (column) => `${getQualifiedName(column)} DESC`;
193
+
136
194
  // src/orm.ts
137
195
  var SelectQueryBuilder = class {
138
196
  _table = null;
@@ -143,7 +201,9 @@ var SelectQueryBuilder = class {
143
201
  _limit = null;
144
202
  _offset = null;
145
203
  _eager = {};
146
- constructor(fields) {
204
+ _dbProvider;
205
+ constructor(dbProvider, fields) {
206
+ this._dbProvider = dbProvider;
147
207
  if (fields) {
148
208
  this._selectedColumns = Object.values(fields).map((c) => c.name);
149
209
  }
@@ -179,7 +239,7 @@ var SelectQueryBuilder = class {
179
239
  if (!this._table) {
180
240
  throw new Error("Cannot execute select query without a 'from' table.");
181
241
  }
182
- const db3 = getDb();
242
+ const db = await this._dbProvider();
183
243
  const bindings = [];
184
244
  let query = `SELECT ${this._selectedColumns.join(", ")} FROM ${this._table._tableName}`;
185
245
  if (this._joins.length > 0) {
@@ -207,7 +267,7 @@ var SelectQueryBuilder = class {
207
267
  query += ` OFFSET ?`;
208
268
  bindings.push(this._offset);
209
269
  }
210
- return db3.select(query, bindings);
270
+ return db.select(query, bindings);
211
271
  }
212
272
  };
213
273
  var TauriORM = class {
@@ -215,15 +275,23 @@ var TauriORM = class {
215
275
  query = {};
216
276
  _tables = null;
217
277
  _relations = null;
278
+ _dbPromise;
279
+ constructor(dbUri) {
280
+ this._dbPromise = Database.load(dbUri);
281
+ }
282
+ async getDb() {
283
+ return this._dbPromise;
284
+ }
218
285
  // Deprecated: use configure()
219
286
  configureQuery(tables, relations2) {
220
287
  this.configure(tables, relations2);
221
288
  }
222
289
  select(fields) {
223
- return new SelectQueryBuilder(fields);
290
+ return new SelectQueryBuilder(this.getDb.bind(this), fields);
224
291
  }
225
292
  // --- Drizzle-style CRUD builders ---
226
293
  insert(table) {
294
+ const self = this;
227
295
  return new class InsertBuilder {
228
296
  _table = table;
229
297
  _rows = [];
@@ -232,13 +300,17 @@ var TauriORM = class {
232
300
  return this;
233
301
  }
234
302
  async execute() {
235
- const db3 = getDb();
303
+ const db = await self.getDb();
236
304
  for (const data of this._rows) {
237
305
  const finalData = { ...data };
238
306
  const schema = this._table._schema;
239
307
  for (const [key, col] of Object.entries(schema)) {
240
- if (finalData[key] === void 0 && col.defaultFn) {
241
- finalData[key] = col.defaultFn();
308
+ if (finalData[key] === void 0) {
309
+ if (col.defaultFn) {
310
+ finalData[key] = col.defaultFn();
311
+ } else if (col.onUpdateFn) {
312
+ finalData[key] = col.onUpdateFn();
313
+ }
242
314
  }
243
315
  }
244
316
  const keys = Object.keys(finalData);
@@ -247,12 +319,13 @@ var TauriORM = class {
247
319
  const query = `INSERT INTO ${this._table._tableName} (${keys.join(
248
320
  ", "
249
321
  )}) VALUES (${placeholders})`;
250
- await db3.execute(query, values);
322
+ await db.execute(query, values);
251
323
  }
252
324
  }
253
325
  }();
254
326
  }
255
327
  update(table) {
328
+ const self = this;
256
329
  return new class UpdateBuilder {
257
330
  _table = table;
258
331
  _data = null;
@@ -268,10 +341,17 @@ var TauriORM = class {
268
341
  async execute() {
269
342
  if (!this._data)
270
343
  throw new Error("Update requires set() before execute()");
271
- const db3 = getDb();
272
- const setKeys = Object.keys(this._data);
344
+ const db = await self.getDb();
345
+ const schema = this._table._schema;
346
+ const dataToSet = { ...this._data };
347
+ for (const [key, col] of Object.entries(schema)) {
348
+ if (!(key in dataToSet) && col.onUpdateFn) {
349
+ dataToSet[key] = col.onUpdateFn();
350
+ }
351
+ }
352
+ const setKeys = Object.keys(dataToSet);
273
353
  const setClause = setKeys.map((k) => `${k} = ?`).join(", ");
274
- const bindings = Object.values(this._data);
354
+ const bindings = Object.values(dataToSet);
275
355
  let query = `UPDATE ${this._table._tableName} SET ${setClause}`;
276
356
  if (this._where) {
277
357
  if (typeof this._where.toSQL === "function") {
@@ -286,11 +366,12 @@ var TauriORM = class {
286
366
  }
287
367
  }
288
368
  }
289
- await db3.execute(query, bindings);
369
+ await db.execute(query, bindings);
290
370
  }
291
371
  }();
292
372
  }
293
373
  delete(table) {
374
+ const self = this;
294
375
  return new class DeleteBuilder {
295
376
  _table = table;
296
377
  _where = null;
@@ -299,7 +380,7 @@ var TauriORM = class {
299
380
  return this;
300
381
  }
301
382
  async execute() {
302
- const db3 = getDb();
383
+ const db = await self.getDb();
303
384
  let query = `DELETE FROM ${this._table._tableName}`;
304
385
  const bindings = [];
305
386
  if (this._where) {
@@ -315,7 +396,7 @@ var TauriORM = class {
315
396
  }
316
397
  }
317
398
  }
318
- await db3.execute(query, bindings);
399
+ await db.execute(query, bindings);
319
400
  }
320
401
  }();
321
402
  }
@@ -323,8 +404,8 @@ var TauriORM = class {
323
404
  // legacy direct methods removed in favor of builder APIs
324
405
  // legacy direct methods removed in favor of builder APIs
325
406
  async run(query, bindings = []) {
326
- const db3 = getDb();
327
- await db3.execute(query, bindings);
407
+ const db = await this.getDb();
408
+ await db.execute(query, bindings);
328
409
  }
329
410
  // --- Migrations API ---
330
411
  generateCreateTableSql(table) {
@@ -374,16 +455,16 @@ var TauriORM = class {
374
455
  );
375
456
  }
376
457
  async hasMigration(name) {
377
- const db3 = getDb();
378
- const rows = await db3.select(
458
+ const db = await this.getDb();
459
+ const rows = await db.select(
379
460
  `SELECT name FROM _migrations WHERE name = ?`,
380
461
  [name]
381
462
  );
382
463
  return Array.isArray(rows) && rows.length > 0;
383
464
  }
384
465
  async recordMigration(name) {
385
- const db3 = getDb();
386
- await db3.execute(
466
+ const db = await this.getDb();
467
+ await db.execute(
387
468
  `INSERT INTO _migrations (name, applied_at) VALUES (?, ?)`,
388
469
  [name, Date.now()]
389
470
  );
@@ -405,7 +486,7 @@ var TauriORM = class {
405
486
  configure(tables, relDefs) {
406
487
  this._tables = tables;
407
488
  this._relations = relDefs ?? {};
408
- this.query = makeQueryAPI(tables, this._relations);
489
+ this.query = makeQueryAPI(tables, this._relations, this.getDb.bind(this));
409
490
  return this;
410
491
  }
411
492
  // Convenience: migrate from configured tables
@@ -417,7 +498,7 @@ var TauriORM = class {
417
498
  // --- Schema diff and CLI-like helpers ---
418
499
  async diffSchema() {
419
500
  if (!this._tables) throw new Error("No tables configured.");
420
- const dbi = getDb();
501
+ const dbi = await this.getDb();
421
502
  const configuredNames = Object.values(this._tables).map(
422
503
  (t) => t._tableName
423
504
  );
@@ -492,7 +573,7 @@ var TauriORM = class {
492
573
  return this.pullSchema();
493
574
  }
494
575
  async studio() {
495
- const dbi = getDb();
576
+ const dbi = await this.getDb();
496
577
  return { driver: "sqlite", path: dbi.path };
497
578
  }
498
579
  // --- Schema detection / signature ---
@@ -502,7 +583,7 @@ var TauriORM = class {
502
583
  );
503
584
  }
504
585
  async getSchemaMeta(key) {
505
- const dbi = getDb();
586
+ const dbi = await this.getDb();
506
587
  await this.ensureSchemaMeta();
507
588
  const rows = await dbi.select(
508
589
  `SELECT value FROM _schema_meta WHERE key = ?`,
@@ -511,7 +592,7 @@ var TauriORM = class {
511
592
  return rows?.[0]?.value ?? null;
512
593
  }
513
594
  async setSchemaMeta(key, value) {
514
- const dbi = getDb();
595
+ const dbi = await this.getDb();
515
596
  await this.ensureSchemaMeta();
516
597
  await dbi.execute(
517
598
  `INSERT INTO _schema_meta(key, value) VALUES(?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value`,
@@ -560,7 +641,7 @@ var TauriORM = class {
560
641
  // Pull current DB schema (minimal) for configured tables
561
642
  async pullSchema() {
562
643
  if (!this._tables) throw new Error("No tables configured.");
563
- const dbi = getDb();
644
+ const dbi = await this.getDb();
564
645
  const result = {};
565
646
  for (const tbl of Object.values(this._tables)) {
566
647
  const name = tbl._tableName;
@@ -579,7 +660,7 @@ var TauriORM = class {
579
660
  return this.generateCreateTableSql(table);
580
661
  }
581
662
  async tableExists(name) {
582
- const dbi = getDb();
663
+ const dbi = await this.getDb();
583
664
  const rows = await dbi.select(
584
665
  `SELECT name FROM sqlite_master WHERE type='table' AND name = ?`,
585
666
  [name]
@@ -589,7 +670,7 @@ var TauriORM = class {
589
670
  // Force push model to DB: add missing tables/columns, rebuild tables if incompatible
590
671
  async forcePush(options) {
591
672
  if (!this._tables) throw new Error("No tables configured.");
592
- const dbi = getDb();
673
+ const dbi = await this.getDb();
593
674
  const preserve = options?.preserveData !== false;
594
675
  for (const tbl of Object.values(this._tables)) {
595
676
  const tableName = tbl._tableName;
@@ -659,7 +740,6 @@ var TauriORM = class {
659
740
  await this.setSchemaMeta("schema_signature", this.computeModelSignature());
660
741
  }
661
742
  };
662
- var db2 = new TauriORM();
663
743
  function relations(baseTable, builder) {
664
744
  const ctx = {
665
745
  one: (table, cfg) => ({ kind: "one", table, cfg }),
@@ -685,10 +765,29 @@ function guessChildFk(child, base, rel) {
685
765
  ];
686
766
  return childCols.find((c) => guessNames.includes(c.name)) || childCols.find((c) => /.*_id$/i.test(c.name)) || null;
687
767
  }
768
+ function guessOneRelationJoin(base, rel) {
769
+ const child = rel.table;
770
+ const basePk = getPrimaryKey(base);
771
+ const childCols = Object.values(child._schema);
772
+ if (rel.cfg?.fields && rel.cfg?.references && rel.cfg.fields[0] && rel.cfg.references[0]) {
773
+ const fk = rel.cfg.fields[0];
774
+ const ref = rel.cfg.references[0];
775
+ if (childCols.some((c) => c.name === fk.name)) {
776
+ return { lhsTable: child, lhsCol: fk, rhsTable: base, rhsCol: ref };
777
+ }
778
+ const baseCols = Object.values(base._schema);
779
+ if (baseCols.some((c) => c.name === fk.name)) {
780
+ return { lhsTable: base, lhsCol: fk, rhsTable: child, rhsCol: ref };
781
+ }
782
+ }
783
+ const childFk = guessChildFk(child, base, rel);
784
+ if (!childFk) return null;
785
+ return { lhsTable: child, lhsCol: childFk, rhsTable: base, rhsCol: basePk };
786
+ }
688
787
  function isFlatWith(spec) {
689
788
  return Object.values(spec).every((v) => typeof v === "boolean");
690
789
  }
691
- function makeQueryAPI(tables, relDefs) {
790
+ function makeQueryAPI(tables, relDefs, dbProvider) {
692
791
  const api = {};
693
792
  const tableKeyByName = {};
694
793
  for (const [k, t] of Object.entries(tables)) tableKeyByName[t._tableName] = k;
@@ -697,7 +796,7 @@ function makeQueryAPI(tables, relDefs) {
697
796
  async findMany(opts) {
698
797
  const base = tbl;
699
798
  const withSpec = opts?.with ?? {};
700
- const dbi = getDb();
799
+ const dbi = await dbProvider();
701
800
  const rels = relDefs[tblKey] ?? {};
702
801
  if (opts?.join && isFlatWith(withSpec)) {
703
802
  const baseCols = Object.values(base._schema);
@@ -723,23 +822,43 @@ function makeQueryAPI(tables, relDefs) {
723
822
  const child = rel.table;
724
823
  const childCols = Object.values(child._schema);
725
824
  const childPk = childCols.find((c) => c.isPrimaryKey) || childCols.find((c) => c.name === "id") || null;
726
- const childFk = guessChildFk(child, base, rel);
727
- if (!childFk) continue;
728
- fkMap[relName] = { childFk, childPk };
825
+ if (rel.kind === "one") {
826
+ const mapping = guessOneRelationJoin(base, rel);
827
+ if (!mapping) continue;
828
+ if (mapping.lhsTable._tableName === child._tableName) {
829
+ fkMap[relName] = { childFk: mapping.lhsCol, childPk };
830
+ joins.push(
831
+ `LEFT JOIN ${child._tableName} ON ${mapping.lhsTable._tableName}.${mapping.lhsCol.name} = ${mapping.rhsTable._tableName}.${mapping.rhsCol.name}`
832
+ );
833
+ } else {
834
+ fkMap[relName] = { childFk: mapping.rhsCol, childPk };
835
+ joins.push(
836
+ `LEFT JOIN ${child._tableName} ON ${mapping.lhsTable._tableName}.${mapping.lhsCol.name} = ${mapping.rhsTable._tableName}.${mapping.rhsCol.name}`
837
+ );
838
+ }
839
+ } else {
840
+ const childFk = guessChildFk(child, base, rel);
841
+ if (!childFk) continue;
842
+ fkMap[relName] = { childFk, childPk };
843
+ joins.push(
844
+ `LEFT JOIN ${child._tableName} ON ${child._tableName}.${childFk.name} = ${base._tableName}.${basePk.name}`
845
+ );
846
+ }
729
847
  const selected = typeof enabled === "object" && enabled.columns?.length ? enabled.columns : childCols.map((c) => c.name);
730
848
  relColsMap[relName] = selected;
731
849
  for (const name of selected)
732
850
  selectParts.push(
733
851
  `${child._tableName}.${name} AS __rel_${relName}_${name}`
734
852
  );
735
- joins.push(
736
- `LEFT JOIN ${child._tableName} ON ${child._tableName}.${childFk.name} = ${base._tableName}.${basePk.name}`
737
- );
738
853
  }
739
854
  let sqlText = `SELECT ${selectParts.join(", ")} FROM ${base._tableName}${joins.length ? " " + joins.join(" ") : ""}`;
740
855
  const bindings = [];
741
856
  if (opts?.where) {
742
- if (typeof opts.where.toSQL === "function") {
857
+ if (typeof opts.where === "function") {
858
+ const w = opts.where(base, { eq, ne, gt, gte, lt, lte, like }).toSQL();
859
+ sqlText += ` WHERE ${w.clause}`;
860
+ bindings.push(...w.bindings);
861
+ } else if (typeof opts.where.toSQL === "function") {
743
862
  const w = opts.where.toSQL();
744
863
  sqlText += ` WHERE ${w.clause}`;
745
864
  bindings.push(...w.bindings);
@@ -751,8 +870,9 @@ function makeQueryAPI(tables, relDefs) {
751
870
  }
752
871
  }
753
872
  }
754
- if (opts?.orderBy?.length)
755
- sqlText += ` ORDER BY ${opts.orderBy.join(", ")}`;
873
+ const orderByClauses = typeof opts?.orderBy === "function" ? opts.orderBy(base, { asc, desc }) : opts?.orderBy;
874
+ if (orderByClauses?.length)
875
+ sqlText += ` ORDER BY ${orderByClauses.join(", ")}`;
756
876
  if (typeof opts?.limit === "number")
757
877
  sqlText += ` LIMIT ${opts.limit}`;
758
878
  if (typeof opts?.offset === "number")
@@ -819,7 +939,11 @@ function makeQueryAPI(tables, relDefs) {
819
939
  let baseSql = `SELECT ${baseSelected.join(", ")} FROM ${base._tableName}`;
820
940
  const baseBindings = [];
821
941
  if (opts?.where) {
822
- if (typeof opts.where.toSQL === "function") {
942
+ if (typeof opts.where === "function") {
943
+ const w = opts.where(base, { eq, ne, gt, gte, lt, lte, like }).toSQL();
944
+ baseSql += ` WHERE ${w.clause}`;
945
+ baseBindings.push(...w.bindings);
946
+ } else if (typeof opts.where.toSQL === "function") {
823
947
  const w = opts.where.toSQL();
824
948
  baseSql += ` WHERE ${w.clause}`;
825
949
  baseBindings.push(...w.bindings);
@@ -831,8 +955,9 @@ function makeQueryAPI(tables, relDefs) {
831
955
  }
832
956
  }
833
957
  }
834
- if (opts?.orderBy?.length)
835
- baseSql += ` ORDER BY ${opts.orderBy.join(", ")}`;
958
+ const orderByClauses2 = typeof opts?.orderBy === "function" ? opts.orderBy(base, { asc, desc }) : opts?.orderBy;
959
+ if (orderByClauses2?.length)
960
+ baseSql += ` ORDER BY ${orderByClauses2.join(", ")}`;
836
961
  if (typeof opts?.limit === "number") baseSql += ` LIMIT ${opts.limit}`;
837
962
  if (typeof opts?.offset === "number")
838
963
  baseSql += ` OFFSET ${opts.offset}`;
@@ -851,9 +976,9 @@ function makeQueryAPI(tables, relDefs) {
851
976
  const child = rel.table;
852
977
  const childCols = Object.values(child._schema);
853
978
  const selectCols = enabled?.columns && enabled.columns.length > 0 ? enabled.columns : childCols.map((c) => c.name);
854
- const fkCol = guessChildFk(child, parentTable, rel);
855
- if (!fkCol) continue;
856
979
  if (rel.kind === "many") {
980
+ const fkCol = guessChildFk(child, parentTable, rel);
981
+ if (!fkCol) continue;
857
982
  const sql2 = `SELECT ${selectCols.join(", ")} FROM ${child._tableName} WHERE ${fkCol.name} IN (${parentIds.map(() => "?").join(", ")})`;
858
983
  const rows = await dbi.select(sql2, parentIds);
859
984
  const buckets = /* @__PURE__ */ new Map();
@@ -872,14 +997,32 @@ function makeQueryAPI(tables, relDefs) {
872
997
  await loadRelationsFor(children, child, enabled.with);
873
998
  }
874
999
  } else {
875
- const sql2 = `SELECT ${selectCols.join(", ")} FROM ${child._tableName} WHERE ${fkCol.name} IN (${parentIds.map(() => "?").join(", ")})`;
876
- const rows = await dbi.select(sql2, parentIds);
877
- const mapOne = /* @__PURE__ */ new Map();
878
- for (const r of rows) mapOne.set(r[fkCol.name], r);
879
- for (const p of parents)
880
- p[relName] = mapOne.get(p[parentPk.name]) ?? null;
1000
+ const mapping = guessOneRelationJoin(parentTable, rel);
1001
+ if (!mapping) continue;
1002
+ if (mapping.lhsTable._tableName === child._tableName) {
1003
+ const sql2 = `SELECT ${selectCols.join(", ")} FROM ${child._tableName} WHERE ${mapping.lhsCol.name} IN (${parentIds.map(() => "?").join(", ")})`;
1004
+ const rows = await dbi.select(sql2, parentIds);
1005
+ const mapOne = /* @__PURE__ */ new Map();
1006
+ for (const r of rows) mapOne.set(r[mapping.lhsCol.name], r);
1007
+ for (const p of parents)
1008
+ p[relName] = mapOne.get(p[parentPk.name]) ?? null;
1009
+ } else {
1010
+ const parentFkName = mapping.lhsCol.name;
1011
+ const childPkName = mapping.rhsCol.name;
1012
+ const childIds = parents.map((p) => p[parentFkName]).filter((v2) => v2 !== void 0 && v2 !== null);
1013
+ if (childIds.length === 0) {
1014
+ for (const p of parents) p[relName] = null;
1015
+ } else {
1016
+ const sql2 = `SELECT ${selectCols.join(", ")} FROM ${child._tableName} WHERE ${childPkName} IN (${childIds.map(() => "?").join(", ")})`;
1017
+ const rows = await dbi.select(sql2, childIds);
1018
+ const mapOne = /* @__PURE__ */ new Map();
1019
+ for (const r of rows) mapOne.set(r[childPkName], r);
1020
+ for (const p of parents)
1021
+ p[relName] = mapOne.get(p[parentFkName]) ?? null;
1022
+ }
1023
+ }
881
1024
  if (enabled?.with) {
882
- const children = parents.map((p) => p[relName]).filter(Boolean);
1025
+ const children = parents.map((p) => p[relName]).filter((x) => Boolean(x));
883
1026
  if (children.length > 0)
884
1027
  await loadRelationsFor(children, child, enabled.with);
885
1028
  }
@@ -890,60 +1033,26 @@ function makeQueryAPI(tables, relDefs) {
890
1033
  await loadRelationsFor(result, base, withSpec);
891
1034
  }
892
1035
  return result;
1036
+ },
1037
+ async findFirst(opts) {
1038
+ const rows = await api[tblKey].findMany({ ...opts, limit: 1 });
1039
+ return rows?.[0] ?? null;
893
1040
  }
894
1041
  };
895
1042
  }
896
1043
  return api;
897
1044
  }
898
-
899
- // src/sql-helpers.ts
900
- function isColumn(value) {
901
- return typeof value === "object" && value !== null && "_dataType" in value;
902
- }
903
- function getQualifiedName(column) {
904
- if (column.tableName) return `${column.tableName}.${column.name}`;
905
- return column.name;
906
- }
907
- function comparison(operator, column, value) {
908
- return {
909
- toSQL: () => {
910
- if (isColumn(value)) {
911
- return {
912
- clause: `${getQualifiedName(column)} ${operator} ${getQualifiedName(
913
- value
914
- )}`,
915
- bindings: []
916
- };
917
- }
918
- return {
919
- clause: `${getQualifiedName(column)} ${operator} ?`,
920
- bindings: [value]
921
- };
922
- }
923
- };
924
- }
925
- var eq = (column, value) => comparison("=", column, value);
926
- var ne = (column, value) => comparison("!=", column, value);
927
- var gt = (column, value) => comparison(">", column, value);
928
- var gte = (column, value) => comparison(">=", column, value);
929
- var lt = (column, value) => comparison("<", column, value);
930
- var lte = (column, value) => comparison("<=", column, value);
931
- var like = (column, value) => comparison("LIKE", column, value);
932
- var asc = (column) => `${column.name} ASC`;
933
- var desc = (column) => `${column.name} DESC`;
934
1045
  export {
935
1046
  TauriORM,
936
1047
  asc,
937
1048
  blob,
938
1049
  boolean,
939
- db2 as db,
940
1050
  defineTable,
941
1051
  desc,
942
1052
  eq,
943
- getDb,
944
1053
  gt,
945
1054
  gte,
946
- initDb,
1055
+ increments,
947
1056
  integer,
948
1057
  like,
949
1058
  lt,