@crossdelta/platform-sdk 0.11.3 → 0.12.0

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.
@@ -77,6 +77,56 @@ console.log(`Service running on http://localhost:${port}`)
77
77
 
78
78
  ---
79
79
 
80
+ ## Environment Validation (Optional)
81
+
82
+ For services with required env vars, validate at startup with Zod:
83
+
84
+ ```ts
85
+ // src/config/env.ts
86
+ import { z } from 'zod'
87
+
88
+ const envSchema = z.object({
89
+ PORT: z.string().transform(Number).default('8080'),
90
+ PUSHER_INSTANCE_ID: z.string().min(1),
91
+ PUSHER_SECRET_KEY: z.string().min(1),
92
+ NATS_URL: z.string().url().default('nats://localhost:4222'),
93
+ })
94
+
95
+ export type Env = z.infer<typeof envSchema>
96
+
97
+ // Validate at module load - crashes immediately if invalid
98
+ export const env = envSchema.parse(process.env)
99
+ ```
100
+
101
+ ```ts
102
+ // src/index.ts
103
+ import '@crossdelta/telemetry'
104
+ import { Hono } from 'hono'
105
+ import { env } from './config/env'
106
+
107
+ const app = new Hono()
108
+
109
+ app.get('/health', (c) => c.json({ status: 'ok' }))
110
+
111
+ app.post('/notify', async (c) => {
112
+ const data = await c.req.json()
113
+ // Use validated env
114
+ await sendPush(env.PUSHER_INSTANCE_ID, env.PUSHER_SECRET_KEY, data)
115
+ return c.json({ success: true })
116
+ })
117
+
118
+ Bun.serve({ port: env.PORT, fetch: app.fetch })
119
+ console.log(`Service running on http://localhost:${env.PORT}`)
120
+ ```
121
+
122
+ **Benefits:**
123
+ - ✅ Fail-fast at startup (not at runtime)
124
+ - ✅ Type-safe env access (`env.PORT` is `number`)
125
+ - ✅ Clear error messages for missing vars
126
+ - ✅ Defaults for optional vars
127
+
128
+ ---
129
+
80
130
  ## Rules
81
131
 
82
132
  - ✅ `Bun.serve({ port, fetch: app.fetch })`
@@ -83,6 +83,58 @@ serve({ fetch: app.fetch, port }, (info) => {
83
83
 
84
84
  ---
85
85
 
86
+ ## Environment Validation (Optional)
87
+
88
+ For services with required env vars, validate at startup with Zod:
89
+
90
+ ```ts
91
+ // src/config/env.ts
92
+ import { z } from 'zod'
93
+
94
+ const envSchema = z.object({
95
+ PORT: z.string().transform(Number).default('8080'),
96
+ PUSHER_INSTANCE_ID: z.string().min(1),
97
+ PUSHER_SECRET_KEY: z.string().min(1),
98
+ NATS_URL: z.string().url().default('nats://localhost:4222'),
99
+ })
100
+
101
+ export type Env = z.infer<typeof envSchema>
102
+
103
+ // Validate at module load - crashes immediately if invalid
104
+ export const env = envSchema.parse(process.env)
105
+ ```
106
+
107
+ ```ts
108
+ // src/index.ts
109
+ import '@crossdelta/telemetry'
110
+ import { serve } from '@hono/node-server'
111
+ import { Hono } from 'hono'
112
+ import { env } from './config/env'
113
+
114
+ const app = new Hono()
115
+
116
+ app.get('/health', (c) => c.json({ status: 'ok' }))
117
+
118
+ app.post('/notify', async (c) => {
119
+ const data = await c.req.json()
120
+ // Use validated env
121
+ await sendPush(env.PUSHER_INSTANCE_ID, env.PUSHER_SECRET_KEY, data)
122
+ return c.json({ success: true })
123
+ })
124
+
125
+ serve({ fetch: app.fetch, port: env.PORT }, (info) => {
126
+ console.log(`Server running on http://localhost:${info.port}`)
127
+ })
128
+ ```
129
+
130
+ **Benefits:**
131
+ - ✅ Fail-fast at startup (not at runtime)
132
+ - ✅ Type-safe env access (`env.PORT` is `number`)
133
+ - ✅ Clear error messages for missing vars
134
+ - ✅ Defaults for optional vars
135
+
136
+ ---
137
+
86
138
  ## Rules
87
139
 
88
140
  - ✅ `serve()` from `@hono/node-server`
@@ -22,9 +22,13 @@
22
22
  import { z } from 'zod'
23
23
 
24
24
  export const envSchema = z.object({
25
+ PORT: z.string().transform(Number).default('8080'),
25
26
  PUSHER_INSTANCE_ID: z.string().min(1),
26
27
  PUSHER_SECRET_KEY: z.string().min(1),
28
+ NATS_URL: z.string().url().default('nats://localhost:4222'),
27
29
  })
30
+
31
+ export type Env = z.infer<typeof envSchema>
28
32
  ```
