@jaypie/mcp 0.7.2 → 0.7.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/suites/docs/index.js +1 -1
- package/package.json +1 -1
- package/release-notes/constructs/1.2.26.md +11 -0
- package/release-notes/express/1.2.6.md +17 -0
- package/release-notes/express/1.2.7.md +48 -0
- package/release-notes/mcp/0.7.3.md +14 -0
- package/skills/cors.md +354 -0
- package/skills/express.md +1 -1
|
@@ -9,7 +9,7 @@ import { gt } from 'semver';
|
|
|
9
9
|
/**
|
|
10
10
|
* Docs Suite - Documentation services (skill, version, release_notes)
|
|
11
11
|
*/
|
|
12
|
-
const BUILD_VERSION_STRING = "@jaypie/mcp@0.7.
|
|
12
|
+
const BUILD_VERSION_STRING = "@jaypie/mcp@0.7.4#9b2629b2"
|
|
13
13
|
;
|
|
14
14
|
const __filename$1 = fileURLToPath(import.meta.url);
|
|
15
15
|
const __dirname$1 = path.dirname(__filename$1);
|
package/package.json
CHANGED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
version: 1.2.26
|
|
3
|
+
date: 2025-01-30
|
|
4
|
+
summary: Auto-set PROJECT_BASE_URL on Lambda handlers
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Changes
|
|
8
|
+
|
|
9
|
+
- JaypieDistribution now automatically sets `PROJECT_BASE_URL` environment variable on Lambda handlers when host is resolved
|
|
10
|
+
- JaypieApiGateway now automatically sets `PROJECT_BASE_URL` environment variable on Lambda handlers when host is resolved
|
|
11
|
+
- This enables automatic CORS configuration when using the cors helper from `@jaypie/express`
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
version: 1.2.6
|
|
3
|
+
date: 2025-01-30
|
|
4
|
+
summary: Improve CORS origin matching security with subdomain support
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Changes
|
|
8
|
+
|
|
9
|
+
- Fixed CORS security issue: origin matching now uses proper hostname extraction instead of `.includes()`
|
|
10
|
+
- Added subdomain matching: when `example.com` is allowed, subdomains like `app.example.com` are automatically allowed
|
|
11
|
+
- Added `extractHostname()` and `isOriginAllowed()` helpers for secure origin validation
|
|
12
|
+
- Origins in config can now be specified without protocol prefix (defaults to `https://`)
|
|
13
|
+
|
|
14
|
+
## Security
|
|
15
|
+
|
|
16
|
+
- Previously, allowing `example.com` would incorrectly allow `notexample.com` or `fakeexample.com` due to `.includes()` matching
|
|
17
|
+
- Now uses proper hostname extraction and suffix matching with dot separator to prevent this issue
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
version: 1.2.7
|
|
3
|
+
date: 2025-01-30
|
|
4
|
+
summary: Fix CORS OPTIONS preflight hanging with Lambda streaming
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Bug Fix
|
|
8
|
+
|
|
9
|
+
Fixed an issue where CORS OPTIONS preflight requests would hang indefinitely when using `createLambdaStreamHandler` with the `cors` middleware.
|
|
10
|
+
|
|
11
|
+
### Problem
|
|
12
|
+
|
|
13
|
+
When using Lambda response streaming, browser CORS preflight (OPTIONS) requests would hang because:
|
|
14
|
+
- The standard cors package's call to `res.end()` could get delayed by streaming's async behavior
|
|
15
|
+
- The Lambda response stream would stay open waiting for streaming data that never comes
|
|
16
|
+
- Browsers would timeout waiting for the preflight response
|
|
17
|
+
|
|
18
|
+
### Solution
|
|
19
|
+
|
|
20
|
+
The `cors` middleware now detects OPTIONS requests early and handles them synchronously:
|
|
21
|
+
- Sets all required CORS headers directly (Access-Control-Allow-Origin, Access-Control-Allow-Methods, etc.)
|
|
22
|
+
- Terminates the response immediately with a 204 No Content status
|
|
23
|
+
- Ensures the response stream doesn't enter streaming mode for preflight requests
|
|
24
|
+
|
|
25
|
+
### Usage
|
|
26
|
+
|
|
27
|
+
No changes required. The fix is automatic when using `cors` with `createLambdaStreamHandler`:
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import express from "express";
|
|
31
|
+
import { cors, createLambdaStreamHandler } from "@jaypie/express";
|
|
32
|
+
|
|
33
|
+
const app = express();
|
|
34
|
+
app.use(cors({ origin: "https://example.com" }));
|
|
35
|
+
|
|
36
|
+
// Streaming endpoint works correctly with CORS
|
|
37
|
+
app.post("/stream", (req, res) => {
|
|
38
|
+
res.setHeader("Content-Type", "text/event-stream");
|
|
39
|
+
res.write("data: hello\n\n");
|
|
40
|
+
res.end();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
export const handler = createLambdaStreamHandler(app);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Related
|
|
47
|
+
|
|
48
|
+
- GitHub Issue: [#174](https://github.com/finlaysonstudio/jaypie/issues/174)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
version: 0.7.3
|
|
3
|
+
date: 2025-01-30
|
|
4
|
+
summary: Add CORS skill documentation comparing Jaypie cors helper with standard Express cors
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Changes
|
|
8
|
+
|
|
9
|
+
- Add `skill("cors")` documentation covering:
|
|
10
|
+
- Jaypie `cors` helper from `@jaypie/express`
|
|
11
|
+
- Comparison with standard `express/cors` middleware
|
|
12
|
+
- Environment variable integration (`BASE_URL`, `PROJECT_BASE_URL`)
|
|
13
|
+
- Sandbox mode automatic localhost handling
|
|
14
|
+
- Configuration options and usage patterns
|
package/skills/cors.md
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: CORS configuration for Express.js using Jaypie's cors helper
|
|
3
|
+
related: express, handlers, lambda
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# CORS Configuration
|
|
7
|
+
|
|
8
|
+
Jaypie's `@jaypie/express` package provides a `cors` helper that wraps the standard [express/cors](https://github.com/expressjs/cors) middleware with Jaypie-specific defaults and environment awareness.
|
|
9
|
+
|
|
10
|
+
## Quick Start
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
import express from "express";
|
|
14
|
+
import { cors } from "jaypie";
|
|
15
|
+
|
|
16
|
+
const app = express();
|
|
17
|
+
|
|
18
|
+
// Basic usage - environment-aware defaults
|
|
19
|
+
app.use(cors());
|
|
20
|
+
|
|
21
|
+
// With specific origin
|
|
22
|
+
app.use(cors({ origin: "https://example.com" }));
|
|
23
|
+
|
|
24
|
+
// Multiple origins
|
|
25
|
+
app.use(cors({ origin: ["https://a.com", "https://b.com"] }));
|
|
26
|
+
|
|
27
|
+
// Allow all origins
|
|
28
|
+
app.use(cors({ origin: "*" }));
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Default Behavior: `cors()`
|
|
32
|
+
|
|
33
|
+
When called with no arguments, `cors()` allows origins based on environment variables only:
|
|
34
|
+
|
|
35
|
+
| Condition | Allowed |
|
|
36
|
+
|-----------|---------|
|
|
37
|
+
| No `Origin` header (curl, mobile, server-to-server) | ✅ Always |
|
|
38
|
+
| Origin matches `BASE_URL` env var | ✅ Yes |
|
|
39
|
+
| Origin matches `PROJECT_BASE_URL` env var | ✅ Yes |
|
|
40
|
+
| Origin is subdomain of allowed origin | ✅ Yes |
|
|
41
|
+
| `PROJECT_ENV=sandbox` and origin is `localhost[:port]` | ✅ Yes |
|
|
42
|
+
| Any other cross-origin request | ❌ Rejected |
|
|
43
|
+
|
|
44
|
+
### Subdomain Matching
|
|
45
|
+
|
|
46
|
+
When you allow a domain, all subdomains of that domain are also automatically allowed:
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
// Allow example.com and all its subdomains
|
|
50
|
+
app.use(cors({ origin: "example.com" }));
|
|
51
|
+
// Allows: https://example.com, https://app.example.com, https://sub.app.example.com
|
|
52
|
+
// Rejects: https://notexample.com, https://fakeexample.com
|
|
53
|
+
|
|
54
|
+
// Protocol prefix is optional in config
|
|
55
|
+
app.use(cors({ origin: "example.com" })); // Same as https://example.com
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
This is secure because it uses proper hostname extraction and suffix matching with a dot separator—domains like `notexample.com` or `fakeexample.com` are correctly rejected.
|
|
59
|
+
|
|
60
|
+
**Important:** With no environment variables set and not in sandbox mode, `cors()` rejects all cross-origin browser requests. This is secure by default—you must explicitly configure allowed origins.
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
// Example: Production environment
|
|
64
|
+
// BASE_URL=https://api.example.com
|
|
65
|
+
// PROJECT_BASE_URL=https://example.com
|
|
66
|
+
|
|
67
|
+
app.use(cors());
|
|
68
|
+
// Allows: https://api.example.com, https://example.com
|
|
69
|
+
// Rejects: all other origins
|
|
70
|
+
|
|
71
|
+
// Example: Sandbox/development
|
|
72
|
+
// PROJECT_ENV=sandbox
|
|
73
|
+
|
|
74
|
+
app.use(cors());
|
|
75
|
+
// Allows: http://localhost, http://localhost:3000, http://localhost:5173, etc.
|
|
76
|
+
// Rejects: all other origins (unless BASE_URL/PROJECT_BASE_URL set)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Jaypie vs Standard Express CORS
|
|
80
|
+
|
|
81
|
+
### Key Differences
|
|
82
|
+
|
|
83
|
+
| Feature | Standard `cors` | Jaypie `cors` |
|
|
84
|
+
|---------|-----------------|---------------|
|
|
85
|
+
| **Origin Validation** | Static or manual callback | Dynamic environment-aware callback |
|
|
86
|
+
| **Subdomain Matching** | Manual implementation | Automatic (subdomains of allowed origins permitted) |
|
|
87
|
+
| **Environment URLs** | Not supported | Reads `BASE_URL`, `PROJECT_BASE_URL` |
|
|
88
|
+
| **Sandbox Mode** | Manual localhost config | Auto-allows localhost in sandbox |
|
|
89
|
+
| **Error Response** | Generic CORS error | Returns `CorsError` with JSON body |
|
|
90
|
+
| **No-Origin Requests** | Configurable | Always allowed (mobile apps, curl) |
|
|
91
|
+
| **Protocol Prefix** | Required in config | Optional (defaults to `https://`) |
|
|
92
|
+
|
|
93
|
+
### Jaypie-Specific Behavior
|
|
94
|
+
|
|
95
|
+
1. **Environment Variable Integration**
|
|
96
|
+
- `BASE_URL` - Automatically added to allowed origins
|
|
97
|
+
- `PROJECT_BASE_URL` - Automatically added to allowed origins
|
|
98
|
+
- Protocol auto-added if missing (defaults to `https://`)
|
|
99
|
+
|
|
100
|
+
2. **Sandbox Mode** (when `PROJECT_ENV=sandbox` or `PROJECT_SANDBOX_MODE=true`)
|
|
101
|
+
- `http://localhost` is automatically allowed
|
|
102
|
+
- `http://localhost:*` (any port) is automatically allowed
|
|
103
|
+
|
|
104
|
+
3. **No-Origin Requests**
|
|
105
|
+
- Requests without an `Origin` header are always allowed
|
|
106
|
+
- Enables mobile apps, server-to-server calls, and curl testing
|
|
107
|
+
|
|
108
|
+
4. **Error Handling**
|
|
109
|
+
- Invalid origins return a `CorsError` from `@jaypie/errors`
|
|
110
|
+
- Response includes JSON body with error details
|
|
111
|
+
|
|
112
|
+
## Configuration Options
|
|
113
|
+
|
|
114
|
+
### Jaypie `cors` Options
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
interface CorsConfig {
|
|
118
|
+
origin?: string | string[]; // Additional allowed origins
|
|
119
|
+
overrides?: Record<string, unknown>; // Pass-through to express/cors
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Standard `cors` Options (via `overrides`)
|
|
124
|
+
|
|
125
|
+
All standard `cors` options can be passed through the `overrides` parameter:
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
import { cors } from "jaypie";
|
|
129
|
+
|
|
130
|
+
app.use(cors({
|
|
131
|
+
origin: "https://example.com",
|
|
132
|
+
overrides: {
|
|
133
|
+
methods: ["GET", "POST", "PUT"],
|
|
134
|
+
allowedHeaders: ["Content-Type", "Authorization"],
|
|
135
|
+
exposedHeaders: ["X-Request-Id"],
|
|
136
|
+
credentials: true,
|
|
137
|
+
maxAge: 86400,
|
|
138
|
+
optionsSuccessStatus: 200,
|
|
139
|
+
preflightContinue: false,
|
|
140
|
+
},
|
|
141
|
+
}));
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Full Standard Options Reference
|
|
145
|
+
|
|
146
|
+
| Option | Type | Default | Description |
|
|
147
|
+
|--------|------|---------|-------------|
|
|
148
|
+
| `methods` | `string \| string[]` | All methods | Allowed HTTP methods |
|
|
149
|
+
| `allowedHeaders` | `string \| string[]` | Reflect request | Allowed request headers |
|
|
150
|
+
| `exposedHeaders` | `string \| string[]` | None | Headers exposed to browser |
|
|
151
|
+
| `credentials` | `boolean` | `false` | Allow credentials (cookies) |
|
|
152
|
+
| `maxAge` | `number` | None | Preflight cache duration (seconds) |
|
|
153
|
+
| `preflightContinue` | `boolean` | `false` | Pass OPTIONS to next handler |
|
|
154
|
+
| `optionsSuccessStatus` | `number` | `204` | Status for successful OPTIONS |
|
|
155
|
+
|
|
156
|
+
## Usage Patterns
|
|
157
|
+
|
|
158
|
+
### API with Multiple Allowed Origins
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
import { cors } from "jaypie";
|
|
162
|
+
|
|
163
|
+
// Production + staging + local origins
|
|
164
|
+
app.use(cors({
|
|
165
|
+
origin: [
|
|
166
|
+
"https://app.example.com",
|
|
167
|
+
"https://staging.example.com",
|
|
168
|
+
],
|
|
169
|
+
}));
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### API with Credentials
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import { cors } from "jaypie";
|
|
176
|
+
|
|
177
|
+
// Enable cookies for cross-origin requests
|
|
178
|
+
app.use(cors({
|
|
179
|
+
origin: "https://app.example.com",
|
|
180
|
+
overrides: {
|
|
181
|
+
credentials: true,
|
|
182
|
+
},
|
|
183
|
+
}));
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Public API (Allow All Origins)
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
import { cors } from "jaypie";
|
|
190
|
+
|
|
191
|
+
// Allow any origin
|
|
192
|
+
app.use(cors({ origin: "*" }));
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Per-Route CORS
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
import express from "express";
|
|
199
|
+
import { cors } from "jaypie";
|
|
200
|
+
|
|
201
|
+
const app = express();
|
|
202
|
+
|
|
203
|
+
// Public endpoints - wide open
|
|
204
|
+
app.use("/api/public", cors({ origin: "*" }));
|
|
205
|
+
|
|
206
|
+
// Protected endpoints - restricted origins
|
|
207
|
+
app.use("/api/admin", cors({
|
|
208
|
+
origin: "https://admin.example.com",
|
|
209
|
+
overrides: { credentials: true },
|
|
210
|
+
}));
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## How Origin Validation Works
|
|
214
|
+
|
|
215
|
+
Jaypie's `cors` uses a dynamic origin callback that checks origins in this order:
|
|
216
|
+
|
|
217
|
+
1. **Wildcard** - If `origin: "*"`, allow all origins
|
|
218
|
+
2. **No Origin** - Allow requests without Origin header (mobile, curl, etc.)
|
|
219
|
+
3. **Environment URLs** - Check `BASE_URL` and `PROJECT_BASE_URL`
|
|
220
|
+
4. **Configured Origins** - Check origins passed to `cors({ origin: [...] })`
|
|
221
|
+
5. **Sandbox Localhost** - In sandbox mode, allow localhost with any port
|
|
222
|
+
|
|
223
|
+
For each allowed origin, the validation checks:
|
|
224
|
+
- **Exact hostname match** - `example.com` matches `https://example.com`
|
|
225
|
+
- **Subdomain match** - `example.com` matches `https://app.example.com`
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
// Simplified origin validation logic
|
|
229
|
+
const isAllowed =
|
|
230
|
+
origin === "*" ||
|
|
231
|
+
!requestOrigin ||
|
|
232
|
+
isOriginAllowed(requestOrigin, process.env.BASE_URL) ||
|
|
233
|
+
isOriginAllowed(requestOrigin, process.env.PROJECT_BASE_URL) ||
|
|
234
|
+
configuredOrigins.some(o => isOriginAllowed(requestOrigin, o)) ||
|
|
235
|
+
(isSandbox && requestOrigin.match(/^http:\/\/localhost(:\d+)?$/));
|
|
236
|
+
|
|
237
|
+
// Where isOriginAllowed checks for exact match OR subdomain match
|
|
238
|
+
// e.g., "app.example.com".endsWith(".example.com") → true
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Error Responses
|
|
242
|
+
|
|
243
|
+
When an origin is not allowed, Jaypie returns a structured error response:
|
|
244
|
+
|
|
245
|
+
```json
|
|
246
|
+
{
|
|
247
|
+
"errors": [{
|
|
248
|
+
"title": "CorsError",
|
|
249
|
+
"status": 403,
|
|
250
|
+
"detail": "Cross-Origin Request Blocked"
|
|
251
|
+
}]
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Comparison: Standard vs Jaypie CORS Setup
|
|
256
|
+
|
|
257
|
+
### Standard Express CORS
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
import express from "express";
|
|
261
|
+
import cors from "cors";
|
|
262
|
+
|
|
263
|
+
const app = express();
|
|
264
|
+
|
|
265
|
+
// Manual origin validation
|
|
266
|
+
app.use(cors({
|
|
267
|
+
origin: (origin, callback) => {
|
|
268
|
+
const allowedOrigins = [
|
|
269
|
+
process.env.BASE_URL,
|
|
270
|
+
process.env.PROJECT_BASE_URL,
|
|
271
|
+
"https://app.example.com",
|
|
272
|
+
];
|
|
273
|
+
|
|
274
|
+
// Allow no-origin requests
|
|
275
|
+
if (!origin) {
|
|
276
|
+
return callback(null, true);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Check allowed list
|
|
280
|
+
if (allowedOrigins.some(o => origin.includes(o))) {
|
|
281
|
+
return callback(null, true);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Sandbox localhost
|
|
285
|
+
if (process.env.PROJECT_ENV === "sandbox" &&
|
|
286
|
+
origin.match(/^http:\/\/localhost(:\d+)?$/)) {
|
|
287
|
+
return callback(null, true);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
callback(new Error("Not allowed by CORS"));
|
|
291
|
+
},
|
|
292
|
+
credentials: true,
|
|
293
|
+
}));
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Jaypie CORS (Equivalent)
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
import express from "express";
|
|
300
|
+
import { cors } from "jaypie";
|
|
301
|
+
|
|
302
|
+
const app = express();
|
|
303
|
+
|
|
304
|
+
// Same behavior, less code
|
|
305
|
+
app.use(cors({
|
|
306
|
+
origin: "https://app.example.com",
|
|
307
|
+
overrides: { credentials: true },
|
|
308
|
+
}));
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Environment Variables
|
|
312
|
+
|
|
313
|
+
| Variable | Description |
|
|
314
|
+
|----------|-------------|
|
|
315
|
+
| `BASE_URL` | Automatically added to allowed origins |
|
|
316
|
+
| `PROJECT_BASE_URL` | Automatically added to allowed origins |
|
|
317
|
+
| `PROJECT_ENV` | Set to `sandbox` to enable localhost access |
|
|
318
|
+
| `PROJECT_SANDBOX_MODE` | Set to `true` to enable localhost access |
|
|
319
|
+
|
|
320
|
+
## Lambda Streaming Compatibility
|
|
321
|
+
|
|
322
|
+
Jaypie's `cors` middleware is designed to work seamlessly with Lambda response streaming (`createLambdaStreamHandler`). Unlike the standard `cors` package, Jaypie's implementation handles OPTIONS preflight requests early and terminates them immediately, preventing the response stream from hanging.
|
|
323
|
+
|
|
324
|
+
This is critical because:
|
|
325
|
+
- Browser CORS preflight requests (OPTIONS) must complete quickly with a 204 response
|
|
326
|
+
- Lambda streaming keeps the response stream open for streaming data
|
|
327
|
+
- Without early termination, OPTIONS requests would hang waiting for streaming data that never comes
|
|
328
|
+
|
|
329
|
+
No special configuration is needed—Jaypie's `cors` automatically detects OPTIONS requests and handles them appropriately.
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
import express from "express";
|
|
333
|
+
import { cors, createLambdaStreamHandler } from "@jaypie/express";
|
|
334
|
+
|
|
335
|
+
const app = express();
|
|
336
|
+
|
|
337
|
+
// CORS works automatically with streaming
|
|
338
|
+
app.use(cors({ origin: "https://example.com" }));
|
|
339
|
+
|
|
340
|
+
app.post("/stream", (req, res) => {
|
|
341
|
+
res.setHeader("Content-Type", "text/event-stream");
|
|
342
|
+
res.write("data: hello\n\n");
|
|
343
|
+
res.end();
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
// Deploy with Lambda streaming - CORS preflight works correctly
|
|
347
|
+
export const handler = createLambdaStreamHandler(app);
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
## See Also
|
|
351
|
+
|
|
352
|
+
- **`skill("express")`** - Express handler and Lambda adapter documentation
|
|
353
|
+
- **`skill("handlers")`** - Jaypie handler lifecycle documentation
|
|
354
|
+
- **`skill("lambda")`** - AWS Lambda deployment patterns
|