@getstrata/core 0.5.98 → 0.5.100

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.
@@ -97,7 +97,7 @@ function buildOperatorClauses(column, operator, params) {
97
97
  clauses.push(`${column} <= ${pushParam(params, operator.lte)}`);
98
98
  }
99
99
  if (operator.ilike !== undefined) {
100
- clauses.push(`${column} ILIKE ${pushParam(params, `%${operator.ilike}%`)}`);
100
+ clauses.push(`${column} ILIKE ${pushParam(params, operator.ilike)}`);
101
101
  }
102
102
  if (operator.tsMatch !== undefined) {
103
103
  clauses.push(`${column} @@ plainto_tsquery('english', ${pushParam(params, operator.tsMatch)})`);
@@ -458,6 +458,9 @@ function ownerId(owner, ownerKey) {
458
458
  }
459
459
  throw new Error("belongsTo.associate() requires a related model or { id }.");
460
460
  }
461
+ function thenGet(get, onfulfilled, onrejected) {
462
+ return get().then(onfulfilled ?? undefined, onrejected ?? undefined);
463
+ }
461
464
 
462
465
  class HasManyRelationQuery {
463
466
  parent;
@@ -521,13 +524,28 @@ class HasManyRelationQuery {
521
524
  return rows[0] ?? null;
522
525
  }
523
526
  async count() {
524
- return (await this.get()).length;
527
+ return this.scopedQuery().count();
528
+ }
529
+ then(onfulfilled, onrejected) {
530
+ return thenGet(() => this.get(), onfulfilled, onrejected);
525
531
  }
526
532
  async create(attributes = {}) {
527
533
  return this.related.create(attributes, {
528
534
  [this.relation.foreignKey]: this.parent.get(this.relation.localKey)
529
535
  });
530
536
  }
537
+ async save(related) {
538
+ const forced = {
539
+ [this.relation.foreignKey]: this.parent.get(this.relation.localKey)
540
+ };
541
+ const savable = related;
542
+ if (typeof savable.save === "function") {
543
+ savable.mergeAttributes?.(forced);
544
+ await savable.save();
545
+ return related;
546
+ }
547
+ return this.create(related);
548
+ }
531
549
  async createMany(records) {
532
550
  const created = [];
533
551
  for (const attributes of records) {
@@ -575,11 +593,17 @@ class HasOneRelationQuery {
575
593
  return this.get();
576
594
  }
577
595
  async count() {
578
- return await this.get() ? 1 : 0;
596
+ return this.inner.count();
597
+ }
598
+ then(onfulfilled, onrejected) {
599
+ return thenGet(() => this.get(), onfulfilled, onrejected);
579
600
  }
580
601
  async create(attributes = {}) {
581
602
  return this.inner.create(attributes);
582
603
  }
604
+ async save(related) {
605
+ return this.inner.save(related);
606
+ }
583
607
  }
584
608
 
585
609
  class BelongsToRelationQuery {
@@ -587,12 +611,17 @@ class BelongsToRelationQuery {
587
611
  related;
588
612
  relation;
589
613
  kind = "belongsTo";
614
+ extraWhere = {};
590
615
  extraOptions = {};
591
616
  constructor(parent, related, relation) {
592
617
  this.parent = parent;
593
618
  this.related = related;
594
619
  this.relation = relation;
595
620
  }
621
+ where(where) {
622
+ this.extraWhere = { ...this.extraWhere, ...where };
623
+ return this;
624
+ }
596
625
  orderBy(orderBy) {
597
626
  this.extraOptions = { ...this.extraOptions, orderBy };
598
627
  return this;
@@ -606,10 +635,10 @@ class BelongsToRelationQuery {
606
635
  }
607
636
  toExistsClause(parentTable) {
608
637
  const relatedTable = this.related.repository().getTable().name;
609
- return {
610
- sql: `SELECT 1 FROM ${quoteIdentifier(relatedTable)} WHERE ${qualifyColumn(relatedTable, this.relation.ownerKey)} = ${qualifyColumn(parentTable, this.relation.foreignKey)}`,
611
- params: []
612
- };
638
+ const extra = buildAdvancedWhereClause(relatedTable, this.extraWhere, [], []);
639
+ const extraSql = extra.clause.replace(/^ WHERE /, "");
640
+ const sql = `SELECT 1 FROM ${quoteIdentifier(relatedTable)} WHERE ${qualifyColumn(relatedTable, this.relation.ownerKey)} = ${qualifyColumn(parentTable, this.relation.foreignKey)}${extraSql ? ` AND ${extraSql}` : ""}`;
641
+ return { sql, params: extra.params };
613
642
  }
614
643
  async get() {
615
644
  const foreign = this.parent.get(this.relation.foreignKey);
@@ -617,7 +646,7 @@ class BelongsToRelationQuery {
617
646
  return null;
618
647
  }
619
648
  const repository = this.related.repository().withConnection(this.parent.getRepository().getConnection());
620
- let query = repository.query(asWhere({ [this.relation.ownerKey]: foreign }));
649
+ let query = repository.query(asWhere({ [this.relation.ownerKey]: foreign, ...this.extraWhere }));
621
650
  if (this.extraOptions.orderBy) {
622
651
  query = query.orderBy(this.extraOptions.orderBy);
623
652
  }
@@ -627,6 +656,9 @@ class BelongsToRelationQuery {
627
656
  async first() {
628
657
  return this.get();
629
658
  }
659
+ then(onfulfilled, onrejected) {
660
+ return thenGet(() => this.get(), onfulfilled, onrejected);
661
+ }
630
662
  async associate(owner) {
631
663
  await this.parent.getRepository().updateById(this.parent.id, {
632
664
  [this.relation.foreignKey]: ownerId(owner, this.relation.ownerKey)
@@ -646,6 +678,7 @@ class BelongsToManyRelationQuery {
646
678
  kind = "belongsToMany";
647
679
  extraWhere = {};
648
680
  extraOptions = {};
681
+ pivotValues = {};
649
682
  constructor(parent, related, relation) {
650
683
  this.parent = parent;
651
684
  this.related = related;
@@ -659,7 +692,13 @@ class BelongsToManyRelationQuery {
659
692
  this.extraOptions = { ...this.extraOptions, orderBy };
660
693
  return this;
661
694
  }
662
- applyEagerLoad(_query, _alias) {}
695
+ applyEagerLoad(query, alias) {
696
+ query.withBelongsToMany(alias, this.relation, this.related.repository(), this.extraOptions);
697
+ }
698
+ withPivotValues(values) {
699
+ this.pivotValues = { ...this.pivotValues, ...values };
700
+ return this;
701
+ }
663
702
  hydrateEager(row, alias) {
664
703
  const value = row[alias] ?? [];
665
704
  const rows = Array.isArray(value) ? value : [];
@@ -699,13 +738,34 @@ class BelongsToManyRelationQuery {
699
738
  return rows[0] ?? null;
700
739
  }
701
740
  async count() {
702
- return (await this.get()).length;
741
+ const parentId = this.parent.get(this.relation.parentKey);
742
+ const rows = await this.connection().unsafe(`SELECT COUNT(*) AS count FROM ${this.relation.pivotTable} WHERE ${String(this.relation.foreignPivotKey)} = $1`, [parentId]);
743
+ return Number(rows[0]?.count ?? 0);
744
+ }
745
+ then(onfulfilled, onrejected) {
746
+ return thenGet(() => this.get(), onfulfilled, onrejected);
703
747
  }
704
748
  async attach(ids) {
705
749
  const list = Array.isArray(ids) ? ids : [ids];
706
750
  const parentId = this.parent.get(this.relation.parentKey);
751
+ const extraKeys = Object.keys(this.pivotValues);
752
+ const extraColumns = extraKeys.length > 0 ? `, ${extraKeys.join(", ")}` : "";
753
+ const extraPlaceholders = extraKeys.map((_, index) => `$${index + 3}`).join(", ");
754
+ const extraValues = extraKeys.map((key) => this.pivotValues[key]);
707
755
  for (const id of list) {
708
- await this.connection().unsafe(`INSERT INTO ${this.relation.pivotTable} (${String(this.relation.foreignPivotKey)}, ${String(this.relation.relatedPivotKey)}) VALUES ($1, $2)`, [parentId, id]);
756
+ 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]);
757
+ }
758
+ }
759
+ async toggle(ids) {
760
+ const list = Array.isArray(ids) ? ids : [ids];
761
+ const parentId = this.parent.get(this.relation.parentKey);
762
+ for (const id of list) {
763
+ 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]);
764
+ if (existing.length > 0) {
765
+ await this.detach(id);
766
+ } else {
767
+ await this.attach(id);
768
+ }
709
769
  }
710
770
  }
711
771
  async detach(ids) {
@@ -777,6 +837,17 @@ class MorphManyRelationQuery {
777
837
  const rows = await this.get();
778
838
  return rows[0] ?? null;
779
839
  }
840
+ async count() {
841
+ const repository = this.related.repository().withConnection(this.parent.getRepository().getConnection());
842
+ return repository.query(asWhere({
843
+ [this.relation.morphTypeKey]: this.relation.morphType,
844
+ [this.relation.morphIdKey]: this.parent.get(this.relation.localKey),
845
+ ...this.extraWhere
846
+ })).count();
847
+ }
848
+ then(onfulfilled, onrejected) {
849
+ return thenGet(() => this.get(), onfulfilled, onrejected);
850
+ }
780
851
  async create(attributes = {}) {
781
852
  return this.related.create(attributes, {
782
853
  [this.relation.morphTypeKey]: this.relation.morphType,
@@ -817,6 +888,15 @@ class MorphOneRelationQuery {
817
888
  async get() {
818
889
  return this.inner.first();
819
890
  }
891
+ async first() {
892
+ return this.get();
893
+ }
894
+ async count() {
895
+ return this.inner.count();
896
+ }
897
+ then(onfulfilled, onrejected) {
898
+ return thenGet(() => this.get(), onfulfilled, onrejected);
899
+ }
820
900
  async create(attributes = {}) {
821
901
  return this.inner.create(attributes);
822
902
  }
@@ -827,11 +907,16 @@ class MorphToRelationQuery {
827
907
  relatedByType;
828
908
  relation;
829
909
  kind = "morphTo";
910
+ extraWhere = {};
830
911
  constructor(parent, relatedByType, relation) {
831
912
  this.parent = parent;
832
913
  this.relatedByType = relatedByType;
833
914
  this.relation = relation;
834
915
  }
916
+ where(where) {
917
+ this.extraWhere = { ...this.extraWhere, ...where };
918
+ return this;
919
+ }
835
920
  applyEagerLoad(query, alias) {
836
921
  const repositories = new Map(Object.entries(this.relatedByType).map(([type, model]) => [
837
922
  type,
@@ -849,9 +934,11 @@ class MorphToRelationQuery {
849
934
  return { sql: "SELECT 1 WHERE 1 = 0", params: [] };
850
935
  }
851
936
  const relatedTable = related.repository().getTable();
937
+ const extra = buildAdvancedWhereClause(relatedTable.name, this.extraWhere, [], []);
938
+ const extraSql = extra.clause.replace(/^ WHERE /, "");
852
939
  return {
853
- sql: `SELECT 1 FROM ${quoteIdentifier(relatedTable.name)} WHERE ${qualifyColumn(relatedTable.name, relatedTable.primaryKey)} = ${qualifyColumn(parentTable, this.relation.morphIdKey)}`,
854
- params: []
940
+ sql: `SELECT 1 FROM ${quoteIdentifier(relatedTable.name)} WHERE ${qualifyColumn(relatedTable.name, relatedTable.primaryKey)} = ${qualifyColumn(parentTable, this.relation.morphIdKey)}${extraSql ? ` AND ${extraSql}` : ""}`,
941
+ params: extra.params
855
942
  };
856
943
  }
857
944
  async get() {
@@ -862,9 +949,12 @@ class MorphToRelationQuery {
862
949
  return null;
863
950
  }
864
951
  const table = related.repository().getTable();
865
- const row = await related.repository().withConnection(this.parent.getRepository().getConnection()).query(asWhere({ [table.primaryKey]: id })).first();
952
+ const row = await related.repository().withConnection(this.parent.getRepository().getConnection()).query(asWhere({ [table.primaryKey]: id, ...this.extraWhere })).first();
866
953
  return row ? related.newFromRecord(row) : null;
867
954
  }
955
+ then(onfulfilled, onrejected) {
956
+ return thenGet(() => this.get(), onfulfilled, onrejected);
957
+ }
868
958
  }
869
959
 
870
960
  // ../../src/core/database/relationships.ts
@@ -892,26 +982,67 @@ function belongsToMany(definition) {
892
982
  ...definition
893
983
  };
894
984
  }
985
+ function relationMatchKey(value) {
986
+ if (value === null || value === undefined) {
987
+ return "";
988
+ }
989
+ if (typeof value === "bigint") {
990
+ return value.toString();
991
+ }
992
+ if (typeof value === "number" && Number.isFinite(value)) {
993
+ return String(value);
994
+ }
995
+ if (typeof value === "string" && /^-?\d+$/.test(value)) {
996
+ return BigInt(value).toString();
997
+ }
998
+ return String(value);
999
+ }
1000
+ function getByRelationKey(map, key) {
1001
+ if (map.has(key)) {
1002
+ return map.get(key);
1003
+ }
1004
+ const want = relationMatchKey(key);
1005
+ if (want === "") {
1006
+ return;
1007
+ }
1008
+ for (const [existing, value] of map) {
1009
+ if (relationMatchKey(existing) === want) {
1010
+ return value;
1011
+ }
1012
+ }
1013
+ return;
1014
+ }
895
1015
  function indexHasManyRelation(parents, children, relation) {
896
1016
  const groups = new Map;
1017
+ const originalKeys = new Map;
897
1018
  for (const parent of parents) {
898
- groups.set(parent[relation.localKey], []);
1019
+ const key = relationMatchKey(parent[relation.localKey]);
1020
+ if (!groups.has(key)) {
1021
+ groups.set(key, []);
1022
+ originalKeys.set(key, parent[relation.localKey]);
1023
+ }
899
1024
  }
900
1025
  for (const child of children) {
901
- const key = child[relation.foreignKey];
902
- const group = groups.get(key);
1026
+ const group = groups.get(relationMatchKey(child[relation.foreignKey]));
903
1027
  if (!group) {
904
1028
  continue;
905
1029
  }
906
1030
  group.push(child);
907
1031
  }
908
- return groups;
1032
+ const result = new Map;
1033
+ for (const [key, group] of groups) {
1034
+ const original = originalKeys.get(key);
1035
+ if (original !== undefined) {
1036
+ result.set(original, group);
1037
+ }
1038
+ }
1039
+ return result;
909
1040
  }
910
1041
  function indexHasOneRelation(parents, children, relation) {
911
1042
  const grouped = indexHasManyRelation(parents, children, relation);
912
1043
  const result = new Map;
913
1044
  for (const parent of parents) {
914
- const matches = grouped.get(parent[relation.localKey]) ?? [];
1045
+ const matches = getByRelationKey(grouped, parent[relation.localKey]) ?? [];
915
1046
  result.set(parent[relation.localKey], matches[0]);
916
1047
  }
917
1048
  return result;
@@ -919,12 +1050,12 @@ function indexHasOneRelation(parents, children, relation) {
919
1050
  function indexBelongsToRelation(children, parents, relation) {
920
1051
  const parentsById = new Map;
921
1052
  for (const parent of parents) {
922
- parentsById.set(parent[relation.ownerKey], parent);
1053
+ parentsById.set(relationMatchKey(parent[relation.ownerKey]), parent);
923
1054
  }
924
1055
  const result = new Map;
925
1056
  for (const child of children) {
926
1057
  const foreignKey = child[relation.foreignKey];
927
- const parent = parentsById.get(foreignKey);
1058
+ const parent = parentsById.get(relationMatchKey(foreignKey));
928
1059
  if (parent) {
929
1060
  result.set(foreignKey, parent);
930
1061
  }
@@ -934,23 +1065,33 @@ function indexBelongsToRelation(children, parents, relation) {
934
1065
  function indexBelongsToManyRelation(parents, pivotRows, relatedRows, relation) {
935
1066
  const relatedById = new Map;
936
1067
  for (const related of relatedRows) {
937
- relatedById.set(related[relation.relatedKey], related);
1068
+ relatedById.set(relationMatchKey(related[relation.relatedKey]), related);
938
1069
  }
939
1070
  const groups = new Map;
1071
+ const originalKeys = new Map;
940
1072
  for (const parent of parents) {
941
- groups.set(parent[relation.parentKey], []);
1073
+ const key = relationMatchKey(parent[relation.parentKey]);
1074
+ if (!groups.has(key)) {
1075
+ groups.set(key, []);
1076
+ originalKeys.set(key, parent[relation.parentKey]);
1077
+ }
942
1078
  }
943
1079
  for (const pivot of pivotRows) {
944
- const parentId = pivot[relation.foreignPivotKey];
945
- const relatedId = pivot[relation.relatedPivotKey];
946
- const group = groups.get(parentId);
947
- const related = relatedById.get(relatedId);
1080
+ const group = groups.get(relationMatchKey(pivot[relation.foreignPivotKey]));
1081
+ const related = relatedById.get(relationMatchKey(pivot[relation.relatedPivotKey]));
948
1082
  if (!group || !related) {
949
1083
  continue;
950
1084
  }
951
1085
  group.push(related);
952
1086
  }
953
- return groups;
1087
+ const result = new Map;
1088
+ for (const [key, group] of groups) {
1089
+ const original = originalKeys.get(key);
1090
+ if (original !== undefined) {
1091
+ result.set(original, group);
1092
+ }
1093
+ }
1094
+ return result;
954
1095
  }
955
1096
  function morphMany(definition) {
956
1097
  return {
@@ -972,27 +1113,38 @@ function morphTo(definition) {
972
1113
  }
973
1114
  function indexMorphManyRelation(parents, children, relation) {
974
1115
  const groups = new Map;
1116
+ const originalKeys = new Map;
975
1117
  for (const parent of parents) {
976
- groups.set(parent[relation.localKey], []);
1118
+ const key = relationMatchKey(parent[relation.localKey]);
1119
+ if (!groups.has(key)) {
1120
+ groups.set(key, []);
1121
+ originalKeys.set(key, parent[relation.localKey]);
1122
+ }
977
1123
  }
978
1124
  for (const child of children) {
979
1125
  if (child[relation.morphTypeKey] !== relation.morphType) {
980
1126
  continue;
981
1127
  }
982
- const key = child[relation.morphIdKey];
983
- const group = groups.get(key);
1128
+ const group = groups.get(relationMatchKey(child[relation.morphIdKey]));
984
1129
  if (!group) {
985
1130
  continue;
986
1131
  }
987
1132
  group.push(child);
988
1133
  }
989
- return groups;
1134
+ const result = new Map;
1135
+ for (const [key, group] of groups) {
1136
+ const original = originalKeys.get(key);
1137
+ if (original !== undefined) {
1138
+ result.set(original, group);
1139
+ }
1140
+ }
1141
+ return result;
990
1142
  }
991
1143
  function indexMorphOneRelation(parents, children, relation) {
992
1144
  const grouped = indexMorphManyRelation(parents, children, relation);
993
1145
  const result = new Map;
994
1146
  for (const parent of parents) {
995
- const matches = grouped.get(parent[relation.localKey]) ?? [];
1147
+ const matches = getByRelationKey(grouped, parent[relation.localKey]) ?? [];
996
1148
  result.set(parent[relation.localKey], matches[0]);
997
1149
  }
998
1150
  return result;
@@ -1005,7 +1157,7 @@ function indexMorphToRelation(children, parentsByType, relation) {
1005
1157
  if (!parents) {
1006
1158
  continue;
1007
1159
  }
1008
- const parent = parents.get(child[relation.morphIdKey]);
1160
+ const parent = getByRelationKey(parents, child[relation.morphIdKey]);
1009
1161
  if (parent) {
1010
1162
  result.set(child[relation.morphIdKey], parent);
1011
1163
  }
@@ -1015,6 +1167,7 @@ function indexMorphToRelation(children, parentsByType, relation) {
1015
1167
 
1016
1168
  // ../../src/core/database/model.ts
1017
1169
  var modelRepositories = new WeakMap;
1170
+ var namedModels = new Map;
1018
1171
  var modelGlobalScopes = new WeakMap;
1019
1172
  var modelObservers = new WeakMap;
1020
1173
  var modelBooted = new WeakSet;
@@ -1032,38 +1185,59 @@ function accessorName(key) {
1032
1185
  const pascal = key.replace(/_([a-z])/g, (_match, letter) => letter.toUpperCase());
1033
1186
  return `get${pascal.charAt(0).toUpperCase()}${pascal.slice(1)}Attribute`;
1034
1187
  }
1035
- function constrainRelationExists(model, name, constrain, not) {
1036
- const statics = modelStatics(model);
1037
- ensureBooted(model);
1038
- const repository = resolveModelRepository(model);
1039
- const dummy = statics.newFromRecord({});
1040
- const method = dummy[name];
1041
- if (typeof method !== "function") {
1042
- throw new Error(`${model.name} has no relation method ${name}().`);
1043
- }
1044
- const relationQuery = method.call(dummy);
1045
- constrain?.(relationQuery);
1046
- const exists = relationQuery.toExistsClause(repository.getTable().name);
1047
- const query = Model.query.call(model);
1048
- return not ? query.whereNotExists(exists.sql, exists.params) : query.whereExists(exists.sql, exists.params);
1188
+ function isLoadableModel(value) {
1189
+ return Boolean(value && typeof value === "object" && typeof value.load === "function" && typeof value.loaded === "function" && typeof value.setLoaded === "function");
1049
1190
  }
1050
- async function loadNested(model, path) {
1051
- const [head, ...rest] = path.split(".");
1052
- if (!head) {
1191
+ async function eagerLoadOnModels(models, paths) {
1192
+ if (models.length === 0 || paths.length === 0) {
1053
1193
  return;
1054
1194
  }
1055
- await model.load(head);
1056
- if (rest.length === 0) {
1057
- return;
1195
+ const grouped = new Map;
1196
+ for (const path of paths) {
1197
+ const [head, ...rest] = path.split(".");
1198
+ if (!head) {
1199
+ continue;
1200
+ }
1201
+ const nested = rest.join(".");
1202
+ const existing = grouped.get(head) ?? [];
1203
+ if (nested) {
1204
+ existing.push(nested);
1205
+ }
1206
+ grouped.set(head, existing);
1058
1207
  }
1059
- const loaded = model.loaded(head);
1060
- const children = Array.isArray(loaded) ? loaded : loaded ? [loaded] : [];
1061
- for (const child of children) {
1062
- if (child && typeof child === "object" && typeof child.load === "function") {
1063
- await loadNested(child, rest.join("."));
1208
+ for (const [head, nested] of grouped) {
1209
+ const unloaded = models.filter((model) => model.loaded(head) === undefined);
1210
+ if (unloaded.length > 0) {
1211
+ const first = unloaded[0];
1212
+ if (!first) {
1213
+ continue;
1214
+ }
1215
+ const method = first[head];
1216
+ if (typeof method !== "function") {
1217
+ throw new Error(`${first.constructor.name} has no relation method ${head}().`);
1218
+ }
1219
+ const relationQuery = method.call(first);
1220
+ const query = first.getRepository().query();
1221
+ relationQuery.applyEagerLoad(query, head);
1222
+ const attached = await query.attachToRows(unloaded.map((model) => model.toObject()));
1223
+ for (const [index, model] of unloaded.entries()) {
1224
+ const row = attached[index] ?? model.toObject();
1225
+ model.setLoaded(head, relationQuery.hydrateEager(row, head));
1226
+ }
1227
+ }
1228
+ if (nested.length === 0) {
1229
+ continue;
1064
1230
  }
1231
+ const children = models.flatMap((model) => {
1232
+ const loaded = model.loaded(head);
1233
+ return Array.isArray(loaded) ? loaded : loaded ? [loaded] : [];
1234
+ });
1235
+ await eagerLoadOnModels(children.filter(isLoadableModel), nested);
1065
1236
  }
1066
1237
  }
1238
+ async function loadNested(model, path) {
1239
+ await eagerLoadOnModels([model], [path]);
1240
+ }
1067
1241
  function resolveModelRepository(model) {
1068
1242
  const repository = modelRepositories.get(model);
1069
1243
  if (!repository) {
@@ -1071,6 +1245,48 @@ function resolveModelRepository(model) {
1071
1245
  }
1072
1246
  return repository;
1073
1247
  }
1248
+ function registerModelClass(name, model) {
1249
+ namedModels.set(name, model);
1250
+ }
1251
+ function resolveRelated(related) {
1252
+ if (typeof related === "string") {
1253
+ const found = namedModels.get(related);
1254
+ if (!found) {
1255
+ throw new Error(`Model [${related}] is not registered. Call registerModelClass() first.`);
1256
+ }
1257
+ return found;
1258
+ }
1259
+ if (typeof related === "function" && typeof related.repository !== "function") {
1260
+ return related();
1261
+ }
1262
+ return related;
1263
+ }
1264
+ function inferRelationMethodName(callee) {
1265
+ const stack = new Error().stack ?? "";
1266
+ let seenCallee = false;
1267
+ for (const line of stack.split(`
1268
+ `)) {
1269
+ const match = /at (?:async )?(?:[^.\s]+\.)?(\w+)/.exec(line);
1270
+ const name = match?.[1];
1271
+ if (!name || name === "Error" || name === "inferRelationMethodName") {
1272
+ continue;
1273
+ }
1274
+ if (!seenCallee) {
1275
+ if (name === callee) {
1276
+ seenCallee = true;
1277
+ }
1278
+ continue;
1279
+ }
1280
+ if (name !== callee) {
1281
+ return name;
1282
+ }
1283
+ }
1284
+ return;
1285
+ }
1286
+ function morphClassOf(model) {
1287
+ const statics = modelStatics(model.constructor === Function ? model : model.constructor);
1288
+ return statics.$morphClass ?? (model.constructor === Function ? model.name : model.constructor.name);
1289
+ }
1074
1290
  function modelStatics(model) {
1075
1291
  return model;
1076
1292
  }
@@ -1100,6 +1316,11 @@ function hydrateValue(value, cast) {
1100
1316
  case "bool":
1101
1317
  case "boolean":
1102
1318
  return value === true || value === 1 || value === "1" || value === "true";
1319
+ case "integer":
1320
+ case "int":
1321
+ return value === "" ? null : Number(value);
1322
+ case "hashed":
1323
+ return value;
1103
1324
  default:
1104
1325
  return value;
1105
1326
  }
@@ -1117,6 +1338,11 @@ function dehydrateValue(value, cast) {
1117
1338
  case "bool":
1118
1339
  case "boolean":
1119
1340
  return Boolean(value);
1341
+ case "integer":
1342
+ case "int":
1343
+ return value === "" ? null : Number(value);
1344
+ case "hashed":
1345
+ return value;
1120
1346
  default:
1121
1347
  return value;
1122
1348
  }
@@ -1171,6 +1397,153 @@ function applyTimestampsOnUpdate(columns, values, enabled) {
1171
1397
  return result;
1172
1398
  }
1173
1399
 
1400
+ class ModelQuery {
1401
+ modelClass;
1402
+ query;
1403
+ eager = [];
1404
+ constructor(modelClass, query) {
1405
+ this.modelClass = modelClass;
1406
+ this.query = query;
1407
+ }
1408
+ with(...relations) {
1409
+ const statics = modelStatics(this.modelClass);
1410
+ ensureBooted(this.modelClass);
1411
+ const dummy = statics.newFromRecord({}, false);
1412
+ for (const path of relations) {
1413
+ const name = path.split(".")[0] ?? path;
1414
+ const method = dummy[name];
1415
+ if (typeof method !== "function") {
1416
+ throw new Error(`${this.modelClass.name} has no relation method ${name}().`);
1417
+ }
1418
+ const relationQuery = method.call(dummy);
1419
+ this.eager.push({ name, path, relationQuery });
1420
+ relationQuery.applyEagerLoad(this.query, name);
1421
+ }
1422
+ return this;
1423
+ }
1424
+ where(input) {
1425
+ this.query.where(input);
1426
+ return this;
1427
+ }
1428
+ orWhere(input) {
1429
+ this.query.orWhere(input);
1430
+ return this;
1431
+ }
1432
+ orderBy(orderBy) {
1433
+ this.query.orderBy(orderBy);
1434
+ return this;
1435
+ }
1436
+ limit(limit) {
1437
+ this.query.limit(limit);
1438
+ return this;
1439
+ }
1440
+ offset(offset) {
1441
+ this.query.offset(offset);
1442
+ return this;
1443
+ }
1444
+ whereNull(column) {
1445
+ this.query.whereNull(column);
1446
+ return this;
1447
+ }
1448
+ whereIn(column, values) {
1449
+ this.query.whereIn(column, values);
1450
+ return this;
1451
+ }
1452
+ whereExists(sql, params = []) {
1453
+ this.query.whereExists(sql, params);
1454
+ return this;
1455
+ }
1456
+ whereNotExists(sql, params = []) {
1457
+ this.query.whereNotExists(sql, params);
1458
+ return this;
1459
+ }
1460
+ whereHas(name, constrain) {
1461
+ return this.constrainExists(name, constrain, false);
1462
+ }
1463
+ has(name) {
1464
+ return this.constrainExists(name, undefined, false);
1465
+ }
1466
+ doesntHave(name) {
1467
+ return this.constrainExists(name, undefined, true);
1468
+ }
1469
+ whereDoesntHave(name, constrain) {
1470
+ return this.constrainExists(name, constrain, true);
1471
+ }
1472
+ withHasMany(...args) {
1473
+ this.query.withHasMany(...args);
1474
+ return this;
1475
+ }
1476
+ withBelongsTo(...args) {
1477
+ this.query.withBelongsTo(...args);
1478
+ return this;
1479
+ }
1480
+ withBelongsToMany(...args) {
1481
+ this.query.withBelongsToMany(...args);
1482
+ return this;
1483
+ }
1484
+ withMorphMany(...args) {
1485
+ this.query.withMorphMany(...args);
1486
+ return this;
1487
+ }
1488
+ withMorphOne(...args) {
1489
+ this.query.withMorphOne(...args);
1490
+ return this;
1491
+ }
1492
+ withMorphTo(...args) {
1493
+ this.query.withMorphTo(...args);
1494
+ return this;
1495
+ }
1496
+ async get() {
1497
+ const statics = modelStatics(this.modelClass);
1498
+ const rows = await this.query.get();
1499
+ const models = [];
1500
+ for (const row of rows) {
1501
+ const model = statics.newFromRecord(row, true);
1502
+ await runObservers(model, "retrieved");
1503
+ for (const { name, relationQuery } of this.eager) {
1504
+ model.setLoaded(name, relationQuery.hydrateEager(row, name));
1505
+ }
1506
+ models.push(model);
1507
+ }
1508
+ const nested = this.eager.filter((item) => item.path.includes(".")).map((item) => item.path);
1509
+ await eagerLoadOnModels(models.filter(isLoadableModel), nested);
1510
+ return models;
1511
+ }
1512
+ async first() {
1513
+ this.query.limit(1);
1514
+ const models = await this.get();
1515
+ return models[0] ?? null;
1516
+ }
1517
+ async find(id) {
1518
+ const primaryKey = resolveModelRepository(this.modelClass).getTable().primaryKey;
1519
+ return this.where({ [primaryKey]: id }).first();
1520
+ }
1521
+ async findOrFail(id, errorFactory) {
1522
+ const model = await this.find(id);
1523
+ if (model) {
1524
+ return model;
1525
+ }
1526
+ throw errorFactory?.(id) ?? new NotFoundError(`${this.modelClass.name} ${String(id)} not found.`);
1527
+ }
1528
+ then(onfulfilled, onrejected) {
1529
+ return this.get().then(onfulfilled ?? undefined, onrejected ?? undefined);
1530
+ }
1531
+ constrainExists(name, constrain, not) {
1532
+ const statics = modelStatics(this.modelClass);
1533
+ ensureBooted(this.modelClass);
1534
+ const repository = resolveModelRepository(this.modelClass);
1535
+ const dummy = statics.newFromRecord({});
1536
+ const method = dummy[name];
1537
+ if (typeof method !== "function") {
1538
+ throw new Error(`${this.modelClass.name} has no relation method ${name}().`);
1539
+ }
1540
+ const relationQuery = method.call(dummy);
1541
+ constrain?.(relationQuery);
1542
+ const exists = relationQuery.toExistsClause(repository.getTable().name);
1543
+ return not ? this.whereNotExists(exists.sql, exists.params) : this.whereExists(exists.sql, exists.params);
1544
+ }
1545
+ }
1546
+
1174
1547
  class Model {
1175
1548
  attributes;
1176
1549
  repository;
@@ -1181,6 +1554,7 @@ class Model {
1181
1554
  static $hidden;
1182
1555
  static $visible;
1183
1556
  static $appends;
1557
+ static $morphClass;
1184
1558
  _exists;
1185
1559
  loadedRelations = {};
1186
1560
  hiddenOverrides = [];
@@ -1190,6 +1564,7 @@ class Model {
1190
1564
  this.attributes = attributes;
1191
1565
  this.repository = repository;
1192
1566
  this._exists = exists;
1567
+ this.attributes = modelStatics(this.constructor).hydrateAttributes(attributes);
1193
1568
  }
1194
1569
  getRepository() {
1195
1570
  return this.repository;
@@ -1250,7 +1625,7 @@ class Model {
1250
1625
  return this;
1251
1626
  }
1252
1627
  primaryKey() {
1253
- throw new Error(`${this.constructor.name}.primaryKey() is not implemented.`);
1628
+ return this.repository.getTable().primaryKey;
1254
1629
  }
1255
1630
  static primaryKeyField() {
1256
1631
  return resolveModelRepository(this).getTable().primaryKey;
@@ -1291,7 +1666,7 @@ class Model {
1291
1666
  for (const scope of getGlobalScopes(this)) {
1292
1667
  query = scope(query);
1293
1668
  }
1294
- return query;
1669
+ return new ModelQuery(this, query);
1295
1670
  }
1296
1671
  static newFromRecord(record, exists = true) {
1297
1672
  const repository = resolveModelRepository(this);
@@ -1310,79 +1685,36 @@ class Model {
1310
1685
  const withTimestamps = applyTimestampsOnCreate(table.columns, assignable, timestamps);
1311
1686
  const payload = statics.dehydrateAttributes(withTimestamps);
1312
1687
  const pending = statics.newFromRecord({ ...payload }, false);
1688
+ if (await runObservers(pending, "saving") === false) {
1689
+ throw new Error(`${this.name}.create() was cancelled by an observer.`);
1690
+ }
1313
1691
  if (await runObservers(pending, "creating") === false) {
1314
1692
  throw new Error(`${this.name}.create() was cancelled by an observer.`);
1315
1693
  }
1316
1694
  const record = await repository.create(payload);
1317
1695
  const created = statics.fromRecord(record, repository, true);
1318
1696
  await runObservers(created, "created");
1697
+ await runObservers(created, "saved");
1319
1698
  return created;
1320
1699
  }
1321
1700
  static with(...relations) {
1322
- const statics = modelStatics(this);
1323
- ensureBooted(this);
1324
- const repository = resolveModelRepository(this);
1325
- const dummy = statics.fromRecord({}, repository, false);
1326
- const resolved = relations.map((path) => {
1327
- const name = path.split(".")[0] ?? path;
1328
- const method = dummy[name];
1329
- if (typeof method !== "function") {
1330
- throw new Error(`${this.name} has no relation method ${name}().`);
1331
- }
1332
- const relationQuery = method.call(dummy);
1333
- return { name, path, relationQuery };
1334
- });
1335
- const query = Model.query.call(this);
1336
- for (const { name, relationQuery } of resolved) {
1337
- if (relationQuery.kind !== "belongsToMany") {
1338
- relationQuery.applyEagerLoad(query, name);
1339
- }
1340
- }
1341
- return {
1342
- async get() {
1343
- const rows = await query.get();
1344
- const models = [];
1345
- for (const row of rows) {
1346
- const model = statics.fromRecord(row, repository, true);
1347
- for (const { name, path, relationQuery } of resolved) {
1348
- if (relationQuery.kind === "belongsToMany") {
1349
- await model.load(path.includes(".") ? path : name);
1350
- continue;
1351
- }
1352
- model.setLoaded(name, relationQuery.hydrateEager(row, name));
1353
- const nested = path.split(".").slice(1).join(".");
1354
- if (nested) {
1355
- await loadNested(model, path);
1356
- }
1357
- }
1358
- models.push(model);
1359
- }
1360
- return models;
1361
- },
1362
- async first() {
1363
- const [model] = await this.get();
1364
- return model ?? null;
1365
- }
1366
- };
1701
+ return Model.query.call(this).with(...relations);
1367
1702
  }
1368
1703
  static whereHas(name, constrain) {
1369
- return constrainRelationExists(this, name, constrain, false);
1704
+ return Model.query.call(this).whereHas(name, constrain);
1370
1705
  }
1371
1706
  static has(name) {
1372
- return constrainRelationExists(this, name, undefined, false);
1707
+ return Model.query.call(this).has(name);
1373
1708
  }
1374
1709
  static doesntHave(name) {
1375
- return constrainRelationExists(this, name, undefined, true);
1710
+ return Model.query.call(this).doesntHave(name);
1376
1711
  }
1377
1712
  static whereDoesntHave(name, constrain) {
1378
- return constrainRelationExists(this, name, constrain, true);
1713
+ return Model.query.call(this).whereDoesntHave(name, constrain);
1379
1714
  }
1380
1715
  static async find(id) {
1381
- const statics = modelStatics(this);
1382
- const repository = resolveModelRepository(this);
1383
- const primaryKey = repository.getTable().primaryKey;
1384
- const record = await Model.query.call(this).where({ [primaryKey]: id }).first();
1385
- return record ? statics.fromRecord(record, repository, true) : null;
1716
+ const primaryKey = resolveModelRepository(this).getTable().primaryKey;
1717
+ return Model.query.call(this).where({ [primaryKey]: id }).first();
1386
1718
  }
1387
1719
  static async findOrFail(id, errorFactory) {
1388
1720
  const model = await Model.find.call(this, id);
@@ -1392,8 +1724,6 @@ class Model {
1392
1724
  throw errorFactory?.(id) ?? new NotFoundError(`${this.name} ${String(id)} not found.`);
1393
1725
  }
1394
1726
  static async all(options = {}) {
1395
- const statics = modelStatics(this);
1396
- const repository = resolveModelRepository(this);
1397
1727
  let query = Model.query.call(this);
1398
1728
  if (options.orderBy) {
1399
1729
  query = query.orderBy(options.orderBy);
@@ -1401,21 +1731,17 @@ class Model {
1401
1731
  if (options.limit !== undefined) {
1402
1732
  query = query.limit(options.limit);
1403
1733
  }
1404
- const rows = await query.get();
1405
- return rows.map((row) => statics.fromRecord(row, repository, true));
1734
+ return query.get();
1406
1735
  }
1407
1736
  static where(where) {
1408
1737
  return Model.query.call(this).where(where);
1409
1738
  }
1410
1739
  static async firstWhere(where, options = {}) {
1411
- const statics = modelStatics(this);
1412
- const repository = resolveModelRepository(this);
1413
1740
  let query = Model.query.call(this).where(where);
1414
1741
  if (options.orderBy) {
1415
1742
  query = query.orderBy(options.orderBy);
1416
1743
  }
1417
- const record = await query.first();
1418
- return record ? statics.fromRecord(record, repository, true) : null;
1744
+ return query.first();
1419
1745
  }
1420
1746
  static async firstOrNew(where, values = {}) {
1421
1747
  const existing = await Model.firstWhere.call(this, where);
@@ -1444,6 +1770,9 @@ class Model {
1444
1770
  const casts = ModelClass.$casts ?? {};
1445
1771
  const table = this.repository.getTable();
1446
1772
  const updating = this.$exists;
1773
+ if (await runObservers(this, "saving") === false) {
1774
+ return this;
1775
+ }
1447
1776
  if (await runObservers(this, updating ? "updating" : "creating") === false) {
1448
1777
  return this;
1449
1778
  }
@@ -1452,6 +1781,7 @@ class Model {
1452
1781
  const record2 = await this.repository.updateByIdOrThrow(this.id, changes);
1453
1782
  this.attributes = ModelClass.hydrateAttributes(record2);
1454
1783
  await runObservers(this, "updated");
1784
+ await runObservers(this, "saved");
1455
1785
  return this;
1456
1786
  }
1457
1787
  const assignable = filterMassAssignable(ModelClass.$fillable, ModelClass.$guarded, this.attributes);
@@ -1461,6 +1791,7 @@ class Model {
1461
1791
  this.attributes = ModelClass.hydrateAttributes(record);
1462
1792
  this._exists = true;
1463
1793
  await runObservers(this, "created");
1794
+ await runObservers(this, "saved");
1464
1795
  return this;
1465
1796
  }
1466
1797
  async update(changes) {
@@ -1493,7 +1824,7 @@ class Model {
1493
1824
  }
1494
1825
  async loadHasMany(as, relation, childRepository, options = {}) {
1495
1826
  const grouped = await childRepository.withConnection(this.repository.getConnection()).loadHasManyForParents([this.attributes], relation, options);
1496
- const loaded = grouped.get(this.attributes[relation.localKey]) ?? [];
1827
+ const loaded = getByRelationKey(grouped, this.attributes[relation.localKey]) ?? [];
1497
1828
  return Object.assign(this, { [as]: loaded });
1498
1829
  }
1499
1830
  async loadHasOne(as, relation, childRepository, options = {}) {
@@ -1503,7 +1834,7 @@ class Model {
1503
1834
  }
1504
1835
  async loadBelongsTo(as, relation, parentRepository, options = {}) {
1505
1836
  const grouped = await this.repository.loadBelongsToForParents([this.attributes], relation, parentRepository, options);
1506
- const loaded = grouped.get(this.attributes[relation.foreignKey]);
1837
+ const loaded = getByRelationKey(grouped, this.attributes[relation.foreignKey]);
1507
1838
  return Object.assign(this, { [as]: loaded });
1508
1839
  }
1509
1840
  async loadBelongsToMany(as, relation, relatedRepository, options = {}) {
@@ -1523,28 +1854,31 @@ class Model {
1523
1854
  }
1524
1855
  });
1525
1856
  const grouped = indexBelongsToManyRelation([this.attributes], pivotRows, relatedRows, relation);
1526
- const loaded = grouped.get(parentId) ?? [];
1857
+ const loaded = getByRelationKey(grouped, parentId) ?? [];
1527
1858
  return Object.assign(this, { [as]: loaded });
1528
1859
  }
1529
1860
  hasMany(related, foreignKey, localKey) {
1530
1861
  const table = this.repository.getTable();
1531
- return new HasManyRelationQuery(this, related, hasMany({
1532
- name: related.repository().getTable().name,
1862
+ const relatedClass = resolveRelated(related);
1863
+ return new HasManyRelationQuery(this, relatedClass, hasMany({
1864
+ name: relatedClass.repository().getTable().name,
1533
1865
  localKey: localKey ?? table.primaryKey,
1534
1866
  foreignKey: foreignKey ?? foreignKeyFromTable(table.name)
1535
1867
  }));
1536
1868
  }
1537
1869
  hasOne(related, foreignKey, localKey) {
1538
1870
  const table = this.repository.getTable();
1539
- return new HasOneRelationQuery(this, related, hasOne({
1540
- name: related.repository().getTable().name,
1871
+ const relatedClass = resolveRelated(related);
1872
+ return new HasOneRelationQuery(this, relatedClass, hasOne({
1873
+ name: relatedClass.repository().getTable().name,
1541
1874
  localKey: localKey ?? table.primaryKey,
1542
1875
  foreignKey: foreignKey ?? foreignKeyFromTable(table.name)
1543
1876
  }));
1544
1877
  }
1545
1878
  belongsTo(related, foreignKey, ownerKey) {
1546
- const relatedTable = related.repository().getTable();
1547
- return new BelongsToRelationQuery(this, related, belongsTo({
1879
+ const relatedClass = resolveRelated(related);
1880
+ const relatedTable = relatedClass.repository().getTable();
1881
+ return new BelongsToRelationQuery(this, relatedClass, belongsTo({
1548
1882
  name: relatedTable.name,
1549
1883
  foreignKey: foreignKey ?? foreignKeyFromTable(relatedTable.name),
1550
1884
  ownerKey: ownerKey ?? relatedTable.primaryKey
@@ -1552,8 +1886,9 @@ class Model {
1552
1886
  }
1553
1887
  belongsToMany(related, pivotTable, foreignPivotKey, relatedPivotKey) {
1554
1888
  const table = this.repository.getTable();
1555
- const relatedTable = related.repository().getTable();
1556
- return new BelongsToManyRelationQuery(this, related, belongsToMany({
1889
+ const relatedClass = resolveRelated(related);
1890
+ const relatedTable = relatedClass.repository().getTable();
1891
+ return new BelongsToManyRelationQuery(this, relatedClass, belongsToMany({
1557
1892
  name: relatedTable.name,
1558
1893
  pivotTable: pivotTable ?? pivotTableName(table.name, relatedTable.name),
1559
1894
  parentKey: table.primaryKey,
@@ -1562,31 +1897,38 @@ class Model {
1562
1897
  relatedPivotKey: relatedPivotKey ?? foreignKeyFromTable(relatedTable.name)
1563
1898
  }));
1564
1899
  }
1565
- morphMany(related, morphName, typeKey, idKey) {
1900
+ morphMany(related, morphName, typeKey, idKey, morphType) {
1566
1901
  const table = this.repository.getTable();
1567
- return new MorphManyRelationQuery(this, related, morphMany({
1902
+ const relatedClass = resolveRelated(related);
1903
+ return new MorphManyRelationQuery(this, relatedClass, morphMany({
1568
1904
  name: morphName,
1569
1905
  localKey: table.primaryKey,
1570
1906
  morphTypeKey: typeKey ?? `${morphName}_type`,
1571
1907
  morphIdKey: idKey ?? `${morphName}_id`,
1572
- morphType: table.name
1908
+ morphType: morphType ?? morphClassOf(this)
1573
1909
  }));
1574
1910
  }
1575
- morphOne(related, morphName, typeKey, idKey) {
1911
+ morphOne(related, morphName, typeKey, idKey, morphType) {
1576
1912
  const table = this.repository.getTable();
1577
- return new MorphOneRelationQuery(this, related, morphOne({
1913
+ const relatedClass = resolveRelated(related);
1914
+ return new MorphOneRelationQuery(this, relatedClass, morphOne({
1578
1915
  name: morphName,
1579
1916
  localKey: table.primaryKey,
1580
1917
  morphTypeKey: typeKey ?? `${morphName}_type`,
1581
1918
  morphIdKey: idKey ?? `${morphName}_id`,
1582
- morphType: table.name
1919
+ morphType: morphType ?? morphClassOf(this)
1583
1920
  }));
1584
1921
  }
1585
- morphTo(relatedByType, morphName = "imageable", typeKey, idKey) {
1586
- return new MorphToRelationQuery(this, relatedByType, morphTo({
1587
- name: morphName,
1588
- morphTypeKey: typeKey ?? `${morphName}_type`,
1589
- morphIdKey: idKey ?? `${morphName}_id`
1922
+ morphTo(relatedByType, morphName, typeKey, idKey) {
1923
+ const resolvedName = morphName ?? inferRelationMethodName("morphTo");
1924
+ if (!resolvedName) {
1925
+ throw new Error(`${this.constructor.name}.morphTo() needs an explicit morph name (Laravel infers it from the relation method).`);
1926
+ }
1927
+ const resolvedMap = Object.fromEntries(Object.entries(relatedByType).map(([type, related]) => [type, resolveRelated(related)]));
1928
+ return new MorphToRelationQuery(this, resolvedMap, morphTo({
1929
+ name: resolvedName,
1930
+ morphTypeKey: typeKey ?? `${resolvedName}_type`,
1931
+ morphIdKey: idKey ?? `${resolvedName}_id`
1590
1932
  }));
1591
1933
  }
1592
1934
  async load(...names) {
@@ -1618,6 +1960,14 @@ class Model {
1618
1960
  }
1619
1961
  function registerModelRepository(model, repository) {
1620
1962
  modelRepositories.set(model, repository);
1963
+ const name = model.name;
1964
+ if (name) {
1965
+ namedModels.set(name, model);
1966
+ }
1967
+ const morphClass = model.$morphClass;
1968
+ if (morphClass) {
1969
+ namedModels.set(morphClass, model);
1970
+ }
1621
1971
  ensureBooted(model);
1622
1972
  return model;
1623
1973
  }
@@ -1627,6 +1977,7 @@ export {
1627
1977
  HasManyRelationQuery,
1628
1978
  HasOneRelationQuery,
1629
1979
  Model,
1980
+ ModelQuery,
1630
1981
  MorphManyRelationQuery,
1631
1982
  MorphOneRelationQuery,
1632
1983
  MorphToRelationQuery,
@@ -1634,5 +1985,6 @@ export {
1634
1985
  dehydrateValue,
1635
1986
  filterMassAssignable,
1636
1987
  hydrateValue,
1988
+ registerModelClass,
1637
1989
  registerModelRepository
1638
1990
  };