@type32/tauri-sqlite-orm 0.1.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.
- package/LICENSE +21 -0
- package/README.md +209 -0
- package/dist/index.d.mts +193 -0
- package/dist/index.d.ts +193 -0
- package/dist/index.js +764 -0
- package/dist/index.mjs +704 -0
- package/package.json +38 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,764 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc2) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc2 = __getOwnPropDesc(from, key)) || desc2.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
TauriORM: () => TauriORM,
|
|
34
|
+
asc: () => asc,
|
|
35
|
+
blob: () => blob,
|
|
36
|
+
boolean: () => boolean,
|
|
37
|
+
db: () => db2,
|
|
38
|
+
defineTable: () => defineTable,
|
|
39
|
+
desc: () => desc,
|
|
40
|
+
eq: () => eq,
|
|
41
|
+
getDb: () => getDb,
|
|
42
|
+
gt: () => gt,
|
|
43
|
+
gte: () => gte,
|
|
44
|
+
initDb: () => initDb,
|
|
45
|
+
integer: () => integer,
|
|
46
|
+
like: () => like,
|
|
47
|
+
lt: () => lt,
|
|
48
|
+
lte: () => lte,
|
|
49
|
+
makeQueryAPI: () => makeQueryAPI,
|
|
50
|
+
ne: () => ne,
|
|
51
|
+
numeric: () => numeric,
|
|
52
|
+
real: () => real,
|
|
53
|
+
relations: () => relations,
|
|
54
|
+
sql: () => sql,
|
|
55
|
+
text: () => text,
|
|
56
|
+
timestamp: () => timestamp
|
|
57
|
+
});
|
|
58
|
+
module.exports = __toCommonJS(index_exports);
|
|
59
|
+
|
|
60
|
+
// src/connection.ts
|
|
61
|
+
var import_plugin_sql = __toESM(require("@tauri-apps/plugin-sql"));
|
|
62
|
+
var db = null;
|
|
63
|
+
async function initDb(dbPath) {
|
|
64
|
+
db = await import_plugin_sql.default.load(dbPath);
|
|
65
|
+
return db;
|
|
66
|
+
}
|
|
67
|
+
function getDb() {
|
|
68
|
+
if (!db) {
|
|
69
|
+
throw new Error("Database not initialized. Please call initDb() first.");
|
|
70
|
+
}
|
|
71
|
+
return db;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/schema-builder.ts
|
|
75
|
+
function sql(strings, ...values) {
|
|
76
|
+
const raw = strings.reduce(
|
|
77
|
+
(acc, part, idx) => acc + part + (idx < values.length ? String(values[idx]) : ""),
|
|
78
|
+
""
|
|
79
|
+
);
|
|
80
|
+
return { raw };
|
|
81
|
+
}
|
|
82
|
+
function createColumn(params) {
|
|
83
|
+
const col = { ...params };
|
|
84
|
+
col.primaryKey = (opts) => {
|
|
85
|
+
col.isPrimaryKey = true;
|
|
86
|
+
if (opts?.autoIncrement) col.autoIncrement = true;
|
|
87
|
+
return col;
|
|
88
|
+
};
|
|
89
|
+
col.notNull = () => {
|
|
90
|
+
col.isNotNull = true;
|
|
91
|
+
return col;
|
|
92
|
+
};
|
|
93
|
+
col.default = (value) => {
|
|
94
|
+
col.defaultValue = value;
|
|
95
|
+
return col;
|
|
96
|
+
};
|
|
97
|
+
col.$type = () => col;
|
|
98
|
+
col.references = (target, actions) => {
|
|
99
|
+
const t = target();
|
|
100
|
+
col.references = {
|
|
101
|
+
table: t.tableName,
|
|
102
|
+
column: t.name,
|
|
103
|
+
onDelete: actions?.onDelete,
|
|
104
|
+
onUpdate: actions?.onUpdate
|
|
105
|
+
};
|
|
106
|
+
return col;
|
|
107
|
+
};
|
|
108
|
+
return col;
|
|
109
|
+
}
|
|
110
|
+
function text(name, config) {
|
|
111
|
+
const col = createColumn({
|
|
112
|
+
name,
|
|
113
|
+
type: "TEXT",
|
|
114
|
+
isPrimaryKey: config?.isPrimaryKey,
|
|
115
|
+
_dataType: ""
|
|
116
|
+
});
|
|
117
|
+
if (config?.enum) col.enumValues = config.enum;
|
|
118
|
+
return col;
|
|
119
|
+
}
|
|
120
|
+
function integer(name, config) {
|
|
121
|
+
let dt = 0;
|
|
122
|
+
if (config?.mode === "boolean") dt = false;
|
|
123
|
+
if (config?.mode === "timestamp") dt = /* @__PURE__ */ new Date();
|
|
124
|
+
const col = createColumn({
|
|
125
|
+
name,
|
|
126
|
+
type: "INTEGER",
|
|
127
|
+
isPrimaryKey: config?.isPrimaryKey,
|
|
128
|
+
autoIncrement: config?.autoIncrement,
|
|
129
|
+
mode: config?.mode ?? "number",
|
|
130
|
+
_dataType: dt
|
|
131
|
+
});
|
|
132
|
+
return col;
|
|
133
|
+
}
|
|
134
|
+
function real(name) {
|
|
135
|
+
return createColumn({ name, type: "REAL", _dataType: 0 });
|
|
136
|
+
}
|
|
137
|
+
function blob(name, config) {
|
|
138
|
+
let dt = new Uint8Array();
|
|
139
|
+
if (config?.mode === "bigint") dt = 0n;
|
|
140
|
+
if (config?.mode === "json") dt = void 0;
|
|
141
|
+
return createColumn({
|
|
142
|
+
name,
|
|
143
|
+
type: "BLOB",
|
|
144
|
+
mode: config?.mode,
|
|
145
|
+
_dataType: dt
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
function numeric(name, config) {
|
|
149
|
+
let dt = "";
|
|
150
|
+
if (config?.mode === "number") dt = 0;
|
|
151
|
+
if (config?.mode === "bigint") dt = 0n;
|
|
152
|
+
return createColumn({
|
|
153
|
+
name,
|
|
154
|
+
type: "NUMERIC",
|
|
155
|
+
mode: config?.mode,
|
|
156
|
+
_dataType: dt
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
var boolean = (name) => createColumn({
|
|
160
|
+
name,
|
|
161
|
+
type: "INTEGER",
|
|
162
|
+
_dataType: false,
|
|
163
|
+
mode: "boolean"
|
|
164
|
+
});
|
|
165
|
+
var timestamp = (name) => createColumn({
|
|
166
|
+
name,
|
|
167
|
+
type: "INTEGER",
|
|
168
|
+
_dataType: /* @__PURE__ */ new Date(),
|
|
169
|
+
mode: "timestamp"
|
|
170
|
+
});
|
|
171
|
+
function defineTable(tableName, schema) {
|
|
172
|
+
const finalizedSchema = { ...schema };
|
|
173
|
+
for (const key of Object.keys(finalizedSchema)) {
|
|
174
|
+
const col = finalizedSchema[key];
|
|
175
|
+
col.tableName = tableName;
|
|
176
|
+
}
|
|
177
|
+
const table = {
|
|
178
|
+
_tableName: tableName,
|
|
179
|
+
_schema: finalizedSchema,
|
|
180
|
+
// The Drizzle-like type inference properties
|
|
181
|
+
$inferSelect: {},
|
|
182
|
+
$inferInsert: {}
|
|
183
|
+
// Example: omit 'id' for inserts
|
|
184
|
+
};
|
|
185
|
+
for (const [key, col] of Object.entries(finalizedSchema)) {
|
|
186
|
+
table[key] = col;
|
|
187
|
+
}
|
|
188
|
+
return table;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// src/orm.ts
|
|
192
|
+
var SelectQueryBuilder = class {
|
|
193
|
+
_table = null;
|
|
194
|
+
_selectedColumns = ["*"];
|
|
195
|
+
_joins = [];
|
|
196
|
+
_where = [];
|
|
197
|
+
_orderBy = [];
|
|
198
|
+
_limit = null;
|
|
199
|
+
_offset = null;
|
|
200
|
+
_eager = {};
|
|
201
|
+
constructor(fields) {
|
|
202
|
+
if (fields) {
|
|
203
|
+
this._selectedColumns = Object.values(fields).map((c) => c.name);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
from(table) {
|
|
207
|
+
this._table = table;
|
|
208
|
+
return this;
|
|
209
|
+
}
|
|
210
|
+
where(...conditions) {
|
|
211
|
+
this._where.push(...conditions);
|
|
212
|
+
return this;
|
|
213
|
+
}
|
|
214
|
+
leftJoin(otherTable, on) {
|
|
215
|
+
const onSql = on.toSQL();
|
|
216
|
+
const joinClause = `LEFT JOIN ${otherTable._tableName} ON ${onSql.clause}`;
|
|
217
|
+
this._joins.push(joinClause);
|
|
218
|
+
return this;
|
|
219
|
+
}
|
|
220
|
+
orderBy(...clauses) {
|
|
221
|
+
this._orderBy.push(...clauses);
|
|
222
|
+
return this;
|
|
223
|
+
}
|
|
224
|
+
limit(value) {
|
|
225
|
+
this._limit = value;
|
|
226
|
+
return this;
|
|
227
|
+
}
|
|
228
|
+
offset(value) {
|
|
229
|
+
this._offset = value;
|
|
230
|
+
return this;
|
|
231
|
+
}
|
|
232
|
+
// The final execution step
|
|
233
|
+
async execute() {
|
|
234
|
+
if (!this._table) {
|
|
235
|
+
throw new Error("Cannot execute select query without a 'from' table.");
|
|
236
|
+
}
|
|
237
|
+
const db3 = getDb();
|
|
238
|
+
const bindings = [];
|
|
239
|
+
let query = `SELECT ${this._selectedColumns.join(", ")} FROM ${this._table._tableName}`;
|
|
240
|
+
if (this._joins.length > 0) {
|
|
241
|
+
query += ` ${this._joins.join(" ")}`;
|
|
242
|
+
}
|
|
243
|
+
if (this._where.length > 0) {
|
|
244
|
+
const whereClauses = this._where.map((condition) => {
|
|
245
|
+
const sql2 = condition.toSQL();
|
|
246
|
+
bindings.push(...sql2.bindings);
|
|
247
|
+
return `(${sql2.clause})`;
|
|
248
|
+
});
|
|
249
|
+
query += ` WHERE ${whereClauses.join(" AND ")}`;
|
|
250
|
+
}
|
|
251
|
+
if (this._orderBy.length > 0) {
|
|
252
|
+
query += ` ORDER BY ${this._orderBy.join(", ")}`;
|
|
253
|
+
}
|
|
254
|
+
if (this._limit !== null) {
|
|
255
|
+
query += ` LIMIT ?`;
|
|
256
|
+
bindings.push(this._limit);
|
|
257
|
+
}
|
|
258
|
+
if (this._offset !== null) {
|
|
259
|
+
if (this._limit === null) {
|
|
260
|
+
query += ` LIMIT -1`;
|
|
261
|
+
}
|
|
262
|
+
query += ` OFFSET ?`;
|
|
263
|
+
bindings.push(this._offset);
|
|
264
|
+
}
|
|
265
|
+
return db3.select(query, bindings);
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
var TauriORM = class {
|
|
269
|
+
// Soft relational query API: db.query.users.findMany({ with: { posts: true } })
|
|
270
|
+
query = {};
|
|
271
|
+
_tables = null;
|
|
272
|
+
_relations = null;
|
|
273
|
+
// Deprecated: use configure()
|
|
274
|
+
configureQuery(tables, relations2) {
|
|
275
|
+
this.configure(tables, relations2);
|
|
276
|
+
}
|
|
277
|
+
select(fields) {
|
|
278
|
+
return new SelectQueryBuilder(fields);
|
|
279
|
+
}
|
|
280
|
+
// --- Drizzle-style CRUD builders ---
|
|
281
|
+
insert(table) {
|
|
282
|
+
return new class InsertBuilder {
|
|
283
|
+
_table = table;
|
|
284
|
+
_rows = [];
|
|
285
|
+
values(rowOrRows) {
|
|
286
|
+
this._rows = Array.isArray(rowOrRows) ? rowOrRows : [rowOrRows];
|
|
287
|
+
return this;
|
|
288
|
+
}
|
|
289
|
+
async execute() {
|
|
290
|
+
const db3 = getDb();
|
|
291
|
+
for (const data of this._rows) {
|
|
292
|
+
const keys = Object.keys(data);
|
|
293
|
+
const values = Object.values(data);
|
|
294
|
+
const placeholders = values.map(() => "?").join(", ");
|
|
295
|
+
const query = `INSERT INTO ${this._table._tableName} (${keys.join(
|
|
296
|
+
", "
|
|
297
|
+
)}) VALUES (${placeholders})`;
|
|
298
|
+
await db3.execute(query, values);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}();
|
|
302
|
+
}
|
|
303
|
+
update(table) {
|
|
304
|
+
return new class UpdateBuilder {
|
|
305
|
+
_table = table;
|
|
306
|
+
_data = null;
|
|
307
|
+
_where = null;
|
|
308
|
+
set(data) {
|
|
309
|
+
this._data = data;
|
|
310
|
+
return this;
|
|
311
|
+
}
|
|
312
|
+
where(cond) {
|
|
313
|
+
this._where = cond;
|
|
314
|
+
return this;
|
|
315
|
+
}
|
|
316
|
+
async execute() {
|
|
317
|
+
if (!this._data)
|
|
318
|
+
throw new Error("Update requires set() before execute()");
|
|
319
|
+
const db3 = getDb();
|
|
320
|
+
const setKeys = Object.keys(this._data);
|
|
321
|
+
const setClause = setKeys.map((k) => `${k} = ?`).join(", ");
|
|
322
|
+
const bindings = Object.values(this._data);
|
|
323
|
+
let query = `UPDATE ${this._table._tableName} SET ${setClause}`;
|
|
324
|
+
if (this._where) {
|
|
325
|
+
if (typeof this._where.toSQL === "function") {
|
|
326
|
+
const sql2 = this._where.toSQL();
|
|
327
|
+
query += ` WHERE ${sql2.clause}`;
|
|
328
|
+
bindings.push(...sql2.bindings);
|
|
329
|
+
} else {
|
|
330
|
+
const entries = Object.entries(this._where);
|
|
331
|
+
if (entries.length > 0) {
|
|
332
|
+
query += ` WHERE ${entries.map(([k]) => `${k} = ?`).join(" AND ")}`;
|
|
333
|
+
bindings.push(...entries.map(([, v]) => v));
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
await db3.execute(query, bindings);
|
|
338
|
+
}
|
|
339
|
+
}();
|
|
340
|
+
}
|
|
341
|
+
delete(table) {
|
|
342
|
+
return new class DeleteBuilder {
|
|
343
|
+
_table = table;
|
|
344
|
+
_where = null;
|
|
345
|
+
where(cond) {
|
|
346
|
+
this._where = cond;
|
|
347
|
+
return this;
|
|
348
|
+
}
|
|
349
|
+
async execute() {
|
|
350
|
+
const db3 = getDb();
|
|
351
|
+
let query = `DELETE FROM ${this._table._tableName}`;
|
|
352
|
+
const bindings = [];
|
|
353
|
+
if (this._where) {
|
|
354
|
+
if (typeof this._where.toSQL === "function") {
|
|
355
|
+
const sql2 = this._where.toSQL();
|
|
356
|
+
query += ` WHERE ${sql2.clause}`;
|
|
357
|
+
bindings.push(...sql2.bindings);
|
|
358
|
+
} else {
|
|
359
|
+
const entries = Object.entries(this._where);
|
|
360
|
+
if (entries.length > 0) {
|
|
361
|
+
query += ` WHERE ${entries.map(([k]) => `${k} = ?`).join(" AND ")}`;
|
|
362
|
+
bindings.push(...entries.map(([, v]) => v));
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
await db3.execute(query, bindings);
|
|
367
|
+
}
|
|
368
|
+
}();
|
|
369
|
+
}
|
|
370
|
+
// legacy direct methods removed in favor of builder APIs
|
|
371
|
+
// legacy direct methods removed in favor of builder APIs
|
|
372
|
+
// legacy direct methods removed in favor of builder APIs
|
|
373
|
+
async run(query, bindings = []) {
|
|
374
|
+
const db3 = getDb();
|
|
375
|
+
await db3.execute(query, bindings);
|
|
376
|
+
}
|
|
377
|
+
// --- Migrations API ---
|
|
378
|
+
generateCreateTableSql(table) {
|
|
379
|
+
const tableName = table._tableName;
|
|
380
|
+
const columns = Object.values(table._schema);
|
|
381
|
+
const columnDefs = columns.map((col) => {
|
|
382
|
+
let def = `${col.name} ${col.type}`;
|
|
383
|
+
if (col.isPrimaryKey) {
|
|
384
|
+
def += col.autoIncrement ? " PRIMARY KEY AUTOINCREMENT" : " PRIMARY KEY";
|
|
385
|
+
}
|
|
386
|
+
if (col.isNotNull) def += " NOT NULL";
|
|
387
|
+
if (col.defaultValue !== void 0) {
|
|
388
|
+
const dv = col.defaultValue;
|
|
389
|
+
if (dv && typeof dv === "object" && "raw" in dv) {
|
|
390
|
+
def += ` DEFAULT ${dv.raw}`;
|
|
391
|
+
} else if (typeof dv === "string") {
|
|
392
|
+
def += ` DEFAULT '${dv.replace(/'/g, "''")}'`;
|
|
393
|
+
} else {
|
|
394
|
+
def += ` DEFAULT ${dv}`;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
if (col.references && !col.isPrimaryKey) {
|
|
398
|
+
def += ` REFERENCES ${col.references.table} (${col.references.column})`;
|
|
399
|
+
if (col.references.onDelete)
|
|
400
|
+
def += ` ON DELETE ${col.references.onDelete.toUpperCase()}`;
|
|
401
|
+
if (col.references.onUpdate)
|
|
402
|
+
def += ` ON UPDATE ${col.references.onUpdate.toUpperCase()}`;
|
|
403
|
+
}
|
|
404
|
+
return def;
|
|
405
|
+
});
|
|
406
|
+
return `CREATE TABLE IF NOT EXISTS ${tableName} (${columnDefs.join(
|
|
407
|
+
", "
|
|
408
|
+
)});`;
|
|
409
|
+
}
|
|
410
|
+
async createTableIfNotExists(table) {
|
|
411
|
+
const sql2 = this.generateCreateTableSql(table);
|
|
412
|
+
await this.run(sql2);
|
|
413
|
+
}
|
|
414
|
+
async createTablesIfNotExist(tables) {
|
|
415
|
+
for (const t of tables) {
|
|
416
|
+
await this.createTableIfNotExists(t);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
async ensureMigrationsTable() {
|
|
420
|
+
await this.run(
|
|
421
|
+
`CREATE TABLE IF NOT EXISTS _migrations (name TEXT PRIMARY KEY, applied_at INTEGER NOT NULL)`
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
async hasMigration(name) {
|
|
425
|
+
const db3 = getDb();
|
|
426
|
+
const rows = await db3.select(
|
|
427
|
+
`SELECT name FROM _migrations WHERE name = ?`,
|
|
428
|
+
[name]
|
|
429
|
+
);
|
|
430
|
+
return Array.isArray(rows) && rows.length > 0;
|
|
431
|
+
}
|
|
432
|
+
async recordMigration(name) {
|
|
433
|
+
const db3 = getDb();
|
|
434
|
+
await db3.execute(
|
|
435
|
+
`INSERT INTO _migrations (name, applied_at) VALUES (?, ?)`,
|
|
436
|
+
[name, Date.now()]
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
async migrate(tables, options) {
|
|
440
|
+
const track = options?.track ?? true;
|
|
441
|
+
if (track) {
|
|
442
|
+
await this.ensureMigrationsTable();
|
|
443
|
+
const name = options?.name ?? `init:${tables.map((t) => t._tableName).join(",")}`;
|
|
444
|
+
const already = await this.hasMigration(name);
|
|
445
|
+
if (already) return;
|
|
446
|
+
await this.createTablesIfNotExist(tables);
|
|
447
|
+
await this.recordMigration(name);
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
await this.createTablesIfNotExist(tables);
|
|
451
|
+
}
|
|
452
|
+
// Configure schema and relations, and generate db.query automatically
|
|
453
|
+
configure(tables, relDefs) {
|
|
454
|
+
this._tables = tables;
|
|
455
|
+
this._relations = relDefs ?? {};
|
|
456
|
+
this.query = makeQueryAPI(tables, this._relations);
|
|
457
|
+
return this;
|
|
458
|
+
}
|
|
459
|
+
// Convenience: migrate from configured tables
|
|
460
|
+
async migrateConfigured(options) {
|
|
461
|
+
if (!this._tables)
|
|
462
|
+
throw new Error("No tables configured. Call db.configure({...}) first.");
|
|
463
|
+
await this.migrate(Object.values(this._tables), options);
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
var db2 = new TauriORM();
|
|
467
|
+
function relations(baseTable, builder) {
|
|
468
|
+
const ctx = {
|
|
469
|
+
one: (table, cfg) => ({ kind: "one", table, cfg }),
|
|
470
|
+
many: (table, cfg) => ({ kind: "many", table, cfg })
|
|
471
|
+
};
|
|
472
|
+
const rels = builder(ctx);
|
|
473
|
+
return rels;
|
|
474
|
+
}
|
|
475
|
+
function getPrimaryKey(table) {
|
|
476
|
+
const cols = Object.values(table._schema);
|
|
477
|
+
return cols.find((c) => c.isPrimaryKey) || cols.find((c) => c.name === "id") || cols[0];
|
|
478
|
+
}
|
|
479
|
+
function guessChildFk(child, base, rel) {
|
|
480
|
+
const childCols = Object.values(child._schema);
|
|
481
|
+
if (rel && rel.kind === "one" && rel.cfg?.fields?.[0])
|
|
482
|
+
return rel.cfg.fields[0];
|
|
483
|
+
const basePk = getPrimaryKey(base);
|
|
484
|
+
const guessNames = [
|
|
485
|
+
`${base._tableName}_id`,
|
|
486
|
+
`${base._tableName}Id`,
|
|
487
|
+
`${basePk.name}`,
|
|
488
|
+
`${base._tableName.slice(0, -1)}Id`
|
|
489
|
+
];
|
|
490
|
+
return childCols.find((c) => guessNames.includes(c.name)) || childCols.find((c) => /.*_id$/i.test(c.name)) || null;
|
|
491
|
+
}
|
|
492
|
+
function isFlatWith(spec) {
|
|
493
|
+
return Object.values(spec).every((v) => typeof v === "boolean");
|
|
494
|
+
}
|
|
495
|
+
function makeQueryAPI(tables, relDefs) {
|
|
496
|
+
const api = {};
|
|
497
|
+
const tableKeyByName = {};
|
|
498
|
+
for (const [k, t] of Object.entries(tables)) tableKeyByName[t._tableName] = k;
|
|
499
|
+
for (const [tblKey, tbl] of Object.entries(tables)) {
|
|
500
|
+
api[tblKey] = {
|
|
501
|
+
async findMany(opts) {
|
|
502
|
+
const base = tbl;
|
|
503
|
+
const withSpec = opts?.with ?? {};
|
|
504
|
+
const dbi = getDb();
|
|
505
|
+
const rels = relDefs[tblKey] ?? {};
|
|
506
|
+
if (opts?.join && isFlatWith(withSpec)) {
|
|
507
|
+
const baseCols = Object.values(base._schema);
|
|
508
|
+
const basePk = baseCols.find((c) => c.isPrimaryKey) || baseCols.find((c) => c.name === "id") || baseCols[0];
|
|
509
|
+
const selectParts = [];
|
|
510
|
+
let baseSelected2;
|
|
511
|
+
if (opts?.columns && !Array.isArray(opts.columns)) {
|
|
512
|
+
baseSelected2 = Object.entries(opts.columns).filter(([, v]) => !!v).map(([k]) => k);
|
|
513
|
+
} else if (Array.isArray(opts?.columns) && opts.columns.length > 0) {
|
|
514
|
+
baseSelected2 = opts.columns;
|
|
515
|
+
} else {
|
|
516
|
+
baseSelected2 = baseCols.map((c) => c.name);
|
|
517
|
+
}
|
|
518
|
+
for (const name of baseSelected2)
|
|
519
|
+
selectParts.push(`${base._tableName}.${name} AS __base_${name}`);
|
|
520
|
+
const joins = [];
|
|
521
|
+
const relColsMap = {};
|
|
522
|
+
const fkMap = {};
|
|
523
|
+
for (const [relName, enabled] of Object.entries(withSpec)) {
|
|
524
|
+
if (!enabled) continue;
|
|
525
|
+
const rel = rels[relName];
|
|
526
|
+
if (!rel) continue;
|
|
527
|
+
const child = rel.table;
|
|
528
|
+
const childCols = Object.values(child._schema);
|
|
529
|
+
const childPk = childCols.find((c) => c.isPrimaryKey) || childCols.find((c) => c.name === "id") || null;
|
|
530
|
+
const childFk = guessChildFk(child, base, rel);
|
|
531
|
+
if (!childFk) continue;
|
|
532
|
+
fkMap[relName] = { childFk, childPk };
|
|
533
|
+
const selected = typeof enabled === "object" && enabled.columns?.length ? enabled.columns : childCols.map((c) => c.name);
|
|
534
|
+
relColsMap[relName] = selected;
|
|
535
|
+
for (const name of selected)
|
|
536
|
+
selectParts.push(
|
|
537
|
+
`${child._tableName}.${name} AS __rel_${relName}_${name}`
|
|
538
|
+
);
|
|
539
|
+
joins.push(
|
|
540
|
+
`LEFT JOIN ${child._tableName} ON ${child._tableName}.${childFk.name} = ${base._tableName}.${basePk.name}`
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
let sqlText = `SELECT ${selectParts.join(", ")} FROM ${base._tableName}${joins.length ? " " + joins.join(" ") : ""}`;
|
|
544
|
+
const bindings = [];
|
|
545
|
+
if (opts?.where) {
|
|
546
|
+
if (typeof opts.where.toSQL === "function") {
|
|
547
|
+
const w = opts.where.toSQL();
|
|
548
|
+
sqlText += ` WHERE ${w.clause}`;
|
|
549
|
+
bindings.push(...w.bindings);
|
|
550
|
+
} else {
|
|
551
|
+
const entries = Object.entries(opts.where);
|
|
552
|
+
if (entries.length > 0) {
|
|
553
|
+
sqlText += ` WHERE ${entries.map(([k]) => `${base._tableName}.${k} = ?`).join(" AND ")}`;
|
|
554
|
+
bindings.push(...entries.map(([, v]) => v));
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
if (opts?.orderBy?.length)
|
|
559
|
+
sqlText += ` ORDER BY ${opts.orderBy.join(", ")}`;
|
|
560
|
+
if (typeof opts?.limit === "number")
|
|
561
|
+
sqlText += ` LIMIT ${opts.limit}`;
|
|
562
|
+
if (typeof opts?.offset === "number")
|
|
563
|
+
sqlText += ` OFFSET ${opts.offset}`;
|
|
564
|
+
const rows = await dbi.select(sqlText, bindings);
|
|
565
|
+
const groups = /* @__PURE__ */ new Map();
|
|
566
|
+
for (const row of rows) {
|
|
567
|
+
const baseObj = {};
|
|
568
|
+
for (const name of baseSelected2)
|
|
569
|
+
baseObj[name] = row[`__base_${name}`];
|
|
570
|
+
const baseKey = baseObj[basePk.name];
|
|
571
|
+
if (!groups.has(baseKey)) {
|
|
572
|
+
const seed = { ...baseObj };
|
|
573
|
+
for (const [relName, enabled] of Object.entries(withSpec)) {
|
|
574
|
+
if (!enabled) continue;
|
|
575
|
+
const rel = rels[relName];
|
|
576
|
+
if (!rel) continue;
|
|
577
|
+
seed[relName] = rel.kind === "many" ? [] : null;
|
|
578
|
+
}
|
|
579
|
+
groups.set(baseKey, seed);
|
|
580
|
+
}
|
|
581
|
+
const acc = groups.get(baseKey);
|
|
582
|
+
for (const [relName, enabled] of Object.entries(withSpec)) {
|
|
583
|
+
if (!enabled) continue;
|
|
584
|
+
const rel = rels[relName];
|
|
585
|
+
if (!rel) continue;
|
|
586
|
+
const childCols = relColsMap[relName];
|
|
587
|
+
const childObj = {};
|
|
588
|
+
let allNull = true;
|
|
589
|
+
for (const name of childCols) {
|
|
590
|
+
const v = row[`__rel_${relName}_${name}`];
|
|
591
|
+
childObj[name] = v;
|
|
592
|
+
if (v !== null && v !== void 0) allNull = false;
|
|
593
|
+
}
|
|
594
|
+
if (allNull) continue;
|
|
595
|
+
if (rel.kind === "many") {
|
|
596
|
+
const childPk = fkMap[relName].childPk;
|
|
597
|
+
if (childPk) {
|
|
598
|
+
if (!acc[relName]) acc[relName] = [];
|
|
599
|
+
const exists = acc[relName].some(
|
|
600
|
+
(r) => r[childPk.name] === childObj[childPk.name]
|
|
601
|
+
);
|
|
602
|
+
if (!exists) acc[relName].push(childObj);
|
|
603
|
+
} else {
|
|
604
|
+
acc[relName].push(childObj);
|
|
605
|
+
}
|
|
606
|
+
} else {
|
|
607
|
+
acc[relName] = childObj;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return Array.from(groups.values());
|
|
612
|
+
}
|
|
613
|
+
let baseSelected;
|
|
614
|
+
if (opts?.columns && !Array.isArray(opts.columns)) {
|
|
615
|
+
baseSelected = Object.entries(opts.columns).filter(([, v]) => !!v).map(([k]) => k);
|
|
616
|
+
} else if (Array.isArray(opts?.columns) && opts.columns.length > 0) {
|
|
617
|
+
baseSelected = opts.columns;
|
|
618
|
+
} else {
|
|
619
|
+
baseSelected = Object.values(base._schema).map(
|
|
620
|
+
(c) => c.name
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
let baseSql = `SELECT ${baseSelected.join(", ")} FROM ${base._tableName}`;
|
|
624
|
+
const baseBindings = [];
|
|
625
|
+
if (opts?.where) {
|
|
626
|
+
if (typeof opts.where.toSQL === "function") {
|
|
627
|
+
const w = opts.where.toSQL();
|
|
628
|
+
baseSql += ` WHERE ${w.clause}`;
|
|
629
|
+
baseBindings.push(...w.bindings);
|
|
630
|
+
} else {
|
|
631
|
+
const entries = Object.entries(opts.where);
|
|
632
|
+
if (entries.length > 0) {
|
|
633
|
+
baseSql += ` WHERE ${entries.map(([k]) => `${k} = ?`).join(" AND ")}`;
|
|
634
|
+
baseBindings.push(...entries.map(([, v]) => v));
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
if (opts?.orderBy?.length)
|
|
639
|
+
baseSql += ` ORDER BY ${opts.orderBy.join(", ")}`;
|
|
640
|
+
if (typeof opts?.limit === "number") baseSql += ` LIMIT ${opts.limit}`;
|
|
641
|
+
if (typeof opts?.offset === "number")
|
|
642
|
+
baseSql += ` OFFSET ${opts.offset}`;
|
|
643
|
+
const baseRows = await dbi.select(baseSql, baseBindings);
|
|
644
|
+
const result = baseRows.map((r) => ({ ...r }));
|
|
645
|
+
async function loadRelationsFor(parents, parentTable, spec) {
|
|
646
|
+
const parentPk = getPrimaryKey(parentTable);
|
|
647
|
+
const parentIds = parents.map((p) => p[parentPk.name]);
|
|
648
|
+
const relsMap = relDefs[Object.keys(tables).find(
|
|
649
|
+
(k) => tables[k]._tableName === parentTable._tableName
|
|
650
|
+
)] || {};
|
|
651
|
+
for (const [relName, v] of Object.entries(spec)) {
|
|
652
|
+
const enabled = v;
|
|
653
|
+
const rel = relsMap[relName];
|
|
654
|
+
if (!rel) continue;
|
|
655
|
+
const child = rel.table;
|
|
656
|
+
const childCols = Object.values(child._schema);
|
|
657
|
+
const selectCols = enabled?.columns && enabled.columns.length > 0 ? enabled.columns : childCols.map((c) => c.name);
|
|
658
|
+
const fkCol = guessChildFk(child, parentTable, rel);
|
|
659
|
+
if (!fkCol) continue;
|
|
660
|
+
if (rel.kind === "many") {
|
|
661
|
+
const sql2 = `SELECT ${selectCols.join(", ")} FROM ${child._tableName} WHERE ${fkCol.name} IN (${parentIds.map(() => "?").join(", ")})`;
|
|
662
|
+
const rows = await dbi.select(sql2, parentIds);
|
|
663
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
664
|
+
for (const r of rows) {
|
|
665
|
+
const key = r[fkCol.name];
|
|
666
|
+
if (!buckets.has(key)) buckets.set(key, []);
|
|
667
|
+
buckets.get(key).push(r);
|
|
668
|
+
}
|
|
669
|
+
for (const p of parents)
|
|
670
|
+
p[relName] = buckets.get(p[parentPk.name]) ?? [];
|
|
671
|
+
if (enabled?.with) {
|
|
672
|
+
const children = parents.flatMap(
|
|
673
|
+
(p) => p[relName]
|
|
674
|
+
);
|
|
675
|
+
if (children.length > 0)
|
|
676
|
+
await loadRelationsFor(children, child, enabled.with);
|
|
677
|
+
}
|
|
678
|
+
} else {
|
|
679
|
+
const sql2 = `SELECT ${selectCols.join(", ")} FROM ${child._tableName} WHERE ${fkCol.name} IN (${parentIds.map(() => "?").join(", ")})`;
|
|
680
|
+
const rows = await dbi.select(sql2, parentIds);
|
|
681
|
+
const mapOne = /* @__PURE__ */ new Map();
|
|
682
|
+
for (const r of rows) mapOne.set(r[fkCol.name], r);
|
|
683
|
+
for (const p of parents)
|
|
684
|
+
p[relName] = mapOne.get(p[parentPk.name]) ?? null;
|
|
685
|
+
if (enabled?.with) {
|
|
686
|
+
const children = parents.map((p) => p[relName]).filter(Boolean);
|
|
687
|
+
if (children.length > 0)
|
|
688
|
+
await loadRelationsFor(children, child, enabled.with);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
if (Object.keys(withSpec).length > 0) {
|
|
694
|
+
await loadRelationsFor(result, base, withSpec);
|
|
695
|
+
}
|
|
696
|
+
return result;
|
|
697
|
+
}
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
return api;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// src/sql-helpers.ts
|
|
704
|
+
function isColumn(value) {
|
|
705
|
+
return typeof value === "object" && value !== null && "_dataType" in value;
|
|
706
|
+
}
|
|
707
|
+
function getQualifiedName(column) {
|
|
708
|
+
if (column.tableName) return `${column.tableName}.${column.name}`;
|
|
709
|
+
return column.name;
|
|
710
|
+
}
|
|
711
|
+
function comparison(operator, column, value) {
|
|
712
|
+
return {
|
|
713
|
+
toSQL: () => {
|
|
714
|
+
if (isColumn(value)) {
|
|
715
|
+
return {
|
|
716
|
+
clause: `${getQualifiedName(column)} ${operator} ${getQualifiedName(
|
|
717
|
+
value
|
|
718
|
+
)}`,
|
|
719
|
+
bindings: []
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
return {
|
|
723
|
+
clause: `${getQualifiedName(column)} ${operator} ?`,
|
|
724
|
+
bindings: [value]
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
var eq = (column, value) => comparison("=", column, value);
|
|
730
|
+
var ne = (column, value) => comparison("!=", column, value);
|
|
731
|
+
var gt = (column, value) => comparison(">", column, value);
|
|
732
|
+
var gte = (column, value) => comparison(">=", column, value);
|
|
733
|
+
var lt = (column, value) => comparison("<", column, value);
|
|
734
|
+
var lte = (column, value) => comparison("<=", column, value);
|
|
735
|
+
var like = (column, value) => comparison("LIKE", column, value);
|
|
736
|
+
var asc = (column) => `${column.name} ASC`;
|
|
737
|
+
var desc = (column) => `${column.name} DESC`;
|
|
738
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
739
|
+
0 && (module.exports = {
|
|
740
|
+
TauriORM,
|
|
741
|
+
asc,
|
|
742
|
+
blob,
|
|
743
|
+
boolean,
|
|
744
|
+
db,
|
|
745
|
+
defineTable,
|
|
746
|
+
desc,
|
|
747
|
+
eq,
|
|
748
|
+
getDb,
|
|
749
|
+
gt,
|
|
750
|
+
gte,
|
|
751
|
+
initDb,
|
|
752
|
+
integer,
|
|
753
|
+
like,
|
|
754
|
+
lt,
|
|
755
|
+
lte,
|
|
756
|
+
makeQueryAPI,
|
|
757
|
+
ne,
|
|
758
|
+
numeric,
|
|
759
|
+
real,
|
|
760
|
+
relations,
|
|
761
|
+
sql,
|
|
762
|
+
text,
|
|
763
|
+
timestamp
|
|
764
|
+
});
|