@vyriy/server 0.3.0 → 0.3.2

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 CHANGED
@@ -32,58 +32,54 @@ server(async () => ({
32
32
  }));
33
33
  ```
34
34
 
35
- Run a Lambda response streaming handler locally:
35
+ Use `api(...)` when you want the handler package wrappers. It keeps the same `(event, context)` Lambda handler shape.
36
36
 
37
37
  ```ts
38
- import { api, streamify } from '@vyriy/handler';
38
+ import { api } from '@vyriy/handler';
39
39
  import { server } from '@vyriy/server';
40
40
 
41
- server(
42
- streamify(
43
- api(async (event, responseStream) => {
44
- responseStream.setContentType?.('text/plain');
45
- responseStream.write(`Path: ${event.path}\n`);
46
- responseStream.end('Done');
47
- }),
48
- ),
49
- );
50
- ```
41
+ const handler = api(async (event) => ({
42
+ statusCode: 200,
43
+ body: JSON.stringify({
44
+ ok: true,
45
+ path: event.path,
46
+ }),
47
+ }));
51
48
 
52
- Keep the AWS-specific `awslambda.streamifyResponse(handler)` wrapper in a separate Lambda entrypoint.
49
+ server(handler);
50
+ ```
53
51
 
54
- Serve static files through `@vyriy/router` prefix routes:
52
+ Run a Lambda response streaming handler locally:
55
53
 
56
54
  ```ts
57
- import { createRouter } from '@vyriy/router';
58
- import { staticFiles } from '@vyriy/server/static';
55
+ import { streamServer } from '@vyriy/server';
59
56
 
60
- const router = createRouter().prefix('/static', staticFiles('./public'));
57
+ streamServer(async (event, responseStream) => {
58
+ responseStream.setContentType?.('text/plain');
59
+ responseStream.write(`Path: ${event.path}\n`);
60
+ responseStream.end('Done');
61
+ });
61
62
  ```
62
63
 
63
- Serve a static directory directly when the server only needs files:
64
+ The stream handler receives the API Gateway-style event first, the response stream second, and the Lambda context third.
65
+
66
+ Use `streamApi(...)` when you want the handler package wrappers. It keeps the same `(event, responseStream, context)` stream handler shape.
64
67
 
65
68
  ```ts
66
- import { server, staticFiles } from '@vyriy/server';
69
+ import { streamApi } from '@vyriy/handler';
70
+ import { streamServer } from '@vyriy/server';
67
71
 
68
- server(
69
- staticFiles('./public', {
70
- fallback: 'index.html',
71
- fallbackStatusCode: 200,
72
- }),
73
- );
72
+ const handler = streamApi((event, responseStream) => {
73
+ responseStream.setContentType?.('text/plain');
74
+ responseStream.write(`Path: ${event.path}\n`);
75
+ responseStream.end('Done');
76
+ });
77
+
78
+ streamServer(handler);
74
79
  ```
75
80
 
76
- By default, missing static files fall back to `404.html` from the same directory with status `404` when that file exists.
77
- For static apps that should fall back to `index.html`, configure the fallback explicitly:
81
+ Keep the AWS-specific `awslambda.streamifyResponse(handler)` wrapper in a separate Lambda entrypoint.
78
82
 
79
- ```ts
80
- const router = createRouter().prefix(
81
- '/static',
82
- staticFiles('./public', {
83
- fallback: 'index.html',
84
- fallbackStatusCode: 200,
85
- }),
86
- );
87
- ```
83
+ Static files are intentionally left to the Docker/web-server layer, for example Nginx, Caddy, or the platform serving assets in front of this Node process.
88
84
 
89
85
  The server listens on `PORT` from `@vyriy/env`. The default port is `3000`.
package/index.d.ts CHANGED
@@ -1,3 +1,2 @@
1
- export { server } from './server.js';
2
- export { staticFiles } from './static.js';
1
+ export { server, streamServer } from './server.js';
3
2
  export type * from './types.js';
package/index.js CHANGED
@@ -1,2 +1 @@
1
- export { server } from './server.js';
2
- export { staticFiles } from './static.js';
1
+ export { server, streamServer } from './server.js';
package/listener.d.ts CHANGED
@@ -1,2 +1,3 @@
1
- import type { NativeRequestListener, ServerHandler } from './types.js';
2
- export declare const listener: (handler: ServerHandler) => NativeRequestListener;
1
+ import type { LambdaHandler, LambdaStreamHandler, NativeRequestListener } from './types.js';
2
+ export declare const listener: (handler: LambdaHandler) => NativeRequestListener;
3
+ export declare const streamListener: (handler: LambdaStreamHandler) => NativeRequestListener;
package/listener.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import { mapParams } from './params.js';
2
2
  import { error, result } from './result.js';
