@koalarx/nest 3.1.29 → 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;
@@ -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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@koalarx/nest",
3
- "version": "3.1.29",
3
+ "version": "3.1.30",
4
4
  "description": "",
5
5
  "author": "Igor D. Rangel",
6
6
  "license": "MIT",