arkormx 2.4.4 → 2.4.6

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.cjs CHANGED
@@ -10,6 +10,7 @@ let _h3ravel_support = require("@h3ravel/support");
10
10
  let kysely = require("kysely");
11
11
  let _h3ravel_shared = require("@h3ravel/shared");
12
12
  let _h3ravel_musket = require("@h3ravel/musket");
13
+ let node_async_hooks = require("node:async_hooks");
13
14
 
14
15
  //#region src/Exceptions/QueryExecutionException.ts
15
16
  var QueryExecutionException = class extends require_relationship.ArkormException {
@@ -3529,6 +3530,9 @@ var Seeder = class Seeder {
3529
3530
  static {
3530
3531
  this[SEEDER_BRAND] = true;
3531
3532
  }
3533
+ static {
3534
+ this.executionReport = new node_async_hooks.AsyncLocalStorage();
3535
+ }
3532
3536
  /**
3533
3537
  * Runs one or more seeders.
3534
3538
  *
@@ -3538,6 +3542,19 @@ var Seeder = class Seeder {
3538
3542
  await Seeder.runSeeders(...seeders);
3539
3543
  }
3540
3544
  /**
3545
+ * Run seeders and return every seeder class executed, including nested calls.
3546
+ *
3547
+ * @param seeders
3548
+ * @returns
3549
+ */
3550
+ static async runWithReport(...seeders) {
3551
+ const report = [];
3552
+ await this.executionReport.run(report, async () => {
3553
+ await this.runSeeders(...seeders);
3554
+ });
3555
+ return report;
3556
+ }
3557
+ /**
3541
3558
  * Converts a SeederInput into a Seeder instance.
3542
3559
  *
3543
3560
  * @param input The SeederInput to convert.
@@ -3561,7 +3578,11 @@ var Seeder = class Seeder {
3561
3578
  all.push(current);
3562
3579
  return all;
3563
3580
  }, []);
3564
- for (const seeder of queue) await this.toSeederInstance(seeder).run();
3581
+ for (const seeder of queue) {
3582
+ const instance = this.toSeederInstance(seeder);
3583
+ this.executionReport.getStore()?.push(instance.constructor.name);
3584
+ await instance.run();
3585
+ }
3565
3586
  }
3566
3587
  };
3567
3588
 
@@ -3594,9 +3615,10 @@ var SeedCommand = class extends _h3ravel_musket.Command {
3594
3615
  if (seederDirs.length === 0 && require_relationship.getRegisteredSeeders().length === 0) return void this.error(`ERROR: Seeders directory not found: ${this.app.formatPathForLog(configuredSeedersDir)}`);
3595
3616
  const classes = this.option("all") ? await this.loadAllSeeders(seederDirs) : await this.loadNamedSeeder(seederDirs, this.argument("name") ?? "DatabaseSeeder");
3596
3617
  if (classes.length === 0) return void this.error("ERROR: No seeder classes found to run.");
3597
- for (const SeederClassItem of classes) await new SeederClassItem().run();
3618
+ const executedSeeders = [];
3619
+ for (const SeederClassItem of classes) executedSeeders.push(...await Seeder.runWithReport(new SeederClassItem()));
3598
3620
  this.success("Database seeding completed");
3599
- classes.forEach((cls) => this.success(this.app.splitLogger("Seeded", cls.name)));
3621
+ executedSeeders.forEach((name) => this.success(this.app.splitLogger("Seeded", name)));
3600
3622
  }
3601
3623
  /**
3602
3624
  * Load all seeder classes from the specified directory.
@@ -3665,191 +3687,42 @@ var logo_default = String.raw`
3665
3687
  `;
3666
3688
 
3667
3689
  //#endregion
3668
- //#region src/database/factories.ts
3669
- /**
3670
- * Base class for defining model factories.
3671
- * Not meant to be used directly.
3672
- *
3673
- * @template TModel The type of model the factory creates.
3674
- * @template TAttributes The type of attributes used to create the model.
3675
- * @author Legacy (3m1n3nc3)
3676
- * @since 0.1.0
3677
- */
3678
- var ModelFactory = class ModelFactory {
3679
- constructor() {
3680
- this.amount = 1;
3681
- this.sequence = 0;
3682
- this.states = [];
3683
- }
3684
- /**
3685
- * Set the number of models to create.
3686
- *
3687
- * @param amount
3688
- * @returns
3689
- */
3690
- count(amount) {
3691
- this.amount = Math.max(1, Math.floor(amount));
3692
- return this;
3693
- }
3694
- /**
3695
- * Define a state transformation for the factory.
3696
- * States are applied in the order they were defined.
3697
- *
3698
- * @param resolver A function that takes the current attributes and sequence number, and returns the transformed attributes.
3699
- * @returns The factory instance for chaining.
3700
- */
3701
- state(resolver) {
3702
- this.states.push(resolver);
3703
- return this;
3704
- }
3705
- /**
3706
- * Create a new model instance without saving it to the database.
3707
- *
3708
- * @param overrides
3709
- * @returns
3710
- */
3711
- make(overrides = {}) {
3712
- const attributes = this.buildAttributes(overrides);
3713
- return new this.model(attributes);
3714
- }
3715
- /**
3716
- * Create a new model instance from an async factory definition without
3717
- * saving it to the database.
3718
- *
3719
- * @param overrides
3720
- * @returns
3721
- */
3722
- async makeAsync(overrides = {}) {
3723
- const attributes = await this.buildAttributesAsync(overrides);
3724
- return new this.model(attributes);
3725
- }
3726
- /**
3727
- * Create multiple model instances without saving them to the database.
3728
- *
3729
- * @param amount
3730
- * @param overrides
3731
- * @returns
3732
- */
3733
- makeMany(amount = this.amount, overrides = {}) {
3734
- const total = Math.max(1, Math.floor(amount));
3735
- return Array.from({ length: total }, () => this.make(overrides));
3736
- }
3737
- /**
3738
- * Create multiple model instances from async factory definitions without
3739
- * saving them to the database.
3740
- *
3741
- * @param amount
3742
- * @param overrides
3743
- * @returns
3744
- */
3745
- async makeManyAsync(amount = this.amount, overrides = {}) {
3746
- const total = Math.max(1, Math.floor(amount));
3747
- const models = [];
3748
- for (let index = 0; index < total; index++) models.push(await this.makeAsync(overrides));
3749
- return models;
3750
- }
3751
- /**
3752
- * Create a new model instance and save it to the database.
3753
- *
3754
- * @param overrides
3755
- * @returns
3756
- */
3757
- async create(overrides = {}) {
3758
- const model = await this.makeAsync(overrides);
3759
- if (typeof model.save !== "function") throw new Error("Factory model does not support save().");
3760
- return await model.save();
3761
- }
3762
- /**
3763
- * Create multiple model instances and save them to the database.
3764
- *
3765
- * @param amount
3766
- * @param overrides
3767
- * @returns
3768
- */
3769
- async createMany(amount = this.amount, overrides = {}) {
3770
- const models = await this.makeManyAsync(amount, overrides);
3771
- return await Promise.all(models.map(async (model) => {
3772
- if (typeof model.save !== "function") throw new Error("Factory model does not support save().");
3773
- return await model.save();
3774
- }));
3775
- }
3776
- /**
3777
- * Build the attributes for a model instance, applying the factory
3778
- * definition and any defined states, and merging in any overrides.
3779
- *
3780
- * @param overrides
3781
- * @returns
3782
- */
3783
- buildAttributes(overrides) {
3784
- const sequence = this.sequence;
3785
- this.sequence += 1;
3786
- let resolved = this.definition(sequence);
3787
- if (ModelFactory.isPromiseLike(resolved)) {
3788
- this.sequence = sequence;
3789
- throw new Error("This factory has an async definition. Use makeAsync(), makeManyAsync(), create(), or createMany() instead.");
3790
- }
3791
- for (const state of this.states) {
3792
- resolved = state(resolved, sequence);
3793
- if (ModelFactory.isPromiseLike(resolved)) {
3794
- this.sequence = sequence;
3795
- throw new Error("This factory has an async state. Use makeAsync(), makeManyAsync(), create(), or createMany() instead.");
3796
- }
3797
- }
3798
- return {
3799
- ...resolved,
3800
- ...overrides
3801
- };
3802
- }
3803
- /**
3804
- * Build attributes for async and sync factory definitions.
3805
- *
3806
- * @param overrides
3807
- * @returns
3808
- */
3809
- async buildAttributesAsync(overrides) {
3810
- const sequence = this.sequence;
3811
- this.sequence += 1;
3812
- let resolved = await this.definition(sequence);
3813
- for (const state of this.states) resolved = await state(resolved, sequence);
3814
- return {
3815
- ...resolved,
3816
- ...overrides
3817
- };
3818
- }
3819
- static isPromiseLike(value) {
3820
- return typeof value?.then === "function";
3821
- }
3690
+ //#region src/helpers/runtime-compatibility.ts
3691
+ const isObjectLike = (value) => {
3692
+ return Boolean(value) && typeof value === "object";
3822
3693
  };
3823
- /**
3824
- * A helper class for defining factories using an inline definition
3825
- * function, without needing to create a separate factory class.
3826
- *
3827
- * @template TModel
3828
- * @template TAttributes
3829
- * @author Legacy (3m1n3nc3)
3830
- * @since 0.1.0
3831
- */
3832
- var InlineFactory = class extends ModelFactory {
3833
- constructor(model, resolver) {
3834
- super();
3835
- this.resolver = resolver;
3836
- this.model = model;
3837
- }
3838
- definition(sequence) {
3839
- return this.resolver(sequence);
3840
- }
3694
+ const isCompatibilityClient = (value) => {
3695
+ return Boolean(value) && typeof value === "object";
3841
3696
  };
3842
- /**
3843
- * Define a factory for a given model using an inline definition function.
3844
- *
3845
- * @template TModel The type of model the factory creates.
3846
- * @template TAttributes The type of attributes used to create the model.
3847
- * @param model The model constructor.
3848
- * @param definition The factory definition function.
3849
- * @returns A new instance of the model factory.
3850
- */
3851
- const defineFactory = (model, definition) => {
3852
- return new InlineFactory(model, definition);
3697
+ const getCompatibilitySources = (preferredClient) => {
3698
+ const activeTransactionClient = require_relationship.getActiveTransactionClient();
3699
+ const runtimeClient = require_relationship.getRuntimeClient();
3700
+ return activeTransactionClient ? [
3701
+ activeTransactionClient,
3702
+ preferredClient,
3703
+ runtimeClient
3704
+ ] : [preferredClient, runtimeClient];
3705
+ };
3706
+ const getRuntimeCompatibilityAdapter = (preferredClient) => {
3707
+ const client = getCompatibilitySources(preferredClient).find((source) => isCompatibilityClient(source));
3708
+ if (!client) return void 0;
3709
+ return createPrismaCompatibilityAdapter(client);
3710
+ };
3711
+ const resolveRuntimeCompatibilityQuerySchema = (candidates, preferredClient) => {
3712
+ return getCompatibilitySources(preferredClient).flatMap((source) => {
3713
+ if (!isObjectLike(source)) return [];
3714
+ return candidates.map((candidate) => source[candidate]);
3715
+ }).find((candidate) => require_relationship.isQuerySchemaLike(candidate));
3716
+ };
3717
+ const resolveRuntimeCompatibilityQuerySchemaOrThrow = (key, candidates, modelName, preferredClient) => {
3718
+ const resolved = resolveRuntimeCompatibilityQuerySchema(candidates, preferredClient);
3719
+ if (!resolved) throw new MissingDelegateException(`Database delegate [${key}] is not configured.`, {
3720
+ operation: "getDelegate",
3721
+ model: modelName,
3722
+ delegate: key,
3723
+ meta: { candidates }
3724
+ });
3725
+ return resolved;
3853
3726
  };
3854
3727
 
3855
3728
  //#endregion
@@ -4758,6 +4631,11 @@ var QueryBuilder = class QueryBuilder {
4758
4631
  const resolvedKey = key ?? this.model.getPrimaryKey();
4759
4632
  return this.where({ [resolvedKey]: value }).first();
4760
4633
  }
4634
+ async findOrFail(value, key) {
4635
+ const model = await this.find(value, key);
4636
+ if (!model) throw new ModelNotFoundException(this.model.name, "Record not found.");
4637
+ return model;
4638
+ }
4761
4639
  async findOr(value, keyOrCallback, maybeCallback) {
4762
4640
  const key = typeof keyOrCallback === "string" ? keyOrCallback : this.model.getPrimaryKey();
4763
4641
  const callback = typeof keyOrCallback === "function" ? keyOrCallback : maybeCallback;
@@ -6320,164 +6198,6 @@ var QueryBuilder = class QueryBuilder {
6320
6198
  }
6321
6199
  };
6322
6200
 
6323
- //#endregion
6324
- //#region src/helpers/runtime-compatibility.ts
6325
- const isObjectLike = (value) => {
6326
- return Boolean(value) && typeof value === "object";
6327
- };
6328
- const isCompatibilityClient = (value) => {
6329
- return Boolean(value) && typeof value === "object";
6330
- };
6331
- const getCompatibilitySources = (preferredClient) => {
6332
- const activeTransactionClient = require_relationship.getActiveTransactionClient();
6333
- const runtimeClient = require_relationship.getRuntimeClient();
6334
- return activeTransactionClient ? [
6335
- activeTransactionClient,
6336
- preferredClient,
6337
- runtimeClient
6338
- ] : [preferredClient, runtimeClient];
6339
- };
6340
- const getRuntimeCompatibilityAdapter = (preferredClient) => {
6341
- const client = getCompatibilitySources(preferredClient).find((source) => isCompatibilityClient(source));
6342
- if (!client) return void 0;
6343
- return createPrismaCompatibilityAdapter(client);
6344
- };
6345
- const resolveRuntimeCompatibilityQuerySchema = (candidates, preferredClient) => {
6346
- return getCompatibilitySources(preferredClient).flatMap((source) => {
6347
- if (!isObjectLike(source)) return [];
6348
- return candidates.map((candidate) => source[candidate]);
6349
- }).find((candidate) => require_relationship.isQuerySchemaLike(candidate));
6350
- };
6351
- const resolveRuntimeCompatibilityQuerySchemaOrThrow = (key, candidates, modelName, preferredClient) => {
6352
- const resolved = resolveRuntimeCompatibilityQuerySchema(candidates, preferredClient);
6353
- if (!resolved) throw new MissingDelegateException(`Database delegate [${key}] is not configured.`, {
6354
- operation: "getDelegate",
6355
- model: modelName,
6356
- delegate: key,
6357
- meta: { candidates }
6358
- });
6359
- return resolved;
6360
- };
6361
-
6362
- //#endregion
6363
- //#region src/DB.ts
6364
- const defaultSoftDeleteConfig = {
6365
- enabled: false,
6366
- column: "deletedAt"
6367
- };
6368
- var DB = class DB {
6369
- constructor(adapter) {
6370
- this.scopedAdapter = adapter;
6371
- }
6372
- static setAdapter(adapter) {
6373
- this.adapter = adapter;
6374
- }
6375
- static getAdapter() {
6376
- const runtimeAdapter = require_relationship.getRuntimeAdapter();
6377
- if (runtimeAdapter) return runtimeAdapter;
6378
- if (this.adapter) return this.adapter;
6379
- return getRuntimeCompatibilityAdapter();
6380
- }
6381
- getAdapter() {
6382
- return this.scopedAdapter ?? DB.getAdapter();
6383
- }
6384
- static table(table, options = {}) {
6385
- return new DB().table(table, options);
6386
- }
6387
- table(table, options = {}) {
6388
- return DB.createTableModel(table, options, this.getAdapter()).query();
6389
- }
6390
- static async raw(sql, bindings = []) {
6391
- return await new DB().raw(sql, bindings);
6392
- }
6393
- async raw(sql, bindings = []) {
6394
- const adapter = this.getAdapter();
6395
- if (!adapter) throw new require_relationship.ArkormException("Raw queries require a configured database adapter.", {
6396
- code: "ADAPTER_NOT_CONFIGURED",
6397
- operation: "db.raw"
6398
- });
6399
- if (!adapter.rawQuery) throw new require_relationship.UnsupportedAdapterFeatureException("Raw queries are not supported by the current adapter.", {
6400
- operation: "db.raw",
6401
- meta: { feature: "rawQuery" }
6402
- });
6403
- const rows = await adapter.rawQuery({
6404
- sql,
6405
- bindings
6406
- });
6407
- return require_relationship.ArkormCollection.make(rows);
6408
- }
6409
- static async transaction(callback, context) {
6410
- return await new DB().transaction(callback, context);
6411
- }
6412
- async transaction(callback, context) {
6413
- const adapter = this.getAdapter();
6414
- if (!adapter) throw new require_relationship.ArkormException("DB transactions require a configured database adapter.", {
6415
- code: "ADAPTER_NOT_CONFIGURED",
6416
- operation: "db.transaction"
6417
- });
6418
- return await adapter.transaction(async (transactionAdapter) => {
6419
- return await callback(new DB(transactionAdapter));
6420
- }, context);
6421
- }
6422
- static createTableModel(table, options, adapter) {
6423
- const primaryKey = options.primaryKey ?? "id";
6424
- const resolvedAdapter = options.adapter ?? adapter ?? DB.getAdapter();
6425
- const persistedMetadata = DB.resolvePersistedTableMetadata(table, options, resolvedAdapter);
6426
- const columns = {
6427
- ...persistedMetadata.columns,
6428
- ...options.columns ?? {}
6429
- };
6430
- const softDelete = options.softDelete ?? defaultSoftDeleteConfig;
6431
- const primaryKeyGeneration = options.primaryKeyGeneration ? { ...options.primaryKeyGeneration } : persistedMetadata.primaryKeyGeneration?.column === primaryKey ? {
6432
- strategy: persistedMetadata.primaryKeyGeneration.strategy,
6433
- prismaDefault: persistedMetadata.primaryKeyGeneration.prismaDefault,
6434
- databaseDefault: persistedMetadata.primaryKeyGeneration.databaseDefault,
6435
- runtimeFactory: persistedMetadata.primaryKeyGeneration.runtimeFactory
6436
- } : void 0;
6437
- const timestampColumns = options.timestampColumns?.map((column) => ({ ...column })) ?? persistedMetadata.timestampColumns?.map((column) => ({ ...column }));
6438
- const buildMetadata = () => {
6439
- return {
6440
- table,
6441
- primaryKey,
6442
- columns: { ...columns },
6443
- softDelete: { ...softDelete },
6444
- primaryKeyGeneration,
6445
- timestampColumns
6446
- };
6447
- };
6448
- const modelStatic = {
6449
- query: () => new QueryBuilder(modelStatic, modelStatic.getAdapter()),
6450
- hydrate: (attributes) => ({ ...attributes }),
6451
- hydrateMany: (attributes) => attributes.map((attribute) => ({ ...attribute })),
6452
- hydrateRetrieved: async (attributes) => ({ ...attributes }),
6453
- hydrateManyRetrieved: async (attributes) => attributes.map((attribute) => ({ ...attribute })),
6454
- getAdapter: () => resolvedAdapter,
6455
- getColumnMap: () => ({ ...columns }),
6456
- getColumnName: (attribute) => columns[attribute] ?? attribute,
6457
- getModelMetadata: () => buildMetadata(),
6458
- getPrimaryKey: () => primaryKey,
6459
- getRelationMetadata: () => null,
6460
- setAdapter: () => {},
6461
- getSoftDeleteConfig: () => ({ ...softDelete }),
6462
- getTable: () => table
6463
- };
6464
- return modelStatic;
6465
- }
6466
- static resolvePersistedTableMetadata(table, options, adapter) {
6467
- if (options.persistedMetadata === false) return {
6468
- columns: {},
6469
- enums: {}
6470
- };
6471
- const persistedMetadataOptions = typeof options.persistedMetadata === "object" ? options.persistedMetadata : {};
6472
- return require_relationship.getPersistedTableMetadata(table, {
6473
- cwd: persistedMetadataOptions.cwd,
6474
- configuredPath: persistedMetadataOptions.configuredPath,
6475
- features: require_relationship.resolvePersistedMetadataFeatures(require_relationship.getUserConfig("features")),
6476
- strict: persistedMetadataOptions.strict ?? (Boolean(adapter) && !(adapter instanceof PrismaDatabaseAdapter))
6477
- });
6478
- }
6479
- };
6480
-
6481
6201
  //#endregion
6482
6202
  //#region src/Model.ts
6483
6203
  /**
@@ -7720,6 +7440,552 @@ var Model = class Model {
7720
7440
  }
7721
7441
  };
7722
7442
 
7443
+ //#endregion
7444
+ //#region src/database/factories.ts
7445
+ /**
7446
+ * Base class for defining model factories.
7447
+ * Not meant to be used directly.
7448
+ *
7449
+ * @template TModel The type of model the factory creates.
7450
+ * @template TAttributes The type of attributes used to create the model.
7451
+ * @author Legacy (3m1n3nc3)
7452
+ * @since 0.1.0
7453
+ */
7454
+ var ModelFactory = class ModelFactory {
7455
+ constructor() {
7456
+ this.amount = 1;
7457
+ this.sequence = 0;
7458
+ this.states = [];
7459
+ this.configured = false;
7460
+ this.afterMakingCallbacks = [];
7461
+ this.afterCreatingCallbacks = [];
7462
+ this.hasRelations = [];
7463
+ this.forRelations = [];
7464
+ this.attachedRelations = [];
7465
+ this.recyclePool = /* @__PURE__ */ new Map();
7466
+ this.recycleOffsets = /* @__PURE__ */ new Map();
7467
+ }
7468
+ /**
7469
+ * Configure states and lifecycle callbacks for each new factory instance.
7470
+ */
7471
+ configure() {}
7472
+ /**
7473
+ * Set the number of models to create.
7474
+ *
7475
+ * @param amount
7476
+ * @returns
7477
+ */
7478
+ count(amount) {
7479
+ this.ensureConfigured();
7480
+ this.amount = Math.max(1, Math.floor(amount));
7481
+ return this;
7482
+ }
7483
+ /**
7484
+ * Define a state transformation for the factory.
7485
+ * States are applied in the order they were defined.
7486
+ *
7487
+ * @param resolver A function that takes the current attributes and sequence number, and returns the transformed attributes.
7488
+ * @returns The factory instance for chaining.
7489
+ */
7490
+ state(resolver) {
7491
+ this.ensureConfigured();
7492
+ this.states.push(resolver);
7493
+ return this;
7494
+ }
7495
+ /**
7496
+ * Register a callback that runs after a model is made.
7497
+ *
7498
+ * @param callback
7499
+ * @returns
7500
+ */
7501
+ afterMaking(callback) {
7502
+ this.ensureConfigured();
7503
+ this.afterMakingCallbacks.push(callback);
7504
+ return this;
7505
+ }
7506
+ /**
7507
+ * Register a callback that runs after a model is persisted.
7508
+ *
7509
+ * @param callback
7510
+ * @returns
7511
+ */
7512
+ afterCreating(callback) {
7513
+ this.ensureConfigured();
7514
+ this.afterCreatingCallbacks.push(callback);
7515
+ return this;
7516
+ }
7517
+ /**
7518
+ * Create a new model instance without saving it to the database.
7519
+ *
7520
+ * @param overrides
7521
+ * @returns
7522
+ */
7523
+ make(overrides = {}) {
7524
+ this.ensureConfigured();
7525
+ const attributes = this.buildAttributes(overrides);
7526
+ const model = new this.model(attributes);
7527
+ this.runCallbacksSync(this.afterMakingCallbacks, model, "afterMaking");
7528
+ return model;
7529
+ }
7530
+ /**
7531
+ * Create a new model instance from an async factory definition without
7532
+ * saving it to the database.
7533
+ *
7534
+ * @param overrides
7535
+ * @returns
7536
+ */
7537
+ async makeAsync(overrides = {}) {
7538
+ this.ensureConfigured();
7539
+ const attributes = await this.buildAttributesAsync(overrides);
7540
+ const model = new this.model(attributes);
7541
+ await this.runCallbacks(this.afterMakingCallbacks, model);
7542
+ return model;
7543
+ }
7544
+ /**
7545
+ * Create multiple model instances without saving them to the database.
7546
+ *
7547
+ * @param amount
7548
+ * @param overrides
7549
+ * @returns
7550
+ */
7551
+ makeMany(amount = this.amount, overrides = {}) {
7552
+ const total = Math.max(1, Math.floor(amount));
7553
+ return Array.from({ length: total }, () => this.make(overrides));
7554
+ }
7555
+ /**
7556
+ * Create multiple model instances from async factory definitions without
7557
+ * saving them to the database.
7558
+ *
7559
+ * @param amount
7560
+ * @param overrides
7561
+ * @returns
7562
+ */
7563
+ async makeManyAsync(amount = this.amount, overrides = {}) {
7564
+ const total = Math.max(1, Math.floor(amount));
7565
+ const models = [];
7566
+ for (let index = 0; index < total; index++) models.push(await this.makeAsync(overrides));
7567
+ return models;
7568
+ }
7569
+ /**
7570
+ * Create a new model instance and save it to the database.
7571
+ *
7572
+ * @param overrides
7573
+ * @returns
7574
+ */
7575
+ async create(overrides = {}) {
7576
+ return await this.createPersisted(overrides);
7577
+ }
7578
+ /**
7579
+ * Create multiple model instances and save them to the database.
7580
+ *
7581
+ * @param amount
7582
+ * @param overrides
7583
+ * @returns
7584
+ */
7585
+ async createMany(amount = this.amount, overrides = {}) {
7586
+ this.ensureConfigured();
7587
+ const total = Math.max(1, Math.floor(amount));
7588
+ const models = [];
7589
+ for (let index = 0; index < total; index++) models.push(await this.create(overrides));
7590
+ return models;
7591
+ }
7592
+ /**
7593
+ * Create related models through a has-one or has-many relationship.
7594
+ *
7595
+ * @param factory
7596
+ * @param relationship
7597
+ * @returns
7598
+ */
7599
+ has(factory, relationship) {
7600
+ this.ensureConfigured();
7601
+ this.hasRelations.push({
7602
+ factory,
7603
+ relationship
7604
+ });
7605
+ return this;
7606
+ }
7607
+ /**
7608
+ * Associate the created model with a parent model or factory.
7609
+ *
7610
+ * @param related
7611
+ * @param relationship
7612
+ * @returns
7613
+ */
7614
+ for(related, relationship) {
7615
+ this.ensureConfigured();
7616
+ this.forRelations.push({
7617
+ related,
7618
+ relationship
7619
+ });
7620
+ return this;
7621
+ }
7622
+ /**
7623
+ * Create or reuse related models and attach them through a many-to-many relationship.
7624
+ *
7625
+ * @param related
7626
+ * @param pivot
7627
+ * @param relationship
7628
+ * @returns
7629
+ */
7630
+ hasAttached(related, pivot = {}, relationship) {
7631
+ this.ensureConfigured();
7632
+ this.attachedRelations.push({
7633
+ related,
7634
+ pivot,
7635
+ relationship
7636
+ });
7637
+ return this;
7638
+ }
7639
+ /**
7640
+ * Reuse existing models when resolving factory-backed relationships.
7641
+ *
7642
+ * @param models
7643
+ * @returns
7644
+ */
7645
+ recycle(models) {
7646
+ this.ensureConfigured();
7647
+ (Array.isArray(models) ? models : "all" in models ? models.all() : [models]).forEach((model) => {
7648
+ const constructor = model.constructor;
7649
+ const existing = this.recyclePool.get(constructor) ?? [];
7650
+ if (!existing.includes(model)) existing.push(model);
7651
+ this.recyclePool.set(constructor, existing);
7652
+ });
7653
+ return this;
7654
+ }
7655
+ /**
7656
+ * Get the model contgructor
7657
+ *
7658
+ * @returns
7659
+ */
7660
+ getModelConstructor() {
7661
+ return this.model;
7662
+ }
7663
+ /**
7664
+ * Build the attributes for a model instance, applying the factory
7665
+ * definition and any defined states, and merging in any overrides.
7666
+ *
7667
+ * @param overrides
7668
+ * @returns
7669
+ */
7670
+ buildAttributes(overrides) {
7671
+ const sequence = this.sequence;
7672
+ this.sequence += 1;
7673
+ let resolved = this.definition(sequence);
7674
+ if (ModelFactory.isPromiseLike(resolved)) {
7675
+ this.sequence = sequence;
7676
+ throw new Error("This factory has an async definition. Use makeAsync(), makeManyAsync(), create(), or createMany() instead.");
7677
+ }
7678
+ for (const state of this.states) {
7679
+ resolved = state(resolved, sequence);
7680
+ if (ModelFactory.isPromiseLike(resolved)) {
7681
+ this.sequence = sequence;
7682
+ throw new Error("This factory has an async state. Use makeAsync(), makeManyAsync(), create(), or createMany() instead.");
7683
+ }
7684
+ }
7685
+ const attributes = {
7686
+ ...resolved,
7687
+ ...overrides
7688
+ };
7689
+ return this.resolveAttributesSync(this.applyBelongsToRelationshipsSync(attributes));
7690
+ }
7691
+ /**
7692
+ * Build attributes for async and sync factory definitions.
7693
+ *
7694
+ * @param overrides
7695
+ * @returns
7696
+ */
7697
+ async buildAttributesAsync(overrides) {
7698
+ const sequence = this.sequence;
7699
+ this.sequence += 1;
7700
+ let resolved = await this.definition(sequence);
7701
+ for (const state of this.states) resolved = await state(resolved, sequence);
7702
+ const attributes = {
7703
+ ...resolved,
7704
+ ...overrides
7705
+ };
7706
+ return await this.resolveAttributesAsync(await this.applyBelongsToRelationships(attributes));
7707
+ }
7708
+ ensureConfigured() {
7709
+ if (this.configured) return;
7710
+ this.configured = true;
7711
+ this.configure();
7712
+ }
7713
+ resolveAttributesSync(attributes) {
7714
+ const resolved = {};
7715
+ for (const [key, value] of Object.entries(attributes)) {
7716
+ if (ModelFactory.isFactory(value)) throw new Error("This factory definition creates a related model. Use makeAsync(), makeManyAsync(), create(), or createMany() instead.");
7717
+ const next = typeof value === "function" ? value(resolved) : value;
7718
+ if (ModelFactory.isPromiseLike(next)) throw new Error("This factory has an async attribute resolver. Use makeAsync(), makeManyAsync(), create(), or createMany() instead.");
7719
+ resolved[key] = next;
7720
+ }
7721
+ return resolved;
7722
+ }
7723
+ async resolveAttributesAsync(attributes) {
7724
+ const resolved = {};
7725
+ for (const [key, value] of Object.entries(attributes)) {
7726
+ if (ModelFactory.isFactory(value)) {
7727
+ resolved[key] = await this.resolveFactoryKey(value);
7728
+ continue;
7729
+ }
7730
+ resolved[key] = typeof value === "function" ? await value(resolved) : value;
7731
+ }
7732
+ return resolved;
7733
+ }
7734
+ applyBelongsToRelationshipsSync(attributes) {
7735
+ return this.forRelations.reduce((resolved, relation) => {
7736
+ if (relation.related instanceof Model) return this.mergeBelongsToAttribute(resolved, relation.related, relation.relationship);
7737
+ throw new Error("This factory creates a parent model. Use makeAsync(), makeManyAsync(), create(), or createMany() instead.");
7738
+ }, attributes);
7739
+ }
7740
+ async applyBelongsToRelationships(attributes) {
7741
+ let resolved = attributes;
7742
+ for (const relation of this.forRelations) {
7743
+ const related = relation.related instanceof Model ? relation.related : await this.resolveFactoryModel(relation.related);
7744
+ resolved = this.mergeBelongsToAttribute(resolved, related, relation.relationship);
7745
+ }
7746
+ return resolved;
7747
+ }
7748
+ mergeBelongsToAttribute(attributes, related, relationship) {
7749
+ const relationName = relationship ?? `${(0, _h3ravel_support.str)(related.constructor.name).camel().singular()}`;
7750
+ const metadata = this.model.getRelationMetadata?.(relationName);
7751
+ if (metadata?.type !== "belongsTo" || !metadata.foreignKey || !metadata.ownerKey) throw new Error(`Factory relationship [${relationName}] is not a belongsTo relation on [${this.model.name}].`);
7752
+ return {
7753
+ ...attributes,
7754
+ [metadata.foreignKey]: related.getAttribute(metadata.ownerKey)
7755
+ };
7756
+ }
7757
+ async createPersisted(overrides, persist) {
7758
+ const model = await this.makeAsync(overrides);
7759
+ const persisted = persist ? await persist(model) : await this.saveModel(model);
7760
+ await this.createHasRelations(persisted);
7761
+ await this.createAttachedRelations(persisted);
7762
+ await this.runCallbacks(this.afterCreatingCallbacks, persisted);
7763
+ return persisted;
7764
+ }
7765
+ async saveModel(model) {
7766
+ const saveable = model;
7767
+ const constructor = model?.constructor;
7768
+ const primaryKey = constructor.getPrimaryKey?.() ?? "id";
7769
+ if (saveable.getAttribute?.(primaryKey) != null && constructor.query && saveable.getRawAttributes) return await constructor.query().create(saveable.getRawAttributes());
7770
+ if (typeof saveable.save !== "function") throw new Error("Factory model does not support save().");
7771
+ return await saveable.save();
7772
+ }
7773
+ async createHasRelations(model) {
7774
+ for (const definition of this.hasRelations) {
7775
+ const relationship = definition.relationship ?? `${(0, _h3ravel_support.str)(definition.factory.getModelConstructor().name).camel().plural()}`;
7776
+ const relation = this.resolveRelation(model, relationship);
7777
+ definition.factory.inheritRecyclePool(this.recyclePool);
7778
+ for (let index = 0; index < definition.factory.amount; index++) await definition.factory.createPersisted({}, async (related) => await relation.save(related));
7779
+ }
7780
+ }
7781
+ async createAttachedRelations(model) {
7782
+ for (const definition of this.attachedRelations) {
7783
+ const factory = definition.related instanceof ModelFactory ? definition.related : null;
7784
+ const relatedModels = factory ? await factory.inheritRecyclePool(this.recyclePool).createMany() : Array.isArray(definition.related) ? definition.related : [definition.related];
7785
+ const relatedName = factory?.getModelConstructor().name ?? relatedModels[0]?.constructor.name;
7786
+ const relationship = definition.relationship ?? `${(0, _h3ravel_support.str)(relatedName ?? "").camel().plural()}`;
7787
+ const relation = this.resolveRelation(model, relationship);
7788
+ if (typeof relation.attach !== "function") throw new Error(`Factory relationship [${relationship}] does not support attach().`);
7789
+ await relation.attach(relatedModels, definition.pivot);
7790
+ }
7791
+ }
7792
+ resolveRelation(model, relationship) {
7793
+ const resolver = model[relationship];
7794
+ if (typeof resolver !== "function") throw new Error(`Factory relationship [${relationship}] is not defined on [${this.model.name}].`);
7795
+ return resolver.call(model);
7796
+ }
7797
+ async resolveFactoryKey(factory) {
7798
+ const model = await this.resolveFactoryModel(factory);
7799
+ const primaryKey = model.constructor.getPrimaryKey?.() ?? "id";
7800
+ return model.getAttribute(primaryKey);
7801
+ }
7802
+ async resolveFactoryModel(factory) {
7803
+ factory.inheritRecyclePool(this.recyclePool);
7804
+ return factory.takeRecycledModel() ?? await factory.create();
7805
+ }
7806
+ inheritRecyclePool(pool) {
7807
+ pool.forEach((models, constructor) => {
7808
+ const existing = this.recyclePool.get(constructor) ?? [];
7809
+ const inherited = models.filter((model) => !existing.includes(model));
7810
+ this.recyclePool.set(constructor, [...existing, ...inherited]);
7811
+ });
7812
+ return this;
7813
+ }
7814
+ takeRecycledModel() {
7815
+ const constructor = this.model;
7816
+ const models = this.recyclePool.get(constructor);
7817
+ if (!models || models.length === 0) return null;
7818
+ const offset = this.recycleOffsets.get(constructor) ?? 0;
7819
+ this.recycleOffsets.set(constructor, offset + 1);
7820
+ return models[offset % models.length] ?? null;
7821
+ }
7822
+ runCallbacksSync(callbacks, model, callbackName) {
7823
+ callbacks.forEach((callback) => {
7824
+ const result = callback(model);
7825
+ if (ModelFactory.isPromiseLike(result)) throw new Error(`Factory ${callbackName} callback is async. Use makeAsync(), makeManyAsync(), create(), or createMany() instead.`);
7826
+ });
7827
+ }
7828
+ async runCallbacks(callbacks, model) {
7829
+ for (const callback of callbacks) await callback(model);
7830
+ }
7831
+ static isPromiseLike(value) {
7832
+ return typeof value?.then === "function";
7833
+ }
7834
+ static isFactory(value) {
7835
+ return value instanceof ModelFactory;
7836
+ }
7837
+ };
7838
+ /**
7839
+ * A helper class for defining factories using an inline definition
7840
+ * function, without needing to create a separate factory class.
7841
+ *
7842
+ * @template TModel
7843
+ * @template TAttributes
7844
+ * @author Legacy (3m1n3nc3)
7845
+ * @since 0.1.0
7846
+ */
7847
+ var InlineFactory = class extends ModelFactory {
7848
+ constructor(model, resolver) {
7849
+ super();
7850
+ this.resolver = resolver;
7851
+ this.model = model;
7852
+ }
7853
+ definition(sequence) {
7854
+ return this.resolver(sequence);
7855
+ }
7856
+ };
7857
+ /**
7858
+ * Define a factory for a given model using an inline definition function.
7859
+ *
7860
+ * @template TModel The type of model the factory creates.
7861
+ * @template TAttributes The type of attributes used to create the model.
7862
+ * @param model The model constructor.
7863
+ * @param definition The factory definition function.
7864
+ * @returns A new instance of the model factory.
7865
+ */
7866
+ const defineFactory = (model, definition) => {
7867
+ return new InlineFactory(model, definition);
7868
+ };
7869
+
7870
+ //#endregion
7871
+ //#region src/DB.ts
7872
+ const defaultSoftDeleteConfig = {
7873
+ enabled: false,
7874
+ column: "deletedAt"
7875
+ };
7876
+ var DB = class DB {
7877
+ constructor(adapter) {
7878
+ this.scopedAdapter = adapter;
7879
+ }
7880
+ static setAdapter(adapter) {
7881
+ this.adapter = adapter;
7882
+ }
7883
+ static getAdapter() {
7884
+ const runtimeAdapter = require_relationship.getRuntimeAdapter();
7885
+ if (runtimeAdapter) return runtimeAdapter;
7886
+ if (this.adapter) return this.adapter;
7887
+ return getRuntimeCompatibilityAdapter();
7888
+ }
7889
+ getAdapter() {
7890
+ return this.scopedAdapter ?? DB.getAdapter();
7891
+ }
7892
+ static table(table, options = {}) {
7893
+ return new DB().table(table, options);
7894
+ }
7895
+ table(table, options = {}) {
7896
+ return DB.createTableModel(table, options, this.getAdapter()).query();
7897
+ }
7898
+ static async raw(sql, bindings = []) {
7899
+ return await new DB().raw(sql, bindings);
7900
+ }
7901
+ async raw(sql, bindings = []) {
7902
+ const adapter = this.getAdapter();
7903
+ if (!adapter) throw new require_relationship.ArkormException("Raw queries require a configured database adapter.", {
7904
+ code: "ADAPTER_NOT_CONFIGURED",
7905
+ operation: "db.raw"
7906
+ });
7907
+ if (!adapter.rawQuery) throw new require_relationship.UnsupportedAdapterFeatureException("Raw queries are not supported by the current adapter.", {
7908
+ operation: "db.raw",
7909
+ meta: { feature: "rawQuery" }
7910
+ });
7911
+ const rows = await adapter.rawQuery({
7912
+ sql,
7913
+ bindings
7914
+ });
7915
+ return require_relationship.ArkormCollection.make(rows);
7916
+ }
7917
+ static async transaction(callback, context) {
7918
+ return await new DB().transaction(callback, context);
7919
+ }
7920
+ async transaction(callback, context) {
7921
+ const adapter = this.getAdapter();
7922
+ if (!adapter) throw new require_relationship.ArkormException("DB transactions require a configured database adapter.", {
7923
+ code: "ADAPTER_NOT_CONFIGURED",
7924
+ operation: "db.transaction"
7925
+ });
7926
+ return await adapter.transaction(async (transactionAdapter) => {
7927
+ return await callback(new DB(transactionAdapter));
7928
+ }, context);
7929
+ }
7930
+ static createTableModel(table, options, adapter) {
7931
+ const primaryKey = options.primaryKey ?? "id";
7932
+ const resolvedAdapter = options.adapter ?? adapter ?? DB.getAdapter();
7933
+ const persistedMetadata = DB.resolvePersistedTableMetadata(table, options, resolvedAdapter);
7934
+ const columns = {
7935
+ ...persistedMetadata.columns,
7936
+ ...options.columns ?? {}
7937
+ };
7938
+ const softDelete = options.softDelete ?? defaultSoftDeleteConfig;
7939
+ const primaryKeyGeneration = options.primaryKeyGeneration ? { ...options.primaryKeyGeneration } : persistedMetadata.primaryKeyGeneration?.column === primaryKey ? {
7940
+ strategy: persistedMetadata.primaryKeyGeneration.strategy,
7941
+ prismaDefault: persistedMetadata.primaryKeyGeneration.prismaDefault,
7942
+ databaseDefault: persistedMetadata.primaryKeyGeneration.databaseDefault,
7943
+ runtimeFactory: persistedMetadata.primaryKeyGeneration.runtimeFactory
7944
+ } : void 0;
7945
+ const timestampColumns = options.timestampColumns?.map((column) => ({ ...column })) ?? persistedMetadata.timestampColumns?.map((column) => ({ ...column }));
7946
+ const buildMetadata = () => {
7947
+ return {
7948
+ table,
7949
+ primaryKey,
7950
+ columns: { ...columns },
7951
+ softDelete: { ...softDelete },
7952
+ primaryKeyGeneration,
7953
+ timestampColumns
7954
+ };
7955
+ };
7956
+ const modelStatic = {
7957
+ query: () => new QueryBuilder(modelStatic, modelStatic.getAdapter()),
7958
+ hydrate: (attributes) => ({ ...attributes }),
7959
+ hydrateMany: (attributes) => attributes.map((attribute) => ({ ...attribute })),
7960
+ hydrateRetrieved: async (attributes) => ({ ...attributes }),
7961
+ hydrateManyRetrieved: async (attributes) => attributes.map((attribute) => ({ ...attribute })),
7962
+ getAdapter: () => resolvedAdapter,
7963
+ getColumnMap: () => ({ ...columns }),
7964
+ getColumnName: (attribute) => columns[attribute] ?? attribute,
7965
+ getModelMetadata: () => buildMetadata(),
7966
+ getPrimaryKey: () => primaryKey,
7967
+ getRelationMetadata: () => null,
7968
+ setAdapter: () => {},
7969
+ getSoftDeleteConfig: () => ({ ...softDelete }),
7970
+ getTable: () => table
7971
+ };
7972
+ return modelStatic;
7973
+ }
7974
+ static resolvePersistedTableMetadata(table, options, adapter) {
7975
+ if (options.persistedMetadata === false) return {
7976
+ columns: {},
7977
+ enums: {}
7978
+ };
7979
+ const persistedMetadataOptions = typeof options.persistedMetadata === "object" ? options.persistedMetadata : {};
7980
+ return require_relationship.getPersistedTableMetadata(table, {
7981
+ cwd: persistedMetadataOptions.cwd,
7982
+ configuredPath: persistedMetadataOptions.configuredPath,
7983
+ features: require_relationship.resolvePersistedMetadataFeatures(require_relationship.getUserConfig("features")),
7984
+ strict: persistedMetadataOptions.strict ?? (Boolean(adapter) && !(adapter instanceof PrismaDatabaseAdapter))
7985
+ });
7986
+ }
7987
+ };
7988
+
7723
7989
  //#endregion
7724
7990
  //#region src/PivotModel.ts
7725
7991
  /**