@type32/tauri-sqlite-orm 0.1.20 → 0.2.1

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.js CHANGED
@@ -21,47 +21,77 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  BaseQueryBuilder: () => BaseQueryBuilder,
24
+ ColumnNotFoundError: () => ColumnNotFoundError,
24
25
  DeleteQueryBuilder: () => DeleteQueryBuilder,
25
26
  InsertQueryBuilder: () => InsertQueryBuilder,
27
+ InsertValidationError: () => InsertValidationError,
26
28
  ManyRelation: () => ManyRelation,
29
+ ManyToManyRelation: () => ManyToManyRelation,
30
+ MigrationError: () => MigrationError,
31
+ MissingWhereClauseError: () => MissingWhereClauseError,
27
32
  OneRelation: () => OneRelation,
33
+ QueryBuilderError: () => QueryBuilderError,
28
34
  Relation: () => Relation,
35
+ RelationError: () => RelationError,
29
36
  SQLiteColumn: () => SQLiteColumn,
30
37
  SelectQueryBuilder: () => SelectQueryBuilder,
31
38
  Table: () => Table,
39
+ TableNotFoundError: () => TableNotFoundError,
32
40
  TauriORM: () => TauriORM,
41
+ TauriORMError: () => TauriORMError,
33
42
  UpdateQueryBuilder: () => UpdateQueryBuilder,
43
+ UpdateValidationError: () => UpdateValidationError,
44
+ ValidationError: () => ValidationError,
34
45
  WithQueryBuilder: () => WithQueryBuilder,
35
46
  alias: () => alias,
36
47
  and: () => and,
48
+ as: () => as,
37
49
  asc: () => asc,
38
50
  avg: () => avg,
51
+ between: () => between,
39
52
  blob: () => blob,
40
53
  boolean: () => boolean,
54
+ contains: () => contains,
41
55
  count: () => count,
42
56
  countDistinct: () => countDistinct,
43
57
  desc: () => desc,
58
+ endsWith: () => endsWith,
44
59
  enumType: () => enumType,
45
60
  eq: () => eq,
61
+ eqSubquery: () => eqSubquery,
62
+ exists: () => exists,
46
63
  getTableColumns: () => getTableColumns,
64
+ groupConcat: () => groupConcat,
47
65
  gt: () => gt,
66
+ gtSubquery: () => gtSubquery,
48
67
  gte: () => gte,
68
+ gteSubquery: () => gteSubquery,
69
+ ilike: () => ilike,
49
70
  inArray: () => inArray,
50
71
  integer: () => integer,
51
72
  isNotNull: () => isNotNull,
52
73
  isNull: () => isNull,
53
74
  like: () => like,
54
75
  lt: () => lt,
76
+ ltSubquery: () => ltSubquery,
55
77
  lte: () => lte,
78
+ lteSubquery: () => lteSubquery,
56
79
  max: () => max,
57
80
  min: () => min,
81
+ ne: () => ne,
82
+ neSubquery: () => neSubquery,
58
83
  not: () => not,
84
+ notExists: () => notExists,
85
+ notIn: () => notIn,
59
86
  numeric: () => numeric,
60
87
  or: () => or,
61
88
  real: () => real,
62
89
  relations: () => relations,
90
+ scalarSubquery: () => scalarSubquery,
63
91
  sql: () => sql,
64
92
  sqliteTable: () => sqliteTable,
93
+ startsWith: () => startsWith,
94
+ subquery: () => subquery,
65
95
  sum: () => sum,
66
96
  text: () => text
67
97
  });
@@ -102,6 +132,9 @@ var BaseQueryBuilder = class {
102
132
  params: this.params
103
133
  };
104
134
  }
135
+ toSQL() {
136
+ return this.build();
137
+ }
105
138
  };
106
139
 
107
140
  // src/operators.ts
@@ -112,6 +145,13 @@ var eq = (column, value, tableAlias) => {
112
145
  params: [value]
113
146
  };
114
147
  };
