@getstrata/core 0.5.98 → 0.5.99

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.js CHANGED
@@ -1719,7 +1719,7 @@ function buildOperatorClauses(column, operator, params) {
1719
1719
  clauses.push(`${column} <= ${pushParam(params, operator.lte)}`);
1720
1720
  }
1721
1721
  if (operator.ilike !== undefined) {
1722
- clauses.push(`${column} ILIKE ${pushParam(params, `%${operator.ilike}%`)}`);
1722
+ clauses.push(`${column} ILIKE ${pushParam(params, operator.ilike)}`);
1723
1723
  }
1724
1724
  if (operator.tsMatch !== undefined) {
1725
1725
  clauses.push(`${column} @@ plainto_tsquery('english', ${pushParam(params, operator.tsMatch)})`);
@@ -2362,13 +2362,30 @@ class RepositoryQuery {
2362
2362
  });
2363
2363
  return this;
2364
2364
  }
2365
+ withBelongsToMany(as, relation, relatedRepository, options = {}) {
2366
+ this.eagerLoads.push({
2367
+ kind: "belongsToMany",
2368
+ as,
2369
+ relation,
2370
+ repository: relatedRepository,
2371
+ options
2372
+ });
2373
+ return this;
2374
+ }
2365
2375
  async get() {
2366
2376
  const rows = await this.repository.findAll(this.buildOptions());
2367
2377
  return await this.attach(rows);
2368
2378
  }
2369
2379
  async first() {
2370
- const rows = await this.get();
2371
- return rows[0] ?? null;
2380
+ const rows = await this.repository.findAll({ ...this.buildOptions(), limit: 1 });
2381
+ const attached = await this.attach(rows);
2382
+ return attached[0] ?? null;
2383
+ }
2384
+ async count() {
2385
+ return await this.repository.count(this.buildOptions());
2386
+ }
2387
+ async attachToRows(rows) {
2388
+ return await this.attach(rows);
2372
2389
  }
2373
2390
  async paginate(options) {
2374
2391
  return await this.repository.paginate({
@@ -2440,6 +2457,15 @@ class RepositoryQuery {
2440
2457
  }));
2441
2458
  continue;
2442
2459
  }
2460
+ if (load.kind === "belongsToMany") {
2461
+ const relation2 = load.relation;
2462
+ const grouped2 = await this.repository.loadBelongsToManyForParents(rows, relation2, load.repository, load.options);
2463
+ result = result.map((row) => ({
2464
+ ...row,
2465
+ [load.as]: grouped2.get(row[relation2.parentKey]) ?? []
2466
+ }));
2467
+ continue;
2468
+ }
2443
2469
  if (load.kind === "morphTo") {
2444
2470
  const relation2 = load.relation;
2445
2471
  const grouped2 = await this.repository.loadMorphToForChildren(rows, relation2, load.morphRepositories ?? new Map, load.options);
@@ -2468,6 +2494,10 @@ class BaseRepository {
2468
2494
  this.table = table;
2469
2495
  this.connection = connection;
2470
2496
  }
2497
+ async count(options = {}) {
2498
+ const { whereNodes, where, ...rest } = options;
2499
+ return await this.countWhere(where ?? {}, rest, whereNodes ?? []);
2500
+ }
2471
2501
  async findAll(options = {}) {
2472
2502
  return await withDatabaseErrorHandling(async () => {
2473
2503
  const { whereNodes, ...queryOptions } = options;
@@ -2775,6 +2805,23 @@ class BaseRepository {
2775
2805
  }
2776
2806
  return indexMorphToRelation(children, parentsByType, relation);
2777
2807
  }
2808
+ async loadBelongsToManyForParents(parents, relation, relatedRepository, options = {}) {
2809
+ if (parents.length === 0) {
2810
+ return indexBelongsToManyRelation(parents, [], [], relation);
2811
+ }
2812
+ const parentIds = [...new Set(parents.map((parent) => parent[relation.parentKey]))];
2813
+ const pivotRows = await this.connection.unsafe(`SELECT * FROM ${relation.pivotTable} WHERE ${String(relation.foreignPivotKey)} = ANY($1)`, [parentIds]);
2814
+ if (pivotRows.length === 0) {
2815
+ return indexBelongsToManyRelation(parents, [], [], relation);
2816
+ }
2817
+ const relatedIds = [
2818
+ ...new Set(pivotRows.map((row) => row[relation.relatedPivotKey]))
2819
+ ];
2820
+ const relatedRows = await relatedRepository.withConnection(this.connection).findWhere({
2821
+ [relation.relatedKey]: relatedIds
2822
+ }, options);
2823
+ return indexBelongsToManyRelation(parents, pivotRows, relatedRows, relation);
2824
+ }
2778
2825
  }
2779
2826
  var baseRepository_default = BaseRepository;
2780
2827
  // ../../src/core/database/bunSql.ts
@@ -2804,7 +2851,40 @@ function createDatabaseConnection(source) {
2804
2851
  }
2805
2852
  };
2806
2853
  }
2854
+ // ../../src/core/database/inflection.ts
2855
+ function singularize(word) {
2856
+ if (word.endsWith("ies") && word.length > 3) {
2857
+ return `${word.slice(0, -3)}y`;
2858
+ }
2859
+ if (/(ses|xes|zes|ches|shes)$/i.test(word)) {
2860
+ return word.slice(0, -2);
2861
+ }
2862
+ if (word.endsWith("s") && !word.endsWith("ss")) {
2863
+ return word.slice(0, -1);
2864
+ }
2865
+ return word;
2866
+ }
2867
+ function foreignKeyFromTable(tableName) {
2868
+ return `${singularize(tableName)}_id`;
2869
+ }
2870
+ function pivotTableName(leftTable, rightTable) {
2871
+ return [singularize(leftTable), singularize(rightTable)].sort().join("_");
2872
+ }
2873
+
2807
2874
  // ../../src/core/database/factory.ts
