<p align="center">
  <img src="https://unpkg.com/@crossdelta/platform-sdk/logo.png" alt="pf" width="140" />
</p>

<h1 align="center">Platform SDK</h1>

<p align="center">
  <strong>The platform toolkit for backend + infrastructure.</strong>
</p>

<p align="center">
  <code>pf</code> scaffolds real-world platforms: backend services, event-driven messaging, infrastructure — all wired together and kept in sync.
</p>

<p align="center">
  <sub>Fontend SDK coming soon — contract-based client models.</sub>

</p>

<br />

<p align="center">
  <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>
  &nbsp;
  <img src="https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript" />
  &nbsp;
  <img src="https://img.shields.io/badge/Bun-000000?style=for-the-badge&logo=bun&logoColor=white" alt="Bun" />
  &nbsp;
  <img src="https://img.shields.io/badge/License-MIT-22c55e?style=for-the-badge" alt="MIT License" />
</p>

<p align="center">
  <img src="https://img.shields.io/badge/Hono-E36002?style=flat-square&logo=hono&logoColor=white" alt="Hono" />
  <img src="https://img.shields.io/badge/NestJS-E0234E?style=flat-square&logo=nestjs&logoColor=white" alt="NestJS" />
  <img src="https://img.shields.io/badge/NATS-27AAE1?style=flat-square&logo=natsdotio&logoColor=white" alt="NATS" />
  <img src="https://img.shields.io/badge/Pulumi-8A3391?style=flat-square&logo=pulumi&logoColor=white" alt="Pulumi" />
  <img src="https://img.shields.io/badge/Turborepo-EF4444?style=flat-square&logo=turborepo&logoColor=white" alt="Turborepo" />
  <img src="https://img.shields.io/badge/DigitalOcean-0080FF?style=flat-square&logo=digitalocean&logoColor=white" alt="DigitalOcean" />
</p>

<br />

<p align="center">
  <a href="#-quick-start">Quick Start</a> •
  <a href="#-5-minute-tutorial">Tutorial</a> •
  <a href="#-commands">Commands</a> •
  <a href="#-ai-assisted-generation">AI Generation</a> •
  <a href="#-deployment">Deployment</a>
</p>

---

<br />

## 👋 Who is this for?

<table>
<tr>
<td width="60">🏗️</td>
<td><strong>Teams building full-stack platforms</strong> — services, events, apps that actually work together</td>
</tr>
<tr>
<td>📡</td>
<td><strong>Event-driven architectures</strong> — or teams ready to start using events</td>
</tr>
<tr>
<td>😤</td>
<td><strong>Engineers tired of drift</strong> — ports, env vars, and infra configs that never match</td>
</tr>
<tr>
<td>🚀</td>
<td><strong>Startups & platform teams</strong> — who want consistency without building everything from scratch</td>
</tr>
</table>

> **You can start without understanding all the pieces.**
> The depth reveals itself as you grow.

---

## 🚀 Quick Start

```bash
# 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 | bash
```

> **Windows users:** Use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) for the best experience.

**Then create your first platform:**
```bash
pf new workspace my-platform -y && cd my-platform
pf dev
```

<br />

<table>
<tr>
<td>✅</td>
<td>Turborepo monorepo with Biome linting</td>
</tr>
<tr>
<td>✅</td>
<td>NATS + JetStream for event-driven messaging</td>
</tr>
<tr>
<td>✅</td>
<td>Pulumi infrastructure ready for deployment</td>
</tr>
<tr>
<td>✅</td>
<td>Auto-generated <code>.env.local</code> with service ports</td>
</tr>
</table>

---

## 🎯 5-Minute Tutorial

Add two services and wire an event between them:

```bash
# 1. Create two services
pf new hono-micro services/orders
pf new hono-micro services/notifications

# 2. Wire an event from orders → notifications
pf event add orders.created --service services/notifications

# 3. Restart to pick up new services
pf dev

# 4. Publish a test event — watch notifications react
pf event publish orders.created
```

<br />

<table>
<tr>
<th align="left">What just happened</th>
</tr>
<tr>
<td>
  📦 Two services with health checks and Pulumi configs<br />
  📝 One event contract in <code>packages/contracts/</code><br />
  🔗 One handler that auto-discovers the event<br />
  ✨ <strong>No manual wiring. No config drift. Everything is explicit.</strong>
</td>
</tr>
</table>

---

## 💡 What problem does pf solve?

<table>
<tr>
<th width="300">❌ The Problem</th>
<th width="300">✅ With pf</th>
</tr>
<tr>
<td>Application code lives here, infra config lives somewhere else</td>
<td>Both generated from the same source of truth</td>
</tr>
<tr>
<td>Ports, env vars, and event wiring drift over time</td>
<td>Change a service → infra stays in sync</td>
</tr>
<tr>
<td>New devs spend days setting up local environments</td>
<td>Run <code>pf dev</code> → everything starts together</td>
</tr>
<tr>
<td>Manual event subscriptions and handler registration</td>
<td>Add an event → consumers are wired automatically</td>
</tr>
</table>

