@lenne.tech/nest-server 8.6.27 → 8.6.28

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 (45) hide show
  1. package/dist/core/common/helpers/input.helper.d.ts +10 -1
  2. package/dist/core/common/helpers/input.helper.js +46 -4
  3. package/dist/core/common/helpers/input.helper.js.map +1 -1
  4. package/dist/core/common/helpers/model.helper.d.ts +4 -0
  5. package/dist/core/common/helpers/model.helper.js +10 -3
  6. package/dist/core/common/helpers/model.helper.js.map +1 -1
  7. package/dist/core/common/helpers/service.helper.d.ts +12 -0
  8. package/dist/core/common/helpers/service.helper.js +13 -5
  9. package/dist/core/common/helpers/service.helper.js.map +1 -1
  10. package/dist/core/common/services/config.service.d.ts +75 -5
  11. package/dist/core/common/services/config.service.js +168 -4
  12. package/dist/core/common/services/config.service.js.map +1 -1
  13. package/dist/core/common/services/email.service.js +2 -2
  14. package/dist/core/common/services/email.service.js.map +1 -1
  15. package/dist/core/common/services/mailjet.service.js +4 -4
  16. package/dist/core/common/services/mailjet.service.js.map +1 -1
  17. package/dist/core/common/services/template.service.js +1 -1
  18. package/dist/core/common/services/template.service.js.map +1 -1
  19. package/dist/core/modules/user/core-user.service.d.ts +5 -3
  20. package/dist/core/modules/user/core-user.service.js +3 -3
  21. package/dist/core/modules/user/core-user.service.js.map +1 -1
  22. package/dist/server/common/services/cron-jobs.service.d.ts +3 -1
  23. package/dist/server/common/services/cron-jobs.service.js +5 -4
  24. package/dist/server/common/services/cron-jobs.service.js.map +1 -1
  25. package/dist/server/modules/auth/auth.service.d.ts +3 -1
  26. package/dist/server/modules/auth/auth.service.js +9 -4
  27. package/dist/server/modules/auth/auth.service.js.map +1 -1
  28. package/dist/server/modules/user/user.service.js +5 -3
  29. package/dist/server/modules/user/user.service.js.map +1 -1
  30. package/dist/server/server.controller.js +2 -2
  31. package/dist/server/server.controller.js.map +1 -1
  32. package/dist/tsconfig.build.tsbuildinfo +1 -1
  33. package/package.json +4 -3
  34. package/src/core/common/helpers/input.helper.ts +68 -4
  35. package/src/core/common/helpers/model.helper.ts +14 -3
  36. package/src/core/common/helpers/service.helper.ts +25 -5
  37. package/src/core/common/services/config.service.ts +378 -12
  38. package/src/core/common/services/email.service.ts +2 -2
  39. package/src/core/common/services/mailjet.service.ts +4 -4
  40. package/src/core/common/services/template.service.ts +1 -1
  41. package/src/core/modules/user/core-user.service.ts +5 -4
  42. package/src/server/common/services/cron-jobs.service.ts +3 -3
  43. package/src/server/modules/auth/auth.service.ts +7 -3
  44. package/src/server/modules/user/user.service.ts +5 -3
  45. package/src/server/server.controller.ts +2 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenne.tech/nest-server",
3
- "version": "8.6.27",
3
+ "version": "8.6.28",
4
4
  "description": "Modern, fast, powerful Node.js web framework in TypeScript based on Nest with a GraphQL API and a connection to MongoDB (or other databases).",
5
5
  "keywords": [
6
6
  "node",
@@ -91,6 +91,7 @@
91
91
  "passport": "0.6.0",
92
92
  "passport-jwt": "4.0.0",
93
93
  "reflect-metadata": "0.1.13",
94
+ "rfdc": "1.3.0",
94
95
  "rimraf": "3.0.2",
95
96
  "rxjs": "7.5.5"
96
97
  },
@@ -105,8 +106,8 @@
105
106
  "@types/nodemailer": "6.4.4",
106
107
  "@types/passport": "1.0.9",
107
108
  "@types/supertest": "2.0.12",
108
- "@typescript-eslint/eslint-plugin": "5.30.4",
109
- "@typescript-eslint/parser": "5.30.4",
109
+ "@typescript-eslint/eslint-plugin": "5.30.5",
110
+ "@typescript-eslint/parser": "5.30.5",
110
111
  "coffeescript": "2.7.0",
111
112
  "eslint": "8.19.0",
112
113
  "eslint-config-prettier": "8.5.0",
