@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
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: coding style resource, helpful when stuck on lint errors
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# Jaypie Eslint ๐ฆโโฌ๐งน
|
|
6
|
-
|
|
7
|
-
Linting rules for Jaypie project coding style, Prettier, and bespoke rules.
|
|
8
|
-
|
|
9
|
-
## ๐ฏ Goals
|
|
10
|
-
|
|
11
|
-
* Opinionated "umbrella" config for common projects using Jaypie
|
|
12
|
-
* Strengthen rules that prevent mistakes
|
|
13
|
-
* Loosen rules that slow down development
|
|
14
|
-
* Use warn for things that will not break (console, prettier)
|
|
15
|
-
|
|
16
|
-
Tries to "just work" but sometimes conflicts with opinionated project linters like nuxt.
|
|
17
|
-
|
|
18
|
-
## ๐ Capabilities
|
|
19
|
-
|
|
20
|
-
* `@eslint/js` recommended
|
|
21
|
-
* `eslint-plugin-import-x` recommended
|
|
22
|
-
* Ignore cdk, dist
|
|
23
|
-
* Language options; assume module
|
|
24
|
-
* Custom rules
|
|
25
|
-
* CommonJS for .cjs
|
|
26
|
-
* Ignore Nuxt
|
|
27
|
-
* Prettier
|
|
28
|
-
* TypeScript for .ts
|
|
29
|
-
* Tests
|
|
30
|
-
* Prettier-Vue for .vue
|
|
31
|
-
|
|
32
|
-
## ๐ฟ Installation
|
|
33
|
-
|
|
34
|
-
```sh
|
|
35
|
-
npm install --save-dev @jaypie/eslint
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## ๐ Usage
|
|
39
|
-
|
|
40
|
-
```javascript
|
|
41
|
-
// eslint.config.js
|
|
42
|
-
export { default as default } from "@jaypie/eslint";
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
### With Nuxt
|
|
46
|
-
|
|
47
|
-
```javascript
|
|
48
|
-
// @ts-check
|
|
49
|
-
import jaypie from "@jaypie/eslint/nuxt";
|
|
50
|
-
import { withNuxt } from "./.nuxt/eslint.config.mjs";
|
|
51
|
-
|
|
52
|
-
export default withNuxt(...jaypie, {
|
|
53
|
-
languageOptions: {
|
|
54
|
-
globals: {
|
|
55
|
-
defineNuxtConfig: "readonly",
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
rules: {
|
|
59
|
-
"@typescript-eslint/no-explicit-any": "off",
|
|
60
|
-
},
|
|
61
|
-
});
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
### In CommonJS
|
|
65
|
-
|
|
66
|
-
```javascript
|
|
67
|
-
export { default as default } from "@jaypie/commonjs";
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### Other Configs
|
|
71
|
-
|
|
72
|
-
```javascript
|
|
73
|
-
import jaypie from "@jaypie/eslint";
|
|
74
|
-
export default [
|
|
75
|
-
...jaypie
|
|
76
|
-
// More configuration
|
|
77
|
-
];
|
|
78
|
-
```
|
|
@@ -1,630 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Complete guide to using Jaypie Express features including expressHandler, CORS, lifecycle hooks, and pre-built routes
|
|
3
|
-
globs: packages/express/**
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Jaypie Express Package
|
|
7
|
-
|
|
8
|
-
Jaypie provides Express utilities through `@jaypie/express` (also available via `jaypie`). The main export is `expressHandler`, a function that wraps Express route handlers to add error handling, logging, lifecycle hooks, and automatic response formatting.
|
|
9
|
-
|
|
10
|
-
## Installation
|
|
11
|
-
|
|
12
|
-
```bash
|
|
13
|
-
npm install jaypie
|
|
14
|
-
# or
|
|
15
|
-
npm install @jaypie/express
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## expressHandler
|
|
19
|
-
|
|
20
|
-
Wraps Express route handlers with error handling, logging, and lifecycle management.
|
|
21
|
-
|
|
22
|
-
### Basic Usage
|
|
23
|
-
|
|
24
|
-
```typescript
|
|
25
|
-
import { expressHandler } from "jaypie";
|
|
26
|
-
import type { Request, Response } from "express";
|
|
27
|
-
|
|
28
|
-
const myRoute = expressHandler(async (req: Request, res: Response) => {
|
|
29
|
-
return { message: "Hello, World!" };
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
// Use in Express
|
|
33
|
-
app.get("/hello", myRoute);
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
### Return Value Handling
|
|
37
|
-
|
|
38
|
-
expressHandler automatically formats responses based on return values:
|
|
39
|
-
|
|
40
|
-
| Return Value | HTTP Status | Response |
|
|
41
|
-
|--------------|-------------|----------|
|
|
42
|
-
| Object | 200 | JSON body |
|
|
43
|
-
| Array | 200 | JSON body |
|
|
44
|
-
| String (JSON) | 200 | Parsed JSON |
|
|
45
|
-
| String (other) | 200 | Text body |
|
|
46
|
-
| Number | 200 | Sent via `res.send()` |
|
|
47
|
-
| `true` | 201 Created | Empty |
|
|
48
|
-
| `null`, `undefined`, `false` | 204 No Content | Empty |
|
|
49
|
-
| Object with `.json()` method | 200 | Result of `.json()` |
|
|
50
|
-
|
|
51
|
-
**Note:** If you call `res.json()`, `res.send()`, or `res.end()` directly in your handler, expressHandler will log a warning but respect your call. Prefer using return values instead.
|
|
52
|
-
|
|
53
|
-
### Options
|
|
54
|
-
|
|
55
|
-
```typescript
|
|
56
|
-
import { expressHandler } from "jaypie";
|
|
57
|
-
import type { ExpressHandlerOptions } from "jaypie";
|
|
58
|
-
|
|
59
|
-
const options: ExpressHandlerOptions = {
|
|
60
|
-
name: "myHandler", // Handler name for logging
|
|
61
|
-
chaos: "low", // Chaos testing level
|
|
62
|
-
unavailable: false, // Return 503 if true
|
|
63
|
-
setup: [], // Setup function(s)
|
|
64
|
-
teardown: [], // Teardown function(s)
|
|
65
|
-
validate: [], // Validation function(s)
|
|
66
|
-
locals: {}, // Values to set on req.locals
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
const handler = expressHandler(async (req, res) => {
|
|
70
|
-
return { success: true };
|
|
71
|
-
}, options);
|
|
72
|
-
|
|
73
|
-
// Alternative: options first
|
|
74
|
-
const handler2 = expressHandler(options, async (req, res) => {
|
|
75
|
-
return { success: true };
|
|
76
|
-
});
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
## Lifecycle Hooks
|
|
80
|
-
|
|
81
|
-
Lifecycle hooks execute in this order:
|
|
82
|
-
1. Setup functions (in array order)
|
|
83
|
-
2. Locals functions (in object key order, after all setup)
|
|
84
|
-
3. Validate functions (in array order)
|
|
85
|
-
4. Main handler
|
|
86
|
-
5. Teardown functions (always run, even on error)
|
|
87
|
-
|
|
88
|
-
### Setup Functions
|
|
89
|
-
|
|
90
|
-
Run before validation and the main handler. Use for initialization, authentication checks, or setting up request context.
|
|
91
|
-
|
|
92
|
-
```typescript
|
|
93
|
-
import { expressHandler } from "jaypie";
|
|
94
|
-
import type { JaypieHandlerSetup } from "jaypie";
|
|
95
|
-
|
|
96
|
-
const authenticateUser: JaypieHandlerSetup = async (req, res) => {
|
|
97
|
-
const token = req.headers.authorization;
|
|
98
|
-
// Validate token, throw UnauthorizedError if invalid
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
const loadTenant: JaypieHandlerSetup = async (req, res) => {
|
|
102
|
-
req.locals.tenant = await getTenant(req.params.tenantId);
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
const handler = expressHandler(
|
|
106
|
-
async (req, res) => {
|
|
107
|
-
// req.locals.tenant is available here
|
|
108
|
-
return { tenant: req.locals.tenant };
|
|
109
|
-
},
|
|
110
|
-
{
|
|
111
|
-
setup: [authenticateUser, loadTenant],
|
|
112
|
-
}
|
|
113
|
-
);
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
### Teardown Functions
|
|
117
|
-
|
|
118
|
-
Run after the main handler completes. Teardown functions execute regardless of success or error, making them suitable for cleanup operations.
|
|
119
|
-
|
|
120
|
-
```typescript
|
|
121
|
-
import type { JaypieHandlerTeardown } from "jaypie";
|
|
122
|
-
|
|
123
|
-
const closeConnection: JaypieHandlerTeardown = async (req, res) => {
|
|
124
|
-
await req.locals.dbConnection?.close();
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
const handler = expressHandler(
|
|
128
|
-
async (req, res) => {
|
|
129
|
-
req.locals.dbConnection = await openConnection();
|
|
130
|
-
return await doWork(req.locals.dbConnection);
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
teardown: closeConnection,
|
|
134
|
-
}
|
|
135
|
-
);
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
### Validation Functions
|
|
139
|
-
|
|
140
|
-
Run before the main handler. Return `true` to continue or `false`/throw to reject.
|
|
141
|
-
|
|
142
|
-
```typescript
|
|
143
|
-
import { ForbiddenError } from "jaypie";
|
|
144
|
-
import type { JaypieHandlerValidate } from "jaypie";
|
|
145
|
-
|
|
146
|
-
const requireAdmin: JaypieHandlerValidate = (req, res) => {
|
|
147
|
-
if (!req.locals.user?.isAdmin) {
|
|
148
|
-
throw new ForbiddenError();
|
|
149
|
-
}
|
|
150
|
-
return true;
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
const validateBody: JaypieHandlerValidate = (req, res) => {
|
|
154
|
-
return req.body?.email && req.body?.name;
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
const handler = expressHandler(
|
|
158
|
-
async (req, res) => {
|
|
159
|
-
// Only runs if user is admin and body is valid
|
|
160
|
-
return { success: true };
|
|
161
|
-
},
|
|
162
|
-
{
|
|
163
|
-
validate: [requireAdmin, validateBody],
|
|
164
|
-
}
|
|
165
|
-
);
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
### Locals
|
|
169
|
-
|
|
170
|
-
Set values on `req.locals`. Values can be static or functions that receive `(req, res)`. Locals functions are called AFTER setup functions.
|
|
171
|
-
|
|
172
|
-
```typescript
|
|
173
|
-
import type { ExpressHandlerLocals } from "jaypie";
|
|
174
|
-
|
|
175
|
-
const getUser: ExpressHandlerLocals = async (req, res) => {
|
|
176
|
-
return await User.findById(req.params.userId);
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
const handler = expressHandler(
|
|
180
|
-
async (req, res) => {
|
|
181
|
-
// Access via req.locals
|
|
182
|
-
console.log(req.locals.apiVersion); // "v1"
|
|
183
|
-
console.log(req.locals.user); // User object
|
|
184
|
-
return req.locals.user;
|
|
185
|
-
},
|
|
186
|
-
{
|
|
187
|
-
locals: {
|
|
188
|
-
apiVersion: "v1", // Static value
|
|
189
|
-
user: getUser, // Function called after setup
|
|
190
|
-
},
|
|
191
|
-
}
|
|
192
|
-
);
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
## CORS Helper
|
|
196
|
-
|
|
197
|
-
Configures CORS middleware using the `cors` npm package with automatic origin validation.
|
|
198
|
-
|
|
199
|
-
```typescript
|
|
200
|
-
import { cors } from "jaypie";
|
|
201
|
-
import type { CorsConfig } from "jaypie";
|
|
202
|
-
|
|
203
|
-
// Default: uses BASE_URL or PROJECT_BASE_URL env vars
|
|
204
|
-
app.use(cors());
|
|
205
|
-
|
|
206
|
-
// Wildcard origin
|
|
207
|
-
app.use(cors({ origin: "*" }));
|
|
208
|
-
|
|
209
|
-
// Specific origin
|
|
210
|
-
app.use(cors({ origin: "https://example.com" }));
|
|
211
|
-
|
|
212
|
-
// Multiple origins
|
|
213
|
-
app.use(cors({ origin: ["https://example.com", "https://app.example.com"] }));
|
|
214
|
-
|
|
215
|
-
// Custom configuration
|
|
216
|
-
const corsConfig: CorsConfig = {
|
|
217
|
-
origin: "https://api.example.com",
|
|
218
|
-
overrides: {
|
|
219
|
-
// Additional options passed to the cors package
|
|
220
|
-
credentials: true,
|
|
221
|
-
},
|
|
222
|
-
};
|
|
223
|
-
app.use(cors(corsConfig));
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
Environment variables:
|
|
227
|
-
- `BASE_URL` or `PROJECT_BASE_URL`: Default allowed origins
|
|
228
|
-
- `PROJECT_ENV=sandbox` or `PROJECT_SANDBOX_MODE=true`: Allows localhost origins (including ports)
|
|
229
|
-
|
|
230
|
-
## Pre-built Routes
|
|
231
|
-
|
|
232
|
-
Ready-to-use route handlers for common responses.
|
|
233
|
-
|
|
234
|
-
```typescript
|
|
235
|
-
import {
|
|
236
|
-
badRequestRoute, // 400 Bad Request
|
|
237
|
-
echoRoute, // 200 with request echo
|
|
238
|
-
forbiddenRoute, // 403 Forbidden
|
|
239
|
-
goneRoute, // 410 Gone
|
|
240
|
-
methodNotAllowedRoute, // 405 Method Not Allowed
|
|
241
|
-
noContentRoute, // 204 No Content
|
|
242
|
-
notFoundRoute, // 404 Not Found
|
|
243
|
-
notImplementedRoute, // 501 Not Implemented
|
|
244
|
-
} from "jaypie";
|
|
245
|
-
|
|
246
|
-
// Use as catch-all or placeholder routes
|
|
247
|
-
app.all("/deprecated/*", goneRoute);
|
|
248
|
-
app.use("*", notFoundRoute);
|
|
249
|
-
|
|
250
|
-
// Echo route for debugging
|
|
251
|
-
app.get("/debug/echo", echoRoute);
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
## HTTP Code Handler
|
|
255
|
-
|
|
256
|
-
Create custom HTTP status code handlers.
|
|
257
|
-
|
|
258
|
-
```typescript
|
|
259
|
-
import { expressHttpCodeHandler, HTTP } from "jaypie";
|
|
260
|
-
|
|
261
|
-
// Returns 200 OK with empty body
|
|
262
|
-
const okRoute = expressHttpCodeHandler(HTTP.CODE.OK);
|
|
263
|
-
|
|
264
|
-
// Returns 202 Accepted
|
|
265
|
-
const acceptedRoute = expressHttpCodeHandler(202, { name: "accepted" });
|
|
266
|
-
|
|
267
|
-
app.post("/jobs", acceptedRoute);
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
## Error Handling
|
|
271
|
-
|
|
272
|
-
Throw Jaypie errors for proper HTTP responses.
|
|
273
|
-
|
|
274
|
-
```typescript
|
|
275
|
-
import {
|
|
276
|
-
expressHandler,
|
|
277
|
-
BadRequestError,
|
|
278
|
-
NotFoundError,
|
|
279
|
-
UnauthorizedError,
|
|
280
|
-
ForbiddenError,
|
|
281
|
-
InternalError,
|
|
282
|
-
log,
|
|
283
|
-
} from "jaypie";
|
|
284
|
-
|
|
285
|
-
const handler = expressHandler(async (req, res) => {
|
|
286
|
-
const item = await findItem(req.params.id);
|
|
287
|
-
|
|
288
|
-
if (!item) {
|
|
289
|
-
log.warn("Item not found");
|
|
290
|
-
throw new NotFoundError();
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
if (!canAccess(req.user, item)) {
|
|
294
|
-
throw new ForbiddenError();
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
return item;
|
|
298
|
-
});
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
Errors return JSON:API compliant error responses:
|
|
302
|
-
|
|
303
|
-
```json
|
|
304
|
-
{
|
|
305
|
-
"errors": [{
|
|
306
|
-
"status": 404,
|
|
307
|
-
"title": "Not Found",
|
|
308
|
-
"detail": "The requested resource was not found"
|
|
309
|
-
}]
|
|
310
|
-
}
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
## TypeScript Types
|
|
314
|
-
|
|
315
|
-
All lifecycle function types are exported for type safety:
|
|
316
|
-
|
|
317
|
-
```typescript
|
|
318
|
-
import type {
|
|
319
|
-
ExpressHandlerOptions,
|
|
320
|
-
ExpressHandlerLocals,
|
|
321
|
-
JaypieHandlerSetup,
|
|
322
|
-
JaypieHandlerTeardown,
|
|
323
|
-
JaypieHandlerValidate,
|
|
324
|
-
CorsConfig,
|
|
325
|
-
} from "jaypie";
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
## Complete Example
|
|
329
|
-
|
|
330
|
-
```typescript
|
|
331
|
-
import express from "express";
|
|
332
|
-
import {
|
|
333
|
-
cors,
|
|
334
|
-
expressHandler,
|
|
335
|
-
notFoundRoute,
|
|
336
|
-
NotFoundError,
|
|
337
|
-
ForbiddenError,
|
|
338
|
-
UnauthorizedError,
|
|
339
|
-
log,
|
|
340
|
-
} from "jaypie";
|
|
341
|
-
import type {
|
|
342
|
-
JaypieHandlerSetup,
|
|
343
|
-
JaypieHandlerValidate,
|
|
344
|
-
ExpressHandlerLocals,
|
|
345
|
-
} from "jaypie";
|
|
346
|
-
|
|
347
|
-
const app = express();
|
|
348
|
-
app.use(express.json());
|
|
349
|
-
app.use(cors());
|
|
350
|
-
|
|
351
|
-
// Lifecycle functions
|
|
352
|
-
const authenticate: JaypieHandlerSetup = async (req, res) => {
|
|
353
|
-
const token = req.headers.authorization?.replace("Bearer ", "");
|
|
354
|
-
if (!token) throw new UnauthorizedError();
|
|
355
|
-
req.locals.user = await verifyToken(token);
|
|
356
|
-
};
|
|
357
|
-
|
|
358
|
-
const requireOwner: JaypieHandlerValidate = (req, res) => {
|
|
359
|
-
return req.locals.resource?.ownerId === req.locals.user?.id;
|
|
360
|
-
};
|
|
361
|
-
|
|
362
|
-
const loadResource: ExpressHandlerLocals = async (req, res) => {
|
|
363
|
-
const resource = await Resource.findById(req.params.id);
|
|
364
|
-
if (!resource) throw new NotFoundError();
|
|
365
|
-
return resource;
|
|
366
|
-
};
|
|
367
|
-
|
|
368
|
-
// Route handler
|
|
369
|
-
const updateResource = expressHandler(
|
|
370
|
-
async (req, res) => {
|
|
371
|
-
const { resource, user } = req.locals;
|
|
372
|
-
|
|
373
|
-
resource.name = req.body.name;
|
|
374
|
-
resource.updatedBy = user.id;
|
|
375
|
-
await resource.save();
|
|
376
|
-
|
|
377
|
-
log.trace("Resource updated");
|
|
378
|
-
return resource;
|
|
379
|
-
},
|
|
380
|
-
{
|
|
381
|
-
name: "updateResource",
|
|
382
|
-
setup: authenticate,
|
|
383
|
-
validate: requireOwner,
|
|
384
|
-
locals: {
|
|
385
|
-
resource: loadResource,
|
|
386
|
-
},
|
|
387
|
-
}
|
|
388
|
-
);
|
|
389
|
-
|
|
390
|
-
app.put("/resources/:id", updateResource);
|
|
391
|
-
app.use("*", notFoundRoute);
|
|
392
|
-
|
|
393
|
-
export default app;
|
|
394
|
-
```
|
|
395
|
-
|
|
396
|
-
## Response Headers
|
|
397
|
-
|
|
398
|
-
expressHandler automatically sets these headers:
|
|
399
|
-
- `X-Powered-By: @jaypie/express` (always set, overrides Express default)
|
|
400
|
-
- `X-Project-Handler: {name}` (when name option is provided)
|
|
401
|
-
- `X-Project-Invocation: {uuid}` (request tracking ID, always set)
|
|
402
|
-
- `X-Project-Environment: {env}` (when PROJECT_ENV is set)
|
|
403
|
-
- `X-Project-Key: {key}` (when PROJECT_KEY is set)
|
|
404
|
-
- `X-Project-Version: {version}` (when PROJECT_VERSION is set or version option provided)
|
|
405
|
-
|
|
406
|
-
## Datadog Integration
|
|
407
|
-
|
|
408
|
-
When Datadog environment variables are configured, expressHandler automatically submits metrics for each request including status code and path.
|
|
409
|
-
|
|
410
|
-
## Streaming Responses
|
|
411
|
-
|
|
412
|
-
Use `expressStreamHandler` for Server-Sent Events (SSE) streaming responses. Ideal for real-time updates, LLM streaming, and long-running operations.
|
|
413
|
-
|
|
414
|
-
### Basic Streaming Usage
|
|
415
|
-
|
|
416
|
-
```typescript
|
|
417
|
-
import { expressStreamHandler } from "jaypie";
|
|
418
|
-
import type { Request, Response } from "express";
|
|
419
|
-
|
|
420
|
-
const streamRoute = expressStreamHandler(async (req: Request, res: Response) => {
|
|
421
|
-
// Write SSE events directly to response
|
|
422
|
-
res.write("event: message\ndata: {\"text\": \"Hello\"}\n\n");
|
|
423
|
-
res.write("event: message\ndata: {\"text\": \"World\"}\n\n");
|
|
424
|
-
// Handler automatically ends the stream
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
app.get("/stream", streamRoute);
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
### Streaming with LLM
|
|
431
|
-
|
|
432
|
-
```typescript
|
|
433
|
-
import { expressStreamHandler, Llm, createExpressStream } from "jaypie";
|
|
434
|
-
|
|
435
|
-
const llmStreamRoute = expressStreamHandler(async (req: Request, res: Response) => {
|
|
436
|
-
const llm = new Llm("anthropic");
|
|
437
|
-
const stream = llm.stream(req.body.prompt);
|
|
438
|
-
|
|
439
|
-
// createExpressStream pipes LLM chunks as SSE events
|
|
440
|
-
await createExpressStream(stream, res);
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
app.post("/chat", llmStreamRoute);
|
|
444
|
-
```
|
|
445
|
-
|
|
446
|
-
### Stream Handler Options
|
|
447
|
-
|
|
448
|
-
`expressStreamHandler` supports the same lifecycle options as `expressHandler`:
|
|
449
|
-
|
|
450
|
-
```typescript
|
|
451
|
-
import { expressStreamHandler } from "jaypie";
|
|
452
|
-
import type { ExpressStreamHandlerOptions } from "jaypie";
|
|
453
|
-
|
|
454
|
-
const options: ExpressStreamHandlerOptions = {
|
|
455
|
-
name: "myStreamHandler", // Handler name for logging
|
|
456
|
-
contentType: "text/event-stream", // Default SSE content type
|
|
457
|
-
chaos: "low", // Chaos testing level
|
|
458
|
-
secrets: ["API_KEY"], // Secrets to load
|
|
459
|
-
setup: [], // Setup function(s)
|
|
460
|
-
teardown: [], // Teardown function(s)
|
|
461
|
-
validate: [], // Validation function(s)
|
|
462
|
-
locals: {}, // Values to set on req.locals
|
|
463
|
-
unavailable: false, // Return 503 if true
|
|
464
|
-
};
|
|
465
|
-
|
|
466
|
-
const handler = expressStreamHandler(async (req, res) => {
|
|
467
|
-
// Streaming logic
|
|
468
|
-
}, options);
|
|
469
|
-
```
|
|
470
|
-
|
|
471
|
-
### SSE Headers
|
|
472
|
-
|
|
473
|
-
`expressStreamHandler` automatically sets SSE headers:
|
|
474
|
-
- `Content-Type: text/event-stream`
|
|
475
|
-
- `Cache-Control: no-cache`
|
|
476
|
-
- `Connection: keep-alive`
|
|
477
|
-
- `X-Accel-Buffering: no` (disables nginx buffering)
|
|
478
|
-
|
|
479
|
-
### Error Handling in Streams
|
|
480
|
-
|
|
481
|
-
Errors are formatted as SSE error events:
|
|
482
|
-
|
|
483
|
-
```typescript
|
|
484
|
-
// Jaypie errors and unhandled errors are written as:
|
|
485
|
-
// event: error
|
|
486
|
-
// data: {"errors":[{"status":500,"title":"Internal Error"}]}
|
|
487
|
-
```
|
|
488
|
-
|
|
489
|
-
### TypeScript Types
|
|
490
|
-
|
|
491
|
-
```typescript
|
|
492
|
-
import type {
|
|
493
|
-
ExpressStreamHandlerOptions,
|
|
494
|
-
ExpressStreamHandlerLocals,
|
|
495
|
-
JaypieStreamHandlerSetup,
|
|
496
|
-
JaypieStreamHandlerTeardown,
|
|
497
|
-
JaypieStreamHandlerValidate,
|
|
498
|
-
} from "jaypie";
|
|
499
|
-
```
|
|
500
|
-
|
|
501
|
-
## Lambda Handlers
|
|
502
|
-
|
|
503
|
-
Create Lambda handlers from Express apps using `createLambdaHandler` and `createLambdaStreamHandler`. These functions wrap Express applications to run directly on AWS Lambda Function URLs without requiring a separate Lambda adapter library.
|
|
504
|
-
|
|
505
|
-
### Buffered Handler
|
|
506
|
-
|
|
507
|
-
Use `createLambdaHandler` for standard Lambda responses where the entire response is buffered before sending.
|
|
508
|
-
|
|
509
|
-
```typescript
|
|
510
|
-
import express from "express";
|
|
511
|
-
import { createLambdaHandler, expressHandler } from "jaypie";
|
|
512
|
-
|
|
513
|
-
const app = express();
|
|
514
|
-
|
|
515
|
-
app.get("/", expressHandler(async (req, res) => {
|
|
516
|
-
return { message: "Hello from Lambda!" };
|
|
517
|
-
}));
|
|
518
|
-
|
|
519
|
-
// Export for Lambda Function URL
|
|
520
|
-
export const handler = createLambdaHandler(app);
|
|
521
|
-
```
|
|
522
|
-
|
|
523
|
-
### Streaming Handler
|
|
524
|
-
|
|
525
|
-
Use `createLambdaStreamHandler` for Lambda response streaming, ideal for Server-Sent Events (SSE) and real-time updates.
|
|
526
|
-
|
|
527
|
-
```typescript
|
|
528
|
-
import express from "express";
|
|
529
|
-
import { createLambdaStreamHandler, expressStreamHandler } from "jaypie";
|
|
530
|
-
|
|
531
|
-
const app = express();
|
|
532
|
-
|
|
533
|
-
app.get("/stream", expressStreamHandler(async (req, res) => {
|
|
534
|
-
res.write("event: message\ndata: {\"text\": \"Hello\"}\n\n");
|
|
535
|
-
res.write("event: message\ndata: {\"text\": \"World\"}\n\n");
|
|
536
|
-
}));
|
|
537
|
-
|
|
538
|
-
// Export for Lambda Function URL with streaming
|
|
539
|
-
export const handler = createLambdaStreamHandler(app);
|
|
540
|
-
```
|
|
541
|
-
|
|
542
|
-
### Combined Usage
|
|
543
|
-
|
|
544
|
-
A typical Lambda Express application with both buffered and streaming endpoints:
|
|
545
|
-
|
|
546
|
-
```typescript
|
|
547
|
-
import express from "express";
|
|
548
|
-
import {
|
|
549
|
-
createLambdaHandler,
|
|
550
|
-
createLambdaStreamHandler,
|
|
551
|
-
expressHandler,
|
|
552
|
-
expressStreamHandler,
|
|
553
|
-
cors,
|
|
554
|
-
} from "jaypie";
|
|
555
|
-
|
|
556
|
-
const app = express();
|
|
557
|
-
app.use(express.json());
|
|
558
|
-
app.use(cors());
|
|
559
|
-
|
|
560
|
-
// Standard buffered route
|
|
561
|
-
app.get("/api/data", expressHandler(async (req, res) => {
|
|
562
|
-
return { data: "buffered response" };
|
|
563
|
-
}));
|
|
564
|
-
|
|
565
|
-
// SSE streaming route
|
|
566
|
-
app.get("/api/stream", expressStreamHandler(async (req, res) => {
|
|
567
|
-
for (let i = 0; i < 5; i++) {
|
|
568
|
-
res.write(`event: update\ndata: {"count": ${i}}\n\n`);
|
|
569
|
-
}
|
|
570
|
-
}));
|
|
571
|
-
|
|
572
|
-
// Choose handler based on your needs
|
|
573
|
-
// For buffered: export const handler = createLambdaHandler(app);
|
|
574
|
-
// For streaming: export const handler = createLambdaStreamHandler(app);
|
|
575
|
-
export const handler = createLambdaHandler(app);
|
|
576
|
-
```
|
|
577
|
-
|
|
578
|
-
### Lambda Context Access
|
|
579
|
-
|
|
580
|
-
Both handlers expose Lambda context on the request object:
|
|
581
|
-
|
|
582
|
-
```typescript
|
|
583
|
-
app.get("/", expressHandler(async (req, res) => {
|
|
584
|
-
// Access Lambda context directly on request
|
|
585
|
-
const awsRequestId = (req as any)._lambdaContext?.awsRequestId;
|
|
586
|
-
return { requestId: awsRequestId };
|
|
587
|
-
}));
|
|
588
|
-
```
|
|
589
|
-
|
|
590
|
-
### TypeScript Types
|
|
591
|
-
|
|
592
|
-
```typescript
|
|
593
|
-
import type {
|
|
594
|
-
LambdaHandler,
|
|
595
|
-
LambdaStreamHandler,
|
|
596
|
-
LambdaContext,
|
|
597
|
-
FunctionUrlEvent,
|
|
598
|
-
LambdaResponse,
|
|
599
|
-
} from "jaypie";
|
|
600
|
-
```
|
|
601
|
-
|
|
602
|
-
## Invoke UUID Detection
|
|
603
|
-
|
|
604
|
-
Use `getCurrentInvokeUuid` to get the current request ID. Automatically detects the environment (Lambda, Lambda Web Adapter, or local development).
|
|
605
|
-
|
|
606
|
-
### Basic Usage
|
|
607
|
-
|
|
608
|
-
```typescript
|
|
609
|
-
import { getCurrentInvokeUuid } from "jaypie";
|
|
610
|
-
|
|
611
|
-
const handler = expressHandler(async (req, res) => {
|
|
612
|
-
const invokeUuid = getCurrentInvokeUuid(req);
|
|
613
|
-
// Returns AWS request ID in Lambda, or generates a local UUID
|
|
614
|
-
return { requestId: invokeUuid };
|
|
615
|
-
});
|
|
616
|
-
```
|
|
617
|
-
|
|
618
|
-
### Environment Detection
|
|
619
|
-
|
|
620
|
-
The function adapts to different runtime environments:
|
|
621
|
-
|
|
622
|
-
1. **Lambda (native)**: Uses `awsRequestId` from Lambda context
|
|
623
|
-
2. **Lambda Web Adapter**: Extracts from `x-amzn-request-id` header or `_X_AMZN_TRACE_ID` env var
|
|
624
|
-
3. **Local development**: Generates a UUID for consistent tracing
|
|
625
|
-
|
|
626
|
-
### Lambda Web Adapter Headers
|
|
627
|
-
|
|
628
|
-
When running Express behind AWS Lambda Web Adapter, the function extracts the request ID from:
|
|
629
|
-
- `x-amzn-request-id` header (set by Lambda Web Adapter)
|
|
630
|
-
- `_X_AMZN_TRACE_ID` environment variable (X-Ray trace ID, set by Lambda runtime)
|