@crossdelta/platform-sdk 0.8.3 → 0.8.4
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 +265 -230
- package/bin/cli.js +151 -151
- package/bin/docs/generators/README.md +47 -0
- package/bin/docs/generators/service.md +258 -0
- package/bin/templates/workspace/.github/copilot-instructions.md.hbs +1 -2
- package/install.sh +14 -14
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,118 +1,137 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="https://unpkg.com/@crossdelta/platform-sdk/logo.png" alt="pf" width="
|
|
2
|
+
<img src="https://unpkg.com/@crossdelta/platform-sdk/logo.png" alt="pf" width="140" />
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<h1 align="center">Platform SDK</h1>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<
|
|
8
|
+
<strong>The platform toolkit for backend + infrastructure.</strong>
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
11
|
<p align="center">
|
|
12
|
-
<
|
|
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 for speed and simplicity, providers replaceable by design.
|
|
12
|
+
<code>pf</code> scaffolds real-world platforms: backend services, event-driven messaging, infrastructure — all wired together and kept in sync.
|
|
15
13
|
</p>
|
|
16
14
|
|
|
17
15
|
<p align="center">
|
|
18
|
-
<
|
|
19
|
-
It encodes architectural decisions so teams don't have to rediscover them.</em>
|
|
20
|
-
</p>
|
|
16
|
+
<sub>Fontend SDK coming soon — contract-based client models.</sub>
|
|
21
17
|
|
|
22
|
-
<p align="center">
|
|
23
|
-
<em>Includes optional AI-assisted service generation (OpenAI / Anthropic).</em>
|
|
24
18
|
</p>
|
|
25
19
|
|
|
20
|
+
<br />
|
|
21
|
+
|
|
26
22
|
<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=
|
|
28
|
-
|
|
29
|
-
<img src="https://img.shields.io/badge/
|
|
30
|
-
|
|
23
|
+
<a href="https://www.npmjs.com/package/@crossdelta/platform-sdk"><img src="https://img.shields.io/npm/v/@crossdelta/platform-sdk.svg?style=for-the-badge&color=cb3837" alt="npm version" /></a>
|
|
24
|
+
|
|
25
|
+
<img src="https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript" />
|
|
26
|
+
|
|
27
|
+
<img src="https://img.shields.io/badge/Bun-000000?style=for-the-badge&logo=bun&logoColor=white" alt="Bun" />
|
|
28
|
+
|
|
29
|
+
<img src="https://img.shields.io/badge/License-MIT-22c55e?style=for-the-badge" alt="MIT License" />
|
|
31
30
|
</p>
|
|
32
31
|
|
|
33
32
|
<p align="center">
|
|
34
33
|
<img src="https://img.shields.io/badge/Hono-E36002?style=flat-square&logo=hono&logoColor=white" alt="Hono" />
|
|
35
34
|
<img src="https://img.shields.io/badge/NestJS-E0234E?style=flat-square&logo=nestjs&logoColor=white" alt="NestJS" />
|
|
36
35
|
<img src="https://img.shields.io/badge/NATS-27AAE1?style=flat-square&logo=natsdotio&logoColor=white" alt="NATS" />
|
|
36
|
+
<img src="https://img.shields.io/badge/Pulumi-8A3391?style=flat-square&logo=pulumi&logoColor=white" alt="Pulumi" />
|
|
37
|
+
<img src="https://img.shields.io/badge/Turborepo-EF4444?style=flat-square&logo=turborepo&logoColor=white" alt="Turborepo" />
|
|
38
|
+
<img src="https://img.shields.io/badge/DigitalOcean-0080FF?style=flat-square&logo=digitalocean&logoColor=white" alt="DigitalOcean" />
|
|
37
39
|
</p>
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
<br />
|
|
40
42
|
|
|
41
|
-
|
|
43
|
+
<p align="center">
|
|
44
|
+
<a href="#-quick-start">Quick Start</a> •
|
|
45
|
+
<a href="#-5-minute-tutorial">Tutorial</a> •
|
|
46
|
+
<a href="#-commands">Commands</a> •
|
|
47
|
+
<a href="#-ai-assisted-generation">AI Generation</a> •
|
|
48
|
+
<a href="#-deployment">Deployment</a>
|
|
49
|
+
</p>
|
|
42
50
|
|
|
43
|
-
|
|
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
|
|
51
|
+
---
|
|
46
52
|
|
|
47
|
-
|
|
53
|
+
<br />
|
|
54
|
+
|
|
55
|
+
## 👋 Who is this for?
|
|
56
|
+
|
|
57
|
+
<table>
|
|
58
|
+
<tr>
|
|
59
|
+
<td width="60">🏗️</td>
|
|
60
|
+
<td><strong>Teams building full-stack platforms</strong> — services, events, apps that actually work together</td>
|
|
61
|
+
</tr>
|
|
62
|
+
<tr>
|
|
63
|
+
<td>📡</td>
|
|
64
|
+
<td><strong>Event-driven architectures</strong> — or teams ready to start using events</td>
|
|
65
|
+
</tr>
|
|
66
|
+
<tr>
|
|
67
|
+
<td>😤</td>
|
|
68
|
+
<td><strong>Engineers tired of drift</strong> — ports, env vars, and infra configs that never match</td>
|
|
69
|
+
</tr>
|
|
70
|
+
<tr>
|
|
71
|
+
<td>🚀</td>
|
|
72
|
+
<td><strong>Startups & platform teams</strong> — who want consistency without building everything from scratch</td>
|
|
73
|
+
</tr>
|
|
74
|
+
</table>
|
|
75
|
+
|
|
76
|
+
> **You can start without understanding all the pieces.**
|
|
77
|
+
> The depth reveals itself as you grow.
|
|
48
78
|
|
|
49
79
|
---
|
|
50
80
|
|
|
51
|
-
##
|
|
81
|
+
## 🚀 Quick Start
|
|
52
82
|
|
|
53
83
|
```bash
|
|
84
|
+
# With Bun
|
|
54
85
|
bun add -g @crossdelta/platform-sdk
|
|
55
|
-
# or: npm install -g @crossdelta/platform-sdk
|
|
56
|
-
```
|
|
57
86
|
|
|
58
|
-
|
|
59
|
-
<summary>Alternative: Auto-installer or run without installing</summary>
|
|
60
|
-
|
|
61
|
-
**Unix/macOS:**
|
|
62
|
-
```bash
|
|
63
|
-
# Auto-installer (installs CLI globally, prompts to install Bun if missing)
|
|
64
|
-
curl -fsSL https://unpkg.com/@crossdelta/platform-sdk@latest/install.sh | bash
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
**Windows:**
|
|
68
|
-
```powershell
|
|
69
|
-
# Recommended: Use npm directly
|
|
87
|
+
# With npm
|
|
70
88
|
npm install -g @crossdelta/platform-sdk
|
|
71
89
|
|
|
72
|
-
# Or
|
|
73
|
-
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
**Run without installing:**
|
|
77
|
-
```bash
|
|
78
|
-
bunx @crossdelta/platform-sdk new workspace my-platform
|
|
79
|
-
# or: npx @crossdelta/platform-sdk new workspace my-platform
|
|
90
|
+
# Or auto-install (macOS/Linux/WSL)
|
|
91
|
+
curl -fsSL https://unpkg.com/@crossdelta/platform-sdk@latest/install.sh | bash
|
|
80
92
|
```
|
|
81
93
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
---
|
|
85
|
-
|
|
86
|
-
## Quick Start
|
|
94
|
+
> **Windows users:** Use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) for the best experience.
|
|
87
95
|
|
|
96
|
+
**Then create your first platform:**
|
|
88
97
|
```bash
|
|
89
98
|
pf new workspace my-platform -y && cd my-platform
|
|
90
99
|
pf dev
|
|
91
100
|
```
|
|
92
101
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
>
|
|
100
|
-
>
|
|
101
|
-
>
|
|
102
|
-
>
|
|
102
|
+
<br />
|
|
103
|
+
|
|
104
|
+
<table>
|
|
105
|
+
<tr>
|
|
106
|
+
<td>✅</td>
|
|
107
|
+
<td>Turborepo monorepo with Biome linting</td>
|
|
108
|
+
</tr>
|
|
109
|
+
<tr>
|
|
110
|
+
<td>✅</td>
|
|
111
|
+
<td>NATS + JetStream for event-driven messaging</td>
|
|
112
|
+
</tr>
|
|
113
|
+
<tr>
|
|
114
|
+
<td>✅</td>
|
|
115
|
+
<td>Pulumi infrastructure ready for deployment</td>
|
|
116
|
+
</tr>
|
|
117
|
+
<tr>
|
|
118
|
+
<td>✅</td>
|
|
119
|
+
<td>Auto-generated <code>.env.local</code> with service ports</td>
|
|
120
|
+
</tr>
|
|
121
|
+
</table>
|
|
103
122
|
|
|
104
123
|
---
|
|
105
124
|
|
|
106
|
-
## 5-Minute Tutorial
|
|
125
|
+
## 🎯 5-Minute Tutorial
|
|
107
126
|
|
|
108
|
-
|
|
127
|
+
Add two services and wire an event between them:
|
|
109
128
|
|
|
110
129
|
```bash
|
|
111
|
-
# 1.
|
|
130
|
+
# 1. Create two services
|
|
112
131
|
pf new hono-micro services/orders
|
|
113
132
|
pf new hono-micro services/notifications
|
|
114
133
|
|
|
115
|
-
# 2. Wire an event
|
|
134
|
+
# 2. Wire an event from orders → notifications
|
|
116
135
|
pf event add orders.created --service services/notifications
|
|
117
136
|
|
|
118
137
|
# 3. Restart to pick up new services
|
|
@@ -122,92 +141,194 @@ pf dev
|
|
|
122
141
|
pf event publish orders.created
|
|
123
142
|
```
|
|
124
143
|
|
|
125
|
-
|
|
144
|
+
<br />
|
|
145
|
+
|
|
146
|
+
<table>
|
|
147
|
+
<tr>
|
|
148
|
+
<th align="left">What just happened</th>
|
|
149
|
+
</tr>
|
|
150
|
+
<tr>
|
|
151
|
+
<td>
|
|
152
|
+
📦 Two services with health checks and Pulumi configs<br />
|
|
153
|
+
📝 One event contract in <code>packages/contracts/</code><br />
|
|
154
|
+
🔗 One handler that auto-discovers the event<br />
|
|
155
|
+
✨ <strong>No manual wiring. No config drift. Everything is explicit.</strong>
|
|
156
|
+
</td>
|
|
157
|
+
</tr>
|
|
158
|
+
</table>
|
|
126
159
|
|
|
127
160
|
---
|
|
128
161
|
|
|
129
|
-
##
|
|
162
|
+
## 💡 What problem does pf solve?
|
|
163
|
+
|
|
164
|
+
<table>
|
|
165
|
+
<tr>
|
|
166
|
+
<th width="300">❌ The Problem</th>
|
|
167
|
+
<th width="300">✅ With pf</th>
|
|
168
|
+
</tr>
|
|
169
|
+
<tr>
|
|
170
|
+
<td>Application code lives here, infra config lives somewhere else</td>
|
|
171
|
+
<td>Both generated from the same source of truth</td>
|
|
172
|
+
</tr>
|
|
173
|
+
<tr>
|
|
174
|
+
<td>Ports, env vars, and event wiring drift over time</td>
|
|
175
|
+
<td>Change a service → infra stays in sync</td>
|
|
176
|
+
</tr>
|
|
177
|
+
<tr>
|
|
178
|
+
<td>New devs spend days setting up local environments</td>
|
|
179
|
+
<td>Run <code>pf dev</code> → everything starts together</td>
|
|
180
|
+
</tr>
|
|
181
|
+
<tr>
|
|
182
|
+
<td>Manual event subscriptions and handler registration</td>
|
|
183
|
+
<td>Add an event → consumers are wired automatically</td>
|
|
184
|
+
</tr>
|
|
185
|
+
</table>
|
|
130
186
|
|
|
131
|
-
|
|
187
|
+
---
|
|
132
188
|
|
|
133
|
-
|
|
134
|
-
```bash
|
|
135
|
-
pf new hono-micro services/orders
|
|
136
|
-
```
|
|
189
|
+
## 📋 Commands
|
|
137
190
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
pf
|
|
141
|
-
|
|
191
|
+
| Command | What it does |
|
|
192
|
+
|:--------|:-------------|
|
|
193
|
+
| `pf dev` | 🚀 Start NATS + all services in watch mode |
|
|
194
|
+
| `pf new hono-micro <path>` | ⚡ Create a Hono service (lightweight, Bun-optimized) |
|
|
195
|
+
| `pf new nest-micro <path>` | 🏢 Create a NestJS service (full-featured, decorators) |
|
|
196
|
+
| `pf event add <type> --service <path>` | 🔗 Add contract + handler + wire stream |
|
|
197
|
+
| `pf event list` | 📋 List available events and consumers |
|
|
198
|
+
| `pf event publish <type>` | 📤 Publish mock event to NATS |
|
|
199
|
+
| `pf test` | 🧪 Run tests across the monorepo |
|
|
200
|
+
| `pf build` | 📦 Build all packages and services |
|
|
201
|
+
| `pf lint` | ✨ Lint and format with Biome |
|
|
202
|
+
|
|
203
|
+
> `pf` proxies all commands to Turborepo (works from any subdirectory).
|
|
204
|
+
|
|
205
|
+
---
|
|
142
206
|
|
|
143
|
-
|
|
207
|
+
## 🤖 AI-Assisted Generation
|
|
144
208
|
|
|
145
|
-
|
|
209
|
+
> **Optional but powerful.** Configure once, then use `--ai` with any generator.
|
|
146
210
|
|
|
147
|
-
Configure once:
|
|
148
211
|
```bash
|
|
149
212
|
pf setup --ai # OpenAI or Anthropic
|
|
213
|
+
|
|
214
|
+
pf new hono-micro services/notifications --ai \
|
|
215
|
+
-d "Send emails when orders are created"
|
|
150
216
|
```
|
|
151
217
|
|
|
152
|
-
|
|
218
|
+
AI generates service code, event handlers, use cases, and tests — **all following the same conventions you'd write by hand.**
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## 🚢 Deployment
|
|
223
|
+
|
|
153
224
|
```bash
|
|
154
|
-
pf
|
|
225
|
+
pf pulumi up --stack dev
|
|
155
226
|
```
|
|
156
227
|
|
|
157
|
-
|
|
158
|
-
All generated code follows the same conventions and can be written manually.
|
|
228
|
+
**Pre-configured GitHub Actions included:**
|
|
159
229
|
|
|
160
|
-
|
|
230
|
+
| Trigger | Action |
|
|
231
|
+
|:--------|:-------|
|
|
232
|
+
| PR to `main` | 🧪 Lint + test |
|
|
233
|
+
| Push to `main` | 🚀 Build, push to GHCR, deploy |
|
|
234
|
+
| Changes in `packages/` | 📦 Publish to npm |
|
|
161
235
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
|
168
|
-
|
|
169
|
-
| `
|
|
170
|
-
| `
|
|
171
|
-
| `
|
|
236
|
+
<details>
|
|
237
|
+
<summary>🔐 Required secrets</summary>
|
|
238
|
+
|
|
239
|
+
<br />
|
|
240
|
+
|
|
241
|
+
| Secret | Description |
|
|
242
|
+
|:-------|:------------|
|
|
243
|
+
| `PULUMI_ACCESS_TOKEN` | [Pulumi Cloud token](https://app.pulumi.com/account/tokens) |
|
|
244
|
+
| `DIGITALOCEAN_TOKEN` | [DO API token](https://cloud.digitalocean.com/account/api/tokens) |
|
|
245
|
+
| `GHCR_TOKEN` | GitHub Container Registry PAT |
|
|
246
|
+
|
|
247
|
+
</details>
|
|
248
|
+
|
|
249
|
+
---
|
|
172
250
|
|
|
173
|
-
|
|
251
|
+
## 📋 Requirements
|
|
174
252
|
|
|
175
|
-
|
|
253
|
+
| Requirement | Notes |
|
|
254
|
+
|:------------|:------|
|
|
255
|
+
| **Bun** or Node.js ≥ 21 | Bun recommended for speed |
|
|
256
|
+
| **[Pulumi CLI](https://www.pulumi.com/docs/install/)** | For infrastructure deployment |
|
|
257
|
+
| **[Docker](https://www.docker.com/)** | For local NATS |
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## 🔍 Under the Hood
|
|
262
|
+
|
|
263
|
+
<details>
|
|
264
|
+
<summary>📁 <b>Project structure</b></summary>
|
|
265
|
+
|
|
266
|
+
<br />
|
|
267
|
+
|
|
268
|
+
```
|
|
269
|
+
my-platform/
|
|
270
|
+
├── services/ # Your microservices
|
|
271
|
+
│ ├── orders/
|
|
272
|
+
│ └── notifications/
|
|
273
|
+
├── packages/
|
|
274
|
+
│ └── contracts/ # Event contracts (single source of truth)
|
|
275
|
+
├── infra/
|
|
276
|
+
│ └── services/ # Auto-generated Pulumi configs
|
|
277
|
+
└── .env.local # Auto-generated from infra
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
**The key insight:** Services define their ports, env vars, and event subscriptions once. Everything else is derived.
|
|
281
|
+
|
|
282
|
+
</details>
|
|
176
283
|
|
|
177
284
|
<details>
|
|
178
|
-
<summary
|
|
285
|
+
<summary>🛠️ <b>Tech stack</b></summary>
|
|
286
|
+
|
|
287
|
+
<br />
|
|
288
|
+
|
|
289
|
+
| Layer | Technology |
|
|
290
|
+
|:------|:-----------|
|
|
291
|
+
| Monorepo | [Turborepo](https://turbo.build/repo) |
|
|
292
|
+
| Services | [Hono](https://hono.dev) or [NestJS](https://nestjs.com) |
|
|
293
|
+
| Messaging | [NATS](https://nats.io) + JetStream |
|
|
294
|
+
| Events | [CloudEvents](https://cloudevents.io) + Zod validation |
|
|
295
|
+
| Infrastructure | [Pulumi](https://pulumi.com) → DigitalOcean (extensible) |
|
|
296
|
+
| Runtime | [Bun](https://bun.sh) |
|
|
297
|
+
|
|
298
|
+
</details>
|
|
299
|
+
|
|
300
|
+
<details>
|
|
301
|
+
<summary>⚙️ <b>Workspace configuration</b></summary>
|
|
302
|
+
|
|
303
|
+
<br />
|
|
179
304
|
|
|
180
305
|
```json
|
|
181
306
|
{
|
|
182
307
|
"pf": {
|
|
183
|
-
"registry": "my-
|
|
308
|
+
"registry": "my-org/my-platform",
|
|
184
309
|
"commands": {
|
|
185
|
-
"pulumi": { "cwd": "infra" },
|
|
186
310
|
"deploy": { "cwd": "infra", "command": "pulumi up --yes" }
|
|
187
311
|
},
|
|
188
312
|
"paths": {
|
|
189
313
|
"services": { "path": "services", "watch": true },
|
|
190
|
-
"apps": { "path": "apps", "watch": true },
|
|
191
|
-
"packages": { "path": "packages", "watch": true },
|
|
192
314
|
"contracts": { "path": "packages/contracts" }
|
|
193
315
|
}
|
|
194
316
|
}
|
|
195
317
|
}
|
|
196
318
|
```
|
|
197
319
|
|
|
198
|
-
- `commands`: Custom shortcuts with working directory overrides
|
|
199
|
-
- `paths.<type>.watch`: Enable file watching in dev mode
|
|
200
|
-
- `paths.<type>.ignorePatterns`: Glob patterns to exclude
|
|
201
|
-
|
|
202
320
|
</details>
|
|
203
321
|
|
|
204
322
|
<details>
|
|
205
|
-
<summary>
|
|
323
|
+
<summary>🏗️ <b>Service infrastructure config</b></summary>
|
|
206
324
|
|
|
207
|
-
|
|
325
|
+
<br />
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
// infra/services/orders.ts
|
|
208
329
|
import { ports, type K8sServiceConfig } from '@crossdelta/infrastructure'
|
|
209
330
|
|
|
210
|
-
|
|
331
|
+
const config: K8sServiceConfig = {
|
|
211
332
|
name: 'orders',
|
|
212
333
|
ports: ports().http(4001).build(),
|
|
213
334
|
replicas: 1,
|
|
@@ -217,102 +338,21 @@ export const config: K8sServiceConfig = {
|
|
|
217
338
|
limits: { cpu: '150m', memory: '128Mi' },
|
|
218
339
|
},
|
|
219
340
|
}
|
|
341
|
+
|
|
342
|
+
export default config
|
|
220
343
|
```
|
|
221
344
|
|
|
222
345
|
See [@crossdelta/infrastructure](https://www.npmjs.com/package/@crossdelta/infrastructure) for full options.
|
|
223
346
|
|
|
224
347
|
</details>
|
|
225
348
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
## 🎯 Why This Exists
|
|
229
|
-
|
|
230
|
-
`pf` solves platform engineering problems:
|
|
231
|
-
|
|
232
|
-
- **Infrastructure drift** — Infra configs diverge from code in separate repos
|
|
233
|
-
- **Manual wiring** — Ports, env vars, service discovery configured by hand
|
|
234
|
-
- **Slow onboarding** — New devs spend days setting up local environments
|
|
235
|
-
|
|
236
|
-
**Solution:** Services auto-generate `infra/services/*.ts`, ports/env derived automatically, event handlers type-safe and auto-discovered.
|
|
237
|
-
|
|
238
|
-
**In short: docker-compose starts services — `pf` prevents systems from drifting apart over time.**
|
|
239
|
-
|
|
240
|
-
---
|
|
241
|
-
|
|
242
|
-
## 🧩 What pf Is / Is Not
|
|
243
|
-
|
|
244
|
-
**✅ pf is:**
|
|
245
|
-
- Opinionated platform toolkit for event-driven microservices
|
|
246
|
-
- Turborepo scaffolder with Pulumi IaC and NATS messaging built-in
|
|
247
|
-
- AI-assisted code generator from natural language descriptions
|
|
248
|
-
- Unified dev workflow — one command to run everything
|
|
249
|
-
- DigitalOcean-first for speed and simplicity — providers replaceable by design
|
|
250
|
-
|
|
251
|
-
**❌ pf is not:**
|
|
252
|
-
- Generic microservice generator — makes architectural choices for you
|
|
253
|
-
- Multi-cloud out of the box — DigitalOcean first, extensible to other providers
|
|
254
|
-
- Runtime manager — scaffolds code, you deploy with Pulumi
|
|
255
|
-
|
|
256
|
-
**Note:** `pf` runs nothing implicitly. No hidden daemons, no automatic deployments. You control when code runs and infrastructure deploys.
|
|
257
|
-
|
|
258
|
-
---
|
|
259
|
-
|
|
260
|
-
## 🏗️ Architecture
|
|
261
|
-
|
|
262
|
-
```
|
|
263
|
-
my-platform/
|
|
264
|
-
├── services/ # Microservices
|
|
265
|
-
│ ├── nats/ # NATS message broker (auto-scaffolded)
|
|
266
|
-
│ └── <service>/
|
|
267
|
-
│ └── src/
|
|
268
|
-
│ ├── events/ # Event handlers (*.event.ts)
|
|
269
|
-
│ ├── use-cases/ # Business logic (*.use-case.ts)
|
|
270
|
-
│ ├── types/ # Zod schemas
|
|
271
|
-
│ └── index.ts
|
|
272
|
-
├── packages/
|
|
273
|
-
│ └── contracts/ # Event contracts (Schema Registry)
|
|
274
|
-
├── infra/ # Pulumi Infrastructure-as-Code
|
|
275
|
-
│ └── services/ # Per-service K8s configs
|
|
276
|
-
└── .env.local # Auto-generated from infra
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
### Conventions
|
|
280
|
-
|
|
281
|
-
| Location | Purpose |
|
|
282
|
-
|----------|---------|
|
|
283
|
-
| `services/*/src/events/*.event.ts` | Event handlers (auto-discovered) |
|
|
284
|
-
| `services/*/src/use-cases/*.use-case.ts` | Business logic (pure functions) |
|
|
285
|
-
| `packages/contracts/src/events/*.ts` | Event contracts (single source of truth) |
|
|
286
|
-
| `infra/services/*.ts` | Pulumi configs per service |
|
|
287
|
-
|
|
288
|
-
> **Schema Registry pattern:**
|
|
289
|
-
> All event contracts live in `packages/contracts` as the single source of truth.
|
|
290
|
-
> Services import contracts instead of redefining schemas, ensuring
|
|
291
|
-
> type safety, compatibility, and explicit ownership across service boundaries.
|
|
292
|
-
|
|
293
|
-
### Event-Driven Pattern
|
|
294
|
-
|
|
295
|
-
**Add events with one command:**
|
|
296
|
-
```bash
|
|
297
|
-
# Creates contract, mock, handler, and wires the stream automatically
|
|
298
|
-
pf event add orders.created --service services/notifications
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
This generates:
|
|
302
|
-
- `packages/contracts/src/events/orders-created.ts` (contract)
|
|
303
|
-
- `packages/contracts/src/events/orders-created.mock.json` (test data)
|
|
304
|
-
- `services/notifications/src/events/orders-created.event.ts` (handler)
|
|
305
|
-
- Adds stream to service `index.ts` if needed
|
|
349
|
+
<details>
|
|
350
|
+
<summary>📡 <b>Event-driven architecture</b></summary>
|
|
306
351
|
|
|
307
|
-
|
|
308
|
-
```bash
|
|
309
|
-
pf event list # Show available events
|
|
310
|
-
pf event publish orders.created # Publish mock event to NATS
|
|
311
|
-
```
|
|
352
|
+
<br />
|
|
312
353
|
|
|
313
|
-
**
|
|
354
|
+
**Contracts** live in `packages/contracts/` as the single source of truth:
|
|
314
355
|
|
|
315
|
-
**1. Define contract:**
|
|
316
356
|
```typescript
|
|
317
357
|
// packages/contracts/src/events/orders-created.ts
|
|
318
358
|
import { createContract } from '@crossdelta/cloudevents'
|
|
@@ -324,25 +364,27 @@ export const OrdersCreatedContract = createContract({
|
|
|
324
364
|
})
|
|
325
365
|
```
|
|
326
366
|
|
|
327
|
-
**
|
|
328
|
-
```typescript
|
|
329
|
-
// services/orders/src/use-cases/create-order.use-case.ts
|
|
330
|
-
await publish(OrdersCreatedContract, { orderId: '123', total: 99.99 })
|
|
331
|
-
```
|
|
367
|
+
**Handlers** import contracts and delegate to use cases:
|
|
332
368
|
|
|
333
|
-
**3. Handle (Service B, auto-discovered):**
|
|
334
369
|
```typescript
|
|
335
370
|
// services/notifications/src/events/orders-created.event.ts
|
|
371
|
+
import { handleEvent } from '@crossdelta/cloudevents'
|
|
372
|
+
import { OrdersCreatedContract } from '@my-org/contracts'
|
|
373
|
+
import { sendOrderNotification } from '../use-cases/send-notification.use-case'
|
|
374
|
+
|
|
336
375
|
export default handleEvent(OrdersCreatedContract, async (data) => {
|
|
337
376
|
await sendOrderNotification(data)
|
|
338
377
|
})
|
|
339
378
|
```
|
|
340
379
|
|
|
341
|
-
|
|
380
|
+
Handlers are auto-discovered. No manual subscriptions.
|
|
342
381
|
|
|
343
|
-
|
|
382
|
+
</details>
|
|
383
|
+
|
|
384
|
+
<details>
|
|
385
|
+
<summary>⚡ <b>NATS message broker</b></summary>
|
|
344
386
|
|
|
345
|
-
|
|
387
|
+
<br />
|
|
346
388
|
|
|
347
389
|
Every workspace includes pre-configured NATS with JetStream:
|
|
348
390
|
|
|
@@ -350,46 +392,39 @@ Every workspace includes pre-configured NATS with JetStream:
|
|
|
350
392
|
- Ports: `4222` (client), `8222` (monitoring)
|
|
351
393
|
- Health check: `curl http://localhost:8222/healthz`
|
|
352
394
|
|
|
353
|
-
|
|
395
|
+
</details>
|
|
354
396
|
|
|
355
397
|
---
|
|
356
398
|
|
|
357
|
-
##
|
|
358
|
-
|
|
359
|
-
```bash
|
|
360
|
-
pulumi login && pulumi up --stack dev
|
|
361
|
-
```
|
|
399
|
+
## 🧭 Philosophy
|
|
362
400
|
|
|
363
|
-
|
|
401
|
+
<table>
|
|
402
|
+
<tr>
|
|
403
|
+
<td width="50%">
|
|
364
404
|
|
|
365
|
-
|
|
366
|
-
|----------|---------|---------|
|
|
367
|
-
| `lint-and-tests.yml` | PR to `main` | Lint + test |
|
|
368
|
-
| `build-and-deploy.yml` | Push to `main` | Build, push to GHCR, deploy |
|
|
369
|
-
| `publish-packages.yml` | Changes in `packages/` | Publish to npm |
|
|
405
|
+
### ✅ What pf is
|
|
370
406
|
|
|
371
|
-
|
|
372
|
-
|
|
407
|
+
- Opinionated full-stack platform toolkit
|
|
408
|
+
- Unified dev workflow — one command to run everything
|
|
409
|
+
- Infrastructure that stays in sync with your code
|
|
373
410
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
| `PULUMI_ACCESS_TOKEN` | [Pulumi Cloud token](https://app.pulumi.com/account/tokens) |
|
|
377
|
-
| `DIGITALOCEAN_TOKEN` | [DO API token](https://cloud.digitalocean.com/account/api/tokens) |
|
|
378
|
-
| `GHCR_TOKEN` | GitHub Container Registry PAT |
|
|
379
|
-
| `NPM_TOKEN` | [npm access token](https://www.npmjs.com/settings/~/tokens) (only for `publish-packages.yml`) |
|
|
411
|
+
</td>
|
|
412
|
+
<td width="50%">
|
|
380
413
|
|
|
381
|
-
|
|
414
|
+
### ❌ What pf is not
|
|
382
415
|
|
|
383
|
-
|
|
416
|
+
- Generic scaffolder — it makes choices for you
|
|
417
|
+
- Multi-cloud out of the box — DigitalOcean first, extensible later
|
|
418
|
+
- Magic — no hidden daemons, you control when things run
|
|
384
419
|
|
|
385
|
-
|
|
420
|
+
</td>
|
|
421
|
+
</tr>
|
|
422
|
+
</table>
|
|
386
423
|
|
|
387
|
-
|
|
388
|
-
- **[Pulumi CLI](https://www.pulumi.com/docs/install/)** for deployment
|
|
389
|
-
- **[Docker](https://www.docker.com/)** for local NATS
|
|
424
|
+
> **`pf` makes architectural decisions so your team doesn't have to — without hiding how things work.**
|
|
390
425
|
|
|
391
426
|
---
|
|
392
427
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
428
|
+
<p align="center">
|
|
429
|
+
<strong>MIT © crossdelta</strong>
|
|
430
|
+
</p>
|