@crossdelta/platform-sdk 0.3.42 → 0.4.1

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
@@ -30,36 +30,116 @@
30
30
 
31
31
  ---
32
32
 
33
- ## Who is this for?
33
+ ## 🎯 Why This Exists
34
34
 
35
- - You run **multiple microservices** and want a unified developer experience
36
- - You prefer **monorepos + Infrastructure-as-Code** as your architectural baseline
37
- - You're okay with **DigitalOcean as the initial cloud provider** (AWS/GCP coming soon)
35
+ Building microservice platforms is hard. You start with great intentions, but complexity creeps in fast:
38
36
 
39
- > ⚠️ **Cloud Provider Support:** Currently only **DigitalOcean** is supported:
40
- > - **App Platform** — Managed PaaS for simple deployments
41
- > - **DOKS (Kubernetes)** — Full Kubernetes control for production workloads
42
- >
43
- > **AWS and GCP support** is planned for future releases.
37
+ - **Scattered repositories** with inconsistent conventions across services
38
+ - **No unified DX** — every team member invents their own scripts
39
+ - **Infrastructure drift** — Terraform/Pulumi configs live in separate repos
40
+ - **Environment hell** — `.env` files manually maintained, ports collide, services don't talk
41
+ - **Slow onboarding** new developers spend days setting up the platform
42
+
43
+ **`pf` acts like an opinionated platform engineer on your team.** It's a single CLI that:
44
+
45
+ - Scaffolds production-ready monorepos with best practices baked in
46
+ - Generates microservices (manually or with AI) with consistent structure
47
+ - Auto-wires infrastructure, environment variables, and port assignments
48
+ - Unifies your dev workflow: one command to run everything locally
49
+
50
+ Built for teams using **Turborepo + Pulumi + NATS + Bun** who want to move fast without sacrificing quality.
44
51
 
45
52
  <br />
46
53
 
47
54
  ---
48
55
 
49
- ## Features
56
+ ## 🧩 What This SDK Is / Is Not
50
57
 
