@ripple-ts/adapter-node 0.2.210 → 0.2.212
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/CHANGELOG.md +21 -0
- package/LICENSE +21 -0
- package/README.md +3 -0
- package/package.json +6 -6
- package/src/index.js +28 -29
- package/tests/serve.test.js +27 -0
- package/types/index.d.ts +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @ripple-ts/adapter-node
|
|
2
2
|
|
|
3
|
+
## 0.2.212
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies []:
|
|
8
|
+
- @ripple-ts/adapter@0.2.212
|
|
9
|
+
|
|
10
|
+
## 0.2.211
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- [#694](https://github.com/Ripple-TS/ripple/pull/694)
|
|
15
|
+
[`fa285f4`](https://github.com/Ripple-TS/ripple/commit/fa285f441ab8d748c3dfea6adb463e3ca6d614b5)
|
|
16
|
+
Thanks [@trueadm](https://github.com/trueadm)! - Add a shared
|
|
17
|
+
`ServeStaticDirectoryOptions` type in `@ripple-ts/adapter` and update node/bun
|
|
18
|
+
adapters to consume it instead of redefining the same
|
|
19
|
+
`ServeStaticOptions & { dir?: string }` shape locally.
|
|
20
|
+
- Updated dependencies
|
|
21
|
+
[[`fa285f4`](https://github.com/Ripple-TS/ripple/commit/fa285f441ab8d748c3dfea6adb463e3ca6d614b5)]:
|
|
22
|
+
- @ripple-ts/adapter@0.2.211
|
|
23
|
+
|
|
3
24
|
## 0.2.210
|
|
4
25
|
|
|
5
26
|
### Patch Changes
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Dominic Gannaway
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# @ripple-ts/adapter-node
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@ripple-ts/adapter-node)
|
|
4
|
+
[](https://www.npmjs.com/package/@ripple-ts/adapter-node)
|
|
5
|
+
|
|
3
6
|
Node.js adapter for Ripple metaframework apps.
|
|
4
7
|
|
|
5
8
|
It bridges Node's `IncomingMessage`/`ServerResponse` API to Web
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Node.js adapter for Ripple metaframework (Web Request/Response bridge)",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Dominic Gannaway",
|
|
6
|
-
"version": "0.2.
|
|
6
|
+
"version": "0.2.212",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"module": "src/index.js",
|
|
9
9
|
"main": "src/index.js",
|
|
@@ -14,11 +14,8 @@
|
|
|
14
14
|
"default": "./src/index.js"
|
|
15
15
|
}
|
|
16
16
|
},
|
|
17
|
-
"scripts": {
|
|
18
|
-
"test": "pnpm -w test --project adapter-node"
|
|
19
|
-
},
|
|
20
17
|
"dependencies": {
|
|
21
|
-
"@ripple-ts/adapter": "
|
|
18
|
+
"@ripple-ts/adapter": "0.2.212"
|
|
22
19
|
},
|
|
23
20
|
"homepage": "https://ripplejs.com",
|
|
24
21
|
"repository": {
|
|
@@ -28,5 +25,8 @@
|
|
|
28
25
|
},
|
|
29
26
|
"bugs": {
|
|
30
27
|
"url": "https://github.com/Ripple-TS/ripple/issues"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"test": "pnpm -w test --project adapter-node"
|
|
31
31
|
}
|
|
32
|
-
}
|
|
32
|
+
}
|
package/src/index.js
CHANGED
|
@@ -30,18 +30,29 @@ function normalize_forwarded_value(value) {
|
|
|
30
30
|
/**
|
|
31
31
|
* @param {import('node:http').IncomingMessage} node_request
|
|
32
32
|
* @param {AbortSignal} signal
|
|
33
|
+
* @param {boolean} [trust_proxy=false]
|
|
33
34
|
* @returns {Request}
|
|
34
35
|
*/
|
|
35
|
-
function node_request_to_web_request(node_request, signal) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
36
|
+
function node_request_to_web_request(node_request, signal, trust_proxy = false) {
|
|
37
|
+
let proto = 'http';
|
|
38
|
+
let host = first_header_value(node_request.headers.host) ?? 'localhost';
|
|
39
|
+
|
|
40
|
+
if (trust_proxy) {
|
|
41
|
+
const forwarded_proto = normalize_forwarded_value(
|
|
42
|
+
first_header_value(node_request.headers['x-forwarded-proto']),
|
|
43
|
+
);
|
|
44
|
+
const forwarded_host = normalize_forwarded_value(
|
|
45
|
+
first_header_value(node_request.headers['x-forwarded-host']),
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
if (forwarded_proto) proto = forwarded_proto;
|
|
49
|
+
if (forwarded_host) host = forwarded_host;
|
|
50
|
+
} else {
|
|
51
|
+
// Derive protocol from the socket when not trusting proxy headers
|
|
52
|
+
if (/** @type {import('node:tls').TLSSocket} */ (node_request.socket).encrypted) {
|
|
53
|
+
proto = 'https';
|
|
54
|
+
}
|
|
55
|
+
}
|
|
45
56
|
|
|
46
57
|
const raw_url = node_request.url ?? '/';
|
|
47
58
|
const base = `${proto}://${host}`;
|
|
@@ -114,21 +125,6 @@ function web_response_to_node_response(web_response, node_response, request_meth
|
|
|
114
125
|
node_stream.pipe(node_response);
|
|
115
126
|
}
|
|
116
127
|
|
|
117
|
-
/** @typedef {import('@ripple-ts/adapter').ServeStaticDirectoryOptions} StaticServeOptions */
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* @typedef {{
|
|
121
|
-
* port?: number,
|
|
122
|
-
* hostname?: string,
|
|
123
|
-
* middleware?: ((
|
|
124
|
-
* req: import('node:http').IncomingMessage,
|
|
125
|
-
* res: import('node:http').ServerResponse,
|
|
126
|
-
* next: (error?: any) => void
|
|
127
|
-
* ) => void) | null,
|
|
128
|
-
* static?: StaticServeOptions | false,
|
|
129
|
-
* }} ServeOptions
|
|
130
|
-
*/
|
|
131
|
-
|
|
132
128
|
/**
|
|
133
129
|
* @param {(req: import('node:http').IncomingMessage, res: import('node:http').ServerResponse, next: (error?: any) => void) => void} middleware
|
|
134
130
|
* @param {import('node:http').IncomingMessage} node_request
|
|
@@ -160,9 +156,7 @@ function run_node_middleware(middleware, node_request, node_response) {
|
|
|
160
156
|
}
|
|
161
157
|
|
|
162
158
|
/**
|
|
163
|
-
* @
|
|
164
|
-
* @param {ServeOptions} [options]
|
|
165
|
-
* @returns {{ listen: (port?: number) => import('node:http').Server, close: () => void }}
|
|
159
|
+
* @type {typeof import('@ripple-ts/adapter-node').serve}
|
|
166
160
|
*/
|
|
167
161
|
export function serve(fetch_handler, options = {}) {
|
|
168
162
|
const {
|
|
@@ -170,6 +164,7 @@ export function serve(fetch_handler, options = {}) {
|
|
|
170
164
|
hostname = DEFAULT_HOSTNAME,
|
|
171
165
|
middleware = null,
|
|
172
166
|
static: static_options = {},
|
|
167
|
+
trustProxy: trust_proxy = false,
|
|
173
168
|
} = options;
|
|
174
169
|
|
|
175
170
|
/** @type {ReturnType<typeof serveStatic> | null} */
|
|
@@ -191,7 +186,11 @@ export function serve(fetch_handler, options = {}) {
|
|
|
191
186
|
|
|
192
187
|
try {
|
|
193
188
|
const run_fetch_handler = async () => {
|
|
194
|
-
const request = node_request_to_web_request(
|
|
189
|
+
const request = node_request_to_web_request(
|
|
190
|
+
node_request,
|
|
191
|
+
abort_controller.signal,
|
|
192
|
+
trust_proxy,
|
|
193
|
+
);
|
|
195
194
|
return await fetch_handler(request, { node_request, node_response });
|
|
196
195
|
};
|
|
197
196
|
|
package/tests/serve.test.js
CHANGED
|
@@ -130,6 +130,7 @@ describe('@ripple-ts/adapter-node serve()', () => {
|
|
|
130
130
|
expect(response.headers.get('x-response')).toBe('ok');
|
|
131
131
|
expect(await response.text()).toBe('created');
|
|
132
132
|
},
|
|
133
|
+
{ trustProxy: true },
|
|
133
134
|
);
|
|
134
135
|
|
|
135
136
|
expect(captured).toEqual({
|
|
@@ -140,6 +141,32 @@ describe('@ripple-ts/adapter-node serve()', () => {
|
|
|
140
141
|
});
|
|
141
142
|
});
|
|
142
143
|
|
|
144
|
+
it('ignores x-forwarded-* headers when trustProxy is not enabled', async () => {
|
|
145
|
+
/** @type {{ url: string } | null} */
|
|
146
|
+
let captured = null;
|
|
147
|
+
|
|
148
|
+
await with_server(
|
|
149
|
+
async (request) => {
|
|
150
|
+
captured = { url: request.url };
|
|
151
|
+
return new Response('ok');
|
|
152
|
+
},
|
|
153
|
+
async (base_url) => {
|
|
154
|
+
await fetch(`${base_url}/test`, {
|
|
155
|
+
headers: {
|
|
156
|
+
'x-forwarded-proto': 'https',
|
|
157
|
+
'x-forwarded-host': 'evil.com',
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
},
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
// Should NOT use forwarded headers; URL should use http and the real host
|
|
164
|
+
expect(captured).not.toBeNull();
|
|
165
|
+
const url = new URL((captured ?? { url: '' }).url);
|
|
166
|
+
expect(url.protocol).toBe('http:');
|
|
167
|
+
expect(url.hostname).not.toBe('evil.com');
|
|
168
|
+
});
|
|
169
|
+
|
|
143
170
|
it('runs middleware before handler when middleware calls next()', async () => {
|
|
144
171
|
/** @type {string[]} */
|
|
145
172
|
const calls = [];
|
package/types/index.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
AdapterCoreOptions,
|
|
3
|
-
|
|
4
|
-
ServeResult,
|
|
3
|
+
ServeFunction,
|
|
5
4
|
ServeStaticOptions as BaseServeStaticOptions,
|
|
6
5
|
ServeStaticDirectoryOptions as BaseServeStaticDirectoryOptions,
|
|
7
6
|
} from '@ripple-ts/adapter';
|
|
@@ -25,13 +24,14 @@ export type StaticMiddleware = (
|
|
|
25
24
|
next: (error?: any) => void,
|
|
26
25
|
) => void;
|
|
27
26
|
|
|
28
|
-
export
|
|
29
|
-
|
|
27
|
+
export const serve: ServeFunction<
|
|
28
|
+
{
|
|
30
29
|
node_request: import('node:http').IncomingMessage;
|
|
31
30
|
node_response: import('node:http').ServerResponse;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
},
|
|
32
|
+
ServeOptions,
|
|
33
|
+
import('node:http').Server
|
|
34
|
+
>;
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
37
|
* Create a middleware that serves static files from a directory
|