2875
+ function inferFactoryForeignKey(parent, explicit) {
2876
+ if (explicit) {
2877
+ if (explicit.endsWith("_id")) {
2878
+ return explicit;
2879
+ }
2880
+ return `${explicit}_id`;
2881
+ }
2882
+ if (typeof parent.getRepository === "function") {
2883
+ return foreignKeyFromTable(parent.getRepository().getTable().name);
2884
+ }
2885
+ throw new Error("Factory.for() requires a foreign key or a parent Model.");
2886
+ }
2887
+
2808
2888
  class Factory {
2809
2889
  quantity = 1;
2810
2890
  counted = false;
@@ -2815,6 +2895,7 @@ class Factory {
2815
2895
  children = [];
2816
2896
  afterMakingCallbacks = [];
2817
2897
  afterCreatingCallbacks = [];
2898
+ model;
2818
2899
  definition() {
2819
2900
  throw new Error("Factory definition must be implemented by subclass.");
2820
2901
  }
@@ -2855,8 +2936,9 @@ class Factory {
2855
2936
  if (parent.id === undefined || parent.id === null) {
2856
2937
  throw new Error("Factory.for() requires a parent with an id.");
2857
2938
  }
2939
+ const key = inferFactoryForeignKey(parent, foreignKey);
2858
2940
  const next = this.clone();
2859
- next.parentAssociations = [...this.parentAssociations, { foreignKey, value: parent.id }];
2941
+ next.parentAssociations = [...this.parentAssociations, { foreignKey: key, value: parent.id }];
2860
2942
  return next;
2861
2943
  }
2862
2944
  recycle(parent, foreignKey) {
@@ -2937,29 +3019,17 @@ class Factory {
2937
3019
  }
2938
3020
  return values;
2939
3021
  }
2940
- persist(_values) {
3022
+ async persist(values) {
3023
+ if (this.model) {
3024
+ const created = await this.model.create(values);
3025
+ if (created && typeof created.toObject === "function") {
3026
+ return created.toObject();
3027
+ }
3028
+ return created;
3029
+ }
2941
3030
  throw new Error("Factory.persist() must be implemented to use create().");
2942
3031
  }
2943
3032
  }
2944
- // ../../src/core/database/inflection.ts
2945
- function singularize(word) {
2946
- if (word.endsWith("ies") && word.length > 3) {
2947
- return `${word.slice(0, -3)}y`;
2948
- }
2949
- if (/(ses|xes|zes|ches|shes)$/i.test(word)) {
2950
- return word.slice(0, -2);
2951
- }
2952
- if (word.endsWith("s") && !word.endsWith("ss")) {
2953
- return word.slice(0, -1);
2954
- }
2955
- return word;
2956
- }
2957
- function foreignKeyFromTable(tableName) {
2958
- return `${singularize(tableName)}_id`;
2959
- }
2960
- function pivotTableName(leftTable, rightTable) {
2961
- return [singularize(leftTable), singularize(rightTable)].sort().join("_");
2962
- }
2963
3033
  // ../../src/core/database/migrations/advisoryLock.ts
2964
3034
  var MIGRATION_LOCK_KEY = 42424242;
2965
3035
  async function withMigrationLock(db, callback, lockKey = MIGRATION_LOCK_KEY) {
@@ -3085,6 +3155,9 @@ function ownerId(owner, ownerKey) {
3085
3155
  }
3086
3156
  throw new Error("belongsTo.associate() requires a related model or { id }.");
3087
3157
  }
3158
+ function thenGet(get, onfulfilled, onrejected) {
3159
+ return get().then(onfulfilled ?? undefined, onrejected ?? undefined);
3160
+ }
3088
3161
 
3089
3162
  class HasManyRelationQuery {
3090
3163
  parent;
@@ -3148,13 +3221,28 @@ class HasManyRelationQuery {
3148
3221
  return rows[0] ?? null;
3149
3222
  }
3150
3223
  async count() {
3151
- return (await this.get()).length;
3224
+ return this.scopedQuery().count();
3225
+ }
3226
+ then(onfulfilled, onrejected) {
3227
+ return thenGet(() => this.get(), onfulfilled, onrejected);
3152
3228
  }
3153
3229
  async create(attributes = {}) {
3154
3230
  return this.related.create(attributes, {
3155
3231
  [this.relation.foreignKey]: this.parent.get(this.relation.localKey)
3156
3232
  });
3157
3233
  }
3234
+ async save(related) {
3235
+ const forced = {
3236
+ [this.relation.foreignKey]: this.parent.get(this.relation.localKey)
3237
+ };
3238
+ const savable = related;
3239
+ if (typeof savable.save === "function") {
3240
+ savable.mergeAttributes?.(forced);
3241
+ await savable.save();
3242
+ return related;
3243
+ }
3244
+ return this.create(related);
3245
+ }
3158
3246
  async createMany(records) {
3159
3247
  const created = [];
3160
3248
  for (const attributes of records) {
@@ -3202,11 +3290,17 @@ class HasOneRelationQuery {
3202
3290
  return this.get();
3203
3291
  }
3204
3292
  async count() {
3205
- return await this.get() ? 1 : 0;
3293
+ return this.inner.count();
3294
+ }
3295
+ then(onfulfilled, onrejected) {
3296
+ return thenGet(() => this.get(), onfulfilled, onrejected);
3206
3297
  }
3207
3298
  async create(attributes = {}) {
3208
3299
  return this.inner.create(attributes);
3209
3300
  }
3301
+ async save(related) {
3302
+ return this.inner.save(related);
3303
+ }
3210
3304
  }
3211
3305
 
3212
3306
  class BelongsToRelationQuery {
@@ -3214,12 +3308,17 @@ class BelongsToRelationQuery {
3214
3308
  related;
3215
3309
  relation;
3216
3310
  kind = "belongsTo";
3311
+ extraWhere = {};
3217
3312
  extraOptions = {};
3218
3313
  constructor(parent, related, relation) {
3219
3314
  this.parent = parent;
3220
3315
  this.related = related;
3221
3316
  this.relation = relation;
3222
3317
  }
3318
+ where(where) {
3319
+ this.extraWhere = { ...this.extraWhere, ...where };
3320
+ return this;
3321
+ }
3223
3322
  orderBy(orderBy) {
3224
3323
  this.extraOptions = { ...this.extraOptions, orderBy };
3225
3324
  return this;
@@ -3233,10 +3332,10 @@ class BelongsToRelationQuery {
3233
3332
  }
3234
3333
  toExistsClause(parentTable) {
3235
3334
  const relatedTable = this.related.repository().getTable().name;
3236
- return {
3237
- sql: `SELECT 1 FROM ${quoteIdentifier(relatedTable)} WHERE ${qualifyColumn(relatedTable, this.relation.ownerKey)} = ${qualifyColumn(parentTable, this.relation.foreignKey)}`,
3238
- params: []
3239
- };
3335
+ const extra = buildAdvancedWhereClause(relatedTable, this.extraWhere, [], []);
3336
+ const extraSql = extra.clause.replace(/^ WHERE /, "");
3337
+ const sql = `SELECT 1 FROM ${quoteIdentifier(relatedTable)} WHERE ${qualifyColumn(relatedTable, this.relation.ownerKey)} = ${qualifyColumn(parentTable, this.relation.foreignKey)}${extraSql ? ` AND ${extraSql}` : ""}`;
3338
+ return { sql, params: extra.params };
3240
3339
  }
3241
3340
  async get() {
3242
3341
  const foreign = this.parent.get(this.relation.foreignKey);
@@ -3244,7 +3343,7 @@ class BelongsToRelationQuery {
3244
3343
  return null;
3245
3344
  }
3246
3345
  const repository = this.related.repository().withConnection(this.parent.getRepository().getConnection());
3247
- let query = repository.query(asWhere({ [this.relation.ownerKey]: foreign }));
3346
+ let query = repository.query(asWhere({ [this.relation.ownerKey]: foreign, ...this.extraWhere }));
3248
3347
  if (this.extraOptions.orderBy) {
3249
3348
  query = query.orderBy(this.extraOptions.orderBy);
3250
3349
  }
@@ -3254,6 +3353,9 @@ class BelongsToRelationQuery {
3254
3353
  async first() {
3255
3354
  return this.get();
3256
3355
  }
3356
+ then(onfulfilled, onrejected) {
3357
+ return thenGet(() => this.get(), onfulfilled, onrejected);
3358
+ }
3257
3359
  async associate(owner) {
3258
3360
  await this.parent.getRepository().updateById(this.parent.id, {
3259
3361
  [this.relation.foreignKey]: ownerId(owner, this.relation.ownerKey)
@@ -3273,6 +3375,7 @@ class BelongsToManyRelationQuery {
3273
3375
  kind = "belongsToMany";
3274
3376
  extraWhere = {};
3275
3377
  extraOptions = {};
3378
+ pivotValues = {};
3276
3379
  constructor(parent, related, relation) {
3277
3380
  this.parent = parent;
3278
3381
  this.related = related;
@@ -3286,7 +3389,13 @@ class BelongsToManyRelationQuery {
3286
3389
  this.extraOptions = { ...this.extraOptions, orderBy };
3287
3390
  return this;
3288
3391
  }
3289
- applyEagerLoad(_query, _alias) {}
3392
+ applyEagerLoad(query, alias) {
3393
+ query.withBelongsToMany(alias, this.relation, this.related.repository(), this.extraOptions);
3394
+ }
3395
+ withPivotValues(values) {
3396
+ this.pivotValues = { ...this.pivotValues, ...values };
3397
+ return this;
3398
+ }
3290
3399
  hydrateEager(row, alias) {
3291
3400
  const value = row[alias] ?? [];
3292
3401
  const rows = Array.isArray(value) ? value : [];
@@ -3326,13 +3435,34 @@ class BelongsToManyRelationQuery {
3326
3435
  return rows[0] ?? null;
3327
3436
  }
3328
3437
  async count() {
3329
- return (await this.get()).length;
3438
+ const parentId = this.parent.get(this.relation.parentKey);
3439
+ const rows = await this.connection().unsafe(`SELECT COUNT(*) AS count FROM ${this.relation.pivotTable} WHERE ${String(this.relation.foreignPivotKey)} = $1`, [parentId]);
3440
+ return Number(rows[0]?.count ?? 0);
3441
+ }
3442
+ then(onfulfilled, onrejected) {
3443
+ return thenGet(() => this.get(), onfulfilled, onrejected);
3330
3444
  }
3331
3445
  async attach(ids) {
3446
+ const list = Array.isArray(ids) ? ids : [ids];
3447
+ const parentId = this.parent.get(this.relation.parentKey);
3448
+ const extraKeys = Object.keys(this.pivotValues);
3449
+ const extraColumns = extraKeys.length > 0 ? `, ${extraKeys.join(", ")}` : "";
3450
+ const extraPlaceholders = extraKeys.map((_, index) => `$${index + 3}`).join(", ");
3451
+ const extraValues = extraKeys.map((key) => this.pivotValues[key]);
3452
+ for (const id of list) {
3453
+ await this.connection().unsafe(extraKeys.length > 0 ? `INSERT INTO ${this.relation.pivotTable} (${String(this.relation.foreignPivotKey)}, ${String(this.relation.relatedPivotKey)}${extraColumns}) VALUES ($1, $2, ${extraPlaceholders})` : `INSERT INTO ${this.relation.pivotTable} (${String(this.relation.foreignPivotKey)}, ${String(this.relation.relatedPivotKey)}) VALUES ($1, $2)`, [parentId, id, ...extraValues]);
3454
+ }
3455
+ }
3456
+ async toggle(ids) {
3332
3457
  const list = Array.isArray(ids) ? ids : [ids];
3333
3458
  const parentId = this.parent.get(this.relation.parentKey);
3334
3459
  for (const id of list) {
3335
- await this.connection().unsafe(`INSERT INTO ${this.relation.pivotTable} (${String(this.relation.foreignPivotKey)}, ${String(this.relation.relatedPivotKey)}) VALUES ($1, $2)`, [parentId, id]);
3460
+ const existing = await this.connection().unsafe(`SELECT 1 FROM ${this.relation.pivotTable} WHERE ${String(this.relation.foreignPivotKey)} = $1 AND ${String(this.relation.relatedPivotKey)} = $2 LIMIT 1`, [parentId, id]);
3461
+ if (existing.length > 0) {
3462
+ await this.detach(id);
3463
+ } else {
3464
+ await this.attach(id);
3465
+ }
3336
3466
  }
3337
3467
  }
3338
3468
  async detach(ids) {
@@ -3404,6 +3534,17 @@ class MorphManyRelationQuery {
3404
3534
  const rows = await this.get();
3405
3535
  return rows[0] ?? null;
3406
3536
  }
3537
+ async count() {
3538
+ const repository = this.related.repository().withConnection(this.parent.getRepository().getConnection());
3539
+ return repository.query(asWhere({
3540
+ [this.relation.morphTypeKey]: this.relation.morphType,
3541
+ [this.relation.morphIdKey]: this.parent.get(this.relation.localKey),
3542
+ ...this.extraWhere
3543
+ })).count();
3544
+ }
3545
+ then(onfulfilled, onrejected) {
3546
+ return thenGet(() => this.get(), onfulfilled, onrejected);
3547
+ }
3407
3548
  async create(attributes = {}) {
3408
3549
  return this.related.create(attributes, {
3409
3550
  [this.relation.morphTypeKey]: this.relation.morphType,
@@ -3444,6 +3585,15 @@ class MorphOneRelationQuery {
3444
3585
  async get() {
3445
3586
  return this.inner.first();
3446
3587
  }
3588
+ async first() {
3589
+ return this.get();
3590
+ }
3591
+ async count() {
3592
+ return this.inner.count();
3593
+ }
3594
+ then(onfulfilled, onrejected) {
3595
+ return thenGet(() => this.get(), onfulfilled, onrejected);
3596
+ }
3447
3597
  async create(attributes = {}) {
3448
3598
  return this.inner.create(attributes);
3449
3599
  }
@@ -3454,11 +3604,16 @@ class MorphToRelationQuery {
3454
3604
  relatedByType;
3455
3605
  relation;
3456
3606
  kind = "morphTo";
3607
+ extraWhere = {};
3457
3608
  constructor(parent, relatedByType, relation) {
3458
3609
  this.parent = parent;
3459
3610
  this.relatedByType = relatedByType;
3460
3611
  this.relation = relation;
3461
3612
  }
3613
+ where(where) {
3614
+ this.extraWhere = { ...this.extraWhere, ...where };
3615
+ return this;
3616
+ }
3462
3617
  applyEagerLoad(query, alias) {
3463
3618
  const repositories = new Map(Object.entries(this.relatedByType).map(([type, model]) => [
3464
3619
  type,
@@ -3476,9 +3631,11 @@ class MorphToRelationQuery {
3476
3631
  return { sql: "SELECT 1 WHERE 1 = 0", params: [] };
3477
3632
  }
3478
3633
  const relatedTable = related.repository().getTable();
3634
+ const extra = buildAdvancedWhereClause(relatedTable.name, this.extraWhere, [], []);
3635
+ const extraSql = extra.clause.replace(/^ WHERE /, "");
3479
3636
  return {
3480
- sql: `SELECT 1 FROM ${quoteIdentifier(relatedTable.name)} WHERE ${qualifyColumn(relatedTable.name, relatedTable.primaryKey)} = ${qualifyColumn(parentTable, this.relation.morphIdKey)}`,
3481
- params: []
3637
+ sql: `SELECT 1 FROM ${quoteIdentifier(relatedTable.name)} WHERE ${qualifyColumn(relatedTable.name, relatedTable.primaryKey)} = ${qualifyColumn(parentTable, this.relation.morphIdKey)}${extraSql ? ` AND ${extraSql}` : ""}`,
3638
+ params: extra.params
3482
3639
  };
3483
3640
  }
3484
3641
  async get() {
@@ -3489,13 +3646,17 @@ class MorphToRelationQuery {
3489
3646
  return null;
3490
3647
  }
3491
3648
  const table = related.repository().getTable();
3492
- const row = await related.repository().withConnection(this.parent.getRepository().getConnection()).query(asWhere({ [table.primaryKey]: id })).first();
3649
+ const row = await related.repository().withConnection(this.parent.getRepository().getConnection()).query(asWhere({ [table.primaryKey]: id, ...this.extraWhere })).first();
3493
3650
  return row ? related.newFromRecord(row) : null;
3494
3651
  }
3652
+ then(onfulfilled, onrejected) {
3653
+ return thenGet(() => this.get(), onfulfilled, onrejected);
3654
+ }
3495
3655
  }
3496
3656
 
3497
3657
  // ../../src/core/database/model.ts
3498
3658
  var modelRepositories = new WeakMap;
3659
+ var namedModels = new Map;
3499
3660
  var modelGlobalScopes = new WeakMap;
3500
3661
  var modelObservers = new WeakMap;
3501
3662
  var modelBooted = new WeakSet;
@@ -3513,38 +3674,59 @@ function accessorName(key) {
3513
3674
  const pascal = key.replace(/_([a-z])/g, (_match, letter) => letter.toUpperCase());
3514
3675
  return `get${pascal.charAt(0).toUpperCase()}${pascal.slice(1)}Attribute`;
3515
3676
  }
3516
- function constrainRelationExists(model, name, constrain, not) {
3517
- const statics = modelStatics(model);
3518
- ensureBooted(model);
3519
- const repository = resolveModelRepository(model);
3520
- const dummy = statics.newFromRecord({});
3521
- const method = dummy[name];
3522
- if (typeof method !== "function") {
3523
- throw new Error(`${model.name} has no relation method ${name}().`);
3524
- }
3525
- const relationQuery = method.call(dummy);
3526
- constrain?.(relationQuery);
3527
- const exists = relationQuery.toExistsClause(repository.getTable().name);
3528
- const query = Model.query.call(model);
3529
- return not ? query.whereNotExists(exists.sql, exists.params) : query.whereExists(exists.sql, exists.params);
3677
+ function isLoadableModel(value) {
3678
+ return Boolean(value && typeof value === "object" && typeof value.load === "function" && typeof value.loaded === "function" && typeof value.setLoaded === "function");
3530
3679
  }
3531
- async function loadNested(model, path) {
3532
- const [head, ...rest] = path.split(".");
3533
- if (!head) {
3680
+ async function eagerLoadOnModels(models, paths) {
3681
+ if (models.length === 0 || paths.length === 0) {
3534
3682
  return;
3535
3683
  }
3536
- await model.load(head);
3537
- if (rest.length === 0) {
3538
- return;
3684
+ const grouped = new Map;
3685
+ for (const path of paths) {
3686
+ const [head, ...rest] = path.split(".");
3687
+ if (!head) {
3688
+ continue;
3689
+ }
3690
+ const nested = rest.join(".");
3691
+ const existing = grouped.get(head) ?? [];
3692
+ if (nested) {
3693
+ existing.push(nested);
3694
+ }
3695
+ grouped.set(head, existing);
3539
3696
  }
3540
- const loaded = model.loaded(head);
3541
- const children = Array.isArray(loaded) ? loaded : loaded ? [loaded] : [];
3542
- for (const child of children) {
3543
- if (child && typeof child === "object" && typeof child.load === "function") {
3544
- await loadNested(child, rest.join("."));
3697
+ for (const [head, nested] of grouped) {
3698
+ const unloaded = models.filter((model) => model.loaded(head) === undefined);
3699
+ if (unloaded.length > 0) {
3700
+ const first = unloaded[0];
3701
+ if (!first) {
3702
+ continue;
3703
+ }
3704
+ const method = first[head];
3705
+ if (typeof method !== "function") {
3706
+ throw new Error(`${first.constructor.name} has no relation method ${head}().`);
3707
+ }
3708
+ const relationQuery = method.call(first);
3709
+ const query = first.getRepository().query();
3710
+ relationQuery.applyEagerLoad(query, head);
3711
+ const attached = await query.attachToRows(unloaded.map((model) => model.toObject()));
3712
+ for (const [index, model] of unloaded.entries()) {
3713
+ const row = attached[index] ?? model.toObject();
3714
+ model.setLoaded(head, relationQuery.hydrateEager(row, head));
3715
+ }
3545
3716
  }
3717
+ if (nested.length === 0) {
3718
+ continue;
3719
+ }
3720
+ const children = models.flatMap((model) => {
3721
+ const loaded = model.loaded(head);
3722
+ return Array.isArray(loaded) ? loaded : loaded ? [loaded] : [];
3723
+ });
3724
+ await eagerLoadOnModels(children.filter(isLoadableModel), nested);
3546
3725
  }
3547
3726
  }
3727
+ async function loadNested(model, path) {
3728
+ await eagerLoadOnModels([model], [path]);
3729
+ }
3548
3730
  function resolveModelRepository(model) {
3549
3731
  const repository = modelRepositories.get(model);
3550
3732
  if (!repository) {
@@ -3552,6 +3734,48 @@ function resolveModelRepository(model) {
3552
3734
  }
3553
3735
  return repository;
3554
3736
  }
3737
+ function registerModelClass(name, model) {
3738
+ namedModels.set(name, model);
3739
+ }
3740
+ function resolveRelated(related) {
3741
+ if (typeof related === "string") {
3742
+ const found = namedModels.get(related);
3743
+ if (!found) {
3744
+ throw new Error(`Model [${related}] is not registered. Call registerModelClass() first.`);
3745
+ }
3746
+ return found;
3747
+ }
3748
+ if (typeof related === "function" && typeof related.repository !== "function") {
3749
+ return related();
3750
+ }
3751
+ return related;
3752
+ }
3753
+ function inferRelationMethodName(callee) {
3754
+ const stack = new Error().stack ?? "";
3755
+ let seenCallee = false;
3756
+ for (const line of stack.split(`
3757
+ `)) {
3758
+ const match = /at (?:async )?(?:[^.\s]+\.)?(\w+)/.exec(line);
3759
+ const name = match?.[1];
3760
+ if (!name || name === "Error" || name === "inferRelationMethodName") {
3761
+ continue;
3762
+ }
3763
+ if (!seenCallee) {
3764
+ if (name === callee) {
3765
+ seenCallee = true;
3766
+ }
3767
+ continue;
3768
+ }
3769
+ if (name !== callee) {
3770
+ return name;
3771
+ }
3772
+ }
3773
+ return;
3774
+ }
3775
+ function morphClassOf(model) {
3776
+ const statics = modelStatics(model.constructor === Function ? model : model.constructor);
3777
+ return statics.$morphClass ?? (model.constructor === Function ? model.name : model.constructor.name);
3778
+ }
3555
3779
  function modelStatics(model) {
3556
3780
  return model;
3557
3781
  }
@@ -3581,6 +3805,11 @@ function hydrateValue(value, cast) {
3581
3805
  case "bool":
3582
3806
  case "boolean":
3583
3807
  return value === true || value === 1 || value === "1" || value === "true";
3808
+ case "integer":
3809
+ case "int":
3810
+ return value === "" ? null : Number(value);
3811
+ case "hashed":
3812
+ return value;
3584
3813
  default:
3585
3814
  return value;
3586
3815
  }
@@ -3598,6 +3827,11 @@ function dehydrateValue(value, cast) {
3598
3827
  case "bool":
3599
3828
  case "boolean":
3600
3829
  return Boolean(value);
3830
+ case "integer":
3831
+ case "int":
3832
+ return value === "" ? null : Number(value);
3833
+ case "hashed":
3834
+ return value;
3601
3835
  default:
3602
3836
  return value;
3603
3837
  }
@@ -3652,6 +3886,153 @@ function applyTimestampsOnUpdate(columns, values, enabled) {
3652
3886
  return result;
3653
3887
  }
3654
3888
 
3889
+ class ModelQuery {
3890
+ modelClass;
3891
+ query;
3892
+ eager = [];
3893
+ constructor(modelClass, query) {
3894
+ this.modelClass = modelClass;
3895
+ this.query = query;
3896
+ }
3897
+ with(...relations) {
3898
+ const statics = modelStatics(this.modelClass);
3899
+ ensureBooted(this.modelClass);
3900
+ const dummy = statics.newFromRecord({}, false);
3901
+ for (const path of relations) {
3902
+ const name = path.split(".")[0] ?? path;
3903
+ const method = dummy[name];
3904
+ if (typeof method !== "function") {
3905
+ throw new Error(`${this.modelClass.name} has no relation method ${name}().`);
3906
+ }
3907
+ const relationQuery = method.call(dummy);
3908
+ this.eager.push({ name, path, relationQuery });
3909
+ relationQuery.applyEagerLoad(this.query, name);
3910
+ }
3911
+ return this;
3912
+ }
3913
+ where(input) {
3914
+ this.query.where(input);
3915
+ return this;
3916
+ }
3917
+ orWhere(input) {
3918
+ this.query.orWhere(input);
3919
+ return this;
3920
+ }
3921
+ orderBy(orderBy) {
3922
+ this.query.orderBy(orderBy);
3923
+ return this;
3924
+ }
3925
+ limit(limit) {
3926
+ this.query.limit(limit);
3927
+ return this;
3928
+ }
3929
+ offset(offset) {
3930
+ this.query.offset(offset);
3931
+ return this;
3932
+ }
3933
+ whereNull(column) {
3934
+ this.query.whereNull(column);
3935
+ return this;
3936
+ }
3937
+ whereIn(column, values) {
3938
+ this.query.whereIn(column, values);
3939
+ return this;
3940
+ }
3941
+ whereExists(sql, params = []) {
3942
+ this.query.whereExists(sql, params);
3943
+ return this;
3944
+ }
3945
+ whereNotExists(sql, params = []) {
3946
+ this.query.whereNotExists(sql, params);
3947
+ return this;
3948
+ }
3949
+ whereHas(name, constrain) {
3950
+ return this.constrainExists(name, constrain, false);
3951
+ }
3952
+ has(name) {
3953
+ return this.constrainExists(name, undefined, false);
3954
+ }
3955
+ doesntHave(name) {
3956
+ return this.constrainExists(name, undefined, true);
3957
+ }
3958
+ whereDoesntHave(name, constrain) {
3959
+ return this.constrainExists(name, constrain, true);
3960
+ }
3961
+ withHasMany(...args) {
3962
+ this.query.withHasMany(...args);
3963
+ return this;
3964
+ }
3965
+ withBelongsTo(...args) {
3966
+ this.query.withBelongsTo(...args);
3967
+ return this;
3968
+ }
3969
+ withBelongsToMany(...args) {
3970
+ this.query.withBelongsToMany(...args);
3971
+ return this;
3972
+ }
3973
+ withMorphMany(...args) {
3974
+ this.query.withMorphMany(...args);
3975
+ return this;
3976
+ }
3977
+ withMorphOne(...args) {
3978
+ this.query.withMorphOne(...args);
3979
+ return this;
3980
+ }
3981
+ withMorphTo(...args) {
3982
+ this.query.withMorphTo(...args);
3983
+ return this;
3984
+ }
3985
+ async get() {
3986
+ const statics = modelStatics(this.modelClass);
3987
+ const rows = await this.query.get();
3988
+ const models = [];
3989
+ for (const row of rows) {
3990
+ const model = statics.newFromRecord(row, true);
3991
+ await runObservers(model, "retrieved");
3992
+ for (const { name, relationQuery } of this.eager) {
3993
+ model.setLoaded(name, relationQuery.hydrateEager(row, name));
3994
+ }
3995
+ models.push(model);
3996
+ }
3997
+ const nested = this.eager.filter((item) => item.path.includes(".")).map((item) => item.path);
3998
+ await eagerLoadOnModels(models.filter(isLoadableModel), nested);
3999
+ return models;
4000
+ }
4001
+ async first() {
4002
+ this.query.limit(1);
4003
+ const models = await this.get();
4004
+ return models[0] ?? null;
4005
+ }
4006
+ async find(id) {
4007
+ const primaryKey = resolveModelRepository(this.modelClass).getTable().primaryKey;
4008
+ return this.where({ [primaryKey]: id }).first();
4009
+ }
4010
+ async findOrFail(id, errorFactory) {
4011
+ const model = await this.find(id);
4012
+ if (model) {
4013
+ return model;
4014
+ }
4015
+ throw errorFactory?.(id) ?? new NotFoundError(`${this.modelClass.name} ${String(id)} not found.`);
4016
+ }
4017
+ then(onfulfilled, onrejected) {
4018
+ return this.get().then(onfulfilled ?? undefined, onrejected ?? undefined);
4019
+ }
4020
+ constrainExists(name, constrain, not) {
4021
+ const statics = modelStatics(this.modelClass);
4022
+ ensureBooted(this.modelClass);
4023
+ const repository = resolveModelRepository(this.modelClass);
4024
+ const dummy = statics.newFromRecord({});
4025
+ const method = dummy[name];
4026
+ if (typeof method !== "function") {
4027
+ throw new Error(`${this.modelClass.name} has no relation method ${name}().`);
4028
+ }
4029
+ const relationQuery = method.call(dummy);
4030
+ constrain?.(relationQuery);
4031
+ const exists = relationQuery.toExistsClause(repository.getTable().name);
4032
+ return not ? this.whereNotExists(exists.sql, exists.params) : this.whereExists(exists.sql, exists.params);
4033
+ }
4034
+ }
4035
+
3655
4036
  class Model {
3656
4037
  attributes;
3657
4038
  repository;
@@ -3662,6 +4043,7 @@ class Model {
3662
4043
  static $hidden;
3663
4044
  static $visible;
3664
4045
  static $appends;
4046
+ static $morphClass;
3665
4047
  _exists;
3666
4048
  loadedRelations = {};
3667
4049
  hiddenOverrides = [];
@@ -3671,6 +4053,7 @@ class Model {
3671
4053
  this.attributes = attributes;
3672
4054
  this.repository = repository;
3673
4055
  this._exists = exists;
4056
+ this.attributes = modelStatics(this.constructor).hydrateAttributes(attributes);
3674
4057
  }
3675
4058
  getRepository() {
3676
4059
  return this.repository;
@@ -3731,7 +4114,7 @@ class Model {
3731
4114
  return this;
3732
4115
  }
3733
4116
  primaryKey() {
3734
- throw new Error(`${this.constructor.name}.primaryKey() is not implemented.`);
4117
+ return this.repository.getTable().primaryKey;
3735
4118
  }
3736
4119
  static primaryKeyField() {
3737
4120
  return resolveModelRepository(this).getTable().primaryKey;
@@ -3772,7 +4155,7 @@ class Model {
3772
4155
  for (const scope of getGlobalScopes(this)) {
3773
4156
  query = scope(query);
3774
4157
  }
3775
- return query;
4158
+ return new ModelQuery(this, query);
3776
4159
  }
3777
4160
  static newFromRecord(record, exists = true) {
3778
4161
  const repository = resolveModelRepository(this);
@@ -3791,79 +4174,36 @@ class Model {
3791
4174
  const withTimestamps = applyTimestampsOnCreate(table.columns, assignable, timestamps);
3792
4175
  const payload = statics.dehydrateAttributes(withTimestamps);
3793
4176
  const pending = statics.newFromRecord({ ...payload }, false);
4177
+ if (await runObservers(pending, "saving") === false) {
4178
+ throw new Error(`${this.name}.create() was cancelled by an observer.`);
4179
+ }
3794
4180
  if (await runObservers(pending, "creating") === false) {
3795
4181
  throw new Error(`${this.name}.create() was cancelled by an observer.`);
3796
4182
  }
3797
4183
  const record = await repository.create(payload);
3798
4184
  const created = statics.fromRecord(record, repository, true);
3799
4185
  await runObservers(created, "created");
4186
+ await runObservers(created, "saved");
3800
4187
  return created;
3801
4188
  }
3802
4189
  static with(...relations) {
3803
- const statics = modelStatics(this);
3804
- ensureBooted(this);
3805
- const repository = resolveModelRepository(this);
3806
- const dummy = statics.fromRecord({}, repository, false);
3807
- const resolved = relations.map((path) => {
3808
- const name = path.split(".")[0] ?? path;
3809
- const method = dummy[name];
3810
- if (typeof method !== "function") {
3811
- throw new Error(`${this.name} has no relation method ${name}().`);
3812
- }
3813
- const relationQuery = method.call(dummy);
3814
- return { name, path, relationQuery };
3815
- });
3816
- const query = Model.query.call(this);
3817
- for (const { name, relationQuery } of resolved) {
3818
- if (relationQuery.kind !== "belongsToMany") {
3819
- relationQuery.applyEagerLoad(query, name);
3820
- }
3821
- }
3822
- return {
3823
- async get() {
3824
- const rows = await query.get();
3825
- const models = [];
3826
- for (const row of rows) {
3827
- const model = statics.fromRecord(row, repository, true);
3828
- for (const { name, path, relationQuery } of resolved) {
3829
- if (relationQuery.kind === "belongsToMany") {
3830
- await model.load(path.includes(".") ? path : name);
3831
- continue;
3832
- }
3833
- model.setLoaded(name, relationQuery.hydrateEager(row, name));
3834
- const nested = path.split(".").slice(1).join(".");
3835
- if (nested) {
3836
- await loadNested(model, path);
3837
- }
3838
- }
3839
- models.push(model);
3840
- }
3841
- return models;
3842
- },
3843
- async first() {
3844
- const [model] = await this.get();
3845
- return model ?? null;
3846
- }
3847
- };
4190
+ return Model.query.call(this).with(...relations);
3848
4191
  }
3849
4192
  static whereHas(name, constrain) {
3850
- return constrainRelationExists(this, name, constrain, false);
4193
+ return Model.query.call(this).whereHas(name, constrain);
3851
4194
  }
3852
4195
  static has(name) {
3853
- return constrainRelationExists(this, name, undefined, false);
4196
+ return Model.query.call(this).has(name);
3854
4197
  }
3855
4198
  static doesntHave(name) {
3856
- return constrainRelationExists(this, name, undefined, true);
4199
+ return Model.query.call(this).doesntHave(name);
3857
4200
  }
3858
4201
  static whereDoesntHave(name, constrain) {
3859
- return constrainRelationExists(this, name, constrain, true);
4202
+ return Model.query.call(this).whereDoesntHave(name, constrain);
3860
4203
  }
3861
4204
  static async find(id) {
3862
- const statics = modelStatics(this);
3863
- const repository = resolveModelRepository(this);
3864
- const primaryKey = repository.getTable().primaryKey;
3865
- const record = await Model.query.call(this).where({ [primaryKey]: id }).first();
3866
- return record ? statics.fromRecord(record, repository, true) : null;
4205
+ const primaryKey = resolveModelRepository(this).getTable().primaryKey;
4206
+ return Model.query.call(this).where({ [primaryKey]: id }).first();
3867
4207
  }
3868
4208
  static async findOrFail(id, errorFactory) {
3869
4209
  const model = await Model.find.call(this, id);
@@ -3873,8 +4213,6 @@ class Model {
3873
4213
  throw errorFactory?.(id) ?? new NotFoundError(`${this.name} ${String(id)} not found.`);
3874
4214
  }
3875
4215
  static async all(options = {}) {
3876
- const statics = modelStatics(this);
3877
- const repository = resolveModelRepository(this);
3878
4216
  let query = Model.query.call(this);
3879
4217
  if (options.orderBy) {
3880
4218
  query = query.orderBy(options.orderBy);
@@ -3882,21 +4220,17 @@ class Model {
3882
4220
  if (options.limit !== undefined) {
3883
4221
  query = query.limit(options.limit);
3884
4222
  }
3885
- const rows = await query.get();
3886
- return rows.map((row) => statics.fromRecord(row, repository, true));
4223
+ return query.get();
3887
4224
  }
3888
4225
  static where(where) {
3889
4226
  return Model.query.call(this).where(where);
3890
4227
  }
3891
4228
  static async firstWhere(where, options = {}) {
3892
- const statics = modelStatics(this);
3893
- const repository = resolveModelRepository(this);
3894
4229
  let query = Model.query.call(this).where(where);
3895
4230
  if (options.orderBy) {
3896
4231
  query = query.orderBy(options.orderBy);
3897
4232
  }
3898
- const record = await query.first();
3899
- return record ? statics.fromRecord(record, repository, true) : null;
4233
+ return query.first();
3900
4234
  }
3901
4235
  static async firstOrNew(where, values = {}) {
3902
4236
  const existing = await Model.firstWhere.call(this, where);
@@ -3925,6 +4259,9 @@ class Model {
3925
4259
  const casts = ModelClass.$casts ?? {};
3926
4260
  const table = this.repository.getTable();
3927
4261
  const updating = this.$exists;
4262
+ if (await runObservers(this, "saving") === false) {
4263
+ return this;
4264
+ }
3928
4265
  if (await runObservers(this, updating ? "updating" : "creating") === false) {
3929
4266
  return this;
3930
4267
  }
@@ -3933,6 +4270,7 @@ class Model {
3933
4270
  const record2 = await this.repository.updateByIdOrThrow(this.id, changes);
3934
4271
  this.attributes = ModelClass.hydrateAttributes(record2);
3935
4272
  await runObservers(this, "updated");
4273
+ await runObservers(this, "saved");
3936
4274
  return this;
3937
4275
  }
3938
4276
  const assignable = filterMassAssignable(ModelClass.$fillable, ModelClass.$guarded, this.attributes);
@@ -3942,6 +4280,7 @@ class Model {
3942
4280
  this.attributes = ModelClass.hydrateAttributes(record);
3943
4281
  this._exists = true;
3944
4282
  await runObservers(this, "created");
4283
+ await runObservers(this, "saved");
3945
4284
  return this;
3946
4285
  }
3947
4286
  async update(changes) {
@@ -4009,23 +4348,26 @@ class Model {
4009
4348
  }
4010
4349
  hasMany(related, foreignKey, localKey) {
4011
4350
  const table = this.repository.getTable();
4012
- return new HasManyRelationQuery(this, related, hasMany({
4013
- name: related.repository().getTable().name,
4351
+ const relatedClass = resolveRelated(related);
4352
+ return new HasManyRelationQuery(this, relatedClass, hasMany({
4353
+ name: relatedClass.repository().getTable().name,
4014
4354
  localKey: localKey ?? table.primaryKey,
4015
4355
  foreignKey: foreignKey ?? foreignKeyFromTable(table.name)
4016
4356
  }));
4017
4357
  }
4018
4358
  hasOne(related, foreignKey, localKey) {
4019
4359
  const table = this.repository.getTable();
4020
- return new HasOneRelationQuery(this, related, hasOne({
4021
- name: related.repository().getTable().name,
4360
+ const relatedClass = resolveRelated(related);
4361
+ return new HasOneRelationQuery(this, relatedClass, hasOne({
4362
+ name: relatedClass.repository().getTable().name,
4022
4363
  localKey: localKey ?? table.primaryKey,
4023
4364
  foreignKey: foreignKey ?? foreignKeyFromTable(table.name)
4024
4365
  }));
4025
4366
  }
4026
4367
  belongsTo(related, foreignKey, ownerKey) {
4027
- const relatedTable = related.repository().getTable();
4028
- return new BelongsToRelationQuery(this, related, belongsTo({
4368
+ const relatedClass = resolveRelated(related);
4369
+ const relatedTable = relatedClass.repository().getTable();
4370
+ return new BelongsToRelationQuery(this, relatedClass, belongsTo({
4029
4371
  name: relatedTable.name,
4030
4372
  foreignKey: foreignKey ?? foreignKeyFromTable(relatedTable.name),
4031
4373
  ownerKey: ownerKey ?? relatedTable.primaryKey
@@ -4033,8 +4375,9 @@ class Model {
4033
4375
  }
4034
4376
  belongsToMany(related, pivotTable, foreignPivotKey, relatedPivotKey) {
4035
4377
  const table = this.repository.getTable();
4036
- const relatedTable = related.repository().getTable();
4037
- return new BelongsToManyRelationQuery(this, related, belongsToMany({
4378
+ const relatedClass = resolveRelated(related);
4379
+ const relatedTable = relatedClass.repository().getTable();
4380
+ return new BelongsToManyRelationQuery(this, relatedClass, belongsToMany({
4038
4381
  name: relatedTable.name,
4039
4382
  pivotTable: pivotTable ?? pivotTableName(table.name, relatedTable.name),
4040
4383
  parentKey: table.primaryKey,
@@ -4043,31 +4386,38 @@ class Model {
4043
4386
  relatedPivotKey: relatedPivotKey ?? foreignKeyFromTable(relatedTable.name)
4044
4387
  }));
4045
4388
  }
4046
- morphMany(related, morphName, typeKey, idKey) {
4389
+ morphMany(related, morphName, typeKey, idKey, morphType) {
4047
4390
  const table = this.repository.getTable();
4048
- return new MorphManyRelationQuery(this, related, morphMany({
4391
+ const relatedClass = resolveRelated(related);
4392
+ return new MorphManyRelationQuery(this, relatedClass, morphMany({
4049
4393
  name: morphName,
4050
4394
  localKey: table.primaryKey,
4051
4395
  morphTypeKey: typeKey ?? `${morphName}_type`,
4052
4396
  morphIdKey: idKey ?? `${morphName}_id`,
4053
- morphType: table.name
4397
+ morphType: morphType ?? morphClassOf(this)
4054
4398
  }));
4055
4399
  }
4056
- morphOne(related, morphName, typeKey, idKey) {
4400
+ morphOne(related, morphName, typeKey, idKey, morphType) {
4057
4401
  const table = this.repository.getTable();
4058
- return new MorphOneRelationQuery(this, related, morphOne({
4402
+ const relatedClass = resolveRelated(related);
4403
+ return new MorphOneRelationQuery(this, relatedClass, morphOne({
4059
4404
  name: morphName,
4060
4405
  localKey: table.primaryKey,
4061
4406
  morphTypeKey: typeKey ?? `${morphName}_type`,
4062
4407
  morphIdKey: idKey ?? `${morphName}_id`,
4063
- morphType: table.name
4408
+ morphType: morphType ?? morphClassOf(this)
4064
4409
  }));
4065
4410
  }
4066
- morphTo(relatedByType, morphName = "imageable", typeKey, idKey) {
4067
- return new MorphToRelationQuery(this, relatedByType, morphTo({
4068
- name: morphName,
4069
- morphTypeKey: typeKey ?? `${morphName}_type`,
4070
- morphIdKey: idKey ?? `${morphName}_id`
4411
+ morphTo(relatedByType, morphName, typeKey, idKey) {
4412
+ const resolvedName = morphName ?? inferRelationMethodName("morphTo");
4413
+ if (!resolvedName) {
4414
+ throw new Error(`${this.constructor.name}.morphTo() needs an explicit morph name (Laravel infers it from the relation method).`);
4415
+ }
4416
+ const resolvedMap = Object.fromEntries(Object.entries(relatedByType).map(([type, related]) => [type, resolveRelated(related)]));
4417
+ return new MorphToRelationQuery(this, resolvedMap, morphTo({
4418
+ name: resolvedName,
4419
+ morphTypeKey: typeKey ?? `${resolvedName}_type`,
4420
+ morphIdKey: idKey ?? `${resolvedName}_id`
4071
4421
  }));
4072
4422
  }
4073
4423
  async load(...names) {
@@ -4099,6 +4449,14 @@ class Model {
4099
4449
  }
4100
4450
  function registerModelRepository(model, repository) {
4101
4451
  modelRepositories.set(model, repository);
4452
+ const name = model.name;
4453
+ if (name) {
4454
+ namedModels.set(name, model);
4455
+ }
4456
+ const morphClass = model.$morphClass;
4457
+ if (morphClass) {
4458
+ namedModels.set(morphClass, model);
4459
+ }
4102
4460
  ensureBooted(model);
4103
4461
  return model;
4104
4462
  }
@@ -6043,9 +6401,15 @@ class JsonResource {
6043
6401
 
6044
6402
  class ResourceCollection extends JsonResource {
6045
6403
  toArray() {
6046
- return {
6047
- data: this.resource.map((item) => item instanceof JsonResource ? item.toArray() : { ...item })
6048
- };
6404
+ const items = this.resource.map((item) => item instanceof JsonResource ? item.toArray() : { ...item });
6405
+ const wrap = this.constructor.wrap;
6406
+ if (wrap === null) {
6407
+ return { data: items };
6408
+ }
6409
+ return { [wrap]: items };
6410
+ }
6411
+ toResponse() {
6412
+ return { ...this.toArray(), ...this.extra };
6049
6413
  }
6050
6414
  }
6051
6415
  function toResourceCollection(items, transformer) {
@@ -8161,6 +8525,7 @@ export {
8161
8525
  Mailer,
8162
8526
  membershipService_default as MembershipService,
8163
8527
  Model,
8528
+ ModelQuery,
8164
8529
  MorphManyRelationQuery,
8165
8530
  MorphOneRelationQuery,
8166
8531
  MorphToRelationQuery,
@@ -8348,6 +8713,7 @@ export {
8348
8713
  readTenancyDriver,
8349
8714
  redirectResponse,
8350
8715
  registerDefaultDatabasePool,
8716
+ registerModelClass,
8351
8717
  registerModelRepository,
8352
8718
  registerShutdownHandler,
8353
8719
  renderKernelErrorChrome,