@whatwg-node/node-fetch 0.7.17 → 0.7.18-alpha-20250414141945-85ba4bb116008ce30707b20368f44d791a52afaa

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 (3) hide show
  1. package/cjs/Body.js +55 -27
  2. package/esm/Body.js +55 -26
  3. package/package.json +3 -3
package/cjs/Body.js CHANGED
@@ -1,12 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PonyfillBody = void 0;
4
- const tslib_1 = require("tslib");
5
4
  /* eslint-disable @typescript-eslint/ban-ts-comment */
6
5
  const node_buffer_1 = require("node:buffer");
7
6
  const node_http_1 = require("node:http");
8
7
  const node_stream_1 = require("node:stream");
9
- const busboy_1 = tslib_1.__importDefault(require("busboy"));
8
+ const busboy_1 = require("@fastify/busboy");
10
9
  const promise_helpers_1 = require("@whatwg-node/promise-helpers");
11
10
  const Blob_js_1 = require("./Blob.js");
12
11
  const File_js_1 = require("./File.js");
@@ -189,56 +188,85 @@ class PonyfillBody {
189
188
  ...opts?.formDataLimits,
190
189
  };
191
190
  return new Promise((resolve, reject) => {
192
- const bb = (0, busboy_1.default)({
191
+ const stream = this.body?.readable;
192
+ if (!stream) {
193
+ return reject(new Error('No stream available'));
194
+ }
195
+ let lastError;
196
+ // form data file that is currently being processed, it's
197
+ // important to keep track of it in case the stream ends early
198
+ let currFile = null;
199
+ const bb = new busboy_1.Busboy({
193
200
  headers: {
201
+ 'content-length': typeof this.contentLength === 'number'
202
+ ? this.contentLength.toString()
203
+ : this.contentLength || '',
194
204
  'content-type': this.contentType || '',
195
205
  },
196
206
  limits: formDataLimits,
197
- defParamCharset: 'utf-8',
207
+ defCharset: 'utf-8',
198
208
  });
199
- bb.on('field', (name, value, { nameTruncated, valueTruncated }) => {
200
- if (nameTruncated) {
201
- reject(new Error(`Field name size exceeded: ${formDataLimits?.fieldNameSize} bytes`));
209
+ const complete = (err) => {
210
+ stream.unpipe(bb);
211
+ bb.destroy();
212
+ if (currFile) {
213
+ currFile.destroy();
214
+ currFile = null;
215
+ }
216
+ if (err || lastError) {
217
+ reject(err || lastError);
218
+ }
219
+ else {
220
+ // no error occured, this is a successful end/complete/finish
221
+ resolve(this._formData);
222
+ }
223
+ };
224
+ // we dont need to listen to the stream close event because bb will close or error when necessary
225
+ // stream.on('close', complete);
226
+ // stream can be aborted, for example
227
+ stream.on('error', complete);
228
+ bb.on('field', (name, value, fieldnameTruncated, valueTruncated) => {
229
+ if (fieldnameTruncated) {
230
+ return complete(new Error(`Field name size exceeded: ${formDataLimits?.fieldNameSize} bytes`));
202
231
  }
203
232
  if (valueTruncated) {
204
- reject(new Error(`Field value size exceeded: ${formDataLimits?.fieldSize} bytes`));
233
+ return complete(new Error(`Field value size exceeded: ${formDataLimits?.fieldSize} bytes`));
205
234
  }
206
235
  this._formData.set(name, value);
207
236
  });
208
- bb.on('fieldsLimit', () => {
209
- reject(new Error(`Fields limit exceeded: ${formDataLimits?.fields}`));
210
- });
211
- bb.on('file', (name, fileStream, { filename, mimeType }) => {
237
+ bb.on('file', (name, fileStream, filename, _transferEncoding, mimeType) => {
238
+ currFile = fileStream;
212
239
  const chunks = [];
213
- fileStream.on('limit', () => {
214
- reject(new Error(`File size limit exceeded: ${formDataLimits?.fileSize} bytes`));
215
- });
216
240
  fileStream.on('data', chunk => {
217
241
  chunks.push(chunk);
218
242
  });
243
+ fileStream.on('error', complete);
244
+ fileStream.on('limit', () => {
245
+ complete(new Error(`File size limit exceeded: ${formDataLimits?.fileSize} bytes`));
246
+ });
219
247
  fileStream.on('close', () => {
220
248
  if (fileStream.truncated) {
221
- reject(new Error(`File size limit exceeded: ${formDataLimits?.fileSize} bytes`));
249
+ complete(new Error(`File size limit exceeded: ${formDataLimits?.fileSize} bytes`));
222
250
  }
251
+ currFile = null;
223
252
  const file = new File_js_1.PonyfillFile(chunks, filename, { type: mimeType });
224
253
  this._formData.set(name, file);
225
254
  });
226
255
  });
256
+ bb.on('fieldsLimit', () => {
257
+ complete(new Error(`Fields limit exceeded: ${formDataLimits?.fields}`));
258
+ });
227
259
  bb.on('filesLimit', () => {
228
- reject(new Error(`Files limit exceeded: ${formDataLimits?.files}`));
260
+ complete(new Error(`Files limit exceeded: ${formDataLimits?.files}`));
229
261
  });
230
262
  bb.on('partsLimit', () => {
231
- reject(new Error(`Parts limit exceeded: ${formDataLimits?.parts}`));
232
- });
233
- bb.on('close', () => {
234
- resolve(this._formData);
235
- });
236
- bb.on('error', (err = 'An error occurred while parsing the form data') => {
237
- const errMessage = err.message || err.toString();
238
- // @ts-ignore - `cause` is in `TypeError`in node
239
- reject(new TypeError(errMessage, err.cause));
263
+ complete(new Error(`Parts limit exceeded: ${formDataLimits?.parts}`));
240
264
  });
241
- _body?.readable.pipe(bb);
265
+ bb.on('end', complete);
266
+ bb.on('finish', complete);
267
+ bb.on('close', complete);
268
+ bb.on('error', complete);
269
+ stream.pipe(bb);
242
270
  });
243
271
  }
244
272
  buffer() {
package/esm/Body.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { Buffer } from 'node:buffer';
3
3
  import { IncomingMessage } from 'node:http';
4
4
  import { Readable } from 'node:stream';
5
- import busboy from 'busboy';
5
+ import { Busboy } from '@fastify/busboy';
6
6
  import { handleMaybePromise } from '@whatwg-node/promise-helpers';
7
7
  import { hasArrayBufferMethod, hasBufferMethod, hasBytesMethod, PonyfillBlob } from './Blob.js';
8
8
  import { PonyfillFile } from './File.js';
@@ -185,56 +185,85 @@ export class PonyfillBody {
185
185
  ...opts?.formDataLimits,
186
186
  };
187
187
  return new Promise((resolve, reject) => {
188
- const bb = busboy({
188
+ const stream = this.body?.readable;
189
+ if (!stream) {
190
+ return reject(new Error('No stream available'));
191
+ }
192
+ let lastError;
193
+ // form data file that is currently being processed, it's
194
+ // important to keep track of it in case the stream ends early
195
+ let currFile = null;
196
+ const bb = new Busboy({
189
197
  headers: {
198
+ 'content-length': typeof this.contentLength === 'number'
199
+ ? this.contentLength.toString()
200
+ : this.contentLength || '',
190
201
  'content-type': this.contentType || '',
191
202
  },
192
203
  limits: formDataLimits,
193
- defParamCharset: 'utf-8',
204
+ defCharset: 'utf-8',
194
205
  });
195
- bb.on('field', (name, value, { nameTruncated, valueTruncated }) => {
196
- if (nameTruncated) {
197
- reject(new Error(`Field name size exceeded: ${formDataLimits?.fieldNameSize} bytes`));
206
+ const complete = (err) => {
207
+ stream.unpipe(bb);
208
+ bb.destroy();
209
+ if (currFile) {
210
+ currFile.destroy();
211
+ currFile = null;
212
+ }
213
+ if (err || lastError) {
214
+ reject(err || lastError);
215
+ }
216
+ else {
217
+ // no error occured, this is a successful end/complete/finish
218
+ resolve(this._formData);
219
+ }
220
+ };
221
+ // we dont need to listen to the stream close event because bb will close or error when necessary
222
+ // stream.on('close', complete);
223
+ // stream can be aborted, for example
224
+ stream.on('error', complete);
225
+ bb.on('field', (name, value, fieldnameTruncated, valueTruncated) => {
226
+ if (fieldnameTruncated) {
227
+ return complete(new Error(`Field name size exceeded: ${formDataLimits?.fieldNameSize} bytes`));
198
228
  }
199
229
  if (valueTruncated) {
200
- reject(new Error(`Field value size exceeded: ${formDataLimits?.fieldSize} bytes`));
230
+ return complete(new Error(`Field value size exceeded: ${formDataLimits?.fieldSize} bytes`));
201
231
  }
202
232
  this._formData.set(name, value);
203
233
  });
204
- bb.on('fieldsLimit', () => {
205
- reject(new Error(`Fields limit exceeded: ${formDataLimits?.fields}`));
206
- });
207
- bb.on('file', (name, fileStream, { filename, mimeType }) => {
234
+ bb.on('file', (name, fileStream, filename, _transferEncoding, mimeType) => {
235
+ currFile = fileStream;
208
236
  const chunks = [];
209
- fileStream.on('limit', () => {
210
- reject(new Error(`File size limit exceeded: ${formDataLimits?.fileSize} bytes`));
211
- });
212
237
  fileStream.on('data', chunk => {
213
238
  chunks.push(chunk);
214
239
  });
240
+ fileStream.on('error', complete);
241
+ fileStream.on('limit', () => {
242
+ complete(new Error(`File size limit exceeded: ${formDataLimits?.fileSize} bytes`));
243
+ });
215
244
  fileStream.on('close', () => {
216
245
  if (fileStream.truncated) {
217
- reject(new Error(`File size limit exceeded: ${formDataLimits?.fileSize} bytes`));
246
+ complete(new Error(`File size limit exceeded: ${formDataLimits?.fileSize} bytes`));
218
247
  }
248
+ currFile = null;
219
249
  const file = new PonyfillFile(chunks, filename, { type: mimeType });
220
250
  this._formData.set(name, file);
221
251
  });
222
252
  });
253
+ bb.on('fieldsLimit', () => {
254
+ complete(new Error(`Fields limit exceeded: ${formDataLimits?.fields}`));
255
+ });
223
256
  bb.on('filesLimit', () => {
224
- reject(new Error(`Files limit exceeded: ${formDataLimits?.files}`));
257
+ complete(new Error(`Files limit exceeded: ${formDataLimits?.files}`));
225
258
  });
226
259
  bb.on('partsLimit', () => {
227
- reject(new Error(`Parts limit exceeded: ${formDataLimits?.parts}`));
228
- });
229
- bb.on('close', () => {
230
- resolve(this._formData);
231
- });
232
- bb.on('error', (err = 'An error occurred while parsing the form data') => {
233
- const errMessage = err.message || err.toString();
234
- // @ts-ignore - `cause` is in `TypeError`in node
235
- reject(new TypeError(errMessage, err.cause));
260
+ complete(new Error(`Parts limit exceeded: ${formDataLimits?.parts}`));
236
261
  });
237
- _body?.readable.pipe(bb);
262
+ bb.on('end', complete);
263
+ bb.on('finish', complete);
264
+ bb.on('close', complete);
265
+ bb.on('error', complete);
266
+ stream.pipe(bb);
238
267
  });
239
268
  }
240
269
  buffer() {
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@whatwg-node/node-fetch",
3
- "version": "0.7.17",
3
+ "version": "0.7.18-alpha-20250414141945-85ba4bb116008ce30707b20368f44d791a52afaa",
4
4
  "description": "Fetch API implementation for Node",
5
5
  "sideEffects": false,
6
6
  "dependencies": {
7
+ "@fastify/busboy": "^3.1.1",
7
8
  "@whatwg-node/disposablestack": "^0.0.6",
8
- "@whatwg-node/promise-helpers": "^1.2.5",
9
- "busboy": "^1.6.0",
9
+ "@whatwg-node/promise-helpers": "1.3.1-alpha-20250414141945-85ba4bb116008ce30707b20368f44d791a52afaa",
10
10
  "tslib": "^2.6.3"
11
11
  },
12
12
  "repository": {