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