@vyriy/router 0.3.0 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -1,41 +1,62 @@
1
- # Vyriy Package Agent Guide
1
+ # Project Agent Guide
2
2
 
3
- This package belongs to the Vyriy toolkit. Keep changes calm, explicit, reusable, and easy to reason about.
3
+ This repository follows a calm engineering style: changes should be explicit, reusable, typed, documented, tested, and easy to reason about.
4
4
 
5
- ## Architecture
5
+ Use this guide as the default behavior for AI agents and contributors working in this repository. Prefer local package conventions when they are more specific than this document.
6
+
7
+ ## Core Principles
6
8
 
7
9
  - Prefer simple modules over clever frameworks or hidden conventions.
8
- - Keep package boundaries explicit and avoid project-specific coupling.
10
+ - Keep package and project boundaries explicit.
11
+ - Avoid project-specific coupling in reusable code.
9
12
  - Extract only proven reusable behavior.
10
13
  - Keep public APIs small, typed, documented, and stable.
11
- - Prefer SSR-friendly and SSG-friendly code paths.
12
- - Keep integrations replaceable and avoid hard coupling to a CMS or runtime host.
13
- - Prefer AWS serverless-compatible assumptions when infrastructure concerns appear.
14
+ - Prefer SSR-friendly and SSG-friendly code paths when working with frontend or shared code.
15
+ - Keep integrations replaceable and avoid hard coupling to a CMS, framework, vendor, or runtime host.
16
+ - Prefer infrastructure assumptions that are easy to deploy, observe, and replace.
17
+ - Prefer the option that is simpler to explain, easier to evolve, and calmer to maintain.
14
18
 
15
19
  ## File Shape
16
20
 
17
- - Prefer one exported runtime method, component, or helper per production file when it stays readable.
21
+ - Prefer one exported runtime method, component, helper, or class per production file when it stays readable.
18
22
  - Prefer one matching test file per production file, for example `feature.ts` and `feature.test.ts`.
19
23
  - Use focused folders when behavior naturally splits into several related files.
20
24
  - Keep `index.ts` as a public re-export surface only. Do not place implementation logic in it.
21
- - Use `.js` relative import and export specifiers in TypeScript source where package style requires it.
25
+ - Use relative import and export specifiers that match the package module style.
26
+ - Use `.js` relative specifiers in TypeScript source for ESM/NodeNext packages.
22
27
  - Add `types.ts` when public shared types are part of the package contract.
23
28
  - Keep constants near the code that owns them unless they are shared or clarify repeated behavior.
24
29
 
25
30
  ## Public Surface
26
31
 
27
- - Every new public export should be re-exported from `index.ts`.
28
- - Add or update `index.test.ts` when the public export surface changes.
32
+ - Every new public export must be re-exported from the package or module public entry point.
33
+ - Add or update public-surface tests when exports change.
29
34
  - Add JSDoc for public exports when behavior, parameters, return values, or usage expectations need explanation.
30
- - Do not hand-maintain source package `exports` maps unless the package has a real custom publishing need.
35
+ - Avoid exporting internal helpers only to make tests easier.
36
+ - Do not hand-maintain package `exports` maps unless the project has a real custom publishing need.
31
37
 
32
38
  ## Tests
33
39
 
34
- - Tests use Jest and `@jest/globals`.
35
40
  - Cover public behavior and meaningful edge cases.
36
41
  - Prefer behavior-focused tests over private implementation lock-in.
42
+ - Keep tests deterministic.
43
+ - Avoid real network, filesystem, timers, browser, or cloud dependencies unless the behavior specifically requires them.
37
44
  - When mocking modules, install mocks before loading the module under test.
38
- - Use focused validation when changing package behavior:
45
+ - Use focused validation when changing behavior.
46
+
47
+ Example validation commands:
48
+
49
+ ```bash
50
+ yarn test
51
+ ```
52
+
53
+ For workspaces, prefer the project convention, for example:
54
+
55
+ ```bash
56
+ yarn workspace <package-name> test
57
+ ```
58
+
59
+ For Jest-based packages, focused validation may look like:
39
60
 
