@type32/tauri-sqlite-orm 0.2.10 → 0.2.12

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
@@ -286,8 +286,21 @@ declare class TauriORM {
286
286
  private tables;
287
287
  constructor(db: Database, schema?: Record<string, AnyTable | Record<string, Relation>> | undefined);
288
288
  private buildColumnDefinition;
289
+ checkMigration(): Promise<{
290
+ safe: boolean;
291
+ changes: {
292
+ tablesToCreate: string[];
293
+ tablesToRecreate: string[];
294
+ tablesToDrop: string[];
295
+ columnsToAdd: Array<{
296
+ table: string;
297
+ column: string;
298
+ }>;
299
+ };
300
+ }>;
289
301
  migrate(options?: {
290
302
  performDestructiveActions?: boolean;
303
+ dryRun?: boolean;
291
304
  }): Promise<void>;
292
305
  private canAddColumnWithAlter;
293
306
  private hasColumnDefinitionChanged;
package/dist/index.d.ts CHANGED
@@ -286,8 +286,21 @@ declare class TauriORM {
286
286
  private tables;
287
287
  constructor(db: Database, schema?: Record<string, AnyTable | Record<string, Relation>> | undefined);
288
288
  private buildColumnDefinition;
289
+ checkMigration(): Promise<{
290
+ safe: boolean;
291
+ changes: {
292
+ tablesToCreate: string[];
293
+ tablesToRecreate: string[];
294
+ tablesToDrop: string[];
295
+ columnsToAdd: Array<{
296
+ table: string;
297
+ column: string;
298
+ }>;
299
+ };
300
+ }>;
289
301
  migrate(options?: {
290
302
  performDestructiveActions?: boolean;
303
+ dryRun?: boolean;
291
304
  }): Promise<void>;
292
305
  private canAddColumnWithAlter;
293
306
  private hasColumnDefinitionChanged;
package/dist/index.js CHANGED
@@ -269,6 +269,107 @@ var between = (column, min2, max2) => ({
269
269
  params: [min2, max2]
270
270
  });
271
271
 
272
+ // src/serialization.ts
273
+ function serializeValue(value, column) {
274
+ if (value === null || value === void 0) {
275
+ return null;
276
+ }
277
+ const { dataType, mode } = column._;
278
+ if (dataType === "TEXT") {
279
+ if (mode === "json") {
280
+ return typeof value === "string" ? value : JSON.stringify(value);
281
+ }
282
+ return String(value);
283
+ }
284
+ if (dataType === "INTEGER") {
285
+ if (mode === "timestamp" || mode === "timestamp_ms") {
286
+ if (value instanceof Date) {
287
+ return mode === "timestamp_ms" ? value.getTime() : Math.floor(value.getTime() / 1e3);
288
+ }
289
+ return typeof value === "number" ? value : parseInt(String(value), 10);
290
+ }
291
+ if (mode === "boolean") {
292
+ return value ? 1 : 0;
293
+ }
294
+ return typeof value === "number" ? Math.floor(value) : parseInt(String(value), 10);
295
+ }
296
+ if (dataType === "REAL") {
297
+ return typeof value === "number" ? value : parseFloat(String(value));
298
+ }
299
+ if (dataType === "BOOLEAN") {
300
+ return value ? 1 : 0;
301
+ }
302
+ if (dataType === "BLOB") {
303
+ if (mode === "json") {
304
+ return typeof value === "string" ? value : JSON.stringify(value);
305
+ }
306
+ if (mode === "bigint") {
307
+ return String(value);
308
+ }
309
+ return value;
310
+ }
311
+ if (dataType === "NUMERIC") {
312
+ if (mode === "bigint") {
313
+ return String(value);
314
+ }
315
+ return typeof value === "number" ? value : parseFloat(String(value));
316
+ }
317
+ return value;
318
+ }
319
+ function deserializeValue(value, column) {
320
+ if (value === null || value === void 0) {
321
+ return null;
322
+ }
323
+ const { dataType, mode } = column._;
324
+ if (dataType === "TEXT") {
325
+ if (mode === "json") {
326
+ try {
327
+ return JSON.parse(value);
328
+ } catch {
329
+ return value;
330
+ }
331
+ }
332
+ return value;
333
+ }
334
+ if (dataType === "INTEGER") {
335
+ if (mode === "timestamp" || mode === "timestamp_ms") {
336
+ const num = typeof value === "string" ? parseInt(value, 10) : value;
337
+ if (isNaN(num)) return null;
338
+ return mode === "timestamp_ms" ? new Date(num) : new Date(num * 1e3);
339
+ }
340
+ if (mode === "boolean") {
341
+ return value === 1 || value === "1" || value === true;
342
+ }
343
+ return typeof value === "string" ? parseInt(value, 10) : value;
344
+ }
345
+ if (dataType === "REAL") {
346
+ return typeof value === "string" ? parseFloat(value) : value;
347
+ }
348
+ if (dataType === "BOOLEAN") {
349
+ return value === 1 || value === "1" || value === true;
350
+ }
351
+ if (dataType === "BLOB") {
352
+ if (mode === "json") {
353
+ try {
354
+ return JSON.parse(value);
355
+ } catch {
356
+ return value;
357
+ }
358
+ }
359
+ if (mode === "bigint") {
360
+ return typeof value === "string" ? BigInt(value) : BigInt(value);
361
+ }
362
+ return value;
363
+ }
364
+ if (dataType === "NUMERIC") {
365
+ if (mode === "bigint") {
366
+ return typeof value === "string" ? BigInt(value) : BigInt(value);
367
+ }
368
+ return typeof value === "string" ? parseFloat(value) : value;
369
+ }
370
+ return value;
371
+ }
372
+
272
373
  // src/builders/select.ts
273
374
  var SelectQueryBuilder = class extends BaseQueryBuilder {
274
375
  constructor(db, table, columns) {
@@ -445,10 +546,12 @@ var SelectQueryBuilder = class extends BaseQueryBuilder {
445
546
  return rawResults.map((row) => {
446
547
  const newRow = {};
447
548
  for (const key in row) {
448
- if (key.startsWith(prefix)) {
449
- newRow[key.substring(prefix.length)] = row[key];
549
+ const columnName = key.startsWith(prefix) ? key.substring(prefix.length) : key;
550
+ const column = this.table._.columns[columnName];
551
+ if (column) {
552
+ newRow[columnName] = deserializeValue(row[key], column);
450
553
  } else {
451
- newRow[key] = row[key];
554
+ newRow[columnName] = row[key];
452
555
  }
453
556
  }
454
557
  return newRow;
@@ -502,18 +605,23 @@ var SelectQueryBuilder = class extends BaseQueryBuilder {
502
605
  if (key.includes(".")) {
503
606
  const [tableAlias, columnName] = key.split(".");
504
607
  if (tableAlias === this.selectedTableAlias) {
505
- result[columnName] = value;
608
+ const column = this.table._.columns[columnName];
609
+ result[columnName] = column ? deserializeValue(value, column) : value;
506
610
  } else {
507
611
  const relationPath = parseRelationPath(tableAlias, this.selectedTableAlias);
508
612
  if (relationPath.length > 0) {
509
- setNestedValue(relations2, relationPath, value, columnName);
613
+ const relationConfig = getNestedRelation(this.table, relationPath);
614
+ const column = relationConfig?.foreignTable?._.columns?.[columnName];
615
+ const deserializedValue = column ? deserializeValue(value, column) : value;
616
+ setNestedValue(relations2, relationPath, deserializedValue, columnName);
510
617
  } else {
511
618
  if (!result[tableAlias]) result[tableAlias] = {};
512
619
  result[tableAlias][columnName] = value;
513
620
  }
514
621
  }
515
622
  } else {
516
- result[key] = value;
623
+ const column = this.table._.columns[key];
624
+ result[key] = column ? deserializeValue(value, column) : value;
517
625
  }
518
626
  }
519
627
  const attachRelations = (target, relationsData, table, pathPrefix = []) => {
@@ -799,7 +907,7 @@ var UpdateQueryBuilder = class extends BaseQueryBuilder {
799
907
  throw new ColumnNotFoundError(key, this.table._.name);
800
908
  }
801
909
  setClauses.push(`${column._.name} = ?`);
802
- setParams.push(value);
910
+ setParams.push(serializeValue(value, column));
803
911
  }
804
912
  }
805
913
  for (const op of this.incrementDecrementOps) {
@@ -946,11 +1054,18 @@ var InsertQueryBuilder = class extends BaseQueryBuilder {
946
1054
  ", "
947
1055
  )}) VALUES ${valuesSql}${conflictClause}`;
948
1056
  const params = dataSets.flatMap(
949
- (data) => columns.map((col) => data[col] ?? null)
1057
+ (data) => columns.map((col) => {
1058
+ const value = data[col] ?? null;
1059
+ const column = this.table._.columns[col];
1060
+ return column ? serializeValue(value, column) : value;
1061
+ })
950
1062
  );
951
1063
  if (this.onConflictAction === "update") {
952
1064
  const setValues = Object.entries(this.updateSet).map(
953
- ([, value]) => value
1065
+ ([key, value]) => {
1066
+ const column = this.table._.columns[key];
1067
+ return column ? serializeValue(value, column) : value;
1068
+ }
954
1069
  );
955
1070
  params.push(...setValues);
956
1071
  }
@@ -1002,11 +1117,18 @@ var InsertQueryBuilder = class extends BaseQueryBuilder {
1002
1117
  ", "
1003
1118
  )}) VALUES ${valuesSql}${conflictClause}`;
1004
1119
  const params = processedDataSets.flatMap(
1005
- (data) => columns.map((col) => data[col] ?? null)
1120
+ (data) => columns.map((col) => {
1121
+ const value = data[col] ?? null;
1122
+ const column = this.table._.columns[col];
1123
+ return column ? serializeValue(value, column) : value;
1124
+ })
1006
1125
  );
1007
1126
  if (this.onConflictAction === "update") {
1008
1127
  const setValues = Object.entries(this.updateSet).map(
1009
- ([, value]) => value
1128
+ ([key, value]) => {
1129
+ const column = this.table._.columns[key];
1130
+ return column ? serializeValue(value, column) : value;
1131
+ }
1010
1132
  );
1011
1133
  params.push(...setValues);
1012
1134
  }
@@ -1258,7 +1380,86 @@ var TauriORM = class {
1258
1380
  }
1259
1381
  return sql2;
1260
1382
  }
1383
+ async checkMigration() {
1384
+ const dbTables = await this.db.select(
1385
+ `SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'`
1386
+ );
1387
+ const dbTableNames = new Set(dbTables.map((t) => t.name));
1388
+ const schemaTableNames = new Set(Array.from(this.tables.keys()));
1389
+ const changes = {
1390
+ tablesToCreate: [],
1391
+ tablesToRecreate: [],
1392
+ tablesToDrop: [],
1393
+ columnsToAdd: []
1394
+ };
1395
+ for (const tableName of schemaTableNames) {
1396
+ if (!dbTableNames.has(tableName)) {
1397
+ changes.tablesToCreate.push(tableName);
1398
+ }
1399
+ }
1400
+ for (const tableName of dbTableNames) {
1401
+ if (!schemaTableNames.has(tableName)) {
1402
+ changes.tablesToDrop.push(tableName);
1403
+ }
1404
+ }
1405
+ for (const table of this.tables.values()) {
1406
+ const tableName = table._.name;
1407
+ if (!dbTableNames.has(tableName)) continue;
1408
+ const existingTableInfo = await this.db.select(`PRAGMA table_info('${tableName}')`);
1409
+ const existingIndexes = await this.db.select(`PRAGMA index_list('${tableName}')`);
1410
+ const uniqueColumns = /* @__PURE__ */ new Set();
1411
+ for (const index of existingIndexes) {
1412
+ if (index.unique === 1 && index.origin === "u") {
1413
+ const indexInfo = await this.db.select(`PRAGMA index_info('${index.name}')`);
1414
+ if (indexInfo.length === 1) {
1415
+ uniqueColumns.add(indexInfo[0].name);
1416
+ }
1417
+ }
1418
+ }
1419
+ const existingColumns = new Map(existingTableInfo.map((c) => [c.name, c]));
1420
+ const schemaColumns = table._.columns;
1421
+ let needsRecreate = false;
1422
+ for (const [colName, column] of Object.entries(schemaColumns)) {
1423
+ const existing = existingColumns.get(colName);
1424
+ if (!existing) {
1425
+ if (!this.canAddColumnWithAlter(column)) {
1426
+ needsRecreate = true;
1427
+ break;
1428
+ }
1429
+ changes.columnsToAdd.push({ table: tableName, column: colName });
1430
+ } else {
1431
+ const hasUniqueInDB = uniqueColumns.has(colName);
1432
+ const wantsUnique = !!column.options.unique;
1433
+ if (hasUniqueInDB !== wantsUnique || this.hasColumnDefinitionChanged(column, existing)) {
1434
+ needsRecreate = true;
1435
+ break;
1436
+ }
1437
+ }
1438
+ }
1439
+ for (const existingCol of existingColumns.keys()) {
1440
+ if (!schemaColumns[existingCol]) {
1441
+ needsRecreate = true;
1442
+ break;
1443
+ }
1444
+ }
1445
+ if (needsRecreate) {
1446
+ changes.tablesToRecreate.push(tableName);
1447
+ }
1448
+ }
1449
+ const safe = changes.tablesToRecreate.length === 0 && changes.tablesToDrop.length === 0;
1450
+ return { safe, changes };
1451
+ }
1261
1452
  async migrate(options) {
1453
+ if (options?.dryRun) {
1454
+ const check = await this.checkMigration();
1455
+ console.log("[Tauri-ORM] Migration Preview (Dry Run):");
1456
+ console.log(" Tables to create:", check.changes.tablesToCreate);
1457
+ console.log(" Tables to recreate (DESTRUCTIVE):", check.changes.tablesToRecreate);
1458
+ console.log(" Tables to drop:", check.changes.tablesToDrop);
1459
+ console.log(" Columns to add:", check.changes.columnsToAdd);
1460
+ console.log(" Safe migration:", check.safe);
1461
+ return;
1462
+ }
1262
1463
  const dbTables = await this.db.select(
1263
1464
  `SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'`
1264
1465
  );
package/dist/index.mjs CHANGED
@@ -170,6 +170,107 @@ var between = (column, min2, max2) => ({
170
170
  params: [min2, max2]
171
171
  });
172
172
 
173
+ // src/serialization.ts
174
+ function serializeValue(value, column) {
175
+ if (value === null || value === void 0) {
176
+ return null;
177
+ }
178
+ const { dataType, mode } = column._;
179
+ if (dataType === "TEXT") {
180
+ if (mode === "json") {
181
+ return typeof value === "string" ? value : JSON.stringify(value);
182
+ }
183
+ return String(value);
184
+ }
185
+ if (dataType === "INTEGER") {
186
+ if (mode === "timestamp" || mode === "timestamp_ms") {
187
+ if (value instanceof Date) {
188
+ return mode === "timestamp_ms" ? value.getTime() : Math.floor(value.getTime() / 1e3);
189
+ }
190
+ return typeof value === "number" ? value : parseInt(String(value), 10);
191
+ }
192
+ if (mode === "boolean") {
193
+ return value ? 1 : 0;
194
+ }
195
+ return typeof value === "number" ? Math.floor(value) : parseInt(String(value), 10);
196
+ }
197
+ if (dataType === "REAL") {
198
+ return typeof value === "number" ? value : parseFloat(String(value));
199
+ }
200
+ if (dataType === "BOOLEAN") {
201
+ return value ? 1 : 0;
202
+ }
203
+ if (dataType === "BLOB") {
204
+ if (mode === "json") {
205
+ return typeof value === "string" ? value : JSON.stringify(value);
206
+ }
207
+ if (mode === "bigint") {
208
+ return String(value);
209
+ }
210
+ return value;
211
+ }
212
+ if (dataType === "NUMERIC") {
213
+ if (mode === "bigint") {
214
+ return String(value);
215
+ }
216
+ return typeof value === "number" ? value : parseFloat(String(value));
217
+ }
218
+ return value;
219
+ }
220
+ function deserializeValue(value, column) {
221
+ if (value === null || value === void 0) {
222
+ return null;
223
+ }
224
+ const { dataType, mode } = column._;
225
+ if (dataType === "TEXT") {
226
+ if (mode === "json") {
227
+ try {
228
+ return JSON.parse(value);
229
+ } catch {
230
+ return value;
231
+ }
232
+ }
233
+ return value;
234
+ }
235
+ if (dataType === "INTEGER") {
236
+ if (mode === "timestamp" || mode === "timestamp_ms") {
237
+ const num = typeof value === "string" ? parseInt(value, 10) : value;
238
+ if (isNaN(num)) return null;
239
+ return mode === "timestamp_ms" ? new Date(num) : new Date(num * 1e3);
240
+ }
241
+ if (mode === "boolean") {
242
+ return value === 1 || value === "1" || value === true;
243
+ }
244
+ return typeof value === "string" ? parseInt(value, 10) : value;
245
+ }
246
+ if (dataType === "REAL") {
247
+ return typeof value === "string" ? parseFloat(value) : value;
248
+ }
249
+ if (dataType === "BOOLEAN") {
250
+ return value === 1 || value === "1" || value === true;
251
+ }
252
+ if (dataType === "BLOB") {
253
+ if (mode === "json") {
254
+ try {
255
+ return JSON.parse(value);
256
+ } catch {
257
+ return value;
258
+ }
259
+ }
260
+ if (mode === "bigint") {
261
+ return typeof value === "string" ? BigInt(value) : BigInt(value);
262
+ }
263
+ return value;
264
+ }
265
+ if (dataType === "NUMERIC") {
266
+ if (mode === "bigint") {
267
+ return typeof value === "string" ? BigInt(value) : BigInt(value);
268
+ }
269
+ return typeof value === "string" ? parseFloat(value) : value;
270
+ }
271
+ return value;
272
+ }
273
+
173
274
  // src/builders/select.ts
174
275
  var SelectQueryBuilder = class extends BaseQueryBuilder {
175
276
  constructor(db, table, columns) {
@@ -346,10 +447,12 @@ var SelectQueryBuilder = class extends BaseQueryBuilder {
346
447
  return rawResults.map((row) => {
347
448
  const newRow = {};
348
449
  for (const key in row) {
349
- if (key.startsWith(prefix)) {
350
- newRow[key.substring(prefix.length)] = row[key];
450
+ const columnName = key.startsWith(prefix) ? key.substring(prefix.length) : key;
451
+ const column = this.table._.columns[columnName];
452
+ if (column) {
453
+ newRow[columnName] = deserializeValue(row[key], column);
351
454
  } else {
352
- newRow[key] = row[key];
455
+ newRow[columnName] = row[key];
353
456
  }
354
457
  }
355
458
  return newRow;
@@ -403,18 +506,23 @@ var SelectQueryBuilder = class extends BaseQueryBuilder {
403
506
  if (key.includes(".")) {
404
507
  const [tableAlias, columnName] = key.split(".");
405
508
  if (tableAlias === this.selectedTableAlias) {
406
- result[columnName] = value;
509
+ const column = this.table._.columns[columnName];
510
+ result[columnName] = column ? deserializeValue(value, column) : value;
407
511
  } else {
408
512
  const relationPath = parseRelationPath(tableAlias, this.selectedTableAlias);
409
513
  if (relationPath.length > 0) {
410
- setNestedValue(relations2, relationPath, value, columnName);
514
+ const relationConfig = getNestedRelation(this.table, relationPath);
515
+ const column = relationConfig?.foreignTable?._.columns?.[columnName];
516
+ const deserializedValue = column ? deserializeValue(value, column) : value;
517
+ setNestedValue(relations2, relationPath, deserializedValue, columnName);
411
518
  } else {
412
519
  if (!result[tableAlias]) result[tableAlias] = {};
413
520
  result[tableAlias][columnName] = value;
414
521
  }
415
522
  }
416
523
  } else {
417
- result[key] = value;
524
+ const column = this.table._.columns[key];
525
+ result[key] = column ? deserializeValue(value, column) : value;
418
526
  }
419
527
  }
420
528
  const attachRelations = (target, relationsData, table, pathPrefix = []) => {
@@ -700,7 +808,7 @@ var UpdateQueryBuilder = class extends BaseQueryBuilder {
700
808
  throw new ColumnNotFoundError(key, this.table._.name);
701
809
  }
702
810
  setClauses.push(`${column._.name} = ?`);
703
- setParams.push(value);
811
+ setParams.push(serializeValue(value, column));
704
812
  }
705
813
  }
706
814
  for (const op of this.incrementDecrementOps) {
@@ -847,11 +955,18 @@ var InsertQueryBuilder = class extends BaseQueryBuilder {
847
955
  ", "
848
956
  )}) VALUES ${valuesSql}${conflictClause}`;
849
957
  const params = dataSets.flatMap(
850
- (data) => columns.map((col) => data[col] ?? null)
958
+ (data) => columns.map((col) => {
959
+ const value = data[col] ?? null;
960
+ const column = this.table._.columns[col];
961
+ return column ? serializeValue(value, column) : value;
962
+ })
851
963
  );
852
964
  if (this.onConflictAction === "update") {
853
965
  const setValues = Object.entries(this.updateSet).map(
854
- ([, value]) => value
966
+ ([key, value]) => {
967
+ const column = this.table._.columns[key];
968
+ return column ? serializeValue(value, column) : value;
969
+ }
855
970
  );
856
971
  params.push(...setValues);
857
972
  }
@@ -903,11 +1018,18 @@ var InsertQueryBuilder = class extends BaseQueryBuilder {
903
1018
  ", "
904
1019
  )}) VALUES ${valuesSql}${conflictClause}`;
905
1020
  const params = processedDataSets.flatMap(
906
- (data) => columns.map((col) => data[col] ?? null)
1021
+ (data) => columns.map((col) => {
1022
+ const value = data[col] ?? null;
1023
+ const column = this.table._.columns[col];
1024
+ return column ? serializeValue(value, column) : value;
1025
+ })
907
1026
  );
908
1027
  if (this.onConflictAction === "update") {
909
1028
  const setValues = Object.entries(this.updateSet).map(
910
- ([, value]) => value
1029
+ ([key, value]) => {
1030
+ const column = this.table._.columns[key];
1031
+ return column ? serializeValue(value, column) : value;
1032
+ }
911
1033
  );
912
1034
  params.push(...setValues);
913
1035
  }
@@ -1159,7 +1281,86 @@ var TauriORM = class {
1159
1281
  }
1160
1282
  return sql2;
1161
1283
  }
1284
+ async checkMigration() {
1285
+ const dbTables = await this.db.select(
1286
+ `SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'`
1287
+ );
1288
+ const dbTableNames = new Set(dbTables.map((t) => t.name));
1289
+ const schemaTableNames = new Set(Array.from(this.tables.keys()));
1290
+ const changes = {
1291
+ tablesToCreate: [],
1292
+ tablesToRecreate: [],
1293
+ tablesToDrop: [],
1294
+ columnsToAdd: []
1295
+ };
1296
+ for (const tableName of schemaTableNames) {
1297
+ if (!dbTableNames.has(tableName)) {
1298
+ changes.tablesToCreate.push(tableName);
1299
+ }
1300
+ }
1301
+ for (const tableName of dbTableNames) {
1302
+ if (!schemaTableNames.has(tableName)) {
1303
+ changes.tablesToDrop.push(tableName);
1304
+ }
1305
+ }
1306
+ for (const table of this.tables.values()) {
1307
+ const tableName = table._.name;
1308
+ if (!dbTableNames.has(tableName)) continue;
1309
+ const existingTableInfo = await this.db.select(`PRAGMA table_info('${tableName}')`);
1310
+ const existingIndexes = await this.db.select(`PRAGMA index_list('${tableName}')`);
1311
+ const uniqueColumns = /* @__PURE__ */ new Set();
1312
+ for (const index of existingIndexes) {
1313
+ if (index.unique === 1 && index.origin === "u") {
1314
+ const indexInfo = await this.db.select(`PRAGMA index_info('${index.name}')`);
1315
+ if (indexInfo.length === 1) {
1316
+ uniqueColumns.add(indexInfo[0].name);
1317
+ }
1318
+ }
1319
+ }
1320
+ const existingColumns = new Map(existingTableInfo.map((c) => [c.name, c]));
1321
+ const schemaColumns = table._.columns;
1322
+ let needsRecreate = false;
1323
+ for (const [colName, column] of Object.entries(schemaColumns)) {
1324
+ const existing = existingColumns.get(colName);
1325
+ if (!existing) {
1326
+ if (!this.canAddColumnWithAlter(column)) {
1327
+ needsRecreate = true;
1328
+ break;
1329
+ }
1330
+ changes.columnsToAdd.push({ table: tableName, column: colName });
1331
+ } else {
1332
+ const hasUniqueInDB = uniqueColumns.has(colName);
1333
+ const wantsUnique = !!column.options.unique;
1334
+ if (hasUniqueInDB !== wantsUnique || this.hasColumnDefinitionChanged(column, existing)) {
1335
+ needsRecreate = true;
1336
+ break;
1337
+ }
1338
+ }
1339
+ }
1340
+ for (const existingCol of existingColumns.keys()) {
1341
+ if (!schemaColumns[existingCol]) {
1342
+ needsRecreate = true;
1343
+ break;
1344
+ }
1345
+ }
1346
+ if (needsRecreate) {
1347
+ changes.tablesToRecreate.push(tableName);
1348
+ }
1349
+ }
1350
+ const safe = changes.tablesToRecreate.length === 0 && changes.tablesToDrop.length === 0;
1351
+ return { safe, changes };
1352
+ }
1162
1353
  async migrate(options) {
1354
+ if (options?.dryRun) {
1355
+ const check = await this.checkMigration();
1356
+ console.log("[Tauri-ORM] Migration Preview (Dry Run):");
1357
+ console.log(" Tables to create:", check.changes.tablesToCreate);
1358
+ console.log(" Tables to recreate (DESTRUCTIVE):", check.changes.tablesToRecreate);
1359
+ console.log(" Tables to drop:", check.changes.tablesToDrop);
1360
+ console.log(" Columns to add:", check.changes.columnsToAdd);
1361
+ console.log(" Safe migration:", check.safe);
1362
+ return;
1363
+ }
1163
1364
  const dbTables = await this.db.select(
1164
1365
  `SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'`
1165
1366
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@type32/tauri-sqlite-orm",
3
- "version": "0.2.10",
3
+ "version": "0.2.12",
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",