create-forgeon 0.2.2 → 0.2.3

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,6 +1,16 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { copyRecursive, writeJson } from '../utils/fs.mjs';
4
+ import {
5
+ ensureBuildSteps,
6
+ ensureDependency,
7
+ ensureLineAfter,
8
+ ensureLineBefore,
9
+ ensureLoadItem,
10
+ ensureScript,
11
+ ensureValidatorSchema,
12
+ upsertEnvLines,
13
+ } from './shared/patch-utils.mjs';
4
14
 
5
15
  function copyFromBase(packageRoot, targetRoot, relativePath) {
6
16
  const source = path.join(packageRoot, 'templates', 'base', relativePath);
@@ -20,140 +30,6 @@ function copyFromPreset(packageRoot, targetRoot, relativePath) {
20
30
  copyRecursive(source, destination);
21
31
  }
22
32
 
23
- function ensureDependency(packageJson, name, version) {
24
- if (!packageJson.dependencies) {
25
- packageJson.dependencies = {};
26
- }
27
- packageJson.dependencies[name] = version;
28
- }
29
-
30
- function ensureScript(packageJson, name, command) {
31
- if (!packageJson.scripts) {
32
- packageJson.scripts = {};
33
- }
34
- packageJson.scripts[name] = command;
35
- }
36
-
37
- function ensureBuildSteps(packageJson, scriptName, requiredCommands) {
38
- if (!packageJson.scripts) {
39
- packageJson.scripts = {};
40
- }
41
-
42
- const current = packageJson.scripts[scriptName];
43
- const steps =
44
- typeof current === 'string' && current.trim().length > 0
45
- ? current
46
- .split('&&')
47
- .map((item) => item.trim())
48
- .filter(Boolean)
49
- : [];
50
-
51
- for (const command of requiredCommands) {
52
- if (!steps.includes(command)) {
53
- steps.push(command);
54
- }
55
- }
56
-
57
- if (steps.length > 0) {
58
- packageJson.scripts[scriptName] = steps.join(' && ');
59
- }
60
- }
61
-
62
- function upsertEnvLines(filePath, lines) {
63
- let content = '';
64
- if (fs.existsSync(filePath)) {
65
- content = fs.readFileSync(filePath, 'utf8').replace(/\r\n/g, '\n');
66
- }
67
-
68
- const keys = new Set(
69
- content
70
- .split('\n')
71
- .filter(Boolean)
72
- .map((line) => line.split('=')[0]),
73
- );
74
-
75
- const append = [];
76
- for (const line of lines) {
77
- const key = line.split('=')[0];
78
- if (!keys.has(key)) {
79
- append.push(line);
80
- }
81
- }
82
-
83
- const next =
84
- append.length > 0 ? `${content.trimEnd()}\n${append.join('\n')}\n` : `${content.trimEnd()}\n`;
85
- fs.writeFileSync(filePath, next.replace(/^\n/, ''), 'utf8');
86
- }
87
-
88
- function ensureLineAfter(content, anchorLine, lineToInsert) {
89
- if (content.includes(lineToInsert)) {
90
- return content;
91
- }
92
-
93
- const index = content.indexOf(anchorLine);
94
- if (index < 0) {
95
- return `${content.trimEnd()}\n${lineToInsert}\n`;
96
- }
97
-
98
- const insertAt = index + anchorLine.length;
99
- return `${content.slice(0, insertAt)}\n${lineToInsert}${content.slice(insertAt)}`;
100
- }
101
-
102
- function ensureLineBefore(content, anchorLine, lineToInsert) {
103
- if (content.includes(lineToInsert)) {
104
- return content;
105
- }
106
-
107
- const index = content.indexOf(anchorLine);
108
- if (index < 0) {
109
- return `${content.trimEnd()}\n${lineToInsert}\n`;
110
- }
111
-
112
- return `${content.slice(0, index)}${lineToInsert}\n${content.slice(index)}`;
113
- }
114
-
115
- function ensureLoadItem(content, itemName) {
116
- const pattern = /load:\s*\[([^\]]*)\]/m;
117
- const match = content.match(pattern);
118
- if (!match) {
119
- return content;
120
- }
121
-
122
- const rawList = match[1];
123
- const items = rawList
124
- .split(',')
125
- .map((item) => item.trim())
126
- .filter(Boolean);
127
-
128
- if (!items.includes(itemName)) {
129
- items.push(itemName);
130
- }
131
-
132
- const next = `load: [${items.join(', ')}]`;
133
- return content.replace(pattern, next);
134
- }
135
-
136
- function ensureValidatorSchema(content, schemaName) {
137
- const pattern = /validate:\s*createEnvValidator\(\[([^\]]*)\]\)/m;
138
- const match = content.match(pattern);
139
- if (!match) {
140
- return content;
141
- }
142
-
143
- const rawList = match[1];
144
- const items = rawList
145
- .split(',')
146
- .map((item) => item.trim())
147
- .filter(Boolean);
148
-
149
- if (!items.includes(schemaName)) {
150
- items.push(schemaName);
151
- }
152
-
153
- const next = `validate: createEnvValidator([${items.join(', ')}])`;
154
- return content.replace(pattern, next);
155
- }
156
-
157
33
  function patchApiDockerfile(targetRoot) {
158
34
  const dockerfilePath = path.join(targetRoot, 'apps', 'api', 'Dockerfile');
159
35
  if (!fs.existsSync(dockerfilePath)) {
@@ -1,6 +1,15 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { copyRecursive, writeJson } from '../utils/fs.mjs';
4
+ import {
5
+ ensureBuildSteps,
6
+ ensureDependency,
7
+ ensureLineAfter,
8
+ ensureLineBefore,
9
+ ensureLoadItem,
10
+ ensureValidatorSchema,
11
+ upsertEnvLines,
12
+ } from './shared/patch-utils.mjs';
4
13
 
5
14
  function copyFromPreset(packageRoot, targetRoot, relativePath) {
6
15
  const source = path.join(packageRoot, 'templates', 'module-presets', 'jwt-auth', relativePath);
@@ -12,177 +21,6 @@ function copyFromPreset(packageRoot, targetRoot, relativePath) {
12
21
  copyRecursive(source, destination);
13
22
  }
14
23
 
15
- function ensureDependency(packageJson, name, version) {
16
- if (!packageJson.dependencies) {
17
- packageJson.dependencies = {};
18
- }
19
- packageJson.dependencies[name] = version;
20
- }
21
-
22
- function ensureBuildSteps(packageJson, scriptName, requiredCommands) {
23
- if (!packageJson.scripts) {
24
- packageJson.scripts = {};
25
- }
26
-
27
- const current = packageJson.scripts[scriptName];
28
- const steps =
29
- typeof current === 'string' && current.trim().length > 0
30
- ? current
31
- .split('&&')
32
- .map((item) => item.trim())
33
- .filter(Boolean)
34
- : [];
35
-
36
- for (const command of requiredCommands) {
37
- if (!steps.includes(command)) {
38
- steps.push(command);
39
- }
40
- }
41
-
42
- if (steps.length > 0) {
43
- packageJson.scripts[scriptName] = steps.join(' && ');
44
- }
45
- }
46
-
47
- function ensureLineAfter(content, anchorLine, lineToInsert) {
48
- if (content.includes(lineToInsert)) {
49
- return content;
50
- }
51
-
52
- const index = content.indexOf(anchorLine);
53
- if (index < 0) {
54
- return `${content.trimEnd()}\n${lineToInsert}\n`;
55
- }
56
-
57
- const insertAt = index + anchorLine.length;
58
- return `${content.slice(0, insertAt)}\n${lineToInsert}${content.slice(insertAt)}`;
59
- }
60
-
61
- function ensureLineBefore(content, anchorLine, lineToInsert) {
62
- if (content.includes(lineToInsert)) {
63
- return content;
64
- }
65
-
66
- const index = content.indexOf(anchorLine);
67
- if (index < 0) {
68
- return `${content.trimEnd()}\n${lineToInsert}\n`;
69
- }
70
-
71
- return `${content.slice(0, index)}${lineToInsert}\n${content.slice(index)}`;
72
- }
73
-
74
- function ensureLoadItem(content, itemName) {
75
- const pattern = /load:\s*\[([^\]]*)\]/m;
76
- const match = content.match(pattern);
77
- if (!match) {
78
- return content;
79
- }
80
-
81
- const rawList = match[1];
82
- const items = rawList
83
- .split(',')
84
- .map((item) => item.trim())
85
- .filter(Boolean);
86
-
87
- if (!items.includes(itemName)) {
88
- items.push(itemName);
89
- }
90
-
91
- const next = `load: [${items.join(', ')}]`;
92
- return content.replace(pattern, next);
93
- }
94
-
95
- function ensureValidatorSchema(content, schemaName) {
96
- const pattern = /validate:\s*createEnvValidator\(\[([^\]]*)\]\)/m;
97
- const match = content.match(pattern);
98
- if (!match) {
99
- return content;
100
- }
101
-
102
- const rawList = match[1];
103
- const items = rawList
104
- .split(',')
105
- .map((item) => item.trim())
106
- .filter(Boolean);
107
-
108
- if (!items.includes(schemaName)) {
109
- items.push(schemaName);
110
- }
111
-
112
- const next = `validate: createEnvValidator([${items.join(', ')}])`;
113
- return content.replace(pattern, next);
114
- }
115
-
116
- function upsertEnvLines(filePath, lines) {
117
- let content = '';
118
- if (fs.existsSync(filePath)) {
119
- content = fs.readFileSync(filePath, 'utf8').replace(/\r\n/g, '\n');
120
- }
121
-
122
- const keys = new Set(
123
- content
124
- .split('\n')
125
- .filter(Boolean)
126
- .map((line) => line.split('=')[0]),
127
- );
128
-
129
- const append = [];
130
- for (const line of lines) {
131
- const key = line.split('=')[0];
132
- if (!keys.has(key)) {
133
- append.push(line);
134
- }
135
- }
136
-
137
- const next =
138
- append.length > 0 ? `${content.trimEnd()}\n${append.join('\n')}\n` : `${content.trimEnd()}\n`;
139
- fs.writeFileSync(filePath, next.replace(/^\n/, ''), 'utf8');
140
- }
141
-
142
- function detectDbAdapter(targetRoot) {
143
- const apiPackagePath = path.join(targetRoot, 'apps', 'api', 'package.json');
144
- let deps = {};
145
- if (fs.existsSync(apiPackagePath)) {
146
- const packageJson = JSON.parse(fs.readFileSync(apiPackagePath, 'utf8'));
147
- deps = {
148
- ...(packageJson.dependencies ?? {}),
149
- ...(packageJson.devDependencies ?? {}),
150
- };
151
- }
152
-
153
- if (
154
- deps['@forgeon/db-prisma'] ||
155
- fs.existsSync(path.join(targetRoot, 'packages', 'db-prisma', 'package.json'))
156
- ) {
157
- return { id: 'db-prisma', supported: true, tokenStore: 'prisma' };
158
- }
159
-
160
- const dbDeps = Object.keys(deps).filter((name) => name.startsWith('@forgeon/db-'));
161
- if (dbDeps.length > 0) {
162
- return { id: dbDeps[0], supported: false, tokenStore: 'none' };
163
- }
164
-
165
- const packagesPath = path.join(targetRoot, 'packages');
166
- if (fs.existsSync(packagesPath)) {
167
- const localDbPackages = fs
168
- .readdirSync(packagesPath, { withFileTypes: true })
169
- .filter((entry) => entry.isDirectory() && entry.name.startsWith('db-'))
170
- .map((entry) => entry.name);
171
- if (localDbPackages.includes('db-prisma')) {
172
- return { id: 'db-prisma', supported: true, tokenStore: 'prisma' };
173
- }
174
- if (localDbPackages.length > 0) {
175
- return { id: `@forgeon/${localDbPackages[0]}`, supported: false, tokenStore: 'none' };
176
- }
177
- }
178
-
179
- return null;
180
- }
181
-
182
- function printDbWarning(message) {
183
- console.error(`\x1b[31m[create-forgeon add jwt-auth] ${message}\x1b[0m`);
184
- }
185
-
186
24
  function patchApiPackage(targetRoot) {
187
25
  const packagePath = path.join(targetRoot, 'apps', 'api', 'package.json');
188
26
  if (!fs.existsSync(packagePath)) {
@@ -201,21 +39,19 @@ function patchApiPackage(targetRoot) {
201
39
  writeJson(packagePath, packageJson);
202
40
  }
203
41
 
204
- function patchAppModule(targetRoot, dbAdapter) {
42
+ function patchAppModule(targetRoot) {
205
43
  const filePath = path.join(targetRoot, 'apps', 'api', 'src', 'app.module.ts');
206
44
  if (!fs.existsSync(filePath)) {
207
45
  return;
208
46
  }
209
47
 
210
- const withPrismaStore = dbAdapter?.supported === true && dbAdapter?.id === 'db-prisma';
211
-
212
48
  let content = fs.readFileSync(filePath, 'utf8').replace(/\r\n/g, '\n');
213
49
  if (!content.includes("from '@forgeon/auth-api';")) {
214
50
  if (content.includes("import { ForgeonI18nModule, i18nConfig, i18nEnvSchema } from '@forgeon/i18n';")) {
215
51
  content = ensureLineAfter(
216
52
  content,
217
53
  "import { ForgeonI18nModule, i18nConfig, i18nEnvSchema } from '@forgeon/i18n';",
218
- "import { AUTH_REFRESH_TOKEN_STORE, authConfig, authEnvSchema, ForgeonAuthModule } from '@forgeon/auth-api';",
54
+ "import { authConfig, authEnvSchema, ForgeonAuthModule } from '@forgeon/auth-api';",
219
55
  );
220
56
  } else if (
221
57
  content.includes("import { ForgeonLoggerModule, loggerConfig, loggerEnvSchema } from '@forgeon/logger';")
@@ -223,7 +59,7 @@ function patchAppModule(targetRoot, dbAdapter) {
223
59
  content = ensureLineAfter(
224
60
  content,
225
61
  "import { ForgeonLoggerModule, loggerConfig, loggerEnvSchema } from '@forgeon/logger';",
226
- "import { AUTH_REFRESH_TOKEN_STORE, authConfig, authEnvSchema, ForgeonAuthModule } from '@forgeon/auth-api';",
62
+ "import { authConfig, authEnvSchema, ForgeonAuthModule } from '@forgeon/auth-api';",
227
63
  );
228
64
  } else if (
229
65
  content.includes("import { ForgeonSwaggerModule, swaggerConfig, swaggerEnvSchema } from '@forgeon/swagger';")
@@ -231,7 +67,7 @@ function patchAppModule(targetRoot, dbAdapter) {
231
67
  content = ensureLineAfter(
232
68
  content,
233
69
  "import { ForgeonSwaggerModule, swaggerConfig, swaggerEnvSchema } from '@forgeon/swagger';",
234
- "import { AUTH_REFRESH_TOKEN_STORE, authConfig, authEnvSchema, ForgeonAuthModule } from '@forgeon/auth-api';",
70
+ "import { authConfig, authEnvSchema, ForgeonAuthModule } from '@forgeon/auth-api';",
235
71
  );
236
72
  } else if (
237
73
  content.includes("import { dbPrismaConfig, dbPrismaEnvSchema, DbPrismaModule } from '@forgeon/db-prisma';")
@@ -239,38 +75,22 @@ function patchAppModule(targetRoot, dbAdapter) {
239
75
  content = ensureLineAfter(
240
76
  content,
241
77
  "import { dbPrismaConfig, dbPrismaEnvSchema, DbPrismaModule } from '@forgeon/db-prisma';",
242
- "import { AUTH_REFRESH_TOKEN_STORE, authConfig, authEnvSchema, ForgeonAuthModule } from '@forgeon/auth-api';",
78
+ "import { authConfig, authEnvSchema, ForgeonAuthModule } from '@forgeon/auth-api';",
243
79
  );
244
80
  } else {
245
81
  content = ensureLineAfter(
246
82
  content,
247
83
  "import { ConfigModule } from '@nestjs/config';",
248
- "import { AUTH_REFRESH_TOKEN_STORE, authConfig, authEnvSchema, ForgeonAuthModule } from '@forgeon/auth-api';",
84
+ "import { authConfig, authEnvSchema, ForgeonAuthModule } from '@forgeon/auth-api';",
249
85
  );
250
86
  }
251
87
  }
252
88
 
253
- if (withPrismaStore && !content.includes("./auth/prisma-auth-refresh-token.store")) {
254
- content = ensureLineBefore(
255
- content,
256
- "import { HealthController } from './health/health.controller';",
257
- "import { PrismaAuthRefreshTokenStore } from './auth/prisma-auth-refresh-token.store';",
258
- );
259
- }
260
-
261
89
  content = ensureLoadItem(content, 'authConfig');
262
90
  content = ensureValidatorSchema(content, 'authEnvSchema');
263
91
 
264
92
  if (!content.includes('ForgeonAuthModule.register(')) {
265
- const moduleBlock = withPrismaStore
266
- ? ` ForgeonAuthModule.register({
267
- imports: [DbPrismaModule],
268
- refreshTokenStoreProvider: {
269
- provide: AUTH_REFRESH_TOKEN_STORE,
270
- useClass: PrismaAuthRefreshTokenStore,
271
- },
272
- }),`
273
- : ` ForgeonAuthModule.register(),`;
93
+ const moduleBlock = ' ForgeonAuthModule.register(),';
274
94
 
275
95
  if (content.includes(' ForgeonI18nModule.register({')) {
276
96
  content = ensureLineBefore(content, ' ForgeonI18nModule.register({', moduleBlock);
@@ -297,6 +117,7 @@ function patchHealthController(targetRoot) {
297
117
  let content = fs.readFileSync(filePath, 'utf8').replace(/\r\n/g, '\n');
298
118
 
299
119
  if (!content.includes("from '@forgeon/auth-api';")) {
120
+ const nestCommonImport = content.match(/import\s*\{[^}]*\}\s*from '@nestjs\/common';/m)?.[0];
300
121
  if (content.includes("import { PrismaService } from '@forgeon/db-prisma';")) {
301
122
  content = ensureLineAfter(
302
123
  content,
@@ -306,7 +127,7 @@ function patchHealthController(targetRoot) {
306
127
  } else {
307
128
  content = ensureLineAfter(
308
129
  content,
309
- "import { BadRequestException, ConflictException, Controller, Get, Post, Query } from '@nestjs/common';",
130
+ nestCommonImport ?? "import { Controller, Get } from '@nestjs/common';",
310
131
  "import { AuthService } from '@forgeon/auth-api';",
311
132
  );
312
133
  }
@@ -323,6 +144,16 @@ function patchHealthController(targetRoot) {
323
144
  private readonly authService: AuthService,
324
145
  ) {`;
325
146
  content = content.replace(original, next);
147
+ } else {
148
+ const classAnchor = 'export class HealthController {';
149
+ if (content.includes(classAnchor)) {
150
+ content = content.replace(
151
+ classAnchor,
152
+ `${classAnchor}
153
+ constructor(private readonly authService: AuthService) {}
154
+ `,
155
+ );
156
+ }
326
157
  }
327
158
  }
328
159
 
@@ -339,7 +170,12 @@ function patchHealthController(targetRoot) {
339
170
  const index = content.indexOf('private translate(');
340
171
  content = `${content.slice(0, index).trimEnd()}\n\n${method}\n${content.slice(index)}`;
341
172
  } else {
342
- content = `${content.trimEnd()}\n${method}\n`;
173
+ const classEnd = content.lastIndexOf('\n}');
174
+ if (classEnd >= 0) {
175
+ content = `${content.slice(0, classEnd).trimEnd()}\n\n${method}\n${content.slice(classEnd)}`;
176
+ } else {
177
+ content = `${content.trimEnd()}\n${method}\n`;
178
+ }
343
179
  }
344
180
  }
345
181
 
@@ -488,22 +324,17 @@ function patchCompose(targetRoot) {
488
324
  fs.writeFileSync(composePath, `${content.trimEnd()}\n`, 'utf8');
489
325
  }
490
326
 
491
- function patchReadme(targetRoot, dbAdapter) {
327
+ function patchReadme(targetRoot) {
492
328
  const readmePath = path.join(targetRoot, 'README.md');
493
329
  if (!fs.existsSync(readmePath)) {
494
330
  return;
495
331
  }
496
332
 
497
333
  const persistenceSummary =
498
- dbAdapter?.supported && dbAdapter.id === 'db-prisma'
499
- ? '- refresh token persistence: enabled (`db-prisma` adapter)'
500
- : '- refresh token persistence: disabled (no supported DB adapter found)';
501
- const dbFollowUp =
502
- dbAdapter?.supported && dbAdapter.id === 'db-prisma'
503
- ? '- migration: `apps/api/prisma/migrations/0002_auth_refresh_token_hash`'
504
- : `- to enable persistence later:
334
+ '- refresh token persistence: disabled by default (stateless mode)';
335
+ const dbFollowUp = `- to enable persistence later:
505
336
  1. install a DB module first (for now: \`create-forgeon add db-prisma --project .\`);
506
- 2. run \`create-forgeon add jwt-auth --project .\` again to auto-wire the adapter.`;
337
+ 2. run \`pnpm forgeon:sync-integrations\` to auto-wire pair integrations.`;
507
338
 
508
339
  const section = `## JWT Auth Module
509
340
 
@@ -547,86 +378,17 @@ Default routes:
547
378
  fs.writeFileSync(readmePath, `${content.trimEnd()}\n`, 'utf8');
548
379
  }
549
380
 
550
- function patchPrismaSchema(targetRoot) {
551
- const schemaPath = path.join(targetRoot, 'apps', 'api', 'prisma', 'schema.prisma');
552
- if (!fs.existsSync(schemaPath)) {
553
- return;
554
- }
555
-
556
- let content = fs.readFileSync(schemaPath, 'utf8').replace(/\r\n/g, '\n');
557
- if (!content.includes('refreshTokenHash')) {
558
- content = content.replace(
559
- /email\s+String\s+@unique/g,
560
- 'email String @unique\n refreshTokenHash String?',
561
- );
562
- fs.writeFileSync(schemaPath, `${content.trimEnd()}\n`, 'utf8');
563
- }
564
- }
565
-
566
- function patchPrismaMigration(packageRoot, targetRoot) {
567
- const migrationSource = path.join(
568
- packageRoot,
569
- 'templates',
570
- 'module-presets',
571
- 'jwt-auth',
572
- 'apps',
573
- 'api',
574
- 'prisma',
575
- 'migrations',
576
- '0002_auth_refresh_token_hash',
577
- );
578
- const migrationTarget = path.join(
579
- targetRoot,
580
- 'apps',
581
- 'api',
582
- 'prisma',
583
- 'migrations',
584
- '0002_auth_refresh_token_hash',
585
- );
586
-
587
- if (!fs.existsSync(migrationTarget) && fs.existsSync(migrationSource)) {
588
- copyRecursive(migrationSource, migrationTarget);
589
- }
590
- }
591
-
592
381
  export function applyJwtAuthModule({ packageRoot, targetRoot }) {
593
- const dbAdapter = detectDbAdapter(targetRoot);
594
- const supportsPrismaStore = dbAdapter?.supported === true && dbAdapter?.id === 'db-prisma';
595
-
596
382
  copyFromPreset(packageRoot, targetRoot, path.join('packages', 'auth-contracts'));
597
383
  copyFromPreset(packageRoot, targetRoot, path.join('packages', 'auth-api'));
598
384
 
599
- const swaggerPackagePath = path.join(targetRoot, 'packages', 'swagger', 'package.json');
600
- const authApiPackagePath = path.join(targetRoot, 'packages', 'auth-api', 'package.json');
601
- if (fs.existsSync(swaggerPackagePath) && fs.existsSync(authApiPackagePath)) {
602
- const authApiPackage = JSON.parse(fs.readFileSync(authApiPackagePath, 'utf8'));
603
- ensureDependency(authApiPackage, '@nestjs/swagger', '^11.2.0');
604
- writeJson(authApiPackagePath, authApiPackage);
605
- }
606
-
607
- if (supportsPrismaStore) {
608
- copyFromPreset(
609
- packageRoot,
610
- targetRoot,
611
- path.join('apps', 'api', 'src', 'auth', 'prisma-auth-refresh-token.store.ts'),
612
- );
613
- patchPrismaSchema(targetRoot);
614
- patchPrismaMigration(packageRoot, targetRoot);
615
- } else {
616
- const detected = dbAdapter?.id ? `detected: ${dbAdapter.id}` : 'no DB adapter detected';
617
- printDbWarning(
618
- `jwt-auth installed without persistent refresh token store (${detected}). ` +
619
- 'Login/refresh works in stateless mode. Re-run add after supported DB module is installed.',
620
- );
621
- }
622
-
623
385
  patchApiPackage(targetRoot);
624
- patchAppModule(targetRoot, dbAdapter);
386
+ patchAppModule(targetRoot);
625
387
  patchHealthController(targetRoot);
626
388
  patchWebApp(targetRoot);
627
389
  patchApiDockerfile(targetRoot);
628
390
  patchCompose(targetRoot);
629
- patchReadme(targetRoot, dbAdapter);
391
+ patchReadme(targetRoot);
630
392
 
631
393
  upsertEnvLines(path.join(targetRoot, 'apps', 'api', '.env.example'), [
632
394
  'JWT_ACCESS_SECRET=forgeon-access-secret-change-me',