@crossdelta/platform-sdk 0.13.1 → 0.13.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.
Files changed (62) hide show
  1. package/package.json +1 -1
  2. package/bin/cli.js +0 -312
  3. package/bin/docs/generators/README.md +0 -56
  4. package/bin/docs/generators/code-style.md +0 -96
  5. package/bin/docs/generators/hono-bun.md +0 -181
  6. package/bin/docs/generators/hono-node.md +0 -194
  7. package/bin/docs/generators/nest.md +0 -358
  8. package/bin/docs/generators/service.md +0 -564
  9. package/bin/docs/generators/testing.md +0 -97
  10. package/bin/integration.collection.json +0 -18
  11. package/bin/templates/hono-microservice/Dockerfile.hbs +0 -16
  12. package/bin/templates/hono-microservice/biome.json.hbs +0 -3
  13. package/bin/templates/hono-microservice/src/index.ts.hbs +0 -18
  14. package/bin/templates/hono-microservice/tsconfig.json.hbs +0 -14
  15. package/bin/templates/nest-microservice/Dockerfile.hbs +0 -37
  16. package/bin/templates/nest-microservice/biome.json.hbs +0 -3
  17. package/bin/templates/nest-microservice/src/app.context.ts.hbs +0 -17
  18. package/bin/templates/nest-microservice/src/events/events.module.ts.hbs +0 -8
  19. package/bin/templates/nest-microservice/src/events/events.service.ts.hbs +0 -22
  20. package/bin/templates/nest-microservice/src/main.ts.hbs +0 -34
  21. package/bin/templates/workspace/.github/README.md +0 -70
  22. package/bin/templates/workspace/.github/actions/check-image-tag-exists/action.yml +0 -27
  23. package/bin/templates/workspace/.github/actions/check-image-tag-exists/index.js +0 -179
  24. package/bin/templates/workspace/.github/actions/check-path-changes/action.yml +0 -21
  25. package/bin/templates/workspace/.github/actions/check-path-changes/index.js +0 -192
  26. package/bin/templates/workspace/.github/actions/detect-skipped-services/action.yml +0 -38
  27. package/bin/templates/workspace/.github/actions/generate-scope-matrix/action.yml +0 -17
  28. package/bin/templates/workspace/.github/actions/generate-scope-matrix/index.js +0 -355
  29. package/bin/templates/workspace/.github/actions/prepare-build-context/action.yml +0 -49
  30. package/bin/templates/workspace/.github/actions/resolve-scope-tags/action.yml +0 -31
  31. package/bin/templates/workspace/.github/actions/resolve-scope-tags/index.js +0 -398
  32. package/bin/templates/workspace/.github/actions/setup-bun-install/action.yml.hbs +0 -57
  33. package/bin/templates/workspace/.github/copilot-chat-configuration.json +0 -49
  34. package/bin/templates/workspace/.github/copilot-instructions.md.hbs +0 -72
  35. package/bin/templates/workspace/.github/dependabot.yml +0 -18
  36. package/bin/templates/workspace/.github/workflows/build-and-deploy.yml.hbs +0 -228
  37. package/bin/templates/workspace/.github/workflows/lint-and-tests.yml.hbs +0 -32
  38. package/bin/templates/workspace/.github/workflows/publish-packages.yml +0 -154
  39. package/bin/templates/workspace/apps/.gitkeep +0 -0
  40. package/bin/templates/workspace/biome.json.hbs +0 -62
  41. package/bin/templates/workspace/bunfig.toml.hbs +0 -5
  42. package/bin/templates/workspace/docs/.gitkeep +0 -0
  43. package/bin/templates/workspace/editorconfig.hbs +0 -9
  44. package/bin/templates/workspace/gitignore.hbs +0 -15
  45. package/bin/templates/workspace/infra/Pulumi.dev.yaml.hbs +0 -5
  46. package/bin/templates/workspace/infra/Pulumi.yaml.hbs +0 -6
  47. package/bin/templates/workspace/infra/index.ts.hbs +0 -56
  48. package/bin/templates/workspace/infra/package.json.hbs +0 -21
  49. package/bin/templates/workspace/infra/services/.gitkeep +0 -0
  50. package/bin/templates/workspace/infra/tsconfig.json.hbs +0 -15
  51. package/bin/templates/workspace/npmrc.hbs +0 -2
  52. package/bin/templates/workspace/package.json.hbs +0 -51
  53. package/bin/templates/workspace/packages/.gitkeep +0 -0
  54. package/bin/templates/workspace/packages/contracts/README.md.hbs +0 -166
  55. package/bin/templates/workspace/packages/contracts/package.json.hbs +0 -22
  56. package/bin/templates/workspace/packages/contracts/src/events/index.ts +0 -16
  57. package/bin/templates/workspace/packages/contracts/src/index.ts +0 -10
  58. package/bin/templates/workspace/packages/contracts/src/stream-policies.ts.hbs +0 -40
  59. package/bin/templates/workspace/packages/contracts/tsconfig.json.hbs +0 -7
  60. package/bin/templates/workspace/pnpm-workspace.yaml.hbs +0 -5
  61. package/bin/templates/workspace/turbo.json +0 -37
  62. package/bin/templates/workspace/turbo.json.hbs +0 -29
