@type32/tauri-sqlite-orm 0.1.2 → 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
@@ -414,8 +495,251 @@ var TauriORM = class {
414
495
  throw new Error("No tables configured. Call db.configure({...}) first.");
415
496
  await this.migrate(Object.values(this._tables), options);
416
497
  }
498
+ // --- Schema diff and CLI-like helpers ---
499
+ async diffSchema() {
500
+ if (!this._tables) throw new Error("No tables configured.");
501
+ const dbi = await this.getDb();
502
+ const configuredNames = Object.values(this._tables).map(
503
+ (t) => t._tableName
504
+ );
505
+ const existing = await dbi.select(
506
+ `SELECT name FROM sqlite_master WHERE type='table'`
507
+ );
508
+ const existingNames = existing.map((r) => r.name);
509
+ const extraTables = existingNames.filter(
510
+ (n) => !configuredNames.includes(n)
511
+ );
512
+ const missingTables = configuredNames.filter(
513
+ (n) => !existingNames.includes(n)
514
+ );
515
+ const tables = {};
516
+ for (const tbl of Object.values(this._tables)) {
517
+ const tableName = tbl._tableName;
518
+ if (!existingNames.includes(tableName)) {
519
+ tables[tableName] = {
520
+ missingColumns: Object.keys(tbl._schema),
521
+ extraColumns: [],
522
+ changedColumns: []
523
+ };
524
+ continue;
525
+ }
526
+ const cols = await dbi.select(`PRAGMA table_info('${tableName}')`);
527
+ const colMap = new Map(cols.map((c) => [c.name, c]));
528
+ const modelCols = Object.values(
529
+ tbl._schema
530
+ );
531
+ const missingColumns = [];
532
+ const extraColumns = [];
533
+ const changedColumns = [];
534
+ const modelNamesSet = new Set(modelCols.map((c) => c.name));
535
+ for (const m of modelCols) {
536
+ const info = colMap.get(m.name);
537
+ if (!info) {
538
+ missingColumns.push(m.name);
539
+ continue;
540
+ }
541
+ const diffs = {};
542
+ if ((info.type || "").toUpperCase() !== m.type.toUpperCase())
543
+ diffs.type = true;
544
+ if (!!info.pk !== !!m.isPrimaryKey) diffs.pk = true;
545
+ if (!!info.notnull !== !!m.isNotNull) diffs.notNull = true;
546
+ const modelDv = m.defaultValue && typeof m.defaultValue === "object" && m.defaultValue.raw ? m.defaultValue.raw : m.defaultValue ?? null;
547
+ if ((info.dflt_value ?? null) !== modelDv)
548
+ diffs.default = true;
549
+ if (Object.keys(diffs).length)
550
+ changedColumns.push({ name: m.name, diffs });
551
+ }
552
+ for (const c of cols)
553
+ if (!modelNamesSet.has(c.name)) extraColumns.push(c.name);
554
+ tables[tableName] = { missingColumns, extraColumns, changedColumns };
555
+ }
556
+ return { extraTables, missingTables, tables };
557
+ }
558
+ async generate() {
559
+ if (!this._tables) throw new Error("No tables configured.");
560
+ return {
561
+ statements: Object.values(this._tables).map(
562
+ (t) => this.buildCreateTableSQL(t)
563
+ )
564
+ };
565
+ }
566
+ async migrateCli(opts) {
567
+ return this.migrateConfigured(opts);
568
+ }
569
+ async push(opts) {
570
+ return this.forcePush(opts);
571
+ }
572
+ async pull() {
573
+ return this.pullSchema();
574
+ }
575
+ async studio() {
576
+ const dbi = await this.getDb();
577
+ return { driver: "sqlite", path: dbi.path };
578
+ }
579
+ // --- Schema detection / signature ---
580
+ async ensureSchemaMeta() {
581
+ await this.run(
582
+ `CREATE TABLE IF NOT EXISTS _schema_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)`
583
+ );
584
+ }
585
+ async getSchemaMeta(key) {
586
+ const dbi = await this.getDb();
587
+ await this.ensureSchemaMeta();
588
+ const rows = await dbi.select(
589
+ `SELECT value FROM _schema_meta WHERE key = ?`,
590
+ [key]
591
+ );
592
+ return rows?.[0]?.value ?? null;
593
+ }
594
+ async setSchemaMeta(key, value) {
595
+ const dbi = await this.getDb();
596
+ await this.ensureSchemaMeta();
597
+ await dbi.execute(
598
+ `INSERT INTO _schema_meta(key, value) VALUES(?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value`,
599
+ [key, value]
600
+ );
601
+ }
602
+ normalizeColumn(col) {
603
+ return {
604
+ name: col.name,
605
+ type: col.type,
606
+ pk: !!col.isPrimaryKey,
607
+ ai: !!col.autoIncrement,
608
+ nn: !!col.isNotNull,
609
+ dv: col.defaultValue && typeof col.defaultValue === "object" && col.defaultValue.raw ? { raw: col.defaultValue.raw } : col.defaultValue ?? null
610
+ };
611
+ }
612
+ computeModelSignature() {
613
+ if (!this._tables) return "";
614
+ const entries = Object.entries(this._tables).map(([k, tbl]) => {
615
+ const cols = Object.values(
616
+ tbl._schema
617
+ ).map((c) => this.normalizeColumn(c)).sort((a, b) => a.name.localeCompare(b.name));
618
+ return { table: tbl._tableName, columns: cols };
619
+ });
620
+ entries.sort((a, b) => a.table.localeCompare(b.table));
621
+ return JSON.stringify(entries);
622
+ }
623
+ async isSchemaDirty() {
624
+ const sig = this.computeModelSignature();
625
+ const stored = await this.getSchemaMeta("schema_signature");
626
+ return { dirty: sig !== stored, current: sig, stored };
627
+ }
628
+ async migrateIfDirty(options) {
629
+ const status = await this.isSchemaDirty();
630
+ if (!this._tables) throw new Error("No tables configured.");
631
+ if (status.dirty) {
632
+ await this.migrate(Object.values(this._tables), options);
633
+ await this.setSchemaMeta(
634
+ "schema_signature",
635
+ this.computeModelSignature()
636
+ );
637
+ return true;
638
+ }
639
+ return false;
640
+ }
641
+ // Pull current DB schema (minimal) for configured tables
642
+ async pullSchema() {
643
+ if (!this._tables) throw new Error("No tables configured.");
644
+ const dbi = await this.getDb();
645
+ const result = {};
646
+ for (const tbl of Object.values(this._tables)) {
647
+ const name = tbl._tableName;
648
+ const cols = await dbi.select(`PRAGMA table_info('${name}')`);
649
+ result[name] = cols.map((c) => ({
650
+ name: c.name,
651
+ type: c.type,
652
+ notnull: !!c.notnull,
653
+ pk: !!c.pk,
654
+ dflt_value: c.dflt_value ?? null
655
+ }));
656
+ }
657
+ return result;
658
+ }
659
+ buildCreateTableSQL(table) {
660
+ return this.generateCreateTableSql(table);
661
+ }
662
+ async tableExists(name) {
663
+ const dbi = await this.getDb();
664
+ const rows = await dbi.select(
665
+ `SELECT name FROM sqlite_master WHERE type='table' AND name = ?`,
666
+ [name]
667
+ );
668
+ return rows.length > 0;
669
+ }
670
+ // Force push model to DB: add missing tables/columns, rebuild tables if incompatible
671
+ async forcePush(options) {
672
+ if (!this._tables) throw new Error("No tables configured.");
673
+ const dbi = await this.getDb();
674
+ const preserve = options?.preserveData !== false;
675
+ for (const tbl of Object.values(this._tables)) {
676
+ const tableName = tbl._tableName;
677
+ const exists = await this.tableExists(tableName);
678
+ if (!exists) {
679
+ await this.run(this.buildCreateTableSQL(tbl));
680
+ continue;
681
+ }
682
+ const existingCols = await dbi.select(
683
+ `PRAGMA table_info('${tableName}')`
684
+ );
685
+ const existingMap = new Map(existingCols.map((c) => [c.name, c]));
686
+ const modelCols = Object.values(
687
+ tbl._schema
688
+ );
689
+ const missing = [];
690
+ let requiresRebuild = false;
691
+ for (const m of modelCols) {
692
+ const info = existingMap.get(m.name);
693
+ if (!info) {
694
+ missing.push(m);
695
+ continue;
696
+ }
697
+ const typeDiff = (info.type || "").toUpperCase() !== m.type.toUpperCase();
698
+ const pkDiff = !!info.pk !== !!m.isPrimaryKey;
699
+ const nnDiff = !!info.notnull !== !!m.isNotNull;
700
+ const modelDv = m.defaultValue && typeof m.defaultValue === "object" && m.defaultValue.raw ? m.defaultValue.raw : m.defaultValue ?? null;
701
+ const defDiff = (info.dflt_value ?? null) !== modelDv;
702
+ if (typeDiff || pkDiff || nnDiff && !modelDv || defDiff) {
703
+ requiresRebuild = true;
704
+ }
705
+ }
706
+ if (requiresRebuild) {
707
+ const tmp = `_new_${tableName}`;
708
+ await this.run(this.buildCreateTableSQL(tbl));
709
+ await this.run(
710
+ this.buildCreateTableSQL({ ...tbl, _tableName: tmp })
711
+ );
712
+ const existingNames = existingCols.map((c) => c.name);
713
+ const modelNames = modelCols.map((c) => c.name);
714
+ const shared = existingNames.filter((n) => modelNames.includes(n));
715
+ if (preserve && shared.length > 0) {
716
+ await this.run(
717
+ `INSERT INTO ${tmp} (${shared.join(", ")}) SELECT ${shared.join(
718
+ ", "
719
+ )} FROM ${tableName}`
720
+ );
721
+ }
722
+ await this.run(`DROP TABLE ${tableName}`);
723
+ await this.run(`ALTER TABLE ${tmp} RENAME TO ${tableName}`);
724
+ } else {
725
+ for (const m of missing) {
726
+ let clause = `${m.name} ${m.type}`;
727
+ if (m.isNotNull) clause += " NOT NULL";
728
+ if (m.defaultValue !== void 0) {
729
+ const dv = m.defaultValue;
730
+ if (dv && typeof dv === "object" && "raw" in dv)
731
+ clause += ` DEFAULT ${dv.raw}`;
732
+ else if (typeof dv === "string")
733
+ clause += ` DEFAULT '${dv.replace(/'/g, "''")}'`;
734
+ else clause += ` DEFAULT ${dv}`;
735
+ }
736
+ await this.run(`ALTER TABLE ${tableName} ADD COLUMN ${clause}`);
737
+ }
738
+ }
739
+ }
740
+ await this.setSchemaMeta("schema_signature", this.computeModelSignature());
741
+ }
417
742
  };