29
33
 
30
34
  ```ts
@@ -48,17 +52,26 @@ export class AppModule {}
48
52
  // ✅ Service uses ConfigService - already validated at startup
49
53
  import { Injectable } from '@nestjs/common'
50
54
  import { ConfigService } from '@nestjs/config'
55
+ import type { Env } from './config/env.schema'
51
56
 
52
57
  @Injectable()
53
58
  export class MyService {
54
- constructor(private config: ConfigService) {}
59
+ constructor(private config: ConfigService<Env, true>) {}
55
60
 
56
61
  doSomething() {
57
- const instanceId = this.config.getOrThrow<string>('PUSHER_INSTANCE_ID')
62
+ const instanceId = this.config.get('PUSHER_INSTANCE_ID') // Type-safe!
63
+ const port = this.config.get('PORT') // number (auto-transformed)
58
64
  }
59
65
  }
60
66
  ```
61
67
 
68
+ **Benefits:**
69
+ - ✅ Fail-fast at startup (not at runtime)
70
+ - ✅ Type-safe config access with `ConfigService<Env, true>`
71
+ - ✅ Clear error messages for missing vars
72
+ - ✅ Defaults for optional vars
73
+ - ✅ Auto-transform strings to numbers/booleans
74
+
62
75
  ---
63
76
 
64
77
  ## 🚨 CRITICAL: NestJS Structure (NO use-cases folder!)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crossdelta/platform-sdk",
3
- "version": "0.11.3",
3
+ "version": "0.12.0",
4
4
  "description": "Platform toolkit for event-driven microservices — keeping code and infrastructure in lockstep.",
5
5
  "keywords": [
6
6
  "cli",
@@ -49,136 +49,132 @@
49
49
  },
50
50
  "generatorConfig": {
51
51
  "docs": {
52
- "base": [
53
- "service.md",
54
- "code-style.md",
55
- "testing.md"
56
- ],
52
+ "base": ["service.md", "code-style.md", "testing.md"],
57
53
  "frameworks": {
58
54
  "hono": {
59
- "bun": "hono-bun.md",
60
- "node": "hono-node.md"
61
- },
62
- "nest": "nest.md"
63
- }
64
- },
65
- "serviceTypes": {
66
- "hono": {
67
- "commandType": "hono-micro",
68
- "entryPoint": "src/index.ts",
69
- "skipFiles": [
70
- "package.json",
71
- "tsconfig.json",
72
- "Dockerfile"
73
- ]
55
+ "bun": "hono-bun.md",
56
+ "node": "hono-node.md"
74
57
  },
75
- "nest": {
76
- "commandType": "nest-micro",
77
- "entryPoint": "src/main.ts",
78
- "skipFiles": [
79
- "package.json",
80
- "tsconfig.json",
81
- "Dockerfile"
82
- ]
83
- }
58
+ "nest": "nest.md"
84
59
  }
85
60
  },
