@crossdelta/platform-sdk 0.7.21 → 0.8.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.
package/README.md CHANGED
@@ -11,48 +11,59 @@
11
11
  <p align="center">
12
12
  <strong>Opinionated platform toolkit for event-driven microservices.</strong><br />
13
13
  Scaffold <a href="https://turbo.build/repo">Turborepo</a> workspaces, generate services from natural language,<br />
14
- and deploy via <a href="https://www.pulumi.com">Pulumi</a> — DigitalOcean-first, provider-agnostic by design.
14
+ and deploy via <a href="https://www.pulumi.com">Pulumi</a> — DigitalOcean-first for speed and simplicity, providers replaceable by design.
15
15
  </p>
16
16
 
17
17
  <p align="center">
18
- <strong>🚀 Scaffold 🤖 Generate ☁️ Deploy</strong>
18
+ <em>Platform SDK (<code>pf</code>) is a platform opinion, not a generator.<br />
19
+ It encodes architectural decisions so teams don't have to rediscover them.</em>
19
20
  </p>
20
21
 
21
22
  <p align="center">
22
- <a href="https://www.npmjs.com/package/@crossdelta/platform-sdk"><img src="https://img.shields.io/npm/v/@crossdelta/platform-sdk.svg?style=flat-square" alt="npm version" /></a>
23
+ <em>Includes optional AI-assisted service generation (OpenAI / Anthropic).</em>
24
+ </p>
23
25
 
26
+ <p align="center">
27
+ <a href="https://www.npmjs.com/package/@crossdelta/platform-sdk"><img src="https://img.shields.io/npm/v/@crossdelta/platform-sdk.svg?style=flat-square" alt="npm version" /></a>
24
28
  <img src="https://img.shields.io/badge/bun-%E2%9A%A1-f9f1e1?style=flat-square" alt="Bun" />
25
29
  <img src="https://img.shields.io/badge/pulumi-ready-blueviolet?style=flat-square" alt="Pulumi" />
26
30
  <img src="https://img.shields.io/badge/DigitalOcean-App_Platform_|_DOKS-0080FF?style=flat-square" alt="DigitalOcean" />
27
31
  </p>
28
32
 
29
33
  <p align="center">
30
- <em><code>pf</code> enforces conventions that keep application code and cloud infrastructure in lockstep — from local development to production.</em>
34
+ <img src="https://img.shields.io/badge/Hono-E36002?style=flat-square&logo=hono&logoColor=white" alt="Hono" />
35
+ <img src="https://img.shields.io/badge/NestJS-E0234E?style=flat-square&logo=nestjs&logoColor=white" alt="NestJS" />
36
+ <img src="https://img.shields.io/badge/NATS-27AAE1?style=flat-square&logo=natsdotio&logoColor=white" alt="NATS" />
31
37
  </p>
32
38
 
33
- <br />
39
+ ---
40
+
41
+ ## Who is this for?
42
+
43
+ - **Teams building event-driven backends** — NATS, CloudEvents, type-safe contracts
44
+ - **Startups & internal platforms** that want long-term consistency over short-term speed
45
+ - **Engineers tired of infra & app drift** — ports, env vars, deployments always in sync
46
+
47
+ You can start without understanding all the pieces. The depth reveals itself as you grow.
34
48
 
35
49
  ---
36
50
 
37
51
  ## Installation
38
52
 
39
53
  ```bash
40
- # Pick your package manager
41
54
  bun add -g @crossdelta/platform-sdk
42
- npm install -g @crossdelta/platform-sdk
43
- pnpm add -g @crossdelta/platform-sdk
55
+ # or: npm install -g @crossdelta/platform-sdk
44
56
  ```
45
57
 
46
58
  <details>
47
- <summary>Alternative: Auto-installer or use without installing</summary>
59
+ <summary>Alternative: Auto-installer or run without installing</summary>
48
60
 
49
61
  ```bash
50
- # Auto-installer (detects package manager, offers to install Bun if needed)
62
+ # Auto-installer (installs CLI globally, prompts to install Bun if missing)
51
63
  curl -fsSL https://unpkg.com/@crossdelta/platform-sdk@latest/install.sh | bash
52
64
 
53
- # Or run directly without global install
65
+ # Or run directly without installing
54
66
  bunx @crossdelta/platform-sdk new workspace my-platform
55
- npx @crossdelta/platform-sdk new workspace my-platform
56
67
  ```
57
68
 
58
69
  </details>
@@ -62,277 +73,120 @@ npx @crossdelta/platform-sdk new workspace my-platform
62
73
  ## Quick Start