418
- var db2 = new TauriORM();
419
743
  function relations(baseTable, builder) {
420
744
  const ctx = {
421
745
  one: (table, cfg) => ({ kind: "one", table, cfg }),
@@ -441,10 +765,29 @@ function guessChildFk(child, base, rel) {
441
765
  ];
442
766
  return childCols.find((c) => guessNames.includes(c.name)) || childCols.find((c) => /.*_id$/i.test(c.name)) || null;
443
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
+ }
444
787
  function isFlatWith(spec) {
445
788
  return Object.values(spec).every((v) => typeof v === "boolean");
446
789
  }
447
- function makeQueryAPI(tables, relDefs) {
790
+ function makeQueryAPI(tables, relDefs, dbProvider) {
448
791
  const api = {};
449
792
  const tableKeyByName = {};
450
793
  for (const [k, t] of Object.entries(tables)) tableKeyByName[t._tableName] = k;
@@ -453,7 +796,7 @@ function makeQueryAPI(tables, relDefs) {
453
796
  async findMany(opts) {
454
797
  const base = tbl;
455
798
  const withSpec = opts?.with ?? {};
456
- const dbi = getDb();
799
+ const dbi = await dbProvider();
457
800
  const rels = relDefs[tblKey] ?? {};
458
801
  if (opts?.join && isFlatWith(withSpec)) {
459
802
  const baseCols = Object.values(base._schema);
@@ -479,23 +822,43 @@ function makeQueryAPI(tables, relDefs) {
479
822
  const child = rel.table;
480
823
  const childCols = Object.values(child._schema);
481
824
  const childPk = childCols.find((c) => c.isPrimaryKey) || childCols.find((c) => c.name === "id") || null;
482
- const childFk = guessChildFk(child, base, rel);
483
- if (!childFk) continue;
484
- 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
+ }
485
847
  const selected = typeof enabled === "object" && enabled.columns?.length ? enabled.columns : childCols.map((c) => c.name);
486
848
  relColsMap[relName] = selected;
487
849
  for (const name of selected)
488
850
  selectParts.push(
489
851
  `${child._tableName}.${name} AS __rel_${relName}_${name}`
490
852
  );
491
- joins.push(
492
- `LEFT JOIN ${child._tableName} ON ${child._tableName}.${childFk.name} = ${base._tableName}.${basePk.name}`
493
- );
494
853
  }
495
854
  let sqlText = `SELECT ${selectParts.join(", ")} FROM ${base._tableName}${joins.length ? " " + joins.join(" ") : ""}`;
496
855
  const bindings = [];
497
856
  if (opts?.where) {
498
- 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") {
499
862
  const w = opts.where.toSQL();
500
863
  sqlText += ` WHERE ${w.clause}`;
501
864
  bindings.push(...w.bindings);
@@ -507,8 +870,9 @@ function makeQueryAPI(tables, relDefs) {
507
870
  }
508
871
  }
509
872
  }
510
- if (opts?.orderBy?.length)
511
- 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(", ")}`;
512
876
  if (typeof opts?.limit === "number")
513
877
  sqlText += ` LIMIT ${opts.limit}`;
514
878
  if (typeof opts?.offset === "number")
@@ -575,7 +939,11 @@ function makeQueryAPI(tables, relDefs) {
575
939
  let baseSql = `SELECT ${baseSelected.join(", ")} FROM ${base._tableName}`;
576
940
  const baseBindings = [];
577
941
  if (opts?.where) {
578
- 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") {
579
947
  const w = opts.where.toSQL();
580
948
  baseSql += ` WHERE ${w.clause}`;
581
949
  baseBindings.push(...w.bindings);
@@ -587,8 +955,9 @@ function makeQueryAPI(tables, relDefs) {
587
955
  }
588
956
  }
589
957
  }
590
- if (opts?.orderBy?.length)
591
- 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(", ")}`;
592
961
  if (typeof opts?.limit === "number") baseSql += ` LIMIT ${opts.limit}`;
593
962
  if (typeof opts?.offset === "number")
594
963
  baseSql += ` OFFSET ${opts.offset}`;
@@ -607,9 +976,9 @@ function makeQueryAPI(tables, relDefs) {
607
976
  const child = rel.table;
608
977
  const childCols = Object.values(child._schema);
609
978
  const selectCols = enabled?.columns && enabled.columns.length > 0 ? enabled.columns : childCols.map((c) => c.name);
610
- const fkCol = guessChildFk(child, parentTable, rel);
611
- if (!fkCol) continue;
612
979
  if (rel.kind === "many") {
980
+ const fkCol = guessChildFk(child, parentTable, rel);
981
+ if (!fkCol) continue;
613
982
  const sql2 = `SELECT ${selectCols.join(", ")} FROM ${child._tableName} WHERE ${fkCol.name} IN (${parentIds.map(() => "?").join(", ")})`;
614
983
  const rows = await dbi.select(sql2, parentIds);
615
984
  const buckets = /* @__PURE__ */ new Map();
@@ -628,14 +997,32 @@ function makeQueryAPI(tables, relDefs) {
628
997
  await loadRelationsFor(children, child, enabled.with);
629
998
  }
630
999
  } else {
631
- const sql2 = `SELECT ${selectCols.join(", ")} FROM ${child._tableName} WHERE ${fkCol.name} IN (${parentIds.map(() => "?").join(", ")})`;
632
- const rows = await dbi.select(sql2, parentIds);
633
- const mapOne = /* @__PURE__ */ new Map();
634
- for (const r of rows) mapOne.set(r[fkCol.name], r);
635
- for (const p of parents)
636
- 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
+ }
637
1024
  if (enabled?.with) {
638
- const children = parents.map((p) => p[relName]).filter(Boolean);
1025
+ const children = parents.map((p) => p[relName]).filter((x) => Boolean(x));
639
1026
  if (children.length > 0)
640
1027
  await loadRelationsFor(children, child, enabled.with);
641
1028
  }
@@ -646,60 +1033,26 @@ function makeQueryAPI(tables, relDefs) {
646
1033
  await loadRelationsFor(result, base, withSpec);
647
1034
  }
648
1035
  return result;
1036
+ },
1037
+ async findFirst(opts) {
1038
+ const rows = await api[tblKey].findMany({ ...opts, limit: 1 });
1039
+ return rows?.[0] ?? null;
649
1040
  }
650
1041
  };
651
1042
  }
652
1043
  return api;
653
1044
  }
