@vyriy/handler 0.7.7 → 0.7.8
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 +94 -1
- package/api.d.ts +1 -0
- package/api.js +12 -6
- package/compose.d.ts +2 -1
- package/compose.js +1 -0
- package/factory.d.ts +2 -1
- package/factory.js +1 -0
- package/package.json +6 -6
- package/types.d.ts +7 -0
- package/wrapper/cors.d.ts +1 -0
- package/wrapper/cors.js +9 -1
- package/wrapper/error.d.ts +2 -1
- package/wrapper/error.js +20 -0
- package/wrapper/headers.d.ts +1 -0
- package/wrapper/headers.js +8 -1
- package/wrapper/healthcheck.d.ts +4 -0
- package/wrapper/healthcheck.js +18 -1
- package/wrapper/logger.d.ts +1 -0
- package/wrapper/logger.js +27 -1
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
|
-
This package provides ready-made Lambda handler chains for common Vyriy workloads and a small set of reusable wrappers for logging, timeouts, smoke checks, development chaos injection, context setup, and error handling.
|
|
7
|
+
This package provides ready-made Lambda handler chains for common Vyriy workloads and a small set of reusable wrappers for logging, timeouts, smoke checks, development chaos injection, context setup, and error handling. It also ships a native Node HTTP chain (`httpApi`) with matching `httpWith*` wrappers for handlers that work directly with `IncomingMessage` and `ServerResponse`.
|
|
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
|
|
|
@@ -98,6 +98,30 @@ export const main = awslambda.streamifyResponse(
|
|
|
98
98
|
);
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
+
For native Node HTTP handlers, use the HTTP chain. Handlers receive `(request, response)` and own the response lifecycle, which fits transports such as MCP Streamable HTTP:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import { httpApi } from '@vyriy/handler';
|
|
105
|
+
|
|
106
|
+
export const handler = httpApi(async (request, response) => {
|
|
107
|
+
response
|
|
108
|
+
.writeHead(200, {
|
|
109
|
+
'content-type': 'application/json',
|
|
110
|
+
})
|
|
111
|
+
.end(JSON.stringify({ ok: true, url: request.url }));
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Run it locally or in a container with `httpServer` from `@vyriy/server`:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
import { httpServer } from '@vyriy/server';
|
|
119
|
+
|
|
120
|
+
import { handler } from './handler.js';
|
|
121
|
+
|
|
122
|
+
httpServer(handler);
|
|
123
|
+
```
|
|
124
|
+
|
|
101
125
|
Use a prebuilt queue or event handler chain:
|
|
102
126
|
|
|
103
127
|
```ts
|
|
@@ -178,6 +202,8 @@ export const handler = eventBridge(async (event) => {
|
|
|
178
202
|
});
|
|
179
203
|
```
|
|
180
204
|
|
|
205
|
+
Each chain has its own composition and factory helpers: `compose`/`factory` for Lambda handlers, `streamCompose`/`streamFactory` for response streaming handlers, and `httpCompose`/`httpFactory` for native Node HTTP handlers.
|
|
206
|
+
|
|
181
207
|
Compose a custom handler pipeline from individual helpers:
|
|
182
208
|
|
|
183
209
|
```ts
|
|
@@ -232,6 +258,25 @@ export const handler = compose(
|
|
|
232
258
|
});
|
|
233
259
|
```
|
|
234
260
|
|
|
261
|
+
Compose a custom native HTTP pipeline with `httpCompose(...)` and the `httpWith*` wrappers:
|
|
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
|
+
|
|
235
280
|
## Prebuilt Chains
|
|
236
281
|
|
|
237
282
|
- `api`
|
|
@@ -239,6 +284,9 @@ export const handler = compose(
|
|
|
239
284
|
- `streamApi`
|
|
240
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.
|
|
241
286
|
|
|
287
|
+
- `httpApi`
|
|
288
|
+
Native Node HTTP chain with error handling, logging, healthcheck handling, default headers, and CORS preflight handling. Handlers receive `(request, response)` and write the response themselves. Unlike `api`, the chain sets no default `content-type` because native handlers own the response body format.
|
|
289
|
+
|
|
242
290
|
- `dynamodb`
|
|
243
291
|
DynamoDB Streams chain with logging, timeout handling, context setup, smoke checks, and rethrown errors.
|
|
244
292
|
|
|
@@ -424,17 +472,62 @@ export const handler = withSmoke()(async () => {
|
|
|
424
472
|
|
|
425
473
|
`withSmoke()` is used by the API, DynamoDB Streams, S3, SES receipt, schedule, SNS, and SQS chains.
|
|
426
474
|
|
|
475
|
+
### Native HTTP Wrappers
|
|
476
|
+
|
|
477
|
+
The `httpWith*` wrappers decorate native Node HTTP handlers and are composed with `httpCompose(...)`:
|
|
478
|
+
|
|
479
|
+
- `httpWithError(options?)`
|
|
480
|
+
Catches 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.
|
|
481
|
+
|
|
482
|
+
- `httpWithLogger(options?)`
|
|
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`.
|
|
484
|
+
|
|
485
|
+
- `httpWithHealthcheck(options?)`
|
|
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.
|
|
487
|
+
|
|
488
|
+
- `httpWithHeaders(options?)`
|
|
489
|
+
Sets configured headers on the response before delegating, so handler-defined headers win on key conflicts.
|
|
490
|
+
|
|
491
|
+
- `httpWithCors()`
|
|
492
|
+
Short-circuits `OPTIONS` preflight requests with a `204` response and delegates all other requests.
|
|
493
|
+
|
|
494
|
+
Custom native HTTP wrappers are created with `httpFactory(...)`:
|
|
495
|
+
|
|
496
|
+
```ts
|
|
497
|
+
import { httpCompose, httpFactory, httpWithError } from '@vyriy/handler';
|
|
498
|
+
|
|
499
|
+
const httpWithRequestId = httpFactory<{ headerName?: string }>(async (handler, args, options = {}) => {
|
|
500
|
+
const [request] = args;
|
|
501
|
+
const requestId = request.headers[options.headerName ?? 'x-request-id'];
|
|
502
|
+
|
|
503
|
+
if (requestId) {
|
|
504
|
+
console.info('Request ID:', requestId);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
await handler(...args);
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
export const handler = httpCompose(
|
|
511
|
+
httpWithError(),
|
|
512
|
+
httpWithRequestId(),
|
|
513
|
+
)(async (request, response) => {
|
|
514
|
+
response.writeHead(200).end('ok');
|
|
515
|
+
});
|
|
516
|
+
```
|
|
517
|
+
|
|
427
518
|
## Types
|
|
428
519
|
|
|
429
520
|
The package also exports shared handler types:
|
|
430
521
|
|
|
431
522
|
```ts
|
|
432
523
|
import type { Context, Decorator, Handler, HandlerParams, Response } from '@vyriy/handler';
|
|
524
|
+
import type { HttpDecorator, HttpHandler, HttpHandlerParams } from '@vyriy/handler';
|
|
433
525
|
```
|
|
434
526
|
|
|
435
527
|
## Notes
|
|
436
528
|
|
|
437
529
|
- `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; it also sets no default `content-type`
|
|
438
531
|
- `dynamodb`, `s3`, `ses`, `schedule`, `sns`, and `sqs` use `withError()` so failures are rethrown for event-source retry behavior
|
|
439
532
|
- `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
|
|
440
533
|
- `withSmoke()` delegates matching to `@vyriy/smoke` and returns its API Gateway-compatible response
|
package/api.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export declare const api: import("./types.js").Decorator<import("aws-lambda").APIGatewayProxyEvent, import("aws-lambda").APIGatewayProxyResult>;
|
|
2
2
|
export declare const streamApi: import("./types.js").StreamDecorator<import("aws-lambda").APIGatewayProxyEvent>;
|
|
3
|
+
export declare const httpApi: import("./types.js").HttpDecorator;
|
package/api.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { compose, streamCompose } from './compose.js';
|
|
2
|
-
import { streamWithApiError, withApiError } from './wrapper/error.js';
|
|
3
|
-
import { streamWithLogger, withLogger } from './wrapper/logger.js';
|
|
1
|
+
import { compose, httpCompose, streamCompose } from './compose.js';
|
|
2
|
+
import { httpWithError, streamWithApiError, withApiError } from './wrapper/error.js';
|
|
3
|
+
import { httpWithLogger, streamWithLogger, withLogger } from './wrapper/logger.js';
|
|
4
4
|
import { streamWithTimeout, withTimeout } from './wrapper/timeout.js';
|
|
5
5
|
import { streamWithContext, withContext } from './wrapper/context.js';
|
|
6
6
|
import { streamWithSmoke, withSmoke } from './wrapper/smoke.js';
|
|
7
|
-
import { streamWithHealthcheck, withHealthcheck } from './wrapper/healthcheck.js';
|
|
8
|
-
import { streamWithHeaders, withHeaders } from './wrapper/headers.js';
|
|
9
|
-
import { streamWithCors, withCors } from './wrapper/cors.js';
|
|
7
|
+
import { httpWithHealthcheck, streamWithHealthcheck, withHealthcheck } from './wrapper/healthcheck.js';
|
|
8
|
+
import { httpWithHeaders, streamWithHeaders, withHeaders } from './wrapper/headers.js';
|
|
9
|
+
import { httpWithCors, streamWithCors, withCors } from './wrapper/cors.js';
|
|
10
10
|
import { streamWithChaos, withChaos } from './wrapper/chaos.js';
|
|
11
11
|
export const api = compose(withApiError(), withLogger(), withTimeout(), withContext(), withSmoke(), withHealthcheck(), withHeaders({
|
|
12
12
|
'access-control-allow-origin': '*',
|
|
@@ -22,3 +22,9 @@ export const streamApi = streamCompose(streamWithApiError(), streamWithLogger(),
|
|
|
22
22
|
'content-type': 'application/json',
|
|
23
23
|
'x-robots-tag': 'noindex, nofollow',
|
|
24
24
|
}), streamWithCors(), streamWithChaos());
|
|
25
|
+
export const httpApi = httpCompose(httpWithError(), httpWithLogger(), httpWithHealthcheck(), httpWithHeaders({
|
|
26
|
+
'access-control-allow-origin': '*',
|
|
27
|
+
'access-control-allow-methods': 'GET, POST, PUT, PATCH, DELETE, OPTIONS',
|
|
28
|
+
'access-control-allow-headers': 'Content-Type, Authorization, X-Requested-With, X-Api-Key, Accept, User-Agent, X-CSRF-Token',
|
|
29
|
+
'x-robots-tag': 'noindex, nofollow',
|
|
30
|
+
}), httpWithCors());
|
package/compose.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import type { Compose, StreamCompose } from './types.js';
|
|
1
|
+
import type { Compose, HttpCompose, StreamCompose } from './types.js';
|
|
2
2
|
export declare const compose: Compose;
|
|
3
3
|
export declare const streamCompose: StreamCompose;
|
|
4
|
+
export declare const httpCompose: HttpCompose;
|
package/compose.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export const compose = (...fns) => fns.reduceRight((prevDecorator, nextDecorator) => (handler) => nextDecorator(prevDecorator(handler)));
|
|
2
2
|
export const streamCompose = (...fns) => fns.reduceRight((prevDecorator, nextDecorator) => (handler) => nextDecorator(prevDecorator(handler)));
|
|
3
|
+
export const httpCompose = (...fns) => fns.reduceRight((prevDecorator, nextDecorator) => (handler) => nextDecorator(prevDecorator(handler)));
|
package/factory.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { Context } from 'aws-lambda';
|
|
2
|
-
import type { Factory, HandlerParams, ResponseStream, StreamFactory, StreamHandlerParams } from './types.js';
|
|
2
|
+
import type { Factory, HandlerParams, HttpFactory, ResponseStream, StreamFactory, StreamHandlerParams } from './types.js';
|
|
3
3
|
export declare const getContext: <Event>(args: HandlerParams<Event>) => Context;
|
|
4
4
|
export declare const getResponseStream: <Event>(args: StreamHandlerParams<Event>) => ResponseStream;
|
|
5
5
|
export declare const getStreamContext: <Event>(args: StreamHandlerParams<Event>) => Context;
|
|
6
6
|
export declare const factory: Factory;
|
|
7
7
|
export declare const streamFactory: StreamFactory;
|
|
8
|
+
export declare const httpFactory: HttpFactory;
|
package/factory.js
CHANGED
|
@@ -3,3 +3,4 @@ export const getResponseStream = (args) => args[1];
|
|
|
3
3
|
export const getStreamContext = (args) => args[2];
|
|
4
4
|
export const factory = (wrapper) => (options) => (handler) => async (event, context) => wrapper(handler, [event, context], options);
|
|
5
5
|
export const streamFactory = (wrapper) => (options) => (handler) => async (event, responseStream, context) => wrapper(handler, [event, responseStream, context], options);
|
|
6
|
+
export const httpFactory = (wrapper) => (options) => (handler) => async (request, response) => wrapper(handler, [request, response], options);
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vyriy/handler",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.8",
|
|
4
4
|
"description": "Composable AWS Lambda handler chains and wrappers for Vyriy projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@types/aws-lambda": "^8.10.162",
|
|
8
|
-
"@vyriy/chaos": "0.7.
|
|
9
|
-
"@vyriy/config": "0.7.
|
|
10
|
-
"@vyriy/logger": "0.7.
|
|
11
|
-
"@vyriy/smoke": "0.7.
|
|
12
|
-
"@vyriy/timeout": "0.7.
|
|
8
|
+
"@vyriy/chaos": "0.7.8",
|
|
9
|
+
"@vyriy/config": "0.7.8",
|
|
10
|
+
"@vyriy/logger": "0.7.8",
|
|
11
|
+
"@vyriy/smoke": "0.7.8",
|
|
12
|
+
"@vyriy/timeout": "0.7.8"
|
|
13
13
|
},
|
|
14
14
|
"agents": "./AGENTS.md",
|
|
15
15
|
"license": "MIT",
|
package/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
|
|
2
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
3
|
export type { Context } from 'aws-lambda';
|
|
3
4
|
export type ResponseStream = {
|
|
4
5
|
end: {
|
|
@@ -16,12 +17,17 @@ export type StreamHandlerParams<Event> = [event: Event, responseStream: Response
|
|
|
16
17
|
export type Response<Result> = Promise<Result>;
|
|
17
18
|
export type Handler<Event, Result> = (event: Event, context: Context) => Response<Result>;
|
|
18
19
|
export type StreamHandler<Event> = (event: Event, responseStream: ResponseStream, context: Context) => Response<void>;
|
|
20
|
+
export type HttpHandler = (request: IncomingMessage, response: ServerResponse) => Promise<void> | void;
|
|
21
|
+
export type HttpHandlerParams = [request: IncomingMessage, response: ServerResponse];
|
|
19
22
|
export type Decorator<Event, Result> = (handler: Handler<Event, Result>) => Handler<Event, Result>;
|
|
20
23
|
export type StreamDecorator<Event> = (handler: StreamHandler<Event>) => StreamHandler<Event>;
|
|
24
|
+
export type HttpDecorator = (handler: HttpHandler) => HttpHandler;
|
|
21
25
|
export type Compose = <Event, Result>(...decorators: Array<Decorator<Event, Result>>) => Decorator<Event, Result>;
|
|
22
26
|
export type StreamCompose = <Event>(...decorators: Array<StreamDecorator<Event>>) => StreamDecorator<Event>;
|
|
27
|
+
export type HttpCompose = (...decorators: Array<HttpDecorator>) => HttpDecorator;
|
|
23
28
|
export type Wrapper<Options> = <Event, Result>(handler: Handler<Event, Result>, args: HandlerParams<Event>, options?: Options) => Response<Result>;
|
|
24
29
|
export type StreamWrapper<Options> = <Event>(handler: StreamHandler<Event>, args: StreamHandlerParams<Event>, options?: Options) => Response<void>;
|
|
30
|
+
export type HttpWrapper<Options> = (handler: HttpHandler, args: HttpHandlerParams, options?: Options) => Response<void>;
|
|
25
31
|
export type TypedWrapper<Event, Result, Options> = (handler: Handler<Event, Result>, args: HandlerParams<Event>, options?: Options) => Response<Result>;
|
|
26
32
|
export type StreamTypedWrapper<Event, Options> = (handler: StreamHandler<Event>, args: StreamHandlerParams<Event>, options?: Options) => Response<void>;
|
|
27
33
|
export type Factory = {
|
|
@@ -32,3 +38,4 @@ export type StreamFactory = {
|
|
|
32
38
|
<Options = undefined>(wrapper: StreamWrapper<Options>): <Event>(options?: Options) => StreamDecorator<Event>;
|
|
33
39
|
<Options, Event>(wrapper: StreamTypedWrapper<Event, Options>): (options?: Options) => StreamDecorator<Event>;
|
|
34
40
|
};
|
|
41
|
+
export type HttpFactory = <Options = undefined>(wrapper: HttpWrapper<Options>) => (options?: Options) => HttpDecorator;
|
package/wrapper/cors.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export declare const withCors: (options?: unknown) => import("../types.js").Decorator<import("aws-lambda").APIGatewayProxyEvent, import("aws-lambda").APIGatewayProxyResult>;
|
|
2
2
|
export declare const streamWithCors: (options?: unknown) => import("../types.js").StreamDecorator<import("aws-lambda").APIGatewayProxyEvent>;
|
|
3
|
+
export declare const httpWithCors: (options?: undefined) => import("../types.js").HttpDecorator;
|
package/wrapper/cors.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { factory, streamFactory } from '../factory.js';
|
|
1
|
+
import { factory, httpFactory, streamFactory } from '../factory.js';
|
|
2
2
|
import { responseStream } from './stream.js';
|
|
3
3
|
export const withCors = factory(async (handler, args) => {
|
|
4
4
|
const [request] = args;
|
|
@@ -18,3 +18,11 @@ export const streamWithCors = streamFactory(async (handler, args) => {
|
|
|
18
18
|
}
|
|
19
19
|
await handler(...args);
|
|
20
20
|
});
|
|
21
|
+
export const httpWithCors = httpFactory(async (handler, args) => {
|
|
22
|
+
const [request, response] = args;
|
|
23
|
+
if (request.method === 'OPTIONS') {
|
|
24
|
+
response.writeHead(204).end();
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
await handler(...args);
|
|
28
|
+
});
|
package/wrapper/error.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ApiEvent, ApiResult, Decorator, HandlerParams, StreamDecorator, StreamHandlerParams } from '../types.js';
|
|
1
|
+
import type { ApiEvent, ApiResult, Decorator, HandlerParams, HttpDecorator, HttpHandlerParams, StreamDecorator, StreamHandlerParams } from '../types.js';
|
|
2
2
|
export type ErrorHandler<Params extends unknown[]> = (err: unknown, args: Params) => Promise<void> | void;
|
|
3
3
|
export type ApiErrorHandler<Params extends unknown[], Result> = (err: unknown, args: Params) => Promise<Result> | Result;
|
|
4
4
|
export type ErrorOptions<Params extends unknown[]> = {
|
|
@@ -10,3 +10,4 @@ export type ApiErrorOptions<Params extends unknown[], Result> = {
|
|
|
10
10
|
export declare const withError: <Event, Result>(options?: ErrorOptions<HandlerParams<Event>>) => Decorator<Event, Result>;
|
|
11
11
|
export declare const withApiError: (options?: ApiErrorOptions<HandlerParams<ApiEvent>, ApiResult>) => Decorator<ApiEvent, ApiResult>;
|
|
12
12
|
export declare const streamWithApiError: (options?: ApiErrorOptions<StreamHandlerParams<ApiEvent>, ApiResult | void>) => StreamDecorator<ApiEvent>;
|
|
13
|
+
export declare const httpWithError: (options?: ErrorOptions<HttpHandlerParams>) => HttpDecorator;
|
package/wrapper/error.js
CHANGED
|
@@ -40,3 +40,23 @@ export const streamWithApiError = (options = {}) => (handler) => async (event, s
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
};
|
|
43
|
+
export const httpWithError = (options = {}) => (handler) => async (request, response) => {
|
|
44
|
+
try {
|
|
45
|
+
await handler(request, response);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
await options.errorHandler?.(err, [request, response]);
|
|
49
|
+
if (response.writableEnded) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (response.headersSent) {
|
|
53
|
+
response.end();
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
response
|
|
57
|
+
.writeHead(500, {
|
|
58
|
+
'content-type': 'application/json',
|
|
59
|
+
})
|
|
60
|
+
.end(JSON.stringify({ message: STATUS_CODES[500] }));
|
|
61
|
+
}
|
|
62
|
+
};
|
package/wrapper/headers.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export declare const withHeaders: (options?: Record<string, string> | undefined) => import("../types.js").Decorator<import("aws-lambda").APIGatewayProxyEvent, import("aws-lambda").APIGatewayProxyResult>;
|
|
2
2
|
export declare const streamWithHeaders: (options?: Record<string, string> | undefined) => import("../types.js").StreamDecorator<import("aws-lambda").APIGatewayProxyEvent>;
|
|
3
|
+
export declare const httpWithHeaders: (options?: Record<string, string> | undefined) => import("../types.js").HttpDecorator;
|
package/wrapper/headers.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { factory, streamFactory } from '../factory.js';
|
|
1
|
+
import { factory, httpFactory, streamFactory } from '../factory.js';
|
|
2
2
|
import { responseStream } from './stream.js';
|
|
3
3
|
const normalizeHeaders = (headers) => Object.fromEntries(Object.entries(headers ?? {}).map(([key, value]) => [key.toLowerCase(), value]));
|
|
4
4
|
const mergeHeaders = (result, options) => {
|
|
@@ -16,3 +16,10 @@ export const streamWithHeaders = streamFactory(async (handler, args, options = {
|
|
|
16
16
|
const [event, stream, context] = args;
|
|
17
17
|
await handler(event, responseStream(stream, { headers: normalizeHeaders(options) }), context);
|
|
18
18
|
});
|
|
19
|
+
export const httpWithHeaders = httpFactory(async (handler, args, options = {}) => {
|
|
20
|
+
const [request, response] = args;
|
|
21
|
+
for (const [name, value] of Object.entries(options)) {
|
|
22
|
+
response.setHeader(name.toLowerCase(), value);
|
|
23
|
+
}
|
|
24
|
+
await handler(request, response);
|
|
25
|
+
});
|
package/wrapper/healthcheck.d.ts
CHANGED
|
@@ -2,8 +2,12 @@ export type HealthcheckOptions = {
|
|
|
2
2
|
path?: string;
|
|
3
3
|
action?: () => Promise<void>;
|
|
4
4
|
};
|
|
5
|
+
export type HttpHealthcheckOptions = HealthcheckOptions & {
|
|
6
|
+
body?: unknown;
|
|
7
|
+
};
|
|
5
8
|
export declare const withHealthcheck: (options?: HealthcheckOptions | undefined) => import("../types.js").Decorator<import("aws-lambda").APIGatewayProxyEvent, import("aws-lambda").APIGatewayProxyResult | {
|
|
6
9
|
statusCode: number;
|
|
7
10
|
body: string;
|
|
8
11
|
}>;
|
|
9
12
|
export declare const streamWithHealthcheck: (options?: HealthcheckOptions | undefined) => import("../types.js").StreamDecorator<import("aws-lambda").APIGatewayProxyEvent>;
|
|
13
|
+
export declare const httpWithHealthcheck: (options?: HttpHealthcheckOptions | undefined) => import("../types.js").HttpDecorator;
|
package/wrapper/healthcheck.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { STATUS_CODES } from 'node:http';
|
|
2
|
-
import { factory, streamFactory } from '../factory.js';
|
|
2
|
+
import { factory, httpFactory, streamFactory } from '../factory.js';
|
|
3
3
|
import { responseStream } from './stream.js';
|
|
4
4
|
const getHealthcheckResult = async (event, options = {}) => {
|
|
5
5
|
const { path = '/healthcheck', action } = options;
|
|
@@ -30,3 +30,20 @@ export const streamWithHealthcheck = streamFactory(async (handler, args, options
|
|
|
30
30
|
}
|
|
31
31
|
await handler(...args);
|
|
32
32
|
});
|
|
33
|
+
export const httpWithHealthcheck = httpFactory(async (handler, args, options = {}) => {
|
|
34
|
+
const { path = '/healthcheck', action, body } = options;
|
|
35
|
+
const [request, response] = args;
|
|
36
|
+
const pathname = (request.url ?? '').split('?')[0];
|
|
37
|
+
if (pathname !== path) {
|
|
38
|
+
await handler(...args);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (action) {
|
|
42
|
+
await action();
|
|
43
|
+
}
|
|
44
|
+
response
|
|
45
|
+
.writeHead(200, {
|
|
46
|
+
'content-type': 'application/json',
|
|
47
|
+
})
|
|
48
|
+
.end(JSON.stringify(body ?? { message: STATUS_CODES[200] }));
|
|
49
|
+
});
|
package/wrapper/logger.d.ts
CHANGED
|
@@ -3,3 +3,4 @@ export type LoggerOptions = {
|
|
|
3
3
|
};
|
|
4
4
|
export declare const withLogger: <Event, Result>(options?: LoggerOptions | undefined) => import("../types.js").Decorator<Event, Result>;
|
|
5
5
|
export declare const streamWithLogger: <Event>(options?: LoggerOptions | undefined) => import("../types.js").StreamDecorator<Event>;
|
|
6
|
+
export declare const httpWithLogger: (options?: LoggerOptions | undefined) => import("../types.js").HttpDecorator;
|
package/wrapper/logger.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createLogger } from '@vyriy/logger';
|
|
2
|
-
import { factory, getContext, getStreamContext, streamFactory } from '../factory.js';
|
|
2
|
+
import { factory, getContext, getStreamContext, httpFactory, streamFactory } from '../factory.js';
|
|
3
3
|
export const withLogger = factory(async (handler, args, options = {}) => {
|
|
4
4
|
const { logger = createLogger() } = options;
|
|
5
5
|
const [event] = args;
|
|
@@ -37,3 +37,29 @@ export const streamWithLogger = streamFactory(async (handler, args, options = {}
|
|
|
37
37
|
throw error;
|
|
38
38
|
}
|
|
39
39
|
});
|
|
40
|
+
export const httpWithLogger = httpFactory(async (handler, args, options = {}) => {
|
|
41
|
+
const { logger = createLogger() } = options;
|
|
42
|
+
const [request, response] = args;
|
|
43
|
+
logger.info('Request:', request.method, request.url);
|
|
44
|
+
const cleanup = () => {
|
|
45
|
+
response.off('close', logResult);
|
|
46
|
+
response.off('finish', logResult);
|
|
47
|
+
};
|
|
48
|
+
const logResult = () => {
|
|
49
|
+
cleanup();
|
|
50
|
+
logger.info('Result:', response.statusCode);
|
|
51
|
+
};
|
|
52
|
+
response.once('close', logResult);
|
|
53
|
+
response.once('finish', logResult);
|
|
54
|
+
try {
|
|
55
|
+
await handler(...args);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
cleanup();
|
|
59
|
+
if (error instanceof Error) {
|
|
60
|
+
logger.error('Error:', error.message);
|
|
61
|
+
}
|
|
62
|
+
logger.error(error);
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
});
|