@type32/tauri-sqlite-orm 0.3.0 → 0.4.0

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.
Files changed (41) hide show
  1. package/dist/aggregates.d.ts +12 -0
  2. package/dist/aggregates.js +9 -0
  3. package/dist/builders/delete.d.ts +23 -0
  4. package/dist/builders/delete.js +73 -0
  5. package/dist/builders/index.d.ts +7 -0
  6. package/dist/builders/index.js +7 -0
  7. package/dist/builders/insert.d.ts +31 -0
  8. package/dist/builders/insert.js +141 -0
  9. package/dist/builders/query-base.d.ts +1 -0
  10. package/dist/builders/query-base.js +1 -0
  11. package/dist/builders/relations.d.ts +11 -0
  12. package/dist/builders/relations.js +1 -0
  13. package/dist/builders/select.d.ts +54 -0
  14. package/dist/builders/select.js +427 -0
  15. package/dist/builders/update.d.ts +30 -0
  16. package/dist/builders/update.js +124 -0
  17. package/dist/builders/with.d.ts +17 -0
  18. package/dist/builders/with.js +34 -0
  19. package/dist/column-helpers.d.ts +22 -0
  20. package/dist/column-helpers.js +17 -0
  21. package/dist/dialect.d.ts +21 -0
  22. package/dist/dialect.js +67 -0
  23. package/dist/errors.d.ts +30 -0
  24. package/dist/errors.js +66 -0
  25. package/dist/index.d.mts +11 -6
  26. package/dist/index.d.ts +11 -6
  27. package/dist/operators.d.ts +30 -0
  28. package/dist/operators.js +84 -0
  29. package/dist/orm.d.ts +180 -0
  30. package/dist/orm.js +556 -0
  31. package/dist/relational-types.d.ts +87 -0
  32. package/dist/relational-types.js +1 -0
  33. package/dist/relations-v2.d.ts +77 -0
  34. package/dist/relations-v2.js +157 -0
  35. package/dist/serialization.d.ts +14 -0
  36. package/dist/serialization.js +135 -0
  37. package/dist/subquery.d.ts +5 -0
  38. package/dist/subquery.js +6 -0
  39. package/dist/types.d.ts +58 -0
  40. package/dist/types.js +1 -0
  41. package/package.json +2 -1
