@type32/tauri-sqlite-orm 0.1.7 → 0.1.9

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,22 +294,25 @@ 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;
295
307
  execute(): Promise<unknown>;
296
308
  };
297
309
  run(query: string, bindings?: any[]): Promise<void>;
310
+ private formatDefaultValue;
298
311
  private generateCreateTableSql;
299
312
  createTableIfNotExists(table: Table<any>): Promise<void>;
300
313
  createTablesIfNotExist(tables: Table<any>[]): Promise<void>;
314
+ private generateCreateIndexSqls;
315
+ private createIndexesForTable;
301
316
  private ensureMigrationsTable;
302
317
  private hasMigration;
303
318
  private recordMigration;
@@ -305,7 +320,14 @@ declare class TauriORM {
305
320
  name?: string;
306
321
  track?: boolean;
307
322
  }): Promise<void>;
308
- 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
+ };
309
331
  migrateConfigured(options?: {
310
332
  name?: string;
311
333
  track?: boolean;
@@ -348,6 +370,8 @@ declare class TauriORM {
348
370
  private setSchemaMeta;
349
371
  private normalizeColumn;
350
372
  private computeModelSignature;
373
+ getSchemaSignature(): string;
374
+ printSchemaDiff(): Promise<void>;
351
375
  isSchemaDirty(): Promise<{
352
376
  dirty: boolean;
353
377
  current: string;
@@ -364,6 +388,7 @@ declare class TauriORM {
364
388
  dropExtraColumns?: boolean;
365
389
  preserveData?: boolean;
366
390
  }): Promise<void>;
391
+ private forcePushForTables;
367
392
  }
368
393
  type OneConfig = {
369
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,22 +294,25 @@ 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;
295
307
  execute(): Promise<unknown>;
296
308
  };
297
309
  run(query: string, bindings?: any[]): Promise<void>;
310
+ private formatDefaultValue;
298
311
  private generateCreateTableSql;
299
312
  createTableIfNotExists(table: Table<any>): Promise<void>;
300
313
  createTablesIfNotExist(tables: Table<any>[]): Promise<void>;
314
+ private generateCreateIndexSqls;
315
+ private createIndexesForTable;
301
316
  private ensureMigrationsTable;
302
317
  private hasMigration;
303
318
  private recordMigration;
@@ -305,7 +320,14 @@ declare class TauriORM {
305
320
  name?: string;
306
321
  track?: boolean;
307
322
  }): Promise<void>;
308
- 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
+ };
309
331
  migrateConfigured(options?: {
310
332
  name?: string;
311
333
  track?: boolean;
@@ -348,6 +370,8 @@ declare class TauriORM {
348
370
  private setSchemaMeta;
349
371
  private normalizeColumn;
350
372
  private computeModelSignature;
373
+ getSchemaSignature(): string;
374
+ printSchemaDiff(): Promise<void>;
351
375
  isSchemaDirty(): Promise<{
352
376
  dirty: boolean;
353
377
  current: string;
@@ -364,6 +388,7 @@ declare class TauriORM {
364
388
  dropExtraColumns?: boolean;
365
389
  preserveData?: boolean;
366
390
  }): Promise<void>;
391
+ private forcePushForTables;
367
392
  }
368
393
  type OneConfig = {
369
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,7 +731,7 @@ var TauriORM = class {
721
731
  return ret;
722
732
  }
723
733
  for (const data of this._rows) {
724
- const finalData = { ...data };
734
+ const finalData = Object.assign({}, data);
725
735
  const schema = this._table._schema;
726
736
  for (const [key, col] of Object.entries(schema)) {
727
737
  if (finalData[key] === void 0) {
@@ -975,6 +985,25 @@ var TauriORM = class {
975
985
  await db.execute(query, bindings);
976
986
  }
977
987
  // --- Migrations API ---
988
+ formatDefaultValue(col) {
989
+ const dv = col.defaultValue;
990
+ if (dv === void 0) return null;
991
+ if (dv && typeof dv === "object" && "raw" in dv) {
992
+ return dv.raw;
993
+ }
994
+ if (dv instanceof Date) {
995
+ const isMs = col.mode === "timestamp_ms";
996
+ const num = isMs ? dv.getTime() : Math.floor(dv.getTime() / 1e3);
997
+ return String(num);
998
+ }
999
+ if (col.mode === "boolean") {
1000
+ return String(dv ? 1 : 0);
1001
+ }
1002
+ if (typeof dv === "string") {
1003
+ return `'${dv.replace(/'/g, "''")}'`;
1004
+ }
1005
+ return String(dv);
1006
+ }
978
1007
  generateCreateTableSql(table) {
979
1008
  const tableName = table._tableName;
980
1009
  const columns = Object.values(table._schema);
@@ -985,14 +1014,8 @@ var TauriORM = class {
985
1014
  }
986
1015
  if (col.isNotNull) def += " NOT NULL";
987
1016
  if (col.defaultValue !== void 0) {
988
- const dv = col.defaultValue;
989
- if (dv && typeof dv === "object" && "raw" in dv) {
990
- def += ` DEFAULT ${dv.raw}`;
991
- } else if (typeof dv === "string") {
992
- def += ` DEFAULT '${dv.replace(/'/g, "''")}'`;
993
- } else {
994
- def += ` DEFAULT ${dv}`;
995
- }
1017
+ const formatted = this.formatDefaultValue(col);
1018
+ if (formatted !== null) def += ` DEFAULT ${formatted}`;
996
1019
  }
997
1020
  if (col.references && !col.isPrimaryKey) {
998
1021
  def += ` REFERENCES ${col.references.table} (${col.references.column})`;
@@ -1003,19 +1026,86 @@ var TauriORM = class {
1003
1026
  }
1004
1027
  return def;
1005
1028
  });
1006
- return `CREATE TABLE IF NOT EXISTS ${tableName} (${columnDefs.join(
1007
- ", "
1008
- )});`;
1029
+ const tableConstraints = [];
1030
+ const constraints = table._constraints;
1031
+ if (constraints && constraints.length) {
1032
+ for (const spec of constraints) {
1033
+ if (spec.expr) {
1034
+ const name = spec.name;
1035
+ const expr = spec.expr.raw ?? spec.expr?.raw ?? String(spec.expr);
1036
+ tableConstraints.push(
1037
+ name ? `CONSTRAINT ${name} CHECK (${expr})` : `CHECK (${expr})`
1038
+ );
1039
+ continue;
1040
+ }
1041
+ if (spec.foreignColumns) {
1042
+ const name = spec.name;
1043
+ const cols = spec.columns.join(", ");
1044
+ const fTable = spec.foreignTable;
1045
+ const fCols = spec.foreignColumns.join(", ");
1046
+ let clause = `${name ? `CONSTRAINT ${name} ` : ""}FOREIGN KEY (${cols}) REFERENCES ${fTable} (${fCols})`;
1047
+ if (spec.onDelete)
1048
+ clause += ` ON DELETE ${String(
1049
+ spec.onDelete
1050
+ ).toUpperCase()}`;
1051
+ if (spec.onUpdate)
1052
+ clause += ` ON UPDATE ${String(
1053
+ spec.onUpdate
1054
+ ).toUpperCase()}`;
1055
+ tableConstraints.push(clause);
1056
+ continue;
1057
+ }
1058
+ if (spec.columns) {
1059
+ const cols = spec.columns.join(", ");
1060
+ const name = spec.name;
1061
+ const isPk = spec.kind === "primaryKey" || name && name.toLowerCase().includes("pk");
1062
+ if (isPk) {
1063
+ tableConstraints.push(
1064
+ name ? `CONSTRAINT ${name} PRIMARY KEY (${cols})` : `PRIMARY KEY (${cols})`
1065
+ );
1066
+ } else {
1067
+ tableConstraints.push(
1068
+ name ? `CONSTRAINT ${name} UNIQUE (${cols})` : `UNIQUE (${cols})`
1069
+ );
1070
+ }
1071
+ continue;
1072
+ }
1073
+ }
1074
+ }
1075
+ const parts = [...columnDefs, ...tableConstraints];
1076
+ return `CREATE TABLE IF NOT EXISTS ${tableName} (${parts.join(", ")});`;
1009
1077
  }
1010
1078
  async createTableIfNotExists(table) {
1011
1079
  const sql2 = this.generateCreateTableSql(table);
1012
1080
  await this.run(sql2);
1081
+ await this.createIndexesForTable(table);
1013
1082
  }
1014
1083
  async createTablesIfNotExist(tables) {
1015
1084
  for (const t of tables) {
1016
1085
  await this.createTableIfNotExists(t);
1017
1086
  }
1018
1087
  }
1088
+ generateCreateIndexSqls(table) {
1089
+ const tableName = table._tableName;
1090
+ const indexes = table._indexes || [];
1091
+ const stmts = [];
1092
+ for (const idx of indexes) {
1093
+ const unique2 = idx.unique ? "UNIQUE " : "";
1094
+ if (!idx.name) continue;
1095
+ const colList = Array.isArray(idx.columns) ? idx.columns : [];
1096
+ if (colList.length === 0) continue;
1097
+ const cols = `(${colList.join(", ")})`;
1098
+ const where = idx.where?.raw ? ` WHERE ${idx.where.raw}` : "";
1099
+ stmts.push(
1100
+ `CREATE ${unique2}INDEX IF NOT EXISTS ${idx.name} ON ${tableName} ${cols}${where};`
1101
+ );
1102
+ }
1103
+ return stmts;
1104
+ }
1105
+ async createIndexesForTable(table) {
1106
+ const stmts = this.generateCreateIndexSqls(table);
1107
+ for (const s of stmts) await this.run(s);
1108
+ }
1019
1109
  async ensureMigrationsTable() {
1020
1110
  await this.run(
1021
1111
  `CREATE TABLE IF NOT EXISTS _migrations (name TEXT PRIMARY KEY, applied_at INTEGER NOT NULL)`
@@ -1040,20 +1130,23 @@ var TauriORM = class {
1040
1130
  const track = options?.track ?? true;
1041
1131
  if (track) {
1042
1132
  await this.ensureMigrationsTable();
1133
+ }
1134
+ await this.forcePushForTables(tables, { preserveData: true });
1135
+ if (track) {
1043
1136
  const name = options?.name ?? `init:${tables.map((t) => t._tableName).join(",")}`;
1044
1137
  const already = await this.hasMigration(name);
1045
- if (already) return;
1046
- await this.createTablesIfNotExist(tables);
1047
- await this.recordMigration(name);
1048
- return;
1138
+ if (!already) await this.recordMigration(name);
1049
1139
  }
1050
- await this.createTablesIfNotExist(tables);
1051
1140
  }
1052
1141
  // Configure schema and relations, and generate db.query automatically
1053
1142
  configure(tables, relDefs) {
1054
1143
  this._tables = tables;
1055
1144
  this._relations = relDefs ?? {};
1056
- this.query = makeQueryAPI(tables, this._relations, this.getDb.bind(this));
1145
+ this.query = makeQueryAPI(
1146
+ tables,
1147
+ this._relations ?? {},
1148
+ this.getDb.bind(this)
1149
+ );
1057
1150
  return this;
1058
1151
  }
1059
1152
  // Convenience: migrate from configured tables
@@ -1061,6 +1154,7 @@ var TauriORM = class {
1061
1154
  if (!this._tables)
1062
1155
  throw new Error("No tables configured. Call db.configure({...}) first.");
1063
1156
  await this.migrate(Object.values(this._tables), options);
1157
+ await this.setSchemaMeta("schema_signature", this.computeModelSignature());
1064
1158
  }
1065
1159
  // --- Schema diff and CLI-like helpers ---
1066
1160
  async diffSchema() {
@@ -1187,6 +1281,13 @@ var TauriORM = class {
1187
1281
  entries.sort((a, b) => a.table.localeCompare(b.table));
1188
1282
  return JSON.stringify(entries);
1189
1283
  }
1284
+ getSchemaSignature() {
1285
+ return this.computeModelSignature();
1286
+ }
1287
+ async printSchemaDiff() {
1288
+ const diff = await this.diffSchema();
1289
+ console.log("Schema diff:", JSON.stringify(diff, null, 2));
1290
+ }
1190
1291
  async isSchemaDirty() {
1191
1292
  const sig = this.computeModelSignature();
1192
1293
  const stored = await this.getSchemaMeta("schema_signature");
@@ -1196,7 +1297,9 @@ var TauriORM = class {
1196
1297
  const status = await this.isSchemaDirty();
1197
1298
  if (!this._tables) throw new Error("No tables configured.");
1198
1299
  if (status.dirty) {
1199
- await this.migrate(Object.values(this._tables), options);
1300
+ await this.forcePushForTables(Object.values(this._tables), {
1301
+ preserveData: true
1302
+ });
1200
1303
  await this.setSchemaMeta(
1201
1304
  "schema_signature",
1202
1305
  this.computeModelSignature()
@@ -1237,13 +1340,17 @@ var TauriORM = class {
1237
1340
  // Force push model to DB: add missing tables/columns, rebuild tables if incompatible
1238
1341
  async forcePush(options) {
1239
1342
  if (!this._tables) throw new Error("No tables configured.");
1343
+ await this.forcePushForTables(Object.values(this._tables), options);
1344
+ }
1345
+ async forcePushForTables(tables, options) {
1240
1346
  const dbi = await this.getDb();
1241
1347
  const preserve = options?.preserveData !== false;
1242
- for (const tbl of Object.values(this._tables)) {
1348
+ for (const tbl of tables) {
1243
1349
  const tableName = tbl._tableName;
1244
1350
  const exists2 = await this.tableExists(tableName);
1245
1351
  if (!exists2) {
1246
1352
  await this.run(this.buildCreateTableSQL(tbl));
1353
+ await this.createIndexesForTable(tbl);
1247
1354
  continue;
1248
1355
  }
1249
1356
  const existingCols = await dbi.select(
@@ -1288,20 +1395,18 @@ var TauriORM = class {
1288
1395
  }
1289
1396
  await this.run(`DROP TABLE ${tableName}`);
1290
1397
  await this.run(`ALTER TABLE ${tmp} RENAME TO ${tableName}`);
1398
+ await this.createIndexesForTable(tbl);
1291
1399
  } else {
1292
1400
  for (const m of missing) {
1293
1401
  let clause = `${m.name} ${m.type}`;
1294
1402
  if (m.isNotNull) clause += " NOT NULL";
1295
1403
  if (m.defaultValue !== void 0) {
1296
- const dv = m.defaultValue;
1297
- if (dv && typeof dv === "object" && "raw" in dv)
1298
- clause += ` DEFAULT ${dv.raw}`;
1299
- else if (typeof dv === "string")
1300
- clause += ` DEFAULT '${dv.replace(/'/g, "''")}'`;
1301
- else clause += ` DEFAULT ${dv}`;
1404
+ const formatted = this.formatDefaultValue(m);
1405
+ if (formatted !== null) clause += ` DEFAULT ${formatted}`;
1302
1406
  }
1303
1407
  await this.run(`ALTER TABLE ${tableName} ADD COLUMN ${clause}`);
1304
1408
  }
1409
+ await this.createIndexesForTable(tbl);
1305
1410
  }
1306
1411
  }
1307
1412
  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,7 +653,7 @@ var TauriORM = class {
643
653
  return ret;
644
654
  }
645
655
  for (const data of this._rows) {
646
- const finalData = { ...data };
656
+ const finalData = Object.assign({}, data);
647
657
  const schema = this._table._schema;
648
658
  for (const [key, col] of Object.entries(schema)) {
649
659
  if (finalData[key] === void 0) {
@@ -897,6 +907,25 @@ var TauriORM = class {
897
907
  await db.execute(query, bindings);
898
908
  }
899
909
  // --- Migrations API ---
910
+ formatDefaultValue(col) {
911
+ const dv = col.defaultValue;
912
+ if (dv === void 0) return null;
913
+ if (dv && typeof dv === "object" && "raw" in dv) {
914
+ return dv.raw;
915
+ }
916
+ if (dv instanceof Date) {
917
+ const isMs = col.mode === "timestamp_ms";
918
+ const num = isMs ? dv.getTime() : Math.floor(dv.getTime() / 1e3);
919
+ return String(num);
920
+ }
921
+ if (col.mode === "boolean") {
922
+ return String(dv ? 1 : 0);
923
+ }
924
+ if (typeof dv === "string") {
925
+ return `'${dv.replace(/'/g, "''")}'`;
926
+ }
927
+ return String(dv);
928
+ }
900
929
  generateCreateTableSql(table) {
901
930
  const tableName = table._tableName;
902
931
  const columns = Object.values(table._schema);
@@ -907,14 +936,8 @@ var TauriORM = class {
907
936
  }
908
937
  if (col.isNotNull) def += " NOT NULL";
909
938
  if (col.defaultValue !== void 0) {
910
- const dv = col.defaultValue;
911
- if (dv && typeof dv === "object" && "raw" in dv) {
912
- def += ` DEFAULT ${dv.raw}`;
913
- } else if (typeof dv === "string") {
914
- def += ` DEFAULT '${dv.replace(/'/g, "''")}'`;
915
- } else {
916
- def += ` DEFAULT ${dv}`;
917
- }
939
+ const formatted = this.formatDefaultValue(col);
940
+ if (formatted !== null) def += ` DEFAULT ${formatted}`;
918
941
  }
919
942
  if (col.references && !col.isPrimaryKey) {
920
943
  def += ` REFERENCES ${col.references.table} (${col.references.column})`;
@@ -925,19 +948,86 @@ var TauriORM = class {
925
948
  }
926
949
  return def;
927
950
  });
928
- return `CREATE TABLE IF NOT EXISTS ${tableName} (${columnDefs.join(
929
- ", "
930
- )});`;
951
+ const tableConstraints = [];
952
+ const constraints = table._constraints;
953
+ if (constraints && constraints.length) {
954
+ for (const spec of constraints) {
955
+ if (spec.expr) {
956
+ const name = spec.name;
957
+ const expr = spec.expr.raw ?? spec.expr?.raw ?? String(spec.expr);
958
+ tableConstraints.push(
959
+ name ? `CONSTRAINT ${name} CHECK (${expr})` : `CHECK (${expr})`
960
+ );
961
+ continue;
962
+ }
963
+ if (spec.foreignColumns) {
964
+ const name = spec.name;
965
+ const cols = spec.columns.join(", ");
966
+ const fTable = spec.foreignTable;
967
+ const fCols = spec.foreignColumns.join(", ");
968
+ let clause = `${name ? `CONSTRAINT ${name} ` : ""}FOREIGN KEY (${cols}) REFERENCES ${fTable} (${fCols})`;
969
+ if (spec.onDelete)
970
+ clause += ` ON DELETE ${String(
971
+ spec.onDelete
972
+ ).toUpperCase()}`;
973
+ if (spec.onUpdate)
974
+ clause += ` ON UPDATE ${String(
975
+ spec.onUpdate
976
+ ).toUpperCase()}`;
977
+ tableConstraints.push(clause);
978
+ continue;
979
+ }
980
+ if (spec.columns) {
981
+ const cols = spec.columns.join(", ");
982
+ const name = spec.name;
983
+ const isPk = spec.kind === "primaryKey" || name && name.toLowerCase().includes("pk");
984
+ if (isPk) {
985
+ tableConstraints.push(
986
+ name ? `CONSTRAINT ${name} PRIMARY KEY (${cols})` : `PRIMARY KEY (${cols})`
987
+ );
988
+ } else {
989
+ tableConstraints.push(
990
+ name ? `CONSTRAINT ${name} UNIQUE (${cols})` : `UNIQUE (${cols})`
991
+ );
992
+ }
993
+ continue;
994
+ }
995
+ }
996
+ }
997
+ const parts = [...columnDefs, ...tableConstraints];
998
+ return `CREATE TABLE IF NOT EXISTS ${tableName} (${parts.join(", ")});`;
931
999
  }
932
1000
  async createTableIfNotExists(table) {
933
1001
  const sql2 = this.generateCreateTableSql(table);
934
1002
  await this.run(sql2);
1003
+ await this.createIndexesForTable(table);
935
1004
  }
936
1005
  async createTablesIfNotExist(tables) {
937
1006
  for (const t of tables) {
938
1007
  await this.createTableIfNotExists(t);
939
1008
  }
940
1009
  }
1010
+ generateCreateIndexSqls(table) {
1011
+ const tableName = table._tableName;
1012
+ const indexes = table._indexes || [];
1013
+ const stmts = [];
1014
+ for (const idx of indexes) {
1015
+ const unique2 = idx.unique ? "UNIQUE " : "";
1016
+ if (!idx.name) continue;
1017
+ const colList = Array.isArray(idx.columns) ? idx.columns : [];
1018
+ if (colList.length === 0) continue;
1019
+ const cols = `(${colList.join(", ")})`;
1020
+ const where = idx.where?.raw ? ` WHERE ${idx.where.raw}` : "";
1021
+ stmts.push(
1022
+ `CREATE ${unique2}INDEX IF NOT EXISTS ${idx.name} ON ${tableName} ${cols}${where};`
1023
+ );
1024
+ }
1025
+ return stmts;
1026
+ }
1027
+ async createIndexesForTable(table) {
1028
+ const stmts = this.generateCreateIndexSqls(table);
1029
+ for (const s of stmts) await this.run(s);
1030
+ }
941
1031
  async ensureMigrationsTable() {
942
1032
  await this.run(
943
1033
  `CREATE TABLE IF NOT EXISTS _migrations (name TEXT PRIMARY KEY, applied_at INTEGER NOT NULL)`
@@ -962,20 +1052,23 @@ var TauriORM = class {
962
1052
  const track = options?.track ?? true;
963
1053
  if (track) {
964
1054
  await this.ensureMigrationsTable();
1055
+ }
1056
+ await this.forcePushForTables(tables, { preserveData: true });
1057
+ if (track) {
965
1058
  const name = options?.name ?? `init:${tables.map((t) => t._tableName).join(",")}`;
966
1059
  const already = await this.hasMigration(name);
967
- if (already) return;
968
- await this.createTablesIfNotExist(tables);
969
- await this.recordMigration(name);
970
- return;
1060
+ if (!already) await this.recordMigration(name);
971
1061
  }
972
- await this.createTablesIfNotExist(tables);
973
1062
  }
974
1063
  // Configure schema and relations, and generate db.query automatically
975
1064
  configure(tables, relDefs) {
976
1065
  this._tables = tables;
977
1066
  this._relations = relDefs ?? {};
978
- this.query = makeQueryAPI(tables, this._relations, this.getDb.bind(this));
1067
+ this.query = makeQueryAPI(
1068
+ tables,
1069
+ this._relations ?? {},
1070
+ this.getDb.bind(this)
1071
+ );
979
1072
  return this;
980
1073
  }
981
1074
  // Convenience: migrate from configured tables
@@ -983,6 +1076,7 @@ var TauriORM = class {
983
1076
  if (!this._tables)
984
1077
  throw new Error("No tables configured. Call db.configure({...}) first.");
985
1078
  await this.migrate(Object.values(this._tables), options);
1079
+ await this.setSchemaMeta("schema_signature", this.computeModelSignature());
986
1080
  }
987
1081
  // --- Schema diff and CLI-like helpers ---
988
1082
  async diffSchema() {
@@ -1109,6 +1203,13 @@ var TauriORM = class {
1109
1203
  entries.sort((a, b) => a.table.localeCompare(b.table));
1110
1204
  return JSON.stringify(entries);
1111
1205
  }
1206
+ getSchemaSignature() {
1207
+ return this.computeModelSignature();
1208
+ }
1209
+ async printSchemaDiff() {
1210
+ const diff = await this.diffSchema();
1211
+ console.log("Schema diff:", JSON.stringify(diff, null, 2));
1212
+ }
1112
1213
  async isSchemaDirty() {
1113
1214
  const sig = this.computeModelSignature();
1114
1215
  const stored = await this.getSchemaMeta("schema_signature");
@@ -1118,7 +1219,9 @@ var TauriORM = class {
1118
1219
  const status = await this.isSchemaDirty();
1119
1220
  if (!this._tables) throw new Error("No tables configured.");
1120
1221
  if (status.dirty) {
1121
- await this.migrate(Object.values(this._tables), options);
1222
+ await this.forcePushForTables(Object.values(this._tables), {
1223
+ preserveData: true
1224
+ });
1122
1225
  await this.setSchemaMeta(
1123
1226
  "schema_signature",
1124
1227
  this.computeModelSignature()
@@ -1159,13 +1262,17 @@ var TauriORM = class {
1159
1262
  // Force push model to DB: add missing tables/columns, rebuild tables if incompatible
1160
1263
  async forcePush(options) {
1161
1264
  if (!this._tables) throw new Error("No tables configured.");
1265
+ await this.forcePushForTables(Object.values(this._tables), options);
1266
+ }
1267
+ async forcePushForTables(tables, options) {
1162
1268
  const dbi = await this.getDb();
1163
1269
  const preserve = options?.preserveData !== false;
1164
- for (const tbl of Object.values(this._tables)) {
1270
+ for (const tbl of tables) {
1165
1271
  const tableName = tbl._tableName;
1166
1272
  const exists2 = await this.tableExists(tableName);
1167
1273
  if (!exists2) {
1168
1274
  await this.run(this.buildCreateTableSQL(tbl));
1275
+ await this.createIndexesForTable(tbl);
1169
1276
  continue;
1170
1277
  }
1171
1278
  const existingCols = await dbi.select(
@@ -1210,20 +1317,18 @@ var TauriORM = class {
1210
1317
  }
1211
1318
  await this.run(`DROP TABLE ${tableName}`);
1212
1319
  await this.run(`ALTER TABLE ${tmp} RENAME TO ${tableName}`);
1320
+ await this.createIndexesForTable(tbl);
1213
1321
  } else {
1214
1322
  for (const m of missing) {
1215
1323
  let clause = `${m.name} ${m.type}`;
1216
1324
  if (m.isNotNull) clause += " NOT NULL";
1217
1325
  if (m.defaultValue !== void 0) {
1218
- const dv = m.defaultValue;
1219
- if (dv && typeof dv === "object" && "raw" in dv)
1220
- clause += ` DEFAULT ${dv.raw}`;
1221
- else if (typeof dv === "string")
1222
- clause += ` DEFAULT '${dv.replace(/'/g, "''")}'`;
1223
- else clause += ` DEFAULT ${dv}`;
1326
+ const formatted = this.formatDefaultValue(m);
1327
+ if (formatted !== null) clause += ` DEFAULT ${formatted}`;
1224
1328
  }
1225
1329
  await this.run(`ALTER TABLE ${tableName} ADD COLUMN ${clause}`);
1226
1330
  }
1331
+ await this.createIndexesForTable(tbl);
1227
1332
  }
1228
1333
  }
1229
1334
  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.7",
3
+ "version": "0.1.9",
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",