@type32/tauri-sqlite-orm 0.1.18-2 → 0.1.18-21

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.mjs CHANGED
@@ -1,110 +1,48 @@
1
- // src/orm.ts
2
- var SQLiteColumn = class _SQLiteColumn {
3
- constructor(name, type, options = {}, mode) {
4
- this.type = type;
5
- this.options = options;
6
- this._ = {
7
- name,
8
- dataType: type,
9
- mode: mode || "default",
10
- notNull: options.notNull ?? false,
11
- hasDefault: options.default !== void 0 || options.$defaultFn !== void 0,
12
- autoincrement: options.autoincrement ?? false
13
- };
14
- }
15
- _;
16
- notNull() {
17
- return new _SQLiteColumn(
18
- this._.name,
19
- this.type,
20
- { ...this.options, notNull: true },
21
- this._.mode
22
- );
23
- }
24
- default(value) {
25
- return new _SQLiteColumn(
26
- this._.name,
27
- this.type,
28
- { ...this.options, default: value },
29
- this._.mode
30
- );
31
- }
32
- $defaultFn(fn) {
33
- return new _SQLiteColumn(
34
- this._.name,
35
- this.type,
36
- { ...this.options, $defaultFn: fn },
37
- this._.mode
38
- );
39
- }
40
- primaryKey() {
41
- return new _SQLiteColumn(
42
- this._.name,
43
- this.type,
44
- { ...this.options, primaryKey: true },
45
- this._.mode
46
- );
1
+ // src/builders/query-base.ts
2
+ var BaseQueryBuilder = class {
3
+ constructor(db) {
4
+ this.db = db;
47
5
  }
48
- autoincrement() {
49
- return new _SQLiteColumn(
50
- this._.name,
51
- this.type,
52
- { ...this.options, autoincrement: true },
53
- this._.mode
54
- );
6
+ query = "";
7
+ params = [];
8
+ where(condition) {
9
+ this.query += ` WHERE ${condition.sql}`;
10
+ this.params.push(...condition.params);
11
+ return this;
55
12
  }
56
- unique() {
57
- return new _SQLiteColumn(
58
- this._.name,
59
- this.type,
60
- { ...this.options, unique: true },
61
- this._.mode
62
- );
13
+ orderBy(column, direction = "ASC") {
14
+ if ("sql" in column) {
15
+ this.query += ` ORDER BY ${column.sql} ${direction}`;
16
+ this.params.push(...column.params);
17
+ } else {
18
+ this.query += ` ORDER BY ${column._.name} ${direction}`;
19
+ }
20
+ return this;
63
21
  }
64
- references(ref, column) {
65
- return new _SQLiteColumn(
66
- this._.name,
67
- this.type,
68
- {
69
- ...this.options,
70
- references: {
71
- table: ref,
72
- column: ref._.columns[column]
73
- }
74
- },
75
- this._.mode
76
- );
22
+ limit(count2) {
23
+ this.query += ` LIMIT ${count2}`;
24
+ return this;
77
25
  }
78
- $onUpdateFn(fn) {
79
- return new _SQLiteColumn(
80
- this._.name,
81
- this.type,
82
- { ...this.options, $onUpdateFn: fn },
83
- this._.mode
84
- );
26
+ offset(count2) {
27
+ this.query += ` OFFSET ${count2}`;
28
+ return this;
85
29
  }
86
- };
87
- var text = (name) => new SQLiteColumn(name, "TEXT");
88
- var integer = (name, config) => new SQLiteColumn(name, "INTEGER", {}, config?.mode || "default");
89
- var real = (name) => new SQLiteColumn(name, "REAL");
90
- var blob = (name) => new SQLiteColumn(name, "BLOB");
91
- var boolean = (name) => new SQLiteColumn(name, "BOOLEAN");
92
- var Table = class {
93
- _;
94
- constructor(name, columns) {
95
- this._ = {
96
- name,
97
- columns
30
+ build() {
31
+ return {
32
+ sql: this.query,
33
+ params: this.params
98
34
  };
99
35
  }
100
36
  };
101
- var sqliteTable = (tableName, columns) => {
102
- return new Table(tableName, columns);
37
+
38
+ // src/operators.ts
39
+ var eq = (column, value, tableAlias) => {
40
+ const columnName = tableAlias ? `${tableAlias}.${column._.name}` : column._.name;
41
+ return {
42
+ sql: `${columnName} = ?`,
43
+ params: [value]
44
+ };
103
45
  };
104
- var eq = (column, value) => ({
105
- sql: `${column._.name} = ?`,
106
- params: [value]
107
- });
108
46
  var and = (...conditions) => ({
109
47
  sql: conditions.map((c) => `(${c.sql})`).join(" AND "),
110
48
  params: conditions.flatMap((c) => c.params)
@@ -113,6 +51,10 @@ var or = (...conditions) => ({
113
51
  sql: conditions.map((c) => `(${c.sql})`).join(" OR "),
114
52
  params: conditions.flatMap((c) => c.params)
115
53
  });
54
+ var not = (condition) => ({
55
+ sql: `NOT (${condition.sql})`,
56
+ params: condition.params
57
+ });
116
58
  var gt = (column, value) => ({
117
59
  sql: `${column._.name} > ?`,
118
60
  params: [value]
@@ -145,81 +87,241 @@ var inArray = (column, values) => ({
145
87
  sql: `${column._.name} IN (${values.map(() => "?").join(",")})`,
146
88
  params: values
147
89
  });
148
- var BaseQueryBuilder = class {
149
- constructor(db) {
150
- this.db = db;
90
+ var count = (column) => ({
91
+ sql: `COUNT(${column ? column._.name : "*"})`,
92
+ params: []
93
+ });
94
+ var countDistinct = (column) => ({
95
+ sql: `COUNT(DISTINCT ${column._.name})`,
96
+ params: []
97
+ });
98
+ var sum = (column) => ({
99
+ sql: `SUM(${column._.name})`,
100
+ params: []
101
+ });
102
+ var avg = (column) => ({
103
+ sql: `AVG(${column._.name})`,
104
+ params: []
105
+ });
106
+ var max = (column) => ({
107
+ sql: `MAX(${column._.name})`,
108
+ params: []
109
+ });
110
+ var min = (column) => ({
111
+ sql: `MIN(${column._.name})`,
112
+ params: []
113
+ });
114
+
115
+ // src/builders/select.ts
116
+ var SelectQueryBuilder = class extends BaseQueryBuilder {
117
+ constructor(db, table, columns) {
118
+ super(db);
119
+ this.table = table;
120
+ this.columns = columns;
121
+ this.selectedTableAlias = table._.name;
122
+ const selected = columns ? columns.map((c) => this.table._.columns[c]) : Object.values(this.table._.columns);
123
+ this.selectedColumns = selected.map(
124
+ (col) => `${this.selectedTableAlias}.${col._.name} AS "${this.selectedTableAlias}.${col._.name}"`
125
+ );
126
+ this.query = `FROM ${table._.name} ${this.selectedTableAlias}`;
127
+ }
128
+ isDistinct = false;
129
+ groupByColumns = [];
130
+ havingCondition = null;
131
+ joins = [];
132
+ includeRelations = {};
133
+ selectedTableAlias;
134
+ selectedColumns = [];
135
+ distinct() {
136
+ this.isDistinct = true;
137
+ return this;
151
138
  }
152
- query = "";
153
- params = [];
154
- where(condition) {
155
- this.query += ` WHERE ${condition.sql}`;
156
- this.params.push(...condition.params);
139
+ groupBy(...columns) {
140
+ this.groupByColumns.push(...columns);
141
+ const columnNames = columns.map((col) => `${this.selectedTableAlias}.${col._.name}`).join(", ");
142
+ this.query += ` GROUP BY ${columnNames}`;
157
143
  return this;
158
144
  }
159
- orderBy(column, direction = "ASC") {
160
- this.query += ` ORDER BY ${column._.name} ${direction}`;
145
+ having(condition) {
146
+ this.havingCondition = condition;
147
+ this.query += ` HAVING ${condition.sql}`;
148
+ this.params.push(...condition.params);
161
149
  return this;
162
150
  }
163
- limit(count) {
164
- this.query += ` LIMIT ${count}`;
151
+ leftJoin(table, condition, alias2) {
152
+ this.joins.push({ type: "LEFT", table, condition, alias: alias2 });
153
+ const aliasedColumns = Object.values(table._.columns).map(
154
+ (col) => `${alias2}.${col._.name} AS "${alias2}.${col._.name}"`
155
+ );
156
+ this.selectedColumns.push(...aliasedColumns);
165
157
  return this;
166
158
  }
167
- offset(count) {
168
- this.query += ` OFFSET ${count}`;
159
+ innerJoin(table, condition, alias2) {
160
+ this.joins.push({ type: "INNER", table, condition, alias: alias2 });
161
+ const aliasedColumns = Object.values(table._.columns).map(
162
+ (col) => `${alias2}.${col._.name} AS "${alias2}.${col._.name}"`
163
+ );
164
+ this.selectedColumns.push(...aliasedColumns);
169
165
  return this;
170
166
  }
171
- build() {
172
- return {
173
- sql: this.query,
174
- params: this.params
175
- };
167
+ include(relations2) {
168
+ this.includeRelations = { ...this.includeRelations, ...relations2 };
169
+ return this;
176
170
  }
177
- };
178
- var SelectQueryBuilder = class extends BaseQueryBuilder {
179
- constructor(db, table, columns) {
180
- super(db);
181
- this.table = table;
182
- this.columns = columns;
183
- const columnNames = columns ? columns.map((c) => table._.columns[c]._.name) : ["*"];
184
- this.query = `SELECT ${columnNames.join(", ")} FROM ${table._.name}`;
171
+ buildJoins() {
172
+ let sql2 = "";
173
+ const params = [];
174
+ for (const join of this.joins) {
175
+ sql2 += ` ${join.type} JOIN ${join.table._.name} ${join.alias} ON ${join.condition.sql}`;
176
+ params.push(...join.condition.params);
177
+ }
178
+ for (const [relationName, include] of Object.entries(this.includeRelations)) {
179
+ if (!include) continue;
180
+ const relation = this.table.relations[relationName];
181
+ if (!relation) {
182
+ console.warn(
183
+ `[Tauri-ORM] Relation "${relationName}" not found on table "${this.table._.name}". Skipping include.`
184
+ );
185
+ continue;
186
+ }
187
+ const foreignTable = relation.foreignTable;
188
+ const foreignAlias = `${this.selectedTableAlias}_${relationName}`;
189
+ const aliasedColumns = Object.values(foreignTable._.columns).map(
190
+ (col) => `${foreignAlias}.${col._.name} AS "${foreignAlias}.${col._.name}"`
191
+ );
192
+ this.selectedColumns.push(...aliasedColumns);
193
+ if (relation.type === "one" && relation.fields && relation.references) {
194
+ const conditions = relation.fields.map((field, i) => {
195
+ const localColumn = `${this.selectedTableAlias}.${field._.name}`;
196
+ const foreignColumn = `${foreignAlias}.${relation.references[i]._.name}`;
197
+ return {
198
+ sql: `${localColumn} = ${foreignColumn}`,
199
+ params: []
200
+ };
201
+ });
202
+ const condition = conditions.length > 1 ? and(...conditions) : conditions[0];
203
+ sql2 += ` LEFT JOIN ${foreignTable._.name} ${foreignAlias} ON ${condition.sql}`;
204
+ params.push(...condition.params);
205
+ } else if (relation.type === "many") {
206
+ const refRelation = Object.entries(foreignTable.relations).find(
207
+ ([_, r]) => r.foreignTable === this.table
208
+ );
209
+ if (refRelation && refRelation[1].fields && refRelation[1].references) {
210
+ const [_, relationConfig] = refRelation;
211
+ const conditions = relationConfig.fields.map((field, i) => {
212
+ const localColumn = `${foreignAlias}.${field._.name}`;
213
+ const foreignColumn = `${this.selectedTableAlias}.${relationConfig.references[i]._.name}`;
214
+ return {
215
+ sql: `${localColumn} = ${foreignColumn}`,
216
+ params: []
217
+ };
218
+ });
219
+ const condition = conditions.length > 1 ? and(...conditions) : conditions[0];
220
+ sql2 += ` LEFT JOIN ${foreignTable._.name} ${foreignAlias} ON ${condition.sql}`;
221
+ params.push(...condition.params);
222
+ }
223
+ }
224
+ }
225
+ return { sql: sql2, params };
185
226
  }
227
+ // Enhanced execute method that handles relation data mapping
186
228
  async execute() {
187
- const { sql, params } = this.build();
188
- return this.db.select(sql, params);
229
+ const { sql: joinSql, params: joinParams } = this.buildJoins();
230
+ const distinct = this.isDistinct ? "DISTINCT " : "";
231
+ this.query = `SELECT ${distinct}${this.selectedColumns.join(", ")} ${this.query}`;
232
+ this.query += joinSql;
233
+ this.params.push(...joinParams);
234
+ const { sql: sql2, params } = this.build();
235
+ console.log("Executing SQL:", sql2, "with params:", params);
236
+ const rawResults = await this.db.select(sql2, params);
237
+ const hasIncludes = Object.values(this.includeRelations).some((i) => i);
238
+ if (hasIncludes) {
239
+ return this.processRelationResults(rawResults);
240
+ }
241
+ const hasJoins = this.joins.length > 0;
242
+ if (hasJoins) {
243
+ return rawResults;
244
+ }
245
+ const prefix = `${this.selectedTableAlias}.`;
246
+ return rawResults.map((row) => {
247
+ const newRow = {};
248
+ for (const key in row) {
249
+ if (key.startsWith(prefix)) {
250
+ newRow[key.substring(prefix.length)] = row[key];
251
+ } else {
252
+ newRow[key] = row[key];
253
+ }
254
+ }
255
+ return newRow;
256
+ });
189
257
  }
190
- };
191
- var InsertQueryBuilder = class extends BaseQueryBuilder {
192
- constructor(db, table) {
193
- super(db);
194
- this.table = table;
195
- this.query = `INSERT INTO ${table._.name}`;
258
+ processRelationResults(rawResults) {
259
+ if (!rawResults.length) return [];
260
+ const mainTablePks = Object.values(this.table._.columns).filter((c) => c.options.primaryKey).map((c) => c._.name);
261
+ if (mainTablePks.length === 0) {
262
+ return rawResults;
263
+ }
264
+ const groupedResults = /* @__PURE__ */ new Map();
265
+ for (const row of rawResults) {
266
+ const mainTableKey = mainTablePks.map((pk) => row[`${this.selectedTableAlias}.${pk}`] ?? row[pk]).join("_");
267
+ if (!groupedResults.has(mainTableKey)) {
268
+ groupedResults.set(mainTableKey, {});
269
+ }
270
+ const result = groupedResults.get(mainTableKey);
271
+ const relations2 = {};
272
+ for (const [key, value] of Object.entries(row)) {
273
+ if (key.includes(".")) {
274
+ const [tableAlias, columnName] = key.split(".");
275
+ if (tableAlias === this.selectedTableAlias) {
276
+ result[columnName] = value;
277
+ } else {
278
+ const parts = tableAlias.split("_");
279
+ if (parts.length >= 2 && parts[0] === this.selectedTableAlias) {
280
+ const relationName = parts.slice(1).join("_");
281
+ if (!relations2[relationName]) relations2[relationName] = {};
282
+ relations2[relationName][columnName] = value;
283
+ } else {
284
+ if (!result[tableAlias]) result[tableAlias] = {};
285
+ result[tableAlias][columnName] = value;
286
+ }
287
+ }
288
+ } else {
289
+ result[key] = value;
290
+ }
291
+ }
292
+ for (const [relName, relData] of Object.entries(relations2)) {
293
+ const relationConfig = this.table.relations[relName];
294
+ if (!relationConfig) continue;
295
+ const hasData = Object.values(relData).some(
296
+ (v) => v !== null && v !== void 0 && v !== ""
297
+ );
298
+ if (!hasData) continue;
299
+ if (relationConfig.type === "many") {
300
+ if (!result[relName]) result[relName] = [];
301
+ const relatedPks = Object.values(relationConfig.foreignTable._.columns).filter((c) => c.options.primaryKey).map((c) => c._.name);
302
+ const relDataKey = relatedPks.map((pk) => relData[pk]).join("_");
303
+ if (relatedPks.length === 0 || !result[relName].some((r) => relatedPks.map((pk) => r[pk]).join("_") === relDataKey)) {
304
+ result[relName].push(relData);
305
+ }
306
+ } else {
307
+ result[relName] = relData;
308
+ }
309
+ }
310
+ }
311
+ return Array.from(groupedResults.values());
196
312
  }
197
- dataSets = [];
198
- values(data) {
199
- const dataArray = Array.isArray(data) ? data : [data];
200
- this.dataSets.push(...dataArray);
201
- return this;
313
+ // Update the return type signatures
314
+ async all() {
315
+ return this.execute();
202
316
  }
203
- async execute() {
204
- if (this.dataSets.length === 0) {
205
- throw new Error("No data provided for insert");
206
- }
207
- const columns = Object.keys(
208
- this.dataSets[0]
209
- );
210
- const columnNames = columns.map(
211
- (c) => this.table._.columns[c]._.name
212
- );
213
- const placeholders = `(${columnNames.map(() => "?").join(", ")})`;
214
- const valuesSql = this.dataSets.map(() => placeholders).join(", ");
215
- this.query += ` (${columnNames.join(", ")}) VALUES ${valuesSql}`;
216
- const params = this.dataSets.flatMap(
217
- (data) => columns.map((col) => data[col])
218
- );
219
- const result = await this.db.execute(this.query, params);
220
- return result.lastInsertId ?? 0;
317
+ async get() {
318
+ this.limit(1);
319
+ const result = await this.execute();
320
+ return result[0];
221
321
  }
222
322
  };
323
+
324
+ // src/builders/update.ts
223
325
  var UpdateQueryBuilder = class extends BaseQueryBuilder {
224
326
  constructor(db, table) {
225
327
  super(db);
@@ -227,11 +329,23 @@ var UpdateQueryBuilder = class extends BaseQueryBuilder {
227
329
  this.query = `UPDATE ${table._.name}`;
228
330
  }
229
331
  updateData = {};
332
+ returningColumns = [];
230
333
  set(data) {
231
334
  this.updateData = { ...this.updateData, ...data };
232
335
  return this;
233
336
  }
234
- build() {
337
+ returning(...columns) {
338
+ this.returningColumns.push(...columns);
339
+ return this;
340
+ }
341
+ buildUpdateClause() {
342
+ const finalUpdateData = { ...this.updateData };
343
+ for (const [key, column] of Object.entries(this.table._.columns)) {
344
+ const typedKey = key;
345
+ if (finalUpdateData[typedKey] === void 0 && column.options.$onUpdateFn) {
346
+ finalUpdateData[typedKey] = column.options.$onUpdateFn();
347
+ }
348
+ }
235
349
  const baseQuery = this.query;
236
350
  const whereParams = this.params;
237
351
  let tablePart = baseQuery;
@@ -241,7 +355,7 @@ var UpdateQueryBuilder = class extends BaseQueryBuilder {
241
355
  tablePart = baseQuery.substring(0, whereIndex);
242
356
  whereClause = baseQuery.substring(whereIndex);
243
357
  }
244
- const entries = Object.entries(this.updateData);
358
+ const entries = Object.entries(finalUpdateData);
245
359
  if (entries.length === 0) {
246
360
  throw new Error("Cannot execute an update query without a .set() call.");
247
361
  }
@@ -255,61 +369,416 @@ var UpdateQueryBuilder = class extends BaseQueryBuilder {
255
369
  return `${column._.name} = ?`;
256
370
  }).join(", ");
257
371
  const setParams = entries.map(([, value]) => value);
258
- const sql = `${tablePart} SET ${setClause}${whereClause}`;
372
+ const sql2 = `${tablePart} SET ${setClause}${whereClause}`;
259
373
  const params = [...setParams, ...whereParams];
260
- return { sql, params };
374
+ return { sql: sql2, params };
261
375
  }
262
376
  async execute() {
263
- const { sql, params } = this.build();
264
- const result = await this.db.execute(sql, params);
265
- return result.rowsAffected;
377
+ const { sql: updateSql, params } = this.buildUpdateClause();
378
+ if (this.returningColumns.length > 0) {
379
+ const returningNames = this.returningColumns.map((col) => this.table._.columns[col]._.name).join(", ");
380
+ const sqlWithReturning = `${updateSql} RETURNING ${returningNames}`;
381
+ return this.db.select(sqlWithReturning, params);
382
+ } else {
383
+ const result = await this.db.execute(updateSql, params);
384
+ return [{ rowsAffected: result.rowsAffected }];
385
+ }
386
+ }
387
+ async returningAll() {
388
+ const allColumns = Object.keys(
389
+ this.table._.columns
390
+ );
391
+ return this.returning(...allColumns).execute();
266
392
  }
267
393
  };
394
+
395
+ // src/builders/insert.ts
396
+ var InsertQueryBuilder = class extends BaseQueryBuilder {
397
+ constructor(db, table) {
398
+ super(db);
399
+ this.table = table;
400
+ this.query = `INSERT INTO ${table._.name}`;
401
+ }
402
+ dataSets = [];
403
+ returningColumns = [];
404
+ onConflictAction = null;
405
+ conflictTarget = [];
406
+ updateSet = {};
407
+ values(data) {
408
+ const dataArray = Array.isArray(data) ? data : [data];
409
+ this.dataSets.push(...dataArray);
410
+ return this;
411
+ }
412
+ returning(...columns) {
413
+ this.returningColumns.push(...columns);
414
+ return this;
415
+ }
416
+ onConflictDoNothing(target) {
417
+ this.onConflictAction = "nothing";
418
+ if (target) {
419
+ this.conflictTarget = Array.isArray(target) ? target : [target];
420
+ }
421
+ return this;
422
+ }
423
+ onConflictDoUpdate(config) {
424
+ this.onConflictAction = "update";
425
+ this.conflictTarget = Array.isArray(config.target) ? config.target : [config.target];
426
+ this.updateSet = config.set;
427
+ return this;
428
+ }
429
+ processDefaultValues(data) {
430
+ const finalData = { ...data };
431
+ for (const [key, column] of Object.entries(this.table._.columns)) {
432
+ const typedKey = key;
433
+ if (finalData[typedKey] === void 0) {
434
+ if (column.options.$defaultFn) {
435
+ finalData[typedKey] = column.options.$defaultFn();
436
+ }
437
+ }
438
+ }
439
+ return finalData;
440
+ }
441
+ buildConflictClause() {
442
+ if (!this.onConflictAction) return "";
443
+ let clause = " ON CONFLICT";
444
+ if (this.conflictTarget.length > 0) {
445
+ const targetNames = this.conflictTarget.map((col) => col._.name).join(", ");
446
+ clause += ` (${targetNames})`;
447
+ }
448
+ if (this.onConflictAction === "nothing") {
449
+ clause += " DO NOTHING";
450
+ } else if (this.onConflictAction === "update") {
451
+ const setEntries = Object.entries(this.updateSet);
452
+ if (setEntries.length > 0) {
453
+ const setClause = setEntries.map(([key]) => `${key} = ?`).join(", ");
454
+ clause += ` DO UPDATE SET ${setClause}`;
455
+ }
456
+ }
457
+ return clause;
458
+ }
459
+ async execute() {
460
+ if (this.dataSets.length === 0) {
461
+ throw new Error("No data provided for insert");
462
+ }
463
+ const processedDataSets = this.dataSets.map(
464
+ (data) => this.processDefaultValues(data)
465
+ );
466
+ const groups = /* @__PURE__ */ new Map();
467
+ for (const dataSet of processedDataSets) {
468
+ const keys = Object.keys(dataSet).sort().join(",");
469
+ if (!groups.has(keys)) {
470
+ groups.set(keys, []);
471
+ }
472
+ groups.get(keys).push(dataSet);
473
+ }
474
+ let results = [];
475
+ let lastInsertId;
476
+ let rowsAffected = 0;
477
+ for (const [_, dataSets] of groups) {
478
+ const columns = Object.keys(dataSets[0]);
479
+ const columnNames = columns.map(
480
+ (key) => this.table._.columns[key]._.name
481
+ );
482
+ const placeholders = `(${columns.map(() => "?").join(", ")})`;
483
+ const valuesSql = dataSets.map(() => placeholders).join(", ");
484
+ const conflictClause = this.buildConflictClause();
485
+ const finalQuery = `${this.query} (${columnNames.join(
486
+ ", "
487
+ )}) VALUES ${valuesSql}${conflictClause}`;
488
+ const params = dataSets.flatMap(
489
+ (data) => columns.map((col) => data[col] ?? null)
490
+ );
491
+ if (this.onConflictAction === "update") {
492
+ const setValues = Object.entries(this.updateSet).map(
493
+ ([, value]) => value
494
+ );
495
+ params.push(...setValues);
496
+ }
497
+ if (this.returningColumns.length > 0) {
498
+ const returningNames = this.returningColumns.map((col) => this.table._.columns[col]._.name).join(", ");
499
+ const queryWithReturning = `${finalQuery} RETURNING ${returningNames}`;
500
+ const rows = await this.db.select(queryWithReturning, params);
501
+ results = results.concat(rows);
502
+ } else {
503
+ const result = await this.db.execute(finalQuery, params);
504
+ lastInsertId = result.lastInsertId;
505
+ rowsAffected += result.rowsAffected;
506
+ }
507
+ }
508
+ if (this.returningColumns.length > 0) {
509
+ return results;
510
+ }
511
+ return [{ lastInsertId, rowsAffected }];
512
+ }
513
+ async returningAll() {
514
+ const allColumns = Object.keys(
515
+ this.table._.columns
516
+ );
517
+ return this.returning(...allColumns).execute();
518
+ }
519
+ };
520
+
521
+ // src/builders/delete.ts
268
522
  var DeleteQueryBuilder = class extends BaseQueryBuilder {
269
523
  constructor(db, table) {
270
524
  super(db);
271
525
  this.table = table;
272
526
  this.query = `DELETE FROM ${table._.name}`;
273
527
  }
528
+ returningColumns = [];
529
+ returning(...columns) {
530
+ this.returningColumns.push(...columns);
531
+ return this;
532
+ }
274
533
  async execute() {
275
- const { sql, params } = this.build();
276
- const result = await this.db.execute(sql, params);
277
- return result.rowsAffected;
534
+ const { sql: sql2, params } = this.build();
535
+ if (this.returningColumns.length > 0) {
536
+ const returningNames = this.returningColumns.map((col) => this.table._.columns[col]._.name).join(", ");
537
+ const sqlWithReturning = `${sql2} RETURNING ${returningNames}`;
538
+ return this.db.select(sqlWithReturning, params);
539
+ } else {
540
+ const result = await this.db.execute(sql2, params);
541
+ return [{ rowsAffected: result.rowsAffected }];
542
+ }
543
+ }
544
+ async returningAll() {
545
+ const allColumns = Object.keys(this.table._.columns);
546
+ return this.returning(...allColumns).execute();
547
+ }
548
+ };
549
+
550
+ // src/builders/with.ts
551
+ var WithQueryBuilder = class {
552
+ constructor(db) {
553
+ this.db = db;
554
+ }
555
+ ctes = [];
556
+ with(alias2, query) {
557
+ this.ctes.push({ alias: alias2, query: query.sql, params: query.params });
558
+ return this;
559
+ }
560
+ select(table, columns) {
561
+ const builder = new SelectQueryBuilder(this.db, table, columns);
562
+ this.applyWithClause(builder);
563
+ return builder;
564
+ }
565
+ insert(table) {
566
+ const builder = new InsertQueryBuilder(this.db, table);
567
+ this.applyWithClause(builder);
568
+ return builder;
569
+ }
570
+ update(table) {
571
+ const builder = new UpdateQueryBuilder(this.db, table);
572
+ this.applyWithClause(builder);
573
+ return builder;
574
+ }
575
+ delete(table) {
576
+ const builder = new DeleteQueryBuilder(this.db, table);
577
+ this.applyWithClause(builder);
578
+ return builder;
579
+ }
580
+ applyWithClause(builder) {
581
+ if (this.ctes.length > 0) {
582
+ const cteSql = this.ctes.map((cte) => `${cte.alias} AS (${cte.query})`).join(", ");
583
+ builder["query"] = `WITH ${cteSql} ${builder["query"]}`;
584
+ builder["params"] = [
585
+ ...this.ctes.flatMap((cte) => cte.params),
586
+ ...builder["params"]
587
+ ];
588
+ }
278
589
  }
279
590
  };
591
+
592
+ // src/orm.ts
593
+ var SQLiteColumn = class _SQLiteColumn {
594
+ constructor(name, type, options = {}, mode) {
595
+ this.type = type;
596
+ this.options = options;
597
+ this._ = {
598
+ name,
599
+ dataType: type,
600
+ mode: mode || "default",
601
+ notNull: options.notNull ?? false,
602
+ hasDefault: options.default !== void 0 || options.$defaultFn !== void 0,
603
+ autoincrement: options.autoincrement ?? false,
604
+ enum: options.enum,
605
+ customType: void 0
606
+ };
607
+ }
608
+ _;
609
+ notNull() {
610
+ return new _SQLiteColumn(this._.name, this.type, { ...this.options, notNull: true }, this._.mode);
611
+ }
612
+ default(value) {
613
+ return new _SQLiteColumn(this._.name, this.type, { ...this.options, default: value }, this._.mode);
614
+ }
615
+ $defaultFn(fn) {
616
+ return new _SQLiteColumn(this._.name, this.type, { ...this.options, $defaultFn: fn }, this._.mode);
617
+ }
618
+ primaryKey() {
619
+ return new _SQLiteColumn(
620
+ this._.name,
621
+ this.type,
622
+ { ...this.options, primaryKey: true, notNull: true },
623
+ this._.mode
624
+ );
625
+ }
626
+ autoincrement() {
627
+ return new _SQLiteColumn(this._.name, this.type, { ...this.options, autoincrement: true }, this._.mode);
628
+ }
629
+ unique() {
630
+ return new _SQLiteColumn(this._.name, this.type, { ...this.options, unique: true }, this._.mode);
631
+ }
632
+ references(ref, column) {
633
+ return new _SQLiteColumn(
634
+ this._.name,
635
+ this.type,
636
+ {
637
+ ...this.options,
638
+ references: {
639
+ table: ref,
640
+ column: ref._.columns[column]
641
+ }
642
+ },
643
+ this._.mode
644
+ );
645
+ }
646
+ $onUpdateFn(fn) {
647
+ return new _SQLiteColumn(this._.name, this.type, { ...this.options, $onUpdateFn: fn }, this._.mode);
648
+ }
649
+ $type() {
650
+ return this;
651
+ }
652
+ as(alias2) {
653
+ return this;
654
+ }
655
+ };
656
+ var Table = class {
657
+ _;
658
+ relations = {};
659
+ constructor(name, columns) {
660
+ this._ = {
661
+ name,
662
+ columns
663
+ };
664
+ }
665
+ };
666
+ var sqliteTable = (tableName, columns) => {
667
+ return new Table(tableName, columns);
668
+ };
669
+ var asc = (column) => ({
670
+ sql: `${column._.name} ASC`,
671
+ params: []
672
+ });
673
+ var desc = (column) => ({
674
+ sql: `${column._.name} DESC`,
675
+ params: []
676
+ });
677
+ var sql = (strings, ...values) => {
678
+ const queryParts = [];
679
+ const params = [];
680
+ strings.forEach((str, i) => {
681
+ queryParts.push(str);
682
+ if (values[i] !== void 0) {
683
+ if (typeof values[i] === "object" && values[i].sql) {
684
+ queryParts.push(values[i].sql);
685
+ params.push(...values[i].params);
686
+ } else {
687
+ queryParts.push("?");
688
+ params.push(values[i]);
689
+ }
690
+ }
691
+ });
692
+ return {
693
+ sql: queryParts.join(""),
694
+ params
695
+ };
696
+ };
280
697
  var TauriORM = class {
281
698
  constructor(db, schema = void 0) {
282
699
  this.db = db;
283
700
  if (schema) {
284
- for (const table of Object.values(schema)) {
285
- this.tables.set(table._.name, table);
701
+ for (const [key, value] of Object.entries(schema)) {
702
+ if (value instanceof Table) {
703
+ this.tables.set(value._.name, value);
704
+ }
286
705
  }
287
706
  }
288
707
  }
289
708
  tables = /* @__PURE__ */ new Map();
290
- async migrate() {
709
+ buildColumnDefinition(col, forAlterTable = false) {
710
+ let sql2 = `${col._.name} ${col.type}`;
711
+ if (col.options.primaryKey && !forAlterTable) {
712
+ sql2 += " PRIMARY KEY";
713
+ if (col._.autoincrement) {
714
+ sql2 += " AUTOINCREMENT";
715
+ }
716
+ }
717
+ if (col._.notNull) sql2 += " NOT NULL";
718
+ if (col.options.unique) sql2 += " UNIQUE";
719
+ if (col.options.default !== void 0) {
720
+ const value = col.options.default;
721
+ sql2 += ` DEFAULT ${typeof value === "string" ? `'${value.replace(/'/g, "''")}'` : value}`;
722
+ }
723
+ if (col.options.references) {
724
+ sql2 += ` REFERENCES ${col.options.references.table._.name}(${col.options.references.column._.name})`;
725
+ }
726
+ return sql2;
727
+ }
728
+ async migrate(options) {
729
+ const dbTables = await this.db.select(
730
+ `SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'`
731
+ );
732
+ const dbTableNames = new Set(dbTables.map((t) => t.name));
733
+ const schemaTableNames = new Set(Array.from(this.tables.keys()));
291
734
  for (const table of this.tables.values()) {
292
- const columnsSql = Object.entries(table._.columns).map(([name, col]) => {
293
- let sql = `${col._.name} ${col.type}`;
294
- if (col.options.primaryKey) sql += " PRIMARY KEY";
295
- if (col._.autoincrement) sql += " AUTOINCREMENT";
296
- if (col._.notNull) sql += " NOT NULL";
297
- if (col.options.unique) sql += " UNIQUE";
298
- if (col.options.default !== void 0) {
299
- const value = col.options.default;
300
- sql += ` DEFAULT ${typeof value === "string" ? `'${value}'` : value}`;
735
+ const tableName = table._.name;
736
+ const tableExists = dbTableNames.has(tableName);
737
+ if (!tableExists) {
738
+ const columnsSql = Object.values(table._.columns).map((col) => this.buildColumnDefinition(col)).join(", ");
739
+ const createSql = `CREATE TABLE ${tableName}
740
+ (
741
+ ${columnsSql}
742
+ )`;
743
+ await this.db.execute(createSql);
744
+ } else {
745
+ const existingTableInfo = await this.db.select(`PRAGMA table_info('${tableName}')`);
746
+ const existingColumnNames = new Set(existingTableInfo.map((c) => c.name));
747
+ const schemaColumnNames = new Set(Object.keys(table._.columns));
748
+ for (const column of Object.values(table._.columns)) {
749
+ if (!existingColumnNames.has(column._.name)) {
750
+ const columnSql = this.buildColumnDefinition(column, true);
751
+ const alterSql = `ALTER TABLE ${tableName}
752
+ ADD COLUMN ${columnSql}`;
753
+ await this.db.execute(alterSql);
754
+ }
755
+ }
756
+ if (options?.performDestructiveActions) {
757
+ for (const colName of existingColumnNames) {
758
+ if (!schemaColumnNames.has(colName)) {
759
+ await this.dropColumn(tableName, colName);
760
+ }
761
+ }
301
762
  }
302
- if (col.options.references) {
303
- sql += ` REFERENCES ${col.options.references.table._.name}(${col.options.references.column._.name})`;
763
+ }
764
+ }
765
+ if (options?.performDestructiveActions) {
766
+ for (const tableName of dbTableNames) {
767
+ if (!schemaTableNames.has(tableName)) {
768
+ await this.dropTable(tableName);
304
769
  }
305
- return sql;
306
- }).join(", ");
307
- const createSql = `CREATE TABLE IF NOT EXISTS ${table._.name} (${columnsSql})`;
308
- await this.db.execute(createSql);
770
+ }
309
771
  }
310
772
  }
311
773
  select(table, columns) {
312
- return new SelectQueryBuilder(this.db, table, columns);
774
+ const internalTable = this.tables.get(table._.name);
775
+ if (!internalTable) {
776
+ console.warn(
777
+ `[Tauri-ORM] Table "${table._.name}" was not passed in the schema to the ORM constructor. Relations will not be available.`
778
+ );
779
+ return new SelectQueryBuilder(this.db, table, columns);
780
+ }
781
+ return new SelectQueryBuilder(this.db, internalTable, columns);
313
782
  }
314
783
  insert(table) {
315
784
  return new InsertQueryBuilder(this.db, table);
@@ -320,6 +789,15 @@ var TauriORM = class {
320
789
  delete(table) {
321
790
  return new DeleteQueryBuilder(this.db, table);
322
791
  }
792
+ $with(alias2) {
793
+ const withBuilder = new WithQueryBuilder(this.db);
794
+ return {
795
+ as: (query) => {
796
+ withBuilder.with(alias2, query);
797
+ return withBuilder;
798
+ }
799
+ };
800
+ }
323
801
  async transaction(callback) {
324
802
  await this.db.execute("BEGIN TRANSACTION");
325
803
  try {
@@ -331,16 +809,31 @@ var TauriORM = class {
331
809
  throw error;
332
810
  }
333
811
  }
812
+ rollback() {
813
+ throw new Error("Transaction rolled back");
814
+ }
334
815
  // --- Schema detection / signature ---
335
816
  async ensureSchemaMeta() {
336
817
  await this.db.execute(
337
- `CREATE TABLE IF NOT EXISTS _schema_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)`
818
+ `CREATE TABLE IF NOT EXISTS _schema_meta
819
+ (
820
+ key
821
+ TEXT
822
+ PRIMARY
823
+ KEY,
824
+ value
825
+ TEXT
826
+ NOT
827
+ NULL
828
+ )`
338
829
  );
339
830
  }
340
831
  async getSchemaMeta(key) {
341
832
  await this.ensureSchemaMeta();
342
833
  const rows = await this.db.select(
343
- `SELECT value FROM _schema_meta WHERE key = ?`,
834
+ `SELECT value
835
+ FROM _schema_meta
836
+ WHERE key = ?`,
344
837
  [key]
345
838
  );
346
839
  return rows?.[0]?.value ?? null;
@@ -348,7 +841,10 @@ var TauriORM = class {
348
841
  async setSchemaMeta(key, value) {
349
842
  await this.ensureSchemaMeta();
350
843
  await this.db.execute(
351
- `INSERT INTO _schema_meta(key, value) VALUES(?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value`,
844
+ `INSERT INTO _schema_meta(key, value)
845
+ VALUES (?, ?) ON CONFLICT(key) DO
846
+ UPDATE
847
+ SET value = excluded.value`,
352
848
  [key, value]
353
849
  );
354
850
  }
@@ -359,7 +855,10 @@ var TauriORM = class {
359
855
  pk: !!col.options.primaryKey,
360
856
  ai: !!col._.autoincrement,
361
857
  nn: !!col._.notNull,
362
- dv: col.options.default && typeof col.options.default === "object" && col.options.default.raw ? { raw: col.options.default.raw } : col.options.default ?? null
858
+ unique: !!col.options.unique,
859
+ dv: col.options.default && typeof col.options.default === "object" && col.options.default.raw ? { raw: col.options.default.raw } : col.options.default ?? null,
860
+ hasDefaultFn: col.options.$defaultFn !== void 0,
861
+ hasOnUpdateFn: col.options.$onUpdateFn !== void 0
363
862
  };
364
863
  }
365
864
  computeModelSignature() {
@@ -382,41 +881,117 @@ var TauriORM = class {
382
881
  const status = await this.isSchemaDirty();
383
882
  if (status.dirty) {
384
883
  await this.migrate();
385
- await this.setSchemaMeta(
386
- "schema_signature",
387
- this.computeModelSignature()
388
- );
884
+ await this.setSchemaMeta("schema_signature", this.computeModelSignature());
389
885
  return true;
390
886
  }
391
887
  return false;
392
888
  }
889
+ async doesTableExist(tableName) {
890
+ const result = await this.db.select(
891
+ `SELECT name FROM sqlite_master WHERE type='table' AND name=?`,
892
+ [tableName]
893
+ );
894
+ return result.length > 0;
895
+ }
896
+ async dropTable(tableName) {
897
+ await this.db.execute(`DROP TABLE IF EXISTS ${tableName}`);
898
+ }
899
+ async doesColumnExist(tableName, columnName) {
900
+ const result = await this.db.select(`PRAGMA table_info('${tableName}')`);
901
+ return result.some((col) => col.name === columnName);
902
+ }
903
+ async renameTable(from, to) {
904
+ await this.db.execute(`ALTER TABLE ${from} RENAME TO ${to}`);
905
+ }
906
+ async dropColumn(tableName, columnName) {
907
+ await this.db.execute(`ALTER TABLE ${tableName} DROP COLUMN ${columnName}`);
908
+ }
909
+ async renameColumn(tableName, from, to) {
910
+ await this.db.execute(`ALTER TABLE ${tableName} RENAME COLUMN ${from} TO ${to}`);
911
+ }
912
+ };
913
+ var Relation = class {
914
+ constructor(foreignTable) {
915
+ this.foreignTable = foreignTable;
916
+ }
917
+ };
918
+ var OneRelation = class extends Relation {
919
+ constructor(foreignTable, config) {
920
+ super(foreignTable);
921
+ this.config = config;
922
+ }
923
+ };
924
+ var ManyRelation = class extends Relation {
925
+ constructor(foreignTable) {
926
+ super(foreignTable);
927
+ }
393
928
  };
394
929
  var relations = (table, relationsCallback) => {
395
- return relationsCallback({
396
- one: (table2, config) => ({
397
- table: table2,
398
- type: "one",
399
- foreignKey: config.fields[0],
400
- localKey: config.references[0]
401
- }),
402
- many: (table2) => ({
403
- table: table2,
404
- type: "many"
405
- })
930
+ const builtRelations = relationsCallback({
931
+ one: (foreignTable, config) => {
932
+ return new OneRelation(foreignTable, config);
933
+ },
934
+ many: (foreignTable) => {
935
+ return new ManyRelation(foreignTable);
936
+ }
406
937
  });
938
+ for (const [name, relation] of Object.entries(builtRelations)) {
939
+ if (relation instanceof OneRelation) {
940
+ table.relations[name] = {
941
+ type: "one",
942
+ foreignTable: relation.foreignTable,
943
+ fields: relation.config?.fields,
944
+ references: relation.config?.references
945
+ };
946
+ } else if (relation instanceof ManyRelation) {
947
+ table.relations[name] = {
948
+ type: "many",
949
+ foreignTable: relation.foreignTable
950
+ };
951
+ }
952
+ }
953
+ return builtRelations;
407
954
  };
955
+ var getTableColumns = (table) => {
956
+ return table._.columns;
957
+ };
958
+ var alias = (table, alias2) => {
959
+ return table;
960
+ };
961
+
962
+ // src/column-helpers.ts
963
+ var text = (name, config) => new SQLiteColumn(name, "TEXT", config, config?.mode);
964
+ var integer = (name, config) => new SQLiteColumn(name, "INTEGER", {}, config?.mode || "default");
965
+ var real = (name) => new SQLiteColumn(name, "REAL");
966
+ var blob = (name, config) => new SQLiteColumn(name, "BLOB", {}, config?.mode);
967
+ var boolean = (name) => new SQLiteColumn(name, "BOOLEAN");
968
+ var numeric = (name, config) => new SQLiteColumn(name, "NUMERIC", {}, config?.mode);
969
+ var enumType = (name, values) => text(name, { enum: values });
408
970
  export {
971
+ BaseQueryBuilder,
409
972
  DeleteQueryBuilder,
410
973
  InsertQueryBuilder,
974
+ ManyRelation,
975
+ OneRelation,
976
+ Relation,
411
977
  SQLiteColumn,
412
978
  SelectQueryBuilder,
413
979
  Table,
414
980
  TauriORM,
415
981
  UpdateQueryBuilder,
982
+ WithQueryBuilder,
983
+ alias,
416
984
  and,
985
+ asc,
986
+ avg,
417
987
  blob,
418
988
  boolean,
989
+ count,
990
+ countDistinct,
991
+ desc,
992
+ enumType,
419
993
  eq,
994
+ getTableColumns,
420
995
  gt,
421
996
  gte,
422
997
  inArray,
@@ -426,9 +1001,15 @@ export {
426
1001
  like,
427
1002
  lt,
428
1003
  lte,
1004
+ max,
1005
+ min,
1006
+ not,
1007
+ numeric,
429
1008
  or,
430
1009
  real,
431
1010
  relations,
1011
+ sql,
432
1012
  sqliteTable,
1013
+ sum,
433
1014
  text
434
1015
  };