@medusajs/orchestration 3.0.0-preview-20250410180148 → 3.0.0-preview-20251201152819

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.
Files changed (40) hide show
  1. package/dist/joiner/remote-joiner.d.ts +3 -2
  2. package/dist/joiner/remote-joiner.d.ts.map +1 -1
  3. package/dist/joiner/remote-joiner.js +312 -141
  4. package/dist/joiner/remote-joiner.js.map +1 -1
  5. package/dist/transaction/datastore/abstract-storage.d.ts +7 -5
  6. package/dist/transaction/datastore/abstract-storage.d.ts.map +1 -1
  7. package/dist/transaction/datastore/abstract-storage.js +3 -3
  8. package/dist/transaction/datastore/abstract-storage.js.map +1 -1
  9. package/dist/transaction/datastore/base-in-memory-storage.d.ts +2 -1
  10. package/dist/transaction/datastore/base-in-memory-storage.d.ts.map +1 -1
  11. package/dist/transaction/datastore/base-in-memory-storage.js +2 -0
  12. package/dist/transaction/datastore/base-in-memory-storage.js.map +1 -1
  13. package/dist/transaction/distributed-transaction.d.ts +23 -6
  14. package/dist/transaction/distributed-transaction.d.ts.map +1 -1
  15. package/dist/transaction/distributed-transaction.js +284 -54
  16. package/dist/transaction/distributed-transaction.js.map +1 -1
  17. package/dist/transaction/errors.d.ts +11 -0
  18. package/dist/transaction/errors.d.ts.map +1 -1
  19. package/dist/transaction/errors.js +34 -2
  20. package/dist/transaction/errors.js.map +1 -1
  21. package/dist/transaction/transaction-orchestrator.d.ts +99 -10
  22. package/dist/transaction/transaction-orchestrator.d.ts.map +1 -1
  23. package/dist/transaction/transaction-orchestrator.js +599 -274
  24. package/dist/transaction/transaction-orchestrator.js.map +1 -1
  25. package/dist/transaction/transaction-step.d.ts +2 -0
  26. package/dist/transaction/transaction-step.d.ts.map +1 -1
  27. package/dist/transaction/transaction-step.js +10 -3
  28. package/dist/transaction/transaction-step.js.map +1 -1
  29. package/dist/transaction/types.d.ts +31 -1
  30. package/dist/transaction/types.d.ts.map +1 -1
  31. package/dist/transaction/types.js.map +1 -1
  32. package/dist/tsconfig.tsbuildinfo +1 -1
  33. package/dist/workflow/global-workflow.d.ts.map +1 -1
  34. package/dist/workflow/global-workflow.js +16 -4
  35. package/dist/workflow/global-workflow.js.map +1 -1
  36. package/dist/workflow/local-workflow.d.ts +3 -1
  37. package/dist/workflow/local-workflow.d.ts.map +1 -1
  38. package/dist/workflow/local-workflow.js +71 -9
  39. package/dist/workflow/local-workflow.js.map +1 -1
  40. package/package.json +8 -26
