arkormx 2.0.0-next.1 → 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 +392 -20
- package/dist/index.d.mts +392 -20
- package/dist/index.mjs +1307 -73
- package/package.json +3 -1
package/dist/index.cjs
CHANGED
|
@@ -26,6 +26,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
26
26
|
}) : target, mod));
|
|
27
27
|
|
|
28
28
|
//#endregion
|
|
29
|
+
let _h3ravel_support = require("@h3ravel/support");
|
|
29
30
|
let kysely = require("kysely");
|
|
30
31
|
let async_hooks = require("async_hooks");
|
|
31
32
|
let _rexxars_jiti = require("@rexxars/jiti");
|
|
@@ -36,7 +37,6 @@ let fs = require("fs");
|
|
|
36
37
|
let url = require("url");
|
|
37
38
|
let path = require("path");
|
|
38
39
|
path = __toESM(path);
|
|
39
|
-
let _h3ravel_support = require("@h3ravel/support");
|
|
40
40
|
let node_fs = require("node:fs");
|
|
41
41
|
let node_child_process = require("node:child_process");
|
|
42
42
|
let _h3ravel_shared = require("@h3ravel/shared");
|
|
@@ -99,24 +99,66 @@ var UnsupportedAdapterFeatureException = class extends ArkormException {
|
|
|
99
99
|
|
|
100
100
|
//#endregion
|
|
101
101
|
//#region src/adapters/KyselyDatabaseAdapter.ts
|
|
102
|
+
/**
|
|
103
|
+
* Database adapter implementation for Kysely, allowing Arkorm to execute queries using Kysely
|
|
104
|
+
* as the underlying query builder and executor.
|
|
105
|
+
*
|
|
106
|
+
* @author Legacy (3m1n3nc3)
|
|
107
|
+
* @since 2.0.0-next.0
|
|
108
|
+
*/
|
|
102
109
|
var KyselyDatabaseAdapter = class KyselyDatabaseAdapter {
|
|
103
110
|
capabilities = {
|
|
104
111
|
transactions: true,
|
|
105
112
|
returning: true,
|
|
106
113
|
insertMany: true,
|
|
114
|
+
upsert: true,
|
|
107
115
|
updateMany: true,
|
|
108
116
|
deleteMany: true,
|
|
109
117
|
exists: true,
|
|
110
118
|
relationLoads: false,
|
|
111
|
-
relationAggregates:
|
|
112
|
-
relationFilters:
|
|
119
|
+
relationAggregates: true,
|
|
120
|
+
relationFilters: true,
|
|
113
121
|
rawWhere: false
|
|
114
122
|
};
|
|
115
|
-
constructor(db) {
|
|
123
|
+
constructor(db, mapping = {}) {
|
|
116
124
|
this.db = db;
|
|
125
|
+
this.mapping = mapping;
|
|
126
|
+
}
|
|
127
|
+
introspectionTypeToTs(typeName, enumValues) {
|
|
128
|
+
if (enumValues && enumValues.length > 0) return enumValues.map((value) => `'${value.replace(/'/g, "\\'")}'`).join(" | ");
|
|
129
|
+
switch (typeName) {
|
|
130
|
+
case "bool": return "boolean";
|
|
131
|
+
case "int2":
|
|
132
|
+
case "int4":
|
|
133
|
+
case "int8":
|
|
134
|
+
case "float4":
|
|
135
|
+
case "float8":
|
|
136
|
+
case "numeric":
|
|
137
|
+
case "money": return "number";
|
|
138
|
+
case "json":
|
|
139
|
+
case "jsonb": return "Record<string, unknown> | unknown[]";
|
|
140
|
+
case "date":
|
|
141
|
+
case "timestamp":
|
|
142
|
+
case "timestamptz": return "Date";
|
|
143
|
+
case "bytea": return "Uint8Array";
|
|
144
|
+
case "uuid":
|
|
145
|
+
case "varchar":
|
|
146
|
+
case "bpchar":
|
|
147
|
+
case "char":
|
|
148
|
+
case "text":
|
|
149
|
+
case "citext":
|
|
150
|
+
case "time":
|
|
151
|
+
case "timetz":
|
|
152
|
+
case "interval":
|
|
153
|
+
case "inet":
|
|
154
|
+
case "cidr":
|
|
155
|
+
case "macaddr":
|
|
156
|
+
case "macaddr8": return "string";
|
|
157
|
+
default: return "unknown";
|
|
158
|
+
}
|
|
117
159
|
}
|
|
118
160
|
resolveTable(target) {
|
|
119
|
-
if (target.table && target.table.trim().length > 0) return target.table;
|
|
161
|
+
if (target.table && target.table.trim().length > 0) return this.mapping[target.table] ?? target.table;
|
|
120
162
|
throw new ArkormException("Kysely adapter requires a concrete target table.", {
|
|
121
163
|
operation: "adapter.table",
|
|
122
164
|
model: target.modelName,
|
|
@@ -225,29 +267,223 @@ var KyselyDatabaseAdapter = class KyselyDatabaseAdapter {
|
|
|
225
267
|
if (clauses.length === 0) return kysely.sql``;
|
|
226
268
|
return kysely.sql.join(clauses, kysely.sql``);
|
|
227
269
|
}
|
|
270
|
+
buildColumnReference(table, column) {
|
|
271
|
+
return kysely.sql`${kysely.sql.table(table)}.${kysely.sql.id(column)}`;
|
|
272
|
+
}
|
|
273
|
+
buildRelatedTargetFromRelation(target, relation) {
|
|
274
|
+
const metadata = target.model?.getRelationMetadata(relation);
|
|
275
|
+
if (!metadata) throw new UnsupportedAdapterFeatureException(`Relation [${relation}] could not be resolved for SQL-backed relation execution.`, {
|
|
276
|
+
operation: "adapter.relation.metadata",
|
|
277
|
+
model: target.modelName,
|
|
278
|
+
relation
|
|
279
|
+
});
|
|
280
|
+
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.`, {
|
|
281
|
+
operation: "adapter.relation.metadata",
|
|
282
|
+
model: target.modelName,
|
|
283
|
+
relation,
|
|
284
|
+
meta: {
|
|
285
|
+
feature: "relationFilters",
|
|
286
|
+
relationType: metadata.type
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
const relatedMetadata = metadata.relatedModel.getModelMetadata();
|
|
290
|
+
return {
|
|
291
|
+
metadata,
|
|
292
|
+
relatedTarget: {
|
|
293
|
+
model: metadata.relatedModel,
|
|
294
|
+
modelName: metadata.relatedModel.name,
|
|
295
|
+
table: relatedMetadata.table,
|
|
296
|
+
primaryKey: relatedMetadata.primaryKey,
|
|
297
|
+
columns: relatedMetadata.columns,
|
|
298
|
+
softDelete: relatedMetadata.softDelete
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
resolveMappedTable(table) {
|
|
303
|
+
return this.mapping[table] ?? table;
|
|
304
|
+
}
|
|
305
|
+
buildBelongsToManyJoinSource(outerTarget, relatedTarget, metadata) {
|
|
306
|
+
const outerTable = this.resolveTable(outerTarget);
|
|
307
|
+
const relatedTable = this.resolveTable(relatedTarget);
|
|
308
|
+
const pivotTable = this.resolveMappedTable(metadata.throughTable);
|
|
309
|
+
return {
|
|
310
|
+
from: kysely.sql`${kysely.sql.table(relatedTable)} inner join ${kysely.sql.table(pivotTable)} on ${this.buildColumnReference(relatedTable, this.mapColumn(relatedTarget, metadata.relatedKey))} = ${this.buildColumnReference(pivotTable, metadata.relatedPivotKey)}`,
|
|
311
|
+
condition: kysely.sql`
|
|
312
|
+
${this.buildColumnReference(pivotTable, metadata.foreignPivotKey)}
|
|
313
|
+
=
|
|
314
|
+
${this.buildColumnReference(outerTable, this.mapColumn(outerTarget, metadata.parentKey))}
|
|
315
|
+
`
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
buildThroughJoinSource(outerTarget, relatedTarget, metadata) {
|
|
319
|
+
const outerTable = this.resolveTable(outerTarget);
|
|
320
|
+
const relatedTable = this.resolveTable(relatedTarget);
|
|
321
|
+
const throughTable = this.resolveMappedTable(metadata.throughTable);
|
|
322
|
+
return {
|
|
323
|
+
from: kysely.sql`${kysely.sql.table(relatedTable)} inner join ${kysely.sql.table(throughTable)} on ${this.buildColumnReference(relatedTable, this.mapColumn(relatedTarget, metadata.secondKey))} = ${this.buildColumnReference(throughTable, metadata.secondLocalKey)}`,
|
|
324
|
+
condition: kysely.sql`
|
|
325
|
+
${this.buildColumnReference(throughTable, metadata.firstKey)}
|
|
326
|
+
=
|
|
327
|
+
${this.buildColumnReference(outerTable, this.mapColumn(outerTarget, metadata.localKey))}
|
|
328
|
+
`
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
buildRelatedJoinCondition(outerTarget, relation) {
|
|
332
|
+
const { metadata, relatedTarget } = this.buildRelatedTargetFromRelation(outerTarget, relation);
|
|
333
|
+
const outerTable = this.resolveTable(outerTarget);
|
|
334
|
+
const relatedTable = this.resolveTable(relatedTarget);
|
|
335
|
+
if (metadata.type === "belongsToMany") {
|
|
336
|
+
const joinSource = this.buildBelongsToManyJoinSource(outerTarget, relatedTarget, metadata);
|
|
337
|
+
return {
|
|
338
|
+
relatedTarget,
|
|
339
|
+
from: joinSource.from,
|
|
340
|
+
condition: joinSource.condition
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
if (metadata.type === "hasOneThrough" || metadata.type === "hasManyThrough") {
|
|
344
|
+
const joinSource = this.buildThroughJoinSource(outerTarget, relatedTarget, metadata);
|
|
345
|
+
return {
|
|
346
|
+
relatedTarget,
|
|
347
|
+
from: joinSource.from,
|
|
348
|
+
condition: joinSource.condition
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
if (metadata.type === "hasMany" || metadata.type === "hasOne") return {
|
|
352
|
+
relatedTarget,
|
|
353
|
+
from: kysely.sql`${kysely.sql.table(relatedTable)}`,
|
|
354
|
+
condition: kysely.sql`
|
|
355
|
+
${this.buildColumnReference(relatedTable, this.mapColumn(relatedTarget, metadata.foreignKey))}
|
|
356
|
+
=
|
|
357
|
+
${this.buildColumnReference(outerTable, this.mapColumn(outerTarget, metadata.localKey))}
|
|
358
|
+
`
|
|
359
|
+
};
|
|
360
|
+
return {
|
|
361
|
+
relatedTarget,
|
|
362
|
+
from: kysely.sql`${kysely.sql.table(relatedTable)}`,
|
|
363
|
+
condition: kysely.sql`
|
|
364
|
+
${this.buildColumnReference(relatedTable, this.mapColumn(relatedTarget, metadata.ownerKey))}
|
|
365
|
+
=
|
|
366
|
+
${this.buildColumnReference(outerTable, this.mapColumn(outerTarget, metadata.foreignKey))}
|
|
367
|
+
`
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
combineConditions(conditions) {
|
|
371
|
+
const filtered = conditions.filter((condition) => Boolean(condition));
|
|
372
|
+
if (filtered.length === 0) return kysely.sql`1 = 1`;
|
|
373
|
+
if (filtered.length === 1) return filtered[0];
|
|
374
|
+
return kysely.sql`(${kysely.sql.join(filtered, kysely.sql` and `)})`;
|
|
375
|
+
}
|
|
376
|
+
buildRelationFilterExpression(target, filter) {
|
|
377
|
+
const { relatedTarget, from, condition } = this.buildRelatedJoinCondition(target, filter.relation);
|
|
378
|
+
return kysely.sql`(
|
|
379
|
+
select count(*)::int
|
|
380
|
+
from ${from}
|
|
381
|
+
where ${this.combineConditions([condition, filter.where ? this.buildWhereCondition(relatedTarget, filter.where) : void 0])}
|
|
382
|
+
) ${filter.operator === "!=" ? kysely.sql.raw("!=") : kysely.sql.raw(filter.operator)} ${filter.count}`;
|
|
383
|
+
}
|
|
384
|
+
buildRelationFilterCondition(target, relationFilters) {
|
|
385
|
+
if (!relationFilters || relationFilters.length === 0) return kysely.sql`1 = 1`;
|
|
386
|
+
let expression = null;
|
|
387
|
+
relationFilters.forEach((filter) => {
|
|
388
|
+
const next = this.buildRelationFilterExpression(target, filter);
|
|
389
|
+
if (!expression) {
|
|
390
|
+
expression = next;
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
expression = filter.boolean === "OR" ? kysely.sql`(${expression} or ${next})` : kysely.sql`(${expression} and ${next})`;
|
|
394
|
+
});
|
|
395
|
+
return expression ?? kysely.sql`1 = 1`;
|
|
396
|
+
}
|
|
397
|
+
buildQueryFilterCondition(target, condition, relationFilters) {
|
|
398
|
+
let expression = condition ? this.buildWhereCondition(target, condition) : null;
|
|
399
|
+
relationFilters?.forEach((filter) => {
|
|
400
|
+
const next = this.buildRelationFilterExpression(target, filter);
|
|
401
|
+
if (!expression) {
|
|
402
|
+
expression = next;
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
expression = filter.boolean === "OR" ? kysely.sql`(${expression} or ${next})` : kysely.sql`(${expression} and ${next})`;
|
|
406
|
+
});
|
|
407
|
+
return expression ?? kysely.sql`1 = 1`;
|
|
408
|
+
}
|
|
409
|
+
buildRelationAggregateSelectList(target, relationAggregates) {
|
|
410
|
+
if (!relationAggregates || relationAggregates.length === 0) return kysely.sql``;
|
|
411
|
+
return kysely.sql.join(relationAggregates.map((aggregate) => {
|
|
412
|
+
const { relatedTarget, from, condition } = this.buildRelatedJoinCondition(target, aggregate.relation);
|
|
413
|
+
const relatedTable = this.resolveTable(relatedTarget);
|
|
414
|
+
const whereCondition = this.combineConditions([condition, aggregate.where ? this.buildWhereCondition(relatedTarget, aggregate.where) : void 0]);
|
|
415
|
+
if (aggregate.type === "exists") return kysely.sql`, exists(
|
|
416
|
+
select 1
|
|
417
|
+
from ${from}
|
|
418
|
+
where ${whereCondition}
|
|
419
|
+
) as ${kysely.sql.id(aggregate.alias ?? `${aggregate.relation}Exists`)}`;
|
|
420
|
+
const selectedColumn = aggregate.column ? this.buildColumnReference(relatedTable, this.mapColumn(relatedTarget, aggregate.column)) : kysely.sql.raw("*");
|
|
421
|
+
return kysely.sql`, (
|
|
422
|
+
select ${aggregate.type === "count" ? kysely.sql`count(*)::int` : aggregate.type === "sum" ? kysely.sql`sum(${selectedColumn})::double precision` : aggregate.type === "avg" ? kysely.sql`avg(${selectedColumn})::double precision` : aggregate.type === "min" ? kysely.sql`min(${selectedColumn})` : kysely.sql`max(${selectedColumn})`}
|
|
423
|
+
from ${from}
|
|
424
|
+
where ${whereCondition}
|
|
425
|
+
) as ${kysely.sql.id(aggregate.alias ?? `${aggregate.relation}${aggregate.type}`)}`;
|
|
426
|
+
}), kysely.sql``);
|
|
427
|
+
}
|
|
428
|
+
buildCombinedWhereClause(target, condition, relationFilters) {
|
|
429
|
+
if (!condition && (!relationFilters || relationFilters.length === 0)) return kysely.sql``;
|
|
430
|
+
return kysely.sql` where ${this.buildQueryFilterCondition(target, condition, relationFilters)}`;
|
|
431
|
+
}
|
|
432
|
+
buildSingleRowTargetCte(target, where) {
|
|
433
|
+
const primaryKey = this.resolvePrimaryKey(target);
|
|
434
|
+
return kysely.sql`target_row as (
|
|
435
|
+
select ${kysely.sql.id(primaryKey)}
|
|
436
|
+
from ${kysely.sql.table(this.resolveTable(target))}
|
|
437
|
+
where ${this.buildWhereCondition(target, where)}
|
|
438
|
+
limit 1
|
|
439
|
+
)`;
|
|
440
|
+
}
|
|
228
441
|
assertNoRelationLoads(spec) {
|
|
229
442
|
if ("relationLoads" in spec && spec.relationLoads && spec.relationLoads.length > 0) throw new UnsupportedAdapterFeatureException("Kysely adapter relation-load execution is planned for a later phase.", {
|
|
230
443
|
operation: "adapter.relationLoads",
|
|
231
444
|
meta: { feature: "relationLoads" }
|
|
232
445
|
});
|
|
233
446
|
}
|
|
447
|
+
/**
|
|
448
|
+
* Selects records from the database matching the specified criteria and returns
|
|
449
|
+
* them as an array of database rows.
|
|
450
|
+
*
|
|
451
|
+
* @param spec The specification defining the selection criteria.
|
|
452
|
+
* @returns A promise that resolves to an array of database rows.
|
|
453
|
+
*/
|
|
234
454
|
async select(spec) {
|
|
235
455
|
this.assertNoRelationLoads(spec);
|
|
236
456
|
const result = await kysely.sql`
|
|
237
457
|
select ${this.buildSelectList(spec.target, spec.columns)}
|
|
458
|
+
${this.buildRelationAggregateSelectList(spec.target, spec.relationAggregates)}
|
|
238
459
|
from ${kysely.sql.table(this.resolveTable(spec.target))}
|
|
239
|
-
${this.
|
|
460
|
+
${this.buildCombinedWhereClause(spec.target, spec.where, spec.relationFilters)}
|
|
240
461
|
${this.buildOrderBy(spec.target, spec.orderBy)}
|
|
241
462
|
${this.buildPaginationClause(spec)}
|
|
242
463
|
`.execute(this.db);
|
|
243
464
|
return this.mapRows(spec.target, result.rows);
|
|
244
465
|
}
|
|
466
|
+
/**
|
|
467
|
+
* Selects a single record from the database matching the specified criteria and returns it as
|
|
468
|
+
* a database row. If multiple records match the criteria, only the first one is returned.
|
|
469
|
+
* If no records match, null is returned.
|
|
470
|
+
*
|
|
471
|
+
* @param spec The specification defining the selection criteria.
|
|
472
|
+
* @returns A promise that resolves to a database row or null if no records match.
|
|
473
|
+
*/
|
|
245
474
|
async selectOne(spec) {
|
|
246
475
|
return (await this.select({
|
|
247
476
|
...spec,
|
|
248
477
|
limit: spec.limit ?? 1
|
|
249
478
|
}))[0] ?? null;
|
|
250
479
|
}
|
|
480
|
+
/**
|
|
481
|
+
* Inserts a new record into the database with the specified values and returns the
|
|
482
|
+
* inserted record as a database row.
|
|
483
|
+
*
|
|
484
|
+
* @param spec
|
|
485
|
+
* @returns
|
|
486
|
+
*/
|
|
251
487
|
async insert(spec) {
|
|
252
488
|
const values = this.mapValues(spec.target, spec.values);
|
|
253
489
|
const columns = Object.keys(values);
|
|
@@ -262,6 +498,13 @@ var KyselyDatabaseAdapter = class KyselyDatabaseAdapter {
|
|
|
262
498
|
`.execute(this.db);
|
|
263
499
|
return this.mapRow(spec.target, result.rows[0]);
|
|
264
500
|
}
|
|
501
|
+
/**
|
|
502
|
+
* Inserts multiple records into the database with the specified values and returns the number
|
|
503
|
+
* of records successfully inserted.
|
|
504
|
+
*
|
|
505
|
+
* @param spec The specification defining the values to be inserted.
|
|
506
|
+
* @returns A promise that resolves to the number of records successfully inserted.
|
|
507
|
+
*/
|
|
265
508
|
async insertMany(spec) {
|
|
266
509
|
if (spec.values.length === 0) return 0;
|
|
267
510
|
const rows = spec.values.map((row) => this.mapValues(spec.target, row));
|
|
@@ -282,6 +525,39 @@ var KyselyDatabaseAdapter = class KyselyDatabaseAdapter {
|
|
|
282
525
|
returning ${kysely.sql.id(this.resolvePrimaryKey(spec.target))}
|
|
283
526
|
`.execute(this.db)).rows.length;
|
|
284
527
|
}
|
|
528
|
+
async upsert(spec) {
|
|
529
|
+
if (spec.values.length === 0) return 0;
|
|
530
|
+
const rows = spec.values.map((row) => this.mapValues(spec.target, row));
|
|
531
|
+
const columns = Array.from(new Set(rows.flatMap((row) => Object.keys(row))));
|
|
532
|
+
const uniqueColumns = spec.uniqueBy.map((column) => this.mapColumn(spec.target, column));
|
|
533
|
+
const updateColumns = (spec.updateColumns ?? []).map((column) => this.mapColumn(spec.target, column)).filter((column) => !uniqueColumns.includes(column));
|
|
534
|
+
const conflictTarget = kysely.sql.join(uniqueColumns.map((column) => kysely.sql.id(column)), kysely.sql`, `);
|
|
535
|
+
if (columns.length === 0) {
|
|
536
|
+
await kysely.sql`
|
|
537
|
+
insert into ${kysely.sql.table(this.resolveTable(spec.target))}
|
|
538
|
+
default values
|
|
539
|
+
on conflict (${conflictTarget}) do nothing
|
|
540
|
+
`.execute(this.db);
|
|
541
|
+
return spec.values.length;
|
|
542
|
+
}
|
|
543
|
+
const values = kysely.sql.join(rows.map((row) => {
|
|
544
|
+
return kysely.sql`(${kysely.sql.join(columns.map((column) => row[column] ?? null), kysely.sql`, `)})`;
|
|
545
|
+
}), kysely.sql`, `);
|
|
546
|
+
const conflictAction = updateColumns.length === 0 ? kysely.sql`do nothing` : kysely.sql`do update set ${kysely.sql.join(updateColumns.map((column) => kysely.sql`${kysely.sql.id(column)} = excluded.${kysely.sql.id(column)}`), kysely.sql`, `)}`;
|
|
547
|
+
await kysely.sql`
|
|
548
|
+
insert into ${kysely.sql.table(this.resolveTable(spec.target))} (${kysely.sql.join(columns.map((column) => kysely.sql.id(column)), kysely.sql`, `)})
|
|
549
|
+
values ${values}
|
|
550
|
+
on conflict (${conflictTarget}) ${conflictAction}
|
|
551
|
+
`.execute(this.db);
|
|
552
|
+
return spec.values.length;
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Updates records in the database matching the specified criteria with the given values
|
|
556
|
+
* and returns the updated record as a database row.
|
|
557
|
+
*
|
|
558
|
+
* @param spec The specification defining the update criteria and values.
|
|
559
|
+
* @returns A promise that resolves to the updated record as a database row, or null if no records match the criteria.
|
|
560
|
+
*/
|
|
285
561
|
async update(spec) {
|
|
286
562
|
const values = this.mapValues(spec.target, spec.values);
|
|
287
563
|
const assignments = Object.entries(values).map(([column, value]) => {
|
|
@@ -300,6 +576,41 @@ var KyselyDatabaseAdapter = class KyselyDatabaseAdapter {
|
|
|
300
576
|
`.execute(this.db);
|
|
301
577
|
return this.mapRow(spec.target, result.rows[0]);
|
|
302
578
|
}
|
|
579
|
+
/**
|
|
580
|
+
* Updates a single record in the database matching the specified criteria with the given values.
|
|
581
|
+
*
|
|
582
|
+
* @param spec
|
|
583
|
+
* @returns
|
|
584
|
+
*/
|
|
585
|
+
async updateFirst(spec) {
|
|
586
|
+
const values = this.mapValues(spec.target, spec.values);
|
|
587
|
+
const assignments = Object.entries(values).map(([column, value]) => {
|
|
588
|
+
return kysely.sql`${kysely.sql.id(column)} = ${value}`;
|
|
589
|
+
});
|
|
590
|
+
if (assignments.length === 0) return await this.selectOne({
|
|
591
|
+
target: spec.target,
|
|
592
|
+
where: spec.where,
|
|
593
|
+
limit: 1
|
|
594
|
+
});
|
|
595
|
+
const primaryKey = this.resolvePrimaryKey(spec.target);
|
|
596
|
+
const table = this.resolveTable(spec.target);
|
|
597
|
+
const result = await kysely.sql`
|
|
598
|
+
with ${this.buildSingleRowTargetCte(spec.target, spec.where)}
|
|
599
|
+
update ${kysely.sql.table(table)}
|
|
600
|
+
set ${kysely.sql.join(assignments, kysely.sql`, `)}
|
|
601
|
+
from target_row
|
|
602
|
+
where ${this.buildColumnReference(table, primaryKey)} = ${kysely.sql`target_row.${kysely.sql.id(primaryKey)}`}
|
|
603
|
+
returning ${kysely.sql.table(table)}.*
|
|
604
|
+
`.execute(this.db);
|
|
605
|
+
return this.mapRow(spec.target, result.rows[0]);
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Updates multiple records in the database matching the specified criteria with the
|
|
609
|
+
* given values and returns the number of records successfully updated.
|
|
610
|
+
*
|
|
611
|
+
* @param spec The specification defining the update criteria and values.
|
|
612
|
+
* @returns A promise that resolves to the number of records successfully updated.
|
|
613
|
+
*/
|
|
303
614
|
async updateMany(spec) {
|
|
304
615
|
const values = this.mapValues(spec.target, spec.values);
|
|
305
616
|
const assignments = Object.entries(values).map(([column, value]) => {
|
|
@@ -313,6 +624,13 @@ var KyselyDatabaseAdapter = class KyselyDatabaseAdapter {
|
|
|
313
624
|
returning ${kysely.sql.id(this.resolvePrimaryKey(spec.target))}
|
|
314
625
|
`.execute(this.db)).rows.length;
|
|
315
626
|
}
|
|
627
|
+
/**
|
|
628
|
+
* Deletes records from the database matching the specified criteria and returns the
|
|
629
|
+
* deleted record as a database row.
|
|
630
|
+
*
|
|
631
|
+
* @param spec The specification defining the delete criteria.
|
|
632
|
+
* @returns A promise that resolves to the deleted record as a database row, or null if no records match the criteria.
|
|
633
|
+
*/
|
|
316
634
|
async delete(spec) {
|
|
317
635
|
const result = await kysely.sql`
|
|
318
636
|
delete from ${kysely.sql.table(this.resolveTable(spec.target))}
|
|
@@ -321,6 +639,31 @@ var KyselyDatabaseAdapter = class KyselyDatabaseAdapter {
|
|
|
321
639
|
`.execute(this.db);
|
|
322
640
|
return this.mapRow(spec.target, result.rows[0]);
|
|
323
641
|
}
|
|
642
|
+
/**
|
|
643
|
+
* Deletes a single record from the database matching the specified criteria and returns it as a database row.
|
|
644
|
+
*
|
|
645
|
+
* @param spec
|
|
646
|
+
* @returns
|
|
647
|
+
*/
|
|
648
|
+
async deleteFirst(spec) {
|
|
649
|
+
const primaryKey = this.resolvePrimaryKey(spec.target);
|
|
650
|
+
const table = this.resolveTable(spec.target);
|
|
651
|
+
const result = await kysely.sql`
|
|
652
|
+
with ${this.buildSingleRowTargetCte(spec.target, spec.where)}
|
|
653
|
+
delete from ${kysely.sql.table(table)}
|
|
654
|
+
using target_row
|
|
655
|
+
where ${this.buildColumnReference(table, primaryKey)} = ${kysely.sql`target_row.${kysely.sql.id(primaryKey)}`}
|
|
656
|
+
returning ${kysely.sql.table(table)}.*
|
|
657
|
+
`.execute(this.db);
|
|
658
|
+
return this.mapRow(spec.target, result.rows[0]);
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Deletes multiple records from the database matching the specified criteria and
|
|
662
|
+
* returns the number of records successfully deleted.
|
|
663
|
+
*
|
|
664
|
+
* @param spec The specification defining the delete criteria.
|
|
665
|
+
* @returns A promise that resolves to the number of records successfully deleted.
|
|
666
|
+
*/
|
|
324
667
|
async deleteMany(spec) {
|
|
325
668
|
return (await kysely.sql`
|
|
326
669
|
delete from ${kysely.sql.table(this.resolveTable(spec.target))}
|
|
@@ -328,36 +671,113 @@ var KyselyDatabaseAdapter = class KyselyDatabaseAdapter {
|
|
|
328
671
|
returning ${kysely.sql.id(this.resolvePrimaryKey(spec.target))}
|
|
329
672
|
`.execute(this.db)).rows.length;
|
|
330
673
|
}
|
|
674
|
+
/**
|
|
675
|
+
* Counts the number of records in the database matching the specified criteria and returns
|
|
676
|
+
* the count as a number.
|
|
677
|
+
*
|
|
678
|
+
* @param spec The specification defining the count criteria.
|
|
679
|
+
* @returns A promise that resolves to the number of records matching the criteria.
|
|
680
|
+
*/
|
|
331
681
|
async count(spec) {
|
|
332
682
|
const result = await kysely.sql`
|
|
333
683
|
select count(*)::int as count
|
|
334
684
|
from ${kysely.sql.table(this.resolveTable(spec.target))}
|
|
335
|
-
${this.
|
|
685
|
+
${this.buildCombinedWhereClause(spec.target, spec.where, spec.relationFilters)}
|
|
336
686
|
`.execute(this.db);
|
|
337
687
|
return Number(result.rows[0]?.count ?? 0);
|
|
338
688
|
}
|
|
689
|
+
/**
|
|
690
|
+
* Checks for the existence of records matching the specified criteria.
|
|
691
|
+
*
|
|
692
|
+
* @param spec The specification defining the existence criteria.
|
|
693
|
+
* @returns A promise that resolves to a boolean indicating whether any records match the criteria.
|
|
694
|
+
*/
|
|
339
695
|
async exists(spec) {
|
|
340
696
|
const result = await kysely.sql`
|
|
341
697
|
select exists(
|
|
342
698
|
select 1
|
|
343
699
|
from ${kysely.sql.table(this.resolveTable(spec.target))}
|
|
344
|
-
${this.
|
|
700
|
+
${this.buildCombinedWhereClause(spec.target, spec.where, spec.relationFilters)}
|
|
345
701
|
limit 1
|
|
346
702
|
) as exists
|
|
347
703
|
`.execute(this.db);
|
|
348
704
|
return Boolean(result.rows[0]?.exists);
|
|
349
705
|
}
|
|
706
|
+
async introspectModels(options = {}) {
|
|
707
|
+
const tables = options.tables?.filter(Boolean) ?? [];
|
|
708
|
+
const result = await kysely.sql`
|
|
709
|
+
select
|
|
710
|
+
cls.relname as table_name,
|
|
711
|
+
att.attname as column_name,
|
|
712
|
+
not att.attnotnull as is_nullable,
|
|
713
|
+
typ.typname as type_name,
|
|
714
|
+
case when typ.typcategory = 'A' then elem.typname else null end as element_type_name,
|
|
715
|
+
case when typ.typtype = 'e'
|
|
716
|
+
then array(select enumlabel from pg_enum where enumtypid = typ.oid order by enumsortorder)
|
|
717
|
+
else null
|
|
718
|
+
end as enum_values,
|
|
719
|
+
case when elem.typtype = 'e'
|
|
720
|
+
then array(select enumlabel from pg_enum where enumtypid = elem.oid order by enumsortorder)
|
|
721
|
+
else null
|
|
722
|
+
end as element_enum_values
|
|
723
|
+
from pg_attribute att
|
|
724
|
+
inner join pg_class cls on cls.oid = att.attrelid
|
|
725
|
+
inner join pg_namespace ns on ns.oid = cls.relnamespace
|
|
726
|
+
inner join pg_type typ on typ.oid = att.atttypid
|
|
727
|
+
left join pg_type elem on elem.oid = typ.typelem and typ.typcategory = 'A'
|
|
728
|
+
where cls.relkind in ('r', 'p')
|
|
729
|
+
and att.attnum > 0
|
|
730
|
+
and not att.attisdropped
|
|
731
|
+
and ns.nspname not in ('pg_catalog', 'information_schema')
|
|
732
|
+
${tables.length > 0 ? kysely.sql` and cls.relname in (${kysely.sql.join(tables)})` : kysely.sql``}
|
|
733
|
+
order by cls.relname asc, att.attnum asc
|
|
734
|
+
`.execute(this.db);
|
|
735
|
+
const models = /* @__PURE__ */ new Map();
|
|
736
|
+
result.rows.forEach((row) => {
|
|
737
|
+
const existing = models.get(row.table_name) ?? {
|
|
738
|
+
name: (0, _h3ravel_support.str)(row.table_name).studly().singular().toString(),
|
|
739
|
+
table: row.table_name,
|
|
740
|
+
fields: []
|
|
741
|
+
};
|
|
742
|
+
const isArray = row.element_type_name !== null;
|
|
743
|
+
const baseType = isArray ? this.introspectionTypeToTs(row.element_type_name ?? "unknown", row.element_enum_values) : this.introspectionTypeToTs(row.type_name, row.enum_values);
|
|
744
|
+
existing.fields.push({
|
|
745
|
+
name: row.column_name,
|
|
746
|
+
type: isArray ? `Array<${baseType}>` : baseType,
|
|
747
|
+
nullable: row.is_nullable
|
|
748
|
+
});
|
|
749
|
+
models.set(row.table_name, existing);
|
|
750
|
+
});
|
|
751
|
+
return [...models.values()];
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Executes a series of database operations within a transaction.
|
|
755
|
+
* The provided callback function is called with a new instance of the
|
|
756
|
+
* KyselyDatabaseAdapter that is bound to the transaction context.
|
|
757
|
+
*
|
|
758
|
+
* @param callback The callback function containing the database operations to be executed within the transaction.
|
|
759
|
+
* @param context The transaction context specifying options such as read-only mode and isolation level.
|
|
760
|
+
* @returns A promise that resolves to the result of the callback function.
|
|
761
|
+
*/
|
|
350
762
|
async transaction(callback, context = {}) {
|
|
351
763
|
let transactionBuilder = this.db.transaction();
|
|
352
764
|
if (context.readOnly !== void 0) transactionBuilder = transactionBuilder.setAccessMode(context.readOnly ? "read only" : "read write");
|
|
353
765
|
if (context.isolationLevel) transactionBuilder = transactionBuilder.setIsolationLevel(context.isolationLevel);
|
|
354
766
|
return await transactionBuilder.execute(async (transaction) => {
|
|
355
|
-
return await callback(new KyselyDatabaseAdapter(transaction));
|
|
767
|
+
return await callback(new KyselyDatabaseAdapter(transaction, this.mapping));
|
|
356
768
|
});
|
|
357
769
|
}
|
|
358
770
|
};
|
|
359
|
-
|
|
360
|
-
|
|
771
|
+
/**
|
|
772
|
+
* Factory function to create a KyselyDatabaseAdapter instance with the given Kysely executor
|
|
773
|
+
* and optional table name mapping.
|
|
774
|
+
*
|
|
775
|
+
* @param db The Kysely executor to be used by the adapter.
|
|
776
|
+
* @param mapping Optional table name mapping for the adapter.
|
|
777
|
+
* @returns A new instance of KyselyDatabaseAdapter.
|
|
778
|
+
*/
|
|
779
|
+
const createKyselyAdapter = (db, mapping = {}) => {
|
|
780
|
+
return new KyselyDatabaseAdapter(db, mapping);
|
|
361
781
|
};
|
|
362
782
|
|
|
363
783
|
//#endregion
|
|
@@ -416,6 +836,7 @@ const userConfig = {
|
|
|
416
836
|
let runtimeConfigLoaded = false;
|
|
417
837
|
let runtimeConfigLoadingPromise;
|
|
418
838
|
let runtimeClientResolver;
|
|
839
|
+
let runtimeAdapter;
|
|
419
840
|
let runtimePaginationURLDriverFactory;
|
|
420
841
|
let runtimePaginationCurrentPageResolver;
|
|
421
842
|
const transactionClientStorage = new async_hooks.AsyncLocalStorage();
|
|
@@ -441,6 +862,12 @@ const mergePathConfig = (paths) => {
|
|
|
441
862
|
const defineConfig = (config) => {
|
|
442
863
|
return config;
|
|
443
864
|
};
|
|
865
|
+
const bindAdapterToModels = (adapter, models) => {
|
|
866
|
+
models.forEach((model) => {
|
|
867
|
+
model.setAdapter(adapter);
|
|
868
|
+
});
|
|
869
|
+
return adapter;
|
|
870
|
+
};
|
|
444
871
|
/**
|
|
445
872
|
* Get the user-provided ArkORM configuration.
|
|
446
873
|
*
|
|
@@ -460,15 +887,22 @@ const getUserConfig = (key) => {
|
|
|
460
887
|
const configureArkormRuntime = (prisma, options = {}) => {
|
|
461
888
|
const nextConfig = {
|
|
462
889
|
...userConfig,
|
|
463
|
-
prisma,
|
|
464
890
|
paths: mergePathConfig(options.paths)
|
|
465
891
|
};
|
|
892
|
+
nextConfig.prisma = prisma;
|
|
466
893
|
if (options.pagination !== void 0) nextConfig.pagination = options.pagination;
|
|
894
|
+
if (options.adapter !== void 0) nextConfig.adapter = options.adapter;
|
|
895
|
+
if (options.boot !== void 0) nextConfig.boot = options.boot;
|
|
467
896
|
if (options.outputExt !== void 0) nextConfig.outputExt = options.outputExt;
|
|
468
897
|
Object.assign(userConfig, { ...nextConfig });
|
|
469
898
|
runtimeClientResolver = prisma;
|
|
899
|
+
runtimeAdapter = options.adapter;
|
|
470
900
|
runtimePaginationURLDriverFactory = nextConfig.pagination?.urlDriver;
|
|
471
901
|
runtimePaginationCurrentPageResolver = nextConfig.pagination?.resolveCurrentPage;
|
|
902
|
+
options.boot?.({
|
|
903
|
+
prisma: resolveClient(prisma),
|
|
904
|
+
bindAdapter: bindAdapterToModels
|
|
905
|
+
});
|
|
472
906
|
};
|
|
473
907
|
/**
|
|
474
908
|
* Reset the ArkORM runtime configuration.
|
|
@@ -482,6 +916,7 @@ const resetArkormRuntimeForTests = () => {
|
|
|
482
916
|
runtimeConfigLoaded = false;
|
|
483
917
|
runtimeConfigLoadingPromise = void 0;
|
|
484
918
|
runtimeClientResolver = void 0;
|
|
919
|
+
runtimeAdapter = void 0;
|
|
485
920
|
runtimePaginationURLDriverFactory = void 0;
|
|
486
921
|
runtimePaginationCurrentPageResolver = void 0;
|
|
487
922
|
};
|
|
@@ -508,8 +943,10 @@ const resolveClient = (resolver) => {
|
|
|
508
943
|
*/
|
|
509
944
|
const resolveAndApplyConfig = (imported) => {
|
|
510
945
|
const config = imported?.default ?? imported;
|
|
511
|
-
if (!config || typeof config !== "object"
|
|
946
|
+
if (!config || typeof config !== "object") return;
|
|
512
947
|
configureArkormRuntime(config.prisma, {
|
|
948
|
+
adapter: config.adapter,
|
|
949
|
+
boot: config.boot,
|
|
513
950
|
pagination: config.pagination,
|
|
514
951
|
paths: config.paths,
|
|
515
952
|
outputExt: config.outputExt
|
|
@@ -591,6 +1028,10 @@ const getRuntimePrismaClient = () => {
|
|
|
591
1028
|
if (!runtimeConfigLoaded) loadRuntimeConfigSync();
|
|
592
1029
|
return resolveClient(runtimeClientResolver);
|
|
593
1030
|
};
|
|
1031
|
+
const getRuntimeAdapter = () => {
|
|
1032
|
+
if (!runtimeConfigLoaded) loadRuntimeConfigSync();
|
|
1033
|
+
return runtimeAdapter;
|
|
1034
|
+
};
|
|
594
1035
|
const getActiveTransactionClient = () => {
|
|
595
1036
|
return transactionClientStorage.getStore();
|
|
596
1037
|
};
|
|
@@ -658,9 +1099,11 @@ loadArkormConfig();
|
|
|
658
1099
|
//#endregion
|
|
659
1100
|
//#region src/helpers/prisma.ts
|
|
660
1101
|
/**
|
|
661
|
-
* Create an adapter to convert a Prisma client instance into a format
|
|
1102
|
+
* Create an adapter to convert a Prisma client instance into a format
|
|
662
1103
|
* compatible with ArkORM's expectations.
|
|
663
|
-
*
|
|
1104
|
+
*
|
|
1105
|
+
* @deprecated Prefer createPrismaDatabaseAdapter(prisma) for runtime usage.
|
|
1106
|
+
*
|
|
664
1107
|
* @param prisma The Prisma client instance to adapt.
|
|
665
1108
|
* @param mapping An optional mapping of Prisma delegate names to ArkORM delegate names.
|
|
666
1109
|
* @returns A record of adapted Prisma delegates compatible with ArkORM.
|
|
@@ -675,6 +1118,9 @@ function createPrismaAdapter(prisma) {
|
|
|
675
1118
|
/**
|
|
676
1119
|
* Create a delegate mapping record for Model.setClient() from a Prisma client.
|
|
677
1120
|
*
|
|
1121
|
+
* @deprecated Prefer createPrismaDatabaseAdapter(prisma, mapping) and bind the
|
|
1122
|
+
* resulting adapter with Model.setAdapter(...).
|
|
1123
|
+
*
|
|
678
1124
|
* @param prisma The Prisma client instance.
|
|
679
1125
|
* @param mapping Optional mapping of Arkormˣ delegate names to Prisma delegate names.
|
|
680
1126
|
* @returns A delegate map keyed by Arkormˣ delegate names.
|
|
@@ -701,6 +1147,13 @@ function inferDelegateName(modelName) {
|
|
|
701
1147
|
|
|
702
1148
|
//#endregion
|
|
703
1149
|
//#region src/adapters/PrismaDatabaseAdapter.ts
|
|
1150
|
+
/**
|
|
1151
|
+
* Database adapter implementation for Prisma, allowing Arkorm to execute queries using Prisma
|
|
1152
|
+
* as the underlying query builder and executor.
|
|
1153
|
+
*
|
|
1154
|
+
* @author Legacy (3m1n3nc3)
|
|
1155
|
+
* @since 2.0.0-next.0
|
|
1156
|
+
*/
|
|
704
1157
|
var PrismaDatabaseAdapter = class PrismaDatabaseAdapter {
|
|
705
1158
|
capabilities;
|
|
706
1159
|
delegates;
|
|
@@ -715,6 +1168,7 @@ var PrismaDatabaseAdapter = class PrismaDatabaseAdapter {
|
|
|
715
1168
|
this.capabilities = {
|
|
716
1169
|
transactions: this.hasTransactionSupport(prisma),
|
|
717
1170
|
insertMany: Object.values(this.delegates).some((delegate) => typeof delegate.createMany === "function"),
|
|
1171
|
+
upsert: false,
|
|
718
1172
|
updateMany: Object.values(this.delegates).some((delegate) => typeof delegate.updateMany === "function"),
|
|
719
1173
|
deleteMany: false,
|
|
720
1174
|
exists: true,
|
|
@@ -734,6 +1188,27 @@ var PrismaDatabaseAdapter = class PrismaDatabaseAdapter {
|
|
|
734
1188
|
unique(values) {
|
|
735
1189
|
return [...new Set(values.filter(Boolean))];
|
|
736
1190
|
}
|
|
1191
|
+
runtimeModelTypeToTs(typeName, kind, enumValues) {
|
|
1192
|
+
if (kind === "enum" && enumValues && enumValues.length > 0) return enumValues.map((value) => `'${value.replace(/'/g, "\\'")}'`).join(" | ");
|
|
1193
|
+
switch (typeName) {
|
|
1194
|
+
case "Int":
|
|
1195
|
+
case "Float":
|
|
1196
|
+
case "Decimal":
|
|
1197
|
+
case "BigInt": return "number";
|
|
1198
|
+
case "Boolean": return "boolean";
|
|
1199
|
+
case "DateTime": return "Date";
|
|
1200
|
+
case "Json": return "Record<string, unknown> | unknown[]";
|
|
1201
|
+
case "Bytes": return "Uint8Array";
|
|
1202
|
+
case "String":
|
|
1203
|
+
case "UUID": return "string";
|
|
1204
|
+
default: return "string";
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
getRuntimeDataModel() {
|
|
1208
|
+
const runtimeDataModel = this.prisma._runtimeDataModel;
|
|
1209
|
+
if (runtimeDataModel && typeof runtimeDataModel === "object") return runtimeDataModel;
|
|
1210
|
+
return null;
|
|
1211
|
+
}
|
|
737
1212
|
toQuerySelect(columns) {
|
|
738
1213
|
if (!columns || columns.length === 0) return void 0;
|
|
739
1214
|
return columns.reduce((select, column) => {
|
|
@@ -815,6 +1290,29 @@ var PrismaDatabaseAdapter = class PrismaDatabaseAdapter {
|
|
|
815
1290
|
return include;
|
|
816
1291
|
}, {});
|
|
817
1292
|
}
|
|
1293
|
+
async introspectModels(options = {}) {
|
|
1294
|
+
const runtimeDataModel = this.getRuntimeDataModel();
|
|
1295
|
+
if (!runtimeDataModel?.models) return [];
|
|
1296
|
+
const requestedTables = new Set(options.tables?.filter(Boolean) ?? []);
|
|
1297
|
+
const enums = runtimeDataModel.enums ?? {};
|
|
1298
|
+
return Object.entries(runtimeDataModel.models).flatMap(([name, model]) => {
|
|
1299
|
+
const table = model.dbName ?? `${(0, _h3ravel_support.str)(name).camel().plural()}`;
|
|
1300
|
+
if (requestedTables.size > 0 && !requestedTables.has(table)) return [];
|
|
1301
|
+
return [{
|
|
1302
|
+
name,
|
|
1303
|
+
table,
|
|
1304
|
+
fields: (model.fields ?? []).filter((field) => field.kind !== "object").map((field) => {
|
|
1305
|
+
const enumValues = field.kind === "enum" ? (enums[field.type]?.values ?? []).map((value) => typeof value === "string" ? value : value.name ?? "").filter(Boolean) : null;
|
|
1306
|
+
const baseType = this.runtimeModelTypeToTs(field.type, field.kind, enumValues);
|
|
1307
|
+
return {
|
|
1308
|
+
name: field.name,
|
|
1309
|
+
type: field.isList ? `Array<${baseType}>` : baseType,
|
|
1310
|
+
nullable: field.isRequired === false
|
|
1311
|
+
};
|
|
1312
|
+
})
|
|
1313
|
+
}];
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
818
1316
|
resolveDelegate(target) {
|
|
819
1317
|
const tableName = target.table ? this.normalizeCandidate(target.table) : "";
|
|
820
1318
|
const singularTableName = tableName ? `${(0, _h3ravel_support.str)(tableName).singular()}` : "";
|
|
@@ -846,15 +1344,41 @@ var PrismaDatabaseAdapter = class PrismaDatabaseAdapter {
|
|
|
846
1344
|
}
|
|
847
1345
|
});
|
|
848
1346
|
}
|
|
1347
|
+
/**
|
|
1348
|
+
* @todo Implement relationLoads by performing separate queries and merging results
|
|
1349
|
+
* in-memory, since Prisma does not support nested reads with constraints, ordering, or
|
|
1350
|
+
* pagination on related models as of now.
|
|
1351
|
+
*
|
|
1352
|
+
* @param spec
|
|
1353
|
+
* @returns
|
|
1354
|
+
*/
|
|
849
1355
|
async select(spec) {
|
|
850
1356
|
return await this.resolveDelegate(spec.target).findMany(this.buildFindArgs(spec));
|
|
851
1357
|
}
|
|
1358
|
+
/**
|
|
1359
|
+
* Selects a single record matching the specified criteria.
|
|
1360
|
+
*
|
|
1361
|
+
* @param spec
|
|
1362
|
+
* @returns
|
|
1363
|
+
*/
|
|
852
1364
|
async selectOne(spec) {
|
|
853
1365
|
return await this.resolveDelegate(spec.target).findFirst(this.buildFindArgs(spec));
|
|
854
1366
|
}
|
|
1367
|
+
/**
|
|
1368
|
+
* Inserts a single record into the database and returns the created record.
|
|
1369
|
+
*
|
|
1370
|
+
* @param spec
|
|
1371
|
+
* @returns
|
|
1372
|
+
*/
|
|
855
1373
|
async insert(spec) {
|
|
856
1374
|
return await this.resolveDelegate(spec.target).create({ data: spec.values });
|
|
857
1375
|
}
|
|
1376
|
+
/**
|
|
1377
|
+
* Inserts multiple records into the database.
|
|
1378
|
+
*
|
|
1379
|
+
* @param spec
|
|
1380
|
+
* @returns
|
|
1381
|
+
*/
|
|
858
1382
|
async insertMany(spec) {
|
|
859
1383
|
const delegate = this.resolveDelegate(spec.target);
|
|
860
1384
|
if (typeof delegate.createMany === "function") {
|
|
@@ -874,6 +1398,12 @@ var PrismaDatabaseAdapter = class PrismaDatabaseAdapter {
|
|
|
874
1398
|
}
|
|
875
1399
|
return spec.ignoreDuplicates ? inserted : spec.values.length;
|
|
876
1400
|
}
|
|
1401
|
+
/**
|
|
1402
|
+
* Updates a single record matching the specified criteria and returns the updated record.
|
|
1403
|
+
*
|
|
1404
|
+
* @param spec
|
|
1405
|
+
* @returns
|
|
1406
|
+
*/
|
|
877
1407
|
async update(spec) {
|
|
878
1408
|
const delegate = this.resolveDelegate(spec.target);
|
|
879
1409
|
const where = this.toQueryWhere(spec.where);
|
|
@@ -883,6 +1413,12 @@ var PrismaDatabaseAdapter = class PrismaDatabaseAdapter {
|
|
|
883
1413
|
data: spec.values
|
|
884
1414
|
});
|
|
885
1415
|
}
|
|
1416
|
+
/**
|
|
1417
|
+
* Updates multiple records matching the specified criteria.
|
|
1418
|
+
*
|
|
1419
|
+
* @param spec
|
|
1420
|
+
* @returns
|
|
1421
|
+
*/
|
|
886
1422
|
async updateMany(spec) {
|
|
887
1423
|
const delegate = this.resolveDelegate(spec.target);
|
|
888
1424
|
const where = this.toQueryWhere(spec.where);
|
|
@@ -903,12 +1439,24 @@ var PrismaDatabaseAdapter = class PrismaDatabaseAdapter {
|
|
|
903
1439
|
}));
|
|
904
1440
|
return rows.length;
|
|
905
1441
|
}
|
|
1442
|
+
/**
|
|
1443
|
+
* Deletes a single record matching the specified criteria and returns the deleted record.
|
|
1444
|
+
*
|
|
1445
|
+
* @param spec
|
|
1446
|
+
* @returns
|
|
1447
|
+
*/
|
|
906
1448
|
async delete(spec) {
|
|
907
1449
|
const delegate = this.resolveDelegate(spec.target);
|
|
908
1450
|
const where = this.toQueryWhere(spec.where);
|
|
909
1451
|
if (!where) return null;
|
|
910
1452
|
return await delegate.delete({ where });
|
|
911
1453
|
}
|
|
1454
|
+
/**
|
|
1455
|
+
* Deletes multiple records matching the specified criteria.
|
|
1456
|
+
*
|
|
1457
|
+
* @param spec
|
|
1458
|
+
* @returns
|
|
1459
|
+
*/
|
|
912
1460
|
async deleteMany(spec) {
|
|
913
1461
|
const delegate = this.resolveDelegate(spec.target);
|
|
914
1462
|
const where = this.toQueryWhere(spec.where);
|
|
@@ -918,21 +1466,46 @@ var PrismaDatabaseAdapter = class PrismaDatabaseAdapter {
|
|
|
918
1466
|
}));
|
|
919
1467
|
return rows.length;
|
|
920
1468
|
}
|
|
1469
|
+
/**
|
|
1470
|
+
* Counts the number of records matching the specified criteria.
|
|
1471
|
+
*
|
|
1472
|
+
* @param spec
|
|
1473
|
+
* @returns
|
|
1474
|
+
*/
|
|
921
1475
|
async count(spec) {
|
|
922
1476
|
return await this.resolveDelegate(spec.target).count({ where: this.toQueryWhere(spec.where) });
|
|
923
1477
|
}
|
|
1478
|
+
/**
|
|
1479
|
+
* Checks for the existence of records matching the specified criteria.
|
|
1480
|
+
*
|
|
1481
|
+
* @param spec
|
|
1482
|
+
* @returns
|
|
1483
|
+
*/
|
|
924
1484
|
async exists(spec) {
|
|
925
1485
|
return await this.selectOne({
|
|
926
1486
|
...spec,
|
|
927
1487
|
limit: 1
|
|
928
1488
|
}) != null;
|
|
929
1489
|
}
|
|
1490
|
+
/**
|
|
1491
|
+
* Loads related models for a batch of parent records based on the specified relation load plans.
|
|
1492
|
+
*
|
|
1493
|
+
* @param _spec
|
|
1494
|
+
*/
|
|
930
1495
|
async loadRelations(_spec) {
|
|
931
1496
|
throw new UnsupportedAdapterFeatureException("Relation batch loading is not supported by the Prisma compatibility adapter yet.", {
|
|
932
1497
|
operation: "adapter.loadRelations",
|
|
933
1498
|
meta: { feature: "relationLoads" }
|
|
934
1499
|
});
|
|
935
1500
|
}
|
|
1501
|
+
/**
|
|
1502
|
+
* Executes a series of database operations within a transaction.
|
|
1503
|
+
* If the underlying Prisma client does not support transactions, an exception is thrown.
|
|
1504
|
+
*
|
|
1505
|
+
* @param callback
|
|
1506
|
+
* @param context
|
|
1507
|
+
* @returns
|
|
1508
|
+
*/
|
|
936
1509
|
async transaction(callback, context = {}) {
|
|
937
1510
|
if (!this.hasTransactionSupport(this.prisma)) throw new UnsupportedAdapterFeatureException("Transactions are not supported by the Prisma compatibility adapter.", {
|
|
938
1511
|
operation: "adapter.transaction",
|
|
@@ -947,9 +1520,25 @@ var PrismaDatabaseAdapter = class PrismaDatabaseAdapter {
|
|
|
947
1520
|
});
|
|
948
1521
|
}
|
|
949
1522
|
};
|
|
1523
|
+
/**
|
|
1524
|
+
* Factory function to create a PrismaDatabaseAdapter instance with the given
|
|
1525
|
+
* Prisma client and optional delegate name mapping.
|
|
1526
|
+
*
|
|
1527
|
+
* @param prisma The Prisma client instance to be used by the adapter.
|
|
1528
|
+
* @param mapping Optional mapping of delegate names.
|
|
1529
|
+
* @returns A new instance of PrismaDatabaseAdapter.
|
|
1530
|
+
*/
|
|
950
1531
|
const createPrismaDatabaseAdapter = (prisma, mapping = {}) => {
|
|
951
1532
|
return new PrismaDatabaseAdapter(prisma, mapping);
|
|
952
1533
|
};
|
|
1534
|
+
/**
|
|
1535
|
+
* Alias for createPrismaDatabaseAdapter to maintain backward compatibility with
|
|
1536
|
+
* previous versions of Arkorm that exported the adapter factory under a different name.
|
|
1537
|
+
*
|
|
1538
|
+
* @param prisma The Prisma client instance to be used by the adapter.
|
|
1539
|
+
* @param mapping Optional mapping of delegate names.
|
|
1540
|
+
* @returns A new instance of PrismaDatabaseAdapter.
|
|
1541
|
+
*/
|
|
953
1542
|
const createPrismaCompatibilityAdapter = createPrismaDatabaseAdapter;
|
|
954
1543
|
|
|
955
1544
|
//#endregion
|
|
@@ -2921,6 +3510,40 @@ var CliApp = class {
|
|
|
2921
3510
|
lines.splice(insertionIndex, 0, `import type { ${enumTypes.join(", ")} } from '@prisma/client'`);
|
|
2922
3511
|
return lines.join("\n");
|
|
2923
3512
|
}
|
|
3513
|
+
parseModelSyncSource(modelSource) {
|
|
3514
|
+
const classMatch = modelSource.match(/export\s+class\s+(\w+)\s+extends\s+Model(?:<[^\n]+>)?\s*\{/);
|
|
3515
|
+
if (!classMatch) return null;
|
|
3516
|
+
const className = classMatch[1];
|
|
3517
|
+
const tableMatch = modelSource.match(/protected\s+static\s+override\s+table\s*=\s*['"]([^'"]+)['"]/) ?? modelSource.match(/static\s+table\s*=\s*['"]([^'"]+)['"]/);
|
|
3518
|
+
const delegateMatch = modelSource.match(/protected\s+static\s+override\s+delegate\s*=\s*['"]([^'"]+)['"]/) ?? modelSource.match(/static\s+delegate\s*=\s*['"]([^'"]+)['"]/);
|
|
3519
|
+
return {
|
|
3520
|
+
className,
|
|
3521
|
+
table: tableMatch?.[1] ?? delegateMatch?.[1] ?? (0, _h3ravel_support.str)(className).camel().plural().toString()
|
|
3522
|
+
};
|
|
3523
|
+
}
|
|
3524
|
+
syncModelFiles(modelFiles, resolveStructure, enums) {
|
|
3525
|
+
const updated = [];
|
|
3526
|
+
const skipped = [];
|
|
3527
|
+
modelFiles.forEach((filePath) => {
|
|
3528
|
+
const source = (0, fs.readFileSync)(filePath, "utf-8");
|
|
3529
|
+
const structure = resolveStructure(filePath, source);
|
|
3530
|
+
if (!structure || structure.fields.length === 0) {
|
|
3531
|
+
skipped.push(filePath);
|
|
3532
|
+
return;
|
|
3533
|
+
}
|
|
3534
|
+
const synced = this.syncModelDeclarations(source, structure.fields, enums);
|
|
3535
|
+
if (!synced.updated) {
|
|
3536
|
+
skipped.push(filePath);
|
|
3537
|
+
return;
|
|
3538
|
+
}
|
|
3539
|
+
(0, fs.writeFileSync)(filePath, synced.content);
|
|
3540
|
+
updated.push(filePath);
|
|
3541
|
+
});
|
|
3542
|
+
return {
|
|
3543
|
+
updated,
|
|
3544
|
+
skipped
|
|
3545
|
+
};
|
|
3546
|
+
}
|
|
2924
3547
|
/**
|
|
2925
3548
|
* Parse Prisma enum definitions from a schema and return their member names.
|
|
2926
3549
|
*
|
|
@@ -3013,7 +3636,7 @@ var CliApp = class {
|
|
|
3013
3636
|
*/
|
|
3014
3637
|
syncModelDeclarations(modelSource, declarations, enums) {
|
|
3015
3638
|
const lines = modelSource.split("\n");
|
|
3016
|
-
const classIndex = lines.findIndex((line) => /export\s+class\s+\w+\s+extends\s+Model
|
|
3639
|
+
const classIndex = lines.findIndex((line) => /export\s+class\s+\w+\s+extends\s+Model(?:<[^\n]+>)?\s*\{/.test(line));
|
|
3017
3640
|
if (classIndex < 0) return {
|
|
3018
3641
|
content: modelSource,
|
|
3019
3642
|
updated: false
|
|
@@ -3067,6 +3690,36 @@ var CliApp = class {
|
|
|
3067
3690
|
updated: contentWithImports !== modelSource
|
|
3068
3691
|
};
|
|
3069
3692
|
}
|
|
3693
|
+
async syncModels(options = {}) {
|
|
3694
|
+
const modelsDir = options.modelsDir ?? this.resolveConfigPath("models", (0, path.join)(process.cwd(), "src", "models"));
|
|
3695
|
+
if (!(0, fs.existsSync)(modelsDir)) throw new Error(`Models directory not found: ${modelsDir}`);
|
|
3696
|
+
const modelFiles = (0, fs.readdirSync)(modelsDir).filter((file) => file.endsWith(".ts")).map((file) => (0, path.join)(modelsDir, file));
|
|
3697
|
+
const adapter = this.getConfig("adapter");
|
|
3698
|
+
if (adapter && typeof adapter.introspectModels === "function") {
|
|
3699
|
+
const sources = modelFiles.reduce((all, filePath) => {
|
|
3700
|
+
const parsed = this.parseModelSyncSource((0, fs.readFileSync)(filePath, "utf-8"));
|
|
3701
|
+
if (parsed) all.set(filePath, parsed);
|
|
3702
|
+
return all;
|
|
3703
|
+
}, /* @__PURE__ */ new Map());
|
|
3704
|
+
const discovered = await adapter.introspectModels({ tables: [...new Set([...sources.values()].map((source) => source.table))] });
|
|
3705
|
+
const structuresByTable = new Map(discovered.map((model) => [model.table, model]));
|
|
3706
|
+
const result = this.syncModelFiles(modelFiles, (filePath) => {
|
|
3707
|
+
const parsed = sources.get(filePath);
|
|
3708
|
+
return parsed ? structuresByTable.get(parsed.table) : void 0;
|
|
3709
|
+
}, /* @__PURE__ */ new Map());
|
|
3710
|
+
return {
|
|
3711
|
+
source: "adapter",
|
|
3712
|
+
modelsDir,
|
|
3713
|
+
total: modelFiles.length,
|
|
3714
|
+
updated: result.updated,
|
|
3715
|
+
skipped: result.skipped
|
|
3716
|
+
};
|
|
3717
|
+
}
|
|
3718
|
+
return {
|
|
3719
|
+
source: "prisma",
|
|
3720
|
+
...this.syncModelsFromPrisma(options)
|
|
3721
|
+
};
|
|
3722
|
+
}
|
|
3070
3723
|
/**
|
|
3071
3724
|
* Sync model attribute declarations in model files based on the Prisma schema.
|
|
3072
3725
|
* This method reads the Prisma schema to extract model definitions and their
|
|
@@ -3086,38 +3739,18 @@ var CliApp = class {
|
|
|
3086
3739
|
const schema = (0, fs.readFileSync)(schemaPath, "utf-8");
|
|
3087
3740
|
const prismaEnums = this.parsePrismaEnums(schema);
|
|
3088
3741
|
const prismaModels = this.parsePrismaModels(schema);
|
|
3089
|
-
const modelFiles = (0, fs.readdirSync)(modelsDir).filter((file) => file.endsWith(".ts"));
|
|
3090
|
-
const
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
const classMatch = source.match(/export\s+class\s+(\w+)\s+extends\s+Model<'([^']+)'>/);
|
|
3096
|
-
if (!classMatch) {
|
|
3097
|
-
skipped.push(filePath);
|
|
3098
|
-
return;
|
|
3099
|
-
}
|
|
3100
|
-
const className = classMatch[1];
|
|
3101
|
-
const delegate = classMatch[2];
|
|
3102
|
-
const prismaModel = prismaModels.find((model) => model.table === delegate) ?? prismaModels.find((model) => model.name === className);
|
|
3103
|
-
if (!prismaModel || prismaModel.fields.length === 0) {
|
|
3104
|
-
skipped.push(filePath);
|
|
3105
|
-
return;
|
|
3106
|
-
}
|
|
3107
|
-
const synced = this.syncModelDeclarations(source, prismaModel.fields, prismaEnums);
|
|
3108
|
-
if (!synced.updated) {
|
|
3109
|
-
skipped.push(filePath);
|
|
3110
|
-
return;
|
|
3111
|
-
}
|
|
3112
|
-
(0, fs.writeFileSync)(filePath, synced.content);
|
|
3113
|
-
updated.push(filePath);
|
|
3114
|
-
});
|
|
3742
|
+
const modelFiles = (0, fs.readdirSync)(modelsDir).filter((file) => file.endsWith(".ts")).map((file) => (0, path.join)(modelsDir, file));
|
|
3743
|
+
const result = this.syncModelFiles(modelFiles, (filePath, source) => {
|
|
3744
|
+
const parsed = this.parseModelSyncSource(source);
|
|
3745
|
+
if (!parsed) return void 0;
|
|
3746
|
+
return prismaModels.find((model) => model.table === parsed.table) ?? prismaModels.find((model) => model.name === parsed.className);
|
|
3747
|
+
}, prismaEnums);
|
|
3115
3748
|
return {
|
|
3116
3749
|
schemaPath,
|
|
3117
3750
|
modelsDir,
|
|
3118
3751
|
total: modelFiles.length,
|
|
3119
|
-
updated,
|
|
3120
|
-
skipped
|
|
3752
|
+
updated: result.updated,
|
|
3753
|
+
skipped: result.skipped
|
|
3121
3754
|
};
|
|
3122
3755
|
}
|
|
3123
3756
|
};
|
|
@@ -3695,20 +4328,21 @@ var MigrationHistoryCommand = class extends _h3ravel_musket.Command {
|
|
|
3695
4328
|
//#region src/cli/commands/ModelsSyncCommand.ts
|
|
3696
4329
|
var ModelsSyncCommand = class extends _h3ravel_musket.Command {
|
|
3697
4330
|
signature = `models:sync
|
|
3698
|
-
{--schema= : Path to prisma schema file}
|
|
4331
|
+
{--schema= : Path to prisma schema file used when adapter introspection is unavailable}
|
|
3699
4332
|
{--models= : Path to models directory}
|
|
3700
4333
|
`;
|
|
3701
|
-
description = "Sync model declare attributes from
|
|
4334
|
+
description = "Sync model declare attributes from the active adapter when supported, otherwise fall back to the Prisma schema";
|
|
3702
4335
|
async handle() {
|
|
3703
4336
|
this.app.command = this;
|
|
3704
|
-
const result = this.app.
|
|
4337
|
+
const result = await this.app.syncModels({
|
|
3705
4338
|
schemaPath: this.option("schema") ? (0, node_path.resolve)(String(this.option("schema"))) : void 0,
|
|
3706
4339
|
modelsDir: this.option("models") ? (0, node_path.resolve)(String(this.option("models"))) : void 0
|
|
3707
4340
|
});
|
|
3708
4341
|
const updatedLines = result.updated.length === 0 ? [this.app.splitLogger("Updated", "none")] : result.updated.map((path) => this.app.splitLogger("Updated", path));
|
|
3709
4342
|
this.success("SUCCESS: Model sync completed with the following results:");
|
|
3710
4343
|
[
|
|
3711
|
-
this.app.splitLogger("
|
|
4344
|
+
this.app.splitLogger("Source", result.source === "adapter" ? "adapter introspection" : "prisma schema"),
|
|
4345
|
+
...result.schemaPath ? [this.app.splitLogger("Schema", result.schemaPath)] : [],
|
|
3712
4346
|
this.app.splitLogger("Models", result.modelsDir),
|
|
3713
4347
|
this.app.splitLogger("Processed", String(result.total)),
|
|
3714
4348
|
...updatedLines,
|
|
@@ -4068,6 +4702,13 @@ var UniqueConstraintResolutionException = class extends ArkormException {
|
|
|
4068
4702
|
|
|
4069
4703
|
//#endregion
|
|
4070
4704
|
//#region src/relationship/RelationTableLoader.ts
|
|
4705
|
+
/**
|
|
4706
|
+
* Utility class responsible for loading data from relation tables, which are used to
|
|
4707
|
+
* manage relationships between models in Arkorm.
|
|
4708
|
+
*
|
|
4709
|
+
* @author Legacy (3m1n3nc3)
|
|
4710
|
+
* @since 2.0.0-next.0
|
|
4711
|
+
*/
|
|
4071
4712
|
var RelationTableLoader = class {
|
|
4072
4713
|
constructor(adapter) {
|
|
4073
4714
|
this.adapter = adapter;
|
|
@@ -4829,6 +5470,472 @@ var MorphToManyRelation = class extends Relation {
|
|
|
4829
5470
|
}
|
|
4830
5471
|
};
|
|
4831
5472
|
|
|
5473
|
+
//#endregion
|
|
5474
|
+
//#region src/relationship/SetBasedEagerLoader.ts
|
|
5475
|
+
/**
|
|
5476
|
+
* Utility class responsible for performing set-based eager loading of relationships for
|
|
5477
|
+
* a collection of models.
|
|
5478
|
+
*
|
|
5479
|
+
* @author Legacy (3m1n3nc3)
|
|
5480
|
+
* @since 2.0.0-next.2
|
|
5481
|
+
*/
|
|
5482
|
+
var SetBasedEagerLoader = class {
|
|
5483
|
+
constructor(models, relations) {
|
|
5484
|
+
this.models = models;
|
|
5485
|
+
this.relations = relations;
|
|
5486
|
+
}
|
|
5487
|
+
/**
|
|
5488
|
+
* Performs eager loading of all specified relationships for the set of models.
|
|
5489
|
+
*
|
|
5490
|
+
* @returns
|
|
5491
|
+
*/
|
|
5492
|
+
async load() {
|
|
5493
|
+
if (this.models.length === 0) return;
|
|
5494
|
+
await Promise.all(Object.entries(this.relations).map(async ([name, constraint]) => {
|
|
5495
|
+
await this.loadRelation(name, constraint);
|
|
5496
|
+
}));
|
|
5497
|
+
}
|
|
5498
|
+
/**
|
|
5499
|
+
* Loads a specific relationship for the set of models based on the relationship name
|
|
5500
|
+
* and an optional constraint.
|
|
5501
|
+
*
|
|
5502
|
+
* @param name The name of the relationship to load.
|
|
5503
|
+
* @param constraint An optional constraint to apply to the query.
|
|
5504
|
+
* @returns A promise that resolves when the relationship is loaded.
|
|
5505
|
+
*/
|
|
5506
|
+
async loadRelation(name, constraint) {
|
|
5507
|
+
const resolver = this.resolveRelationResolver(name);
|
|
5508
|
+
if (!resolver) return;
|
|
5509
|
+
const metadata = resolver.call(this.models[0]).getMetadata();
|
|
5510
|
+
switch (metadata.type) {
|
|
5511
|
+
case "belongsTo":
|
|
5512
|
+
await this.loadBelongsTo(name, resolver, metadata, constraint);
|
|
5513
|
+
return;
|
|
5514
|
+
case "belongsToMany":
|
|
5515
|
+
await this.loadBelongsToMany(name, metadata, constraint);
|
|
5516
|
+
return;
|
|
5517
|
+
case "hasMany":
|
|
5518
|
+
await this.loadHasMany(name, metadata, constraint);
|
|
5519
|
+
return;
|
|
5520
|
+
case "hasOne":
|
|
5521
|
+
await this.loadHasOne(name, resolver, metadata, constraint);
|
|
5522
|
+
return;
|
|
5523
|
+
case "hasManyThrough":
|
|
5524
|
+
await this.loadHasManyThrough(name, metadata, constraint);
|
|
5525
|
+
return;
|
|
5526
|
+
case "hasOneThrough":
|
|
5527
|
+
await this.loadHasOneThrough(name, resolver, metadata, constraint);
|
|
5528
|
+
return;
|
|
5529
|
+
default: await this.loadIndividually(name, resolver, constraint);
|
|
5530
|
+
}
|
|
5531
|
+
}
|
|
5532
|
+
/**
|
|
5533
|
+
* Resolves the relation resolver function for a given relationship name by inspecting
|
|
5534
|
+
* the first model in the set.
|
|
5535
|
+
*
|
|
5536
|
+
* @param name The name of the relationship to resolve.
|
|
5537
|
+
* @returns The relation resolver function or null if not found.
|
|
5538
|
+
*/
|
|
5539
|
+
resolveRelationResolver(name) {
|
|
5540
|
+
const resolver = this.models[0][name];
|
|
5541
|
+
if (typeof resolver !== "function") return null;
|
|
5542
|
+
return resolver;
|
|
5543
|
+
}
|
|
5544
|
+
/**
|
|
5545
|
+
* Loads a "belongs to" relationship for the set of models.
|
|
5546
|
+
*
|
|
5547
|
+
* @param name The name of the relationship to load.
|
|
5548
|
+
* @param resolver The relation resolver function.
|
|
5549
|
+
* @param metadata The metadata for the relationship.
|
|
5550
|
+
* @param constraint An optional constraint to apply to the query.
|
|
5551
|
+
* @returns A promise that resolves when the relationship is loaded.
|
|
5552
|
+
*/
|
|
5553
|
+
async loadBelongsTo(name, resolver, metadata, constraint) {
|
|
5554
|
+
const keys = this.collectUniqueKeys((model) => model.getAttribute(metadata.foreignKey));
|
|
5555
|
+
if (keys.length === 0) {
|
|
5556
|
+
this.models.forEach((model) => {
|
|
5557
|
+
model.setLoadedRelation(name, this.resolveSingleDefault(resolver, model));
|
|
5558
|
+
});
|
|
5559
|
+
return;
|
|
5560
|
+
}
|
|
5561
|
+
let query = metadata.relatedModel.query().whereIn(metadata.ownerKey, keys);
|
|
5562
|
+
query = this.applyConstraint(query, constraint);
|
|
5563
|
+
const relatedModels = (await query.get()).all();
|
|
5564
|
+
const relatedByOwnerKey = /* @__PURE__ */ new Map();
|
|
5565
|
+
relatedModels.forEach((related) => {
|
|
5566
|
+
const value = this.readModelAttribute(related, metadata.ownerKey);
|
|
5567
|
+
if (value == null) return;
|
|
5568
|
+
const lookupKey = this.toLookupKey(value);
|
|
5569
|
+
if (!relatedByOwnerKey.has(lookupKey)) relatedByOwnerKey.set(lookupKey, related);
|
|
5570
|
+
});
|
|
5571
|
+
this.models.forEach((model) => {
|
|
5572
|
+
const foreignValue = model.getAttribute(metadata.foreignKey);
|
|
5573
|
+
const relationValue = foreignValue == null ? void 0 : relatedByOwnerKey.get(this.toLookupKey(foreignValue));
|
|
5574
|
+
model.setLoadedRelation(name, relationValue ?? this.resolveSingleDefault(resolver, model));
|
|
5575
|
+
});
|
|
5576
|
+
}
|
|
5577
|
+
/**
|
|
5578
|
+
* Loads a "has many" relationship for the set of models.
|
|
5579
|
+
*
|
|
5580
|
+
* @param name
|
|
5581
|
+
* @param metadata
|
|
5582
|
+
* @param constraint
|
|
5583
|
+
* @returns
|
|
5584
|
+
*/
|
|
5585
|
+
async loadHasMany(name, metadata, constraint) {
|
|
5586
|
+
const keys = this.collectUniqueKeys((model) => model.getAttribute(metadata.localKey));
|
|
5587
|
+
if (keys.length === 0) {
|
|
5588
|
+
this.models.forEach((model) => {
|
|
5589
|
+
model.setLoadedRelation(name, new ArkormCollection([]));
|
|
5590
|
+
});
|
|
5591
|
+
return;
|
|
5592
|
+
}
|
|
5593
|
+
let query = metadata.relatedModel.query().whereIn(metadata.foreignKey, keys);
|
|
5594
|
+
query = this.applyConstraint(query, constraint);
|
|
5595
|
+
const relatedModels = (await query.get()).all();
|
|
5596
|
+
const relatedByForeignKey = /* @__PURE__ */ new Map();
|
|
5597
|
+
relatedModels.forEach((related) => {
|
|
5598
|
+
const value = this.readModelAttribute(related, metadata.foreignKey);
|
|
5599
|
+
if (value == null) return;
|
|
5600
|
+
const lookupKey = this.toLookupKey(value);
|
|
5601
|
+
const bucket = relatedByForeignKey.get(lookupKey) ?? [];
|
|
5602
|
+
bucket.push(related);
|
|
5603
|
+
relatedByForeignKey.set(lookupKey, bucket);
|
|
5604
|
+
});
|
|
5605
|
+
this.models.forEach((model) => {
|
|
5606
|
+
const localValue = model.getAttribute(metadata.localKey);
|
|
5607
|
+
const related = localValue == null ? [] : relatedByForeignKey.get(this.toLookupKey(localValue)) ?? [];
|
|
5608
|
+
model.setLoadedRelation(name, new ArkormCollection(related));
|
|
5609
|
+
});
|
|
5610
|
+
}
|
|
5611
|
+
/**
|
|
5612
|
+
* Loads a "belongs to many" relationship for the set of models.
|
|
5613
|
+
*
|
|
5614
|
+
* @param name
|
|
5615
|
+
* @param metadata
|
|
5616
|
+
* @param constraint
|
|
5617
|
+
* @returns
|
|
5618
|
+
*/
|
|
5619
|
+
async loadBelongsToMany(name, metadata, constraint) {
|
|
5620
|
+
const parentKeys = this.collectUniqueKeys((model) => model.getAttribute(metadata.parentKey));
|
|
5621
|
+
if (parentKeys.length === 0) {
|
|
5622
|
+
this.models.forEach((model) => {
|
|
5623
|
+
model.setLoadedRelation(name, new ArkormCollection([]));
|
|
5624
|
+
});
|
|
5625
|
+
return;
|
|
5626
|
+
}
|
|
5627
|
+
const pivotRows = await this.createRelationTableLoader().selectRows({
|
|
5628
|
+
table: metadata.throughTable,
|
|
5629
|
+
where: {
|
|
5630
|
+
type: "comparison",
|
|
5631
|
+
column: metadata.foreignPivotKey,
|
|
5632
|
+
operator: "in",
|
|
5633
|
+
value: parentKeys
|
|
5634
|
+
}
|
|
5635
|
+
});
|
|
5636
|
+
const relatedIds = this.collectUniqueRowValues(pivotRows, metadata.relatedPivotKey);
|
|
5637
|
+
if (relatedIds.length === 0) {
|
|
5638
|
+
this.models.forEach((model) => {
|
|
5639
|
+
model.setLoadedRelation(name, new ArkormCollection([]));
|
|
5640
|
+
});
|
|
5641
|
+
return;
|
|
5642
|
+
}
|
|
5643
|
+
let query = metadata.relatedModel.query().whereIn(metadata.relatedKey, relatedIds);
|
|
5644
|
+
query = this.applyConstraint(query, constraint);
|
|
5645
|
+
const relatedModels = (await query.get()).all();
|
|
5646
|
+
const relatedByKey = /* @__PURE__ */ new Map();
|
|
5647
|
+
relatedModels.forEach((related) => {
|
|
5648
|
+
const relatedValue = this.readModelAttribute(related, metadata.relatedKey);
|
|
5649
|
+
if (relatedValue == null) return;
|
|
5650
|
+
relatedByKey.set(this.toLookupKey(relatedValue), related);
|
|
5651
|
+
});
|
|
5652
|
+
const relatedKeysByParent = /* @__PURE__ */ new Map();
|
|
5653
|
+
pivotRows.forEach((row) => {
|
|
5654
|
+
const parentValue = row[metadata.foreignPivotKey];
|
|
5655
|
+
const relatedValue = row[metadata.relatedPivotKey];
|
|
5656
|
+
if (parentValue == null || relatedValue == null) return;
|
|
5657
|
+
const bucket = relatedKeysByParent.get(this.toLookupKey(parentValue)) ?? [];
|
|
5658
|
+
bucket.push(relatedValue);
|
|
5659
|
+
relatedKeysByParent.set(this.toLookupKey(parentValue), bucket);
|
|
5660
|
+
});
|
|
5661
|
+
this.models.forEach((model) => {
|
|
5662
|
+
const parentValue = model.getAttribute(metadata.parentKey);
|
|
5663
|
+
const related = (parentValue == null ? [] : relatedKeysByParent.get(this.toLookupKey(parentValue)) ?? []).reduce((all, relatedValue) => {
|
|
5664
|
+
const candidate = relatedByKey.get(this.toLookupKey(relatedValue));
|
|
5665
|
+
if (candidate) all.push(candidate);
|
|
5666
|
+
return all;
|
|
5667
|
+
}, []);
|
|
5668
|
+
model.setLoadedRelation(name, new ArkormCollection(related));
|
|
5669
|
+
});
|
|
5670
|
+
}
|
|
5671
|
+
/**
|
|
5672
|
+
* Loads a "belongs to many" relationship for the set of models.
|
|
5673
|
+
*
|
|
5674
|
+
* @param name
|
|
5675
|
+
* @param resolver
|
|
5676
|
+
* @param metadata
|
|
5677
|
+
* @param constraint
|
|
5678
|
+
* @returns
|
|
5679
|
+
*/
|
|
5680
|
+
async loadHasOne(name, resolver, metadata, constraint) {
|
|
5681
|
+
const keys = this.collectUniqueKeys((model) => model.getAttribute(metadata.localKey));
|
|
5682
|
+
if (keys.length === 0) {
|
|
5683
|
+
this.models.forEach((model) => {
|
|
5684
|
+
model.setLoadedRelation(name, this.resolveSingleDefault(resolver, model));
|
|
5685
|
+
});
|
|
5686
|
+
return;
|
|
5687
|
+
}
|
|
5688
|
+
let query = metadata.relatedModel.query().whereIn(metadata.foreignKey, keys);
|
|
5689
|
+
query = this.applyConstraint(query, constraint);
|
|
5690
|
+
const relatedModels = (await query.get()).all();
|
|
5691
|
+
const relatedByForeignKey = /* @__PURE__ */ new Map();
|
|
5692
|
+
relatedModels.forEach((related) => {
|
|
5693
|
+
const value = this.readModelAttribute(related, metadata.foreignKey);
|
|
5694
|
+
if (value == null) return;
|
|
5695
|
+
const lookupKey = this.toLookupKey(value);
|
|
5696
|
+
if (!relatedByForeignKey.has(lookupKey)) relatedByForeignKey.set(lookupKey, related);
|
|
5697
|
+
});
|
|
5698
|
+
this.models.forEach((model) => {
|
|
5699
|
+
const localValue = model.getAttribute(metadata.localKey);
|
|
5700
|
+
const relationValue = localValue == null ? void 0 : relatedByForeignKey.get(this.toLookupKey(localValue));
|
|
5701
|
+
model.setLoadedRelation(name, relationValue ?? this.resolveSingleDefault(resolver, model));
|
|
5702
|
+
});
|
|
5703
|
+
}
|
|
5704
|
+
/**
|
|
5705
|
+
* Loads a "has many through" relationship for the set of models.
|
|
5706
|
+
*
|
|
5707
|
+
* @param name
|
|
5708
|
+
* @param metadata
|
|
5709
|
+
* @param constraint
|
|
5710
|
+
* @returns
|
|
5711
|
+
*/
|
|
5712
|
+
async loadHasManyThrough(name, metadata, constraint) {
|
|
5713
|
+
const parentKeys = this.collectUniqueKeys((model) => model.getAttribute(metadata.localKey));
|
|
5714
|
+
if (parentKeys.length === 0) {
|
|
5715
|
+
this.models.forEach((model) => {
|
|
5716
|
+
model.setLoadedRelation(name, new ArkormCollection([]));
|
|
5717
|
+
});
|
|
5718
|
+
return;
|
|
5719
|
+
}
|
|
5720
|
+
const throughRows = await this.createRelationTableLoader().selectRows({
|
|
5721
|
+
table: metadata.throughTable,
|
|
5722
|
+
where: {
|
|
5723
|
+
type: "comparison",
|
|
5724
|
+
column: metadata.firstKey,
|
|
5725
|
+
operator: "in",
|
|
5726
|
+
value: parentKeys
|
|
5727
|
+
}
|
|
5728
|
+
});
|
|
5729
|
+
const intermediateKeys = this.collectUniqueRowValues(throughRows, metadata.secondLocalKey);
|
|
5730
|
+
if (intermediateKeys.length === 0) {
|
|
5731
|
+
this.models.forEach((model) => {
|
|
5732
|
+
model.setLoadedRelation(name, new ArkormCollection([]));
|
|
5733
|
+
});
|
|
5734
|
+
return;
|
|
5735
|
+
}
|
|
5736
|
+
let query = metadata.relatedModel.query().whereIn(metadata.secondKey, intermediateKeys);
|
|
5737
|
+
query = this.applyConstraint(query, constraint);
|
|
5738
|
+
const relatedModels = (await query.get()).all();
|
|
5739
|
+
const relatedByIntermediate = /* @__PURE__ */ new Map();
|
|
5740
|
+
relatedModels.forEach((related) => {
|
|
5741
|
+
const relatedValue = this.readModelAttribute(related, metadata.secondKey);
|
|
5742
|
+
if (relatedValue == null) return;
|
|
5743
|
+
const bucket = relatedByIntermediate.get(this.toLookupKey(relatedValue)) ?? [];
|
|
5744
|
+
bucket.push(related);
|
|
5745
|
+
relatedByIntermediate.set(this.toLookupKey(relatedValue), bucket);
|
|
5746
|
+
});
|
|
5747
|
+
const intermediateByParent = /* @__PURE__ */ new Map();
|
|
5748
|
+
throughRows.forEach((row) => {
|
|
5749
|
+
const parentValue = row[metadata.firstKey];
|
|
5750
|
+
const intermediateValue = row[metadata.secondLocalKey];
|
|
5751
|
+
if (parentValue == null || intermediateValue == null) return;
|
|
5752
|
+
const bucket = intermediateByParent.get(this.toLookupKey(parentValue)) ?? [];
|
|
5753
|
+
bucket.push(intermediateValue);
|
|
5754
|
+
intermediateByParent.set(this.toLookupKey(parentValue), bucket);
|
|
5755
|
+
});
|
|
5756
|
+
this.models.forEach((model) => {
|
|
5757
|
+
const parentValue = model.getAttribute(metadata.localKey);
|
|
5758
|
+
const related = (parentValue == null ? [] : intermediateByParent.get(this.toLookupKey(parentValue)) ?? []).flatMap((intermediateValue) => relatedByIntermediate.get(this.toLookupKey(intermediateValue)) ?? []);
|
|
5759
|
+
model.setLoadedRelation(name, new ArkormCollection(related));
|
|
5760
|
+
});
|
|
5761
|
+
}
|
|
5762
|
+
/**
|
|
5763
|
+
* Loads a "has one through" relationship for the set of models.
|
|
5764
|
+
*
|
|
5765
|
+
* @param name
|
|
5766
|
+
* @param resolver
|
|
5767
|
+
* @param metadata
|
|
5768
|
+
* @param constraint
|
|
5769
|
+
* @returns
|
|
5770
|
+
*/
|
|
5771
|
+
async loadHasOneThrough(name, resolver, metadata, constraint) {
|
|
5772
|
+
const parentKeys = this.collectUniqueKeys((model) => model.getAttribute(metadata.localKey));
|
|
5773
|
+
if (parentKeys.length === 0) {
|
|
5774
|
+
this.models.forEach((model) => {
|
|
5775
|
+
model.setLoadedRelation(name, this.resolveSingleDefault(resolver, model));
|
|
5776
|
+
});
|
|
5777
|
+
return;
|
|
5778
|
+
}
|
|
5779
|
+
const throughRows = await this.createRelationTableLoader().selectRows({
|
|
5780
|
+
table: metadata.throughTable,
|
|
5781
|
+
where: {
|
|
5782
|
+
type: "comparison",
|
|
5783
|
+
column: metadata.firstKey,
|
|
5784
|
+
operator: "in",
|
|
5785
|
+
value: parentKeys
|
|
5786
|
+
}
|
|
5787
|
+
});
|
|
5788
|
+
const intermediateKeys = this.collectUniqueRowValues(throughRows, metadata.secondLocalKey);
|
|
5789
|
+
if (intermediateKeys.length === 0) {
|
|
5790
|
+
this.models.forEach((model) => {
|
|
5791
|
+
model.setLoadedRelation(name, this.resolveSingleDefault(resolver, model));
|
|
5792
|
+
});
|
|
5793
|
+
return;
|
|
5794
|
+
}
|
|
5795
|
+
let query = metadata.relatedModel.query().whereIn(metadata.secondKey, intermediateKeys);
|
|
5796
|
+
query = this.applyConstraint(query, constraint);
|
|
5797
|
+
const relatedModels = (await query.get()).all();
|
|
5798
|
+
const relatedByIntermediate = /* @__PURE__ */ new Map();
|
|
5799
|
+
relatedModels.forEach((related) => {
|
|
5800
|
+
const relatedValue = this.readModelAttribute(related, metadata.secondKey);
|
|
5801
|
+
if (relatedValue == null) return;
|
|
5802
|
+
const lookupKey = this.toLookupKey(relatedValue);
|
|
5803
|
+
if (!relatedByIntermediate.has(lookupKey)) relatedByIntermediate.set(lookupKey, related);
|
|
5804
|
+
});
|
|
5805
|
+
const intermediateByParent = /* @__PURE__ */ new Map();
|
|
5806
|
+
throughRows.forEach((row) => {
|
|
5807
|
+
const parentValue = row[metadata.firstKey];
|
|
5808
|
+
const intermediateValue = row[metadata.secondLocalKey];
|
|
5809
|
+
if (parentValue == null || intermediateValue == null) return;
|
|
5810
|
+
const lookupKey = this.toLookupKey(parentValue);
|
|
5811
|
+
if (!intermediateByParent.has(lookupKey)) intermediateByParent.set(lookupKey, intermediateValue);
|
|
5812
|
+
});
|
|
5813
|
+
this.models.forEach((model) => {
|
|
5814
|
+
const parentValue = model.getAttribute(metadata.localKey);
|
|
5815
|
+
const intermediateValue = parentValue == null ? void 0 : intermediateByParent.get(this.toLookupKey(parentValue));
|
|
5816
|
+
const relationValue = intermediateValue == null ? void 0 : relatedByIntermediate.get(this.toLookupKey(intermediateValue));
|
|
5817
|
+
model.setLoadedRelation(name, relationValue ?? this.resolveSingleDefault(resolver, model));
|
|
5818
|
+
});
|
|
5819
|
+
}
|
|
5820
|
+
/**
|
|
5821
|
+
* Fallback method to load relationships individually for each model when the
|
|
5822
|
+
* relationship type is not supported for set-based loading.
|
|
5823
|
+
*
|
|
5824
|
+
* @param name
|
|
5825
|
+
* @param resolver
|
|
5826
|
+
* @param constraint
|
|
5827
|
+
*/
|
|
5828
|
+
async loadIndividually(name, resolver, constraint) {
|
|
5829
|
+
await Promise.all(this.models.map(async (model) => {
|
|
5830
|
+
const relation = resolver.call(model);
|
|
5831
|
+
if (constraint) relation.constrain(constraint);
|
|
5832
|
+
model.setLoadedRelation(name, await relation.getResults());
|
|
5833
|
+
}));
|
|
5834
|
+
}
|
|
5835
|
+
/**
|
|
5836
|
+
* Applies an eager load constraint to a query if provided.
|
|
5837
|
+
*
|
|
5838
|
+
* @param query
|
|
5839
|
+
* @param constraint
|
|
5840
|
+
* @returns
|
|
5841
|
+
*/
|
|
5842
|
+
applyConstraint(query, constraint) {
|
|
5843
|
+
if (!constraint) return query;
|
|
5844
|
+
return constraint(query) ?? query;
|
|
5845
|
+
}
|
|
5846
|
+
/**
|
|
5847
|
+
* Collects unique values from the set of models based on a resolver function, which
|
|
5848
|
+
* is used to extract the value from each model.
|
|
5849
|
+
*
|
|
5850
|
+
* @param resolve A function that takes a model and returns the value to be collected.
|
|
5851
|
+
* @returns An array of unique values.
|
|
5852
|
+
*/
|
|
5853
|
+
collectUniqueKeys(resolve) {
|
|
5854
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5855
|
+
const values = [];
|
|
5856
|
+
this.models.forEach((model) => {
|
|
5857
|
+
const value = resolve(model);
|
|
5858
|
+
if (value == null) return;
|
|
5859
|
+
const lookupKey = this.toLookupKey(value);
|
|
5860
|
+
if (seen.has(lookupKey)) return;
|
|
5861
|
+
seen.add(lookupKey);
|
|
5862
|
+
values.push(value);
|
|
5863
|
+
});
|
|
5864
|
+
return values;
|
|
5865
|
+
}
|
|
5866
|
+
/**
|
|
5867
|
+
* Collects unique values from an array of database rows based on a specified key, which
|
|
5868
|
+
* is used to extract the value from each row.
|
|
5869
|
+
*
|
|
5870
|
+
* @param rows An array of database rows.
|
|
5871
|
+
* @param key The key to extract values from each row.
|
|
5872
|
+
* @returns An array of unique values.
|
|
5873
|
+
*/
|
|
5874
|
+
collectUniqueRowValues(rows, key) {
|
|
5875
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5876
|
+
const values = [];
|
|
5877
|
+
rows.forEach((row) => {
|
|
5878
|
+
const value = row[key];
|
|
5879
|
+
if (value == null) return;
|
|
5880
|
+
const lookupKey = this.toLookupKey(value);
|
|
5881
|
+
if (seen.has(lookupKey)) return;
|
|
5882
|
+
seen.add(lookupKey);
|
|
5883
|
+
values.push(value);
|
|
5884
|
+
});
|
|
5885
|
+
return values;
|
|
5886
|
+
}
|
|
5887
|
+
/**
|
|
5888
|
+
* Loads a "belongs to many" relationship for the set of models.
|
|
5889
|
+
*
|
|
5890
|
+
* @returns
|
|
5891
|
+
*/
|
|
5892
|
+
createRelationTableLoader() {
|
|
5893
|
+
return new RelationTableLoader(this.resolveAdapter());
|
|
5894
|
+
}
|
|
5895
|
+
/**
|
|
5896
|
+
* Loads a "belongs to many" relationship for the set of models.
|
|
5897
|
+
*
|
|
5898
|
+
* @returns
|
|
5899
|
+
*/
|
|
5900
|
+
resolveAdapter() {
|
|
5901
|
+
const adapter = this.models[0].constructor.getAdapter?.();
|
|
5902
|
+
if (!adapter) throw new Error("Set-based eager loading requires a configured adapter.");
|
|
5903
|
+
return adapter;
|
|
5904
|
+
}
|
|
5905
|
+
/**
|
|
5906
|
+
* Reads an attribute value from a model using the getAttribute method, which is used
|
|
5907
|
+
* to access model attributes in a way that is compatible with Arkorm's internal model structure.
|
|
5908
|
+
*
|
|
5909
|
+
* @param model The model to read the attribute from.
|
|
5910
|
+
* @param key The name of the attribute to read.
|
|
5911
|
+
* @returns
|
|
5912
|
+
*/
|
|
5913
|
+
readModelAttribute(model, key) {
|
|
5914
|
+
return model.getAttribute?.(key);
|
|
5915
|
+
}
|
|
5916
|
+
/**
|
|
5917
|
+
* Resolves the default result for a relationship when no related models are found.
|
|
5918
|
+
*
|
|
5919
|
+
* @param resolver
|
|
5920
|
+
* @param model
|
|
5921
|
+
* @returns
|
|
5922
|
+
*/
|
|
5923
|
+
resolveSingleDefault(resolver, model) {
|
|
5924
|
+
return resolver.call(model).resolveDefaultResult?.() ?? null;
|
|
5925
|
+
}
|
|
5926
|
+
/**
|
|
5927
|
+
* Generates a unique lookup key for a given value, which is used to store and retrieve
|
|
5928
|
+
* values in maps during the eager loading process.
|
|
5929
|
+
*
|
|
5930
|
+
* @param value The value to generate a lookup key for.
|
|
5931
|
+
* @returns A unique string representing the value.
|
|
5932
|
+
*/
|
|
5933
|
+
toLookupKey(value) {
|
|
5934
|
+
if (value instanceof Date) return `date:${value.toISOString()}`;
|
|
5935
|
+
return `${typeof value}:${String(value)}`;
|
|
5936
|
+
}
|
|
5937
|
+
};
|
|
5938
|
+
|
|
4832
5939
|
//#endregion
|
|
4833
5940
|
//#region src/URLDriver.ts
|
|
4834
5941
|
/**
|
|
@@ -5727,21 +6834,20 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5727
6834
|
* @returns
|
|
5728
6835
|
*/
|
|
5729
6836
|
async get() {
|
|
6837
|
+
const useAdapterRelationFeatures = this.canExecuteRelationFeaturesInAdapter();
|
|
5730
6838
|
const relationCache = /* @__PURE__ */ new WeakMap();
|
|
5731
6839
|
const rows = await this.executeReadRows();
|
|
5732
6840
|
const normalizedRows = this.randomOrderEnabled ? this.shuffleRows(rows) : rows;
|
|
5733
6841
|
const models = await this.model.hydrateManyRetrieved(normalizedRows);
|
|
5734
6842
|
let filteredModels = models;
|
|
5735
|
-
if (this.hasRelationFilters()) if (this.hasOrRelationFilters() && this.hasBaseWhereConstraints()) {
|
|
6843
|
+
if (this.hasRelationFilters() && !useAdapterRelationFeatures) if (this.hasOrRelationFilters() && this.hasBaseWhereConstraints()) {
|
|
5736
6844
|
const baseIds = new Set(models.map((model) => this.getModelId(model)).filter((id) => id != null));
|
|
5737
6845
|
const allRows = await this.executeReadRows(this.buildSoftDeleteOnlyWhere(), true);
|
|
5738
6846
|
const allModels = this.model.hydrateMany(allRows);
|
|
5739
6847
|
filteredModels = await this.filterModelsByRelationConstraints(allModels, relationCache, baseIds);
|
|
5740
6848
|
} else filteredModels = await this.filterModelsByRelationConstraints(models, relationCache);
|
|
5741
|
-
if (this.hasRelationAggregates()) await this.applyRelationAggregates(filteredModels, relationCache);
|
|
5742
|
-
await
|
|
5743
|
-
await model.load(this.eagerLoads);
|
|
5744
|
-
}));
|
|
6849
|
+
if (this.hasRelationAggregates() && !useAdapterRelationFeatures) await this.applyRelationAggregates(filteredModels, relationCache);
|
|
6850
|
+
await this.eagerLoadModels(filteredModels);
|
|
5745
6851
|
return new ArkormCollection(filteredModels);
|
|
5746
6852
|
}
|
|
5747
6853
|
/**
|
|
@@ -5751,20 +6857,20 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5751
6857
|
* @returns
|
|
5752
6858
|
*/
|
|
5753
6859
|
async first() {
|
|
5754
|
-
if (this.hasRelationFilters() || this.hasRelationAggregates()) return (await this.get()).all()[0] ?? null;
|
|
6860
|
+
if ((this.hasRelationFilters() || this.hasRelationAggregates()) && !this.canExecuteRelationFeaturesInAdapter()) return (await this.get()).all()[0] ?? null;
|
|
5755
6861
|
if (this.randomOrderEnabled) {
|
|
5756
6862
|
const rows = await this.executeReadRows();
|
|
5757
6863
|
if (rows.length === 0) return null;
|
|
5758
6864
|
const row = this.shuffleRows(rows)[0];
|
|
5759
6865
|
if (!row) return null;
|
|
5760
6866
|
const model = await this.model.hydrateRetrieved(row);
|
|
5761
|
-
await
|
|
6867
|
+
await this.eagerLoadModels([model]);
|
|
5762
6868
|
return model;
|
|
5763
6869
|
}
|
|
5764
6870
|
const row = await this.executeReadRow();
|
|
5765
6871
|
if (!row) return null;
|
|
5766
6872
|
const model = await this.model.hydrateRetrieved(row);
|
|
5767
|
-
await
|
|
6873
|
+
await this.eagerLoadModels([model]);
|
|
5768
6874
|
return model;
|
|
5769
6875
|
}
|
|
5770
6876
|
/**
|
|
@@ -5928,6 +7034,13 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5928
7034
|
operation: "update",
|
|
5929
7035
|
model: this.model.name
|
|
5930
7036
|
});
|
|
7037
|
+
const directSpec = this.tryBuildUpdateSpec(where, data);
|
|
7038
|
+
const adapter = this.requireAdapter();
|
|
7039
|
+
if (!this.isUniqueWhere(where) && directSpec && typeof adapter.updateFirst === "function") {
|
|
7040
|
+
const updated = await adapter.updateFirst(directSpec);
|
|
7041
|
+
if (!updated) throw new ModelNotFoundException(this.model.name, "Record not found for update operation.", { operation: "update" });
|
|
7042
|
+
return this.model.hydrate(updated);
|
|
7043
|
+
}
|
|
5931
7044
|
const uniqueWhere = await this.resolveUniqueWhere(where);
|
|
5932
7045
|
const updated = await this.executeUpdateRow(uniqueWhere, data);
|
|
5933
7046
|
return this.model.hydrate(updated);
|
|
@@ -5954,6 +7067,13 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5954
7067
|
* @returns
|
|
5955
7068
|
*/
|
|
5956
7069
|
async updateOrInsert(attributes, values = {}) {
|
|
7070
|
+
if (typeof values !== "function" && this.adapter?.capabilities?.upsert && typeof this.requireAdapter().upsert === "function") {
|
|
7071
|
+
await this.executeUpsertRows([{
|
|
7072
|
+
...attributes,
|
|
7073
|
+
...values
|
|
7074
|
+
}], Object.keys(attributes), Object.keys(values));
|
|
7075
|
+
return true;
|
|
7076
|
+
}
|
|
5957
7077
|
const exists = await this.clone().where(attributes).first() != null;
|
|
5958
7078
|
const resolvedValues = typeof values === "function" ? await values(exists) : values;
|
|
5959
7079
|
if (!exists) {
|
|
@@ -5976,6 +7096,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
5976
7096
|
async upsert(values, uniqueBy, update = null) {
|
|
5977
7097
|
if (values.length === 0) return 0;
|
|
5978
7098
|
const uniqueKeys = Array.isArray(uniqueBy) ? uniqueBy : [uniqueBy];
|
|
7099
|
+
if (this.adapter?.capabilities?.upsert && typeof this.requireAdapter().upsert === "function") return await this.executeUpsertRows(values, uniqueKeys, update ?? void 0);
|
|
5979
7100
|
let affected = 0;
|
|
5980
7101
|
for (const row of values) {
|
|
5981
7102
|
const attributes = uniqueKeys.reduce((all, key) => {
|
|
@@ -6003,6 +7124,13 @@ var QueryBuilder = class QueryBuilder {
|
|
|
6003
7124
|
operation: "delete",
|
|
6004
7125
|
model: this.model.name
|
|
6005
7126
|
});
|
|
7127
|
+
const directSpec = this.tryBuildDeleteSpec(where);
|
|
7128
|
+
const adapter = this.requireAdapter();
|
|
7129
|
+
if (!this.isUniqueWhere(where) && directSpec && typeof adapter.deleteFirst === "function") {
|
|
7130
|
+
const deleted = await adapter.deleteFirst(directSpec);
|
|
7131
|
+
if (!deleted) throw new ModelNotFoundException(this.model.name, "Record not found for delete operation.", { operation: "delete" });
|
|
7132
|
+
return this.model.hydrate(deleted);
|
|
7133
|
+
}
|
|
6006
7134
|
const uniqueWhere = await this.resolveUniqueWhere(where);
|
|
6007
7135
|
const deleted = await this.executeDeleteRow(uniqueWhere);
|
|
6008
7136
|
return this.model.hydrate(deleted);
|
|
@@ -6019,6 +7147,14 @@ var QueryBuilder = class QueryBuilder {
|
|
|
6019
7147
|
values
|
|
6020
7148
|
};
|
|
6021
7149
|
}
|
|
7150
|
+
tryBuildUpsertSpec(values, uniqueBy, updateColumns) {
|
|
7151
|
+
return {
|
|
7152
|
+
target: this.buildQueryTarget(),
|
|
7153
|
+
values,
|
|
7154
|
+
uniqueBy,
|
|
7155
|
+
updateColumns
|
|
7156
|
+
};
|
|
7157
|
+
}
|
|
6022
7158
|
tryBuildInsertOrIgnoreManySpec(values) {
|
|
6023
7159
|
return {
|
|
6024
7160
|
...this.tryBuildInsertManySpec(values),
|
|
@@ -6057,7 +7193,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
6057
7193
|
* @returns
|
|
6058
7194
|
*/
|
|
6059
7195
|
async count() {
|
|
6060
|
-
if (this.hasRelationFilters()) return (await this.get()).all().length;
|
|
7196
|
+
if (this.hasRelationFilters() && !this.canExecuteRelationFeaturesInAdapter()) return (await this.get()).all().length;
|
|
6061
7197
|
return this.executeReadCount();
|
|
6062
7198
|
}
|
|
6063
7199
|
/**
|
|
@@ -6066,7 +7202,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
6066
7202
|
* @returns
|
|
6067
7203
|
*/
|
|
6068
7204
|
async exists() {
|
|
6069
|
-
if (this.hasRelationFilters()) return await this.count() > 0;
|
|
7205
|
+
if (this.hasRelationFilters() && !this.canExecuteRelationFeaturesInAdapter()) return await this.count() > 0;
|
|
6070
7206
|
return await this.executeReadExists();
|
|
6071
7207
|
}
|
|
6072
7208
|
/**
|
|
@@ -6241,7 +7377,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
6241
7377
|
*/
|
|
6242
7378
|
async paginate(perPage = 15, page = void 0, options = {}) {
|
|
6243
7379
|
const currentPage = this.resolvePaginationPage(page, options);
|
|
6244
|
-
if (this.hasRelationFilters() || this.hasRelationAggregates()) {
|
|
7380
|
+
if ((this.hasRelationFilters() || this.hasRelationAggregates()) && !this.canExecuteRelationFeaturesInAdapter()) {
|
|
6245
7381
|
const pageSize = Math.max(1, perPage);
|
|
6246
7382
|
const rows = (await this.get()).all();
|
|
6247
7383
|
const start = (currentPage - 1) * pageSize;
|
|
@@ -6260,7 +7396,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
6260
7396
|
*/
|
|
6261
7397
|
async simplePaginate(perPage = 15, page = void 0, options = {}) {
|
|
6262
7398
|
const currentPage = this.resolvePaginationPage(page, options);
|
|
6263
|
-
if (this.hasRelationFilters() || this.hasRelationAggregates()) {
|
|
7399
|
+
if ((this.hasRelationFilters() || this.hasRelationAggregates()) && !this.canExecuteRelationFeaturesInAdapter()) {
|
|
6264
7400
|
const pageSize = Math.max(1, perPage);
|
|
6265
7401
|
const rows = (await this.get()).all();
|
|
6266
7402
|
const start = (currentPage - 1) * pageSize;
|
|
@@ -6364,6 +7500,10 @@ var QueryBuilder = class QueryBuilder {
|
|
|
6364
7500
|
};
|
|
6365
7501
|
});
|
|
6366
7502
|
}
|
|
7503
|
+
async eagerLoadModels(models) {
|
|
7504
|
+
if (models.length === 0 || Object.keys(this.eagerLoads).length === 0) return;
|
|
7505
|
+
await new SetBasedEagerLoader(models, this.eagerLoads).load();
|
|
7506
|
+
}
|
|
6367
7507
|
normalizeRelationLoadSelect(select) {
|
|
6368
7508
|
if (Array.isArray(select) || typeof select !== "object" || !select) return null;
|
|
6369
7509
|
const entries = Object.entries(select);
|
|
@@ -6601,7 +7741,11 @@ var QueryBuilder = class QueryBuilder {
|
|
|
6601
7741
|
const columns = this.tryBuildQuerySelectColumns();
|
|
6602
7742
|
const orderBy = this.tryBuildQueryOrderBy();
|
|
6603
7743
|
const condition = this.buildQueryWhereCondition(softDeleteOnly);
|
|
7744
|
+
const relationFilters = this.tryBuildRelationFilterSpecs();
|
|
7745
|
+
const relationAggregates = this.tryBuildRelationAggregateSpecs();
|
|
6604
7746
|
if (columns === null || orderBy === null || condition === null) return null;
|
|
7747
|
+
if (this.hasRelationFilters() && this.canExecuteRelationFiltersInAdapter() && relationFilters === null) return null;
|
|
7748
|
+
if (this.hasRelationAggregates() && this.canExecuteRelationAggregatesInAdapter() && relationAggregates === null) return null;
|
|
6605
7749
|
return {
|
|
6606
7750
|
target: this.buildQueryTarget(),
|
|
6607
7751
|
columns,
|
|
@@ -6609,15 +7753,20 @@ var QueryBuilder = class QueryBuilder {
|
|
|
6609
7753
|
orderBy,
|
|
6610
7754
|
limit: this.limitValue,
|
|
6611
7755
|
offset: this.offsetValue,
|
|
6612
|
-
relationLoads: this.queryRelationLoads
|
|
7756
|
+
relationLoads: this.queryRelationLoads,
|
|
7757
|
+
relationFilters: this.canExecuteRelationFiltersInAdapter() ? relationFilters ?? void 0 : void 0,
|
|
7758
|
+
relationAggregates: this.canExecuteRelationAggregatesInAdapter() ? relationAggregates ?? void 0 : void 0
|
|
6613
7759
|
};
|
|
6614
7760
|
}
|
|
6615
7761
|
tryBuildAggregateSpec() {
|
|
6616
7762
|
const condition = this.buildQueryWhereCondition(false);
|
|
7763
|
+
const relationFilters = this.tryBuildRelationFilterSpecs();
|
|
6617
7764
|
if (condition === null) return null;
|
|
7765
|
+
if (this.hasRelationFilters() && this.canExecuteRelationFiltersInAdapter() && relationFilters === null) return null;
|
|
6618
7766
|
return {
|
|
6619
7767
|
target: this.buildQueryTarget(),
|
|
6620
7768
|
where: condition,
|
|
7769
|
+
relationFilters: this.canExecuteRelationFiltersInAdapter() ? relationFilters ?? void 0 : void 0,
|
|
6621
7770
|
aggregate: { type: "count" }
|
|
6622
7771
|
};
|
|
6623
7772
|
}
|
|
@@ -6687,6 +7836,14 @@ var QueryBuilder = class QueryBuilder {
|
|
|
6687
7836
|
}
|
|
6688
7837
|
return inserted;
|
|
6689
7838
|
}
|
|
7839
|
+
async executeUpsertRows(values, uniqueBy, updateColumns) {
|
|
7840
|
+
const adapter = this.requireAdapter();
|
|
7841
|
+
if (typeof adapter.upsert !== "function") throw new UnsupportedAdapterFeatureException("Upsert is not supported by the current adapter.", {
|
|
7842
|
+
operation: "query.upsert",
|
|
7843
|
+
model: this.model.name
|
|
7844
|
+
});
|
|
7845
|
+
return await adapter.upsert(this.tryBuildUpsertSpec(values, uniqueBy, updateColumns));
|
|
7846
|
+
}
|
|
6690
7847
|
async executeUpdateRow(where, values) {
|
|
6691
7848
|
const adapter = this.requireAdapter();
|
|
6692
7849
|
const spec = this.tryBuildUpdateSpec(where, values);
|
|
@@ -6815,6 +7972,71 @@ var QueryBuilder = class QueryBuilder {
|
|
|
6815
7972
|
hasRelationAggregates() {
|
|
6816
7973
|
return this.relationAggregates.length > 0;
|
|
6817
7974
|
}
|
|
7975
|
+
canExecuteRelationFiltersInAdapter() {
|
|
7976
|
+
const adapter = this.adapter;
|
|
7977
|
+
if (!this.hasRelationFilters()) return false;
|
|
7978
|
+
return adapter?.capabilities?.relationFilters === true && this.tryBuildRelationFilterSpecs() !== null;
|
|
7979
|
+
}
|
|
7980
|
+
canExecuteRelationAggregatesInAdapter() {
|
|
7981
|
+
const adapter = this.adapter;
|
|
7982
|
+
if (!this.hasRelationAggregates()) return false;
|
|
7983
|
+
return adapter?.capabilities?.relationAggregates === true && this.tryBuildRelationAggregateSpecs() !== null;
|
|
7984
|
+
}
|
|
7985
|
+
canExecuteRelationFeaturesInAdapter() {
|
|
7986
|
+
const filtersSupported = !this.hasRelationFilters() || this.canExecuteRelationFiltersInAdapter();
|
|
7987
|
+
const aggregatesSupported = !this.hasRelationAggregates() || this.canExecuteRelationAggregatesInAdapter();
|
|
7988
|
+
return filtersSupported && aggregatesSupported;
|
|
7989
|
+
}
|
|
7990
|
+
tryBuildRelationFilterSpecs() {
|
|
7991
|
+
return this.relationFilters.reduce((specs, filter) => {
|
|
7992
|
+
if (!specs) return null;
|
|
7993
|
+
const metadata = this.model.getRelationMetadata(filter.relation);
|
|
7994
|
+
if (!this.isSqlRelationFeatureMetadata(metadata)) return null;
|
|
7995
|
+
const where = this.tryBuildRelationConstraintWhere(filter.relation, filter.callback);
|
|
7996
|
+
if (where === null) return null;
|
|
7997
|
+
specs.push({
|
|
7998
|
+
relation: filter.relation,
|
|
7999
|
+
operator: filter.operator,
|
|
8000
|
+
count: filter.count,
|
|
8001
|
+
boolean: filter.boolean,
|
|
8002
|
+
where
|
|
8003
|
+
});
|
|
8004
|
+
return specs;
|
|
8005
|
+
}, []);
|
|
8006
|
+
}
|
|
8007
|
+
tryBuildRelationAggregateSpecs() {
|
|
8008
|
+
return this.relationAggregates.reduce((specs, aggregate) => {
|
|
8009
|
+
if (!specs) return null;
|
|
8010
|
+
const metadata = this.model.getRelationMetadata(aggregate.relation);
|
|
8011
|
+
if (!this.isSqlRelationFeatureMetadata(metadata)) return null;
|
|
8012
|
+
const where = this.tryBuildRelationConstraintWhere(aggregate.relation);
|
|
8013
|
+
if (where === null) return null;
|
|
8014
|
+
specs.push({
|
|
8015
|
+
relation: aggregate.relation,
|
|
8016
|
+
type: aggregate.type,
|
|
8017
|
+
column: aggregate.column,
|
|
8018
|
+
alias: this.buildAggregateAttributeKey(aggregate),
|
|
8019
|
+
where
|
|
8020
|
+
});
|
|
8021
|
+
return specs;
|
|
8022
|
+
}, []);
|
|
8023
|
+
}
|
|
8024
|
+
tryBuildRelationConstraintWhere(relation, callback) {
|
|
8025
|
+
const metadata = this.model.getRelationMetadata(relation);
|
|
8026
|
+
if (!this.isSqlRelationFeatureMetadata(metadata)) return null;
|
|
8027
|
+
const relatedQuery = metadata?.relatedModel.query();
|
|
8028
|
+
if (!relatedQuery) return null;
|
|
8029
|
+
if (callback) {
|
|
8030
|
+
const constrained = callback(relatedQuery);
|
|
8031
|
+
if (constrained && constrained !== relatedQuery) return null;
|
|
8032
|
+
}
|
|
8033
|
+
if (relatedQuery.hasRelationFilters() || relatedQuery.hasRelationAggregates() || relatedQuery.queryRelationLoads || relatedQuery.querySelect || relatedQuery.queryOrderBy || relatedQuery.offsetValue !== void 0 || relatedQuery.limitValue !== void 0 || relatedQuery.randomOrderEnabled) return null;
|
|
8034
|
+
return relatedQuery.buildQueryWhereCondition(false);
|
|
8035
|
+
}
|
|
8036
|
+
isSqlRelationFeatureMetadata(metadata) {
|
|
8037
|
+
if (!metadata) return false;
|
|
8038
|
+
return metadata.type === "hasMany" || metadata.type === "hasOne" || metadata.type === "belongsTo" || metadata.type === "belongsToMany" || metadata.type === "hasOneThrough" || metadata.type === "hasManyThrough";
|
|
8039
|
+
}
|
|
6818
8040
|
async filterModelsByRelationConstraints(models, relationCache, baseIds) {
|
|
6819
8041
|
return (await Promise.all(models.map(async (model) => {
|
|
6820
8042
|
let result = null;
|
|
@@ -6971,6 +8193,7 @@ var QueryBuilder = class QueryBuilder {
|
|
|
6971
8193
|
*/
|
|
6972
8194
|
var Model = class Model {
|
|
6973
8195
|
static lifecycleStates = /* @__PURE__ */ new WeakMap();
|
|
8196
|
+
static emittedDeprecationWarnings = /* @__PURE__ */ new Set();
|
|
6974
8197
|
static eventsSuppressed = 0;
|
|
6975
8198
|
static factoryClass;
|
|
6976
8199
|
static adapter;
|
|
@@ -7014,12 +8237,24 @@ var Model = class Model {
|
|
|
7014
8237
|
}
|
|
7015
8238
|
});
|
|
7016
8239
|
}
|
|
8240
|
+
static emitDeprecationWarning(code, message) {
|
|
8241
|
+
if (Model.emittedDeprecationWarnings.has(code)) return;
|
|
8242
|
+
Model.emittedDeprecationWarnings.add(code);
|
|
8243
|
+
process.emitWarning(message, {
|
|
8244
|
+
type: "DeprecationWarning",
|
|
8245
|
+
code
|
|
8246
|
+
});
|
|
8247
|
+
}
|
|
7017
8248
|
/**
|
|
7018
|
-
* Set the Prisma client delegates for all models.
|
|
7019
|
-
*
|
|
7020
|
-
* @
|
|
8249
|
+
* Set the Prisma client delegates for all models.
|
|
8250
|
+
*
|
|
8251
|
+
* @deprecated Use Model.setAdapter(createPrismaDatabaseAdapter(...)) or another
|
|
8252
|
+
* adapter-first bootstrap path instead.
|
|
8253
|
+
*
|
|
8254
|
+
* @param client
|
|
7021
8255
|
*/
|
|
7022
8256
|
static setClient(client) {
|
|
8257
|
+
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.");
|
|
7023
8258
|
this.client = client;
|
|
7024
8259
|
}
|
|
7025
8260
|
static setAdapter(adapter) {
|
|
@@ -7252,6 +8487,8 @@ var Model = class Model {
|
|
|
7252
8487
|
static getAdapter() {
|
|
7253
8488
|
ensureArkormConfigLoading();
|
|
7254
8489
|
if (this.adapter) return this.adapter;
|
|
8490
|
+
const runtimeAdapter = getRuntimeAdapter();
|
|
8491
|
+
if (runtimeAdapter) return runtimeAdapter;
|
|
7255
8492
|
const client = getActiveTransactionClient() ?? this.client ?? getRuntimePrismaClient();
|
|
7256
8493
|
if (!client || typeof client !== "object") return void 0;
|
|
7257
8494
|
return createPrismaCompatibilityAdapter(client);
|
|
@@ -7556,14 +8793,11 @@ var Model = class Model {
|
|
|
7556
8793
|
*/
|
|
7557
8794
|
async load(relations) {
|
|
7558
8795
|
const relationMap = this.normalizeRelationMap(relations);
|
|
7559
|
-
await
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
7564
|
-
const results = await relation.getResults();
|
|
7565
|
-
this.attributes[name] = results;
|
|
7566
|
-
}));
|
|
8796
|
+
await new SetBasedEagerLoader([this], relationMap).load();
|
|
8797
|
+
return this;
|
|
8798
|
+
}
|
|
8799
|
+
setLoadedRelation(name, value) {
|
|
8800
|
+
this.attributes[name] = value;
|
|
7567
8801
|
return this;
|
|
7568
8802
|
}
|
|
7569
8803
|
/**
|
|
@@ -8093,6 +9327,7 @@ exports.applyDropTableOperation = applyDropTableOperation;
|
|
|
8093
9327
|
exports.applyMigrationRollbackToPrismaSchema = applyMigrationRollbackToPrismaSchema;
|
|
8094
9328
|
exports.applyMigrationToPrismaSchema = applyMigrationToPrismaSchema;
|
|
8095
9329
|
exports.applyOperationsToPrismaSchema = applyOperationsToPrismaSchema;
|
|
9330
|
+
exports.bindAdapterToModels = bindAdapterToModels;
|
|
8096
9331
|
exports.buildEnumBlock = buildEnumBlock;
|
|
8097
9332
|
exports.buildFieldLine = buildFieldLine;
|
|
8098
9333
|
exports.buildIndexLine = buildIndexLine;
|
|
@@ -8131,6 +9366,7 @@ exports.getDefaultStubsPath = getDefaultStubsPath;
|
|
|
8131
9366
|
exports.getLastMigrationRun = getLastMigrationRun;
|
|
8132
9367
|
exports.getLatestAppliedMigrations = getLatestAppliedMigrations;
|
|
8133
9368
|
exports.getMigrationPlan = getMigrationPlan;
|
|
9369
|
+
exports.getRuntimeAdapter = getRuntimeAdapter;
|
|
8134
9370
|
exports.getRuntimePaginationCurrentPageResolver = getRuntimePaginationCurrentPageResolver;
|
|
8135
9371
|
exports.getRuntimePaginationURLDriverFactory = getRuntimePaginationURLDriverFactory;
|
|
8136
9372
|
exports.getRuntimePrismaClient = getRuntimePrismaClient;
|