3
- const isStreamHandler = (handler) => handler.length >= 3;
4
3
  const createResponseStream = (response) => {
5
4
  response.setContentType = (contentType) => response.setHeader?.('content-type', contentType) ?? response;
6
5
  return response;
@@ -8,10 +7,6 @@ const createResponseStream = (response) => {
8
7
  export const listener = (handler) => async (request, response) => {
9
8
  try {
10
9
  const { context, event } = await mapParams(request);
11
- if (isStreamHandler(handler)) {
12
- await handler(event, createResponseStream(response), context);
13
- return;
14
- }
15
10
  const value = await handler(event, context);
16
11
  await result(response, value);
17
12
  }
@@ -19,3 +14,12 @@ export const listener = (handler) => async (request, response) => {
19
14
  error(response);
20
15
  }
21
16
  };
17
+ export const streamListener = (handler) => async (request, response) => {
18
+ try {
19
+ const { context, event } = await mapParams(request);
20
+ await handler(event, createResponseStream(response), context);
21
+ }
22
+ catch {
23
+ error(response);
24
+ }
25
+ };
package/package.json CHANGED
@@ -1,16 +1,15 @@
1
1
  {
2
2
  "name": "@vyriy/server",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Small HTTP server adapter for Lambda-style Vyriy handlers",
5
5
  "type": "module",
6
6
  "main": "./index.js",
7
7
  "dependencies": {
8
8
  "@types/aws-lambda": "^8.10.161",
9
- "@vyriy/env": "0.3.0",
10
- "@vyriy/error": "0.3.0",
11
- "@vyriy/handler": "0.3.0",
12
- "@vyriy/logger": "0.3.0",
13
- "@vyriy/router": "0.3.0"
9
+ "@vyriy/env": "0.3.2",
10
+ "@vyriy/error": "0.3.2",
11
+ "@vyriy/handler": "0.3.2",
12
+ "@vyriy/logger": "0.3.2"
14
13
  },
15
14
  "agents": "./AGENTS.md",
16
15
  "license": "MIT",
@@ -115,16 +114,6 @@
115
114
  "types": "./shutdown.d.ts",
116
115
  "import": "./shutdown.js",
117
116
  "default": "./shutdown.js"
118
- },
119
- "./static": {
120
- "types": "./static.d.ts",
121
- "import": "./static.js",
122
- "default": "./static.js"
123
- },
124
- "./static.js": {
125
- "types": "./static.d.ts",
126
- "import": "./static.js",
127
- "default": "./static.js"
128
117
  }
129
118
  }
130
119
  }
package/server.d.ts CHANGED
@@ -1,2 +1,3 @@
1
- import type { CreateServer } from './types.js';
1
+ import type { CreateServer, CreateStreamServer } from './types.js';
2
2
  export declare const server: CreateServer;
3
+ export declare const streamServer: CreateStreamServer;
package/server.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as http from 'node:http';
2
- import { listener } from './listener.js';
2
+ import { listener, streamListener } from './listener.js';
3
3
  import { listen } from './listen.js';
4
4
  export const server = (handler) => listen(http.createServer(listener(handler)));
5
+ export const streamServer = (handler) => listen(http.createServer(streamListener(handler)));
package/types.d.ts CHANGED
@@ -7,7 +7,6 @@ export type LambdaEvent = APIGatewayProxyEvent;
7
7
  export type LambdaResult = APIGatewayProxyResult;
8
8
  export type LambdaHandler = (event: LambdaEvent, context: Context) => Promise<LambdaResult>;
9
9
  export type LambdaStreamHandler = (event: LambdaEvent, responseStream: ResponseStream, context: Context) => Promise<LambdaResult | void>;