@@ -3,6 +3,7 @@ import { plainToInstance } from 'class-transformer';
3
3
  import { validate } from 'class-validator';
4
4
  import { ValidatorOptions } from 'class-validator/types/validation/ValidatorOptions';
5
5
  import * as _ from 'lodash';
6
+ import * as rfdc from 'rfdc';
6
7
  import { checkRestricted } from '../decorators/restricted.decorator';
7
8
  import { ProcessType } from '../enums/process-type.enum';
8
9
  import { RoleEnum } from '../enums/role.enum';
@@ -196,7 +197,7 @@ export function assignPlain(target: Record<any, any>, ...args: Record<any, any>[
196
197
  ? // Return item if not an object
197
198
  item
198
199
  : // Return cloned record with undefined properties removed
199
- filterProperties(JSON.parse(JSON.stringify(item)), (prop) => prop !== undefined)
200
+ filterProperties(clone(item, { circles: false }), (prop) => prop !== undefined)
200
201
  )
201
202
  );
202
203
  }
@@ -297,6 +298,44 @@ export async function check(
297
298
  return value;
298
299
  }
299
300
 
301
+ /**
302
+ * Clone object
303
+ * @param object Any object
304
+ * @param options Finetuning of rfdc cloning
305
+ * @param options.proto Copy prototype properties as well as own properties into the new object.
306
+ * It's marginally faster to allow enumerable properties on the prototype to be copied into the
307
+ * cloned object (not onto it's prototype, directly onto the object).
308
+ * @param options.circles Keeping track of circular references will slow down performance with an additional 25% overhead.
309
+ * Even if an object doesn't have any circular references, the tracking overhead is the cost.
310
+ * By default if an object with a circular reference is passed to rfdc, it will throw
311
+ * (similar to how JSON.stringify would throw). Use the circles option to detect and preserve
312
+ * circular references in the object. If performance is important, try removing the circular
313
+ * reference from the object (set to undefined) and then add it back manually after cloning
314
+ * instead of using this option.
315
+ */
316
+ export function clone(object: any, options?: { proto?: boolean; circles?: boolean }) {
317
+ const config = {
318
+ proto: false,
319
+ circles: true,
320
+ ...options,
321
+ };
322
+ try {
323
+ return rfdc(config)(object);
324
+ } catch (e) {
325
+ console.info(e);
326
+ if (object.circle) {
327
+ try {
328
+ return rfdc({ ...config, ...{ circles: true } })(object);
329
+ } catch (e) {
330
+ console.info(e);
331
+ return _.clone(object);
332
+ }
333
+ } else {
334
+ return _.clone(object);
335
+ }
336
+ }
337
+ }
338
+
300
339
  /**
301
340
  * Combines objects to a new single plain object and ignores undefined
302
341
  */
@@ -304,6 +343,19 @@ export function combinePlain(...args: Record<any, any>[]): any {
304
343
  return assignPlain({}, ...args);
305
344
  }
306
345
 
346
+ /**
347
+ * Get deep frozen object
348
+ */
349
+ export function deepFreeze(object: any) {
350
+ if (typeof object !== 'object') {
351
+ return object;
352
+ }
353
+ for (const [key, value] of Object.entries(object)) {
354
+ object[key] = deepFreeze(value);
355
+ }
356
+ return Object.freeze(object);
357
+ }
358
+
307
359
  /**
308
360
  * Standard error function
309
361
  */