86
- "scripts": {
87
- "start:dev": "node esbuild.config.mjs --watch",
88
- "build:cli": "node esbuild.config.mjs",
89
- "build:cli:copy": "cp cli/integration.collection.json bin/integration.collection.json && rm -rf bin/templates && mkdir -p bin/templates && cp -r cli/src/commands/create/workspace/templates bin/templates/workspace && cp -r cli/src/commands/create/hono-microservice/templates bin/templates/hono-microservice && cp -r cli/src/commands/create/nest-microservice/templates bin/templates/nest-microservice && mkdir -p bin/docs/generators && cp -r docs/generators/* bin/docs/generators/",
90
- "build:schematics:transpile": "tsc --project tsconfig.schematics.json",
91
- "build:schematics:copy": "cp -R schematics/* dist/schematics && find dist/schematics -name '*.ts' -delete",
92
- "build:schematics": "npm run build:schematics:transpile && npm run build:schematics:copy",
93
- "build": "npm run build:cli && npm run build:schematics && npm run build:cli:copy",
94
- "prepublishOnly": "npm run build",
95
- "lint": "biome lint --fix",
96
- "test": "bun test",
97
- "test:watch": "bun test --watch"
98
- },
99
- "schematics": "./dist/schematics/collection.json",
100
- "esbuild": {
101
- "external": [
102
- "@crossdelta/infrastructure",
103
- "@faker-js/faker",
104
- "@inquirer/prompts",
105
- "ai",
106
- "@ai-sdk/openai",
107
- "@ai-sdk/anthropic",
108
- "handlebars",
109
- "listr2",
110
- "enquirer",
111
- "execa",
112
- "globby",
113
- "fs-extra",
114
- "chalk",
115
- "ora",
116
- "jiti",
117
- "zod",
118
- "ts-morph",
119
- "commander",
120
- "chokidar"
121
- ]
122
- },
123
- "dependencies": {
124
- "@ai-sdk/anthropic": "^2.0.53",
125
- "@ai-sdk/openai": "^2.0.79",
126
- "@angular-devkit/core": "^21.0.0",
127
- "@faker-js/faker": "^9.8.0",
128
- "@inquirer/prompts": "^7.5.0",
129
- "@listr2/prompt-adapter-enquirer": "^2.0.15",
130
- "ai": "^5.0.108",
131
- "chalk": "^5.4.1",
132
- "chokidar": "^5.0.0",
133
- "cli-table3": "^0.6.5",
134
- "commander": "^13.1.0",
135
- "dotenv": "^17.2.3",
136
- "enquirer": "^2.4.1",
137
- "execa": "^9.5.2",
138
- "fs-extra": "^11.3.0",
139
- "globby": "^14.1.0",
140
- "handlebars": "^4.7.8",
141
- "jiti": "^2.6.1",
142
- "listr2": "^8.3.2",
143
- "ora": "^8.2.0",
144
- "package-up": "^5.0.0",
145
- "rimraf": "^6.1.2",
146
- "terminal-link": "^4.0.0",
147
- "ts-morph": "^27.0.0",
148
- "zod": "^3.24.3"
149
- },
150
- "peerDependencies": {
151
- "@crossdelta/cloudevents": "*",
152
- "@crossdelta/infrastructure": "*",
153
- "@nestjs/schematics": "^11.0.5",
154
- "turbo": "^2.0.0"
155
- },
156
- "peerDependenciesMeta": {
157
- "@crossdelta/cloudevents": {
158
- "optional": true
159
- },
160
- "@crossdelta/infrastructure": {
161
- "optional": true
61
+ "serviceTypes": {
62
+ "hono": {
63
+ "commandType": "hono-micro",
64
+ "entryPoint": "src/index.ts",
65
+ "skipFiles": [
66
+ "package.json",
67
+ "tsconfig.json",
68
+ "Dockerfile"
69
+ ]
162
70
  },
163
- "@nestjs/schematics": {
164
- "optional": true
165
- },
166
- "turbo": {
167
- "optional": true
71
+ "nest": {
72
+ "commandType": "nest-micro",
73
+ "entryPoint": "src/main.ts",
74
+ "skipFiles": [
75
+ "package.json",
76
+ "tsconfig.json",
77
+ "Dockerfile"
78
+ ]
168
79
  }
80
+ }
81
+ },
82
+ "scripts": {
83
+ "start:dev": "node esbuild.config.mjs --watch",
84
+ "build:cli": "node esbuild.config.mjs",
85
+ "build:cli:copy": "cp cli/integration.collection.json bin/integration.collection.json && rm -rf bin/templates && mkdir -p bin/templates && cp -r cli/src/commands/create/workspace/templates bin/templates/workspace && cp -r cli/src/commands/create/hono-microservice/templates bin/templates/hono-microservice && cp -r cli/src/commands/create/nest-microservice/templates bin/templates/nest-microservice && mkdir -p bin/docs/generators && cp -r docs/generators/* bin/docs/generators/",
86
+ "build:schematics:transpile": "tsc --project tsconfig.schematics.json",
87
+ "build:schematics:copy": "cp -R schematics/* dist/schematics && find dist/schematics -name '*.ts' -delete",
88
+ "build:schematics": "npm run build:schematics:transpile && npm run build:schematics:copy",
89
+ "build": "npm run build:cli && npm run build:schematics && npm run build:cli:copy",
90
+ "prepublishOnly": "npm run build",
91
+ "lint": "biome lint --fix",
92
+ "test": "bun test",
93
+ "test:watch": "bun test --watch"
94
+ },
95
+ "schematics": "./dist/schematics/collection.json",
96
+ "esbuild": {
97
+ "external": [
98
+ "@crossdelta/infrastructure",
99
+ "@faker-js/faker",
100
+ "@inquirer/prompts",
101
+ "ai",
102
+ "@ai-sdk/openai",
103
+ "@ai-sdk/anthropic",
104
+ "handlebars",
105
+ "listr2",
106
+ "enquirer",
107
+ "execa",
108
+ "globby",
109
+ "fs-extra",
110
+ "chalk",
111
+ "ora",
112
+ "jiti",
113
+ "zod",
114
+ "ts-morph",
115
+ "commander",
116
+ "chokidar"
117
+ ]
118
+ },
119
+ "dependencies": {
120
+ "@ai-sdk/anthropic": "^2.0.53",
121
+ "@ai-sdk/openai": "^2.0.79",
122
+ "@angular-devkit/core": "^21.0.0",
123
+ "@faker-js/faker": "^9.8.0",
124
+ "@inquirer/prompts": "^7.5.0",
125
+ "@listr2/prompt-adapter-enquirer": "^2.0.15",
126
+ "ai": "^5.0.108",
127
+ "chalk": "^5.4.1",
128
+ "chokidar": "^5.0.0",
129
+ "cli-table3": "^0.6.5",
130
+ "commander": "^13.1.0",
131
+ "dotenv": "^17.2.3",
132
+ "enquirer": "^2.4.1",
133
+ "execa": "^9.5.2",
134
+ "fs-extra": "^11.3.0",
135
+ "globby": "^14.1.0",
136
+ "handlebars": "^4.7.8",
137
+ "jiti": "^2.6.1",
138
+ "listr2": "^8.3.2",
139
+ "ora": "^8.2.0",
140
+ "package-up": "^5.0.0",
141
+ "rimraf": "^6.1.2",
142
+ "terminal-link": "^4.0.0",
143
+ "ts-morph": "^27.0.0",
144
+ "zod": "^3.24.3"
145
+ },
146
+ "peerDependencies": {
147
+ "@crossdelta/cloudevents": "*",
148
+ "@crossdelta/infrastructure": "*",
149
+ "@nestjs/schematics": "^11.0.5",
150
+ "turbo": "^2.0.0"
151
+ },
152
+ "peerDependenciesMeta": {
153
+ "@crossdelta/cloudevents": {
154
+ "optional": true
155
+ },
156
+ "@crossdelta/infrastructure": {
157
+ "optional": true
169
158
  },
170
- "devDependencies": {
171
- "@angular-devkit/core": "^21.0.0",
172
- "@angular-devkit/schematics": "^21.0.0",
173
- "@angular-devkit/schematics-cli": "^19.2.10",
174
- "@eslint/js": "^9.22.0",
175
- "@nestjs/schematics": "^11.0.9",
176
- "@types/fs-extra": "^11.0.4",
177
- "bun-types": "^1.3.4",
178
- "comment-json": "^4.4.1",
179
- "esbuild": "^0.25.3",
180
- "eslint": "^9.25.1",
181
- "typescript": "^5.8.3",
182
- "typescript-eslint": "^8.31.1"
159
+ "@nestjs/schematics": {
160
+ "optional": true
161
+ },
162
+ "turbo": {
163
+ "optional": true
183
164
  }
165
+ },
166
+ "devDependencies": {
167
+ "@angular-devkit/core": "^21.0.0",
168
+ "@angular-devkit/schematics": "^21.0.0",
169
+ "@angular-devkit/schematics-cli": "^19.2.10",
170
+ "@eslint/js": "^9.22.0",
171
+ "@nestjs/schematics": "^11.0.9",
172
+ "@types/fs-extra": "^11.0.4",
173
+ "bun-types": "^1.3.4",
174
+ "comment-json": "^4.4.1",
175
+ "esbuild": "^0.25.3",
176
+ "eslint": "^9.25.1",
177
+ "typescript": "^5.8.3",
178
+ "typescript-eslint": "^8.31.1"
179
+ }
184
180
  }