@crossdelta/platform-sdk 0.16.3 → 0.16.5

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.
@@ -14,17 +14,17 @@
14
14
 
15
15
  ```ts
16
16
  // ✅ CORRECT - sorted alphabetically, type imports first
17
- import type { DomainCreatedData } from '@my-platform/contracts'
18
- import type { OrdersCreatedData } from '@scope/contracts'
17
+ import type { DomainCreatedData } from '{workspaceScope}/contracts'
18
+ import type { OrderCreatedData } from '@scope/contracts'
19
19
  import { handleEvent } from '@crossdelta/cloudevents'
20
20
  import PusherPushNotifications from '@pusher/push-notifications-server'
21
21
 
22
22
  // ❌ WRONG - unsorted
23
23
  import PusherPushNotifications from '@pusher/push-notifications-server'
24
- import type { DomainCreatedData } from '@my-platform/contracts'
24
+ import type { DomainCreatedData } from '{workspaceScope}/contracts'
25
25
 
26
26
  // ❌ WRONG - missing 'type' keyword
27
- import { OrdersCreatedData } from '@scope/contracts'
27
+ import { OrderCreatedData } from '@scope/contracts'
28
28
 
29
29
  // ❌ WRONG - unused imports
30
30
  import { handleEvent, publish } from '@crossdelta/cloudevents'
@@ -51,7 +51,7 @@ const apiKey = process.env.API_KEY! // no assertions
51
51
  |------|------------|---------|
52
52
  | Files | kebab-case | `send-notification.use-case.ts` |
53
53
  | Contracts | PascalCase | `OrdersCreatedContract` |
54
- | Types | PascalCase + Data | `OrdersCreatedData` |
54
+ | Types | PascalCase + Data | `OrderCreatedData` |
55
55
 
56
56
  ---
57
57
 
@@ -83,7 +83,7 @@ consumeJetStreams({
83
83
  ```
84
84
 
85
85
  **CRITICAL:** Stream names MUST be PLURAL:
86
- - ✅ `streams: ['ORDERS']` - for orders.created event
86
+ - ✅ `streams: ['ORDERS']` - for order.created event
87
87
  - ✅ `streams: ['DOMAINS']` - for domain.created event
88
88
  - ❌ `streams: ['ORDER']` - WRONG (singular)
89
89
  - ❌ `streams: ['DOMAIN']` - WRONG (singular)
@@ -104,7 +104,7 @@ app.get('/health', (c) => c.json({ status: 'ok' }))
104
104
 
105
105
  app.post('/orders', async (c) => {
106
106
  const data = await c.req.json()
107
- await publish('orders.created', data)
107
+ await publish('order.created', data)
108
108
  return c.json({ success: true })
109
109
  })
110
110
 
