arkormx 2.0.0-next.0 → 2.0.0-next.2

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