@nest-yalc-2/app 2.2.2 → 2.2.4

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 (42) hide show
  1. package/package.json +5 -3
  2. package/src/__tests__/app-bootstrap-base.helper.spec.ts +86 -38
  3. package/src/__tests__/app-bootstrap-standalone.helper.spec.ts +35 -35
  4. package/src/__tests__/app-bootstrap.helper.spec.ts +162 -98
  5. package/src/__tests__/app-config.service.spec.ts +55 -55
  6. package/src/__tests__/app-context.service.spec.ts +28 -28
  7. package/src/__tests__/app.events.spec.ts +12 -12
  8. package/src/__tests__/app.helper.spec.ts +71 -204
  9. package/src/__tests__/base-app-module.helper.spec.ts +295 -231
  10. package/src/__tests__/base-app.controller.spec.ts +90 -90
  11. package/src/__tests__/base-app.service.spec.ts +39 -39
  12. package/src/__tests__/cls.module.spec.ts +33 -33
  13. package/src/__tests__/life-cycle-handler.service.spec.ts +57 -48
  14. package/src/__tests__/unwrap-result.interceptor.spec.ts +52 -52
  15. package/src/app-bootstrap-base.helper.ts +206 -206
  16. package/src/app-bootstrap-standalone.helper.ts +67 -67
  17. package/src/app-bootstrap.helper.js +9 -9
  18. package/src/app-bootstrap.helper.ts +279 -279
  19. package/src/app-config.service.ts +61 -61
  20. package/src/app-context.module.ts +6 -6
  21. package/src/app-context.service.ts +16 -16
  22. package/src/app-imports.factory.js +2 -2
  23. package/src/app-imports.factory.js.map +1 -1
  24. package/src/app-imports.factory.ts +321 -321
  25. package/src/app.events.ts +8 -8
  26. package/src/app.helper.ts +73 -73
  27. package/src/base-app-module.helper.d.ts +2 -2
  28. package/src/base-app-module.helper.js +8 -5
  29. package/src/base-app-module.helper.js.map +1 -1
  30. package/src/base-app-module.helper.ts +457 -453
  31. package/src/base-app.controller.ts +51 -51
  32. package/src/base-app.interface.ts +55 -55
  33. package/src/base-app.service.ts +33 -33
  34. package/src/cls.module.d.ts +2 -1
  35. package/src/cls.module.js.map +1 -1
  36. package/src/cls.module.ts +76 -75
  37. package/src/conf.type.ts +22 -22
  38. package/src/def.const.ts +33 -33
  39. package/src/global.enum.ts +6 -6
  40. package/src/life-cycle-handler.service.ts +66 -66
  41. package/src/unwrap-result.interceptor.ts +32 -32
  42. package/src/yalc-controller.interface.ts +6 -6