148
+ var ne = (column, value, tableAlias) => {
149
+ const columnName = tableAlias ? `${tableAlias}.${column._.name}` : column._.name;
150
+ return {
151
+ sql: `${columnName} != ?`,
152
+ params: [value]
153
+ };
154
+ };
115
155
  var and = (...conditions) => ({
116
156
  sql: conditions.map((c) => `(${c.sql})`).join(" AND "),
117
157
  params: conditions.flatMap((c) => c.params)
@@ -144,6 +184,22 @@ var like = (column, pattern) => ({
144
184
  sql: `${column._.name} LIKE ?`,
145
185
  params: [pattern]
146
186
  });
187
+ var ilike = (column, pattern) => ({
188
+ sql: `${column._.name} LIKE ? COLLATE NOCASE`,
189
+ params: [pattern]
190
+ });
191
+ var startsWith = (column, value) => ({
192
+ sql: `${column._.name} LIKE ?`,
193
+ params: [`${value}%`]
194
+ });
195
+ var endsWith = (column, value) => ({
196
+ sql: `${column._.name} LIKE ?`,
197
+ params: [`%${value}`]
198
+ });
199
+ var contains = (column, value) => ({
200
+ sql: `${column._.name} LIKE ?`,
201
+ params: [`%${value}%`]
202
+ });
147
203
  var isNull = (column) => ({
148
204
  sql: `${column._.name} IS NULL`,
149
205
  params: []
@@ -152,33 +208,65 @@ var isNotNull = (column) => ({
152
208
  sql: `${column._.name} IS NOT NULL`,
153
209
  params: []
154
210
  });
155
- var inArray = (column, values) => ({
156
- sql: `${column._.name} IN (${values.map(() => "?").join(",")})`,
157
- params: values
211
+ var exists = (subquery2) => ({
212
+ sql: `EXISTS (${subquery2.sql})`,
213
+ params: subquery2.params
158
214
  });
159
- var count = (column) => ({
160
- sql: `COUNT(${column ? column._.name : "*"})`,
161
- params: []
215
+ var notExists = (subquery2) => ({
216
+ sql: `NOT EXISTS (${subquery2.sql})`,
217
+ params: subquery2.params
162
218
  });
163
- var countDistinct = (column) => ({
164
- sql: `COUNT(DISTINCT ${column._.name})`,
165
- params: []
219
+ var eqSubquery = (column, subquery2) => ({
220
+ sql: `${column._.name} = ${subquery2.sql}`,
221
+ params: subquery2.params
166
222
  });
167
- var sum = (column) => ({
168
- sql: `SUM(${column._.name})`,
169
- params: []
223
+ var neSubquery = (column, subquery2) => ({
224
+ sql: `${column._.name} != ${subquery2.sql}`,
225
+ params: subquery2.params
170
226
  });
171
- var avg = (column) => ({
172
- sql: `AVG(${column._.name})`,
173
- params: []
227
+ var gtSubquery = (column, subquery2) => ({
228
+ sql: `${column._.name} > ${subquery2.sql}`,
229
+ params: subquery2.params
174
230
  });
175
- var max = (column) => ({
176
- sql: `MAX(${column._.name})`,
177
- params: []
231
+ var gteSubquery = (column, subquery2) => ({
232
+ sql: `${column._.name} >= ${subquery2.sql}`,
233
+ params: subquery2.params
178
234
  });
179
- var min = (column) => ({
180
- sql: `MIN(${column._.name})`,
181
- params: []
235
+ var ltSubquery = (column, subquery2) => ({
236
+ sql: `${column._.name} < ${subquery2.sql}`,
237
+ params: subquery2.params
238
+ });
239
+ var lteSubquery = (column, subquery2) => ({
240
+ sql: `${column._.name} <= ${subquery2.sql}`,
241
+ params: subquery2.params
242
+ });
243
+ var inArray = (column, values) => {
244
+ if ("_isSubquery" in values && values._isSubquery) {
245
+ return {
246
+ sql: `${column._.name} IN ${values.sql}`,
247
+ params: values.params
248
+ };
249
+ }
250
+ return {
251
+ sql: `${column._.name} IN (${values.map(() => "?").join(",")})`,
252
+ params: values
253
+ };
254
+ };
255
+ var notIn = (column, values) => {
256
+ if ("_isSubquery" in values && values._isSubquery) {
257
+ return {
258
+ sql: `${column._.name} NOT IN ${values.sql}`,
259
+ params: values.params
260
+ };
261
+ }
262
+ return {
263
+ sql: `${column._.name} NOT IN (${values.map(() => "?").join(",")})`,
264
+ params: values
265
+ };
266
+ };
267
+ var between = (column, min2, max2) => ({
268
+ sql: `${column._.name} BETWEEN ? AND ?`,
269
+ params: [min2, max2]
182
270
  });
183
271
 
184
272
  // src/builders/select.ts
@@ -244,42 +332,30 @@ var SelectQueryBuilder = class extends BaseQueryBuilder {
244
332
  sql2 += ` ${join.type} JOIN ${join.table._.name} ${join.alias} ON ${join.condition.sql}`;
245
333
  params.push(...join.condition.params);
246
334
  }
247
- for (const [relationName, include] of Object.entries(this.includeRelations)) {
248
- if (!include) continue;
249
- const relation = this.table.relations[relationName];
250
- if (!relation) {
251
- console.warn(
252
- `[Tauri-ORM] Relation "${relationName}" not found on table "${this.table._.name}". Skipping include.`
253
- );
254
- continue;
335
+ const processRelations = (parentTable, parentAlias, relations2, depth = 0) => {
336
+ if (depth > 10) {
337
+ console.warn("[Tauri-ORM] Maximum relation depth (10) exceeded. Skipping deeper relations.");
338
+ return;
255
339
  }
256
- const foreignTable = relation.foreignTable;
257
- const foreignAlias = `${this.selectedTableAlias}_${relationName}`;
258
- const aliasedColumns = Object.values(foreignTable._.columns).map(
259
- (col) => `${foreignAlias}.${col._.name} AS "${foreignAlias}.${col._.name}"`
260
- );
261
- this.selectedColumns.push(...aliasedColumns);
262
- if (relation.type === "one" && relation.fields && relation.references) {
263
- const conditions = relation.fields.map((field, i) => {
264
- const localColumn = `${this.selectedTableAlias}.${field._.name}`;
265
- const foreignColumn = `${foreignAlias}.${relation.references[i]._.name}`;
266
- return {
267
- sql: `${localColumn} = ${foreignColumn}`,
268
- params: []
269
- };
270
- });
271
- const condition = conditions.length > 1 ? and(...conditions) : conditions[0];
272
- sql2 += ` LEFT JOIN ${foreignTable._.name} ${foreignAlias} ON ${condition.sql}`;
273
- params.push(...condition.params);
274
- } else if (relation.type === "many") {
275
- const refRelation = Object.entries(foreignTable.relations).find(
276
- ([_, r]) => r.foreignTable === this.table
340
+ for (const [relationName, include] of Object.entries(relations2)) {
341
+ if (!include) continue;
342
+ const relation = parentTable.relations[relationName];
343
+ if (!relation) {
344
+ console.warn(
345
+ `[Tauri-ORM] Relation "${relationName}" not found on table "${parentTable._.name}". Skipping include.`
346
+ );
347
+ continue;
348
+ }
349
+ const foreignTable = relation.foreignTable;
350
+ const foreignAlias = `${parentAlias}_${relationName}`;
351
+ const aliasedColumns = Object.values(foreignTable._.columns).map(
352
+ (col) => `${foreignAlias}.${col._.name} AS "${foreignAlias}.${col._.name}"`
277
353
  );
278
- if (refRelation && refRelation[1].fields && refRelation[1].references) {
279
- const [_, relationConfig] = refRelation;
280
- const conditions = relationConfig.fields.map((field, i) => {
281
- const localColumn = `${foreignAlias}.${field._.name}`;
282
- const foreignColumn = `${this.selectedTableAlias}.${relationConfig.references[i]._.name}`;
354
+ this.selectedColumns.push(...aliasedColumns);
355
+ if (relation.type === "one" && relation.fields && relation.references) {
356
+ const conditions = relation.fields.map((field, i) => {
357
+ const localColumn = `${parentAlias}.${field._.name}`;
358
+ const foreignColumn = `${foreignAlias}.${relation.references[i]._.name}`;
283
359
  return {
284
360
  sql: `${localColumn} = ${foreignColumn}`,
285
361
  params: []
@@ -288,9 +364,64 @@ var SelectQueryBuilder = class extends BaseQueryBuilder {
288
364
  const condition = conditions.length > 1 ? and(...conditions) : conditions[0];
289
365
  sql2 += ` LEFT JOIN ${foreignTable._.name} ${foreignAlias} ON ${condition.sql}`;
290
366
  params.push(...condition.params);
367
+ } else if (relation.type === "many") {
368
+ const refRelation = Object.entries(foreignTable.relations).find(
369
+ ([_, r]) => r.foreignTable === parentTable
370
+ );
371
+ if (refRelation && refRelation[1].fields && refRelation[1].references) {
372
+ const [_, relationConfig] = refRelation;
373
+ const conditions = relationConfig.fields.map((field, i) => {
374
+ const localColumn = `${foreignAlias}.${field._.name}`;
375
+ const foreignColumn = `${parentAlias}.${relationConfig.references[i]._.name}`;
376
+ return {
377
+ sql: `${localColumn} = ${foreignColumn}`,
378
+ params: []
379
+ };
380
+ });
381
+ const condition = conditions.length > 1 ? and(...conditions) : conditions[0];
382
+ sql2 += ` LEFT JOIN ${foreignTable._.name} ${foreignAlias} ON ${condition.sql}`;
383
+ params.push(...condition.params);
384
+ }
385
+ } else if (relation.type === "manyToMany" && relation.junctionTable && relation.junctionFields && relation.junctionReferences) {
386
+ const junctionTable = relation.junctionTable;
387
+ const junctionAlias = `${foreignAlias}_junction`;
388
+ const parentTablePks = Object.values(parentTable._.columns).filter((c) => c.options.primaryKey).map((c) => c._.name);
389
+ if (parentTablePks.length > 0 && relation.junctionFields.length > 0) {
390
+ const junctionConditions = relation.junctionFields.map((field, i) => {
391
+ const parentPk = parentTablePks[i] || parentTablePks[0];
392
+ const localColumn = `${parentAlias}.${parentPk}`;
393
+ const junctionColumn = `${junctionAlias}.${field._.name}`;
394
+ return {
395
+ sql: `${localColumn} = ${junctionColumn}`,
396
+ params: []
397
+ };
398
+ });
399
+ const junctionCondition = junctionConditions.length > 1 ? and(...junctionConditions) : junctionConditions[0];
400
+ sql2 += ` LEFT JOIN ${junctionTable._.name} ${junctionAlias} ON ${junctionCondition.sql}`;
401
+ params.push(...junctionCondition.params);
402
+ const foreignTablePks = Object.values(foreignTable._.columns).filter((c) => c.options.primaryKey).map((c) => c._.name);
403
+ if (foreignTablePks.length > 0 && relation.junctionReferences.length > 0) {
404
+ const foreignConditions = relation.junctionReferences.map((field, i) => {
405
+ const foreignPk = foreignTablePks[i] || foreignTablePks[0];
406
+ const junctionColumn = `${junctionAlias}.${field._.name}`;
407
+ const foreignColumn = `${foreignAlias}.${foreignPk}`;
408
+ return {
409
+ sql: `${junctionColumn} = ${foreignColumn}`,
410
+ params: []
411
+ };
412
+ });
413
+ const foreignCondition = foreignConditions.length > 1 ? and(...foreignConditions) : foreignConditions[0];
414
+ sql2 += ` LEFT JOIN ${foreignTable._.name} ${foreignAlias} ON ${foreignCondition.sql}`;
415
+ params.push(...foreignCondition.params);
416
+ }
417
+ }
418
+ }
419
+ if (typeof include === "object" && include.with) {
420
+ processRelations(foreignTable, foreignAlias, include.with, depth + 1);
291
421
  }
292
422
  }
293
- }
423
+ };
424
+ processRelations(this.table, this.selectedTableAlias, this.includeRelations, 0);
294
425
  return { sql: sql2, params };
295
426
  }
296
427
  // Enhanced execute method that handles relation data mapping
@@ -301,7 +432,6 @@ var SelectQueryBuilder = class extends BaseQueryBuilder {
301
432
  this.query += joinSql;
302
433
  this.params.push(...joinParams);
303
434
  const { sql: sql2, params } = this.build();
304
- console.log("Executing SQL:", sql2, "with params:", params);
305
435
  const rawResults = await this.db.select(sql2, params);
306
436
  const hasIncludes = Object.values(this.includeRelations).some((i) => i);
307
437
  if (hasIncludes) {
@@ -331,6 +461,36 @@ var SelectQueryBuilder = class extends BaseQueryBuilder {
331
461
  return rawResults;
332
462
  }
333
463
  const groupedResults = /* @__PURE__ */ new Map();
464
+ const parseRelationPath = (tableAlias, baseAlias) => {
465
+ if (!tableAlias.startsWith(baseAlias + "_")) {
466
+ return [];
467
+ }
468
+ const path = tableAlias.substring(baseAlias.length + 1);
469
+ return path.split("_");
470
+ };
471
+ const setNestedValue = (obj, path, value, columnName) => {
472
+ let current = obj;
473
+ for (let i = 0; i < path.length; i++) {
474
+ const key = path[i];
475
+ if (i === path.length - 1) {
476
+ if (!current[key]) current[key] = {};
477
+ current[key][columnName] = value;
478
+ } else {
479
+ if (!current[key]) current[key] = {};
480
+ current = current[key];
481
+ }
482
+ }
483
+ };
484
+ const getNestedRelation = (table, path) => {
485
+ let currentTable = table;
486
+ let currentRelation = null;
487
+ for (const relationName of path) {
488
+ currentRelation = currentTable.relations[relationName];
489
+ if (!currentRelation) return null;
490
+ currentTable = currentRelation.foreignTable;
491
+ }
492
+ return currentRelation;
493
+ };
334
494
  for (const row of rawResults) {
335
495
  const mainTableKey = mainTablePks.map((pk) => row[`${this.selectedTableAlias}.${pk}`] ?? row[pk]).join("_");
336
496
  if (!groupedResults.has(mainTableKey)) {
@@ -344,11 +504,9 @@ var SelectQueryBuilder = class extends BaseQueryBuilder {
344
504
  if (tableAlias === this.selectedTableAlias) {
345
505
  result[columnName] = value;
346
506
  } else {
347
- const parts = tableAlias.split("_");
348
- if (parts.length >= 2 && parts[0] === this.selectedTableAlias) {
349
- const relationName = parts.slice(1).join("_");
350
- if (!relations2[relationName]) relations2[relationName] = {};
351
- relations2[relationName][columnName] = value;
507
+ const relationPath = parseRelationPath(tableAlias, this.selectedTableAlias);
508
+ if (relationPath.length > 0) {
509
+ setNestedValue(relations2, relationPath, value, columnName);
352
510
  } else {
353
511
  if (!result[tableAlias]) result[tableAlias] = {};
354
512
  result[tableAlias][columnName] = value;
@@ -358,24 +516,72 @@ var SelectQueryBuilder = class extends BaseQueryBuilder {
358
516
  result[key] = value;
359
517
  }
360
518
  }
361
- for (const [relName, relData] of Object.entries(relations2)) {
362
- const relationConfig = this.table.relations[relName];
363
- if (!relationConfig) continue;
364
- const hasData = Object.values(relData).some(
365
- (v) => v !== null && v !== void 0 && v !== ""
366
- );
367
- if (!hasData) continue;
368
- if (relationConfig.type === "many") {
369
- if (!result[relName]) result[relName] = [];
370
- const relatedPks = Object.values(relationConfig.foreignTable._.columns).filter((c) => c.options.primaryKey).map((c) => c._.name);
371
- const relDataKey = relatedPks.map((pk) => relData[pk]).join("_");
372
- if (relatedPks.length === 0 || !result[relName].some((r) => relatedPks.map((pk) => r[pk]).join("_") === relDataKey)) {
373
- result[relName].push(relData);
519
+ const attachRelations = (target, relationsData, table, pathPrefix = []) => {
520
+ for (const [relName, relData] of Object.entries(relationsData)) {
521
+ const currentPath = [...pathPrefix, relName];
522
+ const relationConfig = getNestedRelation(table, currentPath);
523
+ if (!relationConfig) continue;
524
+ const hasDirectData = typeof relData === "object" && relData !== null && Object.entries(relData).some(([k, v]) => {
525
+ return typeof v !== "object" && v !== null && v !== void 0 && v !== "";
526
+ });
527
+ if (!hasDirectData && typeof relData === "object" && relData !== null) {
528
+ const hasNestedData = Object.values(relData).some(
529
+ (v) => typeof v === "object" && v !== null && Object.keys(v).length > 0
530
+ );
531
+ if (!hasNestedData) continue;
532
+ }
533
+ if (relationConfig.type === "many" || relationConfig.type === "manyToMany") {
534
+ if (!target[relName]) target[relName] = [];
535
+ const directData = {};
536
+ const nestedData = {};
537
+ if (typeof relData === "object" && relData !== null) {
538
+ for (const [k, v] of Object.entries(relData)) {
539
+ if (typeof v === "object" && v !== null) {
540
+ nestedData[k] = v;
541
+ } else {
542
+ directData[k] = v;
543
+ }
544
+ }
545
+ }
546
+ const hasData = Object.values(directData).some(
547
+ (v) => v !== null && v !== void 0 && v !== ""
548
+ );
549
+ if (hasData) {
550
+ const relatedPks = Object.values(relationConfig.foreignTable._.columns).filter((c) => c.options.primaryKey).map((c) => c._.name);
551
+ const relDataKey = relatedPks.map((pk) => directData[pk]).join("_");
552
+ if (relatedPks.length === 0 || !target[relName].some((r) => relatedPks.map((pk) => r[pk]).join("_") === relDataKey)) {
553
+ const newItem = { ...directData };
554
+ if (Object.keys(nestedData).length > 0) {
555
+ attachRelations(newItem, nestedData, relationConfig.foreignTable, []);
556
+ }
557
+ target[relName].push(newItem);
558
+ }
559
+ }
560
+ } else {
561
+ const directData = {};
562
+ const nestedData = {};
563
+ if (typeof relData === "object" && relData !== null) {
564
+ for (const [k, v] of Object.entries(relData)) {
565
+ if (typeof v === "object" && v !== null) {
566
+ nestedData[k] = v;
567
+ } else {
568
+ directData[k] = v;
569
+ }
570
+ }
571
+ }
572
+ const hasData = Object.values(directData).some(
573
+ (v) => v !== null && v !== void 0 && v !== ""
574
+ );
575
+ if (hasData || Object.keys(nestedData).length > 0) {
576
+ target[relName] = { ...directData };
577
+ if (Object.keys(nestedData).length > 0) {
578
+ attachRelations(target[relName], nestedData, relationConfig.foreignTable, []);
579
+ }
580
+ }
374
581
  }
375
- } else {
376
- result[relName] = relData;
377
582
  }
378
- }
583
+ };
584
+ attachRelations(result, relations2, this.table);
379
585
  }
380
586
  return Array.from(groupedResults.values());
381
587
  }
@@ -388,6 +594,82 @@ var SelectQueryBuilder = class extends BaseQueryBuilder {
388
594
  const result = await this.execute();
389
595
  return result[0];
390
596
  }
597
+ toSQL() {
598
+ const { sql: joinSql, params: joinParams } = this.buildJoins();
599
+ const distinct = this.isDistinct ? "DISTINCT " : "";
600
+ const finalQuery = `SELECT ${distinct}${this.selectedColumns.join(", ")} ${this.query}${joinSql}`;
601
+ return {
602
+ sql: finalQuery,
603
+ params: [...this.params, ...joinParams]
604
+ };
605
+ }
606
+ };
607
+
608
+ // src/errors.ts
609
+ var TauriORMError = class extends Error {
610
+ constructor(message) {
611
+ super(message);
612
+ this.name = "TauriORMError";
613
+ if (Error.captureStackTrace) {
614
+ Error.captureStackTrace(this, this.constructor);
615
+ }
616
+ }
617
+ };
618
+ var QueryBuilderError = class extends TauriORMError {
619
+ constructor(message) {
620
+ super(message);
621
+ this.name = "QueryBuilderError";
622
+ }
623
+ };
624
+ var MissingWhereClauseError = class extends QueryBuilderError {
625
+ constructor(operation, tableName) {
626
+ super(
627
+ `${operation} operation on table "${tableName}" requires a WHERE clause to prevent accidental data loss. Use .where() to specify conditions, or use .allowGlobalOperation() to explicitly allow operations without WHERE.`
628
+ );
629
+ this.name = "MissingWhereClauseError";
630
+ }
631
+ };
632
+ var ValidationError = class extends TauriORMError {
633
+ constructor(message) {
634
+ super(message);
635
+ this.name = "ValidationError";
636
+ }
637
+ };
638
+ var InsertValidationError = class extends ValidationError {
639
+ constructor(message) {
640
+ super(message);
641
+ this.name = "InsertValidationError";
642
+ }
643
+ };
644
+ var UpdateValidationError = class extends ValidationError {
645
+ constructor(message) {
646
+ super(message);
647
+ this.name = "UpdateValidationError";
648
+ }
649
+ };
650
+ var MigrationError = class extends TauriORMError {
651
+ constructor(message) {
652
+ super(message);
653
+ this.name = "MigrationError";
654
+ }
655
+ };
656
+ var RelationError = class extends TauriORMError {
657
+ constructor(message) {
658
+ super(message);
659
+ this.name = "RelationError";
660
+ }
661
+ };
662
+ var ColumnNotFoundError = class extends TauriORMError {
663
+ constructor(columnName, tableName) {
664
+ super(`Column "${columnName}" does not exist on table "${tableName}"`);
665
+ this.name = "ColumnNotFoundError";
666
+ }
667
+ };
668
+ var TableNotFoundError = class extends TauriORMError {
669
+ constructor(tableName) {
670
+ super(`Table "${tableName}" not found in schema`);
671
+ this.name = "TableNotFoundError";
672
+ }
391
673
  };
392
674
 
393
675
  // src/builders/update.ts
@@ -399,10 +681,37 @@ var UpdateQueryBuilder = class extends BaseQueryBuilder {
399
681
  }
400
682
  updateData = {};
401
683
  returningColumns = [];
684
+ hasWhereClause = false;
685
+ allowGlobal = false;
686
+ incrementDecrementOps = [];
402
687
  set(data) {
403
688
  this.updateData = { ...this.updateData, ...data };
404
689
  return this;
405
690
  }
691
+ where(condition) {
692
+ this.hasWhereClause = true;
693
+ return super.where(condition);
694
+ }
695
+ increment(column, value = 1) {
696
+ const col = this.table._.columns[column];
697
+ if (!col) {
698
+ throw new ColumnNotFoundError(String(column), this.table._.name);
699
+ }
700
+ this.incrementDecrementOps.push({ column: col._.name, op: "increment", value });
701
+ return this;
702
+ }
703
+ decrement(column, value = 1) {
704
+ const col = this.table._.columns[column];
705
+ if (!col) {
706
+ throw new ColumnNotFoundError(String(column), this.table._.name);
707
+ }
708
+ this.incrementDecrementOps.push({ column: col._.name, op: "decrement", value });
709
+ return this;
710
+ }
711
+ allowGlobalOperation() {
712
+ this.allowGlobal = true;
713
+ return this;
714
+ }
406
715
  returning(...columns) {
407
716
  this.returningColumns.push(...columns);
408
717
  return this;
@@ -425,24 +734,37 @@ var UpdateQueryBuilder = class extends BaseQueryBuilder {
425
734
  whereClause = baseQuery.substring(whereIndex);
426
735
  }
427
736
  const entries = Object.entries(finalUpdateData);
428
- if (entries.length === 0) {
429
- throw new Error("Cannot execute an update query without a .set() call.");
737
+ const hasSetData = entries.length > 0;
738
+ const hasIncrementDecrement = this.incrementDecrementOps.length > 0;
739
+ if (!hasSetData && !hasIncrementDecrement) {
740
+ throw new UpdateValidationError("Cannot execute an update query without a .set(), .increment(), or .decrement() call.");
430
741
  }
431
- const setClause = entries.map(([key]) => {
432
- const column = this.table._.columns[key];
433
- if (!column) {
434
- throw new Error(
435
- `Column ${key} does not exist on table ${this.table._.name}`
436
- );
742
+ const setClauses = [];
743
+ const setParams = [];
744
+ if (hasSetData) {
745
+ for (const [key, value] of entries) {
746
+ const column = this.table._.columns[key];
747
+ if (!column) {
748
+ throw new ColumnNotFoundError(key, this.table._.name);
749
+ }
750
+ setClauses.push(`${column._.name} = ?`);
751
+ setParams.push(value);
437
752
  }
438
- return `${column._.name} = ?`;
439
- }).join(", ");
440
- const setParams = entries.map(([, value]) => value);
753
+ }
754
+ for (const op of this.incrementDecrementOps) {
755
+ const sign = op.op === "increment" ? "+" : "-";
756
+ setClauses.push(`${op.column} = ${op.column} ${sign} ?`);
757
+ setParams.push(op.value);
758
+ }
759
+ const setClause = setClauses.join(", ");
441
760
  const sql2 = `${tablePart} SET ${setClause}${whereClause}`;
442
761
  const params = [...setParams, ...whereParams];
443
762
  return { sql: sql2, params };
444
763
  }
445
764
  async execute() {
765
+ if (!this.hasWhereClause && !this.allowGlobal) {
766
+ throw new MissingWhereClauseError("UPDATE", this.table._.name);
767
+ }
446
768
  const { sql: updateSql, params } = this.buildUpdateClause();
447
769
  if (this.returningColumns.length > 0) {
448
770
  const returningNames = this.returningColumns.map((col) => this.table._.columns[col]._.name).join(", ");
@@ -459,6 +781,17 @@ var UpdateQueryBuilder = class extends BaseQueryBuilder {
459
781
  );
460
782
  return this.returning(...allColumns).execute();
461
783
  }
784
+ toSQL() {
785
+ const { sql: updateSql, params } = this.buildUpdateClause();
786
+ if (this.returningColumns.length > 0) {
787
+ const returningNames = this.returningColumns.map((col) => this.table._.columns[col]._.name).join(", ");
788
+ return {
789
+ sql: `${updateSql} RETURNING ${returningNames}`,
790
+ params
791
+ };
792
+ }
793
+ return { sql: updateSql, params };
794
+ }
462
795
  };
463
796
 
464
797
  // src/builders/insert.ts
@@ -527,7 +860,7 @@ var InsertQueryBuilder = class extends BaseQueryBuilder {
527
860
  }
528
861
  async execute() {
529
862
  if (this.dataSets.length === 0) {
530
- throw new Error("No data provided for insert");
863
+ throw new InsertValidationError("No data provided for insert. Use .values() to provide data.");
531
864
  }
532
865
  const processedDataSets = this.dataSets.map(
533
866
  (data) => this.processDefaultValues(data)
@@ -585,6 +918,42 @@ var InsertQueryBuilder = class extends BaseQueryBuilder {
585
918
  );
586
919
  return this.returning(...allColumns).execute();
587
920
  }
921
+ toSQL() {
922
+ if (this.dataSets.length === 0) {
923
+ throw new InsertValidationError("No data provided for insert. Use .values() to provide data.");
924
+ }
925
+ const processedDataSets = this.dataSets.map(
926
+ (data) => this.processDefaultValues(data)
927
+ );
928
+ const dataSet = processedDataSets[0];
929
+ const columns = Object.keys(dataSet);
930
+ const columnNames = columns.map(
931
+ (key) => this.table._.columns[key]._.name
932
+ );
933
+ const placeholders = `(${columns.map(() => "?").join(", ")})`;
934
+ const valuesSql = processedDataSets.map(() => placeholders).join(", ");
935
+ const conflictClause = this.buildConflictClause();
936
+ const finalQuery = `${this.query} (${columnNames.join(
937
+ ", "
938
+ )}) VALUES ${valuesSql}${conflictClause}`;
939
+ const params = processedDataSets.flatMap(
940
+ (data) => columns.map((col) => data[col] ?? null)
941
+ );
942
+ if (this.onConflictAction === "update") {
943
+ const setValues = Object.entries(this.updateSet).map(
944
+ ([, value]) => value
945
+ );
946
+ params.push(...setValues);
947
+ }
948
+ if (this.returningColumns.length > 0) {
949
+ const returningNames = this.returningColumns.map((col) => this.table._.columns[col]._.name).join(", ");
950
+ return {
951
+ sql: `${finalQuery} RETURNING ${returningNames}`,
952
+ params
953
+ };
954
+ }
955
+ return { sql: finalQuery, params };
956
+ }
588
957
  };
589
958
 
590
959
  // src/builders/delete.ts
@@ -595,11 +964,24 @@ var DeleteQueryBuilder = class extends BaseQueryBuilder {
595
964
  this.query = `DELETE FROM ${table._.name}`;
596
965
  }
597
966
  returningColumns = [];
967
+ hasWhereClause = false;
968
+ allowGlobal = false;
969
+ where(condition) {
970
+ this.hasWhereClause = true;
971
+ return super.where(condition);
972
+ }
973
+ allowGlobalOperation() {
974
+ this.allowGlobal = true;
975
+ return this;
976
+ }
598
977
  returning(...columns) {
599
978
  this.returningColumns.push(...columns);
600
979
  return this;
601
980
  }
602
981
  async execute() {
982
+ if (!this.hasWhereClause && !this.allowGlobal) {
983
+ throw new MissingWhereClauseError("DELETE", this.table._.name);
984
+ }
603
985
  const { sql: sql2, params } = this.build();
604
986
  if (this.returningColumns.length > 0) {
605
987
  const returningNames = this.returningColumns.map((col) => this.table._.columns[col]._.name).join(", ");
@@ -614,6 +996,17 @@ var DeleteQueryBuilder = class extends BaseQueryBuilder {
614
996
  const allColumns = Object.keys(this.table._.columns);
615
997
  return this.returning(...allColumns).execute();
616
998
  }
999
+ toSQL() {
1000
+ const { sql: sql2, params } = this.build();
1001
+ if (this.returningColumns.length > 0) {
1002
+ const returningNames = this.returningColumns.map((col) => this.table._.columns[col]._.name).join(", ");
1003
+ return {
1004
+ sql: `${sql2} RETURNING ${returningNames}`,
1005
+ params
1006
+ };
1007
+ }
1008
+ return { sql: sql2, params };
1009
+ }
617
1010
  };
618
1011
 
619
1012
  // src/builders/with.ts
@@ -699,6 +1092,8 @@ var SQLiteColumn = class _SQLiteColumn {
699
1092
  return new _SQLiteColumn(this._.name, this.type, { ...this.options, unique: true }, this._.mode);
700
1093
  }
701
1094
  references(ref, column) {
1095
+ const columnKey = typeof column === "string" ? column : column._.name;
1096
+ const columnObj = typeof column === "string" ? ref._.columns[column] : column;
702
1097
  return new _SQLiteColumn(
703
1098
  this._.name,
704
1099
  this.type,
@@ -706,7 +1101,7 @@ var SQLiteColumn = class _SQLiteColumn {
706
1101
  ...this.options,
707
1102
  references: {
708
1103
  table: ref,
709
- column: ref._.columns[column]
1104
+ column: columnObj
710
1105
  }
711
1106
  },
712
1107
  this._.mode
@@ -995,6 +1390,12 @@ var ManyRelation = class extends Relation {
995
1390
  super(foreignTable);
996
1391
  }
997
1392
  };
1393
+ var ManyToManyRelation = class extends Relation {
1394
+ constructor(foreignTable, config) {
1395
+ super(foreignTable);
1396
+ this.config = config;
1397
+ }
1398
+ };
998
1399
  var relations = (table, relationsCallback) => {
999
1400
  const builtRelations = relationsCallback({
1000
1401
  one: (foreignTable, config) => {
@@ -1002,6 +1403,9 @@ var relations = (table, relationsCallback) => {
1002
1403
  },
1003
1404
  many: (foreignTable) => {
1004
1405
  return new ManyRelation(foreignTable);
1406
+ },
1407
+ manyToMany: (foreignTable, config) => {
1408
+ return new ManyToManyRelation(foreignTable, config);
1005
1409
  }
1006
1410
  });
1007
1411
  for (const [name, relation] of Object.entries(builtRelations)) {
@@ -1017,6 +1421,14 @@ var relations = (table, relationsCallback) => {
1017
1421
  type: "many",
1018
1422
  foreignTable: relation.foreignTable
1019
1423
  };
1424
+ } else if (relation instanceof ManyToManyRelation) {
1425
+ table.relations[name] = {
1426
+ type: "manyToMany",
1427
+ foreignTable: relation.foreignTable,
1428
+ junctionTable: relation.config.junctionTable,
1429
+ junctionFields: relation.config.junctionFields,
1430
+ junctionReferences: relation.config.junctionReferences
1431
+ };
1020
1432
  }
1021
1433
  }
1022
1434
  return builtRelations;
@@ -1028,6 +1440,53 @@ var alias = (table, alias2) => {
1028
1440
  return table;
1029
1441
  };
1030
1442
 
1443
+ // src/aggregates.ts
1444
+ var count = (column) => ({
1445
+ sql: `COUNT(${column ? column._.name : "*"})`,
1446
+ params: []
1447
+ });
1448
+ var countDistinct = (column) => ({
1449
+ sql: `COUNT(DISTINCT ${column._.name})`,
1450
+ params: []
1451
+ });
1452
+ var sum = (column) => ({
1453
+ sql: `SUM(${column._.name})`,
1454
+ params: []
1455
+ });
1456
+ var avg = (column) => ({
1457
+ sql: `AVG(${column._.name})`,
1458
+ params: []
1459
+ });
1460
+ var max = (column) => ({
1461
+ sql: `MAX(${column._.name})`,
1462
+ params: []
1463
+ });
1464
+ var min = (column) => ({
1465
+ sql: `MIN(${column._.name})`,
1466
+ params: []
1467
+ });
1468
+ var groupConcat = (column, separator = ",") => ({
1469
+ sql: `GROUP_CONCAT(${column._.name}, ?)`,
1470
+ params: [separator]
1471
+ });
1472
+ var as = (aggregate, alias2) => ({
1473
+ ...aggregate,
1474
+ alias: alias2
1475
+ });
1476
+
1477
+ // src/subquery.ts
1478
+ var subquery = (query) => {
1479
+ const { sql: sql2, params } = query.toSQL();
1480
+ return {
1481
+ sql: `(${sql2})`,
1482
+ params,
1483
+ _isSubquery: true
1484
+ };
1485
+ };
1486
+ var scalarSubquery = (query) => {
1487
+ return subquery(query);
1488
+ };
1489
+
1031
1490
  // src/column-helpers.ts
1032
1491
  var text = (name, config) => new SQLiteColumn(name, "TEXT", config, config?.mode);
1033
1492
  var integer = (name, config) => new SQLiteColumn(name, "INTEGER", {}, config?.mode || "default");
@@ -1039,47 +1498,77 @@ var enumType = (name, values) => text(name, { enum: values });
1039
1498
  // Annotate the CommonJS export names for ESM import in node:
1040
1499
  0 && (module.exports = {
1041
1500
  BaseQueryBuilder,
1501
+ ColumnNotFoundError,
1042
1502
  DeleteQueryBuilder,
1043
1503
  InsertQueryBuilder,
1504
+ InsertValidationError,
1044
1505
  ManyRelation,
1506
+ ManyToManyRelation,
1507
+ MigrationError,
1508
+ MissingWhereClauseError,
1045
1509
  OneRelation,
1510
+ QueryBuilderError,
1046
1511
  Relation,
1512
+ RelationError,
1047
1513
  SQLiteColumn,
1048
1514
  SelectQueryBuilder,
1049
1515
  Table,
1516
+ TableNotFoundError,
1050
1517
  TauriORM,
1518
+ TauriORMError,
1051
1519
  UpdateQueryBuilder,
1520
+ UpdateValidationError,
1521
+ ValidationError,
1052
1522
  WithQueryBuilder,
1053
1523
  alias,
1054
1524
  and,
1525
+ as,
1055
1526
  asc,
1056
1527
  avg,
1528
+ between,
1057
1529
  blob,
1058
1530
  boolean,
1531
+ contains,
1059
1532
  count,
1060
1533
  countDistinct,
1061
1534
  desc,
1535
+ endsWith,
1062
1536
  enumType,
1063
1537
  eq,
1538
+ eqSubquery,
1539
+ exists,
1064
1540
  getTableColumns,
1541
+ groupConcat,
1065
1542
  gt,
1543
+ gtSubquery,
1066
1544
  gte,
1545
+ gteSubquery,
1546
+ ilike,
1067
1547
  inArray,
1068
1548
  integer,
1069
1549
  isNotNull,
1070
1550
  isNull,
1071
1551
  like,
1072
1552
  lt,
1553
+ ltSubquery,
1073
1554
  lte,
1555
+ lteSubquery,
1074
1556
  max,
1075
1557
  min,
1558
+ ne,
1559
+ neSubquery,
1076
1560
  not,
1561
+ notExists,
1562
+ notIn,
1077
1563
  numeric,
1078
1564
  or,
1079
1565
  real,
1080
1566
  relations,
1567
+ scalarSubquery,
1081
1568
  sql,
1082
1569
  sqliteTable,
1570
+ startsWith,
1571
+ subquery,
1083
1572
  sum,
1084
1573
  text
1085
1574
  });