@@ -13,6 +13,8 @@ export declare class RemoteJoiner {
13
13
  private static filterFields;
14
14
  private static getNestedItems;
15
15
  private static createRelatedDataMap;
16
+ private computeIdsForRelationship;
17
+ private assignRelatedToItems;
16
18
  static parseQuery(graphqlQuery: string, variables?: Record<string, unknown>): RemoteJoinerQuery;
17
19
  constructor(serviceConfigs: ModuleJoinerConfig[], remoteFetchData: RemoteFetchDataCallback, options?: {
18
20
  autoCreateServiceNameAlias?: boolean;
@@ -28,9 +30,8 @@ export declare class RemoteJoiner {
28
30
  private handleFieldAliases;
29
31
  private handleExpands;
30
32
  private getEntityRelationship;
31
- private expandProperty;
32
- private expandRelationshipProperty;
33
33
  private parseExpands;
34
+ private buildQueryPlan;
34
35
  private parseProperties;
35
36
  private getEntity;
36
37
  private parseAlias;
@@ -1 +1 @@
1
- {"version":3,"file":"remote-joiner.d.ts","sourceRoot":"","sources":["../../src/joiner/remote-joiner.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EAElB,MAAM,iBAAiB,CAAA;AAaxB,MAAM,MAAM,uBAAuB,GAAG,CACpC,MAAM,EAAE,oBAAoB,EAC5B,QAAQ,EAAE,MAAM,EAChB,GAAG,CAAC,EAAE,CAAC,OAAO,GAAG,OAAO,EAAE,CAAC,EAAE,EAC7B,YAAY,CAAC,EAAE,GAAG,KACf,OAAO,CAAC;IACX,IAAI,EAAE,OAAO,EAAE,GAAG;QAAE,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAA;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAC,CAAA;AAmBF,qBAAa,YAAY;IA8GrB,OAAO,CAAC,eAAe;IACvB,OAAO,CAAC,OAAO;IA9GjB,OAAO,CAAC,kBAAkB,CACf;IAEX,OAAO,CAAC,SAAS,CAA8C;IAE/D,OAAO,CAAC,MAAM,CAAC,YAAY;IAwD3B,OAAO,CAAC,MAAM,CAAC,cAAc;IAe7B,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAuBnC,MAAM,CAAC,UAAU,CACf,YAAY,EAAE,MAAM,EACpB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,iBAAiB;gBAMlB,cAAc,EAAE,kBAAkB,EAAE,EAC5B,eAAe,EAAE,uBAAuB,EACxC,OAAO,GAAE;QACf,0BAA0B,CAAC,EAAE,OAAO,CAAA;QACpC,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;KAC1B;IAmBD,oBAAoB,CAAC,eAAe,EAAE,uBAAuB,GAAG,IAAI;IAI3E,OAAO,CAAC,eAAe;IAqLvB,OAAO,CAAC,eAAe;IAgCvB,OAAO,CAAC,gBAAgB;IAyBxB,OAAO,CAAC,kBAAkB;YAmDZ,SAAS;IAkFvB,OAAO,CAAC,gBAAgB;IAgDxB,OAAO,CAAC,kBAAkB;YAkFZ,aAAa;IAwC3B,OAAO,CAAC,qBAAqB;YA+Bf,cAAc;YA8Bd,0BAA0B;IA6HxC,OAAO,CAAC,YAAY;IAkCpB,OAAO,CAAC,eAAe;IAuJvB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,UAAU;IA8IlB,OAAO,CAAC,YAAY;IA4CpB,OAAO,CAAC,2BAA2B;IAuHnC,OAAO,CAAC,gBAAgB;IAiFlB,KAAK,CACT,QAAQ,EAAE,iBAAiB,EAC3B,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,GAAG,CAAC;CAkHhB"}
1
+ {"version":3,"file":"remote-joiner.d.ts","sourceRoot":"","sources":["../../src/joiner/remote-joiner.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EAElB,MAAM,iBAAiB,CAAA;AAaxB,MAAM,MAAM,uBAAuB,GAAG,CACpC,MAAM,EAAE,oBAAoB,EAC5B,QAAQ,EAAE,MAAM,EAChB,GAAG,CAAC,EAAE,CAAC,OAAO,GAAG,OAAO,EAAE,CAAC,EAAE,EAC7B,YAAY,CAAC,EAAE,GAAG,KACf,OAAO,CAAC;IACX,IAAI,EAAE,OAAO,EAAE,GAAG;QAAE,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAA;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAC,CAAA;AAoBF,qBAAa,YAAY;IAiNrB,OAAO,CAAC,eAAe;IACvB,OAAO,CAAC,OAAO;IAjNjB,OAAO,CAAC,kBAAkB,CACf;IAEX,OAAO,CAAC,SAAS,CAA8C;IAE/D,OAAO,CAAC,MAAM,CAAC,YAAY;IAwD3B,OAAO,CAAC,MAAM,CAAC,cAAc;IAe7B,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAwBnC,OAAO,CAAC,yBAAyB;IAyCjC,OAAO,CAAC,oBAAoB;IAyD5B,MAAM,CAAC,UAAU,CACf,YAAY,EAAE,MAAM,EACpB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,iBAAiB;gBAMlB,cAAc,EAAE,kBAAkB,EAAE,EAC5B,eAAe,EAAE,uBAAuB,EACxC,OAAO,GAAE;QACf,0BAA0B,CAAC,EAAE,OAAO,CAAA;QACpC,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;KAC1B;IAmBD,oBAAoB,CAAC,eAAe,EAAE,uBAAuB,GAAG,IAAI;IAI3E,OAAO,CAAC,eAAe;IAgNvB,OAAO,CAAC,eAAe;IAgCvB,OAAO,CAAC,gBAAgB;IAyBxB,OAAO,CAAC,kBAAkB;YAmDZ,SAAS;IAmFvB,OAAO,CAAC,gBAAgB;IAgDxB,OAAO,CAAC,kBAAkB;YA0FZ,aAAa;IA4K3B,OAAO,CAAC,qBAAqB;IAiC7B,OAAO,CAAC,YAAY;IAqCpB,OAAO,CAAC,cAAc;IAuFtB,OAAO,CAAC,eAAe;IAuJvB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,UAAU;IAoJlB,OAAO,CAAC,YAAY;IAuDpB,OAAO,CAAC,2BAA2B;IAuHnC,OAAO,CAAC,gBAAgB;IAoFlB,KAAK,CACT,QAAQ,EAAE,iBAAiB,EAC3B,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,GAAG,CAAC;CAmHhB"}
@@ -71,6 +71,80 @@ class RemoteJoiner {
71
71
  return acc;
72
72
  }, {});
73
73
  }
74
+ // compute ids to fetch for a relationship
75
+ computeIdsForRelationship(items, relationship) {
76
+ const field = relationship.inverse
77
+ ? relationship.primaryKey
78
+ : relationship.foreignKey.split(".").pop();
79
+ const fieldsArray = relationship.inverse
80
+ ? relationship.primaryKeyArr
81
+ : relationship.foreignKeyArr;
82
+ const idsToFetch = new Set();
83
+ for (let i = 0; i < items.length; i++) {
84
+ const item = items[i];
85
+ if (!item) {
86
+ continue;
87
+ }
88
+ const values = fieldsArray.map((f) => item?.[f]);
89
+ if (values.length !== fieldsArray.length) {
90
+ continue;
91
+ }
92
+ if (fieldsArray.length === 1) {
93
+ const val = values[0];
94
+ if (Array.isArray(val)) {
95
+ for (let x = 0; x < val.length; x++) {
96
+ idsToFetch.add(val[x]);
97
+ }
98
+ }
99
+ else {
100
+ idsToFetch.add(val);
101
+ }
102
+ }
103
+ else {
104
+ idsToFetch.add(values);
105
+ }
106
+ }
107
+ return { field, fieldsArray, idsToFetch };
108
+ }
109
+ // assign fetched related data to items
110
+ assignRelatedToItems(params) {
111
+ const { items, relationship, relatedDataMap, field, fieldsArray } = params;
112
+ items.forEach((item) => {
113
+ if (!item) {
114
+ return;
115
+ }
116
+ const itemKey = fieldsArray.map((f) => item[f]).join(",");
117
+ if (item[relationship.alias]) {
118
+ if (Array.isArray(item[field])) {
119
+ for (let i = 0; i < item[relationship.alias].length; i++) {
120
+ const it = item[relationship.alias][i];
121
+ item[relationship.alias][i] = Object.assign(it, relatedDataMap[it[relationship.primaryKey]]);
122
+ }
123
+ return;
124
+ }
125
+ item[relationship.alias] = Object.assign(item[relationship.alias], relatedDataMap[itemKey]);
126
+ return;
127
+ }
128
+ if (Array.isArray(item[field])) {
129
+ item[relationship.alias] = item[field].map((id) => {
130
+ if (relationship.isList && !Array.isArray(relatedDataMap[id])) {
131
+ relatedDataMap[id] = (0, utils_1.isDefined)(relatedDataMap[id])
132
+ ? [relatedDataMap[id]]
133
+ : [];
134
+ }
135
+ return relatedDataMap[id];
136
+ });
137
+ }
138
+ else {
139
+ if (relationship.isList && !Array.isArray(relatedDataMap[itemKey])) {
140
+ relatedDataMap[itemKey] = (0, utils_1.isDefined)(relatedDataMap[itemKey])
141
+ ? [relatedDataMap[itemKey]]
142
+ : [];
143
+ }
144
+ item[relationship.alias] = relatedDataMap[itemKey];
145
+ }
146
+ });
147
+ }
74
148
  static parseQuery(graphqlQuery, variables) {
75
149
  const parser = new utils_1.GraphQLUtils.GraphQLParser(graphqlQuery, variables);
76
150
  return parser.parseQuery();
@@ -111,6 +185,27 @@ class RemoteJoiner {
111
185
  }
112
186
  service_.relationships = relationships;
113
187
  }
188
+ // Precompute key arrays for all existing relationships on the service
189
+ if (service_.relationships?.size) {
190
+ for (const [, relVal] of service_.relationships.entries()) {
191
+ if (Array.isArray(relVal)) {
192
+ for (let i = 0; i < relVal.length; i++) {
193
+ const rel = relVal[i];
194
+ rel.primaryKeyArr = rel.primaryKey.split(",");
195
+ rel.foreignKeyArr = rel.foreignKey
196
+ .split(",")
197
+ .map((fk) => fk.split(".").pop());
198
+ }
199
+ }
200
+ else if (relVal) {
201
+ const rel = relVal;
202
+ rel.primaryKeyArr = rel.primaryKey.split(",");
203
+ rel.foreignKeyArr = rel.foreignKey
204
+ .split(",")
205
+ .map((fk) => fk.split(".").pop());
206
+ }
207
+ }
208
+ }
114
209
  // add aliases
115
210
  const isReadOnlyDefinition = !(0, utils_1.isDefined)(service_.serviceName) || service_.isReadOnlyLink;
116
211
  if (!isReadOnlyDefinition) {
@@ -184,6 +279,10 @@ class RemoteJoiner {
184
279
  const service_ = expandedRelationships.get(extend.serviceName);
185
280
  const aliasName = extend.relationship.alias;
186
281
  const rel = extend.relationship;
282
+ rel.primaryKeyArr = rel.primaryKey.split(",");
283
+ rel.foreignKeyArr = rel.foreignKey
284
+ .split(",")
285
+ .map((fk) => fk.split(".").pop());
187
286
  if (service_.relationships?.has(aliasName)) {
188
287
  const existing = service_.relationships.get(aliasName);
189
288
  const newRelation = Array.isArray(existing)
@@ -302,6 +401,9 @@ class RemoteJoiner {
302
401
  const isIdsUsingOperatorMap = (0, utils_1.isObject)(ids) &&
303
402
  Object.keys(ids).some((key) => !!utils_1.FilterOperatorMap[key]);
304
403
  uniqueIds = isIdsUsingOperatorMap ? ids : Array.isArray(ids) ? ids : [ids];
404
+ uniqueIds = Array.isArray(uniqueIds)
405
+ ? uniqueIds.filter((id) => id != null)
406
+ : uniqueIds;
305
407
  }
306
408
  if (uniqueIds && Array.isArray(uniqueIds)) {
307
409
  const isCompositeKey = Array.isArray(uniqueIds[0]);
@@ -317,7 +419,6 @@ class RemoteJoiner {
317
419
  else {
318
420
  uniqueIds = Array.from(new Set(uniqueIds.flat()));
319
421
  }
320
- uniqueIds = uniqueIds.filter((id) => (0, utils_1.isDefined)(id));
321
422
  }
322
423
  let pkFieldAdjusted = pkField;
323
424
  if (relationship) {
@@ -386,10 +487,18 @@ class RemoteJoiner {
386
487
  };
387
488
  const removeChildren = (item, prop) => {
388
489
  if (Array.isArray(item)) {
389
- item.forEach((currentItem) => delete currentItem[prop]);
490
+ for (let i = 0; i < item.length; i++) {
491
+ Object.defineProperty(item[i], prop, {
492
+ value: undefined,
493
+ enumerable: false,
494
+ });
495
+ }
390
496
  }
391
497
  else {
392
- delete item[prop];
498
+ Object.defineProperty(item, prop, {
499
+ value: undefined,
500
+ enumerable: false,
501
+ });
393
502
  }
394
503
  };
395
504
  const cleanup = [];
@@ -445,28 +554,116 @@ class RemoteJoiner {
445
554
  }
446
555
  async handleExpands(params) {
447
556
  const { items, parsedExpands, implodeMapping = [], options } = params;
448
- if (!parsedExpands) {
557
+ if (parsedExpands.size === 0) {
449
558
  return;
450
559
  }
451
- for (const [expandedPath, expand] of parsedExpands.entries()) {
452
- if (expandedPath === BASE_PATH) {
453
- continue;
454
- }
455
- let nestedItems = items;
456
- const expandedPathLevels = expandedPath.split(".");
560
+ const getItemsForPath = (rootItems, fullPath) => {
561
+ let nestedItems = rootItems;
562
+ const expandedPathLevels = fullPath.split(".");
457
563
  for (let idx = 1; idx < expandedPathLevels.length - 1; idx++) {
458
564
  nestedItems = RemoteJoiner.getNestedItems(nestedItems, expandedPathLevels[idx]);
459
565
  }
460
- if (nestedItems.length > 0) {
461
- await this.expandProperty({
462
- items: nestedItems,
463
- parentServiceConfig: expand.parentConfig,
464
- expand,
465
- options,
466
- });
566
+ return nestedItems;
567
+ };
568
+ const root = parsedExpands.get(BASE_PATH);
569
+ const executionStages = root?.executionStages;
570
+ // remove root
571
+ root?.executionStages.shift();
572
+ for (const stage of executionStages) {
573
+ const stageFetchGroups = [];
574
+ for (const { paths } of stage) {
575
+ const pathCtx = [];
576
+ for (const path of paths) {
577
+ const expand = parsedExpands.get(path);
578
+ const nestedItems = getItemsForPath(items, path);
579
+ if (!nestedItems?.length || !expand) {
580
+ continue;
581
+ }
582
+ const relationship = this.getEntityRelationship({
583
+ parentServiceConfig: expand.parentConfig,
584
+ property: expand.property,
585
+ entity: expand.entity,
586
+ });
587
+ if (!relationship) {
588
+ continue;
589
+ }
590
+ const { field, fieldsArray, idsToFetch } = this.computeIdsForRelationship(nestedItems, relationship);
591
+ pathCtx.push({
592
+ path,
593
+ expand,
594
+ relationship,
595
+ nestedItems,
596
+ field,
597
+ fieldsArray,
598
+ args: expand.args,
599
+ ids: idsToFetch,
600
+ });
601
+ }
602
+ if (!pathCtx.length) {
603
+ continue;
604
+ }
605
+ // Group by pkField
606
+ const byPkField = new Map();
607
+ for (const ctx of pathCtx) {
608
+ const key = ctx.field;
609
+ if (!byPkField.has(key)) {
610
+ byPkField.set(key, []);
611
+ }
612
+ byPkField.get(key).push(ctx);
613
+ }
614
+ for (const [pkField, ctxs] of byPkField.entries()) {
615
+ const unionIds = Array.from(new Set(ctxs.flatMap((c) => Array.from(c.ids))));
616
+ const unionFields = Array.from(new Set(ctxs.flatMap((c) => c.expand.fields ?? [])));
617
+ const unionArgs = ctxs.flatMap((c) => c.expand.args ?? []);
618
+ const base = ctxs[0].expand;
619
+ const aggExpand = {
620
+ ...base,
621
+ fields: unionFields,
622
+ };
623
+ if (unionArgs.length) {
624
+ aggExpand.args = unionArgs;
625
+ }
626
+ const relationship = ctxs[0].relationship;
627
+ const promise = this.fetchData({
628
+ expand: aggExpand,
629
+ pkField,
630
+ ids: unionIds,
631
+ relationship,
632
+ options,
633
+ });
634
+ stageFetchGroups.push({ ctxs, relationship, promise });
635
+ }
636
+ }
637
+ const stageResults = await Promise.all(stageFetchGroups.map((g) => g.promise));
638
+ for (let i = 0; i < stageFetchGroups.length; i++) {
639
+ const { ctxs, relationship } = stageFetchGroups[i];
640
+ const relatedDataArray = stageResults[i];
641
+ const joinFields = relationship.inverse
642
+ ? relationship.foreignKeyArr
643
+ : relationship.primaryKeyArr;
644
+ const relData = relatedDataArray.path
645
+ ? relatedDataArray.data[relatedDataArray.path]
646
+ : relatedDataArray.data;
647
+ const relatedDataMap = RemoteJoiner.createRelatedDataMap(relData, joinFields);
648
+ for (let ci = 0; ci < ctxs.length; ci++) {
649
+ const ctx = ctxs[ci];
650
+ this.assignRelatedToItems({
651
+ items: ctx.nestedItems,
652
+ relationship: ctx.relationship,
653
+ relatedDataMap,
654
+ field: ctx.field,
655
+ fieldsArray: ctx.fieldsArray,
656
+ });
657
+ }
467
658
  }
468
659
  }
469
- this.handleFieldAliases({ items, parsedExpands, implodeMapping });
660
+ if (implodeMapping.length > 0) {
661
+ this.handleFieldAliases({
662
+ items,
663
+ parsedExpands,
664
+ implodeMapping,
665
+ });
666
+ }
470
667
  }
471
668
  getEntityRelationship(params) {
472
669
  const { parentServiceConfig, property, entity } = params;
@@ -488,124 +685,8 @@ class RemoteJoiner {
488
685
  }
489
686
  return rel;
490
687
  }
491
- async expandProperty(params) {
492
- const { items, parentServiceConfig, expand, options } = params;
493
- if (!expand) {
494
- return;
495
- }
496
- const relationship = this.getEntityRelationship({
497
- parentServiceConfig,
498
- property: expand.property,
499
- entity: expand.entity,
500
- });
501
- if (!relationship) {
502
- return;
503
- }
504
- await this.expandRelationshipProperty({
505
- items,
506
- expand,
507
- relationship,
508
- options,
509
- });
510
- }
511
- async expandRelationshipProperty(params) {
512
- const { items, expand, relationship, options } = params;
513
- const field = relationship.inverse
514
- ? relationship.primaryKey
515
- : relationship.foreignKey.split(".").pop();
516
- const fieldsArray = field.split(",");
517
- const idsToFetch = new Set();
518
- const requestedFields = new Set(expand.fields ?? []);
519
- const fieldsById = new Map();
520
- items.forEach((item) => {
521
- const values = fieldsArray.map((field) => item?.[field]);
522
- if (values.length === fieldsArray.length) {
523
- if (item?.[relationship.alias]) {
524
- for (const field of requestedFields.values()) {
525
- if (field in item[relationship.alias]) {
526
- requestedFields.delete(field);
527
- fieldsById.delete(field);
528
- }
529
- else {
530
- if (!fieldsById.has(field)) {
531
- fieldsById.set(field, []);
532
- }
533
- fieldsById
534
- .get(field)
535
- .push(fieldsArray.length === 1 ? values[0] : values);
536
- }
537
- }
538
- }
539
- else {
540
- if (fieldsArray.length === 1) {
541
- idsToFetch.add(values[0]);
542
- }
543
- else {
544
- idsToFetch.add(values);
545
- }
546
- }
547
- }
548
- });
549
- for (const values of fieldsById.values()) {
550
- values.forEach((val) => {
551
- idsToFetch.add(val);
552
- });
553
- }
554
- if (idsToFetch.size === 0) {
555
- return;
556
- }
557
- const relatedDataArray = await this.fetchData({
558
- expand,
559
- pkField: field,
560
- ids: Array.from(idsToFetch),
561
- relationship,
562
- options,
563
- });
564
- const joinFields = relationship.inverse
565
- ? relationship.foreignKey.split(",")
566
- : relationship.primaryKey.split(",");
567
- const relData = relatedDataArray.path
568
- ? relatedDataArray.data[relatedDataArray.path]
569
- : relatedDataArray.data;
570
- const relatedDataMap = RemoteJoiner.createRelatedDataMap(relData, joinFields);
571
- items.forEach((item) => {
572
- if (!item) {
573
- return;
574
- }
575
- const itemKey = fieldsArray.map((field) => item[field]).join(",");
576
- if (item[relationship.alias]) {
577
- if (Array.isArray(item[field])) {
578
- for (let i = 0; i < item[relationship.alias].length; i++) {
579
- const it = item[relationship.alias][i];
580
- item[relationship.alias][i] = Object.assign(it, relatedDataMap[it[relationship.primaryKey]]);
581
- }
582
- return;
583
- }
584
- item[relationship.alias] = Object.assign(item[relationship.alias], relatedDataMap[itemKey]);
585
- return;
586
- }
587
- if (Array.isArray(item[field])) {
588
- item[relationship.alias] = item[field].map((id) => {
589
- if (relationship.isList && !Array.isArray(relatedDataMap[id])) {
590
- relatedDataMap[id] = (0, utils_1.isDefined)(relatedDataMap[id])
591
- ? [relatedDataMap[id]]
592
- : [];
593
- }
594
- return relatedDataMap[id];
595
- });
596
- }
597
- else {
598
- if (relationship.isList && !Array.isArray(relatedDataMap[itemKey])) {
599
- relatedDataMap[itemKey] = (0, utils_1.isDefined)(relatedDataMap[itemKey])
600
- ? [relatedDataMap[itemKey]]
601
- : [];
602
- }
603
- item[relationship.alias] = relatedDataMap[itemKey];
604
- }
605
- });
606
- }
607
688
  parseExpands(params) {
608
- const { initialService, query, serviceConfig, expands, implodeMapping, options, initialData, } = params;
689
+ const { initialService, query, serviceConfig, expands, implodeMapping, options, initialData, initialDataOnly, } = params;
609
690
  const { parsedExpands, aliasRealPathMap } = this.parseProperties({
610
691
  initialService,
611
692
  query,
@@ -613,7 +694,7 @@ class RemoteJoiner {
613
694
  expands,
614
695
  implodeMapping,
615
696
  });
616
- if (initialData?.length) {
697
+ if (initialData?.length && initialDataOnly) {
617
698
  this.createFilterFromInitialData({
618
699
  initialData: options?.initialData,
619
700
  parsedExpands,
@@ -621,8 +702,75 @@ class RemoteJoiner {
621
702
  });
622
703
  }
623
704
  const groupedExpands = this.groupExpands(parsedExpands);
705
+ this.buildQueryPlan(parsedExpands, groupedExpands);
624
706
  return groupedExpands;
625
707
  }
708
+ buildQueryPlan(fullParsedExpands, groupedExpands) {
709
+ const stages = [];
710
+ // Root stage
711
+ const rootExp = groupedExpands.get(BASE_PATH);
712
+ const rootService = rootExp.serviceConfig.serviceName;
713
+ stages.push([
714
+ {
715
+ service: rootService,
716
+ entity: rootExp.entity,
717
+ paths: [],
718
+ depth: 0,
719
+ },
720
+ ]);
721
+ // Build service sequence for each path
722
+ const getServiceSequence = (path) => {
723
+ const sequence = [];
724
+ let currentPath = path;
725
+ while (currentPath && currentPath !== BASE_PATH) {
726
+ const expand = fullParsedExpands.get(currentPath);
727
+ if (!expand) {
728
+ break;
729
+ }
730
+ sequence.unshift(expand.serviceConfig.serviceName);
731
+ currentPath = expand.parent;
732
+ }
733
+ return sequence;
734
+ };
735
+ // Group paths by their service sequence length and last service in sequence
736
+ const pathsBySequenceDepth = new Map();
737
+ for (const [path, expand] of groupedExpands.entries()) {
738
+ if (path === BASE_PATH) {
739
+ continue;
740
+ }
741
+ const serviceSequence = getServiceSequence(path);
742
+ const sequenceDepth = serviceSequence.length;
743
+ const lastService = expand.serviceConfig.serviceName;
744
+ if (!pathsBySequenceDepth.has(sequenceDepth)) {
745
+ pathsBySequenceDepth.set(sequenceDepth, new Map());
746
+ }
747
+ const depthMap = pathsBySequenceDepth.get(sequenceDepth);
748
+ if (!depthMap.has(lastService)) {
749
+ depthMap.set(lastService, []);
750
+ }
751
+ depthMap.get(lastService).push(path);
752
+ }
753
+ const maxDepth = Math.max(...Array.from(pathsBySequenceDepth.keys()));
754
+ for (let depth = 1; depth <= maxDepth; depth++) {
755
+ const serviceMap = pathsBySequenceDepth.get(depth);
756
+ if (!serviceMap) {
757
+ continue;
758
+ }
759
+ const stageGroups = [];
760
+ for (const [service, paths] of serviceMap.entries()) {
761
+ stageGroups.push({
762
+ service,
763
+ paths,
764
+ depth: depth,
765
+ });
766
+ }
767
+ if (stageGroups.length > 0) {
768
+ stages.push(stageGroups);
769
+ }
770
+ }
771
+ const root = groupedExpands.get(BASE_PATH);
772
+ root.executionStages = stages;
773
+ }
626
774
  parseProperties(params) {
627
775
  const { initialService, query, serviceConfig, expands, implodeMapping } = params;
628
776
  const aliasRealPathMap = new Map();
@@ -780,12 +928,17 @@ class RemoteJoiner {
780
928
  const midProp = curMiddlePath.join(".");
781
929
  const existingExpand = expands.find((exp) => exp.property === midProp);
782
930
  const extraExtends = {
931
+ fields: existingExpand?.fields,
932
+ args: existingExpand?.args,
783
933
  ...(midProp === fullAliasProp ? expand : {}),
784
934
  property: midProp,
785
935
  isAliasMapping: !existingExpand,
786
936
  };
787
937
  if (forwardArgumentsOnPath.includes(BASE_PATH + "." + midProp)) {
788
- extraExtends.args = (existingExpand?.args ?? []).concat(expand?.args ?? []);
938
+ const forwarded = (existingExpand?.args ?? []).concat(expand?.args ?? []);
939
+ if (forwarded.length) {
940
+ extraExtends.args = forwarded;
941
+ }
789
942
  }
790
943
  extMapping.push(extraExtends);
791
944
  }
@@ -848,8 +1001,19 @@ class RemoteJoiner {
848
1001
  targetExpand.expands ??= {};
849
1002
  targetExpand = targetExpand.expands[key] ??= {};
850
1003
  }
851
- targetExpand.fields = [...new Set(expand.fields)];
852
- targetExpand.args = expand.args;
1004
+ const nextFields = [
1005
+ ...new Set([
1006
+ ...(targetExpand.fields ?? []),
1007
+ ...(expand.fields ?? []),
1008
+ ]),
1009
+ ];
1010
+ targetExpand.fields = nextFields;
1011
+ if (expand.args?.length) {
1012
+ const existingArgs = targetExpand.args;
1013
+ targetExpand.args = existingArgs
1014
+ ? existingArgs.concat(expand.args)
1015
+ : expand.args;
1016
+ }
853
1017
  mergedExpands.delete(path);
854
1018
  mergedPaths.set(path, expand);
855
1019
  parentPath = parentExpand.parent;
@@ -963,7 +1127,10 @@ class RemoteJoiner {
963
1127
  property: key,
964
1128
  });
965
1129
  if (isRel) {
966
- delete shallowProperty[key];
1130
+ Object.defineProperty(shallowProperty, key, {
1131
+ value: undefined,
1132
+ enumerable: false,
1133
+ });
967
1134
  }
968
1135
  }
969
1136
  Object.assign(item, shallowProperty);
@@ -1019,6 +1186,7 @@ class RemoteJoiner {
1019
1186
  implodeMapping,
1020
1187
  options,
1021
1188
  initialData: iniDataArray,
1189
+ initialDataOnly: options?.initialDataOnly,
1022
1190
  };
1023
1191
  const parsedExpands = this.parseExpands(parseExpandsConfig);
1024
1192
  const root = parsedExpands.get(BASE_PATH);
@@ -1095,7 +1263,10 @@ function gerPrimaryKeysAndOtherFilters({ serviceConfig, queryObj }) {
1095
1263
  name: primaryKeyFilter,
1096
1264
  value: filters[primaryKeyFilter],
1097
1265
  };
1098
- delete filters[primaryKeyFilter];
1266
+ Object.defineProperty(filters, primaryKeyFilter, {
1267
+ value: undefined,
1268
+ enumerable: false,
1269
+ });
1099
1270
  }
1100
1271
  }
1101
1272
  otherArgs = otherArgs?.length ? otherArgs : undefined;