@web-widget/node 3.0.0-beta.0 → 3.0.0-beta.3

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.
Files changed (2) hide show
  1. package/dist/node.js +95 -49
  2. 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);
@@ -147,8 +170,8 @@ function toMiddleware(webHandler, options) {
147
170
  const toRequest = buildToRequestWithHttp2Compat(dependencies);
148
171
  const toFetchEvent = buildToFetchEvent(dependencies);
149
172
  return async function middleware(incomingMessage, serverResponse, next) {
173
+ const request = toRequest(incomingMessage, options);
150
174
  try {
151
- const request = toRequest(incomingMessage, options);
152
175
  const env = process.env;
153
176
  const event = toFetchEvent(request);
154
177
  const webResponse = await webHandler(request, env, event);
@@ -157,24 +180,16 @@ function toMiddleware(webHandler, options) {
157
180
  await next();
158
181
  }
159
182
  } catch (error) {
160
- let responded = false;
161
- if (checkWritable(serverResponse) && !serverResponse.headersSent) {
162
- try {
163
- serverResponse.statusCode = 500;
164
- setHttp1StatusMessage(serverResponse, "Internal Server Error");
165
- serverResponse.setHeader("content-type", "text/plain; charset=utf-8");
166
- serverResponse.end(
167
- error instanceof Error ? error.message : "Internal Server Error"
168
- );
169
- responded = true;
170
- } catch {
171
- }
172
- }
173
- if (responded) {
183
+ if (sendServerError(
184
+ serverResponse,
185
+ error instanceof Error ? error.message : void 0
186
+ )) {
174
187
  next();
175
188
  } else {
176
189
  next(error);
177
190
  }
191
+ } finally {
192
+ cancelRequestBody(request);
178
193
  }
179
194
  };
180
195
  }
@@ -206,23 +221,49 @@ async function toServerResponse(webResponse, serverResponse) {
206
221
  await writeReadableStreamToWritable(webResponse.body, serverResponse);
207
222
  }
208
223
  async function writeReadableStreamToWritable(stream, writable) {
209
- let reader = stream.getReader();
210
- let flushable = writable;
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);
211
235
  try {
212
- while (true) {
213
- let { done, value } = await reader.read();
236
+ while (!writable.destroyed) {
237
+ const { done, value } = await reader.read();
214
238
  if (done) {
215
- writable.end();
239
+ if (!writable.destroyed) writable.end();
216
240
  break;
217
241
  }
218
- 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
+ }
219
251
  if (typeof flushable.flush === "function") {
220
252
  flushable.flush();
221
253
  }
222
254
  }
223
255
  } catch (error) {
224
- writable.destroy(error);
225
- throw error;
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
+ }
226
267
  }
227
268
  }
228
269
  function checkWritable(serverResponse) {
@@ -232,6 +273,27 @@ function checkWritable(serverResponse) {
232
273
  if (!socket) return true;
233
274
  return socket.writable;
234
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
+ }
235
297
  function buildToNodeHandler(dependencies2, options) {
236
298
  const toRequest = buildToRequestWithHttp2Compat(dependencies2);
237
299
  const toFetchEvent = buildToFetchEvent(dependencies2);
@@ -241,30 +303,14 @@ function buildToNodeHandler(dependencies2, options) {
241
303
  const env = process.env;
242
304
  const event = toFetchEvent(request);
243
305
  const maybePromise = webHandler(request, env, event);
306
+ const respond = (response) => toServerResponse(response, serverResponse).finally(
307
+ () => cancelRequestBody(request)
308
+ );
309
+ const fail = () => sendServerError(serverResponse);
244
310
  if (maybePromise instanceof Promise) {
245
- maybePromise.then((response) => toServerResponse(response, serverResponse)).catch(() => {
246
- if (checkWritable(serverResponse) && !serverResponse.headersSent) {
247
- serverResponse.statusCode = 500;
248
- setHttp1StatusMessage(serverResponse, "Internal Server Error");
249
- serverResponse.setHeader(
250
- "content-type",
251
- "text/plain; charset=utf-8"
252
- );
253
- serverResponse.end("Internal Server Error");
254
- }
255
- });
311
+ maybePromise.then(respond).catch(fail);
256
312
  } else {
257
- toServerResponse(maybePromise, serverResponse).catch(() => {
258
- if (checkWritable(serverResponse) && !serverResponse.headersSent) {
259
- serverResponse.statusCode = 500;
260
- setHttp1StatusMessage(serverResponse, "Internal Server Error");
261
- serverResponse.setHeader(
262
- "content-type",
263
- "text/plain; charset=utf-8"
264
- );
265
- serverResponse.end("Internal Server Error");
266
- }
267
- });
313
+ respond(maybePromise).catch(fail);
268
314
  }
269
315
  };
270
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": "3.0.0-beta.0",
4
+ "version": "3.0.0-beta.3",
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.5.4"
22
+ "typescript": "^5.9.3"
23
23
  },
24
24
  "engines": {
25
25
  "node": ">=18"