@jaypie/mcp 0.3.1 → 0.3.4
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/dist/aws-B3dW_-bD.js +1202 -0
- package/dist/aws-B3dW_-bD.js.map +1 -0
- package/dist/index.js +357 -1200
- package/dist/index.js.map +1 -1
- package/dist/suite.d.ts +1 -0
- package/dist/suite.js +1252 -0
- package/dist/suite.js.map +1 -0
- package/package.json +11 -2
- package/prompts/Jaypie_Fabric_MCP.md +22 -2
- package/prompts/Jaypie_Fabric_Package.md +86 -0
- package/prompts/Jaypie_MCP_Package.md +34 -2
- package/release-notes/constructs/1.2.17.md +11 -0
- package/release-notes/fabric/0.1.1.md +17 -0
- package/release-notes/fabric/0.1.2.md +11 -0
- package/release-notes/mcp/0.3.2.md +14 -0
- package/release-notes/mcp/0.3.3.md +12 -0
- package/release-notes/mcp/0.3.4.md +36 -0
- package/skills/agents.md +25 -0
- package/skills/aws.md +107 -0
- package/skills/cdk.md +141 -0
- package/skills/cicd.md +152 -0
- package/skills/datadog.md +129 -0
- package/skills/debugging.md +148 -0
- package/skills/dns.md +134 -0
- package/skills/dynamodb.md +140 -0
- package/skills/errors.md +142 -0
- package/skills/fabric.md +164 -0
- package/skills/index.md +7 -0
- package/skills/jaypie.md +100 -0
- package/skills/legacy.md +97 -0
- package/skills/logs.md +160 -0
- package/skills/mocks.md +174 -0
- package/skills/models.md +195 -0
- package/skills/releasenotes.md +94 -0
- package/skills/secrets.md +155 -0
- package/skills/services.md +175 -0
- package/skills/style.md +190 -0
- package/skills/tests.md +209 -0
- package/skills/tools.md +127 -0
- package/skills/topics.md +116 -0
- package/skills/variables.md +146 -0
- package/skills/writing.md +153 -0
package/skills/dns.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: DNS and domain configuration
|
|
3
|
+
related: cdk, aws
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# DNS Configuration
|
|
7
|
+
|
|
8
|
+
Managing domains and DNS for Jaypie applications.
|
|
9
|
+
|
|
10
|
+
## Route 53 Setup
|
|
11
|
+
|
|
12
|
+
### Hosted Zone
|
|
13
|
+
|
|
14
|
+
Create a hosted zone for your domain:
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
import { HostedZone } from "aws-cdk-lib/aws-route53";
|
|
18
|
+
|
|
19
|
+
const zone = HostedZone.fromLookup(this, "Zone", {
|
|
20
|
+
domainName: "example.com",
|
|
21
|
+
});
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### CloudFront Alias
|
|
25
|
+
|
|
26
|
+
Point domain to CloudFront distribution:
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { ARecord, RecordTarget } from "aws-cdk-lib/aws-route53";
|
|
30
|
+
import { CloudFrontTarget } from "aws-cdk-lib/aws-route53-targets";
|
|
31
|
+
|
|
32
|
+
new ARecord(this, "SiteAlias", {
|
|
33
|
+
zone,
|
|
34
|
+
recordName: "app", // app.example.com
|
|
35
|
+
target: RecordTarget.fromAlias(new CloudFrontTarget(distribution)),
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### API Gateway Custom Domain
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import { DomainName } from "aws-cdk-lib/aws-apigatewayv2";
|
|
43
|
+
|
|
44
|
+
const domainName = new DomainName(this, "ApiDomain", {
|
|
45
|
+
domainName: "api.example.com",
|
|
46
|
+
certificate: certificate,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
new ARecord(this, "ApiAlias", {
|
|
50
|
+
zone,
|
|
51
|
+
recordName: "api",
|
|
52
|
+
target: RecordTarget.fromAlias(new ApiGatewayv2DomainProperties(
|
|
53
|
+
domainName.regionalDomainName,
|
|
54
|
+
domainName.regionalHostedZoneId,
|
|
55
|
+
)),
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Certificate Management
|
|
60
|
+
|
|
61
|
+
### ACM Certificate
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
import { Certificate, CertificateValidation } from "aws-cdk-lib/aws-certificatemanager";
|
|
65
|
+
|
|
66
|
+
const certificate = new Certificate(this, "Certificate", {
|
|
67
|
+
domainName: "example.com",
|
|
68
|
+
subjectAlternativeNames: ["*.example.com"],
|
|
69
|
+
validation: CertificateValidation.fromDns(zone),
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Cross-Region Certificates
|
|
74
|
+
|
|
75
|
+
CloudFront requires certificates in `us-east-1`:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
// In us-east-1 stack
|
|
79
|
+
const cfCertificate = new Certificate(this, "CfCertificate", {
|
|
80
|
+
domainName: "app.example.com",
|
|
81
|
+
validation: CertificateValidation.fromDns(zone),
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Export ARN for use in other regions
|
|
85
|
+
new CfnOutput(this, "CertificateArn", {
|
|
86
|
+
value: cfCertificate.certificateArn,
|
|
87
|
+
exportName: "CloudFrontCertificateArn",
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Environment-Based Domains
|
|
92
|
+
|
|
93
|
+
Map environments to subdomains:
|
|
94
|
+
|
|
95
|
+
| Environment | Domain |
|
|
96
|
+
|-------------|--------|
|
|
97
|
+
| production | app.example.com |
|
|
98
|
+
| sandbox | sandbox.example.com |
|
|
99
|
+
| local | localhost:3000 |
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
const subdomain = process.env.PROJECT_ENV === "production"
|
|
103
|
+
? "app"
|
|
104
|
+
: process.env.PROJECT_ENV;
|
|
105
|
+
|
|
106
|
+
new ARecord(this, "Alias", {
|
|
107
|
+
zone,
|
|
108
|
+
recordName: subdomain,
|
|
109
|
+
target: RecordTarget.fromAlias(new CloudFrontTarget(distribution)),
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Debugging DNS
|
|
114
|
+
|
|
115
|
+
### Check DNS Resolution
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# Check A record
|
|
119
|
+
dig app.example.com A
|
|
120
|
+
|
|
121
|
+
# Check CNAME
|
|
122
|
+
dig app.example.com CNAME
|
|
123
|
+
|
|
124
|
+
# Check with specific nameserver
|
|
125
|
+
dig @ns-123.awsdns-45.com app.example.com
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Verify Certificate
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# Check certificate
|
|
132
|
+
openssl s_client -connect app.example.com:443 -servername app.example.com
|
|
133
|
+
```
|
|
134
|
+
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: DynamoDB patterns and queries
|
|
3
|
+
related: aws, cdk, models
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# DynamoDB Patterns
|
|
7
|
+
|
|
8
|
+
Best practices for DynamoDB with Jaypie applications.
|
|
9
|
+
|
|
10
|
+
## MCP DynamoDB Tools
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
aws_dynamodb_describe_table - Get table schema and indexes
|
|
14
|
+
aws_dynamodb_query - Query by partition key (efficient)
|
|
15
|
+
aws_dynamodb_scan - Full table scan (use sparingly)
|
|
16
|
+
aws_dynamodb_get_item - Get single item by key
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Key Design
|
|
20
|
+
|
|
21
|
+
### Single Table Design
|
|
22
|
+
|
|
23
|
+
Use prefixed keys for multiple entity types:
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
// User entity
|
|
27
|
+
{
|
|
28
|
+
pk: "USER#123",
|
|
29
|
+
sk: "PROFILE",
|
|
30
|
+
name: "John Doe",
|
|
31
|
+
email: "john@example.com"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// User's orders
|
|
35
|
+
{
|
|
36
|
+
pk: "USER#123",
|
|
37
|
+
sk: "ORDER#2024-01-15#abc",
|
|
38
|
+
total: 99.99,
|
|
39
|
+
status: "completed"
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Access Patterns
|
|
44
|
+
|
|
45
|
+
| Access Pattern | Key Condition |
|
|
46
|
+
|---------------|---------------|
|
|
47
|
+
| Get user profile | pk = USER#123, sk = PROFILE |
|
|
48
|
+
| List user orders | pk = USER#123, sk begins_with ORDER# |
|
|
49
|
+
| Get specific order | pk = USER#123, sk = ORDER#2024-01-15#abc |
|
|
50
|
+
|
|
51
|
+
## Query Examples
|
|
52
|
+
|
|
53
|
+
### Query via MCP
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
# Get user profile
|
|
57
|
+
aws_dynamodb_get_item --tableName "MyTable" --key '{"pk":{"S":"USER#123"},"sk":{"S":"PROFILE"}}'
|
|
58
|
+
|
|
59
|
+
# List user orders
|
|
60
|
+
aws_dynamodb_query --tableName "MyTable" \
|
|
61
|
+
--keyConditionExpression "pk = :pk AND begins_with(sk, :prefix)" \
|
|
62
|
+
--expressionAttributeValues '{":pk":{"S":"USER#123"},":prefix":{"S":"ORDER#"}}'
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Query in Code
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
|
|
69
|
+
import { QueryCommand } from "@aws-sdk/lib-dynamodb";
|
|
70
|
+
|
|
71
|
+
const client = new DynamoDBClient({});
|
|
72
|
+
|
|
73
|
+
const result = await client.send(new QueryCommand({
|
|
74
|
+
TableName: process.env.CDK_ENV_TABLE,
|
|
75
|
+
KeyConditionExpression: "pk = :pk AND begins_with(sk, :prefix)",
|
|
76
|
+
ExpressionAttributeValues: {
|
|
77
|
+
":pk": "USER#123",
|
|
78
|
+
":prefix": "ORDER#",
|
|
79
|
+
},
|
|
80
|
+
ScanIndexForward: false, // Newest first
|
|
81
|
+
Limit: 10,
|
|
82
|
+
}));
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## GSI Patterns
|
|
86
|
+
|
|
87
|
+
### By-Status Index
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
// GSI for querying by status across all users
|
|
91
|
+
{
|
|
92
|
+
pk: "USER#123",
|
|
93
|
+
sk: "ORDER#2024-01-15#abc",
|
|
94
|
+
gsi1pk: "ORDER#pending",
|
|
95
|
+
gsi1sk: "2024-01-15T10:00:00Z",
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Query pending orders:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
aws_dynamodb_query --tableName "MyTable" --indexName "gsi1" \
|
|
103
|
+
--keyConditionExpression "gsi1pk = :status" \
|
|
104
|
+
--expressionAttributeValues '{":status":{"S":"ORDER#pending"}}'
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## CDK Table Definition
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
import { JaypieTable } from "@jaypie/constructs";
|
|
111
|
+
|
|
112
|
+
const table = new JaypieTable(this, "DataTable", {
|
|
113
|
+
partitionKey: { name: "pk", type: AttributeType.STRING },
|
|
114
|
+
sortKey: { name: "sk", type: AttributeType.STRING },
|
|
115
|
+
globalSecondaryIndexes: [
|
|
116
|
+
{
|
|
117
|
+
indexName: "gsi1",
|
|
118
|
+
partitionKey: { name: "gsi1pk", type: AttributeType.STRING },
|
|
119
|
+
sortKey: { name: "gsi1sk", type: AttributeType.STRING },
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Local Development
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
# Start local DynamoDB
|
|
129
|
+
docker run -p 8000:8000 amazon/dynamodb-local
|
|
130
|
+
|
|
131
|
+
# Create table
|
|
132
|
+
AWS_ACCESS_KEY_ID=local AWS_SECRET_ACCESS_KEY=local \
|
|
133
|
+
aws dynamodb create-table \
|
|
134
|
+
--table-name MyTable \
|
|
135
|
+
--attribute-definitions AttributeName=pk,AttributeType=S AttributeName=sk,AttributeType=S \
|
|
136
|
+
--key-schema AttributeName=pk,KeyType=HASH AttributeName=sk,KeyType=RANGE \
|
|
137
|
+
--billing-mode PAY_PER_REQUEST \
|
|
138
|
+
--endpoint-url http://127.0.0.1:8000
|
|
139
|
+
```
|
|
140
|
+
|
package/skills/errors.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Error handling with @jaypie/errors
|
|
3
|
+
related: debugging, logs, tests
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Error Handling
|
|
7
|
+
|
|
8
|
+
Jaypie provides structured error types for consistent error handling.
|
|
9
|
+
|
|
10
|
+
## Core Principle
|
|
11
|
+
|
|
12
|
+
**Never throw vanilla `Error`**. Use Jaypie error types:
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
import { ConfigurationError, NotFoundError } from "jaypie";
|
|
16
|
+
|
|
17
|
+
// BAD
|
|
18
|
+
throw new Error("Missing API key");
|
|
19
|
+
|
|
20
|
+
// GOOD
|
|
21
|
+
throw new ConfigurationError("Missing API key");
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Error Types
|
|
25
|
+
|
|
26
|
+
| Error | HTTP Status | Use When |
|
|
27
|
+
|-------|-------------|----------|
|
|
28
|
+
| BadRequestError | 400 | Invalid input from client |
|
|
29
|
+
| UnauthorizedError | 401 | Authentication required |
|
|
30
|
+
| ForbiddenError | 403 | Authenticated but not permitted |
|
|
31
|
+
| NotFoundError | 404 | Resource doesn't exist |
|
|
32
|
+
| ConfigurationError | 500 | Missing or invalid config |
|
|
33
|
+
| InternalError | 500 | Unexpected server error |
|
|
34
|
+
|
|
35
|
+
## Usage Examples
|
|
36
|
+
|
|
37
|
+
### Bad Request
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
import { BadRequestError } from "jaypie";
|
|
41
|
+
|
|
42
|
+
if (!request.body.email) {
|
|
43
|
+
throw new BadRequestError("Email is required");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!isValidEmail(request.body.email)) {
|
|
47
|
+
throw new BadRequestError("Invalid email format");
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Not Found
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { NotFoundError } from "jaypie";
|
|
55
|
+
|
|
56
|
+
const user = await User.findById(userId);
|
|
57
|
+
if (!user) {
|
|
58
|
+
throw new NotFoundError(`User ${userId} not found`);
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Configuration Error
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { ConfigurationError } from "jaypie";
|
|
66
|
+
|
|
67
|
+
if (!process.env.API_KEY) {
|
|
68
|
+
throw new ConfigurationError("API_KEY environment variable is required");
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Unauthorized vs Forbidden
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import { UnauthorizedError, ForbiddenError } from "jaypie";
|
|
76
|
+
|
|
77
|
+
// No credentials provided
|
|
78
|
+
if (!token) {
|
|
79
|
+
throw new UnauthorizedError("Authentication required");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Credentials provided but insufficient permission
|
|
83
|
+
if (!user.hasRole("admin")) {
|
|
84
|
+
throw new ForbiddenError("Admin access required");
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Error Context
|
|
89
|
+
|
|
90
|
+
Add context to errors for debugging:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
import { NotFoundError } from "jaypie";
|
|
94
|
+
|
|
95
|
+
throw new NotFoundError("User not found", {
|
|
96
|
+
context: {
|
|
97
|
+
userId,
|
|
98
|
+
requestId: req.id,
|
|
99
|
+
searchedTables: ["users", "legacy_users"],
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Error Handling in Handlers
|
|
105
|
+
|
|
106
|
+
Jaypie handlers automatically catch and format errors:
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
import { lambdaHandler } from "@jaypie/lambda";
|
|
110
|
+
import { NotFoundError } from "jaypie";
|
|
111
|
+
|
|
112
|
+
export const handler = lambdaHandler(async (event) => {
|
|
113
|
+
const item = await getItem(event.id);
|
|
114
|
+
if (!item) {
|
|
115
|
+
throw new NotFoundError("Item not found");
|
|
116
|
+
// Returns: { statusCode: 404, body: { error: "Item not found" } }
|
|
117
|
+
}
|
|
118
|
+
return item;
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Testing Errors
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
import { expect, it } from "vitest";
|
|
126
|
+
import { NotFoundError } from "jaypie";
|
|
127
|
+
|
|
128
|
+
it("throws NotFoundError when user missing", async () => {
|
|
129
|
+
await expect(getUser("invalid-id"))
|
|
130
|
+
.rejects
|
|
131
|
+
.toThrow(NotFoundError);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("includes context in error", async () => {
|
|
135
|
+
try {
|
|
136
|
+
await getUser("invalid-id");
|
|
137
|
+
} catch (error) {
|
|
138
|
+
expect(error.context.userId).toBe("invalid-id");
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
package/skills/fabric.md
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Fabric service patterns and adapters
|
|
3
|
+
related: services, errors, tools
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Fabric Services
|
|
7
|
+
|
|
8
|
+
Fabric provides a unified service pattern that works across CLI, Lambda, LLM tools, and MCP.
|
|
9
|
+
|
|
10
|
+
## Core Concept
|
|
11
|
+
|
|
12
|
+
Define a service once, deploy it anywhere:
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
import { fabricService } from "@jaypie/fabric";
|
|
16
|
+
|
|
17
|
+
const greetService = fabricService({
|
|
18
|
+
alias: "greet",
|
|
19
|
+
description: "Greet a user by name",
|
|
20
|
+
input: {
|
|
21
|
+
name: {
|
|
22
|
+
type: String,
|
|
23
|
+
required: true,
|
|
24
|
+
description: "Name to greet",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
service: async ({ name }) => {
|
|
28
|
+
return `Hello, ${name}!`;
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Adapters
|
|
34
|
+
|
|
35
|
+
### Lambda Handler
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { fabricLambdaHandler } from "@jaypie/fabric";
|
|
39
|
+
|
|
40
|
+
export const handler = fabricLambdaHandler(greetService);
|
|
41
|
+
// Invoked via Lambda with { name: "World" }
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### CLI Command
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { fabricCommand } from "@jaypie/fabric";
|
|
48
|
+
|
|
49
|
+
const program = new Command();
|
|
50
|
+
program.addCommand(fabricCommand(greetService));
|
|
51
|
+
// $ cli greet --name World
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### MCP Tool
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { fabricMcpTool } from "@jaypie/fabric";
|
|
58
|
+
|
|
59
|
+
server.tool(...fabricMcpTool(greetService));
|
|
60
|
+
// Available as MCP tool "greet"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### LLM Tool
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
import { fabricLlmTool } from "@jaypie/fabric";
|
|
67
|
+
|
|
68
|
+
const tools = [fabricLlmTool(greetService)];
|
|
69
|
+
// Available to LLM as function call
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Service Suites
|
|
73
|
+
|
|
74
|
+
Group related services:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { createServiceSuite, fabricService } from "@jaypie/fabric";
|
|
78
|
+
|
|
79
|
+
const userService = fabricService({
|
|
80
|
+
alias: "user_get",
|
|
81
|
+
description: "Get user by ID",
|
|
82
|
+
input: { id: { type: String, required: true } },
|
|
83
|
+
service: async ({ id }) => User.findById(id),
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const userListService = fabricService({
|
|
87
|
+
alias: "user_list",
|
|
88
|
+
description: "List all users",
|
|
89
|
+
input: {},
|
|
90
|
+
service: async () => User.find(),
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const suite = createServiceSuite({
|
|
94
|
+
name: "users",
|
|
95
|
+
version: "1.0.0",
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
suite.register(userService, "users");
|
|
99
|
+
suite.register(userListService, "users");
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Input Validation
|
|
103
|
+
|
|
104
|
+
Fabric validates inputs automatically:
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
const service = fabricService({
|
|
108
|
+
input: {
|
|
109
|
+
email: {
|
|
110
|
+
type: String,
|
|
111
|
+
required: true,
|
|
112
|
+
description: "User email address",
|
|
113
|
+
},
|
|
114
|
+
count: {
|
|
115
|
+
type: Number,
|
|
116
|
+
required: false,
|
|
117
|
+
description: "Number of results",
|
|
118
|
+
},
|
|
119
|
+
status: {
|
|
120
|
+
type: ["active", "inactive"] as const,
|
|
121
|
+
required: false,
|
|
122
|
+
description: "Filter by status",
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
service: async ({ email, count, status }) => {
|
|
126
|
+
// email: string (validated)
|
|
127
|
+
// count: number | undefined
|
|
128
|
+
// status: "active" | "inactive" | undefined
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Error Handling
|
|
134
|
+
|
|
135
|
+
Fabric services use Jaypie errors:
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
import { fabricService } from "@jaypie/fabric";
|
|
139
|
+
import { NotFoundError, BadRequestError } from "jaypie";
|
|
140
|
+
|
|
141
|
+
const service = fabricService({
|
|
142
|
+
alias: "user_get",
|
|
143
|
+
input: { id: { type: String, required: true } },
|
|
144
|
+
service: async ({ id }) => {
|
|
145
|
+
if (!isValidId(id)) {
|
|
146
|
+
throw new BadRequestError("Invalid user ID format");
|
|
147
|
+
}
|
|
148
|
+
const user = await User.findById(id);
|
|
149
|
+
if (!user) {
|
|
150
|
+
throw new NotFoundError(`User ${id} not found`);
|
|
151
|
+
}
|
|
152
|
+
return user;
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Best Practices
|
|
158
|
+
|
|
159
|
+
1. **Single Responsibility**: Each service does one thing
|
|
160
|
+
2. **Descriptive Aliases**: Use `noun_verb` format (`user_get`, `order_create`)
|
|
161
|
+
3. **Clear Descriptions**: Write for AI tools that need context
|
|
162
|
+
4. **Input Documentation**: Describe what each input expects
|
|
163
|
+
5. **Return Types**: Return JSON-serializable data
|
|
164
|
+
|
package/skills/index.md
ADDED
package/skills/jaypie.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Jaypie overview and core concepts
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Introduction to Jaypie
|
|
6
|
+
|
|
7
|
+
Jaypie is a complete stack framework for building multi-environment cloud applications on AWS using TypeScript/Node.js.
|
|
8
|
+
|
|
9
|
+
## Core Packages
|
|
10
|
+
|
|
11
|
+
### Main Package: `jaypie`
|
|
12
|
+
|
|
13
|
+
The main package provides:
|
|
14
|
+
- **Secrets**: AWS Secrets Manager integration
|
|
15
|
+
- **Errors**: Structured error types (ConfigurationError, etc.)
|
|
16
|
+
- **Events**: Event parsing for Lambda handlers
|
|
17
|
+
- **Lifecycle**: Handler lifecycle management
|
|
18
|
+
- **Logging**: Structured logging with context
|
|
19
|
+
- **Queues**: SQS messaging utilities
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import {
|
|
23
|
+
getSecret,
|
|
24
|
+
log,
|
|
25
|
+
ConfigurationError,
|
|
26
|
+
HTTP,
|
|
27
|
+
PROJECT
|
|
28
|
+
} from "jaypie";
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Infrastructure: `@jaypie/constructs`
|
|
32
|
+
|
|
33
|
+
CDK constructs for AWS infrastructure:
|
|
34
|
+
- Lambda functions with best practices
|
|
35
|
+
- SQS queues with DLQ
|
|
36
|
+
- S3 buckets with encryption
|
|
37
|
+
- CloudFront distributions
|
|
38
|
+
- Secrets Manager secrets
|
|
39
|
+
|
|
40
|
+
### Testing: `@jaypie/testkit`
|
|
41
|
+
|
|
42
|
+
Testing utilities and mocks:
|
|
43
|
+
- Mock implementations for all Jaypie functions
|
|
44
|
+
- Vitest configuration helpers
|
|
45
|
+
- Test data fabrication
|
|
46
|
+
|
|
47
|
+
## Project Structure
|
|
48
|
+
|
|
49
|
+
Jaypie projects use npm workspaces:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
project/
|
|
53
|
+
├── packages/ # npm packages
|
|
54
|
+
│ ├── api/ # Express API
|
|
55
|
+
│ └── lib/ # Shared library
|
|
56
|
+
├── stacks/ # CDK infrastructure
|
|
57
|
+
│ └── cdk/ # CDK app
|
|
58
|
+
└── package.json # Root workspace config
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Key Conventions
|
|
62
|
+
|
|
63
|
+
### Environment Variables
|
|
64
|
+
|
|
65
|
+
| Variable | Purpose |
|
|
66
|
+
|----------|---------|
|
|
67
|
+
| `PROJECT_ENV` | Environment: local, sandbox, production |
|
|
68
|
+
| `PROJECT_KEY` | Project identifier for logging |
|
|
69
|
+
| `PROJECT_NONCE` | Unique resource identifier |
|
|
70
|
+
| `LOG_LEVEL` | Log level: trace, debug, info, warn, error |
|
|
71
|
+
|
|
72
|
+
### Error Handling
|
|
73
|
+
|
|
74
|
+
Never throw vanilla `Error`. Use Jaypie errors:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { ConfigurationError, NotFoundError } from "jaypie";
|
|
78
|
+
|
|
79
|
+
if (!config.required) {
|
|
80
|
+
throw new ConfigurationError("Missing required config");
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Logging
|
|
85
|
+
|
|
86
|
+
Use structured logging with context:
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import { log } from "jaypie";
|
|
90
|
+
|
|
91
|
+
log.info("Processing request", { userId, action });
|
|
92
|
+
log.error("Request failed", { error: error.message });
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Next Steps
|
|
96
|
+
|
|
97
|
+
- `skill("style")` - Code style conventions
|
|
98
|
+
- `skill("errors")` - Error handling patterns
|
|
99
|
+
- `skill("tests")` - Testing with Vitest
|
|
100
|
+
- `skill("cdk")` - Infrastructure with CDK
|