@type32/tauri-sqlite-orm 0.1.1 → 0.1.3

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/README.md CHANGED
@@ -35,6 +35,7 @@ export const users = defineTable("users", {
35
35
  export const posts = defineTable("posts", {
36
36
  id: integer("id", { isPrimaryKey: true }).primaryKey(),
37
37
  content: text("content"),
38
+ randomId: text("random_id").$defaultFn(() => crypto.randomUUID()),
38
39
  authorId: integer("author_id"),
39
40
  });
40
41
 
package/dist/index.d.mts CHANGED
@@ -25,6 +25,7 @@ interface Column<T = any> {
25
25
  autoIncrement?: boolean;
26
26
  isNotNull?: boolean;
27
27
  defaultValue?: T | SQLExpression;
28
+ defaultFn?: () => any;
28
29
  references?: {
29
30
  table: string;
30
31
  column: string;
@@ -46,6 +47,7 @@ type ColumnBuilder<T> = Column<T> & {
46
47
  notNull: () => ColumnBuilder<T>;
47
48
  default: (value: T | SQLExpression) => ColumnBuilder<T>;
48
49
  $type: <U>() => ColumnBuilder<U>;
50
+ $defaultFn: (fn: () => any) => ColumnBuilder<T>;
49
51
  references: (target: () => Column<any>, actions?: {
50
52
  onDelete?: UpdateDeleteAction;
51
53
  onUpdate?: UpdateDeleteAction;
@@ -152,6 +154,60 @@ declare class TauriORM {
152
154
  name?: string;
153
155
  track?: boolean;
154
156
  }): Promise<void>;
157
+ diffSchema(): Promise<{
158
+ extraTables: string[];
159
+ missingTables: string[];
160
+ tables: Record<string, {
161
+ missingColumns: string[];
162
+ extraColumns: string[];
163
+ changedColumns: Array<{
164
+ name: string;
165
+ diffs: {
166
+ type?: boolean;
167
+ pk?: boolean;
168
+ notNull?: boolean;
169
+ default?: boolean;
170
+ };
171
+ }>;
172
+ }>;
173
+ }>;
174
+ generate(): Promise<{
175
+ statements: string[];
176
+ }>;
177
+ migrateCli(opts?: {
178
+ name?: string;
179
+ track?: boolean;
180
+ }): Promise<void>;
181
+ push(opts?: {
182
+ dropExtraColumns?: boolean;
183
+ preserveData?: boolean;
184
+ }): Promise<void>;
185
+ pull(): Promise<Record<string, any>>;
186
+ studio(): Promise<{
187
+ driver: string;
188
+ path: any;
189
+ }>;
190
+ private ensureSchemaMeta;
191
+ private getSchemaMeta;
192
+ private setSchemaMeta;
193
+ private normalizeColumn;
194
+ private computeModelSignature;
195
+ isSchemaDirty(): Promise<{
196
+ dirty: boolean;
197
+ current: string;
198
+ stored: string | null;
199
+ }>;
200
+ migrateIfDirty(options?: {
201
+ name?: string;
202
+ track?: boolean;
203
+ }): Promise<boolean>;
204
+ pullSchema(): Promise<Record<string, any>>;
205
+ private buildCreateTableSQL;
206
+ private tableExists;
207
+ forcePush(options?: {
208
+ dropExtraColumns?: boolean;
209
+ preserveData?: boolean;
210
+ }): Promise<void>;
155
211
  }
156
212
  declare const db: TauriORM;
157
213
  type OneConfig = {
package/dist/index.d.ts CHANGED
@@ -25,6 +25,7 @@ interface Column<T = any> {
25
25
  autoIncrement?: boolean;
26
26
  isNotNull?: boolean;
27
27
  defaultValue?: T | SQLExpression;
28
+ defaultFn?: () => any;
28
29
  references?: {
29
30
  table: string;
30
31
  column: string;
@@ -46,6 +47,7 @@ type ColumnBuilder<T> = Column<T> & {
46
47
  notNull: () => ColumnBuilder<T>;
47
48
  default: (value: T | SQLExpression) => ColumnBuilder<T>;
48
49
  $type: <U>() => ColumnBuilder<U>;
50
+ $defaultFn: (fn: () => any) => ColumnBuilder<T>;
49
51
  references: (target: () => Column<any>, actions?: {
50
52
  onDelete?: UpdateDeleteAction;
51
53
  onUpdate?: UpdateDeleteAction;
@@ -152,6 +154,60 @@ declare class TauriORM {
152
154
  name?: string;
153
155
  track?: boolean;
154
156
  }): Promise<void>;
157
+ diffSchema(): Promise<{
158
+ extraTables: string[];
159
+ missingTables: string[];
160
+ tables: Record<string, {
161
+ missingColumns: string[];
162
+ extraColumns: string[];
163
+ changedColumns: Array<{
164
+ name: string;
165
+ diffs: {
166
+ type?: boolean;
167
+ pk?: boolean;
168
+ notNull?: boolean;
169
+ default?: boolean;
170
+ };
171
+ }>;
172
+ }>;
173
+ }>;
174
+ generate(): Promise<{
175
+ statements: string[];
176
+ }>;
177
+ migrateCli(opts?: {
178
+ name?: string;
179
+ track?: boolean;
180
+ }): Promise<void>;
181
+ push(opts?: {
182
+ dropExtraColumns?: boolean;
183
+ preserveData?: boolean;
184
+ }): Promise<void>;
185
+ pull(): Promise<Record<string, any>>;
186
+ studio(): Promise<{
187
+ driver: string;
188
+ path: any;
189
+ }>;
190
+ private ensureSchemaMeta;
191
+ private getSchemaMeta;
192
+ private setSchemaMeta;
193
+ private normalizeColumn;
194
+ private computeModelSignature;
195
+ isSchemaDirty(): Promise<{
196
+ dirty: boolean;
197
+ current: string;
198
+ stored: string | null;
199
+ }>;
200
+ migrateIfDirty(options?: {
201
+ name?: string;
202
+ track?: boolean;
203
+ }): Promise<boolean>;
204
+ pullSchema(): Promise<Record<string, any>>;
205
+ private buildCreateTableSQL;
206
+ private tableExists;
207
+ forcePush(options?: {
208
+ dropExtraColumns?: boolean;
209
+ preserveData?: boolean;
210
+ }): Promise<void>;
155
211
  }
156
212
  declare const db: TauriORM;
157
213
  type OneConfig = {
package/dist/index.js CHANGED
@@ -95,6 +95,10 @@ function createColumn(params) {
95
95
  return col;
96
96
  };
97
97
  col.$type = () => col;
98
+ col.$defaultFn = (fn) => {
99
+ col.defaultFn = fn;
100
+ return col;
101
+ };
98
102
  col.references = (target, actions) => {
99
103
  const t = target();
100
104
  col.references = {
@@ -289,8 +293,15 @@ var TauriORM = class {
289
293
  async execute() {
290
294
  const db3 = getDb();
291
295
  for (const data of this._rows) {
292
- const keys = Object.keys(data);
293
- const values = Object.values(data);
296
+ const finalData = { ...data };
297
+ const schema = this._table._schema;
298
+ for (const [key, col] of Object.entries(schema)) {
299
+ if (finalData[key] === void 0 && col.defaultFn) {
300
+ finalData[key] = col.defaultFn();
301
+ }
302
+ }
303
+ const keys = Object.keys(finalData);
304
+ const values = Object.values(finalData);
294
305
  const placeholders = values.map(() => "?").join(", ");
295
306
  const query = `INSERT INTO ${this._table._tableName} (${keys.join(
296
307
  ", "
@@ -462,6 +473,250 @@ var TauriORM = class {
462
473
  throw new Error("No tables configured. Call db.configure({...}) first.");
463
474
  await this.migrate(Object.values(this._tables), options);
464
475
  }
476
+ // --- Schema diff and CLI-like helpers ---
477
+ async diffSchema() {
478
+ if (!this._tables) throw new Error("No tables configured.");
479
+ const dbi = getDb();
480
+ const configuredNames = Object.values(this._tables).map(
481
+ (t) => t._tableName
482
+ );
483
+ const existing = await dbi.select(
484
+ `SELECT name FROM sqlite_master WHERE type='table'`
485
+ );
486
+ const existingNames = existing.map((r) => r.name);
487
+ const extraTables = existingNames.filter(
488
+ (n) => !configuredNames.includes(n)
489
+ );
490
+ const missingTables = configuredNames.filter(
491
+ (n) => !existingNames.includes(n)
492
+ );
493
+ const tables = {};
494
+ for (const tbl of Object.values(this._tables)) {
495
+ const tableName = tbl._tableName;
496
+ if (!existingNames.includes(tableName)) {
497
+ tables[tableName] = {
498
+ missingColumns: Object.keys(tbl._schema),
499
+ extraColumns: [],
500
+ changedColumns: []
501
+ };
502
+ continue;
503
+ }
504
+ const cols = await dbi.select(`PRAGMA table_info('${tableName}')`);
505
+ const colMap = new Map(cols.map((c) => [c.name, c]));
506
+ const modelCols = Object.values(
507
+ tbl._schema
508
+ );
509
+ const missingColumns = [];
510
+ const extraColumns = [];
511
+ const changedColumns = [];
512
+ const modelNamesSet = new Set(modelCols.map((c) => c.name));
513
+ for (const m of modelCols) {
514
+ const info = colMap.get(m.name);
515
+ if (!info) {
516
+ missingColumns.push(m.name);
517
+ continue;
518
+ }
519
+ const diffs = {};
520
+ if ((info.type || "").toUpperCase() !== m.type.toUpperCase())
521
+ diffs.type = true;
522
+ if (!!info.pk !== !!m.isPrimaryKey) diffs.pk = true;
523
+ if (!!info.notnull !== !!m.isNotNull) diffs.notNull = true;
524
+ const modelDv = m.defaultValue && typeof m.defaultValue === "object" && m.defaultValue.raw ? m.defaultValue.raw : m.defaultValue ?? null;
525
+ if ((info.dflt_value ?? null) !== modelDv)
526
+ diffs.default = true;
527
+ if (Object.keys(diffs).length)
528
+ changedColumns.push({ name: m.name, diffs });
529
+ }
530
+ for (const c of cols)
531
+ if (!modelNamesSet.has(c.name)) extraColumns.push(c.name);
532
+ tables[tableName] = { missingColumns, extraColumns, changedColumns };
533
+ }
534
+ return { extraTables, missingTables, tables };
535
+ }
536
+ async generate() {
537
+ if (!this._tables) throw new Error("No tables configured.");
538
+ return {
539
+ statements: Object.values(this._tables).map(
540
+ (t) => this.buildCreateTableSQL(t)
541
+ )
542
+ };
543
+ }
544
+ async migrateCli(opts) {
545
+ return this.migrateConfigured(opts);
546
+ }
547
+ async push(opts) {
548
+ return this.forcePush(opts);
549
+ }
550
+ async pull() {
551
+ return this.pullSchema();
552
+ }
553
+ async studio() {
554
+ const dbi = getDb();
555
+ return { driver: "sqlite", path: dbi.path };
556
+ }
557
+ // --- Schema detection / signature ---
558
+ async ensureSchemaMeta() {
559
+ await this.run(
560
+ `CREATE TABLE IF NOT EXISTS _schema_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)`
561
+ );
562
+ }
563
+ async getSchemaMeta(key) {
564
+ const dbi = getDb();
565
+ await this.ensureSchemaMeta();
566
+ const rows = await dbi.select(
567
+ `SELECT value FROM _schema_meta WHERE key = ?`,
568
+ [key]
569
+ );
570
+ return rows?.[0]?.value ?? null;
571
+ }
572
+ async setSchemaMeta(key, value) {
573
+ const dbi = getDb();
574
+ await this.ensureSchemaMeta();
575
+ await dbi.execute(
576
+ `INSERT INTO _schema_meta(key, value) VALUES(?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value`,
577
+ [key, value]
578
+ );
579
+ }
580
+ normalizeColumn(col) {
581
+ return {
582
+ name: col.name,
583
+ type: col.type,
584
+ pk: !!col.isPrimaryKey,
585
+ ai: !!col.autoIncrement,
586
+ nn: !!col.isNotNull,
587
+ dv: col.defaultValue && typeof col.defaultValue === "object" && col.defaultValue.raw ? { raw: col.defaultValue.raw } : col.defaultValue ?? null
588
+ };
589
+ }
590
+ computeModelSignature() {
591
+ if (!this._tables) return "";
592
+ const entries = Object.entries(this._tables).map(([k, tbl]) => {
593
+ const cols = Object.values(
594
+ tbl._schema
595
+ ).map((c) => this.normalizeColumn(c)).sort((a, b) => a.name.localeCompare(b.name));
596
+ return { table: tbl._tableName, columns: cols };
597
+ });
598
+ entries.sort((a, b) => a.table.localeCompare(b.table));
599
+ return JSON.stringify(entries);
600
+ }
601
+ async isSchemaDirty() {
602
+ const sig = this.computeModelSignature();
603
+ const stored = await this.getSchemaMeta("schema_signature");
604
+ return { dirty: sig !== stored, current: sig, stored };
605
+ }
606
+ async migrateIfDirty(options) {
607
+ const status = await this.isSchemaDirty();
608
+ if (!this._tables) throw new Error("No tables configured.");
609
+ if (status.dirty) {
610
+ await this.migrate(Object.values(this._tables), options);
611
+ await this.setSchemaMeta(
612
+ "schema_signature",
613
+ this.computeModelSignature()
614
+ );
615
+ return true;
616
+ }
617
+ return false;
618
+ }
619
+ // Pull current DB schema (minimal) for configured tables
620
+ async pullSchema() {
621
+ if (!this._tables) throw new Error("No tables configured.");
622
+ const dbi = getDb();
623
+ const result = {};
624
+ for (const tbl of Object.values(this._tables)) {
625
+ const name = tbl._tableName;
626
+ const cols = await dbi.select(`PRAGMA table_info('${name}')`);
627
+ result[name] = cols.map((c) => ({
628
+ name: c.name,
629
+ type: c.type,
630
+ notnull: !!c.notnull,
631
+ pk: !!c.pk,
632
+ dflt_value: c.dflt_value ?? null
633
+ }));
634
+ }
635
+ return result;
636
+ }
637
+ buildCreateTableSQL(table) {
638
+ return this.generateCreateTableSql(table);
639
+ }
640
+ async tableExists(name) {
641
+ const dbi = getDb();
642
+ const rows = await dbi.select(
643
+ `SELECT name FROM sqlite_master WHERE type='table' AND name = ?`,
644
+ [name]
645
+ );
646
+ return rows.length > 0;
647
+ }
648
+ // Force push model to DB: add missing tables/columns, rebuild tables if incompatible
649
+ async forcePush(options) {
650
+ if (!this._tables) throw new Error("No tables configured.");
651
+ const dbi = getDb();
652
+ const preserve = options?.preserveData !== false;
653
+ for (const tbl of Object.values(this._tables)) {
654
+ const tableName = tbl._tableName;
655
+ const exists = await this.tableExists(tableName);
656
+ if (!exists) {
657
+ await this.run(this.buildCreateTableSQL(tbl));
658
+ continue;
659
+ }
660
+ const existingCols = await dbi.select(
661
+ `PRAGMA table_info('${tableName}')`
662
+ );
663
+ const existingMap = new Map(existingCols.map((c) => [c.name, c]));
664
+ const modelCols = Object.values(
665
+ tbl._schema
666
+ );
667
+ const missing = [];
668
+ let requiresRebuild = false;
669
+ for (const m of modelCols) {
670
+ const info = existingMap.get(m.name);
671
+ if (!info) {
672
+ missing.push(m);
673
+ continue;
674
+ }
675
+ const typeDiff = (info.type || "").toUpperCase() !== m.type.toUpperCase();
676
+ const pkDiff = !!info.pk !== !!m.isPrimaryKey;
677
+ const nnDiff = !!info.notnull !== !!m.isNotNull;
678
+ const modelDv = m.defaultValue && typeof m.defaultValue === "object" && m.defaultValue.raw ? m.defaultValue.raw : m.defaultValue ?? null;
679
+ const defDiff = (info.dflt_value ?? null) !== modelDv;
680
+ if (typeDiff || pkDiff || nnDiff && !modelDv || defDiff) {
681
+ requiresRebuild = true;
682
+ }
683
+ }
684
+ if (requiresRebuild) {
685
+ const tmp = `_new_${tableName}`;
686
+ await this.run(this.buildCreateTableSQL(tbl));
687
+ await this.run(
688
+ this.buildCreateTableSQL({ ...tbl, _tableName: tmp })
689
+ );
690
+ const existingNames = existingCols.map((c) => c.name);
691
+ const modelNames = modelCols.map((c) => c.name);
692
+ const shared = existingNames.filter((n) => modelNames.includes(n));
693
+ if (preserve && shared.length > 0) {
694
+ await this.run(
695
+ `INSERT INTO ${tmp} (${shared.join(", ")}) SELECT ${shared.join(
696
+ ", "
697
+ )} FROM ${tableName}`
698
+ );
699
+ }
700
+ await this.run(`DROP TABLE ${tableName}`);
701
+ await this.run(`ALTER TABLE ${tmp} RENAME TO ${tableName}`);
702
+ } else {
703
+ for (const m of missing) {
704
+ let clause = `${m.name} ${m.type}`;
705
+ if (m.isNotNull) clause += " NOT NULL";
706
+ if (m.defaultValue !== void 0) {
707
+ const dv = m.defaultValue;
708
+ if (dv && typeof dv === "object" && "raw" in dv)
709
+ clause += ` DEFAULT ${dv.raw}`;
710
+ else if (typeof dv === "string")
711
+ clause += ` DEFAULT '${dv.replace(/'/g, "''")}'`;
712
+ else clause += ` DEFAULT ${dv}`;
713
+ }
714
+ await this.run(`ALTER TABLE ${tableName} ADD COLUMN ${clause}`);
715
+ }
716
+ }
717
+ }
718
+ await this.setSchemaMeta("schema_signature", this.computeModelSignature());
719
+ }
465
720
  };
466
721
  var db2 = new TauriORM();
467
722
  function relations(baseTable, builder) {
package/dist/index.mjs CHANGED
@@ -36,6 +36,10 @@ function createColumn(params) {
36
36
  return col;
37
37
  };
38
38
  col.$type = () => col;
39
+ col.$defaultFn = (fn) => {
40
+ col.defaultFn = fn;
41
+ return col;
42
+ };
39
43
  col.references = (target, actions) => {
40
44
  const t = target();
41
45
  col.references = {
@@ -230,8 +234,15 @@ var TauriORM = class {
230
234
  async execute() {
231
235
  const db3 = getDb();
232
236
  for (const data of this._rows) {
233
- const keys = Object.keys(data);
234
- const values = Object.values(data);
237
+ const finalData = { ...data };
238
+ const schema = this._table._schema;
239
+ for (const [key, col] of Object.entries(schema)) {
240
+ if (finalData[key] === void 0 && col.defaultFn) {
241
+ finalData[key] = col.defaultFn();
242
+ }
243
+ }
244
+ const keys = Object.keys(finalData);
245
+ const values = Object.values(finalData);
235
246
  const placeholders = values.map(() => "?").join(", ");
236
247
  const query = `INSERT INTO ${this._table._tableName} (${keys.join(
237
248
  ", "
@@ -403,6 +414,250 @@ var TauriORM = class {
403
414
  throw new Error("No tables configured. Call db.configure({...}) first.");
404
415
  await this.migrate(Object.values(this._tables), options);
405
416
  }
417
+ // --- Schema diff and CLI-like helpers ---
418
+ async diffSchema() {
419
+ if (!this._tables) throw new Error("No tables configured.");
420
+ const dbi = getDb();
421
+ const configuredNames = Object.values(this._tables).map(
422
+ (t) => t._tableName
423
+ );
424
+ const existing = await dbi.select(
425
+ `SELECT name FROM sqlite_master WHERE type='table'`
426
+ );
427
+ const existingNames = existing.map((r) => r.name);
428
+ const extraTables = existingNames.filter(
429
+ (n) => !configuredNames.includes(n)
430
+ );
431
+ const missingTables = configuredNames.filter(
432
+ (n) => !existingNames.includes(n)
433
+ );
434
+ const tables = {};
435
+ for (const tbl of Object.values(this._tables)) {
436
+ const tableName = tbl._tableName;
437
+ if (!existingNames.includes(tableName)) {
438
+ tables[tableName] = {
439
+ missingColumns: Object.keys(tbl._schema),
440
+ extraColumns: [],
441
+ changedColumns: []
442
+ };
443
+ continue;
444
+ }
445
+ const cols = await dbi.select(`PRAGMA table_info('${tableName}')`);
446
+ const colMap = new Map(cols.map((c) => [c.name, c]));
447
+ const modelCols = Object.values(
448
+ tbl._schema
449
+ );
450
+ const missingColumns = [];
451
+ const extraColumns = [];
452
+ const changedColumns = [];
453
+ const modelNamesSet = new Set(modelCols.map((c) => c.name));
454
+ for (const m of modelCols) {
455
+ const info = colMap.get(m.name);
456
+ if (!info) {
457
+ missingColumns.push(m.name);
458
+ continue;
459
+ }
460
+ const diffs = {};
461
+ if ((info.type || "").toUpperCase() !== m.type.toUpperCase())
462
+ diffs.type = true;
463
+ if (!!info.pk !== !!m.isPrimaryKey) diffs.pk = true;
464
+ if (!!info.notnull !== !!m.isNotNull) diffs.notNull = true;
465
+ const modelDv = m.defaultValue && typeof m.defaultValue === "object" && m.defaultValue.raw ? m.defaultValue.raw : m.defaultValue ?? null;
466
+ if ((info.dflt_value ?? null) !== modelDv)
467
+ diffs.default = true;
468
+ if (Object.keys(diffs).length)
469
+ changedColumns.push({ name: m.name, diffs });
470
+ }
471
+ for (const c of cols)
472
+ if (!modelNamesSet.has(c.name)) extraColumns.push(c.name);
473
+ tables[tableName] = { missingColumns, extraColumns, changedColumns };
474
+ }
475
+ return { extraTables, missingTables, tables };
476
+ }
477
+ async generate() {
478
+ if (!this._tables) throw new Error("No tables configured.");
479
+ return {
480
+ statements: Object.values(this._tables).map(
481
+ (t) => this.buildCreateTableSQL(t)
482
+ )
483
+ };
484
+ }
485
+ async migrateCli(opts) {
486
+ return this.migrateConfigured(opts);
487
+ }
488
+ async push(opts) {
489
+ return this.forcePush(opts);
490
+ }
491
+ async pull() {
492
+ return this.pullSchema();
493
+ }
494
+ async studio() {
495
+ const dbi = getDb();
496
+ return { driver: "sqlite", path: dbi.path };
497
+ }
498
+ // --- Schema detection / signature ---
499
+ async ensureSchemaMeta() {
500
+ await this.run(
501
+ `CREATE TABLE IF NOT EXISTS _schema_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)`
502
+ );
503
+ }
504
+ async getSchemaMeta(key) {
505
+ const dbi = getDb();
506
+ await this.ensureSchemaMeta();
507
+ const rows = await dbi.select(
508
+ `SELECT value FROM _schema_meta WHERE key = ?`,
509
+ [key]
510
+ );
511
+ return rows?.[0]?.value ?? null;
512
+ }
513
+ async setSchemaMeta(key, value) {
514
+ const dbi = getDb();
515
+ await this.ensureSchemaMeta();
516
+ await dbi.execute(
517
+ `INSERT INTO _schema_meta(key, value) VALUES(?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value`,
518
+ [key, value]
519
+ );
520
+ }
521
+ normalizeColumn(col) {
522
+ return {
523
+ name: col.name,
524
+ type: col.type,
525
+ pk: !!col.isPrimaryKey,
526
+ ai: !!col.autoIncrement,
527
+ nn: !!col.isNotNull,
528
+ dv: col.defaultValue && typeof col.defaultValue === "object" && col.defaultValue.raw ? { raw: col.defaultValue.raw } : col.defaultValue ?? null
529
+ };
530
+ }
531
+ computeModelSignature() {
532
+ if (!this._tables) return "";
533
+ const entries = Object.entries(this._tables).map(([k, tbl]) => {
534
+ const cols = Object.values(
535
+ tbl._schema
536
+ ).map((c) => this.normalizeColumn(c)).sort((a, b) => a.name.localeCompare(b.name));
537
+ return { table: tbl._tableName, columns: cols };
538
+ });
539
+ entries.sort((a, b) => a.table.localeCompare(b.table));
540
+ return JSON.stringify(entries);
541
+ }
542
+ async isSchemaDirty() {
543
+ const sig = this.computeModelSignature();
544
+ const stored = await this.getSchemaMeta("schema_signature");
545
+ return { dirty: sig !== stored, current: sig, stored };
546
+ }
547
+ async migrateIfDirty(options) {
548
+ const status = await this.isSchemaDirty();
549
+ if (!this._tables) throw new Error("No tables configured.");
550
+ if (status.dirty) {
551
+ await this.migrate(Object.values(this._tables), options);
552
+ await this.setSchemaMeta(
553
+ "schema_signature",
554
+ this.computeModelSignature()
555
+ );
556
+ return true;
557
+ }
558
+ return false;
559
+ }
560
+ // Pull current DB schema (minimal) for configured tables
561
+ async pullSchema() {
562
+ if (!this._tables) throw new Error("No tables configured.");
563
+ const dbi = getDb();
564
+ const result = {};
565
+ for (const tbl of Object.values(this._tables)) {
566
+ const name = tbl._tableName;
567
+ const cols = await dbi.select(`PRAGMA table_info('${name}')`);
568
+ result[name] = cols.map((c) => ({
569
+ name: c.name,
570
+ type: c.type,
571
+ notnull: !!c.notnull,
572
+ pk: !!c.pk,
573
+ dflt_value: c.dflt_value ?? null
574
+ }));
575
+ }
576
+ return result;
577
+ }
578
+ buildCreateTableSQL(table) {
579
+ return this.generateCreateTableSql(table);
580
+ }
581
+ async tableExists(name) {
582
+ const dbi = getDb();
583
+ const rows = await dbi.select(
584
+ `SELECT name FROM sqlite_master WHERE type='table' AND name = ?`,
585
+ [name]
586
+ );
587
+ return rows.length > 0;
588
+ }
589
+ // Force push model to DB: add missing tables/columns, rebuild tables if incompatible
590
+ async forcePush(options) {
591
+ if (!this._tables) throw new Error("No tables configured.");
592
+ const dbi = getDb();
593
+ const preserve = options?.preserveData !== false;
594
+ for (const tbl of Object.values(this._tables)) {
595
+ const tableName = tbl._tableName;
596
+ const exists = await this.tableExists(tableName);
597
+ if (!exists) {
598
+ await this.run(this.buildCreateTableSQL(tbl));
599
+ continue;
600
+ }
601
+ const existingCols = await dbi.select(
602
+ `PRAGMA table_info('${tableName}')`
603
+ );
604
+ const existingMap = new Map(existingCols.map((c) => [c.name, c]));
605
+ const modelCols = Object.values(
606
+ tbl._schema
607
+ );
608
+ const missing = [];
609
+ let requiresRebuild = false;
610
+ for (const m of modelCols) {
611
+ const info = existingMap.get(m.name);
612
+ if (!info) {
613
+ missing.push(m);
614
+ continue;
615
+ }
616
+ const typeDiff = (info.type || "").toUpperCase() !== m.type.toUpperCase();
617
+ const pkDiff = !!info.pk !== !!m.isPrimaryKey;
618
+ const nnDiff = !!info.notnull !== !!m.isNotNull;
619
+ const modelDv = m.defaultValue && typeof m.defaultValue === "object" && m.defaultValue.raw ? m.defaultValue.raw : m.defaultValue ?? null;
620
+ const defDiff = (info.dflt_value ?? null) !== modelDv;
621
+ if (typeDiff || pkDiff || nnDiff && !modelDv || defDiff) {
622
+ requiresRebuild = true;
623
+ }
624
+ }
625
+ if (requiresRebuild) {
626
+ const tmp = `_new_${tableName}`;
627
+ await this.run(this.buildCreateTableSQL(tbl));
628
+ await this.run(
629
+ this.buildCreateTableSQL({ ...tbl, _tableName: tmp })
630
+ );
631
+ const existingNames = existingCols.map((c) => c.name);
632
+ const modelNames = modelCols.map((c) => c.name);
633
+ const shared = existingNames.filter((n) => modelNames.includes(n));
634
+ if (preserve && shared.length > 0) {
635
+ await this.run(
636
+ `INSERT INTO ${tmp} (${shared.join(", ")}) SELECT ${shared.join(
637
+ ", "
638
+ )} FROM ${tableName}`
639
+ );
640
+ }
641
+ await this.run(`DROP TABLE ${tableName}`);
642
+ await this.run(`ALTER TABLE ${tmp} RENAME TO ${tableName}`);
643
+ } else {
644
+ for (const m of missing) {
645
+ let clause = `${m.name} ${m.type}`;
646
+ if (m.isNotNull) clause += " NOT NULL";
647
+ if (m.defaultValue !== void 0) {
648
+ const dv = m.defaultValue;
649
+ if (dv && typeof dv === "object" && "raw" in dv)
650
+ clause += ` DEFAULT ${dv.raw}`;
651
+ else if (typeof dv === "string")
652
+ clause += ` DEFAULT '${dv.replace(/'/g, "''")}'`;
653
+ else clause += ` DEFAULT ${dv}`;
654
+ }
655
+ await this.run(`ALTER TABLE ${tableName} ADD COLUMN ${clause}`);
656
+ }
657
+ }
658
+ }
659
+ await this.setSchemaMeta("schema_signature", this.computeModelSignature());
660
+ }
406
661
  };
407
662
  var db2 = new TauriORM();
408
663
  function relations(baseTable, builder) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@type32/tauri-sqlite-orm",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
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",