@bunyad/orm 0.1.0-alpha.0 → 0.2.0-beta.1

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/model.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { DatabaseManager as Db, setDefaultConnection, connections, LengthAwarePaginator, CursorPaginator, Paginator, dateTimeForStorage, withoutQueryWriteHook, wrapSqlName, } from "@bunyad/database";
2
- import { Collection } from "@bunyad/common";
2
+ import { Collection, LazyCollection } from "@bunyad/common";
3
3
  import { OrmCollection } from "./orm-collection.js";
4
4
  import { Attribute, castFromStorage, castToStorage, hashCastValue, isAlreadyHashed, enumFromStorage, enumToStorage, isAttribute, isCastType, isEnumCast, } from "./casts.js";
5
5
  import { bootIfNotBooted, fireModelEvent, hasAnyModelEventListeners, hasModelEventListeners, observeModel, registerModelEvent, withoutModelEvents, } from "./model-events.js";
@@ -355,7 +355,7 @@ export class Model {
355
355
  /** When false, `save()` skips `created_at` / `updated_at`. */
356
356
  static timestamps = true;
357
357
  /**
358
- * Laravel `$casts` property — prefer `static casts()` when adding enums / Attribute.
358
+ * `$casts` property — prefer `static casts()` when adding enums / Attribute.
359
359
  * May be a plain object or a method returning the map.
360
360
  */
361
361
  static casts = {};
@@ -366,19 +366,19 @@ export class Model {
366
366
  */
367
367
  static relations = {};
368
368
  /**
369
- * Laravel `$touches` — relation names whose related models get `touch()` after
369
+ * `$touches` — relation names whose related models get `touch()` after
370
370
  * this model is saved (or soft-deleted). Honor `withoutTouching`.
371
371
  */
372
372
  static touches = [];
373
- /** Laravel `$hidden`. */
373
+ /** `$hidden`. */
374
374
  static hidden = [];
375
- /** Laravel `$visible` allow-list (empty = all non-hidden). */
375
+ /** `$visible` allow-list (empty = all non-hidden). */
376
376
  static visible = [];
377
- /** Laravel `$appends` — accessors included in `toArray()`. */
377
+ /** `$appends` — accessors included in `toArray()`. */
378
378
  static appends = [];
379
- /** Laravel `$fillable`. */
379
+ /** `$fillable`. */
380
380
  static fillable = [];
381
- /** Laravel `$guarded` — default guard all until fillable is set. */
381
+ /** `$guarded` — default guard all until fillable is set. */
382
382
  static guarded = ["*"];
383
383
  /** Set in constructor / hydrate — avoid empty `{}` alloc before assign. */
384
384
  #original;
@@ -395,7 +395,7 @@ export class Model {
395
395
  #visible = null;
396
396
  /** Per-instance connection override (`$model->setConnection()`). */
397
397
  #connection;
398
- /** Laravel `$exists` — true after retrieve / successful insert. */
398
+ /** `$exists` — true after retrieve / successful insert. */
399
399
  get exists() {
400
400
  return this.#exists;
401
401
  }
@@ -444,7 +444,7 @@ export class Model {
444
444
  return out;
445
445
  }
446
446
  /**
447
- * Laravel `getAttribute` — throws when `preventAccessingMissingAttributes` is on
447
+ * `getAttribute` — throws when `preventAccessingMissingAttributes` is on
448
448
  * and the key was not retrieved / does not exist (relations exempt).
449
449
  */
450
450
  getAttribute(key) {
@@ -478,18 +478,18 @@ export class Model {
478
478
  }
479
479
  return undefined;
480
480
  }
481
- /** Laravel `isDirty`. */
481
+ /** `isDirty`. */
482
482
  isDirty(...attributes) {
483
483
  const dirty = this.getDirty();
484
484
  if (attributes.length === 0)
485
485
  return Object.keys(dirty).length > 0;
486
486
  return attributes.some((key) => Object.prototype.hasOwnProperty.call(dirty, key));
487
487
  }
488
- /** Laravel `isClean`. */
488
+ /** `isClean`. */
489
489
  isClean(...attributes) {
490
490
  return !this.isDirty(...attributes);
491
491
  }
492
- /** Laravel `getDirty`. */
492
+ /** `getDirty`. */
493
493
  getDirty() {
494
494
  const dirty = {};
495
495
  const original = this.#original ?? {};
@@ -505,11 +505,11 @@ export class Model {
505
505
  return { ...original };
506
506
  return original[key];
507
507
  }
508
- /** Laravel `getChanges` — attributes changed by the last save. */
508
+ /** `getChanges` — attributes changed by the last save. */
509
509
  getChanges() {
510
510
  return this.#changes ? { ...this.#changes } : {};
511
511
  }
512
- /** Laravel `wasChanged`. */
512
+ /** `wasChanged`. */
513
513
  wasChanged(...attributes) {
514
514
  const changes = this.#changes;
515
515
  if (!changes)
@@ -518,50 +518,50 @@ export class Model {
518
518
  return Object.keys(changes).length > 0;
519
519
  return attributes.some((key) => Object.prototype.hasOwnProperty.call(changes, key));
520
520
  }
521
- /** Laravel `wasRecentlyCreated`. */
521
+ /** `wasRecentlyCreated`. */
522
522
  wasRecentlyCreated() {
523
523
  return this.#wasRecentlyCreated;
524
524
  }
525
- /** Laravel `append`. */
525
+ /** `append`. */
526
526
  append(...attributes) {
527
527
  const current = this.getAppends();
528
528
  this.#appends = [...new Set([...current, ...attributes.flat()])];
529
529
  return this;
530
530
  }
531
- /** Laravel `setAppends`. */
531
+ /** `setAppends`. */
532
532
  setAppends(attributes) {
533
533
  this.#appends = [...attributes];
534
534
  return this;
535
535
  }
536
- /** Laravel `getAppends`. */
536
+ /** `getAppends`. */
537
537
  getAppends() {
538
538
  if (this.#appends)
539
539
  return [...this.#appends];
540
540
  return [...(this.constructor.appends ?? [])];
541
541
  }
542
- /** Laravel `getHidden`. */
542
+ /** `getHidden`. */
543
543
  getHidden() {
544
544
  if (this.#hidden)
545
545
  return [...this.#hidden];
546
546
  return [...(this.constructor.hidden ?? [])];
547
547
  }
548
- /** Laravel `getVisible`. */
548
+ /** `getVisible`. */
549
549
  getVisible() {
550
550
  if (this.#visible)
551
551
  return [...this.#visible];
552
552
  return [...(this.constructor.visible ?? [])];
553
553
  }
554
- /** Laravel `setHidden`. */
554
+ /** `setHidden`. */
555
555
  setHidden(attributes) {
556
556
  this.#hidden = [...attributes];
557
557
  return this;
558
558
  }
559
- /** Laravel `setVisible`. */
559
+ /** `setVisible`. */
560
560
  setVisible(attributes) {
561
561
  this.#visible = [...attributes];
562
562
  return this;
563
563
  }
564
- /** Laravel `makeVisible` — show attributes for this instance. */
564
+ /** `makeVisible` — show attributes for this instance. */
565
565
  makeVisible(...attributes) {
566
566
  const attrs = attributes.flat();
567
567
  const hidden = new Set(this.getHidden());
@@ -574,7 +574,7 @@ export class Model {
574
574
  }
575
575
  return this;
576
576
  }
577
- /** Laravel `makeHidden` — hide attributes for this instance. */
577
+ /** `makeHidden` — hide attributes for this instance. */
578
578
  makeHidden(...attributes) {
579
579
  const attrs = attributes.flat();
580
580
  this.#hidden = [...new Set([...this.getHidden(), ...attrs])];
@@ -586,7 +586,7 @@ export class Model {
586
586
  return this;
587
587
  }
588
588
  /**
589
- * Run a callback without touching timestamps (Laravel `Model::withoutTimestamps`).
589
+ * Run a callback without touching timestamps (`Model::withoutTimestamps`).
590
590
  */
591
591
  static async withoutTimestamps(callback) {
592
592
  const previous = this.timestamps;
@@ -605,23 +605,23 @@ export class Model {
605
605
  static isIgnoringTouch() {
606
606
  return isIgnoringTouch(this);
607
607
  }
608
- /** Laravel `Model::preventLazyLoading`. */
608
+ /** `Model::preventLazyLoading`. */
609
609
  static preventLazyLoading(prevent = true) {
610
610
  setPreventLazyLoading(prevent);
611
611
  }
612
- /** Laravel `Model::preventSilentlyDiscardingAttributes`. */
612
+ /** `Model::preventSilentlyDiscardingAttributes`. */
613
613
  static preventSilentlyDiscardingAttributes(prevent = true) {
614
614
  setPreventSilentlyDiscardingAttributes(prevent);
615
615
  }
616
- /** Laravel `Model::preventAccessingMissingAttributes`. */
616
+ /** `Model::preventAccessingMissingAttributes`. */
617
617
  static preventAccessingMissingAttributes(prevent = true) {
618
618
  setPreventAccessingMissingAttributes(prevent);
619
619
  }
620
- /** Laravel `Model::shouldBeStrict` — enable/disable the strictness trio. */
620
+ /** `Model::shouldBeStrict` — enable/disable the strictness trio. */
621
621
  static shouldBeStrict(should = true) {
622
622
  setShouldBeStrict(should);
623
623
  }
624
- /** Laravel `Model::addGlobalScope`. */
624
+ /** `Model::addGlobalScope`. */
625
625
  static addGlobalScope(nameOrScope, scope) {
626
626
  if (typeof nameOrScope === "function") {
627
627
  registerGlobalScope(this, nameOrScope.name || `scope_${Date.now()}`, nameOrScope);
@@ -631,28 +631,28 @@ export class Model {
631
631
  throw new Error("addGlobalScope requires a callback.");
632
632
  registerGlobalScope(this, nameOrScope, scope);
633
633
  }
634
- /** Laravel `Model::addGlobalScope` removal helper. */
634
+ /** `Model::addGlobalScope` removal helper. */
635
635
  static removeGlobalScope(name) {
636
636
  unregisterGlobalScope(this, name);
637
637
  }
638
638
  /**
639
- * Laravel `boot` — runs once per model class before first use.
639
+ * `boot` — runs once per model class before first use.
640
640
  * Prefer registering listeners in `booted()`.
641
641
  */
642
642
  static boot() { }
643
643
  /**
644
- * Laravel `booted` — runs once after `boot`. Typical place for `static::created(...)`.
644
+ * `booted` — runs once after `boot`. Typical place for `static::created(...)`.
645
645
  */
646
646
  static booted() { }
647
647
  /** Ensure this model class has been booted. */
648
648
  static bootIfNotBooted() {
649
649
  bootIfNotBooted(this);
650
650
  }
651
- /** Laravel `Model::observe`. */
651
+ /** `Model::observe`. */
652
652
  static observe(observer) {
653
653
  observeModel(this, observer);
654
654
  }
655
- /** Laravel `Model::withoutEvents`. */
655
+ /** `Model::withoutEvents`. */
656
656
  static withoutEvents(callback) {
657
657
  return withoutModelEvents(this, callback);
658
658
  }
@@ -779,7 +779,7 @@ export class Model {
779
779
  static getConnection() {
780
780
  return resolveConnection(this.connection);
781
781
  }
782
- /** Laravel `$model->setConnection($name)`. */
782
+ /** `$model->setConnection($name)`. */
783
783
  setConnection(connection) {
784
784
  this.#connection = connection;
785
785
  return this;
@@ -799,7 +799,7 @@ export class Model {
799
799
  return manager;
800
800
  }
801
801
  /**
802
- * Laravel `Model::on($connection)` — same model, different named connection.
802
+ * `Model::on($connection)` — same model, different named connection.
803
803
  */
804
804
  static on(connection) {
805
805
  return this.newQuery({ connection });
@@ -814,21 +814,21 @@ export class Model {
814
814
  const query = new Ctor(this, options);
815
815
  return withLocalScopeProxy(query);
816
816
  }
817
- /** Laravel `withoutGlobalScopes()`. */
817
+ /** `withoutGlobalScopes()`. */
818
818
  static withoutGlobalScopes(names) {
819
819
  return this.newQuery({
820
820
  withoutGlobalScopes: names ?? true,
821
821
  });
822
822
  }
823
- /** Laravel `withoutGlobalScope($name)`. */
823
+ /** `withoutGlobalScope($name)`. */
824
824
  static withoutGlobalScope(name) {
825
825
  return this.newQuery({ withoutGlobalScopes: [name] });
826
826
  }
827
- /** Laravel `Model::query()` — scoped `ModelQuery` (not a raw table builder). */
827
+ /** `Model::query()` — scoped `ModelQuery` (not a raw table builder). */
828
828
  static query(options) {
829
829
  return this.newQuery(options);
830
830
  }
831
- /** Laravel `Model::find`. */
831
+ /** `Model::find`. */
832
832
  static find(id) {
833
833
  this.bootIfNotBooted();
834
834
  if (canFastFind(this)) {
@@ -839,12 +839,12 @@ export class Model {
839
839
  .where(this.primaryKey, id)
840
840
  .first();
841
841
  }
842
- /** Laravel `getRouteKeyName()` — column used for implicit route binding. */
842
+ /** `getRouteKeyName()` — column used for implicit route binding. */
843
843
  static getRouteKeyName() {
844
844
  return this.primaryKey;
845
845
  }
846
846
  /**
847
- * Laravel `resolveRouteBinding` — find by PK or custom `{param:column}` field.
847
+ * `resolveRouteBinding` — find by PK or custom `{param:column}` field.
848
848
  */
849
849
  static resolveRouteBinding(value, field) {
850
850
  const column = field ?? this.getRouteKeyName();
@@ -855,7 +855,7 @@ export class Model {
855
855
  return this.newQuery().where(column, value).first();
856
856
  }
857
857
  /**
858
- * Laravel SoftDeletes `resolveSoftDeletableRouteBinding` — same as
858
+ * `resolveSoftDeletableRouteBinding` — same as
859
859
  * `resolveRouteBinding` but includes soft-deleted rows (`withTrashed()`).
860
860
  * Used by the router when the route opts in via `withTrashed()`.
861
861
  */
@@ -885,20 +885,20 @@ export class Model {
885
885
  model.#exists = true;
886
886
  return model;
887
887
  }
888
- /** Laravel `Model::findOrFail`. */
888
+ /** `Model::findOrFail`. */
889
889
  static async findOrFail(id) {
890
890
  const model = await this.find(id);
891
891
  if (!model)
892
892
  throw new ModelNotFoundException();
893
893
  return model;
894
894
  }
895
- /** Laravel `Model::all`. */
895
+ /** `Model::all`. */
896
896
  static async all() {
897
897
  this.bootIfNotBooted();
898
898
  return this.newQuery().get();
899
899
  }
900
900
  /**
901
- * Laravel `Model::get` — same as `query().get()` / `all()`.
901
+ * `Model::get` — same as `query().get()` / `all()`.
902
902
  * Typed helper for autocomplete; Proxy also forwards other terminals.
903
903
  */
904
904
  static get() {
@@ -995,30 +995,30 @@ export class Model {
995
995
  return q.whereDate(column, opOrValue);
996
996
  return q.whereDate(column, String(opOrValue), value);
997
997
  }
998
- /** Laravel `Model::whereNot(closure)`. */
998
+ /** `Model::whereNot(closure)`. */
999
999
  static whereNot(callback) {
1000
1000
  this.bootIfNotBooted();
1001
1001
  return this.newQuery().whereNot(callback);
1002
1002
  }
1003
- /** Laravel `Model::orWhereNot(closure)`. */
1003
+ /** `Model::orWhereNot(closure)`. */
1004
1004
  static orWhereNot(callback) {
1005
1005
  this.bootIfNotBooted();
1006
1006
  return this.newQuery().orWhereNot(callback);
1007
1007
  }
1008
- /** Laravel `withTrashed()`. */
1008
+ /** `withTrashed()`. */
1009
1009
  static withTrashed() {
1010
1010
  this.bootIfNotBooted();
1011
1011
  return this.newQuery({
1012
1012
  withTrashed: true,
1013
1013
  });
1014
1014
  }
1015
- /** Laravel `onlyTrashed()`. */
1015
+ /** `onlyTrashed()`. */
1016
1016
  static onlyTrashed() {
1017
1017
  return this.newQuery({
1018
1018
  onlyTrashed: true,
1019
1019
  });
1020
1020
  }
1021
- /** Laravel `withoutTrashed()`. */
1021
+ /** `withoutTrashed()`. */
1022
1022
  static withoutTrashed() {
1023
1023
  return this.newQuery({
1024
1024
  withTrashed: false,
@@ -1035,27 +1035,27 @@ export class Model {
1035
1035
  eagerLoad: normalizeWithRelations(relations),
1036
1036
  });
1037
1037
  }
1038
- /** Laravel `Model::whereHas`. */
1038
+ /** `Model::whereHas`. */
1039
1039
  static whereHas(relation, callback) {
1040
1040
  this.bootIfNotBooted();
1041
1041
  return this.newQuery().whereHas(relation, callback);
1042
1042
  }
1043
- /** Laravel `Model::whereDoesntHave`. */
1043
+ /** `Model::whereDoesntHave`. */
1044
1044
  static whereDoesntHave(relation, callback) {
1045
1045
  this.bootIfNotBooted();
1046
1046
  return this.newQuery().whereDoesntHave(relation, callback);
1047
1047
  }
1048
- /** Laravel `Model::orWhereHas`. */
1048
+ /** `Model::orWhereHas`. */
1049
1049
  static orWhereHas(relation, callback) {
1050
1050
  this.bootIfNotBooted();
1051
1051
  return this.newQuery().orWhereHas(relation, callback);
1052
1052
  }
1053
- /** Laravel `Model::whereRelation`. */
1053
+ /** `Model::whereRelation`. */
1054
1054
  static whereRelation(relation, column, opOrValue, value) {
1055
1055
  this.bootIfNotBooted();
1056
1056
  return this.newQuery().whereRelation(relation, column, opOrValue, value);
1057
1057
  }
1058
- /** Laravel `Model::orWhereRelation`. */
1058
+ /** `Model::orWhereRelation`. */
1059
1059
  static orWhereRelation(relation, column, opOrValue, value) {
1060
1060
  this.bootIfNotBooted();
1061
1061
  return this.newQuery().orWhereRelation(relation, column, opOrValue, value);
@@ -1065,7 +1065,7 @@ export class Model {
1065
1065
  this.bootIfNotBooted();
1066
1066
  return this.newQuery().withCount(...relations);
1067
1067
  }
1068
- /** Laravel `Model::withSum`. */
1068
+ /** `Model::withSum`. */
1069
1069
  static withSum(relation, column) {
1070
1070
  this.bootIfNotBooted();
1071
1071
  return this.newQuery().withSum(relation, column);
@@ -1110,40 +1110,56 @@ export class Model {
1110
1110
  this.bootIfNotBooted();
1111
1111
  return this.newQuery().whereKey(id);
1112
1112
  }
1113
- /** Laravel `Model::whereUuid`. */
1113
+ /** `Model::whereUuid`. */
1114
1114
  static whereUuid(column, value) {
1115
1115
  this.bootIfNotBooted();
1116
1116
  return this.newQuery().whereUuid(column, value);
1117
1117
  }
1118
- /** Laravel `Model::whereUlid`. */
1118
+ /** `Model::whereUlid`. */
1119
1119
  static whereUlid(column, value) {
1120
1120
  this.bootIfNotBooted();
1121
1121
  return this.newQuery().whereUlid(column, value);
1122
1122
  }
1123
- /** Laravel `Model::join`. */
1123
+ /** `Model::join`. */
1124
1124
  static join(table, first, op, second) {
1125
1125
  this.bootIfNotBooted();
1126
1126
  return this.newQuery().join(table, first, op, second);
1127
1127
  }
1128
- /** Laravel `Model::leftJoin`. */
1128
+ /** `Model::leftJoin`. */
1129
1129
  static leftJoin(table, first, op, second) {
1130
1130
  this.bootIfNotBooted();
1131
1131
  return this.newQuery().leftJoin(table, first, op, second);
1132
1132
  }
1133
- /** Laravel `Model::paginate`. */
1133
+ /** `Model::paginate`. */
1134
1134
  static async paginate(perPage = 15, page, options = {}) {
1135
1135
  return this.newQuery().paginate(perPage, page, options);
1136
1136
  }
1137
- /** Laravel `Model::cursorPaginate`. */
1137
+ /** `Model::cursorPaginate`. */
1138
1138
  static async cursorPaginate(perPage = 15, cursor = null, options = {}) {
1139
1139
  return this.newQuery().cursorPaginate(perPage, cursor, options);
1140
1140
  }
1141
- /** Laravel `Model::create` (respects `$fillable` / `$guarded`). */
1141
+ /** `Model::cursor()`: stream models from a single query. */
1142
+ static cursor(chunkSize) {
1143
+ return this.newQuery().cursor(chunkSize);
1144
+ }
1145
+ /** `Model::lazy()`: chunked iteration, one query per chunk. */
1146
+ static lazy(chunkSize) {
1147
+ return this.newQuery().lazy(chunkSize);
1148
+ }
1149
+ /** `Model::lazyById()`: chunked iteration by primary key (safe while updating). */
1150
+ static lazyById(chunkSize, column) {
1151
+ return this.newQuery().lazyById(chunkSize, column);
1152
+ }
1153
+ /** `Model::lazyByIdDesc()`. */
1154
+ static lazyByIdDesc(chunkSize, column) {
1155
+ return this.newQuery().lazyByIdDesc(chunkSize, column);
1156
+ }
1157
+ /** `Model::create` (respects `$fillable` / `$guarded`). */
1142
1158
  static create(attributes) {
1143
1159
  this.bootIfNotBooted();
1144
1160
  return this.forceCreate(filterFillable(this, attributes));
1145
1161
  }
1146
- /** Laravel `Model::forceCreate` — insert every given attribute, ignoring `$fillable`. */
1162
+ /** `Model::forceCreate` — insert every given attribute, ignoring `$fillable`. */
1147
1163
  static forceCreate(useAttrs) {
1148
1164
  this.bootIfNotBooted();
1149
1165
  // Assign raw attributes (skip get-casts) so mutators like Attribute.set can run on save.
@@ -1176,7 +1192,7 @@ export class Model {
1176
1192
  }
1177
1193
  return after(saved);
1178
1194
  }
1179
- /** Laravel `firstOrNew`. */
1195
+ /** `firstOrNew`. */
1180
1196
  static async firstOrNew(attributes, values = {}) {
1181
1197
  let q = this.newQuery();
1182
1198
  for (const [column, value] of Object.entries(attributes)) {
@@ -1187,7 +1203,7 @@ export class Model {
1187
1203
  return existing;
1188
1204
  return new this({ ...attributes, ...values });
1189
1205
  }
1190
- /** Laravel `firstOrCreate`. */
1206
+ /** `firstOrCreate`. */
1191
1207
  static async firstOrCreate(attributes, values = {}) {
1192
1208
  let q = this.newQuery();
1193
1209
  for (const [column, value] of Object.entries(attributes)) {
@@ -1198,7 +1214,7 @@ export class Model {
1198
1214
  return existing;
1199
1215
  return this.create({ ...attributes, ...values });
1200
1216
  }
1201
- /** Laravel `updateOrCreate`. */
1217
+ /** `updateOrCreate`. */
1202
1218
  static async updateOrCreate(attributes, values = {}) {
1203
1219
  let q = this.newQuery();
1204
1220
  for (const [column, value] of Object.entries(attributes)) {
@@ -1220,21 +1236,21 @@ export class Model {
1220
1236
  return this.newQuery().insert(values);
1221
1237
  }
1222
1238
  /**
1223
- * Laravel `Model::upsert` — bulk insert-or-update on unique conflict.
1239
+ * `Model::upsert` — bulk insert-or-update on unique conflict.
1224
1240
  * Delegates to `ModelQuery.upsert` (timestamps stamped when enabled).
1225
1241
  */
1226
1242
  static async upsert(values, uniqueBy, update = null) {
1227
1243
  this.bootIfNotBooted();
1228
1244
  return this.newQuery().upsert(values, uniqueBy, update);
1229
1245
  }
1230
- /** Laravel `$model->fill($attributes)`. */
1246
+ /** `$model->fill($attributes)`. */
1231
1247
  fill(attributes) {
1232
1248
  const ctor = this.constructor;
1233
1249
  const finalAttrs = filterFillable(ctor, attributes);
1234
1250
  assignOwn(this, ctor.castAttributes(finalAttrs, "get"));
1235
1251
  return this;
1236
1252
  }
1237
- /** Laravel `$model->update($attributes)`. */
1253
+ /** `$model->update($attributes)`. */
1238
1254
  async update(attributes) {
1239
1255
  this.fill(attributes);
1240
1256
  await this.save();
@@ -1441,16 +1457,16 @@ export class Model {
1441
1457
  }
1442
1458
  this.syncOriginal();
1443
1459
  await fireModelEvent(this, "saved");
1444
- // Laravel finishSave → touchOwners (Relation.touch honors withoutTouching on related).
1460
+ // finishSave → touchOwners (Relation.touch honors withoutTouching on related).
1445
1461
  await this.touchOwners();
1446
1462
  return this;
1447
1463
  }
1448
- /** Laravel `$model->saveQuietly()`. */
1464
+ /** `$model->saveQuietly()`. */
1449
1465
  async saveQuietly() {
1450
1466
  const ctor = this.constructor;
1451
1467
  return ctor.withoutEvents(() => this.save());
1452
1468
  }
1453
- /** Laravel `$model->refresh()`. */
1469
+ /** `$model->refresh()`. */
1454
1470
  async refresh() {
1455
1471
  const ctor = this.constructor;
1456
1472
  const key = ctor.primaryKey;
@@ -1464,7 +1480,7 @@ export class Model {
1464
1480
  return this;
1465
1481
  }
1466
1482
  /**
1467
- * Laravel-style refresh under row lock (`lockForUpdate`).
1483
+ * Refresh under row lock (`lockForUpdate`).
1468
1484
  * Reloads this instance from a locked select of the same primary key.
1469
1485
  */
1470
1486
  async refreshForUpdate() {
@@ -1482,7 +1498,7 @@ export class Model {
1482
1498
  }
1483
1499
  return this;
1484
1500
  }
1485
- /** Laravel `$model->fresh()`. */
1501
+ /** `$model->fresh()`. */
1486
1502
  async fresh() {
1487
1503
  const ctor = this.constructor;
1488
1504
  const key = ctor.primaryKey;
@@ -1502,7 +1518,7 @@ export class Model {
1502
1518
  await eagerLoadModels([this], missing);
1503
1519
  return this;
1504
1520
  }
1505
- /** Laravel `$model->relationLoaded($key)`. */
1521
+ /** `$model->relationLoaded($key)`. */
1506
1522
  relationLoaded(key) {
1507
1523
  return Object.prototype.hasOwnProperty.call(this, key);
1508
1524
  }
@@ -1526,14 +1542,14 @@ export class Model {
1526
1542
  }
1527
1543
  }
1528
1544
  /**
1529
- * Laravel `$model->touches($relation)` — whether `$touches` lists this relation.
1545
+ * `$model->touches($relation)` — whether `$touches` lists this relation.
1530
1546
  */
1531
1547
  touches(relation) {
1532
1548
  const ctor = this.constructor;
1533
1549
  return (ctor.touches ?? []).includes(relation);
1534
1550
  }
1535
1551
  /**
1536
- * Laravel `$model->touchOwners()` — touch each relation in `$touches`.
1552
+ * `$model->touchOwners()` — touch each relation in `$touches`.
1537
1553
  * Uses `relation.touch()` when available (BelongsTo / HasOne / HasMany);
1538
1554
  * otherwise loads the related model and calls `touch()` on it.
1539
1555
  */
@@ -1556,7 +1572,7 @@ export class Model {
1556
1572
  }
1557
1573
  }
1558
1574
  /**
1559
- * Laravel `$model->touch()` — bump `updated_at` (no-op while `withoutTouching`).
1575
+ * `$model->touch()` — bump `updated_at` (no-op while `withoutTouching`).
1560
1576
  * Saving also runs `$touches` / `touchOwners()` for listed relations.
1561
1577
  */
1562
1578
  async touch() {
@@ -1574,7 +1590,7 @@ export class Model {
1574
1590
  return true;
1575
1591
  }
1576
1592
  /**
1577
- * Relation query builder (`$model->product()` in Laravel).
1593
+ * Relation query builder (`$model->product()`).
1578
1594
  *
1579
1595
  * Define relations on `static relations` and declare the loaded property:
1580
1596
  * `declare product: Product | null` — then use `model.product` after `with('product')`,
@@ -1605,7 +1621,7 @@ export class Model {
1605
1621
  }
1606
1622
  throw new Error(`Call to undefined relationship [${name}] on model [${ctor.name ?? "Model"}].`);
1607
1623
  }
1608
- /** Laravel `$model->replicate()`. */
1624
+ /** `$model->replicate()`. */
1609
1625
  replicate(except = []) {
1610
1626
  const ctor = this.constructor;
1611
1627
  ctor.bootIfNotBooted();
@@ -1618,7 +1634,7 @@ export class Model {
1618
1634
  attrs[k] = v;
1619
1635
  }
1620
1636
  const copy = new ctor(attrs);
1621
- // Fire sync-style; listeners may be async but replicate is sync in Laravel.
1637
+ // Fire sync-style; listeners may be async but replicate is sync.
1622
1638
  void fireModelEvent(copy, "replicating");
1623
1639
  return copy;
1624
1640
  }
@@ -1633,7 +1649,7 @@ export class Model {
1633
1649
  await fireModelEvent(this, "trashed");
1634
1650
  return;
1635
1651
  }
1636
- // Laravel delete(): touchOwners before performDeleteOnModel.
1652
+ // delete(): touchOwners before performDeleteOnModel.
1637
1653
  await this.touchOwners();
1638
1654
  await this.#performForceDelete();
1639
1655
  await fireModelEvent(this, "deleted");
@@ -1664,7 +1680,7 @@ export class Model {
1664
1680
  await fireModelEvent(this, "forceDeleted");
1665
1681
  await fireModelEvent(this, "deleted");
1666
1682
  }
1667
- /** Laravel `$model->deleteQuietly()`. */
1683
+ /** `$model->deleteQuietly()`. */
1668
1684
  async deleteQuietly() {
1669
1685
  const ctor = this.constructor;
1670
1686
  await ctor.withoutEvents(() => this.delete());
@@ -1705,7 +1721,7 @@ export class Model {
1705
1721
  const value = this[col];
1706
1722
  return value != null && value !== "";
1707
1723
  }
1708
- /** Laravel `$model->toArray()`. */
1724
+ /** `$model->toArray()`. */
1709
1725
  toArray() {
1710
1726
  const ctor = this.constructor;
1711
1727
  const hiddenList = this.getHidden();
@@ -1746,7 +1762,7 @@ export class Model {
1746
1762
  }
1747
1763
  return this[key];
1748
1764
  }
1749
- /** Laravel `$model->loadCount(...$relations)`. */
1765
+ /** `$model->loadCount(...$relations)`. */
1750
1766
  async loadCount(...relations) {
1751
1767
  const names = relations.flat();
1752
1768
  if (names.length === 0)
@@ -1758,7 +1774,7 @@ export class Model {
1758
1774
  }
1759
1775
  return this;
1760
1776
  }
1761
- /** Laravel `$model->loadSum($relation, $column)`. */
1777
+ /** `$model->loadSum($relation, $column)`. */
1762
1778
  async loadSum(relation, column) {
1763
1779
  const parsed = parseRelationAlias(relation);
1764
1780
  const value = await aggregateRelation(this, parsed.relation, "sum", column);
@@ -1783,7 +1799,7 @@ export class Model {
1783
1799
  this[parsed.alias ?? `${parsed.relation}_max_${column}`] = value;
1784
1800
  return this;
1785
1801
  }
1786
- /** Laravel `$model->loadExists(...$relations)`. */
1802
+ /** `$model->loadExists(...$relations)`. */
1787
1803
  async loadExists(...relations) {
1788
1804
  for (const relation of relations.flat()) {
1789
1805
  const parsed = parseRelationAlias(relation);
@@ -1799,7 +1815,7 @@ export class Model {
1799
1815
  toJson() {
1800
1816
  return this.toJSON();
1801
1817
  }
1802
- /** Laravel `$model->increment($column, $amount)`. */
1818
+ /** `$model->increment($column, $amount)`. */
1803
1819
  async increment(column, amount = 1) {
1804
1820
  const row = this;
1805
1821
  const current = Number(row[column] ?? 0);
@@ -1807,11 +1823,11 @@ export class Model {
1807
1823
  await this.save();
1808
1824
  return this;
1809
1825
  }
1810
- /** Laravel `$model->decrement($column, $amount)`. */
1826
+ /** `$model->decrement($column, $amount)`. */
1811
1827
  async decrement(column, amount = 1) {
1812
1828
  return this.increment(column, -amount);
1813
1829
  }
1814
- /** Laravel `Model::destroy($ids)`. */
1830
+ /** `Model::destroy($ids)`. */
1815
1831
  static async destroy(ids) {
1816
1832
  const list = Array.isArray(ids) ? ids : [ids];
1817
1833
  let n = 0;
@@ -1825,7 +1841,7 @@ export class Model {
1825
1841
  return n;
1826
1842
  }
1827
1843
  /**
1828
- * Laravel `Model::forceDestroy($ids)` — permanent delete by key(s).
1844
+ * `Model::forceDestroy($ids)` — permanent delete by key(s).
1829
1845
  * Soft-delete models are found via `withTrashed()` so trashed rows are included.
1830
1846
  */
1831
1847
  static async forceDestroy(ids) {
@@ -1842,18 +1858,18 @@ export class Model {
1842
1858
  }
1843
1859
  return n;
1844
1860
  }
1845
- /** Laravel `$this->hasMany(Related::class)`. */
1861
+ /** `$this->hasMany(Related::class)`. */
1846
1862
  hasMany(related, foreignKey, localKey) {
1847
1863
  const key = foreignKey ?? `${singular(this.constructor.table)}_id`;
1848
1864
  return new HasMany(this, related, key, localKey);
1849
1865
  }
1850
- /** Laravel `$this->hasOne(Related::class)`. */
1866
+ /** `$this->hasOne(Related::class)`. */
1851
1867
  hasOne(related, foreignKey, localKey) {
1852
1868
  const key = foreignKey ?? `${singular(this.constructor.table)}_id`;
1853
1869
  return new HasOne(this, related, key, localKey);
1854
1870
  }
1855
1871
  /**
1856
- * Laravel `$this->hasManyThrough(Related::class, Through::class)`.
1872
+ * `$this->hasManyThrough(Related::class, Through::class)`.
1857
1873
  * Parent → Through (firstKey) → Related (secondKey).
1858
1874
  */
1859
1875
  hasManyThrough(related, through, firstKey, secondKey, localKey, secondLocalKey) {
@@ -1861,18 +1877,18 @@ export class Model {
1861
1877
  return new HasManyThrough(this, related, through, firstKey ?? `${singular(parentCtor.table)}_id`, secondKey ?? `${singular(through.table)}_id`, localKey ?? parentCtor.primaryKey, secondLocalKey ?? through.primaryKey);
1862
1878
  }
1863
1879
  /**
1864
- * Laravel `$this->hasOneThrough(Related::class, Through::class)`.
1880
+ * `$this->hasOneThrough(Related::class, Through::class)`.
1865
1881
  */
1866
1882
  hasOneThrough(related, through, firstKey, secondKey, localKey, secondLocalKey) {
1867
1883
  const parentCtor = this.constructor;
1868
1884
  return new HasOneThrough(this, related, through, firstKey ?? `${singular(parentCtor.table)}_id`, secondKey ?? `${singular(through.table)}_id`, localKey ?? parentCtor.primaryKey, secondLocalKey ?? through.primaryKey);
1869
1885
  }
1870
- /** Laravel `$this->belongsTo(Related::class)`. */
1886
+ /** `$this->belongsTo(Related::class)`. */
1871
1887
  belongsTo(related, foreignKey, ownerKey) {
1872
1888
  const key = foreignKey ?? `${singular(related.table)}_id`;
1873
1889
  return new BelongsTo(this, related, key, ownerKey);
1874
1890
  }
1875
- /** Laravel `$this->belongsToMany(Related::class, pivot)`. */
1891
+ /** `$this->belongsToMany(Related::class, pivot)`. */
1876
1892
  belongsToMany(related, table, foreignPivotKey, relatedPivotKey) {
1877
1893
  const parent = this.constructor;
1878
1894
  const pivot = table ??
@@ -1903,15 +1919,15 @@ export class Model {
1903
1919
  : [name, primaryMorph].filter((value, index, all) => all.indexOf(value) === index);
1904
1920
  return new MorphToMany(this, related, table, foreign, relatedKey, morphTypeColumn, morphTypes, options.pivotTenantKey, options.parentTenantKey ?? options.pivotTenantKey);
1905
1921
  }
1906
- /** Laravel `$this->morphMany(Related::class, name)`. */
1922
+ /** `$this->morphMany(Related::class, name)`. */
1907
1923
  morphMany(related, name, type, id, localKey) {
1908
1924
  return new MorphMany(this, related, type ?? `${name}_type`, id ?? `${name}_id`, morphTypeFor(this.constructor), localKey);
1909
1925
  }
1910
- /** Laravel `$this->morphOne(Related::class, name)`. */
1926
+ /** `$this->morphOne(Related::class, name)`. */
1911
1927
  morphOne(related, name, type, id, localKey) {
1912
1928
  return new MorphOne(this, related, type ?? `${name}_type`, id ?? `${name}_id`, morphTypeFor(this.constructor), localKey);
1913
1929
  }
1914
- /** Laravel `$this->morphTo($name)`. */
1930
+ /** `$this->morphTo($name)`. */
1915
1931
  morphTo(name, type, id, ownerKey) {
1916
1932
  return new MorphTo(this, type ?? `${name}_type`, id ?? `${name}_id`, ownerKey);
1917
1933
  }