@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 +226 -403
- package/bin/cli.js +195 -182
- package/bin/services/ai/instructions/ai-instructions.md +32 -31
- package/bin/templates/workspace/packages/contracts/README.md.hbs +5 -5
- package/package.json +2 -2
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,
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
|
|
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
|
|
59
|
+
<summary>Alternative: Auto-installer or run without installing</summary>
|
|
48
60
|
|
|
49
61
|
```bash
|
|
50
|
-
# Auto-installer (
|
|
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
|
|
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
|
-
|
|
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
|
|
78
|
-
|
|
79
|
-
-
|
|
80
|
-
-
|
|
81
|
-
-
|
|
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
|
-
|
|
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
|
-
##
|
|
93
|
+
## Day 1 with pf
|
|
120
94
|
|
|
121
|
-
|
|
95
|
+
**5 minutes to your first event-driven system:**
|
|
122
96
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
|
|
105
|
+
# 3. Wire an event between them
|
|
106
|
+
pf event add orders.created --service services/notifications
|
|
137
107
|
|
|
138
|
-
|
|
108
|
+
# 4. Start everything
|
|
109
|
+
pf dev
|
|
139
110
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
111
|
+
# 5. Publish a test event — watch notifications react
|
|
112
|
+
pf event publish orders.created
|
|
113
|
+
```
|
|
143
114
|
|
|
144
|
-
|
|
115
|
+
That's it. Two services, one event contract, auto-discovered handlers, no manual wiring.
|
|
145
116
|
|
|
146
117
|
---
|
|
147
118
|
|
|
148
|
-
##
|
|
119
|
+
## 🧭 How You Work With pf (Happy Paths)
|
|
149
120
|
|
|
150
|
-
|
|
121
|
+
### Start a new platform
|
|
151
122
|
|
|
152
|
-
```
|
|
153
|
-
my-platform
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
129
|
+
This scaffolds a complete Turborepo monorepo with NATS, Pulumi infrastructure, GitHub Actions workflows, and auto-generated environment variables.
|
|
177
130
|
|
|
178
|
-
|
|
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
|
-
|
|
133
|
+
**Hono** (lightweight, Bun-optimized):
|
|
134
|
+
```bash
|
|
135
|
+
pf new hono-micro services/orders
|
|
213
136
|
```
|
|
214
137
|
|
|
215
|
-
**
|
|
216
|
-
|
|
217
|
-
|
|
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
|
-
|
|
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
|
-
|
|
238
|
-
|
|
239
|
-
})
|
|
145
|
+
```bash
|
|
146
|
+
pf new hono-micro services/orders --ai -d "Handle order creation and publish order events"
|
|
240
147
|
```
|
|
241
148
|
|
|
242
|
-
|
|
149
|
+
Both generate the service structure, Pulumi config in `infra/services/`, and wire everything automatically.
|
|
243
150
|
|
|
244
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
311
|
-
pf dev
|
|
162
|
+
pf new hono-micro services/notifications --ai -d "Send emails on order events"
|
|
312
163
|
```
|
|
313
164
|
|
|
314
|
-
|
|
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
|
-
|
|
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
|
-
|
|
323
|
-
# Run all tests across the monorepo
|
|
324
|
-
pf test
|
|
169
|
+
### Daily commands
|
|
325
170
|
|
|
326
|
-
|
|
327
|
-
|
|
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
|
-
|
|
330
|
-
pf build
|
|
331
|
-
```
|
|
184
|
+
`pf` proxies to Turborepo from any subdirectory.
|
|
332
185
|
|
|
333
|
-
###
|
|
186
|
+
### Configuration (advanced)
|
|
334
187
|
|
|
335
|
-
|
|
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
|
-
|
|
348
|
-
|
|
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
|
-
|
|
368
|
-
- `
|
|
369
|
-
- `paths.<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
|
|
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
|
-
|
|
398
|
-
|
|
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
|
-
|
|
404
|
-
|
|
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
|
-
|
|
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
|
-
|
|
239
|
+
## 🎯 Why This Exists
|
|
419
240
|
|
|
420
|
-
|
|
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
|
-
|
|
426
|
-
|
|
427
|
-
|
|
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
|
-
**
|
|
249
|
+
**In short: docker-compose starts services — `pf` prevents systems from drifting apart over time.**
|
|
431
250
|
|
|
432
251
|
---
|
|
433
252
|
|
|
434
|
-
##
|
|
253
|
+
## 🧩 What pf Is / Is Not
|
|
435
254
|
|
|
436
|
-
|
|
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
|
-
|
|
440
|
-
|
|
441
|
-
|
|
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
|
-
**
|
|
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
|
-
##
|
|
271
|
+
## 🏗️ Architecture
|
|
459
272
|
|
|
460
|
-
|
|
461
|
-
|
|
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
|
-
|
|
290
|
+
### Conventions
|
|
464
291
|
|
|
465
|
-
|
|
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
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
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
|
-
###
|
|
304
|
+
### Event-Driven Pattern
|
|
474
305
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
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
|
-
|
|
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
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
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
|
-
|
|
324
|
+
**Manual pattern (if you prefer):**
|
|
491
325
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
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
|
-
|
|
332
|
+
export const OrdersCreatedContract = createContract({
|
|
333
|
+
type: 'orders.created',
|
|
334
|
+
schema: z.object({ orderId: z.string(), total: z.number() }),
|
|
335
|
+
})
|
|
336
|
+
```
|
|
498
337
|
|
|
499
|
-
|
|
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
|
-
|
|
352
|
+
No manual subscriptions. Convention over configuration.
|
|
504
353
|
|
|
505
|
-
|
|
354
|
+
---
|
|
506
355
|
|
|
507
|
-
|
|
508
|
-
import { ports } from '@crossdelta/infrastructure'
|
|
509
|
-
import type { K8sServiceConfig } from '@crossdelta/infrastructure'
|
|
356
|
+
## ⚡ NATS Message Broker
|
|
510
357
|
|
|
511
|
-
|
|
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
|
-
|
|
360
|
+
- Auto-started with `pf dev`
|
|
361
|
+
- Ports: `4222` (client), `8222` (monitoring)
|
|
362
|
+
- Health check: `curl http://localhost:8222/healthz`
|
|
528
363
|
|
|
529
|
-
|
|
364
|
+
See `services/nats/README.md` for details.
|
|
530
365
|
|
|
531
366
|
---
|
|
532
367
|
|
|
533
|
-
##
|
|
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
|
-
###
|
|
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
|
-
|
|
|
549
|
-
|
|
|
550
|
-
|
|
|
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
|
-
|
|
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
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
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
|
-
|
|
392
|
+
</details>
|
|
569
393
|
|
|
570
394
|
---
|
|
571
395
|
|
|
572
396
|
## 📚 Requirements
|
|
573
397
|
|
|
574
|
-
- **
|
|
575
|
-
- **[Pulumi CLI](https://www.pulumi.com/docs/install/)**
|
|
576
|
-
- **[Docker](https://www.docker.com/)**
|
|
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
|
|