@whatwg-node/server 0.9.1 → 0.9.2-alpha-20230702144225-cd4a797
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/cjs/createServerAdapter.js +66 -135
- package/cjs/index.js +3 -1
- package/cjs/internal-plugins/useFetchEvent.js +36 -0
- package/cjs/internal-plugins/useNodeAdapter.js +236 -0
- package/cjs/{uwebsockets.js → internal-plugins/useUWSAdapter.js} +34 -1
- package/cjs/utils.js +9 -206
- package/esm/createServerAdapter.js +67 -136
- package/esm/index.js +3 -1
- package/esm/internal-plugins/useFetchEvent.js +31 -0
- package/esm/internal-plugins/useNodeAdapter.js +227 -0
- package/esm/{uwebsockets.js → internal-plugins/useUWSAdapter.js} +32 -0
- package/esm/utils.js +7 -199
- package/package.json +1 -1
- package/typings/index.d.cts +3 -1
- package/typings/index.d.ts +3 -1
- package/typings/internal-plugins/useFetchEvent.d.cts +4 -0
- package/typings/internal-plugins/useFetchEvent.d.ts +4 -0
- package/typings/internal-plugins/useNodeAdapter.d.cts +34 -0
- package/typings/internal-plugins/useNodeAdapter.d.ts +34 -0
- package/typings/{uwebsockets.d.ts → internal-plugins/useUWSAdapter.d.cts} +7 -1
- package/typings/{uwebsockets.d.cts → internal-plugins/useUWSAdapter.d.ts} +7 -1
- package/typings/plugins/types.d.cts +13 -0
- package/typings/plugins/types.d.ts +13 -0
- package/typings/types.d.cts +3 -8
- package/typings/types.d.ts +3 -8
- package/typings/utils.d.cts +1 -29
- package/typings/utils.d.ts +1 -29
|
@@ -4,32 +4,10 @@ exports.createServerAdapter = void 0;
|
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
/* eslint-disable @typescript-eslint/ban-types */
|
|
6
6
|
const DefaultFetchAPI = tslib_1.__importStar(require("@whatwg-node/fetch"));
|
|
7
|
+
const useFetchEvent_js_1 = require("./internal-plugins/useFetchEvent.js");
|
|
8
|
+
const useNodeAdapter_js_1 = require("./internal-plugins/useNodeAdapter.js");
|
|
9
|
+
const useUWSAdapter_js_1 = require("./internal-plugins/useUWSAdapter.js");
|
|
7
10
|
const utils_js_1 = require("./utils.js");
|
|
8
|
-
const uwebsockets_js_1 = require("./uwebsockets.js");
|
|
9
|
-
async function handleWaitUntils(waitUntilPromises) {
|
|
10
|
-
const waitUntils = await Promise.allSettled(waitUntilPromises);
|
|
11
|
-
waitUntils.forEach(waitUntil => {
|
|
12
|
-
if (waitUntil.status === 'rejected') {
|
|
13
|
-
console.error(waitUntil.reason);
|
|
14
|
-
}
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
// Required for envs like nextjs edge runtime
|
|
18
|
-
function isRequestAccessible(serverContext) {
|
|
19
|
-
try {
|
|
20
|
-
return !!serverContext?.request;
|
|
21
|
-
}
|
|
22
|
-
catch {
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
function addWaitUntil(serverContext, waitUntilPromises) {
|
|
27
|
-
serverContext['waitUntil'] = function (promise) {
|
|
28
|
-
if (promise != null) {
|
|
29
|
-
waitUntilPromises.push(promise);
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
11
|
const EMPTY_OBJECT = {};
|
|
34
12
|
function createServerAdapter(serverAdapterBaseObject, options) {
|
|
35
13
|
const fetchAPI = {
|
|
@@ -39,16 +17,20 @@ function createServerAdapter(serverAdapterBaseObject, options) {
|
|
|
39
17
|
const givenHandleRequest = typeof serverAdapterBaseObject === 'function'
|
|
40
18
|
? serverAdapterBaseObject
|
|
41
19
|
: serverAdapterBaseObject.handle;
|
|
20
|
+
const onRequestAdaptHooks = [];
|
|
42
21
|
const onRequestHooks = [];
|
|
43
22
|
const onResponseHooks = [];
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
23
|
+
const plugins = options?.plugins ?? [];
|
|
24
|
+
plugins.push((0, useUWSAdapter_js_1.useUWSAdapter)(), (0, useNodeAdapter_js_1.useNodeAdapter)(), (0, useFetchEvent_js_1.useFetchEvent)());
|
|
25
|
+
for (const plugin of plugins) {
|
|
26
|
+
if (plugin.onRequestAdapt) {
|
|
27
|
+
onRequestAdaptHooks.push(plugin.onRequestAdapt);
|
|
28
|
+
}
|
|
29
|
+
if (plugin.onRequest) {
|
|
30
|
+
onRequestHooks.push(plugin.onRequest);
|
|
31
|
+
}
|
|
32
|
+
if (plugin.onResponse) {
|
|
33
|
+
onResponseHooks.push(plugin.onResponse);
|
|
52
34
|
}
|
|
53
35
|
}
|
|
54
36
|
async function handleRequest(request, serverContext) {
|
|
@@ -60,6 +42,16 @@ function createServerAdapter(serverAdapterBaseObject, options) {
|
|
|
60
42
|
});
|
|
61
43
|
let requestHandler = givenHandleRequest;
|
|
62
44
|
let response;
|
|
45
|
+
let waitUntilPromises;
|
|
46
|
+
if (serverContext['waitUntil'] == null) {
|
|
47
|
+
waitUntilPromises = new Set();
|
|
48
|
+
serverContext['waitUntil'] = (promise) => {
|
|
49
|
+
waitUntilPromises.add(promise);
|
|
50
|
+
promise.then(() => {
|
|
51
|
+
waitUntilPromises.delete(promise);
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
}
|
|
63
55
|
for (const onRequestHook of onRequestHooks) {
|
|
64
56
|
await onRequestHook({
|
|
65
57
|
request,
|
|
@@ -81,6 +73,12 @@ function createServerAdapter(serverAdapterBaseObject, options) {
|
|
|
81
73
|
if (!response) {
|
|
82
74
|
response = await requestHandler(request, serverContext);
|
|
83
75
|
}
|
|
76
|
+
if (!response) {
|
|
77
|
+
response = new fetchAPI.Response(undefined, {
|
|
78
|
+
status: 404,
|
|
79
|
+
statusText: 'Not Found',
|
|
80
|
+
});
|
|
81
|
+
}
|
|
84
82
|
for (const onResponseHook of onResponseHooks) {
|
|
85
83
|
await onResponseHook({
|
|
86
84
|
request,
|
|
@@ -88,125 +86,58 @@ function createServerAdapter(serverAdapterBaseObject, options) {
|
|
|
88
86
|
serverContext,
|
|
89
87
|
});
|
|
90
88
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
async function requestListener(nodeRequest, serverResponse, ...ctx) {
|
|
99
|
-
const waitUntilPromises = [];
|
|
100
|
-
const defaultServerContext = {
|
|
101
|
-
req: nodeRequest,
|
|
102
|
-
res: serverResponse,
|
|
103
|
-
};
|
|
104
|
-
addWaitUntil(defaultServerContext, waitUntilPromises);
|
|
105
|
-
const response = await handleNodeRequest(nodeRequest, defaultServerContext, ...ctx);
|
|
106
|
-
if (response) {
|
|
107
|
-
await (0, utils_js_1.sendNodeResponse)(response, serverResponse, nodeRequest);
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
await new Promise(resolve => {
|
|
111
|
-
serverResponse.statusCode = 404;
|
|
112
|
-
serverResponse.once('end', resolve);
|
|
113
|
-
serverResponse.end();
|
|
89
|
+
if (waitUntilPromises?.size) {
|
|
90
|
+
const waitUntils = await Promise.allSettled(waitUntilPromises);
|
|
91
|
+
waitUntils.forEach(waitUntil => {
|
|
92
|
+
if (waitUntil.status === 'rejected') {
|
|
93
|
+
console.error(waitUntil.reason);
|
|
94
|
+
}
|
|
114
95
|
});
|
|
115
96
|
}
|
|
116
|
-
|
|
117
|
-
await handleWaitUntils(waitUntilPromises);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
async function handleUWS(res, req, ...ctx) {
|
|
121
|
-
const waitUntilPromises = [];
|
|
122
|
-
const defaultServerContext = {
|
|
123
|
-
res,
|
|
124
|
-
req,
|
|
125
|
-
};
|
|
126
|
-
addWaitUntil(defaultServerContext, waitUntilPromises);
|
|
127
|
-
const serverContext = ctx.length > 0 ? (0, utils_js_1.completeAssign)(defaultServerContext, ...ctx) : defaultServerContext;
|
|
128
|
-
const request = (0, uwebsockets_js_1.getRequestFromUWSRequest)({
|
|
129
|
-
req,
|
|
130
|
-
res,
|
|
131
|
-
fetchAPI,
|
|
132
|
-
});
|
|
133
|
-
const response = await handleRequest(request, serverContext);
|
|
134
|
-
if (!response) {
|
|
135
|
-
res.writeStatus('404 Not Found');
|
|
136
|
-
res.end();
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
return (0, uwebsockets_js_1.sendResponseToUwsOpts)({
|
|
140
|
-
response,
|
|
141
|
-
res,
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
function handleEvent(event, ...ctx) {
|
|
145
|
-
if (!event.respondWith || !event.request) {
|
|
146
|
-
throw new TypeError(`Expected FetchEvent, got ${event}`);
|
|
147
|
-
}
|
|
148
|
-
const serverContext = ctx.length > 0 ? Object.assign({}, event, ...ctx) : event;
|
|
149
|
-
const response$ = handleRequest(event.request, serverContext);
|
|
150
|
-
event.respondWith(response$);
|
|
151
|
-
}
|
|
152
|
-
function handleRequestWithWaitUntil(request, ...ctx) {
|
|
153
|
-
const serverContext = (ctx.length > 1 ? (0, utils_js_1.completeAssign)(...ctx) : ctx[0]) || {};
|
|
154
|
-
if (serverContext.waitUntil == null) {
|
|
155
|
-
const waitUntilPromises = [];
|
|
156
|
-
addWaitUntil(serverContext, waitUntilPromises);
|
|
157
|
-
const response$ = handleRequest(request, serverContext);
|
|
158
|
-
if (waitUntilPromises.length > 0) {
|
|
159
|
-
return handleWaitUntils(waitUntilPromises).then(() => response$);
|
|
160
|
-
}
|
|
161
|
-
return response$;
|
|
162
|
-
}
|
|
163
|
-
return handleRequest(request, serverContext);
|
|
97
|
+
return response;
|
|
164
98
|
}
|
|
165
99
|
const fetchFn = (input, ...maybeCtx) => {
|
|
166
100
|
if (typeof input === 'string' || 'href' in input) {
|
|
167
101
|
const [initOrCtx, ...restOfCtx] = maybeCtx;
|
|
168
102
|
if ((0, utils_js_1.isRequestInit)(initOrCtx)) {
|
|
169
|
-
|
|
103
|
+
const serverContext = restOfCtx.length > 0 ? (0, utils_js_1.completeAssign)(...restOfCtx) : {};
|
|
104
|
+
return handleRequest(new fetchAPI.Request(input, initOrCtx), serverContext);
|
|
170
105
|
}
|
|
171
|
-
|
|
106
|
+
const serverContext = maybeCtx.length > 0 ? (0, utils_js_1.completeAssign)(...maybeCtx) : {};
|
|
107
|
+
return handleRequest(new fetchAPI.Request(input), serverContext);
|
|
172
108
|
}
|
|
173
|
-
|
|
109
|
+
const serverContext = maybeCtx.length > 0 ? (0, utils_js_1.completeAssign)(...maybeCtx) : {};
|
|
110
|
+
return handleRequest(input, serverContext);
|
|
174
111
|
};
|
|
175
|
-
const genericRequestHandler = (
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
112
|
+
const genericRequestHandler = (...args) => {
|
|
113
|
+
let request;
|
|
114
|
+
let serverContext;
|
|
115
|
+
for (const onRequestAdapt of onRequestAdaptHooks) {
|
|
116
|
+
onRequestAdapt({
|
|
117
|
+
args,
|
|
118
|
+
setRequest(newRequest) {
|
|
119
|
+
request = newRequest;
|
|
120
|
+
},
|
|
121
|
+
setServerContext(newServerContext) {
|
|
122
|
+
serverContext = newServerContext;
|
|
123
|
+
},
|
|
124
|
+
fetchAPI,
|
|
125
|
+
});
|
|
189
126
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
if ((0, utils_js_1.isFetchEvent)(input)) {
|
|
194
|
-
return handleEvent(input, ...maybeCtx);
|
|
127
|
+
if (request) {
|
|
128
|
+
if (!serverContext) {
|
|
129
|
+
serverContext = {};
|
|
195
130
|
}
|
|
196
|
-
|
|
197
|
-
return handleRequestWithWaitUntil(input.request, input, ...maybeCtx);
|
|
131
|
+
return handleRequest(request, serverContext);
|
|
198
132
|
}
|
|
199
|
-
|
|
200
|
-
// Then ctx is present and it is the context
|
|
201
|
-
return fetchFn(input, ...maybeCtx);
|
|
133
|
+
return fetchFn(...args);
|
|
202
134
|
};
|
|
203
135
|
const adapterObj = {
|
|
204
136
|
handleRequest,
|
|
205
137
|
fetch: fetchFn,
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
handleEvent,
|
|
209
|
-
handleUWS,
|
|
138
|
+
requestListener: genericRequestHandler,
|
|
139
|
+
handleNodeRequest: genericRequestHandler,
|
|
140
|
+
handleEvent: genericRequestHandler,
|
|
210
141
|
handle: genericRequestHandler,
|
|
211
142
|
};
|
|
212
143
|
const serverAdapter = new Proxy(genericRequestHandler, {
|
package/cjs/index.js
CHANGED
|
@@ -8,6 +8,8 @@ tslib_1.__exportStar(require("./utils.js"), exports);
|
|
|
8
8
|
tslib_1.__exportStar(require("./plugins/types.js"), exports);
|
|
9
9
|
tslib_1.__exportStar(require("./plugins/useCors.js"), exports);
|
|
10
10
|
tslib_1.__exportStar(require("./plugins/useErrorHandling.js"), exports);
|
|
11
|
-
tslib_1.__exportStar(require("./
|
|
11
|
+
tslib_1.__exportStar(require("./internal-plugins/useFetchEvent.js"), exports);
|
|
12
|
+
tslib_1.__exportStar(require("./internal-plugins/useNodeAdapter.js"), exports);
|
|
13
|
+
tslib_1.__exportStar(require("./internal-plugins/useUWSAdapter.js"), exports);
|
|
12
14
|
var fetch_1 = require("@whatwg-node/fetch");
|
|
13
15
|
Object.defineProperty(exports, "Response", { enumerable: true, get: function () { return fetch_1.Response; } });
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useFetchEvent = exports.isFetchEvent = void 0;
|
|
4
|
+
const utils_js_1 = require("../utils.js");
|
|
5
|
+
function isFetchEvent(event) {
|
|
6
|
+
return event != null && event.request != null && event.respondWith != null;
|
|
7
|
+
}
|
|
8
|
+
exports.isFetchEvent = isFetchEvent;
|
|
9
|
+
// Required for envs like nextjs edge runtime
|
|
10
|
+
function isRequestAccessible(serverContext) {
|
|
11
|
+
try {
|
|
12
|
+
return !!serverContext?.request;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function useFetchEvent() {
|
|
19
|
+
const eventMap = new WeakMap();
|
|
20
|
+
return {
|
|
21
|
+
onRequestAdapt({ args: [event, ...restOfCtx], setRequest, setServerContext }) {
|
|
22
|
+
if (isRequestAccessible(event)) {
|
|
23
|
+
setRequest(event.request);
|
|
24
|
+
const serverContext = restOfCtx.length > 0 ? (0, utils_js_1.completeAssign)(...restOfCtx) : event;
|
|
25
|
+
setServerContext(serverContext);
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
onResponse({ request, response }) {
|
|
29
|
+
const event = eventMap.get(request);
|
|
30
|
+
if (isFetchEvent(event)) {
|
|
31
|
+
event.respondWith(response);
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
exports.useFetchEvent = useFetchEvent;
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sendNodeResponse = exports.normalizeNodeRequest = exports.isServerResponse = exports.isNodeRequest = exports.isReadable = exports.useNodeAdapter = void 0;
|
|
4
|
+
const fetch_1 = require("@whatwg-node/fetch");
|
|
5
|
+
const utils_js_1 = require("../utils.js");
|
|
6
|
+
function useNodeAdapter() {
|
|
7
|
+
const nodeResponseMap = new WeakMap();
|
|
8
|
+
return {
|
|
9
|
+
onRequestAdapt({ args: [req, res, ...restOfCtx], setRequest, setServerContext, fetchAPI }) {
|
|
10
|
+
if (isNodeRequest(req)) {
|
|
11
|
+
const defaultServerContext = {
|
|
12
|
+
req,
|
|
13
|
+
};
|
|
14
|
+
const request = normalizeNodeRequest(req, fetchAPI.Request);
|
|
15
|
+
setRequest(request);
|
|
16
|
+
let ctxParams = restOfCtx;
|
|
17
|
+
if (isServerResponse(res)) {
|
|
18
|
+
defaultServerContext.res = res;
|
|
19
|
+
nodeResponseMap.set(request, res);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
ctxParams = [res, ...restOfCtx];
|
|
23
|
+
}
|
|
24
|
+
const serverContext = ctxParams.length > 0 ? (0, utils_js_1.completeAssign)(...ctxParams) : defaultServerContext;
|
|
25
|
+
setServerContext(serverContext);
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
onResponse({ request, response }) {
|
|
29
|
+
const nodeResponse = nodeResponseMap.get(request);
|
|
30
|
+
if (nodeResponse) {
|
|
31
|
+
return sendNodeResponse(response, nodeResponse);
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
exports.useNodeAdapter = useNodeAdapter;
|
|
37
|
+
function isReadable(stream) {
|
|
38
|
+
return stream.read != null;
|
|
39
|
+
}
|
|
40
|
+
exports.isReadable = isReadable;
|
|
41
|
+
function isNodeRequest(request) {
|
|
42
|
+
return isReadable(request);
|
|
43
|
+
}
|
|
44
|
+
exports.isNodeRequest = isNodeRequest;
|
|
45
|
+
function isServerResponse(stream) {
|
|
46
|
+
// Check all used functions are defined
|
|
47
|
+
return (stream != null &&
|
|
48
|
+
stream.setHeader != null &&
|
|
49
|
+
stream.end != null &&
|
|
50
|
+
stream.once != null &&
|
|
51
|
+
stream.write != null);
|
|
52
|
+
}
|
|
53
|
+
exports.isServerResponse = isServerResponse;
|
|
54
|
+
function getPort(nodeRequest) {
|
|
55
|
+
if (nodeRequest.socket?.localPort) {
|
|
56
|
+
return nodeRequest.socket?.localPort;
|
|
57
|
+
}
|
|
58
|
+
const hostInHeader = nodeRequest.headers?.[':authority'] || nodeRequest.headers?.host;
|
|
59
|
+
const portInHeader = hostInHeader?.split(':')?.[1];
|
|
60
|
+
if (portInHeader) {
|
|
61
|
+
return portInHeader;
|
|
62
|
+
}
|
|
63
|
+
return 80;
|
|
64
|
+
}
|
|
65
|
+
function getHostnameWithPort(nodeRequest) {
|
|
66
|
+
if (nodeRequest.headers?.[':authority']) {
|
|
67
|
+
return nodeRequest.headers?.[':authority'];
|
|
68
|
+
}
|
|
69
|
+
if (nodeRequest.headers?.host) {
|
|
70
|
+
return nodeRequest.headers?.host;
|
|
71
|
+
}
|
|
72
|
+
const port = getPort(nodeRequest);
|
|
73
|
+
if (nodeRequest.hostname) {
|
|
74
|
+
return nodeRequest.hostname + ':' + port;
|
|
75
|
+
}
|
|
76
|
+
const localIp = nodeRequest.socket?.localAddress;
|
|
77
|
+
if (localIp && !localIp?.includes('::') && !localIp?.includes('ffff')) {
|
|
78
|
+
return `${localIp}:${port}`;
|
|
79
|
+
}
|
|
80
|
+
return 'localhost';
|
|
81
|
+
}
|
|
82
|
+
function buildFullUrl(nodeRequest) {
|
|
83
|
+
const hostnameWithPort = getHostnameWithPort(nodeRequest);
|
|
84
|
+
const protocol = nodeRequest.protocol || 'http';
|
|
85
|
+
const endpoint = nodeRequest.originalUrl || nodeRequest.url || '/graphql';
|
|
86
|
+
return `${protocol}://${hostnameWithPort}${endpoint}`;
|
|
87
|
+
}
|
|
88
|
+
function isRequestBody(body) {
|
|
89
|
+
const stringTag = body[Symbol.toStringTag];
|
|
90
|
+
if (typeof body === 'string' ||
|
|
91
|
+
stringTag === 'Uint8Array' ||
|
|
92
|
+
stringTag === 'Blob' ||
|
|
93
|
+
stringTag === 'FormData' ||
|
|
94
|
+
stringTag === 'URLSearchParams' ||
|
|
95
|
+
(0, utils_js_1.isAsyncIterable)(body)) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
function normalizeNodeRequest(nodeRequest, RequestCtor) {
|
|
101
|
+
const rawRequest = nodeRequest.raw || nodeRequest.req || nodeRequest;
|
|
102
|
+
let fullUrl = buildFullUrl(rawRequest);
|
|
103
|
+
if (nodeRequest.query) {
|
|
104
|
+
const url = new fetch_1.URL(fullUrl);
|
|
105
|
+
for (const key in nodeRequest.query) {
|
|
106
|
+
url.searchParams.set(key, nodeRequest.query[key]);
|
|
107
|
+
}
|
|
108
|
+
fullUrl = url.toString();
|
|
109
|
+
}
|
|
110
|
+
if (nodeRequest.method === 'GET' || nodeRequest.method === 'HEAD') {
|
|
111
|
+
return new RequestCtor(fullUrl, {
|
|
112
|
+
method: nodeRequest.method,
|
|
113
|
+
headers: nodeRequest.headers,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Some Node server frameworks like Serverless Express sends a dummy object with body but as a Buffer not string
|
|
118
|
+
* so we do those checks to see is there something we can use directly as BodyInit
|
|
119
|
+
* because the presence of body means the request stream is already consumed and,
|
|
120
|
+
* rawRequest cannot be used as BodyInit/ReadableStream by Fetch API in this case.
|
|
121
|
+
*/
|
|
122
|
+
const maybeParsedBody = nodeRequest.body;
|
|
123
|
+
if (maybeParsedBody != null && Object.keys(maybeParsedBody).length > 0) {
|
|
124
|
+
if (isRequestBody(maybeParsedBody)) {
|
|
125
|
+
return new RequestCtor(fullUrl, {
|
|
126
|
+
method: nodeRequest.method,
|
|
127
|
+
headers: nodeRequest.headers,
|
|
128
|
+
body: maybeParsedBody,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
const request = new RequestCtor(fullUrl, {
|
|
132
|
+
method: nodeRequest.method,
|
|
133
|
+
headers: nodeRequest.headers,
|
|
134
|
+
});
|
|
135
|
+
if (!request.headers.get('content-type')?.includes('json')) {
|
|
136
|
+
request.headers.set('content-type', 'application/json; charset=utf-8');
|
|
137
|
+
}
|
|
138
|
+
return new Proxy(request, {
|
|
139
|
+
get: (target, prop, receiver) => {
|
|
140
|
+
switch (prop) {
|
|
141
|
+
case 'json':
|
|
142
|
+
return async () => maybeParsedBody;
|
|
143
|
+
case 'text':
|
|
144
|
+
return async () => JSON.stringify(maybeParsedBody);
|
|
145
|
+
default:
|
|
146
|
+
return Reflect.get(target, prop, receiver);
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
// perf: instead of spreading the object, we can just pass it as is and it performs better
|
|
152
|
+
return new RequestCtor(fullUrl, {
|
|
153
|
+
method: nodeRequest.method,
|
|
154
|
+
headers: nodeRequest.headers,
|
|
155
|
+
body: rawRequest,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
exports.normalizeNodeRequest = normalizeNodeRequest;
|
|
159
|
+
function configureSocket(rawRequest) {
|
|
160
|
+
rawRequest?.socket?.setTimeout?.(0);
|
|
161
|
+
rawRequest?.socket?.setNoDelay?.(true);
|
|
162
|
+
rawRequest?.socket?.setKeepAlive?.(true);
|
|
163
|
+
}
|
|
164
|
+
function endResponse(serverResponse) {
|
|
165
|
+
// @ts-expect-error Avoid arguments adaptor trampoline https://v8.dev/blog/adaptor-frame
|
|
166
|
+
serverResponse.end(null, null, null);
|
|
167
|
+
}
|
|
168
|
+
function getHeaderPairs(headers) {
|
|
169
|
+
const headerPairs = new Map();
|
|
170
|
+
headers.forEach((value, key) => {
|
|
171
|
+
let headerValues = headerPairs.get(key);
|
|
172
|
+
if (headerValues === undefined) {
|
|
173
|
+
headerValues = [];
|
|
174
|
+
headerPairs.set(key, headerValues);
|
|
175
|
+
}
|
|
176
|
+
if (key === 'set-cookie') {
|
|
177
|
+
const setCookies = headers.getSetCookie?.();
|
|
178
|
+
if (setCookies) {
|
|
179
|
+
setCookies.forEach(setCookie => {
|
|
180
|
+
headerValues.push(setCookie);
|
|
181
|
+
});
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
headerValues.push(value);
|
|
186
|
+
});
|
|
187
|
+
return headerPairs;
|
|
188
|
+
}
|
|
189
|
+
async function sendAsyncIterable(serverResponse, asyncIterable) {
|
|
190
|
+
for await (const chunk of asyncIterable) {
|
|
191
|
+
if (!serverResponse
|
|
192
|
+
// @ts-expect-error http and http2 writes are actually compatible
|
|
193
|
+
.write(chunk)) {
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
endResponse(serverResponse);
|
|
198
|
+
}
|
|
199
|
+
function sendNodeResponse(fetchResponse, serverResponse) {
|
|
200
|
+
const headerPairs = getHeaderPairs(fetchResponse.headers);
|
|
201
|
+
serverResponse.writeHead(fetchResponse.status, fetchResponse.statusText, Object.fromEntries(headerPairs.entries()));
|
|
202
|
+
// Optimizations for node-fetch
|
|
203
|
+
if (fetchResponse.bodyType === 'Buffer' ||
|
|
204
|
+
fetchResponse.bodyType === 'String' ||
|
|
205
|
+
fetchResponse.bodyType === 'Uint8Array') {
|
|
206
|
+
// @ts-expect-error http and http2 writes are actually compatible
|
|
207
|
+
serverResponse.write(fetchResponse.bodyInit);
|
|
208
|
+
endResponse(serverResponse);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
// Other fetch implementations
|
|
212
|
+
const fetchBody = fetchResponse.body;
|
|
213
|
+
if (fetchBody == null) {
|
|
214
|
+
endResponse(serverResponse);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
if (fetchBody[Symbol.toStringTag] === 'Uint8Array') {
|
|
218
|
+
serverResponse
|
|
219
|
+
// @ts-expect-error http and http2 writes are actually compatible
|
|
220
|
+
.write(fetchBody);
|
|
221
|
+
endResponse(serverResponse);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
configureSocket(serverResponse.req);
|
|
225
|
+
if (isReadable(fetchBody)) {
|
|
226
|
+
serverResponse.once('close', () => {
|
|
227
|
+
fetchBody.destroy();
|
|
228
|
+
});
|
|
229
|
+
fetchBody.pipe(serverResponse);
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
if ((0, utils_js_1.isAsyncIterable)(fetchBody)) {
|
|
233
|
+
return sendAsyncIterable(serverResponse, fetchBody);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
exports.sendNodeResponse = sendNodeResponse;
|
|
@@ -1,6 +1,39 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.sendResponseToUwsOpts = exports.getRequestFromUWSRequest = exports.isUWSResponse = void 0;
|
|
3
|
+
exports.sendResponseToUwsOpts = exports.getRequestFromUWSRequest = exports.isUWSResponse = exports.useUWSAdapter = void 0;
|
|
4
|
+
const utils_js_1 = require("../utils.js");
|
|
5
|
+
function useUWSAdapter() {
|
|
6
|
+
const uwsResponseMap = new WeakMap();
|
|
7
|
+
return {
|
|
8
|
+
onRequestAdapt({ args: [res, req, ...restOfCtx], setRequest, setServerContext, fetchAPI }) {
|
|
9
|
+
if (isUWSResponse(res)) {
|
|
10
|
+
const request = getRequestFromUWSRequest({
|
|
11
|
+
req: req,
|
|
12
|
+
res,
|
|
13
|
+
fetchAPI,
|
|
14
|
+
});
|
|
15
|
+
uwsResponseMap.set(request, res);
|
|
16
|
+
setRequest(request);
|
|
17
|
+
const defaultServerContext = {
|
|
18
|
+
req,
|
|
19
|
+
res,
|
|
20
|
+
};
|
|
21
|
+
const serverContext = restOfCtx.length > 0 ? (0, utils_js_1.completeAssign)(...restOfCtx) : defaultServerContext;
|
|
22
|
+
setServerContext(serverContext);
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
onResponse({ request, response }) {
|
|
26
|
+
const res = uwsResponseMap.get(request);
|
|
27
|
+
if (res) {
|
|
28
|
+
sendResponseToUwsOpts({
|
|
29
|
+
res,
|
|
30
|
+
response,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
exports.useUWSAdapter = useUWSAdapter;
|
|
4
37
|
function isUWSResponse(res) {
|
|
5
38
|
return !!res.onData;
|
|
6
39
|
}
|