arkormx 0.2.11 → 1.1.0

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
@@ -34,6 +34,7 @@ let path = require("path");
34
34
  path = __toESM(path);
35
35
  let fs = require("fs");
36
36
  let url = require("url");
37
+ let async_hooks = require("async_hooks");
37
38
  let module$1 = require("module");
38
39
  let _h3ravel_shared = require("@h3ravel/shared");
39
40
  let _h3ravel_musket = require("@h3ravel/musket");
@@ -122,17 +123,43 @@ function resolveCast(definition) {
122
123
 
123
124
  //#endregion
124
125
  //#region src/Exceptions/ArkormException.ts
125
- /**
126
- * The ArkormException class is a custom error type for handling
127
- * exceptions specific to the Arkormˣ.
128
- *
129
- * @author Legacy (3m1n3nc3)
130
- * @since 0.1.0
131
- */
132
126
  var ArkormException = class extends Error {
133
- constructor(message) {
134
- super(message);
127
+ code;
128
+ operation;
129
+ model;
130
+ delegate;
131
+ relation;
132
+ scope;
133
+ meta;
134
+ constructor(message, context = {}) {
135
+ super(message, context.cause === void 0 ? void 0 : { cause: context.cause });
135
136
  this.name = "ArkormException";
137
+ this.code = context.code;
138
+ this.operation = context.operation;
139
+ this.model = context.model;
140
+ this.delegate = context.delegate;
141
+ this.relation = context.relation;
142
+ this.scope = context.scope;
143
+ this.meta = context.meta;
144
+ }
145
+ getContext() {
146
+ return {
147
+ code: this.code,
148
+ operation: this.operation,
149
+ model: this.model,
150
+ delegate: this.delegate,
151
+ relation: this.relation,
152
+ scope: this.scope,
153
+ meta: this.meta,
154
+ cause: this.cause
155
+ };
156
+ }
157
+ toJSON() {
158
+ return {
159
+ name: this.name,
160
+ message: this.message,
161
+ ...this.getContext()
162
+ };
136
163
  }
137
164
  };
138
165
 
@@ -1242,6 +1269,18 @@ const runMigrationWithPrisma = async (migration, options = {}) => {
1242
1269
  };
1243
1270
  };
1244
1271
 
1272
+ //#endregion
1273
+ //#region src/Exceptions/UnsupportedAdapterFeatureException.ts
1274
+ var UnsupportedAdapterFeatureException = class extends ArkormException {
1275
+ constructor(message, context = {}) {
1276
+ super(message, {
1277
+ code: "UNSUPPORTED_ADAPTER_FEATURE",
1278
+ ...context
1279
+ });
1280
+ this.name = "UnsupportedAdapterFeatureException";
1281
+ }
1282
+ };
1283
+
1245
1284
  //#endregion
1246
1285
  //#region src/helpers/runtime-config.ts
1247
1286
  const resolveDefaultStubsPath = () => {
@@ -1276,6 +1315,7 @@ let runtimeConfigLoadingPromise;
1276
1315
  let runtimeClientResolver;
1277
1316
  let runtimePaginationURLDriverFactory;
1278
1317
  let runtimePaginationCurrentPageResolver;
1318
+ const transactionClientStorage = new async_hooks.AsyncLocalStorage();
1279
1319
  const mergePathConfig = (paths) => {
1280
1320
  const defaults = baseConfig.paths ?? {};
1281
1321
  const current = userConfig.paths ?? {};
@@ -1443,9 +1483,36 @@ const getDefaultStubsPath = () => {
1443
1483
  * @returns
1444
1484
  */
1445
1485
  const getRuntimePrismaClient = () => {
1486
+ const activeTransactionClient = transactionClientStorage.getStore();
1487
+ if (activeTransactionClient) return activeTransactionClient;
1446
1488
  if (!runtimeConfigLoaded) loadRuntimeConfigSync();
1447
1489
  return resolveClient(runtimeClientResolver);
1448
1490
  };
1491
+ const getActiveTransactionClient = () => {
1492
+ return transactionClientStorage.getStore();
1493
+ };
1494
+ const isTransactionCapableClient = (value) => {
1495
+ if (!value || typeof value !== "object") return false;
1496
+ return typeof value.$transaction === "function";
1497
+ };
1498
+ const runArkormTransaction = async (callback, options = {}) => {
1499
+ const activeTransactionClient = transactionClientStorage.getStore();
1500
+ if (activeTransactionClient) return await callback(activeTransactionClient);
1501
+ const client = getRuntimePrismaClient();
1502
+ if (!client) throw new ArkormException("Cannot start a transaction without a configured Prisma client.", {
1503
+ code: "CLIENT_NOT_CONFIGURED",
1504
+ operation: "transaction"
1505
+ });
1506
+ if (!isTransactionCapableClient(client)) throw new UnsupportedAdapterFeatureException("Transactions are not supported by the current adapter.", {
1507
+ code: "TRANSACTION_NOT_SUPPORTED",
1508
+ operation: "transaction"
1509
+ });
1510
+ return await client.$transaction(async (transactionClient) => {
1511
+ return await transactionClientStorage.run(transactionClient, async () => {
1512
+ return await callback(transactionClient);
1513
+ });
1514
+ }, options);
1515
+ };
1449
1516
  /**
1450
1517
  * Get the configured pagination URL driver factory from runtime config.
1451
1518
  *
@@ -2801,6 +2868,18 @@ const defineFactory = (model, definition) => {
2801
2868
  return new InlineFactory(model, definition);
2802
2869
  };
2803
2870
 
2871
+ //#endregion
2872
+ //#region src/Exceptions/MissingDelegateException.ts
2873
+ var MissingDelegateException = class extends ArkormException {
2874
+ constructor(message, context = {}) {
2875
+ super(message, {
2876
+ code: "MISSING_DELEGATE",
2877
+ ...context
2878
+ });
2879
+ this.name = "MissingDelegateException";
2880
+ }
2881
+ };
2882
+
2804
2883
  //#endregion
2805
2884
  //#region src/Exceptions/ModelNotFoundException.ts
2806
2885
  /**
@@ -2812,8 +2891,12 @@ const defineFactory = (model, definition) => {
2812
2891
  */
2813
2892
  var ModelNotFoundException = class extends ArkormException {
2814
2893
  modelName;
2815
- constructor(modelName, message = "No query results for the given model.") {
2816
- super(message);
2894
+ constructor(modelName, message = "No query results for the given model.", context = {}) {
2895
+ super(message, {
2896
+ code: "MODEL_NOT_FOUND",
2897
+ model: modelName,
2898
+ ...context
2899
+ });
2817
2900
  this.name = "ModelNotFoundException";
2818
2901
  this.modelName = modelName;
2819
2902
  }
@@ -2822,6 +2905,54 @@ var ModelNotFoundException = class extends ArkormException {
2822
2905
  }
2823
2906
  };
2824
2907
 
2908
+ //#endregion
2909
+ //#region src/Exceptions/QueryConstraintException.ts
2910
+ var QueryConstraintException = class extends ArkormException {
2911
+ constructor(message, context = {}) {
2912
+ super(message, {
2913
+ code: "QUERY_CONSTRAINT",
2914
+ ...context
2915
+ });
2916
+ this.name = "QueryConstraintException";
2917
+ }
2918
+ };
2919
+
2920
+ //#endregion
2921
+ //#region src/Exceptions/RelationResolutionException.ts
2922
+ var RelationResolutionException = class extends ArkormException {
2923
+ constructor(message, context = {}) {
2924
+ super(message, {
2925
+ code: "RELATION_RESOLUTION_FAILED",
2926
+ ...context
2927
+ });
2928
+ this.name = "RelationResolutionException";
2929
+ }
2930
+ };
2931
+
2932
+ //#endregion
2933
+ //#region src/Exceptions/ScopeNotDefinedException.ts
2934
+ var ScopeNotDefinedException = class extends ArkormException {
2935
+ constructor(message, context = {}) {
2936
+ super(message, {
2937
+ code: "SCOPE_NOT_DEFINED",
2938
+ ...context
2939
+ });
2940
+ this.name = "ScopeNotDefinedException";
2941
+ }
2942
+ };
2943
+
2944
+ //#endregion
2945
+ //#region src/Exceptions/UniqueConstraintResolutionException.ts
2946
+ var UniqueConstraintResolutionException = class extends ArkormException {
2947
+ constructor(message, context = {}) {
2948
+ super(message, {
2949
+ code: "UNIQUE_CONSTRAINT_RESOLUTION_FAILED",
2950
+ ...context
2951
+ });
2952
+ this.name = "UniqueConstraintResolutionException";
2953
+ }
2954
+ };
2955
+
2825
2956
  //#endregion
2826
2957
  //#region src/helpers/prisma.ts
2827
2958
  /**
@@ -3062,6 +3193,7 @@ var BelongsToManyRelation = class extends Relation {
3062
3193
  async getResults() {
3063
3194
  const parentValue = this.parent.getAttribute(this.parentKey);
3064
3195
  const ids = (await this.related.getDelegate(this.throughDelegate).findMany({ where: { [this.foreignPivotKey]: parentValue } })).map((row) => row[this.relatedPivotKey]);
3196
+ if (ids.length === 0) return new ArkormCollection([]);
3065
3197
  return this.applyConstraint(this.related.query().where({ [this.relatedKey]: { in: ids } })).get();
3066
3198
  }
3067
3199
  };
@@ -3148,6 +3280,7 @@ var HasManyThroughRelation = class extends Relation {
3148
3280
  async getResults() {
3149
3281
  const localValue = this.parent.getAttribute(this.localKey);
3150
3282
  const keys = (await this.related.getDelegate(this.throughDelegate).findMany({ where: { [this.firstKey]: localValue } })).map((row) => row[this.secondLocalKey]);
3283
+ if (keys.length === 0) return new ArkormCollection([]);
3151
3284
  return this.applyConstraint(this.related.query().where({ [this.secondKey]: { in: keys } })).get();
3152
3285
  }
3153
3286
  };
@@ -3305,6 +3438,7 @@ var MorphToManyRelation = class extends Relation {
3305
3438
  [`${this.morphName}Id`]: parentValue,
3306
3439
  [`${this.morphName}Type`]: morphType
3307
3440
  } })).map((row) => row[this.relatedPivotKey]);
3441
+ if (ids.length === 0) return new ArkormCollection([]);
3308
3442
  return this.applyConstraint(this.related.query().where({ [this.relatedKey]: { in: ids } })).get();
3309
3443
  }
3310
3444
  };
@@ -4056,7 +4190,11 @@ var QueryBuilder = class QueryBuilder {
4056
4190
  scope(name, ...args) {
4057
4191
  const methodName = `scope${name.charAt(0).toUpperCase()}${name.slice(1)}`;
4058
4192
  const scope = this.model.prototype?.[methodName];
4059
- if (typeof scope !== "function") throw new ArkormException(`Scope [${name}] is not defined.`);
4193
+ if (typeof scope !== "function") throw new ScopeNotDefinedException(`Scope [${name}] is not defined.`, {
4194
+ operation: "scope",
4195
+ model: this.model.name,
4196
+ scope: name
4197
+ });
4060
4198
  const scoped = scope.call(void 0, this, ...args);
4061
4199
  if (scoped && scoped !== this) return scoped;
4062
4200
  return this;
@@ -4178,7 +4316,7 @@ var QueryBuilder = class QueryBuilder {
4178
4316
  const relationCache = /* @__PURE__ */ new WeakMap();
4179
4317
  const rows = await this.delegate.findMany(this.buildFindArgs());
4180
4318
  const normalizedRows = this.randomOrderEnabled ? this.shuffleRows(rows) : rows;
4181
- const models = this.model.hydrateMany(normalizedRows);
4319
+ const models = await this.model.hydrateManyRetrieved(normalizedRows);
4182
4320
  let filteredModels = models;
4183
4321
  if (this.hasRelationFilters()) if (this.hasOrRelationFilters() && this.args.where) {
4184
4322
  const baseIds = new Set(models.map((model) => this.getModelId(model)).filter((id) => id != null));
@@ -4208,13 +4346,13 @@ var QueryBuilder = class QueryBuilder {
4208
4346
  if (rows.length === 0) return null;
4209
4347
  const row = this.shuffleRows(rows)[0];
4210
4348
  if (!row) return null;
4211
- const model = this.model.hydrate(row);
4349
+ const model = await this.model.hydrateRetrieved(row);
4212
4350
  await model.load(this.eagerLoads);
4213
4351
  return model;
4214
4352
  }
4215
4353
  const row = await this.delegate.findFirst(this.buildFindArgs());
4216
4354
  if (!row) return null;
4217
- const model = this.model.hydrate(row);
4355
+ const model = await this.model.hydrateRetrieved(row);
4218
4356
  await model.load(this.eagerLoads);
4219
4357
  return model;
4220
4358
  }
@@ -4234,7 +4372,10 @@ var QueryBuilder = class QueryBuilder {
4234
4372
  async findOr(value, keyOrCallback, maybeCallback) {
4235
4373
  const key = typeof keyOrCallback === "string" ? keyOrCallback : "id";
4236
4374
  const callback = typeof keyOrCallback === "function" ? keyOrCallback : maybeCallback;
4237
- if (!callback) throw new ArkormException("findOr requires a fallback callback.");
4375
+ if (!callback) throw new QueryConstraintException("findOr requires a fallback callback.", {
4376
+ operation: "findOr",
4377
+ model: this.model.name
4378
+ });
4238
4379
  const found = await this.find(value, key);
4239
4380
  if (found) return found;
4240
4381
  return callback();
@@ -4348,7 +4489,11 @@ var QueryBuilder = class QueryBuilder {
4348
4489
  async insertGetId(values, sequence) {
4349
4490
  const created = await this.delegate.create({ data: values });
4350
4491
  const key = sequence ?? "id";
4351
- if (!(key in created)) throw new ArkormException(`Inserted record does not contain key [${key}].`);
4492
+ if (!(key in created)) throw new UniqueConstraintResolutionException(`Inserted record does not contain key [${key}].`, {
4493
+ operation: "insertGetId",
4494
+ model: this.model.name,
4495
+ meta: { key }
4496
+ });
4352
4497
  return created[key];
4353
4498
  }
4354
4499
  /**
@@ -4385,7 +4530,10 @@ var QueryBuilder = class QueryBuilder {
4385
4530
  */
4386
4531
  async update(data) {
4387
4532
  const where = this.buildWhere();
4388
- if (!where) throw new ArkormException("Update requires a where clause.");
4533
+ if (!where) throw new QueryConstraintException("Update requires a where clause.", {
4534
+ operation: "update",
4535
+ model: this.model.name
4536
+ });
4389
4537
  const uniqueWhere = await this.resolveUniqueWhere(where);
4390
4538
  const updated = await this.delegate.update({
4391
4539
  where: uniqueWhere,
@@ -4401,7 +4549,10 @@ var QueryBuilder = class QueryBuilder {
4401
4549
  */
4402
4550
  async updateFrom(data) {
4403
4551
  const where = this.buildWhere();
4404
- if (!where) throw new ArkormException("Update requires a where clause.");
4552
+ if (!where) throw new QueryConstraintException("Update requires a where clause.", {
4553
+ operation: "updateFrom",
4554
+ model: this.model.name
4555
+ });
4405
4556
  const delegate = this.delegate;
4406
4557
  if (typeof delegate.updateMany === "function") {
4407
4558
  const result = await delegate.updateMany({
@@ -4466,7 +4617,10 @@ var QueryBuilder = class QueryBuilder {
4466
4617
  */
4467
4618
  async delete() {
4468
4619
  const where = this.buildWhere();
4469
- if (!where) throw new ArkormException("Delete requires a where clause.");
4620
+ if (!where) throw new QueryConstraintException("Delete requires a where clause.", {
4621
+ operation: "delete",
4622
+ model: this.model.name
4623
+ });
4470
4624
  const uniqueWhere = await this.resolveUniqueWhere(where);
4471
4625
  const deleted = await this.delegate.delete({ where: uniqueWhere });
4472
4626
  return this.model.hydrate(deleted);
@@ -4532,7 +4686,10 @@ var QueryBuilder = class QueryBuilder {
4532
4686
  if (Array.isArray(source)) return source;
4533
4687
  }
4534
4688
  if (Array.isArray(source)) return source;
4535
- throw new ArkormException("insertUsing expects a query builder, array of records, or async resolver.");
4689
+ throw new QueryConstraintException("insertUsing expects a query builder, array of records, or async resolver.", {
4690
+ operation: "insertUsing",
4691
+ model: this.model.name
4692
+ });
4536
4693
  }
4537
4694
  /**
4538
4695
  * Execute callback when no records exist.
@@ -4616,7 +4773,11 @@ var QueryBuilder = class QueryBuilder {
4616
4773
  */
4617
4774
  whereRaw(sql, bindings = []) {
4618
4775
  const delegate = this.delegate;
4619
- if (typeof delegate.applyRawWhere !== "function") throw new ArkormException("Raw where clauses are not supported by the current adapter.");
4776
+ if (typeof delegate.applyRawWhere !== "function") throw new UnsupportedAdapterFeatureException("Raw where clauses are not supported by the current adapter.", {
4777
+ operation: "whereRaw",
4778
+ model: this.model.name,
4779
+ meta: { feature: "rawWhere" }
4780
+ });
4620
4781
  this.args.where = delegate.applyRawWhere(this.buildWhere(), sql, bindings);
4621
4782
  return this;
4622
4783
  }
@@ -4629,7 +4790,11 @@ var QueryBuilder = class QueryBuilder {
4629
4790
  */
4630
4791
  orWhereRaw(sql, bindings = []) {
4631
4792
  const delegate = this.delegate;
4632
- if (typeof delegate.applyRawWhere !== "function") throw new ArkormException("Raw where clauses are not supported by the current adapter.");
4793
+ if (typeof delegate.applyRawWhere !== "function") throw new UnsupportedAdapterFeatureException("Raw where clauses are not supported by the current adapter.", {
4794
+ operation: "orWhereRaw",
4795
+ model: this.model.name,
4796
+ meta: { feature: "rawWhere" }
4797
+ });
4633
4798
  const rawWhere = delegate.applyRawWhere(void 0, sql, bindings);
4634
4799
  return this.orWhere(rawWhere);
4635
4800
  }
@@ -4751,9 +4916,16 @@ var QueryBuilder = class QueryBuilder {
4751
4916
  async resolveUniqueWhere(where) {
4752
4917
  if (this.isUniqueWhere(where)) return where;
4753
4918
  const row = await this.delegate.findFirst({ where });
4754
- if (!row) throw new ArkormException("Record not found for update/delete operation.");
4919
+ if (!row) throw new ModelNotFoundException(this.model.name, "Record not found for update/delete operation.", {
4920
+ operation: "resolveUniqueWhere",
4921
+ meta: { where }
4922
+ });
4755
4923
  const record = row;
4756
- if (!Object.prototype.hasOwnProperty.call(record, "id")) throw new ArkormException("Unable to resolve a unique identifier for update/delete operation. Include an id in the query constraints.");
4924
+ if (!Object.prototype.hasOwnProperty.call(record, "id")) throw new UniqueConstraintResolutionException("Unable to resolve a unique identifier for update/delete operation. Include an id in the query constraints.", {
4925
+ operation: "resolveUniqueWhere",
4926
+ model: this.model.name,
4927
+ meta: { where }
4928
+ });
4757
4929
  return { id: record.id };
4758
4930
  }
4759
4931
  /**
@@ -4876,7 +5048,11 @@ var QueryBuilder = class QueryBuilder {
4876
5048
  if (cached) return await cached;
4877
5049
  const resolver = (async () => {
4878
5050
  const relationMethod = model[relation];
4879
- if (typeof relationMethod !== "function") throw new ArkormException(`Relation [${relation}] is not defined on the model.`);
5051
+ if (typeof relationMethod !== "function") throw new RelationResolutionException(`Relation [${relation}] is not defined on the model.`, {
5052
+ operation: "resolveRelatedResults",
5053
+ model: this.model.name,
5054
+ relation
5055
+ });
4880
5056
  const relationInstance = relationMethod.call(model);
4881
5057
  if (callback && typeof relationInstance.constrain === "function") relationInstance.constrain((query) => {
4882
5058
  return callback(query) ?? query;
@@ -4891,7 +5067,11 @@ var QueryBuilder = class QueryBuilder {
4891
5067
  if (results instanceof ArkormCollection) return results.all();
4892
5068
  return results;
4893
5069
  }
4894
- throw new ArkormException(`Relation [${relation}] does not support result resolution.`);
5070
+ throw new RelationResolutionException(`Relation [${relation}] does not support result resolution.`, {
5071
+ operation: "resolveRelatedResults",
5072
+ model: this.model.name,
5073
+ relation
5074
+ });
4895
5075
  })();
4896
5076
  callbackMap.set(callbackCacheKey, resolver);
4897
5077
  return await resolver;
@@ -4932,6 +5112,8 @@ var QueryBuilder = class QueryBuilder {
4932
5112
  * @since 0.1.0
4933
5113
  */
4934
5114
  var Model = class Model {
5115
+ static lifecycleStates = /* @__PURE__ */ new WeakMap();
5116
+ static eventsSuppressed = 0;
4935
5117
  static factoryClass;
4936
5118
  static client;
4937
5119
  static delegate;
@@ -4939,13 +5121,20 @@ var Model = class Model {
4939
5121
  static deletedAtColumn = "deletedAt";
4940
5122
  static globalScopes = {};
4941
5123
  static eventListeners = {};
5124
+ static dispatchesEvents = {};
4942
5125
  casts = {};
4943
5126
  hidden = [];
4944
5127
  visible = [];
4945
5128
  appends = [];
4946
5129
  attributes;
5130
+ original;
5131
+ changes;
5132
+ touchedAttributes;
4947
5133
  constructor(attributes = {}) {
4948
5134
  this.attributes = {};
5135
+ this.original = {};
5136
+ this.changes = {};
5137
+ this.touchedAttributes = /* @__PURE__ */ new Set();
4949
5138
  this.fill(attributes);
4950
5139
  return new Proxy(this, {
4951
5140
  get: (target, key, receiver) => {
@@ -4976,7 +5165,11 @@ var Model = class Model {
4976
5165
  }
4977
5166
  static factory(count) {
4978
5167
  const factoryClass = this.factoryClass;
4979
- if (!factoryClass) throw new ArkormException(`Factory is not configured for model [${this.name}].`);
5168
+ if (!factoryClass) throw new ArkormException(`Factory is not configured for model [${this.name}].`, {
5169
+ code: "FACTORY_NOT_CONFIGURED",
5170
+ operation: "factory",
5171
+ model: this.name
5172
+ });
4980
5173
  const factory = new factoryClass();
4981
5174
  if (typeof count === "number") factory.count(count);
4982
5175
  return factory;
@@ -4992,6 +5185,21 @@ var Model = class Model {
4992
5185
  this.globalScopes[name] = scope;
4993
5186
  }
4994
5187
  /**
5188
+ * Execute a callback without applying global scopes for the current model class.
5189
+ *
5190
+ * @param callback
5191
+ * @returns
5192
+ */
5193
+ static async withoutGlobalScopes(callback) {
5194
+ const state = Model.getLifecycleState(this);
5195
+ state.globalScopesSuppressed += 1;
5196
+ try {
5197
+ return await callback();
5198
+ } finally {
5199
+ state.globalScopesSuppressed = Math.max(0, state.globalScopesSuppressed - 1);
5200
+ }
5201
+ }
5202
+ /**
4995
5203
  * Remove a global scope by name.
4996
5204
  *
4997
5205
  * @param name
@@ -5013,11 +5221,60 @@ var Model = class Model {
5013
5221
  * @param listener
5014
5222
  */
5015
5223
  static on(event, listener) {
5224
+ Model.ensureModelBooted(this);
5016
5225
  this.ensureOwnEventListeners();
5017
5226
  if (!this.eventListeners[event]) this.eventListeners[event] = [];
5018
5227
  this.eventListeners[event]?.push(listener);
5019
5228
  }
5020
5229
  /**
5230
+ * Register a model lifecycle callback listener.
5231
+ *
5232
+ * @param event
5233
+ * @param listener
5234
+ */
5235
+ static event(event, listener) {
5236
+ this.on(event, listener);
5237
+ }
5238
+ static retrieved(listener) {
5239
+ this.event("retrieved", listener);
5240
+ }
5241
+ static saving(listener) {
5242
+ this.event("saving", listener);
5243
+ }
5244
+ static saved(listener) {
5245
+ this.event("saved", listener);
5246
+ }
5247
+ static creating(listener) {
5248
+ this.event("creating", listener);
5249
+ }
5250
+ static created(listener) {
5251
+ this.event("created", listener);
5252
+ }
5253
+ static updating(listener) {
5254
+ this.event("updating", listener);
5255
+ }
5256
+ static updated(listener) {
5257
+ this.event("updated", listener);
5258
+ }
5259
+ static deleting(listener) {
5260
+ this.event("deleting", listener);
5261
+ }
5262
+ static deleted(listener) {
5263
+ this.event("deleted", listener);
5264
+ }
5265
+ static restoring(listener) {
5266
+ this.event("restoring", listener);
5267
+ }
5268
+ static restored(listener) {
5269
+ this.event("restored", listener);
5270
+ }
5271
+ static forceDeleting(listener) {
5272
+ this.event("forceDeleting", listener);
5273
+ }
5274
+ static forceDeleted(listener) {
5275
+ this.event("forceDeleted", listener);
5276
+ }
5277
+ /**
5021
5278
  * Remove listeners for an event. If listener is omitted, all listeners for that event are removed.
5022
5279
  *
5023
5280
  * @param event
@@ -5038,6 +5295,34 @@ var Model = class Model {
5038
5295
  this.eventListeners = {};
5039
5296
  }
5040
5297
  /**
5298
+ * Execute a callback while suppressing lifecycle events for all models.
5299
+ *
5300
+ * @param callback
5301
+ * @returns
5302
+ */
5303
+ static async withoutEvents(callback) {
5304
+ Model.eventsSuppressed += 1;
5305
+ try {
5306
+ return await callback();
5307
+ } finally {
5308
+ Model.eventsSuppressed = Math.max(0, Model.eventsSuppressed - 1);
5309
+ }
5310
+ }
5311
+ /**
5312
+ * Execute a callback within a transaction scope.
5313
+ * Nested calls reuse the active transaction client.
5314
+ *
5315
+ * @param callback
5316
+ * @param options
5317
+ * @returns
5318
+ */
5319
+ static async transaction(callback, options = {}) {
5320
+ ensureArkormConfigLoading();
5321
+ return await runArkormTransaction(async (client) => {
5322
+ return await callback(client);
5323
+ }, options);
5324
+ }
5325
+ /**
5041
5326
  * Get the Prisma delegate for the model.
5042
5327
  * If a delegate name is provided, it will attempt to resolve that delegate.
5043
5328
  * Otherwise, it will attempt to resolve a delegate based on the model's name or
@@ -5055,9 +5340,20 @@ var Model = class Model {
5055
5340
  `${(0, _h3ravel_support.str)(key).singular()}`,
5056
5341
  `${(0, _h3ravel_support.str)(key).camel().singular()}`
5057
5342
  ];
5343
+ const activeTransactionClient = getActiveTransactionClient();
5058
5344
  const runtimeClient = getRuntimePrismaClient();
5059
- const resolved = candidates.map((name) => this.client?.[name] ?? runtimeClient?.[name]).find((candidate) => isDelegateLike(candidate));
5060
- if (!resolved) throw new ArkormException(`Database delegate [${key}] is not configured.`);
5345
+ const sources = activeTransactionClient ? [
5346
+ activeTransactionClient,
5347
+ this.client,
5348
+ runtimeClient
5349
+ ] : [this.client, runtimeClient];
5350
+ const resolved = candidates.flatMap((name) => sources.map((source) => source?.[name])).find((candidate) => isDelegateLike(candidate));
5351
+ if (!resolved) throw new MissingDelegateException(`Database delegate [${key}] is not configured.`, {
5352
+ operation: "getDelegate",
5353
+ model: this.name,
5354
+ delegate: key,
5355
+ meta: { candidates }
5356
+ });
5061
5357
  return resolved;
5062
5358
  }
5063
5359
  /**
@@ -5067,16 +5363,27 @@ var Model = class Model {
5067
5363
  * @returns
5068
5364
  */
5069
5365
  static query() {
5366
+ Model.ensureModelBooted(this);
5070
5367
  let builder = new QueryBuilder(this.getDelegate(), this);
5071
5368
  const modelClass = this;
5072
- modelClass.ensureOwnGlobalScopes();
5073
- Object.values(modelClass.globalScopes).forEach((scope) => {
5074
- const scoped = scope(builder);
5075
- if (scoped && scoped !== builder) builder = scoped;
5076
- });
5369
+ if (!Model.areGlobalScopesSuppressed(modelClass)) {
5370
+ modelClass.ensureOwnGlobalScopes();
5371
+ Object.values(modelClass.globalScopes).forEach((scope) => {
5372
+ const scoped = scope(builder);
5373
+ if (scoped && scoped !== builder) builder = scoped;
5374
+ });
5375
+ }
5077
5376
  return builder;
5078
5377
  }
5079
5378
  /**
5379
+ * Boot hook for subclasses to register scopes or perform one-time setup.
5380
+ */
5381
+ static boot() {}
5382
+ /**
5383
+ * Booted hook for subclasses to register callbacks after boot logic runs.
5384
+ */
5385
+ static booted() {}
5386
+ /**
5080
5387
  * Get a query builder instance that includes soft-deleted records.
5081
5388
  *
5082
5389
  * @param this
@@ -5127,7 +5434,10 @@ var Model = class Model {
5127
5434
  * @returns
5128
5435
  */
5129
5436
  static hydrate(attributes) {
5130
- return new this(attributes);
5437
+ const model = new this(attributes);
5438
+ model.syncOriginal();
5439
+ model.syncChanges({});
5440
+ return model;
5131
5441
  }
5132
5442
  /**
5133
5443
  * Hydrate multiple model instances from an array of plain objects of attributes.
@@ -5139,6 +5449,36 @@ var Model = class Model {
5139
5449
  static hydrateMany(attributes) {
5140
5450
  return attributes.map((attribute) => new this(attribute));
5141
5451
  }
5452
+ /**
5453
+ * Hydrate a model instance and dispatch the retrieved lifecycle event.
5454
+ *
5455
+ * @param this
5456
+ * @param attributes
5457
+ * @returns
5458
+ */
5459
+ static async hydrateRetrieved(attributes) {
5460
+ Model.ensureModelBooted(this);
5461
+ if (!Model.hasEventListeners(this, "retrieved")) return this.hydrate(attributes);
5462
+ const model = this.hydrate(attributes);
5463
+ await Model.dispatchEvent(this, "retrieved", model);
5464
+ return model;
5465
+ }
5466
+ /**
5467
+ * Hydrate multiple model instances and dispatch the retrieved lifecycle event for each.
5468
+ *
5469
+ * @param this
5470
+ * @param attributes
5471
+ * @returns
5472
+ */
5473
+ static async hydrateManyRetrieved(attributes) {
5474
+ Model.ensureModelBooted(this);
5475
+ if (!Model.hasEventListeners(this, "retrieved")) return this.hydrateMany(attributes);
5476
+ const models = this.hydrateMany(attributes);
5477
+ await Promise.all(models.map(async (model) => {
5478
+ await Model.dispatchEvent(this, "retrieved", model);
5479
+ }));
5480
+ return models;
5481
+ }
5142
5482
  fill(attributes) {
5143
5483
  Object.entries(attributes).forEach(([key, value]) => {
5144
5484
  this.setAttribute(key, value);
@@ -5164,6 +5504,7 @@ var Model = class Model {
5164
5504
  else if (mutator) resolved = mutator.call(this, resolved);
5165
5505
  if (cast) resolved = resolveCast(cast).set(resolved);
5166
5506
  this.attributes[key] = resolved;
5507
+ this.touchedAttributes.add(key);
5167
5508
  return this;
5168
5509
  }
5169
5510
  /**
@@ -5176,12 +5517,15 @@ var Model = class Model {
5176
5517
  async save() {
5177
5518
  const identifier = this.getAttribute("id");
5178
5519
  const payload = this.getRawAttributes();
5520
+ const previousOriginal = this.getOriginal();
5179
5521
  const constructor = this.constructor;
5180
5522
  if (identifier == null) {
5181
5523
  await Model.dispatchEvent(constructor, "saving", this);
5182
5524
  await Model.dispatchEvent(constructor, "creating", this);
5183
5525
  const model = await constructor.query().create(payload);
5184
5526
  this.fill(model.getRawAttributes());
5527
+ this.syncChanges(previousOriginal);
5528
+ this.syncOriginal();
5185
5529
  await Model.dispatchEvent(constructor, "created", this);
5186
5530
  await Model.dispatchEvent(constructor, "saved", this);
5187
5531
  return this;
@@ -5190,11 +5534,21 @@ var Model = class Model {
5190
5534
  await Model.dispatchEvent(constructor, "updating", this);
5191
5535
  const model = await constructor.query().where({ id: identifier }).update(payload);
5192
5536
  this.fill(model.getRawAttributes());
5537
+ this.syncChanges(previousOriginal);
5538
+ this.syncOriginal();
5193
5539
  await Model.dispatchEvent(constructor, "updated", this);
5194
5540
  await Model.dispatchEvent(constructor, "saved", this);
5195
5541
  return this;
5196
5542
  }
5197
5543
  /**
5544
+ * Save the model without dispatching lifecycle events.
5545
+ *
5546
+ * @returns
5547
+ */
5548
+ async saveQuietly() {
5549
+ return await Model.withoutEvents(() => this.save());
5550
+ }
5551
+ /**
5198
5552
  * Delete the model from the database.
5199
5553
  * If soft deletes are enabled, it will perform a soft delete by
5200
5554
  * setting the deleted at column to the current date.
@@ -5205,21 +5559,34 @@ var Model = class Model {
5205
5559
  async delete() {
5206
5560
  const identifier = this.getAttribute("id");
5207
5561
  if (identifier == null) throw new ArkormException("Cannot delete a model without an id.");
5562
+ const previousOriginal = this.getOriginal();
5208
5563
  const constructor = this.constructor;
5209
5564
  await Model.dispatchEvent(constructor, "deleting", this);
5210
5565
  const softDeleteConfig = constructor.getSoftDeleteConfig();
5211
5566
  if (softDeleteConfig.enabled) {
5212
5567
  const model = await constructor.query().where({ id: identifier }).update({ [softDeleteConfig.column]: /* @__PURE__ */ new Date() });
5213
5568
  this.fill(model.getRawAttributes());
5569
+ this.syncChanges(previousOriginal);
5570
+ this.syncOriginal();
5214
5571
  await Model.dispatchEvent(constructor, "deleted", this);
5215
5572
  return this;
5216
5573
  }
5217
5574
  const deleted = await constructor.query().where({ id: identifier }).delete();
5218
5575
  this.fill(deleted.getRawAttributes());
5576
+ this.syncChanges(previousOriginal);
5577
+ this.syncOriginal();
5219
5578
  await Model.dispatchEvent(constructor, "deleted", this);
5220
5579
  return this;
5221
5580
  }
5222
5581
  /**
5582
+ * Delete the model without dispatching lifecycle events.
5583
+ *
5584
+ * @returns
5585
+ */
5586
+ async deleteQuietly() {
5587
+ return await Model.withoutEvents(() => this.delete());
5588
+ }
5589
+ /**
5223
5590
  * Permanently delete the model from the database, regardless of whether soft
5224
5591
  * deletes are enabled.
5225
5592
  *
@@ -5228,16 +5595,27 @@ var Model = class Model {
5228
5595
  async forceDelete() {
5229
5596
  const identifier = this.getAttribute("id");
5230
5597
  if (identifier == null) throw new ArkormException("Cannot force delete a model without an id.");
5598
+ const previousOriginal = this.getOriginal();
5231
5599
  const constructor = this.constructor;
5232
5600
  await Model.dispatchEvent(constructor, "forceDeleting", this);
5233
5601
  await Model.dispatchEvent(constructor, "deleting", this);
5234
5602
  const deleted = await constructor.query().withTrashed().where({ id: identifier }).delete();
5235
5603
  this.fill(deleted.getRawAttributes());
5604
+ this.syncChanges(previousOriginal);
5605
+ this.syncOriginal();
5236
5606
  await Model.dispatchEvent(constructor, "deleted", this);
5237
5607
  await Model.dispatchEvent(constructor, "forceDeleted", this);
5238
5608
  return this;
5239
5609
  }
5240
5610
  /**
5611
+ * Force delete the model without dispatching lifecycle events.
5612
+ *
5613
+ * @returns
5614
+ */
5615
+ async forceDeleteQuietly() {
5616
+ return await Model.withoutEvents(() => this.forceDelete());
5617
+ }
5618
+ /**
5241
5619
  * Restore a soft-deleted model by setting the deleted at column to null.
5242
5620
  *
5243
5621
  * @returns
@@ -5248,13 +5626,24 @@ var Model = class Model {
5248
5626
  const constructor = this.constructor;
5249
5627
  const softDeleteConfig = constructor.getSoftDeleteConfig();
5250
5628
  if (!softDeleteConfig.enabled) return this;
5629
+ const previousOriginal = this.getOriginal();
5251
5630
  await Model.dispatchEvent(constructor, "restoring", this);
5252
5631
  const model = await constructor.query().withTrashed().where({ id: identifier }).update({ [softDeleteConfig.column]: null });
5253
5632
  this.fill(model.getRawAttributes());
5633
+ this.syncChanges(previousOriginal);
5634
+ this.syncOriginal();
5254
5635
  await Model.dispatchEvent(constructor, "restored", this);
5255
5636
  return this;
5256
5637
  }
5257
5638
  /**
5639
+ * Restore the model without dispatching lifecycle events.
5640
+ *
5641
+ * @returns
5642
+ */
5643
+ async restoreQuietly() {
5644
+ return await Model.withoutEvents(() => this.restore());
5645
+ }
5646
+ /**
5258
5647
  * Load related models onto the current model instance.
5259
5648
  *
5260
5649
  * @param relations
@@ -5280,6 +5669,42 @@ var Model = class Model {
5280
5669
  getRawAttributes() {
5281
5670
  return { ...this.attributes };
5282
5671
  }
5672
+ getOriginal(key) {
5673
+ if (typeof key === "string") return Model.cloneAttributeValue(this.original[key]);
5674
+ return Object.entries(this.original).reduce((all, [originalKey, value]) => {
5675
+ all[originalKey] = Model.cloneAttributeValue(value);
5676
+ return all;
5677
+ }, {});
5678
+ }
5679
+ /**
5680
+ * Determine whether the model has unsaved attribute changes.
5681
+ *
5682
+ * @param keys
5683
+ * @returns
5684
+ */
5685
+ isDirty(keys) {
5686
+ return Object.keys(this.getDirtyAttributes(keys)).length > 0;
5687
+ }
5688
+ /**
5689
+ * Determine whether the model has no unsaved attribute changes.
5690
+ *
5691
+ * @param keys
5692
+ * @returns
5693
+ */
5694
+ isClean(keys) {
5695
+ return !this.isDirty(keys);
5696
+ }
5697
+ /**
5698
+ * Determine whether the model changed during the last successful persistence operation.
5699
+ *
5700
+ * @param keys
5701
+ * @returns
5702
+ */
5703
+ wasChanged(keys) {
5704
+ const keyList = this.normalizeAttributeKeys(keys);
5705
+ if (keyList.length === 0) return Object.keys(this.changes).length > 0;
5706
+ return keyList.some((key) => Object.prototype.hasOwnProperty.call(this.changes, key));
5707
+ }
5283
5708
  /**
5284
5709
  * Convert the model instance to a plain object, applying visibility
5285
5710
  * rules, appends, and mutators.
@@ -5307,6 +5732,47 @@ var Model = class Model {
5307
5732
  return this.toObject();
5308
5733
  }
5309
5734
  /**
5735
+ * Determine if another model represents the same persisted record.
5736
+ *
5737
+ * @param model
5738
+ * @returns
5739
+ */
5740
+ is(model) {
5741
+ if (!(model instanceof Model)) return false;
5742
+ if (this.constructor !== model.constructor) return false;
5743
+ const identifier = this.getAttribute("id");
5744
+ const otherIdentifier = model.getAttribute("id");
5745
+ if (identifier == null || otherIdentifier == null) return false;
5746
+ return identifier === otherIdentifier;
5747
+ }
5748
+ /**
5749
+ * Determine if another model does not represent the same persisted record.
5750
+ *
5751
+ * @param model
5752
+ * @returns
5753
+ */
5754
+ isNot(model) {
5755
+ return !this.is(model);
5756
+ }
5757
+ /**
5758
+ * Determine if another model is the same in-memory instance.
5759
+ *
5760
+ * @param model
5761
+ * @returns
5762
+ */
5763
+ isSame(model) {
5764
+ return this === model;
5765
+ }
5766
+ /**
5767
+ * Determine if another model is not the same in-memory instance.
5768
+ *
5769
+ * @param model
5770
+ * @returns
5771
+ */
5772
+ isNotSame(model) {
5773
+ return !this.isSame(model);
5774
+ }
5775
+ /**
5310
5776
  * Define a has one relationship.
5311
5777
  *
5312
5778
  * @param related
@@ -5429,6 +5895,34 @@ var Model = class Model {
5429
5895
  return typeof method === "function" ? method : null;
5430
5896
  }
5431
5897
  /**
5898
+ * Build a map of dirty attributes, optionally limited to specific keys.
5899
+ *
5900
+ * @param keys
5901
+ * @returns
5902
+ */
5903
+ getDirtyAttributes(keys) {
5904
+ const requestedKeys = this.normalizeAttributeKeys(keys);
5905
+ return (requestedKeys.length > 0 ? requestedKeys : Array.from(new Set([...Object.keys(this.original), ...this.touchedAttributes]))).reduce((dirty, key) => {
5906
+ const currentValue = this.attributes[key];
5907
+ const originalValue = this.original[key];
5908
+ const hasCurrent = Object.prototype.hasOwnProperty.call(this.attributes, key);
5909
+ const hasOriginal = Object.prototype.hasOwnProperty.call(this.original, key);
5910
+ if (!hasCurrent && !hasOriginal) return dirty;
5911
+ if (hasCurrent !== hasOriginal || !Model.areAttributeValuesEqual(currentValue, originalValue)) dirty[key] = Model.cloneAttributeValue(currentValue);
5912
+ return dirty;
5913
+ }, {});
5914
+ }
5915
+ /**
5916
+ * Normalize a key or key list for dirty/change lookups.
5917
+ *
5918
+ * @param keys
5919
+ * @returns
5920
+ */
5921
+ normalizeAttributeKeys(keys) {
5922
+ if (typeof keys === "undefined") return [];
5923
+ return Array.isArray(keys) ? keys : [keys];
5924
+ }
5925
+ /**
5432
5926
  * Resolve an Attribute object mutator method for a given key, if it exists.
5433
5927
  *
5434
5928
  * @param key
@@ -5470,6 +5964,144 @@ var Model = class Model {
5470
5964
  if (!Object.prototype.hasOwnProperty.call(this, "eventListeners")) this.eventListeners = { ...this.eventListeners || {} };
5471
5965
  }
5472
5966
  /**
5967
+ * Clone an attribute value to keep snapshot state isolated from live mutations.
5968
+ *
5969
+ * @param value
5970
+ * @returns
5971
+ */
5972
+ static cloneAttributeValue(value) {
5973
+ if (value instanceof Date) return new Date(value.getTime());
5974
+ if (Array.isArray(value)) return value.map((item) => Model.cloneAttributeValue(item));
5975
+ if (value && typeof value === "object") return Object.entries(value).reduce((all, [key, nestedValue]) => {
5976
+ all[key] = Model.cloneAttributeValue(nestedValue);
5977
+ return all;
5978
+ }, {});
5979
+ return value;
5980
+ }
5981
+ /**
5982
+ * Compare attribute values for dirty/change detection.
5983
+ *
5984
+ * @param left
5985
+ * @param right
5986
+ * @returns
5987
+ */
5988
+ static areAttributeValuesEqual(left, right) {
5989
+ if (left === right) return true;
5990
+ if (left instanceof Date && right instanceof Date) return left.getTime() === right.getTime();
5991
+ if (Array.isArray(left) && Array.isArray(right)) {
5992
+ if (left.length !== right.length) return false;
5993
+ return left.every((value, index) => Model.areAttributeValuesEqual(value, right[index]));
5994
+ }
5995
+ if (left && right && typeof left === "object" && typeof right === "object") {
5996
+ const leftEntries = Object.entries(left);
5997
+ const rightEntries = Object.entries(right);
5998
+ if (leftEntries.length !== rightEntries.length) return false;
5999
+ return leftEntries.every(([key, value]) => {
6000
+ return Object.prototype.hasOwnProperty.call(right, key) && Model.areAttributeValuesEqual(value, right[key]);
6001
+ });
6002
+ }
6003
+ return false;
6004
+ }
6005
+ /**
6006
+ * Sync the original snapshot to the model's current raw attributes.
6007
+ */
6008
+ syncOriginal() {
6009
+ this.original = Object.entries(this.attributes).reduce((all, [key, value]) => {
6010
+ all[key] = Model.cloneAttributeValue(value);
6011
+ return all;
6012
+ }, {});
6013
+ this.touchedAttributes.clear();
6014
+ }
6015
+ /**
6016
+ * Sync the last-changed snapshot from a previous original state.
6017
+ *
6018
+ * @param previousOriginal
6019
+ */
6020
+ syncChanges(previousOriginal) {
6021
+ this.changes = Object.entries(this.getDirtyAttributes()).reduce((all, [key, value]) => {
6022
+ if (!Object.prototype.hasOwnProperty.call(previousOriginal, key) || !Model.areAttributeValuesEqual(value, previousOriginal[key])) all[key] = Model.cloneAttributeValue(value);
6023
+ return all;
6024
+ }, {});
6025
+ }
6026
+ /**
6027
+ * Resolve lifecycle state for the provided model class.
6028
+ *
6029
+ * @param modelClass
6030
+ * @returns
6031
+ */
6032
+ static getLifecycleState(modelClass) {
6033
+ const existing = Model.lifecycleStates.get(modelClass);
6034
+ if (existing) return existing;
6035
+ const state = {
6036
+ booted: false,
6037
+ booting: false,
6038
+ globalScopesSuppressed: 0
6039
+ };
6040
+ Model.lifecycleStates.set(modelClass, state);
6041
+ return state;
6042
+ }
6043
+ /**
6044
+ * Ensure the target model class has executed its boot lifecycle.
6045
+ *
6046
+ * @param modelClass
6047
+ */
6048
+ static ensureModelBooted(modelClass) {
6049
+ const state = Model.getLifecycleState(modelClass);
6050
+ if (state.booted || state.booting) return;
6051
+ state.booting = true;
6052
+ try {
6053
+ const boot = modelClass.boot;
6054
+ if (boot !== Model.boot) boot.call(modelClass);
6055
+ const booted = modelClass.booted;
6056
+ if (booted !== Model.booted) booted.call(modelClass);
6057
+ state.booted = true;
6058
+ } finally {
6059
+ state.booting = false;
6060
+ }
6061
+ }
6062
+ /**
6063
+ * Determine if global scopes are currently suppressed for the model class.
6064
+ *
6065
+ * @param modelClass
6066
+ * @returns
6067
+ */
6068
+ static areGlobalScopesSuppressed(modelClass) {
6069
+ return Model.getLifecycleState(modelClass).globalScopesSuppressed > 0;
6070
+ }
6071
+ /**
6072
+ * Resolve configured class-based event handlers for a lifecycle event.
6073
+ *
6074
+ * @param modelClass
6075
+ * @param event
6076
+ * @returns
6077
+ */
6078
+ static resolveDispatchedEventListeners(modelClass, event) {
6079
+ const configured = modelClass.dispatchesEvents[event];
6080
+ if (!configured) return [];
6081
+ return (Array.isArray(configured) ? configured : [configured]).map((entry) => {
6082
+ const handler = typeof entry === "function" ? new entry() : entry;
6083
+ if (!handler || typeof handler.handle !== "function") throw new ArkormException(`Invalid event handler configured for [${modelClass.name}.${event}].`);
6084
+ return async (model) => {
6085
+ await handler.handle(model);
6086
+ };
6087
+ });
6088
+ }
6089
+ /**
6090
+ * Determine whether a lifecycle event has any registered listeners.
6091
+ *
6092
+ * @param modelClass
6093
+ * @param event
6094
+ * @returns
6095
+ */
6096
+ static hasEventListeners(modelClass, event) {
6097
+ if (Model.eventsSuppressed > 0) return false;
6098
+ modelClass.ensureOwnEventListeners();
6099
+ if ((modelClass.eventListeners[event] || []).length > 0) return true;
6100
+ const configuredDispatchers = modelClass.dispatchesEvents[event];
6101
+ if (!configuredDispatchers) return false;
6102
+ return Array.isArray(configuredDispatchers) ? configuredDispatchers.length > 0 : true;
6103
+ }
6104
+ /**
5473
6105
  * Dispatches lifecycle events to registered listeners.
5474
6106
  *
5475
6107
  * @param modelClass
@@ -5477,8 +6109,9 @@ var Model = class Model {
5477
6109
  * @param model
5478
6110
  */
5479
6111
  static async dispatchEvent(modelClass, event, model) {
5480
- modelClass.ensureOwnEventListeners();
5481
- const listeners = modelClass.eventListeners[event] || [];
6112
+ Model.ensureModelBooted(modelClass);
6113
+ if (!Model.hasEventListeners(modelClass, event)) return;
6114
+ const listeners = [...Model.resolveDispatchedEventListeners(modelClass, event), ...modelClass.eventListeners[event] || []];
5482
6115
  for (const listener of listeners) await listener(model);
5483
6116
  }
5484
6117
  /**
@@ -5515,6 +6148,7 @@ exports.MigrateCommand = MigrateCommand;
5515
6148
  exports.MigrateRollbackCommand = MigrateRollbackCommand;
5516
6149
  exports.Migration = Migration;
5517
6150
  exports.MigrationHistoryCommand = MigrationHistoryCommand;
6151
+ exports.MissingDelegateException = MissingDelegateException;
5518
6152
  exports.Model = Model;
5519
6153
  exports.ModelFactory = ModelFactory;
5520
6154
  exports.ModelNotFoundException = ModelNotFoundException;
@@ -5522,12 +6156,17 @@ exports.ModelsSyncCommand = ModelsSyncCommand;
5522
6156
  exports.PRISMA_MODEL_REGEX = PRISMA_MODEL_REGEX;
5523
6157
  exports.Paginator = Paginator;
5524
6158
  exports.QueryBuilder = QueryBuilder;
6159
+ exports.QueryConstraintException = QueryConstraintException;
6160
+ exports.RelationResolutionException = RelationResolutionException;
5525
6161
  exports.SEEDER_BRAND = SEEDER_BRAND;
5526
6162
  exports.SchemaBuilder = SchemaBuilder;
6163
+ exports.ScopeNotDefinedException = ScopeNotDefinedException;
5527
6164
  exports.SeedCommand = SeedCommand;
5528
6165
  exports.Seeder = Seeder;
5529
6166
  exports.TableBuilder = TableBuilder;
5530
6167
  exports.URLDriver = URLDriver;
6168
+ exports.UniqueConstraintResolutionException = UniqueConstraintResolutionException;
6169
+ exports.UnsupportedAdapterFeatureException = UnsupportedAdapterFeatureException;
5531
6170
  exports.applyAlterTableOperation = applyAlterTableOperation;
5532
6171
  exports.applyCreateTableOperation = applyCreateTableOperation;
5533
6172
  exports.applyDropTableOperation = applyDropTableOperation;
@@ -5559,6 +6198,7 @@ exports.findModelBlock = findModelBlock;
5559
6198
  exports.formatDefaultValue = formatDefaultValue;
5560
6199
  exports.formatRelationAction = formatRelationAction;
5561
6200
  exports.generateMigrationFile = generateMigrationFile;
6201
+ exports.getActiveTransactionClient = getActiveTransactionClient;
5562
6202
  exports.getDefaultStubsPath = getDefaultStubsPath;
5563
6203
  exports.getLastMigrationRun = getLastMigrationRun;
5564
6204
  exports.getLatestAppliedMigrations = getLatestAppliedMigrations;
@@ -5570,6 +6210,7 @@ exports.getUserConfig = getUserConfig;
5570
6210
  exports.inferDelegateName = inferDelegateName;
5571
6211
  exports.isDelegateLike = isDelegateLike;
5572
6212
  exports.isMigrationApplied = isMigrationApplied;
6213
+ exports.isTransactionCapableClient = isTransactionCapableClient;
5573
6214
  exports.loadArkormConfig = loadArkormConfig;
5574
6215
  exports.markMigrationApplied = markMigrationApplied;
5575
6216
  exports.markMigrationRun = markMigrationRun;
@@ -5581,6 +6222,7 @@ exports.resolveCast = resolveCast;
5581
6222
  exports.resolveMigrationClassName = resolveMigrationClassName;
5582
6223
  exports.resolveMigrationStateFilePath = resolveMigrationStateFilePath;
5583
6224
  exports.resolvePrismaType = resolvePrismaType;
6225
+ exports.runArkormTransaction = runArkormTransaction;
5584
6226
  exports.runMigrationWithPrisma = runMigrationWithPrisma;
5585
6227
  exports.runPrismaCommand = runPrismaCommand;
5586
6228
  exports.toMigrationFileSlug = toMigrationFileSlug;