51
- | Feature | Description |
52
- |---------|-------------|
53
- | **Instant Scaffolding** | Create fully wired platform workspaces with best practices baked in |
54
- | 🤖 **AI-Powered Generation** | Generate services with AI using `--ai` flag (OpenAI/Anthropic) |
55
- | 🥟 **Bun-first DX** | Ultra-fast dev workflow with fallback to npm/yarn/pnpm |
56
- | 🏗️ **Turborepo Monorepo** | Parallel builds, caching, and unified dev scripts |
57
- | 🔧 **Service Generators** | Hono (lightweight) and NestJS (enterprise-grade) templates |
58
- | 📦 **Pulumi-Ready Infra** | Infrastructure-as-Code for DigitalOcean |
59
- | 🚀 **GitHub Actions CI/CD** | Pre-configured workflows for build, test, deploy, and npm publish |
60
- | 🌍 **Auto `.env.local`** | Environment variables derived from `infra/services/*.ts` |
61
- | 🔌 **Auto Port Assignment** | Unique ports per service |
62
- | 🔁 **Smart Watch Mode** | Watches infra & services, regenerates env, reinstalls deps |
58
+ ### `pf` is:
59
+
60
+ - **Opinionated CLI** for event-driven microservice platforms
61
+ - **Turborepo scaffolder** with Pulumi IaC and NATS messaging built-in
62
+ - **AI-powered code generator** that creates complete services from natural language
63
+ - **Unified dev workflow** one command to rule them all (`bun dev`)
64
+ - **DigitalOcean-first** deployment target (App Platform + DOKS)
65
+
66
+ ### `pf` is not:
67
+
68
+ - **Not a generic microservice generator** we make architectural choices for you
69
+ - **Not cloud-agnostic** (yet) DigitalOcean first, AWS/GCP coming later
70
+ - **Not a runtime manager** — we scaffold code, you deploy with Pulumi
71
+ - **Not a replacement for Kubernetes** — we generate K8s configs via Pulumi
72
+
73
+ <br />
74
+
75
+ ---
76
+
77
+ ## 🏗️ Architecture at 10,000 ft
78
+
79
+ When you create a workspace with `pf`, you get a **Turborepo monorepo** with this structure:
80
+
81
+ ```
82
+ my-platform/
83
+ ├── services/ # Microservices (Hono, NestJS)
84
+ │ ├── orders/ # Example: Order processing service
85
+ │ ├── notifications/# Example: Notification service
86
+ │ └── nats/ # NATS message broker (auto-scaffolded)
87
+ ├── apps/ # Frontend apps (optional: Qwik, Next.js, etc.)
88
+ ├── packages/ # Shared libraries
89
+ │ ├── cloudevents/ # Event publishing/consuming toolkit
90
+ │ └── telemetry/ # OpenTelemetry setup
91
+ ├── infra/ # Pulumi Infrastructure-as-Code
92
+ │ ├── index.ts # Main Pulumi program
93
+ │ └── services/ # Per-service K8s configs
94
+ │ ├── orders.ts
95
+ │ └── notifications.ts
96
+ ├── .github/
97
+ │ └── workflows/ # CI/CD pipelines (auto-generated)
98
+ ├── turbo.json # Turborepo task orchestration
99
+ └── .env.local # Auto-generated from infra configs
100
+ ```
101
+
102
+ ### Key Architectural Decisions
103
+
104
+ 1. **NATS + JetStream baseline** — Event-driven communication is built-in, not bolted on
105
+ 2. **Infrastructure-as-Code by default** — Every service has a matching `infra/services/<name>.ts` config
106
+ 3. **Auto-wiring everywhere** — Ports, env vars, and NATS subjects are derived automatically
107
+ 4. **Opinionated conventions** — Event handlers live in `src/handlers/*.event.ts`, business logic in `src/use-cases/*.use-case.ts`
108
+ 5. **Bun-first DX** — Ultra-fast installs, tests, and dev server with fallback to npm/yarn
109
+
110
+ ### Event-Driven Mental Model
111
+
112
+ Services communicate via **CloudEvents** over **NATS JetStream**:
113
+
114
+ ```typescript
115
+ // Service A publishes an event
116
+ await publish('orders.created', { orderId: '123', total: 99.99 })
117
+
118
+ // Service B auto-discovers and handles it
119
+ // File: services/notifications/src/handlers/order-created.event.ts
120
+ export default handleEvent(
121
+ { schema: OrderCreatedSchema, type: 'orders.created' },
122
+ async (data) => { await sendNotification(data) }
123
+ )
124
+ ```
125
+
126
+ No manual NATS subscriptions. No boilerplate. Just **convention over configuration**.
127
+
128
+ <br />
129
+
130
+ ---
131
+
132
+ ## 🧭 Design Principles
133
+
134
+ These principles guide every decision in `pf`:
135
+
136
+ - **🥟 Bun-first DX** — Leverage Bun's speed for installs, tests, and dev mode (npm/yarn fallback supported)
137
+ - **📦 Monorepo-centric** — One repo to rule them all. Turborepo for caching and parallel builds
138
+ - **🏗️ Infrastructure-as-Code by default** — No ClickOps. Every service has Pulumi config
139
+ - **🔁 Event-driven communication baked in** — NATS + JetStream are first-class citizens, not afterthoughts
140
+ - **🌊 DigitalOcean-first, cloud-agnostic later** — Start simple with DO, expand to AWS/GCP when needed
141
+ - **🤖 AI-augmented development** — Use AI to generate complete services, not just snippets
142
+ - **📏 Convention over configuration** — Strong opinions enable automation
63
143
 
64
144
  <br />
65
145
 
@@ -126,30 +206,230 @@ This will:
126
206
  - 🔌 Auto-assign unique ports per service
127
207
  - 📦 Monitor for file changes and hot-reload
128
208
 
209
+ ### 4. Essential workspace commands
210
+
211
+ ```bash
212
+ # Run all tests across the monorepo
213
+ bun test
214
+
215
+ # Lint and format all code with Biome
216
+ bun lint
217
+
218
+ # Build all packages and services
219
+ bun run build
220
+ ```
221
+
129
222
  <br />
130
223
 
131
224
  ---
132
225
 
133
- ## 📁 Workspace Structure
226
+ ## 📘 Typical Workflows
227
+
228
+ Here's how developers actually use `pf` in real-world scenarios:
134
229
 
230
+ ### Workflow 1: Start a New Platform
231
+
232
+ You're building a new product from scratch and want to establish a solid foundation.
233
+
234
+ ```bash
235
+ # Step 1: Scaffold the workspace
236
+ bunx @crossdelta/platform-sdk new workspace orderboss-platform \
237
+ --github-owner orderboss \
238
+ --pulumi-stack dev \
239
+ -y
240
+
241
+ cd orderboss-platform
242
+
243
+ # Step 2: Configure infrastructure (optional)
244
+ # Edit infra/config.ts to customize:
245
+ # - DigitalOcean region (defaults to nyc3)
246
+ # - Kubernetes cluster size
247
+ # - Database instance sizes
248
+
249
+ # Step 3: Start local development
250
+ bun dev
135
251
  ```
