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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-forgeon",
3
- "version": "0.1.23",
3
+ "version": "0.1.24",
4
4
  "description": "Forgeon project generator CLI",
5
5
  "license": "MIT",
6
6
  "author": "Forgeon",
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('90_next_steps');
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('40_change_boundaries_base');
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
 
@@ -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.doesNotMatch(readme, /i18n Configuration/);
45
-
46
- assert.match(projectDoc, /### Docker mode/);
47
- assert.match(projectDoc, /Active proxy preset: `none`/);
48
- assert.doesNotMatch(projectDoc, /packages\/i18n/);
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
- assert.match(projectDoc, /`infra` - Docker Compose \(always\) \+ proxy preset \(`caddy`\)/);
86
- assert.match(projectDoc, /Main proxy config: `infra\/caddy\/Caddyfile`/);
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
+ ```