---

## 📋 Commands

| Command | What it does |
|:--------|:-------------|
| `pf dev` | 🚀 Start 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 event add <type> --service <path>` | 🔗 Add contract + handler + wire stream |
| `pf event list` | 📋 List available events and consumers |
| `pf event publish <type>` | 📤 Publish mock event to NATS |
| `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 |

> `pf` proxies all commands to Turborepo (works from any subdirectory).

---

## 🤖 AI-Assisted Generation

> **Optional but powerful.** Configure once, then use `--ai` with any generator.

```bash
pf setup --ai   # OpenAI or Anthropic

pf new hono-micro services/notifications --ai \
  -d "Send emails when orders are created"
```

AI generates service code, event handlers, use cases, and tests — **all following the same conventions you'd write by hand.**

---

## 🚢 Deployment

```bash
pf pulumi up --stack dev
```

**Pre-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 |

<details>
<summary>🔐 Required secrets</summary>

<br />

| Secret | Description |
|:-------|:------------|
| `PULUMI_ACCESS_TOKEN` | [Pulumi Cloud token](https://app.pulumi.com/account/tokens) |
| `DIGITALOCEAN_TOKEN` | [DO API token](https://cloud.digitalocean.com/account/api/tokens) |
| `GHCR_TOKEN` | GitHub Container Registry PAT |

</details>

---

## 📋 Requirements

| Requirement | Notes |
|:------------|:------|
| **Bun** or Node.js ≥ 21 | Bun recommended for speed |
| **[Pulumi CLI](https://www.pulumi.com/docs/install/)** | For infrastructure deployment |
| **[Docker](https://www.docker.com/)** | For local NATS |

---

## 🔍 Under the Hood

<details>
<summary>📁 <b>Project structure</b></summary>

<br />

```
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 infra
```

**The key insight:** Services define their ports, env vars, and event subscriptions once. Everything else is derived.

</details>

<details>
<summary>🛠️ <b>Tech stack</b></summary>

<br />

| Layer | Technology |
|:------|:-----------|
| Monorepo | [Turborepo](https://turbo.build/repo) |
| Services | [Hono](https://hono.dev) or [NestJS](https://nestjs.com) |
| Messaging | [NATS](https://nats.io) + JetStream |
| Events | [CloudEvents](https://cloudevents.io) + Zod validation |
| Infrastructure | [Pulumi](https://pulumi.com) → DigitalOcean (extensible) |
| Runtime | [Bun](https://bun.sh) |

</details>

<details>
<summary>⚙️ <b>Workspace configuration</b></summary>

<br />

```json
{
  "pf": {
    "registry": "my-org/my-platform",
    "commands": {
      "deploy": { "cwd": "infra", "command": "pulumi up --yes" }
    },
    "paths": {
      "services": { "path": "services", "watch": true },
      "contracts": { "path": "packages/contracts" }
    }
  }
}
```

</details>

<details>
<summary>🏗️ <b>Service infrastructure config</b></summary>

<br />

```typescript
// 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 config
```

See [@crossdelta/infrastructure](https://www.npmjs.com/package/@crossdelta/infrastructure) for full options.

</details>

<details>
<summary>📡 <b>Event-driven architecture</b></summary>

<br />

**Contracts** live in `packages/contracts/` as the single source of truth:

```typescript
// packages/contracts/src/events/orders-created.ts
import { createContract } from '@crossdelta/cloudevents'
import { z } from 'zod'

export const OrdersCreatedContract = createContract({
  type: 'orders.created',
  schema: z.object({ orderId: z.string(), total: z.number() }),
})
```

**Handlers** import contracts and delegate to use cases:

```typescript
// services/notifications/src/events/orders-created.event.ts
import { handleEvent } from '@crossdelta/cloudevents'
import { OrdersCreatedContract } from '@my-org/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.

</details>

<details>
<summary>⚡ <b>NATS message broker</b></summary>

<br />

Every workspace includes pre-configured NATS with JetStream:

- Auto-started with `pf dev`
- Ports: `4222` (client), `8222` (monitoring)
- Health check: `curl http://localhost:8222/healthz`

</details>

---

## 🧭 Philosophy

<table>
<tr>
<td width="50%">

### ✅ What pf is

- Opinionated full-stack platform toolkit
- Unified dev workflow — one command to run everything
- Infrastructure that stays in sync with your code

</td>
<td width="50%">

### ❌ 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

</td>
</tr>
</table>

> **`pf` makes architectural decisions so your team doesn't have to — without hiding how things work.**

---

<p align="center">
  <strong>MIT © crossdelta</strong>
</p>
