create-forgeon 0.3.5 → 0.3.7

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 (109) hide show
  1. package/package.json +1 -1
  2. package/src/cli/add-help.mjs +1 -0
  3. package/src/cli/add-options.mjs +6 -0
  4. package/src/cli/add-options.test.mjs +2 -0
  5. package/src/modules/dependencies.mjs +31 -0
  6. package/src/modules/dependencies.test.mjs +207 -5
  7. package/src/modules/executor.mjs +14 -0
  8. package/src/modules/executor.test.mjs +752 -14
  9. package/src/modules/files-access.mjs +446 -0
  10. package/src/modules/files-image.mjs +540 -0
  11. package/src/modules/files-local.mjs +221 -0
  12. package/src/modules/files-quotas.mjs +402 -0
  13. package/src/modules/files-s3.mjs +266 -0
  14. package/src/modules/files.mjs +527 -0
  15. package/src/modules/queue.mjs +410 -0
  16. package/src/modules/registry.mjs +93 -3
  17. package/src/modules/shared/patch-utils.mjs +25 -0
  18. package/src/run-add-module.mjs +89 -2
  19. package/templates/module-fragments/files/00_title.md +1 -0
  20. package/templates/module-fragments/files/10_overview.md +17 -0
  21. package/templates/module-fragments/files/20_scope.md +13 -0
  22. package/templates/module-fragments/files/90_status_implemented.md +3 -0
  23. package/templates/module-fragments/files-access/00_title.md +1 -0
  24. package/templates/module-fragments/files-access/10_overview.md +9 -0
  25. package/templates/module-fragments/files-access/20_scope.md +20 -0
  26. package/templates/module-fragments/files-access/90_status_implemented.md +3 -0
  27. package/templates/module-fragments/files-image/00_title.md +1 -0
  28. package/templates/module-fragments/files-image/10_overview.md +10 -0
  29. package/templates/module-fragments/files-image/20_scope.md +20 -0
  30. package/templates/module-fragments/files-image/90_status_implemented.md +3 -0
  31. package/templates/module-fragments/files-local/00_title.md +1 -0
  32. package/templates/module-fragments/files-local/10_overview.md +9 -0
  33. package/templates/module-fragments/files-local/20_scope.md +10 -0
  34. package/templates/module-fragments/files-local/90_status_implemented.md +3 -0
  35. package/templates/module-fragments/files-quotas/00_title.md +1 -0
  36. package/templates/module-fragments/files-quotas/10_overview.md +9 -0
  37. package/templates/module-fragments/files-quotas/20_scope.md +20 -0
  38. package/templates/module-fragments/files-quotas/90_status_implemented.md +3 -0
  39. package/templates/module-fragments/files-s3/00_title.md +1 -0
  40. package/templates/module-fragments/files-s3/10_overview.md +17 -0
  41. package/templates/module-fragments/files-s3/20_scope.md +11 -0
  42. package/templates/module-fragments/files-s3/90_status_implemented.md +5 -0
  43. package/templates/module-fragments/queue/20_scope.md +8 -7
  44. package/templates/module-fragments/queue/90_status_implemented.md +3 -0
  45. package/templates/module-presets/files/apps/api/prisma/migrations/20260306_files_file_record/migration.sql +30 -0
  46. package/templates/module-presets/files/apps/api/prisma/migrations/20260306_files_file_variant/migration.sql +55 -0
  47. package/templates/module-presets/files/packages/files/package.json +24 -0
  48. package/templates/module-presets/files/packages/files/src/dto/create-file.dto.ts +30 -0
  49. package/templates/module-presets/files/packages/files/src/files-config.loader.ts +21 -0
  50. package/templates/module-presets/files/packages/files/src/files-config.module.ts +12 -0
  51. package/templates/module-presets/files/packages/files/src/files-config.service.ts +32 -0
  52. package/templates/module-presets/files/packages/files/src/files-env.schema.ts +30 -0
  53. package/templates/module-presets/files/packages/files/src/files.controller.ts +90 -0
  54. package/templates/module-presets/files/packages/files/src/files.service.ts +762 -0
  55. package/templates/module-presets/files/packages/files/src/files.types.ts +35 -0
  56. package/templates/module-presets/files/packages/files/src/forgeon-files.module.ts +12 -0
  57. package/templates/module-presets/files/packages/files/src/index.ts +9 -0
  58. package/templates/module-presets/files/packages/files/tsconfig.json +9 -0
  59. package/templates/module-presets/files-access/packages/files-access/package.json +17 -0
  60. package/templates/module-presets/files-access/packages/files-access/src/files-access.service.ts +59 -0
  61. package/templates/module-presets/files-access/packages/files-access/src/files-access.subject.ts +45 -0
  62. package/templates/module-presets/files-access/packages/files-access/src/files-access.types.ts +14 -0
  63. package/templates/module-presets/files-access/packages/files-access/src/forgeon-files-access.module.ts +8 -0
  64. package/templates/module-presets/files-access/packages/files-access/src/index.ts +4 -0
  65. package/templates/module-presets/files-access/packages/files-access/tsconfig.json +9 -0
  66. package/templates/module-presets/files-image/packages/files-image/package.json +21 -0
  67. package/templates/module-presets/files-image/packages/files-image/src/files-image-config.loader.ts +32 -0
  68. package/templates/module-presets/files-image/packages/files-image/src/files-image-config.module.ts +11 -0
  69. package/templates/module-presets/files-image/packages/files-image/src/files-image-config.service.ts +55 -0
  70. package/templates/module-presets/files-image/packages/files-image/src/files-image-env.schema.ts +28 -0
  71. package/templates/module-presets/files-image/packages/files-image/src/files-image.service.ts +420 -0
  72. package/templates/module-presets/files-image/packages/files-image/src/files-image.types.ts +18 -0
  73. package/templates/module-presets/files-image/packages/files-image/src/forgeon-files-image.module.ts +10 -0
  74. package/templates/module-presets/files-image/packages/files-image/src/index.ts +7 -0
  75. package/templates/module-presets/files-image/packages/files-image/tsconfig.json +9 -0
  76. package/templates/module-presets/files-local/packages/files-local/package.json +19 -0
  77. package/templates/module-presets/files-local/packages/files-local/src/files-local-config.loader.ts +13 -0
  78. package/templates/module-presets/files-local/packages/files-local/src/files-local-config.module.ts +12 -0
  79. package/templates/module-presets/files-local/packages/files-local/src/files-local-config.service.ts +11 -0
  80. package/templates/module-presets/files-local/packages/files-local/src/files-local-env.schema.ts +13 -0
  81. package/templates/module-presets/files-local/packages/files-local/src/index.ts +4 -0
  82. package/templates/module-presets/files-local/packages/files-local/tsconfig.json +9 -0
  83. package/templates/module-presets/files-quotas/packages/files-quotas/package.json +20 -0
  84. package/templates/module-presets/files-quotas/packages/files-quotas/src/files-quotas-config.loader.ts +22 -0
  85. package/templates/module-presets/files-quotas/packages/files-quotas/src/files-quotas-config.module.ts +11 -0
  86. package/templates/module-presets/files-quotas/packages/files-quotas/src/files-quotas-config.service.ts +27 -0
  87. package/templates/module-presets/files-quotas/packages/files-quotas/src/files-quotas-env.schema.ts +15 -0
  88. package/templates/module-presets/files-quotas/packages/files-quotas/src/files-quotas.service.ts +118 -0
  89. package/templates/module-presets/files-quotas/packages/files-quotas/src/files-quotas.types.ts +22 -0
  90. package/templates/module-presets/files-quotas/packages/files-quotas/src/forgeon-files-quotas.module.ts +11 -0
  91. package/templates/module-presets/files-quotas/packages/files-quotas/src/index.ts +7 -0
  92. package/templates/module-presets/files-quotas/packages/files-quotas/tsconfig.json +9 -0
  93. package/templates/module-presets/files-s3/packages/files-s3/package.json +20 -0
  94. package/templates/module-presets/files-s3/packages/files-s3/src/files-s3-config.loader.ts +57 -0
  95. package/templates/module-presets/files-s3/packages/files-s3/src/files-s3-config.module.ts +12 -0
  96. package/templates/module-presets/files-s3/packages/files-s3/src/files-s3-config.service.ts +44 -0
  97. package/templates/module-presets/files-s3/packages/files-s3/src/files-s3-env.schema.ts +51 -0
  98. package/templates/module-presets/files-s3/packages/files-s3/src/index.ts +4 -0
  99. package/templates/module-presets/files-s3/packages/files-s3/tsconfig.json +9 -0
  100. package/templates/module-presets/queue/packages/queue/package.json +21 -0
  101. package/templates/module-presets/queue/packages/queue/src/forgeon-queue.module.ts +10 -0
  102. package/templates/module-presets/queue/packages/queue/src/index.ts +6 -0
  103. package/templates/module-presets/queue/packages/queue/src/queue-config.loader.ts +24 -0
  104. package/templates/module-presets/queue/packages/queue/src/queue-config.module.ts +10 -0
  105. package/templates/module-presets/queue/packages/queue/src/queue-config.service.ts +69 -0
  106. package/templates/module-presets/queue/packages/queue/src/queue-env.schema.ts +17 -0
  107. package/templates/module-presets/queue/packages/queue/src/queue.service.ts +88 -0
  108. package/templates/module-presets/queue/packages/queue/tsconfig.json +9 -0
  109. package/templates/module-fragments/queue/90_status_planned.md +0 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-forgeon",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
