@react-router/node 8.3.0 → 8.4.0

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 CHANGED
@@ -1,5 +1,23 @@
1
1
  # `@react-router/node`
2
2
 
3
+ ## v8.4.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Fix a memory leak in `writeReadableStreamToWritable` and `writeAsyncIterableToWritable` that retained memory for the lifetime of a long-running stream ([#15500](https://github.com/remix-run/react-router/pull/15500))
8
+ - Preserve error handling when a producer closes the destination while reading the next chunk
9
+ - Updated dependencies:
10
+ - [`react-router@8.4.0`](https://github.com/remix-run/react-router/releases/tag/react-router@8.4.0)
11
+
12
+ ## v8.3.1
13
+
14
+ ### Patch Changes
15
+
16
+ - Bump `@remix-run/node-fetch-server` dependency ([#15447](https://github.com/remix-run/react-router/pull/15447))
17
+ - fix: Prevent client disconnects during streaming from crashing the Node process ([#15324](https://github.com/remix-run/react-router/pull/15324))
18
+ - Updated dependencies:
19
+ - [`react-router@8.3.1`](https://github.com/remix-run/react-router/releases/tag/react-router@8.3.1)
20
+
3
21
  ## v8.3.0
4
22
 
5
23
  ### Patch Changes
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/node v8.3.0
2
+ * @react-router/node v8.4.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -123,9 +123,9 @@ async function writeReadableStreamToWritable(stream, writable) {
123
123
  }
124
124
  } catch (error) {
125
125
  try {
126
- reader.cancel(error).catch(() => {});
126
+ reader.cancel(error instanceof WritableClosedError ? void 0 : error).catch(() => {});
127
127
  } catch {}
128
- writable.destroy(error);
128
+ destroyWritable(writable, error);
129
129
  throw error;
130
130
  } finally {
131
131
  writableError.cleanup();
@@ -134,15 +134,20 @@ async function writeReadableStreamToWritable(stream, writable) {
134
134
  } catch {}
135
135
  }
136
136
  }
137
+ var WritableClosedError = class extends Error {};
138
+ function destroyWritable(writable, error) {
139
+ if (error instanceof WritableClosedError || writable.destroyed) return;
140
+ writable.once("error", () => {});
141
+ writable.destroy(error);
142
+ }
137
143
  function monitorWritableError(writable) {
144
+ let writableStartedDestroyed = writable.destroyed;
138
145
  let settled = false;
146
+ let writableErrorEmitted = false;
139
147
  let writableError;
140
- let rejectWritableError;
141
- let writableErrorPromise = new Promise((_, reject) => {
142
- rejectWritableError = reject;
143
- });
144
- writableErrorPromise.catch(() => {});
148
+ let pendingRejections = /* @__PURE__ */ new Set();
145
149
  function cleanup() {
150
+ if (!writableStartedDestroyed && writable.destroyed && writable.errored && !writableErrorEmitted) return;
146
151
  writable.off("error", onError);
147
152
  writable.off("close", onClose);
148
153
  }
@@ -151,24 +156,36 @@ function monitorWritableError(writable) {
151
156
  settled = true;
152
157
  writableError = error;
153
158
  cleanup();
154
- rejectWritableError(error);
159
+ let rejections = [...pendingRejections];
160
+ pendingRejections.clear();
161
+ for (let rejectPending of rejections) rejectPending(error);
155
162
  }
156
163
  function onError(error) {
164
+ writableErrorEmitted = true;
157
165
  reject(error);
158
166
  }
159
167
  function onClose() {
160
- reject(/* @__PURE__ */ new Error("Writable closed before stream finished"));
168
+ reject(new WritableClosedError("Writable closed before stream finished"));
161
169
  }
162
170
  writable.once("error", onError);
163
171
  writable.once("close", onClose);
164
172
  return {
165
173
  cleanup,
166
174
  race(promise) {
167
- return Promise.race([promise, writableErrorPromise]);
175
+ if (writableError) return Promise.race([promise, Promise.reject(writableError)]);
176
+ let rejectPending;
177
+ let pendingPromise = new Promise((_, reject) => {
178
+ rejectPending = reject;
179
+ });
180
+ pendingPromise.catch(() => {});
181
+ pendingRejections.add(rejectPending);
182
+ return Promise.race([promise, pendingPromise]).finally(() => {
183
+ pendingRejections.delete(rejectPending);
184
+ });
168
185
  },
169
186
  throwIfClosed() {
170
187
  if (writableError) throw writableError;
171
- if (writable.destroyed || writable.writableEnded) throw new Error("Cannot write to a destroyed or ended writable stream");
188
+ if (writable.destroyed || writable.writableEnded) throw new WritableClosedError("Cannot write to a destroyed or ended writable stream");
172
189
  }
173
190
  };
174
191
  }
@@ -206,7 +223,7 @@ async function writeAsyncIterableToWritable(iterable, writable) {
206
223
  if (!completed) try {
207
224
  Promise.resolve(iterator.return?.()).catch(() => {});
208
225
  } catch {}
209
- writable.destroy(error);
226
+ destroyWritable(writable, error);
210
227
  throw error;
211
228
  } finally {
212
229
  writableError.cleanup();
@@ -265,7 +282,7 @@ var StreamPump = class {
265
282
  let available = (this.controller.desiredSize || 0) - bytes.byteLength;
266
283
  this.controller.enqueue(bytes);
267
284
  if (available <= 0) this.pause();
268
- } catch (e) {
285
+ } catch {
269
286
  this.controller.error(/* @__PURE__ */ new Error("Could not create Buffer, chunk must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object"));
270
287
  this.cancel();
271
288
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@react-router/node",
3
3
  "type": "module",
4
- "version": "8.3.0",
4
+ "version": "8.4.0",
5
5
  "description": "Node.js platform abstractions for React Router",
6
6
  "bugs": {
7
7
  "url": "https://github.com/remix-run/react-router/issues"
@@ -38,18 +38,18 @@
38
38
  }
39
39
  },
40
40
  "dependencies": {
41
- "@remix-run/node-fetch-server": "^0.13.3"
41
+ "@remix-run/node-fetch-server": "^0.14.1"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/node": "^22.19.19",
45
45
  "tsdown": "^0.22.0",
46
46
  "typescript": "^6.0.3",
47
47
  "wireit": "0.14.12",
48
- "react-router": "8.3.0"
48
+ "react-router": "8.4.0"
49
49
  },
50
50
  "peerDependencies": {
51
51
  "typescript": "^5.1.0 || ^6.0.0 || ^7.0.0",
52
- "react-router": "8.3.0"
52
+ "react-router": "8.4.0"
53
53
  },
54
54
  "peerDependenciesMeta": {
55
55
  "typescript": {