@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.js CHANGED
@@ -34,14 +34,12 @@ __export(index_exports, {
34
34
  asc: () => asc,
35
35
  blob: () => blob,
36
36
  boolean: () => boolean,
37
- db: () => db2,
38
37
  defineTable: () => defineTable,
39
38
  desc: () => desc,
40
39
  eq: () => eq,
41
- getDb: () => getDb,
42
40
  gt: () => gt,
43
41
  gte: () => gte,
44
- initDb: () => initDb,
42
+ increments: () => increments,
45
43
  integer: () => integer,
46
44
  like: () => like,
47
45
  lt: () => lt,
@@ -57,20 +55,6 @@ __export(index_exports, {
57
55
  });
58
56
  module.exports = __toCommonJS(index_exports);
59
57
 
60
- // src/connection.ts
61
- var import_plugin_sql = __toESM(require("@tauri-apps/plugin-sql"));
62
- var db = null;
63
- async function initDb(dbPath) {
64
- db = await import_plugin_sql.default.load(dbPath);
65
- return db;
66
- }
67
- function getDb() {
68
- if (!db) {
69
- throw new Error("Database not initialized. Please call initDb() first.");
70
- }
71
- return db;
72
- }
73
-
74
58
  // src/schema-builder.ts
75
59
  function sql(strings, ...values) {
76
60
  const raw = strings.reduce(
@@ -99,6 +83,18 @@ function createColumn(params) {
99
83
  col.defaultFn = fn;
100
84
  return col;
101
85
  };
86
+ col.$default = (fn) => {
87
+ col.defaultFn = fn;
88
+ return col;
89
+ };
90
+ col.$onUpdate = (fn) => {
91
+ col.onUpdateFn = fn;
92
+ return col;
93
+ };
94
+ col.$onUpdateFn = (fn) => {
95
+ col.onUpdateFn = fn;
96
+ return col;
97
+ };
102
98
  col.references = (target, actions) => {
103
99
  const t = target();
104
100
  col.references = {
@@ -111,34 +107,43 @@ function createColumn(params) {
111
107
  };
112
108
  return col;
113
109
  }
114
- function text(name, config) {
110
+ function text(nameOrConfig, maybeConfig) {
111
+ const name = typeof nameOrConfig === "string" ? nameOrConfig : "";
112
+ const config = typeof nameOrConfig === "string" ? maybeConfig : nameOrConfig;
115
113
  const col = createColumn({
116
114
  name,
117
115
  type: "TEXT",
118
- isPrimaryKey: config?.isPrimaryKey,
119
116
  _dataType: ""
120
117
  });
121
118
  if (config?.enum) col.enumValues = config.enum;
119
+ if (config?.mode) col.mode = config.mode;
122
120
  return col;
123
121
  }
124
- function integer(name, config) {
122
+ function integer(nameOrConfig, maybeConfig) {
123
+ const name = typeof nameOrConfig === "string" ? nameOrConfig : "";
124
+ const config = typeof nameOrConfig === "string" ? maybeConfig : nameOrConfig;
125
125
  let dt = 0;
126
126
  if (config?.mode === "boolean") dt = false;
127
- if (config?.mode === "timestamp") dt = /* @__PURE__ */ new Date();
127
+ if (config?.mode === "timestamp" || config?.mode === "timestamp_ms")
128
+ dt = /* @__PURE__ */ new Date();
128
129
  const col = createColumn({
129
130
  name,
130
131
  type: "INTEGER",
131
- isPrimaryKey: config?.isPrimaryKey,
132
- autoIncrement: config?.autoIncrement,
133
132
  mode: config?.mode ?? "number",
134
133
  _dataType: dt
135
134
  });
136
135
  return col;
137
136
  }
138
137
  function real(name) {
139
- return createColumn({ name, type: "REAL", _dataType: 0 });
138
+ return createColumn({
139
+ name: name ?? "",
140
+ type: "REAL",
141
+ _dataType: 0
142
+ });
140
143
  }
141
- function blob(name, config) {
144
+ function blob(nameOrConfig, maybeConfig) {
145
+ const name = typeof nameOrConfig === "string" ? nameOrConfig : "";
146
+ const config = typeof nameOrConfig === "string" ? maybeConfig : nameOrConfig;
142
147
  let dt = new Uint8Array();
143
148
  if (config?.mode === "bigint") dt = 0n;
144
149
  if (config?.mode === "json") dt = void 0;
@@ -149,7 +154,9 @@ function blob(name, config) {
149
154
  _dataType: dt
150
155
  });
151
156
  }
152
- function numeric(name, config) {
157
+ function numeric(nameOrConfig, maybeConfig) {
158
+ const name = typeof nameOrConfig === "string" ? nameOrConfig : "";
159
+ const config = typeof nameOrConfig === "string" ? maybeConfig : nameOrConfig;
153
160
  let dt = "";
154
161
  if (config?.mode === "number") dt = 0;
155
162
  if (config?.mode === "bigint") dt = 0n;
@@ -160,22 +167,32 @@ function numeric(name, config) {
160
167
  _dataType: dt
161
168
  });
162
169
  }
163
- var boolean = (name) => createColumn({
164
- name,
165
- type: "INTEGER",
166
- _dataType: false,
167
- mode: "boolean"
168
- });
169
- var timestamp = (name) => createColumn({
170
- name,
171
- type: "INTEGER",
172
- _dataType: /* @__PURE__ */ new Date(),
173
- mode: "timestamp"
174
- });
170
+ function boolean(name) {
171
+ return createColumn({
172
+ name: name ?? "",
173
+ type: "INTEGER",
174
+ _dataType: false,
175
+ mode: "boolean"
176
+ });
177
+ }
178
+ function timestamp(name) {
179
+ return createColumn({
180
+ name: name ?? "",
181
+ type: "INTEGER",
182
+ _dataType: /* @__PURE__ */ new Date(),
183
+ mode: "timestamp"
184
+ });
185
+ }
186
+ function increments(name) {
187
+ return integer(name ?? "").primaryKey({
188
+ autoIncrement: true
189
+ });
190
+ }
175
191
  function defineTable(tableName, schema) {
176
192
  const finalizedSchema = { ...schema };
177
193
  for (const key of Object.keys(finalizedSchema)) {
178
194
  const col = finalizedSchema[key];
195
+ if (!col.name || col.name === "") col.name = key;
179
196
  col.tableName = tableName;
180
197
  }
181
198
  const table = {
@@ -184,7 +201,7 @@ function defineTable(tableName, schema) {
184
201
  // The Drizzle-like type inference properties
185
202
  $inferSelect: {},
186
203
  $inferInsert: {}
187
- // Example: omit 'id' for inserts
204
+ // omit PK columns
188
205
  };
189
206
  for (const [key, col] of Object.entries(finalizedSchema)) {
190
207
  table[key] = col;
@@ -192,6 +209,45 @@ function defineTable(tableName, schema) {
192
209
  return table;
193
210
  }
194
211
 
212
+ // src/orm.ts
213
+ var import_plugin_sql = __toESM(require("@tauri-apps/plugin-sql"));
214
+
215
+ // src/sql-helpers.ts
216
+ function isColumn(value) {
217
+ return typeof value === "object" && value !== null && "_dataType" in value;
218
+ }
219
+ function getQualifiedName(column) {
220
+ if (column.tableName) return `${column.tableName}.${column.name}`;
221
+ return column.name;
222
+ }
223
+ function comparison(operator, column, value) {
224
+ return {
225
+ toSQL: () => {
226
+ if (isColumn(value)) {
227
+ return {
228
+ clause: `${getQualifiedName(column)} ${operator} ${getQualifiedName(
229
+ value
230
+ )}`,
231
+ bindings: []
232
+ };
233
+ }
234
+ return {
235
+ clause: `${getQualifiedName(column)} ${operator} ?`,
236
+ bindings: [value]
237
+ };
238
+ }
239
+ };
240
+ }
241
+ var eq = (column, value) => comparison("=", column, value);
242
+ var ne = (column, value) => comparison("!=", column, value);
243
+ var gt = (column, value) => comparison(">", column, value);
244
+ var gte = (column, value) => comparison(">=", column, value);
245
+ var lt = (column, value) => comparison("<", column, value);
246
+ var lte = (column, value) => comparison("<=", column, value);
247
+ var like = (column, value) => comparison("LIKE", column, value);
248
+ var asc = (column) => `${getQualifiedName(column)} ASC`;
249
+ var desc = (column) => `${getQualifiedName(column)} DESC`;
250
+
195
251
  // src/orm.ts
196
252
  var SelectQueryBuilder = class {
197
253
  _table = null;
@@ -202,7 +258,9 @@ var SelectQueryBuilder = class {
202
258
  _limit = null;
203
259
  _offset = null;
204
260
  _eager = {};
205
- constructor(fields) {
261
+ _dbProvider;
262
+ constructor(dbProvider, fields) {
263
+ this._dbProvider = dbProvider;
206
264
  if (fields) {
207
265
  this._selectedColumns = Object.values(fields).map((c) => c.name);
208
266
  }
@@ -238,7 +296,7 @@ var SelectQueryBuilder = class {
238
296
  if (!this._table) {
239
297
  throw new Error("Cannot execute select query without a 'from' table.");
240
298
  }
241
- const db3 = getDb();
299
+ const db = await this._dbProvider();
242
300
  const bindings = [];
243
301
  let query = `SELECT ${this._selectedColumns.join(", ")} FROM ${this._table._tableName}`;
244
302
  if (this._joins.length > 0) {
@@ -266,7 +324,7 @@ var SelectQueryBuilder = class {
266
324
  query += ` OFFSET ?`;
267
325
  bindings.push(this._offset);
268
326
  }
269
- return db3.select(query, bindings);
327
+ return db.select(query, bindings);
270
328
  }
271
329
  };
272
330
  var TauriORM = class {
@@ -274,15 +332,23 @@ var TauriORM = class {
274
332
  query = {};
275
333
  _tables = null;
276
334
  _relations = null;
335
+ _dbPromise;
336
+ constructor(dbUri) {
337
+ this._dbPromise = import_plugin_sql.default.load(dbUri);
338
+ }
339
+ async getDb() {
340
+ return this._dbPromise;
341
+ }
277
342
  // Deprecated: use configure()
278
343
  configureQuery(tables, relations2) {
279
344
  this.configure(tables, relations2);
280
345
  }
281
346
  select(fields) {
282
- return new SelectQueryBuilder(fields);
347
+ return new SelectQueryBuilder(this.getDb.bind(this), fields);
283
348
  }
284
349
  // --- Drizzle-style CRUD builders ---
285
350
  insert(table) {
351
+ const self = this;
286
352
  return new class InsertBuilder {
287
353
  _table = table;
288
354
  _rows = [];
@@ -291,13 +357,17 @@ var TauriORM = class {
291
357
  return this;
292
358
  }
293
359
  async execute() {
294
- const db3 = getDb();
360
+ const db = await self.getDb();
295
361
  for (const data of this._rows) {
296
362
  const finalData = { ...data };
297
363
  const schema = this._table._schema;
298
364
  for (const [key, col] of Object.entries(schema)) {
299
- if (finalData[key] === void 0 && col.defaultFn) {
300
- finalData[key] = col.defaultFn();
365
+ if (finalData[key] === void 0) {
366
+ if (col.defaultFn) {
367
+ finalData[key] = col.defaultFn();
368
+ } else if (col.onUpdateFn) {
369
+ finalData[key] = col.onUpdateFn();
370
+ }
301
371
  }
302
372
  }
303
373
  const keys = Object.keys(finalData);
@@ -306,12 +376,13 @@ var TauriORM = class {
306
376
  const query = `INSERT INTO ${this._table._tableName} (${keys.join(
307
377
  ", "
308
378
  )}) VALUES (${placeholders})`;
309
- await db3.execute(query, values);
379
+ await db.execute(query, values);
310
380
  }
311
381
  }
312
382
  }();
313
383
  }
314
384
  update(table) {
385
+ const self = this;
315
386
  return new class UpdateBuilder {
316
387
  _table = table;
317
388
  _data = null;
@@ -327,10 +398,17 @@ var TauriORM = class {
327
398
  async execute() {
328
399
  if (!this._data)
329
400
  throw new Error("Update requires set() before execute()");
330
- const db3 = getDb();
331
- const setKeys = Object.keys(this._data);
401
+ const db = await self.getDb();
402
+ const schema = this._table._schema;
403
+ const dataToSet = { ...this._data };
404
+ for (const [key, col] of Object.entries(schema)) {
405
+ if (!(key in dataToSet) && col.onUpdateFn) {
406
+ dataToSet[key] = col.onUpdateFn();
407
+ }
408
+ }
409
+ const setKeys = Object.keys(dataToSet);
332
410
  const setClause = setKeys.map((k) => `${k} = ?`).join(", ");
333
- const bindings = Object.values(this._data);
411
+ const bindings = Object.values(dataToSet);
334
412
  let query = `UPDATE ${this._table._tableName} SET ${setClause}`;
335
413
  if (this._where) {
336
414
  if (typeof this._where.toSQL === "function") {
@@ -345,11 +423,12 @@ var TauriORM = class {
345
423
  }
346
424
  }
347
425
  }
348
- await db3.execute(query, bindings);
426
+ await db.execute(query, bindings);
349
427
  }
350
428
  }();
351
429
  }
352
430
  delete(table) {
431
+ const self = this;
353
432
  return new class DeleteBuilder {
354
433
  _table = table;
355
434
  _where = null;
@@ -358,7 +437,7 @@ var TauriORM = class {
358
437
  return this;
359
438
  }
360
439
  async execute() {
361
- const db3 = getDb();
440
+ const db = await self.getDb();
362
441
  let query = `DELETE FROM ${this._table._tableName}`;
363
442
  const bindings = [];
364
443
  if (this._where) {
@@ -374,7 +453,7 @@ var TauriORM = class {
374
453
  }
375
454
  }
376
455
  }
377
- await db3.execute(query, bindings);
456
+ await db.execute(query, bindings);
378
457
  }
379
458
  }();
380
459
  }
@@ -382,8 +461,8 @@ var TauriORM = class {
382
461
  // legacy direct methods removed in favor of builder APIs
383
462
  // legacy direct methods removed in favor of builder APIs
384
463
  async run(query, bindings = []) {
385
- const db3 = getDb();
386
- await db3.execute(query, bindings);
464
+ const db = await this.getDb();
465
+ await db.execute(query, bindings);
387
466
  }
388
467
  // --- Migrations API ---
389
468
  generateCreateTableSql(table) {
@@ -433,16 +512,16 @@ var TauriORM = class {
433
512
  );
434
513
  }
435
514
  async hasMigration(name) {
436
- const db3 = getDb();
437
- const rows = await db3.select(
515
+ const db = await this.getDb();
516
+ const rows = await db.select(
438
517
  `SELECT name FROM _migrations WHERE name = ?`,
439
518
  [name]
440
519
  );
441
520
  return Array.isArray(rows) && rows.length > 0;
442
521
  }
443
522
  async recordMigration(name) {
444
- const db3 = getDb();
445
- await db3.execute(
523
+ const db = await this.getDb();
524
+ await db.execute(
446
525
  `INSERT INTO _migrations (name, applied_at) VALUES (?, ?)`,
447
526
  [name, Date.now()]
448
527
  );
@@ -464,7 +543,7 @@ var TauriORM = class {
464
543
  configure(tables, relDefs) {
465
544
  this._tables = tables;
466
545
  this._relations = relDefs ?? {};
467
- this.query = makeQueryAPI(tables, this._relations);
546
+ this.query = makeQueryAPI(tables, this._relations, this.getDb.bind(this));
468
547
  return this;
469
548
  }
470
549
  // Convenience: migrate from configured tables
@@ -473,8 +552,251 @@ var TauriORM = class {
473
552
  throw new Error("No tables configured. Call db.configure({...}) first.");
474
553
  await this.migrate(Object.values(this._tables), options);
475
554
  }
555
+ // --- Schema diff and CLI-like helpers ---
556
+ async diffSchema() {
557
+ if (!this._tables) throw new Error("No tables configured.");
558
+ const dbi = await this.getDb();
559
+ const configuredNames = Object.values(this._tables).map(
560
+ (t) => t._tableName
561
+ );
562
+ const existing = await dbi.select(
563
+ `SELECT name FROM sqlite_master WHERE type='table'`
564
+ );
565
+ const existingNames = existing.map((r) => r.name);
566
+ const extraTables = existingNames.filter(
567
+ (n) => !configuredNames.includes(n)
568
+ );
569
+ const missingTables = configuredNames.filter(
570
+ (n) => !existingNames.includes(n)
571
+ );
572
+ const tables = {};
573
+ for (const tbl of Object.values(this._tables)) {
574
+ const tableName = tbl._tableName;
575
+ if (!existingNames.includes(tableName)) {
576
+ tables[tableName] = {
577
+ missingColumns: Object.keys(tbl._schema),
578
+ extraColumns: [],
579
+ changedColumns: []
580
+ };
581
+ continue;
582
+ }
583
+ const cols = await dbi.select(`PRAGMA table_info('${tableName}')`);
584
+ const colMap = new Map(cols.map((c) => [c.name, c]));
585
+ const modelCols = Object.values(
586
+ tbl._schema
587
+ );
588
+ const missingColumns = [];
589
+ const extraColumns = [];
590
+ const changedColumns = [];
591
+ const modelNamesSet = new Set(modelCols.map((c) => c.name));
592
+ for (const m of modelCols) {
593
+ const info = colMap.get(m.name);
594
+ if (!info) {
595
+ missingColumns.push(m.name);
596
+ continue;
597
+ }
598
+ const diffs = {};
599
+ if ((info.type || "").toUpperCase() !== m.type.toUpperCase())
600
+ diffs.type = true;
601
+ if (!!info.pk !== !!m.isPrimaryKey) diffs.pk = true;
602
+ if (!!info.notnull !== !!m.isNotNull) diffs.notNull = true;
603
+ const modelDv = m.defaultValue && typeof m.defaultValue === "object" && m.defaultValue.raw ? m.defaultValue.raw : m.defaultValue ?? null;
604
+ if ((info.dflt_value ?? null) !== modelDv)
605
+ diffs.default = true;
606
+ if (Object.keys(diffs).length)
607
+ changedColumns.push({ name: m.name, diffs });
608
+ }
609
+ for (const c of cols)
610
+ if (!modelNamesSet.has(c.name)) extraColumns.push(c.name);
611
+ tables[tableName] = { missingColumns, extraColumns, changedColumns };
612
+ }
613
+ return { extraTables, missingTables, tables };
614
+ }
615
+ async generate() {
616
+ if (!this._tables) throw new Error("No tables configured.");
617
+ return {
618
+ statements: Object.values(this._tables).map(
619
+ (t) => this.buildCreateTableSQL(t)
620
+ )
621
+ };
622
+ }
623
+ async migrateCli(opts) {
624
+ return this.migrateConfigured(opts);
625
+ }
626
+ async push(opts) {
627
+ return this.forcePush(opts);
628
+ }
629
+ async pull() {
630
+ return this.pullSchema();
631
+ }
632
+ async studio() {
633
+ const dbi = await this.getDb();
634
+ return { driver: "sqlite", path: dbi.path };
635
+ }
636
+ // --- Schema detection / signature ---
637
+ async ensureSchemaMeta() {
638
+ await this.run(
639
+ `CREATE TABLE IF NOT EXISTS _schema_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)`
640
+ );
641
+ }
642
+ async getSchemaMeta(key) {
643
+ const dbi = await this.getDb();
644
+ await this.ensureSchemaMeta();
645
+ const rows = await dbi.select(
646
+ `SELECT value FROM _schema_meta WHERE key = ?`,
647
+ [key]
648
+ );
649
+ return rows?.[0]?.value ?? null;
650
+ }
651
+ async setSchemaMeta(key, value) {
652
+ const dbi = await this.getDb();
653
+ await this.ensureSchemaMeta();
654
+ await dbi.execute(
655
+ `INSERT INTO _schema_meta(key, value) VALUES(?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value`,
656
+ [key, value]
657
+ );
658
+ }
659
+ normalizeColumn(col) {
660
+ return {
661
+ name: col.name,
662
+ type: col.type,
663
+ pk: !!col.isPrimaryKey,
664
+ ai: !!col.autoIncrement,
665
+ nn: !!col.isNotNull,
666
+ dv: col.defaultValue && typeof col.defaultValue === "object" && col.defaultValue.raw ? { raw: col.defaultValue.raw } : col.defaultValue ?? null
667
+ };
668
+ }
669
+ computeModelSignature() {
670
+ if (!this._tables) return "";
671
+ const entries = Object.entries(this._tables).map(([k, tbl]) => {
672
+ const cols = Object.values(
673
+ tbl._schema
674
+ ).map((c) => this.normalizeColumn(c)).sort((a, b) => a.name.localeCompare(b.name));
675
+ return { table: tbl._tableName, columns: cols };
676
+ });
677
+ entries.sort((a, b) => a.table.localeCompare(b.table));
678
+ return JSON.stringify(entries);
679
+ }
680
+ async isSchemaDirty() {
681
+ const sig = this.computeModelSignature();
682
+ const stored = await this.getSchemaMeta("schema_signature");
683
+ return { dirty: sig !== stored, current: sig, stored };
684
+ }
685
+ async migrateIfDirty(options) {
686
+ const status = await this.isSchemaDirty();
687
+ if (!this._tables) throw new Error("No tables configured.");
688
+ if (status.dirty) {
689
+ await this.migrate(Object.values(this._tables), options);
690
+ await this.setSchemaMeta(
691
+ "schema_signature",
692
+ this.computeModelSignature()
693
+ );
694
+ return true;
695
+ }
696
+ return false;
697
+ }
698
+ // Pull current DB schema (minimal) for configured tables
699
+ async pullSchema() {
700
+ if (!this._tables) throw new Error("No tables configured.");
701
+ const dbi = await this.getDb();
702
+ const result = {};
703
+ for (const tbl of Object.values(this._tables)) {
704
+ const name = tbl._tableName;
705
+ const cols = await dbi.select(`PRAGMA table_info('${name}')`);
706
+ result[name] = cols.map((c) => ({
707
+ name: c.name,
708
+ type: c.type,
709
+ notnull: !!c.notnull,
710
+ pk: !!c.pk,
711
+ dflt_value: c.dflt_value ?? null
712
+ }));
713
+ }
714
+ return result;
715
+ }
716
+ buildCreateTableSQL(table) {
717
+ return this.generateCreateTableSql(table);
718
+ }
719
+ async tableExists(name) {
720
+ const dbi = await this.getDb();
721
+ const rows = await dbi.select(
722
+ `SELECT name FROM sqlite_master WHERE type='table' AND name = ?`,
723
+ [name]
724
+ );
725
+ return rows.length > 0;
726
+ }
727
+ // Force push model to DB: add missing tables/columns, rebuild tables if incompatible
728
+ async forcePush(options) {
729
+ if (!this._tables) throw new Error("No tables configured.");
730
+ const dbi = await this.getDb();
731
+ const preserve = options?.preserveData !== false;
732
+ for (const tbl of Object.values(this._tables)) {
733
+ const tableName = tbl._tableName;
734
+ const exists = await this.tableExists(tableName);
735
+ if (!exists) {
736
+ await this.run(this.buildCreateTableSQL(tbl));
737
+ continue;
738
+ }
739
+ const existingCols = await dbi.select(
740
+ `PRAGMA table_info('${tableName}')`
741
+ );
742
+ const existingMap = new Map(existingCols.map((c) => [c.name, c]));
743
+ const modelCols = Object.values(
744
+ tbl._schema
745
+ );
746
+ const missing = [];
747
+ let requiresRebuild = false;
748
+ for (const m of modelCols) {
749
+ const info = existingMap.get(m.name);
750
+ if (!info) {
751
+ missing.push(m);
752
+ continue;
753
+ }
754
+ const typeDiff = (info.type || "").toUpperCase() !== m.type.toUpperCase();
755
+ const pkDiff = !!info.pk !== !!m.isPrimaryKey;
756
+ const nnDiff = !!info.notnull !== !!m.isNotNull;
757
+ const modelDv = m.defaultValue && typeof m.defaultValue === "object" && m.defaultValue.raw ? m.defaultValue.raw : m.defaultValue ?? null;
758
+ const defDiff = (info.dflt_value ?? null) !== modelDv;
759
+ if (typeDiff || pkDiff || nnDiff && !modelDv || defDiff) {
760
+ requiresRebuild = true;
761
+ }
762
+ }
763
+ if (requiresRebuild) {
764
+ const tmp = `_new_${tableName}`;
765
+ await this.run(this.buildCreateTableSQL(tbl));
766
+ await this.run(
767
+ this.buildCreateTableSQL({ ...tbl, _tableName: tmp })
768
+ );
769
+ const existingNames = existingCols.map((c) => c.name);
770
+ const modelNames = modelCols.map((c) => c.name);
771
+ const shared = existingNames.filter((n) => modelNames.includes(n));
772
+ if (preserve && shared.length > 0) {
773
+ await this.run(
774
+ `INSERT INTO ${tmp} (${shared.join(", ")}) SELECT ${shared.join(
775
+ ", "
776
+ )} FROM ${tableName}`
777
+ );
778
+ }
779
+ await this.run(`DROP TABLE ${tableName}`);
780
+ await this.run(`ALTER TABLE ${tmp} RENAME TO ${tableName}`);
781
+ } else {
782
+ for (const m of missing) {
783
+ let clause = `${m.name} ${m.type}`;
784
+ if (m.isNotNull) clause += " NOT NULL";
785
+ if (m.defaultValue !== void 0) {
786
+ const dv = m.defaultValue;
787
+ if (dv && typeof dv === "object" && "raw" in dv)
788
+ clause += ` DEFAULT ${dv.raw}`;
789
+ else if (typeof dv === "string")
790
+ clause += ` DEFAULT '${dv.replace(/'/g, "''")}'`;
791
+ else clause += ` DEFAULT ${dv}`;
792
+ }
793
+ await this.run(`ALTER TABLE ${tableName} ADD COLUMN ${clause}`);
794
+ }
795
+ }
796
+ }
797
+ await this.setSchemaMeta("schema_signature", this.computeModelSignature());
798
+ }
476
799
  };
477
- var db2 = new TauriORM();
478
800
  function relations(baseTable, builder) {
479
801
  const ctx = {
480
802
  one: (table, cfg) => ({ kind: "one", table, cfg }),
@@ -500,10 +822,29 @@ function guessChildFk(child, base, rel) {
500
822
  ];
501
823
  return childCols.find((c) => guessNames.includes(c.name)) || childCols.find((c) => /.*_id$/i.test(c.name)) || null;
502
824
  }
825
+ function guessOneRelationJoin(base, rel) {
826
+ const child = rel.table;
827
+ const basePk = getPrimaryKey(base);
828
+ const childCols = Object.values(child._schema);
829
+ if (rel.cfg?.fields && rel.cfg?.references && rel.cfg.fields[0] && rel.cfg.references[0]) {
830
+ const fk = rel.cfg.fields[0];
831
+ const ref = rel.cfg.references[0];
832
+ if (childCols.some((c) => c.name === fk.name)) {
833
+ return { lhsTable: child, lhsCol: fk, rhsTable: base, rhsCol: ref };
834
+ }
835
+ const baseCols = Object.values(base._schema);
836
+ if (baseCols.some((c) => c.name === fk.name)) {
837
+ return { lhsTable: base, lhsCol: fk, rhsTable: child, rhsCol: ref };
838
+ }
839
+ }
840
+ const childFk = guessChildFk(child, base, rel);
841
+ if (!childFk) return null;
842
+ return { lhsTable: child, lhsCol: childFk, rhsTable: base, rhsCol: basePk };
843
+ }
503
844
  function isFlatWith(spec) {
504
845
  return Object.values(spec).every((v) => typeof v === "boolean");
505
846
  }
506
- function makeQueryAPI(tables, relDefs) {
847
+ function makeQueryAPI(tables, relDefs, dbProvider) {
507
848
  const api = {};
508
849
  const tableKeyByName = {};
509
850
  for (const [k, t] of Object.entries(tables)) tableKeyByName[t._tableName] = k;
@@ -512,7 +853,7 @@ function makeQueryAPI(tables, relDefs) {
512
853
  async findMany(opts) {
513
854
  const base = tbl;
514
855
  const withSpec = opts?.with ?? {};
515
- const dbi = getDb();
856
+ const dbi = await dbProvider();
516
857
  const rels = relDefs[tblKey] ?? {};
517
858
  if (opts?.join && isFlatWith(withSpec)) {
518
859
  const baseCols = Object.values(base._schema);
@@ -538,23 +879,43 @@ function makeQueryAPI(tables, relDefs) {
538
879
  const child = rel.table;
539
880
  const childCols = Object.values(child._schema);
540
881
  const childPk = childCols.find((c) => c.isPrimaryKey) || childCols.find((c) => c.name === "id") || null;
541
- const childFk = guessChildFk(child, base, rel);
542
- if (!childFk) continue;
543
- fkMap[relName] = { childFk, childPk };
882
+ if (rel.kind === "one") {
883
+ const mapping = guessOneRelationJoin(base, rel);
884
+ if (!mapping) continue;
885
+ if (mapping.lhsTable._tableName === child._tableName) {
886
+ fkMap[relName] = { childFk: mapping.lhsCol, childPk };
887
+ joins.push(
888
+ `LEFT JOIN ${child._tableName} ON ${mapping.lhsTable._tableName}.${mapping.lhsCol.name} = ${mapping.rhsTable._tableName}.${mapping.rhsCol.name}`
889
+ );
890
+ } else {
891
+ fkMap[relName] = { childFk: mapping.rhsCol, childPk };
892
+ joins.push(
893
+ `LEFT JOIN ${child._tableName} ON ${mapping.lhsTable._tableName}.${mapping.lhsCol.name} = ${mapping.rhsTable._tableName}.${mapping.rhsCol.name}`
894
+ );
895
+ }
896
+ } else {
897
+ const childFk = guessChildFk(child, base, rel);
898
+ if (!childFk) continue;
899
+ fkMap[relName] = { childFk, childPk };
900
+ joins.push(
901
+ `LEFT JOIN ${child._tableName} ON ${child._tableName}.${childFk.name} = ${base._tableName}.${basePk.name}`
902
+ );
903
+ }
544
904
  const selected = typeof enabled === "object" && enabled.columns?.length ? enabled.columns : childCols.map((c) => c.name);
545
905
  relColsMap[relName] = selected;
546
906
  for (const name of selected)
547
907
  selectParts.push(
548
908
  `${child._tableName}.${name} AS __rel_${relName}_${name}`
549
909
  );
550
- joins.push(
551
- `LEFT JOIN ${child._tableName} ON ${child._tableName}.${childFk.name} = ${base._tableName}.${basePk.name}`
552
- );
553
910
  }
554
911
  let sqlText = `SELECT ${selectParts.join(", ")} FROM ${base._tableName}${joins.length ? " " + joins.join(" ") : ""}`;
555
912
  const bindings = [];
556
913
  if (opts?.where) {
557
- if (typeof opts.where.toSQL === "function") {
914
+ if (typeof opts.where === "function") {
915
+ const w = opts.where(base, { eq, ne, gt, gte, lt, lte, like }).toSQL();
916
+ sqlText += ` WHERE ${w.clause}`;
917
+ bindings.push(...w.bindings);
918
+ } else if (typeof opts.where.toSQL === "function") {
558
919
  const w = opts.where.toSQL();
559
920
  sqlText += ` WHERE ${w.clause}`;
560
921
  bindings.push(...w.bindings);
@@ -566,8 +927,9 @@ function makeQueryAPI(tables, relDefs) {
566
927
  }
567
928
  }
568
929
  }
569
- if (opts?.orderBy?.length)
570
- sqlText += ` ORDER BY ${opts.orderBy.join(", ")}`;
930
+ const orderByClauses = typeof opts?.orderBy === "function" ? opts.orderBy(base, { asc, desc }) : opts?.orderBy;
931
+ if (orderByClauses?.length)
932
+ sqlText += ` ORDER BY ${orderByClauses.join(", ")}`;
571
933
  if (typeof opts?.limit === "number")
572
934
  sqlText += ` LIMIT ${opts.limit}`;
573
935
  if (typeof opts?.offset === "number")
@@ -634,7 +996,11 @@ function makeQueryAPI(tables, relDefs) {
634
996
  let baseSql = `SELECT ${baseSelected.join(", ")} FROM ${base._tableName}`;
635
997
  const baseBindings = [];
636
998
  if (opts?.where) {
637
- if (typeof opts.where.toSQL === "function") {
999
+ if (typeof opts.where === "function") {
1000
+ const w = opts.where(base, { eq, ne, gt, gte, lt, lte, like }).toSQL();
1001
+ baseSql += ` WHERE ${w.clause}`;
1002
+ baseBindings.push(...w.bindings);
1003
+ } else if (typeof opts.where.toSQL === "function") {
638
1004
  const w = opts.where.toSQL();
639
1005
  baseSql += ` WHERE ${w.clause}`;
640
1006
  baseBindings.push(...w.bindings);
@@ -646,8 +1012,9 @@ function makeQueryAPI(tables, relDefs) {
646
1012
  }
647
1013
  }
648
1014
  }
649
- if (opts?.orderBy?.length)
650
- baseSql += ` ORDER BY ${opts.orderBy.join(", ")}`;
1015
+ const orderByClauses2 = typeof opts?.orderBy === "function" ? opts.orderBy(base, { asc, desc }) : opts?.orderBy;
1016
+ if (orderByClauses2?.length)
1017
+ baseSql += ` ORDER BY ${orderByClauses2.join(", ")}`;
651
1018
  if (typeof opts?.limit === "number") baseSql += ` LIMIT ${opts.limit}`;
652
1019
  if (typeof opts?.offset === "number")
653
1020
  baseSql += ` OFFSET ${opts.offset}`;
@@ -666,9 +1033,9 @@ function makeQueryAPI(tables, relDefs) {
666
1033
  const child = rel.table;
667
1034
  const childCols = Object.values(child._schema);
668
1035
  const selectCols = enabled?.columns && enabled.columns.length > 0 ? enabled.columns : childCols.map((c) => c.name);
669
- const fkCol = guessChildFk(child, parentTable, rel);
670
- if (!fkCol) continue;
671
1036
  if (rel.kind === "many") {
1037
+ const fkCol = guessChildFk(child, parentTable, rel);
1038
+ if (!fkCol) continue;
672
1039
  const sql2 = `SELECT ${selectCols.join(", ")} FROM ${child._tableName} WHERE ${fkCol.name} IN (${parentIds.map(() => "?").join(", ")})`;
673
1040
  const rows = await dbi.select(sql2, parentIds);
674
1041
  const buckets = /* @__PURE__ */ new Map();
@@ -687,14 +1054,32 @@ function makeQueryAPI(tables, relDefs) {
687
1054
  await loadRelationsFor(children, child, enabled.with);
688
1055
  }
689
1056
  } else {
690
- const sql2 = `SELECT ${selectCols.join(", ")} FROM ${child._tableName} WHERE ${fkCol.name} IN (${parentIds.map(() => "?").join(", ")})`;
691
- const rows = await dbi.select(sql2, parentIds);
692
- const mapOne = /* @__PURE__ */ new Map();
693
- for (const r of rows) mapOne.set(r[fkCol.name], r);
694
- for (const p of parents)
695
- p[relName] = mapOne.get(p[parentPk.name]) ?? null;
1057
+ const mapping = guessOneRelationJoin(parentTable, rel);
1058
+ if (!mapping) continue;
1059
+ if (mapping.lhsTable._tableName === child._tableName) {
1060
+ const sql2 = `SELECT ${selectCols.join(", ")} FROM ${child._tableName} WHERE ${mapping.lhsCol.name} IN (${parentIds.map(() => "?").join(", ")})`;
1061
+ const rows = await dbi.select(sql2, parentIds);
1062
+ const mapOne = /* @__PURE__ */ new Map();
1063
+ for (const r of rows) mapOne.set(r[mapping.lhsCol.name], r);
1064
+ for (const p of parents)
1065
+ p[relName] = mapOne.get(p[parentPk.name]) ?? null;
1066
+ } else {
1067
+ const parentFkName = mapping.lhsCol.name;
1068
+ const childPkName = mapping.rhsCol.name;
1069
+ const childIds = parents.map((p) => p[parentFkName]).filter((v2) => v2 !== void 0 && v2 !== null);
1070
+ if (childIds.length === 0) {
1071
+ for (const p of parents) p[relName] = null;
1072
+ } else {
1073
+ const sql2 = `SELECT ${selectCols.join(", ")} FROM ${child._tableName} WHERE ${childPkName} IN (${childIds.map(() => "?").join(", ")})`;
1074
+ const rows = await dbi.select(sql2, childIds);
1075
+ const mapOne = /* @__PURE__ */ new Map();
1076
+ for (const r of rows) mapOne.set(r[childPkName], r);
1077
+ for (const p of parents)
1078
+ p[relName] = mapOne.get(p[parentFkName]) ?? null;
1079
+ }
1080
+ }
696
1081
  if (enabled?.with) {
697
- const children = parents.map((p) => p[relName]).filter(Boolean);
1082
+ const children = parents.map((p) => p[relName]).filter((x) => Boolean(x));
698
1083
  if (children.length > 0)
699
1084
  await loadRelationsFor(children, child, enabled.with);
700
1085
  }
@@ -705,61 +1090,27 @@ function makeQueryAPI(tables, relDefs) {
705
1090
  await loadRelationsFor(result, base, withSpec);
706
1091
  }
707
1092
  return result;
1093
+ },
1094
+ async findFirst(opts) {
1095
+ const rows = await api[tblKey].findMany({ ...opts, limit: 1 });
1096
+ return rows?.[0] ?? null;
708
1097
  }
709
1098
  };
710
1099
  }
711
1100
  return api;
712
1101
  }
713
-
714
- // src/sql-helpers.ts
715
- function isColumn(value) {
716
- return typeof value === "object" && value !== null && "_dataType" in value;
717
- }
718
- function getQualifiedName(column) {
719
- if (column.tableName) return `${column.tableName}.${column.name}`;
720
- return column.name;
721
- }
722
- function comparison(operator, column, value) {
723
- return {
724
- toSQL: () => {
725
- if (isColumn(value)) {
726
- return {
727
- clause: `${getQualifiedName(column)} ${operator} ${getQualifiedName(
728
- value
729
- )}`,
730
- bindings: []
731
- };
732
- }
733
- return {
734
- clause: `${getQualifiedName(column)} ${operator} ?`,
735
- bindings: [value]
736
- };
737
- }
738
- };
739
- }
740
- var eq = (column, value) => comparison("=", column, value);
741
- var ne = (column, value) => comparison("!=", column, value);
742
- var gt = (column, value) => comparison(">", column, value);
743
- var gte = (column, value) => comparison(">=", column, value);
744
- var lt = (column, value) => comparison("<", column, value);
745
- var lte = (column, value) => comparison("<=", column, value);
746
- var like = (column, value) => comparison("LIKE", column, value);
747
- var asc = (column) => `${column.name} ASC`;
748
- var desc = (column) => `${column.name} DESC`;
749
1102
  // Annotate the CommonJS export names for ESM import in node:
750
1103
  0 && (module.exports = {
751
1104
  TauriORM,
752
1105
  asc,
753
1106
  blob,
754
1107
  boolean,
755
- db,
756
1108
  defineTable,
757
1109
  desc,
758
1110
  eq,
759
- getDb,
760
1111
  gt,
761
1112
  gte,
762
- initDb,
1113
+ increments,
763
1114
  integer,
764
1115
  like,
765
1116
  lt,