arkormx 2.0.0-next.22 → 2.0.0-next.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -6010,7 +6010,7 @@ var RelationTableLoader = class {
6010
6010
  * @author Legacy (3m1n3nc3)
6011
6011
  * @since 2.0.0-next.2
6012
6012
  */
6013
- var SetBasedEagerLoader = class {
6013
+ var SetBasedEagerLoader = class SetBasedEagerLoader {
6014
6014
  constructor(models, relations) {
6015
6015
  this.models = models;
6016
6016
  this.relations = relations;
@@ -6022,10 +6022,18 @@ var SetBasedEagerLoader = class {
6022
6022
  */
6023
6023
  async load() {
6024
6024
  if (this.models.length === 0) return;
6025
- await Promise.all(Object.entries(this.relations).map(async ([name, constraint]) => {
6026
- await this.loadRelation(name, constraint);
6025
+ const relationTree = this.buildRelationTree(this.relations);
6026
+ await Promise.all(Array.from(relationTree.entries()).map(async ([name, node]) => {
6027
+ await this.loadRelationNode(name, node);
6027
6028
  }));
6028
6029
  }
6030
+ async loadRelationNode(name, node) {
6031
+ await this.loadRelation(name, node.constraint);
6032
+ if (node.children.size === 0) return;
6033
+ const relatedModels = this.collectLoadedRelationModels(name);
6034
+ if (relatedModels.length === 0) return;
6035
+ await new SetBasedEagerLoader(relatedModels, this.relationTreeToMap(node.children)).load();
6036
+ }
6029
6037
  /**
6030
6038
  * Loads a specific relationship for the set of models based on the relationship name
6031
6039
  * and an optional constraint.
@@ -6069,9 +6077,58 @@ var SetBasedEagerLoader = class {
6069
6077
  */
6070
6078
  resolveRelationResolver(name) {
6071
6079
  const resolver = this.models[0][name];
6072
- if (typeof resolver !== "function") return null;
6080
+ if (typeof resolver !== "function") {
6081
+ const modelName = this.models[0].constructor?.name ?? "Model";
6082
+ throw new RelationResolutionException(`Relation [${name}] is not defined on the model.`, {
6083
+ operation: "eagerLoad",
6084
+ model: modelName,
6085
+ relation: name
6086
+ });
6087
+ }
6073
6088
  return resolver;
6074
6089
  }
6090
+ buildRelationTree(relations) {
6091
+ const tree = /* @__PURE__ */ new Map();
6092
+ Object.entries(relations).forEach(([path, constraint]) => {
6093
+ const segments = path.split(".").map((segment) => segment.trim()).filter((segment) => segment.length > 0);
6094
+ if (segments.length === 0) return;
6095
+ let current = tree;
6096
+ segments.forEach((segment, index) => {
6097
+ const existing = current.get(segment) ?? {
6098
+ constraint: void 0,
6099
+ children: /* @__PURE__ */ new Map()
6100
+ };
6101
+ if (index === segments.length - 1 && constraint) existing.constraint = constraint;
6102
+ current.set(segment, existing);
6103
+ current = existing.children;
6104
+ });
6105
+ });
6106
+ return tree;
6107
+ }
6108
+ relationTreeToMap(tree, prefix = "") {
6109
+ return Array.from(tree.entries()).reduce((all, [name, node]) => {
6110
+ const path = prefix ? `${prefix}.${name}` : name;
6111
+ all[path] = node.constraint;
6112
+ Object.assign(all, this.relationTreeToMap(node.children, path));
6113
+ return all;
6114
+ }, {});
6115
+ }
6116
+ collectLoadedRelationModels(name) {
6117
+ return this.models.reduce((all, model) => {
6118
+ const loaded = model.getAttribute(name);
6119
+ if (loaded instanceof ArkormCollection) {
6120
+ loaded.all().forEach((item) => {
6121
+ if (this.isEagerLoadableModel(item)) all.push(item);
6122
+ });
6123
+ return all;
6124
+ }
6125
+ if (this.isEagerLoadableModel(loaded)) all.push(loaded);
6126
+ return all;
6127
+ }, []);
6128
+ }
6129
+ isEagerLoadableModel(value) {
6130
+ return typeof value === "object" && value !== null && typeof value.getAttribute === "function" && typeof value.setLoadedRelation === "function";
6131
+ }
6075
6132
  /**
6076
6133
  * Loads a "belongs to" relationship for the set of models.
6077
6134
  *
package/dist/index.mjs CHANGED
@@ -5981,7 +5981,7 @@ var RelationTableLoader = class {
5981
5981
  * @author Legacy (3m1n3nc3)
5982
5982
  * @since 2.0.0-next.2
5983
5983
  */
5984
- var SetBasedEagerLoader = class {
5984
+ var SetBasedEagerLoader = class SetBasedEagerLoader {
5985
5985
  constructor(models, relations) {
5986
5986
  this.models = models;
5987
5987
  this.relations = relations;
@@ -5993,10 +5993,18 @@ var SetBasedEagerLoader = class {
5993
5993
  */
5994
5994
  async load() {
5995
5995
  if (this.models.length === 0) return;
5996
- await Promise.all(Object.entries(this.relations).map(async ([name, constraint]) => {
5997
- await this.loadRelation(name, constraint);
5996
+ const relationTree = this.buildRelationTree(this.relations);
5997
+ await Promise.all(Array.from(relationTree.entries()).map(async ([name, node]) => {
5998
+ await this.loadRelationNode(name, node);
5998
5999
  }));
5999
6000
  }
6001
+ async loadRelationNode(name, node) {
6002
+ await this.loadRelation(name, node.constraint);
6003
+ if (node.children.size === 0) return;
6004
+ const relatedModels = this.collectLoadedRelationModels(name);
6005
+ if (relatedModels.length === 0) return;
6006
+ await new SetBasedEagerLoader(relatedModels, this.relationTreeToMap(node.children)).load();
6007
+ }
6000
6008
  /**
6001
6009
  * Loads a specific relationship for the set of models based on the relationship name
6002
6010
  * and an optional constraint.
@@ -6040,9 +6048,58 @@ var SetBasedEagerLoader = class {
6040
6048
  */
6041
6049
  resolveRelationResolver(name) {
6042
6050
  const resolver = this.models[0][name];
6043
- if (typeof resolver !== "function") return null;
6051
+ if (typeof resolver !== "function") {
6052
+ const modelName = this.models[0].constructor?.name ?? "Model";
6053
+ throw new RelationResolutionException(`Relation [${name}] is not defined on the model.`, {
6054
+ operation: "eagerLoad",
6055
+ model: modelName,
6056
+ relation: name
6057
+ });
6058
+ }
6044
6059
  return resolver;
6045
6060
  }
6061
+ buildRelationTree(relations) {
6062
+ const tree = /* @__PURE__ */ new Map();
6063
+ Object.entries(relations).forEach(([path, constraint]) => {
6064
+ const segments = path.split(".").map((segment) => segment.trim()).filter((segment) => segment.length > 0);
6065
+ if (segments.length === 0) return;
6066
+ let current = tree;
6067
+ segments.forEach((segment, index) => {
6068
+ const existing = current.get(segment) ?? {
6069
+ constraint: void 0,
6070
+ children: /* @__PURE__ */ new Map()
6071
+ };
6072
+ if (index === segments.length - 1 && constraint) existing.constraint = constraint;
6073
+ current.set(segment, existing);
6074
+ current = existing.children;
6075
+ });
6076
+ });
6077
+ return tree;
6078
+ }
6079
+ relationTreeToMap(tree, prefix = "") {
6080
+ return Array.from(tree.entries()).reduce((all, [name, node]) => {
6081
+ const path = prefix ? `${prefix}.${name}` : name;
6082
+ all[path] = node.constraint;
6083
+ Object.assign(all, this.relationTreeToMap(node.children, path));
6084
+ return all;
6085
+ }, {});
6086
+ }
6087
+ collectLoadedRelationModels(name) {
6088
+ return this.models.reduce((all, model) => {
6089
+ const loaded = model.getAttribute(name);
6090
+ if (loaded instanceof ArkormCollection) {
6091
+ loaded.all().forEach((item) => {
6092
+ if (this.isEagerLoadableModel(item)) all.push(item);
6093
+ });
6094
+ return all;
6095
+ }
6096
+ if (this.isEagerLoadableModel(loaded)) all.push(loaded);
6097
+ return all;
6098
+ }, []);
6099
+ }
6100
+ isEagerLoadableModel(value) {
6101
+ return typeof value === "object" && value !== null && typeof value.getAttribute === "function" && typeof value.setLoadedRelation === "function";
6102
+ }
6046
6103
  /**
6047
6104
  * Loads a "belongs to" relationship for the set of models.
6048
6105
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arkormx",
3
- "version": "2.0.0-next.22",
3
+ "version": "2.0.0-next.23",
4
4
  "description": "Modern TypeScript-first ORM for Node.js.",
5
5
  "keywords": [
6
6
  "orm",