654
-
655
- // src/sql-helpers.ts
656
- function isColumn(value) {
657
- return typeof value === "object" && value !== null && "_dataType" in value;
658
- }
659
- function getQualifiedName(column) {
660
- if (column.tableName) return `${column.tableName}.${column.name}`;
661
- return column.name;
662
- }
663
- function comparison(operator, column, value) {
664
- return {
665
- toSQL: () => {
666
- if (isColumn(value)) {
667
- return {
668
- clause: `${getQualifiedName(column)} ${operator} ${getQualifiedName(
669
- value
670
- )}`,
671
- bindings: []
672
- };
673
- }
674
- return {
675
- clause: `${getQualifiedName(column)} ${operator} ?`,
676
- bindings: [value]
677
- };
678
- }
679
- };
680
- }
681
- var eq = (column, value) => comparison("=", column, value);
682
- var ne = (column, value) => comparison("!=", column, value);
683
- var gt = (column, value) => comparison(">", column, value);
684
- var gte = (column, value) => comparison(">=", column, value);
685
- var lt = (column, value) => comparison("<", column, value);
686
- var lte = (column, value) => comparison("<=", column, value);
687
- var like = (column, value) => comparison("LIKE", column, value);
688
- var asc = (column) => `${column.name} ASC`;
689
- var desc = (column) => `${column.name} DESC`;
690
1045
  export {
691
1046
  TauriORM,
692
1047
  asc,
693
1048
  blob,
694
1049
  boolean,
695
- db2 as db,
696
1050
  defineTable,
697
1051
  desc,
698
1052
  eq,
699
- getDb,
700
1053
  gt,
701
1054
  gte,
702
- initDb,
1055
+ increments,
703
1056
  integer,
704
1057
  like,
705
1058
  lt,