@koalarx/nest 3.1.28 → 3.1.30

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.
@@ -1,4 +1,5 @@
1
1
  import { Overwrite } from '..';
2
+ import { AutomapContext } from '../utils/automap-cycle-context';
2
3
  import { IComparable, IComparableId } from '../utils/interfaces/icomparable';
3
4
  export declare enum EntityActionType {
4
5
  create = 1,
@@ -15,6 +16,6 @@ export declare abstract class EntityBase<T extends IComparable<T>> implements IC
15
16
  _id: IComparableId;
16
17
  _action: EntityActionType;
17
18
  constructor(props?: EntityProps<T>);
18
- automap(props?: EntityProps<T>): void;
19
+ automap(props?: EntityProps<T>, context?: AutomapContext): void;
19
20
  equals(obj: EntityBase<T>): boolean;
20
21
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.EntityBase = exports.EntityActionType = void 0;
4
4
  const utils_1 = require("@koalarx/utils");
5
5
  const auto_mapping_list_1 = require("../mapping/auto-mapping-list");
6
+ const automap_cycle_context_1 = require("../utils/automap-cycle-context");
6
7
  const list_1 = require("../utils/list");
7
8
  var EntityActionType;
8
9
  (function (EntityActionType) {
@@ -17,8 +18,15 @@ class EntityBase {
17
18
  this.automap(props);
18
19
  }
19
20
  }
20
- automap(props) {
21
+ automap(props, context = (0, automap_cycle_context_1.createAutomapContext)()) {
21
22
  if (props) {
23
+ if (typeof props === 'object' && props !== null) {
24
+ const cachedInstance = context.references.get(props);
25
+ if (cachedInstance && cachedInstance !== this) {
26
+ return;
27
+ }
28
+ context.references.set(props, this);
29
+ }
22
30
  for (const key of Object.keys(props)) {
23
31
  if (props[key] === undefined) {
24
32
  continue;
@@ -29,10 +37,7 @@ class EntityBase {
29
37
  let value = props[key];
30
38
  if (this[key].entityType) {
31
39
  value = value.map((item) => {
32
- const entity = new this[key].entityType();
33
- entity._action = this._action;
34
- entity.automap(item);
35
- return entity;
40
+ return (0, automap_cycle_context_1.mapEntityReference)(item, this[key].entityType, context, this._action);
36
41
  });
37
42
  }
38
43
  this[key].setList(value);
@@ -51,10 +56,7 @@ class EntityBase {
51
56
  }
52
57
  else if (EntityOnPropKey) {
53
58
  if (props[key]) {
54
- const entity = new EntityOnPropKey();
55
- entity._action = this._action;
56
- entity.automap(props[key]);
57
- this[key] = entity;
59
+ this[key] = (0, automap_cycle_context_1.mapEntityReference)(props[key], EntityOnPropKey, context, this._action);
58
60
  }
59
61
  }
60
62
  else if (propDefinitions) {
@@ -12,7 +12,7 @@ export declare function Entity<T extends new (...args: any[]) => EntityBase<any>
12
12
  new (...args: any[]): {
13
13
  _id: import("../utils/interfaces/icomparable").IComparableId;
14
14
  _action: import("./entity.base").EntityActionType;
15
- automap(props?: import("./entity.base").EntityProps<any> | undefined): void;
15
+ automap(props?: import("./entity.base").EntityProps<any> | undefined, context?: import("../utils/automap-cycle-context").AutomapContext): void;
16
16
  equals(obj: EntityBase<any>): boolean;
17
17
  };
18
18
  } & T;
@@ -14,6 +14,7 @@ export declare abstract class RepositoryBase<TEntity extends EntityBase<TEntity>
14
14
  private readonly _modelName;
15
15
  private readonly _includeFindMany?;
16
16
  constructor({ context, modelName }: RepositoryInitProps<TEntity, TContext>);
17
+ private get entityInstance();
17
18
  private getIdPropName;
18
19
  private getWhereByIdSchema;
19
20
  private getConnectPrismaSchemaForRelation;
@@ -30,6 +31,7 @@ export declare abstract class RepositoryBase<TEntity extends EntityBase<TEntity>
30
31
  private orphanRemoval;
31
32
  private getIdOnEntity;
32
33
  private loadRelationForEntity;
34
+ private loadEntityFromCache;
33
35
  private enrichEntityWithRelations;
34
36
  private persistRelations;
35
37
  protected context(transactionalClient?: TContext): TContext[TModelKey];
@@ -6,9 +6,10 @@ const pagination_dto_1 = require("../dtos/pagination.dto");
6
6
  const koala_global_vars_1 = require("../koala-global-vars");
7
7
  const auto_mapping_list_1 = require("../mapping/auto-mapping-list");
8
8
  const generate_prisma_include_schema_1 = require("../utils/generate-prisma-include-schema");
9
+ const hydrate_entity_from_cache_1 = require("../utils/hydrate-entity-from-cache");
9
10
  const list_1 = require("../utils/list");
10
- const entity_base_1 = require("./entity.base");
11
11
  const proxy_1 = require("../utils/proxy");
12
+ const entity_base_1 = require("./entity.base");
12
13
  class RepositoryBase {
13
14
  _context;
14
15
  _modelName;
@@ -22,6 +23,9 @@ class RepositoryBase {
22
23
  entity: this._modelName,
23
24
  });
24
25
  }
26
+ get entityInstance() {
27
+ return this._modelName.prototype.constructor;
28
+ }
25
29
  getIdPropName(entity) {
26
30
  const idConfig = Reflect.getMetadata('entity:id', entity ? entity.constructor.prototype : this._modelName.prototype);
27
31
  if (idConfig) {
@@ -310,6 +314,24 @@ class RepositoryBase {
310
314
  })
311
315
  .then((data) => this.enrichEntityWithRelations(entity, (0, proxy_1.createProxy)(data), cache));
312
316
  }
317
+ loadEntityFromCache(entity, data, cache) {
318
+ return (0, hydrate_entity_from_cache_1.hydrateEntityFromCache)({
319
+ entity: entity,
320
+ data,
321
+ cache,
322
+ dependencies: {
323
+ getAllProps: (sourceEntity) => auto_mapping_list_1.AutoMappingList.getAllProps(sourceEntity),
324
+ getPropDefinitions: (sourceEntity, propName) => auto_mapping_list_1.AutoMappingList.getPropDefinitions(sourceEntity, propName),
325
+ getSourceByName: (sourceName) => auto_mapping_list_1.AutoMappingList.getSourceByName(sourceName),
326
+ getListEntityType: (sourceEntity, propName) => {
327
+ const list = new sourceEntity()[propName];
328
+ return list.entityType;
329
+ },
330
+ getIdOnEntity: (currentEntity, value) => this.getIdOnEntity(currentEntity, value),
331
+ createEntity: (sourceEntity) => new sourceEntity(),
332
+ },
333
+ });
334
+ }
313
335
  async enrichEntityWithRelations(entity, data, cache = new Map()) {
314
336
  if (!data) {
315
337
  return null;
@@ -321,31 +343,24 @@ class RepositoryBase {
321
343
  if (propDef?.type === list_1.List.name) {
322
344
  const list = new entity()[prop.name];
323
345
  const entityInstance = list.entityType;
324
- const items = [];
325
346
  for (const item of data[propName] || []) {
326
- const cacheKey = `${entity.name}-${this.getIdOnEntity(new entityInstance(), item)}`;
347
+ const cacheKey = `${entityInstance.name}-${this.getIdOnEntity(new entityInstance(), item)}`;
327
348
  if (cache.has(cacheKey)) {
328
- items.push(cache.get(cacheKey));
329
349
  continue;
330
350
  }
331
351
  cache.set(cacheKey, item);
332
- const enrichedItem = await this.loadRelationForEntity(this.getWhereByIdSchema(new entityInstance(), item), entityInstance, cache);
333
- cache.set(cacheKey, enrichedItem);
334
- items.push(enrichedItem);
352
+ await this.loadRelationForEntity(this.getWhereByIdSchema(new entityInstance(), item), entityInstance, cache).then((response) => cache.set(cacheKey, response));
335
353
  }
336
- data[propName] = items;
337
354
  continue;
338
355
  }
339
356
  const relationEntity = auto_mapping_list_1.AutoMappingList.getSourceByName(propDef?.type ?? '');
340
357
  if (relationEntity && data[propName]) {
341
- const cacheKey = `${entity.name}-${this.getIdOnEntity(new relationEntity(), data[propName])}`;
358
+ const cacheKey = `${relationEntity.name}-${this.getIdOnEntity(new relationEntity(), data[propName])}`;
342
359
  if (cache.has(cacheKey)) {
343
- data[propName] = cache.get(cacheKey);
344
360
  continue;
345
361
  }
346
362
  cache.set(cacheKey, data[propName]);
347
- data[propName] = await this.loadRelationForEntity(this.getWhereByIdSchema(new relationEntity(), data[propName]), relationEntity, cache);
348
- cache.set(cacheKey, data[propName]);
363
+ await this.loadRelationForEntity(this.getWhereByIdSchema(new relationEntity(), data[propName]), relationEntity, cache).then((response) => cache.set(cacheKey, response));
349
364
  }
350
365
  }
351
366
  return data;
@@ -411,35 +426,41 @@ class RepositoryBase {
411
426
  }
412
427
  async findById(id) {
413
428
  const data = await this.context().findFirst({
414
- select: this.getSelectWithRelationsId(this._modelName.prototype.constructor),
415
- where: this.getWhereByIdSchema(this._modelName.prototype.constructor, {
429
+ select: this.getSelectWithRelationsId(this.entityInstance),
430
+ where: this.getWhereByIdSchema(this.entityInstance, {
416
431
  id,
417
432
  }),
418
433
  });
419
434
  if (!data)
420
435
  return null;
421
- const enrichedEntity = await this.enrichEntityWithRelations(this._modelName.prototype.constructor, (0, proxy_1.createProxy)(data));
422
- return this.createEntity(enrichedEntity);
436
+ const cache = new Map();
437
+ return this.enrichEntityWithRelations(this.entityInstance, (0, proxy_1.createProxy)(data), cache)
438
+ .then((response) => this.loadEntityFromCache(this.entityInstance, response, cache))
439
+ .then((response) => this.createEntity(response));
423
440
  }
424
441
  async findFirst(where) {
425
442
  const data = await this.context().findFirst({
426
- select: this.getSelectWithRelationsId(this._modelName.prototype.constructor),
443
+ select: this.getSelectWithRelationsId(this.entityInstance),
427
444
  where,
428
445
  });
429
446
  if (!data)
430
447
  return null;
431
- const enrichedEntity = await this.enrichEntityWithRelations(this._modelName.prototype.constructor, (0, proxy_1.createProxy)(data));
432
- return this.createEntity(enrichedEntity);
448
+ const cache = new Map();
449
+ return this.enrichEntityWithRelations(this.entityInstance, (0, proxy_1.createProxy)(data), cache)
450
+ .then((response) => this.loadEntityFromCache(this.entityInstance, response, cache))
451
+ .then((response) => this.createEntity(response));
433
452
  }
434
453
  async findUnique(where) {
435
454
  const data = await this.context().findUnique({
436
- select: this.getSelectWithRelationsId(this._modelName.prototype.constructor),
455
+ select: this.getSelectWithRelationsId(this.entityInstance),
437
456
  where,
438
457
  });
439
458
  if (!data)
440
459
  return null;
441
- const enrichedEntity = await this.enrichEntityWithRelations(this._modelName.prototype.constructor, (0, proxy_1.createProxy)(data));
442
- return this.createEntity(enrichedEntity);
460
+ const cache = new Map();
461
+ return this.enrichEntityWithRelations(this.entityInstance, (0, proxy_1.createProxy)(data), cache)
462
+ .then((response) => this.loadEntityFromCache(this.entityInstance, response, cache))
463
+ .then((response) => this.createEntity(response));
443
464
  }
444
465
  async findMany(where, pagination) {
445
466
  return this.context()
@@ -2,9 +2,15 @@ import { Type } from '@nestjs/common';
2
2
  import { AutoMappingProfile } from './auto-mapping-profile';
3
3
  export declare class AutoMappingService {
4
4
  private readonly _contextList;
5
+ private readonly _circularCutPolicy;
6
+ private createMapContext;
5
7
  constructor(automappingProfile: AutoMappingProfile);
6
8
  private getInstanceType;
7
9
  map<S, T>(data: any, source: Type<S>, target: Type<T>): T;
10
+ private getIdOnlyValue;
11
+ private shouldCutCircularMapping;
12
+ private getCutValue;
13
+ private mapWithContext;
8
14
  private getTarget;
9
15
  private mapNestedProp;
10
16
  private mapListToArray;
@@ -17,6 +17,15 @@ const auto_mapping_list_1 = require("./auto-mapping-list");
17
17
  const auto_mapping_profile_1 = require("./auto-mapping-profile");
18
18
  let AutoMappingService = class AutoMappingService {
19
19
  _contextList = auto_mapping_list_1.AutoMappingList;
20
+ _circularCutPolicy = {
21
+ onCut: 'id-only',
22
+ };
23
+ createMapContext() {
24
+ return {
25
+ references: new WeakMap(),
26
+ targetPath: [],
27
+ };
28
+ }
20
29
  constructor(automappingProfile) {
21
30
  automappingProfile.profile();
22
31
  }
@@ -29,57 +38,105 @@ let AutoMappingService = class AutoMappingService {
29
38
  }
30
39
  }
31
40
  map(data, source, target) {
41
+ return this.mapWithContext(data, source, target, this.createMapContext());
42
+ }
43
+ getIdOnlyValue(data) {
44
+ if (!data || typeof data !== 'object') {
45
+ return data;
46
+ }
47
+ const id = data.id ?? data._id;
48
+ if (id !== undefined) {
49
+ return { id };
50
+ }
51
+ return null;
52
+ }
53
+ shouldCutCircularMapping(target, context) {
54
+ return context.targetPath.some((targetOnPath) => targetOnPath === target);
55
+ }
56
+ getCutValue(data) {
57
+ const onCut = this._circularCutPolicy.onCut;
58
+ if (onCut === 'null') {
59
+ return null;
60
+ }
61
+ if (onCut === 'omit') {
62
+ return undefined;
63
+ }
64
+ return this.getIdOnlyValue(data);
65
+ }
66
+ mapWithContext(data, source, target, context) {
67
+ if (!data || this.isPrimitiveType(data)) {
68
+ return data;
69
+ }
70
+ if (this.shouldCutCircularMapping(target, context)) {
71
+ return this.getCutValue(data);
72
+ }
73
+ const cachedByTarget = context.references.get(data);
74
+ const cachedTarget = cachedByTarget?.get(target);
75
+ if (cachedTarget) {
76
+ return cachedTarget;
77
+ }
32
78
  const { mapContext, propSourceContext, propTargetContext } = this._contextList.get(source, target);
33
79
  if (!mapContext) {
34
80
  throw new Error(`No mapping context found for ${source.name} to ${target.name}`);
35
81
  }
36
82
  const mappedTarget = new target.prototype.constructor();
37
- propSourceContext?.props.forEach((propSource) => {
38
- const value = data[propSource.name];
39
- const compositionType = propSource.compositionType;
40
- const compositionAction = propSource.compositionAction;
41
- if (value !== undefined) {
42
- const targetProp = propTargetContext?.props.find((tp) => tp.name === propSource.name);
43
- if (targetProp) {
44
- const typeSource = (0, get_type_by_prop_1.getTypeByProp)(propSource);
45
- const typeTarget = (0, get_type_by_prop_1.getTypeByProp)(targetProp);
46
- const listToArray = typeSource === list_1.List.name &&
47
- typeTarget === Array.name &&
48
- value instanceof list_1.List;
49
- const arrayToList = typeSource === Array.name &&
50
- typeTarget === list_1.List.name &&
51
- value instanceof Array &&
52
- !!compositionType;
53
- const arrayToArray = typeSource === Array.name &&
54
- typeTarget === Array.name &&
55
- value instanceof Array &&
56
- !!compositionType;
57
- let mappedValue = value;
58
- if (listToArray) {
59
- mappedValue = this.mapListToArray(value);
60
- }
61
- else if (arrayToList) {
62
- mappedValue = this.mapArrayToList(value, this.getInstanceType(compositionType), compositionAction === 'onlySet');
63
- }
64
- else if (arrayToArray) {
65
- mappedValue = this.mapArrayToArray(value, this.getInstanceType(compositionType));
66
- }
67
- else {
68
- const propSourceInstance = this._contextList.getSourceByName(typeSource);
69
- if (propSourceInstance) {
70
- mappedValue = this.mapNestedProp(value, propSourceInstance);
83
+ if (typeof data === 'object') {
84
+ const cachedTargets = context.references.get(data) ?? new Map();
85
+ cachedTargets.set(target, mappedTarget);
86
+ context.references.set(data, cachedTargets);
87
+ }
88
+ context.targetPath.push(target);
89
+ try {
90
+ propSourceContext?.props.forEach((propSource) => {
91
+ const value = data[propSource.name];
92
+ const compositionType = propSource.compositionType;
93
+ const compositionAction = propSource.compositionAction;
94
+ if (value !== undefined) {
95
+ const targetProp = propTargetContext?.props.find((tp) => tp.name === propSource.name);
96
+ if (targetProp) {
97
+ const typeSource = (0, get_type_by_prop_1.getTypeByProp)(propSource);
98
+ const typeTarget = (0, get_type_by_prop_1.getTypeByProp)(targetProp);
99
+ const listToArray = typeSource === list_1.List.name &&
100
+ typeTarget === Array.name &&
101
+ value instanceof list_1.List;
102
+ const arrayToList = typeSource === Array.name &&
103
+ typeTarget === list_1.List.name &&
104
+ value instanceof Array &&
105
+ !!compositionType;
106
+ const arrayToArray = typeSource === Array.name &&
107
+ typeTarget === Array.name &&
108
+ value instanceof Array &&
109
+ !!compositionType;
110
+ let mappedValue = value;
111
+ if (listToArray) {
112
+ mappedValue = this.mapListToArray(value, context);
113
+ }
114
+ else if (arrayToList) {
115
+ mappedValue = this.mapArrayToList(value, this.getInstanceType(compositionType), context, compositionAction === 'onlySet');
116
+ }
117
+ else if (arrayToArray) {
118
+ mappedValue = this.mapArrayToArray(value, this.getInstanceType(compositionType), context);
119
+ }
120
+ else {
121
+ const propSourceInstance = this._contextList.getSourceByName(typeSource);
122
+ if (propSourceInstance) {
123
+ mappedValue = this.mapNestedProp(value, propSourceInstance, context);
124
+ }
71
125
  }
126
+ mappedTarget[targetProp.name] = mappedValue;
72
127
  }
73
- mappedTarget[targetProp.name] = mappedValue;
74
128
  }
75
- }
76
- });
77
- propTargetContext?.props.forEach((prop) => {
78
- const formMemberDefinition = mapContext.forMemberDifinitions?.find((def) => def[prop.name])?.[prop.name];
79
- if (formMemberDefinition) {
80
- mappedTarget[prop.name] = formMemberDefinition(data);
81
- }
82
- });
129
+ });
130
+ propTargetContext?.props.forEach((prop) => {
131
+ const formMemberDefinition = mapContext.forMemberDifinitions?.find((def) => def[prop.name])?.[prop.name];
132
+ if (formMemberDefinition) {
133
+ mappedTarget[prop.name] = formMemberDefinition(data);
134
+ }
135
+ });
136
+ }
137
+ finally {
138
+ context.targetPath.pop();
139
+ }
83
140
  return mappedTarget;
84
141
  }
85
142
  getTarget(data, source) {
@@ -92,21 +149,21 @@ let AutoMappingService = class AutoMappingService {
92
149
  }
93
150
  return targets[0];
94
151
  }
95
- mapNestedProp(data, source) {
96
- return this.map(data, source.prototype.constructor, this.getTarget(data, source));
152
+ mapNestedProp(data, source, context) {
153
+ return this.mapWithContext(data, source.prototype.constructor, this.getTarget(data, source), context);
97
154
  }
98
- mapListToArray(value) {
155
+ mapListToArray(value, context) {
99
156
  return value.toArray().map((item) => {
100
157
  const entityOnList = value.entityType?.prototype.constructor;
101
158
  if (entityOnList) {
102
- return this.mapNestedProp(item, entityOnList) ?? {};
159
+ return this.mapNestedProp(item, entityOnList, context) ?? {};
103
160
  }
104
161
  return {};
105
162
  });
106
163
  }
107
- mapArrayToList(value, compositionType, onlySet = true) {
164
+ mapArrayToList(value, compositionType, context, onlySet = true) {
108
165
  const list = new list_1.List(this.getTarget(value, compositionType.prototype.constructor));
109
- const mappedValue = value.map((item) => this.mapNestedProp(item, compositionType.prototype.constructor) ?? {});
166
+ const mappedValue = value.map((item) => this.mapNestedProp(item, compositionType.prototype.constructor, context) ?? {});
110
167
  if (onlySet) {
111
168
  list.setList(mappedValue);
112
169
  }
@@ -115,8 +172,8 @@ let AutoMappingService = class AutoMappingService {
115
172
  }
116
173
  return list;
117
174
  }
118
- mapArrayToArray(value, compositionType) {
119
- return value.map((item) => this.mapNestedProp(item, compositionType.prototype.constructor) ?? {});
175
+ mapArrayToArray(value, compositionType, context) {
176
+ return value.map((item) => this.mapNestedProp(item, compositionType.prototype.constructor, context) ?? {});
120
177
  }
121
178
  isPrimitiveType(value) {
122
179
  switch (typeof value) {
@@ -0,0 +1,6 @@
1
+ import { Type } from '@nestjs/common';
2
+ export interface AutomapContext {
3
+ references: WeakMap<object, any>;
4
+ }
5
+ export declare function createAutomapContext(): AutomapContext;
6
+ export declare function mapEntityReference(value: any, EntityOnPropKey: Type<any>, context: AutomapContext, action: number): any;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createAutomapContext = createAutomapContext;
4
+ exports.mapEntityReference = mapEntityReference;
5
+ function createAutomapContext() {
6
+ return {
7
+ references: new WeakMap(),
8
+ };
9
+ }
10
+ function mapEntityReference(value, EntityOnPropKey, context, action) {
11
+ if (!value || typeof value !== 'object') {
12
+ return value;
13
+ }
14
+ const cachedEntity = context.references.get(value);
15
+ if (cachedEntity) {
16
+ return cachedEntity;
17
+ }
18
+ const entity = new EntityOnPropKey();
19
+ if (entity && typeof entity.automap === 'function') {
20
+ ;
21
+ entity._action = action;
22
+ context.references.set(value, entity);
23
+ entity.automap(value, context);
24
+ }
25
+ return entity;
26
+ }
@@ -0,0 +1,22 @@
1
+ import { Type } from '@nestjs/common';
2
+ export type GraphVisitState = 'loading' | 'loaded';
3
+ export interface HydrateEntityFromCacheDependencies {
4
+ getAllProps(entity: Type<any>): Array<{
5
+ name: string;
6
+ }>;
7
+ getPropDefinitions(entity: Type<any>, propName: string): {
8
+ type?: string;
9
+ } | undefined;
10
+ getSourceByName(sourceName: string): Type<any> | undefined;
11
+ getListEntityType(entity: Type<any>, propName: string): Type<any> | undefined;
12
+ getIdOnEntity(entity: any, data: any): string;
13
+ createEntity(entity: Type<any>): any;
14
+ }
15
+ export interface HydrateEntityFromCacheParams {
16
+ entity: Type<any>;
17
+ data: any;
18
+ cache: Map<string, any>;
19
+ dependencies: HydrateEntityFromCacheDependencies;
20
+ visitState?: Map<string, GraphVisitState>;
21
+ }
22
+ export declare function hydrateEntityFromCache({ entity, data, cache, dependencies, visitState, }: HydrateEntityFromCacheParams): any;
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hydrateEntityFromCache = hydrateEntityFromCache;
4
+ function mergeScalarProps(target, source) {
5
+ Object.keys(source || {}).forEach((key) => {
6
+ const value = source[key];
7
+ if (value === undefined) {
8
+ return;
9
+ }
10
+ if (Array.isArray(value)) {
11
+ return;
12
+ }
13
+ if (value !== null && typeof value === 'object') {
14
+ return;
15
+ }
16
+ target[key] = value;
17
+ });
18
+ }
19
+ function getCacheKey(entity, data, dependencies) {
20
+ const entityId = dependencies.getIdOnEntity(dependencies.createEntity(entity), data);
21
+ return `${entity.name}-${entityId}`;
22
+ }
23
+ function hydrateEntityFromCache({ entity, data, cache, dependencies, visitState = new Map(), }) {
24
+ if (!data) {
25
+ return data;
26
+ }
27
+ const cacheKey = getCacheKey(entity, data, dependencies);
28
+ const sourceData = cache.get(cacheKey) ?? data;
29
+ let canonicalEntity = cache.get(cacheKey);
30
+ if (!canonicalEntity) {
31
+ canonicalEntity = sourceData;
32
+ cache.set(cacheKey, canonicalEntity);
33
+ }
34
+ mergeScalarProps(canonicalEntity, sourceData);
35
+ if (visitState.get(cacheKey) === 'loading') {
36
+ return canonicalEntity;
37
+ }
38
+ visitState.set(cacheKey, 'loading');
39
+ const allProps = dependencies.getAllProps(entity);
40
+ for (const prop of allProps) {
41
+ const propName = prop.name;
42
+ const propDefinition = dependencies.getPropDefinitions(entity, propName);
43
+ const sourcePropValue = sourceData[propName] ?? canonicalEntity[propName];
44
+ if (Array.isArray(sourcePropValue)) {
45
+ const listEntity = dependencies.getListEntityType(entity, propName);
46
+ if (!listEntity) {
47
+ canonicalEntity[propName] = sourcePropValue;
48
+ continue;
49
+ }
50
+ canonicalEntity[propName] = sourcePropValue.map((item) => hydrateEntityFromCache({
51
+ entity: listEntity,
52
+ data: item,
53
+ cache,
54
+ dependencies,
55
+ visitState,
56
+ }));
57
+ continue;
58
+ }
59
+ const relationEntity = dependencies.getSourceByName(propDefinition?.type ?? '');
60
+ if (relationEntity && sourcePropValue) {
61
+ canonicalEntity[propName] = hydrateEntityFromCache({
62
+ entity: relationEntity,
63
+ data: sourcePropValue,
64
+ cache,
65
+ dependencies,
66
+ visitState,
67
+ });
68
+ continue;
69
+ }
70
+ if (sourcePropValue !== undefined) {
71
+ canonicalEntity[propName] = sourcePropValue;
72
+ }
73
+ }
74
+ visitState.set(cacheKey, 'loaded');
75
+ return canonicalEntity;
76
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@koalarx/nest",
3
- "version": "3.1.28",
3
+ "version": "3.1.30",
4
4
  "description": "",
5
5
  "author": "Igor D. Rangel",
6
6
  "license": "MIT",