@vyriy/handler 0.7.7 → 0.8.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/README.md +395 -180
- package/api/api.d.ts +10 -0
- package/api/api.js +19 -0
- package/api/http/compose/compose.d.ts +2 -0
- package/api/http/compose/compose.js +1 -0
- package/api/http/compose/index.d.ts +1 -0
- package/api/http/compose/index.js +1 -0
- package/api/http/factory/factory.d.ts +2 -0
- package/api/http/factory/factory.js +1 -0
- package/api/http/factory/index.d.ts +1 -0
- package/api/http/factory/index.js +1 -0
- package/api/http/http.d.ts +3 -0
- package/api/http/http.js +14 -0
- package/api/http/index.d.ts +3 -0
- package/api/http/index.js +2 -0
- package/api/http/types.d.ts +18 -0
- package/api/http/wrappers/cors.d.ts +1 -0
- package/api/http/wrappers/cors.js +9 -0
- package/api/http/wrappers/error.d.ts +3 -0
- package/api/http/wrappers/error.js +21 -0
- package/api/http/wrappers/headers.d.ts +1 -0
- package/api/http/wrappers/headers.js +8 -0
- package/api/http/wrappers/healthcheck.d.ts +5 -0
- package/api/http/wrappers/healthcheck.js +19 -0
- package/api/http/wrappers/index.d.ts +5 -0
- package/api/http/wrappers/index.js +5 -0
- package/api/http/wrappers/logger.d.ts +2 -0
- package/api/http/wrappers/logger.js +28 -0
- package/api/index.d.ts +3 -0
- package/api/index.js +2 -0
- package/api/stream/compose/compose.d.ts +2 -0
- package/api/stream/compose/compose.js +1 -0
- package/api/stream/compose/index.d.ts +1 -0
- package/api/stream/compose/index.js +1 -0
- package/api/stream/factory/factory.d.ts +5 -0
- package/api/stream/factory/factory.js +3 -0
- package/api/stream/factory/index.d.ts +1 -0
- package/api/stream/factory/index.js +1 -0
- package/api/stream/index.d.ts +3 -0
- package/api/stream/index.js +2 -0
- package/api/stream/stream.d.ts +3 -0
- package/api/stream/stream.js +13 -0
- package/api/stream/types.d.ts +36 -0
- package/api/stream/wrappers/chaos.d.ts +2 -0
- package/{wrapper → api/stream/wrappers}/chaos.js +1 -12
- package/api/stream/wrappers/context.d.ts +1 -0
- package/api/stream/wrappers/context.js +6 -0
- package/api/stream/wrappers/cors.d.ts +1 -0
- package/api/stream/wrappers/cors.js +10 -0
- package/api/stream/wrappers/error.d.ts +3 -0
- package/{wrapper → api/stream/wrappers}/error.js +1 -18
- package/api/stream/wrappers/headers.d.ts +1 -0
- package/api/stream/wrappers/headers.js +7 -0
- package/api/stream/wrappers/healthcheck.d.ts +2 -0
- package/{wrapper → api/stream/wrappers}/healthcheck.js +1 -6
- package/api/stream/wrappers/index.d.ts +10 -0
- package/api/stream/wrappers/index.js +10 -0
- package/api/stream/wrappers/logger.d.ts +2 -0
- package/api/stream/wrappers/logger.js +20 -0
- package/api/stream/wrappers/smoke.d.ts +2 -0
- package/{wrapper → api/stream/wrappers}/smoke.js +1 -5
- package/api/stream/wrappers/timeout.d.ts +1 -0
- package/api/stream/wrappers/timeout.js +6 -0
- package/api/types.d.ts +17 -0
- package/api/wrappers/chaos.d.ts +1 -0
- package/api/wrappers/chaos.js +1 -0
- package/{wrapper → api/wrappers}/cors.d.ts +0 -1
- package/api/wrappers/cors.js +11 -0
- package/api/wrappers/error.d.ts +4 -0
- package/api/wrappers/error.js +18 -0
- package/{wrapper → api/wrappers}/headers.d.ts +0 -1
- package/{wrapper → api/wrappers}/headers.js +1 -6
- package/{wrapper → api/wrappers}/healthcheck.d.ts +0 -1
- package/api/wrappers/healthcheck.js +22 -0
- package/api/wrappers/index.d.ts +5 -0
- package/api/wrappers/index.js +5 -0
- package/compose/compose.d.ts +2 -0
- package/compose/compose.js +1 -0
- package/compose/index.d.ts +1 -0
- package/compose/index.js +1 -0
- package/create/create.d.ts +12 -0
- package/create/create.js +22 -0
- package/create/index.d.ts +1 -0
- package/create/index.js +1 -0
- package/dynamodb/dynamodb.d.ts +4 -0
- package/dynamodb/dynamodb.js +8 -0
- package/dynamodb/index.d.ts +1 -0
- package/dynamodb/index.js +1 -0
- package/eventBridge/eventBridge.d.ts +4 -0
- package/eventBridge/eventBridge.js +8 -0
- package/eventBridge/index.d.ts +1 -0
- package/eventBridge/index.js +1 -0
- package/factory/factory.d.ts +4 -0
- package/factory/factory.js +2 -0
- package/factory/index.d.ts +1 -0
- package/factory/index.js +1 -0
- package/index.d.ts +21 -19
- package/index.js +21 -19
- package/package.json +731 -185
- package/s3/index.d.ts +1 -0
- package/s3/index.js +1 -0
- package/s3/s3.d.ts +4 -0
- package/s3/s3.js +8 -0
- package/schedule/index.d.ts +1 -0
- package/schedule/index.js +1 -0
- package/schedule/schedule.d.ts +4 -0
- package/schedule/schedule.js +8 -0
- package/ses/index.d.ts +1 -0
- package/ses/index.js +1 -0
- package/ses/ses.d.ts +4 -0
- package/ses/ses.js +8 -0
- package/sns/index.d.ts +1 -0
- package/sns/index.js +1 -0
- package/sns/sns.d.ts +4 -0
- package/sns/sns.js +8 -0
- package/sqs/index.d.ts +1 -0
- package/sqs/index.js +1 -0
- package/sqs/sqs.d.ts +4 -0
- package/sqs/sqs.js +8 -0
- package/types.d.ts +12 -22
- package/{wrapper → wrappers}/chaos.d.ts +0 -1
- package/wrappers/chaos.js +35 -0
- package/{wrapper → wrappers}/context.d.ts +0 -1
- package/wrappers/context.js +6 -0
- package/{wrapper → wrappers}/error.d.ts +1 -3
- package/wrappers/error.js +9 -0
- package/wrappers/index.d.ts +6 -0
- package/wrappers/index.js +6 -0
- package/{wrapper → wrappers}/logger.d.ts +0 -1
- package/wrappers/logger.js +21 -0
- package/wrappers/smoke.d.ts +2 -0
- package/wrappers/smoke.js +5 -0
- package/{wrapper → wrappers}/timeout.d.ts +0 -1
- package/wrappers/timeout.js +6 -0
- package/api.d.ts +0 -2
- package/api.js +0 -24
- package/compose.d.ts +0 -3
- package/compose.js +0 -2
- package/dynamodb.d.ts +0 -2
- package/dynamodb.js +0 -7
- package/eventBridge.d.ts +0 -2
- package/eventBridge.js +0 -7
- package/factory.d.ts +0 -7
- package/factory.js +0 -5
- package/s3.d.ts +0 -2
- package/s3.js +0 -7
- package/schedule.d.ts +0 -2
- package/schedule.js +0 -7
- package/ses.d.ts +0 -2
- package/ses.js +0 -7
- package/sns.d.ts +0 -2
- package/sns.js +0 -7
- package/sqs.d.ts +0 -2
- package/sqs.js +0 -7
- package/wrapper/context.js +0 -11
- package/wrapper/cors.js +0 -20
- package/wrapper/logger.js +0 -39
- package/wrapper/smoke.d.ts +0 -3
- package/wrapper/timeout.js +0 -10
- /package/{wrapper → api/stream/wrappers}/stream.d.ts +0 -0
- /package/{wrapper → api/stream/wrappers}/stream.js +0 -0
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Composable AWS Lambda handler chains and wrappers for Vyriy projects.
|
|
|
4
4
|
|
|
5
5
|
## Purpose
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
`@vyriy/handler` provides ready-made handler chains for common AWS Lambda workloads and small reusable wrappers for logging, timeouts, smoke checks, development chaos injection, context setup, headers, CORS, healthchecks, and error handling.
|
|
8
8
|
|
|
9
9
|
It is designed for projects that want a consistent handler pipeline without repeating the same boilerplate in every Lambda entrypoint.
|
|
10
10
|
|
|
@@ -30,9 +30,9 @@ yarn add @types/aws-lambda
|
|
|
30
30
|
|
|
31
31
|
The `awslambda` response streaming helper is a global provided by the AWS Lambda Node.js runtime. It is not imported from `aws-lambda`; `@types/aws-lambda` only lets TypeScript type-check that global.
|
|
32
32
|
|
|
33
|
-
##
|
|
33
|
+
## API
|
|
34
34
|
|
|
35
|
-
Use
|
|
35
|
+
Use `api` for API Gateway-style Lambda handlers:
|
|
36
36
|
|
|
37
37
|
```ts
|
|
38
38
|
import { api } from '@vyriy/handler';
|
|
@@ -45,60 +45,52 @@ export const handler = api(async (event) => ({
|
|
|
45
45
|
}));
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
`api` includes error handling, logging, timeout handling, context setup, smoke checks, healthcheck handling, default headers, CORS preflight handling, and development chaos injection.
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
// handler.ts
|
|
52
|
-
import { streamApi } from '@vyriy/handler';
|
|
53
|
-
|
|
54
|
-
export const handler = streamApi(async (event, responseStream) => {
|
|
55
|
-
responseStream.setContentType?.('text/plain');
|
|
56
|
-
responseStream.write(`Request path: ${event.path}\n`);
|
|
57
|
-
responseStream.write('Part 1 of the response...');
|
|
58
|
-
responseStream.end('Part 2 of the response...');
|
|
59
|
-
});
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
`streamApi(...)` handlers receive `(event, responseStream, context)` and write directly to the response stream.
|
|
63
|
-
|
|
64
|
-
Use the same handler locally, in Docker, or in a Fargate-style HTTP runtime:
|
|
50
|
+
Use `create.api(...)` when a project needs a configured API chain. Passing `headers` replaces the default API headers:
|
|
65
51
|
|
|
66
52
|
```ts
|
|
67
|
-
|
|
68
|
-
import { streamServer } from '@vyriy/server';
|
|
53
|
+
import { create } from '@vyriy/handler';
|
|
69
54
|
|
|
70
|
-
|
|
55
|
+
const api = create.api({
|
|
56
|
+
headers: {
|
|
57
|
+
'access-control-allow-origin': '*',
|
|
58
|
+
'content-type': 'application/json',
|
|
59
|
+
},
|
|
60
|
+
healthcheck: {
|
|
61
|
+
path: '/ready',
|
|
62
|
+
},
|
|
63
|
+
});
|
|
71
64
|
|
|
72
|
-
|
|
65
|
+
export const handler = api(async () => ({
|
|
66
|
+
statusCode: 200,
|
|
67
|
+
body: JSON.stringify({ ok: true }),
|
|
68
|
+
}));
|
|
73
69
|
```
|
|
74
70
|
|
|
75
|
-
|
|
71
|
+
The API-specific entrypoint exposes the same chain as `api`:
|
|
76
72
|
|
|
77
73
|
```ts
|
|
78
|
-
|
|
79
|
-
import { handler } from './handler.js';
|
|
80
|
-
|
|
81
|
-
export const main = awslambda.streamifyResponse(handler);
|
|
74
|
+
import { api, create } from '@vyriy/handler/api';
|
|
82
75
|
```
|
|
83
76
|
|
|
84
|
-
|
|
77
|
+
## Event Sources
|
|
78
|
+
|
|
79
|
+
The event-source chains share the Lambda wrapper set: `withError`, `withLogger`, `withTimeout`, `withContext`, and `withSmoke`. Failures are rethrown so AWS event-source retry behavior still works.
|
|
85
80
|
|
|
86
|
-
|
|
81
|
+
### S3
|
|
87
82
|
|
|
88
83
|
```ts
|
|
89
|
-
import {
|
|
84
|
+
import { s3 } from '@vyriy/handler';
|
|
90
85
|
|
|
91
|
-
export const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
responseStream.end('Part 2 of the response...');
|
|
97
|
-
}),
|
|
98
|
-
);
|
|
86
|
+
export const handler = s3(async (event) => {
|
|
87
|
+
for (const record of event.Records) {
|
|
88
|
+
console.info(record.s3.bucket.name, record.s3.object.key);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
99
91
|
```
|
|
100
92
|
|
|
101
|
-
|
|
93
|
+
### SQS
|
|
102
94
|
|
|
103
95
|
```ts
|
|
104
96
|
import { sqs } from '@vyriy/handler';
|
|
@@ -110,7 +102,7 @@ export const handler = sqs(async (event) => {
|
|
|
110
102
|
});
|
|
111
103
|
```
|
|
112
104
|
|
|
113
|
-
|
|
105
|
+
### SNS
|
|
114
106
|
|
|
115
107
|
```ts
|
|
116
108
|
import { sns } from '@vyriy/handler';
|
|
@@ -122,7 +114,7 @@ export const handler = sns(async (event) => {
|
|
|
122
114
|
});
|
|
123
115
|
```
|
|
124
116
|
|
|
125
|
-
|
|
117
|
+
### DynamoDB Streams
|
|
126
118
|
|
|
127
119
|
```ts
|
|
128
120
|
import { dynamodb } from '@vyriy/handler';
|
|
@@ -134,19 +126,27 @@ export const handler = dynamodb(async (event) => {
|
|
|
134
126
|
});
|
|
135
127
|
```
|
|
136
128
|
|
|
137
|
-
|
|
129
|
+
### EventBridge
|
|
138
130
|
|
|
139
131
|
```ts
|
|
140
|
-
import {
|
|
132
|
+
import { eventBridge } from '@vyriy/handler';
|
|
141
133
|
|
|
142
|
-
export const handler =
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
134
|
+
export const handler = eventBridge(async (event) => {
|
|
135
|
+
console.info('EventBridge event:', event.source, event['detail-type'], event.detail);
|
|
136
|
+
});
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Schedule
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
import { schedule } from '@vyriy/handler';
|
|
143
|
+
|
|
144
|
+
export const handler = schedule(async (event) => {
|
|
145
|
+
console.info('Scheduled event:', event['detail-type']);
|
|
146
146
|
});
|
|
147
147
|
```
|
|
148
148
|
|
|
149
|
-
|
|
149
|
+
### SES
|
|
150
150
|
|
|
151
151
|
```ts
|
|
152
152
|
import { ses } from '@vyriy/handler';
|
|
@@ -158,27 +158,243 @@ export const handler = ses(async (event) => {
|
|
|
158
158
|
});
|
|
159
159
|
```
|
|
160
160
|
|
|
161
|
-
|
|
161
|
+
`ses` targets SES receipt rule Lambda events for incoming email. SES event publishing notifications can still be handled through the `sns` chain when delivered via SNS.
|
|
162
|
+
|
|
163
|
+
## HTTP
|
|
164
|
+
|
|
165
|
+
Use `httpApi` for native Node HTTP handlers. Handlers receive `(request, response)` and own the response lifecycle, which fits transports such as MCP Streamable HTTP:
|
|
162
166
|
|
|
163
167
|
```ts
|
|
164
|
-
import {
|
|
168
|
+
import { httpApi } from '@vyriy/handler';
|
|
169
|
+
|
|
170
|
+
export const handler = httpApi(async (request, response) => {
|
|
171
|
+
response
|
|
172
|
+
.writeHead(200, {
|
|
173
|
+
'content-type': 'application/json',
|
|
174
|
+
})
|
|
175
|
+
.end(JSON.stringify({ ok: true, url: request.url }));
|
|
176
|
+
});
|
|
177
|
+
```
|
|
165
178
|
|
|
166
|
-
|
|
167
|
-
|
|
179
|
+
`httpApi` includes error handling, request logging, healthcheck handling, default headers, and CORS preflight handling. It sets no default `content-type` because native handlers own the response body format.
|
|
180
|
+
|
|
181
|
+
Use the runtime-specific entrypoint when that reads better:
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
import { api, create } from '@vyriy/handler/api/http';
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Configure a native HTTP chain with `create.httpApi(...)`. Passing `headers` replaces the default HTTP headers:
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
import { create } from '@vyriy/handler';
|
|
191
|
+
|
|
192
|
+
const httpApi = create.httpApi({
|
|
193
|
+
headers: {
|
|
194
|
+
'access-control-allow-headers': 'content-type, mcp-protocol-version',
|
|
195
|
+
'access-control-allow-methods': 'GET, POST, OPTIONS',
|
|
196
|
+
'access-control-allow-origin': '*',
|
|
197
|
+
'x-content-type-options': 'nosniff',
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
export const handler = httpApi(async (_request, response) => {
|
|
202
|
+
response.writeHead(200).end('ok');
|
|
168
203
|
});
|
|
169
204
|
```
|
|
170
205
|
|
|
171
|
-
|
|
206
|
+
Run it locally or in a container with `httpServer` from `@vyriy/server`:
|
|
172
207
|
|
|
173
208
|
```ts
|
|
174
|
-
import {
|
|
209
|
+
import { httpServer } from '@vyriy/server';
|
|
175
210
|
|
|
176
|
-
|
|
177
|
-
|
|
211
|
+
import { handler } from './handler.js';
|
|
212
|
+
|
|
213
|
+
httpServer(handler);
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Build a custom native HTTP pipeline with `httpCompose(...)` and `httpFactory(...)`:
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
import { httpCompose, httpFactory, httpWithError } from '@vyriy/handler';
|
|
220
|
+
|
|
221
|
+
const httpWithRequestId = httpFactory<{ headerName?: string }>(async (handler, args, options = {}) => {
|
|
222
|
+
const [request] = args;
|
|
223
|
+
const requestId = request.headers[options.headerName ?? 'x-request-id'];
|
|
224
|
+
|
|
225
|
+
if (requestId) {
|
|
226
|
+
console.info('Request ID:', requestId);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
await handler(...args);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
export const handler = httpCompose(
|
|
233
|
+
httpWithError(),
|
|
234
|
+
httpWithRequestId(),
|
|
235
|
+
)(async (_request, response) => {
|
|
236
|
+
response.writeHead(200).end('ok');
|
|
237
|
+
});
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
The lower-level helpers are also available from runtime subpaths:
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
import { compose } from '@vyriy/handler/api/http/compose';
|
|
244
|
+
import { factory } from '@vyriy/handler/api/http/factory';
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Stream
|
|
248
|
+
|
|
249
|
+
Use `streamApi` for Lambda response streaming. Stream handlers receive `(event, responseStream, context)` and write directly to the response stream:
|
|
250
|
+
|
|
251
|
+
```ts
|
|
252
|
+
import { streamApi } from '@vyriy/handler';
|
|
253
|
+
|
|
254
|
+
export const handler = streamApi(async (event, responseStream) => {
|
|
255
|
+
responseStream.setContentType?.('text/plain');
|
|
256
|
+
responseStream.write(`Request path: ${event.path}\n`);
|
|
257
|
+
responseStream.write('Part 1 of the response...');
|
|
258
|
+
responseStream.end('Part 2 of the response...');
|
|
259
|
+
});
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Use the same handler in AWS Lambda response streaming:
|
|
263
|
+
|
|
264
|
+
```ts
|
|
265
|
+
import { handler } from './handler.js';
|
|
266
|
+
|
|
267
|
+
export const main = awslambda.streamifyResponse(handler);
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Run the same handler locally, in Docker, or in a Fargate-style HTTP runtime:
|
|
271
|
+
|
|
272
|
+
```ts
|
|
273
|
+
import { streamServer } from '@vyriy/server';
|
|
274
|
+
|
|
275
|
+
import { handler } from './handler.js';
|
|
276
|
+
|
|
277
|
+
streamServer(handler);
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
You can also inline the same shape in one file:
|
|
281
|
+
|
|
282
|
+
```ts
|
|
283
|
+
import { streamApi } from '@vyriy/handler';
|
|
284
|
+
|
|
285
|
+
export const main = awslambda.streamifyResponse(
|
|
286
|
+
streamApi(async (event, responseStream) => {
|
|
287
|
+
responseStream.setContentType?.('text/plain');
|
|
288
|
+
responseStream.write(`Request path: ${event.path}\n`);
|
|
289
|
+
responseStream.end('ok');
|
|
290
|
+
}),
|
|
291
|
+
);
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
The runtime-specific entrypoint exposes the same chain as `api`:
|
|
295
|
+
|
|
296
|
+
```ts
|
|
297
|
+
import { api, create } from '@vyriy/handler/api/stream';
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
`streamApi` includes the same wrapper behavior as `api`, adapted for response streaming. Passing `headers` to `create.streamApi(...)` replaces the default stream headers.
|
|
301
|
+
|
|
302
|
+
Custom stream pipelines use stream-specific compose and factory helpers:
|
|
303
|
+
|
|
304
|
+
```ts
|
|
305
|
+
import { streamCompose, streamFactory, streamWithApiError } from '@vyriy/handler';
|
|
306
|
+
|
|
307
|
+
const streamWithRequestId = streamFactory<{ headerName?: string }>(async (handler, args, options = {}) => {
|
|
308
|
+
const [event] = args;
|
|
309
|
+
const requestId = event.headers?.[options.headerName ?? 'x-request-id'];
|
|
310
|
+
|
|
311
|
+
if (requestId) {
|
|
312
|
+
console.info('Request ID:', requestId);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
await handler(...args);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
export const handler = streamCompose(
|
|
319
|
+
streamWithApiError(),
|
|
320
|
+
streamWithRequestId(),
|
|
321
|
+
)(async (_event, responseStream) => {
|
|
322
|
+
responseStream.end('ok');
|
|
178
323
|
});
|
|
179
324
|
```
|
|
180
325
|
|
|
181
|
-
|
|
326
|
+
The lower-level helpers are also available from runtime subpaths:
|
|
327
|
+
|
|
328
|
+
```ts
|
|
329
|
+
import { compose } from '@vyriy/handler/api/stream/compose';
|
|
330
|
+
import { factory } from '@vyriy/handler/api/stream/factory';
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
## Create
|
|
334
|
+
|
|
335
|
+
`create` builds configured versions of the prebuilt chains while keeping the standard exports unchanged:
|
|
336
|
+
|
|
337
|
+
```ts
|
|
338
|
+
import { create } from '@vyriy/handler';
|
|
339
|
+
|
|
340
|
+
const { api, httpApi, s3, sqs, streamApi } = create;
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Each key is a factory:
|
|
344
|
+
|
|
345
|
+
```ts
|
|
346
|
+
import { create } from '@vyriy/handler';
|
|
347
|
+
|
|
348
|
+
const httpApi = create.httpApi({
|
|
349
|
+
healthcheck: {
|
|
350
|
+
path: '/health',
|
|
351
|
+
},
|
|
352
|
+
headers: {
|
|
353
|
+
'access-control-allow-origin': '*',
|
|
354
|
+
'access-control-allow-methods': 'GET, POST, OPTIONS',
|
|
355
|
+
'access-control-allow-headers': 'content-type, mcp-protocol-version',
|
|
356
|
+
'x-content-type-options': 'nosniff',
|
|
357
|
+
},
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
export const handler = httpApi(async (_request, response) => {
|
|
361
|
+
response.writeHead(200).end('ok');
|
|
362
|
+
});
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Available factories:
|
|
366
|
+
|
|
367
|
+
- `create.api(options?)`
|
|
368
|
+
- `create.httpApi(options?)`
|
|
369
|
+
- `create.streamApi(options?)`
|
|
370
|
+
- `create.dynamodb(options?)`
|
|
371
|
+
- `create.eventBridge(options?)`
|
|
372
|
+
- `create.s3(options?)`
|
|
373
|
+
- `create.schedule(options?)`
|
|
374
|
+
- `create.ses(options?)`
|
|
375
|
+
- `create.sns(options?)`
|
|
376
|
+
- `create.sqs(options?)`
|
|
377
|
+
|
|
378
|
+
For `api`, `httpApi`, and `streamApi`, passing `headers` replaces the default header set for that configured chain.
|
|
379
|
+
|
|
380
|
+
Runtime subpaths also expose local factories:
|
|
381
|
+
|
|
382
|
+
```ts
|
|
383
|
+
import { create as createHttpApi } from '@vyriy/handler/api/http';
|
|
384
|
+
import { create as createStreamApi } from '@vyriy/handler/api/stream';
|
|
385
|
+
|
|
386
|
+
const httpApi = createHttpApi({
|
|
387
|
+
headers: {
|
|
388
|
+
'access-control-allow-origin': '*',
|
|
389
|
+
},
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
const streamApi = createStreamApi();
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
## Custom Wrappers
|
|
396
|
+
|
|
397
|
+
Use `compose(...)` to build a custom Lambda pipeline from decorators:
|
|
182
398
|
|
|
183
399
|
```ts
|
|
184
400
|
import { compose, withChaos, withContext, withError, withLogger, withTimeout } from '@vyriy/handler';
|
|
@@ -197,7 +413,7 @@ export const handler = compose(
|
|
|
197
413
|
});
|
|
198
414
|
```
|
|
199
415
|
|
|
200
|
-
|
|
416
|
+
Use `factory(...)` to create a custom Lambda wrapper:
|
|
201
417
|
|
|
202
418
|
```ts
|
|
203
419
|
import { compose, factory, withError, withLogger, withTimeout } from '@vyriy/handler';
|
|
@@ -232,37 +448,28 @@ export const handler = compose(
|
|
|
232
448
|
});
|
|
233
449
|
```
|
|
234
450
|
|
|
235
|
-
|
|
451
|
+
Each runtime has matching helpers:
|
|
236
452
|
|
|
237
|
-
- `
|
|
238
|
-
|
|
239
|
-
- `
|
|
240
|
-
Response streaming API Gateway chain with the same wrapper behavior as `api`. Handlers receive `(event, responseStream, context)` and write directly to the Lambda response stream.
|
|
453
|
+
- Lambda: `compose` and `factory` from `@vyriy/handler`
|
|
454
|
+
- HTTP: `httpCompose` and `httpFactory` from `@vyriy/handler`, or `compose` / `factory` from `@vyriy/handler/api/http/compose` and `@vyriy/handler/api/http/factory`
|
|
455
|
+
- Stream: `streamCompose` and `streamFactory` from `@vyriy/handler`, or `compose` / `factory` from `@vyriy/handler/api/stream/compose` and `@vyriy/handler/api/stream/factory`
|
|
241
456
|
|
|
242
|
-
|
|
243
|
-
DynamoDB Streams chain with logging, timeout handling, context setup, smoke checks, and rethrown errors.
|
|
244
|
-
|
|
245
|
-
- `eventBridge`
|
|
246
|
-
EventBridge custom event chain with logging, timeout handling, context setup, smoke checks, and rethrown errors.
|
|
247
|
-
|
|
248
|
-
- `s3`
|
|
249
|
-
S3 event chain with logging, timeout handling, context setup, smoke checks, and rethrown errors.
|
|
250
|
-
|
|
251
|
-
- `ses`
|
|
252
|
-
SES receipt rule chain for incoming email processing with logging, timeout handling, context setup, smoke checks, and rethrown errors.
|
|
457
|
+
## Wrappers
|
|
253
458
|
|
|
254
|
-
|
|
255
|
-
EventBridge schedule chain with logging, timeout handling, context setup, smoke checks, and rethrown errors.
|
|
459
|
+
Wrapper exports are split by runtime:
|
|
256
460
|
|
|
257
|
-
|
|
258
|
-
|
|
461
|
+
```ts
|
|
462
|
+
import { withError, withLogger } from '@vyriy/handler/wrappers';
|
|
463
|
+
import { withHeaders } from '@vyriy/handler/api/wrappers';
|
|
464
|
+
import { httpWithHeaders } from '@vyriy/handler/api/http/wrappers';
|
|
465
|
+
import { streamWithHeaders } from '@vyriy/handler/api/stream/wrappers';
|
|
466
|
+
```
|
|
259
467
|
|
|
260
|
-
-
|
|
261
|
-
SQS chain with logging, timeout handling, context setup, smoke checks, and rethrown errors.
|
|
468
|
+
The root `@vyriy/handler` entrypoint re-exports the public wrappers for convenience.
|
|
262
469
|
|
|
263
|
-
|
|
470
|
+
### Common Lambda Wrappers
|
|
264
471
|
|
|
265
|
-
|
|
472
|
+
#### `withError(options?)`
|
|
266
473
|
|
|
267
474
|
Catches handler failures, optionally runs a side-effect `errorHandler`, and rethrows the original error.
|
|
268
475
|
|
|
@@ -274,167 +481,175 @@ Options:
|
|
|
274
481
|
}
|
|
275
482
|
```
|
|
276
483
|
|
|
277
|
-
|
|
278
|
-
Callback invoked with the caught error and handler arguments before the original error is rethrown.
|
|
484
|
+
#### `withLogger(options?)`
|
|
279
485
|
|
|
280
|
-
|
|
486
|
+
Logs the incoming event and context, then logs either the result or the thrown error.
|
|
281
487
|
|
|
282
|
-
|
|
283
|
-
import { withError } from '@vyriy/handler';
|
|
488
|
+
Options:
|
|
284
489
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
})(async () => {
|
|
290
|
-
throw new Error('boom');
|
|
291
|
-
});
|
|
490
|
+
```ts
|
|
491
|
+
{
|
|
492
|
+
logger?: typeof console;
|
|
493
|
+
}
|
|
292
494
|
```
|
|
293
495
|
|
|
294
|
-
|
|
496
|
+
By default the wrapper creates a logger via `@vyriy/logger`.
|
|
295
497
|
|
|
296
|
-
|
|
498
|
+
#### `withTimeout()`
|
|
297
499
|
|
|
298
|
-
|
|
500
|
+
Races the handler against a timeout scheduled one second before the Lambda runtime limit.
|
|
299
501
|
|
|
300
|
-
|
|
502
|
+
#### `withContext()`
|
|
503
|
+
|
|
504
|
+
Sets `context.callbackWaitsForEmptyEventLoop = false` before calling the handler.
|
|
505
|
+
|
|
506
|
+
#### `withSmoke()`
|
|
507
|
+
|
|
508
|
+
Returns the smoke response when the incoming event has `isSmoke: true`. Matching is delegated to `@vyriy/smoke`.
|
|
509
|
+
|
|
510
|
+
#### `withChaos(options?)`
|
|
511
|
+
|
|
512
|
+
Injects development-only random failures before the wrapped handler runs.
|
|
301
513
|
|
|
302
514
|
Options:
|
|
303
515
|
|
|
304
516
|
```ts
|
|
305
517
|
{
|
|
306
|
-
|
|
518
|
+
enabled?: boolean;
|
|
519
|
+
probability?: number;
|
|
520
|
+
strategy?: 'error' | 'timeout' | 'random';
|
|
521
|
+
timeoutMs?: number;
|
|
522
|
+
error?: unknown;
|
|
307
523
|
}
|
|
308
524
|
```
|
|
309
525
|
|
|
310
|
-
|
|
311
|
-
Console-compatible logger implementation. By default the wrapper creates one via `@vyriy/logger`.
|
|
526
|
+
`enabled` and `timeoutMs` can be read through `@vyriy/config` when they are not passed directly.
|
|
312
527
|
|
|
313
|
-
|
|
528
|
+
### API Wrappers
|
|
314
529
|
|
|
315
|
-
|
|
316
|
-
import { withLogger } from '@vyriy/handler';
|
|
530
|
+
#### `withApiError(options?)`
|
|
317
531
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
}
|
|
532
|
+
Catches API handler failures and converts them to an API Gateway result. Without a custom `errorHandler`, it returns a JSON `500`.
|
|
533
|
+
|
|
534
|
+
Options:
|
|
535
|
+
|
|
536
|
+
```ts
|
|
537
|
+
{
|
|
538
|
+
errorHandler?: (error: unknown, args: HandlerParams<ApiEvent>) => Promise<ApiResult> | ApiResult;
|
|
539
|
+
}
|
|
326
540
|
```
|
|
327
541
|
|
|
328
|
-
|
|
542
|
+
#### `withHealthcheck(options?)`
|
|
329
543
|
|
|
330
|
-
|
|
544
|
+
Returns a JSON `200` response when `event.path` matches the configured `path`.
|
|
331
545
|
|
|
332
|
-
|
|
546
|
+
Options:
|
|
333
547
|
|
|
334
548
|
```ts
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
});
|
|
549
|
+
{
|
|
550
|
+
path?: string;
|
|
551
|
+
action?: () => Promise<void>;
|
|
552
|
+
}
|
|
340
553
|
```
|
|
341
554
|
|
|
342
|
-
|
|
555
|
+
#### `withHeaders(options?)`
|
|
343
556
|
|
|
344
|
-
|
|
557
|
+
Adds configured headers to the API result.
|
|
345
558
|
|
|
346
|
-
|
|
559
|
+
Options:
|
|
347
560
|
|
|
348
561
|
```ts
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
export const handler = withContext()(async (_event, context) => {
|
|
352
|
-
return {
|
|
353
|
-
waitForEmptyLoop: context.callbackWaitsForEmptyEventLoop,
|
|
354
|
-
};
|
|
355
|
-
});
|
|
562
|
+
Record<string, string>;
|
|
356
563
|
```
|
|
357
564
|
|
|
358
|
-
|
|
565
|
+
#### `withCors()`
|
|
359
566
|
|
|
360
|
-
|
|
567
|
+
Short-circuits API Gateway `OPTIONS` requests with a `204` response and delegates all other requests.
|
|
568
|
+
|
|
569
|
+
### HTTP Wrappers
|
|
570
|
+
|
|
571
|
+
#### `httpWithError(options?)`
|
|
572
|
+
|
|
573
|
+
Catches native HTTP handler failures and runs an optional side-effect `errorHandler`. When the response is still open it writes a JSON `500`; when headers were already sent it only ends the response.
|
|
361
574
|
|
|
362
575
|
Options:
|
|
363
576
|
|
|
364
577
|
```ts
|
|
365
578
|
{
|
|
366
|
-
|
|
367
|
-
probability?: number;
|
|
368
|
-
strategy?: 'error' | 'timeout' | 'random';
|
|
369
|
-
timeoutMs?: number;
|
|
370
|
-
error?: unknown;
|
|
579
|
+
errorHandler?: (error: unknown, args: HttpHandlerParams) => Promise<void> | void;
|
|
371
580
|
}
|
|
372
581
|
```
|
|
373
582
|
|
|
374
|
-
|
|
375
|
-
Turns chaos injection on. By default the wrapper reads `CHAOS_ENABLED` through `@vyriy/config`.
|
|
376
|
-
|
|
377
|
-
- `probability`
|
|
378
|
-
Probability from `0` to `1` that a failure is injected.
|
|
583
|
+
#### `httpWithLogger(options?)`
|
|
379
584
|
|
|
380
|
-
|
|
381
|
-
Chooses whether to throw an error, wait and time out, or pick one randomly. Defaults to `'random'`.
|
|
585
|
+
Logs the incoming request method and URL, then logs either the final response status code or the thrown error. Accepts the same `logger` option as `withLogger`.
|
|
382
586
|
|
|
383
|
-
|
|
384
|
-
Timeout delay used when the timeout strategy is selected. By default the wrapper reads `CHAOS_TIMEOUT_MS` through `@vyriy/config`.
|
|
587
|
+
#### `httpWithHealthcheck(options?)`
|
|
385
588
|
|
|
386
|
-
|
|
387
|
-
Error value normalized through `@vyriy/error` when the error strategy is selected.
|
|
589
|
+
Writes a JSON `200` response when the request path matches the configured `path`.
|
|
388
590
|
|
|
389
|
-
|
|
591
|
+
Options:
|
|
390
592
|
|
|
391
593
|
```ts
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
strategy: 'random',
|
|
398
|
-
timeoutMs: 1500,
|
|
399
|
-
})(async () => {
|
|
400
|
-
return {
|
|
401
|
-
ok: true,
|
|
402
|
-
};
|
|
403
|
-
});
|
|
594
|
+
{
|
|
595
|
+
path?: string;
|
|
596
|
+
action?: () => Promise<void>;
|
|
597
|
+
body?: unknown;
|
|
598
|
+
}
|
|
404
599
|
```
|
|
405
600
|
|
|
406
|
-
|
|
601
|
+
#### `httpWithHeaders(options?)`
|
|
407
602
|
|
|
408
|
-
|
|
603
|
+
Sets configured headers on the response before delegating, so handler-defined headers win on key conflicts.
|
|
409
604
|
|
|
410
|
-
|
|
605
|
+
Options:
|
|
411
606
|
|
|
412
607
|
```ts
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
export const handler = withSmoke()(async () => {
|
|
416
|
-
return {
|
|
417
|
-
statusCode: 200,
|
|
418
|
-
body: JSON.stringify({
|
|
419
|
-
status: 'runtime',
|
|
420
|
-
}),
|
|
421
|
-
};
|
|
422
|
-
});
|
|
608
|
+
Record<string, string>;
|
|
423
609
|
```
|
|
424
610
|
|
|
425
|
-
`
|
|
611
|
+
#### `httpWithCors()`
|
|
612
|
+
|
|
613
|
+
Short-circuits `OPTIONS` preflight requests with a `204` response and delegates all other requests.
|
|
614
|
+
|
|
615
|
+
### Stream Wrappers
|
|
616
|
+
|
|
617
|
+
The `streamWith*` wrappers mirror the API and common Lambda wrappers for response streaming handlers:
|
|
618
|
+
|
|
619
|
+
- `streamWithApiError(options?)`
|
|
620
|
+
- `streamWithLogger(options?)`
|
|
621
|
+
- `streamWithTimeout()`
|
|
622
|
+
- `streamWithContext()`
|
|
623
|
+
- `streamWithSmoke()`
|
|
624
|
+
- `streamWithHealthcheck(options?)`
|
|
625
|
+
- `streamWithHeaders(options?)`
|
|
626
|
+
- `streamWithCors()`
|
|
627
|
+
- `streamWithChaos(options?)`
|
|
628
|
+
|
|
629
|
+
Stream-specific wrappers write metadata through the response stream before or instead of delegating to the wrapped stream handler.
|
|
426
630
|
|
|
427
631
|
## Types
|
|
428
632
|
|
|
429
|
-
The package
|
|
633
|
+
The package exports shared Lambda handler types from the root entrypoint:
|
|
430
634
|
|
|
431
635
|
```ts
|
|
432
636
|
import type { Context, Decorator, Handler, HandlerParams, Response } from '@vyriy/handler';
|
|
433
637
|
```
|
|
434
638
|
|
|
639
|
+
Runtime-specific types are available from their runtime entrypoints:
|
|
640
|
+
|
|
641
|
+
```ts
|
|
642
|
+
import type { ApiEvent, ApiOptions, ApiResult } from '@vyriy/handler/api';
|
|
643
|
+
import type { HttpApiOptions, HttpDecorator, HttpHandler, HttpHandlerParams } from '@vyriy/handler/api/http';
|
|
644
|
+
import type { ResponseStream, StreamApiOptions, StreamHandler } from '@vyriy/handler/api/stream';
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
The root entrypoint still re-exports runtime-specific types for compatibility.
|
|
648
|
+
|
|
435
649
|
## Notes
|
|
436
650
|
|
|
437
651
|
- `api` includes API-specific wrappers such as healthcheck handling, default headers, and CORS preflight handling
|
|
438
|
-
- `
|
|
439
|
-
- `
|
|
440
|
-
- `
|
|
652
|
+
- `httpApi` skips timeout, context, smoke, and chaos wrappers because they rely on the Lambda context and event shape
|
|
653
|
+
- `streamApi` keeps the API-style behavior but adapts result writing to Lambda response streams
|
|
654
|
+
- `dynamodb`, `s3`, `ses`, `schedule`, `sns`, `sqs`, and `eventBridge` use `withError()` so failures are rethrown for event-source retry behavior
|
|
655
|
+
- Passing `headers` to `create.api`, `create.httpApi`, or `create.streamApi` replaces that chain's default headers
|