@@ -1,56 +0,0 @@
1
- # Generator Documentation
2
-
3
- This directory contains AI instructions for code generators.
4
-
5
- ## Structure
6
-
7
- ```
8
- generators/
9
- ├── README.md # This file
10
- ├── service.md # Main service generator instructions
11
- ├── code-style.md # Code style, formatting, naming conventions
12
- ├── testing.md # Testing rules and patterns
13
- ├── hono-bun.md # Hono with Bun runtime specifics
14
- ├── hono-node.md # Hono with Node.js runtime specifics
15
- └── nest.md # NestJS framework specifics
16
- ```
17
-
18
- ## How Instructions Are Loaded
19
-
20
- Instructions are configured in `package.json` under `generatorConfig.docs`:
21
-
22
- ```json
23
- {
24
- "generatorConfig": {
25
- "docs": {
26
- "base": ["service.md", "code-style.md", "testing.md"],
27
- "frameworks": {
28
- "hono": { "bun": "hono-bun.md", "node": "hono-node.md" },
29
- "nest": "nest.md"
30
- }
31
- }
32
- }
33
- }
34
- ```
35
-
36
- **Load Order:**
37
- 1. **Base docs** - Always loaded (service.md, code-style.md, testing.md)
38
- 2. **Framework-specific** - Based on `serviceType` and `packageManager`
39
- 3. **Workspace instructions** - `.github/copilot-instructions.md`, `docs/ai-guidelines.md`
40
-
41
- ## Adding New Generators
42
-
43
- To add instructions for a new framework:
44
-
45
- 1. Create `<framework>.md` in this directory
46
- 2. Add to `generatorConfig.docs.frameworks` in `package.json`
47
- 3. Follow the existing format from `hono-bun.md` or `nest.md`
48
-
49
- ## Template Variables
50
-
51
- The following variables are replaced at runtime:
52
-
53
- | Variable | Description |
54
- |----------|-------------|
55
- | `{{scope}}` | Package scope (e.g., `@orderboss`) |
56
- | `{{AVAILABLE_CONTRACTS}}` | Auto-generated list of existing contracts |
@@ -1,96 +0,0 @@
1
- # Code Style
2
-
3
- ## Formatting
4
-
5
- - Single quotes, no semicolons, 2-space indent, trailing commas
6
- - Arrow functions over `function`
7
- - Template literals over concatenation
8
-
9
- ---
10
-
11
- ## Imports
12
-
13
- **Must be alphabetically sorted** (Biome enforces this):
14
-
15
- ```ts
16
- // ✅ CORRECT - sorted alphabetically, type imports first
17
- import type { DomainCreatedData } from '@my-platform/contracts'
18
- import type { OrdersCreatedData } from '@scope/contracts'
19
- import { handleEvent } from '@crossdelta/cloudevents'
20
- import PusherPushNotifications from '@pusher/push-notifications-server'
21
-
22
- // ❌ WRONG - unsorted
23
- import PusherPushNotifications from '@pusher/push-notifications-server'
24
- import type { DomainCreatedData } from '@my-platform/contracts'
25
-
26
- // ❌ WRONG - missing 'type' keyword
27
- import { OrdersCreatedData } from '@scope/contracts'
28
-
29
- // ❌ WRONG - unused imports
30
- import { handleEvent, publish } from '@crossdelta/cloudevents'
31
- ```
32
-
33
- ---
34
-
35
- ## Environment Variables
36
-
37
- ```ts
38
- // ✅ CORRECT
39
- const apiKey = process.env.API_KEY
40
- if (!apiKey) throw new Error('Missing API_KEY')
41
-
42
- // ❌ WRONG
43
- const apiKey = process.env.API_KEY! // no assertions
44
- ```
45
-
46
- ---
47
-
48
- ## Naming
49
-
50
- | Type | Convention | Example |
51
- |------|------------|---------|
52
- | Files | kebab-case | `send-notification.use-case.ts` |
53
- | Contracts | PascalCase | `OrdersCreatedContract` |
54
- | Types | PascalCase + Data | `OrdersCreatedData` |
55
-
56
- ---
57
-
58
- ## Config Validation (Type Narrowing)
59
-
60
- Use validation functions that return typed objects - avoids `!` assertions:
61
-
62
- ```ts
63
- // ✅ CORRECT - validation returns typed object
64
- export const validateBeamsConfig = (config: {
65
- instanceId?: string
66
- secretKey?: string
67
- }): { instanceId: string; secretKey: string } => {
68
- if (!config.instanceId) throw new Error('Missing PUSHER_BEAMS_INSTANCE_ID')
69
- if (!config.secretKey) throw new Error('Missing PUSHER_BEAMS_SECRET_KEY')
70
- return { instanceId: config.instanceId, secretKey: config.secretKey }
71
- }
72
-
73
- // Usage - no assertions needed
74
- const config = validateBeamsConfig({
75
- instanceId: process.env.PUSHER_BEAMS_INSTANCE_ID,
76
- secretKey: process.env.PUSHER_BEAMS_SECRET_KEY,
77
- })
78
- new PusherClient(config) // ✅ Typed correctly
79
-
80
- // ❌ WRONG - non-null assertions (Biome error)
81
- validateConfig({ instanceId, secretKey })
82
- new PusherClient({ instanceId: instanceId!, secretKey: secretKey! })
83
- ```
84
-
85
- ---
86
-
87
- ## Zod 4
88
-
89
- ```ts
90
- // ✅ No params
91
- z.string().email()
92
- z.string().datetime()
93
-
94
- // ❌ Deprecated
95
- z.string().email('Invalid')
96
- ```
@@ -1,181 +0,0 @@
1
- # Hono (Bun Runtime)
2
-
3
- ## 🚨 CRITICAL: AI Generation Rules
4
-
5
- **DO NOT create from scratch:**
6
- - ❌ `infra/services/<name>.ts` - Created by CLI with port assignment
7
- - ❌ `Dockerfile` - Created by CLI
8
- - ❌ `package.json` - Created by CLI
9
-
10
- **Always generate:**
11
- - ✅ Event handlers (`src/events/*.handler.ts`)
12
- - ✅ Use-cases (`src/use-cases/*.use-case.ts`)
13
- - ✅ Tests (`src/**/*.test.ts`)
14
- - ✅ README.md
15
- - ✅ Contracts (`packages/contracts/src/events/`)
16
-
17
- ---
18
-
19
- ## 🚨 CRITICAL: Commands Block (REQUIRED FIRST)
20
-
21
- ```commands
22
- pf new hono-micro services/<service-name> -y
23
- ```
24
-
25
- **Example:** User asks for "push notifications service" → generate:
26
- ```commands
27
- pf new hono-micro services/push-notifications -y
28
- ```
29
-
30
- ---
31
-
32
- ## Entry Point (src/index.ts)
33
-
34
- **REST API:**
35
- ```ts
36
- import '@crossdelta/telemetry'
37
- import { Hono } from 'hono'
38
-
39
- // Replace MY_SERVICE with actual service name in SCREAMING_SNAKE_CASE
40
- // Example: my-hono-service → MY_HONO_SERVICE_PORT
41
- const port = Number(process.env.MY_SERVICE_PORT) || 8080
42
- const app = new Hono()
43
-
44
- app.get('/health', (c) => c.json({ status: 'ok' }))
45
-
46
- Bun.serve({ port, fetch: app.fetch })
47
- console.log(`Service running on http://localhost:${port}`)
48
- ```
49
-
50
- **Event Consumer:**
51
- ```ts
52
- import '@crossdelta/telemetry'
53
- import { consumeJetStreams } from '@crossdelta/cloudevents'
54
- import { Hono } from 'hono'
55
-
56
- // Replace MY_SERVICE with actual service name in SCREAMING_SNAKE_CASE
57
- const port = Number(process.env.MY_SERVICE_PORT) || 8080
58
- const app = new Hono()
59
-
60
- app.get('/health', (c) => c.json({ status: 'ok' }))
61
-
62
- Bun.serve({ port, fetch: app.fetch })
63
- console.log(`Service running on http://localhost:${port}`)
64
-
65
- // Services NEVER create streams!
66
- // - Development: pf dev auto-creates ephemeral streams from contracts
67
- // - Production: Pulumi materializes persistent streams
68
- consumeJetStreams({
69
- streams: ['ORDERS'], // ⚠️ MUST be PLURAL! Extract from contract's channel.stream
70
- consumer: 'my-service',
71
- discover: './src/events/**/*.handler.ts',
72
- })
73
- ```
74
-
75
- **CRITICAL:** Stream names MUST be PLURAL:
76
- - ✅ `streams: ['ORDERS']` - for orders.created event
77
- - ✅ `streams: ['DOMAINS']` - for domain.created event
78
- - ❌ `streams: ['ORDER']` - WRONG (singular)
79
- - ❌ `streams: ['DOMAIN']` - WRONG (singular)
80
-
81
- **Event Publisher:**
82
- ```ts
83
- import '@crossdelta/telemetry'
84
- import { publish } from '@crossdelta/cloudevents'
85
- import { Hono } from 'hono'
86
-
87
- // Replace MY_SERVICE with actual service name in SCREAMING_SNAKE_CASE
88
- const port = Number(process.env.MY_SERVICE_PORT) || 8080
89
- const app = new Hono()
90
-
91
- app.get('/health', (c) => c.json({ status: 'ok' }))
92
-
93
- app.post('/orders', async (c) => {
94
- const data = await c.req.json()
95
- await publish('orders.created', data)
96
- return c.json({ success: true })
97
- })
98
-
99
- Bun.serve({ port, fetch: app.fetch })
100
- console.log(`Service running on http://localhost:${port}`)
101
- ```
102
-
103
- ---
104
-
105
- ## Environment Validation (Optional)
106
-
107
- **For services with required env vars:**
108
-
109
- ### 1️⃣ Create `src/config/env.ts`:
110
-
111
- ```ts
112
- // src/config/env.ts
113
- import { z } from 'zod'
114
-
115
- // ⚠️ DO NOT include SERVICE_NAME_PORT here - it's already handled by template!
116
- // Only validate YOUR custom env vars
117
- const envSchema = z.object({
118
- PUSHER_INSTANCE_ID: z.string().min(1, 'PUSHER_INSTANCE_ID is required'),
119
- PUSHER_SECRET_KEY: z.string().min(1, 'PUSHER_SECRET_KEY is required'),
120
- NATS_URL: z.string().url().default('nats://localhost:4222'),
121
- })
122
-
123
- export type Env = z.infer<typeof envSchema>
124
-
125
- // Validate and crash process if invalid (like @nestjs/config)
126
- const result = envSchema.safeParse(process.env)
127
- if (!result.success) {
128
- console.error('❌ Environment validation failed:')
129
- console.error(result.error.format())
130
- process.exit(1) // ← Force crash with exit code 1
131
- }
132
-
133
- export const env = result.data
134
- ```
135
-
136
- ### 2️⃣ **CRITICAL: Import in `src/index.ts` early!**
137
-
138
- ```ts
139
- // src/index.ts
140
- import '@crossdelta/telemetry' // ← MUST be first!
141
-
142
- // ⚠️ CRITICAL: Import env IMMEDIATELY after telemetry
143
- // This executes validation and crashes process if env invalid
144
- import { env } from './config/env' // ← THIS LINE IS REQUIRED!
145
-
146
- import { Hono } from 'hono'
147
-
148
- const port = Number(process.env.MY_SERVICE_PORT) || 8080 // ← Handled by template
149
- const app = new Hono()
150
-
151
- app.get('/health', (c) => c.json({ status: 'ok' }))
152
-
153
- app.post('/notify', async (c) => {
154
- const data = await c.req.json()
155
- // Use validated env vars
156
- await sendPush(env.PUSHER_INSTANCE_ID, env.PUSHER_SECRET_KEY, data)
157
- return c.json({ success: true })
158
- })
159
-
160
- Bun.serve({ port, fetch: app.fetch })
161
- console.log(`Service running on http://localhost:${port}`)
162
- ```
163
-
164
- **CRITICAL:**
165
- - ✅ **MUST import `env` in `src/index.ts`** - without import, validation never runs!
166
- - ✅ Import early (after telemetry, before everything else)
167
- - ✅ Use `safeParse()` + `process.exit(1)` - Bun doesn't crash on top-level throws
168
- - ✅ Explicit exit ensures Turbo shows red X (like NestJS)
169
- - ❌ **DO NOT** include `SERVICE_NAME_PORT` in env schema - already handled by CLI template
170
- - ❌ **DO NOT** validate inside handlers or use-cases
171
-
172
- ---
173
-
174
- ## Rules
175
-
176
- - ✅ `Bun.serve({ port, fetch: app.fetch })`
177
- - ✅ Import telemetry FIRST
178
- - ✅ Always include `/health`
179
- - ✅ Use port env var from prompt (e.g., `PUSH_NOTIFICATIONS_PORT`)
180
- - ❌ `export default Bun.serve(...)` - wrong
181
- - ❌ `export default app` - not needed
@@ -1,194 +0,0 @@
1
- # Hono (Node.js Runtime)
2
-
3
- ## 🚨 CRITICAL: AI Generation Rules
4
-
5
- **DO NOT generate these files** - they are created by `pf new hono-micro`:
6
- - ❌ `infra/services/<name>.ts` - Infrastructure config with assigned port
7
- - ❌ `Dockerfile` - Container configuration
8
- - ❌ `package.json` - Dependencies and scripts
9
-
10
- **Always generate:**
11
- - ✅ Event handlers (`src/events/*.handler.ts`)
12
- - ✅ Use-cases (`src/use-cases/*.use-case.ts`)
13
- - ✅ Tests (`src/**/*.test.ts`)
14
- - ✅ README.md
15
- - ✅ Contracts (`packages/contracts/src/events/`)
16
-
17
- ---
18
-
19
- ## 🚨 CRITICAL: Commands Block (REQUIRED FIRST)
20
-
21
- ```commands
22
- pf new hono-micro services/<service-name> -y
23
- ```
24
-
25
- **Example:** User asks for "push notifications service" → generate:
26
- ```commands
27
- pf new hono-micro services/push-notifications -y
28
- ```
29
-
30
- ---
31
-
32
- ## Entry Point (src/index.ts)
33
-
34
- **REST API:**
35
- ```ts
36
- import '@crossdelta/telemetry'
37
- import { serve } from '@hono/node-server'
38
- import { Hono } from 'hono'
39
-
40
- // Replace MY_SERVICE with actual service name in SCREAMING_SNAKE_CASE
41
- const port = Number(process.env.MY_SERVICE_PORT) || 8080
42
- const app = new Hono()
43
-
44
- app.get('/health', (c) => c.json({ status: 'ok' }))
45
-
46
- serve({ fetch: app.fetch, port }, (info) => {
47
- console.log(`Server running on http://localhost:${info.port}`)
48
- })
49
- ```
50
-
51
- **Event Consumer:**
52
- ```ts
53
- import '@crossdelta/telemetry'
54
- import { consumeJetStreams } from '@crossdelta/cloudevents'
55
- import { serve } from '@hono/node-server'
56
- import { Hono } from 'hono'
57
-
58
- // Replace MY_SERVICE with actual service name in SCREAMING_SNAKE_CASE
59
- const port = Number(process.env.MY_SERVICE_PORT) || 8080
60
- const app = new Hono()
61
-
62
- app.get('/health', (c) => c.json({ status: 'ok' }))
63
-
64
- serve({ fetch: app.fetch, port }, (info) => {
65
- console.log(`Server running on http://localhost:${info.port}`)
66
- })
67
-
68
- // Services NEVER create streams!
69
- // - Development: pf dev auto-creates ephemeral streams from contracts
70
- // - Production: Pulumi materializes persistent streams
71
- consumeJetStreams({
72
- streams: ['ORDERS'], // ⚠️ MUST be PLURAL! Extract from contract's channel.stream
73
- consumer: 'my-service',
74
- discover: './src/events/**/*.handler.ts',
75
- })
76
- ```
77
-
78
- **CRITICAL:** Stream names MUST be PLURAL:
79
- - ✅ `streams: ['ORDERS']` - for orders.created event
80
- - ✅ `streams: ['DOMAINS']` - for domain.created event
81
- - ❌ `streams: ['ORDER']` - WRONG (singular)
82
- - ❌ `streams: ['DOMAIN']` - WRONG (singular)
83
-
84
- **Event Publisher:**
85
- ```ts
86
- import '@crossdelta/telemetry'
87
- import { publish } from '@crossdelta/cloudevents'
88
- import { serve } from '@hono/node-server'
89
- import { Hono } from 'hono'
90
-
91
- // Replace MY_SERVICE with actual service name in SCREAMING_SNAKE_CASE
92
- const port = Number(process.env.MY_SERVICE_PORT) || 8080
93
- const app = new Hono()
94
-
95
- app.get('/health', (c) => c.json({ status: 'ok' }))
96
-
97
- app.post('/orders', async (c) => {
98
- const data = await c.req.json()
99
- await publish('orders.created', data)
100
- return c.json({ success: true })
101
- })
102
-
103
- serve({ fetch: app.fetch, port }, (info) => {
104
- console.log(`Server running on http://localhost:${info.port}`)
105
- })
106
- ```
107
-
108
- ---
109
-
110
- ## Environment Validation (Optional)
111
-
112
- **For services with required env vars:**
113
-
114
- ### 1️⃣ Create `src/config/env.ts`:
115
-
116
- ```ts
117
- // src/config/env.ts
118
- import { z } from 'zod'
119
-
120
- // ⚠️ DO NOT include SERVICE_NAME_PORT here - it's already handled by template!
121
- // Only validate YOUR custom env vars
122
- const envSchema = z.object({
123
- PUSHER_INSTANCE_ID: z.string().min(1, 'PUSHER_INSTANCE_ID is required'),
124
- PUSHER_SECRET_KEY: z.string().min(1, 'PUSHER_SECRET_KEY is required'),
125
- NATS_URL: z.string().url().default('nats://localhost:4222'),
126
- })
127
-
128
- export type Env = z.infer<typeof envSchema>
129
-
130
- // Validate and crash process if invalid (like @nestjs/config)
131
- const result = envSchema.safeParse(process.env)
132
- if (!result.success) {
133
- console.error('❌ Environment validation failed:')
134
- console.error(result.error.format())
135
- process.exit(1) // ← Force crash with exit code 1
136
- }
137
-
138
- export const env = result.data
139
- ```
140
-
141
- ### 2️⃣ **CRITICAL: Import in `src/index.ts` early!**
142
-
143
- ```ts
144
- // src/index.ts
145
- import '@crossdelta/telemetry' // ← MUST be first!
146
-
147
- // ⚠️ CRITICAL: Import env IMMEDIATELY after telemetry
148
- // This executes validation and crashes process if env invalid
149
- import { env } from './config/env' // ← THIS LINE IS REQUIRED!
150
-
151
- import { serve } from '@hono/node-server'
152
- import { Hono } from 'hono'
153
- import { consumeJetStreams } from '@crossdelta/cloudevents'
154
-
155
- const port = Number(process.env.NOTIFICATIONS_PORT) || 8080 // ← Handled by template
156
-
157
- const app = new Hono()
158
- app.get('/health', (c) => c.json({ status: 'ok' }))
159
-
160
- app.post('/notify', async (c) => {
161
- const data = await c.req.json()
162
- // Use validated env vars
163
- await sendPush(env.PUSHER_INSTANCE_ID, env.PUSHER_SECRET_KEY, data)
164
- return c.json({ success: true })
165
- })
166
-
167
- consumeJetStreams({
168
- streams: ['DOMAINS'],
169
- consumer: 'notifications',
170
- discover: './src/events/**/*.handler.ts',
171
- })
172
-
173
- serve({ fetch: app.fetch, port }, (info) => {
174
- console.log(`Server running on http://localhost:${info.port}`)
175
- })
176
- ```
177
-
178
- **CRITICAL:**
179
- - ✅ **MUST import `env` in `src/index.ts`** - without import, validation never runs!
180
- - ✅ Import early (after telemetry, before everything else)
181
- - ✅ Use `safeParse()` + `process.exit(1)` - ensures proper crash behavior
182
- - ✅ Explicit exit ensures Turbo shows red X (like NestJS)
183
- - ❌ **DO NOT** include `SERVICE_NAME_PORT` in env schema - already handled by CLI template
184
- - ❌ **DO NOT** validate inside handlers or use-cases
185
-
186
- ---
187
-
188
- ## Rules
189
-
190
- - ✅ `serve()` from `@hono/node-server`
191
- - ✅ Import telemetry FIRST
192
- - ✅ Always include `/health`
193
- - ✅ Use port env var from prompt (e.g., `API_GATEWAY_PORT`)
194
- - ❌ `Bun.serve()` - wrong runtime