@whatwg-node/node-fetch 0.7.17 → 0.7.18-alpha-20250414142501-d6dc56a6dae8ef2cab5ea56aad70f6fd874ade7e

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 +54 -27
  2. package/esm/Body.js +54 -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,84 @@ 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
+ // form data file that is currently being processed, it's
196
+ // important to keep track of it in case the stream ends early
197
+ let currFile = null;
198
+ const bb = new busboy_1.Busboy({
193
199
  headers: {
200
+ 'content-length': typeof this.contentLength === 'number'
201
+ ? this.contentLength.toString()
202
+ : this.contentLength || '',
194
203
  'content-type': this.contentType || '',
195
204
  },
196
205
  limits: formDataLimits,
197
- defParamCharset: 'utf-8',
206
+ defCharset: 'utf-8',
198
207
  });
199
- bb.on('field', (name, value, { nameTruncated, valueTruncated }) => {
200
- if (nameTruncated) {
201
- reject(new Error(`Field name size exceeded: ${formDataLimits?.fieldNameSize} bytes`));
208
+ const complete = (err) => {
209
+ stream.unpipe(bb);
210
+ bb.destroy();
211
+ if (currFile) {
212
+ currFile.destroy();
213
+ currFile = null;
214
+ }
215
+ if (err) {
216
+ reject(err);
217
+ }
218
+ else {
219
+ // no error occured, this is a successful end/complete/finish
220
+ resolve(this._formData);
221
+ }
222
+ };
223
+ // we dont need to listen to the stream close event because bb will close or error when necessary
224
+ // stream.on('close', complete);
225
+ // stream can be aborted, for example
226
+ stream.on('error', complete);
227
+ bb.on('field', (name, value, fieldnameTruncated, valueTruncated) => {
228
+ if (fieldnameTruncated) {
229
+ return complete(new Error(`Field name size exceeded: ${formDataLimits?.fieldNameSize} bytes`));
202
230
  }
203
231
  if (valueTruncated) {
204
- reject(new Error(`Field value size exceeded: ${formDataLimits?.fieldSize} bytes`));
232
+ return complete(new Error(`Field value size exceeded: ${formDataLimits?.fieldSize} bytes`));
205
233
  }
206
234
  this._formData.set(name, value);
207
235
  });
208
- bb.on('fieldsLimit', () => {
209
- reject(new Error(`Fields limit exceeded: ${formDataLimits?.fields}`));
210
- });
211
- bb.on('file', (name, fileStream, { filename, mimeType }) => {
236
+ bb.on('file', (name, fileStream, filename, _transferEncoding, mimeType) => {
237
+ currFile = fileStream;
212
238
  const chunks = [];
213
- fileStream.on('limit', () => {
214
- reject(new Error(`File size limit exceeded: ${formDataLimits?.fileSize} bytes`));
215
- });
216
239
  fileStream.on('data', chunk => {
217
240
  chunks.push(chunk);
218
241
  });
242
+ fileStream.on('error', complete);
243
+ fileStream.on('limit', () => {
244
+ complete(new Error(`File size limit exceeded: ${formDataLimits?.fileSize} bytes`));
245
+ });
219
246
  fileStream.on('close', () => {
220
247
  if (fileStream.truncated) {
221
- reject(new Error(`File size limit exceeded: ${formDataLimits?.fileSize} bytes`));
248
+ complete(new Error(`File size limit exceeded: ${formDataLimits?.fileSize} bytes`));
222
249
  }
250
+ currFile = null;
223
251
  const file = new File_js_1.PonyfillFile(chunks, filename, { type: mimeType });
224
252
  this._formData.set(name, file);
225
253
  });
226
254
  });
255
+ bb.on('fieldsLimit', () => {
256
+ complete(new Error(`Fields limit exceeded: ${formDataLimits?.fields}`));
257
+ });
227
258
  bb.on('filesLimit', () => {
228
- reject(new Error(`Files limit exceeded: ${formDataLimits?.files}`));
259
+ complete(new Error(`Files limit exceeded: ${formDataLimits?.files}`));
229
260
  });
230
261
  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));
262
+ complete(new Error(`Parts limit exceeded: ${formDataLimits?.parts}`));
240
263
  });
241
- _body?.readable.pipe(bb);
264
+ bb.on('end', complete);
265
+ bb.on('finish', complete);
266
+ bb.on('close', complete);
267
+ bb.on('error', complete);
268
+ stream.pipe(bb);
242
269
  });
243
270
  }
244
271
  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,84 @@ 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
