@jaypie/mcp 0.3.2 → 0.4.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/dist/createMcpServer.d.ts +7 -1
- package/dist/index.js +26 -3135
- package/dist/index.js.map +1 -1
- package/dist/suite.d.ts +1 -0
- package/dist/suite.js +2442 -0
- package/dist/suite.js.map +1 -0
- package/package.json +8 -3
- package/release-notes/constructs/1.2.17.md +11 -0
- package/release-notes/fabric/0.1.2.md +11 -0
- package/release-notes/fabric/0.1.3.md +25 -0
- package/release-notes/fabric/0.1.4.md +42 -0
- package/release-notes/mcp/0.3.3.md +12 -0
- package/release-notes/mcp/0.3.4.md +36 -0
- package/release-notes/mcp/0.4.0.md +27 -0
- package/release-notes/testkit/1.2.15.md +23 -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 +191 -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/prompts/Branch_Management.md +0 -34
- package/prompts/Development_Process.md +0 -89
- package/prompts/Jaypie_Agent_Rules.md +0 -110
- package/prompts/Jaypie_Auth0_Express_Mongoose.md +0 -736
- package/prompts/Jaypie_Browser_and_Frontend_Web_Packages.md +0 -18
- package/prompts/Jaypie_CDK_Constructs_and_Patterns.md +0 -430
- package/prompts/Jaypie_CICD_with_GitHub_Actions.md +0 -371
- package/prompts/Jaypie_Commander_CLI_Package.md +0 -166
- package/prompts/Jaypie_Core_Errors_and_Logging.md +0 -39
- package/prompts/Jaypie_DynamoDB_Package.md +0 -774
- package/prompts/Jaypie_Eslint_NPM_Package.md +0 -78
- package/prompts/Jaypie_Express_Package.md +0 -630
- package/prompts/Jaypie_Fabric_Commander.md +0 -411
- package/prompts/Jaypie_Fabric_LLM.md +0 -312
- package/prompts/Jaypie_Fabric_Lambda.md +0 -308
- package/prompts/Jaypie_Fabric_MCP.md +0 -316
- package/prompts/Jaypie_Fabric_Package.md +0 -513
- package/prompts/Jaypie_Fabricator.md +0 -617
- package/prompts/Jaypie_Ideal_Project_Structure.md +0 -78
- package/prompts/Jaypie_Init_CICD_with_GitHub_Actions.md +0 -1186
- package/prompts/Jaypie_Init_Express_on_Lambda.md +0 -115
- package/prompts/Jaypie_Init_Jaypie_CDK_Package.md +0 -35
- package/prompts/Jaypie_Init_Lambda_Package.md +0 -505
- package/prompts/Jaypie_Init_Monorepo_Project.md +0 -44
- package/prompts/Jaypie_Init_Project_Subpackage.md +0 -65
- package/prompts/Jaypie_Legacy_Patterns.md +0 -15
- package/prompts/Jaypie_Llm_Calls.md +0 -449
- package/prompts/Jaypie_Llm_Tools.md +0 -155
- package/prompts/Jaypie_MCP_Package.md +0 -281
- package/prompts/Jaypie_Mocks_and_Testkit.md +0 -137
- package/prompts/Jaypie_Repokit.md +0 -103
- package/prompts/Jaypie_Scrub.md +0 -177
- package/prompts/Jaypie_Streaming.md +0 -467
- package/prompts/Templates_CDK_Subpackage.md +0 -115
- package/prompts/Templates_Express_Subpackage.md +0 -187
- package/prompts/Templates_Project_Monorepo.md +0 -326
- package/prompts/Templates_Project_Subpackage.md +0 -93
- package/prompts/Write_Efficient_Prompt_Guides.md +0 -48
- package/prompts/Write_and_Maintain_Engaging_Readme.md +0 -67
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Debugging techniques and troubleshooting
|
|
3
|
+
related: logs, datadog, errors
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Debugging Techniques
|
|
7
|
+
|
|
8
|
+
Strategies for debugging Jaypie applications.
|
|
9
|
+
|
|
10
|
+
## Log-Based Debugging
|
|
11
|
+
|
|
12
|
+
### Enable Debug Logging
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
LOG_LEVEL=debug npm run dev
|
|
16
|
+
LOG_LEVEL=trace npm run dev # Most verbose
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Search Logs via MCP
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
# Find errors in last hour
|
|
23
|
+
datadog_logs --query "status:error" --from "now-1h"
|
|
24
|
+
|
|
25
|
+
# Find specific request
|
|
26
|
+
datadog_logs --query "@requestId:abc-123"
|
|
27
|
+
|
|
28
|
+
# Search CloudWatch directly
|
|
29
|
+
aws_logs_filter_log_events --logGroupName "/aws/lambda/my-function" --filterPattern "ERROR"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Common Issues
|
|
33
|
+
|
|
34
|
+
### "undefined" in Stack Names
|
|
35
|
+
|
|
36
|
+
If you see `undefined` in a CDK stack name, a required variable is missing:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// BAD: undefined if PROJECT_NONCE not set
|
|
40
|
+
const stackName = `api-${process.env.PROJECT_NONCE}`;
|
|
41
|
+
|
|
42
|
+
// GOOD: Provide default
|
|
43
|
+
const nonce = process.env.PROJECT_NONCE || "dev";
|
|
44
|
+
const stackName = `api-${nonce}`;
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### "unknown" in Logs
|
|
48
|
+
|
|
49
|
+
`unknown` indicates a value was expected but fell back to a default:
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
// This produces "unknown" if PROJECT_KEY not set
|
|
53
|
+
log.info("Starting", { project: process.env.PROJECT_KEY || "unknown" });
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Lambda Cold Starts
|
|
57
|
+
|
|
58
|
+
Check initialization time:
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
datadog_logs --query "@dd.cold_start:true" --from "now-1h"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Connection Timeouts
|
|
65
|
+
|
|
66
|
+
For database or external service timeouts:
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// Check Lambda timeout settings
|
|
70
|
+
aws_lambda_get_function --functionName "my-function"
|
|
71
|
+
|
|
72
|
+
// Look for timeout patterns in logs
|
|
73
|
+
datadog_logs --query "timeout OR ETIMEDOUT" --from "now-1h"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Increase Lambda timeout or optimize slow operations.
|
|
77
|
+
|
|
78
|
+
## Local Testing
|
|
79
|
+
|
|
80
|
+
### Lambda Local Testing
|
|
81
|
+
|
|
82
|
+
Use SAM CLI for local Lambda testing:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
cd packages/express/docker
|
|
86
|
+
sam local invoke -e event.json
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### DynamoDB Local
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Start local DynamoDB
|
|
93
|
+
docker run -p 8000:8000 amazon/dynamodb-local
|
|
94
|
+
|
|
95
|
+
# Access with AWS CLI
|
|
96
|
+
AWS_ACCESS_KEY_ID=local AWS_SECRET_ACCESS_KEY=local \
|
|
97
|
+
aws dynamodb list-tables --endpoint-url http://127.0.0.1:8000
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Stack Traces
|
|
101
|
+
|
|
102
|
+
### Finding Root Cause
|
|
103
|
+
|
|
104
|
+
Jaypie errors include context:
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
try {
|
|
108
|
+
await riskyOperation();
|
|
109
|
+
} catch (error) {
|
|
110
|
+
log.error("Operation failed", {
|
|
111
|
+
error: error.message,
|
|
112
|
+
stack: error.stack,
|
|
113
|
+
context: error.context, // Jaypie errors include this
|
|
114
|
+
});
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Error Classification
|
|
120
|
+
|
|
121
|
+
| Error Type | Cause |
|
|
122
|
+
|------------|-------|
|
|
123
|
+
| ConfigurationError | Missing or invalid config |
|
|
124
|
+
| NotFoundError | Resource doesn't exist |
|
|
125
|
+
| UnauthorizedError | Authentication failed |
|
|
126
|
+
| ForbiddenError | Permission denied |
|
|
127
|
+
| BadRequestError | Invalid input |
|
|
128
|
+
|
|
129
|
+
## Step Function Debugging
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
# List running executions
|
|
133
|
+
aws_stepfunctions_list_executions --stateMachineArn "arn:..." --statusFilter "RUNNING"
|
|
134
|
+
|
|
135
|
+
# Stop stuck execution
|
|
136
|
+
aws_stepfunctions_stop_execution --executionArn "arn:..." --cause "Manual stop for debugging"
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Queue Debugging
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
# Check queue depth
|
|
143
|
+
aws_sqs_get_queue_attributes --queueUrl "https://..."
|
|
144
|
+
|
|
145
|
+
# Peek at messages (does not delete)
|
|
146
|
+
aws_sqs_receive_message --queueUrl "https://..." --maxNumberOfMessages 5
|
|
147
|
+
```
|
|
148
|
+
|
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
|
+
|