10
- export type ServerHandler = LambdaHandler | LambdaStreamHandler;
11
10
  export type RequestMessage = {
12
11
  headers: IncomingHttpHeaders;
13
12
  method?: string;
@@ -42,4 +41,5 @@ export type Listen = (server: Server) => Server;
42
41
  export type NormalizeHeaders = (headers: IncomingHttpHeaders) => Record<string, string | undefined>;
43
42
  export type WriteResult<Response extends ResponseMessage = ResponseMessage> = (response: Response, result: LambdaResult | void) => Promise<void> | void;
44
43
  export type WriteError<Response extends ResponseMessage = ResponseMessage> = (response: Response) => void;
45
- export type CreateServer = (handler: ServerHandler) => Server;
44
+ export type CreateServer = (handler: LambdaHandler) => Server;
45
+ export type CreateStreamServer = (handler: LambdaStreamHandler) => Server;
package/static.d.ts DELETED
@@ -1,8 +0,0 @@
1
- import type { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
2
- import type { Handler } from '@vyriy/router';
3
- export type StaticFilesOptions = {
4
- fallback?: false | string;
5
- fallbackStatusCode?: number;
6
- };
7
- export type StaticFilesHandler = Handler & ((event: APIGatewayProxyEvent) => Promise<APIGatewayProxyResult>);
8
- export declare const staticFiles: (directory: string, options?: StaticFilesOptions) => StaticFilesHandler;
package/static.js DELETED
@@ -1,120 +0,0 @@
1
- import { STATUS_CODES } from 'node:http';
2
- import { readFile, stat } from 'node:fs/promises';
3
- import { extname, resolve, sep } from 'node:path';
4
- const CONTENT_TYPES = {
5
- '.css': 'text/css; charset=utf-8',
6
- '.gif': 'image/gif',
7
- '.html': 'text/html; charset=utf-8',
8
- '.ico': 'image/x-icon',
9
- '.jpeg': 'image/jpeg',
10
- '.jpg': 'image/jpeg',
11
- '.js': 'text/javascript; charset=utf-8',
12
- '.json': 'application/json; charset=utf-8',
13
- '.mjs': 'text/javascript; charset=utf-8',
14
- '.png': 'image/png',
15
- '.svg': 'image/svg+xml; charset=utf-8',
16
- '.txt': 'text/plain; charset=utf-8',
17
- '.webp': 'image/webp',
18
- };
19
- const getContentType = (filePath) => CONTENT_TYPES[extname(filePath).toLowerCase()] ?? 'application/octet-stream';
20
- const notFound = () => ({
21
- body: JSON.stringify({
22
- message: STATUS_CODES[404],
23
- }),
24
- headers: {
25
- 'content-type': 'application/json',
26
- },
27
- statusCode: 404,
28
- });
29
- const methodNotAllowed = () => ({
30
- body: JSON.stringify({
31
- message: STATUS_CODES[405],
32
- }),
33
- headers: {
34
- allow: 'GET, HEAD',
35
- 'content-type': 'application/json',
36
- },
37
- statusCode: 405,
38
- });
39
- const getFilePath = (directory, relativePath) => {
40
- const rootPath = resolve(directory);
41
- const filePath = resolve(rootPath, relativePath.replace(/^\/+/, '') || 'index.html');
42
- if (filePath !== rootPath && !filePath.startsWith(`${rootPath}${sep}`)) {
43
- return undefined;
44
- }
45
- return filePath;
46
- };
47
- const readFileResponse = async (filePath, method, statusCode = 200) => {
48
- const file = await stat(filePath);
49
- if (!file.isFile()) {
50
- return undefined;
51
- }
52
- const headers = {
53
- 'content-length': String(file.size),
54
- 'content-type': getContentType(filePath),
55
- };
56
- if (method === 'HEAD') {
57
- return {
58
- body: '',
59
- headers,
60
- statusCode,
61
- };
62
- }
63
- return {
64
- body: (await readFile(filePath)).toString('base64'),
65
- headers,
66
- isBase64Encoded: true,
67
- statusCode,
68
- };
69
- };
70
- const readFallbackResponse = async (directory, method, options) => {
71
- if (options.fallback === false) {
72
- return undefined;
73
- }
74
- const fallbackPath = getFilePath(directory, options.fallback);
75
- if (!fallbackPath) {
76
- return undefined;
77
- }
78
- try {
79
- return await readFileResponse(fallbackPath, method, options.fallbackStatusCode);
80
- }
81
- catch {
82
- return undefined;
83
- }
84
- };
85
- const isRouterParams = (value) => 'event' in value;
86
- const getRequest = (value) => {
87
- if (isRouterParams(value)) {
88
- return {
89
- event: value.event,
90
- relativePath: value.pathParameters?.proxy ?? '',
91
- };
92
- }
93
- return {
94
- event: value,
95
- relativePath: value.path.replace(/^\/+/, ''),
96
- };
97
- };
98
- export const staticFiles = (directory, options = {}) => async (params) => {
99
- const { event, relativePath } = getRequest(params);
100
- const normalizedOptions = {
101
- fallback: '404.html',
102
- fallbackStatusCode: 404,
103
- ...options,
104
- };
105
- if (event.httpMethod !== 'GET' && event.httpMethod !== 'HEAD') {
106
- return methodNotAllowed();
107
- }
108
- const filePath = getFilePath(directory, relativePath);
109
- if (!filePath) {
110
- return (await readFallbackResponse(directory, event.httpMethod, normalizedOptions)) ?? notFound();
111
- }
112
- try {
113
- return ((await readFileResponse(filePath, event.httpMethod)) ??
114
- (await readFallbackResponse(directory, event.httpMethod, normalizedOptions)) ??
115
- notFound());
116
- }
117
- catch {
118
- return (await readFallbackResponse(directory, event.httpMethod, normalizedOptions)) ?? notFound();
119
- }
120
- };