@@ -0,0 +1,427 @@
1
+ import { sql } from 'kysely';
2
+ import { and } from '../operators';
3
+ import { deserializeValue } from '../serialization';
4
+ /** Map DB column names to TypeScript property names for a table */
5
+ function getDbNameToTsName(table) {
6
+ const map = {};
7
+ for (const [tsName, col] of Object.entries(table._.columns)) {
8
+ map[col._.name] = tsName;
9
+ }
10
+ return map;
11
+ }
12
+ /** Normalize result key - SQLite may return quoted aliases like "users.id" */
13
+ function normalizeRowKey(key) {
14
+ if (key.startsWith('"') && key.endsWith('"')) {
15
+ return key.slice(1, -1);
16
+ }
17
+ return key;
18
+ }
19
+ /** Resolve which columns to select for a relation. Always includes primary keys for deduplication. */
20
+ function resolveRelationColumns(table, include) {
21
+ const allEntries = Object.entries(table._.columns);
22
+ if (include === true || typeof include !== 'object') {
23
+ return allEntries.map(([, col]) => col);
24
+ }
25
+ const cols = include.columns;
26
+ if (!cols) {
27
+ return allEntries.map(([, col]) => col);
28
+ }
29
+ const names = Array.isArray(cols)
30
+ ? cols
31
+ : Object.entries(cols)
32
+ .filter(([, v]) => v)
33
+ .map(([k]) => k);
34
+ const pkNames = allEntries
35
+ .filter(([, c]) => c.options.primaryKey)
36
+ .map(([k]) => k);
37
+ const combined = new Set([...names, ...pkNames]);
38
+ return allEntries
39
+ .filter(([tsName]) => combined.has(tsName))
40
+ .map(([, col]) => col);
41
+ }
42
+ export class SelectQueryBuilder {
43
+ kysely;
44
+ _builder;
45
+ _table;
46
+ _columns;
47
+ _includeRelations = {};
48
+ _manualJoins = [];
49
+ _isDistinct = false;
50
+ _includedColumnAliases = [];
51
+ constructor(kysely, table, columns) {
52
+ this.kysely = kysely;
53
+ this._table = table;
54
+ this._columns = columns;
55
+ const selected = columns
56
+ ? columns.map((c) => table._.columns[c])
57
+ : Object.values(table._.columns);
58
+ const colSelections = selected.map((col) => `${table._.name}.${col._.name} as "${table._.name}.${col._.name}"`);
59
+ this._includedColumnAliases = colSelections;
60
+ this._builder = kysely.selectFrom(table._.name).select(colSelections);
61
+ }
62
+ distinct() {
63
+ this._isDistinct = true;
64
+ this._builder = this._builder.distinct();
65
+ return this;
66
+ }
67
+ where(condition) {
68
+ this._builder = this._builder.where(condition);
69
+ return this;
70
+ }
71
+ orderBy(column, direction = 'asc') {
72
+ if ('toOperationNode' in column) {
73
+ this._builder = this._builder.orderBy(column, direction);
74
+ }
75
+ else {
76
+ this._builder = this._builder.orderBy(sql.ref(column._.name), direction);
77
+ }
78
+ return this;
79
+ }
80
+ limit(count) {
81
+ this._builder = this._builder.limit(count);
82
+ return this;
83
+ }
84
+ offset(count) {
85
+ this._builder = this._builder.offset(count);
86
+ return this;
87
+ }
88
+ groupBy(...columns) {
89
+ for (const col of columns) {
90
+ this._builder = this._builder.groupBy(sql `${sql.ref(this._table._.name)}.${sql.ref(col._.name)}`);
91
+ }
92
+ return this;
93
+ }
94
+ having(condition) {
95
+ this._builder = this._builder.having(condition);
96
+ return this;
97
+ }
98
+ leftJoin(table, condition, alias) {
99
+ this._manualJoins.push({ type: 'LEFT', table, condition, alias });
100
+ const aliasedCols = Object.values(table._.columns).map((col) => `${alias}.${col._.name} as "${alias}.${col._.name}"`);
101
+ this._builder = this._builder
102
+ .leftJoin(`${table._.name} as ${alias}`, (join) => join.on(condition))
103
+ .select(aliasedCols);
104
+ return this;
105
+ }
106
+ innerJoin(table, condition, alias) {
107
+ this._manualJoins.push({ type: 'INNER', table, condition, alias });
108
+ const aliasedCols = Object.values(table._.columns).map((col) => `${alias}.${col._.name} as "${alias}.${col._.name}"`);
109
+ this._builder = this._builder
110
+ .innerJoin(`${table._.name} as ${alias}`, (join) => join.on(condition))
111
+ .select(aliasedCols);
112
+ return this;
113
+ }
114
+ include(relations) {
115
+ this._includeRelations = { ...this._includeRelations, ...relations };
116
+ return this;
117
+ }
118
+ applyIncludes() {
119
+ const processRelations = (parentTable, parentAlias, relations, depth = 0) => {
120
+ if (depth > 10) {
121
+ console.warn('[Tauri-ORM] Maximum relation depth (10) exceeded.');
122
+ return;
123
+ }
124
+ for (const [relationName, include] of Object.entries(relations)) {
125
+ if (!include)
126
+ continue;
127
+ const relation = parentTable.relations[relationName];
128
+ if (!relation) {
129
+ console.warn(`[Tauri-ORM] Relation "${relationName}" not found on table "${parentTable._.name}". Skipping.`);
130
+ continue;
131
+ }
132
+ const foreignTable = relation.foreignTable;
133
+ const foreignAlias = `${parentAlias}_${relationName}`;
134
+ const selectedCols = resolveRelationColumns(foreignTable, include);
135
+ const aliasedCols = selectedCols.map((col) => `${foreignAlias}.${col._.name} as "${foreignAlias}.${col._.name}"`);
136
+ if (relation.type === 'one' && relation.fields && relation.references) {
137
+ const onCondition = sql `${sql.join(relation.fields.map((field, i) => sql `${sql.ref(`${parentAlias}.${field._.name}`)} = ${sql.ref(`${foreignAlias}.${relation.references[i]._.name}`)}`), sql ` AND `)}`;
138
+ this._builder = this._builder
139
+ .leftJoin(`${foreignTable._.name} as ${foreignAlias}`, (join) => join.on(onCondition))
140
+ .select(aliasedCols);
141
+ }
142
+ else if (relation.type === 'many') {
143
+ // Many-to-many via through(): parent -> junction -> foreign
144
+ if (relation.junctionTable && relation.fromJunction && relation.toJunction) {
145
+ const junctionTable = relation.junctionTable;
146
+ const junctionAlias = `${foreignAlias}_jn`;
147
+ const fromJ = relation.fromJunction;
148
+ const toJ = relation.toJunction;
149
+ const join1 = sql `${sql.ref(`${parentAlias}.${fromJ.column._.name}`)} = ${sql.ref(`${junctionAlias}.${fromJ.junctionColumn._.name}`)}`;
150
+ let join2 = sql `${sql.ref(`${junctionAlias}.${toJ.junctionColumn._.name}`)} = ${sql.ref(`${foreignAlias}.${toJ.column._.name}`)}`;
151
+ if (relation.where) {
152
+ join2 = and(join2, relation.where(foreignAlias));
153
+ }
154
+ this._builder = this._builder
155
+ .leftJoin(`${junctionTable._.name} as ${junctionAlias}`, (join) => join.on(join1))
156
+ .leftJoin(`${foreignTable._.name} as ${foreignAlias}`, (join) => join.on(join2))
157
+ .select(aliasedCols);
158
+ }
159
+ else {
160
+ // v2: explicit fields/references, or v1: infer from reverse one relation
161
+ let fields = relation.fields;
162
+ let references = relation.references;
163
+ if (!fields || !references) {
164
+ const refRelation = Object.entries(foreignTable.relations).find(([, r]) => r.foreignTable === parentTable);
165
+ if (refRelation && refRelation[1].fields && refRelation[1].references) {
166
+ const [, relationConfig] = refRelation;
167
+ fields = relationConfig.fields;
168
+ references = relationConfig.references;
169
+ }
170
+ }
171
+ if (fields && references) {
172
+ let onCondition = sql `${sql.join(fields.map((field, i) => sql `${sql.ref(`${foreignAlias}.${field._.name}`)} = ${sql.ref(`${parentAlias}.${references[i]._.name}`)}`), sql ` AND `)}`;
173
+ if (relation.where) {
174
+ onCondition = and(onCondition, relation.where(foreignAlias));
175
+ }
176
+ this._builder = this._builder
177
+ .leftJoin(`${foreignTable._.name} as ${foreignAlias}`, (join) => join.on(onCondition))
178
+ .select(aliasedCols);
179
+ }
180
+ }
181
+ }
182
+ if (typeof include === 'object' && include.with) {
183
+ processRelations(foreignTable, foreignAlias, include.with, depth + 1);
184
+ }
185
+ }
186
+ };
187
+ processRelations(this._table, this._table._.name, this._includeRelations, 0);
188
+ }
189
+ async execute() {
190
+ this.applyIncludes();
191
+ const rawResults = await this._builder.execute();
192
+ const hasIncludes = Object.values(this._includeRelations).some((i) => i);
193
+ if (hasIncludes) {
194
+ return this.processRelationResults(rawResults);
195
+ }
196
+ const hasManualJoins = this._manualJoins.length > 0;
197
+ if (hasManualJoins) {
198
+ return rawResults;
199
+ }
200
+ const prefix = `${this._table._.name}.`;
201
+ const dbNameToTs = getDbNameToTsName(this._table);
202
+ return rawResults.map((row) => {
203
+ const out = {};
204
+ for (const key in row) {
205
+ const normKey = normalizeRowKey(key);
206
+ const dbColName = normKey.startsWith(prefix) ? normKey.slice(prefix.length) : normKey;
207
+ const tsName = dbNameToTs[dbColName] ?? dbColName;
208
+ const column = this._table._.columns[tsName];
209
+ out[tsName] = column ? deserializeValue(row[key], column) : row[key];
210
+ }
211
+ return out;
212
+ });
213
+ }
214
+ processRelationResults(rawResults) {
215
+ if (!rawResults.length)
216
+ return [];
217
+ const mainTablePks = Object.values(this._table._.columns)
218
+ .filter((c) => c.options.primaryKey)
219
+ .map((c) => c._.name);
220
+ if (mainTablePks.length === 0)
221
+ return rawResults;
222
+ const groupedResults = new Map();
223
+ const parseRelationPath = (tableAlias, baseAlias) => {
224
+ if (!tableAlias.startsWith(baseAlias + '_'))
225
+ return [];
226
+ return tableAlias.slice(baseAlias.length + 1).split('_');
227
+ };
228
+ const setNestedValue = (obj, path, columnName, value) => {
229
+ let cur = obj;
230
+ for (let i = 0; i < path.length; i++) {
231
+ const key = path[i];
232
+ if (i === path.length - 1) {
233
+ if (!cur[key])
234
+ cur[key] = {};
235
+ cur[key][columnName] = value;
236
+ }
237
+ else {
238
+ if (!cur[key])
239
+ cur[key] = {};
240
+ cur = cur[key];
241
+ }
242
+ }
243
+ };
244
+ const getNestedRelation = (table, path) => {
245
+ let current = table;
246
+ let relation = null;
247
+ for (const name of path) {
248
+ relation = current.relations[name];
249
+ if (!relation)
250
+ return null;
251
+ current = relation.foreignTable;
252
+ }
253
+ return relation;
254
+ };
255
+ for (const row of rawResults) {
256
+ const getVal = (logicalKey) => {
257
+ const quoted = `"${logicalKey}"`;
258
+ return row[quoted] ?? row[logicalKey];
259
+ };
260
+ const mainTableKey = mainTablePks
261
+ .map((pk) => getVal(`${this._table._.name}.${pk}`) ?? getVal(pk))
262
+ .join('_');
263
+ if (!groupedResults.has(mainTableKey)) {
264
+ groupedResults.set(mainTableKey, {});
265
+ }
266
+ const result = groupedResults.get(mainTableKey);
267
+ const relations = {};
268
+ for (const [key, value] of Object.entries(row)) {
269
+ const normKey = normalizeRowKey(key);
270
+ if (!normKey.includes('.')) {
271
+ const mainDbToTs = getDbNameToTsName(this._table);
272
+ const tsName = mainDbToTs[normKey] ?? normKey;
273
+ const column = this._table._.columns[tsName];
274
+ result[tsName] = column ? deserializeValue(value, column) : value;
275
+ continue;
276
+ }
277
+ const dotIndex = normKey.indexOf('.');
278
+ const tableAlias = normKey.slice(0, dotIndex);
279
+ const columnName = normKey.slice(dotIndex + 1);
280
+ if (tableAlias === this._table._.name) {
281
+ const mainDbToTs = getDbNameToTsName(this._table);
282
+ const tsName = mainDbToTs[columnName] ?? columnName;
283
+ const column = this._table._.columns[tsName];
284
+ result[tsName] = column ? deserializeValue(value, column) : value;
285
+ }
286
+ else {
287
+ // Skip junction table alias (used for through() many-to-many)
288
+ if (tableAlias.endsWith('_jn'))
289
+ continue;
290
+ const path = parseRelationPath(tableAlias, this._table._.name);
291
+ if (path.length > 0) {
292
+ const relationConfig = getNestedRelation(this._table, path);
293
+ const foreignTable = relationConfig?.foreignTable;
294
+ const foreignDbToTs = foreignTable ? getDbNameToTsName(foreignTable) : {};
295
+ const tsName = foreignDbToTs[columnName] ?? columnName;
296
+ const col = foreignTable?._.columns?.[tsName];
297
+ setNestedValue(relations, path, tsName, col ? deserializeValue(value, col) : value);
298
+ }
299
+ else {
300
+ if (!result[tableAlias])
301
+ result[tableAlias] = {};
302
+ result[tableAlias][columnName] = value;
303
+ }
304
+ }
305
+ }
306
+ const attachRelations = (target, relData, table) => {
307
+ for (const [relName, data] of Object.entries(relData)) {
308
+ const relationConfig = table.relations[relName];
309
+ if (!relationConfig)
310
+ continue;
311
+ const directData = {};
312
+ const nestedData = {};
313
+ if (typeof data === 'object' && data !== null) {
314
+ for (const [k, v] of Object.entries(data)) {
315
+ if (typeof v === 'object' && v !== null) {
316
+ nestedData[k] = v;
317
+ }
318
+ else {
319
+ directData[k] = v;
320
+ }
321
+ }
322
+ }
323
+ const hasData = Object.values(directData).some((v) => v !== null && v !== undefined && v !== '');
324
+ if (relationConfig.type === 'many') {
325
+ if (!target[relName])
326
+ target[relName] = [];
327
+ if (hasData) {
328
+ const relPks = Object.values(relationConfig.foreignTable._.columns)
329
+ .filter((c) => c.options.primaryKey)
330
+ .map((c) => c._.name);
331
+ const key = relPks.map((pk) => directData[pk]).join('_');
332
+ if (relPks.length === 0 ||
333
+ !target[relName].some((r) => relPks.map((pk) => r[pk]).join('_') === key)) {
334
+ const newItem = { ...directData };
335
+ if (Object.keys(nestedData).length > 0) {
336
+ attachRelations(newItem, nestedData, relationConfig.foreignTable);
337
+ }
338
+ target[relName].push(newItem);
339
+ }
340
+ }
341
+ }
342
+ else {
343
+ if (hasData || Object.keys(nestedData).length > 0) {
344
+ target[relName] = { ...directData };
345
+ if (Object.keys(nestedData).length > 0) {
346
+ attachRelations(target[relName], nestedData, relationConfig.foreignTable);
347
+ }
348
+ }
349
+ }
350
+ }
351
+ };
352
+ attachRelations(result, relations, this._table);
353
+ }
354
+ return Array.from(groupedResults.values());
355
+ }
356
+ async all() {
357
+ return this.execute();
358
+ }
359
+ async get() {
360
+ this.limit(1);
361
+ const result = await this.execute();
362
+ return result[0];
363
+ }
364
+ async first() {
365
+ return this.get();
366
+ }
367
+ async exists() {
368
+ this.applyIncludes();
369
+ const compiledResult = await this._builder
370
+ .clearSelect()
371
+ .select(sql.raw('1').as('__exists__'))
372
+ .limit(1)
373
+ .execute();
374
+ return compiledResult.length > 0;
375
+ }
376
+ async count() {
377
+ this.applyIncludes();
378
+ const result = await this._builder
379
+ .clearSelect()
380
+ .select(sql `COUNT(*)`.as('count'))
381
+ .execute();
382
+ const row = result[0];
383
+ const val = row ? (row['"count"'] ?? row.count) : undefined;
384
+ return Number(val ?? 0);
385
+ }
386
+ async pluck(column) {
387
+ this.applyIncludes();
388
+ const col = this._table._.columns[column];
389
+ const alias = col._.name;
390
+ const results = await this._builder
391
+ .clearSelect()
392
+ .select(sql.raw(`${this._table._.name}.${alias}`).as(alias))
393
+ .execute();
394
+ return results.map((row) => {
395
+ const val = row['"' + alias + '"'] ?? row[alias];
396
+ return col ? deserializeValue(val, col) : val;
397
+ });
398
+ }
399
+ async paginate(page = 1, pageSize = 10) {
400
+ if (page < 1)
401
+ page = 1;
402
+ if (pageSize < 1)
403
+ pageSize = 10;
404
+ const total = await this.count();
405
+ const totalPages = Math.ceil(total / pageSize);
406
+ const offset = (page - 1) * pageSize;
407
+ const data = await this.limit(pageSize).offset(offset).all();
408
+ return {
409
+ data,
410
+ total,
411
+ page,
412
+ pageSize,
413
+ totalPages,
414
+ hasNextPage: page < totalPages,
415
+ hasPrevPage: page > 1,
416
+ };
417
+ }
418
+ toSQL() {
419
+ this.applyIncludes();
420
+ const compiled = this._builder.compile();
421
+ return { sql: compiled.sql, params: [...compiled.parameters] };
422
+ }
423
+ toKyselyExpression() {
424
+ this.applyIncludes();
425
+ return this._builder;
426
+ }
427
+ }
@@ -0,0 +1,30 @@
1
+ import { Kysely } from 'kysely';
2
+ import { InferInsertModel } from '../orm';
3
+ import { AnyTable, InferSelectModel } from '../types';
4
+ import { Condition } from '../operators';
5
+ export declare class UpdateQueryBuilder<T extends AnyTable> {
6
+ private readonly kysely;
7
+ private _builder;
8
+ private _table;
9
+ private _updateData;
10
+ private _returningColumns;
11
+ private _hasWhereClause;
12
+ private _allowGlobal;
13
+ private _incrementDecrementOps;
14
+ constructor(kysely: Kysely<any>, table: T);
15
+ set(data: Partial<InferInsertModel<T>>): this;
16
+ where(condition: Condition): this;
17
+ increment(column: keyof T['_']['columns'], value?: number): this;
18
+ decrement(column: keyof T['_']['columns'], value?: number): this;
19
+ allowGlobalOperation(): this;
20
+ returning(...columns: (keyof T['_']['columns'])[]): this;
21
+ private mapReturningRows;
22
+ private buildSetClause;
23
+ execute(): Promise<T extends AnyTable ? (InferSelectModel<T> & Record<string, any>)[] : never>;
24
+ returningAll(): Promise<InferSelectModel<T>[]>;
25
+ returningFirst(): Promise<InferSelectModel<T> | undefined>;
26
+ toSQL(): {
27
+ sql: string;
28
+ params: any[];
29
+ };
30
+ }
@@ -0,0 +1,124 @@
1
+ import { sql } from 'kysely';
2
+ import { MissingWhereClauseError, UpdateValidationError, ColumnNotFoundError } from '../errors';
3
+ import { serializeValue, deserializeValue } from '../serialization';
4
+ export class UpdateQueryBuilder {
5
+ kysely;
6
+ _builder;
7
+ _table;
8
+ _updateData = {};
9
+ _returningColumns = [];
10
+ _hasWhereClause = false;
11
+ _allowGlobal = false;
12
+ _incrementDecrementOps = [];
13
+ constructor(kysely, table) {
14
+ this.kysely = kysely;
15
+ this._table = table;
16
+ this._builder = kysely.updateTable(table._.name);
17
+ }
18
+ set(data) {
19
+ this._updateData = { ...this._updateData, ...data };
20
+ return this;
21
+ }
22
+ where(condition) {
23
+ this._hasWhereClause = true;
24
+ this._builder = this._builder.where(condition);
25
+ return this;
26
+ }
27
+ increment(column, value = 1) {
28
+ const col = this._table._.columns[column];
29
+ if (!col)
30
+ throw new ColumnNotFoundError(String(column), this._table._.name);
31
+ this._incrementDecrementOps.push({ column: col._.name, op: 'increment', value });
32
+ return this;
33
+ }
34
+ decrement(column, value = 1) {
35
+ const col = this._table._.columns[column];
36
+ if (!col)
37
+ throw new ColumnNotFoundError(String(column), this._table._.name);
38
+ this._incrementDecrementOps.push({ column: col._.name, op: 'decrement', value });
39
+ return this;
40
+ }
41
+ allowGlobalOperation() {
42
+ this._allowGlobal = true;
43
+ return this;
44
+ }
45
+ returning(...columns) {
46
+ this._returningColumns.push(...columns);
47
+ return this;
48
+ }
49
+ mapReturningRows(rows) {
50
+ const dbNameToTs = {};
51
+ for (const [tsName, col] of Object.entries(this._table._.columns)) {
52
+ dbNameToTs[col._.name] = tsName;
53
+ }
54
+ const norm = (k) => (k.startsWith('"') && k.endsWith('"') ? k.slice(1, -1) : k);
55
+ return rows.map((row) => {
56
+ const out = {};
57
+ for (const [dbKey, value] of Object.entries(row)) {
58
+ const logicalKey = norm(dbKey);
59
+ const tsName = dbNameToTs[logicalKey] ?? logicalKey;
60
+ const column = this._table._.columns[tsName];
61
+ out[tsName] = column ? deserializeValue(value, column) : value;
62
+ }
63
+ return out;
64
+ });
65
+ }
66
+ buildSetClause() {
67
+ const finalData = { ...this._updateData };
68
+ for (const [key, column] of Object.entries(this._table._.columns)) {
69
+ if (finalData[key] === undefined && column.options.$onUpdateFn) {
70
+ ;
71
+ finalData[key] = column.options.$onUpdateFn();
72
+ }
73
+ }
74
+ const entries = Object.entries(finalData);
75
+ const hasSetData = entries.length > 0;
76
+ const hasOps = this._incrementDecrementOps.length > 0;
77
+ if (!hasSetData && !hasOps) {
78
+ throw new UpdateValidationError('Cannot execute an update query without a .set(), .increment(), or .decrement() call.');
79
+ }
80
+ const setMap = {};
81
+ for (const [key, value] of entries) {
82
+ const column = this._table._.columns[key];
83
+ if (!column)
84
+ throw new ColumnNotFoundError(key, this._table._.name);
85
+ setMap[column._.name] = serializeValue(value, column);
86
+ }
87
+ for (const op of this._incrementDecrementOps) {
88
+ const sign = op.op === 'increment' ? '+' : '-';
89
+ setMap[op.column] = sql.raw(`${op.column} ${sign} ${op.value}`);
90
+ }
91
+ return setMap;
92
+ }
93
+ async execute() {
94
+ if (!this._hasWhereClause && !this._allowGlobal) {
95
+ throw new MissingWhereClauseError('UPDATE', this._table._.name);
96
+ }
97
+ const setMap = this.buildSetClause();
98
+ let builder = this._builder.set(setMap);
99
+ if (this._returningColumns.length > 0) {
100
+ const cols = this._returningColumns.map((k) => this._table._.columns[k]._.name);
101
+ const rows = await builder.returning(cols).execute();
102
+ return this.mapReturningRows(rows);
103
+ }
104
+ const result = await builder.executeTakeFirst();
105
+ return [{ rowsAffected: Number(result?.numUpdatedRows ?? 0) }];
106
+ }
107
+ async returningAll() {
108
+ const allCols = Object.keys(this._table._.columns);
109
+ return this.returning(...allCols).execute();
110
+ }
111
+ async returningFirst() {
112
+ const results = await this.returningAll();
113
+ return results[0];
114
+ }
115
+ toSQL() {
116
+ const setMap = this.buildSetClause();
117
+ let builder = this._builder.set(setMap);
118
+ if (this._returningColumns.length > 0) {
119
+ builder = builder.returning(this._returningColumns.map((k) => this._table._.columns[k]._.name));
120
+ }
121
+ const compiled = builder.compile();
122
+ return { sql: compiled.sql, params: [...compiled.parameters] };
123
+ }
124
+ }
@@ -0,0 +1,17 @@
1
+ import { Kysely } from 'kysely';
2
+ import { SelectQueryBuilder } from './select';
3
+ import { InsertQueryBuilder } from './insert';
4
+ import { UpdateQueryBuilder } from './update';
5
+ import { DeleteQueryBuilder } from './delete';
6
+ import { AnyTable } from '../types';
7
+ export declare class WithQueryBuilder {
8
+ private readonly kysely;
9
+ private _ctes;
10
+ constructor(kysely: Kysely<any>);
11
+ with(alias: string, query: SelectQueryBuilder<any, any>): this;
12
+ private applyWith;
13
+ select<T extends AnyTable, C extends (keyof T['_']['columns'])[] | undefined = undefined>(table: T, columns?: C): SelectQueryBuilder<T, C>;
14
+ insert<T extends AnyTable>(table: T): InsertQueryBuilder<T>;
15
+ update<T extends AnyTable>(table: T): UpdateQueryBuilder<T>;
16
+ delete<T extends AnyTable>(table: T): DeleteQueryBuilder<T>;
17
+ }
@@ -0,0 +1,34 @@
1
+ import { SelectQueryBuilder } from './select';
2
+ import { InsertQueryBuilder } from './insert';
3
+ import { UpdateQueryBuilder } from './update';
4
+ import { DeleteQueryBuilder } from './delete';
5
+ export class WithQueryBuilder {
6
+ kysely;
7
+ _ctes = [];
8
+ constructor(kysely) {
9
+ this.kysely = kysely;
10
+ }
11
+ with(alias, query) {
12
+ this._ctes.push({ alias, query: query.toKyselyExpression() });
13
+ return this;
14
+ }
15
+ applyWith(builder) {
16
+ let b = builder;
17
+ for (const { alias, query } of this._ctes) {
18
+ b = b.with(alias, () => query);
19
+ }
20
+ return b;
21
+ }
22
+ select(table, columns) {
23
+ return new SelectQueryBuilder(this.kysely, table, columns);
24
+ }
25
+ insert(table) {
26
+ return new InsertQueryBuilder(this.kysely, table);
27
+ }
28
+ update(table) {
29
+ return new UpdateQueryBuilder(this.kysely, table);
30
+ }
31
+ delete(table) {
32
+ return new DeleteQueryBuilder(this.kysely, table);
33
+ }
34
+ }
@@ -0,0 +1,22 @@
1
+ import { SQLiteColumn } from './orm';
2
+ import { Mode } from './types';
3
+ export declare function text<TName extends string>(name: TName): SQLiteColumn<TName, 'TEXT', 'default', false, false, false, never, never>;
4
+ export declare function text<TName extends string, const TConfig extends {
5
+ mode?: 'default' | 'json';
6
+ enum?: readonly string[];
7
+ }>(name: TName, config: TConfig): SQLiteColumn<TName, 'TEXT', TConfig['mode'] extends 'json' ? 'json' : 'default', false, false, false, TConfig['enum'] extends readonly string[] ? TConfig['enum'] : never, never>;
8
+ export declare function integer<TName extends string>(name: TName): SQLiteColumn<TName, 'INTEGER', 'default', false, false, false, never, never>;
9
+ export declare function integer<TName extends string, const TConfig extends {
10
+ mode: Mode;
11
+ }>(name: TName, config: TConfig): SQLiteColumn<TName, 'INTEGER', TConfig['mode'], false, false, false, never, never>;
12
+ export declare const real: <TName extends string>(name: TName) => SQLiteColumn<TName, "REAL", "default", false, false, false, never, never>;
13
+ export declare function blob<TName extends string>(name: TName): SQLiteColumn<TName, 'BLOB', 'default', false, false, false, never, never>;
14
+ export declare function blob<TName extends string, const TConfig extends {
15
+ mode: 'json' | 'bigint' | 'default';
16
+ }>(name: TName, config: TConfig): SQLiteColumn<TName, 'BLOB', TConfig['mode'], false, false, false, never, never>;
17
+ export declare const boolean: <TName extends string>(name: TName) => SQLiteColumn<TName, "BOOLEAN", "default", false, false, false, never, never>;
18
+ export declare function numeric<TName extends string>(name: TName): SQLiteColumn<TName, 'NUMERIC', 'default', false, false, false, never, never>;
19
+ export declare function numeric<TName extends string, const TConfig extends {
20
+ mode: 'bigint' | 'default';
21
+ }>(name: TName, config: TConfig): SQLiteColumn<TName, 'NUMERIC', TConfig['mode'], false, false, false, never, never>;
22
+ export declare const enumType: <TName extends string, TValues extends readonly [string, ...string[]]>(name: TName, values: TValues) => SQLiteColumn<TName, "TEXT", "default", false, false, false, TValues extends readonly string[] ? TValues : never, never>;
@@ -0,0 +1,17 @@
1
+ // Column helpers
2
+ import { SQLiteColumn } from './orm';
3
+ export function text(name, config) {
4
+ return new SQLiteColumn(name, 'TEXT', config);
5
+ }
6
+ export function integer(name, config) {
7
+ return new SQLiteColumn(name, 'INTEGER', config);
8
+ }
9
+ export const real = (name) => new SQLiteColumn(name, 'REAL');
10
+ export function blob(name, config) {
11
+ return new SQLiteColumn(name, 'BLOB', config);
12
+ }
13
+ export const boolean = (name) => new SQLiteColumn(name, 'BOOLEAN');
14
+ export function numeric(name, config) {
15
+ return new SQLiteColumn(name, 'NUMERIC', config);
16
+ }
17
+ export const enumType = (name, values) => text(name, { enum: values });