+ // form data file that is currently being processed, it's
193
+ // important to keep track of it in case the stream ends early
194
+ let currFile = null;
195
+ const bb = new Busboy({
189
196
  headers: {
197
+ 'content-length': typeof this.contentLength === 'number'
198
+ ? this.contentLength.toString()
199
+ : this.contentLength || '',
190
200
  'content-type': this.contentType || '',
191
201
  },
192
202
  limits: formDataLimits,
193
- defParamCharset: 'utf-8',
203
+ defCharset: 'utf-8',
194
204
  });
195
- bb.on('field', (name, value, { nameTruncated, valueTruncated }) => {
196
- if (nameTruncated) {
197
- reject(new Error(`Field name size exceeded: ${formDataLimits?.fieldNameSize} bytes`));
205
+ const complete = (err) => {
206
+ stream.unpipe(bb);
207
+ bb.destroy();
208
+ if (currFile) {
209
+ currFile.destroy();
210
+ currFile = null;
211
+ }
212
+ if (err) {
213
+ reject(err);
214
+ }
215
+ else {
216
+ // no error occured, this is a successful end/complete/finish
217
+ resolve(this._formData);
218
+ }
219
+ };
220
+ // we dont need to listen to the stream close event because bb will close or error when necessary
221
+ // stream.on('close', complete);
222
+ // stream can be aborted, for example
223
+ stream.on('error', complete);
224
+ bb.on('field', (name, value, fieldnameTruncated, valueTruncated) => {
225
+ if (fieldnameTruncated) {
226
+ return complete(new Error(`Field name size exceeded: ${formDataLimits?.fieldNameSize} bytes`));
198
227
  }
199
228
  if (valueTruncated) {
200
- reject(new Error(`Field value size exceeded: ${formDataLimits?.fieldSize} bytes`));
229
+ return complete(new Error(`Field value size exceeded: ${formDataLimits?.fieldSize} bytes`));
201
230
  }
202
231
  this._formData.set(name, value);
203
232
  });
204
- bb.on('fieldsLimit', () => {
205
- reject(new Error(`Fields limit exceeded: ${formDataLimits?.fields}`));
206
- });
207
- bb.on('file', (name, fileStream, { filename, mimeType }) => {
233
+ bb.on('file', (name, fileStream, filename, _transferEncoding, mimeType) => {
234
+ currFile = fileStream;
208
235
  const chunks = [];
209
- fileStream.on('limit', () => {
210
- reject(new Error(`File size limit exceeded: ${formDataLimits?.fileSize} bytes`));
211
- });
212
236
  fileStream.on('data', chunk => {
213
237
  chunks.push(chunk);
214
238
  });
239
+ fileStream.on('error', complete);
240
+ fileStream.on('limit', () => {
241
+ complete(new Error(`File size limit exceeded: ${formDataLimits?.fileSize} bytes`));
242
+ });
215
243
  fileStream.on('close', () => {
216
244
  if (fileStream.truncated) {
217
- reject(new Error(`File size limit exceeded: ${formDataLimits?.fileSize} bytes`));
245
+ complete(new Error(`File size limit exceeded: ${formDataLimits?.fileSize} bytes`));
218
246
  }
247
+ currFile = null;
219
248
  const file = new PonyfillFile(chunks, filename, { type: mimeType });
220
249
  this._formData.set(name, file);
221
250
  });
222
251
  });
252
+ bb.on('fieldsLimit', () => {
253
+ complete(new Error(`Fields limit exceeded: ${formDataLimits?.fields}`));
254
+ });
223
255
  bb.on('filesLimit', () => {
224
- reject(new Error(`Files limit exceeded: ${formDataLimits?.files}`));
256
+ complete(new Error(`Files limit exceeded: ${formDataLimits?.files}`));
225
257
  });
226
258
  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));
259
+ complete(new Error(`Parts limit exceeded: ${formDataLimits?.parts}`));
236
260
  });
237
- _body?.readable.pipe(bb);
261
+ bb.on('end', complete);
262
+ bb.on('finish', complete);
263
+ bb.on('close', complete);
264
+ bb.on('error', complete);
265
+ stream.pipe(bb);
238
266
  });
239
267
  }
240
268
  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-20250414142501-d6dc56a6dae8ef2cab5ea56aad70f6fd874ade7e",
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-20250414142501-d6dc56a6dae8ef2cab5ea56aad70f6fd874ade7e",
10
10
  "tslib": "^2.6.3"
11
11
  },
12
12
  "repository": {