40
61
  ```bash
41
62
  yarn jest packages/<package> --runInBand --coverage=false
@@ -44,22 +65,38 @@ yarn jest packages/<package> --runInBand --coverage=false
44
65
  ## Documentation
45
66
 
46
67
  - Keep `README.md` concise and usage-oriented.
47
- - Start package READMEs with `# @vyriy/<package>`.
68
+ - Start package READMEs with `# <package>`.
48
69
  - Document real public exports, supported options, and examples that actually work.
49
- - Keep `doc.mdx` as the Storybook/docs wrapper for the README when the package participates in docs.
50
- - For component packages, include Storybook coverage for supported states and common usage.
70
+ - Update docs when public behavior changes.
71
+ - Keep generated docs wrappers, such as `doc.mdx`, aligned with the README when the project uses them.
72
+ - For component packages, include visual documentation or stories for supported states and common usage.
51
73
 
52
74
  ## Components
53
75
 
54
- - Prefer lightweight React 19+ components with TypeScript.
76
+ - Prefer lightweight React components with TypeScript when working in React packages.
55
77
  - Keep components SSR-friendly and avoid browser globals during render.
56
- - Prefer composable props and Bootstrap-compatible ergonomics where practical.
78
+ - Prefer composable props and predictable ergonomics.
57
79
  - Put each public component in its own file with a matching test.
58
- - Add Storybook stories when a component has visual states, variants, or interaction states.
80
+ - Add stories or examples when a component has visual states, variants, or interaction states.
81
+ - Keep styling explicit and reusable. Avoid hidden theme assumptions unless they are part of the package contract.
59
82
 
60
83
  ## Change Discipline
61
84
 
62
85
  - Keep changes scoped to the requested behavior.
63
86
  - Avoid unrelated refactors and metadata churn.
64
- - Sync implementation, tests, README, `doc.mdx`, and public re-exports together.
65
- - Prefer the option that is simpler to explain, easier to evolve, and calmer to maintain.
87
+ - Sync implementation, tests, docs, examples, and public re-exports together.
88
+ - Do not introduce new dependencies unless they clearly reduce complexity or are already part of the project direction.
89
+ - Prefer small, reviewable changes over broad rewrites.
90
+ - Preserve existing conventions unless there is a clear reason to change them.
91
+
92
+ ## Before Finishing
93
+
94
+ Check that the change is complete:
95
+
96
+ - Public exports are updated.
97
+ - Public-surface tests are updated when exports change.
98
+ - Matching unit tests exist for new behavior.
99
+ - README examples still match the real API.
100
+ - Visual docs, stories, or examples are updated for visible component behavior.
101
+ - TypeScript imports follow the package module style.
102
+ - No unrelated files, formatting churn, or generated artifacts were changed.
package/README.md CHANGED
@@ -9,7 +9,6 @@ This package provides a small API Gateway router for Lambda handlers.
9
9
  It is intentionally kept small:
10
10
 
11
11
  - matches by HTTP method and exact path
12
- - supports simple prefix dispatch for handler factories such as static file serving
13
12
  - passes API Gateway event data into handlers
14
13
  - returns a Lambda-friendly response shape
15
14
 
@@ -29,16 +28,14 @@ With Yarn:
29
28
  yarn add @vyriy/router
30
29
  ```
31
30
 
32
- ## Usage
31
+ ## Basic Router
33
32
 
34
33
  ```ts
35
34
  import { createRouter } from '@vyriy/router';
36
- import { staticFiles } from '@vyriy/server/static';
37
35
 
38
36
  const router = createRouter();
39
37
 