@@ -327,7 +379,6 @@ export function filterProperties<T = Record<string, any>>(
327
379
 
328
380
  /**
329
381
  * Get plain copy of object
330
- * @param element
331
382
  */
332
383
  export function getPlain(object: any) {
333
384
  return JSON.parse(JSON.stringify(object));
@@ -512,11 +563,24 @@ export function match(expression: any, cases: Record<any, any>): any {
512
563
  /**
513
564
  * Map values into specific type
514
565
  */
515
- export function mapClass<T>(values: Partial<T>, ctor: new () => T, cloneDeep = true): T {
566
+ export function mapClass<T>(
567
+ values: Partial<T>,
568
+ ctor: new () => T,
569
+ options?: {
570
+ cloneDeep?: boolean;
571
+ circle?: boolean;
572
+ proto?: boolean;
573
+ }
574
+ ): T {
575
+ const config = {
576
+ cloneDeep: true,
577
+ circles: false,
578
+ proto: false,
579
+ };
516
580
  const instance = new ctor();
517
581
 
518
582
  return Object.keys(instance).reduce((obj, key) => {
519
- obj[key] = cloneDeep ? _.cloneDeep(values[key]) : values[key];
583
+ obj[key] = config.cloneDeep ? clone(values[key], { circles: config.circles, proto: config.proto }) : values[key];
520
584
  return obj;
521
585
  }, instance);
522
586
  }
@@ -1,6 +1,6 @@
1
1
  import { plainToInstance } from 'class-transformer';
2
- import * as _ from 'lodash';
3
2
  import { Types } from 'mongoose';
3
+ import { clone } from './input.helper';
4
4
 
5
5
  /**
6
6
  * Helper class for models
@@ -63,15 +63,19 @@ export function prepareMap<T = Record<string, any>>(
63
63
  target: T,
64
64
  options: {
65
65
  cloneDeep?: boolean;
66
+ circles?: boolean;
66
67
  funcAllowed?: boolean;
67
68
  mapId?: boolean;
69
+ proto?: boolean;
68
70
  } = {}
69
71
  ): Partial<T> | Record<string, any> {
70
72
  // Set config
71
73
  const config = {
72
74
  cloneDeep: true,
75
+ circles: true,
73
76
  funcAllowed: false,
74
77
  mapId: false,
78
+ proto: false,
75
79
  ...options,
76
80
  };
77
81
 
@@ -85,7 +89,10 @@ export function prepareMap<T = Record<string, any>>(
85
89
  source[key] !== undefined &&
86
90
  (config.funcAllowed || typeof (source[key] !== 'function'))
87
91
  ) {
88
- result[key] = source[key] !== 'function' && config.cloneDeep ? _.cloneDeep(source[key]) : source[key];
92
+ result[key] =
93
+ source[key] !== 'function' && config.cloneDeep
94
+ ? clone(source[key], { circles: config.circles, proto: config.proto })
95
+ : source[key];
89
96
  } else if (key === 'id' && !config.mapId) {
90
97
  result['id'] = source[key];
91
98
  }
@@ -102,21 +109,25 @@ export function map<T = Record<string, any>>(
102
109
  target: T,
103
110
  options: {
104
111
  cloneDeep?: boolean;
112
+ circles?: boolean;
105
113
  funcAllowed?: boolean;
106
114
  mapId?: boolean;
115
+ proto?: boolean;
107
116
  } = {}
108
117
  ): T {
109
118
  // Set config
110
119
  const config = {
111
120
  cloneDeep: true,
121
+ circles: false,
112
122
  funcAllowed: false,
113
123
  mapId: false,
124
+ proto: false,
114
125
  ...options,
115
126
  };
116
127
 
117
128
  // Check source
118
129
  if (!source || typeof source !== 'object' || Array.isArray(source)) {
119
- return config.cloneDeep ? _.cloneDeep(target) : target;
130
+ return config.cloneDeep ? clone(target, { circles: config.circles, proto: config.proto }) : target;
120
131
  }
121
132
 
122
133
  // Prepare source
@@ -4,12 +4,13 @@ import { plainToInstance } from 'class-transformer';
4
4
  import { sha256 } from 'js-sha256';
5
5
  import * as _ from 'lodash';
6
6
  import { Types } from 'mongoose';
7
- import envConfig from '../../../config.env';
8
7
  import { RoleEnum } from '../enums/role.enum';
9
8
  import { PrepareInputOptions } from '../interfaces/prepare-input-options.interface';
10
9
  import { PrepareOutputOptions } from '../interfaces/prepare-output-options.interface';
11
10
  import { ResolveSelector } from '../interfaces/resolve-selector.interface';
12
11
  import { ServiceOptions } from '../interfaces/service-options.interface';
12
+ import { ConfigService } from '../services/config.service';
13
+ import { clone } from './input.helper';
13
14
 
14
15
  /**
15
16
  * Helper class for services
@@ -24,10 +25,13 @@ export default class ServiceHelper {
24
25
  currentUser: { [key: string]: any; id: string },
25
26
  options: {
26
27
  [key: string]: any;
28
+ checkRoles?: boolean;
27
29
  create?: boolean;
28
30
  clone?: boolean;
29
31
  getNewArray?: boolean;
30
32
  removeUndefined?: boolean;
33
+ sha256?: boolean;
34
+ targetModel?: new (...args: any[]) => T;
31
35
  } = {}
32
36
  ): Promise<T> {
33
37
  return prepareInput(input, currentUser, options);
@@ -42,6 +46,8 @@ export default class ServiceHelper {
42
46
  [key: string]: any;
43
47
  clone?: boolean;
44
48
  getNewArray?: boolean;
49
+ objectIdsToStrings?: boolean;
50
+ removeSecrets?: boolean;
45
51
  removeUndefined?: boolean;
46
52
  targetModel?: new (...args: any[]) => T;
47
53
  } = {}
@@ -59,10 +65,13 @@ export async function prepareInput<T = any>(
59
65
  options: {
60
66
  [key: string]: any;
61
67
  checkRoles?: boolean;
68
+ circles?: boolean;
62
69
  create?: boolean;
63
70
  clone?: boolean;
64
71
  getNewArray?: boolean;
72
+ proto?: boolean;
65
73
  removeUndefined?: boolean;
74
+ sha256?: boolean;
66
75
  targetModel?: new (...args: any[]) => T;
67
76
  } = {}
68
77
  ): Promise<T> {
@@ -70,9 +79,12 @@ export async function prepareInput<T = any>(
70
79
  const config = {
71
80
  checkRoles: false,
72
81
  clone: false,
82
+ circles: false,
73
83
  create: false,
74
84
  getNewArray: false,
85
+ proto: false,
75
86
  removeUndefined: true,
87
+ sha256: ConfigService.configFastButReadOnly.sha256,
76
88
  ...options,
77
89
  };
78
90
 
@@ -98,7 +110,7 @@ export async function prepareInput<T = any>(
98
110
  if ((input as Record<string, any>).mapDeep && typeof (input as any).mapDeep === 'function') {
99
111
  input = await Object.getPrototypeOf(input).mapDeep(input);
100
112
  } else {
101
- input = _.cloneDeep(input);
113
+ input = clone(input, { circles: config.circles, proto: config.proto });
102
114
  }
103
115
  }
104
116
 
@@ -134,7 +146,7 @@ export async function prepareInput<T = any>(
134
146
  if ((input as any).password) {
135
147
  // Check if the password was transmitted encrypted
136
148
  // If not, the password is encrypted to enable future encrypted and unencrypted transmissions
137
- if (envConfig.sha256 && !/^[a-f0-9]{64}$/i.test((input as any).password)) {
149
+ if (config.sha256 && !/^[a-f0-9]{64}$/i.test((input as any).password)) {
138
150
  (input as any).password = sha256((input as any).password);
139
151
  }
140
152
 
@@ -164,8 +176,10 @@ export async function prepareOutput<T = { [key: string]: any; map: (...args: any
164
176
  options: {
165
177
  [key: string]: any;
166
178
  clone?: boolean;
179
+ circles?: boolean;
167
180
  getNewArray?: boolean;
168
181
  objectIdsToStrings?: boolean;
182
+ proto?: boolean;
169
183
  removeSecrets?: boolean;
170
184
  removeUndefined?: boolean;
171
185
  targetModel?: new (...args: any[]) => T;
@@ -174,8 +188,10 @@ export async function prepareOutput<T = { [key: string]: any; map: (...args: any
174
188
  // Configuration
175
189
  const config = {
176
190
  clone: false,
191
+ circles: false,
177
192
  getNewArray: false,
178
193
  objectIdsToStrings: true,
194
+ proto: false,
179
195
  removeSecrets: true,
180
196
  removeUndefined: false,
181
197
  targetModel: undefined,
@@ -204,7 +220,7 @@ export async function prepareOutput<T = { [key: string]: any; map: (...args: any
204
220
  if (output.mapDeep && typeof output.mapDeep === 'function') {
205
221
  output = await Object.getPrototypeOf(output).mapDeep(output);
206
222
  } else {
207
- output = _.cloneDeep(output);
223
+ output = clone(output, { circles: config.circles, proto: config.proto });
208
224
  }
209
225
  }
210
226
 
@@ -259,9 +275,11 @@ export function prepareServiceOptions(
259
275
  serviceOptions: ServiceOptions,
260
276
  options?: {
261
277
  clone?: boolean;
278
+ circles?: boolean;
262
279
  inputType?: any;
263
280
  outputType?: any;
264
281
  subFieldSelection?: string;
282
+ proto?: boolean;
265
283
  prepareInput?: PrepareInputOptions;
266
284
  prepareOutput?: PrepareOutputOptions;
267
285
  }
@@ -269,12 +287,14 @@ export function prepareServiceOptions(
269
287
  // Set default values
270
288
  const config = {
271
289
  clone: true,
290
+ circles: true,
291
+ proto: false,
272
292
  ...options,
273
293
  };
274
294
 
275
295
  // Clone
276
296
  if (serviceOptions && config.clone) {
277
- serviceOptions = _.cloneDeep(serviceOptions);
297
+ serviceOptions = clone(serviceOptions, { circles: config.circles, proto: config.proto });
278
298
  }
279
299
 
280
300
  // Init if not exists