@whatwg-node/fetch 0.3.2 → 0.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,13 @@
1
1
  # @whatwg-node/fetch
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`005937c`](https://github.com/ardatan/whatwg-node/commit/005937c72749dfa3914c8b6193a88c772a522275) Thanks [@ardatan](https://github.com/ardatan)! - feat(fetch): new `fieldsFirst` option to allow async stream consumption for multipart forms
8
+
9
+ - [`effc03d`](https://github.com/ardatan/whatwg-node/commit/effc03d58793328595183ac7cd5c9abab95dec17) Thanks [@ardatan](https://github.com/ardatan)! - Bun Support
10
+
3
11
  ## 0.3.2
4
12
 
5
13
  ### Patch Changes
@@ -2,6 +2,11 @@ const handleFileRequest = require("./handle-file-request");
2
2
 
3
3
  module.exports = function createNodePonyfill(opts = {}) {
4
4
 
5
+ // Bun already has a Fetch API
6
+ if (process.versions.bun) {
7
+ return globalThis;
8
+ }
9
+
5
10
  const ponyfills = {};
6
11
 
7
12
  if (!opts.useNodeFetch) {
@@ -1,7 +1,35 @@
1
1
  const busboy = require('busboy');
2
+ const { resolve } = require('path');
2
3
  const streams = require("stream");
3
4
 
4
5
  module.exports = function getFormDataMethod(File, limits) {
6
+ function consumeStreamAsFile({
7
+ name,
8
+ filename,
9
+ mimeType,
10
+ fileStream,
11
+ formData,
12
+ }) {
13
+ if (fileStream._consumedAsFile) {
14
+ return Promise.resolve(formData.get(name));
15
+ }
16
+ return new Promise((resolve, reject) => {
17
+ const chunks = [];
18
+ fileStream.on('limit', () => {
19
+ reject(new Error(`File size limit exceeded: ${limits.fileSize} bytes`));
20
+ })
21
+ fileStream.on('data', (chunk) => {
22
+ chunks.push(chunk);
23
+ })
24
+ fileStream.on('close', () => {
25
+ const file = new File(chunks, filename, { type: mimeType });
26
+ formData.set(name, file);
27
+ fileStream._consumedAsFile = true;
28
+ resolve(file);
29
+ });
30
+ })
31
+ }
32
+
5
33
  return function formData() {
6
34
  if (this.body == null) {
7
35
  return null;
@@ -17,18 +45,6 @@ module.exports = function getFormDataMethod(File, limits) {
17
45
  });
18
46
  return new Promise((resolve, reject) => {
19
47
  const formData = new Map();
20
- bb.on('file', (name, fileStream, { filename, mimeType }) => {
21
- const chunks = [];
22
- fileStream.on('limit', () => {
23
- reject(new Error(`File size limit exceeded: ${limits.fileSize} bytes`));
24
- })
25
- fileStream.on('data', (chunk) => {
26
- chunks.push(chunk);
27
- })
28
- fileStream.on('close', () => {
29
- formData.set(name, new File(chunks, filename, { type: mimeType }));
30
- });
31
- })
32
48
  bb.on('field', (name, value, { nameTruncated, valueTruncated }) => {
33
49
  if (nameTruncated) {
34
50
  reject(new Error(`Field name size exceeded: ${limits.fieldNameSize} bytes`));
@@ -38,14 +54,57 @@ module.exports = function getFormDataMethod(File, limits) {
38
54
  }
39
55
  formData.set(name, value)
40
56
  })
41
- bb.on('partsLimit', () => {
42
- reject(new Error(`Parts limit exceeded: ${limits.parts}`));
57
+ bb.on('fieldsLimit', () => {
58
+ reject(new Error(`Fields limit exceeded: ${limits.fields}`));
59
+ })
60
+ bb.on('file', (name, fileStream, { filename, mimeType }) => {
61
+ if (limits.fieldsFirst) {
62
+ resolve(formData);
63
+ const fakeFileObj = {
64
+ name: filename,
65
+ type: mimeType,
66
+ }
67
+ Object.setPrototypeOf(fakeFileObj, File.prototype);
68
+ formData.set(name, new Proxy(fakeFileObj, {
69
+ get: (target, prop) => {
70
+ switch(prop) {
71
+ case 'name':
72
+ return filename;
73
+ case 'type':
74
+ return mimeType;
75
+ case 'stream':
76
+ return () => fileStream;
77
+ case 'size':
78
+ throw new Error(`Cannot access file size before consuming the stream.`);
79
+ case 'slice':
80
+ throw new Error(`Cannot slice file before consuming the stream.`);
81
+ case 'text':
82
+ case 'arrayBuffer':
83
+ return () => consumeStreamAsFile({
84
+ name,
85
+ filename,
86
+ mimeType,
87
+ fileStream,
88
+ formData,
89
+ }).then(file => file[prop]())
90
+ }
91
+ },
92
+ }))
93
+ } else {
94
+ consumeStreamAsFile({
95
+ name,
96
+ filename,
97
+ mimeType,
98
+ fileStream,
99
+ formData,
100
+ }).catch(err => reject(err));
101
+ }
43
102
  })
44
103
  bb.on('filesLimit', () => {
45
104
  reject(new Error(`Files limit exceeded: ${limits.files}`));
46
105
  })
47
- bb.on('fieldsLimit', () => {
48
- reject(new Error(`Fields limit exceeded: ${limits.fields}`));
106
+ bb.on('partsLimit', () => {
107
+ reject(new Error(`Parts limit exceeded: ${limits.parts}`));
49
108
  })
50
109
  bb.on('close', () => {
51
110
  resolve(formData);
package/dist/index.d.ts CHANGED
@@ -51,6 +51,8 @@ declare module "@whatwg-node/fetch" {
51
51
  parts?: number;
52
52
  /* For multipart forms, the max number of header key-value pairs to parse. Default: 2000. */
53
53
  headerSize?: number;
54
+ /* For multipart forms, enable this if your data has fields first, then files. Default: false. */
55
+ fieldsFirst?: boolean;
54
56
  }
55
57
  export const createFetch: (opts?: { useNodeFetch?: boolean; formDataLimits?: FormDataLimits }) => ({
56
58
  fetch: typeof _fetch,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@whatwg-node/fetch",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "description": "Cross Platform Smart Fetch Ponyfill",
5
5
  "author": "Arda TANRIKULU <ardatanrikulu@gmail.com>",
6
6
  "repository": {