@web-widget/node 2.3.1 → 3.0.0-beta.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/dist/node.js +120 -56
- package/package.json +2 -2
package/dist/node.js
CHANGED
|
@@ -25,10 +25,7 @@ import {
|
|
|
25
25
|
import primitives2 from "@edge-runtime/primitives";
|
|
26
26
|
|
|
27
27
|
// src/incoming-request.ts
|
|
28
|
-
import {
|
|
29
|
-
buildToHeaders,
|
|
30
|
-
buildToReadableStream
|
|
31
|
-
} from "@edge-runtime/node-utils";
|
|
28
|
+
import { buildToHeaders } from "@edge-runtime/node-utils";
|
|
32
29
|
function isHttp2PseudoHeader(name) {
|
|
33
30
|
return name.startsWith(":");
|
|
34
31
|
}
|
|
@@ -76,6 +73,32 @@ function computeRequestOrigin(headers, defaultOrigin) {
|
|
|
76
73
|
}
|
|
77
74
|
return `${protocol}://${authority}`;
|
|
78
75
|
}
|
|
76
|
+
function buildToReadableStream(dependencies2) {
|
|
77
|
+
const { ReadableStream: ReadableStreamCtor, Uint8Array: Uint8ArrayCtor } = dependencies2;
|
|
78
|
+
return function toReadableStream(stream) {
|
|
79
|
+
return new ReadableStreamCtor({
|
|
80
|
+
start(controller) {
|
|
81
|
+
const onData = (chunk) => {
|
|
82
|
+
controller.enqueue(new Uint8ArrayCtor(chunk));
|
|
83
|
+
if (controller.desiredSize !== null && controller.desiredSize <= 0) {
|
|
84
|
+
stream.pause();
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
const onEnd = () => controller.close();
|
|
88
|
+
const onError = (err) => controller.error(err);
|
|
89
|
+
stream.on("data", onData);
|
|
90
|
+
stream.on("end", onEnd);
|
|
91
|
+
stream.on("error", onError);
|
|
92
|
+
},
|
|
93
|
+
pull() {
|
|
94
|
+
stream.resume();
|
|
95
|
+
},
|
|
96
|
+
cancel() {
|
|
97
|
+
stream.destroy();
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
}
|
|
79
102
|
function buildToRequestWithHttp2Compat(dependencies2) {
|
|
80
103
|
const toHeaders = buildToHeaders(dependencies2);
|
|
81
104
|
const toReadableStream = buildToReadableStream(dependencies2);
|
|
@@ -133,36 +156,40 @@ function addDuplexToInit(init) {
|
|
|
133
156
|
}
|
|
134
157
|
return init;
|
|
135
158
|
}
|
|
159
|
+
function isHttp2ServerResponse(serverResponse) {
|
|
160
|
+
if ("stream" in serverResponse && serverResponse.stream) return true;
|
|
161
|
+
const httpVersionMajor = serverResponse.httpVersionMajor;
|
|
162
|
+
return Number(httpVersionMajor) >= 2;
|
|
163
|
+
}
|
|
164
|
+
function setHttp1StatusMessage(serverResponse, statusMessage) {
|
|
165
|
+
if (!isHttp2ServerResponse(serverResponse) && statusMessage) {
|
|
166
|
+
serverResponse.statusMessage = statusMessage;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
136
169
|
function toMiddleware(webHandler, options) {
|
|
137
170
|
const toRequest = buildToRequestWithHttp2Compat(dependencies);
|
|
138
171
|
const toFetchEvent = buildToFetchEvent(dependencies);
|
|
139
172
|
return async function middleware(incomingMessage, serverResponse, next) {
|
|
173
|
+
const request = toRequest(incomingMessage, options);
|
|
140
174
|
try {
|
|
141
|
-
const request = toRequest(incomingMessage, options);
|
|
142
175
|
const env = process.env;
|
|
143
176
|
const event = toFetchEvent(request);
|
|
144
177
|
const webResponse = await webHandler(request, env, event);
|
|
145
178
|
await toServerResponse(webResponse, serverResponse);
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
let responded = false;
|
|
149
|
-
if (checkWritable(serverResponse) && !serverResponse.headersSent) {
|
|
150
|
-
try {
|
|
151
|
-
serverResponse.statusCode = 500;
|
|
152
|
-
serverResponse.statusMessage = "Internal Server Error";
|
|
153
|
-
serverResponse.setHeader("content-type", "text/plain; charset=utf-8");
|
|
154
|
-
serverResponse.end(
|
|
155
|
-
error instanceof Error ? error.message : "Internal Server Error"
|
|
156
|
-
);
|
|
157
|
-
responded = true;
|
|
158
|
-
} catch {
|
|
159
|
-
}
|
|
179
|
+
if (!serverResponse.headersSent && !serverResponse.writableEnded) {
|
|
180
|
+
await next();
|
|
160
181
|
}
|
|
161
|
-
|
|
182
|
+
} catch (error) {
|
|
183
|
+
if (sendServerError(
|
|
184
|
+
serverResponse,
|
|
185
|
+
error instanceof Error ? error.message : void 0
|
|
186
|
+
)) {
|
|
162
187
|
next();
|
|
163
188
|
} else {
|
|
164
189
|
next(error);
|
|
165
190
|
}
|
|
191
|
+
} finally {
|
|
192
|
+
cancelRequestBody(request);
|
|
166
193
|
}
|
|
167
194
|
};
|
|
168
195
|
}
|
|
@@ -174,14 +201,19 @@ async function toServerResponse(webResponse, serverResponse) {
|
|
|
174
201
|
serverResponse.end();
|
|
175
202
|
return;
|
|
176
203
|
}
|
|
204
|
+
const isHttp2 = isHttp2ServerResponse(serverResponse);
|
|
205
|
+
const headers = toOutgoingHeaders(webResponse.headers);
|
|
206
|
+
const status = webResponse.status;
|
|
177
207
|
if (!serverResponse.headersSent) {
|
|
178
|
-
mergeIntoServerResponse(
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
208
|
+
mergeIntoServerResponse(headers, serverResponse);
|
|
209
|
+
serverResponse.statusCode = status;
|
|
210
|
+
if (!isHttp2) {
|
|
211
|
+
setHttp1StatusMessage(serverResponse, webResponse.statusText);
|
|
212
|
+
}
|
|
213
|
+
} else if (!isHttp2) {
|
|
214
|
+
serverResponse.statusCode = status;
|
|
215
|
+
setHttp1StatusMessage(serverResponse, webResponse.statusText);
|
|
182
216
|
}
|
|
183
|
-
serverResponse.statusCode = webResponse.status;
|
|
184
|
-
serverResponse.statusMessage = webResponse.statusText;
|
|
185
217
|
if (!webResponse.body) {
|
|
186
218
|
serverResponse.end();
|
|
187
219
|
return;
|
|
@@ -189,31 +221,79 @@ async function toServerResponse(webResponse, serverResponse) {
|
|
|
189
221
|
await writeReadableStreamToWritable(webResponse.body, serverResponse);
|
|
190
222
|
}
|
|
191
223
|
async function writeReadableStreamToWritable(stream, writable) {
|
|
192
|
-
|
|
193
|
-
|
|
224
|
+
if (stream.locked) {
|
|
225
|
+
throw new TypeError("ReadableStream is locked.");
|
|
226
|
+
}
|
|
227
|
+
const reader = stream.getReader();
|
|
228
|
+
const flushable = writable;
|
|
229
|
+
const onAbort = () => {
|
|
230
|
+
reader.cancel().catch(() => {
|
|
231
|
+
});
|
|
232
|
+
};
|
|
233
|
+
writable.once("close", onAbort);
|
|
234
|
+
writable.once("error", onAbort);
|
|
194
235
|
try {
|
|
195
|
-
while (
|
|
196
|
-
|
|
236
|
+
while (!writable.destroyed) {
|
|
237
|
+
const { done, value } = await reader.read();
|
|
197
238
|
if (done) {
|
|
198
|
-
writable.end();
|
|
239
|
+
if (!writable.destroyed) writable.end();
|
|
199
240
|
break;
|
|
200
241
|
}
|
|
201
|
-
writable.write(value)
|
|
242
|
+
if (writable.write(value) === false) {
|
|
243
|
+
await new Promise((resolve) => {
|
|
244
|
+
if (writable.destroyed) {
|
|
245
|
+
resolve();
|
|
246
|
+
} else {
|
|
247
|
+
writable.once("drain", resolve);
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
}
|
|
202
251
|
if (typeof flushable.flush === "function") {
|
|
203
252
|
flushable.flush();
|
|
204
253
|
}
|
|
205
254
|
}
|
|
206
255
|
} catch (error) {
|
|
207
|
-
writable.
|
|
208
|
-
|
|
256
|
+
if (!writable.destroyed) {
|
|
257
|
+
writable.destroy(error instanceof Error ? error : void 0);
|
|
258
|
+
throw error;
|
|
259
|
+
}
|
|
260
|
+
} finally {
|
|
261
|
+
writable.removeListener("close", onAbort);
|
|
262
|
+
writable.removeListener("error", onAbort);
|
|
263
|
+
try {
|
|
264
|
+
reader.releaseLock();
|
|
265
|
+
} catch {
|
|
266
|
+
}
|
|
209
267
|
}
|
|
210
268
|
}
|
|
211
269
|
function checkWritable(serverResponse) {
|
|
212
270
|
if (serverResponse.writableEnded || serverResponse.finished) return false;
|
|
271
|
+
if ("stream" in serverResponse && serverResponse.stream) return true;
|
|
213
272
|
const socket = serverResponse.socket;
|
|
214
273
|
if (!socket) return true;
|
|
215
274
|
return socket.writable;
|
|
216
275
|
}
|
|
276
|
+
function sendServerError(serverResponse, message) {
|
|
277
|
+
if (!checkWritable(serverResponse) || serverResponse.headersSent) {
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
try {
|
|
281
|
+
serverResponse.statusCode = 500;
|
|
282
|
+
setHttp1StatusMessage(serverResponse, "Internal Server Error");
|
|
283
|
+
serverResponse.setHeader("content-type", "text/plain; charset=utf-8");
|
|
284
|
+
serverResponse.end(message || "Internal Server Error");
|
|
285
|
+
return true;
|
|
286
|
+
} catch {
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
function cancelRequestBody(request) {
|
|
291
|
+
const body = request.body;
|
|
292
|
+
if (body && !body.locked) {
|
|
293
|
+
body.cancel().catch(() => {
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
}
|
|
217
297
|
function buildToNodeHandler(dependencies2, options) {
|
|
218
298
|
const toRequest = buildToRequestWithHttp2Compat(dependencies2);
|
|
219
299
|
const toFetchEvent = buildToFetchEvent(dependencies2);
|
|
@@ -223,30 +303,14 @@ function buildToNodeHandler(dependencies2, options) {
|
|
|
223
303
|
const env = process.env;
|
|
224
304
|
const event = toFetchEvent(request);
|
|
225
305
|
const maybePromise = webHandler(request, env, event);
|
|
306
|
+
const respond = (response) => toServerResponse(response, serverResponse).finally(
|
|
307
|
+
() => cancelRequestBody(request)
|
|
308
|
+
);
|
|
309
|
+
const fail = () => sendServerError(serverResponse);
|
|
226
310
|
if (maybePromise instanceof Promise) {
|
|
227
|
-
maybePromise.then(
|
|
228
|
-
if (checkWritable(serverResponse) && !serverResponse.headersSent) {
|
|
229
|
-
serverResponse.statusCode = 500;
|
|
230
|
-
serverResponse.statusMessage = "Internal Server Error";
|
|
231
|
-
serverResponse.setHeader(
|
|
232
|
-
"content-type",
|
|
233
|
-
"text/plain; charset=utf-8"
|
|
234
|
-
);
|
|
235
|
-
serverResponse.end("Internal Server Error");
|
|
236
|
-
}
|
|
237
|
-
});
|
|
311
|
+
maybePromise.then(respond).catch(fail);
|
|
238
312
|
} else {
|
|
239
|
-
|
|
240
|
-
if (checkWritable(serverResponse) && !serverResponse.headersSent) {
|
|
241
|
-
serverResponse.statusCode = 500;
|
|
242
|
-
serverResponse.statusMessage = "Internal Server Error";
|
|
243
|
-
serverResponse.setHeader(
|
|
244
|
-
"content-type",
|
|
245
|
-
"text/plain; charset=utf-8"
|
|
246
|
-
);
|
|
247
|
-
serverResponse.end("Internal Server Error");
|
|
248
|
-
}
|
|
249
|
-
});
|
|
313
|
+
respond(maybePromise).catch(fail);
|
|
250
314
|
}
|
|
251
315
|
};
|
|
252
316
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@web-widget/node",
|
|
3
3
|
"description": "Node.js adapter",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "3.0.0-beta.2",
|
|
5
5
|
"homepage": "https://github.com/web-widget/web-widget#readme",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@types/node": "^22.2.0",
|
|
21
21
|
"tsup": "^8.3.4",
|
|
22
|
-
"typescript": "^5.
|
|
22
|
+
"typescript": "^5.9.3"
|
|
23
23
|
},
|
|
24
24
|
"engines": {
|
|
25
25
|
"node": ">=18"
|