create-forgeon 0.1.23 → 0.1.24
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/core/docs.mjs +12 -9
- package/src/core/docs.test.mjs +18 -14
- package/templates/docs-fragments/AI_ARCHITECTURE/23_error_handling.md +11 -0
- package/templates/docs-fragments/AI_PROJECT/34_error_handling.md +7 -0
- package/templates/docs-fragments/README/41_error_handling.md +27 -0
package/package.json
CHANGED
package/src/core/docs.mjs
CHANGED
|
@@ -64,11 +64,12 @@ export function generateDocs(targetRoot, options, packageRoot) {
|
|
|
64
64
|
} else {
|
|
65
65
|
readmeFragments.push('31_proxy_preset_none');
|
|
66
66
|
}
|
|
67
|
-
readmeFragments.push('32_prisma_container_start');
|
|
68
|
-
if (options.i18nEnabled) {
|
|
69
|
-
readmeFragments.push('40_i18n');
|
|
70
|
-
}
|
|
71
|
-
readmeFragments.push('
|
|
67
|
+
readmeFragments.push('32_prisma_container_start');
|
|
68
|
+
if (options.i18nEnabled) {
|
|
69
|
+
readmeFragments.push('40_i18n');
|
|
70
|
+
}
|
|
71
|
+
readmeFragments.push('41_error_handling');
|
|
72
|
+
readmeFragments.push('90_next_steps');
|
|
72
73
|
|
|
73
74
|
const aiProjectFragments = ['00_title', '10_what_is', '20_structure_base'];
|
|
74
75
|
if (options.i18nEnabled) {
|
|
@@ -80,10 +81,11 @@ export function generateDocs(targetRoot, options, packageRoot) {
|
|
|
80
81
|
} else {
|
|
81
82
|
aiProjectFragments.push('32_proxy_notes');
|
|
82
83
|
}
|
|
83
|
-
if (options.i18nEnabled) {
|
|
84
|
-
aiProjectFragments.push('33_i18n_notes');
|
|
85
|
-
}
|
|
86
|
-
aiProjectFragments.push('
|
|
84
|
+
if (options.i18nEnabled) {
|
|
85
|
+
aiProjectFragments.push('33_i18n_notes');
|
|
86
|
+
}
|
|
87
|
+
aiProjectFragments.push('34_error_handling');
|
|
88
|
+
aiProjectFragments.push('40_change_boundaries_base');
|
|
87
89
|
if (options.proxy !== 'none') {
|
|
88
90
|
aiProjectFragments.push('41_change_boundaries_docker');
|
|
89
91
|
}
|
|
@@ -97,6 +99,7 @@ export function generateDocs(targetRoot, options, packageRoot) {
|
|
|
97
99
|
aiArchitectureFragments.push('21_env_i18n');
|
|
98
100
|
}
|
|
99
101
|
aiArchitectureFragments.push('22_ts_module_policy');
|
|
102
|
+
aiArchitectureFragments.push('23_error_handling');
|
|
100
103
|
aiArchitectureFragments.push('30_default_db', '31_docker_runtime', '32_scope_freeze');
|
|
101
104
|
aiArchitectureFragments.push('40_docs_generation', '50_extension_points');
|
|
102
105
|
|
package/src/core/docs.test.mjs
CHANGED
|
@@ -38,14 +38,16 @@ describe('generateDocs', () => {
|
|
|
38
38
|
const projectDoc = readFile(path.join(targetRoot, 'docs', 'AI', 'PROJECT.md'));
|
|
39
39
|
const architectureDoc = readFile(path.join(targetRoot, 'docs', 'AI', 'ARCHITECTURE.md'));
|
|
40
40
|
|
|
41
|
-
assert.match(readme, /Docker\/infra: `enabled`/);
|
|
42
|
-
assert.match(readme, /Quick Start \(Docker\)/);
|
|
43
|
-
assert.match(readme, /Proxy Preset: none/);
|
|
44
|
-
assert.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
assert.match(projectDoc,
|
|
48
|
-
assert.
|
|
41
|
+
assert.match(readme, /Docker\/infra: `enabled`/);
|
|
42
|
+
assert.match(readme, /Quick Start \(Docker\)/);
|
|
43
|
+
assert.match(readme, /Proxy Preset: none/);
|
|
44
|
+
assert.match(readme, /Error Handling \(`core-errors`\)/);
|
|
45
|
+
assert.doesNotMatch(readme, /i18n Configuration/);
|
|
46
|
+
|
|
47
|
+
assert.match(projectDoc, /### Docker mode/);
|
|
48
|
+
assert.match(projectDoc, /Active proxy preset: `none`/);
|
|
49
|
+
assert.match(projectDoc, /CoreErrorsModule/);
|
|
50
|
+
assert.doesNotMatch(projectDoc, /packages\/i18n/);
|
|
49
51
|
|
|
50
52
|
assert.match(architectureDoc, /infra\/\*/);
|
|
51
53
|
assert.doesNotMatch(architectureDoc, /I18N_ENABLED/);
|
|
@@ -78,12 +80,14 @@ describe('generateDocs', () => {
|
|
|
78
80
|
const projectDoc = readFile(path.join(targetRoot, 'docs', 'AI', 'PROJECT.md'));
|
|
79
81
|
const architectureDoc = readFile(path.join(targetRoot, 'docs', 'AI', 'ARCHITECTURE.md'));
|
|
80
82
|
|
|
81
|
-
assert.match(readme, /Quick Start \(Docker\)/);
|
|
82
|
-
assert.match(readme, /Proxy Preset: Caddy/);
|
|
83
|
-
assert.match(readme, /i18n Configuration/);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
assert.match(projectDoc,
|
|
83
|
+
assert.match(readme, /Quick Start \(Docker\)/);
|
|
84
|
+
assert.match(readme, /Proxy Preset: Caddy/);
|
|
85
|
+
assert.match(readme, /i18n Configuration/);
|
|
86
|
+
assert.match(readme, /Error Handling \(`core-errors`\)/);
|
|
87
|
+
|
|
88
|
+
assert.match(projectDoc, /`infra` - Docker Compose \(always\) \+ proxy preset \(`caddy`\)/);
|
|
89
|
+
assert.match(projectDoc, /Main proxy config: `infra\/caddy\/Caddyfile`/);
|
|
90
|
+
assert.match(projectDoc, /CoreExceptionFilter/);
|
|
87
91
|
|
|
88
92
|
assert.match(architectureDoc, /infra\/\*/);
|
|
89
93
|
assert.match(architectureDoc, /I18N_DEFAULT_LANG/);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
## Error Handling Strategy
|
|
2
|
+
|
|
3
|
+
- `@forgeon/core` owns the global HTTP error envelope.
|
|
4
|
+
- API apps import `CoreErrorsModule` and register `CoreExceptionFilter` as a global filter.
|
|
5
|
+
- Envelope fields:
|
|
6
|
+
- `error.code`
|
|
7
|
+
- `error.message`
|
|
8
|
+
- `error.status`
|
|
9
|
+
- `error.details` (optional, mainly validation context)
|
|
10
|
+
- `error.requestId` (optional, derived from `x-request-id`)
|
|
11
|
+
- `error.timestamp`
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
### Error Handling
|
|
2
|
+
|
|
3
|
+
`core-errors` is enabled by default.
|
|
4
|
+
|
|
5
|
+
- `CoreErrorsModule` is imported in `apps/api/src/app.module.ts`.
|
|
6
|
+
- `CoreExceptionFilter` is registered globally in `apps/api/src/main.ts`.
|
|
7
|
+
- Controllers and services should throw standard Nest exceptions; envelope formatting is handled centrally.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
## Error Handling (`core-errors`)
|
|
2
|
+
|
|
3
|
+
`@forgeon/core` includes a default global exception filter (`CoreExceptionFilter`).
|
|
4
|
+
|
|
5
|
+
Wiring:
|
|
6
|
+
- module import: `apps/api/src/app.module.ts` (`CoreErrorsModule`)
|
|
7
|
+
- global registration: `apps/api/src/main.ts` (`app.useGlobalFilters(app.get(CoreExceptionFilter))`)
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
- throw standard Nest exceptions in services/controllers:
|
|
11
|
+
- `throw new ConflictException('Email already exists')`
|
|
12
|
+
- `throw new NotFoundException('Resource not found')`
|
|
13
|
+
|
|
14
|
+
Response envelope:
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"error": {
|
|
19
|
+
"code": "conflict",
|
|
20
|
+
"message": "Email already exists",
|
|
21
|
+
"status": 409,
|
|
22
|
+
"details": [],
|
|
23
|
+
"requestId": "optional",
|
|
24
|
+
"timestamp": "2026-02-25T12:00:00.000Z"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|