@type32/tauri-sqlite-orm 0.1.3 → 0.1.5
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/LICENSE +21 -21
- package/README.md +247 -210
- package/dist/index.d.mts +194 -34
- package/dist/index.d.ts +194 -34
- package/dist/index.js +781 -158
- package/dist/index.mjs +758 -154
- package/package.json +7 -7
package/dist/index.mjs
CHANGED
|
@@ -1,24 +1,10 @@
|
|
|
1
|
-
// src/connection.ts
|
|
2
|
-
import Database from "@tauri-apps/plugin-sql";
|
|
3
|
-
var db = null;
|
|
4
|
-
async function initDb(dbPath) {
|
|
5
|
-
db = await Database.load(dbPath);
|
|
6
|
-
return db;
|
|
7
|
-
}
|
|
8
|
-
function getDb() {
|
|
9
|
-
if (!db) {
|
|
10
|
-
throw new Error("Database not initialized. Please call initDb() first.");
|
|
11
|
-
}
|
|
12
|
-
return db;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
1
|
// src/schema-builder.ts
|
|
16
2
|
function sql(strings, ...values) {
|
|
17
|
-
const
|
|
3
|
+
const raw2 = strings.reduce(
|
|
18
4
|
(acc, part, idx) => acc + part + (idx < values.length ? String(values[idx]) : ""),
|
|
19
5
|
""
|
|
20
6
|
);
|
|
21
|
-
return { raw };
|
|
7
|
+
return { raw: raw2 };
|
|
22
8
|
}
|
|
23
9
|
function createColumn(params) {
|
|
24
10
|
const col = { ...params };
|
|
@@ -40,6 +26,18 @@ function createColumn(params) {
|
|
|
40
26
|
col.defaultFn = fn;
|
|
41
27
|
return col;
|
|
42
28
|
};
|
|
29
|
+
col.$default = (fn) => {
|
|
30
|
+
col.defaultFn = fn;
|
|
31
|
+
return col;
|
|
32
|
+
};
|
|
33
|
+
col.$onUpdate = (fn) => {
|
|
34
|
+
col.onUpdateFn = fn;
|
|
35
|
+
return col;
|
|
36
|
+
};
|
|
37
|
+
col.$onUpdateFn = (fn) => {
|
|
38
|
+
col.onUpdateFn = fn;
|
|
39
|
+
return col;
|
|
40
|
+
};
|
|
43
41
|
col.references = (target, actions) => {
|
|
44
42
|
const t = target();
|
|
45
43
|
col.references = {
|
|
@@ -52,34 +50,43 @@ function createColumn(params) {
|
|
|
52
50
|
};
|
|
53
51
|
return col;
|
|
54
52
|
}
|
|
55
|
-
function text(
|
|
53
|
+
function text(nameOrConfig, maybeConfig) {
|
|
54
|
+
const name = typeof nameOrConfig === "string" ? nameOrConfig : "";
|
|
55
|
+
const config = typeof nameOrConfig === "string" ? maybeConfig : nameOrConfig;
|
|
56
56
|
const col = createColumn({
|
|
57
57
|
name,
|
|
58
58
|
type: "TEXT",
|
|
59
|
-
isPrimaryKey: config?.isPrimaryKey,
|
|
60
59
|
_dataType: ""
|
|
61
60
|
});
|
|
62
61
|
if (config?.enum) col.enumValues = config.enum;
|
|
62
|
+
if (config?.mode) col.mode = config.mode;
|
|
63
63
|
return col;
|
|
64
64
|
}
|
|
65
|
-
function integer(
|
|
65
|
+
function integer(nameOrConfig, maybeConfig) {
|
|
66
|
+
const name = typeof nameOrConfig === "string" ? nameOrConfig : "";
|
|
67
|
+
const config = typeof nameOrConfig === "string" ? maybeConfig : nameOrConfig;
|
|
66
68
|
let dt = 0;
|
|
67
69
|
if (config?.mode === "boolean") dt = false;
|
|
68
|
-
if (config?.mode === "timestamp"
|
|
70
|
+
if (config?.mode === "timestamp" || config?.mode === "timestamp_ms")
|
|
71
|
+
dt = /* @__PURE__ */ new Date();
|
|
69
72
|
const col = createColumn({
|
|
70
73
|
name,
|
|
71
74
|
type: "INTEGER",
|
|
72
|
-
isPrimaryKey: config?.isPrimaryKey,
|
|
73
|
-
autoIncrement: config?.autoIncrement,
|
|
74
75
|
mode: config?.mode ?? "number",
|
|
75
76
|
_dataType: dt
|
|
76
77
|
});
|
|
77
78
|
return col;
|
|
78
79
|
}
|
|
79
80
|
function real(name) {
|
|
80
|
-
return createColumn({
|
|
81
|
+
return createColumn({
|
|
82
|
+
name: name ?? "",
|
|
83
|
+
type: "REAL",
|
|
84
|
+
_dataType: 0
|
|
85
|
+
});
|
|
81
86
|
}
|
|
82
|
-
function blob(
|
|
87
|
+
function blob(nameOrConfig, maybeConfig) {
|
|
88
|
+
const name = typeof nameOrConfig === "string" ? nameOrConfig : "";
|
|
89
|
+
const config = typeof nameOrConfig === "string" ? maybeConfig : nameOrConfig;
|
|
83
90
|
let dt = new Uint8Array();
|
|
84
91
|
if (config?.mode === "bigint") dt = 0n;
|
|
85
92
|
if (config?.mode === "json") dt = void 0;
|
|
@@ -90,7 +97,9 @@ function blob(name, config) {
|
|
|
90
97
|
_dataType: dt
|
|
91
98
|
});
|
|
92
99
|
}
|
|
93
|
-
function numeric(
|
|
100
|
+
function numeric(nameOrConfig, maybeConfig) {
|
|
101
|
+
const name = typeof nameOrConfig === "string" ? nameOrConfig : "";
|
|
102
|
+
const config = typeof nameOrConfig === "string" ? maybeConfig : nameOrConfig;
|
|
94
103
|
let dt = "";
|
|
95
104
|
if (config?.mode === "number") dt = 0;
|
|
96
105
|
if (config?.mode === "bigint") dt = 0n;
|
|
@@ -101,59 +110,346 @@ function numeric(name, config) {
|
|
|
101
110
|
_dataType: dt
|
|
102
111
|
});
|
|
103
112
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
113
|
+
function boolean(name) {
|
|
114
|
+
return createColumn({
|
|
115
|
+
name: name ?? "",
|
|
116
|
+
type: "INTEGER",
|
|
117
|
+
_dataType: false,
|
|
118
|
+
mode: "boolean"
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
function timestamp(name) {
|
|
122
|
+
return createColumn({
|
|
123
|
+
name: name ?? "",
|
|
124
|
+
type: "INTEGER",
|
|
125
|
+
_dataType: /* @__PURE__ */ new Date(),
|
|
126
|
+
mode: "timestamp"
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
function increments(name) {
|
|
130
|
+
return integer(name ?? "").primaryKey({
|
|
131
|
+
autoIncrement: true
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
function unique(name) {
|
|
135
|
+
return {
|
|
136
|
+
on: (...cols) => ({ name, columns: cols.map((c) => c.name) })
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function primaryKey(opts) {
|
|
140
|
+
return { name: opts.name, columns: opts.columns.map((c) => c.name) };
|
|
141
|
+
}
|
|
142
|
+
function check(name, expr) {
|
|
143
|
+
return { name, expr };
|
|
144
|
+
}
|
|
145
|
+
function foreignKey(opts) {
|
|
146
|
+
const first = opts.columns[0];
|
|
147
|
+
return {
|
|
148
|
+
name: opts.name,
|
|
149
|
+
columns: opts.columns.map((c) => c.name),
|
|
150
|
+
foreignTable: first?.tableName || opts.foreignColumns[0]?.tableName || "",
|
|
151
|
+
foreignColumns: opts.foreignColumns.map((c) => c.name),
|
|
152
|
+
onDelete: opts.onDelete,
|
|
153
|
+
onUpdate: opts.onUpdate
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function index(name) {
|
|
157
|
+
return {
|
|
158
|
+
on: (...cols) => ({ name, columns: cols.map((c) => c.name) }),
|
|
159
|
+
where: (expr) => ({ name, columns: [], where: expr })
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function uniqueIndex(name) {
|
|
163
|
+
return {
|
|
164
|
+
on: (...cols) => ({ name, columns: cols.map((c) => c.name), unique: true }),
|
|
165
|
+
where: (expr) => ({ name, columns: [], unique: true, where: expr })
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function defineTable(tableName, schema, extras) {
|
|
117
169
|
const finalizedSchema = { ...schema };
|
|
118
170
|
for (const key of Object.keys(finalizedSchema)) {
|
|
119
171
|
const col = finalizedSchema[key];
|
|
172
|
+
if (!col.name || col.name === "") col.name = key;
|
|
120
173
|
col.tableName = tableName;
|
|
121
174
|
}
|
|
122
175
|
const table = {
|
|
123
176
|
_tableName: tableName,
|
|
124
177
|
_schema: finalizedSchema,
|
|
178
|
+
_constraints: [],
|
|
179
|
+
_indexes: [],
|
|
125
180
|
// The Drizzle-like type inference properties
|
|
126
181
|
$inferSelect: {},
|
|
127
182
|
$inferInsert: {}
|
|
128
|
-
//
|
|
183
|
+
// omit PK columns
|
|
129
184
|
};
|
|
130
185
|
for (const [key, col] of Object.entries(finalizedSchema)) {
|
|
131
186
|
table[key] = col;
|
|
132
187
|
}
|
|
188
|
+
if (extras) {
|
|
189
|
+
const specs = extras(table) || [];
|
|
190
|
+
for (const s of specs) {
|
|
191
|
+
if (s.columns && s.unique !== void 0) {
|
|
192
|
+
table._indexes.push(s);
|
|
193
|
+
} else if (s.columns && s.foreignColumns) {
|
|
194
|
+
table._constraints.push(s);
|
|
195
|
+
} else if (s.columns && (s.name || s.name === void 0)) {
|
|
196
|
+
if (s.columns && s.name !== void 0 && s.columns.length > 0) {
|
|
197
|
+
const pk = s;
|
|
198
|
+
if (pk.columns.length > 1 || pk.name && pk.name.length > 0) {
|
|
199
|
+
table._constraints.push(s);
|
|
200
|
+
} else {
|
|
201
|
+
table._constraints.push(s);
|
|
202
|
+
}
|
|
203
|
+
} else {
|
|
204
|
+
table._constraints.push(s);
|
|
205
|
+
}
|
|
206
|
+
} else if (s.expr) {
|
|
207
|
+
table._constraints.push(s);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
133
211
|
return table;
|
|
134
212
|
}
|
|
135
213
|
|
|
214
|
+
// src/orm.ts
|
|
215
|
+
import Database from "@tauri-apps/plugin-sql";
|
|
216
|
+
|
|
217
|
+
// src/sql-helpers.ts
|
|
218
|
+
function raw(strings, ...values) {
|
|
219
|
+
return {
|
|
220
|
+
toSQL: () => {
|
|
221
|
+
let clause = "";
|
|
222
|
+
const bindings = [];
|
|
223
|
+
for (let i = 0; i < strings.length; i++) {
|
|
224
|
+
clause += strings[i];
|
|
225
|
+
if (i < values.length) {
|
|
226
|
+
const v = values[i];
|
|
227
|
+
if (v && typeof v === "object" && typeof v.toSQL === "function") {
|
|
228
|
+
const s = v.toSQL();
|
|
229
|
+
clause += s.clause;
|
|
230
|
+
bindings.push(...s.bindings);
|
|
231
|
+
} else if (v && typeof v === "object" && "_dataType" in v) {
|
|
232
|
+
clause += getQualifiedName(v);
|
|
233
|
+
} else {
|
|
234
|
+
clause += "?";
|
|
235
|
+
bindings.push(v);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return { clause, bindings };
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
function isColumn(value) {
|
|
244
|
+
return typeof value === "object" && value !== null && "_dataType" in value;
|
|
245
|
+
}
|
|
246
|
+
function getQualifiedName(column) {
|
|
247
|
+
if (column.tableName) return `${column.tableName}.${column.name}`;
|
|
248
|
+
return column.name;
|
|
249
|
+
}
|
|
250
|
+
function comparison(operator, column, value) {
|
|
251
|
+
return {
|
|
252
|
+
toSQL: () => {
|
|
253
|
+
if (isColumn(value)) {
|
|
254
|
+
return {
|
|
255
|
+
clause: `${getQualifiedName(column)} ${operator} ${getQualifiedName(
|
|
256
|
+
value
|
|
257
|
+
)}`,
|
|
258
|
+
bindings: []
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
clause: `${getQualifiedName(column)} ${operator} ?`,
|
|
263
|
+
bindings: [value]
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
var and = (...conditions) => ({
|
|
269
|
+
toSQL: () => {
|
|
270
|
+
const parts = [];
|
|
271
|
+
const bindings = [];
|
|
272
|
+
for (const c of conditions) {
|
|
273
|
+
const s = c.toSQL();
|
|
274
|
+
parts.push(`(${s.clause})`);
|
|
275
|
+
bindings.push(...s.bindings);
|
|
276
|
+
}
|
|
277
|
+
return { clause: parts.join(" AND "), bindings };
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
var or = (...conditions) => ({
|
|
281
|
+
toSQL: () => {
|
|
282
|
+
const parts = [];
|
|
283
|
+
const bindings = [];
|
|
284
|
+
for (const c of conditions) {
|
|
285
|
+
const s = c.toSQL();
|
|
286
|
+
parts.push(`(${s.clause})`);
|
|
287
|
+
bindings.push(...s.bindings);
|
|
288
|
+
}
|
|
289
|
+
return { clause: parts.join(" OR "), bindings };
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
var not = (condition) => ({
|
|
293
|
+
toSQL: () => {
|
|
294
|
+
const s = condition.toSQL();
|
|
295
|
+
return { clause: `NOT (${s.clause})`, bindings: s.bindings };
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
var eq = (column, value) => comparison("=", column, value);
|
|
299
|
+
var ne = (column, value) => comparison("!=", column, value);
|
|
300
|
+
var gt = (column, value) => comparison(">", column, value);
|
|
301
|
+
var gte = (column, value) => comparison(">=", column, value);
|
|
302
|
+
var lt = (column, value) => comparison("<", column, value);
|
|
303
|
+
var lte = (column, value) => comparison("<=", column, value);
|
|
304
|
+
var like = (column, value) => comparison("LIKE", column, value);
|
|
305
|
+
var ilike = (column, value) => ({
|
|
306
|
+
toSQL: () => {
|
|
307
|
+
const colExpr = `LOWER(${getQualifiedName(column)})`;
|
|
308
|
+
if (isColumn(value)) {
|
|
309
|
+
return {
|
|
310
|
+
clause: `${colExpr} LIKE LOWER(${getQualifiedName(value)})`,
|
|
311
|
+
bindings: []
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
return { clause: `${colExpr} LIKE LOWER(?)`, bindings: [value] };
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
var notIlike = (column, value) => ({
|
|
318
|
+
toSQL: () => {
|
|
319
|
+
const colExpr = `LOWER(${getQualifiedName(column)})`;
|
|
320
|
+
if (isColumn(value)) {
|
|
321
|
+
return {
|
|
322
|
+
clause: `${colExpr} NOT LIKE LOWER(${getQualifiedName(value)})`,
|
|
323
|
+
bindings: []
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
return { clause: `${colExpr} NOT LIKE LOWER(?)`, bindings: [value] };
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
var isNull = (column) => ({
|
|
330
|
+
toSQL: () => ({
|
|
331
|
+
clause: `${getQualifiedName(column)} IS NULL`,
|
|
332
|
+
bindings: []
|
|
333
|
+
})
|
|
334
|
+
});
|
|
335
|
+
var isNotNull = (column) => ({
|
|
336
|
+
toSQL: () => ({
|
|
337
|
+
clause: `${getQualifiedName(column)} IS NOT NULL`,
|
|
338
|
+
bindings: []
|
|
339
|
+
})
|
|
340
|
+
});
|
|
341
|
+
var between = (column, from, to) => ({
|
|
342
|
+
toSQL: () => {
|
|
343
|
+
const left = getQualifiedName(column);
|
|
344
|
+
const [fromClause, fromBindings] = isColumn(from) ? [getQualifiedName(from), []] : ["?", [from]];
|
|
345
|
+
const [toClause, toBindings] = isColumn(to) ? [getQualifiedName(to), []] : ["?", [to]];
|
|
346
|
+
return {
|
|
347
|
+
clause: `${left} BETWEEN ${fromClause} AND ${toClause}`,
|
|
348
|
+
bindings: [...fromBindings, ...toBindings]
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
var notBetween = (column, from, to) => ({
|
|
353
|
+
toSQL: () => {
|
|
354
|
+
const left = getQualifiedName(column);
|
|
355
|
+
const [fromClause, fromBindings] = isColumn(from) ? [getQualifiedName(from), []] : ["?", [from]];
|
|
356
|
+
const [toClause, toBindings] = isColumn(to) ? [getQualifiedName(to), []] : ["?", [to]];
|
|
357
|
+
return {
|
|
358
|
+
clause: `${left} NOT BETWEEN ${fromClause} AND ${toClause}`,
|
|
359
|
+
bindings: [...fromBindings, ...toBindings]
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
var inArray = (column, valuesOrQuery) => ({
|
|
364
|
+
toSQL: () => {
|
|
365
|
+
const left = getQualifiedName(column);
|
|
366
|
+
if (Array.isArray(valuesOrQuery)) {
|
|
367
|
+
const placeholders = valuesOrQuery.map(() => "?").join(", ");
|
|
368
|
+
return {
|
|
369
|
+
clause: `${left} IN (${placeholders})`,
|
|
370
|
+
bindings: valuesOrQuery
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
const sq = valuesOrQuery.toSQL ? valuesOrQuery.toSQL() : valuesOrQuery.toSQL();
|
|
374
|
+
return { clause: `${left} IN (${sq.clause})`, bindings: sq.bindings };
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
var notInArray = (column, valuesOrQuery) => ({
|
|
378
|
+
toSQL: () => {
|
|
379
|
+
const left = getQualifiedName(column);
|
|
380
|
+
if (Array.isArray(valuesOrQuery)) {
|
|
381
|
+
const placeholders = valuesOrQuery.map(() => "?").join(", ");
|
|
382
|
+
return {
|
|
383
|
+
clause: `${left} NOT IN (${placeholders})`,
|
|
384
|
+
bindings: valuesOrQuery
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
const sq = valuesOrQuery.toSQL ? valuesOrQuery.toSQL() : valuesOrQuery.toSQL();
|
|
388
|
+
return { clause: `${left} NOT IN (${sq.clause})`, bindings: sq.bindings };
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
var exists = (subquery) => ({
|
|
392
|
+
toSQL: () => {
|
|
393
|
+
const sq = subquery.toSQL ? subquery.toSQL() : subquery.toSQL();
|
|
394
|
+
return { clause: `EXISTS (${sq.clause})`, bindings: sq.bindings };
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
var notExists = (subquery) => ({
|
|
398
|
+
toSQL: () => {
|
|
399
|
+
const sq = subquery.toSQL ? subquery.toSQL() : subquery.toSQL();
|
|
400
|
+
return { clause: `NOT EXISTS (${sq.clause})`, bindings: sq.bindings };
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
var asc = (column) => `${getQualifiedName(column)} ASC`;
|
|
404
|
+
var desc = (column) => `${getQualifiedName(column)} DESC`;
|
|
405
|
+
|
|
136
406
|
// src/orm.ts
|
|
137
407
|
var SelectQueryBuilder = class {
|
|
138
408
|
_table = null;
|
|
139
|
-
_selectedColumns = [
|
|
409
|
+
_selectedColumns = [];
|
|
140
410
|
_joins = [];
|
|
141
411
|
_where = [];
|
|
142
412
|
_orderBy = [];
|
|
143
413
|
_limit = null;
|
|
144
414
|
_offset = null;
|
|
145
|
-
|
|
146
|
-
|
|
415
|
+
_groupBy = [];
|
|
416
|
+
_having = [];
|
|
417
|
+
_distinct = false;
|
|
418
|
+
_dbProvider;
|
|
419
|
+
constructor(dbProvider, fields) {
|
|
420
|
+
this._dbProvider = dbProvider;
|
|
147
421
|
if (fields) {
|
|
148
|
-
|
|
422
|
+
for (const [alias, col] of Object.entries(fields)) {
|
|
423
|
+
const sql2 = getQualifiedName(col);
|
|
424
|
+
this._selectedColumns.push({ sql: sql2, alias });
|
|
425
|
+
}
|
|
149
426
|
}
|
|
150
427
|
}
|
|
428
|
+
distinct() {
|
|
429
|
+
this._distinct = true;
|
|
430
|
+
return this;
|
|
431
|
+
}
|
|
432
|
+
select(fields) {
|
|
433
|
+
this._selectedColumns = [];
|
|
434
|
+
for (const [alias, expr] of Object.entries(fields)) {
|
|
435
|
+
if (typeof expr.toSQL === "function") {
|
|
436
|
+
const s = expr.toSQL();
|
|
437
|
+
this._selectedColumns.push({ sql: s.clause, alias });
|
|
438
|
+
} else {
|
|
439
|
+
this._selectedColumns.push({
|
|
440
|
+
sql: getQualifiedName(expr),
|
|
441
|
+
alias
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
return this;
|
|
446
|
+
}
|
|
151
447
|
from(table) {
|
|
152
448
|
this._table = table;
|
|
153
449
|
return this;
|
|
154
450
|
}
|
|
155
451
|
where(...conditions) {
|
|
156
|
-
this._where.push(...conditions);
|
|
452
|
+
this._where.push(...conditions.filter(Boolean));
|
|
157
453
|
return this;
|
|
158
454
|
}
|
|
159
455
|
leftJoin(otherTable, on) {
|
|
@@ -162,8 +458,26 @@ var SelectQueryBuilder = class {
|
|
|
162
458
|
this._joins.push(joinClause);
|
|
163
459
|
return this;
|
|
164
460
|
}
|
|
461
|
+
groupBy(...exprs) {
|
|
462
|
+
for (const e of exprs) {
|
|
463
|
+
if (!e) continue;
|
|
464
|
+
if (typeof e === "string") this._groupBy.push(e);
|
|
465
|
+
else this._groupBy.push(getQualifiedName(e));
|
|
466
|
+
}
|
|
467
|
+
return this;
|
|
468
|
+
}
|
|
469
|
+
having(...conditions) {
|
|
470
|
+
this._having.push(...conditions);
|
|
471
|
+
return this;
|
|
472
|
+
}
|
|
165
473
|
orderBy(...clauses) {
|
|
166
|
-
|
|
474
|
+
for (const c of clauses) {
|
|
475
|
+
if (!c) continue;
|
|
476
|
+
if (typeof c === "string") this._orderBy.push(c);
|
|
477
|
+
else if (typeof c.toSQL === "function")
|
|
478
|
+
this._orderBy.push(c);
|
|
479
|
+
else this._orderBy.push(getQualifiedName(c));
|
|
480
|
+
}
|
|
167
481
|
return this;
|
|
168
482
|
}
|
|
169
483
|
limit(value) {
|
|
@@ -174,17 +488,15 @@ var SelectQueryBuilder = class {
|
|
|
174
488
|
this._offset = value;
|
|
175
489
|
return this;
|
|
176
490
|
}
|
|
177
|
-
// The final execution step
|
|
178
491
|
async execute() {
|
|
179
492
|
if (!this._table) {
|
|
180
493
|
throw new Error("Cannot execute select query without a 'from' table.");
|
|
181
494
|
}
|
|
182
|
-
const
|
|
495
|
+
const db = await this._dbProvider();
|
|
183
496
|
const bindings = [];
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
497
|
+
const selectList = this._selectedColumns.length > 0 ? this._selectedColumns.map((c) => c.alias ? `${c.sql} AS ${c.alias}` : c.sql).join(", ") : Object.values(this._table._schema).map((c) => `${this._table._tableName}.${c.name}`).join(", ");
|
|
498
|
+
let query = `SELECT ${this._distinct ? "DISTINCT " : ""}${selectList} FROM ${this._table._tableName}`;
|
|
499
|
+
if (this._joins.length > 0) query += ` ${this._joins.join(" ")}`;
|
|
188
500
|
if (this._where.length > 0) {
|
|
189
501
|
const whereClauses = this._where.map((condition) => {
|
|
190
502
|
const sql2 = condition.toSQL();
|
|
@@ -193,21 +505,46 @@ var SelectQueryBuilder = class {
|
|
|
193
505
|
});
|
|
194
506
|
query += ` WHERE ${whereClauses.join(" AND ")}`;
|
|
195
507
|
}
|
|
508
|
+
if (this._groupBy.length > 0) {
|
|
509
|
+
query += ` GROUP BY ${this._groupBy.join(", ")}`;
|
|
510
|
+
}
|
|
511
|
+
if (this._having.length > 0) {
|
|
512
|
+
const havingClauses = this._having.map((h) => {
|
|
513
|
+
const sql2 = h.toSQL();
|
|
514
|
+
bindings.push(...sql2.bindings);
|
|
515
|
+
return `(${sql2.clause})`;
|
|
516
|
+
});
|
|
517
|
+
query += ` HAVING ${havingClauses.join(" AND ")}`;
|
|
518
|
+
}
|
|
196
519
|
if (this._orderBy.length > 0) {
|
|
197
|
-
|
|
520
|
+
const ordParts = [];
|
|
521
|
+
for (const ob of this._orderBy) {
|
|
522
|
+
if (typeof ob === "string") ordParts.push(ob);
|
|
523
|
+
else {
|
|
524
|
+
const s = ob.toSQL();
|
|
525
|
+
ordParts.push(s.clause);
|
|
526
|
+
bindings.push(...s.bindings);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
query += ` ORDER BY ${ordParts.join(", ")}`;
|
|
198
530
|
}
|
|
199
531
|
if (this._limit !== null) {
|
|
200
532
|
query += ` LIMIT ?`;
|
|
201
533
|
bindings.push(this._limit);
|
|
202
534
|
}
|
|
203
535
|
if (this._offset !== null) {
|
|
204
|
-
if (this._limit === null)
|
|
205
|
-
query += ` LIMIT -1`;
|
|
206
|
-
}
|
|
536
|
+
if (this._limit === null) query += ` LIMIT -1`;
|
|
207
537
|
query += ` OFFSET ?`;
|
|
208
538
|
bindings.push(this._offset);
|
|
209
539
|
}
|
|
210
|
-
return
|
|
540
|
+
return db.select(query, bindings);
|
|
541
|
+
}
|
|
542
|
+
async iterator() {
|
|
543
|
+
const rows = await this.execute();
|
|
544
|
+
async function* gen() {
|
|
545
|
+
for (const r of rows) yield r;
|
|
546
|
+
}
|
|
547
|
+
return gen();
|
|
211
548
|
}
|
|
212
549
|
};
|
|
213
550
|
var TauriORM = class {
|
|
@@ -215,48 +552,154 @@ var TauriORM = class {
|
|
|
215
552
|
query = {};
|
|
216
553
|
_tables = null;
|
|
217
554
|
_relations = null;
|
|
555
|
+
_dbPromise;
|
|
556
|
+
constructor(dbUri) {
|
|
557
|
+
this._dbPromise = Database.load(dbUri);
|
|
558
|
+
}
|
|
559
|
+
async getDb() {
|
|
560
|
+
return this._dbPromise;
|
|
561
|
+
}
|
|
218
562
|
// Deprecated: use configure()
|
|
219
563
|
configureQuery(tables, relations2) {
|
|
220
564
|
this.configure(tables, relations2);
|
|
221
565
|
}
|
|
222
566
|
select(fields) {
|
|
223
|
-
return new SelectQueryBuilder(fields);
|
|
567
|
+
return new SelectQueryBuilder(this.getDb.bind(this), fields);
|
|
568
|
+
}
|
|
569
|
+
selectDistinct(fields) {
|
|
570
|
+
const qb = new SelectQueryBuilder(this.getDb.bind(this), fields);
|
|
571
|
+
qb.distinct();
|
|
572
|
+
return qb;
|
|
224
573
|
}
|
|
225
574
|
// --- Drizzle-style CRUD builders ---
|
|
226
575
|
insert(table) {
|
|
576
|
+
const self = this;
|
|
227
577
|
return new class InsertBuilder {
|
|
228
578
|
_table = table;
|
|
229
579
|
_rows = [];
|
|
580
|
+
_selectSql = null;
|
|
581
|
+
_conflict = null;
|
|
582
|
+
_returning = null;
|
|
230
583
|
values(rowOrRows) {
|
|
231
584
|
this._rows = Array.isArray(rowOrRows) ? rowOrRows : [rowOrRows];
|
|
232
585
|
return this;
|
|
233
586
|
}
|
|
587
|
+
select(qb) {
|
|
588
|
+
if (qb.toSQL) this._selectSql = qb.toSQL();
|
|
589
|
+
else this._selectSql = qb.toSQL();
|
|
590
|
+
return this;
|
|
591
|
+
}
|
|
592
|
+
returning(fields) {
|
|
593
|
+
this._returning = fields ?? {};
|
|
594
|
+
return this;
|
|
595
|
+
}
|
|
596
|
+
$returningId() {
|
|
597
|
+
this._returning = "__RETURNING_ID__";
|
|
598
|
+
return this;
|
|
599
|
+
}
|
|
600
|
+
onConflictDoNothing(opts) {
|
|
601
|
+
const target = opts?.target ? Array.isArray(opts.target) ? opts.target.map((c) => c.name) : opts.target.name : void 0;
|
|
602
|
+
this._conflict = {
|
|
603
|
+
kind: "doNothing",
|
|
604
|
+
target,
|
|
605
|
+
where: opts?.where
|
|
606
|
+
};
|
|
607
|
+
return this;
|
|
608
|
+
}
|
|
609
|
+
onConflictDoUpdate(opts) {
|
|
610
|
+
const target = Array.isArray(opts.target) ? opts.target.map((c) => c.name) : opts.target.name;
|
|
611
|
+
this._conflict = {
|
|
612
|
+
kind: "doUpdate",
|
|
613
|
+
target,
|
|
614
|
+
targetWhere: opts.targetWhere,
|
|
615
|
+
set: opts.set,
|
|
616
|
+
setWhere: opts.setWhere
|
|
617
|
+
};
|
|
618
|
+
return this;
|
|
619
|
+
}
|
|
234
620
|
async execute() {
|
|
235
|
-
const
|
|
621
|
+
const db = await self.getDb();
|
|
622
|
+
if (this._selectSql) {
|
|
623
|
+
const cols = Object.keys(this._table._schema);
|
|
624
|
+
let query = `INSERT INTO ${this._table._tableName} (${cols.join(", ")}) ${this._selectSql.clause}`;
|
|
625
|
+
const bindings = [...this._selectSql.bindings];
|
|
626
|
+
query += this._buildConflictClause();
|
|
627
|
+
const ret = await this._executeWithReturning(db, query, bindings);
|
|
628
|
+
return ret;
|
|
629
|
+
}
|
|
236
630
|
for (const data of this._rows) {
|
|
237
631
|
const finalData = { ...data };
|
|
238
632
|
const schema = this._table._schema;
|
|
239
633
|
for (const [key, col] of Object.entries(schema)) {
|
|
240
|
-
if (finalData[key] === void 0
|
|
241
|
-
|
|
634
|
+
if (finalData[key] === void 0) {
|
|
635
|
+
if (col.defaultFn) {
|
|
636
|
+
finalData[key] = col.defaultFn();
|
|
637
|
+
} else if (col.onUpdateFn) {
|
|
638
|
+
finalData[key] = col.onUpdateFn();
|
|
639
|
+
}
|
|
242
640
|
}
|
|
243
641
|
}
|
|
244
642
|
const keys = Object.keys(finalData);
|
|
245
643
|
const values = Object.values(finalData);
|
|
246
644
|
const placeholders = values.map(() => "?").join(", ");
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
await
|
|
645
|
+
let query = `INSERT INTO ${this._table._tableName} (${keys.join(", ")}) VALUES (${placeholders})`;
|
|
646
|
+
const bindings = [...values];
|
|
647
|
+
query += this._buildConflictClause();
|
|
648
|
+
const ret = await this._executeWithReturning(db, query, bindings);
|
|
649
|
+
if (ret !== void 0) return ret;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
_buildConflictClause() {
|
|
653
|
+
if (!this._conflict) return "";
|
|
654
|
+
if (this._conflict.kind === "doNothing") {
|
|
655
|
+
const tgt2 = this._conflict.target ? Array.isArray(this._conflict.target) ? `(${this._conflict.target.join(", ")})` : `(${this._conflict.target})` : "";
|
|
656
|
+
const where = this._conflict.where ? ` WHERE ${this._conflict.where.toSQL().clause}` : "";
|
|
657
|
+
return ` ON CONFLICT ${tgt2} DO NOTHING${where}`;
|
|
251
658
|
}
|
|
659
|
+
const c = this._conflict;
|
|
660
|
+
const tgt = Array.isArray(c.target) ? `(${c.target.join(", ")})` : `(${c.target})`;
|
|
661
|
+
const setKeys = Object.keys(c.set ?? {});
|
|
662
|
+
const setClause = setKeys.map((k) => {
|
|
663
|
+
const v = c.set[k];
|
|
664
|
+
return `${k} = ${typeof v === "object" && v && typeof v.toSQL === "function" ? v.toSQL().clause : "?"}`;
|
|
665
|
+
}).join(", ");
|
|
666
|
+
const targetWhere = c.targetWhere ? ` WHERE ${c.targetWhere.toSQL().clause}` : "";
|
|
667
|
+
const setWhere = c.setWhere ? ` WHERE ${c.setWhere.toSQL().clause}` : "";
|
|
668
|
+
return ` ON CONFLICT ${tgt}${targetWhere} DO UPDATE SET ${setClause}${setWhere}`;
|
|
669
|
+
}
|
|
670
|
+
async _executeWithReturning(db, query, bindings) {
|
|
671
|
+
if (this._returning === null) {
|
|
672
|
+
await db.execute(query, bindings);
|
|
673
|
+
return void 0;
|
|
674
|
+
}
|
|
675
|
+
if (this._returning === "__RETURNING_ID__") {
|
|
676
|
+
const rows = await db.select(`SELECT last_insert_rowid() as id`);
|
|
677
|
+
return rows.map((r) => ({ id: r.id }));
|
|
678
|
+
}
|
|
679
|
+
if (typeof this._returning === "object") {
|
|
680
|
+
const cols = Object.entries(
|
|
681
|
+
this._returning
|
|
682
|
+
).map(
|
|
683
|
+
([alias, col]) => `${col.tableName}.${col.name} AS ${alias}`
|
|
684
|
+
).join(", ");
|
|
685
|
+
const retSql = `${query} RETURNING ${cols}`;
|
|
686
|
+
const res = await db.select(retSql, bindings);
|
|
687
|
+
return res;
|
|
688
|
+
}
|
|
689
|
+
return void 0;
|
|
252
690
|
}
|
|
253
691
|
}();
|
|
254
692
|
}
|
|
255
693
|
update(table) {
|
|
694
|
+
const self = this;
|
|
256
695
|
return new class UpdateBuilder {
|
|
257
696
|
_table = table;
|
|
258
697
|
_data = null;
|
|
259
698
|
_where = null;
|
|
699
|
+
_orderBy = [];
|
|
700
|
+
_limit = null;
|
|
701
|
+
_from = null;
|
|
702
|
+
_returning = null;
|
|
260
703
|
set(data) {
|
|
261
704
|
this._data = data;
|
|
262
705
|
return this;
|
|
@@ -265,14 +708,54 @@ var TauriORM = class {
|
|
|
265
708
|
this._where = cond;
|
|
266
709
|
return this;
|
|
267
710
|
}
|
|
711
|
+
orderBy(...clauses) {
|
|
712
|
+
for (const c of clauses) {
|
|
713
|
+
if (!c) continue;
|
|
714
|
+
if (typeof c === "string") this._orderBy.push(c);
|
|
715
|
+
else if (typeof c.toSQL === "function")
|
|
716
|
+
this._orderBy.push(c);
|
|
717
|
+
else this._orderBy.push(getQualifiedName(c));
|
|
718
|
+
}
|
|
719
|
+
return this;
|
|
720
|
+
}
|
|
721
|
+
limit(n) {
|
|
722
|
+
this._limit = n;
|
|
723
|
+
return this;
|
|
724
|
+
}
|
|
725
|
+
from(tbl) {
|
|
726
|
+
this._from = tbl;
|
|
727
|
+
return this;
|
|
728
|
+
}
|
|
729
|
+
returning(fields) {
|
|
730
|
+
this._returning = fields ?? {};
|
|
731
|
+
return this;
|
|
732
|
+
}
|
|
268
733
|
async execute() {
|
|
269
734
|
if (!this._data)
|
|
270
735
|
throw new Error("Update requires set() before execute()");
|
|
271
|
-
const
|
|
272
|
-
const
|
|
273
|
-
const
|
|
274
|
-
const
|
|
275
|
-
|
|
736
|
+
const db = await self.getDb();
|
|
737
|
+
const schema = this._table._schema;
|
|
738
|
+
const dataToSet = { ...this._data };
|
|
739
|
+
for (const [key, col] of Object.entries(schema)) {
|
|
740
|
+
if (!(key in dataToSet) && col.onUpdateFn) {
|
|
741
|
+
dataToSet[key] = col.onUpdateFn();
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
const setParts = [];
|
|
745
|
+
const bindings = [];
|
|
746
|
+
for (const [k, v] of Object.entries(dataToSet)) {
|
|
747
|
+
if (v === void 0) continue;
|
|
748
|
+
if (v && typeof v === "object" && typeof v.toSQL === "function") {
|
|
749
|
+
const s = v.toSQL();
|
|
750
|
+
setParts.push(`${k} = ${s.clause}`);
|
|
751
|
+
bindings.push(...s.bindings);
|
|
752
|
+
} else {
|
|
753
|
+
setParts.push(`${k} = ?`);
|
|
754
|
+
bindings.push(v);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
let query = `UPDATE ${this._table._tableName} SET ${setParts.join(", ")}`;
|
|
758
|
+
if (this._from) query += ` FROM ${this._from._tableName}`;
|
|
276
759
|
if (this._where) {
|
|
277
760
|
if (typeof this._where.toSQL === "function") {
|
|
278
761
|
const sql2 = this._where.toSQL();
|
|
@@ -286,20 +769,67 @@ var TauriORM = class {
|
|
|
286
769
|
}
|
|
287
770
|
}
|
|
288
771
|
}
|
|
289
|
-
|
|
772
|
+
if (this._orderBy.length > 0) {
|
|
773
|
+
const ordParts = [];
|
|
774
|
+
for (const ob of this._orderBy) {
|
|
775
|
+
if (typeof ob === "string") ordParts.push(ob);
|
|
776
|
+
else {
|
|
777
|
+
const s = ob.toSQL();
|
|
778
|
+
ordParts.push(s.clause);
|
|
779
|
+
bindings.push(...s.bindings);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
query += ` ORDER BY ${ordParts.join(", ")}`;
|
|
783
|
+
}
|
|
784
|
+
if (this._limit !== null) {
|
|
785
|
+
query += ` LIMIT ?`;
|
|
786
|
+
bindings.push(this._limit);
|
|
787
|
+
}
|
|
788
|
+
if (this._returning) {
|
|
789
|
+
const cols = Object.entries(
|
|
790
|
+
this._returning
|
|
791
|
+
).map(
|
|
792
|
+
([alias, col]) => `${col.tableName}.${col.name} AS ${alias}`
|
|
793
|
+
).join(", ");
|
|
794
|
+
const retSql = `${query} RETURNING ${cols}`;
|
|
795
|
+
return await db.select(retSql, bindings);
|
|
796
|
+
}
|
|
797
|
+
await db.execute(query, bindings);
|
|
290
798
|
}
|
|
291
799
|
}();
|
|
292
800
|
}
|
|
293
801
|
delete(table) {
|
|
802
|
+
const self = this;
|
|
294
803
|
return new class DeleteBuilder {
|
|
295
804
|
_table = table;
|
|
296
805
|
_where = null;
|
|
806
|
+
_orderBy = [];
|
|
807
|
+
_limit = null;
|
|
808
|
+
_returning = null;
|
|
297
809
|
where(cond) {
|
|
298
810
|
this._where = cond;
|
|
299
811
|
return this;
|
|
300
812
|
}
|
|
813
|
+
orderBy(...clauses) {
|
|
814
|
+
for (const c of clauses) {
|
|
815
|
+
if (!c) continue;
|
|
816
|
+
if (typeof c === "string") this._orderBy.push(c);
|
|
817
|
+
else if (typeof c.toSQL === "function")
|
|
818
|
+
this._orderBy.push(c);
|
|
819
|
+
else this._orderBy.push(getQualifiedName(c));
|
|
820
|
+
}
|
|
821
|
+
return this;
|
|
822
|
+
}
|
|
823
|
+
limit(n) {
|
|
824
|
+
this._limit = n;
|
|
825
|
+
return this;
|
|
826
|
+
}
|
|
827
|
+
returning(fields) {
|
|
828
|
+
this._returning = fields ?? {};
|
|
829
|
+
return this;
|
|
830
|
+
}
|
|
301
831
|
async execute() {
|
|
302
|
-
const
|
|
832
|
+
const db = await self.getDb();
|
|
303
833
|
let query = `DELETE FROM ${this._table._tableName}`;
|
|
304
834
|
const bindings = [];
|
|
305
835
|
if (this._where) {
|
|
@@ -315,7 +845,32 @@ var TauriORM = class {
|
|
|
315
845
|
}
|
|
316
846
|
}
|
|
317
847
|
}
|
|
318
|
-
|
|
848
|
+
if (this._orderBy.length > 0) {
|
|
849
|
+
const ordParts = [];
|
|
850
|
+
for (const ob of this._orderBy) {
|
|
851
|
+
if (typeof ob === "string") ordParts.push(ob);
|
|
852
|
+
else {
|
|
853
|
+
const s = ob.toSQL();
|
|
854
|
+
ordParts.push(s.clause);
|
|
855
|
+
bindings.push(...s.bindings);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
query += ` ORDER BY ${ordParts.join(", ")}`;
|
|
859
|
+
}
|
|
860
|
+
if (this._limit !== null) {
|
|
861
|
+
query += ` LIMIT ?`;
|
|
862
|
+
bindings.push(this._limit);
|
|
863
|
+
}
|
|
864
|
+
if (this._returning) {
|
|
865
|
+
const cols = Object.entries(
|
|
866
|
+
this._returning
|
|
867
|
+
).map(
|
|
868
|
+
([alias, col]) => `${col.tableName}.${col.name} AS ${alias}`
|
|
869
|
+
).join(", ");
|
|
870
|
+
const retSql = `${query} RETURNING ${cols}`;
|
|
871
|
+
return await db.select(retSql, bindings);
|
|
872
|
+
}
|
|
873
|
+
await db.execute(query, bindings);
|
|
319
874
|
}
|
|
320
875
|
}();
|
|
321
876
|
}
|
|
@@ -323,8 +878,8 @@ var TauriORM = class {
|
|
|
323
878
|
// legacy direct methods removed in favor of builder APIs
|
|
324
879
|
// legacy direct methods removed in favor of builder APIs
|
|
325
880
|
async run(query, bindings = []) {
|
|
326
|
-
const
|
|
327
|
-
await
|
|
881
|
+
const db = await this.getDb();
|
|
882
|
+
await db.execute(query, bindings);
|
|
328
883
|
}
|
|
329
884
|
// --- Migrations API ---
|
|
330
885
|
generateCreateTableSql(table) {
|
|
@@ -374,16 +929,16 @@ var TauriORM = class {
|
|
|
374
929
|
);
|
|
375
930
|
}
|
|
376
931
|
async hasMigration(name) {
|
|
377
|
-
const
|
|
378
|
-
const rows = await
|
|
932
|
+
const db = await this.getDb();
|
|
933
|
+
const rows = await db.select(
|
|
379
934
|
`SELECT name FROM _migrations WHERE name = ?`,
|
|
380
935
|
[name]
|
|
381
936
|
);
|
|
382
937
|
return Array.isArray(rows) && rows.length > 0;
|
|
383
938
|
}
|
|
384
939
|
async recordMigration(name) {
|
|
385
|
-
const
|
|
386
|
-
await
|
|
940
|
+
const db = await this.getDb();
|
|
941
|
+
await db.execute(
|
|
387
942
|
`INSERT INTO _migrations (name, applied_at) VALUES (?, ?)`,
|
|
388
943
|
[name, Date.now()]
|
|
389
944
|
);
|
|
@@ -405,7 +960,7 @@ var TauriORM = class {
|
|
|
405
960
|
configure(tables, relDefs) {
|
|
406
961
|
this._tables = tables;
|
|
407
962
|
this._relations = relDefs ?? {};
|
|
408
|
-
this.query = makeQueryAPI(tables, this._relations);
|
|
963
|
+
this.query = makeQueryAPI(tables, this._relations, this.getDb.bind(this));
|
|
409
964
|
return this;
|
|
410
965
|
}
|
|
411
966
|
// Convenience: migrate from configured tables
|
|
@@ -417,7 +972,7 @@ var TauriORM = class {
|
|
|
417
972
|
// --- Schema diff and CLI-like helpers ---
|
|
418
973
|
async diffSchema() {
|
|
419
974
|
if (!this._tables) throw new Error("No tables configured.");
|
|
420
|
-
const dbi = getDb();
|
|
975
|
+
const dbi = await this.getDb();
|
|
421
976
|
const configuredNames = Object.values(this._tables).map(
|
|
422
977
|
(t) => t._tableName
|
|
423
978
|
);
|
|
@@ -492,7 +1047,7 @@ var TauriORM = class {
|
|
|
492
1047
|
return this.pullSchema();
|
|
493
1048
|
}
|
|
494
1049
|
async studio() {
|
|
495
|
-
const dbi = getDb();
|
|
1050
|
+
const dbi = await this.getDb();
|
|
496
1051
|
return { driver: "sqlite", path: dbi.path };
|
|
497
1052
|
}
|
|
498
1053
|
// --- Schema detection / signature ---
|
|
@@ -502,7 +1057,7 @@ var TauriORM = class {
|
|
|
502
1057
|
);
|
|
503
1058
|
}
|
|
504
1059
|
async getSchemaMeta(key) {
|
|
505
|
-
const dbi = getDb();
|
|
1060
|
+
const dbi = await this.getDb();
|
|
506
1061
|
await this.ensureSchemaMeta();
|
|
507
1062
|
const rows = await dbi.select(
|
|
508
1063
|
`SELECT value FROM _schema_meta WHERE key = ?`,
|
|
@@ -511,7 +1066,7 @@ var TauriORM = class {
|
|
|
511
1066
|
return rows?.[0]?.value ?? null;
|
|
512
1067
|
}
|
|
513
1068
|
async setSchemaMeta(key, value) {
|
|
514
|
-
const dbi = getDb();
|
|
1069
|
+
const dbi = await this.getDb();
|
|
515
1070
|
await this.ensureSchemaMeta();
|
|
516
1071
|
await dbi.execute(
|
|
517
1072
|
`INSERT INTO _schema_meta(key, value) VALUES(?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value`,
|
|
@@ -560,7 +1115,7 @@ var TauriORM = class {
|
|
|
560
1115
|
// Pull current DB schema (minimal) for configured tables
|
|
561
1116
|
async pullSchema() {
|
|
562
1117
|
if (!this._tables) throw new Error("No tables configured.");
|
|
563
|
-
const dbi = getDb();
|
|
1118
|
+
const dbi = await this.getDb();
|
|
564
1119
|
const result = {};
|
|
565
1120
|
for (const tbl of Object.values(this._tables)) {
|
|
566
1121
|
const name = tbl._tableName;
|
|
@@ -579,7 +1134,7 @@ var TauriORM = class {
|
|
|
579
1134
|
return this.generateCreateTableSql(table);
|
|
580
1135
|
}
|
|
581
1136
|
async tableExists(name) {
|
|
582
|
-
const dbi = getDb();
|
|
1137
|
+
const dbi = await this.getDb();
|
|
583
1138
|
const rows = await dbi.select(
|
|
584
1139
|
`SELECT name FROM sqlite_master WHERE type='table' AND name = ?`,
|
|
585
1140
|
[name]
|
|
@@ -589,12 +1144,12 @@ var TauriORM = class {
|
|
|
589
1144
|
// Force push model to DB: add missing tables/columns, rebuild tables if incompatible
|
|
590
1145
|
async forcePush(options) {
|
|
591
1146
|
if (!this._tables) throw new Error("No tables configured.");
|
|
592
|
-
const dbi = getDb();
|
|
1147
|
+
const dbi = await this.getDb();
|
|
593
1148
|
const preserve = options?.preserveData !== false;
|
|
594
1149
|
for (const tbl of Object.values(this._tables)) {
|
|
595
1150
|
const tableName = tbl._tableName;
|
|
596
|
-
const
|
|
597
|
-
if (!
|
|
1151
|
+
const exists2 = await this.tableExists(tableName);
|
|
1152
|
+
if (!exists2) {
|
|
598
1153
|
await this.run(this.buildCreateTableSQL(tbl));
|
|
599
1154
|
continue;
|
|
600
1155
|
}
|
|
@@ -659,7 +1214,6 @@ var TauriORM = class {
|
|
|
659
1214
|
await this.setSchemaMeta("schema_signature", this.computeModelSignature());
|
|
660
1215
|
}
|
|
661
1216
|
};
|
|
662
|
-
var db2 = new TauriORM();
|
|
663
1217
|
function relations(baseTable, builder) {
|
|
664
1218
|
const ctx = {
|
|
665
1219
|
one: (table, cfg) => ({ kind: "one", table, cfg }),
|
|
@@ -685,10 +1239,29 @@ function guessChildFk(child, base, rel) {
|
|
|
685
1239
|
];
|
|
686
1240
|
return childCols.find((c) => guessNames.includes(c.name)) || childCols.find((c) => /.*_id$/i.test(c.name)) || null;
|
|
687
1241
|
}
|
|
1242
|
+
function guessOneRelationJoin(base, rel) {
|
|
1243
|
+
const child = rel.table;
|
|
1244
|
+
const basePk = getPrimaryKey(base);
|
|
1245
|
+
const childCols = Object.values(child._schema);
|
|
1246
|
+
if (rel.cfg?.fields && rel.cfg?.references && rel.cfg.fields[0] && rel.cfg.references[0]) {
|
|
1247
|
+
const fk = rel.cfg.fields[0];
|
|
1248
|
+
const ref = rel.cfg.references[0];
|
|
1249
|
+
if (childCols.some((c) => c.name === fk.name)) {
|
|
1250
|
+
return { lhsTable: child, lhsCol: fk, rhsTable: base, rhsCol: ref };
|
|
1251
|
+
}
|
|
1252
|
+
const baseCols = Object.values(base._schema);
|
|
1253
|
+
if (baseCols.some((c) => c.name === fk.name)) {
|
|
1254
|
+
return { lhsTable: base, lhsCol: fk, rhsTable: child, rhsCol: ref };
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
const childFk = guessChildFk(child, base, rel);
|
|
1258
|
+
if (!childFk) return null;
|
|
1259
|
+
return { lhsTable: child, lhsCol: childFk, rhsTable: base, rhsCol: basePk };
|
|
1260
|
+
}
|
|
688
1261
|
function isFlatWith(spec) {
|
|
689
1262
|
return Object.values(spec).every((v) => typeof v === "boolean");
|
|
690
1263
|
}
|
|
691
|
-
function makeQueryAPI(tables, relDefs) {
|
|
1264
|
+
function makeQueryAPI(tables, relDefs, dbProvider) {
|
|
692
1265
|
const api = {};
|
|
693
1266
|
const tableKeyByName = {};
|
|
694
1267
|
for (const [k, t] of Object.entries(tables)) tableKeyByName[t._tableName] = k;
|
|
@@ -697,7 +1270,7 @@ function makeQueryAPI(tables, relDefs) {
|
|
|
697
1270
|
async findMany(opts) {
|
|
698
1271
|
const base = tbl;
|
|
699
1272
|
const withSpec = opts?.with ?? {};
|
|
700
|
-
const dbi =
|
|
1273
|
+
const dbi = await dbProvider();
|
|
701
1274
|
const rels = relDefs[tblKey] ?? {};
|
|
702
1275
|
if (opts?.join && isFlatWith(withSpec)) {
|
|
703
1276
|
const baseCols = Object.values(base._schema);
|
|
@@ -723,23 +1296,43 @@ function makeQueryAPI(tables, relDefs) {
|
|
|
723
1296
|
const child = rel.table;
|
|
724
1297
|
const childCols = Object.values(child._schema);
|
|
725
1298
|
const childPk = childCols.find((c) => c.isPrimaryKey) || childCols.find((c) => c.name === "id") || null;
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
1299
|
+
if (rel.kind === "one") {
|
|
1300
|
+
const mapping = guessOneRelationJoin(base, rel);
|
|
1301
|
+
if (!mapping) continue;
|
|
1302
|
+
if (mapping.lhsTable._tableName === child._tableName) {
|
|
1303
|
+
fkMap[relName] = { childFk: mapping.lhsCol, childPk };
|
|
1304
|
+
joins.push(
|
|
1305
|
+
`LEFT JOIN ${child._tableName} ON ${mapping.lhsTable._tableName}.${mapping.lhsCol.name} = ${mapping.rhsTable._tableName}.${mapping.rhsCol.name}`
|
|
1306
|
+
);
|
|
1307
|
+
} else {
|
|
1308
|
+
fkMap[relName] = { childFk: mapping.rhsCol, childPk };
|
|
1309
|
+
joins.push(
|
|
1310
|
+
`LEFT JOIN ${child._tableName} ON ${mapping.lhsTable._tableName}.${mapping.lhsCol.name} = ${mapping.rhsTable._tableName}.${mapping.rhsCol.name}`
|
|
1311
|
+
);
|
|
1312
|
+
}
|
|
1313
|
+
} else {
|
|
1314
|
+
const childFk = guessChildFk(child, base, rel);
|
|
1315
|
+
if (!childFk) continue;
|
|
1316
|
+
fkMap[relName] = { childFk, childPk };
|
|
1317
|
+
joins.push(
|
|
1318
|
+
`LEFT JOIN ${child._tableName} ON ${child._tableName}.${childFk.name} = ${base._tableName}.${basePk.name}`
|
|
1319
|
+
);
|
|
1320
|
+
}
|
|
729
1321
|
const selected = typeof enabled === "object" && enabled.columns?.length ? enabled.columns : childCols.map((c) => c.name);
|
|
730
1322
|
relColsMap[relName] = selected;
|
|
731
1323
|
for (const name of selected)
|
|
732
1324
|
selectParts.push(
|
|
733
1325
|
`${child._tableName}.${name} AS __rel_${relName}_${name}`
|
|
734
1326
|
);
|
|
735
|
-
joins.push(
|
|
736
|
-
`LEFT JOIN ${child._tableName} ON ${child._tableName}.${childFk.name} = ${base._tableName}.${basePk.name}`
|
|
737
|
-
);
|
|
738
1327
|
}
|
|
739
1328
|
let sqlText = `SELECT ${selectParts.join(", ")} FROM ${base._tableName}${joins.length ? " " + joins.join(" ") : ""}`;
|
|
740
1329
|
const bindings = [];
|
|
741
1330
|
if (opts?.where) {
|
|
742
|
-
if (typeof opts.where
|
|
1331
|
+
if (typeof opts.where === "function") {
|
|
1332
|
+
const w = opts.where(base, { eq, ne, gt, gte, lt, lte, like }).toSQL();
|
|
1333
|
+
sqlText += ` WHERE ${w.clause}`;
|
|
1334
|
+
bindings.push(...w.bindings);
|
|
1335
|
+
} else if (typeof opts.where.toSQL === "function") {
|
|
743
1336
|
const w = opts.where.toSQL();
|
|
744
1337
|
sqlText += ` WHERE ${w.clause}`;
|
|
745
1338
|
bindings.push(...w.bindings);
|
|
@@ -751,8 +1344,9 @@ function makeQueryAPI(tables, relDefs) {
|
|
|
751
1344
|
}
|
|
752
1345
|
}
|
|
753
1346
|
}
|
|
754
|
-
|
|
755
|
-
|
|
1347
|
+
const orderByClauses = typeof opts?.orderBy === "function" ? opts.orderBy(base, { asc, desc }) : opts?.orderBy;
|
|
1348
|
+
if (orderByClauses?.length)
|
|
1349
|
+
sqlText += ` ORDER BY ${orderByClauses.join(", ")}`;
|
|
756
1350
|
if (typeof opts?.limit === "number")
|
|
757
1351
|
sqlText += ` LIMIT ${opts.limit}`;
|
|
758
1352
|
if (typeof opts?.offset === "number")
|
|
@@ -792,10 +1386,10 @@ function makeQueryAPI(tables, relDefs) {
|
|
|
792
1386
|
const childPk = fkMap[relName].childPk;
|
|
793
1387
|
if (childPk) {
|
|
794
1388
|
if (!acc[relName]) acc[relName] = [];
|
|
795
|
-
const
|
|
1389
|
+
const exists2 = acc[relName].some(
|
|
796
1390
|
(r) => r[childPk.name] === childObj[childPk.name]
|
|
797
1391
|
);
|
|
798
|
-
if (!
|
|
1392
|
+
if (!exists2) acc[relName].push(childObj);
|
|
799
1393
|
} else {
|
|
800
1394
|
acc[relName].push(childObj);
|
|
801
1395
|
}
|
|
@@ -819,7 +1413,11 @@ function makeQueryAPI(tables, relDefs) {
|
|
|
819
1413
|
let baseSql = `SELECT ${baseSelected.join(", ")} FROM ${base._tableName}`;
|
|
820
1414
|
const baseBindings = [];
|
|
821
1415
|
if (opts?.where) {
|
|
822
|
-
if (typeof opts.where
|
|
1416
|
+
if (typeof opts.where === "function") {
|
|
1417
|
+
const w = opts.where(base, { eq, ne, gt, gte, lt, lte, like }).toSQL();
|
|
1418
|
+
baseSql += ` WHERE ${w.clause}`;
|
|
1419
|
+
baseBindings.push(...w.bindings);
|
|
1420
|
+
} else if (typeof opts.where.toSQL === "function") {
|
|
823
1421
|
const w = opts.where.toSQL();
|
|
824
1422
|
baseSql += ` WHERE ${w.clause}`;
|
|
825
1423
|
baseBindings.push(...w.bindings);
|
|
@@ -831,8 +1429,9 @@ function makeQueryAPI(tables, relDefs) {
|
|
|
831
1429
|
}
|
|
832
1430
|
}
|
|
833
1431
|
}
|
|
834
|
-
|
|
835
|
-
|
|
1432
|
+
const orderByClauses2 = typeof opts?.orderBy === "function" ? opts.orderBy(base, { asc, desc }) : opts?.orderBy;
|
|
1433
|
+
if (orderByClauses2?.length)
|
|
1434
|
+
baseSql += ` ORDER BY ${orderByClauses2.join(", ")}`;
|
|
836
1435
|
if (typeof opts?.limit === "number") baseSql += ` LIMIT ${opts.limit}`;
|
|
837
1436
|
if (typeof opts?.offset === "number")
|
|
838
1437
|
baseSql += ` OFFSET ${opts.offset}`;
|
|
@@ -851,9 +1450,9 @@ function makeQueryAPI(tables, relDefs) {
|
|
|
851
1450
|
const child = rel.table;
|
|
852
1451
|
const childCols = Object.values(child._schema);
|
|
853
1452
|
const selectCols = enabled?.columns && enabled.columns.length > 0 ? enabled.columns : childCols.map((c) => c.name);
|
|
854
|
-
const fkCol = guessChildFk(child, parentTable, rel);
|
|
855
|
-
if (!fkCol) continue;
|
|
856
1453
|
if (rel.kind === "many") {
|
|
1454
|
+
const fkCol = guessChildFk(child, parentTable, rel);
|
|
1455
|
+
if (!fkCol) continue;
|
|
857
1456
|
const sql2 = `SELECT ${selectCols.join(", ")} FROM ${child._tableName} WHERE ${fkCol.name} IN (${parentIds.map(() => "?").join(", ")})`;
|
|
858
1457
|
const rows = await dbi.select(sql2, parentIds);
|
|
859
1458
|
const buckets = /* @__PURE__ */ new Map();
|
|
@@ -872,14 +1471,32 @@ function makeQueryAPI(tables, relDefs) {
|
|
|
872
1471
|
await loadRelationsFor(children, child, enabled.with);
|
|
873
1472
|
}
|
|
874
1473
|
} else {
|
|
875
|
-
const
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
1474
|
+
const mapping = guessOneRelationJoin(parentTable, rel);
|
|
1475
|
+
if (!mapping) continue;
|
|
1476
|
+
if (mapping.lhsTable._tableName === child._tableName) {
|
|
1477
|
+
const sql2 = `SELECT ${selectCols.join(", ")} FROM ${child._tableName} WHERE ${mapping.lhsCol.name} IN (${parentIds.map(() => "?").join(", ")})`;
|
|
1478
|
+
const rows = await dbi.select(sql2, parentIds);
|
|
1479
|
+
const mapOne = /* @__PURE__ */ new Map();
|
|
1480
|
+
for (const r of rows) mapOne.set(r[mapping.lhsCol.name], r);
|
|
1481
|
+
for (const p of parents)
|
|
1482
|
+
p[relName] = mapOne.get(p[parentPk.name]) ?? null;
|
|
1483
|
+
} else {
|
|
1484
|
+
const parentFkName = mapping.lhsCol.name;
|
|
1485
|
+
const childPkName = mapping.rhsCol.name;
|
|
1486
|
+
const childIds = parents.map((p) => p[parentFkName]).filter((v2) => v2 !== void 0 && v2 !== null);
|
|
1487
|
+
if (childIds.length === 0) {
|
|
1488
|
+
for (const p of parents) p[relName] = null;
|
|
1489
|
+
} else {
|
|
1490
|
+
const sql2 = `SELECT ${selectCols.join(", ")} FROM ${child._tableName} WHERE ${childPkName} IN (${childIds.map(() => "?").join(", ")})`;
|
|
1491
|
+
const rows = await dbi.select(sql2, childIds);
|
|
1492
|
+
const mapOne = /* @__PURE__ */ new Map();
|
|
1493
|
+
for (const r of rows) mapOne.set(r[childPkName], r);
|
|
1494
|
+
for (const p of parents)
|
|
1495
|
+
p[relName] = mapOne.get(p[parentFkName]) ?? null;
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
881
1498
|
if (enabled?.with) {
|
|
882
|
-
const children = parents.map((p) => p[relName]).filter(Boolean);
|
|
1499
|
+
const children = parents.map((p) => p[relName]).filter((x) => Boolean(x));
|
|
883
1500
|
if (children.length > 0)
|
|
884
1501
|
await loadRelationsFor(children, child, enabled.with);
|
|
885
1502
|
}
|
|
@@ -890,70 +1507,57 @@ function makeQueryAPI(tables, relDefs) {
|
|
|
890
1507
|
await loadRelationsFor(result, base, withSpec);
|
|
891
1508
|
}
|
|
892
1509
|
return result;
|
|
1510
|
+
},
|
|
1511
|
+
async findFirst(opts) {
|
|
1512
|
+
const rows = await api[tblKey].findMany({ ...opts, limit: 1 });
|
|
1513
|
+
return rows?.[0] ?? null;
|
|
893
1514
|
}
|
|
894
1515
|
};
|
|
895
1516
|
}
|
|
896
1517
|
return api;
|
|
897
1518
|
}
|
|
898
|
-
|
|
899
|
-
// src/sql-helpers.ts
|
|
900
|
-
function isColumn(value) {
|
|
901
|
-
return typeof value === "object" && value !== null && "_dataType" in value;
|
|
902
|
-
}
|
|
903
|
-
function getQualifiedName(column) {
|
|
904
|
-
if (column.tableName) return `${column.tableName}.${column.name}`;
|
|
905
|
-
return column.name;
|
|
906
|
-
}
|
|
907
|
-
function comparison(operator, column, value) {
|
|
908
|
-
return {
|
|
909
|
-
toSQL: () => {
|
|
910
|
-
if (isColumn(value)) {
|
|
911
|
-
return {
|
|
912
|
-
clause: `${getQualifiedName(column)} ${operator} ${getQualifiedName(
|
|
913
|
-
value
|
|
914
|
-
)}`,
|
|
915
|
-
bindings: []
|
|
916
|
-
};
|
|
917
|
-
}
|
|
918
|
-
return {
|
|
919
|
-
clause: `${getQualifiedName(column)} ${operator} ?`,
|
|
920
|
-
bindings: [value]
|
|
921
|
-
};
|
|
922
|
-
}
|
|
923
|
-
};
|
|
924
|
-
}
|
|
925
|
-
var eq = (column, value) => comparison("=", column, value);
|
|
926
|
-
var ne = (column, value) => comparison("!=", column, value);
|
|
927
|
-
var gt = (column, value) => comparison(">", column, value);
|
|
928
|
-
var gte = (column, value) => comparison(">=", column, value);
|
|
929
|
-
var lt = (column, value) => comparison("<", column, value);
|
|
930
|
-
var lte = (column, value) => comparison("<=", column, value);
|
|
931
|
-
var like = (column, value) => comparison("LIKE", column, value);
|
|
932
|
-
var asc = (column) => `${column.name} ASC`;
|
|
933
|
-
var desc = (column) => `${column.name} DESC`;
|
|
934
1519
|
export {
|
|
935
1520
|
TauriORM,
|
|
1521
|
+
and,
|
|
936
1522
|
asc,
|
|
1523
|
+
between,
|
|
937
1524
|
blob,
|
|
938
1525
|
boolean,
|
|
939
|
-
|
|
1526
|
+
check,
|
|
940
1527
|
defineTable,
|
|
941
1528
|
desc,
|
|
942
1529
|
eq,
|
|
943
|
-
|
|
1530
|
+
exists,
|
|
1531
|
+
foreignKey,
|
|
1532
|
+
getQualifiedName,
|
|
944
1533
|
gt,
|
|
945
1534
|
gte,
|
|
946
|
-
|
|
1535
|
+
ilike,
|
|
1536
|
+
inArray,
|
|
1537
|
+
increments,
|
|
1538
|
+
index,
|
|
947
1539
|
integer,
|
|
1540
|
+
isNotNull,
|
|
1541
|
+
isNull,
|
|
948
1542
|
like,
|
|
949
1543
|
lt,
|
|
950
1544
|
lte,
|
|
951
1545
|
makeQueryAPI,
|
|
952
1546
|
ne,
|
|
1547
|
+
not,
|
|
1548
|
+
notBetween,
|
|
1549
|
+
notExists,
|
|
1550
|
+
notIlike,
|
|
1551
|
+
notInArray,
|
|
953
1552
|
numeric,
|
|
1553
|
+
or,
|
|
1554
|
+
primaryKey,
|
|
1555
|
+
raw,
|
|
954
1556
|
real,
|
|
955
1557
|
relations,
|
|
956
1558
|
sql,
|
|
957
1559
|
text,
|
|
958
|
-
timestamp
|
|
1560
|
+
timestamp,
|
|
1561
|
+
unique,
|
|
1562
|
+
uniqueIndex
|
|
959
1563
|
};
|