@crossdelta/platform-sdk 0.5.13 β 0.7.0
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 +132 -196
- package/bin/cli.js +215 -152
- package/bin/services/ai/instructions/ai-instructions.md +734 -45
- package/bin/templates/workspace/.github/copilot-instructions.md.hbs +56 -694
- package/bin/templates/workspace/.github/workflows/publish-packages.yml +6 -7
- package/bin/templates/workspace/package.json.hbs +21 -3
- package/bin/templates/workspace/packages/contracts/README.md.hbs +134 -0
- package/bin/templates/workspace/packages/contracts/package.json.hbs +19 -0
- package/bin/templates/workspace/packages/contracts/src/index.ts +8 -0
- package/bin/templates/workspace/packages/contracts/tsconfig.json.hbs +8 -0
- package/install.sh +160 -0
- package/package.json +30 -10
package/README.md
CHANGED
|
@@ -26,44 +26,85 @@
|
|
|
26
26
|
<img src="https://img.shields.io/badge/DigitalOcean-App_Platform_|_DOKS-0080FF?style=flat-square" alt="DigitalOcean" />
|
|
27
27
|
</p>
|
|
28
28
|
|
|
29
|
+
<p align="center">
|
|
30
|
+
<em><code>pf</code> is not a framework β it's a set of conventions that wire code and infrastructure together.</em>
|
|
31
|
+
</p>
|
|
32
|
+
|
|
29
33
|
<br />
|
|
30
34
|
|
|
31
35
|
---
|
|
32
36
|
## π Quick Start
|
|
33
37
|
|
|
34
38
|
```bash
|
|
35
|
-
# Create workspace (works with npx/
|
|
39
|
+
# Create workspace (works with bunx/npx/pnpm dlx/yarn dlx)
|
|
36
40
|
bunx @crossdelta/platform-sdk new workspace my-platform -y
|
|
37
41
|
cd my-platform
|
|
38
42
|
|
|
39
|
-
# Generate
|
|
40
|
-
# (first time: you'll be prompted to configure an AI provider)
|
|
43
|
+
# Generate microservice with AI (first time: run `pf setup --ai` to configure provider)
|
|
41
44
|
bunx @crossdelta/platform-sdk new hono-micro services/orders --ai \
|
|
42
|
-
-d "Handle order creation and payment
|
|
45
|
+
-d "Handle order creation and payment"
|
|
43
46
|
|
|
44
|
-
# Start
|
|
45
|
-
|
|
47
|
+
# Start development
|
|
48
|
+
pf dev
|
|
46
49
|
```
|
|
47
50
|
|
|
48
|
-
**
|
|
51
|
+
**What you get in minutes:**
|
|
49
52
|
|
|
50
|
-
|
|
53
|
+
- β Turborepo monorepo with Biome linting
|
|
54
|
+
- β NATS + event-driven microservices
|
|
55
|
+
- β Pulumi infrastructure (`infra/services/*.ts`)
|
|
56
|
+
- β Auto-generated `.env.local` with service ports
|
|
57
|
+
- β AI-generated service code
|
|
51
58
|
|
|
52
|
-
|
|
53
|
-
|
|
59
|
+
All wired automatically. Local + production infra in lockstep.
|
|
60
|
+
|
|
61
|
+
### β‘ One CLI for everything
|
|
62
|
+
|
|
63
|
+
`pf` runs your workspace scripts from anywhere:
|
|
54
64
|
|
|
55
65
|
```bash
|
|
56
|
-
#
|
|
57
|
-
|
|
66
|
+
pf test # β turbo run test
|
|
67
|
+
pf build # β turbo run build
|
|
68
|
+
pf pulumi up # β runs in infra/ directory
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Why:** Keeps commands consistent across the team and works from any subdirectory.
|
|
72
|
+
|
|
73
|
+
**How it works:** `pf` walks up to find your workspace root, then proxies to your scripts. Configured commands via `pf.commands` can override the working directory or command. Registered `pf` commands (like `pf new`) take precedence.
|
|
74
|
+
|
|
75
|
+
> **π Note:** You can be productive with `pf` in minutes using the commands above. The sections below are reference documentationβexplore them when you need specific details.
|
|
76
|
+
|
|
77
|
+
### No runtime installed?
|
|
78
|
+
|
|
79
|
+
If you don't have a JavaScript runtime, use our installer that sets everything up.
|
|
80
|
+
**The installer is optional** β you can always use `bunx`/`npx` or a global install.
|
|
58
81
|
|
|
59
|
-
|
|
60
|
-
npm install
|
|
82
|
+
```bash
|
|
83
|
+
# Auto-installer (detects bun/pnpm/yarn/npm; if none found it can install Bun)
|
|
84
|
+
curl -fsSL https://unpkg.com/@crossdelta/platform-sdk@latest/install.sh | bash
|
|
61
85
|
|
|
62
86
|
# Then use short commands
|
|
63
87
|
pf new workspace my-platform
|
|
64
88
|
pf new hono-micro services/orders --ai -d "..."
|
|
65
89
|
```
|
|
66
90
|
|
|
91
|
+
> **Security:** The installer asks permission before installing Bun (if needed) and does not run any workspace commands automatically.
|
|
92
|
+
|
|
93
|
+
<details>
|
|
94
|
+
<summary><strong>π What does the installer do?</strong></summary>
|
|
95
|
+
|
|
96
|
+
The script ([view source](https://unpkg.com/@crossdelta/platform-sdk@latest/install.sh)):
|
|
97
|
+
1. Checks for bun/pnpm/yarn/npm
|
|
98
|
+
2. If none found, **asks permission** to install Bun
|
|
99
|
+
3. Installs `pf` CLI globally
|
|
100
|
+
|
|
101
|
+
**Manual installation:**
|
|
102
|
+
```bash
|
|
103
|
+
# With Bun/npm/yarn/pnpm
|
|
104
|
+
bun add -g @crossdelta/platform-sdk
|
|
105
|
+
# or: npm install -g @crossdelta/platform-sdk
|
|
106
|
+
```
|
|
107
|
+
|
|
67
108
|
</details>
|
|
68
109
|
|
|
69
110
|
<br />
|
|
@@ -91,7 +132,7 @@ pf new hono-micro services/orders --ai -d "..."
|
|
|
91
132
|
- **Opinionated CLI** for event-driven microservice platforms
|
|
92
133
|
- **[Turborepo](https://turbo.build/repo) scaffolder** with [Pulumi](https://www.pulumi.com) IaC and [NATS](https://nats.io) messaging built-in
|
|
93
134
|
- **AI-assisted code generator** that creates service boilerplate from descriptions
|
|
94
|
-
- **Unified dev workflow** β one command to run all services (`
|
|
135
|
+
- **Unified dev workflow** β one command to run all services (`pf dev`)
|
|
95
136
|
- **[DigitalOcean](https://www.digitalocean.com)-first** deployment target (App Platform + DOKS)
|
|
96
137
|
|
|
97
138
|
### β `pf` is not:
|
|
@@ -101,6 +142,8 @@ pf new hono-micro services/orders --ai -d "..."
|
|
|
101
142
|
- **Runtime manager** β scaffolds code, you deploy with Pulumi
|
|
102
143
|
- **Kubernetes replacement** β generates K8s configs via Pulumi
|
|
103
144
|
|
|
145
|
+
**Note:** `pf` runs nothing implicitlyβno hidden daemons, no automatic deployments or provisioning. You maintain explicit control over when code runs and infrastructure is deployed.
|
|
146
|
+
|
|
104
147
|
### π₯ Why `pf` vs other tools?
|
|
105
148
|
|
|
106
149
|
- **π Lockstep infra + code** β `infra/services/*.ts` auto-generated from service metadata, ports/env derived automatically
|
|
@@ -141,7 +184,7 @@ my-platform/
|
|
|
141
184
|
1. **NATS + JetStream baseline** β Event-driven communication is built-in, not bolted on
|
|
142
185
|
2. **Infrastructure-as-Code by default** β Every service has a matching `infra/services/<name>.ts` config
|
|
143
186
|
3. **Auto-wiring everywhere** β Ports, env vars, and NATS subjects are derived automatically
|
|
144
|
-
4. **Opinionated conventions** β Event handlers live in `src/
|
|
187
|
+
4. **Opinionated conventions** β Event handlers live in `src/events/*.event.ts`, business logic in `src/use-cases/*.use-case.ts`
|
|
145
188
|
5. **Bun-first DX** β Ultra-fast installs, tests, and dev server with fallback to npm/yarn
|
|
146
189
|
|
|
147
190
|
### Event-Driven Mental Model
|
|
@@ -153,7 +196,7 @@ Services communicate via **CloudEvents** over **NATS JetStream**:
|
|
|
153
196
|
await publish('orders.created', { orderId: '123', total: 99.99 })
|
|
154
197
|
|
|
155
198
|
// Service B auto-discovers and handles it
|
|
156
|
-
// File: services/notifications/src/
|
|
199
|
+
// File: services/notifications/src/events/order-created.event.ts
|
|
157
200
|
export default handleEvent(
|
|
158
201
|
{ schema: OrderCreatedSchema, type: 'orders.created' },
|
|
159
202
|
async (data) => { await sendNotification(data) }
|
|
@@ -186,6 +229,15 @@ These principles guide every decision in `pf`:
|
|
|
186
229
|
|
|
187
230
|
`pf` is designed for **small to mid-sized teams building event-driven systems** who want strong conventions, infra parity, and fast onboarding β without maintaining their own internal platform.
|
|
188
231
|
|
|
232
|
+
<br />
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## π Deep Dive & Reference
|
|
237
|
+
|
|
238
|
+
The sections below cover architectural details, workflows, and advanced usage.
|
|
239
|
+
**You don't need to read them to get started** β they're here when you need them.
|
|
240
|
+
|
|
189
241
|
---
|
|
190
242
|
|
|
191
243
|
## π Workflows
|
|
@@ -220,7 +272,7 @@ pf new hono-micro services/payments --ai \
|
|
|
220
272
|
|
|
221
273
|
```bash
|
|
222
274
|
cd my-platform
|
|
223
|
-
|
|
275
|
+
pf dev
|
|
224
276
|
```
|
|
225
277
|
|
|
226
278
|
This will:
|
|
@@ -233,15 +285,55 @@ This will:
|
|
|
233
285
|
|
|
234
286
|
```bash
|
|
235
287
|
# Run all tests across the monorepo
|
|
236
|
-
|
|
288
|
+
pf test
|
|
237
289
|
|
|
238
290
|
# Lint and format all code with Biome
|
|
239
|
-
|
|
291
|
+
pf lint
|
|
240
292
|
|
|
241
293
|
# Build all packages and services
|
|
242
|
-
|
|
294
|
+
pf build
|
|
243
295
|
```
|
|
244
296
|
|
|
297
|
+
### 5. Workspace Configuration
|
|
298
|
+
|
|
299
|
+
Configure workspace behavior via the `pf` field in your root `package.json`:
|
|
300
|
+
|
|
301
|
+
```json
|
|
302
|
+
{
|
|
303
|
+
"pf": {
|
|
304
|
+
"registry": "my-platform/platform",
|
|
305
|
+
"commands": {
|
|
306
|
+
"pulumi": { "cwd": "infra" },
|
|
307
|
+
"deploy": { "cwd": "infra", "command": "pulumi up --yes" }
|
|
308
|
+
},
|
|
309
|
+
"paths": {
|
|
310
|
+
"services": {
|
|
311
|
+
"path": "services",
|
|
312
|
+
"watch": true
|
|
313
|
+
},
|
|
314
|
+
"apps": {
|
|
315
|
+
"path": "apps",
|
|
316
|
+
"watch": true
|
|
317
|
+
},
|
|
318
|
+
"packages": {
|
|
319
|
+
"path": "packages",
|
|
320
|
+
"watch": true,
|
|
321
|
+
"ignorePatterns": ["packages/some-internal-package"]
|
|
322
|
+
},
|
|
323
|
+
"contracts": {
|
|
324
|
+
"path": "packages/contracts"
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
**Options:**
|
|
332
|
+
- `commands`: Custom command shortcuts with `cwd` (working directory) and `command` overrides
|
|
333
|
+
- `paths.<type>.path`: Directory path for each workspace type
|
|
334
|
+
- `paths.<type>.watch`: Whether to watch this directory in dev mode (default: `false`)
|
|
335
|
+
- `paths.<type>.ignorePatterns`: Glob patterns to ignore within this path (optional)
|
|
336
|
+
|
|
245
337
|
<br />
|
|
246
338
|
|
|
247
339
|
---
|
|
@@ -250,7 +342,10 @@ bun run build
|
|
|
250
342
|
|
|
251
343
|
Here's how developers actually use `pf` in real-world scenarios:
|
|
252
344
|
|
|
253
|
-
|
|
345
|
+
<details>
|
|
346
|
+
<summary><strong>Workflow 1: Start a New Platform</strong></summary>
|
|
347
|
+
|
|
348
|
+
<br />
|
|
254
349
|
|
|
255
350
|
You're building a new product from scratch and want to establish a solid foundation.
|
|
256
351
|
|
|
@@ -270,7 +365,7 @@ cd my-platform
|
|
|
270
365
|
# - Database instance sizes
|
|
271
366
|
|
|
272
367
|
# Step 3: Start local development
|
|
273
|
-
|
|
368
|
+
pf dev
|
|
274
369
|
```
|
|
275
370
|
|
|
276
371
|
**What you get:**
|
|
@@ -280,182 +375,23 @@ bun dev
|
|
|
280
375
|
- β
GitHub Actions workflows for CI/CD
|
|
281
376
|
- β
`.env.local` auto-generated with all service ports
|
|
282
377
|
|
|
283
|
-
|
|
378
|
+
</details>
|
|
284
379
|
|
|
285
380
|
---
|
|
286
381
|
|
|
287
382
|
### Workflow 2: Add a New Microservice
|
|
288
383
|
|
|
289
|
-
Your platform is growing and you need to add a new service for handling payments.
|
|
290
|
-
|
|
291
|
-
**Option A: Manual scaffolding (fast, predictable)**
|
|
292
|
-
|
|
293
|
-
```bash
|
|
294
|
-
# Generate a lightweight Hono microservice
|
|
295
|
-
pf new hono-micro services/payments -y
|
|
296
|
-
|
|
297
|
-
# What gets auto-generated:
|
|
298
|
-
# β
services/payments/src/index.ts (Hono server)
|
|
299
|
-
# β
services/payments/src/handlers/*.event.ts (example event handler)
|
|
300
|
-
# β
infra/services/payments.ts (Pulumi K8s config)
|
|
301
|
-
# β
services/payments/README.md (service documentation)
|
|
302
|
-
# β
Dockerfile + package.json with scripts
|
|
303
|
-
# β
Auto-assigned port (e.g., 4003)
|
|
304
|
-
|
|
305
|
-
# Start the service
|
|
306
|
-
bun dev
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
**Option B: AI-powered generation (intelligent scaffolding)**
|
|
310
|
-
|
|
311
384
|
```bash
|
|
312
|
-
#
|
|
313
|
-
pf setup --ai
|
|
314
|
-
|
|
315
|
-
# Generate service boilerplate with AI
|
|
385
|
+
# Generate with AI (optional: use --ai flag)
|
|
316
386
|
pf new hono-micro services/payments --ai \
|
|
317
|
-
-d "Stripe payment processing: handle
|
|
318
|
-
publish payment.succeeded events, update order status in database"
|
|
319
|
-
|
|
320
|
-
# AI generates:
|
|
321
|
-
# β
Service structure with Stripe SDK integration
|
|
322
|
-
# β
Event handler stubs for incoming webhooks
|
|
323
|
-
# β
Event publisher functions for payment lifecycle
|
|
324
|
-
# β
Use case templates with validation logic
|
|
325
|
-
# β
Test file boilerplate
|
|
326
|
-
# β
Environment variable documentation
|
|
327
|
-
# β
Basic README with usage examples
|
|
328
|
-
```
|
|
329
|
-
|
|
330
|
-
**When to use which:**
|
|
331
|
-
- **Manual scaffolding:** You know exactly what you're building, want full control
|
|
332
|
-
- **AI generation:** Quick prototyping, exploring new integrations, generating boilerplate faster
|
|
333
|
-
|
|
334
|
-
**Note:** AI-generated code is a starting point, not production-ready. Always review and refine.
|
|
335
|
-
|
|
336
|
-
---
|
|
337
|
-
|
|
338
|
-
### Workflow 3: Build an Event-Driven Feature
|
|
339
|
-
|
|
340
|
-
You want to send notifications whenever a new order is created.
|
|
387
|
+
-d "Stripe payment processing: handle webhooks, publish events"
|
|
341
388
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
pf new hono-micro services/notifications -y
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
**Step 2: Implement the event handler**
|
|
349
|
-
|
|
350
|
-
```typescript
|
|
351
|
-
// services/notifications/src/handlers/order-created.event.ts
|
|
352
|
-
import { handleEvent } from '@crossdelta/cloudevents'
|
|
353
|
-
import { z } from 'zod'
|
|
354
|
-
import { sendNotification } from '../use-cases/send-notification.use-case'
|
|
355
|
-
|
|
356
|
-
const OrderCreatedSchema = z.object({
|
|
357
|
-
orderId: z.string(),
|
|
358
|
-
customerId: z.string(),
|
|
359
|
-
total: z.number(),
|
|
360
|
-
items: z.array(
|
|
361
|
-
z.object({
|
|
362
|
-
productId: z.string(),
|
|
363
|
-
quantity: z.number(),
|
|
364
|
-
price: z.number(),
|
|
365
|
-
}),
|
|
366
|
-
).optional(),
|
|
367
|
-
})
|
|
368
|
-
|
|
369
|
-
// Export type for use in use-cases
|
|
370
|
-
export type OrderCreatedEvent = z.infer<typeof OrderCreatedSchema>
|
|
371
|
-
|
|
372
|
-
export default handleEvent(
|
|
373
|
-
{
|
|
374
|
-
schema: OrderCreatedSchema,
|
|
375
|
-
type: 'orders.created', // Event type to subscribe to
|
|
376
|
-
},
|
|
377
|
-
async (data) => {
|
|
378
|
-
await sendNotification(data)
|
|
379
|
-
},
|
|
380
|
-
)
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
**Step 3: Implement the use case**
|
|
384
|
-
|
|
385
|
-
```typescript
|
|
386
|
-
// services/notifications/src/use-cases/send-notification.use-case.ts
|
|
387
|
-
import type { OrderCreatedEvent } from '../handlers/order-created.event'
|
|
388
|
-
|
|
389
|
-
export async function sendNotification(data: OrderCreatedEvent): Promise<void> {
|
|
390
|
-
// Full type inference from the event handler schema
|
|
391
|
-
console.log(`Sending notification for order: ${data.orderId}`)
|
|
392
|
-
|
|
393
|
-
// Your notification logic here:
|
|
394
|
-
// - Send email via SendGrid
|
|
395
|
-
// - Push notification via Firebase
|
|
396
|
-
// - SMS via Twilio
|
|
397
|
-
}
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
**Step 4: Start consuming events**
|
|
401
|
-
|
|
402
|
-
```typescript
|
|
403
|
-
// services/notifications/src/index.ts
|
|
404
|
-
import '@crossdelta/telemetry' // Must be first import
|
|
405
|
-
|
|
406
|
-
import { consumeJetStreamEvents } from '@crossdelta/cloudevents'
|
|
407
|
-
import { Hono } from 'hono'
|
|
408
|
-
|
|
409
|
-
const port = Number(process.env.PORT || process.env.NOTIFICATIONS_PORT) || 4002
|
|
410
|
-
const app = new Hono()
|
|
411
|
-
|
|
412
|
-
app.get('/health', (c) => c.json({ status: 'ok' }))
|
|
413
|
-
|
|
414
|
-
// Auto-discover and register all event handlers
|
|
415
|
-
consumeJetStreamEvents({
|
|
416
|
-
stream: 'ORDERS',
|
|
417
|
-
subjects: ['orders.>'], // Subscribe to all orders.* events
|
|
418
|
-
consumer: 'notifications',
|
|
419
|
-
discover: './src/handlers/**/*.event.ts',
|
|
420
|
-
})
|
|
421
|
-
|
|
422
|
-
Bun.serve({ port, fetch: app.fetch })
|
|
423
|
-
```
|
|
424
|
-
|
|
425
|
-
**Step 5: Publish events from the orders service**
|
|
426
|
-
|
|
427
|
-
```typescript
|
|
428
|
-
// services/orders/src/routes/create-order.ts
|
|
429
|
-
import { publish } from '@crossdelta/cloudevents'
|
|
430
|
-
|
|
431
|
-
export async function createOrder(orderData) {
|
|
432
|
-
const order = await db.orders.create(orderData)
|
|
433
|
-
|
|
434
|
-
// Publish event to NATS
|
|
435
|
-
await publish('orders.created', {
|
|
436
|
-
orderId: order.id,
|
|
437
|
-
customerId: order.customerId,
|
|
438
|
-
total: order.total,
|
|
439
|
-
items: order.items,
|
|
440
|
-
})
|
|
441
|
-
|
|
442
|
-
return order
|
|
443
|
-
}
|
|
389
|
+
# Auto-generated: service code + infra config + tests + README
|
|
390
|
+
# Start the service
|
|
391
|
+
pf dev
|
|
444
392
|
```
|
|
445
393
|
|
|
446
|
-
**
|
|
447
|
-
1. **Publishing:** `publish()` sends a CloudEvent to NATS JetStream
|
|
448
|
-
2. **Auto-discovery:** `consumeJetStreamEvents()` scans `src/handlers/*.event.ts` and registers handlers
|
|
449
|
-
3. **Type safety:** Zod schema validates incoming events, provides TypeScript types
|
|
450
|
-
4. **Decoupling:** Services don't know about each other, only about events
|
|
451
|
-
|
|
452
|
-
**Benefits:**
|
|
453
|
-
- β
Zero boilerplate for NATS subscriptions
|
|
454
|
-
- β
Type-safe event handling with Zod
|
|
455
|
-
- β
Automatic retries and error handling (JetStream guarantees)
|
|
456
|
-
- β
Easy to add new consumers without touching existing services
|
|
457
|
-
|
|
458
|
-
<br />
|
|
394
|
+
**AI generates:** Service structure, event handlers, use cases, tests, and documentation. Always review before deploying.
|
|
459
395
|
|
|
460
396
|
---
|
|
461
397
|
|
|
@@ -498,7 +434,7 @@ await publish('orders.created', { orderId: 'ord_123', total: 99.99 })
|
|
|
498
434
|
### Consume Events (Auto-Discovered)
|
|
499
435
|
|
|
500
436
|
```typescript
|
|
501
|
-
// services/notifications/src/
|
|
437
|
+
// services/notifications/src/events/order-created.event.ts
|
|
502
438
|
import { handleEvent } from '@crossdelta/cloudevents'
|
|
503
439
|
import { z } from 'zod'
|
|
504
440
|
|
|
@@ -550,14 +486,14 @@ export default handleEvent(
|
|
|
550
486
|
| `pf new nest-micro <path>` | Generate a NestJS microservice (enterprise-grade) |
|
|
551
487
|
| `pf new hono-micro <path> --ai` | Generate service with AI (requires `pf setup --ai`) |
|
|
552
488
|
|
|
553
|
-
###
|
|
489
|
+
### Event Testing
|
|
554
490
|
|
|
555
491
|
| Command | Description |
|
|
556
492
|
|---------|-------------|
|
|
557
|
-
| `pf generate
|
|
558
|
-
| `pf generate use-case <name>` | Generate a use case |
|
|
493
|
+
| `pf event:generate <path>` | Generate event mocks from event handlers |
|
|
559
494
|
| `pf event:list` | List all available event mocks |
|
|
560
|
-
| `pf event:
|
|
495
|
+
| `pf event:publish <name>` | Publish to NATS JetStream |
|
|
496
|
+
| `pf event:http <name>` | Send via HTTP Pub/Sub endpoint |
|
|
561
497
|
|
|
562
498
|
### Utility Commands
|
|
563
499
|
|
|
@@ -643,8 +579,8 @@ Configure these secrets in your repository settings (`Settings` β `Secrets and
|
|
|
643
579
|
|
|
644
580
|
## π Requirements
|
|
645
581
|
|
|
646
|
-
- **
|
|
647
|
-
|
|
582
|
+
- **JavaScript runtime** β Bun (recommended) or Node.js β₯ 21 (npm/yarn/pnpm)
|
|
583
|
+
_No runtime? Use our [installer](#no-nodejsbun-installed) β it sets up Bun automatically_
|
|
648
584
|
- **[Pulumi CLI](https://www.pulumi.com/docs/install/)** β for infrastructure deployment
|
|
649
585
|
- **[Docker](https://www.docker.com/)** β required for local NATS. Without Docker, you can still scaffold/build
|
|
650
586
|
|