@sourceregistry/node-webserver 1.3.0 → 1.4.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 +111 -60
- package/dist/app.d.ts +1 -0
- package/dist/index.cjs.js +3 -5
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +1143 -1045
- package/dist/index.es.js.map +1 -1
- package/dist/middlewares/index.d.ts +3 -0
- package/dist/middlewares/requestid/index.d.ts +16 -0
- package/dist/middlewares/security/index.d.ts +39 -0
- package/dist/middlewares/timeout/index.d.ts +22 -0
- package/dist/types/router.d.ts +0 -1
- package/dist/types/server.d.ts +31 -3
- package/examples/public-baseline.ts +75 -0
- package/package.json +4 -4
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Middleware } from '../../types';
|
|
2
|
+
export interface Options {
|
|
3
|
+
/**
|
|
4
|
+
* Header name used for the request ID
|
|
5
|
+
* @default "x-request-id"
|
|
6
|
+
*/
|
|
7
|
+
headerName?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Custom request ID generator
|
|
10
|
+
* @default crypto.randomUUID
|
|
11
|
+
*/
|
|
12
|
+
generate?: () => string;
|
|
13
|
+
}
|
|
14
|
+
export declare function assign(options?: Options): Middleware<string, {
|
|
15
|
+
requestId: string;
|
|
16
|
+
}>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Middleware } from '../../types';
|
|
2
|
+
export interface Options {
|
|
3
|
+
/**
|
|
4
|
+
* Content Security Policy header value
|
|
5
|
+
* @default "default-src 'self'; base-uri 'self'; frame-ancestors 'none'; object-src 'none'"
|
|
6
|
+
*/
|
|
7
|
+
contentSecurityPolicy?: string | false;
|
|
8
|
+
/**
|
|
9
|
+
* X-Frame-Options header value
|
|
10
|
+
* @default "DENY"
|
|
11
|
+
*/
|
|
12
|
+
frameOptions?: "DENY" | "SAMEORIGIN" | false;
|
|
13
|
+
/**
|
|
14
|
+
* Referrer-Policy header value
|
|
15
|
+
* @default "no-referrer"
|
|
16
|
+
*/
|
|
17
|
+
referrerPolicy?: string | false;
|
|
18
|
+
/**
|
|
19
|
+
* Permissions-Policy header value
|
|
20
|
+
* @default "geolocation=(), microphone=(), camera=()"
|
|
21
|
+
*/
|
|
22
|
+
permissionsPolicy?: string | false;
|
|
23
|
+
/**
|
|
24
|
+
* Cross-Origin-Opener-Policy header value
|
|
25
|
+
* @default "same-origin"
|
|
26
|
+
*/
|
|
27
|
+
crossOriginOpenerPolicy?: string | false;
|
|
28
|
+
/**
|
|
29
|
+
* Cross-Origin-Resource-Policy header value
|
|
30
|
+
* @default "same-origin"
|
|
31
|
+
*/
|
|
32
|
+
crossOriginResourcePolicy?: string | false;
|
|
33
|
+
/**
|
|
34
|
+
* Strict-Transport-Security header value
|
|
35
|
+
* @default false
|
|
36
|
+
*/
|
|
37
|
+
strictTransportSecurity?: string | false;
|
|
38
|
+
}
|
|
39
|
+
export declare function headers(options?: Options): Middleware;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Middleware } from '../../types';
|
|
2
|
+
export interface Options {
|
|
3
|
+
/**
|
|
4
|
+
* Deadline in milliseconds
|
|
5
|
+
*/
|
|
6
|
+
ms: number;
|
|
7
|
+
/**
|
|
8
|
+
* Status code to return on timeout
|
|
9
|
+
* @default 504
|
|
10
|
+
*/
|
|
11
|
+
status?: number;
|
|
12
|
+
/**
|
|
13
|
+
* Response body to return on timeout
|
|
14
|
+
* @default "Gateway Timeout"
|
|
15
|
+
*/
|
|
16
|
+
body?: BodyInit | null;
|
|
17
|
+
/**
|
|
18
|
+
* Optional callback invoked when the deadline is exceeded
|
|
19
|
+
*/
|
|
20
|
+
onTimeout?: () => void;
|
|
21
|
+
}
|
|
22
|
+
export declare function deadline(options: Options): Middleware;
|
package/dist/types/router.d.ts
CHANGED
|
@@ -98,7 +98,6 @@ export declare class Router<Locals extends App.Locals = App.Locals> {
|
|
|
98
98
|
private sortWsRoutes;
|
|
99
99
|
private formatActionResult;
|
|
100
100
|
private handleActionError;
|
|
101
|
-
static New(): Router;
|
|
102
101
|
}
|
|
103
102
|
export declare const Action: {
|
|
104
103
|
readonly success: (code?: number, data?: Record<string, any>) => Response;
|
package/dist/types/server.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { ServerOptions } from 'http';
|
|
|
2
2
|
import { ServerOptions as HttpsServerOptions } from 'https';
|
|
3
3
|
import { RequestEvent, Router } from './';
|
|
4
4
|
import { ListenOptions } from 'net';
|
|
5
|
-
type
|
|
5
|
+
type ValueMatcher = string | RegExp | ((value: string) => boolean);
|
|
6
6
|
type InferServerLocals<TServerConfig extends ServerConfig> = Extract<TServerConfig['locals'], (event: RequestEvent) => any> extends (event: RequestEvent) => infer TLocals ? TLocals extends App.Locals ? TLocals : App.Locals : App.Locals;
|
|
7
7
|
export type SecurityConfig = {
|
|
8
8
|
/**
|
|
@@ -13,17 +13,36 @@ export type SecurityConfig = {
|
|
|
13
13
|
/**
|
|
14
14
|
* Restrict trusted Host values when trustHostHeader is enabled.
|
|
15
15
|
*/
|
|
16
|
-
allowedHosts?:
|
|
16
|
+
allowedHosts?: ValueMatcher | ValueMatcher[];
|
|
17
|
+
/**
|
|
18
|
+
* Trust forwarded proxy headers only when the direct peer matches one of these values.
|
|
19
|
+
*/
|
|
20
|
+
trustedProxies?: ValueMatcher | ValueMatcher[];
|
|
17
21
|
/**
|
|
18
22
|
* Restrict accepted WebSocket Origin values.
|
|
19
23
|
* When omitted, Origin is not enforced by default.
|
|
20
24
|
*/
|
|
21
|
-
allowedWebSocketOrigins?:
|
|
25
|
+
allowedWebSocketOrigins?: ValueMatcher | ValueMatcher[];
|
|
22
26
|
/**
|
|
23
27
|
* Maximum accepted request body size based on Content-Length.
|
|
24
28
|
* Requests above the limit are rejected before the body is read.
|
|
25
29
|
*/
|
|
26
30
|
maxRequestBodySize?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Maximum time allowed to receive the complete request headers.
|
|
33
|
+
* @default 30000
|
|
34
|
+
*/
|
|
35
|
+
headersTimeoutMs?: number;
|
|
36
|
+
/**
|
|
37
|
+
* Maximum time allowed for the full request lifecycle.
|
|
38
|
+
* @default 60000
|
|
39
|
+
*/
|
|
40
|
+
requestTimeoutMs?: number;
|
|
41
|
+
/**
|
|
42
|
+
* How long to keep idle keep-alive connections open.
|
|
43
|
+
* @default 5000
|
|
44
|
+
*/
|
|
45
|
+
keepAliveTimeoutMs?: number;
|
|
27
46
|
/**
|
|
28
47
|
* Maximum accepted WebSocket message size in bytes.
|
|
29
48
|
* Passed to ws as maxPayload.
|
|
@@ -69,13 +88,22 @@ export declare class WebServer<TServerConfig extends ServerConfig = ServerConfig
|
|
|
69
88
|
private toRequest;
|
|
70
89
|
private wrapRequestBody;
|
|
71
90
|
private toURL;
|
|
91
|
+
private resolveProtocol;
|
|
72
92
|
private resolveAuthority;
|
|
93
|
+
private getClientAddress;
|
|
73
94
|
private normalizeTrustedHost;
|
|
74
95
|
private matchesValue;
|
|
96
|
+
private configureServerTimeouts;
|
|
97
|
+
private isDevelopment;
|
|
98
|
+
private getTrustedForwardedHeader;
|
|
99
|
+
private isTrustedProxy;
|
|
100
|
+
private normalizeAddress;
|
|
101
|
+
private parseForwardedHeader;
|
|
75
102
|
private toHeaders;
|
|
76
103
|
private isRequestBodyAllowed;
|
|
77
104
|
private handleError;
|
|
78
105
|
private sendWebResponse;
|
|
106
|
+
private isPrematureCloseError;
|
|
79
107
|
private shouldOmitResponseBody;
|
|
80
108
|
private isAllowedWebSocketOrigin;
|
|
81
109
|
private createEventFetch;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CORS,
|
|
3
|
+
RequestId,
|
|
4
|
+
RateLimiter,
|
|
5
|
+
Security,
|
|
6
|
+
Timeout,
|
|
7
|
+
WebServer,
|
|
8
|
+
json,
|
|
9
|
+
text
|
|
10
|
+
} from "../src";
|
|
11
|
+
|
|
12
|
+
const app = new WebServer({
|
|
13
|
+
type: "http",
|
|
14
|
+
options: {},
|
|
15
|
+
locals: () => ({
|
|
16
|
+
startedAt: Date.now()
|
|
17
|
+
}),
|
|
18
|
+
security: {
|
|
19
|
+
trustedProxies: ["127.0.0.1"],
|
|
20
|
+
trustHostHeader: true,
|
|
21
|
+
allowedHosts: ["app.example.com"],
|
|
22
|
+
headersTimeoutMs: 30_000,
|
|
23
|
+
requestTimeoutMs: 60_000,
|
|
24
|
+
keepAliveTimeoutMs: 5_000,
|
|
25
|
+
maxRequestBodySize: 1024 * 1024,
|
|
26
|
+
allowedWebSocketOrigins: "https://app.example.com"
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
app.pre(async (event) => {
|
|
31
|
+
if (event.url.pathname.startsWith("/private")) {
|
|
32
|
+
const auth = event.request.headers.get("authorization");
|
|
33
|
+
if (!auth) {
|
|
34
|
+
return new Response("Unauthorized", {status: 401});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
app.useMiddleware(
|
|
40
|
+
RequestId.assign(),
|
|
41
|
+
Security.headers({
|
|
42
|
+
strictTransportSecurity: "max-age=31536000; includeSubDomains"
|
|
43
|
+
}),
|
|
44
|
+
Timeout.deadline({
|
|
45
|
+
ms: 15_000,
|
|
46
|
+
status: 503,
|
|
47
|
+
body: "Request timed out"
|
|
48
|
+
}),
|
|
49
|
+
CORS.policy({
|
|
50
|
+
origin: "https://app.example.com",
|
|
51
|
+
credentials: true
|
|
52
|
+
}),
|
|
53
|
+
RateLimiter.fixedWindowLimit({
|
|
54
|
+
max: 60,
|
|
55
|
+
windowMs: 60_000
|
|
56
|
+
})
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
app.GET("/", () => text("hello"));
|
|
60
|
+
|
|
61
|
+
app.GET("/users/[id]", (event) => json({
|
|
62
|
+
id: event.params.id,
|
|
63
|
+
requestId: event.locals.requestId,
|
|
64
|
+
startedAt: event.locals.startedAt
|
|
65
|
+
}));
|
|
66
|
+
|
|
67
|
+
app.post(async (_event, response) => {
|
|
68
|
+
const nextResponse = new Response(response.body, response);
|
|
69
|
+
nextResponse.headers.set("x-server", "node-webserver");
|
|
70
|
+
return nextResponse;
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
app.listen(3000, () => {
|
|
74
|
+
console.log("server listening on port 3000");
|
|
75
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sourceregistry/node-webserver",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "TypeScript web server for Node.js with web-standard Request and Response APIs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs.js",
|
|
@@ -68,16 +68,16 @@
|
|
|
68
68
|
"@semantic-release/git": "^10.0.1",
|
|
69
69
|
"@semantic-release/npm": "^13.1.3",
|
|
70
70
|
"@semantic-release/release-notes-generator": "^14.1.0",
|
|
71
|
-
"@types/node": "^
|
|
71
|
+
"@types/node": "^25.5.0",
|
|
72
72
|
"@vitest/coverage-v8": "^4.0.16",
|
|
73
73
|
"@vitest/ui": "^4.0.16",
|
|
74
74
|
"tsx": "^4.21.0",
|
|
75
75
|
"typedoc": "^0.28.15",
|
|
76
76
|
"typescript": "^5.9.3",
|
|
77
|
-
"vite": "^
|
|
77
|
+
"vite": "^8.0.1",
|
|
78
78
|
"vite-plugin-dts": "^4.5.4",
|
|
79
79
|
"@types/ws": "^8.18.1",
|
|
80
|
-
"vitest": "^4.0
|
|
80
|
+
"vitest": "^4.1.0"
|
|
81
81
|
},
|
|
82
82
|
"release": {
|
|
83
83
|
"branches": [
|