@@ -86,7 +86,7 @@ consumeJetStreams({
86
86
  ```
87
87
 
88
88
  **CRITICAL:** Stream names MUST be PLURAL:
89
- - ✅ `streams: ['ORDERS']` - for orders.created event
89
+ - ✅ `streams: ['ORDERS']` - for order.created event
90
90
  - ✅ `streams: ['DOMAINS']` - for domain.created event
91
91
  - ❌ `streams: ['ORDER']` - WRONG (singular)
92
92
  - ❌ `streams: ['DOMAIN']` - WRONG (singular)
@@ -108,7 +108,7 @@ app.get('/health', (c) => c.json({ status: 'ok' }))
108
108
 
109
109
  app.post('/orders', async (c) => {
110
110
  const data = await c.req.json()
111
- await publish('orders.created', data)
111
+ await publish('order.created', data)
112
112
  return c.json({ success: true })
113
113
  })
114
114
 
@@ -242,12 +242,12 @@ export const getService = <T>(serviceClass: Type<T>): T => {
242
242
 
243
243
  ```ts
244
244
  import { handleEvent } from '@crossdelta/cloudevents'
245
- import { OrdersCreatedContract, type OrdersCreatedData } from '{{scope}}/contracts'
245
+ import { OrdersCreatedContract, type OrderCreatedData } from '{workspaceScope}/contracts'
246
246
  import { getService } from '../app.context'
247
247
  import { OrdersService } from '../orders/orders.service'
248
248
 
249
- export default handleEvent(OrdersCreatedContract, async (data: OrdersCreatedData) => {
250
- console.log(`[orders.created] Processing orderId=${data.orderId}`)
249
+ export default handleEvent(OrdersCreatedContract, async (data: OrderCreatedData) => {
250
+ console.log(`[order.created] Processing orderId=${data.orderId}`)
251
251
  const ordersService = getService(OrdersService)
252
252
  await ordersService.processOrder(data)
253
253
  })
@@ -299,7 +299,7 @@ export class AppController {
299
299
 
300
300
  ```ts
301
301
  // src/notifications/send-notification.ts (pure function)
302
- import type { DomainCreatedData } from '{{scope}}/contracts'
302
+ import type { DomainCreatedData } from '{workspaceScope}/contracts'
303
303
 
304
304
  export const buildNotificationPayload = (data: DomainCreatedData) => ({
305
305
  title: 'Domain Created',
@@ -47,8 +47,33 @@ const port = Number(process.env.MY_HONO_SERVICE_PORT) || 8080
47
47
 
48
48
  > **Services NEVER create streams!**
49
49
  >
50
- > In development, `pf dev` ensures ephemeral streams derived from contracts.
51
- > In production, streams are materialized explicitly via Pulumi.
50
+ > **Dev:** `pf dev` auto-creates ephemeral streams from contracts (memory, 1h retention)
51
+ > **Prod:** Streams materialized via Pulumi with retention policies
52
+
53
+ ### Architecture Flow
54
+
55
+ ```typescript
56
+ // 1. Contract defines routing
57
+ export const OrdersCreatedContract = createContract({
58
+ type: 'order.created',
59
+ channel: { stream: 'ORDERS' },
60
+ schema: OrderSchema,
61
+ })
62
+
63
+ // 2. Services consume (never create!)
64
+ consumeJetStreams({
65
+ streams: ['ORDERS'],
66
+ consumer: 'my-service',
67
+ discover: './src/events/**/*.handler.ts',
68
+ })
69
+
70
+ // 3. Infra materializes (infra/streams/)
71
+ import { collectStreamDefinitions } from '@crossdelta/infrastructure'
72
+ const streams = collectStreamDefinitions(contracts)
73
+ deployStreams(provider, namespace, streams)
74
+ ```
75
+
76
+ **Key Principle:** Contracts define **what** (routing), infrastructure defines **how** (retention).
52
77
 
53
78
  ---
54
79
 
@@ -65,7 +90,7 @@ cd my-platform
65
90
 
66
91
  #### Option A: AI Generation (via GitHub Copilot Chat)
67
92
  ```
68
- "Create an order processing service that consumes orders.created events"
93
+ "Create an order processing service that consumes order.created events"
69
94
  ```
70
95
 
71
96
  **What AI generates:**
@@ -81,7 +106,7 @@ pf new hono-micro services/order-processing -y
81
106
  ```
82
107
 
83
108
  ```post-commands
84
- pf event add orders.created --service services/order-processing
109
+ pf event add order.created --service services/order-processing
85
110
  ```
86
111
 
87
112
  **What `pf event add` does:**
@@ -94,10 +119,10 @@ pf event add orders.created --service services/order-processing
94
119
  **Step 1: Create contract with CLI**
95
120
  ```bash
96
121
  # Create contract with schema
97
- pf event add orders.created --fields "orderId:string,total:number,customerId:string"
122
+ pf event add order.created --fields "orderId:string,total:number,customerId:string"
98
123
 
99
124
  # Or with JSON schema
100
- pf event add orders.created --schema '{"orderId":"string","total":"number"}'
125
+ pf event add order.created --schema '{"orderId":"string","total":"number"}'
101
126
  ```
102
127
 
103
128
  **Step 2: Scaffold service**
@@ -107,7 +132,7 @@ pf new hono-micro services/order-processing
107
132
 
108
133
  **Step 3: Register event and create handler**
109
134
  ```bash
110
- pf event add orders.created --service services/order-processing
135
+ pf event add order.created --service services/order-processing
111
136
  ```
112
137
 
113
138
  **Step 4: Implement use-cases**
@@ -160,52 +185,6 @@ pulumi up
160
185
 
161
186
  ---
162
187
 
163
- ## �🚨 CRITICAL: Stream Architecture
164
-
165
- > **Services NEVER create streams!**
166
- > **Dev:** Streams are auto-created (ephemeral, memory)
167
- > **Prod:** Streams are materialized via Pulumi from contracts
168
-
169
- ### Contracts → Streams → Infra Flow
170
-
171
- ```typescript
172
- // 1. Contract defines conceptually (packages/contracts)
173
- export const OrdersCreatedContract = createContract({
174
- type: 'orders.created',
175
- channel: { stream: 'ORDERS' }, // Routing metadata
176
- schema: OrderSchema,
177
- })
178
-
179
- // 2. Services consume (no stream creation!)
180
- consumeJetStreams({
181
- streams: ['ORDERS'],
182
- consumer: 'my-service',
183
- discover: './src/events/**/*.handler.ts',
184
- })
185
-
186
- // 3. Infra materializes (infra/streams/)
187
- import { collectStreamDefinitions } from '@crossdelta/infrastructure'
188
- import * as contracts from '@workspace/contracts'
189
-
190
- // Generic logic in library, workspace data injected
191
- const streams = collectStreamDefinitions(contracts)
192
- deployStreams(provider, namespace, streams)
193
- ```
194
-
195
- **Architecture layers:**
196
- - **@crossdelta/infrastructure:** Generic stream collection logic
197
- - **Contracts:** Define event semantics + channel routing (workspace data)
198
- - **`pf dev`:** Ensures ephemeral streams (transient, memory-only)
199
- - **Platform-Infra:** Materializes streams via Pulumi (persistent, retention)
200
- - **Services:** Consume streams (never create!)
201
-
202
- **Separation of concerns:**
203
- - **Libraries contain logic** (generic, reusable)
204
- - **Workspaces contain data** (contracts, policies)
205
- - **No workspace imports inside libraries**
206
-
207
- ---
208
-
209
188
  ## 🚨 CRITICAL: @crossdelta/cloudevents API
210
189
 
211
190
  **ONLY these exports exist:**
@@ -269,8 +248,8 @@ The exact command depends on the framework - see the framework-specific docs:
269
248
  **For Event Consumer services, include a `post-commands` block with ALL events:**
270
249
 
271
250
  ```post-commands
272
- pf event add orders.created --service services/my-service
273
- pf event add customers.updated --service services/my-service
251
+ pf event add order.created --service services/my-service
252
+ pf event add customer.updated --service services/my-service
274
253
  ```
275
254
 
276
255
  **What `pf event add` creates:**
@@ -285,13 +264,30 @@ pf event add customers.updated --service services/my-service
285
264
 
286
265
  **FOR EVENT CONSUMER SERVICES (services that "consume", "listen to", "react to" events):**
287
266
 
267
+ ### ⚠️ CRITICAL: Contract Exports (MUST DO FIRST!)
268
+
269
+ **Before generating ANY files, check if the contract export exists:**
270
+
271
+ ```typescript
272
+ // packages/contracts/src/index.ts
273
+ export * from './events/orders/created' // ← MUST exist!
274
+ ```
275
+
276
+ **If missing, ADD IT before generating handler!** Services import from `{workspaceScope}/contracts`, not individual files.
277
+
278
+ **Common Error if missing:**
279
+ ```
280
+ Export named 'OrderCreatedContract' not found in module 'packages/contracts/src/index.ts'
281
+ ```
282
+
288
283
  ### What YOU (the AI) generate:
289
284
 
290
285
  1. **`packages/contracts/src/events/<domain>/<event>.ts`** - Contract with correct schema fields in domain-grouped structure (e.g., `events/orders/created.ts`)
291
- 2. **`src/events/<event>.handler.ts`** - Event handler that calls the use-case
292
- 3. **`src/use-cases/*.use-case.ts`** - Business logic (called by handlers)
293
- 4. **`src/use-cases/*.test.ts`** - Tests for use-cases
294
- 5. **`README.md`** - Documentation
286
+ 2. **`packages/contracts/src/index.ts`** - ⚠️ **ADD EXPORT** for the contract (CRITICAL!)
287
+ 3. **`src/events/<event>.handler.ts`** - Event handler that calls the use-case
288
+ 4. **`src/use-cases/*.use-case.ts`** - Business logic (called by handlers)
289
+ 5. **`src/use-cases/*.test.ts`** - Tests for use-cases
290
+ 6. **`README.md`** - Documentation
295
291
 
296
292
  **⚠️ DO NOT generate `src/index.ts` or `src/main.ts`** - These are created by `pf new` with correct port config!
297
293
 
@@ -325,7 +321,7 @@ export const OrdersCreatedHandler = handleEvent(...)
325
321
 
326
322
  ```ts
327
323
  export default handleEvent(OrdersCreatedContract, async (data) => {
328
- console.log(`[orders.created] Processing orderId=${data.orderId}`) // ✅ console.log
324
+ console.log(`[order.created] Processing orderId=${data.orderId}`) // ✅ console.log
329
325
  await processOrder(data)
330
326
  })
331
327
  ```
@@ -345,27 +341,33 @@ pf new hono-micro services/my-service -y
345
341
  import { createContract } from '@crossdelta/cloudevents'
346
342
  import { z } from 'zod'
347
343
 
348
- export const OrdersCreatedSchema = z.object({
344
+ export const OrderCreatedSchema = z.object({
349
345
  orderId: z.string(),
350
346
  total: z.number(),
351
347
  })
352
348
 
349
+ export type OrderCreatedData = z.infer<typeof OrderCreatedSchema>
350
+
353
351
  export const OrdersCreatedContract = createContract({
354
- type: 'orders.created',
352
+ type: 'order.created',
355
353
  channel: { stream: 'ORDERS' },
356
- schema: OrdersCreatedSchema,
354
+ schema: OrderCreatedSchema,
357
355
  })
356
+ ```
358
357
 
359
- export type OrdersCreatedData = z.infer<typeof OrdersCreatedContract.schema>
358
+ #### `packages/contracts/src/index.ts`
359
+ ```ts
360
+ // Export all contracts (REQUIRED - handlers import from here!)
361
+ export * from './events/orders/created'
360
362
  ```
361
363
 
362
364
  #### `src/events/orders-created.handler.ts`
363
365
  ```ts
364
366
  import { handleEvent } from '@crossdelta/cloudevents'
365
- import { OrdersCreatedContract, type OrdersCreatedData } from '{{scope}}/contracts'
367
+ import { OrdersCreatedContract, type OrderCreatedData } from '{workspaceScope}/contracts'
366
368
  import { processOrder } from '../use-cases/process-order.use-case'
367
369
 
368
- export default handleEvent(OrdersCreatedContract, async (data: OrdersCreatedData) => {
370
+ export default handleEvent(OrdersCreatedContract, async (data: OrderCreatedData) => {
369
371
  console.log('📦 Processing order:', data.orderId)
370
372
  await processOrder(data)
371
373
  })
@@ -373,15 +375,15 @@ export default handleEvent(OrdersCreatedContract, async (data: OrdersCreatedData
373
375
 
374
376
  #### `src/use-cases/process-order.use-case.ts`
375
377
  ```ts
376
- import type { OrdersCreatedData } from '{{scope}}/contracts'
378
+ import type { OrderCreatedData } from '{workspaceScope}/contracts'
377
379
 
378
- export const processOrder = async (data: OrdersCreatedData): Promise<void> => {
380
+ export const processOrder = async (data: OrderCreatedData): Promise<void> => {
379
381
  console.log('Processing:', data.orderId)
380
382
  }
381
383
  ```
382
384
 
383
385
  ```post-commands
384
- pf event add orders.created --service services/my-service
386
+ pf event add order.created --service services/my-service
385
387
  ```
386
388
 
387
389
  ```dependencies
@@ -416,14 +418,24 @@ await notifyUser(data.userId) // ❌ WRONG - userId not in schema!
416
418
  ## Contract Schema (Zod 4 Syntax)
417
419
 
418
420
  ```ts
419
- export const OrdersCreatedSchema = z.object({
421
+ export const OrderCreatedSchema = z.object({
420
422
  orderId: z.string(),
421
423
  email: z.string().email(), // ✅ Zod 4: no params
422
424
  total: z.number(),
423
425
  createdAt: z.string().datetime(), // ✅ Zod 4: no params
424
426
  })
427
+
428
+ export type OrderCreatedData = z.infer<typeof OrderCreatedSchema>
429
+
430
+ export const OrdersCreatedContract = createContract({
431
+ type: 'order.created',
432
+ channel: { stream: 'ORDERS' },
433
+ schema: OrderCreatedSchema, // ← Singular schema
434
+ })
425
435
  ```
426
436
 
437
+ **Pattern:** Schema is singular (describes one object), Contract is plural (represents stream)
438
+
427
439
  **Zod 4 Quick Reference:**
428
440
  - ✅ `z.string().email()` - no params
429
441
  - ✅ `z.string().url()` - no params
@@ -437,14 +449,23 @@ export const OrdersCreatedSchema = z.object({
437
449
 
438
450
  | Component | Format | Example | Reason |
439
451
  |-----------|--------|---------|--------|
440
- | **Event Type** | **Singular** | `customer.created` | Describes a single event |
441
- | **Event Folder** | **PLURAL** | `customers/` | Domain grouping (matches stream) |
442
- | **Stream** | **PLURAL** | `CUSTOMERS` | Collection of events for a domain |
443
- | **Mock Files** | **PLURAL folder** | `customers/created.mock.json` | Must match event folder |
452
+ | **Event Type** | **Singular** | `customer.created` | A single fact |
453
+ | **Schema** | **Singular** | `CustomerCreatedSchema` | One event object |
454
+ | **Type** | **Singular** | `CustomerCreatedData` | One event object |
455
+ | **Contract** | **PLURAL** | `CustomersCreatedContract` | Category of events |
456
+ | **Event Folder** | **PLURAL** | `customers/` | Domain (collection) |
457
+ | **Stream** | **PLURAL** | `CUSTOMERS` | Collection of events |
458
+ | **Mock Files** | **PLURAL folder** | `customers/created.mock.json` | Matches event folder |
459
+
460
+ **The final rule:**
461
+ ```
462
+ Schema / Data → singular (one event instance)
463
+ Event Type → singular (one fact)
464
+ Contract → plural (category of events)
465
+ Stream / Domain → plural (collection)
466
+ ```
444
467
 
445
- **Merksatz:**
446
- - **Singular** describes an event
447
- - **Plural** describes a domain
468
+ **Key principle:** Contracts represent event collections, Schemas represent event instances.
448
469
 
449
470
  ### File Structure Example
450
471
 
@@ -466,36 +487,42 @@ packages/contracts/src/events/
466
487
 
467
488
  ### Contract Examples
468
489
 
469
- ### Contract Examples
490
+ | Event Type | Schema | Type | Contract | Folder | Stream |
491
+ |------------|--------|------|----------|--------|--------|
492
+ | `customer.created` | `CustomerCreatedSchema` | `CustomerCreatedData` | `CustomersCreatedContract` | `customers/` | `CUSTOMERS` |
493
+ | `order.created` | `OrderCreatedSchema` | `OrderCreatedData` | `OrdersCreatedContract` | `orders/` | `ORDERS` |
494
+ | `domain.created` | `DomainCreatedSchema` | `DomainCreatedData` | `DomainsCreatedContract` | `domains/` | `DOMAINS` |
470
495
 
471
- | Event Type | Contract | Folder | Stream |
472
- |------------|----------|--------|--------|
473
- | `customer.created` | `CustomerCreatedContract` | `customers/` | `CUSTOMERS` |
474
- | `order.created` | `OrderCreatedContract` | `orders/` | `ORDERS` ✅ |
475
- | `domain.created` | `DomainCreatedContract` | `domains/` | `DOMAINS` ✅ |
496
+ **Naming Patterns:**
497
+ - Schema/Type: `{SingularDomain}{Action}Schema/Data` - describes one object
498
+ - Contract: `{PluralDomain}{Action}Contract` - matches folder and stream names
476
499
 
477
500
  ```typescript
478
501
  // ✅ CORRECT
479
- export const CustomerCreatedContract = createContract({
480
- type: 'customer.created', // Singular event
481
- channel: { stream: 'CUSTOMERS' }, // Plural stream
482
- schema: CustomerCreatedSchema,
502
+ export const CustomerCreatedSchema = z.object({ ... })
503
+ export type CustomerCreatedData = z.infer<typeof CustomerCreatedSchema>
504
+
505
+ export const CustomersCreatedContract = createContract({
506
+ type: 'customer.created', // Singular event type
507
+ channel: { stream: 'CUSTOMERS' }, // Plural stream (matches contract name prefix)
508
+ schema: CustomerCreatedSchema, // Singular schema
483
509
  })
484
510
 
485
511
  // Folder: packages/contracts/src/events/customers/created.ts ✅ PLURAL
486
512
  // Mock: packages/contracts/src/events/customers/created.mock.json ✅ PLURAL
487
513
 
488
- // ❌ WRONG - Singular stream
514
+ // ❌ WRONG - Singular domain in contract name
489
515
  export const CustomerCreatedContract = createContract({
490
516
  type: 'customer.created',
491
- channel: { stream: 'CUSTOMER' }, // Must be CUSTOMERS (plural!)
517
+ channel: { stream: 'CUSTOMERS' }, // Mismatch: plural stream, singular contract
492
518
  schema: CustomerCreatedSchema,
493
519
  })
494
520
 
495
- // ❌ WRONG - Mixed folders
496
- // Folder: packages/contracts/src/events/customers/created.ts ✅
497
- // Mock: packages/contracts/src/events/customer/created.mock.json ❌ Singular!
498
- schema: DomainCreatedSchema,
521
+ // ❌ WRONG - Singular stream
522
+ export const CustomersCreatedContract = createContract({
523
+ type: 'customer.created',
524
+ channel: { stream: 'CUSTOMER' }, // ❌ Wrong! Should be CUSTOMERS (plural)
525
+ schema: CustomerCreatedSchema,
499
526
  })
500
527
  ```
501
528
 
@@ -511,7 +538,7 @@ consumeJetStreams({
511
538
  discover: './src/events/**/*.handler.ts',
512
539
  })
513
540
 
514
- // ✅ CORRECT - orders.created event → ORDERS stream
541
+ // ✅ CORRECT - order.created event → ORDERS stream
515
542
  consumeJetStreams({
516
543
  streams: ['ORDERS'], // ✅ Plural!
517
544
  consumer: 'my-service',
@@ -595,11 +622,12 @@ packages/contracts/src/events/
595
622
  - ❌ Use `src/handlers/` (use `src/events/`)
596
623
  - ❌ Create `src/types/` directory (use contracts)
597
624
  - ❌ Insert semicolons
598
- - ❌ Edit `packages/contracts/src/index.ts` (CLI handles exports)
625
+ - ❌ Create contracts without adding exports to `packages/contracts/src/index.ts`
599
626
  - ❌ Create `use-cases/` folder in NestJS (use Services instead)
600
627
 
601
628
  **DO:**
602
629
  - ✅ Contracts in `packages/contracts/src/events/`
630
+ - ✅ **ALWAYS add contract exports to `packages/contracts/src/index.ts`** (CRITICAL!)
603
631
  - ✅ Handlers in `src/events/*.handler.ts`
604
632
  - ✅ Hono: Use-cases in `src/use-cases/*.use-case.ts`
605
633
  - ✅ NestJS: Business logic in Services + pure helper functions
@@ -7,7 +7,7 @@
7
7
  "pulumi": "pulumi"
8
8
  },
9
9
  "dependencies": {
10
- "@crossdelta/cloudevents": "^0.5.5",
10
+ "@crossdelta/cloudevents": "^0.5.7",
11
11
  "@crossdelta/infrastructure": "^0.5.3",
12
12
  "{{scope}}/contracts": "workspace:*",
13
13
  "@pulumi/digitalocean": "^4.55.0",
@@ -12,7 +12,7 @@ This package contains **shared event definitions** (contracts) for events that a
12
12
  src/
13
13
  ├── events/ # Event contracts grouped by domain
14
14
  │ ├── orders/ # Orders domain
15
- │ │ ├── created.ts # orders.created event
15
+ │ │ ├── created.ts # order.created event
16
16
  │ │ ├── updated.ts # orders.updated event
17
17
  │ │ └── index.ts # Re-exports
18
18
  │ ├── customers/ # Customers domain
@@ -35,7 +35,7 @@ src/
35
35
  ### Using the CLI (Recommended)
36
36
 
37
37
  ```bash
38
- pf event add products.created --fields "productId:string,name:string,price:number"
38
+ pf event add product.created --fields "productId:string,name:string,price:number"
39
39
  ```
40
40
 
41
41
  This will create `src/events/products/created.ts` with proper domain structure.
@@ -50,9 +50,9 @@ See [Adding Events](#manual-creation) section below.
50
50
 
51
51
  ```ts
52
52
  import { handleEvent } from '@crossdelta/cloudevents'
53
- import { OrdersCreatedContract, type OrdersCreatedData } from '{{scope}}/contracts'
53
+ import { OrdersCreatedContract, type OrderCreatedData } from '{{scope}}/contracts'
54
54
 
55
- export default handleEvent(OrdersCreatedContract, async (data: OrdersCreatedData) => {
55
+ export default handleEvent(OrdersCreatedContract, async (data: OrderCreatedData) => {
56
56
  // data is fully typed from contract
57
57
  console.log(data.orderId, data.customerId)
58
58
  })
@@ -76,9 +76,9 @@ await publish(OrdersCreatedContract, {
76
76
  ### In Use-Cases
77
77
 
78
78
  ```ts
79
- import type { OrdersCreatedData } from '{{scope}}/contracts'
79
+ import type { OrderCreatedData } from '{{scope}}/contracts'
80
80
 
81
- export const processOrder = async (data: OrdersCreatedData) => {
81
+ export const processOrder = async (data: OrderCreatedData) => {
82
82
  // Use typed event data
83
83
  }
84
84
  ```
@@ -89,10 +89,10 @@ Contracts are **auto-generated** when you create event handlers:
89
89
 
90
90
  ```bash
91
91
  # 1. Create service with event handler
92
- pf new hono-micro notifications --ai -d "Sends emails on orders.created events"
92
+ pf new hono-micro notifications --ai -d "Sends emails on order.created events"
93
93
 
94
94
  # 2. Add event (creates contract, mock, handler)
95
- pf event add orders.created --service services/notifications
95
+ pf event add order.created --service services/notifications
96
96
  ```
97
97
 
98
98
  This creates:
@@ -107,7 +107,7 @@ This creates:
107
107
  import { createContract } from '@crossdelta/cloudevents'
108
108
  import { z } from 'zod'
109
109
 
110
- export const OrdersCreatedSchema = z.object({
110
+ export const OrderCreatedSchema = z.object({
111
111
  orderId: z.string(),
112
112
  customerId: z.string(),
113
113
  total: z.number(),
@@ -119,17 +119,17 @@ export const OrdersCreatedSchema = z.object({
119
119
  })
120
120
 
121
121
  export const OrdersCreatedContract = createContract({
122
- type: 'orders.created',
122
+ type: 'order.created',
123
123
  channel: { stream: 'ORDERS' }, // Stream routing metadata
124
- schema: OrdersCreatedSchema,
124
+ schema: OrderCreatedSchema,
125
125
  })
126
126
 
127
- export type OrdersCreatedData = z.infer<typeof OrdersCreatedContract.schema>
127
+ export type OrderCreatedData = z.infer<typeof OrdersCreatedContract.schema>
128
128
  ```
129
129
 
130
130
  **Channel Metadata:**
131
131
  - `stream` - NATS JetStream stream name (e.g., `ORDERS`)
132
- - `subject` - Optional, defaults to event type (e.g., `orders.created`)
132
+ - `subject` - Optional, defaults to event type (e.g., `order.created`)
133
133
 
134
134
  **Stream Materialization:**
135
135
  1. **Development**: `pf dev` scans contracts and auto-creates ephemeral streams from channel metadata
@@ -144,10 +144,10 @@ See [`infra/streams/README.md`](../../infra/streams/README.md) for details.
144
144
  pf event list
145
145
 
146
146
  # Publish mock event
147
- pf event publish orders.created
147
+ pf event publish order.created
148
148
 
149
149
  # Publish with custom data
150
- pf event publish orders.created --data '{"orderId":"test-123"}'
150
+ pf event publish order.created --data '{"orderId":"test-123"}'
151
151
  ```
152
152
 
153
153
  ## Guidelines
@@ -161,6 +161,6 @@ pf event publish orders.created --data '{"orderId":"test-123"}'
161
161
 
162
162
  **Naming conventions:**
163
163
  - Contracts: `OrdersCreatedContract` (plural namespace)
164
- - Types: `OrdersCreatedData`
164
+ - Types: `OrderCreatedData`
165
165
  - Files: `orders-created.ts`
166
- - Event types: `orders.created` (plural namespace, dot notation)
166
+ - Event types: `order.created` (plural namespace, dot notation)
@@ -11,7 +11,7 @@
11
11
  }
12
12
  },
13
13
  "dependencies": {
14
- "@crossdelta/cloudevents": "^0.5.5",
14
+ "@crossdelta/cloudevents": "^0.5.7",
15
15
  "@crossdelta/infrastructure": "^0.5.3",
16
16
  "zod": "^4.0.0"
17
17
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crossdelta/platform-sdk",
3
- "version": "0.16.3",
3
+ "version": "0.16.5",
4
4
  "description": "Platform toolkit for event-driven microservices — keeping code and infrastructure in lockstep.",
5
5
  "keywords": [
6
6
  "cli",