4
4
  "description": "Forgeon project generator CLI",
5
5
  "license": "MIT",
6
6
  "author": "Forgeon",
@@ -7,6 +7,7 @@ Usage:
7
7
  Options:
8
8
  --project <path> Target project path (default: current directory)
9
9
  --with-required Allow recursive installation of hard prerequisites
10
+ --with-recommended Auto-install recommended companion modules (non-TTY friendly)
10
11
  --provider <capability>=<module>
11
12
  Explicit provider mapping for non-interactive dependency resolution
12
13
  --list List available modules
@@ -6,6 +6,7 @@ export function parseAddCliArgs(argv) {
6
6
  list: false,
7
7
  help: false,
8
8
  withRequired: false,
9
+ withRecommended: false,
9
10
  providers: {},
10
11
  };
11
12
  const positional = [];
@@ -29,6 +30,11 @@ export function parseAddCliArgs(argv) {
29
30
  continue;
30
31
  }
31
32
 
33
+ if (arg === '--with-recommended') {
34
+ options.withRecommended = true;
35
+ continue;
36
+ }
37
+
32
38
  if (arg.startsWith('--provider=')) {
33
39
  const raw = arg.slice('--provider='.length);
34
40
  const separatorIndex = raw.indexOf('=');
@@ -26,12 +26,14 @@ describe('parseAddCliArgs', () => {
26
26
  const options = parseAddCliArgs([
27
27
  'files',
28
28
  '--with-required',
29
+ '--with-recommended',
29
30
  '--provider',
30
31
  'db-adapter=db-prisma',
31
32
  '--provider=queue-adapter=queue',
32
33
  ]);
33
34
  assert.equal(options.moduleId, 'files');
34
35
  assert.equal(options.withRequired, true);
36
+ assert.equal(options.withRecommended, true);
35
37
  assert.deepEqual(options.providers, {
36
38
  'db-adapter': 'db-prisma',
37
39
  'queue-adapter': 'queue',
@@ -266,3 +266,34 @@ export function getPendingOptionalIntegrations({
266
266
  })
267
267
  .filter(Boolean);
268
268
  }
269
+
270
+ export function getPendingRecommendedCompanions({
271
+ moduleId,
272
+ targetRoot,
273
+ presets = listModulePresets(),
274
+ }) {
275
+ const presetMap = getPresetMap(presets);
276
+ const preset = presetMap.get(moduleId);
277
+ if (!preset || !Array.isArray(preset.recommendedCompanions)) {
278
+ return [];
279
+ }
280
+
281
+ const installedModules = detectInstalledModules(targetRoot, presets);
282
+ return preset.recommendedCompanions
283
+ .map((item) => (typeof item === 'string' ? { id: item } : item))
284
+ .map((item) => {
285
+ const companion = presetMap.get(item.id);
286
+ if (!companion || companion.implemented === false) {
287
+ return null;
288
+ }
289
+ if (installedModules.has(item.id)) {
290
+ return null;
291
+ }
292
+ return {
293
+ id: item.id,
294
+ title: item.title ?? companion.label ?? item.id,
295
+ description: item.description ?? companion.description ?? '',
296
+ };
297
+ })
298
+ .filter(Boolean);
299
+ }
@@ -7,6 +7,7 @@ import {
7
7
  collectProvidedCapabilities,
8
8
  detectInstalledModules,
9
9
  getPendingOptionalIntegrations,
10
+ getPendingRecommendedCompanions,
10
11
  resolveModuleInstallPlan,
11
12
  } from './dependencies.mjs';
12
13
 
@@ -27,10 +28,64 @@ const TEST_PRESETS = [
27
28
  {
28
29
  id: 'files',
29
30
  label: 'Files',
30
- implemented: false,
31
+ implemented: true,
31
32
  detectionPaths: ['packages/files/package.json'],
32
33
  provides: ['files-runtime'],
33
- requires: [{ type: 'capability', id: 'db-adapter' }],
34
+ requires: [
35
+ { type: 'capability', id: 'db-adapter' },
36
+ { type: 'capability', id: 'files-storage-adapter' },
37
+ ],
38
+ recommendedCompanions: [
39
+ {
40
+ id: 'files-image',
41
+ title: 'Files Image Hardening',
42
+ },
43
+ ],
44
+ optionalIntegrations: [],
45
+ },
46
+ {
47
+ id: 'files-local',
48
+ label: 'Files Local Adapter',
49
+ implemented: true,
50
+ detectionPaths: ['packages/files-local/package.json'],
51
+ provides: ['files-storage-adapter'],
52
+ requires: [],
53
+ optionalIntegrations: [],
54
+ },
55
+ {
56
+ id: 'files-s3',
57
+ label: 'Files S3 Adapter',
58
+ implemented: true,
59
+ detectionPaths: ['packages/files-s3/package.json'],
60
+ provides: ['files-storage-adapter'],
61
+ requires: [],
62
+ optionalIntegrations: [],
63
+ },
64
+ {
65
+ id: 'files-access',
66
+ label: 'Files Access',
67
+ implemented: true,
68
+ detectionPaths: ['packages/files-access/package.json'],
69
+ provides: ['files-access-runtime'],
70
+ requires: [{ type: 'capability', id: 'files-runtime' }],
71
+ optionalIntegrations: [],
72
+ },
73
+ {
74
+ id: 'files-quotas',
75
+ label: 'Files Quotas',
76
+ implemented: true,
77
+ detectionPaths: ['packages/files-quotas/package.json'],
78
+ provides: ['files-quotas-runtime'],
79
+ requires: [{ type: 'capability', id: 'files-runtime' }],
80
+ optionalIntegrations: [],
81
+ },
82
+ {
83
+ id: 'files-image',
84
+ label: 'Files Image',
85
+ implemented: true,
86
+ detectionPaths: ['packages/files-image/package.json'],
87
+ provides: ['files-image-runtime'],
88
+ requires: [{ type: 'capability', id: 'files-runtime' }],
34
89
  optionalIntegrations: [],
35
90
  },
36
91
  {
@@ -96,7 +151,27 @@ describe('module dependency helpers', () => {
96
151
  }
97
152
  });
98
153
 
99
- it('builds a concrete install plan in non-interactive mode with --with-required', async () => {
154
+ it('fails in non-interactive mode with --with-required when capability provider mapping is ambiguous', async () => {
155
+ const targetRoot = mkTmp('forgeon-deps-provider-required-');
156
+
157
+ try {
158
+ await assert.rejects(
159
+ () =>
160
+ resolveModuleInstallPlan({
161
+ moduleId: 'files',
162
+ targetRoot,
163
+ presets: TEST_PRESETS,
164
+ withRequired: true,
165
+ isInteractive: false,
166
+ }),
167
+ /required capability "files-storage-adapter" is missing/,
168
+ );
169
+ } finally {
170
+ fs.rmSync(targetRoot, { recursive: true, force: true });
171
+ }
172
+ });
173
+
174
+ it('builds a concrete install plan in non-interactive mode with --with-required and --provider', async () => {
100
175
  const targetRoot = mkTmp('forgeon-deps-plan-');
101
176
 
102
177
  try {
@@ -105,12 +180,99 @@ describe('module dependency helpers', () => {
105
180
  targetRoot,
106
181
  presets: TEST_PRESETS,
107
182
  withRequired: true,
183
+ providerSelections: {
184
+ 'files-storage-adapter': 'files-local',
185
+ },
108
186
  isInteractive: false,
109
187
  });
110
188
 
111
189
  assert.equal(result.cancelled, false);
112
- assert.deepEqual(result.moduleSequence, ['db-prisma', 'files']);
113
- assert.deepEqual(result.selectedProviders, { 'db-adapter': 'db-prisma' });
190
+ assert.deepEqual(result.moduleSequence, ['db-prisma', 'files-local', 'files']);
191
+ assert.deepEqual(result.selectedProviders, {
192
+ 'db-adapter': 'db-prisma',
193
+ 'files-storage-adapter': 'files-local',
194
+ });
195
+ } finally {
196
+ fs.rmSync(targetRoot, { recursive: true, force: true });
197
+ }
198
+ });
199
+
200
+ it('resolves files-access plan through files-runtime capability chain', async () => {
201
+ const targetRoot = mkTmp('forgeon-deps-files-access-plan-');
202
+
203
+ try {
204
+ const result = await resolveModuleInstallPlan({
205
+ moduleId: 'files-access',
206
+ targetRoot,
207
+ presets: TEST_PRESETS,
208
+ withRequired: true,
209
+ providerSelections: {
210
+ 'files-storage-adapter': 'files-local',
211
+ },
212
+ isInteractive: false,
213
+ });
214
+
215
+ assert.equal(result.cancelled, false);
216
+ assert.deepEqual(result.moduleSequence, ['db-prisma', 'files-local', 'files', 'files-access']);
217
+ assert.deepEqual(result.selectedProviders, {
218
+ 'db-adapter': 'db-prisma',
219
+ 'files-storage-adapter': 'files-local',
220
+ 'files-runtime': 'files',
221
+ });
222
+ } finally {
223
+ fs.rmSync(targetRoot, { recursive: true, force: true });
224
+ }
225
+ });
226
+
227
+ it('resolves files-quotas plan through files-runtime capability chain', async () => {
228
+ const targetRoot = mkTmp('forgeon-deps-files-quotas-plan-');
229
+
230
+ try {
231
+ const result = await resolveModuleInstallPlan({
232
+ moduleId: 'files-quotas',
233
+ targetRoot,
234
+ presets: TEST_PRESETS,
235
+ withRequired: true,
236
+ providerSelections: {
237
+ 'files-storage-adapter': 'files-local',
238
+ },
239
+ isInteractive: false,
240
+ });
241
+
242
+ assert.equal(result.cancelled, false);
243
+ assert.deepEqual(result.moduleSequence, ['db-prisma', 'files-local', 'files', 'files-quotas']);
244
+ assert.deepEqual(result.selectedProviders, {
245
+ 'db-adapter': 'db-prisma',
246
+ 'files-storage-adapter': 'files-local',
247
+ 'files-runtime': 'files',
248
+ });
249
+ } finally {
250
+ fs.rmSync(targetRoot, { recursive: true, force: true });
251
+ }
252
+ });
253
+
254
+ it('resolves files-image plan through files-runtime capability chain', async () => {
255
+ const targetRoot = mkTmp('forgeon-deps-files-image-plan-');
256
+
257
+ try {
258
+ const result = await resolveModuleInstallPlan({
259
+ moduleId: 'files-image',
260
+ targetRoot,
261
+ presets: TEST_PRESETS,
262
+ withRequired: true,
263
+ providerSelections: {
264
+ 'files-storage-adapter': 'files-local',
265
+ },
266
+ isInteractive: false,
267
+ });
268
+
269
+ assert.equal(result.cancelled, false);
270
+ assert.deepEqual(result.moduleSequence, ['db-prisma', 'files-local', 'files', 'files-image']);
271
+ assert.deepEqual(result.selectedProviders, {
272
+ 'db-adapter': 'db-prisma',
273
+ 'files-storage-adapter': 'files-local',
274
+ 'files-runtime': 'files',
275
+ });
114
276
  } finally {
115
277
  fs.rmSync(targetRoot, { recursive: true, force: true });
116
278
  }
@@ -155,4 +317,44 @@ describe('module dependency helpers', () => {
155
317
  fs.rmSync(targetRoot, { recursive: true, force: true });
156
318
  }
157
319
  });
320
+
321
+ it('reports pending recommended companions for installed module context', () => {
322
+ const targetRoot = mkTmp('forgeon-deps-recommended-');
323
+ try {
324
+ fs.mkdirSync(path.join(targetRoot, 'packages', 'files'), { recursive: true });
325
+ fs.writeFileSync(path.join(targetRoot, 'packages', 'files', 'package.json'), '{}\n', 'utf8');
326
+
327
+ const pending = getPendingRecommendedCompanions({
328
+ moduleId: 'files',
329
+ targetRoot,
330
+ presets: TEST_PRESETS,
331
+ });
332
+
333
+ assert.equal(pending.length, 1);
334
+ assert.equal(pending[0].id, 'files-image');
335
+ assert.match(pending[0].title, /Files Image Hardening/);
336
+ } finally {
337
+ fs.rmSync(targetRoot, { recursive: true, force: true });
338
+ }
339
+ });
340
+
341
+ it('does not report recommended companions that are already installed', () => {
342
+ const targetRoot = mkTmp('forgeon-deps-recommended-installed-');
343
+ try {
344
+ fs.mkdirSync(path.join(targetRoot, 'packages', 'files'), { recursive: true });
345
+ fs.mkdirSync(path.join(targetRoot, 'packages', 'files-image'), { recursive: true });
346
+ fs.writeFileSync(path.join(targetRoot, 'packages', 'files', 'package.json'), '{}\n', 'utf8');
347
+ fs.writeFileSync(path.join(targetRoot, 'packages', 'files-image', 'package.json'), '{}\n', 'utf8');
348
+
349
+ const pending = getPendingRecommendedCompanions({
350
+ moduleId: 'files',
351
+ targetRoot,
352
+ presets: TEST_PRESETS,
353
+ });
354
+
355
+ assert.equal(pending.length, 0);
356
+ } finally {
357
+ fs.rmSync(targetRoot, { recursive: true, force: true });
358
+ }
359
+ });
158
360
  });
@@ -3,11 +3,18 @@ import path from 'node:path';
3
3
  import { ensureModuleExists } from './registry.mjs';
4
4
  import { writeModuleDocs } from './docs.mjs';
5
5
  import { applyDbPrismaModule } from './db-prisma.mjs';
6
+ import { applyFilesModule } from './files.mjs';
7
+ import { applyFilesAccessModule } from './files-access.mjs';
8
+ import { applyFilesImageModule } from './files-image.mjs';
9
+ import { applyFilesQuotasModule } from './files-quotas.mjs';
10
+ import { applyFilesLocalModule } from './files-local.mjs';
11
+ import { applyFilesS3Module } from './files-s3.mjs';
6
12
  import { applyI18nModule } from './i18n.mjs';
7
13
  import { applyJwtAuthModule } from './jwt-auth.mjs';
8
14
  import { applyLoggerModule } from './logger.mjs';
9
15
  import { applyRateLimitModule } from './rate-limit.mjs';
10
16
  import { applyRbacModule } from './rbac.mjs';
17
+ import { applyQueueModule } from './queue.mjs';
11
18
  import { applySwaggerModule } from './swagger.mjs';
12
19
 
13
20
  function ensureForgeonLikeProject(targetRoot) {
@@ -28,9 +35,16 @@ function ensureForgeonLikeProject(targetRoot) {
28
35
 
29
36
  const MODULE_APPLIERS = {
30
37
  'db-prisma': applyDbPrismaModule,
38
+ files: applyFilesModule,
39
+ 'files-access': applyFilesAccessModule,
40
+ 'files-image': applyFilesImageModule,
41
+ 'files-quotas': applyFilesQuotasModule,
42
+ 'files-local': applyFilesLocalModule,
43
+ 'files-s3': applyFilesS3Module,
31
44
  i18n: applyI18nModule,
32
45
  'jwt-auth': applyJwtAuthModule,
33
46
  logger: applyLoggerModule,
47
+ queue: applyQueueModule,
34
48
  'rate-limit': applyRateLimitModule,
35
49
  rbac: applyRbacModule,
36
50
  swagger: applySwaggerModule,