@farming-labs/orm-sql 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,900 @@
1
+ // src/index.ts
2
+ import { randomUUID } from "crypto";
3
+ import {
4
+ createManifest
5
+ } from "@farming-labs/orm";
6
+ var manifestCache = /* @__PURE__ */ new WeakMap();
7
+ function getManifest(schema) {
8
+ const cached = manifestCache.get(schema);
9
+ if (cached) return cached;
10
+ const next = createManifest(schema);
11
+ manifestCache.set(schema, next);
12
+ return next;
13
+ }
14
+ function quoteIdentifier(value, dialect) {
15
+ if (dialect === "mysql") {
16
+ return `\`${value.replace(/`/g, "``")}\``;
17
+ }
18
+ return `"${value.replace(/"/g, '""')}"`;
19
+ }
20
+ function createPlaceholder(dialect, state, value) {
21
+ state.params.push(value);
22
+ return dialect === "postgres" ? `$${state.params.length}` : "?";
23
+ }
24
+ function parseReference(reference) {
25
+ if (!reference) return null;
26
+ const [model, field] = reference.split(".");
27
+ if (!model || !field) return null;
28
+ return { model, field };
29
+ }
30
+ function identityField(model) {
31
+ if (model.fields.id) return model.fields.id;
32
+ const uniqueField = Object.values(model.fields).find((field) => field.unique);
33
+ if (uniqueField) return uniqueField;
34
+ throw new Error(
35
+ `Model "${model.name}" requires an "id" field or a unique field for the SQL runtime.`
36
+ );
37
+ }
38
+ function applyDefault(value, field) {
39
+ if (value !== void 0) return value;
40
+ if (field.generated === "id") return randomUUID();
41
+ if (field.generated === "now") return /* @__PURE__ */ new Date();
42
+ if (typeof field.defaultValue === "function") {
43
+ return field.defaultValue();
44
+ }
45
+ return field.defaultValue;
46
+ }
47
+ function encodeValue(field, dialect, value) {
48
+ if (value === void 0) return value;
49
+ if (value === null) return null;
50
+ if (field.kind === "boolean") {
51
+ if (dialect === "postgres") return Boolean(value);
52
+ return value ? 1 : 0;
53
+ }
54
+ if (field.kind === "datetime") {
55
+ return value instanceof Date ? value.toISOString() : value;
56
+ }
57
+ return value;
58
+ }
59
+ function decodeValue(field, value) {
60
+ if (value === void 0) return value;
61
+ if (value === null) return null;
62
+ if (field.kind === "boolean") {
63
+ if (typeof value === "boolean") return value;
64
+ if (typeof value === "number") return value !== 0;
65
+ if (typeof value === "string") {
66
+ return value === "1" || value.toLowerCase() === "true" || value.toLowerCase() === "t";
67
+ }
68
+ }
69
+ if (field.kind === "datetime") {
70
+ if (value instanceof Date) return value;
71
+ return new Date(String(value));
72
+ }
73
+ return value;
74
+ }
75
+ function decodeRow(model, row) {
76
+ const output = {};
77
+ for (const field of Object.values(model.fields)) {
78
+ output[field.name] = decodeValue(field, row[field.name]);
79
+ }
80
+ return output;
81
+ }
82
+ function mergeWhere(...clauses) {
83
+ const defined = clauses.filter(Boolean);
84
+ if (!defined.length) return void 0;
85
+ if (defined.length === 1) return defined[0];
86
+ return {
87
+ AND: defined
88
+ };
89
+ }
90
+ function isFilterObject(value) {
91
+ return !!value && typeof value === "object" && !(value instanceof Date) && !Array.isArray(value);
92
+ }
93
+ function extractEqualityValue(filter) {
94
+ if (!isFilterObject(filter)) {
95
+ return {
96
+ supported: true,
97
+ value: filter
98
+ };
99
+ }
100
+ const keys = Object.keys(filter);
101
+ if (keys.length === 1 && "eq" in filter) {
102
+ return {
103
+ supported: true,
104
+ value: filter.eq
105
+ };
106
+ }
107
+ return {
108
+ supported: false,
109
+ value: void 0
110
+ };
111
+ }
112
+ function extractUpsertConflict(model, where) {
113
+ const keys = Object.keys(where).filter((key) => key !== "AND" && key !== "OR" && key !== "NOT");
114
+ if ("AND" in where || "OR" in where || "NOT" in where || keys.length !== 1) {
115
+ throw new Error(
116
+ `Upsert on model "${model.name}" requires a single unique equality filter in "where".`
117
+ );
118
+ }
119
+ const fieldName = keys[0];
120
+ const field = model.fields[fieldName];
121
+ if (!field) {
122
+ throw new Error(`Unknown field "${fieldName}" on model "${model.name}".`);
123
+ }
124
+ if (!(field.kind === "id" || field.unique)) {
125
+ throw new Error(
126
+ `Upsert on model "${model.name}" requires the "where" field "${fieldName}" to be unique or an id field.`
127
+ );
128
+ }
129
+ const { supported, value } = extractEqualityValue(where[fieldName]);
130
+ if (!supported || value === void 0 || value === null) {
131
+ throw new Error(
132
+ `Upsert on model "${model.name}" requires the "where" field "${fieldName}" to use a single non-null equality value.`
133
+ );
134
+ }
135
+ return {
136
+ field,
137
+ value
138
+ };
139
+ }
140
+ function mergeUpsertCreateData(model, createData, conflict) {
141
+ const currentValue = createData[conflict.field.name];
142
+ if (currentValue !== void 0 && currentValue !== conflict.value) {
143
+ throw new Error(
144
+ `Upsert on model "${model.name}" requires create.${conflict.field.name} to match where.${conflict.field.name}.`
145
+ );
146
+ }
147
+ return {
148
+ ...createData,
149
+ [conflict.field.name]: currentValue ?? conflict.value
150
+ };
151
+ }
152
+ function validateUpsertUpdateData(model, updateData, conflict) {
153
+ const nextValue = updateData[conflict.field.name];
154
+ if (nextValue !== void 0 && nextValue !== conflict.value) {
155
+ throw new Error(
156
+ `Upsert on model "${model.name}" cannot change the conflict field "${conflict.field.name}".`
157
+ );
158
+ }
159
+ }
160
+ function compileFieldFilter(model, fieldName, filter, dialect, state) {
161
+ const field = model.fields[fieldName];
162
+ if (!field) {
163
+ throw new Error(`Unknown field "${fieldName}" on model "${model.name}".`);
164
+ }
165
+ const column = `${quoteIdentifier(model.table, dialect)}.${quoteIdentifier(field.column, dialect)}`;
166
+ if (!isFilterObject(filter)) {
167
+ if (filter === null) return `${column} is null`;
168
+ const placeholder = createPlaceholder(dialect, state, encodeValue(field, dialect, filter));
169
+ return `${column} = ${placeholder}`;
170
+ }
171
+ const clauses = [];
172
+ if ("eq" in filter) {
173
+ if (filter.eq === null) {
174
+ clauses.push(`${column} is null`);
175
+ } else {
176
+ const placeholder = createPlaceholder(dialect, state, encodeValue(field, dialect, filter.eq));
177
+ clauses.push(`${column} = ${placeholder}`);
178
+ }
179
+ }
180
+ if ("not" in filter) {
181
+ if (filter.not === null) {
182
+ clauses.push(`${column} is not null`);
183
+ } else {
184
+ const placeholder = createPlaceholder(
185
+ dialect,
186
+ state,
187
+ encodeValue(field, dialect, filter.not)
188
+ );
189
+ clauses.push(`${column} <> ${placeholder}`);
190
+ }
191
+ }
192
+ if ("in" in filter) {
193
+ const values = Array.isArray(filter.in) ? filter.in : [];
194
+ if (!values.length) {
195
+ clauses.push("1 = 0");
196
+ } else {
197
+ const placeholders = values.map(
198
+ (value) => createPlaceholder(dialect, state, encodeValue(field, dialect, value))
199
+ );
200
+ clauses.push(`${column} in (${placeholders.join(", ")})`);
201
+ }
202
+ }
203
+ if ("contains" in filter) {
204
+ const placeholder = createPlaceholder(dialect, state, String(filter.contains ?? ""));
205
+ clauses.push(
206
+ dialect === "postgres" ? `strpos(${column}, ${placeholder}) > 0` : `instr(${column}, ${placeholder}) > 0`
207
+ );
208
+ }
209
+ if ("gt" in filter) {
210
+ const placeholder = createPlaceholder(dialect, state, encodeValue(field, dialect, filter.gt));
211
+ clauses.push(`${column} > ${placeholder}`);
212
+ }
213
+ if ("gte" in filter) {
214
+ const placeholder = createPlaceholder(dialect, state, encodeValue(field, dialect, filter.gte));
215
+ clauses.push(`${column} >= ${placeholder}`);
216
+ }
217
+ if ("lt" in filter) {
218
+ const placeholder = createPlaceholder(dialect, state, encodeValue(field, dialect, filter.lt));
219
+ clauses.push(`${column} < ${placeholder}`);
220
+ }
221
+ if ("lte" in filter) {
222
+ const placeholder = createPlaceholder(dialect, state, encodeValue(field, dialect, filter.lte));
223
+ clauses.push(`${column} <= ${placeholder}`);
224
+ }
225
+ if (!clauses.length) return "1 = 1";
226
+ if (clauses.length === 1) return clauses[0];
227
+ return `(${clauses.join(" and ")})`;
228
+ }
229
+ function compileWhere(model, where, dialect, state) {
230
+ if (!where) return void 0;
231
+ const clauses = [];
232
+ for (const [key, value] of Object.entries(where)) {
233
+ if (key === "AND") {
234
+ const items = Array.isArray(value) ? value : [];
235
+ if (!items.length) continue;
236
+ const nested = items.map((item) => compileWhere(model, item, dialect, state)).filter(Boolean).map((item) => `(${item})`);
237
+ if (nested.length) clauses.push(nested.join(" and "));
238
+ continue;
239
+ }
240
+ if (key === "OR") {
241
+ const items = Array.isArray(value) ? value : [];
242
+ if (!items.length) continue;
243
+ const nested = items.map((item) => compileWhere(model, item, dialect, state)).filter(Boolean).map((item) => `(${item})`);
244
+ if (nested.length) clauses.push(`(${nested.join(" or ")})`);
245
+ continue;
246
+ }
247
+ if (key === "NOT") {
248
+ const nested = compileWhere(model, value, dialect, state);
249
+ if (nested) clauses.push(`not (${nested})`);
250
+ continue;
251
+ }
252
+ clauses.push(compileFieldFilter(model, key, value, dialect, state));
253
+ }
254
+ if (!clauses.length) return void 0;
255
+ return clauses.join(" and ");
256
+ }
257
+ function compileOrderBy(model, orderBy, dialect) {
258
+ if (!orderBy) return "";
259
+ const parts = Object.entries(orderBy).filter(([fieldName]) => fieldName in model.fields).map(([fieldName, direction]) => {
260
+ const field = model.fields[fieldName];
261
+ return `${quoteIdentifier(model.table, dialect)}.${quoteIdentifier(field.column, dialect)} ${direction === "desc" ? "desc" : "asc"}`;
262
+ });
263
+ if (!parts.length) return "";
264
+ return ` order by ${parts.join(", ")}`;
265
+ }
266
+ function compilePagination(dialect, take, skip) {
267
+ if (take === void 0 && skip === void 0) return "";
268
+ if (take !== void 0 && skip !== void 0) {
269
+ return ` limit ${take} offset ${skip}`;
270
+ }
271
+ if (take !== void 0) {
272
+ return ` limit ${take}`;
273
+ }
274
+ if (dialect === "postgres") {
275
+ return ` offset ${skip ?? 0}`;
276
+ }
277
+ if (dialect === "mysql") {
278
+ return ` limit 18446744073709551615 offset ${skip ?? 0}`;
279
+ }
280
+ return ` limit -1 offset ${skip ?? 0}`;
281
+ }
282
+ function buildSelectStatement(model, dialect, args) {
283
+ const state = { params: [] };
284
+ const selectList = Object.values(model.fields).map(
285
+ (field) => `${quoteIdentifier(model.table, dialect)}.${quoteIdentifier(field.column, dialect)} as ${quoteIdentifier(field.name, dialect)}`
286
+ );
287
+ let sql = `select ${selectList.join(", ")} from ${quoteIdentifier(model.table, dialect)}`;
288
+ const where = compileWhere(model, args.where, dialect, state);
289
+ if (where) sql += ` where ${where}`;
290
+ sql += compileOrderBy(model, args.orderBy, dialect);
291
+ sql += compilePagination(dialect, args.take, args.skip);
292
+ return { sql, params: state.params };
293
+ }
294
+ function buildCountStatement(model, dialect, where) {
295
+ const state = { params: [] };
296
+ let sql = `select count(*) as ${quoteIdentifier("count", dialect)} from ${quoteIdentifier(model.table, dialect)}`;
297
+ const compiledWhere = compileWhere(model, where, dialect, state);
298
+ if (compiledWhere) sql += ` where ${compiledWhere}`;
299
+ return { sql, params: state.params };
300
+ }
301
+ function buildInsertRow(model, data) {
302
+ const row = {};
303
+ for (const field of Object.values(model.fields)) {
304
+ row[field.name] = applyDefault(data[field.name], field);
305
+ }
306
+ return row;
307
+ }
308
+ function buildIdentityWhere(model, row) {
309
+ const field = identityField(model);
310
+ const value = row[field.name];
311
+ if (value === void 0) {
312
+ throw new Error(
313
+ `Model "${model.name}" could not resolve the identity field "${field.name}" from the current row.`
314
+ );
315
+ }
316
+ return {
317
+ [field.name]: value
318
+ };
319
+ }
320
+ function buildInsertStatement(model, dialect, row) {
321
+ const state = { params: [] };
322
+ const fields = Object.values(model.fields).filter((field) => row[field.name] !== void 0);
323
+ const columns = fields.map((field) => quoteIdentifier(field.column, dialect));
324
+ const values = fields.map(
325
+ (field) => createPlaceholder(dialect, state, encodeValue(field, dialect, row[field.name]))
326
+ );
327
+ return {
328
+ sql: `insert into ${quoteIdentifier(model.table, dialect)} (${columns.join(", ")}) values (${values.join(", ")})`,
329
+ params: state.params
330
+ };
331
+ }
332
+ function buildUpsertStatement(model, dialect, row, updateData, conflictField) {
333
+ const state = { params: [] };
334
+ const insertFields = Object.values(model.fields).filter((field) => row[field.name] !== void 0);
335
+ const columns = insertFields.map((field) => quoteIdentifier(field.column, dialect));
336
+ const values = insertFields.map(
337
+ (field) => createPlaceholder(dialect, state, encodeValue(field, dialect, row[field.name]))
338
+ );
339
+ const updateEntries = Object.entries(updateData).filter(([, value]) => value !== void 0);
340
+ const conflictColumn = quoteIdentifier(conflictField.column, dialect);
341
+ let sql = `insert into ${quoteIdentifier(model.table, dialect)} (${columns.join(", ")}) values (${values.join(", ")})`;
342
+ if (dialect === "mysql") {
343
+ const updateClause2 = updateEntries.length ? updateEntries.map(([fieldName, value]) => {
344
+ const field = model.fields[fieldName];
345
+ if (!field) {
346
+ throw new Error(`Unknown field "${fieldName}" on model "${model.name}".`);
347
+ }
348
+ const placeholder = createPlaceholder(dialect, state, encodeValue(field, dialect, value));
349
+ return `${quoteIdentifier(field.column, dialect)} = ${placeholder}`;
350
+ }) : [`${conflictColumn} = ${conflictColumn}`];
351
+ sql += ` on duplicate key update ${updateClause2.join(", ")}`;
352
+ return {
353
+ sql,
354
+ params: state.params
355
+ };
356
+ }
357
+ if (!updateEntries.length) {
358
+ sql += ` on conflict (${conflictColumn}) do nothing`;
359
+ return {
360
+ sql,
361
+ params: state.params
362
+ };
363
+ }
364
+ const updateClause = updateEntries.map(([fieldName, value]) => {
365
+ const field = model.fields[fieldName];
366
+ if (!field) {
367
+ throw new Error(`Unknown field "${fieldName}" on model "${model.name}".`);
368
+ }
369
+ const placeholder = createPlaceholder(dialect, state, encodeValue(field, dialect, value));
370
+ return `${quoteIdentifier(field.column, dialect)} = ${placeholder}`;
371
+ });
372
+ sql += ` on conflict (${conflictColumn}) do update set ${updateClause.join(", ")}`;
373
+ return {
374
+ sql,
375
+ params: state.params
376
+ };
377
+ }
378
+ function buildUpdateStatement(model, dialect, data, where) {
379
+ const state = { params: [] };
380
+ const entries = Object.entries(data).filter(([, value]) => value !== void 0);
381
+ if (!entries.length) {
382
+ return null;
383
+ }
384
+ const setClause = entries.map(([fieldName, value]) => {
385
+ const field = model.fields[fieldName];
386
+ if (!field) {
387
+ throw new Error(`Unknown field "${fieldName}" on model "${model.name}".`);
388
+ }
389
+ const placeholder = createPlaceholder(dialect, state, encodeValue(field, dialect, value));
390
+ return `${quoteIdentifier(field.column, dialect)} = ${placeholder}`;
391
+ });
392
+ const compiledWhere = compileWhere(model, where, dialect, state);
393
+ if (!compiledWhere) {
394
+ throw new Error(`Update on model "${model.name}" requires a where clause.`);
395
+ }
396
+ return {
397
+ sql: `update ${quoteIdentifier(model.table, dialect)} set ${setClause.join(", ")} where ${compiledWhere}`,
398
+ params: state.params
399
+ };
400
+ }
401
+ function buildDeleteStatement(model, dialect, where) {
402
+ const state = { params: [] };
403
+ const compiledWhere = compileWhere(model, where, dialect, state);
404
+ if (!compiledWhere) {
405
+ throw new Error(`Delete on model "${model.name}" requires a where clause.`);
406
+ }
407
+ return {
408
+ sql: `delete from ${quoteIdentifier(model.table, dialect)} where ${compiledWhere}`,
409
+ params: state.params
410
+ };
411
+ }
412
+ function createSqliteAdapter(database) {
413
+ let transactionDepth = 0;
414
+ const adapter = {
415
+ dialect: "sqlite",
416
+ async query(sql, params) {
417
+ const statement = database.prepare(sql);
418
+ if (/^\s*(select|with)\b/i.test(sql)) {
419
+ const rows = statement.all(...params);
420
+ return {
421
+ rows,
422
+ affectedRows: rows.length
423
+ };
424
+ }
425
+ const result = statement.run(...params);
426
+ return {
427
+ rows: [],
428
+ affectedRows: Number(result?.changes ?? 0)
429
+ };
430
+ },
431
+ async transaction(run) {
432
+ const savepoint = `farming_labs_${transactionDepth + 1}`;
433
+ if (transactionDepth === 0) {
434
+ database.exec("begin");
435
+ } else {
436
+ database.exec(`savepoint ${savepoint}`);
437
+ }
438
+ transactionDepth += 1;
439
+ try {
440
+ const result = await run(adapter);
441
+ transactionDepth -= 1;
442
+ if (transactionDepth === 0) {
443
+ database.exec("commit");
444
+ } else {
445
+ database.exec(`release savepoint ${savepoint}`);
446
+ }
447
+ return result;
448
+ } catch (error) {
449
+ transactionDepth -= 1;
450
+ if (transactionDepth === 0) {
451
+ database.exec("rollback");
452
+ } else {
453
+ database.exec(`rollback to savepoint ${savepoint}`);
454
+ database.exec(`release savepoint ${savepoint}`);
455
+ }
456
+ throw error;
457
+ }
458
+ }
459
+ };
460
+ return adapter;
461
+ }
462
+ function createPgTransactionalAdapter(client) {
463
+ let transactionDepth = 0;
464
+ const adapter = {
465
+ dialect: "postgres",
466
+ async query(sql, params) {
467
+ const result = await client.query(sql, params);
468
+ return {
469
+ rows: result.rows ?? [],
470
+ affectedRows: Number(result.rowCount ?? result.rows?.length ?? 0)
471
+ };
472
+ },
473
+ async transaction(run) {
474
+ const savepoint = `farming_labs_${transactionDepth + 1}`;
475
+ if (transactionDepth === 0) {
476
+ await client.query("begin");
477
+ } else {
478
+ await client.query(`savepoint ${savepoint}`);
479
+ }
480
+ transactionDepth += 1;
481
+ try {
482
+ const result = await run(adapter);
483
+ transactionDepth -= 1;
484
+ if (transactionDepth === 0) {
485
+ await client.query("commit");
486
+ } else {
487
+ await client.query(`release savepoint ${savepoint}`);
488
+ }
489
+ return result;
490
+ } catch (error) {
491
+ transactionDepth -= 1;
492
+ if (transactionDepth === 0) {
493
+ await client.query("rollback");
494
+ } else {
495
+ await client.query(`rollback to savepoint ${savepoint}`);
496
+ await client.query(`release savepoint ${savepoint}`);
497
+ }
498
+ throw error;
499
+ }
500
+ }
501
+ };
502
+ return adapter;
503
+ }
504
+ function createPgPoolAdapter(pool) {
505
+ return {
506
+ dialect: "postgres",
507
+ async query(sql, params) {
508
+ const result = await pool.query(sql, params);
509
+ return {
510
+ rows: result.rows ?? [],
511
+ affectedRows: Number(result.rowCount ?? result.rows?.length ?? 0)
512
+ };
513
+ },
514
+ async transaction(run) {
515
+ const client = await pool.connect();
516
+ try {
517
+ return await createPgTransactionalAdapter(client).transaction(run);
518
+ } finally {
519
+ client.release?.();
520
+ }
521
+ }
522
+ };
523
+ }
524
+ function createMysqlTransactionalAdapter(connection) {
525
+ let transactionDepth = 0;
526
+ const adapter = {
527
+ dialect: "mysql",
528
+ async query(sql, params) {
529
+ const [result] = await connection.execute(sql, params);
530
+ if (Array.isArray(result)) {
531
+ return {
532
+ rows: result,
533
+ affectedRows: result.length
534
+ };
535
+ }
536
+ return {
537
+ rows: [],
538
+ affectedRows: Number(result.affectedRows ?? 0)
539
+ };
540
+ },
541
+ async transaction(run) {
542
+ const savepoint = `farming_labs_${transactionDepth + 1}`;
543
+ if (transactionDepth === 0) {
544
+ await connection.beginTransaction();
545
+ } else {
546
+ await connection.execute(`savepoint ${savepoint}`);
547
+ }
548
+ transactionDepth += 1;
549
+ try {
550
+ const result = await run(adapter);
551
+ transactionDepth -= 1;
552
+ if (transactionDepth === 0) {
553
+ await connection.commit();
554
+ } else {
555
+ await connection.execute(`release savepoint ${savepoint}`);
556
+ }
557
+ return result;
558
+ } catch (error) {
559
+ transactionDepth -= 1;
560
+ if (transactionDepth === 0) {
561
+ await connection.rollback();
562
+ } else {
563
+ await connection.execute(`rollback to savepoint ${savepoint}`);
564
+ await connection.execute(`release savepoint ${savepoint}`);
565
+ }
566
+ throw error;
567
+ }
568
+ }
569
+ };
570
+ return adapter;
571
+ }
572
+ function createMysqlPoolAdapter(pool) {
573
+ return {
574
+ dialect: "mysql",
575
+ async query(sql, params) {
576
+ const [result] = await pool.execute(sql, params);
577
+ if (Array.isArray(result)) {
578
+ return {
579
+ rows: result,
580
+ affectedRows: result.length
581
+ };
582
+ }
583
+ return {
584
+ rows: [],
585
+ affectedRows: Number(result.affectedRows ?? 0)
586
+ };
587
+ },
588
+ async transaction(run) {
589
+ const connection = await pool.getConnection();
590
+ try {
591
+ return await createMysqlTransactionalAdapter(connection).transaction(run);
592
+ } finally {
593
+ connection.release?.();
594
+ }
595
+ }
596
+ };
597
+ }
598
+ function createSqlDriver(adapter) {
599
+ async function loadRows(schema, modelName, args) {
600
+ const manifest = getManifest(schema);
601
+ const model = manifest.models[modelName];
602
+ const statement = buildSelectStatement(model, adapter.dialect, args);
603
+ const result = await adapter.query(statement.sql, statement.params);
604
+ const rows = result.rows.map((row) => decodeRow(model, row));
605
+ return Promise.all(rows.map((row) => projectRow(schema, modelName, row, args.select)));
606
+ }
607
+ async function loadOneRow(schema, modelName, args) {
608
+ const rows = await loadRows(schema, modelName, {
609
+ ...args,
610
+ take: 1
611
+ });
612
+ return rows[0] ?? null;
613
+ }
614
+ async function loadRawOneRow(schema, modelName, args) {
615
+ const manifest = getManifest(schema);
616
+ const model = manifest.models[modelName];
617
+ const statement = buildSelectStatement(model, adapter.dialect, {
618
+ ...args,
619
+ take: 1
620
+ });
621
+ const result = await adapter.query(statement.sql, statement.params);
622
+ const row = result.rows[0];
623
+ return row ? decodeRow(model, row) : null;
624
+ }
625
+ async function projectRow(schema, modelName, row, select) {
626
+ const manifest = getManifest(schema);
627
+ const model = manifest.models[modelName];
628
+ const output = {};
629
+ if (!select) {
630
+ for (const fieldName of Object.keys(model.fields)) {
631
+ output[fieldName] = row[fieldName];
632
+ }
633
+ return output;
634
+ }
635
+ for (const [key, value] of Object.entries(select)) {
636
+ if (value === void 0) continue;
637
+ if (key in model.fields && value === true) {
638
+ output[key] = row[key];
639
+ continue;
640
+ }
641
+ if (key in schema.models[modelName].relations) {
642
+ output[key] = await resolveRelation(
643
+ schema,
644
+ modelName,
645
+ key,
646
+ row,
647
+ value
648
+ );
649
+ }
650
+ }
651
+ return output;
652
+ }
653
+ async function resolveRelation(schema, modelName, relationName, row, value) {
654
+ const manifest = getManifest(schema);
655
+ const relation = schema.models[modelName].relations[relationName];
656
+ const relationArgs = value === true ? {} : value;
657
+ if (relation.kind === "belongsTo") {
658
+ const foreignField = manifest.models[modelName].fields[relation.foreignKey];
659
+ const targetReference = parseReference(foreignField?.references);
660
+ const targetField2 = targetReference?.field ?? identityField(manifest.models[relation.target]).name;
661
+ const foreignValue = row[relation.foreignKey];
662
+ if (foreignValue == null) return null;
663
+ return loadOneRow(schema, relation.target, {
664
+ where: mergeWhere(
665
+ relationArgs.where,
666
+ {
667
+ [targetField2]: foreignValue
668
+ }
669
+ ),
670
+ orderBy: relationArgs.orderBy,
671
+ select: relationArgs.select
672
+ });
673
+ }
674
+ if (relation.kind === "hasOne") {
675
+ const targetModel = manifest.models[relation.target];
676
+ const foreignField = targetModel.fields[relation.foreignKey];
677
+ const sourceReference = parseReference(foreignField?.references);
678
+ const sourceField2 = sourceReference?.field ?? identityField(manifest.models[modelName]).name;
679
+ const sourceValue2 = row[sourceField2];
680
+ if (sourceValue2 == null) return null;
681
+ return loadOneRow(schema, relation.target, {
682
+ where: mergeWhere(
683
+ relationArgs.where,
684
+ {
685
+ [relation.foreignKey]: sourceValue2
686
+ }
687
+ ),
688
+ orderBy: relationArgs.orderBy,
689
+ select: relationArgs.select
690
+ });
691
+ }
692
+ if (relation.kind === "hasMany") {
693
+ const targetModel = manifest.models[relation.target];
694
+ const foreignField = targetModel.fields[relation.foreignKey];
695
+ const sourceReference = parseReference(foreignField?.references);
696
+ const sourceField2 = sourceReference?.field ?? identityField(manifest.models[modelName]).name;
697
+ const sourceValue2 = row[sourceField2];
698
+ if (sourceValue2 == null) return [];
699
+ return loadRows(schema, relation.target, {
700
+ where: mergeWhere(
701
+ relationArgs.where,
702
+ {
703
+ [relation.foreignKey]: sourceValue2
704
+ }
705
+ ),
706
+ orderBy: relationArgs.orderBy,
707
+ take: relationArgs.take,
708
+ skip: relationArgs.skip,
709
+ select: relationArgs.select
710
+ });
711
+ }
712
+ const throughModel = manifest.models[relation.through];
713
+ const throughFromReference = parseReference(throughModel.fields[relation.from]?.references);
714
+ const throughToReference = parseReference(throughModel.fields[relation.to]?.references);
715
+ const sourceField = throughFromReference?.field ?? identityField(manifest.models[modelName]).name;
716
+ const targetField = throughToReference?.field ?? identityField(manifest.models[relation.target]).name;
717
+ const sourceValue = row[sourceField];
718
+ if (sourceValue == null) return [];
719
+ const throughRows = await loadRows(schema, relation.through, {
720
+ where: {
721
+ [relation.from]: sourceValue
722
+ }
723
+ });
724
+ const targetIds = throughRows.map((item) => item[relation.to]).filter((item) => item != null);
725
+ if (!targetIds.length) return [];
726
+ return loadRows(schema, relation.target, {
727
+ where: mergeWhere(
728
+ relationArgs.where,
729
+ {
730
+ [targetField]: {
731
+ in: targetIds
732
+ }
733
+ }
734
+ ),
735
+ orderBy: relationArgs.orderBy,
736
+ take: relationArgs.take,
737
+ skip: relationArgs.skip,
738
+ select: relationArgs.select
739
+ });
740
+ }
741
+ const driver = {
742
+ async findMany(schema, model, args) {
743
+ return loadRows(schema, model, args);
744
+ },
745
+ async findFirst(schema, model, args) {
746
+ return loadOneRow(schema, model, args);
747
+ },
748
+ async findUnique(schema, model, args) {
749
+ return loadOneRow(schema, model, args);
750
+ },
751
+ async count(schema, model, args) {
752
+ const manifest = getManifest(schema);
753
+ const statement = buildCountStatement(
754
+ manifest.models[model],
755
+ adapter.dialect,
756
+ args?.where
757
+ );
758
+ const result = await adapter.query(statement.sql, statement.params);
759
+ const rawCount = result.rows[0]?.count;
760
+ if (typeof rawCount === "number") return rawCount;
761
+ return Number.parseInt(String(rawCount ?? 0), 10);
762
+ },
763
+ async create(schema, model, args) {
764
+ const manifest = getManifest(schema);
765
+ identityField(manifest.models[model]);
766
+ const row = buildInsertRow(
767
+ manifest.models[model],
768
+ args.data
769
+ );
770
+ const statement = buildInsertStatement(manifest.models[model], adapter.dialect, row);
771
+ await adapter.query(statement.sql, statement.params);
772
+ return loadOneRow(schema, model, {
773
+ where: buildIdentityWhere(manifest.models[model], row),
774
+ select: args.select
775
+ });
776
+ },
777
+ async createMany(schema, model, args) {
778
+ const results = [];
779
+ for (const entry of args.data) {
780
+ results.push(
781
+ await driver.create(schema, model, {
782
+ data: entry,
783
+ select: args.select
784
+ })
785
+ );
786
+ }
787
+ return results;
788
+ },
789
+ async update(schema, model, args) {
790
+ const manifest = getManifest(schema);
791
+ const current = await loadRawOneRow(schema, model, {
792
+ where: args.where
793
+ });
794
+ if (!current) return null;
795
+ const update = buildUpdateStatement(
796
+ manifest.models[model],
797
+ adapter.dialect,
798
+ args.data,
799
+ buildIdentityWhere(manifest.models[model], current)
800
+ );
801
+ if (update) {
802
+ await adapter.query(update.sql, update.params);
803
+ }
804
+ return loadOneRow(schema, model, {
805
+ where: buildIdentityWhere(manifest.models[model], current),
806
+ select: args.select
807
+ });
808
+ },
809
+ async updateMany(schema, model, args) {
810
+ const manifest = getManifest(schema);
811
+ const update = buildUpdateStatement(
812
+ manifest.models[model],
813
+ adapter.dialect,
814
+ args.data,
815
+ args.where
816
+ );
817
+ if (!update) return 0;
818
+ const result = await adapter.query(update.sql, update.params);
819
+ return result.affectedRows;
820
+ },
821
+ async upsert(schema, model, args) {
822
+ const manifest = getManifest(schema);
823
+ const modelManifest = manifest.models[model];
824
+ const conflict = extractUpsertConflict(modelManifest, args.where);
825
+ validateUpsertUpdateData(
826
+ modelManifest,
827
+ args.update,
828
+ conflict
829
+ );
830
+ const row = buildInsertRow(
831
+ modelManifest,
832
+ mergeUpsertCreateData(
833
+ modelManifest,
834
+ args.create,
835
+ conflict
836
+ )
837
+ );
838
+ const statement = buildUpsertStatement(
839
+ modelManifest,
840
+ adapter.dialect,
841
+ row,
842
+ args.update,
843
+ conflict.field
844
+ );
845
+ await adapter.query(statement.sql, statement.params);
846
+ return loadOneRow(schema, model, {
847
+ where: args.where,
848
+ select: args.select
849
+ });
850
+ },
851
+ async delete(schema, model, args) {
852
+ const manifest = getManifest(schema);
853
+ const current = await loadRawOneRow(schema, model, {
854
+ where: args.where
855
+ });
856
+ if (!current) return 0;
857
+ const statement = buildDeleteStatement(
858
+ manifest.models[model],
859
+ adapter.dialect,
860
+ buildIdentityWhere(manifest.models[model], current)
861
+ );
862
+ const result = await adapter.query(statement.sql, statement.params);
863
+ return result.affectedRows;
864
+ },
865
+ async deleteMany(schema, model, args) {
866
+ const manifest = getManifest(schema);
867
+ const statement = buildDeleteStatement(
868
+ manifest.models[model],
869
+ adapter.dialect,
870
+ args.where
871
+ );
872
+ const result = await adapter.query(statement.sql, statement.params);
873
+ return result.affectedRows;
874
+ },
875
+ async transaction(_schema, run) {
876
+ return adapter.transaction(async (txAdapter) => run(createSqlDriver(txAdapter)));
877
+ }
878
+ };
879
+ return driver;
880
+ }
881
+ function createSqliteDriver(database) {
882
+ return createSqlDriver(createSqliteAdapter(database));
883
+ }
884
+ function createPgPoolDriver(pool) {
885
+ return createSqlDriver(createPgPoolAdapter(pool));
886
+ }
887
+ function createPgClientDriver(client) {
888
+ return createSqlDriver(createPgTransactionalAdapter(client));
889
+ }
890
+ function createMysqlDriver(poolOrConnection) {
891
+ const adapter = "getConnection" in poolOrConnection ? createMysqlPoolAdapter(poolOrConnection) : createMysqlTransactionalAdapter(poolOrConnection);
892
+ return createSqlDriver(adapter);
893
+ }
894
+ export {
895
+ createMysqlDriver,
896
+ createPgClientDriver,
897
+ createPgPoolDriver,
898
+ createSqliteDriver
899
+ };
900
+ //# sourceMappingURL=index.js.map