create-forgeon 0.3.1 → 0.3.2
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.
- package/package.json +1 -1
- package/src/integrations/flow.mjs +1 -1
- package/src/modules/dependencies.test.mjs +3 -3
- package/src/modules/executor.test.mjs +38 -0
- package/src/modules/i18n.mjs +45 -0
- package/src/modules/jwt-auth.mjs +3 -3
- package/src/modules/logger.mjs +3 -0
- package/src/modules/rate-limit.mjs +3 -2
- package/src/modules/rbac.mjs +6 -0
- package/src/modules/registry.mjs +16 -9
- package/src/modules/swagger.mjs +3 -0
- package/src/modules/sync-integrations.mjs +5 -5
- package/templates/base/README.md +1 -1
- package/templates/base/docs/AI/ARCHITECTURE.md +1 -1
- package/templates/base/scripts/forgeon-sync-integrations.mjs +5 -5
- package/templates/module-fragments/i18n/10_overview.md +5 -0
- package/templates/module-fragments/i18n/20_scope.md +6 -0
- package/templates/module-fragments/i18n/90_status_implemented.md +5 -0
- package/templates/module-fragments/jwt-auth/20_scope.md +4 -2
- package/templates/module-fragments/jwt-auth/90_status_implemented.md +3 -2
- package/templates/module-fragments/logger/10_overview.md +4 -0
- package/templates/module-fragments/logger/20_scope.md +5 -0
- package/templates/module-fragments/logger/90_status_implemented.md +3 -0
- package/templates/module-fragments/rate-limit/20_idea.md +1 -0
- package/templates/module-fragments/rate-limit/30_what_it_adds.md +2 -0
- package/templates/module-fragments/rate-limit/50_how_to_use.md +2 -0
- package/templates/module-fragments/rbac/30_what_it_adds.md +5 -0
- package/templates/module-fragments/rbac/40_how_it_works.md +5 -0
- package/templates/module-fragments/rbac/50_how_to_use.md +5 -0
- package/templates/module-fragments/swagger/10_overview.md +4 -0
- package/templates/module-fragments/swagger/20_scope.md +4 -0
- package/templates/module-fragments/swagger/90_status_implemented.md +3 -0
package/package.json
CHANGED
|
@@ -125,7 +125,7 @@ export function printOptionalIntegrationsWarning(integrations) {
|
|
|
125
125
|
for (const integration of integrations) {
|
|
126
126
|
console.log(`\n${colorize('yellow', 'Warning: optional integration available')}`);
|
|
127
127
|
console.log(`- ${integration.title}`);
|
|
128
|
-
console.log('Modules:');
|
|
128
|
+
console.log('Modules / capabilities:');
|
|
129
129
|
for (const moduleId of integration.modules ?? []) {
|
|
130
130
|
console.log(`- ${colorize('cyan', moduleId)}`);
|
|
131
131
|
}
|
|
@@ -44,8 +44,8 @@ const TEST_PRESETS = [
|
|
|
44
44
|
{
|
|
45
45
|
id: 'auth-persistence',
|
|
46
46
|
title: 'Auth Persistence Integration',
|
|
47
|
-
modules: ['jwt-auth', 'db-
|
|
48
|
-
requires: [{ type: '
|
|
47
|
+
modules: ['jwt-auth', 'db-adapter'],
|
|
48
|
+
requires: [{ type: 'capability', id: 'db-adapter' }],
|
|
49
49
|
description: ['Persist refresh-token state'],
|
|
50
50
|
followUpCommands: [
|
|
51
51
|
'npx create-forgeon@latest add db-prisma',
|
|
@@ -130,7 +130,7 @@ describe('module dependency helpers', () => {
|
|
|
130
130
|
|
|
131
131
|
assert.equal(pending.length, 1);
|
|
132
132
|
assert.equal(pending[0].id, 'auth-persistence');
|
|
133
|
-
assert.equal(pending[0].missing[0].id, 'db-
|
|
133
|
+
assert.equal(pending[0].missing[0].id, 'db-adapter');
|
|
134
134
|
} finally {
|
|
135
135
|
fs.rmSync(targetRoot, { recursive: true, force: true });
|
|
136
136
|
}
|
|
@@ -79,6 +79,8 @@ function assertRateLimitWiring(projectRoot) {
|
|
|
79
79
|
|
|
80
80
|
const readme = fs.readFileSync(path.join(projectRoot, 'README.md'), 'utf8');
|
|
81
81
|
assert.match(readme, /## Rate Limit Module/);
|
|
82
|
+
assert.match(readme, /installs independently/i);
|
|
83
|
+
assert.match(readme, /no optional integration sync is required/i);
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
function assertRbacWiring(projectRoot) {
|
|
@@ -110,6 +112,8 @@ function assertRbacWiring(projectRoot) {
|
|
|
110
112
|
|
|
111
113
|
const readme = fs.readFileSync(path.join(projectRoot, 'README.md'), 'utf8');
|
|
112
114
|
assert.match(readme, /## RBAC \/ Permissions Module/);
|
|
115
|
+
assert.match(readme, /installs independently/i);
|
|
116
|
+
assert.match(readme, /jwt-auth.*optional/i);
|
|
113
117
|
}
|
|
114
118
|
|
|
115
119
|
function assertJwtAuthWiring(projectRoot, withPrismaStore) {
|
|
@@ -476,6 +480,13 @@ describe('addModule', () => {
|
|
|
476
480
|
assert.match(rootPackage, /"i18n:types"/);
|
|
477
481
|
assert.match(rootPackage, /"i18n:add"/);
|
|
478
482
|
|
|
483
|
+
const rootReadme = fs.readFileSync(path.join(projectRoot, 'README.md'), 'utf8');
|
|
484
|
+
assert.match(rootReadme, /## I18n Module/);
|
|
485
|
+
assert.match(rootReadme, /installs independently/i);
|
|
486
|
+
assert.match(rootReadme, /multi-package split/i);
|
|
487
|
+
assert.match(rootReadme, /pnpm i18n:sync/);
|
|
488
|
+
assert.match(rootReadme, /pnpm i18n:add <locale>/);
|
|
489
|
+
|
|
479
490
|
const i18nAddScriptPath = path.join(projectRoot, 'scripts', 'i18n-add.mjs');
|
|
480
491
|
assert.equal(fs.existsSync(i18nAddScriptPath), true);
|
|
481
492
|
const syncScriptPath = path.join(projectRoot, 'scripts', 'forgeon-sync-integrations.mjs');
|
|
@@ -505,6 +516,11 @@ describe('addModule', () => {
|
|
|
505
516
|
assert.match(apiDockerfile, /RUN pnpm --filter @forgeon\/core build/);
|
|
506
517
|
assert.match(apiDockerfile, /RUN pnpm --filter @forgeon\/db-prisma build/);
|
|
507
518
|
assert.match(apiDockerfile, /COPY packages\/db-prisma\/package\.json packages\/db-prisma\/package\.json/);
|
|
519
|
+
|
|
520
|
+
const moduleDoc = fs.readFileSync(result.docsPath, 'utf8');
|
|
521
|
+
assert.match(moduleDoc, /I18n/);
|
|
522
|
+
assert.match(moduleDoc, /installs independently/i);
|
|
523
|
+
assert.match(moduleDoc, /helper commands are part of the module surface/i);
|
|
508
524
|
} finally {
|
|
509
525
|
fs.rmSync(targetRoot, { recursive: true, force: true });
|
|
510
526
|
}
|
|
@@ -577,6 +593,15 @@ describe('addModule', () => {
|
|
|
577
593
|
assert.match(apiEnv, /LOGGER_HTTP_ENABLED=true/);
|
|
578
594
|
assert.match(apiEnv, /LOGGER_REQUEST_ID_HEADER=x-request-id/);
|
|
579
595
|
|
|
596
|
+
const healthController = fs.readFileSync(
|
|
597
|
+
path.join(projectRoot, 'apps', 'api', 'src', 'health', 'health.controller.ts'),
|
|
598
|
+
'utf8',
|
|
599
|
+
);
|
|
600
|
+
assert.doesNotMatch(healthController, /logger/i);
|
|
601
|
+
|
|
602
|
+
const appTsx = fs.readFileSync(path.join(projectRoot, 'apps', 'web', 'src', 'App.tsx'), 'utf8');
|
|
603
|
+
assert.doesNotMatch(appTsx, /Check logger/i);
|
|
604
|
+
|
|
580
605
|
const dockerEnv = fs.readFileSync(
|
|
581
606
|
path.join(projectRoot, 'infra', 'docker', '.env.example'),
|
|
582
607
|
'utf8',
|
|
@@ -592,12 +617,16 @@ describe('addModule', () => {
|
|
|
592
617
|
|
|
593
618
|
const rootReadme = fs.readFileSync(path.join(projectRoot, 'README.md'), 'utf8');
|
|
594
619
|
assert.match(rootReadme, /## Logger Module/);
|
|
620
|
+
assert.match(rootReadme, /installs independently/i);
|
|
621
|
+
assert.match(rootReadme, /does not add a dedicated API\/web probe/i);
|
|
595
622
|
assert.match(rootReadme, /LOGGER_LEVEL=log/);
|
|
623
|
+
assert.match(rootReadme, /stdout\/stderr/i);
|
|
596
624
|
assert.match(rootReadme, /docker compose logs api/);
|
|
597
625
|
|
|
598
626
|
const moduleDoc = fs.readFileSync(result.docsPath, 'utf8');
|
|
599
627
|
assert.match(moduleDoc, /Logger/);
|
|
600
628
|
assert.match(moduleDoc, /Status: implemented/);
|
|
629
|
+
assert.match(moduleDoc, /no dedicated probe is added by design/i);
|
|
601
630
|
} finally {
|
|
602
631
|
fs.rmSync(targetRoot, { recursive: true, force: true });
|
|
603
632
|
}
|
|
@@ -633,6 +662,8 @@ describe('addModule', () => {
|
|
|
633
662
|
const moduleDoc = fs.readFileSync(result.docsPath, 'utf8');
|
|
634
663
|
assert.match(moduleDoc, /## Idea \/ Why/);
|
|
635
664
|
assert.match(moduleDoc, /## Configuration/);
|
|
665
|
+
assert.match(moduleDoc, /installs independently/i);
|
|
666
|
+
assert.match(moduleDoc, /No follow-up integration sync is required/i);
|
|
636
667
|
} finally {
|
|
637
668
|
fs.rmSync(targetRoot, { recursive: true, force: true });
|
|
638
669
|
}
|
|
@@ -756,12 +787,15 @@ describe('addModule', () => {
|
|
|
756
787
|
|
|
757
788
|
const rootReadme = fs.readFileSync(path.join(projectRoot, 'README.md'), 'utf8');
|
|
758
789
|
assert.match(rootReadme, /## Swagger \/ OpenAPI Module/);
|
|
790
|
+
assert.match(rootReadme, /installs independently/i);
|
|
791
|
+
assert.match(rootReadme, /decorators.*manual/i);
|
|
759
792
|
assert.match(rootReadme, /SWAGGER_ENABLED=false/);
|
|
760
793
|
assert.match(rootReadme, /localhost:3000\/api\/docs/);
|
|
761
794
|
|
|
762
795
|
const moduleDoc = fs.readFileSync(result.docsPath, 'utf8');
|
|
763
796
|
assert.match(moduleDoc, /Swagger \/ OpenAPI/);
|
|
764
797
|
assert.match(moduleDoc, /Status: implemented/);
|
|
798
|
+
assert.match(moduleDoc, /feature-specific Swagger decorators remain manual/i);
|
|
765
799
|
} finally {
|
|
766
800
|
fs.rmSync(targetRoot, { recursive: true, force: true });
|
|
767
801
|
}
|
|
@@ -1098,10 +1132,13 @@ describe('addModule', () => {
|
|
|
1098
1132
|
|
|
1099
1133
|
const readme = fs.readFileSync(path.join(projectRoot, 'README.md'), 'utf8');
|
|
1100
1134
|
assert.match(readme, /refresh token persistence: enabled/);
|
|
1135
|
+
assert.match(readme, /db-adapter/);
|
|
1136
|
+
assert.match(readme, /current provider: `db-prisma`/);
|
|
1101
1137
|
assert.match(readme, /0002_auth_refresh_token_hash/);
|
|
1102
1138
|
|
|
1103
1139
|
const moduleDoc = fs.readFileSync(result.docsPath, 'utf8');
|
|
1104
1140
|
assert.match(moduleDoc, /Status: implemented/);
|
|
1141
|
+
assert.match(moduleDoc, /db-adapter/);
|
|
1105
1142
|
} finally {
|
|
1106
1143
|
fs.rmSync(targetRoot, { recursive: true, force: true });
|
|
1107
1144
|
}
|
|
@@ -1142,6 +1179,7 @@ describe('addModule', () => {
|
|
|
1142
1179
|
|
|
1143
1180
|
const readme = fs.readFileSync(path.join(projectRoot, 'README.md'), 'utf8');
|
|
1144
1181
|
assert.match(readme, /refresh token persistence: disabled/);
|
|
1182
|
+
assert.match(readme, /db-adapter/);
|
|
1145
1183
|
assert.match(readme, /create-forgeon add db-prisma/);
|
|
1146
1184
|
|
|
1147
1185
|
} finally {
|
package/src/modules/i18n.mjs
CHANGED
|
@@ -405,6 +405,50 @@ function patchRootPackage(targetRoot) {
|
|
|
405
405
|
writeJson(packagePath, packageJson);
|
|
406
406
|
}
|
|
407
407
|
|
|
408
|
+
function patchReadme(targetRoot) {
|
|
409
|
+
const readmePath = path.join(targetRoot, 'README.md');
|
|
410
|
+
if (!fs.existsSync(readmePath)) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const marker = '## I18n Module';
|
|
415
|
+
let content = fs.readFileSync(readmePath, 'utf8').replace(/\r\n/g, '\n');
|
|
416
|
+
if (content.includes(marker)) {
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const section = `## I18n Module
|
|
421
|
+
|
|
422
|
+
The i18n add-module provides a full backend + frontend localization baseline.
|
|
423
|
+
|
|
424
|
+
It installs independently and uses a multi-package split:
|
|
425
|
+
- \`@forgeon/i18n\` for Nest runtime wiring
|
|
426
|
+
- \`@forgeon/i18n-contracts\` for generated locale/namespace contracts and maintenance scripts
|
|
427
|
+
- \`@forgeon/i18n-web\` for React-side locale helpers
|
|
428
|
+
|
|
429
|
+
Shared dictionaries:
|
|
430
|
+
- stored under \`resources/i18n/<locale>/*.json\`
|
|
431
|
+
- current default locale set: \`en\`
|
|
432
|
+
|
|
433
|
+
Helper commands:
|
|
434
|
+
- \`pnpm i18n:sync\`
|
|
435
|
+
- \`pnpm i18n:check\`
|
|
436
|
+
- \`pnpm i18n:types\`
|
|
437
|
+
- \`pnpm i18n:add <locale>\`
|
|
438
|
+
|
|
439
|
+
Module env:
|
|
440
|
+
- \`I18N_DEFAULT_LANG=en\`
|
|
441
|
+
- \`I18N_FALLBACK_LANG=en\``;
|
|
442
|
+
|
|
443
|
+
if (content.includes('## Prisma In Docker Start')) {
|
|
444
|
+
content = content.replace('## Prisma In Docker Start', `${section}\n\n## Prisma In Docker Start`);
|
|
445
|
+
} else {
|
|
446
|
+
content = `${content.trimEnd()}\n\n${section}\n`;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
fs.writeFileSync(readmePath, `${content.trimEnd()}\n`, 'utf8');
|
|
450
|
+
}
|
|
451
|
+
|
|
408
452
|
function restoreKnownWebProbes(targetRoot, previousAppContent) {
|
|
409
453
|
if (!previousAppContent) {
|
|
410
454
|
return;
|
|
@@ -533,6 +577,7 @@ export function applyI18nModule({ packageRoot, targetRoot }) {
|
|
|
533
577
|
patchApiPackage(targetRoot);
|
|
534
578
|
patchWebPackage(targetRoot);
|
|
535
579
|
patchRootPackage(targetRoot);
|
|
580
|
+
patchReadme(targetRoot);
|
|
536
581
|
patchAppModule(targetRoot);
|
|
537
582
|
patchHealthController(targetRoot);
|
|
538
583
|
patchApiDockerfile(targetRoot);
|
package/src/modules/jwt-auth.mjs
CHANGED
|
@@ -309,10 +309,10 @@ function patchReadme(targetRoot) {
|
|
|
309
309
|
}
|
|
310
310
|
|
|
311
311
|
const persistenceSummary =
|
|
312
|
-
'- refresh token persistence: disabled by default (stateless mode)';
|
|
312
|
+
'- refresh token persistence: disabled by default (stateless mode; enable it later through a `db-adapter` provider + integration sync)';
|
|
313
313
|
const dbFollowUp = `- to enable persistence later:
|
|
314
|
-
1. install a DB
|
|
315
|
-
2. run \`pnpm forgeon:sync-integrations\` to
|
|
314
|
+
1. install a DB adapter provider first (current provider: \`create-forgeon add db-prisma --project .\`);
|
|
315
|
+
2. run \`pnpm forgeon:sync-integrations\` to wire auth persistence to the active DB adapter implementation.`;
|
|
316
316
|
|
|
317
317
|
const section = `## JWT Auth Module
|
|
318
318
|
|
package/src/modules/logger.mjs
CHANGED
|
@@ -181,6 +181,9 @@ The logger add-module provides:
|
|
|
181
181
|
- HTTP access logs with method/path/status/duration/ip/requestId
|
|
182
182
|
- Nest logger integration via \`app.useLogger(...)\`
|
|
183
183
|
|
|
184
|
+
It installs independently and intentionally does not add a dedicated API/web probe.
|
|
185
|
+
Its verification path is operational: inspect API stdout/stderr in dev or container logs in Docker.
|
|
186
|
+
|
|
184
187
|
Configuration (env):
|
|
185
188
|
- \`LOGGER_LEVEL=log\` (\`error|warn|log|debug|verbose\`)
|
|
186
189
|
- \`LOGGER_HTTP_ENABLED=true\`
|
|
@@ -289,7 +289,7 @@ function patchReadme(targetRoot) {
|
|
|
289
289
|
|
|
290
290
|
const section = `## Rate Limit Module
|
|
291
291
|
|
|
292
|
-
The rate-limit add-module provides a simple first-line safeguard against burst traffic, accidental request loops, and brute-force style abuse.
|
|
292
|
+
The rate-limit add-module installs independently and provides a simple first-line safeguard against burst traffic, accidental request loops, and brute-force style abuse.
|
|
293
293
|
|
|
294
294
|
What it adds:
|
|
295
295
|
- global request throttling for the API
|
|
@@ -310,7 +310,8 @@ Configuration (env):
|
|
|
310
310
|
|
|
311
311
|
Operational notes:
|
|
312
312
|
- \`THROTTLE_TRUST_PROXY=true\` is recommended behind reverse proxies
|
|
313
|
-
- this is an in-memory throttle preset, not a distributed limiter
|
|
313
|
+
- this is an in-memory throttle preset, not a distributed limiter
|
|
314
|
+
- no optional integration sync is required for this module in the current scaffold`;
|
|
314
315
|
|
|
315
316
|
if (content.includes('## Prisma In Docker Start')) {
|
|
316
317
|
content = content.replace('## Prisma In Docker Start', `${section}\n\n## Prisma In Docker Start`);
|
package/src/modules/rbac.mjs
CHANGED
|
@@ -282,6 +282,8 @@ function patchReadme(targetRoot) {
|
|
|
282
282
|
|
|
283
283
|
The rbac add-module provides a minimal authorization layer for role and permission checks.
|
|
284
284
|
|
|
285
|
+
It installs independently. \`jwt-auth\` is optional and only extends demo JWT claims through integration sync.
|
|
286
|
+
|
|
285
287
|
What it adds:
|
|
286
288
|
- \`@Roles(...)\` and \`@Permissions(...)\` decorators
|
|
287
289
|
- \`ForgeonRbacGuard\`
|
|
@@ -299,6 +301,10 @@ How to verify:
|
|
|
299
301
|
- the generated frontend button sends \`x-forgeon-permissions: health.rbac\` and should return \`200\`
|
|
300
302
|
- the same route without that header should return \`403\`
|
|
301
303
|
|
|
304
|
+
Optional integration:
|
|
305
|
+
- if \`jwt-auth\` is installed too, run \`pnpm forgeon:sync-integrations\`
|
|
306
|
+
- that enables demo JWT permissions for the same RBAC probe flow
|
|
307
|
+
|
|
302
308
|
Current scope:
|
|
303
309
|
- no policy engine
|
|
304
310
|
- no database-backed role store
|
package/src/modules/registry.mjs
CHANGED
|
@@ -16,7 +16,8 @@ const MODULE_PRESETS = {
|
|
|
16
16
|
label: 'I18n',
|
|
17
17
|
category: 'localization',
|
|
18
18
|
implemented: true,
|
|
19
|
-
description:
|
|
19
|
+
description:
|
|
20
|
+
'Independent backend/frontend i18n wiring with contracts, web helpers, shared translation resources, and locale maintenance scripts.',
|
|
20
21
|
detectionPaths: ['packages/i18n/package.json'],
|
|
21
22
|
provides: ['i18n-runtime'],
|
|
22
23
|
requires: [],
|
|
@@ -28,7 +29,8 @@ const MODULE_PRESETS = {
|
|
|
28
29
|
label: 'Logger',
|
|
29
30
|
category: 'observability',
|
|
30
31
|
implemented: true,
|
|
31
|
-
description:
|
|
32
|
+
description:
|
|
33
|
+
'Independent structured API logger with request id middleware and HTTP logging interceptor; intentionally no dedicated runtime probe.',
|
|
32
34
|
detectionPaths: ['packages/logger/package.json'],
|
|
33
35
|
provides: ['logger-runtime'],
|
|
34
36
|
requires: [],
|
|
@@ -40,7 +42,8 @@ const MODULE_PRESETS = {
|
|
|
40
42
|
label: 'Swagger / OpenAPI',
|
|
41
43
|
category: 'api-documentation',
|
|
42
44
|
implemented: true,
|
|
43
|
-
description:
|
|
45
|
+
description:
|
|
46
|
+
'Independent OpenAPI docs setup with env-based toggle and route path; feature-level Swagger decorators remain manual.',
|
|
44
47
|
detectionPaths: ['packages/swagger/package.json'],
|
|
45
48
|
provides: ['openapi-runtime'],
|
|
46
49
|
requires: [],
|
|
@@ -52,7 +55,8 @@ const MODULE_PRESETS = {
|
|
|
52
55
|
label: 'JWT Auth',
|
|
53
56
|
category: 'auth-security',
|
|
54
57
|
implemented: true,
|
|
55
|
-
description:
|
|
58
|
+
description:
|
|
59
|
+
'JWT auth preset with contracts/api module split, guard+strategy, and optional db-adapter-backed refresh token persistence via integration sync.',
|
|
56
60
|
detectionPaths: ['packages/auth-api/package.json'],
|
|
57
61
|
provides: ['auth-runtime'],
|
|
58
62
|
requires: [],
|
|
@@ -60,10 +64,11 @@ const MODULE_PRESETS = {
|
|
|
60
64
|
{
|
|
61
65
|
id: 'auth-persistence',
|
|
62
66
|
title: 'Auth Persistence Integration',
|
|
63
|
-
modules: ['jwt-auth', 'db-
|
|
64
|
-
requires: [{ type: '
|
|
67
|
+
modules: ['jwt-auth', 'db-adapter'],
|
|
68
|
+
requires: [{ type: 'capability', id: 'db-adapter' }],
|
|
65
69
|
description: [
|
|
66
|
-
'Persist refresh-token state through the
|
|
70
|
+
'Persist refresh-token state through the db-adapter capability boundary',
|
|
71
|
+
'Use the current DB adapter implementation (today: db-prisma) for refresh-token storage',
|
|
67
72
|
'Enable stronger refresh-token invalidation flows after logout and rotation',
|
|
68
73
|
],
|
|
69
74
|
followUpCommands: [
|
|
@@ -93,7 +98,8 @@ const MODULE_PRESETS = {
|
|
|
93
98
|
label: 'Rate Limit',
|
|
94
99
|
category: 'auth-security',
|
|
95
100
|
implemented: true,
|
|
96
|
-
description:
|
|
101
|
+
description:
|
|
102
|
+
'Independent request throttling preset with env-based limits, proxy-aware trust, and a runtime probe endpoint.',
|
|
97
103
|
detectionPaths: ['packages/rate-limit/package.json'],
|
|
98
104
|
provides: ['rate-limit-runtime'],
|
|
99
105
|
requires: [],
|
|
@@ -115,7 +121,8 @@ const MODULE_PRESETS = {
|
|
|
115
121
|
label: 'RBAC / Permissions',
|
|
116
122
|
category: 'auth-security',
|
|
117
123
|
implemented: true,
|
|
118
|
-
description:
|
|
124
|
+
description:
|
|
125
|
+
'Role and permission decorators with a Nest guard and a protected probe endpoint; installs independently and optionally integrates with jwt-auth.',
|
|
119
126
|
detectionPaths: ['packages/rbac/package.json'],
|
|
120
127
|
provides: ['rbac-runtime'],
|
|
121
128
|
requires: [],
|
package/src/modules/swagger.mjs
CHANGED
|
@@ -181,6 +181,9 @@ function patchReadme(targetRoot) {
|
|
|
181
181
|
|
|
182
182
|
The swagger add-module provides generated OpenAPI docs for the API.
|
|
183
183
|
|
|
184
|
+
It installs independently and only wires the OpenAPI runtime.
|
|
185
|
+
Feature-specific Swagger decorators (for example \`@ApiOperation\`, \`@ApiBody\`, \`@ApiResponse\`) remain manual and are not auto-patched into other modules.
|
|
186
|
+
|
|
184
187
|
Configuration (env):
|
|
185
188
|
- \`SWAGGER_ENABLED=false\`
|
|
186
189
|
- \`SWAGGER_PATH=docs\`
|
|
@@ -97,10 +97,10 @@ const INTEGRATION_GROUPS = [
|
|
|
97
97
|
title: 'Auth Persistence Integration',
|
|
98
98
|
modules: ['jwt-auth', 'db-prisma'],
|
|
99
99
|
description: [
|
|
100
|
-
'Patch AppModule to wire AUTH_REFRESH_TOKEN_STORE
|
|
100
|
+
'Patch AppModule to wire AUTH_REFRESH_TOKEN_STORE to the current db-adapter implementation (today: PrismaAuthRefreshTokenStore)',
|
|
101
101
|
'Add apps/api/src/auth/prisma-auth-refresh-token.store.ts',
|
|
102
102
|
'Extend Prisma User model with refreshTokenHash and add migration 0002_auth_refresh_token_hash',
|
|
103
|
-
'Update JWT auth README note
|
|
103
|
+
'Update JWT auth README note to reflect db-adapter-backed refresh-token persistence',
|
|
104
104
|
],
|
|
105
105
|
isAvailable: (detected) => detected.jwtAuth && detected.dbPrisma,
|
|
106
106
|
isPending: (rootDir) => isAuthPersistencePending(rootDir),
|
|
@@ -236,11 +236,11 @@ function syncJwtDbPrisma({ rootDir, packageRoot, changedFiles }) {
|
|
|
236
236
|
let readme = fs.readFileSync(readmePath, 'utf8').replace(/\r\n/g, '\n');
|
|
237
237
|
const originalReadme = readme;
|
|
238
238
|
readme = readme.replace(
|
|
239
|
-
'- refresh token persistence: disabled by default (stateless mode)',
|
|
240
|
-
'- refresh token persistence: enabled (`db-prisma`
|
|
239
|
+
'- refresh token persistence: disabled by default (stateless mode; enable it later through a `db-adapter` provider + integration sync)',
|
|
240
|
+
'- refresh token persistence: enabled through the `db-adapter` capability (current provider: `db-prisma`)',
|
|
241
241
|
);
|
|
242
242
|
readme = readme.replace(
|
|
243
|
-
/- to enable persistence later:[\s\S]*?2\. run `pnpm forgeon:sync-integrations` to
|
|
243
|
+
/- to enable persistence later:[\s\S]*?2\. run `pnpm forgeon:sync-integrations` to wire auth persistence to the active DB adapter implementation\./m,
|
|
244
244
|
'- migration: `apps/api/prisma/migrations/0002_auth_refresh_token_hash`',
|
|
245
245
|
);
|
|
246
246
|
if (readme !== originalReadme) {
|
package/templates/base/README.md
CHANGED
|
@@ -38,7 +38,7 @@ pnpm forgeon:sync-integrations
|
|
|
38
38
|
|
|
39
39
|
Current sync coverage:
|
|
40
40
|
- `jwt-auth + rbac`: extends demo auth tokens with the `health.rbac` permission.
|
|
41
|
-
- `jwt-auth + db-prisma
|
|
41
|
+
- `jwt-auth + db-adapter` (current provider: `db-prisma`): wires persistent refresh-token storage for auth.
|
|
42
42
|
|
|
43
43
|
`create-forgeon add <module>` scans for relevant integration groups and can apply them immediately.
|
|
44
44
|
|
|
@@ -57,7 +57,7 @@ Reference: `docs/AI/MODULE_SPEC.md`.
|
|
|
57
57
|
- each add-module patches only itself;
|
|
58
58
|
- cross-module changes are allowed only in integration sync rules.
|
|
59
59
|
- Current integration:
|
|
60
|
-
- `jwt-auth + db-
|
|
60
|
+
- `jwt-auth + db-adapter` (current provider: `db-prisma`; persistent refresh-token store wiring + schema/migration sync).
|
|
61
61
|
- Pair sync is explicit (opt-in), not automatic after `add`.
|
|
62
62
|
- Run `pnpm forgeon:sync-integrations` when you want to apply module-pair integrations.
|
|
63
63
|
- Swagger auth decorators are intentionally not auto-patched.
|
|
@@ -162,11 +162,11 @@ function syncJwtDbPrisma({ rootDir, changedFiles }) {
|
|
|
162
162
|
let readme = fs.readFileSync(readmePath, 'utf8').replace(/\r\n/g, '\n');
|
|
163
163
|
const originalReadme = readme;
|
|
164
164
|
readme = readme.replace(
|
|
165
|
-
'- refresh token persistence: disabled by default (stateless mode)',
|
|
166
|
-
'- refresh token persistence: enabled (`db-prisma`
|
|
165
|
+
'- refresh token persistence: disabled by default (stateless mode; enable it later through a `db-adapter` provider + integration sync)',
|
|
166
|
+
'- refresh token persistence: enabled through the `db-adapter` capability (current provider: `db-prisma`)',
|
|
167
167
|
);
|
|
168
168
|
readme = readme.replace(
|
|
169
|
-
/- to enable persistence later:[\s\S]*?2\. run `pnpm forgeon:sync-integrations` to
|
|
169
|
+
/- to enable persistence later:[\s\S]*?2\. run `pnpm forgeon:sync-integrations` to wire auth persistence to the active DB adapter implementation\./m,
|
|
170
170
|
'- migration: `apps/api/prisma/migrations/0002_auth_refresh_token_hash`',
|
|
171
171
|
);
|
|
172
172
|
if (readme !== originalReadme) {
|
|
@@ -293,12 +293,12 @@ function run() {
|
|
|
293
293
|
|
|
294
294
|
if (detected.jwtAuth && detected.dbPrisma) {
|
|
295
295
|
summary.push({
|
|
296
|
-
feature: 'jwt-auth + db-prisma',
|
|
296
|
+
feature: 'jwt-auth + db-adapter (current provider: db-prisma)',
|
|
297
297
|
result: syncJwtDbPrisma({ rootDir, changedFiles }),
|
|
298
298
|
});
|
|
299
299
|
} else {
|
|
300
300
|
summary.push({
|
|
301
|
-
feature: 'jwt-auth + db-prisma',
|
|
301
|
+
feature: 'jwt-auth + db-adapter (current provider: db-prisma)',
|
|
302
302
|
result: { applied: false, reason: 'required modules are not both installed' },
|
|
303
303
|
});
|
|
304
304
|
}
|
|
@@ -10,6 +10,11 @@ Included parts:
|
|
|
10
10
|
- shared dictionaries in `resources/i18n/*` (`en` by default) used by both API and web
|
|
11
11
|
- default namespaces: `common`, `errors`, `validation`, `ui`, `notifications`, `meta`
|
|
12
12
|
|
|
13
|
+
Important boundary:
|
|
14
|
+
- this module installs independently
|
|
15
|
+
- it is intentionally split across runtime, contracts, and web helper packages
|
|
16
|
+
- translation maintenance flows are part of the module contract, not ad-hoc project scripts
|
|
17
|
+
|
|
13
18
|
Utility commands:
|
|
14
19
|
- `pnpm i18n:sync` - regenerate `I18N_LOCALES` and `I18N_NAMESPACES` from `resources/i18n`.
|
|
15
20
|
- `pnpm i18n:check` - verify generated contracts, JSON validity, and missing/extra keys vs fallback locale.
|
|
@@ -10,3 +10,9 @@
|
|
|
10
10
|
- `i18n:sync` for locale/namespace contracts sync from `resources/i18n`
|
|
11
11
|
- `i18n:check` for contract/json/key consistency checks
|
|
12
12
|
- `i18n:types` for translation key type generation
|
|
13
|
+
- `i18n:add` for adding a new locale from the command line
|
|
14
|
+
|
|
15
|
+
Operational notes:
|
|
16
|
+
|
|
17
|
+
- this module owns the i18n helper commands
|
|
18
|
+
- it does not use integration sync groups today because its work is self-contained within the localization stack
|
|
@@ -10,8 +10,10 @@ Implemented scope:
|
|
|
10
10
|
- `JwtStrategy` + `JwtAuthGuard`
|
|
11
11
|
- `authConfig` + `authEnvSchema` wiring through root `ConfigModule` validator chain
|
|
12
12
|
3. DB behavior:
|
|
13
|
-
-
|
|
14
|
-
-
|
|
13
|
+
- module install stays stateless by default
|
|
14
|
+
- refresh token hash persistence is enabled later through the `db-adapter` capability via `pnpm forgeon:sync-integrations`
|
|
15
|
+
- current DB adapter implementation for this integration is `db-prisma`
|
|
16
|
+
- if no DB adapter is installed, the module stays stateless and prints an optional integration warning with follow-up commands
|
|
15
17
|
4. Module checks:
|
|
16
18
|
- API probe endpoint: `GET /api/health/auth`
|
|
17
19
|
- default web probe button + result block
|
|
@@ -3,5 +3,6 @@
|
|
|
3
3
|
Status: implemented.
|
|
4
4
|
|
|
5
5
|
Notes:
|
|
6
|
-
-
|
|
7
|
-
-
|
|
6
|
+
- The persistence boundary is `db-adapter`, not a hard dependency on one concrete DB module.
|
|
7
|
+
- The current DB adapter implementation for auth persistence is `db-prisma`.
|
|
8
|
+
- If no DB adapter is installed, jwt-auth stays in stateless refresh mode and surfaces an optional integration warning.
|
|
@@ -8,3 +8,7 @@ Included parts:
|
|
|
8
8
|
- HTTP logging interceptor for request/response timing
|
|
9
9
|
- env-driven logger config (`LOGGER_LEVEL`, `LOGGER_HTTP_ENABLED`, `LOGGER_REQUEST_ID_HEADER`)
|
|
10
10
|
|
|
11
|
+
Important boundary:
|
|
12
|
+
- this module installs independently
|
|
13
|
+
- it intentionally does not add a dedicated runtime probe
|
|
14
|
+
- verification is done through API process logs
|
|
@@ -9,3 +9,8 @@
|
|
|
9
9
|
- Adds logger env keys to `apps/api/.env.example` and `infra/docker/.env.example`
|
|
10
10
|
- Passes logger env keys through `infra/docker/compose.yml`
|
|
11
11
|
|
|
12
|
+
Intentional exception:
|
|
13
|
+
|
|
14
|
+
- no API `/api/health/*` probe is added
|
|
15
|
+
- no web diagnostics button is added
|
|
16
|
+
- this module is verified by observing structured logs
|
|
@@ -7,4 +7,6 @@
|
|
|
7
7
|
- `GET /api/health/rate-limit` probe endpoint
|
|
8
8
|
- frontend probe button on the generated home page
|
|
9
9
|
|
|
10
|
+
This module installs independently. In the current scaffold it does not depend on, or require sync with, any other add-module.
|
|
11
|
+
|
|
10
12
|
This module is API-first. It does not add shared contracts or a web package in v1 because the runtime value is in backend request throttling, not in reusable client-side types.
|
|
@@ -9,3 +9,8 @@
|
|
|
9
9
|
- a frontend probe button that sends a valid permission header
|
|
10
10
|
|
|
11
11
|
This module is backend-first. It does not include frontend route guards. If frontend access-control helpers are needed later, they should live in a separate module.
|
|
12
|
+
|
|
13
|
+
Optional integration:
|
|
14
|
+
|
|
15
|
+
- `jwt-auth` can extend demo JWT claims with RBAC permissions through `pnpm forgeon:sync-integrations`
|
|
16
|
+
- this module does not require `jwt-auth` to install or work for header-based/manual probe checks
|
|
@@ -18,3 +18,8 @@ Failure path:
|
|
|
18
18
|
|
|
19
19
|
- denied access throws `403`
|
|
20
20
|
- the existing Forgeon error envelope wraps it as `FORBIDDEN`
|
|
21
|
+
|
|
22
|
+
Integration note:
|
|
23
|
+
|
|
24
|
+
- if `jwt-auth` is also installed, the optional `auth-rbac-claims` integration can expose demo permissions inside JWT payloads
|
|
25
|
+
- that integration is explicit and is applied through `pnpm forgeon:sync-integrations`
|
|
@@ -17,3 +17,8 @@ Manual forbidden-path check:
|
|
|
17
17
|
|
|
18
18
|
1. call `GET /api/health/rbac` without the `x-forgeon-permissions` header
|
|
19
19
|
2. the request should return `403`
|
|
20
|
+
|
|
21
|
+
Optional follow-up:
|
|
22
|
+
|
|
23
|
+
1. install `jwt-auth` if you want RBAC claims in demo JWT payloads
|
|
24
|
+
2. run `pnpm forgeon:sync-integrations`
|
|
@@ -8,3 +8,7 @@ Included parts:
|
|
|
8
8
|
- configurable docs path/title/version
|
|
9
9
|
- `setupSwagger(...)` bootstrap helper
|
|
10
10
|
|
|
11
|
+
Important boundary:
|
|
12
|
+
- this module installs independently
|
|
13
|
+
- it wires only the OpenAPI runtime and bootstrap setup
|
|
14
|
+
- feature-specific Swagger decorators remain manual
|