@vyriy/handler 0.7.8 → 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 +375 -253
- 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/api/stream/wrappers/error.js +25 -0
- 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/api/stream/wrappers/healthcheck.js +27 -0
- 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/api/wrappers/cors.d.ts +1 -0
- package/api/wrappers/cors.js +11 -0
- package/api/wrappers/error.d.ts +4 -0
- package/api/wrappers/error.js +18 -0
- package/api/wrappers/headers.d.ts +1 -0
- package/api/wrappers/headers.js +13 -0
- package/api/wrappers/healthcheck.d.ts +8 -0
- 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 -29
- 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 -4
- package/wrappers/error.js +9 -0
- package/wrappers/index.d.ts +6 -0
- package/wrappers/index.js +6 -0
- package/wrappers/logger.d.ts +4 -0
- 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 -3
- package/api.js +0 -30
- package/compose.d.ts +0 -4
- package/compose.js +0 -3
- 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 -8
- package/factory.js +0 -6
- 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.d.ts +0 -3
- package/wrapper/cors.js +0 -28
- package/wrapper/error.js +0 -62
- package/wrapper/headers.d.ts +0 -3
- package/wrapper/headers.js +0 -25
- package/wrapper/healthcheck.d.ts +0 -13
- package/wrapper/healthcheck.js +0 -49
- package/wrapper/logger.d.ts +0 -6
- package/wrapper/logger.js +0 -65
- 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,124 @@ 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
|
+
|
|
50
|
+
Use `create.api(...)` when a project needs a configured API chain. Passing `headers` replaces the default API headers:
|
|
49
51
|
|
|
50
52
|
```ts
|
|
51
|
-
|
|
52
|
-
import { streamApi } from '@vyriy/handler';
|
|
53
|
+
import { create } from '@vyriy/handler';
|
|
53
54
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
const api = create.api({
|
|
56
|
+
headers: {
|
|
57
|
+
'access-control-allow-origin': '*',
|
|
58
|
+
'content-type': 'application/json',
|
|
59
|
+
},
|
|
60
|
+
healthcheck: {
|
|
61
|
+
path: '/ready',
|
|
62
|
+
},
|
|
59
63
|
});
|
|
64
|
+
|
|
65
|
+
export const handler = api(async () => ({
|
|
66
|
+
statusCode: 200,
|
|
67
|
+
body: JSON.stringify({ ok: true }),
|
|
68
|
+
}));
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The API-specific entrypoint exposes the same chain as `api`:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import { api, create } from '@vyriy/handler/api';
|
|
60
75
|
```
|
|
61
76
|
|
|
62
|
-
|
|
77
|
+
## Event Sources
|
|
63
78
|
|
|
64
|
-
|
|
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.
|
|
80
|
+
|
|
81
|
+
### S3
|
|
65
82
|
|
|
66
83
|
```ts
|
|
67
|
-
|
|
68
|
-
import { streamServer } from '@vyriy/server';
|
|
84
|
+
import { s3 } from '@vyriy/handler';
|
|
69
85
|
|
|
70
|
-
|
|
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
|
+
});
|
|
91
|
+
```
|
|
71
92
|
|
|
72
|
-
|
|
93
|
+
### SQS
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
import { sqs } from '@vyriy/handler';
|
|
97
|
+
|
|
98
|
+
export const handler = sqs(async (event) => {
|
|
99
|
+
for (const record of event.Records) {
|
|
100
|
+
console.info(record.body);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
73
103
|
```
|
|
74
104
|
|
|
75
|
-
|
|
105
|
+
### SNS
|
|
76
106
|
|
|
77
107
|
```ts
|
|
78
|
-
|
|
79
|
-
import { handler } from './handler.js';
|
|
108
|
+
import { sns } from '@vyriy/handler';
|
|
80
109
|
|
|
81
|
-
export const
|
|
110
|
+
export const handler = sns(async (event) => {
|
|
111
|
+
for (const record of event.Records) {
|
|
112
|
+
console.info(record.Sns.Message);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
82
115
|
```
|
|
83
116
|
|
|
84
|
-
|
|
117
|
+
### DynamoDB Streams
|
|
85
118
|
|
|
86
|
-
|
|
119
|
+
```ts
|
|
120
|
+
import { dynamodb } from '@vyriy/handler';
|
|
121
|
+
|
|
122
|
+
export const handler = dynamodb(async (event) => {
|
|
123
|
+
for (const record of event.Records) {
|
|
124
|
+
console.info(record.eventName);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### EventBridge
|
|
87
130
|
|
|
88
131
|
```ts
|
|
89
|
-
import {
|
|
132
|
+
import { eventBridge } from '@vyriy/handler';
|
|
90
133
|
|
|
91
|
-
export const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
+
});
|
|
99
147
|
```
|
|
100
148
|
|
|
101
|
-
|
|
149
|
+
### SES
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
import { ses } from '@vyriy/handler';
|
|
153
|
+
|
|
154
|
+
export const handler = ses(async (event) => {
|
|
155
|
+
for (const record of event.Records) {
|
|
156
|
+
console.info(record.ses.mail.messageId, record.ses.mail.source);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
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:
|
|
102
166
|
|
|
103
167
|
```ts
|
|
104
168
|
import { httpApi } from '@vyriy/handler';
|
|
@@ -112,6 +176,33 @@ export const handler = httpApi(async (request, response) => {
|
|
|
112
176
|
});
|
|
113
177
|
```
|
|
114
178
|
|
|
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');
|
|
203
|
+
});
|
|
204
|
+
```
|
|
205
|
+
|
|
115
206
|
Run it locally or in a container with `httpServer` from `@vyriy/server`:
|
|
116
207
|
|
|
117
208
|
```ts
|
|
@@ -122,89 +213,188 @@ import { handler } from './handler.js';
|
|
|
122
213
|
httpServer(handler);
|
|
123
214
|
```
|
|
124
215
|
|
|
125
|
-
|
|
216
|
+
Build a custom native HTTP pipeline with `httpCompose(...)` and `httpFactory(...)`:
|
|
126
217
|
|
|
127
218
|
```ts
|
|
128
|
-
import {
|
|
219
|
+
import { httpCompose, httpFactory, httpWithError } from '@vyriy/handler';
|
|
129
220
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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);
|
|
133
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');
|
|
134
237
|
});
|
|
135
238
|
```
|
|
136
239
|
|
|
137
|
-
|
|
240
|
+
The lower-level helpers are also available from runtime subpaths:
|
|
138
241
|
|
|
139
242
|
```ts
|
|
140
|
-
import {
|
|
243
|
+
import { compose } from '@vyriy/handler/api/http/compose';
|
|
244
|
+
import { factory } from '@vyriy/handler/api/http/factory';
|
|
245
|
+
```
|
|
141
246
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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...');
|
|
146
259
|
});
|
|
147
260
|
```
|
|
148
261
|
|
|
149
|
-
Use
|
|
262
|
+
Use the same handler in AWS Lambda response streaming:
|
|
150
263
|
|
|
151
264
|
```ts
|
|
152
|
-
import {
|
|
265
|
+
import { handler } from './handler.js';
|
|
153
266
|
|
|
154
|
-
export const
|
|
155
|
-
for (const record of event.Records) {
|
|
156
|
-
console.info(record.eventName);
|
|
157
|
-
}
|
|
158
|
-
});
|
|
267
|
+
export const main = awslambda.streamifyResponse(handler);
|
|
159
268
|
```
|
|
160
269
|
|
|
161
|
-
|
|
270
|
+
Run the same handler locally, in Docker, or in a Fargate-style HTTP runtime:
|
|
162
271
|
|
|
163
272
|
```ts
|
|
164
|
-
import {
|
|
273
|
+
import { streamServer } from '@vyriy/server';
|
|
165
274
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
});
|
|
275
|
+
import { handler } from './handler.js';
|
|
276
|
+
|
|
277
|
+
streamServer(handler);
|
|
171
278
|
```
|
|
172
279
|
|
|
173
|
-
|
|
280
|
+
You can also inline the same shape in one file:
|
|
174
281
|
|
|
175
282
|
```ts
|
|
176
|
-
import {
|
|
283
|
+
import { streamApi } from '@vyriy/handler';
|
|
177
284
|
|
|
178
|
-
export const
|
|
179
|
-
|
|
180
|
-
|
|
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);
|
|
181
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');
|
|
182
323
|
});
|
|
183
324
|
```
|
|
184
325
|
|
|
185
|
-
|
|
326
|
+
The lower-level helpers are also available from runtime subpaths:
|
|
186
327
|
|
|
187
328
|
```ts
|
|
188
|
-
import {
|
|
329
|
+
import { compose } from '@vyriy/handler/api/stream/compose';
|
|
330
|
+
import { factory } from '@vyriy/handler/api/stream/factory';
|
|
331
|
+
```
|
|
189
332
|
|
|
190
|
-
|
|
191
|
-
|
|
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');
|
|
192
362
|
});
|
|
193
363
|
```
|
|
194
364
|
|
|
195
|
-
|
|
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:
|
|
196
381
|
|
|
197
382
|
```ts
|
|
198
|
-
import {
|
|
383
|
+
import { create as createHttpApi } from '@vyriy/handler/api/http';
|
|
384
|
+
import { create as createStreamApi } from '@vyriy/handler/api/stream';
|
|
199
385
|
|
|
200
|
-
|
|
201
|
-
|
|
386
|
+
const httpApi = createHttpApi({
|
|
387
|
+
headers: {
|
|
388
|
+
'access-control-allow-origin': '*',
|
|
389
|
+
},
|
|
202
390
|
});
|
|
391
|
+
|
|
392
|
+
const streamApi = createStreamApi();
|
|
203
393
|
```
|
|
204
394
|
|
|
205
|
-
|
|
395
|
+
## Custom Wrappers
|
|
206
396
|
|
|
207
|
-
|
|
397
|
+
Use `compose(...)` to build a custom Lambda pipeline from decorators:
|
|
208
398
|
|
|
209
399
|
```ts
|
|
210
400
|
import { compose, withChaos, withContext, withError, withLogger, withTimeout } from '@vyriy/handler';
|
|
@@ -223,7 +413,7 @@ export const handler = compose(
|
|
|
223
413
|
});
|
|
224
414
|
```
|
|
225
415
|
|
|
226
|
-
|
|
416
|
+
Use `factory(...)` to create a custom Lambda wrapper:
|
|
227
417
|
|
|
228
418
|
```ts
|
|
229
419
|
import { compose, factory, withError, withLogger, withTimeout } from '@vyriy/handler';
|
|
@@ -258,59 +448,28 @@ export const handler = compose(
|
|
|
258
448
|
});
|
|
259
449
|
```
|
|
260
450
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
```ts
|
|
264
|
-
import { httpCompose, httpWithCors, httpWithError, httpWithHealthcheck, httpWithLogger } from '@vyriy/handler';
|
|
265
|
-
|
|
266
|
-
export const handler = httpCompose(
|
|
267
|
-
httpWithError(),
|
|
268
|
-
httpWithLogger(),
|
|
269
|
-
httpWithHealthcheck(),
|
|
270
|
-
httpWithCors(),
|
|
271
|
-
)(async (request, response) => {
|
|
272
|
-
response
|
|
273
|
-
.writeHead(200, {
|
|
274
|
-
'content-type': 'application/json',
|
|
275
|
-
})
|
|
276
|
-
.end(JSON.stringify({ ok: true }));
|
|
277
|
-
});
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
## Prebuilt Chains
|
|
281
|
-
|
|
282
|
-
- `api`
|
|
283
|
-
API Gateway chain with error handling, logging, timeout handling, context setup, smoke checks, healthcheck handling, default headers, and CORS preflight handling.
|
|
284
|
-
- `streamApi`
|
|
285
|
-
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.
|
|
451
|
+
Each runtime has matching helpers:
|
|
286
452
|
|
|
287
|
-
- `
|
|
288
|
-
|
|
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`
|
|
289
456
|
|
|
290
|
-
|
|
291
|
-
DynamoDB Streams chain with logging, timeout handling, context setup, smoke checks, and rethrown errors.
|
|
292
|
-
|
|
293
|
-
- `eventBridge`
|
|
294
|
-
EventBridge custom event chain with logging, timeout handling, context setup, smoke checks, and rethrown errors.
|
|
295
|
-
|
|
296
|
-
- `s3`
|
|
297
|
-
S3 event chain with logging, timeout handling, context setup, smoke checks, and rethrown errors.
|
|
298
|
-
|
|
299
|
-
- `ses`
|
|
300
|
-
SES receipt rule chain for incoming email processing with logging, timeout handling, context setup, smoke checks, and rethrown errors.
|
|
457
|
+
## Wrappers
|
|
301
458
|
|
|
302
|
-
|
|
303
|
-
EventBridge schedule chain with logging, timeout handling, context setup, smoke checks, and rethrown errors.
|
|
459
|
+
Wrapper exports are split by runtime:
|
|
304
460
|
|
|
305
|
-
|
|
306
|
-
|
|
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
|
+
```
|
|
307
467
|
|
|
308
|
-
-
|
|
309
|
-
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.
|
|
310
469
|
|
|
311
|
-
|
|
470
|
+
### Common Lambda Wrappers
|
|
312
471
|
|
|
313
|
-
|
|
472
|
+
#### `withError(options?)`
|
|
314
473
|
|
|
315
474
|
Catches handler failures, optionally runs a side-effect `errorHandler`, and rethrows the original error.
|
|
316
475
|
|
|
@@ -322,28 +481,7 @@ Options:
|
|
|
322
481
|
}
|
|
323
482
|
```
|
|
324
483
|
|
|
325
|
-
|
|
326
|
-
Callback invoked with the caught error and handler arguments before the original error is rethrown.
|
|
327
|
-
|
|
328
|
-
Example:
|
|
329
|
-
|
|
330
|
-
```ts
|
|
331
|
-
import { withError } from '@vyriy/handler';
|
|
332
|
-
|
|
333
|
-
export const handler = withError({
|
|
334
|
-
errorHandler: async (error) => {
|
|
335
|
-
console.error('Handler failed:', error);
|
|
336
|
-
},
|
|
337
|
-
})(async () => {
|
|
338
|
-
throw new Error('boom');
|
|
339
|
-
});
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
### `withApiError(options?)`
|
|
343
|
-
|
|
344
|
-
Catches API handler failures and converts them to an API Gateway result. Without a custom `errorHandler`, it returns a JSON `500`.
|
|
345
|
-
|
|
346
|
-
### `withLogger(options?)`
|
|
484
|
+
#### `withLogger(options?)`
|
|
347
485
|
|
|
348
486
|
Logs the incoming event and context, then logs either the result or the thrown error.
|
|
349
487
|
|
|
@@ -355,55 +493,21 @@ Options:
|
|
|
355
493
|
}
|
|
356
494
|
```
|
|
357
495
|
|
|
358
|
-
|
|
359
|
-
Console-compatible logger implementation. By default the wrapper creates one via `@vyriy/logger`.
|
|
360
|
-
|
|
361
|
-
Example:
|
|
362
|
-
|
|
363
|
-
```ts
|
|
364
|
-
import { withLogger } from '@vyriy/handler';
|
|
496
|
+
By default the wrapper creates a logger via `@vyriy/logger`.
|
|
365
497
|
|
|
366
|
-
|
|
367
|
-
logger: console,
|
|
368
|
-
})(async (event) => {
|
|
369
|
-
return {
|
|
370
|
-
ok: true,
|
|
371
|
-
event,
|
|
372
|
-
};
|
|
373
|
-
});
|
|
374
|
-
```
|
|
375
|
-
|
|
376
|
-
### `withTimeout()`
|
|
498
|
+
#### `withTimeout()`
|
|
377
499
|
|
|
378
500
|
Races the handler against a timeout scheduled one second before the Lambda runtime limit.
|
|
379
501
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
```ts
|
|
383
|
-
import { withTimeout } from '@vyriy/handler';
|
|
384
|
-
|
|
385
|
-
export const handler = withTimeout()(async () => {
|
|
386
|
-
await doWork();
|
|
387
|
-
});
|
|
388
|
-
```
|
|
389
|
-
|
|
390
|
-
### `withContext()`
|
|
502
|
+
#### `withContext()`
|
|
391
503
|
|
|
392
504
|
Sets `context.callbackWaitsForEmptyEventLoop = false` before calling the handler.
|
|
393
505
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
```ts
|
|
397
|
-
import { withContext } from '@vyriy/handler';
|
|
506
|
+
#### `withSmoke()`
|
|
398
507
|
|
|
399
|
-
|
|
400
|
-
return {
|
|
401
|
-
waitForEmptyLoop: context.callbackWaitsForEmptyEventLoop,
|
|
402
|
-
};
|
|
403
|
-
});
|
|
404
|
-
```
|
|
508
|
+
Returns the smoke response when the incoming event has `isSmoke: true`. Matching is delegated to `@vyriy/smoke`.
|
|
405
509
|
|
|
406
|
-
|
|
510
|
+
#### `withChaos(options?)`
|
|
407
511
|
|
|
408
512
|
Injects development-only random failures before the wrapped handler runs.
|
|
409
513
|
|
|
@@ -419,115 +523,133 @@ Options:
|
|
|
419
523
|
}
|
|
420
524
|
```
|
|
421
525
|
|
|
422
|
-
|
|
423
|
-
Turns chaos injection on. By default the wrapper reads `CHAOS_ENABLED` through `@vyriy/config`.
|
|
424
|
-
|
|
425
|
-
- `probability`
|
|
426
|
-
Probability from `0` to `1` that a failure is injected.
|
|
526
|
+
`enabled` and `timeoutMs` can be read through `@vyriy/config` when they are not passed directly.
|
|
427
527
|
|
|
428
|
-
|
|
429
|
-
Chooses whether to throw an error, wait and time out, or pick one randomly. Defaults to `'random'`.
|
|
528
|
+
### API Wrappers
|
|
430
529
|
|
|
431
|
-
|
|
432
|
-
Timeout delay used when the timeout strategy is selected. By default the wrapper reads `CHAOS_TIMEOUT_MS` through `@vyriy/config`.
|
|
530
|
+
#### `withApiError(options?)`
|
|
433
531
|
|
|
434
|
-
|
|
435
|
-
Error value normalized through `@vyriy/error` when the error strategy is selected.
|
|
532
|
+
Catches API handler failures and converts them to an API Gateway result. Without a custom `errorHandler`, it returns a JSON `500`.
|
|
436
533
|
|
|
437
|
-
|
|
534
|
+
Options:
|
|
438
535
|
|
|
439
536
|
```ts
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
enabled: true,
|
|
444
|
-
probability: 0.2,
|
|
445
|
-
strategy: 'random',
|
|
446
|
-
timeoutMs: 1500,
|
|
447
|
-
})(async () => {
|
|
448
|
-
return {
|
|
449
|
-
ok: true,
|
|
450
|
-
};
|
|
451
|
-
});
|
|
537
|
+
{
|
|
538
|
+
errorHandler?: (error: unknown, args: HandlerParams<ApiEvent>) => Promise<ApiResult> | ApiResult;
|
|
539
|
+
}
|
|
452
540
|
```
|
|
453
541
|
|
|
454
|
-
|
|
542
|
+
#### `withHealthcheck(options?)`
|
|
455
543
|
|
|
456
|
-
Returns
|
|
544
|
+
Returns a JSON `200` response when `event.path` matches the configured `path`.
|
|
457
545
|
|
|
458
|
-
|
|
546
|
+
Options:
|
|
459
547
|
|
|
460
548
|
```ts
|
|
461
|
-
|
|
549
|
+
{
|
|
550
|
+
path?: string;
|
|
551
|
+
action?: () => Promise<void>;
|
|
552
|
+
}
|
|
553
|
+
```
|
|
462
554
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
555
|
+
#### `withHeaders(options?)`
|
|
556
|
+
|
|
557
|
+
Adds configured headers to the API result.
|
|
558
|
+
|
|
559
|
+
Options:
|
|
560
|
+
|
|
561
|
+
```ts
|
|
562
|
+
Record<string, string>;
|
|
471
563
|
```
|
|
472
564
|
|
|
473
|
-
`
|
|
565
|
+
#### `withCors()`
|
|
566
|
+
|
|
567
|
+
Short-circuits API Gateway `OPTIONS` requests with a `204` response and delegates all other requests.
|
|
568
|
+
|
|
569
|
+
### HTTP Wrappers
|
|
474
570
|
|
|
475
|
-
|
|
571
|
+
#### `httpWithError(options?)`
|
|
476
572
|
|
|
477
|
-
|
|
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.
|
|
574
|
+
|
|
575
|
+
Options:
|
|
478
576
|
|
|
479
|
-
|
|
480
|
-
|
|
577
|
+
```ts
|
|
578
|
+
{
|
|
579
|
+
errorHandler?: (error: unknown, args: HttpHandlerParams) => Promise<void> | void;
|
|
580
|
+
}
|
|
581
|
+
```
|
|
481
582
|
|
|
482
|
-
|
|
483
|
-
Logs the incoming request method and URL, then logs either the response status code or the thrown error. Accepts the same `logger` option as `withLogger`.
|
|
583
|
+
#### `httpWithLogger(options?)`
|
|
484
584
|
|
|
485
|
-
|
|
486
|
-
Writes a JSON `200` response when the request path matches the configured `path` (default `/healthcheck`). Besides `path` and `action`, it accepts an optional JSON-serializable `body` for the healthcheck response.
|
|
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`.
|
|
487
586
|
|
|
488
|
-
|
|
489
|
-
Sets configured headers on the response before delegating, so handler-defined headers win on key conflicts.
|
|
587
|
+
#### `httpWithHealthcheck(options?)`
|
|
490
588
|
|
|
491
|
-
|
|
492
|
-
Short-circuits `OPTIONS` preflight requests with a `204` response and delegates all other requests.
|
|
589
|
+
Writes a JSON `200` response when the request path matches the configured `path`.
|
|
493
590
|
|
|
494
|
-
|
|
591
|
+
Options:
|
|
495
592
|
|
|
496
593
|
```ts
|
|
497
|
-
|
|
594
|
+
{
|
|
595
|
+
path?: string;
|
|
596
|
+
action?: () => Promise<void>;
|
|
597
|
+
body?: unknown;
|
|
598
|
+
}
|
|
599
|
+
```
|
|
498
600
|
|
|
499
|
-
|
|
500
|
-
const [request] = args;
|
|
501
|
-
const requestId = request.headers[options.headerName ?? 'x-request-id'];
|
|
601
|
+
#### `httpWithHeaders(options?)`
|
|
502
602
|
|
|
503
|
-
|
|
504
|
-
console.info('Request ID:', requestId);
|
|
505
|
-
}
|
|
603
|
+
Sets configured headers on the response before delegating, so handler-defined headers win on key conflicts.
|
|
506
604
|
|
|
507
|
-
|
|
508
|
-
});
|
|
605
|
+
Options:
|
|
509
606
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
httpWithRequestId(),
|
|
513
|
-
)(async (request, response) => {
|
|
514
|
-
response.writeHead(200).end('ok');
|
|
515
|
-
});
|
|
607
|
+
```ts
|
|
608
|
+
Record<string, string>;
|
|
516
609
|
```
|
|
517
610
|
|
|
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.
|
|
630
|
+
|
|
518
631
|
## Types
|
|
519
632
|
|
|
520
|
-
The package
|
|
633
|
+
The package exports shared Lambda handler types from the root entrypoint:
|
|
521
634
|
|
|
522
635
|
```ts
|
|
523
636
|
import type { Context, Decorator, Handler, HandlerParams, Response } from '@vyriy/handler';
|
|
524
|
-
import type { HttpDecorator, HttpHandler, HttpHandlerParams } from '@vyriy/handler';
|
|
525
637
|
```
|
|
526
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
|
+
|
|
527
649
|
## Notes
|
|
528
650
|
|
|
529
651
|
- `api` includes API-specific wrappers such as healthcheck handling, default headers, and CORS preflight handling
|
|
530
|
-
- `httpApi` skips timeout, context, smoke, and chaos wrappers because they rely on the Lambda context and event shape
|
|
531
|
-
- `
|
|
532
|
-
- `
|
|
533
|
-
- `
|
|
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
|