@crossdelta/platform-sdk
v0.21.0
Published
Platform toolkit for event-driven microservices — keeping code and infrastructure in lockstep.
Downloads
2,132
Maintainers
Readme
👋 Who is this for?
You can start without understanding all the pieces. The depth reveals itself as you grow.
🚀 Quick Start
# With Bun
bun add -g @crossdelta/platform-sdk
# With npm
npm install -g @crossdelta/platform-sdk
# Or auto-install (macOS/Linux/WSL)
curl -fsSL https://unpkg.com/@crossdelta/platform-sdk@latest/install.sh | bashWindows users: Use WSL for the best experience.
Then create your first platform:
pf new workspace my-platform -y && cd my-platform
pf dev🎯 5-Minute Tutorial
Add two services and wire an event between them:
# 1. Create two services
pf new hono-micro services/orders
pf new hono-micro services/notifications
# 2. Wire an event from orders → notifications
pf cloudevents add order.created --service services/notifications
# 3. Restart to pick up new services
pf dev
# 4. Publish a test event — watch notifications react
pf cloudevents publish order.created💡 What problem does pf solve?
📋 Commands
| Command | What it does |
|:--------|:-------------|
| pf dev | 🚀 Start development infrastructure (NATS) + all services in watch mode |
| pf new hono-micro <path> | ⚡ Create a Hono service (lightweight, Bun-optimized) |
| pf new nest-micro <path> | 🏢 Create a NestJS service (full-featured, decorators) |
| pf cloudevents add <type> --service <path> | 🔗 Add contract + handler + wire stream |
| pf cloudevents list | 📋 List available events and consumers |
| pf cloudevents publish <type> | 📤 Publish mock event to NATS |
| pf setup completion | 🔧 Install shell autocompletion (zsh/bash) |
| pf setup ai | 🤖 Configure AI provider for code generation |
| pf test | 🧪 Run tests across the monorepo |
| pf build | 📦 Build all packages and services |
| pf lint | ✨ Lint and format with Biome |
| pf audit | 🔒 Run security audit on dependencies |
pfproxies all commands to Turborepo (works from any subdirectory).
Custom Command Descriptions
Add descriptions to your workspace commands for better autocompletion:
{
"pf": {
"commands": {
"deploy": {
"command": "cd infra && pulumi up",
"description": "Deploy infrastructure to production"
}
}
}
}Then pf <TAB> shows: deploy -- Deploy infrastructure to production
Plugins
Extend pf with additional commands via plugins. Plugins are loaded from workspace package.json:
{
"pf": {
"plugins": ["@crossdelta/cloudevents"]
}
}Built-in plugins:
@crossdelta/cloudevents- Event contracts, handlers, and NATS publishing (auto-enabled in new workspaces)
Plugin commands are namespaced:
pf cloudevents add order.created --service services/orders
pf cloudevents list
pf cloudevents publish order.created🤖 AI-Assisted Generation
Optional but powerful. Configure once, then use
--aiwith any generator.
pf setup ai # OpenAI or Anthropic
pf new hono-micro services/notifications --ai \
-d "Send emails when orders are created"The AI generates production-ready code that follows your platform's conventions:
What makes it different:
- Uses your existing contracts when wiring events between services
- Follows framework-specific patterns (Hono use-cases vs. NestJS Services)
- Generates only the code you need — no bloat, no unused abstractions
- Hot-reload friendly —
pf devautomatically restarts when AI finishes
Works with OpenAI (GPT-4o) and Anthropic (Claude 3.5 Sonnet).
🚢 Deployment
pf pulumi up --stack devPre-configured GitHub Actions included:
| Trigger | Action |
|:--------|:-------|
| PR to main | 🧪 Lint + test |
| Push to main | 🚀 Build, push to GHCR, deploy |
| Changes in packages/ | 📦 Publish to npm |
| Secret | Description |
|:-------|:------------|
| PULUMI_ACCESS_TOKEN | Pulumi Cloud token |
| DIGITALOCEAN_TOKEN | DO API token |
| GHCR_TOKEN | GitHub Container Registry PAT |
📋 Requirements
| Requirement | Notes | |:------------|:------| | Bun or Node.js ≥ 21 | Bun recommended for speed | | Pulumi CLI | For infrastructure deployment | | Docker | For local NATS |
🔍 Under the Hood
my-platform/
├── services/ # Your microservices
│ ├── orders/
│ └── notifications/
├── packages/
│ └── contracts/ # Event contracts (single source of truth)
├── infra/
│ └── services/ # Auto-generated Pulumi configs
└── .env.local # Auto-generated from infraThe key insight: Services define their ports, env vars, and event subscriptions once. Everything else is derived.
| Layer | Technology | |:------|:-----------| | Monorepo | Turborepo | | Services | Hono or NestJS | | Messaging | NATS + JetStream | | Events | @crossdelta/cloudevents (CloudEvents + Zod) | | Infrastructure | Pulumi → DigitalOcean (extensible) | | Runtime | Bun |
{
"pf": {
"registry": "my-org/my-platform",
"commands": {
"deploy": {
"cwd": "infra",
"command": "pulumi up --yes",
"description": "Deploy infrastructure to production"
}
},
"paths": {
"services": { "path": "services", "watch": true },
"contracts": { "path": "packages/contracts" }
}
}
}// infra/services/orders.ts
import { ports, type K8sServiceConfig } from '@crossdelta/infrastructure'
const config: K8sServiceConfig = {
name: 'orders',
ports: ports().http(4001).build(),
replicas: 1,
healthCheck: { httpPath: '/health' },
resources: {
requests: { cpu: '50m', memory: '64Mi' },
limits: { cpu: '150m', memory: '128Mi' },
},
}
export default configSee @crossdelta/infrastructure for full options.
Contracts live in packages/contracts/ as the single source of truth:
// packages/contracts/src/events/orders/created.ts
import { createContract } from '@crossdelta/cloudevents'
import { z } from 'zod'
export const OrderCreatedSchema = z.object({
orderId: z.string(),
total: z.number(),
})
export type OrderCreatedData = z.infer<typeof OrderCreatedSchema>
export const OrdersCreatedContract = createContract({
type: 'order.created',
channel: { stream: 'ORDERS' },
schema: OrderCreatedSchema,
})Handlers import contracts and delegate to use cases:
// services/notifications/src/events/order-created.handler.ts
import { handleEvent } from '@crossdelta/cloudevents'
import { OrdersCreatedContract } from '@my-platform/contracts'
import { sendOrderNotification } from '../use-cases/send-notification.use-case'
export default handleEvent(OrdersCreatedContract, async (data) => {
await sendOrderNotification(data)
})Handlers are auto-discovered. No manual subscriptions.
See @crossdelta/cloudevents for full event API.
Local NATS started by
pf devis development infrastructure. Production streams are materialized via Pulumi from contracts.
Every workspace includes pre-configured NATS with JetStream for development:
Development:
- Auto-started with
pf dev(docker-based, ephemeral) - Ports:
4222(client),8222(monitoring) - Health check:
curl http://localhost:8222/healthz - Streams: Auto-created by
pf devfrom contracts (memory, 1h retention)
Production:
- Deployed via Pulumi (
infra/) - Streams materialized from contracts with retention policies
- Persistent storage (file-based) with explicit limits
Services never create streams — in dev:
pf devauto-creates from contracts, in prod: Pulumi materializes.
See infra/dev/README.md for Dev-Infra vs Platform-Infra separation.
🧭 Philosophy
✅ What pf is
- Opinionated full-stack platform toolkit
- Unified dev workflow — one command to run everything
- Infrastructure that stays in sync with your code
❌ What pf is not
- Generic scaffolder — it makes choices for you
- Multi-cloud out of the box — DigitalOcean first, extensible later
- Magic — no hidden daemons, you control when things run
pfmakes architectural decisions so your team doesn't have to — without hiding how things work.
License
MIT © crossdelta