40
38
  router.get('/health', async ({ event, query, headers, pathParameters, body }) => ({
41
- statusCode: 200,
42
39
  body: JSON.stringify({
43
40
  ok: true,
44
41
  method: event.httpMethod,
@@ -49,8 +46,6 @@ router.get('/health', async ({ event, query, headers, pathParameters, body }) =>
49
46
  }),
50
47
  }));
51
48
 
52
- router.prefix('/static', staticFiles('./public'));
53
-
54
49
  router.fallback(async ({ event }) => ({
55
50
  statusCode: 404,
56
51
  body: JSON.stringify({
@@ -58,16 +53,98 @@ router.fallback(async ({ event }) => ({
58
53
  path: event.path,
59
54
  }),
60
55
  }));
56
+
57
+ export const handler = (event: Parameters<typeof router.route>[0]) => router.route(event);
61
58
  ```
62
59
 
63
- Prefix handlers receive the relative matched path in `pathParameters.proxy`, so they can behave like normal handlers:
60
+ ## Stream Router
61
+
62
+ Use `createStreamRouter()` when handlers write directly to a Lambda response stream. The stream is passed as the second handler argument, and stream handlers do not return a response object.
64
63
 
65
64
  ```ts
66
- router.prefix('/events', async ({ pathParameters, responseStream }) => {
67
- responseStream?.setContentType?.('text/plain');
68
- responseStream?.write(`Path: ${pathParameters?.proxy}`);
69
- responseStream?.end();
65
+ import { createStreamRouter } from '@vyriy/router';
66
+
67
+ const streamRouter = createStreamRouter();
68
+
69
+ streamRouter.get('/events', ({ event, query }, responseStream) => {
70
+ responseStream.setContentType?.('text/plain');
71
+ responseStream.write(`path: ${event.path}\n`);
72
+ responseStream.write(`cursor: ${query?.cursor ?? 'start'}\n`);
73
+ responseStream.end('done');
74
+ });
75
+
76
+ streamRouter.fallback(({ event }, responseStream) => {
77
+ responseStream.setContentType?.('application/json');
78
+ responseStream.end(
79
+ JSON.stringify({
80
+ message: 'Not Found',
81
+ path: event.path,
82
+ }),
83
+ );
70
84
  });
85
+
86
+ export const handler = (
87
+ event: Parameters<typeof streamRouter.route>[0],
88
+ responseStream: Parameters<typeof streamRouter.route>[1],
89
+ ) => streamRouter.route(event, responseStream);
90
+ ```
91
+
92
+ ## Calm Composition
93
+
94
+ The router keeps request matching separate from handler wrappers and local server adapters. A small API can stay as a plain composition of focused packages:
95
+
96
+ ```ts
97
+ import { api } from '@vyriy/handler';
98
+ import { createRouter } from '@vyriy/router';
99
+ import { server } from '@vyriy/server';
100
+
101
+ const router = createRouter();
102
+
103
+ router.get('/health', () => ({
104
+ body: JSON.stringify({
105
+ ok: true,
106
+ }),
107
+ }));
108
+
109
+ const handler = api((event) => router.route(event));
110
+
111
+ server(handler);
112
+ ```
113
+
114
+ The same shape works for Lambda response streaming:
115
+
116
+ ```ts
117
+ import { streamApi } from '@vyriy/handler';
118
+ import { createStreamRouter } from '@vyriy/router';
119
+ import { streamServer } from '@vyriy/server';
120
+
121
+ const router = createStreamRouter();
122
+
123
+ router.get('/events', (_params, responseStream) => {
124
+ responseStream.setContentType?.('text/plain');
125
+ responseStream.end('ok');
126
+ });
127
+
128
+ const handler = streamApi((event, responseStream) => router.route(event, responseStream));
129
+
130
+ streamServer(handler);
131
+ ```
132
+
133
+ For a Lambda-only entrypoint, keep the same composition and export the handler:
134
+
135
+ ```ts
136
+ import { api } from '@vyriy/handler';
137
+ import { createRouter } from '@vyriy/router';
138
+
139
+ const router = createRouter();
140
+
141
+ router.get('/health', () => ({
142
+ body: JSON.stringify({
143
+ ok: true,
144
+ }),
145
+ }));
146
+
147
+ export const handler = api((event) => router.route(event));
71
148
  ```
72
149
 
73
150
  ## Exports
@@ -75,28 +152,33 @@ router.prefix('/events', async ({ pathParameters, responseStream }) => {
75
152
  The package exposes both the root entry and the direct module entry:
76
153
 
77
154
  ```ts
78
- import { createRouter } from '@vyriy/router';
79
- import { Router } from '@vyriy/router/router';
155
+ import { createRouter, createStreamRouter } from '@vyriy/router';
156
+ import { Router, StreamRouter } from '@vyriy/router/router';
80
157
  ```
81
158
 
82
159
  ## API
83
160
 
84
161
  - `createRouter()` returns a chainable router API.
162
+ - `createStreamRouter()` returns a chainable response streaming router API.
85
163
  - `router.get(path, handler)` registers a `GET` handler.
86
164
  - `router.post(path, handler)` registers a `POST` handler.
87
165
  - `router.put(path, handler)` registers a `PUT` handler.
88
166
  - `router.delete(path, handler)` registers a `DELETE` handler.
89
167
  - `router.patch(path, handler)` registers a `PATCH` handler.
90
- - `router.prefix(pathPrefix, handler)` registers a handler for every request under a URL prefix.
91
168
  - `router.fallback(handler)` registers a handler for unmatched requests.
92
169
  - `router.route(event)` resolves the matching route and returns an API Gateway response.
170
+ - `streamRouter.route(event, responseStream)` resolves the matching route and writes to the stream.
171
+
172
+ Route handlers may omit `statusCode`; the router normalizes missing status codes to `200` before returning from `router.route(event)`.
93
173
 
94
- The low-level `Router` class is also available from `@vyriy/router/router` and exposes only:
174
+ The low-level `Router` and `StreamRouter` classes are also available from `@vyriy/router/router`:
95
175
 
96
176
  - `router.on(method, path, handler)`
97
- - `router.prefix(pathPrefix, handler)`
98
177
  - `router.fallback(handler)`
99
178
  - `router.route(event)`
179
+ - `streamRouter.on(method, path, handler)`
180
+ - `streamRouter.fallback(handler)`
181
+ - `streamRouter.route(event, responseStream)`
100
182
 
101
183
  Route handlers receive:
102
184
 
@@ -106,7 +188,12 @@ type HandlerParams = {
106
188
  body?: string;
107
189
  headers?: APIGatewayProxyEvent['headers'];
108
190
  pathParameters?: APIGatewayProxyEvent['pathParameters'];
109
- responseStream?: ResponseStream;
110
191
  event: APIGatewayProxyEvent;
111
192
  };
112
193
  ```
194
+
195
+ Stream route handlers receive the same `HandlerParams` as the first argument and `ResponseStream` as the second argument:
196
+
197
+ ```ts
198
+ type StreamHandler = (params: HandlerParams, responseStream: ResponseStream) => void | Promise<void>;
199
+ ```
package/create.d.ts CHANGED
@@ -1,2 +1,3 @@
1
- import type { RouterApi } from './types.js';
1
+ import type { RouterApi, StreamRouterApi } from './types.js';
2
2
  export declare const createRouter: () => RouterApi;
3
+ export declare const createStreamRouter: () => StreamRouterApi;
package/create.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Router } from './router.js';
1
+ import { Router, StreamRouter } from './router.js';
2
2
  export const createRouter = () => {
3
3
  const router = new Router();
4
4
  const api = {
@@ -26,13 +26,42 @@ export const createRouter = () => {
26
26
  router.on('PATCH', path, handler);
27
27
  return api;
28
28
  },
29
- prefix(pathPrefix, handler) {
30
- router.prefix(pathPrefix, handler);
31
- return api;
32
- },
33
29
  route(event) {
34
30
  return router.route(event);
35
31
  },
36
32
  };
37
33
  return api;
38
34
  };
35
+ export const createStreamRouter = () => {
36
+ const router = new StreamRouter();
37
+ const api = {
38
+ get(path, handler) {
39
+ router.on('GET', path, handler);
40
+ return api;
41
+ },
42
+ post(path, handler) {
43
+ router.on('POST', path, handler);
44
+ return api;
45
+ },
46
+ put(path, handler) {
47
+ router.on('PUT', path, handler);
48
+ return api;
49
+ },
50
+ delete(path, handler) {
51
+ router.on('DELETE', path, handler);
52
+ return api;
53
+ },
54
+ fallback(handler) {
55
+ router.fallback(handler);
56
+ return api;
57
+ },
58
+ patch(path, handler) {
59
+ router.on('PATCH', path, handler);
60
+ return api;
61
+ },
62
+ route(event, responseStream) {
63
+ return router.route(event, responseStream);
64
+ },
65
+ };
66
+ return api;
67
+ };
package/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from './create.js';
2
+ export * from './router.js';
2
3
  export type * from './types.js';
package/index.js CHANGED
@@ -1 +1,2 @@
1
1
  export * from './create.js';
2
+ export * from './router.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vyriy/router",
3
- "version": "0.3.0",
3
+ "version": "0.3.4",
4
4
  "description": "Router utility for Vyriy projects",
5
5
  "type": "module",
6
6
  "main": "./index.js",
package/router.d.ts CHANGED
@@ -1,10 +1,14 @@
1
- import type { APIGatewayProxyEvent, Handler, ResponseStream, RouteResult } from './types.js';
2
- export declare class Router {
3
- private fallbackHandler?;
4
- private readonly routes;
5
- private readonly staticRoutes;
6
- on(method: string, path: string, handler: Handler): this;
7
- fallback(handler: Handler): this;
8
- prefix(pathPrefix: string, handler: Handler): this;
9
- route(event: APIGatewayProxyEvent, responseStream?: ResponseStream): Promise<RouteResult | void>;
1
+ import type { APIGatewayProxyEvent, Handler, ResponseStream, RouteResult, StreamHandler } from './types.js';
2
+ declare class BaseRouter<CurrentHandler> {
3
+ protected fallbackHandler?: CurrentHandler;
4
+ protected readonly routes: Record<string, Record<string, CurrentHandler>>;
5
+ on(method: string, path: string, handler: CurrentHandler): this;
6
+ fallback(handler: CurrentHandler): this;
10
7
  }
8
+ export declare class Router extends BaseRouter<Handler> {
9
+ route(event: APIGatewayProxyEvent): Promise<RouteResult>;
10
+ }
11
+ export declare class StreamRouter extends BaseRouter<StreamHandler> {
12
+ route(event: APIGatewayProxyEvent, responseStream: ResponseStream): Promise<void>;
13
+ }
14
+ export {};
package/router.js CHANGED
@@ -1,25 +1,5 @@
1
1
  import { METHODS, STATUS_CODES } from 'node:http';
2
2
  const SUPPORTED_METHODS = new Set(METHODS.map((method) => method.toUpperCase()));
3
- const removeLeadingSlashes = (value) => {
4
- let start = 0;
5
- while (value[start] === '/') {
6
- start += 1;
7
- }
8
- return value.slice(start);
9
- };
10
- const removeTrailingSlashes = (value) => {
11
- let end = value.length;
12
- while (end > 0 && value[end - 1] === '/') {
13
- end -= 1;
14
- }
15
- return value.slice(0, end);
16
- };
17
- const getPrefixPath = (eventPath, route) => {
18
- if (eventPath !== route.pathPrefix && !eventPath.startsWith(`${route.pathPrefix}/`)) {
19
- return undefined;
20
- }
21
- return decodeURIComponent(removeLeadingSlashes(eventPath.slice(route.pathPrefix.length)));
22
- };
23
3
  const normalizeResult = (result) => ({
24
4
  statusCode: result.statusCode ?? 200,
25
5
  body: result.body,
@@ -27,34 +7,31 @@ const normalizeResult = (result) => ({
27
7
  isBase64Encoded: result.isBase64Encoded,
28
8
  multiValueHeaders: result.multiValueHeaders,
29
9
  });
30
- export class Router {
10
+ const registerRoute = (routes, method, path, handler) => {
11
+ const normalizedMethod = method.toUpperCase();
12
+ if (!SUPPORTED_METHODS.has(normalizedMethod)) {
13
+ throw new Error(`Unsupported HTTP method: ${normalizedMethod}`);
14
+ }
15
+ const routeGroup = (routes[normalizedMethod] ??= {});
16
+ if (routeGroup[path]) {
17
+ throw new Error(`${normalizedMethod} ${path} already exists!`);
18
+ }
19
+ routeGroup[path] = handler;
20
+ };
21
+ class BaseRouter {
31
22
  fallbackHandler;
32
23
  routes = {};
33
- staticRoutes = [];
34
24
  on(method, path, handler) {
35
- const normalizedMethod = method.toUpperCase();
36
- if (!SUPPORTED_METHODS.has(normalizedMethod)) {
37
- throw new Error(`Unsupported HTTP method: ${normalizedMethod}`);
38
- }
39
- const routeGroup = (this.routes[normalizedMethod] ??= {});
40
- if (routeGroup[path]) {
41
- throw new Error(`${normalizedMethod} ${path} already exists!`);
42
- }
43
- routeGroup[path] = handler;
25
+ registerRoute(this.routes, method, path, handler);
44
26
  return this;
45
27
  }
46
28
  fallback(handler) {
47
29
  this.fallbackHandler = handler;
48
30
  return this;
49
31
  }
50
- prefix(pathPrefix, handler) {
51
- this.staticRoutes.push({
52
- handler,
53
- pathPrefix: removeTrailingSlashes(pathPrefix) || '/',
54
- });
55
- return this;
56
- }
57
- async route(event, responseStream) {
32
+ }
33
+ export class Router extends BaseRouter {
34
+ async route(event) {
58
35
  const { httpMethod, path, queryStringParameters, body, headers, pathParameters } = event;
59
36
  const exactHandler = this.routes[httpMethod]?.[path];
60
37
  if (exactHandler) {
@@ -63,30 +40,9 @@ export class Router {
63
40
  body: body ?? undefined,
64
41
  headers,
65
42
  pathParameters: pathParameters ?? undefined,
66
- responseStream,
67
43
  event,
68
44
  });
69
- return result ? normalizeResult(result) : undefined;
70
- }
71
- for (const staticRoute of this.staticRoutes) {
72
- const proxy = getPrefixPath(path, staticRoute);
73
- if (proxy !== undefined) {
74
- const prefixResult = await staticRoute.handler({
75
- query: queryStringParameters ?? undefined,
76
- body: body ?? undefined,
77
- headers,
78
- pathParameters: {
79
- ...pathParameters,
80
- proxy,
81
- },
82
- responseStream,
83
- event,
84
- });
85
- if (prefixResult) {
86
- return normalizeResult(prefixResult);
87
- }
88
- return undefined;
89
- }
45
+ return normalizeResult(result);
90
46
  }
91
47
  if (this.fallbackHandler) {
92
48
  const fallbackResult = await this.fallbackHandler({
@@ -94,10 +50,9 @@ export class Router {
94
50
  body: body ?? undefined,
95
51
  headers,
96
52
  pathParameters: pathParameters ?? undefined,
97
- responseStream,
98
53
  event,
99
54
  });
100
- return fallbackResult ? normalizeResult(fallbackResult) : undefined;
55
+ return normalizeResult(fallbackResult);
101
56
  }
102
57
  return {
103
58
  statusCode: 404,
@@ -107,3 +62,33 @@ export class Router {
107
62
  };
108
63
  }
109
64
  }
65
+ export class StreamRouter extends BaseRouter {
66
+ async route(event, responseStream) {
67
+ const { httpMethod, path, queryStringParameters, body, headers, pathParameters } = event;
68
+ const exactHandler = this.routes[httpMethod]?.[path];
69
+ if (exactHandler) {
70
+ await exactHandler({
71
+ query: queryStringParameters ?? undefined,
72
+ body: body ?? undefined,
73
+ headers,
74
+ pathParameters: pathParameters ?? undefined,
75
+ event,
76
+ }, responseStream);
77
+ return;
78
+ }
79
+ if (this.fallbackHandler) {
80
+ await this.fallbackHandler({
81
+ query: queryStringParameters ?? undefined,
82
+ body: body ?? undefined,
83
+ headers,
84
+ pathParameters: pathParameters ?? undefined,
85
+ event,
86
+ }, responseStream);
87
+ return;
88
+ }
89
+ responseStream.setContentType?.('application/json');
90
+ responseStream.end(JSON.stringify({
91
+ message: STATUS_CODES[404],
92
+ }));
93
+ }
94
+ }
package/types.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import type { APIGatewayProxyEvent, APIGatewayProxyEventQueryStringParameters, APIGatewayProxyResult } from 'aws-lambda';
2
2
  export type { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
3
- export type RouteResult = {
3
+ type MaybePromise<Result> = Result | Promise<Result>;
4
+ export type RouteResult = APIGatewayProxyResult;
5
+ export type RouteHandlerResult = {
4
6
  body: APIGatewayProxyResult['body'];
5
7
  headers?: APIGatewayProxyResult['headers'];
6
8
  isBase64Encoded?: APIGatewayProxyResult['isBase64Encoded'];
@@ -21,10 +23,10 @@ export type HandlerParams = {
21
23
  body?: string;
22
24
  headers?: APIGatewayProxyEvent['headers'];
23
25
  pathParameters?: APIGatewayProxyEvent['pathParameters'];
24
- responseStream?: ResponseStream;
25
26
  event: APIGatewayProxyEvent;
26
27
  };
27
- export type Handler = (params: HandlerParams) => Promise<RouteResult | void>;
28
+ export type Handler = (params: HandlerParams) => MaybePromise<RouteHandlerResult>;
29
+ export type StreamHandler = (params: HandlerParams, responseStream: ResponseStream) => MaybePromise<void>;
28
30
  export type RouterApi = {
29
31
  get(path: string, handler: Handler): RouterApi;
30
32
  post(path: string, handler: Handler): RouterApi;
@@ -32,6 +34,14 @@ export type RouterApi = {
32
34
  delete(path: string, handler: Handler): RouterApi;
33
35
  fallback(handler: Handler): RouterApi;
34
36
  patch(path: string, handler: Handler): RouterApi;
35
- prefix(pathPrefix: string, handler: Handler): RouterApi;
36
- route(event: APIGatewayProxyEvent, responseStream?: ResponseStream): Promise<RouteResult | void>;
37
+ route(event: APIGatewayProxyEvent): Promise<RouteResult>;
38
+ };
39
+ export type StreamRouterApi = {
40
+ get(path: string, handler: StreamHandler): StreamRouterApi;
41
+ post(path: string, handler: StreamHandler): StreamRouterApi;
42
+ put(path: string, handler: StreamHandler): StreamRouterApi;
43
+ delete(path: string, handler: StreamHandler): StreamRouterApi;
44
+ fallback(handler: StreamHandler): StreamRouterApi;
45
+ patch(path: string, handler: StreamHandler): StreamRouterApi;
46
+ route(event: APIGatewayProxyEvent, responseStream: ResponseStream): Promise<void>;
37
47
  };