@@ -1,453 +1,457 @@
1
- import {
2
- IYalcBaseAppOptions,
3
- IYalcBaseDynamicModule,
4
- IYalcBaseStaticModule,
5
- } from './base-app.interface.js';
6
- import {
7
- APP_ALIAS_TOKEN,
8
- APP_EVENT_SERVICE,
9
- APP_LOGGER_SERVICE,
10
- APP_OPTION_TOKEN,
11
- MAIN_APP_CONFIG_SERVICE,
12
- MODULE_ALIAS_TOKEN,
13
- MODULE_OPTION_TOKEN,
14
- SYSTEM_EVENT_SERVICE,
15
- SYSTEM_LOGGER_SERVICE,
16
- } from './def.const.js';
17
- import { LifeCycleHandler } from './life-cycle-handler.service.js';
18
- import { DynamicModule, Global, Logger, Module } from '@nestjs/common';
19
- import { LoggerServiceFactory } from '@nest-yalc-2/logger/logger.service.js';
20
- import {
21
- AppConfigService,
22
- createAppConfigProvider,
23
- getAppConfigToken,
24
- getAppEventToken,
25
- getAppLoggerToken,
26
- } from './app-config.service.js';
27
- import { AppContextModule } from './app-context.module.js';
28
- import { NODE_ENV } from './global.enum.js';
29
- import { ConfigModule, ConfigService, registerAs } from '@nestjs/config';
30
- import Joi from 'joi';
31
- import { EventModule } from '@nest-yalc-2/event-manager/index.js';
32
- import { EventEmitter2, EventEmitterModule } from '@nestjs/event-emitter';
33
- import { YalcClsModule } from './cls.module.js';
34
- import { IYalcControllerStaticInterface } from './yalc-controller.interface.js';
35
- import * as _ from 'lodash-es';
36
- import { IGlobalOptions } from './app-bootstrap-base.helper.js';
37
- import { getEnvLoggerLevels } from '@node-yalc/logger/logger.helper.js';
38
-
39
- const singletonDynamicModules = new Map<any, any>();
40
-
41
- export function registerSingletonDynamicModule(
42
- isSingleton: boolean,
43
- moduleToken: any,
44
- module: any,
45
- ): any | true {
46
- if (!isSingleton) {
47
- return false;
48
- }
49
-
50
- const cached = singletonDynamicModules.get(moduleToken);
51
- if (cached) {
52
- return cached;
53
- }
54
-
55
- singletonDynamicModules.set(moduleToken, module);
56
- return singletonDynamicModules.get(moduleToken);
57
- }
58
-
59
- export function getCachedModule(module: any, isSingleton: boolean) {
60
- if (isSingleton) {
61
- return singletonDynamicModules.get(module);
62
- }
63
- return null;
64
- }
65
-
66
- export function envFilePathList(dirname: string = '.') {
67
- const envFilePath: string[] = [];
68
-
69
- envFilePath.push(`${dirname}/.env`); // user-defined env (git-ignored)
70
-
71
- /* istanbul ignore next */
72
- if (process.env.NODE_ENV) {
73
- envFilePath.push(`${dirname}/.env.${process.env.NODE_ENV}`); // user-defined env (git-ignored)
74
- }
75
-
76
- // .env.dist is always loaded except in production
77
- if (process.env.NODE_ENV !== 'production')
78
- envFilePath.push(`${dirname}/.env.dist`);
79
-
80
- return envFilePath;
81
- }
82
-
83
- const _buildEnvFilePath = _.memoize(
84
- (envDir?: string, envPath?: string | string[]) => {
85
- const envFilePath: string[] = [];
86
-
87
- if (!envPath) {
88
- Logger.debug(
89
- `Loading env from: ${envDir}, NODE_ENV: ${process.env.NODE_ENV}`,
90
- );
91
-
92
- envFilePath.push(...envFilePathList(envDir));
93
- } else {
94
- /* istanbul ignore next */
95
- envFilePath.push(...(Array.isArray(envPath) ? envPath : [envPath]));
96
- }
97
-
98
- Logger.debug(`Using env file paths: ${envFilePath}`);
99
-
100
- return envFilePath;
101
- },
102
- // Since memoize use the first argument as the key, we need to generate a unique key based on both arguments
103
- (envDir = '', envPath = '') => {
104
- return `${envDir}-${Array.isArray(envPath) ? envPath.join(',') : envPath}`;
105
- },
106
- );
107
-
108
- /**
109
- * For some strange reason, the memoize doesn't work well with exporting the const at the same time
110
- * and re-use it in the same file
111
- */
112
- export const buildEnvFilePath: (envDir?: string, envPath?: string | string[]) => string[] = _buildEnvFilePath;
113
-
114
- /**
115
- * Used for applications with controller/resolver support
116
- * you should override it
117
- */
118
- export function yalcBaseAppModuleMetadataFactory(
119
- module: any,
120
- appAlias: string,
121
- options?: Omit<IYalcBaseAppOptions, 'module'>,
122
- ): IYalcBaseStaticModule {
123
- /* istanbul ignore next */
124
- const _options = {
125
- // default values
126
- isSingleton: false,
127
- global: true,
128
- // user defined values
129
- ...(options ?? {}),
130
- };
131
- const { controllers, exports, imports, providers } = _options;
132
-
133
- const cached = getCachedModule(module, _options.isSingleton);
134
- if (cached) {
135
- // Logger.debug(`Using cached metadata for ${module.name}`);
136
- return cached;
137
- }
138
-
139
- const _providers: Array<any> = [
140
- {
141
- provide: MODULE_ALIAS_TOKEN,
142
- useValue: appAlias,
143
- },
144
- {
145
- provide: MODULE_OPTION_TOKEN,
146
- useValue: options,
147
- },
148
- {
149
- provide: APP_EVENT_SERVICE,
150
- useExisting: 'INTERNAL_APP_EVENT_SERVICE',
151
- },
152
- {
153
- provide: getAppEventToken(appAlias),
154
- useExisting: 'INTERNAL_APP_EVENT_SERVICE',
155
- },
156
- ];
157
-
158
- const logger = options?.logger;
159
- if (logger) {
160
- /* istanbul ignore next */
161
- _providers.push(
162
- (logger === true ? LoggerServiceFactory : logger)(
163
- appAlias,
164
- APP_LOGGER_SERVICE,
165
- appAlias,
166
- ),
167
- );
168
-
169
- _providers.push({
170
- provide: getAppLoggerToken(appAlias),
171
- useExisting: APP_LOGGER_SERVICE,
172
- });
173
- }
174
-
175
- const hasConfig = _options.extraConfigs || _options.configFactory;
176
-
177
- if (hasConfig) {
178
- _providers.push(
179
- createAppConfigProvider(appAlias),
180
- /**
181
- * Alias
182
- */
183
- {
184
- provide: AppConfigService,
185
- useExisting: getAppConfigToken(appAlias),
186
- },
187
- );
188
- }
189
-
190
- /* istanbul ignore next */
191
- if (!_options.skipDuplicateAppCheck) {
192
- _providers.push(LifeCycleHandler);
193
- }
194
-
195
- // eslint-disable-next-line @typescript-eslint/naming-convention
196
-
197
- if (providers) {
198
- _providers.push(...providers);
199
- }
200
-
201
- const _imports: DynamicModule['imports'] = [];
202
-
203
- if (hasConfig) {
204
- const envFilePath: string[] = _buildEnvFilePath(
205
- options?.envDir,
206
- options?.envPath,
207
- );
208
-
209
- _imports.push(
210
- YalcGlobalStaticModule,
211
- (options?.eventModuleClass ?? EventModule).forRootAsync({
212
- imports: [module],
213
- loggerProvider: {
214
- provide: 'INTERNAL_APP_LOGGER_SERVICE',
215
- useExisting: getAppLoggerToken(appAlias),
216
- },
217
- eventServiceToken: 'INTERNAL_APP_EVENT_SERVICE',
218
- eventEmitter: {
219
- provide: 'INTERNAL_APP_EVENT_EMITTER',
220
- useExisting: EventEmitter2,
221
- },
222
- }),
223
- ConfigModule.forRoot({
224
- envFilePath,
225
- load: [
226
- registerAs(appAlias, async () => {
227
- /**
228
- * @see https://docs.nestjs.com/techniques/configuration#environment-variables-loaded-hook
229
- */
230
- await ConfigModule.envVariablesLoaded;
231
-
232
- /* istanbul ignore next */
233
- return await (_options.configFactory?.() ?? {});
234
- }),
235
- ...(_options.extraConfigs ?? []),
236
- ],
237
- validationSchema: Joi.object({
238
- NODE_ENV: Joi.string()
239
- .valid(
240
- NODE_ENV.DEVELOPMENT,
241
- NODE_ENV.PRODUCTION,
242
- NODE_ENV.TEST,
243
- NODE_ENV.PIPELINE,
244
- )
245
- .default(NODE_ENV.DEVELOPMENT),
246
- }),
247
- validationOptions: {
248
- allowUnknown: true,
249
- abortEarly: true,
250
- } as any,
251
- /**
252
- * It can be global because the ConfigService registers configurations by using an alias, hence there won't be any conflict
253
- * It allows us to use the ConfigService in any module without having to import the ConfigModule
254
- */
255
- isGlobal: true,
256
- }),
257
- YalcClsModule,
258
- );
259
- }
260
-
261
- if (imports) {
262
- _imports.push(...imports);
263
- }
264
-
265
- const _exports: DynamicModule['exports'] = [getAppEventToken(appAlias)];
266
-
267
- if (options?.logger) {
268
- _exports.push(getAppLoggerToken(appAlias));
269
- }
270
-
271
- if (hasConfig) {
272
- _exports.push(getAppConfigToken(appAlias));
273
- }
274
-
275
- if (exports) {
276
- _exports.push(...exports);
277
- }
278
-
279
- const _controllers: DynamicModule['controllers'] = [];
280
-
281
- if (controllers) {
282
- _controllers.push(
283
- ...controllers.map((c) => {
284
- (c as unknown as IYalcControllerStaticInterface)._appAlias = appAlias;
285
- return c;
286
- }),
287
- );
288
- }
289
-
290
- const config = {
291
- imports: _imports,
292
- exports: _exports,
293
- controllers: _controllers,
294
- providers: _providers,
295
- };
296
-
297
- registerSingletonDynamicModule(_options.isSingleton, module, config);
298
-
299
- return config;
300
- }
301
-
302
- /**
303
- * Util class that can be extended to create a NestJS application
304
- */
305
- export class YalcBaseAppModule {
306
- /**
307
- * Used by the CLI and other non-network processes
308
- * you should override it
309
- *
310
- * NOTE: if you need singleton dynamic modules, you should use the `isSingleton` property
311
- * but in most of the cases is better to use the static module instead
312
- */
313
- protected static _forRootStandalone(
314
- appAlias: string,
315
- options?: IYalcBaseAppOptions,
316
- ): IYalcBaseDynamicModule {
317
- return this.assignDynamicProperties(
318
- yalcBaseAppModuleMetadataFactory(this, appAlias, {
319
- isStandalone: true,
320
- ...this.assignDynamicProperties({}),
321
- ...options,
322
- }),
323
- options,
324
- );
325
- }
326
-
327
- /**
328
- * Used for applications with controller/resolver support
329
- * you should override it
330
- *
331
- * NOTE: if you need singleton dynamic modules, you should use the `isSingleton` property
332
- * but in most of the cases is better to use the static module instead
333
- */
334
- protected static _forRoot(
335
- appAlias: string,
336
- options?: IYalcBaseAppOptions,
337
- ): IYalcBaseDynamicModule {
338
- return this.assignDynamicProperties(
339
- yalcBaseAppModuleMetadataFactory(this, appAlias, {
340
- isStandalone: false,
341
- ...this.assignDynamicProperties({}),
342
- ...options,
343
- }),
344
- options,
345
- );
346
- }
347
-
348
- public static assignDynamicProperties(
349
- config: any,
350
- options?: IYalcBaseAppOptions,
351
- ) {
352
- config.module = this;
353
- config.global = options?.global ?? true;
354
- config.isSingleton = options?.isSingleton ?? false;
355
-
356
- return config;
357
- }
358
- }
359
-
360
- function yalcGlobalStaticModuleFactory(): Partial<IYalcBaseStaticModule> {
361
- return {
362
- imports: [
363
- EventEmitterModule.forRoot({
364
- global: true,
365
- maxListeners: 1000,
366
- wildcard: true,
367
- }),
368
- ],
369
- };
370
- }
371
- @Global()
372
- @Module(yalcGlobalStaticModuleFactory())
373
- export class YalcGlobalStaticModule {}
374
-
375
- /**
376
- * This class is used to create true singleton providers
377
- */
378
- export class YalcDefaultAppModule {
379
- static forRoot(
380
- appAlias: string,
381
- imports: NonNullable<DynamicModule['imports']>,
382
- options?: IGlobalOptions,
383
- ) {
384
- const _imports: NonNullable<DynamicModule['imports']> = [
385
- YalcGlobalStaticModule,
386
- (options?.eventModuleClass ?? EventModule).forRootAsync({
387
- loggerProvider: {
388
- provide: 'INTERNAL_SYSTEM_LOGGER_SERVICE',
389
- useExisting: SYSTEM_LOGGER_SERVICE,
390
- },
391
- eventServiceToken: 'INTERNAL_SYSTEM_EVENT_SERVICE',
392
- }),
393
- AppContextModule,
394
- YalcClsModule,
395
- ...imports,
396
- ];
397
-
398
- const providers: NonNullable<DynamicModule['providers']> = [
399
- {
400
- provide: APP_ALIAS_TOKEN,
401
- useValue: appAlias,
402
- },
403
- {
404
- provide: APP_OPTION_TOKEN,
405
- useValue: options,
406
- },
407
- ];
408
-
409
- providers.push(
410
- {
411
- provide: SYSTEM_LOGGER_SERVICE,
412
- useFactory: (configService, eventEmitter) => {
413
- const loggerFactory = options?.logger ?? LoggerServiceFactory;
414
-
415
- return loggerFactory(appAlias, SYSTEM_LOGGER_SERVICE, appAlias, {
416
- event: {
417
- eventEmitter: eventEmitter,
418
- },
419
- overrideLoggerLevels: getEnvLoggerLevels(),
420
- }).useFactory(configService, eventEmitter);
421
- },
422
- inject: [MAIN_APP_CONFIG_SERVICE, EventEmitter2],
423
- },
424
- {
425
- provide: MAIN_APP_CONFIG_SERVICE,
426
- useFactory: (config: ConfigService) => {
427
- return new AppConfigService(config, appAlias);
428
- },
429
- inject: [ConfigService],
430
- },
431
- {
432
- provide: SYSTEM_EVENT_SERVICE,
433
- useExisting: 'INTERNAL_SYSTEM_EVENT_SERVICE',
434
- },
435
- );
436
-
437
- const exports: NonNullable<DynamicModule['exports']> = [
438
- APP_OPTION_TOKEN,
439
- APP_ALIAS_TOKEN,
440
- SYSTEM_LOGGER_SERVICE,
441
- SYSTEM_EVENT_SERVICE,
442
- MAIN_APP_CONFIG_SERVICE,
443
- ];
444
-
445
- return {
446
- exports,
447
- providers,
448
- imports: _imports,
449
- module: YalcDefaultAppModule,
450
- global: true,
451
- };
452
- }
453
- }
1
+ import {
2
+ IYalcBaseAppOptions,
3
+ IYalcBaseDynamicModule,
4
+ IYalcBaseStaticModule,
5
+ } from './base-app.interface.js';
6
+ import {
7
+ APP_ALIAS_TOKEN,
8
+ APP_EVENT_SERVICE,
9
+ APP_LOGGER_SERVICE,
10
+ APP_OPTION_TOKEN,
11
+ MAIN_APP_CONFIG_SERVICE,
12
+ MODULE_ALIAS_TOKEN,
13
+ MODULE_OPTION_TOKEN,
14
+ SYSTEM_EVENT_SERVICE,
15
+ SYSTEM_LOGGER_SERVICE,
16
+ } from './def.const.js';
17
+ import { LifeCycleHandler } from './life-cycle-handler.service.js';
18
+ import { DynamicModule, Global, Logger, Module } from '@nestjs/common';
19
+ import { LoggerServiceFactory } from '@nest-yalc-2/logger/logger.service.js';
20
+ import {
21
+ AppConfigService,
22
+ createAppConfigProvider,
23
+ getAppConfigToken,
24
+ getAppEventToken,
25
+ getAppLoggerToken,
26
+ } from './app-config.service.js';
27
+ import { AppContextModule } from './app-context.module.js';
28
+ import { NODE_ENV } from './global.enum.js';
29
+ import { ConfigModule, ConfigService, registerAs } from '@nestjs/config';
30
+ import Joi from 'joi';
31
+ import { EventModule } from '@nest-yalc-2/event-manager/index.js';
32
+ import { EventEmitter2, EventEmitterModule } from '@nestjs/event-emitter';
33
+ import { YalcClsModule } from './cls.module.js';
34
+ import { IYalcControllerStaticInterface } from './yalc-controller.interface.js';
35
+ import * as _ from 'lodash-es';
36
+ import { IGlobalOptions } from './app-bootstrap-base.helper.js';
37
+ import { getEnvLoggerLevels } from '@node-yalc/logger/logger.helper.js';
38
+
39
+ const singletonDynamicModules = new Map<any, any>();
40
+
41
+ export function registerSingletonDynamicModule(
42
+ isSingleton: boolean,
43
+ moduleToken: any,
44
+ module: any,
45
+ ): any | true {
46
+ if (!isSingleton) {
47
+ return false;
48
+ }
49
+
50
+ const cached = singletonDynamicModules.get(moduleToken);
51
+ if (cached) {
52
+ return cached;
53
+ }
54
+
55
+ singletonDynamicModules.set(moduleToken, module);
56
+ return singletonDynamicModules.get(moduleToken);
57
+ }
58
+
59
+ export function getCachedModule(module: any, isSingleton?: boolean) {
60
+ if (isSingleton) {
61
+ return singletonDynamicModules.get(module);
62
+ }
63
+ return null;
64
+ }
65
+
66
+ export function envFilePathList(dirname?: string) {
67
+ const _dirname = dirname ?? '.';
68
+ const envFilePath: string[] = [];
69
+
70
+ envFilePath.push(`${_dirname}/.env`); // user-defined env (git-ignored)
71
+
72
+ /* istanbul ignore next */
73
+ if (process.env.NODE_ENV) {
74
+ envFilePath.push(`${_dirname}/.env.${process.env.NODE_ENV}`); // user-defined env (git-ignored)
75
+ }
76
+
77
+ // .env.dist is always loaded except in production
78
+ if (process.env.NODE_ENV !== 'production')
79
+ envFilePath.push(`${_dirname}/.env.dist`);
80
+
81
+ return envFilePath;
82
+ }
83
+
84
+ const _buildEnvFilePath = _.memoize(
85
+ (envDir?: string, envPath?: string | string[]) => {
86
+ const envFilePath: string[] = [];
87
+
88
+ if (!envPath) {
89
+ Logger.debug(
90
+ `Loading env from: ${envDir}, NODE_ENV: ${process.env.NODE_ENV}`,
91
+ );
92
+
93
+ envFilePath.push(...envFilePathList(envDir));
94
+ } else {
95
+ /* istanbul ignore next */
96
+ envFilePath.push(...(Array.isArray(envPath) ? envPath : [envPath]));
97
+ }
98
+
99
+ Logger.debug(`Using env file paths: ${envFilePath}`);
100
+
101
+ return envFilePath;
102
+ },
103
+ // Since memoize use the first argument as the key, we need to generate a unique key based on both arguments
104
+ (envDir = '', envPath = '') => {
105
+ return `${envDir}-${Array.isArray(envPath) ? envPath.join(',') : envPath}`;
106
+ },
107
+ );
108
+
109
+ /**
110
+ * For some strange reason, the memoize doesn't work well with exporting the const at the same time
111
+ * and re-use it in the same file
112
+ */
113
+ export function buildEnvFilePath(envDir?: string, envPath?: string | string[]): string[] {
114
+ return _buildEnvFilePath(envDir, envPath);
115
+ }
116
+
117
+ /**
118
+ * Used for applications with controller/resolver support
119
+ * you should override it
120
+ */
121
+ export function yalcBaseAppModuleMetadataFactory(
122
+ module: any,
123
+ appAlias: string,
124
+ options?: Omit<IYalcBaseAppOptions, 'module'>,
125
+ ): IYalcBaseStaticModule {
126
+ /* istanbul ignore next */
127
+ const _options = {
128
+ // default values
129
+ isSingleton: false,
130
+ global: true,
131
+ // user defined values
132
+ ...(options ?? {}),
133
+ };
134
+ const { controllers, exports, imports, providers } = _options;
135
+
136
+ const cached = getCachedModule(module, _options.isSingleton);
137
+ if (cached) {
138
+ // Logger.debug(`Using cached metadata for ${module.name}`);
139
+ return cached;
140
+ }
141
+
142
+ const _providers: Array<any> = [
143
+ {
144
+ provide: MODULE_ALIAS_TOKEN,
145
+ useValue: appAlias,
146
+ },
147
+ {
148
+ provide: MODULE_OPTION_TOKEN,
149
+ useValue: options,
150
+ },
151
+ {
152
+ provide: APP_EVENT_SERVICE,
153
+ useExisting: 'INTERNAL_APP_EVENT_SERVICE',
154
+ },
155
+ {
156
+ provide: getAppEventToken(appAlias),
157
+ useExisting: 'INTERNAL_APP_EVENT_SERVICE',
158
+ },
159
+ ];
160
+
161
+ const logger = options?.logger;
162
+ if (logger) {
163
+ /* istanbul ignore next */
164
+ _providers.push(
165
+ (logger === true ? LoggerServiceFactory : logger)(
166
+ appAlias,
167
+ APP_LOGGER_SERVICE,
168
+ appAlias,
169
+ ),
170
+ );
171
+
172
+ _providers.push({
173
+ provide: getAppLoggerToken(appAlias),
174
+ useExisting: APP_LOGGER_SERVICE,
175
+ });
176
+ }
177
+
178
+ const hasConfig = _options.extraConfigs || _options.configFactory;
179
+
180
+ if (hasConfig) {
181
+ _providers.push(
182
+ createAppConfigProvider(appAlias),
183
+ /**
184
+ * Alias
185
+ */
186
+ {
187
+ provide: AppConfigService,
188
+ useExisting: getAppConfigToken(appAlias),
189
+ },
190
+ );
191
+ }
192
+
193
+ /* istanbul ignore next */
194
+ if (!_options.skipDuplicateAppCheck) {
195
+ _providers.push(LifeCycleHandler);
196
+ }
197
+
198
+ // eslint-disable-next-line @typescript-eslint/naming-convention
199
+
200
+ if (providers) {
201
+ _providers.push(...providers);
202
+ }
203
+
204
+ const _imports: DynamicModule['imports'] = [];
205
+
206
+ if (hasConfig) {
207
+ const envFilePath: string[] = _buildEnvFilePath(
208
+ options?.envDir,
209
+ options?.envPath,
210
+ );
211
+
212
+ _imports.push(
213
+ YalcGlobalStaticModule,
214
+ (options?.eventModuleClass ?? EventModule).forRootAsync({
215
+ imports: [module],
216
+ loggerProvider: {
217
+ provide: 'INTERNAL_APP_LOGGER_SERVICE',
218
+ useExisting: getAppLoggerToken(appAlias),
219
+ },
220
+ eventServiceToken: 'INTERNAL_APP_EVENT_SERVICE',
221
+ eventEmitter: {
222
+ provide: 'INTERNAL_APP_EVENT_EMITTER',
223
+ useExisting: EventEmitter2,
224
+ },
225
+ }),
226
+ ConfigModule.forRoot({
227
+ envFilePath,
228
+ load: [
229
+ registerAs(appAlias, async () => {
230
+ /**
231
+ * @see https://docs.nestjs.com/techniques/configuration#environment-variables-loaded-hook
232
+ */
233
+ await ConfigModule.envVariablesLoaded;
234
+
235
+ /* istanbul ignore next */
236
+ return await (_options.configFactory?.() ?? {});
237
+ }),
238
+ ...(_options.extraConfigs ?? []),
239
+ ],
240
+ validationSchema: Joi.object({
241
+ NODE_ENV: Joi.string()
242
+ .valid(
243
+ NODE_ENV.DEVELOPMENT,
244
+ NODE_ENV.PRODUCTION,
245
+ NODE_ENV.TEST,
246
+ NODE_ENV.PIPELINE,
247
+ )
248
+ .default(NODE_ENV.DEVELOPMENT),
249
+ }),
250
+ validationOptions: {
251
+ allowUnknown: true,
252
+ abortEarly: true,
253
+ } as any,
254
+ /**
255
+ * It can be global because the ConfigService registers configurations by using an alias, hence there won't be any conflict
256
+ * It allows us to use the ConfigService in any module without having to import the ConfigModule
257
+ */
258
+ isGlobal: true,
259
+ }),
260
+ YalcClsModule,
261
+ );
262
+ }
263
+
264
+ if (imports) {
265
+ _imports.push(...imports);
266
+ }
267
+
268
+ const _exports: DynamicModule['exports'] = [getAppEventToken(appAlias)];
269
+
270
+ if (options?.logger) {
271
+ _exports.push(getAppLoggerToken(appAlias));
272
+ }
273
+
274
+ if (hasConfig) {
275
+ _exports.push(getAppConfigToken(appAlias));
276
+ }
277
+
278
+ if (exports) {
279
+ _exports.push(...exports);
280
+ }
281
+
282
+ const _controllers: DynamicModule['controllers'] = [];
283
+
284
+ if (controllers) {
285
+ _controllers.push(
286
+ ...controllers.map((c) => {
287
+ (c as unknown as IYalcControllerStaticInterface)._appAlias = appAlias;
288
+ return c;
289
+ }),
290
+ );
291
+ }
292
+
293
+ const config = {
294
+ imports: _imports,
295
+ exports: _exports,
296
+ controllers: _controllers,
297
+ providers: _providers,
298
+ };
299
+
300
+ registerSingletonDynamicModule(_options.isSingleton, module, config);
301
+
302
+ return config;
303
+ }
304
+
305
+ /**
306
+ * Util class that can be extended to create a NestJS application
307
+ */
308
+ export class YalcBaseAppModule {
309
+ /**
310
+ * Used by the CLI and other non-network processes
311
+ * you should override it
312
+ *
313
+ * NOTE: if you need singleton dynamic modules, you should use the `isSingleton` property
314
+ * but in most of the cases is better to use the static module instead
315
+ */
316
+ protected static _forRootStandalone(
317
+ appAlias: string,
318
+ options?: IYalcBaseAppOptions,
319
+ ): IYalcBaseDynamicModule {
320
+ return this.assignDynamicProperties(
321
+ yalcBaseAppModuleMetadataFactory(this, appAlias, {
322
+ isStandalone: true,
323
+ ...this.assignDynamicProperties({}),
324
+ ...options,
325
+ }),
326
+ options,
327
+ );
328
+ }
329
+
330
+ /**
331
+ * Used for applications with controller/resolver support
332
+ * you should override it
333
+ *
334
+ * NOTE: if you need singleton dynamic modules, you should use the `isSingleton` property
335
+ * but in most of the cases is better to use the static module instead
336
+ */
337
+ protected static _forRoot(
338
+ appAlias: string,
339
+ options?: IYalcBaseAppOptions,
340
+ ): IYalcBaseDynamicModule {
341
+ return this.assignDynamicProperties(
342
+ yalcBaseAppModuleMetadataFactory(this, appAlias, {
343
+ isStandalone: false,
344
+ ...this.assignDynamicProperties({}),
345
+ ...options,
346
+ }),
347
+ options,
348
+ );
349
+ }
350
+
351
+ public static assignDynamicProperties(
352
+ config: any,
353
+ options?: IYalcBaseAppOptions,
354
+ ) {
355
+ config.module = this;
356
+ config.global = options?.global ?? true;
357
+ config.isSingleton = options?.isSingleton ?? false;
358
+
359
+ return config;
360
+ }
361
+ }
362
+
363
+ function yalcGlobalStaticModuleFactory(): Partial<IYalcBaseStaticModule> {
364
+ return {
365
+ imports: [
366
+ EventEmitterModule.forRoot({
367
+ global: true,
368
+ maxListeners: 1000,
369
+ wildcard: true,
370
+ }),
371
+ ],
372
+ };
373
+ }
374
+ @Global()
375
+ @Module(yalcGlobalStaticModuleFactory())
376
+ /* istanbul ignore next */
377
+ export class YalcGlobalStaticModule {}
378
+
379
+ /**
380
+ * This class is used to create true singleton providers
381
+ */
382
+ export class YalcDefaultAppModule {
383
+ static forRoot(
384
+ appAlias: string,
385
+ imports: NonNullable<DynamicModule['imports']>,
386
+ options?: IGlobalOptions,
387
+ ) {
388
+ const _imports: NonNullable<DynamicModule['imports']> = [
389
+ YalcGlobalStaticModule,
390
+ (options?.eventModuleClass ?? EventModule).forRootAsync({
391
+ loggerProvider: {
392
+ provide: 'INTERNAL_SYSTEM_LOGGER_SERVICE',
393
+ useExisting: SYSTEM_LOGGER_SERVICE,
394
+ },
395
+ eventServiceToken: 'INTERNAL_SYSTEM_EVENT_SERVICE',
396
+ }),
397
+ AppContextModule,
398
+ YalcClsModule,
399
+ ...imports,
400
+ ];
401
+
402
+ const providers: NonNullable<DynamicModule['providers']> = [
403
+ {
404
+ provide: APP_ALIAS_TOKEN,
405
+ useValue: appAlias,
406
+ },
407
+ {
408
+ provide: APP_OPTION_TOKEN,
409
+ useValue: options,
410
+ },
411
+ ];
412
+
413
+ providers.push(
414
+ {
415
+ provide: SYSTEM_LOGGER_SERVICE,
416
+ useFactory: (configService, eventEmitter) => {
417
+ const loggerFactory = options?.logger ?? LoggerServiceFactory;
418
+
419
+ return loggerFactory(appAlias, SYSTEM_LOGGER_SERVICE, appAlias, {
420
+ event: {
421
+ eventEmitter: eventEmitter,
422
+ },
423
+ overrideLoggerLevels: getEnvLoggerLevels(),
424
+ }).useFactory(configService, eventEmitter);
425
+ },
426
+ inject: [MAIN_APP_CONFIG_SERVICE, EventEmitter2],
427
+ },
428
+ {
429
+ provide: MAIN_APP_CONFIG_SERVICE,
430
+ useFactory: (config: ConfigService) => {
431
+ return new AppConfigService(config, appAlias);
432
+ },
433
+ inject: [ConfigService],
434
+ },
435
+ {
436
+ provide: SYSTEM_EVENT_SERVICE,
437
+ useExisting: 'INTERNAL_SYSTEM_EVENT_SERVICE',
438
+ },
439
+ );
440
+
441
+ const exports: NonNullable<DynamicModule['exports']> = [
442
+ APP_OPTION_TOKEN,
443
+ APP_ALIAS_TOKEN,
444
+ SYSTEM_LOGGER_SERVICE,
445
+ SYSTEM_EVENT_SERVICE,
446
+ MAIN_APP_CONFIG_SERVICE,
447
+ ];
448
+
449
+ return {
450
+ exports,
451
+ providers,
452
+ imports: _imports,
453
+ module: YalcDefaultAppModule,
454
+ global: true,
455
+ };
456
+ }
457
+ }