@crossdelta/platform-sdk 0.10.1 → 0.11.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 +40 -3
- package/bin/cli.js +109 -109
- package/bin/docs/generators/README.md +29 -20
- package/bin/docs/generators/code-style.md +96 -0
- package/bin/docs/generators/hono-bun.md +31 -45
- package/bin/docs/generators/hono-node.md +33 -57
- package/bin/docs/generators/nest.md +169 -107
- package/bin/docs/generators/service.md +133 -528
- package/bin/docs/generators/testing.md +97 -0
- package/bin/templates/nest-microservice/biome.json.hbs +1 -8
- package/bin/templates/workspace/biome.json.hbs +2 -1
- package/bin/templates/workspace/package.json.hbs +1 -1
- package/bin/templates/workspace/packages/contracts/package.json.hbs +1 -1
- package/package.json +123 -118
- package/bin/docs/generators/nest.md.new +0 -351
|
@@ -7,35 +7,44 @@ This directory contains AI instructions for code generators.
|
|
|
7
7
|
```
|
|
8
8
|
generators/
|
|
9
9
|
├── README.md # This file
|
|
10
|
-
|
|
10
|
+
├── service.md # Main service generator instructions
|
|
11
|
+
├── code-style.md # Code style, formatting, naming conventions
|
|
12
|
+
├── testing.md # Testing rules and patterns
|
|
13
|
+
├── hono-bun.md # Hono with Bun runtime specifics
|
|
14
|
+
├── hono-node.md # Hono with Node.js runtime specifics
|
|
15
|
+
└── nest.md # NestJS framework specifics
|
|
11
16
|
```
|
|
12
17
|
|
|
13
18
|
## How Instructions Are Loaded
|
|
14
19
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
Instructions are configured in `package.json` under `generatorConfig.docs`:
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
{
|
|
24
|
+
"generatorConfig": {
|
|
25
|
+
"docs": {
|
|
26
|
+
"base": ["service.md", "code-style.md", "testing.md"],
|
|
27
|
+
"frameworks": {
|
|
28
|
+
"hono": { "bun": "hono-bun.md", "node": "hono-node.md" },
|
|
29
|
+
"nest": "nest.md"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
26
35
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
36
|
+
**Load Order:**
|
|
37
|
+
1. **Base docs** - Always loaded (service.md, code-style.md, testing.md)
|
|
38
|
+
2. **Framework-specific** - Based on `serviceType` and `packageManager`
|
|
39
|
+
3. **Workspace instructions** - `.github/copilot-instructions.md`, `docs/ai-guidelines.md`
|
|
31
40
|
|
|
32
41
|
## Adding New Generators
|
|
33
42
|
|
|
34
|
-
To add instructions for a new
|
|
43
|
+
To add instructions for a new framework:
|
|
35
44
|
|
|
36
|
-
1. Create `<
|
|
37
|
-
2.
|
|
38
|
-
3. Follow the existing format from `
|
|
45
|
+
1. Create `<framework>.md` in this directory
|
|
46
|
+
2. Add to `generatorConfig.docs.frameworks` in `package.json`
|
|
47
|
+
3. Follow the existing format from `hono-bun.md` or `nest.md`
|
|
39
48
|
|
|
40
49
|
## Template Variables
|
|
41
50
|
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Code Style
|
|
2
|
+
|
|
3
|
+
## Formatting
|
|
4
|
+
|
|
5
|
+
- Single quotes, no semicolons, 2-space indent, trailing commas
|
|
6
|
+
- Arrow functions over `function`
|
|
7
|
+
- Template literals over concatenation
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Imports
|
|
12
|
+
|
|
13
|
+
**Must be alphabetically sorted** (Biome enforces this):
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
// ✅ CORRECT - sorted alphabetically, type imports first
|
|
17
|
+
import type { DomainCreatedData } from '@my-platform/contracts'
|
|
18
|
+
import type { OrdersCreatedData } from '@scope/contracts'
|
|
19
|
+
import { handleEvent } from '@crossdelta/cloudevents'
|
|
20
|
+
import PusherPushNotifications from '@pusher/push-notifications-server'
|
|
21
|
+
|
|
22
|
+
// ❌ WRONG - unsorted
|
|
23
|
+
import PusherPushNotifications from '@pusher/push-notifications-server'
|
|
24
|
+
import type { DomainCreatedData } from '@my-platform/contracts'
|
|
25
|
+
|
|
26
|
+
// ❌ WRONG - missing 'type' keyword
|
|
27
|
+
import { OrdersCreatedData } from '@scope/contracts'
|
|
28
|
+
|
|
29
|
+
// ❌ WRONG - unused imports
|
|
30
|
+
import { handleEvent, publish } from '@crossdelta/cloudevents'
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Environment Variables
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
// ✅ CORRECT
|
|
39
|
+
const apiKey = process.env.API_KEY
|
|
40
|
+
if (!apiKey) throw new Error('Missing API_KEY')
|
|
41
|
+
|
|
42
|
+
// ❌ WRONG
|
|
43
|
+
const apiKey = process.env.API_KEY! // no assertions
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Naming
|
|
49
|
+
|
|
50
|
+
| Type | Convention | Example |
|
|
51
|
+
|------|------------|---------|
|
|
52
|
+
| Files | kebab-case | `send-notification.use-case.ts` |
|
|
53
|
+
| Contracts | PascalCase | `OrdersCreatedContract` |
|
|
54
|
+
| Types | PascalCase + Data | `OrdersCreatedData` |
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Config Validation (Type Narrowing)
|
|
59
|
+
|
|
60
|
+
Use validation functions that return typed objects - avoids `!` assertions:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
// ✅ CORRECT - validation returns typed object
|
|
64
|
+
export const validateBeamsConfig = (config: {
|
|
65
|
+
instanceId?: string
|
|
66
|
+
secretKey?: string
|
|
67
|
+
}): { instanceId: string; secretKey: string } => {
|
|
68
|
+
if (!config.instanceId) throw new Error('Missing PUSHER_BEAMS_INSTANCE_ID')
|
|
69
|
+
if (!config.secretKey) throw new Error('Missing PUSHER_BEAMS_SECRET_KEY')
|
|
70
|
+
return { instanceId: config.instanceId, secretKey: config.secretKey }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Usage - no assertions needed
|
|
74
|
+
const config = validateBeamsConfig({
|
|
75
|
+
instanceId: process.env.PUSHER_BEAMS_INSTANCE_ID,
|
|
76
|
+
secretKey: process.env.PUSHER_BEAMS_SECRET_KEY,
|
|
77
|
+
})
|
|
78
|
+
new PusherClient(config) // ✅ Typed correctly
|
|
79
|
+
|
|
80
|
+
// ❌ WRONG - non-null assertions (Biome error)
|
|
81
|
+
validateConfig({ instanceId, secretKey })
|
|
82
|
+
new PusherClient({ instanceId: instanceId!, secretKey: secretKey! })
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Zod 4
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
// ✅ No params
|
|
91
|
+
z.string().email()
|
|
92
|
+
z.string().datetime()
|
|
93
|
+
|
|
94
|
+
// ❌ Deprecated
|
|
95
|
+
z.string().email('Invalid')
|
|
96
|
+
```
|
|
@@ -1,15 +1,23 @@
|
|
|
1
|
-
# Hono
|
|
1
|
+
# Hono (Bun Runtime)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## 🚨 CRITICAL: Commands Block (REQUIRED FIRST)
|
|
4
|
+
|
|
5
|
+
```commands
|
|
6
|
+
pf new hono-micro services/<service-name> -y
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
**Example:** User asks for "push notifications service" → generate:
|
|
10
|
+
```commands
|
|
11
|
+
pf new hono-micro services/push-notifications -y
|
|
12
|
+
```
|
|
4
13
|
|
|
5
14
|
---
|
|
6
15
|
|
|
7
|
-
## Entry Point
|
|
16
|
+
## Entry Point (src/index.ts)
|
|
8
17
|
|
|
9
|
-
**
|
|
18
|
+
**REST API:**
|
|
10
19
|
```ts
|
|
11
20
|
import '@crossdelta/telemetry'
|
|
12
|
-
|
|
13
21
|
import { Hono } from 'hono'
|
|
14
22
|
|
|
15
23
|
const port = Number(process.env.PORT || process.env.MY_SERVICE_PORT) || 8080
|
|
@@ -17,17 +25,14 @@ const app = new Hono()
|
|
|
17
25
|
|
|
18
26
|
app.get('/health', (c) => c.json({ status: 'ok' }))
|
|
19
27
|
|
|
20
|
-
// Your routes here
|
|
21
|
-
app.get('/', (c) => c.text('Hello Hono!'))
|
|
22
|
-
|
|
23
28
|
Bun.serve({ port, fetch: app.fetch })
|
|
29
|
+
console.log(`Service running on http://localhost:${port}`)
|
|
24
30
|
```
|
|
25
31
|
|
|
26
|
-
**Event Consumer
|
|
32
|
+
**Event Consumer:**
|
|
27
33
|
```ts
|
|
28
34
|
import '@crossdelta/telemetry'
|
|
29
|
-
|
|
30
|
-
import { consumeJetStreamStreams, ensureJetStreamStreams } from '@crossdelta/cloudevents'
|
|
35
|
+
import { consumeJetStreams, ensureJetStreams } from '@crossdelta/cloudevents'
|
|
31
36
|
import { Hono } from 'hono'
|
|
32
37
|
|
|
33
38
|
const port = Number(process.env.PORT || process.env.MY_SERVICE_PORT) || 8080
|
|
@@ -36,25 +41,22 @@ const app = new Hono()
|
|
|
36
41
|
app.get('/health', (c) => c.json({ status: 'ok' }))
|
|
37
42
|
|
|
38
43
|
Bun.serve({ port, fetch: app.fetch })
|
|
44
|
+
console.log(`Service running on http://localhost:${port}`)
|
|
39
45
|
|
|
40
|
-
await
|
|
41
|
-
streams: [
|
|
42
|
-
{ stream: 'ORDERS', subjects: ['orders.*'] },
|
|
43
|
-
{ stream: 'CUSTOMERS', subjects: ['customers.*'] },
|
|
44
|
-
]
|
|
46
|
+
await ensureJetStreams({
|
|
47
|
+
streams: [{ stream: 'ORDERS', subjects: ['orders.*'] }]
|
|
45
48
|
})
|
|
46
49
|
|
|
47
|
-
|
|
48
|
-
streams: ['ORDERS'
|
|
50
|
+
consumeJetStreams({
|
|
51
|
+
streams: ['ORDERS'],
|
|
49
52
|
consumer: 'my-service',
|
|
50
53
|
discover: './src/events/**/*.event.ts',
|
|
51
54
|
})
|
|
52
55
|
```
|
|
53
56
|
|
|
54
|
-
**Event Publisher
|
|
57
|
+
**Event Publisher:**
|
|
55
58
|
```ts
|
|
56
59
|
import '@crossdelta/telemetry'
|
|
57
|
-
|
|
58
60
|
import { publish } from '@crossdelta/cloudevents'
|
|
59
61
|
import { Hono } from 'hono'
|
|
60
62
|
|
|
@@ -65,37 +67,21 @@ app.get('/health', (c) => c.json({ status: 'ok' }))
|
|
|
65
67
|
|
|
66
68
|
app.post('/orders', async (c) => {
|
|
67
69
|
const data = await c.req.json()
|
|
68
|
-
await publish('orders.created', data
|
|
70
|
+
await publish('orders.created', data)
|
|
69
71
|
return c.json({ success: true })
|
|
70
72
|
})
|
|
71
73
|
|
|
72
74
|
Bun.serve({ port, fetch: app.fetch })
|
|
75
|
+
console.log(`Service running on http://localhost:${port}`)
|
|
73
76
|
```
|
|
74
77
|
|
|
75
78
|
---
|
|
76
79
|
|
|
77
|
-
##
|
|
78
|
-
|
|
79
|
-
- ✅ Use `Bun.serve({ port, fetch: app.fetch })` for server
|
|
80
|
-
- ✅ Import telemetry FIRST (before any other imports)
|
|
81
|
-
- ✅ Use the port env var name provided in the prompt (e.g., `PUSH_NOTIFICATIONS_PORT`)
|
|
82
|
-
- ✅ Always include `/health` endpoint
|
|
83
|
-
- ✅ Keep routes simple and delegate logic to use-cases
|
|
84
|
-
- ✅ Use `ensureJetStreamStreams` + `consumeJetStreamStreams` for multiple event types
|
|
80
|
+
## Rules
|
|
85
81
|
|
|
86
|
-
|
|
87
|
-
-
|
|
88
|
-
-
|
|
89
|
-
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
## Dev Command
|
|
94
|
-
|
|
95
|
-
```json
|
|
96
|
-
{
|
|
97
|
-
"scripts": {
|
|
98
|
-
"dev": "bun run --hot src/index.ts"
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
```
|
|
82
|
+
- ✅ `Bun.serve({ port, fetch: app.fetch })`
|
|
83
|
+
- ✅ Import telemetry FIRST
|
|
84
|
+
- ✅ Always include `/health`
|
|
85
|
+
- ✅ Use port env var from prompt (e.g., `PUSH_NOTIFICATIONS_PORT`)
|
|
86
|
+
- ❌ `export default Bun.serve(...)` - wrong
|
|
87
|
+
- ❌ `export default app` - not needed
|
|
@@ -1,15 +1,23 @@
|
|
|
1
|
-
# Hono
|
|
1
|
+
# Hono (Node.js Runtime)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## 🚨 CRITICAL: Commands Block (REQUIRED FIRST)
|
|
4
|
+
|
|
5
|
+
```commands
|
|
6
|
+
pf new hono-micro services/<service-name> -y
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
**Example:** User asks for "push notifications service" → generate:
|
|
10
|
+
```commands
|
|
11
|
+
pf new hono-micro services/push-notifications -y
|
|
12
|
+
```
|
|
4
13
|
|
|
5
14
|
---
|
|
6
15
|
|
|
7
|
-
## Entry Point
|
|
16
|
+
## Entry Point (src/index.ts)
|
|
8
17
|
|
|
9
|
-
**
|
|
18
|
+
**REST API:**
|
|
10
19
|
```ts
|
|
11
20
|
import '@crossdelta/telemetry'
|
|
12
|
-
|
|
13
21
|
import { serve } from '@hono/node-server'
|
|
14
22
|
import { Hono } from 'hono'
|
|
15
23
|
|
|
@@ -18,22 +26,15 @@ const app = new Hono()
|
|
|
18
26
|
|
|
19
27
|
app.get('/health', (c) => c.json({ status: 'ok' }))
|
|
20
28
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
serve({
|
|
25
|
-
fetch: app.fetch,
|
|
26
|
-
port
|
|
27
|
-
}, (info) => {
|
|
28
|
-
console.log(`Server is running on http://localhost:${info.port}`)
|
|
29
|
+
serve({ fetch: app.fetch, port }, (info) => {
|
|
30
|
+
console.log(`Server running on http://localhost:${info.port}`)
|
|
29
31
|
})
|
|
30
32
|
```
|
|
31
33
|
|
|
32
|
-
**Event Consumer
|
|
34
|
+
**Event Consumer:**
|
|
33
35
|
```ts
|
|
34
36
|
import '@crossdelta/telemetry'
|
|
35
|
-
|
|
36
|
-
import { consumeJetStreamStreams, ensureJetStreamStreams } from '@crossdelta/cloudevents'
|
|
37
|
+
import { consumeJetStreams, ensureJetStreams } from '@crossdelta/cloudevents'
|
|
37
38
|
import { serve } from '@hono/node-server'
|
|
38
39
|
import { Hono } from 'hono'
|
|
39
40
|
|
|
@@ -42,31 +43,24 @@ const app = new Hono()
|
|
|
42
43
|
|
|
43
44
|
app.get('/health', (c) => c.json({ status: 'ok' }))
|
|
44
45
|
|
|
45
|
-
serve({
|
|
46
|
-
|
|
47
|
-
port
|
|
48
|
-
}, (info) => {
|
|
49
|
-
console.log(`Server is running on http://localhost:${info.port}`)
|
|
46
|
+
serve({ fetch: app.fetch, port }, (info) => {
|
|
47
|
+
console.log(`Server running on http://localhost:${info.port}`)
|
|
50
48
|
})
|
|
51
49
|
|
|
52
|
-
await
|
|
53
|
-
streams: [
|
|
54
|
-
{ stream: 'ORDERS', subjects: ['orders.*'] },
|
|
55
|
-
{ stream: 'CUSTOMERS', subjects: ['customers.*'] },
|
|
56
|
-
]
|
|
50
|
+
await ensureJetStreams({
|
|
51
|
+
streams: [{ stream: 'ORDERS', subjects: ['orders.*'] }]
|
|
57
52
|
})
|
|
58
53
|
|
|
59
|
-
|
|
60
|
-
streams: ['ORDERS'
|
|
54
|
+
consumeJetStreams({
|
|
55
|
+
streams: ['ORDERS'],
|
|
61
56
|
consumer: 'my-service',
|
|
62
57
|
discover: './src/events/**/*.event.ts',
|
|
63
58
|
})
|
|
64
59
|
```
|
|
65
60
|
|
|
66
|
-
**Event Publisher
|
|
61
|
+
**Event Publisher:**
|
|
67
62
|
```ts
|
|
68
63
|
import '@crossdelta/telemetry'
|
|
69
|
-
|
|
70
64
|
import { publish } from '@crossdelta/cloudevents'
|
|
71
65
|
import { serve } from '@hono/node-server'
|
|
72
66
|
import { Hono } from 'hono'
|
|
@@ -78,39 +72,21 @@ app.get('/health', (c) => c.json({ status: 'ok' }))
|
|
|
78
72
|
|
|
79
73
|
app.post('/orders', async (c) => {
|
|
80
74
|
const data = await c.req.json()
|
|
81
|
-
await publish('orders.created', data
|
|
75
|
+
await publish('orders.created', data)
|
|
82
76
|
return c.json({ success: true })
|
|
83
77
|
})
|
|
84
78
|
|
|
85
|
-
serve({
|
|
86
|
-
|
|
87
|
-
port
|
|
88
|
-
}, (info) => {
|
|
89
|
-
console.log(`Server is running on http://localhost:${info.port}`)
|
|
79
|
+
serve({ fetch: app.fetch, port }, (info) => {
|
|
80
|
+
console.log(`Server running on http://localhost:${info.port}`)
|
|
90
81
|
})
|
|
91
82
|
```
|
|
92
83
|
|
|
93
84
|
---
|
|
94
85
|
|
|
95
|
-
##
|
|
96
|
-
|
|
97
|
-
- ✅ Use `serve()` from `@hono/node-server` (NOT `Bun.serve`)
|
|
98
|
-
- ✅ Import `serve` from `@hono/node-server`
|
|
99
|
-
- ✅ Import telemetry FIRST (before any other imports)
|
|
100
|
-
- ✅ Use the port env var name provided in the prompt (e.g., `API_GATEWAY_PORT`)
|
|
101
|
-
- ✅ Always include `/health` endpoint
|
|
102
|
-
- ✅ Include callback with console.log for server startup
|
|
103
|
-
- ✅ Keep routes simple and delegate logic to use-cases
|
|
104
|
-
- ✅ Use `ensureJetStreamStreams` + `consumeJetStreamStreams` for multiple event types
|
|
105
|
-
|
|
106
|
-
---
|
|
107
|
-
|
|
108
|
-
## Dev Command
|
|
86
|
+
## Rules
|
|
109
87
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
```
|
|
88
|
+
- ✅ `serve()` from `@hono/node-server`
|
|
89
|
+
- ✅ Import telemetry FIRST
|
|
90
|
+
- ✅ Always include `/health`
|
|
91
|
+
- ✅ Use port env var from prompt (e.g., `API_GATEWAY_PORT`)
|
|
92
|
+
- ❌ `Bun.serve()` - wrong runtime
|