@rexeus/typeweaver-server 0.10.2 → 0.10.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/dist/index.cjs +3 -2
- package/dist/index.mjs +4 -3
- package/dist/index.mjs.map +1 -1
- package/dist/lib/BodyLimitPolicy.ts +118 -0
- package/dist/lib/Errors.ts +16 -0
- package/dist/lib/FetchApiAdapter.ts +54 -22
- package/dist/lib/NodeAdapter.ts +570 -36
- package/dist/lib/PathMatcher.ts +54 -10
- package/dist/lib/Router.ts +16 -4
- package/dist/lib/TypeweaverApp.ts +32 -9
- package/dist/lib/TypeweaverAppRuntime.ts +37 -0
- package/dist/lib/TypeweaverInternals.ts +45 -0
- package/dist/lib/index.ts +1 -0
- package/dist/lib/middleware/basicAuth.ts +11 -2
- package/dist/lib/middleware/bearerAuth.ts +11 -2
- package/dist/lib/middleware/cors.ts +120 -12
- package/dist/lib/middleware/header.ts +59 -0
- package/dist/lib/middleware/logger.ts +4 -2
- package/dist/lib/middleware/poweredBy.ts +6 -1
- package/dist/lib/middleware/requestId.ts +8 -8
- package/dist/lib/middleware/scoped.ts +27 -12
- package/dist/lib/middleware/secureHeaders.ts +3 -1
- package/package.json +8 -5
|
@@ -10,10 +10,15 @@ export function poweredBy(options?: PoweredByOptions) {
|
|
|
10
10
|
|
|
11
11
|
return defineMiddleware(async (_ctx, next) => {
|
|
12
12
|
const response = await next();
|
|
13
|
+
const responseHeaders = Object.fromEntries(
|
|
14
|
+
Object.entries(response.header ?? {}).filter(
|
|
15
|
+
([headerName]) => headerName.toLowerCase() !== "x-powered-by"
|
|
16
|
+
)
|
|
17
|
+
);
|
|
13
18
|
|
|
14
19
|
return {
|
|
15
20
|
...response,
|
|
16
|
-
header: { ...
|
|
21
|
+
header: { ...responseHeaders, "x-powered-by": value },
|
|
17
22
|
} satisfies IHttpResponse;
|
|
18
23
|
});
|
|
19
24
|
}
|
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
import type { IHttpResponse } from "@rexeus/typeweaver-core";
|
|
2
2
|
import { defineMiddleware } from "../TypedMiddleware.js";
|
|
3
|
+
import { omitHeaders, readSingletonHeader } from "./header.js";
|
|
3
4
|
|
|
4
5
|
export type RequestIdOptions = {
|
|
5
6
|
readonly headerName?: string;
|
|
6
7
|
readonly generator?: () => string;
|
|
7
8
|
};
|
|
8
9
|
|
|
10
|
+
const isValidRequestId = (value: string | undefined): value is string =>
|
|
11
|
+
value !== undefined && value.length > 0 && !/[\r\n]/.test(value);
|
|
12
|
+
|
|
9
13
|
export function requestId(options?: RequestIdOptions) {
|
|
10
14
|
const headerName = (options?.headerName ?? "x-request-id").toLowerCase();
|
|
11
15
|
const generator = options?.generator ?? (() => crypto.randomUUID());
|
|
12
16
|
|
|
13
17
|
return defineMiddleware<{ requestId: string }>(async (ctx, next) => {
|
|
14
|
-
const existing = ctx.request.header
|
|
15
|
-
const id =
|
|
16
|
-
typeof existing === "string"
|
|
17
|
-
? existing
|
|
18
|
-
: Array.isArray(existing)
|
|
19
|
-
? (existing[0] ?? generator())
|
|
20
|
-
: generator();
|
|
18
|
+
const existing = readSingletonHeader(ctx.request.header, headerName);
|
|
19
|
+
const id = isValidRequestId(existing) ? existing : generator();
|
|
21
20
|
|
|
22
21
|
const response = await next({ requestId: id });
|
|
22
|
+
const header = omitHeaders(response.header, [headerName]);
|
|
23
23
|
|
|
24
24
|
return {
|
|
25
25
|
...response,
|
|
26
|
-
header: { ...
|
|
26
|
+
header: { ...header, [headerName]: id },
|
|
27
27
|
} satisfies IHttpResponse;
|
|
28
28
|
});
|
|
29
29
|
}
|
|
@@ -2,55 +2,70 @@ import { pathMatcher } from "../PathMatcher.js";
|
|
|
2
2
|
import { defineMiddleware } from "../TypedMiddleware.js";
|
|
3
3
|
import type { TypedMiddleware } from "../TypedMiddleware.js";
|
|
4
4
|
|
|
5
|
+
type NoProvidedKeys<TProvides extends Record<string, unknown>> = [
|
|
6
|
+
keyof TProvides,
|
|
7
|
+
] extends [never]
|
|
8
|
+
? unknown
|
|
9
|
+
: never;
|
|
10
|
+
|
|
5
11
|
/**
|
|
6
12
|
* Restricts a middleware to only run on paths matching the given patterns.
|
|
7
13
|
*
|
|
8
14
|
* Accepts the same pattern syntax as {@link pathMatcher}: exact (`"/users"`),
|
|
9
15
|
* prefix (`"/api/*"`), and parameterized (`"/users/:id"`).
|
|
10
16
|
*
|
|
11
|
-
* Only accepts non-state middleware
|
|
17
|
+
* Only accepts non-state-providing middleware to preserve
|
|
12
18
|
* TypeWeaver's compile-time state guarantees — skipping a state-providing
|
|
13
19
|
* middleware would leave downstream consumers with missing state.
|
|
20
|
+
* Any upstream state requirements declared by the wrapped middleware are
|
|
21
|
+
* preserved on the returned middleware descriptor.
|
|
14
22
|
*
|
|
15
23
|
* @example
|
|
16
24
|
* ```typescript
|
|
17
25
|
* app.use(scoped(["/api/*"], cors({ origin: "https://app.com" })));
|
|
18
26
|
* ```
|
|
19
27
|
*/
|
|
20
|
-
export function scoped
|
|
28
|
+
export function scoped<
|
|
29
|
+
TProvides extends Record<string, unknown>,
|
|
30
|
+
TRequires extends Record<string, unknown>,
|
|
31
|
+
>(
|
|
21
32
|
paths: readonly string[],
|
|
22
|
-
middleware: TypedMiddleware<
|
|
23
|
-
): TypedMiddleware<
|
|
33
|
+
middleware: TypedMiddleware<TProvides, TRequires> & NoProvidedKeys<TProvides>
|
|
34
|
+
): TypedMiddleware<TProvides, TRequires> {
|
|
24
35
|
const matchers = paths.map(pathMatcher);
|
|
25
36
|
|
|
26
|
-
return defineMiddleware(async (ctx, next) => {
|
|
37
|
+
return defineMiddleware<{}, TRequires>(async (ctx, next) => {
|
|
27
38
|
if (!matchers.some(match => match(ctx.request.path))) {
|
|
28
39
|
return next();
|
|
29
40
|
}
|
|
30
41
|
return middleware.handler(ctx, next);
|
|
31
|
-
})
|
|
42
|
+
}) as TypedMiddleware<TProvides, TRequires>;
|
|
32
43
|
}
|
|
33
44
|
|
|
34
45
|
/**
|
|
35
46
|
* Runs a middleware on all paths *except* those matching the given patterns.
|
|
36
47
|
*
|
|
37
|
-
* The inverse of {@link scoped}. Same pattern syntax and type
|
|
48
|
+
* The inverse of {@link scoped}. Same pattern syntax and type constraints,
|
|
49
|
+
* including preservation of wrapped middleware state requirements.
|
|
38
50
|
*
|
|
39
51
|
* @example
|
|
40
52
|
* ```typescript
|
|
41
53
|
* app.use(except(["/health", "/ready"], logger()));
|
|
42
54
|
* ```
|
|
43
55
|
*/
|
|
44
|
-
export function except
|
|
56
|
+
export function except<
|
|
57
|
+
TProvides extends Record<string, unknown>,
|
|
58
|
+
TRequires extends Record<string, unknown>,
|
|
59
|
+
>(
|
|
45
60
|
paths: readonly string[],
|
|
46
|
-
middleware: TypedMiddleware<
|
|
47
|
-
): TypedMiddleware<
|
|
61
|
+
middleware: TypedMiddleware<TProvides, TRequires> & NoProvidedKeys<TProvides>
|
|
62
|
+
): TypedMiddleware<TProvides, TRequires> {
|
|
48
63
|
const matchers = paths.map(pathMatcher);
|
|
49
64
|
|
|
50
|
-
return defineMiddleware(async (ctx, next) => {
|
|
65
|
+
return defineMiddleware<{}, TRequires>(async (ctx, next) => {
|
|
51
66
|
if (matchers.some(match => match(ctx.request.path))) {
|
|
52
67
|
return next();
|
|
53
68
|
}
|
|
54
69
|
return middleware.handler(ctx, next);
|
|
55
|
-
})
|
|
70
|
+
}) as TypedMiddleware<TProvides, TRequires>;
|
|
56
71
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { IHttpResponse } from "@rexeus/typeweaver-core";
|
|
2
2
|
import { defineMiddleware } from "../TypedMiddleware.js";
|
|
3
|
+
import { omitHeaders } from "./header.js";
|
|
3
4
|
|
|
4
5
|
export type SecureHeadersOptions = {
|
|
5
6
|
readonly contentTypeOptions?: string | false;
|
|
@@ -48,10 +49,11 @@ export function secureHeaders(options?: SecureHeadersOptions) {
|
|
|
48
49
|
|
|
49
50
|
return defineMiddleware(async (_ctx, next) => {
|
|
50
51
|
const response = await next();
|
|
52
|
+
const header = omitHeaders(response.header, Object.keys(headers));
|
|
51
53
|
|
|
52
54
|
return {
|
|
53
55
|
...response,
|
|
54
|
-
header: { ...
|
|
56
|
+
header: { ...header, ...headers },
|
|
55
57
|
} satisfies IHttpResponse;
|
|
56
58
|
});
|
|
57
59
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rexeus/typeweaver-server",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.4",
|
|
4
4
|
"description": "Generates a lightweight, dependency-free server with built-in routing and middleware from your API definitions. Powered by Typeweaver.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -47,15 +47,18 @@
|
|
|
47
47
|
},
|
|
48
48
|
"homepage": "https://github.com/rexeus/typeweaver#readme",
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"@rexeus/typeweaver-core": "^0.10.
|
|
51
|
-
"@rexeus/typeweaver-gen": "^0.10.
|
|
50
|
+
"@rexeus/typeweaver-core": "^0.10.4",
|
|
51
|
+
"@rexeus/typeweaver-gen": "^0.10.4"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"get-port": "^7.2.0",
|
|
55
55
|
"test-utils": "file:../test-utils",
|
|
56
56
|
"tsx": "^4.21.0",
|
|
57
|
-
"@rexeus/typeweaver-core": "^0.10.
|
|
58
|
-
"@rexeus/typeweaver-gen": "^0.10.
|
|
57
|
+
"@rexeus/typeweaver-core": "^0.10.4",
|
|
58
|
+
"@rexeus/typeweaver-gen": "^0.10.4"
|
|
59
|
+
},
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"polycase": "^1.1.0"
|
|
59
62
|
},
|
|
60
63
|
"scripts": {
|
|
61
64
|
"typecheck": "tsc --noEmit -p tsconfig.typecheck.json",
|