63
74
 
64
75
  ```bash
65
- # Create workspace
66
- pf new workspace my-platform -y
67
- cd my-platform
68
-
69
- # Generate microservice with AI
70
- pf setup --ai # Configure AI provider (first time only)
71
- pf new hono-micro services/orders --ai -d "Handle order creation and payment"
72
-
73
- # Start development
76
+ pf new workspace my-platform -y && cd my-platform
74
77
  pf dev
75
78
  ```
76
79
 
77
- **What you get in minutes:**
78
-
79
- - Turborepo monorepo with Biome linting
80
- - NATS + event-driven microservices
81
- - Pulumi infrastructure (`infra/services/*.ts`)
82
- - ✔ Auto-generated `.env.local` with service ports
83
- - ✔ AI-generated service code
84
-
85
- All wired automatically. Local + production infra in lockstep.
86
-
87
- ### ⚡ One CLI for everything
88
-
89
- `pf` runs your workspace scripts from anywhere:
90
-
91
- ```bash
92
- pf test # → turbo run test
93
- pf build # → turbo run build
94
- pf pulumi up # → runs in infra/ directory
95
- ```
96
-
97
- **Why:** Keeps commands consistent across the team and works from any subdirectory.
98
-
99
- > **📖 Note:** You can be productive with `pf` in minutes using the commands above. The sections below are reference documentation—explore them when you need specific details.
100
-
101
- <br />
102
-
103
- ---
104
-
105
- ## 🎯 Why This Exists
106
-
107
- **`pf` solves these platform engineering problems:**
108
-
109
- - **Infrastructure drift** — Infra configs live in separate repos, diverge from code
110
- - **Manual wiring** — Ports, env vars, service discovery configured by hand
111
- - **Slow onboarding** — New devs spend days setting up local environment
112
-
113
- **`pf` is lockstep infra + code:** Services auto-generate `infra/services/*.ts`, ports/env derived automatically, event handlers type-safe and auto-discovered.
80
+ **What you get:**
81
+ - Turborepo monorepo with Biome linting
82
+ - NATS + JetStream for event-driven messaging
83
+ - Pulumi infrastructure (`infra/services/*.ts`)
84
+ - Auto-generated `.env.local` with service ports
114
85
 
115
- <br />
86
+ > **Lockstep by design:**
87
+ > In `pf`, application code and infrastructure are generated from the same source of truth.
88
+ > Service metadata defines ports, env vars, event wiring, and Pulumi configs —
89
+ > eliminating drift between local development, CI, and production.
116
90
 
117
91
  ---
118
92
 
119
- ## 🧩 What This SDK Is / Is Not
93
+ ## Day 1 with pf
120
94
 
121
- ### `pf` is:
95
+ **5 minutes to your first event-driven system:**
122
96
 
