arkormx 2.0.0-next.0 → 2.0.0-next.2
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/README.md +42 -13
- package/dist/cli.mjs +109 -35
- package/dist/index.cjs +1308 -72
- package/dist/index.d.cts +423 -20
- package/dist/index.d.mts +423 -20
- package/dist/index.mjs +1307 -73
- package/package.json +3 -1
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { str } from "@h3ravel/support";
|
|
1
2
|
import { sql } from "kysely";
|
|
2
3
|
import { AsyncLocalStorage } from "async_hooks";
|
|
3
4
|
import { createJiti } from "@rexxars/jiti";
|
|
@@ -7,7 +8,6 @@ import { createRequire } from "module";
|
|
|
7
8
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "fs";
|
|
8
9
|
import { fileURLToPath } from "url";
|
|
9
10
|
import path, { dirname as dirname$1, extname as extname$1, join as join$1, relative } from "path";
|
|
10
|
-
import { str } from "@h3ravel/support";
|
|
11
11
|
import { existsSync as existsSync$1, mkdirSync as mkdirSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, rmSync as rmSync$1, writeFileSync as writeFileSync$1 } from "node:fs";
|
|
12
12
|
import { spawnSync } from "node:child_process";
|
|
13
13
|
import { Logger } from "@h3ravel/shared";
|
|
@@ -70,24 +70,66 @@ var UnsupportedAdapterFeatureException = class extends ArkormException {
|
|
|
70
70
|
|
|
71
71
|
//#endregion
|
|
72
72
|
//#region src/adapters/KyselyDatabaseAdapter.ts
|
|
73
|
+
/**
|
|
74
|
+
* Database adapter implementation for Kysely, allowing Arkorm to execute queries using Kysely
|
|
75
|
+
* as the underlying query builder and executor.
|
|
76
|
+
*
|
|
77
|
+
* @author Legacy (3m1n3nc3)
|
|
78
|
+
* @since 2.0.0-next.0
|
|
79
|
+
*/
|
|
73
80
|
var KyselyDatabaseAdapter = class KyselyDatabaseAdapter {
|
|
74
81
|
capabilities = {
|
|
75
82
|
transactions: true,
|
|
76
83
|
returning: true,
|
|
77
84
|
insertMany: true,
|
|
85
|
+
upsert: true,
|
|
78
86
|
updateMany: true,
|
|
79
87
|
deleteMany: true,
|
|
80
88
|
exists: true,
|
|
81
89
|
relationLoads: false,
|
|
82
|
-
relationAggregates:
|
|
83
|
-
relationFilters:
|
|
90
|
+
relationAggregates: true,
|
|
91
|
+
relationFilters: true,
|
|
84
92
|
rawWhere: false
|
|
85
93
|
};
|
|
86
|
-
constructor(db) {
|
|
94
|
+
constructor(db, mapping = {}) {
|
|
87
95
|
this.db = db;
|
|
96
|
+
this.mapping = mapping;
|
|
97
|
+
}
|
|
98
|
+
introspectionTypeToTs(typeName, enumValues) {
|
|
99
|
+
if (enumValues && enumValues.length > 0) return enumValues.map((value) => `'${value.replace(/'/g, "\\'")}'`).join(" | ");
|
|
100
|
+
switch (typeName) {
|
|
101
|
+
case "bool": return "boolean";
|
|
102
|
+
case "int2":
|
|
103
|
+
case "int4":
|
|
104
|
+
case "int8":
|
|
105
|
+
case "float4":
|
|
106
|
+
case "float8":
|
|
107
|
+
case "numeric":
|
|
108
|
+
case "money": return "number";
|
|
109
|
+
case "json":
|
|
110
|
+
case "jsonb": return "Record<string, unknown> | unknown[]";
|
|
111
|
+
case "date":
|
|
112
|
+
case "timestamp":
|
|
113
|
+
case "timestamptz": return "Date";
|
|
114
|
+
case "bytea": return "Uint8Array";
|
|
115
|
+
case "uuid":
|
|
116
|
+
case "varchar":
|
|
117
|
+
case "bpchar":
|
|
118
|
+
case "char":
|
|
119
|
+
case "text":
|
|
120
|
+
case "citext":
|
|
121
|
+
case "time":
|
|
122
|
+
case "timetz":
|
|
123
|
+
case "interval":
|
|
124
|
+
case "inet":
|
|
125
|
+
case "cidr":
|
|
126
|
+
case "macaddr":
|
|
127
|
+
case "macaddr8": return "string";
|
|
128
|
+
default: return "unknown";
|
|
129
|
+
}
|
|
88
130
|
}
|
|
89
131
|
resolveTable(target) {
|
|
90
|
-
if (target.table && target.table.trim().length > 0) return target.table;
|
|
132
|
+
if (target.table && target.table.trim().length > 0) return this.mapping[target.table] ?? target.table;
|
|
91
133
|
throw new ArkormException("Kysely adapter requires a concrete target table.", {
|
|
92
134
|
operation: "adapter.table",
|
|
93
135
|
model: target.modelName,
|
|
@@ -196,29 +238,223 @@ var KyselyDatabaseAdapter = class KyselyDatabaseAdapter {
|
|
|
196
238
|
if (clauses.length === 0) return sql``;
|
|
197
239
|
return sql.join(clauses, sql``);
|
|
198
240
|
}
|
|
241
|
+
buildColumnReference(table, column) {
|
|
242
|
+
return sql`${sql.table(table)}.${sql.id(column)}`;
|
|
243
|
+
}
|
|
244
|
+
buildRelatedTargetFromRelation(target, relation) {
|
|
245
|
+
const metadata = target.model?.getRelationMetadata(relation);
|
|
246
|
+
if (!metadata) throw new UnsupportedAdapterFeatureException(`Relation [${relation}] could not be resolved for SQL-backed relation execution.`, {
|
|
247
|
+
operation: "adapter.relation.metadata",
|
|
248
|
+
model: target.modelName,
|
|
249
|
+
relation
|
|
250
|
+
});
|
|
251
|
+
if (metadata.type !== "hasMany" && metadata.type !== "hasOne" && metadata.type !== "belongsTo" && metadata.type !== "belongsToMany" && metadata.type !== "hasOneThrough" && metadata.type !== "hasManyThrough") throw new UnsupportedAdapterFeatureException(`Relation [${relation}] is not supported for SQL-backed relation execution by the Kysely adapter yet.`, {
|
|
252
|
+
operation: "adapter.relation.metadata",
|
|
253
|
+
model: target.modelName,
|
|
254
|
+
relation,
|
|
255
|
+
meta: {
|
|
256
|
+
feature: "relationFilters",
|
|
257
|
+
relationType: metadata.type
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
const relatedMetadata = metadata.relatedModel.getModelMetadata();
|
|
261
|
+
return {
|
|
262
|
+
metadata,
|
|
263
|
+
relatedTarget: {
|
|
264
|
+
model: metadata.relatedModel,
|
|
265
|
+
modelName: metadata.relatedModel.name,
|
|
266
|
+
table: relatedMetadata.table,
|
|
267
|
+
primaryKey: relatedMetadata.primaryKey,
|
|
268
|
+
columns: relatedMetadata.columns,
|
|
269
|
+
softDelete: relatedMetadata.softDelete
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
resolveMappedTable(table) {
|
|
274
|
+
return this.mapping[table] ?? table;
|
|
275
|
+
}
|
|
276
|
+
buildBelongsToManyJoinSource(outerTarget, relatedTarget, metadata) {
|
|
277
|
+
const outerTable = this.resolveTable(outerTarget);
|
|
278
|
+
const relatedTable = this.resolveTable(relatedTarget);
|
|
279
|
+
const pivotTable = this.resolveMappedTable(metadata.throughTable);
|
|
280
|
+
return {
|
|
281
|
+
from: sql`${sql.table(relatedTable)} inner join ${sql.table(pivotTable)} on ${this.buildColumnReference(relatedTable, this.mapColumn(relatedTarget, metadata.relatedKey))} = ${this.buildColumnReference(pivotTable, metadata.relatedPivotKey)}`,
|
|
282
|
+
condition: sql`
|
|
283
|
+
${this.buildColumnReference(pivotTable, metadata.foreignPivotKey)}
|
|
284
|
+
=
|
|
285
|
+
${this.buildColumnReference(outerTable, this.mapColumn(outerTarget, metadata.parentKey))}
|
|
286
|
+
`
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
buildThroughJoinSource(outerTarget, relatedTarget, metadata) {
|
|
290
|
+
const outerTable = this.resolveTable(outerTarget);
|
|
291
|
+
const relatedTable = this.resolveTable(relatedTarget);
|
|
292
|
+
const throughTable = this.resolveMappedTable(metadata.throughTable);
|
|
293
|
+
return {
|
|
294
|
+
from: sql`${sql.table(relatedTable)} inner join ${sql.table(throughTable)} on ${this.buildColumnReference(relatedTable, this.mapColumn(relatedTarget, metadata.secondKey))} = ${this.buildColumnReference(throughTable, metadata.secondLocalKey)}`,
|
|
295
|
+
condition: sql`
|
|
296
|
+
${this.buildColumnReference(throughTable, metadata.firstKey)}
|
|
297
|
+
=
|
|
298
|
+
${this.buildColumnReference(outerTable, this.mapColumn(outerTarget, metadata.localKey))}
|
|
299
|
+
`
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
buildRelatedJoinCondition(outerTarget, relation) {
|
|
303
|
+
const { metadata, relatedTarget } = this.buildRelatedTargetFromRelation(outerTarget, relation);
|
|
304
|
+
const outerTable = this.resolveTable(outerTarget);
|
|
305
|
+
const relatedTable = this.resolveTable(relatedTarget);
|
|
306
|
+
if (metadata.type === "belongsToMany") {
|
|
307
|
+
const joinSource = this.buildBelongsToManyJoinSource(outerTarget, relatedTarget, metadata);
|
|
308
|
+
return {
|
|
309
|
+
relatedTarget,
|
|
310
|
+
from: joinSource.from,
|
|
311
|
+
condition: joinSource.condition
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
if (metadata.type === "hasOneThrough" || metadata.type === "hasManyThrough") {
|
|
315
|
+
const joinSource = this.buildThroughJoinSource(outerTarget, relatedTarget, metadata);
|
|
316
|
+
return {
|
|
317
|
+
relatedTarget,
|
|
318
|
+
from: joinSource.from,
|
|
319
|
+
condition: joinSource.condition
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
if (metadata.type === "hasMany" || metadata.type === "hasOne") return {
|
|
323
|
+
relatedTarget,
|
|
324
|
+
from: sql`${sql.table(relatedTable)}`,
|
|
325
|
+
condition: sql`
|
|
326
|
+
${this.buildColumnReference(relatedTable, this.mapColumn(relatedTarget, metadata.foreignKey))}
|
|
327
|
+
=
|
|
328
|
+
${this.buildColumnReference(outerTable, this.mapColumn(outerTarget, metadata.localKey))}
|
|
329
|
+
`
|
|
330
|
+
};
|
|
331
|
+
return {
|
|
332
|
+
relatedTarget,
|
|
333
|
+
from: sql`${sql.table(relatedTable)}`,
|
|
334
|
+
condition: sql`
|
|
335
|
+
${this.buildColumnReference(relatedTable, this.mapColumn(relatedTarget, metadata.ownerKey))}
|
|
336
|
+
=
|
|
337
|
+
${this.buildColumnReference(outerTable, this.mapColumn(outerTarget, metadata.foreignKey))}
|
|
338
|
+
`
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
combineConditions(conditions) {
|
|
342
|
+
const filtered = conditions.filter((condition) => Boolean(condition));
|
|
343
|
+
if (filtered.length === 0) return sql`1 = 1`;
|
|
344
|
+
if (filtered.length === 1) return filtered[0];
|
|
345
|
+
return sql`(${sql.join(filtered, sql` and `)})`;
|
|
346
|
+
}
|
|
347
|
+
buildRelationFilterExpression(target, filter) {
|
|
348
|
+
const { relatedTarget, from, condition } = this.buildRelatedJoinCondition(target, filter.relation);
|
|
349
|
+
return sql`(
|
|
350
|
+
select count(*)::int
|
|
351
|
+
from ${from}
|
|
352
|
+
where ${this.combineConditions([condition, filter.where ? this.buildWhereCondition(relatedTarget, filter.where) : void 0])}
|
|
353
|
+
) ${filter.operator === "!=" ? sql.raw("!=") : sql.raw(filter.operator)} ${filter.count}`;
|
|
354
|
+
}
|
|
355
|
+
buildRelationFilterCondition(target, relationFilters) {
|
|
356
|
+
if (!relationFilters || relationFilters.length === 0) return sql`1 = 1`;
|
|
357
|
+
let expression = null;
|
|
358
|
+
relationFilters.forEach((filter) => {
|
|
359
|
+
const next = this.buildRelationFilterExpression(target, filter);
|
|
360
|
+
if (!expression) {
|
|
361
|
+
expression = next;
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
expression = filter.boolean === "OR" ? sql`(${expression} or ${next})` : sql`(${expression} and ${next})`;
|
|
365
|
+
});
|
|
366
|
+
return expression ?? sql`1 = 1`;
|
|
367
|
+
}
|
|
368
|
+
buildQueryFilterCondition(target, condition, relationFilters) {
|
|
369
|
+
let expression = condition ? this.buildWhereCondition(target, condition) : null;
|
|
370
|
+
relationFilters?.forEach((filter) => {
|
|
371
|
+
const next = this.buildRelationFilterExpression(target, filter);
|
|
372
|
+
if (!expression) {
|
|
373
|
+
expression = next;
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
expression = filter.boolean === "OR" ? sql`(${expression} or ${next})` : sql`(${expression} and ${next})`;
|
|
377
|
+
});
|
|
378
|
+
return expression ?? sql`1 = 1`;
|
|
379
|
+
}
|
|
380
|
+
buildRelationAggregateSelectList(target, relationAggregates) {
|
|
381
|
+
if (!relationAggregates || relationAggregates.length === 0) return sql``;
|
|
382
|
+
return sql.join(relationAggregates.map((aggregate) => {
|
|
383
|
+
const { relatedTarget, from, condition } = this.buildRelatedJoinCondition(target, aggregate.relation);
|
|
384
|
+
const relatedTable = this.resolveTable(relatedTarget);
|
|
385
|
+
const whereCondition = this.combineConditions([condition, aggregate.where ? this.buildWhereCondition(relatedTarget, aggregate.where) : void 0]);
|
|
386
|
+
if (aggregate.type === "exists") return sql`, exists(
|
|
387
|
+
select 1
|
|
388
|
+
from ${from}
|
|
389
|
+
where ${whereCondition}
|
|
390
|
+
) as ${sql.id(aggregate.alias ?? `${aggregate.relation}Exists`)}`;
|
|
391
|
+
const selectedColumn = aggregate.column ? this.buildColumnReference(relatedTable, this.mapColumn(relatedTarget, aggregate.column)) : sql.raw("*");
|
|
392
|
+
return sql`, (
|
|
393
|
+
select ${aggregate.type === "count" ? sql`count(*)::int` : aggregate.type === "sum" ? sql`sum(${selectedColumn})::double precision` : aggregate.type === "avg" ? sql`avg(${selectedColumn})::double precision` : aggregate.type === "min" ? sql`min(${selectedColumn})` : sql`max(${selectedColumn})`}
|
|
394
|
+
from ${from}
|
|
395
|
+
where ${whereCondition}
|
|
396
|
+
) as ${sql.id(aggregate.alias ?? `${aggregate.relation}${aggregate.type}`)}`;
|
|
397
|
+
}), sql``);
|
|
398
|
+
}
|
|
399
|
+
buildCombinedWhereClause(target, condition, relationFilters) {
|
|
400
|
+
if (!condition && (!relationFilters || relationFilters.length === 0)) return sql``;
|
|
401
|
+
return sql` where ${this.buildQueryFilterCondition(target, condition, relationFilters)}`;
|
|
402
|
+
}
|
|
403
|
+
buildSingleRowTargetCte(target, where) {
|
|
404
|
+
const primaryKey = this.resolvePrimaryKey(target);
|
|
405
|
+
return sql`target_row as (
|
|
406
|
+
select ${sql.id(primaryKey)}
|
|
407
|
+
from ${sql.table(this.resolveTable(target))}
|
|
408
|
+
where ${this.buildWhereCondition(target, where)}
|
|
409
|
+
limit 1
|
|
410
|
+
)`;
|
|
411
|
+
}
|
|
199
412
|
assertNoRelationLoads(spec) {
|
|
200
413
|
if ("relationLoads" in spec && spec.relationLoads && spec.relationLoads.length > 0) throw new UnsupportedAdapterFeatureException("Kysely adapter relation-load execution is planned for a later phase.", {
|
|
201
414
|
operation: "adapter.relationLoads",
|
|
202
415
|
meta: { feature: "relationLoads" }
|
|
203
416
|
});
|
|
204
417
|
}
|
|
418
|
+
/**
|
|
419
|
+
* Selects records from the database matching the specified criteria and returns
|
|
420
|
+
* them as an array of database rows.
|
|
421
|
+
*
|
|
422
|
+
* @param spec The specification defining the selection criteria.
|
|
423
|
+
* @returns A promise that resolves to an array of database rows.
|
|
424
|
+
*/
|
|
205
425
|
async select(spec) {
|
|
206
426
|
this.assertNoRelationLoads(spec);
|
|
207
427
|
const result = await sql`
|
|
208
428
|
select ${this.buildSelectList(spec.target, spec.columns)}
|
|
429
|
+
${this.buildRelationAggregateSelectList(spec.target, spec.relationAggregates)}
|
|
209
430
|
from ${sql.table(this.resolveTable(spec.target))}
|
|
210
|
-
${this.
|
|
431
|
+
${this.buildCombinedWhereClause(spec.target, spec.where, spec.relationFilters)}
|
|
211
432
|
${this.buildOrderBy(spec.target, spec.orderBy)}
|
|
212
433
|
${this.buildPaginationClause(spec)}
|
|
213
434
|
`.execute(this.db);
|
|
214
435
|
return this.mapRows(spec.target, result.rows);
|
|
215
436
|
}
|
|
437
|
+
/**
|
|
438
|
+
* Selects a single record from the database matching the specified criteria and returns it as
|
|
439
|
+
* a database row. If multiple records match the criteria, only the first one is returned.
|
|
440
|
+
* If no records match, null is returned.
|
|
441
|
+
*
|
|
442
|
+
* @param spec The specification defining the selection criteria.
|
|
443
|
+
* @returns A promise that resolves to a database row or null if no records match.
|
|
444
|
+
*/
|
|
216
445
|
async selectOne(spec) {
|
|
217
446
|
return (await this.select({
|
|
218
447
|
...spec,
|
|
219
448
|
limit: spec.limit ?? 1
|
|
220
449
|
}))[0] ?? null;
|
|
221
450
|
}
|
|
451
|
+
/**
|
|
452
|
+
* Inserts a new record into the database with the specified values and returns the
|
|
453
|
+
* inserted record as a database row.
|
|
454
|
+
*
|
|
455
|
+
* @param spec
|
|
456
|
+
* @returns
|
|
457
|
+
*/
|
|
222
458
|
async insert(spec) {
|
|
223
459
|
const values = this.mapValues(spec.target, spec.values);
|
|
224
460
|
const columns = Object.keys(values);
|
|
@@ -233,6 +469,13 @@ var KyselyDatabaseAdapter = class KyselyDatabaseAdapter {
|
|
|
233
469
|
`.execute(this.db);
|
|
234
470
|
return this.mapRow(spec.target, result.rows[0]);
|
|
235
471
|
}
|
|
472
|
+
/**
|
|
473
|
+
* Inserts multiple records into the database with the specified values and returns the number
|
|
474
|
+
* of records successfully inserted.
|
|
475
|
+
*
|
|
476
|
+
* @param spec The specification defining the values to be inserted.
|
|
477
|
+
* @returns A promise that resolves to the number of records successfully inserted.
|
|
478
|
+
*/
|
|
236
479
|
async insertMany(spec) {
|
|
237
480
|
if (spec.values.length === 0) return 0;
|
|
238
481
|
const rows = spec.values.map((row) => this.mapValues(spec.target, row));
|
|
@@ -253,6 +496,39 @@ var KyselyDatabaseAdapter = class KyselyDatabaseAdapter {
|
|
|
253
496
|
returning ${sql.id(this.resolvePrimaryKey(spec.target))}
|
|
254
497
|
`.execute(this.db)).rows.length;
|
|
255
498
|
}
|
|
499
|
+
async upsert(spec) {
|
|
500
|
+
if (spec.values.length === 0) return 0;
|
|
501
|
+
const rows = spec.values.map((row) => this.mapValues(spec.target, row));
|
|
502
|
+
const columns = Array.from(new Set(rows.flatMap((row) => Object.keys(row))));
|
|
503
|
+
const uniqueColumns = spec.uniqueBy.map((column) => this.mapColumn(spec.target, column));
|
|
504
|
+
const updateColumns = (spec.updateColumns ?? []).map((column) => this.mapColumn(spec.target, column)).filter((column) => !uniqueColumns.includes(column));
|
|
505
|
+
const conflictTarget = sql.join(uniqueColumns.map((column) => sql.id(column)), sql`, `);
|
|
506
|
+
if (columns.length === 0) {
|
|
507
|
+
await sql`
|
|
508
|
+
insert into ${sql.table(this.resolveTable(spec.target))}
|
|
509
|
+
default values
|
|
510
|
+
on conflict (${conflictTarget}) do nothing
|
|
511
|
+
`.execute(this.db);
|
|
512
|
+
return spec.values.length;
|
|
513
|
+
}
|
|
514
|
+
const values = sql.join(rows.map((row) => {
|
|
515
|
+
return sql`(${sql.join(columns.map((column) => row[column] ?? null), sql`, `)})`;
|
|
516
|
+
}), sql`, `);
|
|
517
|
+
const conflictAction = updateColumns.length === 0 ? sql`do nothing` : sql`do update set ${sql.join(updateColumns.map((column) => sql`${sql.id(column)} = excluded.${sql.id(column)}`), sql`, `)}`;
|
|
518
|
+
await sql`
|
|
519
|
+
insert into ${sql.table(this.resolveTable(spec.target))} (${sql.join(columns.map((column) => sql.id(column)), sql`, `)})
|
|
520
|
+
values ${values}
|
|
521
|
+
on conflict (${conflictTarget}) ${conflictAction}
|
|
522
|
+
`.execute(this.db);
|
|
523
|
+
return spec.values.length;
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Updates records in the database matching the specified criteria with the given values
|
|
527
|
+
* and returns the updated record as a database row.
|
|
528
|
+
*
|
|
529
|
+
* @param spec The specification defining the update criteria and values.
|
|
530
|
+
* @returns A promise that resolves to the updated record as a database row, or null if no records match the criteria.
|
|
531
|
+
*/
|
|
256
532
|
async update(spec) {
|
|
257
533
|
const values = this.mapValues(spec.target, spec.values);
|
|
258
534
|
const assignments = Object.entries(values).map(([column, value]) => {
|
|
@@ -271,6 +547,41 @@ var KyselyDatabaseAdapter = class KyselyDatabaseAdapter {
|
|
|
271
547
|
`.execute(this.db);
|
|
272
548
|
return this.mapRow(spec.target, result.rows[0]);
|
|
273
549
|
}
|
|
550
|
+
/**
|
|
551
|
+
* Updates a single record in the database matching the specified criteria with the given values.
|
|
552
|
+
*
|
|
553
|
+
* @param spec
|
|
554
|
+
* @returns
|
|
555
|
+
*/
|
|
556
|
+
async updateFirst(spec) {
|
|
557
|
+
const values = this.mapValues(spec.target, spec.values);
|
|
558
|
+
const assignments = Object.entries(values).map(([column, value]) => {
|
|
559
|
+
return sql`${sql.id(column)} = ${value}`;
|
|
560
|
+
});
|
|
561
|
+
if (assignments.length === 0) return await this.selectOne({
|
|
562
|
+
target: spec.target,
|
|
563
|
+
where: spec.where,
|
|
564
|
+
limit: 1
|
|
565
|
+
});
|
|
566
|
+
const primaryKey = this.resolvePrimaryKey(spec.target);
|
|
567
|
+
const table = this.resolveTable(spec.target);
|
|
568
|
+
const result = await sql`
|
|
569
|
+
with ${this.buildSingleRowTargetCte(spec.target, spec.where)}
|
|
570
|
+
update ${sql.table(table)}
|
|
571
|
+
set ${sql.join(assignments, sql`, `)}
|
|
572
|
+
from target_row
|
|
573
|
+
where ${this.buildColumnReference(table, primaryKey)} = ${sql`target_row.${sql.id(primaryKey)}`}
|
|
574
|
+
returning ${sql.table(table)}.*
|
|
575
|
+
`.execute(this.db);
|
|
576
|
+
return this.mapRow(spec.target, result.rows[0]);
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Updates multiple records in the database matching the specified criteria with the
|
|
580
|
+
* given values and returns the number of records successfully updated.
|
|
581
|
+
*
|
|
582
|
+
* @param spec The specification defining the update criteria and values.
|
|
583
|
+
* @returns A promise that resolves to the number of records successfully updated.
|
|
584
|
+
*/
|
|
274
585
|
async updateMany(spec) {
|
|
275
586
|
const values = this.mapValues(spec.target, spec.values);
|
|
276
587
|
const assignments = Object.entries(values).map(([column, value]) => {
|
|
@@ -284,6 +595,13 @@ var KyselyDatabaseAdapter = class KyselyDatabaseAdapter {
|
|
|
284
595
|
returning ${sql.id(this.resolvePrimaryKey(spec.target))}
|
|
285
596
|
`.execute(this.db)).rows.length;
|
|
286
597
|
}
|
|
598
|
+
/**
|
|
599
|
+
* Deletes records from the database matching the specified criteria and returns the
|
|
600
|
+
* deleted record as a database row.
|
|
601
|
+
*
|
|
602
|
+
* @param spec The specification defining the delete criteria.
|
|
603
|
+
* @returns A promise that resolves to the deleted record as a database row, or null if no records match the criteria.
|
|
604
|
+
*/
|
|
287
605
|
async delete(spec) {
|
|
288
606
|
const result = await sql`
|
|
289
607
|
delete from ${sql.table(this.resolveTable(spec.target))}
|
|
@@ -292,6 +610,31 @@ var KyselyDatabaseAdapter = class KyselyDatabaseAdapter {
|
|
|
292
610
|
`.execute(this.db);
|
|
293
611
|
return this.mapRow(spec.target, result.rows[0]);
|
|
294
612
|
}
|
|
613
|
+
/**
|
|
614
|
+
* Deletes a single record from the database matching the specified criteria and returns it as a database row.
|
|
615
|
+
*
|
|
616
|
+
* @param spec
|
|
617
|
+
* @returns
|
|
618
|
+
*/
|
|
619
|
+
async deleteFirst(spec) {
|
|
620
|
+
const primaryKey = this.resolvePrimaryKey(spec.target);
|
|
621
|
+
const table = this.resolveTable(spec.target);
|
|
622
|
+
const result = await sql`
|
|
623
|
+
with ${this.buildSingleRowTargetCte(spec.target, spec.where)}
|
|
624
|
+
delete from ${sql.table(table)}
|
|
625
|
+
using target_row
|
|
626
|
+
where ${this.buildColumnReference(table, primaryKey)} = ${sql`target_row.${sql.id(primaryKey)}`}
|
|
627
|
+
returning ${sql.table(table)}.*
|
|
628
|
+
`.execute(this.db);
|
|
629
|
+
return this.mapRow(spec.target, result.rows[0]);
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Deletes multiple records from the database matching the specified criteria and
|
|
633
|
+
* returns the number of records successfully deleted.
|
|
634
|
+
*
|
|
635
|
+
* @param spec The specification defining the delete criteria.
|
|
636
|
+
* @returns A promise that resolves to the number of records successfully deleted.
|
|
637
|
+
*/
|
|
295
638
|
async deleteMany(spec) {
|
|
296
639
|
return (await sql`
|
|
297
640
|
delete from ${sql.table(this.resolveTable(spec.target))}
|
|
@@ -299,36 +642,113 @@ var KyselyDatabaseAdapter = class KyselyDatabaseAdapter {
|
|
|
299
642
|
returning ${sql.id(this.resolvePrimaryKey(spec.target))}
|
|
300
643
|
`.execute(this.db)).rows.length;
|
|
301
644
|
}
|
|
645
|
+
/**
|
|
646
|
+
* Counts the number of records in the database matching the specified criteria and returns
|
|
647
|
+
* the count as a number.
|
|
648
|
+
*
|
|
649
|
+
* @param spec The specification defining the count criteria.
|
|
650
|
+
* @returns A promise that resolves to the number of records matching the criteria.
|
|
651
|
+
*/
|
|
302
652
|
async count(spec) {
|
|
303
653
|
const result = await sql`
|
|
304
654
|
select count(*)::int as count
|
|
305
655
|
from ${sql.table(this.resolveTable(spec.target))}
|
|
306
|
-
${this.
|
|
656
|
+
${this.buildCombinedWhereClause(spec.target, spec.where, spec.relationFilters)}
|
|
307
657
|
`.execute(this.db);
|
|
308
658
|
return Number(result.rows[0]?.count ?? 0);
|
|
309
659
|
}
|
|
660
|
+
/**
|
|
661
|
+
* Checks for the existence of records matching the specified criteria.
|
|
662
|
+
*
|
|
663
|
+
* @param spec The specification defining the existence criteria.
|
|
664
|
+
* @returns A promise that resolves to a boolean indicating whether any records match the criteria.
|
|
665
|
+
*/
|
|
310
666
|
async exists(spec) {
|
|
311
667
|
const result = await sql`
|
|
312
668
|
select exists(
|
|
313
669
|
select 1
|
|
314
670
|
from ${sql.table(this.resolveTable(spec.target))}
|
|
315
|
-
${this.
|
|
671
|
+
${this.buildCombinedWhereClause(spec.target, spec.where, spec.relationFilters)}
|
|
316
672
|
limit 1
|
|
317
673
|
) as exists
|
|
318
674
|
`.execute(this.db);
|
|
319
675
|
return Boolean(result.rows[0]?.exists);
|
|
320
676
|
}
|
|
677
|
+
async introspectModels(options = {}) {
|
|
678
|
+
const tables = options.tables?.filter(Boolean) ?? [];
|
|
679
|
+
const result = await sql`
|
|
680
|
+
select
|
|
681
|
+
cls.relname as table_name,
|
|
682
|
+
att.attname as column_name,
|
|
683
|
+
not att.attnotnull as is_nullable,
|
|
684
|
+
typ.typname as type_name,
|
|
685
|
+
case when typ.typcategory = 'A' then elem.typname else null end as element_type_name,
|
|
686
|
+
case when typ.typtype = 'e'
|
|
687
|
+
then array(select enumlabel from pg_enum where enumtypid = typ.oid order by enumsortorder)
|
|
688
|
+
else null
|
|
689
|
+
end as enum_values,
|
|
690
|
+
case when elem.typtype = 'e'
|
|
691
|
+
then array(select enumlabel from pg_enum where enumtypid = elem.oid order by enumsortorder)
|
|
692
|
+
else null
|
|
693
|
+
end as element_enum_values
|
|
694
|
+
from pg_attribute att
|
|
695
|
+
inner join pg_class cls on cls.oid = att.attrelid
|
|
696
|
+
inner join pg_namespace ns on ns.oid = cls.relnamespace
|
|
697
|
+
inner join pg_type typ on typ.oid = att.atttypid
|
|
698
|
+
left join pg_type elem on elem.oid = typ.typelem and typ.typcategory = 'A'
|
|
699
|
+
where cls.relkind in ('r', 'p')
|
|
700
|
+
and att.attnum > 0
|
|
701
|
+
and not att.attisdropped
|
|
702
|
+
and ns.nspname not in ('pg_catalog', 'information_schema')
|
|
703
|
+
${tables.length > 0 ? sql` and cls.relname in (${sql.join(tables)})` : sql``}
|
|
704
|
+
order by cls.relname asc, att.attnum asc
|
|
705
|
+
`.execute(this.db);
|
|
706
|
+
const models = /* @__PURE__ */ new Map();
|
|
707
|
+
result.rows.forEach((row) => {
|
|
708
|
+
const existing = models.get(row.table_name) ?? {
|
|
709
|
+
name: str(row.table_name).studly().singular().toString(),
|
|
710
|
+
table: row.table_name,
|
|
711
|
+
fields: []
|
|
712
|
+
};
|
|
713
|
+
const isArray = row.element_type_name !== null;
|
|
714
|
+
const baseType = isArray ? this.introspectionTypeToTs(row.element_type_name ?? "unknown", row.element_enum_values) : this.introspectionTypeToTs(row.type_name, row.enum_values);
|
|
715
|
+
existing.fields.push({
|
|
716
|
+
name: row.column_name,
|
|
717
|
+
type: isArray ? `Array<${baseType}>` : baseType,
|
|
718
|
+
nullable: row.is_nullable
|
|
719
|
+
});
|
|
720
|
+
models.set(row.table_name, existing);
|
|
721
|
+
});
|
|
722
|
+
return [...models.values()];
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* Executes a series of database operations within a transaction.
|
|
726
|
+
* The provided callback function is called with a new instance of the
|
|
727
|
+
* KyselyDatabaseAdapter that is bound to the transaction context.
|
|
728
|
+
*
|
|
729
|
+
* @param callback The callback function containing the database operations to be executed within the transaction.
|
|
730
|
+
* @param context The transaction context specifying options such as read-only mode and isolation level.
|
|
731
|
+
* @returns A promise that resolves to the result of the callback function.
|
|
732
|
+
*/
|
|
321
733
|
async transaction(callback, context = {}) {
|
|
322
734
|
let transactionBuilder = this.db.transaction();
|
|
323
735
|
if (context.readOnly !== void 0) transactionBuilder = transactionBuilder.setAccessMode(context.readOnly ? "read only" : "read write");
|
|
324
736
|
if (context.isolationLevel) transactionBuilder = transactionBuilder.setIsolationLevel(context.isolationLevel);
|
|
325
737
|
return await transactionBuilder.execute(async (transaction) => {
|
|
326
|
-
return await callback(new KyselyDatabaseAdapter(transaction));
|
|
738
|
+
return await callback(new KyselyDatabaseAdapter(transaction, this.mapping));
|
|
327
739
|
});
|
|
328
740
|
}
|
|
329
741
|
};
|
|
330
|
-
|
|
331
|
-
|
|
742
|
+
/**
|
|
743
|
+
* Factory function to create a KyselyDatabaseAdapter instance with the given Kysely executor
|
|
744
|
+
* and optional table name mapping.
|
|
745
|
+
*
|
|
746
|
+
* @param db The Kysely executor to be used by the adapter.
|
|
747
|
+
* @param mapping Optional table name mapping for the adapter.
|
|
748
|
+
* @returns A new instance of KyselyDatabaseAdapter.
|
|
749
|
+
*/
|
|
750
|
+
const createKyselyAdapter = (db, mapping = {}) => {
|
|
751
|
+
return new KyselyDatabaseAdapter(db, mapping);
|
|
332
752
|
};
|
|
333
753
|
|
|
334
754
|
//#endregion
|
|
@@ -387,6 +807,7 @@ const userConfig = {
|
|
|
387
807
|
let runtimeConfigLoaded = false;
|
|
388
808
|
let runtimeConfigLoadingPromise;
|
|
389
809
|
let runtimeClientResolver;
|
|
810
|
+
let runtimeAdapter;
|
|
390
811
|
let runtimePaginationURLDriverFactory;
|
|
391
812
|
let runtimePaginationCurrentPageResolver;
|
|
392
813
|
const transactionClientStorage = new AsyncLocalStorage();
|
|
@@ -412,6 +833,12 @@ const mergePathConfig = (paths) => {
|
|
|
412
833
|
const defineConfig = (config) => {
|
|
413
834
|
return config;
|
|
414
835
|
};
|
|
836
|
+
const bindAdapterToModels = (adapter, models) => {
|
|
837
|
+
models.forEach((model) => {
|
|
838
|
+
model.setAdapter(adapter);
|
|
839
|
+
});
|
|
840
|
+
return adapter;
|
|
841
|
+
};
|
|
415
842
|
/**
|
|
416
843
|
* Get the user-provided ArkORM configuration.
|
|
417
844
|
*
|
|
@@ -431,15 +858,22 @@ const getUserConfig = (key) => {
|
|
|
431
858
|
const configureArkormRuntime = (prisma, options = {}) => {
|
|
432
859
|
const nextConfig = {
|
|
433
860
|
...userConfig,
|
|
434
|
-
prisma,
|
|
435
861
|
paths: mergePathConfig(options.paths)
|
|
436
862
|
};
|
|
863
|
+
nextConfig.prisma = prisma;
|
|
437
864
|
if (options.pagination !== void 0) nextConfig.pagination = options.pagination;
|
|
865
|
+
if (options.adapter !== void 0) nextConfig.adapter = options.adapter;
|
|
866
|
+
if (options.boot !== void 0) nextConfig.boot = options.boot;
|
|
438
867
|
if (options.outputExt !== void 0) nextConfig.outputExt = options.outputExt;
|
|
439
868
|
Object.assign(userConfig, { ...nextConfig });
|
|
440
869
|
runtimeClientResolver = prisma;
|
|
870
|
+
runtimeAdapter = options.adapter;
|
|
441
871
|
runtimePaginationURLDriverFactory = nextConfig.pagination?.urlDriver;
|
|
442
872
|
runtimePaginationCurrentPageResolver = nextConfig.pagination?.resolveCurrentPage;
|
|
873
|
+
options.boot?.({
|
|
874
|
+
prisma: resolveClient(prisma),
|
|
875
|
+
bindAdapter: bindAdapterToModels
|
|
876
|
+
});
|
|
443
877
|
};
|
|
444
878
|
/**
|
|
445
879
|
* Reset the ArkORM runtime configuration.
|
|
@@ -453,6 +887,7 @@ const resetArkormRuntimeForTests = () => {
|
|
|
453
887
|
runtimeConfigLoaded = false;
|
|
454
888
|
runtimeConfigLoadingPromise = void 0;
|
|
455
889
|
runtimeClientResolver = void 0;
|
|
890
|
+
runtimeAdapter = void 0;
|
|
456
891
|
runtimePaginationURLDriverFactory = void 0;
|
|
457
892
|
runtimePaginationCurrentPageResolver = void 0;
|
|
458
893
|
};
|
|
@@ -479,8 +914,10 @@ const resolveClient = (resolver) => {
|
|
|
479
914
|
*/
|
|
480
915
|
const resolveAndApplyConfig = (imported) => {
|
|
481
916
|
const config = imported?.default ?? imported;
|
|
482
|
-
if (!config || typeof config !== "object"
|
|
917
|
+
if (!config || typeof config !== "object") return;
|
|
483
918
|
configureArkormRuntime(config.prisma, {
|
|
919
|
+
adapter: config.adapter,
|
|
920
|
+
boot: config.boot,
|
|
484
921
|
pagination: config.pagination,
|
|
485
922
|
paths: config.paths,
|
|
486
923
|
outputExt: config.outputExt
|
|
@@ -562,6 +999,10 @@ const getRuntimePrismaClient = () => {
|
|
|
562
999
|
if (!runtimeConfigLoaded) loadRuntimeConfigSync();
|
|
563
1000
|
return resolveClient(runtimeClientResolver);
|
|
564
1001
|
};
|
|
1002
|
+
const getRuntimeAdapter = () => {
|
|
1003
|
+
if (!runtimeConfigLoaded) loadRuntimeConfigSync();
|
|
1004
|
+
return runtimeAdapter;
|
|
1005
|
+
};
|
|
565
1006
|
const getActiveTransactionClient = () => {
|
|
566
1007
|
return transactionClientStorage.getStore();
|
|
567
1008
|
};
|
|
@@ -629,9 +1070,11 @@ loadArkormConfig();
|
|
|
629
1070
|
//#endregion
|
|
630
1071
|
//#region src/helpers/prisma.ts
|
|
631
1072
|
/**
|
|
632
|
-
* Create an adapter to convert a Prisma client instance into a format
|
|
1073
|
+
* Create an adapter to convert a Prisma client instance into a format
|
|
633
1074
|
* compatible with ArkORM's expectations.
|
|
634
|
-
*
|
|
1075
|
+
*
|
|
1076
|
+
* @deprecated Prefer createPrismaDatabaseAdapter(prisma) for runtime usage.
|
|
1077
|
+
*
|
|
635
1078
|
* @param prisma The Prisma client instance to adapt.
|
|
636
1079
|
* @param mapping An optional mapping of Prisma delegate names to ArkORM delegate names.
|
|
637
1080
|
* @returns A record of adapted Prisma delegates compatible with ArkORM.
|
|
@@ -646,6 +1089,9 @@ function createPrismaAdapter(prisma) {
|
|
|
646
1089
|
/**
|
|
647
1090
|
* Create a delegate mapping record for Model.setClient() from a Prisma client.
|
|
648
1091
|
*
|
|
1092
|
+
* @deprecated Prefer createPrismaDatabaseAdapter(prisma, mapping) and bind the
|
|
1093
|
+
* resulting adapter with Model.setAdapter(...).
|
|
1094
|
+
*
|
|
649
1095
|
* @param prisma The Prisma client instance.
|
|
650
1096
|
* @param mapping Optional mapping of Arkormˣ delegate names to Prisma delegate names.
|
|
651
1097
|
* @returns A delegate map keyed by Arkormˣ delegate names.
|
|
@@ -672,6 +1118,13 @@ function inferDelegateName(modelName) {
|
|
|
672
1118
|
|
|
673
1119
|
//#endregion
|
|
674
1120
|
//#region src/adapters/PrismaDatabaseAdapter.ts
|
|
1121
|
+
/**
|
|
1122
|
+
* Database adapter implementation for Prisma, allowing Arkorm to execute queries using Prisma
|
|
1123
|
+
* as the underlying query builder and executor.
|
|
1124
|
+
*
|
|
1125
|
+
* @author Legacy (3m1n3nc3)
|
|
1126
|
+
* @since 2.0.0-next.0
|
|
1127
|
+
*/
|
|
675
1128
|
var PrismaDatabaseAdapter = class PrismaDatabaseAdapter {
|
|
676
1129
|
capabilities;
|
|
677
1130
|
delegates;
|
|
@@ -686,6 +1139,7 @@ var PrismaDatabaseAdapter = class PrismaDatabaseAdapter {
|
|
|
686
1139
|
this.capabilities = {
|
|
687
1140
|
transactions: this.hasTransactionSupport(prisma),
|
|
688
1141
|
insertMany: Object.values(this.delegates).some((delegate) => typeof delegate.createMany === "function"),
|
|
1142
|
+
upsert: false,
|
|
689
1143
|
updateMany: Object.values(this.delegates).some((delegate) => typeof delegate.updateMany === "function"),
|
|
690
1144
|
deleteMany: false,
|
|
691
1145
|
exists: true,
|
|
@@ -705,6 +1159,27 @@ var PrismaDatabaseAdapter = class PrismaDatabaseAdapter {
|
|
|
705
1159
|
unique(values) {
|
|
706
1160
|
return [...new Set(values.filter(Boolean))];
|
|
707
1161
|
}
|
|
1162
|
+
runtimeModelTypeToTs(typeName, kind, enumValues) {
|
|
1163
|
+
if (kind === "enum" && enumValues && enumValues.length > 0) return enumValues.map((value) => `'${value.replace(/'/g, "\\'")}'`).join(" | ");
|
|
1164
|
+
switch (typeName) {
|
|
1165
|
+
case "Int":
|
|
1166
|
+
case "Float":
|
|
1167
|
+
case "Decimal":
|
|
1168
|
+
case "BigInt": return "number";
|
|
1169
|
+
case "Boolean": return "boolean";
|
|
1170
|
+
case "DateTime": return "Date";
|
|
1171
|
+
case "Json": return "Record<string, unknown> | unknown[]";
|
|
1172
|
+
case "Bytes": return "Uint8Array";
|
|
1173
|
+
case "String":
|
|
1174
|
+
case "UUID": return "string";
|
|
1175
|
+
default: return "string";
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
getRuntimeDataModel() {
|
|
1179
|
+
const runtimeDataModel = this.prisma._runtimeDataModel;
|
|
1180
|
+
if (runtimeDataModel && typeof runtimeDataModel === "object") return runtimeDataModel;
|
|
1181
|
+
return null;
|
|
1182
|
+
}
|
|
708
1183
|
toQuerySelect(columns) {
|
|
709
1184
|
if (!columns || columns.length === 0) return void 0;
|
|
710
1185
|
return columns.reduce((select, column) => {
|
|
@@ -786,6 +1261,29 @@ var PrismaDatabaseAdapter = class PrismaDatabaseAdapter {
|
|
|
786
1261
|
return include;
|
|
787
1262
|
}, {});
|
|
788
1263
|
}
|
|
1264
|
+
async introspectModels(options = {}) {
|
|
1265
|
+
const runtimeDataModel = this.getRuntimeDataModel();
|
|
1266
|
+
if (!runtimeDataModel?.models) return [];
|
|
1267
|
+
const requestedTables = new Set(options.tables?.filter(Boolean) ?? []);
|
|
1268
|
+
const enums = runtimeDataModel.enums ?? {};
|
|
1269
|
+
return Object.entries(runtimeDataModel.models).flatMap(([name, model]) => {
|
|
1270
|
+
const table = model.dbName ?? `${str(name).camel().plural()}`;
|
|
1271
|
+
if (requestedTables.size > 0 && !requestedTables.has(table)) return [];
|
|
1272
|
+
return [{
|
|
1273
|
+
name,
|
|
1274
|
+
table,
|
|
1275
|
+
fields: (model.fields ?? []).filter((field) => field.kind !== "object").map((field) => {
|
|
1276
|
+
const enumValues = field.kind === "enum" ? (enums[field.type]?.values ?? []).map((value) => typeof value === "string" ? value : value.name ?? "").filter(Boolean) : null;
|
|
1277
|
+
const baseType = this.runtimeModelTypeToTs(field.type, field.kind, enumValues);
|
|
1278
|
+
return {
|
|
1279
|
+
name: field.name,
|
|
1280
|
+
type: field.isList ? `Array<${baseType}>` : baseType,
|
|
1281
|
+
nullable: field.isRequired === false
|
|
1282
|
+
};
|
|
1283
|
+
})
|
|
1284
|
+
}];
|
|
1285
|
+
});
|
|
1286
|
+
}
|
|
789
1287
|
resolveDelegate(target) {
|
|
790
1288
|
const tableName = target.table ? this.normalizeCandidate(target.table) : "";
|
|
791
1289
|
const singularTableName = tableName ? `${str(tableName).singular()}` : "";
|
|
@@ -817,15 +1315,41 @@ var PrismaDatabaseAdapter = class PrismaDatabaseAdapter {
|
|
|
817
1315
|
}
|
|
818
1316
|
});
|
|
819
1317
|
}
|
|
1318
|
+
/**
|
|
1319
|
+
* @todo Implement relationLoads by performing separate queries and merging results
|
|
1320
|
+
* in-memory, since Prisma does not support nested reads with constraints, ordering, or
|
|
1321
|
+
* pagination on related models as of now.
|
|
1322
|
+
*
|
|
1323
|
+
* @param spec
|
|
1324
|
+
* @returns
|
|
1325
|
+
*/
|
|
820
1326
|
async select(spec) {
|
|
821
1327
|
return await this.resolveDelegate(spec.target).findMany(this.buildFindArgs(spec));
|
|
822
1328
|
}
|
|
1329
|
+
/**
|
|
1330
|
+
* Selects a single record matching the specified criteria.
|
|
1331
|
+
*
|
|
1332
|
+
* @param spec
|
|
1333
|
+
* @returns
|
|
1334
|
+
*/
|
|
823
1335
|
async selectOne(spec) {
|
|
824
1336
|
return await this.resolveDelegate(spec.target).findFirst(this.buildFindArgs(spec));
|
|
825
1337
|
}
|
|
1338
|
+
/**
|
|
1339
|
+
* Inserts a single record into the database and returns the created record.
|
|
1340
|
+
*
|
|
1341
|
+
* @param spec
|
|
1342
|
+
* @returns
|
|
1343
|
+
*/
|
|
826
1344
|
async insert(spec) {
|
|
827
1345
|
return await this.resolveDelegate(spec.target).create({ data: spec.values });
|
|
828
1346
|
}
|
|
1347
|
+
/**
|
|
1348
|
+
* Inserts multiple records into the database.
|
|
1349
|
+
*
|
|
1350
|
+
* @param spec
|
|
1351
|
+
* @returns
|
|
1352
|
+
*/
|
|
829
1353
|
async insertMany(spec) {
|
|
830
1354
|
const delegate = this.resolveDelegate(spec.target);
|
|
831
1355
|
if (typeof delegate.createMany === "function") {
|
|
@@ -845,6 +1369,12 @@ var PrismaDatabaseAdapter = class PrismaDatabaseAdapter {
|
|
|
845
1369
|
}
|
|
846
1370
|
return spec.ignoreDuplicates ? inserted : spec.values.length;
|
|
847
1371
|
}
|
|
1372
|
+
/**
|
|
1373
|
+
* Updates a single record matching the specified criteria and returns the updated record.
|
|
1374
|
+
*
|
|
1375
|
+
* @param spec
|
|
1376
|
+
* @returns
|
|
1377
|
+
*/
|
|
848
1378
|
async update(spec) {
|
|
849
1379
|
const delegate = this.resolveDelegate(spec.target);
|
|
850
1380
|
const where = this.toQueryWhere(spec.where);
|
|
@@ -854,6 +1384,12 @@ var PrismaDatabaseAdapter = class PrismaDatabaseAdapter {
|
|
|
854
1384
|
data: spec.values
|
|
855
1385
|
});
|
|
856
1386
|
}
|
|
1387
|
+
/**
|
|
1388
|
+
* Updates multiple records matching the specified criteria.
|
|
1389
|
+
*
|
|
1390
|
+
* @param spec
|
|
1391
|
+
* @returns
|
|
1392
|
+
*/
|
|
857
1393
|
async updateMany(spec) {
|
|
858
1394
|
const delegate = this.resolveDelegate(spec.target);
|
|
859
1395
|
const where = this.toQueryWhere(spec.where);
|
|
@@ -874,12 +1410,24 @@ var PrismaDatabaseAdapter = class PrismaDatabaseAdapter {
|
|
|
874
1410
|
}));
|
|
875
1411
|
return rows.length;
|
|
876
1412
|
}
|
|
1413
|
+
/**
|
|
1414
|
+
* Deletes a single record matching the specified criteria and returns the deleted record.
|
|
1415
|
+
*
|
|
1416
|
+
* @param spec
|
|
1417
|
+
* @returns
|
|
1418
|
+
*/
|
|
877
1419
|
async delete(spec) {
|
|
878
1420
|
const delegate = this.resolveDelegate(spec.target);
|
|
879
1421
|
const where = this.toQueryWhere(spec.where);
|
|
880
1422
|
if (!where) return null;
|
|
881
1423
|
return await delegate.delete({ where });
|
|
882
1424
|
}
|
|
1425
|
+
/**
|
|
1426
|
+
* Deletes multiple records matching the specified criteria.
|
|
1427
|
+
*
|
|
1428
|
+
* @param spec
|
|
1429
|
+
* @returns
|
|
1430
|
+
*/
|
|
883
1431
|
async deleteMany(spec) {
|
|
884
1432
|
const delegate = this.resolveDelegate(spec.target);
|
|
885
1433
|
const where = this.toQueryWhere(spec.where);
|
|
@@ -889,21 +1437,46 @@ var PrismaDatabaseAdapter = class PrismaDatabaseAdapter {
|
|
|
889
1437
|
}));
|
|
890
1438
|
return rows.length;
|
|
891
1439
|
}
|
|
1440
|
+
/**
|
|
1441
|
+
* Counts the number of records matching the specified criteria.
|
|
1442
|
+
*
|
|
1443
|
+
* @param spec
|
|
1444
|
+
* @returns
|
|
1445
|
+
*/
|
|
892
1446
|
async count(spec) {
|
|
893
1447
|
return await this.resolveDelegate(spec.target).count({ where: this.toQueryWhere(spec.where) });
|
|
894
1448
|
}
|
|
1449
|
+
/**
|
|
1450
|
+
* Checks for the existence of records matching the specified criteria.
|
|
1451
|
+
*
|
|
1452
|
+
* @param spec
|
|
1453
|
+
* @returns
|
|
1454
|
+
*/
|
|
895
1455
|
async exists(spec) {
|
|
896
1456
|
return await this.selectOne({
|
|
897
1457
|
...spec,
|
|
898
1458
|
limit: 1
|
|
899
1459
|
}) != null;
|
|
900
1460
|
}
|
|
1461
|
+
/**
|
|
1462
|
+
* Loads related models for a batch of parent records based on the specified relation load plans.
|
|
1463
|
+
*
|
|
1464
|
+
* @param _spec
|
|
1465
|
+
*/
|
|
901
1466
|
async loadRelations(_spec) {
|
|
902
1467
|
throw new UnsupportedAdapterFeatureException("Relation batch loading is not supported by the Prisma compatibility adapter yet.", {
|
|
903
1468
|
operation: "adapter.loadRelations",
|
|
904
1469
|
meta: { feature: "relationLoads" }
|
|
905
1470
|
});
|
|
906
1471
|
}
|
|
1472
|
+
/**
|
|
1473
|
+
* Executes a series of database operations within a transaction.
|
|
1474
|
+
* If the underlying Prisma client does not support transactions, an exception is thrown.
|
|
1475
|
+
*
|
|
1476
|
+
* @param callback
|
|
1477
|
+
* @param context
|
|
1478
|
+
* @returns
|
|
1479
|
+
*/
|
|
907
1480
|
async transaction(callback, context = {}) {
|
|
908
1481
|
if (!this.hasTransactionSupport(this.prisma)) throw new UnsupportedAdapterFeatureException("Transactions are not supported by the Prisma compatibility adapter.", {
|
|
909
1482
|
operation: "adapter.transaction",
|
|
@@ -918,9 +1491,25 @@ var PrismaDatabaseAdapter = class PrismaDatabaseAdapter {
|
|
|
918
1491
|
});
|
|
919
1492
|
}
|
|
920
1493
|
};
|
|
1494
|
+
/**
|
|
1495
|
+
* Factory function to create a PrismaDatabaseAdapter instance with the given
|
|
1496
|
+
* Prisma client and optional delegate name mapping.
|
|
1497
|
+
*
|
|
1498
|
+
* @param prisma The Prisma client instance to be used by the adapter.
|
|
1499
|
+
* @param mapping Optional mapping of delegate names.
|
|
1500
|
+
* @returns A new instance of PrismaDatabaseAdapter.
|
|
1501
|
+
*/
|
|
921
1502
|
const createPrismaDatabaseAdapter = (prisma, mapping = {}) => {
|
|
922
1503
|
return new PrismaDatabaseAdapter(prisma, mapping);
|
|
923
1504
|
};
|
|
1505
|
+
/**
|
|
1506
|
+
* Alias for createPrismaDatabaseAdapter to maintain backward compatibility with
|
|
1507
|
+
* previous versions of Arkorm that exported the adapter factory under a different name.
|
|
1508
|
+
*
|
|
1509
|
+
* @param prisma The Prisma client instance to be used by the adapter.
|
|
1510
|
+
* @param mapping Optional mapping of delegate names.
|
|
1511
|
+
* @returns A new instance of PrismaDatabaseAdapter.
|
|
1512
|
+
*/
|
|
924
1513
|
const createPrismaCompatibilityAdapter = createPrismaDatabaseAdapter;
|
|
925
1514
|
|
|
926
1515
|
//#endregion
|
|
@@ -2892,6 +3481,40 @@ var CliApp = class {
|
|
|
2892
3481
|
lines.splice(insertionIndex, 0, `import type { ${enumTypes.join(", ")} } from '@prisma/client'`);
|
|
2893
3482
|
return lines.join("\n");
|
|
2894
3483
|
}
|
|
3484
|
+
parseModelSyncSource(modelSource) {
|
|
3485
|
+
const classMatch = modelSource.match(/export\s+class\s+(\w+)\s+extends\s+Model(?:<[^\n]+>)?\s*\{/);
|
|
3486
|
+
if (!classMatch) return null;
|
|
3487
|
+
const className = classMatch[1];
|
|
3488
|
+
const tableMatch = modelSource.match(/protected\s+static\s+override\s+table\s*=\s*['"]([^'"]+)['"]/) ?? modelSource.match(/static\s+table\s*=\s*['"]([^'"]+)['"]/);
|
|
3489
|
+
const delegateMatch = modelSource.match(/protected\s+static\s+override\s+delegate\s*=\s*['"]([^'"]+)['"]/) ?? modelSource.match(/static\s+delegate\s*=\s*['"]([^'"]+)['"]/);
|
|
3490
|
+
return {
|
|
3491
|
+
className,
|
|
3492
|
+
table: tableMatch?.[1] ?? delegateMatch?.[1] ?? str(className).camel().plural().toString()
|
|
3493
|
+
};
|
|
3494
|
+
}
|
|
3495
|
+
syncModelFiles(modelFiles, resolveStructure, enums) {
|
|
3496
|
+
const updated = [];
|
|
3497
|
+
const skipped = [];
|
|
3498
|
+
modelFiles.forEach((filePath) => {
|
|
3499
|
+
const source = readFileSync(filePath, "utf-8");
|
|
3500
|
+
const structure = resolveStructure(filePath, source);
|
|
3501
|
+
if (!structure || structure.fields.length === 0) {
|
|
3502
|
+
skipped.push(filePath);
|
|
3503
|
+
return;
|
|
3504
|
+
}
|
|
3505
|
+
const synced = this.syncModelDeclarations(source, structure.fields, enums);
|
|
3506
|
+
if (!synced.updated) {
|
|
3507
|
+
skipped.push(filePath);
|
|
3508
|
+
return;
|
|
3509
|
+
}
|
|
3510
|
+
writeFileSync(filePath, synced.content);
|
|
3511
|
+
updated.push(filePath);
|
|
3512
|
+
});
|
|
3513
|
+
return {
|
|
3514
|
+
updated,
|
|
3515
|
+
skipped
|
|
3516
|
+
};
|
|
3517
|
+
}
|
|
2895
3518
|
/**
|
|
2896
3519
|
* Parse Prisma enum definitions from a schema and return their member names.
|
|
2897
3520
|
*
|
|
@@ -2984,7 +3607,7 @@ var CliApp = class {
|
|
|
2984
3607
|
*/
|
|
2985
3608
|
syncModelDeclarations(modelSource, declarations, enums) {
|
|
2986
3609
|
const lines = modelSource.split("\n");
|
|
2987
|
-
const classIndex = lines.findIndex((line) => /export\s+class\s+\w+\s+extends\s+Model
|
|
3610
|
+
const classIndex = lines.findIndex((line) => /export\s+class\s+\w+\s+extends\s+Model(?:<[^\n]+>)?\s*\{/.test(line));
|
|
2988
3611
|
if (classIndex < 0) return {
|
|
2989
3612
|
content: modelSource,
|
|
2990
3613
|
updated: false
|
|
@@ -3038,6 +3661,36 @@ var CliApp = class {
|
|
|
3038
3661
|
updated: contentWithImports !== modelSource
|
|
3039
3662
|
};
|
|
3040
3663
|
}
|
|
3664
|
+
async syncModels(options = {}) {
|
|
3665
|
+
const modelsDir = options.modelsDir ?? this.resolveConfigPath("models", join$1(process.cwd(), "src", "models"));
|
|
3666
|
+
if (!existsSync(modelsDir)) throw new Error(`Models directory not found: ${modelsDir}`);
|
|
3667
|
+
const modelFiles = readdirSync(modelsDir).filter((file) => file.endsWith(".ts")).map((file) => join$1(modelsDir, file));
|
|
3668
|
+
const adapter = this.getConfig("adapter");
|
|
3669
|
+
if (adapter && typeof adapter.introspectModels === "function") {
|
|
3670
|
+
const sources = modelFiles.reduce((all, filePath) => {
|
|
3671
|
+
const parsed = this.parseModelSyncSource(readFileSync(filePath, "utf-8"));
|
|
3672
|
+
if (parsed) all.set(filePath, parsed);
|
|
3673
|
+
return all;
|
|
3674
|
+
}, /* @__PURE__ */ new Map());
|
|
3675
|
+
const discovered = await adapter.introspectModels({ tables: [...new Set([...sources.values()].map((source) => source.table))] });
|
|
3676
|
+
const structuresByTable = new Map(discovered.map((model) => [model.table, model]));
|
|
3677
|
+
const result = this.syncModelFiles(modelFiles, (filePath) => {
|
|
3678
|
+
const parsed = sources.get(filePath);
|
|
3679
|
+
return parsed ? structuresByTable.get(parsed.table) : void 0;
|
|
3680
|
+
}, /* @__PURE__ */ new Map());
|
|
3681
|
+
return {
|
|
3682
|
+
source: "adapter",
|
|
3683
|
+
modelsDir,
|
|
3684
|
+
total: modelFiles.length,
|
|
3685
|
+
updated: result.updated,
|
|
3686
|
+
skipped: result.skipped
|
|
3687
|
+
};
|
|
3688
|
+
}
|
|
3689
|
+
return {
|
|
3690
|
+
source: "prisma",
|
|
3691
|
+
...this.syncModelsFromPrisma(options)
|
|
3692
|
+
};
|
|
3693
|
+
}
|
|
3041
3694
|
/**
|
|
3042
3695
|
* Sync model attribute declarations in model files based on the Prisma schema.
|
|
3043
3696
|
* This method reads the Prisma schema to extract model definitions and their
|
|
@@ -3057,38 +3710,18 @@ var CliApp = class {
|
|
|
3057
3710
|
const schema = readFileSync(schemaPath, "utf-8");
|
|
3058
3711
|
const prismaEnums = this.parsePrismaEnums(schema);
|
|
3059
3712
|
const prismaModels = this.parsePrismaModels(schema);
|
|
3060
|
-
const modelFiles = readdirSync(modelsDir).filter((file) => file.endsWith(".ts"));
|
|
3061
|
-
const
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
const classMatch = source.match(/export\s+class\s+(\w+)\s+extends\s+Model<'([^']+)'>/);
|
|
3067
|
-
if (!classMatch) {
|
|
3068
|
-
skipped.push(filePath);
|
|
3069
|
-
return;
|
|
3070
|
-
}
|
|
3071
|
-
const className = classMatch[1];
|
|
3072
|
-
const delegate = classMatch[2];
|
|
3073
|
-
const prismaModel = prismaModels.find((model) => model.table === delegate) ?? prismaModels.find((model) => model.name === className);
|
|
3074
|
-
if (!prismaModel || prismaModel.fields.length === 0) {
|
|
3075
|
-
skipped.push(filePath);
|
|
3076
|
-
return;
|
|
3077
|
-
}
|
|
3078
|
-
const synced = this.syncModelDeclarations(source, prismaModel.fields, prismaEnums);
|
|
3079
|
-
if (!synced.updated) {
|
|
3080
|
-
skipped.push(filePath);
|
|
3081
|
-
return;
|
|
3082
|
-
}
|
|
3083
|
-
writeFileSync(filePath, synced.content);
|
|
3084
|
-
updated.push(filePath);
|
|
3085
|
-
});
|
|
3713
|
+
const modelFiles = readdirSync(modelsDir).filter((file) => file.endsWith(".ts")).map((file) => join$1(modelsDir, file));
|
|
3714
|
+
const result = this.syncModelFiles(modelFiles, (filePath, source) => {
|
|
3715
|
+
const parsed = this.parseModelSyncSource(source);
|
|
3716
|
+
if (!parsed) return void 0;
|
|
3717
|
+
return prismaModels.find((model) => model.table === parsed.table) ?? prismaModels.find((model) => model.name === parsed.className);
|
|
3718
|
+
}, prismaEnums);
|
|
3086
3719
|
return {
|
|
3087
3720
|
schemaPath,
|
|
3088
3721
|
modelsDir,
|
|
3089
3722
|
total: modelFiles.length,
|
|
3090
|
-
updated,
|
|
3091
|
-
skipped
|
|
3723
|
+
updated: result.updated,
|
|
3724
|
+
skipped: result.skipped
|
|
3092
3725
|
};
|
|
3093
3726
|
}
|
|
3094
3727
|
};
|
|
@@ -3666,20 +4299,21 @@ var MigrationHistoryCommand = class extends Command {
|
|
|
3666
4299
|
//#region src/cli/commands/ModelsSyncCommand.ts
|
|
3667
4300
|
var ModelsSyncCommand = class extends Command {
|
|
3668
4301
|
signature = `models:sync
|
|
3669
|
-
{--schema= : Path to prisma schema file}
|
|
4302
|
+
{--schema= : Path to prisma schema file used when adapter introspection is unavailable}
|
|
3670
4303
|
{--models= : Path to models directory}
|
|
3671
4304
|
`;
|
|
3672
|
-
description = "Sync model declare attributes from
|
|
4305
|
+
description = "Sync model declare attributes from the active adapter when supported, otherwise fall back to the Prisma schema";
|
|
3673
4306
|
async handle() {
|
|
3674
4307
|
this.app.command = this;
|
|
3675
|
-
const result = this.app.
|
|
4308
|
+
const result = await this.app.syncModels({
|
|
3676
4309
|
schemaPath: this.option("schema") ? resolve(String(this.option("schema"))) : void 0,
|
|
3677
4310
|
modelsDir: this.option("models") ? resolve(String(this.option("models"))) : void 0
|
|
3678
4311
|
});
|
|
3679
4312
|
const updatedLines = result.updated.length === 0 ? [this.app.splitLogger("Updated", "none")] : result.updated.map((path) => this.app.splitLogger("Updated", path));
|
|
3680
4313
|
this.success("SUCCESS: Model sync completed with the following results:");
|
|
3681
4314
|
[
|
|
3682
|
-
this.app.splitLogger("
|
|
4315
|
+
this.app.splitLogger("Source", result.source === "adapter" ? "adapter introspection" : "prisma schema"),
|
|
4316
|
+
...result.schemaPath ? [this.app.splitLogger("Schema", result.schemaPath)] : [],
|
|
3683
4317
|
this.app.splitLogger("Models", result.modelsDir),
|
|
3684
4318
|
this.app.splitLogger("Processed", String(result.total)),
|
|
3685
4319
|
...updatedLines,
|
|
@@ -4039,6 +4673,13 @@ var UniqueConstraintResolutionException = class extends ArkormException {
|
|
|
4039
4673
|
|
|
4040
4674
|
//#endregion
|
|
4041
4675
|
//#region src/relationship/RelationTableLoader.ts
|
|
4676
|
+
/**
|
|
4677
|
+
* Utility class responsible for loading data from relation tables, which are used to
|
|
4678
|
+
* manage relationships between models in Arkorm.
|
|
4679
|
+
*
|
|
4680
|
+
* @author Legacy (3m1n3nc3)
|
|
4681
|
+
* @since 2.0.0-next.0
|
|
4682
|
+
*/
|
|
4042
4683
|
var RelationTableLoader = class {
|
|
4043
4684
|
constructor(adapter) {
|
|
4044
4685
|
this.adapter = adapter;
|
|
@@ -4800,6 +5441,472 @@ var MorphToManyRelation = class extends Relation {
|
|
|
4800
5441
|
}
|
|
4801
5442
|
};
|
|
4802
5443
|
|
|
5444
|
+
//#endregion
|
|
5445
|
+
//#region src/relationship/SetBasedEagerLoader.ts
|
|
5446
|
+
/**
|
|
5447
|
+
* Utility class responsible for performing set-based eager loading of relationships for
|
|
5448
|
+
* a collection of models.
|
|
5449
|
+
*
|
|
5450
|
+
* @author Legacy (3m1n3nc3)
|
|
5451
|
+
* @since 2.0.0-next.2
|
|
5452
|
+
*/
|
|
5453
|
+
var SetBasedEagerLoader = class {
|
|
5454
|
+
constructor(models, relations) {
|
|
5455
|
+
this.models = models;
|
|
5456
|
+
this.relations = relations;
|
|
5457
|
+
}
|
|
5458
|
+
/**
|
|
5459
|
+
* Performs eager loading of all specified relationships for the set of models.
|
|
5460
|
+
*
|
|
5461
|
+
* @returns
|
|
5462
|
+
*/
|
|
5463
|
+
async load() {
|
|
5464
|
+
if (this.models.length === 0) return;
|
|
5465
|
+
await Promise.all(Object.entries(this.relations).map(async ([name, constraint]) => {
|
|
5466
|
+
await this.loadRelation(name, constraint);
|
|
5467
|
+
}));
|
|
5468
|
+
}
|
|
5469
|
+
/**
|
|
5470
|
+
* Loads a specific relationship for the set of models based on the relationship name
|
|
5471
|
+
* and an optional constraint.
|
|
5472
|
+
*
|
|
5473
|
+
* @param name The name of the relationship to load.
|
|
5474
|
+
* @param constraint An optional constraint to apply to the query.
|
|
5475
|
+
* @returns A promise that resolves when the relationship is loaded.
|
|
5476
|
+
*/
|
|
5477
|
+
async loadRelation(name, constraint) {
|
|
5478
|
+
const resolver = this.resolveRelationResolver(name);
|
|
5479
|
+
if (!resolver) return;
|
|
5480
|
+
const metadata = resolver.call(this.models[0]).getMetadata();
|
|
5481
|
+
switch (metadata.type) {
|
|
5482
|
+
case "belongsTo":
|
|
5483
|
+
await this.loadBelongsTo(name, resolver, metadata, constraint);
|
|
5484
|
+
return;
|
|
5485
|
+
case "belongsToMany":
|
|
5486
|
+
await this.loadBelongsToMany(name, metadata, constraint);
|
|
5487
|
+
return;
|
|
5488
|
+
case "hasMany":
|
|
5489
|
+
await this.loadHasMany(name, metadata, constraint);
|
|
5490
|
+
return;
|
|
5491
|
+
case "hasOne":
|
|
5492
|
+
await this.loadHasOne(name, resolver, metadata, constraint);
|
|
5493
|
+
return;
|
|
5494
|
+
case "hasManyThrough":
|
|
5495
|
+
await this.loadHasManyThrough(name, metadata, constraint);
|
|
5496
|
+
return;
|
|
5497
|
+
case "hasOneThrough":
|
|
5498
|
+
await this.loadHasOneThrough(name, resolver, metadata, constraint);
|
|
5499
|
+
return;
|
|
5500
|
+
default: await this.loadIndividually(name, resolver, constraint);
|
|
5501
|
+
}
|
|
5502
|
+
}
|
|
5503
|
+
/**
|
|
5504
|
+
* Resolves the relation resolver function for a given relationship name by inspecting
|
|
5505
|
+
* the first model in the set.
|
|
5506
|
+
*
|
|
5507
|
+
* @param name The name of the relationship to resolve.
|
|
5508
|
+
* @returns The relation resolver function or null if not found.
|
|
5509
|
+
*/
|
|
5510
|
+
resolveRelationResolver(name) {
|
|
5511
|
+
const resolver = this.models[0][name];
|
|
5512
|
+
if (typeof resolver !== "function") return null;
|
|
5513
|
+
return resolver;
|
|
5514
|
+
}
|
|
5515
|
+
/**
|
|
5516
|
+
* Loads a "belongs to" relationship for the set of models.
|
|
5517
|
+
*
|
|
5518
|
+
* @param name The name of the relationship to load.
|
|
5519
|
+
* @param resolver The relation resolver function.
|
|
5520
|
+
* @param metadata The metadata for the relationship.
|
|
5521
|
+
* @param constraint An optional constraint to apply to the query.
|
|
5522
|
+
* @returns A promise that resolves when the relationship is loaded.
|
|
5523
|
+
*/
|
|
5524
|
+
async loadBelongsTo(name, resolver, metadata, constraint) {
|
|
5525
|
+
const keys = this.collectUniqueKeys((model) => model.getAttribute(metadata.foreignKey));
|
|
5526
|
+
if (keys.length === 0) {
|
|
5527
|
+
this.models.forEach((model) => {
|
|
5528
|
+
model.setLoadedRelation(name, this.resolveSingleDefault(resolver, model));
|
|
5529
|
+
});
|
|
5530
|
+
return;
|
|
5531
|
+
}
|
|
5532
|
+
let query = metadata.relatedModel.query().whereIn(metadata.ownerKey, keys);
|
|
5533
|
+
query = this.applyConstraint(query, constraint);
|
|
5534
|
+
const relatedModels = (await query.get()).all();
|
|
5535
|
+
const relatedByOwnerKey = /* @__PURE__ */ new Map();
|
|
5536
|
+
relatedModels.forEach((related) => {
|
|
5537
|
+
const value = this.readModelAttribute(related, metadata.ownerKey);
|
|
5538
|
+
if (value == null) return;
|
|
5539
|
+
const lookupKey = this.toLookupKey(value);
|
|
5540
|
+
if (!relatedByOwnerKey.has(lookupKey)) relatedByOwnerKey.set(lookupKey, related);
|
|
5541
|
+
});
|
|
5542
|
+
this.models.forEach((model) => {
|
|
5543
|
+
const foreignValue = model.getAttribute(metadata.foreignKey);
|
|
5544
|
+
const relationValue = foreignValue == null ? void 0 : relatedByOwnerKey.get(this.toLookupKey(foreignValue));
|
|
5545
|
+
model.setLoadedRelation(name, relationValue ?? this.resolveSingleDefault(resolver, model));
|
|
5546
|
+
});
|
|
5547
|
+
}
|
|
5548
|
+
/**
|
|
5549
|
+
* Loads a "has many" relationship for the set of models.
|
|
5550
|
+
*
|
|
5551
|
+
* @param name
|
|
5552
|
+
* @param metadata
|
|
5553
|
+
* @param constraint
|
|
5554
|
+
* @returns
|
|
5555
|
+
*/
|
|
5556
|
+
async loadHasMany(name, metadata, constraint) {
|
|
5557
|
+
const keys = this.collectUniqueKeys((model) => model.getAttribute(metadata.localKey));
|
|
5558
|
+
if (keys.length === 0) {
|
|
5559
|
+
this.models.forEach((model) => {
|
|
5560
|
+
model.setLoadedRelation(name, new ArkormCollection([]));
|
|
5561
|
+
});
|
|
5562
|
+
return;
|
|
5563
|
+
}
|
|
5564
|
+
let query = metadata.relatedModel.query().whereIn(metadata.foreignKey, keys);
|
|
5565
|
+
query = this.applyConstraint(query, constraint);
|
|
5566
|
+
const relatedModels = (await query.get()).all();
|
|
5567
|
+
const relatedByForeignKey = /* @__PURE__ */ new Map();
|
|
5568
|
+
relatedModels.forEach((related) => {
|
|
5569
|
+
const value = this.readModelAttribute(related, metadata.foreignKey);
|
|
5570
|
+
if (value == null) return;
|
|
5571
|
+
const lookupKey = this.toLookupKey(value);
|
|
5572
|
+
const bucket = relatedByForeignKey.get(lookupKey) ?? [];
|
|
5573
|
+
bucket.push(related);
|
|
5574
|
+
relatedByForeignKey.set(lookupKey, bucket);
|
|
5575
|
+
});
|
|
5576
|
+
this.models.forEach((model) => {
|
|
5577
|
+
const localValue = model.getAttribute(metadata.localKey);
|
|
5578
|
+
const related = localValue == null ? [] : relatedByForeignKey.get(this.toLookupKey(localValue)) ?? [];
|
|
5579
|
+
model.setLoadedRelation(name, new ArkormCollection(related));
|
|
5580
|
+
});
|
|
5581
|
+
}
|
|
5582
|
+
/**
|
|
5583
|
+
* Loads a "belongs to many" relationship for the set of models.
|
|
5584
|
+
*
|
|
5585
|
+
* @param name
|
|
5586
|
+
* @param metadata
|
|
5587
|
+
* @param constraint
|
|
5588
|
+
* @returns
|
|
5589
|
+
*/
|
|
5590
|
+
async loadBelongsToMany(name, metadata, constraint) {
|
|
5591
|
+
const parentKeys = this.collectUniqueKeys((model) => model.getAttribute(metadata.parentKey));
|
|
5592
|
+
if (parentKeys.length === 0) {
|
|
5593
|
+
this.models.forEach((model) => {
|
|
5594
|
+
model.setLoadedRelation(name, new ArkormCollection([]));
|
|
5595
|
+
});
|
|
5596
|
+
return;
|
|
5597
|
+
}
|
|
5598
|
+
const pivotRows = await this.createRelationTableLoader().selectRows({
|
|
5599
|
+
table: metadata.throughTable,
|
|
5600
|
+
where: {
|
|
5601
|
+
type: "comparison",
|
|
5602
|
+
column: metadata.foreignPivotKey,
|
|
5603
|
+
operator: "in",
|
|
5604
|
+
value: parentKeys
|
|
5605
|
+
}
|
|
5606
|
+
});
|
|
5607
|
+
const relatedIds = this.collectUniqueRowValues(pivotRows, metadata.relatedPivotKey);
|
|
5608
|
+
if (relatedIds.length === 0) {
|
|
5609
|
+
this.models.forEach((model) => {
|
|
5610
|
+
model.setLoadedRelation(name, new ArkormCollection([]));
|
|
5611
|
+
});
|
|
5612
|
+
return;
|
|
5613
|
+
}
|
|
5614
|
+
let query = metadata.relatedModel.query().whereIn(metadata.relatedKey, relatedIds);
|
|
5615
|
+
query = this.applyConstraint(query, constraint);
|
|
5616
|
+
const relatedModels = (await query.get()).all();
|
|
5617
|
+
const relatedByKey = /* @__PURE__ */ new Map();
|
|
5618
|
+
relatedModels.forEach((related) => {
|
|
5619
|
+
const relatedValue = this.readModelAttribute(related, metadata.relatedKey);
|
|
5620
|
+
if (relatedValue == null) return;
|
|
5621
|
+
relatedByKey.set(this.toLookupKey(relatedValue), related);
|
|
5622
|
+
});
|
|
5623
|
+
const relatedKeysByParent = /* @__PURE__ */ new Map();
|
|
5624
|
+
pivotRows.forEach((row) => {
|
|
5625
|
+
const parentValue = row[metadata.foreignPivotKey];
|
|
5626
|
+
const relatedValue = row[metadata.relatedPivotKey];
|
|
5627
|
+
if (parentValue == null || relatedValue == null) return;
|
|
5628
|
+
const bucket = relatedKeysByParent.get(this.toLookupKey(parentValue)) ?? [];
|
|
5629
|
+
bucket.push(relatedValue);
|
|
5630
|
+
relatedKeysByParent.set(this.toLookupKey(parentValue), bucket);
|
|
5631
|
+
});
|
|
5632
|
+
this.models.forEach((model) => {
|
|
5633
|
+
const parentValue = model.getAttribute(metadata.parentKey);
|
|
5634
|
+
const related = (parentValue == null ? [] : relatedKeysByParent.get(this.toLookupKey(parentValue)) ?? []).reduce((all, relatedValue) => {
|
|
5635
|
+
const candidate = relatedByKey.get(this.toLookupKey(relatedValue));
|
|
5636
|
+
if (candidate) all.push(candidate);
|
|
5637
|
+
return all;
|
|
5638
|
+
}, []);
|
|
5639
|
+
model.setLoadedRelation(name, new ArkormCollection(related));
|
|
5640
|
+
});
|
|
5641
|
+
}
|
|
5642
|
+
/**
|
|
5643
|
+
* Loads a "belongs to many" relationship for the set of models.
|
|
5644
|
+
*
|
|
5645
|
+
* @param name
|
|
5646
|
+
* @param resolver
|
|
5647
|
+
* @param metadata
|
|
5648
|
+
* @param constraint
|
|
5649
|
+
* @returns
|
|
5650
|
+
*/
|
|
5651
|
+
async loadHasOne(name, resolver, metadata, constraint) {
|
|
5652
|
+
const keys = this.collectUniqueKeys((model) => model.getAttribute(metadata.localKey));
|
|
5653
|
+
if (keys.length === 0) {
|
|
5654
|
+
this.models.forEach((model) => {
|
|
5655
|
+
model.setLoadedRelation(name, this.resolveSingleDefault(resolver, model));
|
|
5656
|
+
});
|
|
5657
|
+
return;
|
|
5658
|
+
}
|
|
5659
|
+
let query = metadata.relatedModel.query().whereIn(metadata.foreignKey, keys);
|
|
5660
|
+
query = this.applyConstraint(query, constraint);
|
|
5661
|
+
const relatedModels = (await query.get()).all();
|
|
5662
|
+
const relatedByForeignKey = /* @__PURE__ */ new Map();
|
|
5663
|
+
relatedModels.forEach((related) => {
|
|
5664
|
+
const value = this.readModelAttribute(related, metadata.foreignKey);
|
|
5665
|
+
if (value == null) return;
|
|
5666
|
+
const lookupKey = this.toLookupKey(value);
|
|
5667
|
+
if (!relatedByForeignKey.has(lookupKey)) relatedByForeignKey.set(lookupKey, related);
|
|
5668
|
+
});
|
|
5669
|
+
this.models.forEach((model) => {
|
|
5670
|
+
const localValue = model.getAttribute(metadata.localKey);
|
|
5671
|
+
const relationValue = localValue == null ? void 0 : relatedByForeignKey.get(this.toLookupKey(localValue));
|
|
5672
|
+
model.setLoadedRelation(name, relationValue ?? this.resolveSingleDefault(resolver, model));
|
|
5673
|
+
});
|
|
5674
|
+
}
|
|
5675
|
+
/**
|
|
5676
|
+
* Loads a "has many through" relationship for the set of models.
|
|
5677
|
+
*
|
|
5678
|
+
* @param name
|
|
5679
|
+
* @param metadata
|
|
5680
|
+
* @param constraint
|
|
5681
|
+
* @returns
|
|
5682
|
+
*/
|
|
5683
|
+
async loadHasManyThrough(name, metadata, constraint) {
|
|
5684
|
+
const parentKeys = this.collectUniqueKeys((model) => model.getAttribute(metadata.localKey));
|
|
5685
|
+
if (parentKeys.length === 0) {
|
|
5686
|
+
this.models.forEach((model) => {
|
|
5687
|
+
model.setLoadedRelation(name, new ArkormCollection([]));
|
|
5688
|
+
});
|
|
5689
|
+
return;
|
|
5690
|
+
}
|
|
5691
|
+
const throughRows = await this.createRelationTableLoader().selectRows({
|
|
5692
|
+
table: metadata.throughTable,
|
|
5693
|
+
where: {
|
|
5694
|
+
type: "comparison",
|
|
5695
|
+
column: metadata.firstKey,
|
|
5696
|
+
operator: "in",
|
|
5697
|
+
value: parentKeys
|
|
5698
|
+
}
|
|
5699
|
+
});
|
|
5700
|
+
const intermediateKeys = this.collectUniqueRowValues(throughRows, metadata.secondLocalKey);
|
|
5701
|
+
if (intermediateKeys.length === 0) {
|
|
5702
|
+
this.models.forEach((model) => {
|
|
5703
|
+
model.setLoadedRelation(name, new ArkormCollection([]));
|
|
5704
|
+
});
|
|
5705
|
+
return;
|
|
5706
|
+
}
|
|
5707
|
+
let query = metadata.relatedModel.query().whereIn(metadata.secondKey, intermediateKeys);
|
|
5708
|
+
query = this.applyConstraint(query, constraint);
|
|
5709
|
+
const relatedModels = (await query.get()).all();
|
|
5710
|
+
const relatedByIntermediate = /* @__PURE__ */ new Map();
|
|
5711
|
+
relatedModels.forEach((related) => {
|
|
5712
|
+
const relatedValue = this.readModelAttribute(related, metadata.secondKey);
|
|
5713
|
+
if (relatedValue == null) return;
|
|
5714
|
+
const bucket = relatedByIntermediate.get(this.toLookupKey(relatedValue)) ?? [];
|
|
5715
|
+
bucket.push(related);
|
|
5716
|
+
relatedByIntermediate.set(this.toLookupKey(relatedValue), bucket);
|
|
5717
|
+
});
|
|
5718
|
+
const intermediateByParent = /* @__PURE__ */ new Map();
|
|
5719
|
+
throughRows.forEach((row) => {
|
|
5720
|
+
const parentValue = row[metadata.firstKey];
|
|
5721
|
+
const intermediateValue = row[metadata.secondLocalKey];
|
|
5722
|
+
if (parentValue == null || intermediateValue == null) return;
|
|
5723
|
+
const bucket = intermediateByParent.get(this.toLookupKey(parentValue)) ?? [];
|
|
5724
|
+
bucket.push(intermediateValue);
|
|
5725
|
+
intermediateByParent.set(this.toLookupKey(parentValue), bucket);
|
|
5726
|
+
});
|
|
5727
|
+
this.models.forEach((model) => {
|
|
5728
|
+
const parentValue = model.getAttribute(metadata.localKey);
|
|
5729
|
+
const related = (parentValue == null ? [] : intermediateByParent.get(this.toLookupKey(parentValue)) ?? []).flatMap((intermediateValue) => relatedByIntermediate.get(this.toLookupKey(intermediateValue)) ?? []);
|
|
5730
|
+
model.setLoadedRelation(name, new ArkormCollection(related));
|
|
5731
|
+
});
|
|
5732
|
+
}
|
|
5733
|
+
/**
|
|
5734
|
+
* Loads a "has one through" relationship for the set of models.
|
|
5735
|
+
*
|
|
5736
|
+
* @param name
|
|
5737
|
+
* @param resolver
|
|
5738
|
+
* @param metadata
|
|
5739
|
+
* @param constraint
|
|
5740
|
+
* @returns
|
|
5741
|
+
*/
|
|
5742
|
+
async loadHasOneThrough(name, resolver, metadata, constraint) {
|
|
5743
|
+
const parentKeys = this.collectUniqueKeys((model) => model.getAttribute(metadata.localKey));
|
|
5744
|
+
if (parentKeys.length === 0) {
|
|
5745
|
+
this.models.forEach((model) => {
|
|
5746
|
+
model.setLoadedRelation(name, this.resolveSingleDefault(resolver, model));
|
|
5747
|
+
});
|
|
5748
|
+
return;
|
|
5749
|
+
}
|
|
5750
|
+
const throughRows = await this.createRelationTableLoader().selectRows({
|
|
5751
|
+
table: metadata.throughTable,
|
|
5752
|
+
where: {
|
|
5753
|
+
type: "comparison",
|
|
5754
|
+
column: metadata.firstKey,
|
|
5755
|
+
operator: "in",
|
|
5756
|
+
value: parentKeys
|
|
5757
|
+
}
|
|
5758
|
+
});
|
|
5759
|
+
const intermediateKeys = this.collectUniqueRowValues(throughRows, metadata.secondLocalKey);
|
|
5760
|
+
if (intermediateKeys.length === 0) {
|
|
5761
|
+
this.models.forEach((model) => {
|
|
5762
|
+
model.setLoadedRelation(name, this.resolveSingleDefault(resolver, model));
|
|
5763
|
+
});
|
|
5764
|
+
return;
|
|
5765
|
+
}
|
|
5766
|
+
let query = metadata.relatedModel.query().whereIn(metadata.secondKey, intermediateKeys);
|
|
5767
|
+
query = this.applyConstraint(query, constraint);
|
|
5768
|
+
const relatedModels = (await query.get()).all();
|
|
5769
|
+
const relatedByIntermediate = /* @__PURE__ */ new Map();
|
|
5770
|
+
relatedModels.forEach((related) => {
|
|
5771
|
+
const relatedValue = this.readModelAttribute(related, metadata.secondKey);
|
|
5772
|
+
if (relatedValue == null) return;
|
|
5773
|
+
const lookupKey = this.toLookupKey(relatedValue);
|
|
5774
|
+
if (!relatedByIntermediate.has(lookupKey)) relatedByIntermediate.set(lookupKey, related);
|
|
5775
|
+
});
|
|
5776
|
+
const intermediateByParent = /* @__PURE__ */ new Map();
|
|
5777
|
+
throughRows.forEach((row) => {
|
|
5778
|
+
const parentValue = row[metadata.firstKey];
|
|
5779
|
+
const intermediateValue = row[metadata.secondLocalKey];
|
|
5780
|
+
if (parentValue == null || intermediateValue == null) return;
|
|
5781
|
+
const lookupKey = this.toLookupKey(parentValue);
|
|
5782
|
+
if (!intermediateByParent.has(lookupKey)) intermediateByParent.set(lookupKey, intermediateValue);
|
|
5783
|
+
});
|
|
5784
|
+
this.models.forEach((model) => {
|
|
5785
|
+
const parentValue = model.getAttribute(metadata.localKey);
|
|
5786
|
+
const intermediateValue = parentValue == null ? void 0 : intermediateByParent.get(this.toLookupKey(parentValue));
|
|
5787
|
+
const relationValue = intermediateValue == null ? void 0 : relatedByIntermediate.get(this.toLookupKey(intermediateValue));
|
|
5788
|
+
model.setLoadedRelation(name, relationValue ?? this.resolveSingleDefault(resolver, model));
|
|
5789
|
+
});
|
|
5790
|
+
}
|
|
5791
|
+
/**
|
|
5792
|
+
* Fallback method to load relationships individually for each model when the
|
|
5793
|
+
* relationship type is not supported for set-based loading.
|
|
5794
|
+
*
|
|
5795
|
+
* @param name
|
|
5796
|
+
* @param resolver
|
|
5797
|
+
* @param constraint
|
|
5798
|
+
*/
|
|
5799
|
+
async loadIndividually(name, resolver, constraint) {
|
|
5800
|
+
await Promise.all(this.models.map(async (model) => {
|
|
5801
|
+
const relation = resolver.call(model);
|
|
5802
|
+
if (constraint) relation.constrain(constraint);
|
|
5803
|
+
model.setLoadedRelation(name, await relation.getResults());
|
|
5804
|
+
}));
|
|
5805
|
+
}
|
|
5806
|
+
/**
|
|
5807
|
+
* Applies an eager load constraint to a query if provided.
|
|
5808
|
+
*
|
|
5809
|
+
* @param query
|
|
5810
|
+
* @param constraint
|
|
5811
|
+
* @returns
|
|
5812
|
+
*/
|
|
5813
|
+
applyConstraint(query, constraint) {
|
|
5814
|
+
if (!constraint) return query;
|
|
5815
|
+
return constraint(query) ?? query;
|
|
5816
|
+
}
|
|
5817
|
+
/**
|
|
5818
|
+
* Collects unique values from the set of models based on a resolver function, which
|
|
5819
|
+
* is used to extract the value from each model.
|
|
5820
|
+
*
|
|
5821
|
+
* @param resolve A function that takes a model and returns the value to be collected.
|
|
5822
|
+
* @returns An array of unique values.
|
|
5823
|
+
*/
|
|
5824
|
+
collectUniqueKeys(resolve) {
|
|
5825
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5826
|
+
const values = [];
|
|
5827
|
+
this.models.forEach((model) => {
|
|
5828
|
+
const value = resolve(model);
|
|
5829
|
+
if (value == null) return;
|
|
5830
|
+
const lookupKey = this.toLookupKey(value);
|
|
5831
|
+
if (seen.has(lookupKey)) return;
|
|
5832
|
+
seen.add(lookupKey);
|
|
5833
|
+
values.push(value);
|
|
5834
|
+
});
|
|
5835
|
+
return values;
|
|
5836
|
+
}
|
|
5837
|
+
/**
|
|
5838
|
+
* Collects unique values from an array of database rows based on a specified key, which
|
|
5839
|
+
* is used to extract the value from each row.
|
|
5840
|
+
*
|
|
5841
|
+
* @param rows An array of database rows.
|
|
5842
|
+
* @param key The key to extract values from each row.
|
|
5843
|
+
* @returns An array of unique values.
|
|
5844
|
+
*/
|
|
5845
|
+
collectUniqueRowValues(rows, key) {
|
|
5846
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5847
|
+
const values = [];
|
|
5848
|
+
rows.forEach((row) => {
|
|
5849
|
+
const value = row[key];
|
|
5850
|
+
if (value == null) return;
|
|
5851
|
+
const lookupKey = this.toLookupKey(value);
|
|
5852
|
+
if (seen.has(lookupKey)) return;
|
|
5853
|
+
seen.add(lookupKey);
|
|
5854
|
+
values.push(value);
|
|
5855
|
+
});
|
|
5856
|
+
return values;
|
|
5857
|
+
}
|
|
5858
|
+
/**
|
|
5859
|
+
* Loads a "belongs to many" relationship for the set of models.
|
|
5860
|
+
*
|
|
5861
|
+
* @returns
|
|
5862
|
+
*/
|
|
5863
|
+
createRelationTableLoader() {
|
|
5864
|
+
return new RelationTableLoader(this.resolveAdapter());
|
|
5865
|
+
}
|
|
5866
|
+
/**
|
|
5867
|
+
* Loads a "belongs to many" relationship for the set of models.
|
|
5868
|
+
*
|
|
5869
|
+
* @returns
|
|
5870
|
+
*/
|
|
5871
|
+
resolveAdapter() {
|
|
5872
|
+
const adapter = this.models[0].constructor.getAdapter?.();
|
|
5873
|
+
if (!adapter) throw new Error("Set-based eager loading requires a configured adapter.");
|
|
5874
|
+
return adapter;
|
|
5875
|
+
}
|
|
5876
|
+
/**
|
|
5877
|
+
* Reads an attribute value from a model using the getAttribute method, which is used
|
|
5878
|
+
* to access model attributes in a way that is compatible with Arkorm's internal model structure.
|
|
5879
|
+
*
|
|
5880
|
+
* @param model The model to read the attribute from.
|
|
5881
|
+
* @param key The name of the attribute to read.
|
|
5882
|
+
* @returns
|
|
5883
|
+
*/
|
|
5884
|
+
readModelAttribute(model, key) {
|
|
5885
|
+
return model.getAttribute?.(key);
|
|
5886
|
+
}
|
|
5887
|
+
/**
|
|
5888
|
+
* Resolves the default result for a relationship when no related models are found.
|
|
5889
|
+
*
|
|
5890
|
+
* @param resolver
|
|
5891
|
+
* @param model
|
|
5892
|
+
* @returns
|
|
5893
|
+
*/
|
|
5894
|
+
resolveSingleDefault(resolver, model) {
|
|
5895
|
+
return resolver.call(model).resolveDefaultResult?.() ?? null;
|
|
5896
|
+
}
|
|
5897
|
+
/**
|
|
5898
|
+
* Generates a unique lookup key for a given value, which is used to store and retrieve
|
|
5899
|
+
* values in maps during the eager loading process.
|
|
5900
|
+
*
|
|
5901
|
+
* @param value The value to generate a lookup key for.
|
|
5902
|
+
* @returns A unique string representing the value.
|
|
5903
|
+
*/
|
|
5904
|
+
toLookupKey(value) {
|
|
5905
|
+
if (value instanceof Date) return `date:${value.toISOString()}`;
|
|
5906
|
+
return `${typeof value}:${String(value)}`;
|
|
5907
|
+
}
|
|
5908
|
+
};
|
|
5909
|
+
|
|
4803
5910
|
//#endregion
|
|
4804
5911
|
//#region src/URLDriver.ts
|
|
4805
5912
|
/**
|
|
@@ -5698,21 +6805,20 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5698
6805
|
* @returns
|
|
5699
6806
|
*/
|
|
5700
6807
|
async get() {
|
|
6808
|
+
const useAdapterRelationFeatures = this.canExecuteRelationFeaturesInAdapter();
|
|
5701
6809
|
const relationCache = /* @__PURE__ */ new WeakMap();
|
|
5702
6810
|
const rows = await this.executeReadRows();
|
|
5703
6811
|
const normalizedRows = this.randomOrderEnabled ? this.shuffleRows(rows) : rows;
|
|
5704
6812
|
const models = await this.model.hydrateManyRetrieved(normalizedRows);
|
|
5705
6813
|
let filteredModels = models;
|
|
5706
|
-
if (this.hasRelationFilters()) if (this.hasOrRelationFilters() && this.hasBaseWhereConstraints()) {
|
|
6814
|
+
if (this.hasRelationFilters() && !useAdapterRelationFeatures) if (this.hasOrRelationFilters() && this.hasBaseWhereConstraints()) {
|
|
5707
6815
|
const baseIds = new Set(models.map((model) => this.getModelId(model)).filter((id) => id != null));
|
|
5708
6816
|
const allRows = await this.executeReadRows(this.buildSoftDeleteOnlyWhere(), true);
|
|
5709
6817
|
const allModels = this.model.hydrateMany(allRows);
|
|
5710
6818
|
filteredModels = await this.filterModelsByRelationConstraints(allModels, relationCache, baseIds);
|
|
5711
6819
|
} else filteredModels = await this.filterModelsByRelationConstraints(models, relationCache);
|
|
5712
|
-
if (this.hasRelationAggregates()) await this.applyRelationAggregates(filteredModels, relationCache);
|
|
5713
|
-
await
|
|
5714
|
-
await model.load(this.eagerLoads);
|
|
5715
|
-
}));
|
|
6820
|
+
if (this.hasRelationAggregates() && !useAdapterRelationFeatures) await this.applyRelationAggregates(filteredModels, relationCache);
|
|
6821
|
+
await this.eagerLoadModels(filteredModels);
|
|
5716
6822
|
return new ArkormCollection(filteredModels);
|
|
5717
6823
|
}
|
|
5718
6824
|
/**
|
|
@@ -5722,20 +6828,20 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5722
6828
|
* @returns
|
|
5723
6829
|
*/
|
|
5724
6830
|
async first() {
|
|
5725
|
-
if (this.hasRelationFilters() || this.hasRelationAggregates()) return (await this.get()).all()[0] ?? null;
|
|
6831
|
+
if ((this.hasRelationFilters() || this.hasRelationAggregates()) && !this.canExecuteRelationFeaturesInAdapter()) return (await this.get()).all()[0] ?? null;
|
|
5726
6832
|
if (this.randomOrderEnabled) {
|
|
5727
6833
|
const rows = await this.executeReadRows();
|
|
5728
6834
|
if (rows.length === 0) return null;
|
|
5729
6835
|
const row = this.shuffleRows(rows)[0];
|
|
5730
6836
|
if (!row) return null;
|
|
5731
6837
|
const model = await this.model.hydrateRetrieved(row);
|
|
5732
|
-
await
|
|
6838
|
+
await this.eagerLoadModels([model]);
|
|
5733
6839
|
return model;
|
|
5734
6840
|
}
|
|
5735
6841
|
const row = await this.executeReadRow();
|
|
5736
6842
|
if (!row) return null;
|
|
5737
6843
|
const model = await this.model.hydrateRetrieved(row);
|
|
5738
|
-
await
|
|
6844
|
+
await this.eagerLoadModels([model]);
|
|
5739
6845
|
return model;
|
|
5740
6846
|
}
|
|
5741
6847
|
/**
|
|
@@ -5899,6 +7005,13 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5899
7005
|
operation: "update",
|
|
5900
7006
|
model: this.model.name
|
|
5901
7007
|
});
|
|
7008
|
+
const directSpec = this.tryBuildUpdateSpec(where, data);
|
|
7009
|
+
const adapter = this.requireAdapter();
|
|
7010
|
+
if (!this.isUniqueWhere(where) && directSpec && typeof adapter.updateFirst === "function") {
|
|
7011
|
+
const updated = await adapter.updateFirst(directSpec);
|
|
7012
|
+
if (!updated) throw new ModelNotFoundException(this.model.name, "Record not found for update operation.", { operation: "update" });
|
|
7013
|
+
return this.model.hydrate(updated);
|
|
7014
|
+
}
|
|
5902
7015
|
const uniqueWhere = await this.resolveUniqueWhere(where);
|
|
5903
7016
|
const updated = await this.executeUpdateRow(uniqueWhere, data);
|
|
5904
7017
|
return this.model.hydrate(updated);
|
|
@@ -5925,6 +7038,13 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5925
7038
|
* @returns
|
|
5926
7039
|
*/
|
|
5927
7040
|
async updateOrInsert(attributes, values = {}) {
|
|
7041
|
+
if (typeof values !== "function" && this.adapter?.capabilities?.upsert && typeof this.requireAdapter().upsert === "function") {
|
|
7042
|
+
await this.executeUpsertRows([{
|
|
7043
|
+
...attributes,
|
|
7044
|
+
...values
|
|
7045
|
+
}], Object.keys(attributes), Object.keys(values));
|
|
7046
|
+
return true;
|
|
7047
|
+
}
|
|
5928
7048
|
const exists = await this.clone().where(attributes).first() != null;
|
|
5929
7049
|
const resolvedValues = typeof values === "function" ? await values(exists) : values;
|
|
5930
7050
|
if (!exists) {
|
|
@@ -5947,6 +7067,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5947
7067
|
async upsert(values, uniqueBy, update = null) {
|
|
5948
7068
|
if (values.length === 0) return 0;
|
|
5949
7069
|
const uniqueKeys = Array.isArray(uniqueBy) ? uniqueBy : [uniqueBy];
|
|
7070
|
+
if (this.adapter?.capabilities?.upsert && typeof this.requireAdapter().upsert === "function") return await this.executeUpsertRows(values, uniqueKeys, update ?? void 0);
|
|
5950
7071
|
let affected = 0;
|
|
5951
7072
|
for (const row of values) {
|
|
5952
7073
|
const attributes = uniqueKeys.reduce((all, key) => {
|
|
@@ -5974,6 +7095,13 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5974
7095
|
operation: "delete",
|
|
5975
7096
|
model: this.model.name
|
|
5976
7097
|
});
|
|
7098
|
+
const directSpec = this.tryBuildDeleteSpec(where);
|
|
7099
|
+
const adapter = this.requireAdapter();
|
|
7100
|
+
if (!this.isUniqueWhere(where) && directSpec && typeof adapter.deleteFirst === "function") {
|
|
7101
|
+
const deleted = await adapter.deleteFirst(directSpec);
|
|
7102
|
+
if (!deleted) throw new ModelNotFoundException(this.model.name, "Record not found for delete operation.", { operation: "delete" });
|
|
7103
|
+
return this.model.hydrate(deleted);
|
|
7104
|
+
}
|
|
5977
7105
|
const uniqueWhere = await this.resolveUniqueWhere(where);
|
|
5978
7106
|
const deleted = await this.executeDeleteRow(uniqueWhere);
|
|
5979
7107
|
return this.model.hydrate(deleted);
|
|
@@ -5990,6 +7118,14 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5990
7118
|
values
|
|
5991
7119
|
};
|
|
5992
7120
|
}
|
|
7121
|
+
tryBuildUpsertSpec(values, uniqueBy, updateColumns) {
|
|
7122
|
+
return {
|
|
7123
|
+
target: this.buildQueryTarget(),
|
|
7124
|
+
values,
|
|
7125
|
+
uniqueBy,
|
|
7126
|
+
updateColumns
|
|
7127
|
+
};
|
|
7128
|
+
}
|
|
5993
7129
|
tryBuildInsertOrIgnoreManySpec(values) {
|
|
5994
7130
|
return {
|
|
5995
7131
|
...this.tryBuildInsertManySpec(values),
|
|
@@ -6028,7 +7164,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
6028
7164
|
* @returns
|
|
6029
7165
|
*/
|
|
6030
7166
|
async count() {
|
|
6031
|
-
if (this.hasRelationFilters()) return (await this.get()).all().length;
|
|
7167
|
+
if (this.hasRelationFilters() && !this.canExecuteRelationFeaturesInAdapter()) return (await this.get()).all().length;
|
|
6032
7168
|
return this.executeReadCount();
|
|
6033
7169
|
}
|
|
6034
7170
|
/**
|
|
@@ -6037,7 +7173,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
6037
7173
|
* @returns
|
|
6038
7174
|
*/
|
|
6039
7175
|
async exists() {
|
|
6040
|
-
if (this.hasRelationFilters()) return await this.count() > 0;
|
|
7176
|
+
if (this.hasRelationFilters() && !this.canExecuteRelationFeaturesInAdapter()) return await this.count() > 0;
|
|
6041
7177
|
return await this.executeReadExists();
|
|
6042
7178
|
}
|
|
6043
7179
|
/**
|
|
@@ -6212,7 +7348,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
6212
7348
|
*/
|
|
6213
7349
|
async paginate(perPage = 15, page = void 0, options = {}) {
|
|
6214
7350
|
const currentPage = this.resolvePaginationPage(page, options);
|
|
6215
|
-
if (this.hasRelationFilters() || this.hasRelationAggregates()) {
|
|
7351
|
+
if ((this.hasRelationFilters() || this.hasRelationAggregates()) && !this.canExecuteRelationFeaturesInAdapter()) {
|
|
6216
7352
|
const pageSize = Math.max(1, perPage);
|
|
6217
7353
|
const rows = (await this.get()).all();
|
|
6218
7354
|
const start = (currentPage - 1) * pageSize;
|
|
@@ -6231,7 +7367,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
6231
7367
|
*/
|
|
6232
7368
|
async simplePaginate(perPage = 15, page = void 0, options = {}) {
|
|
6233
7369
|
const currentPage = this.resolvePaginationPage(page, options);
|
|
6234
|
-
if (this.hasRelationFilters() || this.hasRelationAggregates()) {
|
|
7370
|
+
if ((this.hasRelationFilters() || this.hasRelationAggregates()) && !this.canExecuteRelationFeaturesInAdapter()) {
|
|
6235
7371
|
const pageSize = Math.max(1, perPage);
|
|
6236
7372
|
const rows = (await this.get()).all();
|
|
6237
7373
|
const start = (currentPage - 1) * pageSize;
|
|
@@ -6335,6 +7471,10 @@ var QueryBuilder = class QueryBuilder {
|
|
|
6335
7471
|
};
|
|
6336
7472
|
});
|
|
6337
7473
|
}
|
|
7474
|
+
async eagerLoadModels(models) {
|
|
7475
|
+
if (models.length === 0 || Object.keys(this.eagerLoads).length === 0) return;
|
|
7476
|
+
await new SetBasedEagerLoader(models, this.eagerLoads).load();
|
|
7477
|
+
}
|
|
6338
7478
|
normalizeRelationLoadSelect(select) {
|
|
6339
7479
|
if (Array.isArray(select) || typeof select !== "object" || !select) return null;
|
|
6340
7480
|
const entries = Object.entries(select);
|
|
@@ -6572,7 +7712,11 @@ var QueryBuilder = class QueryBuilder {
|
|
|
6572
7712
|
const columns = this.tryBuildQuerySelectColumns();
|
|
6573
7713
|
const orderBy = this.tryBuildQueryOrderBy();
|
|
6574
7714
|
const condition = this.buildQueryWhereCondition(softDeleteOnly);
|
|
7715
|
+
const relationFilters = this.tryBuildRelationFilterSpecs();
|
|
7716
|
+
const relationAggregates = this.tryBuildRelationAggregateSpecs();
|
|
6575
7717
|
if (columns === null || orderBy === null || condition === null) return null;
|
|
7718
|
+
if (this.hasRelationFilters() && this.canExecuteRelationFiltersInAdapter() && relationFilters === null) return null;
|
|
7719
|
+
if (this.hasRelationAggregates() && this.canExecuteRelationAggregatesInAdapter() && relationAggregates === null) return null;
|
|
6576
7720
|
return {
|
|
6577
7721
|
target: this.buildQueryTarget(),
|
|
6578
7722
|
columns,
|
|
@@ -6580,15 +7724,20 @@ var QueryBuilder = class QueryBuilder {
|
|
|
6580
7724
|
orderBy,
|
|
6581
7725
|
limit: this.limitValue,
|
|
6582
7726
|
offset: this.offsetValue,
|
|
6583
|
-
relationLoads: this.queryRelationLoads
|
|
7727
|
+
relationLoads: this.queryRelationLoads,
|
|
7728
|
+
relationFilters: this.canExecuteRelationFiltersInAdapter() ? relationFilters ?? void 0 : void 0,
|
|
7729
|
+
relationAggregates: this.canExecuteRelationAggregatesInAdapter() ? relationAggregates ?? void 0 : void 0
|
|
6584
7730
|
};
|
|
6585
7731
|
}
|
|
6586
7732
|
tryBuildAggregateSpec() {
|
|
6587
7733
|
const condition = this.buildQueryWhereCondition(false);
|
|
7734
|
+
const relationFilters = this.tryBuildRelationFilterSpecs();
|
|
6588
7735
|
if (condition === null) return null;
|
|
7736
|
+
if (this.hasRelationFilters() && this.canExecuteRelationFiltersInAdapter() && relationFilters === null) return null;
|
|
6589
7737
|
return {
|
|
6590
7738
|
target: this.buildQueryTarget(),
|
|
6591
7739
|
where: condition,
|
|
7740
|
+
relationFilters: this.canExecuteRelationFiltersInAdapter() ? relationFilters ?? void 0 : void 0,
|
|
6592
7741
|
aggregate: { type: "count" }
|
|
6593
7742
|
};
|
|
6594
7743
|
}
|
|
@@ -6658,6 +7807,14 @@ var QueryBuilder = class QueryBuilder {
|
|
|
6658
7807
|
}
|
|
6659
7808
|
return inserted;
|
|
6660
7809
|
}
|
|
7810
|
+
async executeUpsertRows(values, uniqueBy, updateColumns) {
|
|
7811
|
+
const adapter = this.requireAdapter();
|
|
7812
|
+
if (typeof adapter.upsert !== "function") throw new UnsupportedAdapterFeatureException("Upsert is not supported by the current adapter.", {
|
|
7813
|
+
operation: "query.upsert",
|
|
7814
|
+
model: this.model.name
|
|
7815
|
+
});
|
|
7816
|
+
return await adapter.upsert(this.tryBuildUpsertSpec(values, uniqueBy, updateColumns));
|
|
7817
|
+
}
|
|
6661
7818
|
async executeUpdateRow(where, values) {
|
|
6662
7819
|
const adapter = this.requireAdapter();
|
|
6663
7820
|
const spec = this.tryBuildUpdateSpec(where, values);
|
|
@@ -6786,6 +7943,71 @@ var QueryBuilder = class QueryBuilder {
|
|
|
6786
7943
|
hasRelationAggregates() {
|
|
6787
7944
|
return this.relationAggregates.length > 0;
|
|
6788
7945
|
}
|
|
7946
|
+
canExecuteRelationFiltersInAdapter() {
|
|
7947
|
+
const adapter = this.adapter;
|
|
7948
|
+
if (!this.hasRelationFilters()) return false;
|
|
7949
|
+
return adapter?.capabilities?.relationFilters === true && this.tryBuildRelationFilterSpecs() !== null;
|
|
7950
|
+
}
|
|
7951
|
+
canExecuteRelationAggregatesInAdapter() {
|
|
7952
|
+
const adapter = this.adapter;
|
|
7953
|
+
if (!this.hasRelationAggregates()) return false;
|
|
7954
|
+
return adapter?.capabilities?.relationAggregates === true && this.tryBuildRelationAggregateSpecs() !== null;
|
|
7955
|
+
}
|
|
7956
|
+
canExecuteRelationFeaturesInAdapter() {
|
|
7957
|
+
const filtersSupported = !this.hasRelationFilters() || this.canExecuteRelationFiltersInAdapter();
|
|
7958
|
+
const aggregatesSupported = !this.hasRelationAggregates() || this.canExecuteRelationAggregatesInAdapter();
|
|
7959
|
+
return filtersSupported && aggregatesSupported;
|
|
7960
|
+
}
|
|
7961
|
+
tryBuildRelationFilterSpecs() {
|
|
7962
|
+
return this.relationFilters.reduce((specs, filter) => {
|
|
7963
|
+
if (!specs) return null;
|
|
7964
|
+
const metadata = this.model.getRelationMetadata(filter.relation);
|
|
7965
|
+
if (!this.isSqlRelationFeatureMetadata(metadata)) return null;
|
|
7966
|
+
const where = this.tryBuildRelationConstraintWhere(filter.relation, filter.callback);
|
|
7967
|
+
if (where === null) return null;
|
|
7968
|
+
specs.push({
|
|
7969
|
+
relation: filter.relation,
|
|
7970
|
+
operator: filter.operator,
|
|
7971
|
+
count: filter.count,
|
|
7972
|
+
boolean: filter.boolean,
|
|
7973
|
+
where
|
|
7974
|
+
});
|
|
7975
|
+
return specs;
|
|
7976
|
+
}, []);
|
|
7977
|
+
}
|
|
7978
|
+
tryBuildRelationAggregateSpecs() {
|
|
7979
|
+
return this.relationAggregates.reduce((specs, aggregate) => {
|
|
7980
|
+
if (!specs) return null;
|
|
7981
|
+
const metadata = this.model.getRelationMetadata(aggregate.relation);
|
|
7982
|
+
if (!this.isSqlRelationFeatureMetadata(metadata)) return null;
|
|
7983
|
+
const where = this.tryBuildRelationConstraintWhere(aggregate.relation);
|
|
7984
|
+
if (where === null) return null;
|
|
7985
|
+
specs.push({
|
|
7986
|
+
relation: aggregate.relation,
|
|
7987
|
+
type: aggregate.type,
|
|
7988
|
+
column: aggregate.column,
|
|
7989
|
+
alias: this.buildAggregateAttributeKey(aggregate),
|
|
7990
|
+
where
|
|
7991
|
+
});
|
|
7992
|
+
return specs;
|
|
7993
|
+
}, []);
|
|
7994
|
+
}
|
|
7995
|
+
tryBuildRelationConstraintWhere(relation, callback) {
|
|
7996
|
+
const metadata = this.model.getRelationMetadata(relation);
|
|
7997
|
+
if (!this.isSqlRelationFeatureMetadata(metadata)) return null;
|
|
7998
|
+
const relatedQuery = metadata?.relatedModel.query();
|
|
7999
|
+
if (!relatedQuery) return null;
|
|
8000
|
+
if (callback) {
|
|
8001
|
+
const constrained = callback(relatedQuery);
|
|
8002
|
+
if (constrained && constrained !== relatedQuery) return null;
|
|
8003
|
+
}
|
|
8004
|
+
if (relatedQuery.hasRelationFilters() || relatedQuery.hasRelationAggregates() || relatedQuery.queryRelationLoads || relatedQuery.querySelect || relatedQuery.queryOrderBy || relatedQuery.offsetValue !== void 0 || relatedQuery.limitValue !== void 0 || relatedQuery.randomOrderEnabled) return null;
|
|
8005
|
+
return relatedQuery.buildQueryWhereCondition(false);
|
|
8006
|
+
}
|
|
8007
|
+
isSqlRelationFeatureMetadata(metadata) {
|
|
8008
|
+
if (!metadata) return false;
|
|
8009
|
+
return metadata.type === "hasMany" || metadata.type === "hasOne" || metadata.type === "belongsTo" || metadata.type === "belongsToMany" || metadata.type === "hasOneThrough" || metadata.type === "hasManyThrough";
|
|
8010
|
+
}
|
|
6789
8011
|
async filterModelsByRelationConstraints(models, relationCache, baseIds) {
|
|
6790
8012
|
return (await Promise.all(models.map(async (model) => {
|
|
6791
8013
|
let result = null;
|
|
@@ -6942,6 +8164,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
6942
8164
|
*/
|
|
6943
8165
|
var Model = class Model {
|
|
6944
8166
|
static lifecycleStates = /* @__PURE__ */ new WeakMap();
|
|
8167
|
+
static emittedDeprecationWarnings = /* @__PURE__ */ new Set();
|
|
6945
8168
|
static eventsSuppressed = 0;
|
|
6946
8169
|
static factoryClass;
|
|
6947
8170
|
static adapter;
|
|
@@ -6985,12 +8208,24 @@ var Model = class Model {
|
|
|
6985
8208
|
}
|
|
6986
8209
|
});
|
|
6987
8210
|
}
|
|
8211
|
+
static emitDeprecationWarning(code, message) {
|
|
8212
|
+
if (Model.emittedDeprecationWarnings.has(code)) return;
|
|
8213
|
+
Model.emittedDeprecationWarnings.add(code);
|
|
8214
|
+
process.emitWarning(message, {
|
|
8215
|
+
type: "DeprecationWarning",
|
|
8216
|
+
code
|
|
8217
|
+
});
|
|
8218
|
+
}
|
|
6988
8219
|
/**
|
|
6989
|
-
* Set the Prisma client delegates for all models.
|
|
6990
|
-
*
|
|
6991
|
-
* @
|
|
8220
|
+
* Set the Prisma client delegates for all models.
|
|
8221
|
+
*
|
|
8222
|
+
* @deprecated Use Model.setAdapter(createPrismaDatabaseAdapter(...)) or another
|
|
8223
|
+
* adapter-first bootstrap path instead.
|
|
8224
|
+
*
|
|
8225
|
+
* @param client
|
|
6992
8226
|
*/
|
|
6993
8227
|
static setClient(client) {
|
|
8228
|
+
Model.emitDeprecationWarning("ARKORM_SET_CLIENT_DEPRECATED", "Model.setClient() is deprecated and will be removed in Arkorm 3.0. Use Model.setAdapter(createPrismaDatabaseAdapter(...)) or another adapter-first setup path instead.");
|
|
6994
8229
|
this.client = client;
|
|
6995
8230
|
}
|
|
6996
8231
|
static setAdapter(adapter) {
|
|
@@ -7223,6 +8458,8 @@ var Model = class Model {
|
|
|
7223
8458
|
static getAdapter() {
|
|
7224
8459
|
ensureArkormConfigLoading();
|
|
7225
8460
|
if (this.adapter) return this.adapter;
|
|
8461
|
+
const runtimeAdapter = getRuntimeAdapter();
|
|
8462
|
+
if (runtimeAdapter) return runtimeAdapter;
|
|
7226
8463
|
const client = getActiveTransactionClient() ?? this.client ?? getRuntimePrismaClient();
|
|
7227
8464
|
if (!client || typeof client !== "object") return void 0;
|
|
7228
8465
|
return createPrismaCompatibilityAdapter(client);
|
|
@@ -7527,14 +8764,11 @@ var Model = class Model {
|
|
|
7527
8764
|
*/
|
|
7528
8765
|
async load(relations) {
|
|
7529
8766
|
const relationMap = this.normalizeRelationMap(relations);
|
|
7530
|
-
await
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
const results = await relation.getResults();
|
|
7536
|
-
this.attributes[name] = results;
|
|
7537
|
-
}));
|
|
8767
|
+
await new SetBasedEagerLoader([this], relationMap).load();
|
|
8768
|
+
return this;
|
|
8769
|
+
}
|
|
8770
|
+
setLoadedRelation(name, value) {
|
|
8771
|
+
this.attributes[name] = value;
|
|
7538
8772
|
return this;
|
|
7539
8773
|
}
|
|
7540
8774
|
/**
|
|
@@ -8016,4 +9250,4 @@ var Model = class Model {
|
|
|
8016
9250
|
};
|
|
8017
9251
|
|
|
8018
9252
|
//#endregion
|
|
8019
|
-
export { ArkormCollection, ArkormException, Attribute, CliApp, EnumBuilder, ForeignKeyBuilder, InitCommand, InlineFactory, KyselyDatabaseAdapter, LengthAwarePaginator, MIGRATION_BRAND, MakeFactoryCommand, MakeMigrationCommand, MakeModelCommand, MakeSeederCommand, MigrateCommand, MigrateRollbackCommand, Migration, MigrationHistoryCommand, MissingDelegateException, Model, ModelFactory, ModelNotFoundException, ModelsSyncCommand, PRISMA_ENUM_MEMBER_REGEX, PRISMA_ENUM_REGEX, PRISMA_MODEL_REGEX, Paginator, PrismaDatabaseAdapter, QueryBuilder, QueryConstraintException, RelationResolutionException, RuntimeModuleLoader, SEEDER_BRAND, SchemaBuilder, ScopeNotDefinedException, SeedCommand, Seeder, TableBuilder, URLDriver, UniqueConstraintResolutionException, UnsupportedAdapterFeatureException, applyAlterTableOperation, applyCreateTableOperation, applyDropTableOperation, applyMigrationRollbackToPrismaSchema, applyMigrationToPrismaSchema, applyOperationsToPrismaSchema, buildEnumBlock, buildFieldLine, buildIndexLine, buildInverseRelationLine, buildMigrationIdentity, buildMigrationRunId, buildMigrationSource, buildModelBlock, buildRelationLine, computeMigrationChecksum, configureArkormRuntime, createKyselyAdapter, createMigrationTimestamp, createPrismaAdapter, createPrismaCompatibilityAdapter, createPrismaDatabaseAdapter, createPrismaDelegateMap, defineConfig, defineFactory, deriveCollectionFieldName, deriveInverseRelationAlias, deriveRelationAlias, deriveRelationFieldName, deriveSingularFieldName, ensureArkormConfigLoading, escapeRegex, findAppliedMigration, findEnumBlock, findModelBlock, formatDefaultValue, formatEnumDefaultValue, formatRelationAction, generateMigrationFile, getActiveTransactionClient, getDefaultStubsPath, getLastMigrationRun, getLatestAppliedMigrations, getMigrationPlan, getRuntimePaginationCurrentPageResolver, getRuntimePaginationURLDriverFactory, getRuntimePrismaClient, getUserConfig, inferDelegateName, isDelegateLike, isMigrationApplied, isTransactionCapableClient, loadArkormConfig, markMigrationApplied, markMigrationRun, pad, readAppliedMigrationsState, removeAppliedMigration, resetArkormRuntimeForTests, resolveCast, resolveEnumName, resolveMigrationClassName, resolveMigrationStateFilePath, resolvePrismaType, runArkormTransaction, runMigrationWithPrisma, runPrismaCommand, toMigrationFileSlug, toModelName, writeAppliedMigrationsState };
|
|
9253
|
+
export { ArkormCollection, ArkormException, Attribute, CliApp, EnumBuilder, ForeignKeyBuilder, InitCommand, InlineFactory, KyselyDatabaseAdapter, LengthAwarePaginator, MIGRATION_BRAND, MakeFactoryCommand, MakeMigrationCommand, MakeModelCommand, MakeSeederCommand, MigrateCommand, MigrateRollbackCommand, Migration, MigrationHistoryCommand, MissingDelegateException, Model, ModelFactory, ModelNotFoundException, ModelsSyncCommand, PRISMA_ENUM_MEMBER_REGEX, PRISMA_ENUM_REGEX, PRISMA_MODEL_REGEX, Paginator, PrismaDatabaseAdapter, QueryBuilder, QueryConstraintException, RelationResolutionException, RuntimeModuleLoader, SEEDER_BRAND, SchemaBuilder, ScopeNotDefinedException, SeedCommand, Seeder, TableBuilder, URLDriver, UniqueConstraintResolutionException, UnsupportedAdapterFeatureException, applyAlterTableOperation, applyCreateTableOperation, applyDropTableOperation, applyMigrationRollbackToPrismaSchema, applyMigrationToPrismaSchema, applyOperationsToPrismaSchema, bindAdapterToModels, buildEnumBlock, buildFieldLine, buildIndexLine, buildInverseRelationLine, buildMigrationIdentity, buildMigrationRunId, buildMigrationSource, buildModelBlock, buildRelationLine, computeMigrationChecksum, configureArkormRuntime, createKyselyAdapter, createMigrationTimestamp, createPrismaAdapter, createPrismaCompatibilityAdapter, createPrismaDatabaseAdapter, createPrismaDelegateMap, defineConfig, defineFactory, deriveCollectionFieldName, deriveInverseRelationAlias, deriveRelationAlias, deriveRelationFieldName, deriveSingularFieldName, ensureArkormConfigLoading, escapeRegex, findAppliedMigration, findEnumBlock, findModelBlock, formatDefaultValue, formatEnumDefaultValue, formatRelationAction, generateMigrationFile, getActiveTransactionClient, getDefaultStubsPath, getLastMigrationRun, getLatestAppliedMigrations, getMigrationPlan, getRuntimeAdapter, getRuntimePaginationCurrentPageResolver, getRuntimePaginationURLDriverFactory, getRuntimePrismaClient, getUserConfig, inferDelegateName, isDelegateLike, isMigrationApplied, isTransactionCapableClient, loadArkormConfig, markMigrationApplied, markMigrationRun, pad, readAppliedMigrationsState, removeAppliedMigration, resetArkormRuntimeForTests, resolveCast, resolveEnumName, resolveMigrationClassName, resolveMigrationStateFilePath, resolvePrismaType, runArkormTransaction, runMigrationWithPrisma, runPrismaCommand, toMigrationFileSlug, toModelName, writeAppliedMigrationsState };
|