136
- my-platform/
137
- ├── .github/
138
- │ ├── workflows/
139
- │ └── actions/
140
- ├── infra/
141
- │ ├── index.ts
142
- │ ├── services/
143
- │ ├── Pulumi.yaml
144
- │ └── Pulumi.dev.yaml
145
- ├── services/
146
- ├── apps/
147
- ├── packages/
148
- ├── package.json
149
- ├── turbo.json
150
- └── biome.json
252
+
253
+ **What you get:**
254
+ - ✅ Turborepo monorepo with Biome linting/formatting
255
+ - ✅ NATS service running in Docker
256
+ - ✅ Pulumi infrastructure setup for DigitalOcean
257
+ - ✅ GitHub Actions workflows for CI/CD
258
+ - ✅ `.env.local` auto-generated with all service ports
259
+
260
+ **Time to first service running:** ~60 seconds
261
+
262
+ ---
263
+
264
+ ### Workflow 2: Add a New Microservice
265
+
266
+ Your platform is growing and you need to add a new service for handling payments.
267
+
268
+ **Option A: Manual scaffolding (fast, predictable)**
269
+
270
+ ```bash
271
+ # Generate a lightweight Hono microservice
272
+ pf new hono-micro services/payments -y
273
+
274
+ # What gets auto-generated:
275
+ # ✅ services/payments/src/index.ts (Hono server)
276
+ # ✅ services/payments/src/handlers/*.event.ts (example event handler)
277
+ # ✅ infra/services/payments.ts (Pulumi K8s config)
278
+ # ✅ services/payments/README.md (service documentation)
279
+ # ✅ Dockerfile + package.json with scripts
280
+ # ✅ Auto-assigned port (e.g., 4003)
281
+
282
+ # Start the service
283
+ bun dev
284
+ ```
285
+
286
+ **Option B: AI-powered generation (intelligent, complete)**
287
+
288
+ ```bash
289
+ # First time: configure AI provider
290
+ pf setup --ai
291
+
292
+ # Generate a complete service with AI
293
+ pf new hono-micro services/payments --ai \
294
+ -d "Stripe payment processing: handle checkout.session.completed webhooks, \
295
+ publish payment.succeeded events, update order status in database"
296
+
297
+ # AI generates:
298
+ # ✅ Complete service implementation with Stripe SDK
299
+ # ✅ Event handlers for incoming orders
300
+ # ✅ Event publishers for payment lifecycle
301
+ # ✅ Use cases with validation logic
302
+ # ✅ Test files for all use cases
303
+ # ✅ Environment variable documentation
304
+ # ✅ Complete README with API docs
305
+ ```
306
+
307
+ **When to use which:**
308
+ - **Manual scaffolding:** You know exactly what you're building, want full control
309
+ - **AI generation:** Bootstrapping new domains, exploring integrations, speeding up prototyping
310
+
311
+ ---
312
+
313
+ ### Workflow 3: Build an Event-Driven Feature
314
+
315
+ You want to send notifications whenever a new order is created.
316
+
317
+ **Step 1: Create the notification service**
318
+
319
+ ```bash
320
+ pf new hono-micro services/notifications -y
321
+ ```
322
+
323
+ **Step 2: Implement the event handler**
324
+
325
+ ```typescript
326
+ // services/notifications/src/handlers/order-created.event.ts
327
+ import { handleEvent } from '@crossdelta/cloudevents'
328
+ import { z } from 'zod'
329
+ import { sendNotification } from '../use-cases/send-notification.use-case'
330
+
331
+ const OrderCreatedSchema = z.object({
332
+ orderId: z.string(),
333
+ customerId: z.string(),
334
+ total: z.number(),
335
+ items: z.array(
336
+ z.object({
337
+ productId: z.string(),
338
+ quantity: z.number(),
339
+ price: z.number(),
340
+ }),
341
+ ).optional(),
342
+ })
343
+
344
+ // Export type for use in use-cases
345
+ export type OrderCreatedEvent = z.infer<typeof OrderCreatedSchema>
346
+
347
+ export default handleEvent(
348
+ {
349
+ schema: OrderCreatedSchema,
350
+ type: 'orders.created', // Event type to subscribe to
351
+ },
352
+ async (data) => {
353
+ await sendNotification(data)
354
+ },
355
+ )
151
356
  ```
152
357
 