123
- - **Opinionated platform toolkit** for event-driven microservices
124
- - **[Turborepo](https://turbo.build/repo) scaffolder** with [Pulumi](https://www.pulumi.com) IaC and [NATS](https://nats.io) messaging built-in
125
- - **AI-assisted code generator** that creates service boilerplate from descriptions
126
- - **Unified dev workflow** — one command to run all services (`pf dev`)
127
- - **[DigitalOcean](https://www.digitalocean.com)-first** deployment target (App Platform + DOKS)
128
-
129
- ### ❌ `pf` is not:
97
+ ```bash
98
+ # 1. Create a new platform
99
+ pf new workspace my-platform -y && cd my-platform
130
100
 
131
- - **Generic microservice generator** — makes architectural choices for you
132
- - **Multi-cloud** (not yet) — DigitalOcean first, extensible provider architecture
133
- - **Runtime manager** — scaffolds code, you deploy with Pulumi
134
- - **Kubernetes replacement** — generates K8s configs via Pulumi
101
+ # 2. Add two services
102
+ pf new hono-micro services/orders
103
+ pf new hono-micro services/notifications
135
104
 
136
- **Note:** `pf` runs nothing implicitly—no hidden daemons, no automatic deployments or provisioning. You maintain explicit control over when code runs and infrastructure is deployed.
105
+ # 3. Wire an event between them
106
+ pf event add orders.created --service services/notifications
137
107
 
138
- ### 🔥 Why `pf` vs other tools?
108
+ # 4. Start everything
109
+ pf dev
139
110
 
140
- - **🔗 Lockstep infra + code**`infra/services/*.ts` auto-generated from service metadata, ports/env derived automatically
141
- - **⚡ Event-driven by default** — [NATS](https://nats.io) + JetStream built-in with auto-discovered handlers (`*.event.ts`) and Zod-validated payloads
142
- - **🤖 Automation through conventions** — New service = infra config + env vars + port assignment happens automatically
111
+ # 5. Publish a test eventwatch notifications react
112
+ pf event publish orders.created
113
+ ```
143
114
 
144
- <br />
115
+ That's it. Two services, one event contract, auto-discovered handlers, no manual wiring.
145
116
 
146
117
  ---
147
118
 
148
- ## 🏗️ Architecture at 10,000 ft
119
+ ## 🧭 How You Work With pf (Happy Paths)
149
120
 
150
- When you create a workspace with `pf`, you get a **Turborepo monorepo** with this structure:
121
+ ### Start a new platform
151
122
 
152
- ```
153
- my-platform/
154
- ├── services/ # Microservices (generate with pf new hono-micro)
155
- │ ├── nats/ # NATS message broker (auto-scaffolded)
156
- │ └── <service>/ # Your services go here
157
- │ └── src/
158
- │ ├── events/ # Event handlers (*.event.ts)
159
- │ ├── use-cases/ # Business logic (*.use-case.ts)
160
- │ ├── types/ # Zod schemas & types
161
- │ └── index.ts # Service entrypoint
162
- ├── apps/ # Frontend apps (optional)
163
- ├── packages/
164
- │ └── contracts/ # Event contracts (Schema Registry)
165
- │ └── src/
166
- │ └── events/ # Event schemas (single source of truth)
167
- ├── infra/ # Pulumi Infrastructure-as-Code
168
- │ ├── index.ts # Main Pulumi program
169
- │ └── services/ # Per-service K8s configs (auto-generated)
170
- ├── .github/
171
- │ └── workflows/ # CI/CD pipelines
172
- ├── turbo.json # Turborepo task orchestration
173
- └── .env.local # Auto-generated from infra configs
123
+ ```bash
124
+ pf new workspace my-platform --github-owner my-org --pulumi-stack dev -y
125
+ cd my-platform
126
+ pf dev
174
127
  ```
175
128
 
176
- ### Key Conventions
129
+ This scaffolds a complete Turborepo monorepo with NATS, Pulumi infrastructure, GitHub Actions workflows, and auto-generated environment variables.
177
130
 
178
- | Location | Purpose |
179
- |----------|---------|
180
- | `services/*/src/events/*.event.ts` | Event handlers (auto-discovered) |
181
- | `services/*/src/use-cases/*.use-case.ts` | Business logic (pure functions) |
182
- | `services/*/src/types/*.ts` | Service-local Zod schemas |
183
- | `packages/contracts/src/events/*.ts` | Event contracts (Schema Registry) |
184
- | `infra/services/*.ts` | Pulumi configs per service |
185
-
186
- ### Key Rules
187
-
188
- - **Never duplicate schemas** — Contracts in `packages/contracts` are the single source of truth
189
- - **Event handlers stay thin** — Delegate to use-cases, no business logic in handlers
190
- - **Infrastructure is explicit** — Ports, env vars derived from `infra/services/*.ts`
191
-
192
- ### Event-Driven Mental Model
193
-
194
- Services communicate via **CloudEvents** over **NATS JetStream** using the **Schema Registry** as single source of truth:
195
-
196
- **1. Define contract (Schema Registry):**
197
-
198
- ```typescript
199
- // packages/contracts/src/events/orders-created.ts
200
- import { createContract } from '@crossdelta/cloudevents'
201
- import { z } from 'zod'
202
-
203
- export const OrdersCreatedContract = createContract({
204
- type: 'orders.created',
205
- schema: z.object({
206
- orderId: z.string(),
207
- customerId: z.string(),
208
- total: z.number(),
209
- }),
210
- })
131
+ ### Add a new microservice
211
132
 
212
- export type OrdersCreatedData = z.infer<typeof OrdersCreatedContract.schema>
133
+ **Hono** (lightweight, Bun-optimized):
134
+ ```bash
135
+ pf new hono-micro services/orders
213
136
  ```
214
137
 
215
- **2. Service A publishes an event:**
216
-
217
- ```typescript
218
- // services/orders/src/index.ts
219
- import { publish } from '@crossdelta/cloudevents'
220
- import { OrdersCreatedContract } from '@my-platform/contracts'
221
-
222
- await publish(OrdersCreatedContract, {
223
- orderId: '123',
224
- customerId: 'cust-456',
225
- total: 99.99
226
- })
138
+ **NestJS** (full-featured, decorator-based):
139
+ ```bash
140
+ pf new nest-micro services/orders
227
141
  ```
228
142
 
229
- **3. Service B auto-discovers and handles it:**
230
-
231
- ```typescript
232
- // services/notifications/src/events/orders-created.event.ts
233
- import { handleEvent } from '@crossdelta/cloudevents'
234
- import { OrdersCreatedContract, type OrdersCreatedData } from '@my-platform/contracts'
235
- import { sendOrderNotification } from '../use-cases/send-order-notification.use-case'
143
+ Or use AI generation with either:
236
144
 
237
- export default handleEvent(OrdersCreatedContract, async (data: OrdersCreatedData) => {
238
- await sendOrderNotification(data) // Delegate to use-case
239
- })
145
+ ```bash
146
+ pf new hono-micro services/orders --ai -d "Handle order creation and publish order events"
240
147
  ```
241
148
 
242
- No manual NATS subscriptions. No boilerplate. Just **convention over configuration**.
149
+ Both generate the service structure, Pulumi config in `infra/services/`, and wire everything automatically.
243
150
 
244
- <br />
151
+ ### Generate with AI (optional)
245
152
 
246
- ---
247
-
248
- ## 🧭 Design Principles
249
-
250
- These principles guide every decision in `pf`:
251
-
252
- - **🥟 Bun-first DX** — Leverage Bun's speed for installs, tests, and dev mode (npm/yarn fallback supported)
253
- - **📦 Monorepo-centric** — One repo to rule them all. Turborepo for caching and parallel builds
254
- - **🏗️ Infrastructure-as-Code by default** — No ClickOps. Every service has Pulumi config
255
- - **🔁 Event-driven communication baked in** — NATS + JetStream are first-class citizens, not afterthoughts
256
- - **🌊 DigitalOcean-first, cloud-agnostic later** — Start simple with DO, expand to AWS/GCP when needed
257
- - **🤖 AI-augmented development** — Use AI to generate complete services, not just snippets
258
- - **📏 Convention over configuration** — Strong opinions enable automation
259
-
260
- <br />
261
-
262
- ---
263
-
264
- ## 👥 Who Is This For?
265
-
266
- `pf` is designed for **small to mid-sized teams building event-driven systems** who want strong conventions, infra parity, and fast onboarding — without maintaining their own internal platform.
267
-
268
- <br />
269
-
270
- ---
271
-
272
- ## 📚 Deep Dive & Reference
153
+ Configure your AI provider once:
273
154
 
274
- The sections below cover architectural details, workflows, and advanced usage.
275
- **You don't need to read them to get started** — they're here when you need them.
276
-
277
- ---
278
-
279
- ## 📘 Workflows
280
-
281
- ### Generate microservices
282
-
283
- **Manual scaffolding:**
284
- ```bash
285
- pf new hono-micro services/orders
286
- pf new nest-micro services/api-gateway
287
- ```
288
-
289
- **AI-powered generation:**
290
155
  ```bash
291
- # First time: configure AI provider (OpenAI or Anthropic)
292
- pf setup --ai
293
-
294
- # Generate service with AI
295
- pf new hono-micro services/payments --ai \
296
- -d "Handle Stripe webhooks and send payment confirmations"
156
+ pf setup --ai # OpenAI or Anthropic
297
157
  ```
298
158
 
299
- **✨ AI generates:**
300
- - ✅ Complete service with event handlers & use cases
301
- - ✅ Pulumi infrastructure configuration
302
- - ✅ Test files with validation logic
303
- - ✅ Complete README documentation
304
-
305
- > **Note:** AI-generated code is a starting point. Always review and test before deploying to production. `pf` never deploys or provisions infrastructure automatically — deployment always happens explicitly via Pulumi.
306
-
307
- ### Start local development
159
+ Then use `--ai` with any service generator:
308
160
 
309
161
  ```bash
310
- cd my-platform
311
- pf dev
162
+ pf new hono-micro services/notifications --ai -d "Send emails on order events"
312
163
  ```
313
164
 
314
- This will:
315
- - 🔄 Auto-generate `.env.local` from infrastructure config
316
- - 🚀 Start NATS + all services in watch mode (via Turbo)
317
- - 🔌 Auto-assign unique ports per service
318
- - 📦 Monitor for file changes and hot-reload
165
+ AI generates: service code, event handlers, use cases, tests, and documentation.
319
166
 
320
- ### 4. Essential workspace commands
167
+ AI is a productivity layer — all generated code follows the same conventions and can be written manually without loss of functionality.
321
168
 
322
- ```bash
323
- # Run all tests across the monorepo
324
- pf test
169
+ ### Daily commands
325
170
 
326
- # Lint and format all code with Biome
327
- pf lint
171
+ | Command | What it does |
172
+ |---------|--------------|
173
+ | `pf dev` | Start NATS + all services in watch mode |
174
+ | `pf test` | Run tests across the monorepo |
175
+ | `pf lint` | Lint and format with Biome |
176
+ | `pf build` | Build all packages and services |
177
+ | `pf new hono-micro <path>` | Create Hono microservice (lightweight) |
178
+ | `pf new nest-micro <path>` | Create NestJS microservice (full-featured) |
179
+ | `pf event add <type> --service <path>` | Add contract, mock, handler + wire stream |
180
+ | `pf event list` | List available events and their consumers |
181
+ | `pf event publish <type>` | Publish mock event to NATS |
182
+ | `pf pulumi up` | Deploy infrastructure (runs in `infra/`) |
328
183
 
329
- # Build all packages and services
330
- pf build
331
- ```
184
+ `pf` proxies to Turborepo from any subdirectory.
332
185
 
333
- ### 5. Workspace Configuration
186
+ ### Configuration (advanced)
334
187
 
335
- Configure workspace behavior via the `pf` field in your root `package.json`:
188
+ <details>
189
+ <summary>Workspace configuration via <code>package.json</code></summary>
336
190
 
337
191
  ```json
338
192
  {
@@ -343,238 +197,207 @@ Configure workspace behavior via the `pf` field in your root `package.json`:
343
197
  "deploy": { "cwd": "infra", "command": "pulumi up --yes" }
344
198
  },
345
199
  "paths": {
346
- "services": {
347
- "path": "services",
348
- "watch": true
349
- },
350
- "apps": {
351
- "path": "apps",
352
- "watch": true
353
- },
354
- "packages": {
355
- "path": "packages",
356
- "watch": true,
357
- "ignorePatterns": ["packages/some-internal-package"]
358
- },
359
- "contracts": {
360
- "path": "packages/contracts"
361
- }
200
+ "services": { "path": "services", "watch": true },
201
+ "apps": { "path": "apps", "watch": true },
202
+ "packages": { "path": "packages", "watch": true },
203
+ "contracts": { "path": "packages/contracts" }
362
204
  }
363
205
  }
364
206
  }
365
207
  ```
366
208
 
367
- **Options:**
368
- - `commands`: Custom command shortcuts with `cwd` (working directory) and `command` overrides
369
- - `paths.<type>.path`: Directory path for each workspace type
370
- - `paths.<type>.watch`: Whether to watch this directory in dev mode (default: `false`)
371
- - `paths.<type>.ignorePatterns`: Glob patterns to ignore within this path (optional)
372
-
373
- <br />
209
+ - `commands`: Custom shortcuts with working directory overrides
210
+ - `paths.<type>.watch`: Enable file watching in dev mode
211
+ - `paths.<type>.ignorePatterns`: Glob patterns to exclude
374
212
 
375
- ---
376
-
377
- ## 📘 Typical Workflows
378
-
379
- Here's how developers actually use `pf` in real-world scenarios:
213
+ </details>
380
214
 
381
215
  <details>
382
- <summary><strong>Workflow 1: Start a New Platform</strong></summary>
383
-
384
- <br />
385
-
386
- You're building a new product from scratch and want to establish a solid foundation.
387
-
388
- ```bash
389
- # Step 1: Scaffold the workspace
390
- bunx @crossdelta/platform-sdk new workspace my-platform \
391
- --github-owner my-platform \
392
- --pulumi-stack dev \
393
- -y
394
-
395
- cd my-platform
216
+ <summary>Infrastructure configuration via <code>infra/services/*.ts</code></summary>
396
217
 
397
- # Step 2: Configure infrastructure (optional)
398
- # Edit infra/config.ts to customize:
399
- # - DigitalOcean region (defaults to nyc3)
400
- # - Kubernetes cluster size
401
- # - Database instance sizes
218
+ ```ts
219
+ import { ports, type K8sServiceConfig } from '@crossdelta/infrastructure'
402
220
 
403
- # Step 3: Start local development
404
- pf dev
221
+ export const config: K8sServiceConfig = {
222
+ name: 'orders',
223
+ ports: ports().http(4001).build(),
224
+ replicas: 1,
225
+ healthCheck: { httpPath: '/health' },
226
+ resources: {
227
+ requests: { cpu: '50m', memory: '64Mi' },
228
+ limits: { cpu: '150m', memory: '128Mi' },
229
+ },
230
+ }
405
231
  ```
406
232
 
407
- **What you get:**
408
- - ✅ Turborepo monorepo with Biome linting/formatting
409
- - ✅ NATS service running in Docker
410
- - ✅ Pulumi infrastructure setup for DigitalOcean
411
- - ✅ GitHub Actions workflows for CI/CD
412
- - ✅ `.env.local` auto-generated with all service ports
233
+ See [@crossdelta/infrastructure](https://www.npmjs.com/package/@crossdelta/infrastructure) for full options.
413
234
 
414
235
  </details>
415
236
 
416
237
  ---
417
238
 
418
- ### Workflow 2: Add a New Microservice
239
+ ## 🎯 Why This Exists
419
240
 
420
- ```bash
421
- # Generate with AI (optional: use --ai flag)
422
- pf new hono-micro services/payments --ai \
423
- -d "Stripe payment processing: handle webhooks, publish events"
241
+ `pf` solves platform engineering problems:
424
242
 
425
- # Auto-generated: service code + infra config + tests + README
426
- # Start the service
427
- pf dev
428
- ```
243
+ - **Infrastructure drift** Infra configs diverge from code in separate repos
244
+ - **Manual wiring** — Ports, env vars, service discovery configured by hand
245
+ - **Slow onboarding** — New devs spend days setting up local environments
246
+
247
+ **Solution:** Services auto-generate `infra/services/*.ts`, ports/env derived automatically, event handlers type-safe and auto-discovered.
429
248
 
430
- **AI generates:** Service structure, event handlers, use cases, tests, and documentation. Always review before deploying.
249
+ **In short: docker-compose starts services `pf` prevents systems from drifting apart over time.**
431
250
 
432
251
  ---
433
252
 
434
- ## NATS Message Broker
253
+ ## 🧩 What pf Is / Is Not
435
254
 
436
- Every workspace includes a pre-configured NATS service with JetStream:
255
+ **✅ pf is:**
256
+ - Opinionated platform toolkit for event-driven microservices
257
+ - Turborepo scaffolder with Pulumi IaC and NATS messaging built-in
258
+ - AI-assisted code generator from natural language descriptions
259
+ - Unified dev workflow — one command to run everything
260
+ - DigitalOcean-first for speed and simplicity — providers replaceable by design
437
261
 
438
- ```
439
- services/nats/
440
- ├── nats.conf # Local dev config (JetStream enabled)
441
- ├── nats.prod.conf # Production config (auth + persistence)
442
- ├── scripts/start-dev.sh # Docker startup script
443
- └── README.md # Full NATS documentation
444
- ```
262
+ **❌ pf is not:**
263
+ - Generic microservice generator — makes architectural choices for you
264
+ - Multi-cloud out of the box — DigitalOcean first, extensible to other providers
265
+ - Runtime manager scaffolds code, you deploy with Pulumi
445
266
 
446
- **Local Development:**
447
- - Auto-started with `pf dev`
448
- - Runs in Docker on ports `4222` (client) and `8222` (HTTP monitoring)
449
- - Data persisted in `.nats-data/` directory
450
- - Health check: `curl http://localhost:8222/healthz`
451
-
452
- **📚 Learn more:** See `services/nats/README.md` in your workspace for monitoring, configuration, and JetStream details.
453
-
454
- <br />
267
+ **Note:** `pf` runs nothing implicitly. No hidden daemons, no automatic deployments. You control when code runs and infrastructure deploys.
455
268
 
456
269
  ---
457
270
 
458
- ## 🛠 CLI Commands
271
+ ## 🏗️ Architecture
459
272
 
460
- <details>
461
- <summary><strong>📖 View Full Command Reference</strong></summary>
273
+ ```
274
+ my-platform/
275
+ ├── services/ # Microservices
276
+ │ ├── nats/ # NATS message broker (auto-scaffolded)
277
+ │ └── <service>/
278
+ │ └── src/
279
+ │ ├── events/ # Event handlers (*.event.ts)
280
+ │ ├── use-cases/ # Business logic (*.use-case.ts)
281
+ │ ├── types/ # Zod schemas
282
+ │ └── index.ts
283
+ ├── packages/
284
+ │ └── contracts/ # Event contracts (Schema Registry)
285
+ ├── infra/ # Pulumi Infrastructure-as-Code
286
+ │ └── services/ # Per-service K8s configs
287
+ └── .env.local # Auto-generated from infra
288
+ ```
462
289
 
463
- <br />
290
+ ### Conventions
464
291
 
465
- ### Workspace Commands
292
+ | Location | Purpose |
293
+ |----------|---------|
294
+ | `services/*/src/events/*.event.ts` | Event handlers (auto-discovered) |
295
+ | `services/*/src/use-cases/*.use-case.ts` | Business logic (pure functions) |
296
+ | `packages/contracts/src/events/*.ts` | Event contracts (single source of truth) |
297
+ | `infra/services/*.ts` | Pulumi configs per service |
466
298
 
467
- | Command | Description |
468
- |---------|-------------|
469
- | `pf new` | Interactive creation wizard |
470
- | `pf new workspace <name>` | Scaffold a complete platform monorepo |
471
- | `pf setup --ai` | Configure AI provider (OpenAI or Anthropic) |
299
+ > **Schema Registry pattern:**
300
+ > All event contracts live in `packages/contracts` as the single source of truth.
301
+ > Services import contracts instead of redefining schemas, ensuring
302
+ > type safety, compatibility, and explicit ownership across service boundaries.
472
303
 
473
- ### Service Commands
304
+ ### Event-Driven Pattern
474
305
 
475
- | Command | Description |
476
- |---------|-------------|
477
- | `pf new hono-micro <path>` | Generate a Hono microservice (lightweight) |
478
- | `pf new nest-micro <path>` | Generate a NestJS microservice (enterprise-grade) |
479
- | `pf new hono-micro <path> --ai` | Generate service with AI (requires `pf setup --ai`) |
306
+ **Add events with one command:**
307
+ ```bash
308
+ # Creates contract, mock, handler, and wires the stream automatically
309
+ pf event add orders.created --service services/notifications
310
+ ```
480
311
 
481
- ### Event Testing
312
+ This generates:
313
+ - `packages/contracts/src/events/orders-created.ts` (contract)
314
+ - `packages/contracts/src/events/orders-created.mock.json` (test data)
315
+ - `services/notifications/src/events/orders-created.event.ts` (handler)
316
+ - Adds stream to service `index.ts` if needed
482
317
 
483
- | Command | Description |
484
- |---------|-------------|
485
- | `pf event:generate <path>` | Generate event mocks from event handlers |
486
- | `pf event:list` | List all available event mocks |
487
- | `pf event:publish <name>` | Publish to NATS JetStream |
488
- | `pf event:http <name>` | Send via HTTP Pub/Sub endpoint |
318
+ **Test events locally:**
319
+ ```bash
320
+ pf event list # Show available events
321
+ pf event publish orders.created # Publish mock event to NATS
322
+ ```
489
323
 
490
- ### Utility Commands
324
+ **Manual pattern (if you prefer):**
491
325
 
492
- | Command | Description |
493
- |---------|-------------|
494
- | `pf --version` | Show version information |
495
- | `pf --help` | Show help for any command |
326
+ **1. Define contract:**
327
+ ```typescript
328
+ // packages/contracts/src/events/orders-created.ts
329
+ import { createContract } from '@crossdelta/cloudevents'
330
+ import { z } from 'zod'
496
331
 
497
- </details>
332
+ export const OrdersCreatedContract = createContract({
333
+ type: 'orders.created',
334
+ schema: z.object({ orderId: z.string(), total: z.number() }),
335
+ })
336
+ ```
498
337
 
499
- <br />
338
+ **2. Publish (Service A):**
339
+ ```typescript
340
+ // services/orders/src/use-cases/create-order.use-case.ts
341
+ await publish(OrdersCreatedContract, { orderId: '123', total: 99.99 })
342
+ ```
500
343
 
501
- ---
344
+ **3. Handle (Service B, auto-discovered):**
345
+ ```typescript
346
+ // services/notifications/src/events/orders-created.event.ts
347
+ export default handleEvent(OrdersCreatedContract, async (data) => {
348
+ await sendOrderNotification(data)
349
+ })
350
+ ```
502
351
 
503
- ## 🔧 Configuration Example
352
+ No manual subscriptions. Convention over configuration.
504
353
 
505
- Define infrastructure in `infra/services/*.ts` using [`@crossdelta/infrastructure`](https://www.npmjs.com/package/@crossdelta/infrastructure):
354
+ ---
506
355
 
507
- ```ts
508
- import { ports } from '@crossdelta/infrastructure'
509
- import type { K8sServiceConfig } from '@crossdelta/infrastructure'
356
+ ## ⚡ NATS Message Broker
510
357
 
511
- export const config: K8sServiceConfig = {
512
- name: 'orders',
513
- ports: ports().http(4001).build(),
514
- replicas: 1,
515
- healthCheck: { httpPath: '/health' },
516
- resources: {
517
- requests: { cpu: '50m', memory: '64Mi' },
518
- limits: { cpu: '150m', memory: '128Mi' },
519
- },
520
- env: {
521
- DATABASE_URL: databaseUrl,
522
- NATS_URL: natsUrl,
523
- },
524
- }
525
- ```
358
+ Every workspace includes pre-configured NATS with JetStream:
526
359
 
527
- **📖 See the [Infrastructure Package Docs](https://www.npmjs.com/package/@crossdelta/infrastructure) for advanced configuration options.**
360
+ - Auto-started with `pf dev`
361
+ - Ports: `4222` (client), `8222` (monitoring)
362
+ - Health check: `curl http://localhost:8222/healthz`
528
363
 
529
- <br />
364
+ See `services/nats/README.md` for details.
530
365
 
531
366
  ---
532
367
 
533
- ## 🚢 Deployment
534
-
535
- ### Local Deployment
368
+ ## Deployment
536
369
 
537
370
  ```bash
538
- pulumi login
539
- pulumi up --stack dev
371
+ pulumi login && pulumi up --stack dev
540
372
  ```
541
373
 
542
- ### CI/CD with GitHub Actions
543
-
544
- Every workspace includes pre-configured GitHub Actions workflows in `.github/workflows/`:
374
+ ### GitHub Actions (pre-configured)
545
375
 
546
376
  | Workflow | Trigger | Purpose |
547
377
  |----------|---------|---------|
548
- | **`lint-and-tests.yml`** | Pull Requests to `main` | Runs `bun lint` and `bun test` on PRs |
549
- | **`build-and-deploy.yml`** | Push to `main` | Builds Docker images, pushes to GHCR, deploys via Pulumi |
550
- | **`publish-packages.yml`** | Changes in `packages/` | Auto-publishes packages to npm with versioning |
551
-
552
- ### Required GitHub Secrets
553
-
554
- Configure these secrets in your repository settings (`Settings` → `Secrets and variables` → `Actions`):
378
+ | `lint-and-tests.yml` | PR to `main` | Lint + test |
379
+ | `build-and-deploy.yml` | Push to `main` | Build, push to GHCR, deploy |
380
+ | `publish-packages.yml` | Changes in `packages/` | Publish to npm |
555
381
 
556
- | Secret | Description | Required For |
557
- |--------|-------------|--------------|
558
- | `PULUMI_ACCESS_TOKEN` | Pulumi Cloud access token | All deployments |
559
- | `DIGITALOCEAN_TOKEN` | DigitalOcean API token | Infrastructure provisioning |
560
- | `NPM_TOKEN` | npm registry token | Package publishing (optional) |
561
- | `GHCR_TOKEN` | GitHub Container Registry PAT | Docker image publishing |
382
+ <details>
383
+ <summary>Required secrets</summary>
562
384
 
563
- **Get tokens:**
564
- - **Pulumi:** [Create access token](https://app.pulumi.com/account/tokens)
565
- - **DigitalOcean:** [Generate API token](https://cloud.digitalocean.com/account/api/tokens)
566
- - **npm:** [Create access token](https://www.npmjs.com/settings/~/tokens)
385
+ | Secret | Description |
386
+ |--------|-------------|
387
+ | `PULUMI_ACCESS_TOKEN` | [Pulumi Cloud token](https://app.pulumi.com/account/tokens) |
388
+ | `DIGITALOCEAN_TOKEN` | [DO API token](https://cloud.digitalocean.com/account/api/tokens) |
389
+ | `GHCR_TOKEN` | GitHub Container Registry PAT |
390
+ | `NPM_TOKEN` | [npm access token](https://www.npmjs.com/settings/~/tokens) (only for `publish-packages.yml`) |
567
391
 
568
- <br />
392
+ </details>
569
393
 
570
394
  ---
571
395
 
572
396
  ## 📚 Requirements
573
397
 
574
- - **JavaScript runtime** — Bun (recommended) or Node.js ≥ 21 (npm/yarn/pnpm)
575
- - **[Pulumi CLI](https://www.pulumi.com/docs/install/)** for infrastructure deployment
576
- - **[Docker](https://www.docker.com/)** — required for local NATS. Without Docker, you can still scaffold/build
577
-
398
+ - **Bun** (recommended) or Node.js ≥ 21
399
+ - **[Pulumi CLI](https://www.pulumi.com/docs/install/)** for deployment
400
+ - **[Docker](https://www.docker.com/)** for local NATS
578
401
 
579
402
  ---
580
403