@whatwg-node/fetch 0.6.0 → 0.6.2-alpha-20230102102521-2e7c4e5

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,30 @@
1
1
  # @whatwg-node/fetch
2
2
 
3
+ ## 0.6.2-alpha-20230102102521-2e7c4e5
4
+
5
+ ### Patch Changes
6
+
7
+ - [#154](https://github.com/ardatan/whatwg-node/pull/154) [`02b2895`](https://github.com/ardatan/whatwg-node/commit/02b289550751fd2a8317666ecad101037a4e6551) Thanks [@ardatan](https://github.com/ardatan)! - dependencies updates:
8
+
9
+ - Updated dependency [`web-streams-polyfill@^3.2.1` ↗︎](https://www.npmjs.com/package/web-streams-polyfill/v/3.2.1) (from `^3.2.0`, in `dependencies`)
10
+ - Added dependency [`@whatwg-node/node-fetch@0.0.0` ↗︎](https://www.npmjs.com/package/@whatwg-node/node-fetch/v/0.0.0) (to `dependencies`)
11
+ - Removed dependency [`abort-controller@^3.0.0` ↗︎](https://www.npmjs.com/package/abort-controller/v/3.0.0) (from `dependencies`)
12
+ - Removed dependency [`form-data-encoder@^1.7.1` ↗︎](https://www.npmjs.com/package/form-data-encoder/v/1.7.1) (from `dependencies`)
13
+ - Removed dependency [`formdata-node@^4.3.1` ↗︎](https://www.npmjs.com/package/formdata-node/v/4.3.1) (from `dependencies`)
14
+ - Removed dependency [`node-fetch@^2.6.7` ↗︎](https://www.npmjs.com/package/node-fetch/v/2.6.7) (from `dependencies`)
15
+ - Removed dependency [`undici@^5.12.0` ↗︎](https://www.npmjs.com/package/undici/v/5.12.0) (from `dependencies`)
16
+
17
+ - [#154](https://github.com/ardatan/whatwg-node/pull/154) [`02b2895`](https://github.com/ardatan/whatwg-node/commit/02b289550751fd2a8317666ecad101037a4e6551) Thanks [@ardatan](https://github.com/ardatan)! - New Fetch API implementation for Node
18
+
19
+ - Updated dependencies [[`02b2895`](https://github.com/ardatan/whatwg-node/commit/02b289550751fd2a8317666ecad101037a4e6551)]:
20
+ - @whatwg-node/node-fetch@0.0.1-alpha-20230102102521-2e7c4e5
21
+
22
+ ## 0.6.1
23
+
24
+ ### Patch Changes
25
+
26
+ - [`9752cca`](https://github.com/ardatan/whatwg-node/commit/9752cca54e7636114d87849ca9c7eb9be3d9dba8) Thanks [@ardatan](https://github.com/ardatan)! - Remove unnecessary ponyfill for the envs supporting Fetch by default
27
+
3
28
  ## 0.6.0
4
29
 
5
30
  ### Minor Changes
@@ -1,7 +1,3 @@
1
- const http2 = require('http2')
2
- const handleFileRequest = require("./handle-file-request");
3
- const readableStreamToReadable = require("./readableStreamToReadable");
4
-
5
1
  module.exports = function createNodePonyfill(opts = {}) {
6
2
 
7
3
  // Bun already has a Fetch API
@@ -9,75 +5,64 @@ module.exports = function createNodePonyfill(opts = {}) {
9
5
  return globalThis;
10
6
  }
11
7
 
12
- const ponyfills = {};
8
+ const newNodeFetch = require('@whatwg-node/node-fetch');
13
9
 
14
- if (!opts.useNodeFetch) {
15
- ponyfills.fetch = globalThis.fetch;
16
- ponyfills.Headers = globalThis.Headers;
17
- ponyfills.Request = globalThis.Request;
18
- ponyfills.Response = globalThis.Response;
19
- ponyfills.FormData = globalThis.FormData;
20
- ponyfills.File = globalThis.File;
21
- }
10
+ const ponyfills = {};
22
11
 
23
- ponyfills.AbortController = globalThis.AbortController;
24
- ponyfills.ReadableStream = globalThis.ReadableStream;
12
+ ponyfills.fetch = newNodeFetch.fetch;
13
+ ponyfills.Request = newNodeFetch.Request;
14
+ ponyfills.Response = newNodeFetch.Response;
15
+ ponyfills.Headers = newNodeFetch.Headers;
16
+ ponyfills.FormData = newNodeFetch.FormData;
17
+ ponyfills.AbortController = newNodeFetch.AbortController;
18
+ ponyfills.ReadableStream = newNodeFetch.ReadableStream;
25
19
  ponyfills.WritableStream = globalThis.WritableStream;
26
20
  ponyfills.TransformStream = globalThis.TransformStream;
27
- ponyfills.Blob = globalThis.Blob;
28
- ponyfills.crypto = globalThis.crypto;
29
- ponyfills.URLPattern = globalThis.URLPattern;
30
-
31
- if (!ponyfills.AbortController) {
32
- const abortControllerModule = require("abort-controller");
33
- ponyfills.AbortController =
34
- abortControllerModule.default || abortControllerModule;
35
- }
36
21
 
37
- if (!ponyfills.Blob) {
38
- const bufferModule = require('buffer')
39
- ponyfills.Blob = bufferModule.Blob;
40
- }
41
-
42
- if (!ponyfills.Blob) {
43
- const formDataModule = require("formdata-node");
44
- ponyfills.Blob = formDataModule.Blob
45
- }
46
-
47
- if (!ponyfills.ReadableStream) {
22
+ if (!ponyfills.WritableStream) {
48
23
  try {
49
24
  const streamsWeb = require("stream/web");
50
-
51
- ponyfills.ReadableStream = streamsWeb.ReadableStream;
52
25
  ponyfills.WritableStream = streamsWeb.WritableStream;
53
26
  ponyfills.TransformStream = streamsWeb.TransformStream;
54
27
  } catch (e) {
55
28
  const streamsWeb = require("web-streams-polyfill/ponyfill");
56
- ponyfills.ReadableStream = streamsWeb.ReadableStream;
57
29
  ponyfills.WritableStream = streamsWeb.WritableStream;
58
30
  ponyfills.TransformStream = streamsWeb.TransformStream;
59
31
  }
60
32
  }
61
33
 
62
- ponyfills.btoa = globalThis.btoa
63
- if (!ponyfills.btoa) {
64
- ponyfills.btoa = function btoa(data) {
65
- return Buffer.from(data, 'binary').toString('base64');
66
- };
67
- }
68
-
69
- ponyfills.TextEncoder = function TextEncoder(encoding = 'utf-8') {
70
- return {
71
- encode(str) {
72
- return Buffer.from(str, encoding);
34
+ ponyfills.Blob = newNodeFetch.Blob;
35
+ ponyfills.File = newNodeFetch.File;
36
+ ponyfills.crypto = globalThis.crypto;
37
+ ponyfills.btoa = newNodeFetch.btoa;
38
+ ponyfills.TextEncoder = newNodeFetch.TextEncoder;
39
+ ponyfills.TextDecoder = newNodeFetch.TextDecoder;
40
+ ponyfills.AbortError = newNodeFetch.AbortError;
41
+ ponyfills.AbortSignal = newNodeFetch.AbortSignal;
42
+
43
+ if (opts.formDataLimits) {
44
+ ponyfills.Body = class Body extends newNodeFetch.Body {
45
+ constructor(body, userOpts) {
46
+ super(body, {
47
+ formDataLimits: opts.formDataLimits,
48
+ ...userOpts,
49
+ });
73
50
  }
74
51
  }
75
- }
76
-
77
- ponyfills.TextDecoder = function TextDecoder(encoding = 'utf-8') {
78
- return {
79
- decode(buf) {
80
- return Buffer.from(buf).toString(encoding);
52
+ ponyfills.Request = class Request extends newNodeFetch.Request {
53
+ constructor(input, userOpts) {
54
+ super(input, {
55
+ formDataLimits: opts.formDataLimits,
56
+ ...userOpts,
57
+ });
58
+ }
59
+ }
60
+ ponyfills.Response = class Response extends newNodeFetch.Response {
61
+ constructor(body, userOpts) {
62
+ super(body, {
63
+ formDataLimits: opts.formDataLimits,
64
+ ...userOpts,
65
+ });
81
66
  }
82
67
  }
83
68
  }
@@ -91,261 +76,11 @@ module.exports = function createNodePonyfill(opts = {}) {
91
76
  const cryptoPonyfill = require('@peculiar/webcrypto');
92
77
  ponyfills.crypto = new cryptoPonyfill.Crypto();
93
78
  }
94
-
79
+
95
80
  if (!ponyfills.URLPattern) {
96
81
  const urlPatternModule = require('urlpattern-polyfill');
97
82
  ponyfills.URLPattern = urlPatternModule.URLPattern;
98
83
  }
99
84
 
100
- // If any of classes of Fetch API is missing, we need to ponyfill them.
101
- if (!ponyfills.fetch ||
102
- !ponyfills.Request ||
103
- !ponyfills.Headers ||
104
- !ponyfills.Response ||
105
- !ponyfills.FormData ||
106
- !ponyfills.File ||
107
- opts.useNodeFetch) {
108
-
109
- const [
110
- nodeMajorStr,
111
- nodeMinorStr
112
- ] = process.versions.node.split('.');
113
-
114
- const nodeMajor = parseInt(nodeMajorStr);
115
- const nodeMinor = parseInt(nodeMinorStr);
116
- const getFormDataMethod = require('./getFormDataMethod');
117
-
118
- if (!opts.useNodeFetch && (nodeMajor > 16 || (nodeMajor === 16 && nodeMinor >= 5))) {
119
- const undici = require("undici");
120
-
121
- if (!ponyfills.Headers) {
122
- ponyfills.Headers = undici.Headers;
123
- }
124
-
125
- const streams = require("stream");
126
-
127
- const OriginalRequest = ponyfills.Request || undici.Request;
128
-
129
- class Request extends OriginalRequest {
130
- constructor(requestOrUrl, options) {
131
- if (typeof requestOrUrl === "string" || requestOrUrl instanceof URL) {
132
- if (options != null && typeof options === "object" && !options.duplex) {
133
- options.duplex = 'half';
134
- }
135
- super(requestOrUrl, options);
136
- const contentType = this.headers.get("content-type");
137
- if (contentType && contentType.startsWith("multipart/form-data")) {
138
- this.headers.set("content-type", contentType.split(', ')[0]);
139
- }
140
- } else {
141
- super(requestOrUrl);
142
- }
143
- this.formData = getFormDataMethod(undici.File, opts.formDataLimits);
144
- }
145
- }
146
-
147
- ponyfills.Request = Request;
148
-
149
- const originalFetch = ponyfills.fetch || undici.fetch;
150
-
151
- const fetch = function (requestOrUrl, options) {
152
- if (typeof requestOrUrl === "string" || requestOrUrl instanceof URL) {
153
- if (options != null && typeof options === "object" && !options.duplex) {
154
- options.duplex = 'half';
155
- }
156
- // We cannot use our ctor because it leaks on Node 18's global fetch
157
- return originalFetch(requestOrUrl, options);
158
- }
159
- if (requestOrUrl.url.startsWith('file:')) {
160
- return handleFileRequest(requestOrUrl.url, ponyfills.Response);
161
- }
162
- return originalFetch(requestOrUrl);
163
- };
164
-
165
- ponyfills.fetch = fetch;
166
-
167
- if (!ponyfills.Response) {
168
- ponyfills.Response = undici.Response;
169
- }
170
-
171
- if (!ponyfills.FormData) {
172
- ponyfills.FormData = undici.FormData;
173
- }
174
-
175
- if (!ponyfills.File) {
176
- ponyfills.File = undici.File
177
- }
178
- } else {
179
- const nodeFetch = require("node-fetch");
180
- const realFetch = ponyfills.fetch || nodeFetch.default || nodeFetch;
181
- if (!ponyfills.Headers) {
182
- ponyfills.Headers = nodeFetch.Headers;
183
- // Sveltekit
184
- if (globalThis.Headers && nodeMajor < 18) {
185
- Object.defineProperty(globalThis.Headers, Symbol.hasInstance, {
186
- value(obj) {
187
- return obj && obj.get && obj.set && obj.delete && obj.has && obj.append;
188
- },
189
- configurable: true,
190
- })
191
- }
192
- }
193
- const formDataEncoderModule = require("form-data-encoder");
194
- const streams = require("stream");
195
- const formDataModule = require("formdata-node");
196
- if (!ponyfills.FormData) {
197
- ponyfills.FormData = formDataModule.FormData
198
- }
199
- if (!ponyfills.File) {
200
- ponyfills.File = formDataModule.File
201
- }
202
-
203
- const OriginalRequest = ponyfills.Request || nodeFetch.Request;
204
-
205
- class Request extends OriginalRequest {
206
- constructor(requestOrUrl, options) {
207
- if (typeof requestOrUrl === "string" || requestOrUrl instanceof URL) {
208
- // Support schemaless URIs on the server for parity with the browser.
209
- // Ex: //github.com/ -> https://github.com/
210
- if (/^\/\//.test(requestOrUrl.toString())) {
211
- requestOrUrl = "https:" + requestOrUrl.toString();
212
- }
213
- let method = (options || {}).method;
214
- const headers = {};
215
- if ('headers' in (options || {})) {
216
- let isHttp2 = false;
217
- for (const [key, value] of Object.entries(options.headers)) {
218
- if (key.startsWith(':')) {
219
- // omit http2 headers
220
- isHttp2 = true;
221
- } else {
222
- headers[key] = value
223
- }
224
- }
225
- if (isHttp2) {
226
- // translate http2 if applicable
227
- method = options.headers[http2.constants.HTTP2_HEADER_METHOD];
228
- const scheme = options.headers[http2.constants.HTTP2_HEADER_SCHEME];
229
- const authority = options.headers[http2.constants.HTTP2_HEADER_AUTHORITY];
230
- const path = options.headers[http2.constants.HTTP2_HEADER_PATH];
231
- headers.host = authority;
232
- requestOrUrl = `${scheme}://${authority}${path}`
233
- }
234
- }
235
- const fixedOptions = {
236
- ...options,
237
- method,
238
- headers,
239
- };
240
- fixedOptions.headers = new ponyfills.Headers(fixedOptions.headers || {});
241
- fixedOptions.headers.set('Connection', 'keep-alive');
242
- if (fixedOptions.body != null) {
243
- if (fixedOptions.body[Symbol.toStringTag] === 'FormData') {
244
- const encoder = new formDataEncoderModule.FormDataEncoder(fixedOptions.body)
245
- for (const headerKey in encoder.headers) {
246
- fixedOptions.headers.set(headerKey, encoder.headers[headerKey])
247
- }
248
- fixedOptions.body = streams.Readable.from(encoder);
249
- } else if (fixedOptions.body[Symbol.toStringTag] === 'ReadableStream') {
250
- fixedOptions.body = readableStreamToReadable(fixedOptions.body);
251
- }
252
- }
253
- super(requestOrUrl, fixedOptions);
254
- } else {
255
- super(requestOrUrl);
256
- }
257
- this.formData = getFormDataMethod(formDataModule.File, opts.formDataLimits);
258
- }
259
- }
260
- ponyfills.Request = Request;
261
- const fetch = function (requestOrUrl, options) {
262
- if (typeof requestOrUrl === "string" || requestOrUrl instanceof URL) {
263
- return fetch(new Request(requestOrUrl, options));
264
- }
265
- if (requestOrUrl.url.startsWith('file:')) {
266
- return handleFileRequest(requestOrUrl.url, ponyfills.Response);
267
- }
268
- const abortCtrl = new ponyfills.AbortController();
269
-
270
- return realFetch(requestOrUrl, {
271
- ...options,
272
- signal: abortCtrl.signal
273
- }).then(res => {
274
- return new Proxy(res, {
275
- get(target, prop, receiver) {
276
- if (prop === 'body') {
277
- return new Proxy(res.body, {
278
- get(target, prop, receiver) {
279
- if (prop === Symbol.asyncIterator) {
280
- return () => {
281
- const originalAsyncIterator = target[Symbol.asyncIterator]();
282
- return {
283
- next() {
284
- return originalAsyncIterator.next();
285
- },
286
- return() {
287
- abortCtrl.abort();
288
- return originalAsyncIterator.return();
289
- },
290
- throw(error) {
291
- abortCtrl.abort(error);
292
- return originalAsyncIterator.throw(error);
293
- }
294
- }
295
- }
296
- }
297
- return Reflect.get(target, prop, receiver);
298
- }
299
- })
300
- }
301
- return Reflect.get(target, prop, receiver);
302
- }
303
- })
304
- });
305
- };
306
-
307
- ponyfills.fetch = fetch;
308
-
309
- const OriginalResponse = ponyfills.Response || nodeFetch.Response;
310
- ponyfills.Response = function Response(body, init) {
311
- if (body != null && body[Symbol.toStringTag] === 'ReadableStream') {
312
- const actualBody = readableStreamToReadable(body);
313
- // Polyfill ReadableStream is not working well with node-fetch's Response
314
- return new OriginalResponse(actualBody, init);
315
- }
316
- return new OriginalResponse(body, init);
317
- };
318
-
319
- }
320
- }
321
-
322
- if (!ponyfills.Response.redirect) {
323
- ponyfills.Response.redirect = function (url, status = 302) {
324
- return new ponyfills.Response(null, {
325
- status,
326
- headers: {
327
- Location: url,
328
- },
329
- });
330
- };
331
- }
332
- if (!ponyfills.Response.json) {
333
- ponyfills.Response.json = function (data, init = {}) {
334
- return new ponyfills.Response(JSON.stringify(data), {
335
- ...init,
336
- headers: {
337
- "Content-Type": "application/json",
338
- ...init.headers,
339
- },
340
- });
341
- };
342
- }
343
- if (!ponyfills.Response.error) {
344
- ponyfills.Response.error = function () {
345
- return new ponyfills.Response(null, {
346
- status: 500,
347
- });
348
- };
349
- }
350
85
  return ponyfills;
351
86
  }
@@ -14,8 +14,4 @@ module.exports.btoa = globalThis.btoa;
14
14
  module.exports.TextEncoder = globalThis.TextEncoder;
15
15
  module.exports.TextDecoder = globalThis.TextDecoder;
16
16
  module.exports.URLPattern = globalThis.URLPattern;
17
- if (!module.exports.URLPattern) {
18
- const urlPatternModule = require('urlpattern-polyfill');
19
- module.exports.URLPattern = urlPatternModule.URLPattern;
20
- }
21
17
  module.exports.createFetch = () => globalThis;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@whatwg-node/fetch",
3
- "version": "0.6.0",
3
+ "version": "0.6.2-alpha-20230102102521-2e7c4e5",
4
4
  "description": "Cross Platform Smart Fetch Ponyfill",
5
5
  "author": "Arda TANRIKULU <ardatanrikulu@gmail.com>",
6
6
  "repository": {
@@ -19,14 +19,10 @@
19
19
  },
20
20
  "dependencies": {
21
21
  "@peculiar/webcrypto": "^1.4.0",
22
- "abort-controller": "^3.0.0",
22
+ "@whatwg-node/node-fetch": "0.0.1-alpha-20230102102521-2e7c4e5",
23
23
  "busboy": "^1.6.0",
24
- "form-data-encoder": "^1.7.1",
25
- "formdata-node": "^4.3.1",
26
- "node-fetch": "^2.6.7",
27
- "undici": "^5.12.0",
28
- "web-streams-polyfill": "^3.2.0",
29
- "urlpattern-polyfill": "^6.0.2"
24
+ "urlpattern-polyfill": "^6.0.2",
25
+ "web-streams-polyfill": "^3.2.1"
30
26
  },
31
27
  "publishConfig": {
32
28
  "access": "public"
@@ -1,36 +1,58 @@
1
- import { createTestContainer } from '../../server/test/create-test-container';
1
+ import * as fetchAPI from '@whatwg-node/fetch';
2
2
 
3
3
  describe('getFormDataMethod', () => {
4
- createTestContainer(fetchAPI => {
5
- it('should parse fields correctly', async () => {
6
- const formData = new fetchAPI.FormData();
7
- formData.append('greetings', 'Hello world!');
8
- formData.append('bye', 'Goodbye world!');
9
- const request = new fetchAPI.Request('http://localhost:8080', {
10
- method: 'POST',
11
- body: formData,
12
- });
13
- const formdata = await request.formData();
14
- expect(formdata.get('greetings')).toBe('Hello world!');
15
- expect(formdata.get('bye')).toBe('Goodbye world!');
4
+ it('should parse fields correctly', async () => {
5
+ const formData = new fetchAPI.FormData();
6
+ formData.append('greetings', 'Hello world!');
7
+ formData.append('bye', 'Goodbye world!');
8
+ const request = new fetchAPI.Request('http://localhost:8080', {
9
+ method: 'POST',
10
+ body: formData,
16
11
  });
17
- it('should parse and receive text files correctly', async () => {
18
- const formData = new fetchAPI.FormData();
19
- const greetingsFile = new fetchAPI.File(['Hello world!'], 'greetings.txt', { type: 'text/plain' });
20
- const byeFile = new fetchAPI.File(['Goodbye world!'], 'bye.txt', { type: 'text/plain' });
21
- formData.append('greetings', greetingsFile);
22
- formData.append('bye', byeFile);
23
- const request = new fetchAPI.Request('http://localhost:8080', {
24
- method: 'POST',
25
- body: formData,
26
- });
27
- const formdata = await request.formData();
28
- const receivedGreetingsFile = formdata.get('greetings') as File;
29
- const receivedGreetingsText = await receivedGreetingsFile.text();
30
- expect(receivedGreetingsText).toBe('Hello world!');
31
- const receivedByeFile = formdata.get('bye') as File;
32
- const receivedByeText = await receivedByeFile.text();
33
- expect(receivedByeText).toBe('Goodbye world!');
12
+ const formdata = await request.formData();
13
+ expect(formdata.get('greetings')).toBe('Hello world!');
14
+ expect(formdata.get('bye')).toBe('Goodbye world!');
15
+ });
16
+ it('should parse and receive text files correctly', async () => {
17
+ const formData = new fetchAPI.FormData();
18
+ const greetingsFile = new fetchAPI.File(['Hello world!'], 'greetings.txt', { type: 'text/plain' });
19
+ const byeFile = new fetchAPI.File(['Goodbye world!'], 'bye.txt', { type: 'text/plain' });
20
+ formData.append('greetings', greetingsFile);
21
+ formData.append('bye', byeFile);
22
+ const request = new fetchAPI.Request('http://localhost:8080', {
23
+ method: 'POST',
24
+ body: formData,
25
+ });
26
+ const formdata = await request.formData();
27
+ const receivedGreetingsFile = formdata.get('greetings') as File;
28
+ const receivedGreetingsText = await receivedGreetingsFile.text();
29
+ expect(receivedGreetingsText).toBe('Hello world!');
30
+ const receivedByeFile = formdata.get('bye') as File;
31
+ const receivedByeText = await receivedByeFile.text();
32
+ expect(receivedByeText).toBe('Goodbye world!');
33
+ });
34
+ it('should handle file limits', async () => {
35
+ const limitedFetchAPI = fetchAPI.createFetch({
36
+ formDataLimits: {
37
+ fileSize: 1,
38
+ },
39
+ });
40
+ const formData = new limitedFetchAPI.FormData();
41
+ const greetingsFile = new limitedFetchAPI.File(['Hello world!'], 'greetings.txt', { type: 'text/plain' });
42
+ formData.append('greetings', greetingsFile);
43
+ const proxyRequest = new limitedFetchAPI.Request('http://localhost:8080', {
44
+ method: 'POST',
45
+ body: formData,
46
+ });
47
+ const formDataInText = await proxyRequest.text();
48
+ const contentType = proxyRequest.headers.get('content-type')!;
49
+ const requestWillParse = new limitedFetchAPI.Request('http://localhost:8080', {
50
+ method: 'POST',
51
+ body: formDataInText,
52
+ headers: {
53
+ 'content-type': contentType,
54
+ },
34
55
  });
56
+ await expect(() => requestWillParse.formData()).rejects.toThrowError('File size limit exceeded: 1 bytes');
35
57
  });
36
58
  });
@@ -1,65 +0,0 @@
1
- const busboy = require('busboy');
2
- const { resolve } = require('path');
3
- const streams = require("stream");
4
-
5
- module.exports = function getFormDataMethod(File, limits) {
6
-
7
- return function formData() {
8
- if (this.body == null) {
9
- return null;
10
- }
11
- const contentType = this.headers.get('Content-Type');
12
- const nodeReadable = this.body.on ? this.body : streams.Readable.from(this.body);
13
- const bb = busboy({
14
- headers: {
15
- 'content-type': contentType
16
- },
17
- limits,
18
- defParamCharset: 'utf-8'
19
- });
20
- return new Promise((resolve, reject) => {
21
- const formData = new Map();
22
- bb.on('field', (name, value, { nameTruncated, valueTruncated }) => {
23
- if (nameTruncated) {
24
- reject(new Error(`Field name size exceeded: ${limits.fieldNameSize} bytes`));
25
- }
26
- if (valueTruncated) {
27
- reject(new Error(`Field value size exceeded: ${limits.fieldSize} bytes`));
28
- }
29
- formData.set(name, value)
30
- })
31
- bb.on('fieldsLimit', () => {
32
- reject(new Error(`Fields limit exceeded: ${limits.fields}`));
33
- })
34
- bb.on('file', (name, fileStream, { filename, mimeType }) => {
35
- const chunks = [];
36
- fileStream.on('limit', () => {
37
- reject(new Error(`File size limit exceeded: ${limits.fileSize} bytes`));
38
- })
39
- fileStream.on('data', (chunk) => {
40
- chunks.push(Buffer.from(chunk));
41
- })
42
- fileStream.on('close', () => {
43
- if (fileStream.truncated) {
44
- reject(new Error(`File size limit exceeded: ${limits.fileSize} bytes`));
45
- }
46
- const file = new File(chunks, filename, { type: mimeType });
47
- formData.set(name, file);
48
- });
49
- })
50
- bb.on('filesLimit', () => {
51
- reject(new Error(`Files limit exceeded: ${limits.files}`));
52
- })
53
- bb.on('partsLimit', () => {
54
- reject(new Error(`Parts limit exceeded: ${limits.parts}`));
55
- })
56
- bb.on('close', () => {
57
- resolve(formData);
58
- });
59
- bb.on('error', err => {
60
- reject(err);
61
- })
62
- nodeReadable.pipe(bb);
63
- })
64
- }
65
- }
@@ -1,7 +0,0 @@
1
- const { fileURLToPath } = require('url');
2
- const { createReadStream } = require('fs');
3
- module.exports = function handleFileRequest(url, Response) {
4
- return new Response(
5
- createReadStream(fileURLToPath(url)),
6
- )
7
- }
@@ -1,26 +0,0 @@
1
- const streams = require('stream');
2
-
3
- module.exports = function readableStreamToReadable(readableStream) {
4
- const reader = readableStream.getReader();
5
- return new streams.Readable({
6
- read() {
7
- reader.read().then(({ done, value }) => {
8
- if (done) {
9
- this.push(null);
10
- } else {
11
- this.push(value);
12
- }
13
- })
14
- },
15
- async destroy(err, callback) {
16
- try {
17
- reader.cancel();
18
- reader.releaseLock();
19
- await readableStream.cancel();
20
- callback();
21
- } catch (error) {
22
- callback(error);
23
- }
24
- }
25
- })
26
- }