358
+ **Step 3: Implement the use case**
359
+
360
+ ```typescript
361
+ // services/notifications/src/use-cases/send-notification.use-case.ts
362
+ import type { OrderCreatedEvent } from '../handlers/order-created.event'
363
+
364
+ export async function sendNotification(data: OrderCreatedEvent): Promise<void> {
365
+ // Full type inference from the event handler schema
366
+ console.log(`Sending notification for order: ${data.orderId}`)
367
+
368
+ // Your notification logic here:
369
+ // - Send email via SendGrid
370
+ // - Push notification via Firebase
371
+ // - SMS via Twilio
372
+ }
373
+ ```
374
+
375
+ **Step 4: Start consuming events**
376
+
377
+ ```typescript
378
+ // services/notifications/src/index.ts
379
+ import '@crossdelta/telemetry' // Must be first import
380
+
381
+ import { consumeJetStreamEvents } from '@crossdelta/cloudevents/transports/nats'
382
+ import { Hono } from 'hono'
383
+
384
+ const port = Number(process.env.PORT || process.env.NOTIFICATIONS_PORT) || 4002
385
+ const app = new Hono()
386
+
387
+ app.get('/health', (c) => c.json({ status: 'ok' }))
388
+
389
+ // Auto-discover and register all event handlers
390
+ consumeJetStreamEvents({
391
+ stream: 'ORDERS',
392
+ subjects: ['orders.>'], // Subscribe to all orders.* events
393
+ consumer: 'notifications',
394
+ discover: './src/handlers/**/*.event.ts',
395
+ })
396
+
397
+ Bun.serve({ port, fetch: app.fetch })
398
+ ```
399
+
400
+ **Step 5: Publish events from the orders service**
401
+
402
+ ```typescript
403
+ // services/orders/src/routes/create-order.ts
404
+ import { publish } from '@crossdelta/cloudevents'
405
+
406
+ export async function createOrder(orderData) {
407
+ const order = await db.orders.create(orderData)
408
+
409
+ // Publish event to NATS
410
+ await publish('orders.created', {
411
+ orderId: order.id,
412
+ customerId: order.customerId,
413
+ total: order.total,
414
+ items: order.items,
415
+ })
416
+
417
+ return order
418
+ }
419
+ ```
420
+
421
+ **How it works:**
422
+ 1. **Publishing:** `publish()` sends a CloudEvent to NATS JetStream
423
+ 2. **Auto-discovery:** `consumeJetStreamEvents()` scans `src/handlers/*.event.ts` and registers handlers
424
+ 3. **Type safety:** Zod schema validates incoming events, provides TypeScript types
425
+ 4. **Decoupling:** Services don't know about each other, only about events
426
+
427
+ **Benefits:**
428
+ - ✅ Zero boilerplate for NATS subscriptions
429
+ - ✅ Type-safe event handling with Zod
430
+ - ✅ Automatic retries and error handling (JetStream guarantees)
431
+ - ✅ Easy to add new consumers without touching existing services
432
+
153
433
  <br />
154
434
 
155
435
  ---
@@ -291,16 +571,38 @@ export const config: K8sServiceConfig = {
291
571
 
292
572
  ## 🚢 Deployment
293
573
 
574
+ ### Local Deployment
575
+
294
576
  ```bash
295
577
  pulumi login
296
578
  pulumi up --stack dev
297
579
  ```
298
580
 
299
- GitHub Secrets:
581
+ ### CI/CD with GitHub Actions
582
+
583
+ Every workspace includes pre-configured GitHub Actions workflows in `.github/workflows/`:
584
+
585
+ | Workflow | Trigger | Purpose |
586
+ |----------|---------|---------|
587
+ | **`lint-and-tests.yml`** | Pull Requests to `main` | Runs `bun lint` and `bun test` on PRs |
588
+ | **`build-and-deploy.yml`** | Push to `main` | Builds Docker images, pushes to GHCR, deploys via Pulumi |
589
+ | **`publish-packages.yml`** | Changes in `packages/` | Auto-publishes packages to npm with versioning |
590
+
591
+ ### Required GitHub Secrets
592
+
593
+ Configure these secrets in your repository settings (`Settings` → `Secrets and variables` → `Actions`):
594
+
595
+ | Secret | Description | Required For |
596
+ |--------|-------------|--------------|
597
+ | `PULUMI_ACCESS_TOKEN` | Pulumi Cloud access token | All deployments |
598
+ | `DIGITALOCEAN_TOKEN` | DigitalOcean API token | Infrastructure provisioning |
599
+ | `NPM_TOKEN` | npm registry token | Package publishing (optional) |
600
+ | `GHCR_TOKEN` | GitHub Container Registry PAT | Docker image publishing |
300
601
 
301
- - `PULUMI_ACCESS_TOKEN`
302
- - `DIGITALOCEAN_TOKEN`
303
- - `NPM_TOKEN` (optional)
602
+ **Get tokens:**
603
+ - **Pulumi:** https://app.pulumi.com/account/tokens
604
+ - **DigitalOcean:** https://cloud.digitalocean.com/account/api/tokens
605
+ - **npm:** https://www.npmjs.com/settings/~/tokens
304
606
 
305
607
  <br />
306
608