@type32/tauri-sqlite-orm 0.1.8 → 0.1.10

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.d.mts CHANGED
@@ -188,6 +188,12 @@ declare const notExists: (subquery: SQL | {
188
188
  declare const asc: (column: Column) => string;
189
189
  declare const desc: (column: Column) => string;
190
190
 
191
+ type InferInsert<T> = T extends {
192
+ $inferInsert: infer I;
193
+ } ? I : never;
194
+ type InferSelect<T> = T extends {
195
+ $inferSelect: infer S;
196
+ } ? S : never;
191
197
  declare class SelectQueryBuilder<T> {
192
198
  private _table;
193
199
  private _selectedColumns;
@@ -222,11 +228,17 @@ declare class TauriORM {
222
228
  constructor(dbUri: string);
223
229
  private getDb;
224
230
  configureQuery(tables: Record<string, Table<any>>, relations: Record<string, Record<string, RelationConfig>>): void;
225
- select<T>(fields?: Record<string, Column<any>>): SelectQueryBuilder<T>;
226
- selectDistinct<T>(fields?: Record<string, Column<any>>): SelectQueryBuilder<T>;
227
- insert(table: Table<any>): {
228
- _table: any;
229
- _rows: Record<string, any>[];
231
+ select<TFields extends Record<string, Column<any>>>(fields: TFields): SelectQueryBuilder<{
232
+ [K in keyof TFields]: TFields[K]["_dataType"];
233
+ }>;
234
+ select<T = any>(fields?: undefined): SelectQueryBuilder<T>;
235
+ selectDistinct<TFields extends Record<string, Column<any>>>(fields: TFields): SelectQueryBuilder<{
236
+ [K in keyof TFields]: TFields[K]["_dataType"];
237
+ }>;
238
+ selectDistinct<T = any>(fields?: undefined): SelectQueryBuilder<T>;
239
+ insert<TTable extends Table<any>>(table: TTable): {
240
+ _table: TTable;
241
+ _rows: Array<InferInsert<TTable>>;
230
242
  _selectSql: {
231
243
  clause: string;
232
244
  bindings: any[];
@@ -243,7 +255,7 @@ declare class TauriORM {
243
255
  setWhere?: SQL;
244
256
  };
245
257
  _returning: null | "__RETURNING_ID__" | Record<string, Column<any>>;
246
- values(rowOrRows: Record<string, any> | Record<string, any>[]): /*elided*/ any;
258
+ values(rowOrRows: InferInsert<TTable> | Array<InferInsert<TTable>>): /*elided*/ any;
247
259
  select(qb: {
248
260
  toSQL?: () => {
249
261
  clause: string;
@@ -266,15 +278,15 @@ declare class TauriORM {
266
278
  _buildConflictClause(): string;
267
279
  _executeWithReturning(db: any, query: string, bindings: any[]): Promise<any>;
268
280
  };
269
- update(table: Table<any>): {
270
- _table: any;
271
- _data: Record<string, any> | null;
281
+ update<TTable extends Table<any>>(table: TTable): {
282
+ _table: TTable;
283
+ _data: Partial<InferInsert<TTable>> | null;
272
284
  _where: Record<string, any> | SQL | null;
273
285
  _orderBy: Array<string | SQL>;
274
286
  _limit: number | null;
275
287
  _from: Table<any> | null;
276
288
  _returning: null | Record<string, Column<any>>;
277
- set(data: Record<string, any>): /*elided*/ any;
289
+ set(data: Partial<InferInsert<TTable>>): /*elided*/ any;
278
290
  where(cond: Record<string, any> | SQL): /*elided*/ any;
279
291
  orderBy(...clauses: (string | Column<any> | SQL)[]): /*elided*/ any;
280
292
  limit(n: number): /*elided*/ any;
@@ -282,13 +294,13 @@ declare class TauriORM {
282
294
  returning(fields?: Record<string, Column<any>>): any;
283
295
  execute(): Promise<unknown>;
284
296
  };
285
- delete(table: Table<any>): {
286
- _table: any;
287
- _where: Record<string, any> | SQL | null;
297
+ delete<TTable extends Table<any>>(table: TTable): {
298
+ _table: TTable;
299
+ _where: Partial<InferInsert<TTable>> | SQL | null;
288
300
  _orderBy: Array<string | SQL>;
289
301
  _limit: number | null;
290
302
  _returning: null | Record<string, Column<any>>;
291
- where(cond: Record<string, any> | SQL): /*elided*/ any;
303
+ where(cond: Partial<InferInsert<TTable>> | SQL): /*elided*/ any;
292
304
  orderBy(...clauses: (string | Column<any> | SQL)[]): /*elided*/ any;
293
305
  limit(n: number): /*elided*/ any;
294
306
  returning(fields?: Record<string, Column<any>>): any;
@@ -299,6 +311,8 @@ declare class TauriORM {
299
311
  private generateCreateTableSql;
300
312
  createTableIfNotExists(table: Table<any>): Promise<void>;
301
313
  createTablesIfNotExist(tables: Table<any>[]): Promise<void>;
314
+ private generateCreateIndexSqls;
315
+ private createIndexesForTable;
302
316
  private ensureMigrationsTable;
303
317
  private hasMigration;
304
318
  private recordMigration;
@@ -306,7 +320,14 @@ declare class TauriORM {
306
320
  name?: string;
307
321
  track?: boolean;
308
322
  }): Promise<void>;
309
- configure(tables: Record<string, Table<any>>, relDefs?: Record<string, Record<string, RelationConfig>>): this;
323
+ configure<TTables extends Record<string, Table<any>>, TRelDefs extends Record<string, Record<string, RelationConfig>> = Record<string, Record<string, RelationConfig>>>(tables: TTables, relDefs?: TRelDefs): this & {
324
+ query: {
325
+ [K in keyof TTables]: {
326
+ findMany: (opts?: any) => Promise<Array<InferSelect<TTables[K]>>>;
327
+ findFirst: (opts?: any) => Promise<InferSelect<TTables[K]> | null>;
328
+ };
329
+ };
330
+ };
310
331
  migrateConfigured(options?: {
311
332
  name?: string;
312
333
  track?: boolean;
@@ -349,6 +370,8 @@ declare class TauriORM {
349
370
  private setSchemaMeta;
350
371
  private normalizeColumn;
351
372
  private computeModelSignature;
373
+ getSchemaSignature(): string;
374
+ printSchemaDiff(): Promise<void>;
352
375
  isSchemaDirty(): Promise<{
353
376
  dirty: boolean;
354
377
  current: string;
@@ -365,6 +388,7 @@ declare class TauriORM {
365
388
  dropExtraColumns?: boolean;
366
389
  preserveData?: boolean;
367
390
  }): Promise<void>;
391
+ private forcePushForTables;
368
392
  }
369
393
  type OneConfig = {
370
394
  fields?: Column[];
package/dist/index.d.ts CHANGED
@@ -188,6 +188,12 @@ declare const notExists: (subquery: SQL | {
188
188
  declare const asc: (column: Column) => string;
189
189
  declare const desc: (column: Column) => string;
190
190
 
191
+ type InferInsert<T> = T extends {
192
+ $inferInsert: infer I;
193
+ } ? I : never;
194
+ type InferSelect<T> = T extends {
195
+ $inferSelect: infer S;
196
+ } ? S : never;
191
197
  declare class SelectQueryBuilder<T> {
192
198
  private _table;
193
199
  private _selectedColumns;
@@ -222,11 +228,17 @@ declare class TauriORM {
222
228
  constructor(dbUri: string);
223
229
  private getDb;
224
230
  configureQuery(tables: Record<string, Table<any>>, relations: Record<string, Record<string, RelationConfig>>): void;
225
- select<T>(fields?: Record<string, Column<any>>): SelectQueryBuilder<T>;
226
- selectDistinct<T>(fields?: Record<string, Column<any>>): SelectQueryBuilder<T>;
227
- insert(table: Table<any>): {
228
- _table: any;
229
- _rows: Record<string, any>[];
231
+ select<TFields extends Record<string, Column<any>>>(fields: TFields): SelectQueryBuilder<{
232
+ [K in keyof TFields]: TFields[K]["_dataType"];
233
+ }>;
234
+ select<T = any>(fields?: undefined): SelectQueryBuilder<T>;
235
+ selectDistinct<TFields extends Record<string, Column<any>>>(fields: TFields): SelectQueryBuilder<{
236
+ [K in keyof TFields]: TFields[K]["_dataType"];
237
+ }>;
238
+ selectDistinct<T = any>(fields?: undefined): SelectQueryBuilder<T>;
239
+ insert<TTable extends Table<any>>(table: TTable): {
240
+ _table: TTable;
241
+ _rows: Array<InferInsert<TTable>>;
230
242
  _selectSql: {
231
243
  clause: string;
232
244
  bindings: any[];
@@ -243,7 +255,7 @@ declare class TauriORM {
243
255
  setWhere?: SQL;
244
256
  };
245
257
  _returning: null | "__RETURNING_ID__" | Record<string, Column<any>>;
246
- values(rowOrRows: Record<string, any> | Record<string, any>[]): /*elided*/ any;
258
+ values(rowOrRows: InferInsert<TTable> | Array<InferInsert<TTable>>): /*elided*/ any;
247
259
  select(qb: {
248
260
  toSQL?: () => {
249
261
  clause: string;
@@ -266,15 +278,15 @@ declare class TauriORM {
266
278
  _buildConflictClause(): string;
267
279
  _executeWithReturning(db: any, query: string, bindings: any[]): Promise<any>;
268
280
  };
269
- update(table: Table<any>): {
270
- _table: any;
271
- _data: Record<string, any> | null;
281
+ update<TTable extends Table<any>>(table: TTable): {
282
+ _table: TTable;
283
+ _data: Partial<InferInsert<TTable>> | null;
272
284
  _where: Record<string, any> | SQL | null;
273
285
  _orderBy: Array<string | SQL>;
274
286
  _limit: number | null;
275
287
  _from: Table<any> | null;
276
288
  _returning: null | Record<string, Column<any>>;
277
- set(data: Record<string, any>): /*elided*/ any;
289
+ set(data: Partial<InferInsert<TTable>>): /*elided*/ any;
278
290
  where(cond: Record<string, any> | SQL): /*elided*/ any;
279
291
  orderBy(...clauses: (string | Column<any> | SQL)[]): /*elided*/ any;
280
292
  limit(n: number): /*elided*/ any;
@@ -282,13 +294,13 @@ declare class TauriORM {
282
294
  returning(fields?: Record<string, Column<any>>): any;
283
295
  execute(): Promise<unknown>;
284
296
  };
285
- delete(table: Table<any>): {
286
- _table: any;
287
- _where: Record<string, any> | SQL | null;
297
+ delete<TTable extends Table<any>>(table: TTable): {
298
+ _table: TTable;
299
+ _where: Partial<InferInsert<TTable>> | SQL | null;
288
300
  _orderBy: Array<string | SQL>;
289
301
  _limit: number | null;
290
302
  _returning: null | Record<string, Column<any>>;
291
- where(cond: Record<string, any> | SQL): /*elided*/ any;
303
+ where(cond: Partial<InferInsert<TTable>> | SQL): /*elided*/ any;
292
304
  orderBy(...clauses: (string | Column<any> | SQL)[]): /*elided*/ any;
293
305
  limit(n: number): /*elided*/ any;
294
306
  returning(fields?: Record<string, Column<any>>): any;
@@ -299,6 +311,8 @@ declare class TauriORM {
299
311
  private generateCreateTableSql;
300
312
  createTableIfNotExists(table: Table<any>): Promise<void>;
301
313
  createTablesIfNotExist(tables: Table<any>[]): Promise<void>;
314
+ private generateCreateIndexSqls;
315
+ private createIndexesForTable;
302
316
  private ensureMigrationsTable;
303
317
  private hasMigration;
304
318
  private recordMigration;
@@ -306,7 +320,14 @@ declare class TauriORM {
306
320
  name?: string;
307
321
  track?: boolean;
308
322
  }): Promise<void>;
309
- configure(tables: Record<string, Table<any>>, relDefs?: Record<string, Record<string, RelationConfig>>): this;
323
+ configure<TTables extends Record<string, Table<any>>, TRelDefs extends Record<string, Record<string, RelationConfig>> = Record<string, Record<string, RelationConfig>>>(tables: TTables, relDefs?: TRelDefs): this & {
324
+ query: {
325
+ [K in keyof TTables]: {
326
+ findMany: (opts?: any) => Promise<Array<InferSelect<TTables[K]>>>;
327
+ findFirst: (opts?: any) => Promise<InferSelect<TTables[K]> | null>;
328
+ };
329
+ };
330
+ };
310
331
  migrateConfigured(options?: {
311
332
  name?: string;
312
333
  track?: boolean;
@@ -349,6 +370,8 @@ declare class TauriORM {
349
370
  private setSchemaMeta;
350
371
  private normalizeColumn;
351
372
  private computeModelSignature;
373
+ getSchemaSignature(): string;
374
+ printSchemaDiff(): Promise<void>;
352
375
  isSchemaDirty(): Promise<{
353
376
  dirty: boolean;
354
377
  current: string;
@@ -365,6 +388,7 @@ declare class TauriORM {
365
388
  dropExtraColumns?: boolean;
366
389
  preserveData?: boolean;
367
390
  }): Promise<void>;
391
+ private forcePushForTables;
368
392
  }
369
393
  type OneConfig = {
370
394
  fields?: Column[];
package/dist/index.js CHANGED
@@ -213,12 +213,19 @@ function unique(name) {
213
213
  return {
214
214
  on: (...cols) => ({
215
215
  name,
216
- columns: cols.map((c) => c.name)
216
+ columns: cols.map((c) => c.name),
217
+ // runtime marker for DDL rendering
218
+ kind: "unique"
217
219
  })
218
220
  };
219
221
  }
220
222
  function primaryKey(opts) {
221
- return { name: opts.name, columns: opts.columns.map((c) => c.name) };
223
+ return {
224
+ name: opts.name,
225
+ columns: opts.columns.map((c) => c.name),
226
+ // runtime marker for DDL rendering
227
+ kind: "primaryKey"
228
+ };
222
229
  }
223
230
  function check(name, expr) {
224
231
  return { name, expr };
@@ -647,7 +654,10 @@ var TauriORM = class {
647
654
  _relations = null;
648
655
  _dbPromise;
649
656
  constructor(dbUri) {
650
- this._dbPromise = import_plugin_sql.default.load(dbUri);
657
+ this._dbPromise = import_plugin_sql.default.load(dbUri).then(async (db) => {
658
+ await db.execute("PRAGMA foreign_keys = ON");
659
+ return db;
660
+ });
651
661
  }
652
662
  async getDb() {
653
663
  return this._dbPromise;
@@ -721,19 +731,32 @@ var TauriORM = class {
721
731
  return ret;
722
732
  }
723
733
  for (const data of this._rows) {
724
- const finalData = { ...data };
734
+ let coerceValue2 = function(col, value) {
735
+ if (col && col.mode === "boolean") {
736
+ return value ? 1 : 0;
737
+ }
738
+ if (value instanceof Date) {
739
+ if (col && col.mode === "timestamp_ms") return value.getTime();
740
+ if (col && col.mode === "timestamp")
741
+ return Math.floor(value.getTime() / 1e3);
742
+ }
743
+ return value;
744
+ };
745
+ var coerceValue = coerceValue2;
746
+ const finalData = Object.assign({}, data);
725
747
  const schema = this._table._schema;
726
748
  for (const [key, col] of Object.entries(schema)) {
727
749
  if (finalData[key] === void 0) {
728
750
  if (col.defaultFn) {
729
- finalData[key] = col.defaultFn();
751
+ finalData[key] = coerceValue2(col, col.defaultFn());
730
752
  } else if (col.onUpdateFn) {
731
- finalData[key] = col.onUpdateFn();
753
+ finalData[key] = coerceValue2(col, col.onUpdateFn());
732
754
  }
733
755
  }
734
756
  }
735
- const keys = Object.keys(finalData);
736
- const values = Object.values(finalData);
757
+ const entries = Object.entries(finalData);
758
+ const keys = entries.map(([k]) => schema[k]?.name ?? k);
759
+ const values = entries.map(([k, v]) => coerceValue2(schema[k], v));
737
760
  const placeholders = values.map(() => "?").join(", ");
738
761
  let query = `INSERT INTO ${this._table._tableName} (${keys.join(", ")}) VALUES (${placeholders})`;
739
762
  const bindings = [...values];
@@ -831,7 +854,13 @@ var TauriORM = class {
831
854
  const dataToSet = { ...this._data };
832
855
  for (const [key, col] of Object.entries(schema)) {
833
856
  if (!(key in dataToSet) && col.onUpdateFn) {
834
- dataToSet[key] = col.onUpdateFn();
857
+ const v = col.onUpdateFn();
858
+ if (col.mode === "boolean") dataToSet[key] = v ? 1 : 0;
859
+ else if (v instanceof Date) {
860
+ if (col.mode === "timestamp_ms") dataToSet[key] = v.getTime();
861
+ else if (col.mode === "timestamp") dataToSet[key] = Math.floor(v.getTime() / 1e3);
862
+ else dataToSet[key] = v;
863
+ } else dataToSet[key] = v;
835
864
  }
836
865
  }
837
866
  const setParts = [];
@@ -840,11 +869,20 @@ var TauriORM = class {
840
869
  if (v === void 0) continue;
841
870
  if (v && typeof v === "object" && typeof v.toSQL === "function") {
842
871
  const s = v.toSQL();
843
- setParts.push(`${k} = ${s.clause}`);
872
+ const colName = schema[k]?.name ?? k;
873
+ setParts.push(`${colName} = ${s.clause}`);
844
874
  bindings.push(...s.bindings);
845
875
  } else {
846
- setParts.push(`${k} = ?`);
847
- bindings.push(v);
876
+ const colName = schema[k]?.name ?? k;
877
+ let val = v;
878
+ const col = schema[k];
879
+ if (col && col.mode === "boolean") val = v ? 1 : 0;
880
+ else if (v instanceof Date) {
881
+ if (col && col.mode === "timestamp_ms") val = v.getTime();
882
+ else if (col && col.mode === "timestamp") val = Math.floor(v.getTime() / 1e3);
883
+ }
884
+ setParts.push(`${colName} = ?`);
885
+ bindings.push(val);
848
886
  }
849
887
  }
850
888
  let query = `UPDATE ${this._table._tableName} SET ${setParts.join(", ")}`;
@@ -857,7 +895,7 @@ var TauriORM = class {
857
895
  } else {
858
896
  const entries = Object.entries(this._where);
859
897
  if (entries.length > 0) {
860
- query += ` WHERE ${entries.map(([k]) => `${k} = ?`).join(" AND ")}`;
898
+ query += ` WHERE ${entries.map(([k]) => `${schema[k]?.name ?? k} = ?`).join(" AND ")}`;
861
899
  bindings.push(...entries.map(([, v]) => v));
862
900
  }
863
901
  }
@@ -933,7 +971,8 @@ var TauriORM = class {
933
971
  } else {
934
972
  const entries = Object.entries(this._where);
935
973
  if (entries.length > 0) {
936
- query += ` WHERE ${entries.map(([k]) => `${k} = ?`).join(" AND ")}`;
974
+ const schema = this._table._schema;
975
+ query += ` WHERE ${entries.map(([k]) => `${schema[k]?.name ?? k} = ?`).join(" AND ")}`;
937
976
  bindings.push(...entries.map(([, v]) => v));
938
977
  }
939
978
  }
@@ -1016,19 +1055,86 @@ var TauriORM = class {
1016
1055
  }
1017
1056
  return def;
1018
1057
  });
1019
- return `CREATE TABLE IF NOT EXISTS ${tableName} (${columnDefs.join(
1020
- ", "
1021
- )});`;
1058
+ const tableConstraints = [];
1059
+ const constraints = table._constraints;
1060
+ if (constraints && constraints.length) {
1061
+ for (const spec of constraints) {
1062
+ if (spec.expr) {
1063
+ const name = spec.name;
1064
+ const expr = spec.expr.raw ?? spec.expr?.raw ?? String(spec.expr);
1065
+ tableConstraints.push(
1066
+ name ? `CONSTRAINT ${name} CHECK (${expr})` : `CHECK (${expr})`
1067
+ );
1068
+ continue;
1069
+ }
1070
+ if (spec.foreignColumns) {
1071
+ const name = spec.name;
1072
+ const cols = spec.columns.join(", ");
1073
+ const fTable = spec.foreignTable;
1074
+ const fCols = spec.foreignColumns.join(", ");
1075
+ let clause = `${name ? `CONSTRAINT ${name} ` : ""}FOREIGN KEY (${cols}) REFERENCES ${fTable} (${fCols})`;
1076
+ if (spec.onDelete)
1077
+ clause += ` ON DELETE ${String(
1078
+ spec.onDelete
1079
+ ).toUpperCase()}`;
1080
+ if (spec.onUpdate)
1081
+ clause += ` ON UPDATE ${String(
1082
+ spec.onUpdate
1083
+ ).toUpperCase()}`;
1084
+ tableConstraints.push(clause);
1085
+ continue;
1086
+ }
1087
+ if (spec.columns) {
1088
+ const cols = spec.columns.join(", ");
1089
+ const name = spec.name;
1090
+ const isPk = spec.kind === "primaryKey" || name && name.toLowerCase().includes("pk");
1091
+ if (isPk) {
1092
+ tableConstraints.push(
1093
+ name ? `CONSTRAINT ${name} PRIMARY KEY (${cols})` : `PRIMARY KEY (${cols})`
1094
+ );
1095
+ } else {
1096
+ tableConstraints.push(
1097
+ name ? `CONSTRAINT ${name} UNIQUE (${cols})` : `UNIQUE (${cols})`
1098
+ );
1099
+ }
1100
+ continue;
1101
+ }
1102
+ }
1103
+ }
1104
+ const parts = [...columnDefs, ...tableConstraints];
1105
+ return `CREATE TABLE IF NOT EXISTS ${tableName} (${parts.join(", ")});`;
1022
1106
  }
1023
1107
  async createTableIfNotExists(table) {
1024
1108
  const sql2 = this.generateCreateTableSql(table);
1025
1109
  await this.run(sql2);
1110
+ await this.createIndexesForTable(table);
1026
1111
  }
1027
1112
  async createTablesIfNotExist(tables) {
1028
1113
  for (const t of tables) {
1029
1114
  await this.createTableIfNotExists(t);
1030
1115
  }
1031
1116
  }
1117
+ generateCreateIndexSqls(table) {
1118
+ const tableName = table._tableName;
1119
+ const indexes = table._indexes || [];
1120
+ const stmts = [];
1121
+ for (const idx of indexes) {
1122
+ const unique2 = idx.unique ? "UNIQUE " : "";
1123
+ if (!idx.name) continue;
1124
+ const colList = Array.isArray(idx.columns) ? idx.columns : [];
1125
+ if (colList.length === 0) continue;
1126
+ const cols = `(${colList.join(", ")})`;
1127
+ const where = idx.where?.raw ? ` WHERE ${idx.where.raw}` : "";
1128
+ stmts.push(
1129
+ `CREATE ${unique2}INDEX IF NOT EXISTS ${idx.name} ON ${tableName} ${cols}${where};`
1130
+ );
1131
+ }
1132
+ return stmts;
1133
+ }
1134
+ async createIndexesForTable(table) {
1135
+ const stmts = this.generateCreateIndexSqls(table);
1136
+ for (const s of stmts) await this.run(s);
1137
+ }
1032
1138
  async ensureMigrationsTable() {
1033
1139
  await this.run(
1034
1140
  `CREATE TABLE IF NOT EXISTS _migrations (name TEXT PRIMARY KEY, applied_at INTEGER NOT NULL)`
@@ -1053,20 +1159,23 @@ var TauriORM = class {
1053
1159
  const track = options?.track ?? true;
1054
1160
  if (track) {
1055
1161
  await this.ensureMigrationsTable();
1162
+ }
1163
+ await this.forcePushForTables(tables, { preserveData: true });
1164
+ if (track) {
1056
1165
  const name = options?.name ?? `init:${tables.map((t) => t._tableName).join(",")}`;
1057
1166
  const already = await this.hasMigration(name);
1058
- if (already) return;
1059
- await this.createTablesIfNotExist(tables);
1060
- await this.recordMigration(name);
1061
- return;
1167
+ if (!already) await this.recordMigration(name);
1062
1168
  }
1063
- await this.createTablesIfNotExist(tables);
1064
1169
  }
1065
1170
  // Configure schema and relations, and generate db.query automatically
1066
1171
  configure(tables, relDefs) {
1067
1172
  this._tables = tables;
1068
1173
  this._relations = relDefs ?? {};
1069
- this.query = makeQueryAPI(tables, this._relations, this.getDb.bind(this));
1174
+ this.query = makeQueryAPI(
1175
+ tables,
1176
+ this._relations ?? {},
1177
+ this.getDb.bind(this)
1178
+ );
1070
1179
  return this;
1071
1180
  }
1072
1181
  // Convenience: migrate from configured tables
@@ -1074,6 +1183,7 @@ var TauriORM = class {
1074
1183
  if (!this._tables)
1075
1184
  throw new Error("No tables configured. Call db.configure({...}) first.");
1076
1185
  await this.migrate(Object.values(this._tables), options);
1186
+ await this.setSchemaMeta("schema_signature", this.computeModelSignature());
1077
1187
  }
1078
1188
  // --- Schema diff and CLI-like helpers ---
1079
1189
  async diffSchema() {
@@ -1200,6 +1310,13 @@ var TauriORM = class {
1200
1310
  entries.sort((a, b) => a.table.localeCompare(b.table));
1201
1311
  return JSON.stringify(entries);
1202
1312
  }
1313
+ getSchemaSignature() {
1314
+ return this.computeModelSignature();
1315
+ }
1316
+ async printSchemaDiff() {
1317
+ const diff = await this.diffSchema();
1318
+ console.log("Schema diff:", JSON.stringify(diff, null, 2));
1319
+ }
1203
1320
  async isSchemaDirty() {
1204
1321
  const sig = this.computeModelSignature();
1205
1322
  const stored = await this.getSchemaMeta("schema_signature");
@@ -1209,7 +1326,9 @@ var TauriORM = class {
1209
1326
  const status = await this.isSchemaDirty();
1210
1327
  if (!this._tables) throw new Error("No tables configured.");
1211
1328
  if (status.dirty) {
1212
- await this.migrate(Object.values(this._tables), options);
1329
+ await this.forcePushForTables(Object.values(this._tables), {
1330
+ preserveData: true
1331
+ });
1213
1332
  await this.setSchemaMeta(
1214
1333
  "schema_signature",
1215
1334
  this.computeModelSignature()
@@ -1250,13 +1369,17 @@ var TauriORM = class {
1250
1369
  // Force push model to DB: add missing tables/columns, rebuild tables if incompatible
1251
1370
  async forcePush(options) {
1252
1371
  if (!this._tables) throw new Error("No tables configured.");
1372
+ await this.forcePushForTables(Object.values(this._tables), options);
1373
+ }
1374
+ async forcePushForTables(tables, options) {
1253
1375
  const dbi = await this.getDb();
1254
1376
  const preserve = options?.preserveData !== false;
1255
- for (const tbl of Object.values(this._tables)) {
1377
+ for (const tbl of tables) {
1256
1378
  const tableName = tbl._tableName;
1257
1379
  const exists2 = await this.tableExists(tableName);
1258
1380
  if (!exists2) {
1259
1381
  await this.run(this.buildCreateTableSQL(tbl));
1382
+ await this.createIndexesForTable(tbl);
1260
1383
  continue;
1261
1384
  }
1262
1385
  const existingCols = await dbi.select(
@@ -1301,6 +1424,7 @@ var TauriORM = class {
1301
1424
  }
1302
1425
  await this.run(`DROP TABLE ${tableName}`);
1303
1426
  await this.run(`ALTER TABLE ${tmp} RENAME TO ${tableName}`);
1427
+ await this.createIndexesForTable(tbl);
1304
1428
  } else {
1305
1429
  for (const m of missing) {
1306
1430
  let clause = `${m.name} ${m.type}`;
@@ -1311,6 +1435,7 @@ var TauriORM = class {
1311
1435
  }
1312
1436
  await this.run(`ALTER TABLE ${tableName} ADD COLUMN ${clause}`);
1313
1437
  }
1438
+ await this.createIndexesForTable(tbl);
1314
1439
  }
1315
1440
  }
1316
1441
  await this.setSchemaMeta("schema_signature", this.computeModelSignature());
package/dist/index.mjs CHANGED
@@ -135,12 +135,19 @@ function unique(name) {
135
135
  return {
136
136
  on: (...cols) => ({
137
137
  name,
138
- columns: cols.map((c) => c.name)
138
+ columns: cols.map((c) => c.name),
139
+ // runtime marker for DDL rendering
140
+ kind: "unique"
139
141
  })
140
142
  };
141
143
  }
142
144
  function primaryKey(opts) {
143
- return { name: opts.name, columns: opts.columns.map((c) => c.name) };
145
+ return {
146
+ name: opts.name,
147
+ columns: opts.columns.map((c) => c.name),
148
+ // runtime marker for DDL rendering
149
+ kind: "primaryKey"
150
+ };
144
151
  }
145
152
  function check(name, expr) {
146
153
  return { name, expr };
@@ -569,7 +576,10 @@ var TauriORM = class {
569
576
  _relations = null;
570
577
  _dbPromise;
571
578
  constructor(dbUri) {
572
- this._dbPromise = Database.load(dbUri);
579
+ this._dbPromise = Database.load(dbUri).then(async (db) => {
580
+ await db.execute("PRAGMA foreign_keys = ON");
581
+ return db;
582
+ });
573
583
  }
574
584
  async getDb() {
575
585
  return this._dbPromise;
@@ -643,19 +653,32 @@ var TauriORM = class {
643
653
  return ret;
644
654
  }
645
655
  for (const data of this._rows) {
646
- const finalData = { ...data };
656
+ let coerceValue2 = function(col, value) {
657
+ if (col && col.mode === "boolean") {
658
+ return value ? 1 : 0;
659
+ }
660
+ if (value instanceof Date) {
661
+ if (col && col.mode === "timestamp_ms") return value.getTime();
662
+ if (col && col.mode === "timestamp")
663
+ return Math.floor(value.getTime() / 1e3);
664
+ }
665
+ return value;
666
+ };
667
+ var coerceValue = coerceValue2;
668
+ const finalData = Object.assign({}, data);
647
669
  const schema = this._table._schema;
648
670
  for (const [key, col] of Object.entries(schema)) {
649
671
  if (finalData[key] === void 0) {
650
672
  if (col.defaultFn) {
651
- finalData[key] = col.defaultFn();
673
+ finalData[key] = coerceValue2(col, col.defaultFn());
652
674
  } else if (col.onUpdateFn) {
653
- finalData[key] = col.onUpdateFn();
675
+ finalData[key] = coerceValue2(col, col.onUpdateFn());
654
676
  }
655
677
  }
656
678
  }
657
- const keys = Object.keys(finalData);
658
- const values = Object.values(finalData);
679
+ const entries = Object.entries(finalData);
680
+ const keys = entries.map(([k]) => schema[k]?.name ?? k);
681
+ const values = entries.map(([k, v]) => coerceValue2(schema[k], v));
659
682
  const placeholders = values.map(() => "?").join(", ");
660
683
  let query = `INSERT INTO ${this._table._tableName} (${keys.join(", ")}) VALUES (${placeholders})`;
661
684
  const bindings = [...values];
@@ -753,7 +776,13 @@ var TauriORM = class {
753
776
  const dataToSet = { ...this._data };
754
777
  for (const [key, col] of Object.entries(schema)) {
755
778
  if (!(key in dataToSet) && col.onUpdateFn) {
756
- dataToSet[key] = col.onUpdateFn();
779
+ const v = col.onUpdateFn();
780
+ if (col.mode === "boolean") dataToSet[key] = v ? 1 : 0;
781
+ else if (v instanceof Date) {
782
+ if (col.mode === "timestamp_ms") dataToSet[key] = v.getTime();
783
+ else if (col.mode === "timestamp") dataToSet[key] = Math.floor(v.getTime() / 1e3);
784
+ else dataToSet[key] = v;
785
+ } else dataToSet[key] = v;
757
786
  }
758
787
  }
759
788
  const setParts = [];
@@ -762,11 +791,20 @@ var TauriORM = class {
762
791
  if (v === void 0) continue;
763
792
  if (v && typeof v === "object" && typeof v.toSQL === "function") {
764
793
  const s = v.toSQL();
765
- setParts.push(`${k} = ${s.clause}`);
794
+ const colName = schema[k]?.name ?? k;
795
+ setParts.push(`${colName} = ${s.clause}`);
766
796
  bindings.push(...s.bindings);
767
797
  } else {
768
- setParts.push(`${k} = ?`);
769
- bindings.push(v);
798
+ const colName = schema[k]?.name ?? k;
799
+ let val = v;
800
+ const col = schema[k];
801
+ if (col && col.mode === "boolean") val = v ? 1 : 0;
802
+ else if (v instanceof Date) {
803
+ if (col && col.mode === "timestamp_ms") val = v.getTime();
804
+ else if (col && col.mode === "timestamp") val = Math.floor(v.getTime() / 1e3);
805
+ }
806
+ setParts.push(`${colName} = ?`);
807
+ bindings.push(val);
770
808
  }
771
809
  }
772
810
  let query = `UPDATE ${this._table._tableName} SET ${setParts.join(", ")}`;
@@ -779,7 +817,7 @@ var TauriORM = class {
779
817
  } else {
780
818
  const entries = Object.entries(this._where);
781
819
  if (entries.length > 0) {
782
- query += ` WHERE ${entries.map(([k]) => `${k} = ?`).join(" AND ")}`;
820
+ query += ` WHERE ${entries.map(([k]) => `${schema[k]?.name ?? k} = ?`).join(" AND ")}`;
783
821
  bindings.push(...entries.map(([, v]) => v));
784
822
  }
785
823
  }
@@ -855,7 +893,8 @@ var TauriORM = class {
855
893
  } else {
856
894
  const entries = Object.entries(this._where);
857
895
  if (entries.length > 0) {
858
- query += ` WHERE ${entries.map(([k]) => `${k} = ?`).join(" AND ")}`;
896
+ const schema = this._table._schema;
897
+ query += ` WHERE ${entries.map(([k]) => `${schema[k]?.name ?? k} = ?`).join(" AND ")}`;
859
898
  bindings.push(...entries.map(([, v]) => v));
860
899
  }
861
900
  }
@@ -938,19 +977,86 @@ var TauriORM = class {
938
977
  }
939
978
  return def;
940
979
  });
941
- return `CREATE TABLE IF NOT EXISTS ${tableName} (${columnDefs.join(
942
- ", "
943
- )});`;
980
+ const tableConstraints = [];
981
+ const constraints = table._constraints;
982
+ if (constraints && constraints.length) {
983
+ for (const spec of constraints) {
984
+ if (spec.expr) {
985
+ const name = spec.name;
986
+ const expr = spec.expr.raw ?? spec.expr?.raw ?? String(spec.expr);
987
+ tableConstraints.push(
988
+ name ? `CONSTRAINT ${name} CHECK (${expr})` : `CHECK (${expr})`
989
+ );
990
+ continue;
991
+ }
992
+ if (spec.foreignColumns) {
993
+ const name = spec.name;
994
+ const cols = spec.columns.join(", ");
995
+ const fTable = spec.foreignTable;
996
+ const fCols = spec.foreignColumns.join(", ");
997
+ let clause = `${name ? `CONSTRAINT ${name} ` : ""}FOREIGN KEY (${cols}) REFERENCES ${fTable} (${fCols})`;
998
+ if (spec.onDelete)
999
+ clause += ` ON DELETE ${String(
1000
+ spec.onDelete
1001
+ ).toUpperCase()}`;
1002
+ if (spec.onUpdate)
1003
+ clause += ` ON UPDATE ${String(
1004
+ spec.onUpdate
1005
+ ).toUpperCase()}`;
1006
+ tableConstraints.push(clause);
1007
+ continue;
1008
+ }
1009
+ if (spec.columns) {
1010
+ const cols = spec.columns.join(", ");
1011
+ const name = spec.name;
1012
+ const isPk = spec.kind === "primaryKey" || name && name.toLowerCase().includes("pk");
1013
+ if (isPk) {
1014
+ tableConstraints.push(
1015
+ name ? `CONSTRAINT ${name} PRIMARY KEY (${cols})` : `PRIMARY KEY (${cols})`
1016
+ );
1017
+ } else {
1018
+ tableConstraints.push(
1019
+ name ? `CONSTRAINT ${name} UNIQUE (${cols})` : `UNIQUE (${cols})`
1020
+ );
1021
+ }
1022
+ continue;
1023
+ }
1024
+ }
1025
+ }
1026
+ const parts = [...columnDefs, ...tableConstraints];
1027
+ return `CREATE TABLE IF NOT EXISTS ${tableName} (${parts.join(", ")});`;
944
1028
  }
945
1029
  async createTableIfNotExists(table) {
946
1030
  const sql2 = this.generateCreateTableSql(table);
947
1031
  await this.run(sql2);
1032
+ await this.createIndexesForTable(table);
948
1033
  }
949
1034
  async createTablesIfNotExist(tables) {
950
1035
  for (const t of tables) {
951
1036
  await this.createTableIfNotExists(t);
952
1037
  }
953
1038
  }
1039
+ generateCreateIndexSqls(table) {
1040
+ const tableName = table._tableName;
1041
+ const indexes = table._indexes || [];
1042
+ const stmts = [];
1043
+ for (const idx of indexes) {
1044
+ const unique2 = idx.unique ? "UNIQUE " : "";
1045
+ if (!idx.name) continue;
1046
+ const colList = Array.isArray(idx.columns) ? idx.columns : [];
1047
+ if (colList.length === 0) continue;
1048
+ const cols = `(${colList.join(", ")})`;
1049
+ const where = idx.where?.raw ? ` WHERE ${idx.where.raw}` : "";
1050
+ stmts.push(
1051
+ `CREATE ${unique2}INDEX IF NOT EXISTS ${idx.name} ON ${tableName} ${cols}${where};`
1052
+ );
1053
+ }
1054
+ return stmts;
1055
+ }
1056
+ async createIndexesForTable(table) {
1057
+ const stmts = this.generateCreateIndexSqls(table);
1058
+ for (const s of stmts) await this.run(s);
1059
+ }
954
1060
  async ensureMigrationsTable() {
955
1061
  await this.run(
956
1062
  `CREATE TABLE IF NOT EXISTS _migrations (name TEXT PRIMARY KEY, applied_at INTEGER NOT NULL)`
@@ -975,20 +1081,23 @@ var TauriORM = class {
975
1081
  const track = options?.track ?? true;
976
1082
  if (track) {
977
1083
  await this.ensureMigrationsTable();
1084
+ }
1085
+ await this.forcePushForTables(tables, { preserveData: true });
1086
+ if (track) {
978
1087
  const name = options?.name ?? `init:${tables.map((t) => t._tableName).join(",")}`;
979
1088
  const already = await this.hasMigration(name);
980
- if (already) return;
981
- await this.createTablesIfNotExist(tables);
982
- await this.recordMigration(name);
983
- return;
1089
+ if (!already) await this.recordMigration(name);
984
1090
  }
985
- await this.createTablesIfNotExist(tables);
986
1091
  }
987
1092
  // Configure schema and relations, and generate db.query automatically
988
1093
  configure(tables, relDefs) {
989
1094
  this._tables = tables;
990
1095
  this._relations = relDefs ?? {};
991
- this.query = makeQueryAPI(tables, this._relations, this.getDb.bind(this));
1096
+ this.query = makeQueryAPI(
1097
+ tables,
1098
+ this._relations ?? {},
1099
+ this.getDb.bind(this)
1100
+ );
992
1101
  return this;
993
1102
  }
994
1103
  // Convenience: migrate from configured tables
@@ -996,6 +1105,7 @@ var TauriORM = class {
996
1105
  if (!this._tables)
997
1106
  throw new Error("No tables configured. Call db.configure({...}) first.");
998
1107
  await this.migrate(Object.values(this._tables), options);
1108
+ await this.setSchemaMeta("schema_signature", this.computeModelSignature());
999
1109
  }
1000
1110
  // --- Schema diff and CLI-like helpers ---
1001
1111
  async diffSchema() {
@@ -1122,6 +1232,13 @@ var TauriORM = class {
1122
1232
  entries.sort((a, b) => a.table.localeCompare(b.table));
1123
1233
  return JSON.stringify(entries);
1124
1234
  }
1235
+ getSchemaSignature() {
1236
+ return this.computeModelSignature();
1237
+ }
1238
+ async printSchemaDiff() {
1239
+ const diff = await this.diffSchema();
1240
+ console.log("Schema diff:", JSON.stringify(diff, null, 2));
1241
+ }
1125
1242
  async isSchemaDirty() {
1126
1243
  const sig = this.computeModelSignature();
1127
1244
  const stored = await this.getSchemaMeta("schema_signature");
@@ -1131,7 +1248,9 @@ var TauriORM = class {
1131
1248
  const status = await this.isSchemaDirty();
1132
1249
  if (!this._tables) throw new Error("No tables configured.");
1133
1250
  if (status.dirty) {
1134
- await this.migrate(Object.values(this._tables), options);
1251
+ await this.forcePushForTables(Object.values(this._tables), {
1252
+ preserveData: true
1253
+ });
1135
1254
  await this.setSchemaMeta(
1136
1255
  "schema_signature",
1137
1256
  this.computeModelSignature()
@@ -1172,13 +1291,17 @@ var TauriORM = class {
1172
1291
  // Force push model to DB: add missing tables/columns, rebuild tables if incompatible
1173
1292
  async forcePush(options) {
1174
1293
  if (!this._tables) throw new Error("No tables configured.");
1294
+ await this.forcePushForTables(Object.values(this._tables), options);
1295
+ }
1296
+ async forcePushForTables(tables, options) {
1175
1297
  const dbi = await this.getDb();
1176
1298
  const preserve = options?.preserveData !== false;
1177
- for (const tbl of Object.values(this._tables)) {
1299
+ for (const tbl of tables) {
1178
1300
  const tableName = tbl._tableName;
1179
1301
  const exists2 = await this.tableExists(tableName);
1180
1302
  if (!exists2) {
1181
1303
  await this.run(this.buildCreateTableSQL(tbl));
1304
+ await this.createIndexesForTable(tbl);
1182
1305
  continue;
1183
1306
  }
1184
1307
  const existingCols = await dbi.select(
@@ -1223,6 +1346,7 @@ var TauriORM = class {
1223
1346
  }
1224
1347
  await this.run(`DROP TABLE ${tableName}`);
1225
1348
  await this.run(`ALTER TABLE ${tmp} RENAME TO ${tableName}`);
1349
+ await this.createIndexesForTable(tbl);
1226
1350
  } else {
1227
1351
  for (const m of missing) {
1228
1352
  let clause = `${m.name} ${m.type}`;
@@ -1233,6 +1357,7 @@ var TauriORM = class {
1233
1357
  }
1234
1358
  await this.run(`ALTER TABLE ${tableName} ADD COLUMN ${clause}`);
1235
1359
  }
1360
+ await this.createIndexesForTable(tbl);
1236
1361
  }
1237
1362
  }
1238
1363
  await this.setSchemaMeta("schema_signature", this.computeModelSignature());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@type32/tauri-sqlite-orm",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "A Drizzle-like ORM for Tauri v2's SQL JS API plugin.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",