From 06263695266c86595fe0ec4a2b0563ce8c469086 Mon Sep 17 00:00:00 2001 From: Khafra Date: Sat, 24 Aug 2024 12:27:34 -0400 Subject: [PATCH 01/10] remove third party everything support in fetch --- lib/web/fetch/formdata.js | 4 +-- lib/web/fetch/request.js | 3 +- lib/web/fetch/response.js | 4 +-- lib/web/fetch/webidl.js | 14 +++------ lib/web/websocket/websocket.js | 2 +- package.json | 3 -- test/fetch/abort.js | 31 ------------------- test/fetch/formdata.js | 18 ----------- .../jsdom-abortcontroller-1910-1464495619.js | 27 ---------------- test/fetch/request.js | 24 -------------- test/fetch/response.js | 18 ----------- types/webidl.d.ts | 5 +-- 12 files changed, 13 insertions(+), 140 deletions(-) delete mode 100644 test/fetch/jsdom-abortcontroller-1910-1464495619.js diff --git a/lib/web/fetch/formdata.js b/lib/web/fetch/formdata.js index 43e39068d5e..1bfbce5994c 100644 --- a/lib/web/fetch/formdata.js +++ b/lib/web/fetch/formdata.js @@ -41,7 +41,7 @@ class FormData { name = webidl.converters.USVString(name) value = isBlobLike(value) - ? webidl.converters.Blob(value, prefix, 'value', { strict: false }) + ? webidl.converters.Blob(value, prefix, 'value') : webidl.converters.USVString(value) filename = arguments.length === 3 ? webidl.converters.USVString(filename) @@ -137,7 +137,7 @@ class FormData { name = webidl.converters.USVString(name) value = isBlobLike(value) - ? webidl.converters.Blob(value, prefix, 'name', { strict: false }) + ? webidl.converters.Blob(value, prefix, 'name') : webidl.converters.USVString(value) filename = arguments.length === 3 ? webidl.converters.USVString(filename) diff --git a/lib/web/fetch/request.js b/lib/web/fetch/request.js index ca0153922af..a0527d3abf7 100644 --- a/lib/web/fetch/request.js +++ b/lib/web/fetch/request.js @@ -1011,8 +1011,7 @@ webidl.converters.RequestInit = webidl.dictionaryConverter([ (signal) => webidl.converters.AbortSignal( signal, 'RequestInit', - 'signal', - { strict: false } + 'signal' ) ) }, diff --git a/lib/web/fetch/response.js b/lib/web/fetch/response.js index 155dbadd1ad..0a3e35fd386 100644 --- a/lib/web/fetch/response.js +++ b/lib/web/fetch/response.js @@ -542,7 +542,7 @@ webidl.converters.XMLHttpRequestBodyInit = function (V, prefix, name) { } if (isBlobLike(V)) { - return webidl.converters.Blob(V, prefix, name, { strict: false }) + return webidl.converters.Blob(V, prefix, name) } if (ArrayBuffer.isView(V) || types.isArrayBuffer(V)) { @@ -550,7 +550,7 @@ webidl.converters.XMLHttpRequestBodyInit = function (V, prefix, name) { } if (util.isFormDataLike(V)) { - return webidl.converters.FormData(V, prefix, name, { strict: false }) + return webidl.converters.FormData(V, prefix, name) } if (V instanceof URLSearchParams) { diff --git a/lib/web/fetch/webidl.js b/lib/web/fetch/webidl.js index de185fdea3a..2238337841c 100644 --- a/lib/web/fetch/webidl.js +++ b/lib/web/fetch/webidl.js @@ -33,14 +33,8 @@ webidl.errors.invalidArgument = function (context) { } // https://webidl.spec.whatwg.org/#implements -webidl.brandCheck = function (V, I, opts) { - if (opts?.strict !== false && !(V instanceof I)) { - const err = new TypeError('Illegal invocation') - err.code = 'ERR_INVALID_THIS' // node compat. - throw err - } else if (V?.[Symbol.toStringTag] !== I.prototype[Symbol.toStringTag]) { - // TODO(@KhafraDev): this check does not work properly on extended classes - +webidl.brandCheck = function (V, I) { + if (!(V instanceof I)) { const err = new TypeError('Illegal invocation') err.code = 'ERR_INVALID_THIS' // node compat. throw err @@ -346,8 +340,8 @@ webidl.recordConverter = function (keyConverter, valueConverter) { } webidl.interfaceConverter = function (i) { - return (V, prefix, argument, opts) => { - if (opts?.strict !== false && !(V instanceof i)) { + return (V, prefix, argument) => { + if (!(V instanceof i)) { throw webidl.errors.exception({ header: prefix, message: `Expected ${argument} ("${webidl.util.Stringify(V)}") to be an instance of ${i.name}.` diff --git a/lib/web/websocket/websocket.js b/lib/web/websocket/websocket.js index d8991fe3cf3..1d46f4bebb8 100644 --- a/lib/web/websocket/websocket.js +++ b/lib/web/websocket/websocket.js @@ -792,7 +792,7 @@ webidl.converters['DOMString or sequence or WebSocketInit'] = functio webidl.converters.WebSocketSendData = function (V) { if (webidl.util.Type(V) === 'Object') { if (isBlobLike(V)) { - return webidl.converters.Blob(V, { strict: false }) + return webidl.converters.Blob(V) } if (ArrayBuffer.isView(V) || types.isArrayBuffer(V)) { diff --git a/package.json b/package.json index a2dfab7ddca..5737e0c74eb 100644 --- a/package.json +++ b/package.json @@ -113,12 +113,9 @@ "dns-packet": "^5.4.0", "eslint": "^9.9.0", "fast-check": "^3.17.1", - "form-data": "^4.0.0", - "formdata-node": "^6.0.3", "https-pem": "^3.0.0", "husky": "^9.0.7", "jest": "^29.0.2", - "jsdom": "^24.0.0", "neostandard": "^0.11.2", "node-forge": "^1.3.1", "pre-commit": "^1.2.2", diff --git a/test/fetch/abort.js b/test/fetch/abort.js index 018c9a10b68..73b4764fe81 100644 --- a/test/fetch/abort.js +++ b/test/fetch/abort.js @@ -6,39 +6,8 @@ const { tspl } = require('@matteo.collina/tspl') const { fetch } = require('../..') const { createServer } = require('node:http') const { once } = require('node:events') - const { closeServerAsPromise } = require('../utils/node-http') -const { AbortController: NPMAbortController } = require('abort-controller') - -test('Allow the usage of custom implementation of AbortController', async (t) => { - const body = { - fixes: 1605 - } - - const server = createServer((req, res) => { - res.statusCode = 200 - res.end(JSON.stringify(body)) - }) - - t.after(closeServerAsPromise(server)) - - server.listen(0) - await once(server, 'listening') - - const controller = new NPMAbortController() - const signal = controller.signal - controller.abort() - - try { - await fetch(`http://localhost:${server.address().port}`, { - signal - }) - } catch (e) { - assert.strictEqual(e.code, DOMException.ABORT_ERR) - } -}) - test('allows aborting with custom errors', async (t) => { const server = createServer().listen(0) diff --git a/test/fetch/formdata.js b/test/fetch/formdata.js index 31b563b0763..997e77ebabf 100644 --- a/test/fetch/formdata.js +++ b/test/fetch/formdata.js @@ -4,10 +4,8 @@ const { test } = require('node:test') const assert = require('node:assert') const { tspl } = require('@matteo.collina/tspl') const { FormData, Response, Request } = require('../../') -const { Blob: ThirdPartyBlob } = require('formdata-node') const { Blob, File } = require('node:buffer') const { isFormDataLike } = require('../../lib/core/util') -const ThirdPartyFormDataInvalid = require('form-data') test('arg validation', () => { const form = new FormData() @@ -110,17 +108,6 @@ test('append blob', async () => { assert.strictEqual(form.get('asd'), null) }) -test('append third-party blob', async () => { - const form = new FormData() - form.set('asd', new ThirdPartyBlob(['asd1'], { type: 'text/plain' })) - - assert.strictEqual(form.has('asd'), true) - assert.strictEqual(form.get('asd').type, 'text/plain') - assert.strictEqual(await form.get('asd').text(), 'asd1') - form.delete('asd') - assert.strictEqual(form.get('asd'), null) -}) - test('append string', () => { const form = new FormData() form.set('k1', 'v1') @@ -304,11 +291,6 @@ test('formData should be an instance of FormData', async (t) => { assert.strictEqual(isFormDataLike(form), false) }) - await t.test('Invalid third-party FormData', () => { - const form = new ThirdPartyFormDataInvalid() - assert.strictEqual(isFormDataLike(form), false) - }) - await t.test('Valid FormData', () => { const form = new FormData() assert.strictEqual(isFormDataLike(form), true) diff --git a/test/fetch/jsdom-abortcontroller-1910-1464495619.js b/test/fetch/jsdom-abortcontroller-1910-1464495619.js deleted file mode 100644 index b59ef9f95ca..00000000000 --- a/test/fetch/jsdom-abortcontroller-1910-1464495619.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict' - -const { test } = require('node:test') -const assert = require('node:assert') -const { createServer } = require('node:http') -const { once } = require('node:events') -const { fetch } = require('../..') -const { JSDOM } = require('jsdom') - -// https://github.com/nodejs/undici/pull/1910#issuecomment-1464495619 -test('third party AbortControllers', async (t) => { - const server = createServer((_, res) => res.end()).listen(0) - - const { AbortController } = new JSDOM().window - let controller = new AbortController() - - t.after(() => { - controller.abort() - controller = null - return server.close() - }) - await once(server, 'listening') - - await assert.doesNotReject(fetch(`http://localhost:${server.address().port}`, { - signal: controller.signal - })) -}) diff --git a/test/fetch/request.js b/test/fetch/request.js index 2cc1f6437de..64ead02f0ff 100644 --- a/test/fetch/request.js +++ b/test/fetch/request.js @@ -11,10 +11,6 @@ const { fetch } = require('../../') const { fromInnerRequest, makeRequest } = require('../../lib/web/fetch/request') -const { - Blob: ThirdPartyBlob, - FormData: ThirdPartyFormData -} = require('formdata-node') const { kState, kSignal, kHeaders } = require('../../lib/web/fetch/symbols') const { getHeadersGuard, getHeadersList } = require('../../lib/web/fetch/headers') @@ -419,26 +415,6 @@ test('RequestInit.signal option', async () => { }), TypeError) }) -test('constructing Request with third party Blob body', async () => { - const blob = new ThirdPartyBlob(['text']) - const req = new Request('http://asd', { - method: 'POST', - body: blob - }) - assert.strictEqual(await req.text(), 'text') -}) -test('constructing Request with third party FormData body', async () => { - const form = new ThirdPartyFormData() - form.set('key', 'value') - const req = new Request('http://asd', { - method: 'POST', - body: form - }) - const contentType = req.headers.get('content-type').split('=') - assert.strictEqual(contentType[0], 'multipart/form-data; boundary') - assert.ok((await req.text()).startsWith(`--${contentType[1]}`)) -}) - // https://github.com/nodejs/undici/issues/2050 test('set-cookie headers get cleared when passing a Request as first param', () => { const req1 = new Request('http://localhost', { diff --git a/test/fetch/response.js b/test/fetch/response.js index 8e7c865c78e..58c04a27422 100644 --- a/test/fetch/response.js +++ b/test/fetch/response.js @@ -10,10 +10,6 @@ const { FormData } = require('../../') const { fromInnerResponse, makeResponse } = require('../../lib/web/fetch/response') -const { - Blob: ThirdPartyBlob, - FormData: ThirdPartyFormData -} = require('formdata-node') const { kState, kHeaders } = require('../../lib/web/fetch/symbols') const { getHeadersGuard, getHeadersList } = require('../../lib/web/fetch/headers') @@ -239,20 +235,6 @@ test('constructing a Response with a ReadableStream body', async (t) => { }) }) -test('constructing Response with third party Blob body', async () => { - const blob = new ThirdPartyBlob(['text']) - const res = new Response(blob) - assert.strictEqual(await res.text(), 'text') -}) -test('constructing Response with third party FormData body', async () => { - const form = new ThirdPartyFormData() - form.set('key', 'value') - const res = new Response(form) - const contentType = res.headers.get('content-type').split('=') - assert.strictEqual(contentType[0], 'multipart/form-data; boundary') - assert.ok((await res.text()).startsWith(`--${contentType[1]}`)) -}) - // https://github.com/nodejs/undici/issues/2465 test('Issue#2465', async (t) => { const { strictEqual } = tspl(t, { plan: 1 }) diff --git a/types/webidl.d.ts b/types/webidl.d.ts index 7ea596cafd0..eebe99c801c 100644 --- a/types/webidl.d.ts +++ b/types/webidl.d.ts @@ -167,7 +167,7 @@ export interface Webidl { * @description Performs a brand-check on {@param V} to ensure it is a * {@param cls} object. */ - brandCheck unknown>(V: unknown, cls: Interface, opts?: { strict?: boolean }): asserts V is Interface + brandCheck unknown>(V: unknown, cls: Interface): asserts V is Interface brandCheckMultiple unknown)[]> (V: unknown, list: Interfaces): asserts V is Interfaces[number] @@ -194,7 +194,8 @@ export interface Webidl { */ interfaceConverter (cls: Interface): ( V: unknown, - opts?: { strict: boolean } + prefix: string, + argument: string ) => asserts V is typeof cls // TODO(@KhafraDev): a type could likely be implemented that can infer the return type From 18bdcd9cb5a44b9dc8710842234ed41dd17162ef Mon Sep 17 00:00:00 2001 From: Khafra Date: Sat, 24 Aug 2024 12:40:45 -0400 Subject: [PATCH 02/10] fixup --- test/issue-2065.js | 43 ------------------------------------------- 1 file changed, 43 deletions(-) diff --git a/test/issue-2065.js b/test/issue-2065.js index b84fe04085b..9037fb557c3 100644 --- a/test/issue-2065.js +++ b/test/issue-2065.js @@ -4,7 +4,6 @@ const { tspl } = require('@matteo.collina/tspl') const { test, after } = require('node:test') const { createServer } = require('node:http') const { once } = require('node:events') -const { createReadStream } = require('node:fs') const { FormData, request } = require('..') const { File } = require('node:buffer') @@ -27,45 +26,3 @@ test('undici.request with a FormData body should set content-length header', asy body }) }) - -test('undici.request with a FormData stream value should set transfer-encoding header', async (t) => { - t = tspl(t, { plan: 1 }) - - const server = createServer((req, res) => { - t.strictEqual(req.headers['transfer-encoding'], 'chunked') - res.end() - }).listen(0) - - after(() => server.close()) - await once(server, 'listening') - - class BlobFromStream { - #stream - #type - constructor (stream, type) { - this.#stream = stream - this.#type = type - } - - stream () { - return this.#stream - } - - get type () { - return this.#type - } - - get [Symbol.toStringTag] () { - return 'Blob' - } - } - - const body = new FormData() - const fileReadable = createReadStream(__filename) - body.set('file', new BlobFromStream(fileReadable, '.js'), 'streamfile') - - await request(`http://localhost:${server.address().port}`, { - method: 'POST', - body - }) -}) From 7dde356aa40f73d8a65458ab69a0b55269f1dcca Mon Sep 17 00:00:00 2001 From: Khafra Date: Sat, 24 Aug 2024 12:44:45 -0400 Subject: [PATCH 03/10] fixup --- test/node-fetch/main.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/node-fetch/main.js b/test/node-fetch/main.js index 2360620740c..6d2e73b455d 100644 --- a/test/node-fetch/main.js +++ b/test/node-fetch/main.js @@ -919,9 +919,9 @@ describe('node-fetch', () => { it('should throw a TypeError if a signal is not of type AbortSignal or EventTarget', () => { return Promise.all([ - assert.rejects(fetch(`${base}inspect`, { signal: {} }), new TypeError("Failed to construct 'Request': member signal is not of type AbortSignal.")), - assert.rejects(fetch(`${base}inspect`, { signal: '' }), new TypeError("Failed to construct 'Request': member signal is not of type AbortSignal.")), - assert.rejects(fetch(`${base}inspect`, { signal: Object.create(null) }), new TypeError("Failed to construct 'Request': member signal is not of type AbortSignal.")) + assert.rejects(fetch(`${base}inspect`, { signal: {} }), new TypeError('RequestInit: Expected signal ("{}") to be an instance of AbortSignal.')), + assert.rejects(fetch(`${base}inspect`, { signal: '' }), new TypeError('RequestInit: Expected signal ("""") to be an instance of AbortSignal.')), + assert.rejects(fetch(`${base}inspect`, { signal: Object.create(null) }), new TypeError('RequestInit: Expected signal ("[Object: null prototype] {}") to be an instance of AbortSignal.')) ]) }) From 2bc211090d4eb8bbaad363f35754e614fe3b9a5f Mon Sep 17 00:00:00 2001 From: Khafra Date: Sat, 24 Aug 2024 13:09:55 -0400 Subject: [PATCH 04/10] test --- lib/web/websocket/websocket.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/web/websocket/websocket.js b/lib/web/websocket/websocket.js index 1d46f4bebb8..6eb7835c415 100644 --- a/lib/web/websocket/websocket.js +++ b/lib/web/websocket/websocket.js @@ -792,7 +792,9 @@ webidl.converters['DOMString or sequence or WebSocketInit'] = functio webidl.converters.WebSocketSendData = function (V) { if (webidl.util.Type(V) === 'Object') { if (isBlobLike(V)) { - return webidl.converters.Blob(V) + console.log(V) + // return webidl.converters.Blob(V) + return V } if (ArrayBuffer.isView(V) || types.isArrayBuffer(V)) { From 8c6da343278175bf87a4134ef113febee4f748ce Mon Sep 17 00:00:00 2001 From: Khafra Date: Sat, 24 Aug 2024 13:23:09 -0400 Subject: [PATCH 05/10] test --- lib/web/websocket/websocket.js | 4 +--- test/autobahn/client.js | 7 ++++++- test/autobahn/config/fuzzingserver.json | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/web/websocket/websocket.js b/lib/web/websocket/websocket.js index 6eb7835c415..1d46f4bebb8 100644 --- a/lib/web/websocket/websocket.js +++ b/lib/web/websocket/websocket.js @@ -792,9 +792,7 @@ webidl.converters['DOMString or sequence or WebSocketInit'] = functio webidl.converters.WebSocketSendData = function (V) { if (webidl.util.Type(V) === 'Object') { if (isBlobLike(V)) { - console.log(V) - // return webidl.converters.Blob(V) - return V + return webidl.converters.Blob(V) } if (ArrayBuffer.isView(V) || types.isArrayBuffer(V)) { diff --git a/test/autobahn/client.js b/test/autobahn/client.js index 3f013922839..86b3d3f8c65 100644 --- a/test/autobahn/client.js +++ b/test/autobahn/client.js @@ -24,7 +24,12 @@ function nextTest () { `${autobahnFuzzingserverUrl}/runCase?case=${currentTest}&agent=undici` ) ws.addEventListener('message', (data) => { - ws.send(data.data) + try { + ws.send(data.data) + console.log('sending', data.data) + } catch (e) { + console.log('error', e) + } }) ws.addEventListener('close', () => { currentTest++ diff --git a/test/autobahn/config/fuzzingserver.json b/test/autobahn/config/fuzzingserver.json index e21b420ffa5..9da3784c227 100644 --- a/test/autobahn/config/fuzzingserver.json +++ b/test/autobahn/config/fuzzingserver.json @@ -1,7 +1,7 @@ { "url": "ws://127.0.0.1:9001", "outdir": "./reports/clients", - "cases": ["*"], + "cases": ["12.4.3", "12.4.4", "12.4.5", "12.4.6", "12.4.7", "12.4.8", "12.4.9", "12.4.10", "12.4.11", "12.4.12", "12.4.13", "12.4.14", "12.4.15", "12.4.16", "12.4.17", "12.4.18"], "exclude-cases": [], "exclude-agent-cases": {} } From c397421ba412cc4a510f74ece0541519865e4b9c Mon Sep 17 00:00:00 2001 From: Khafra Date: Sat, 24 Aug 2024 13:25:08 -0400 Subject: [PATCH 06/10] test --- test/autobahn/client.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/autobahn/client.js b/test/autobahn/client.js index 86b3d3f8c65..f7018163137 100644 --- a/test/autobahn/client.js +++ b/test/autobahn/client.js @@ -26,7 +26,7 @@ function nextTest () { ws.addEventListener('message', (data) => { try { ws.send(data.data) - console.log('sending', data.data) + console.log('sending', typeof data.data) } catch (e) { console.log('error', e) } From 19e81516a0d4359ccb3681cfc1aa6955ffd7fcc8 Mon Sep 17 00:00:00 2001 From: Khafra Date: Sat, 24 Aug 2024 13:42:32 -0400 Subject: [PATCH 07/10] fixup --- test/autobahn/client.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/test/autobahn/client.js b/test/autobahn/client.js index f7018163137..3f013922839 100644 --- a/test/autobahn/client.js +++ b/test/autobahn/client.js @@ -24,12 +24,7 @@ function nextTest () { `${autobahnFuzzingserverUrl}/runCase?case=${currentTest}&agent=undici` ) ws.addEventListener('message', (data) => { - try { - ws.send(data.data) - console.log('sending', typeof data.data) - } catch (e) { - console.log('error', e) - } + ws.send(data.data) }) ws.addEventListener('close', () => { currentTest++ From 1c8f3a4729b15aabad1b4742a519848d5c975c4a Mon Sep 17 00:00:00 2001 From: Khafra Date: Sat, 24 Aug 2024 13:43:06 -0400 Subject: [PATCH 08/10] fixup --- test/autobahn/config/fuzzingserver.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/autobahn/config/fuzzingserver.json b/test/autobahn/config/fuzzingserver.json index 9da3784c227..e21b420ffa5 100644 --- a/test/autobahn/config/fuzzingserver.json +++ b/test/autobahn/config/fuzzingserver.json @@ -1,7 +1,7 @@ { "url": "ws://127.0.0.1:9001", "outdir": "./reports/clients", - "cases": ["12.4.3", "12.4.4", "12.4.5", "12.4.6", "12.4.7", "12.4.8", "12.4.9", "12.4.10", "12.4.11", "12.4.12", "12.4.13", "12.4.14", "12.4.15", "12.4.16", "12.4.17", "12.4.18"], + "cases": ["*"], "exclude-cases": [], "exclude-agent-cases": {} } From f1ef160be33ddd39dc58ae9d3475ae8c1f908111 Mon Sep 17 00:00:00 2001 From: Khafra Date: Tue, 27 Aug 2024 11:08:15 -0400 Subject: [PATCH 09/10] fixup --- lib/web/fetch/request.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/lib/web/fetch/request.js b/lib/web/fetch/request.js index a0527d3abf7..b82127f0c6b 100644 --- a/lib/web/fetch/request.js +++ b/lib/web/fetch/request.js @@ -400,16 +400,6 @@ class Request { // 29. If signal is not null, then make this’s signal follow signal. if (signal != null) { - if ( - !signal || - typeof signal.aborted !== 'boolean' || - typeof signal.addEventListener !== 'function' - ) { - throw new TypeError( - "Failed to construct 'Request': member signal is not of type AbortSignal." - ) - } - if (signal.aborted) { ac.abort(signal.reason) } else { From 358faba1a1ac9928089f6bbc0bc4e1c14120402b Mon Sep 17 00:00:00 2001 From: Khafra Date: Tue, 27 Aug 2024 11:50:50 -0400 Subject: [PATCH 10/10] fixup! remove is*Like, *Like --- lib/web/fetch/body.js | 10 +-- lib/web/fetch/file.js | 126 ------------------------------- lib/web/fetch/formdata-parser.js | 3 +- lib/web/fetch/formdata.js | 21 ++---- lib/web/fetch/index.js | 3 +- lib/web/fetch/response.js | 5 +- lib/web/fetch/util.js | 11 +-- lib/web/fetch/webidl.js | 2 + lib/web/websocket/websocket.js | 6 +- 9 files changed, 22 insertions(+), 165 deletions(-) delete mode 100644 lib/web/fetch/file.js diff --git a/lib/web/fetch/body.js b/lib/web/fetch/body.js index 464e7b50e5c..683a67400c8 100644 --- a/lib/web/fetch/body.js +++ b/lib/web/fetch/body.js @@ -3,8 +3,6 @@ const util = require('../../core/util') const { ReadableStreamFrom, - isBlobLike, - isReadableStreamLike, readableStreamClose, createDeferredPromise, fullyReadBody, @@ -44,7 +42,7 @@ function extractBody (object, keepalive = false) { // 2. If object is a ReadableStream object, then set stream to object. if (object instanceof ReadableStream) { stream = object - } else if (isBlobLike(object)) { + } else if (object instanceof Blob) { // 3. Otherwise, if object is a Blob object, set stream to the // result of running object’s get stream. stream = object.stream() @@ -67,7 +65,7 @@ function extractBody (object, keepalive = false) { } // 5. Assert: stream is a ReadableStream object. - assert(isReadableStreamLike(stream)) + assert(stream instanceof ReadableStream) // 6. Let action be null. let action = null @@ -112,7 +110,7 @@ function extractBody (object, keepalive = false) { // Set source to a copy of the bytes held by object. source = new Uint8Array(object.buffer.slice(object.byteOffset, object.byteOffset + object.byteLength)) - } else if (util.isFormDataLike(object)) { + } else if (object instanceof FormData) { const boundary = `----formdata-undici-0${`${Math.floor(Math.random() * 1e11)}`.padStart(11, '0')}` const prefix = `--${boundary}\r\nContent-Disposition: form-data` @@ -178,7 +176,7 @@ function extractBody (object, keepalive = false) { // followed by the multipart/form-data boundary string generated // by the multipart/form-data encoding algorithm. type = `multipart/form-data; boundary=${boundary}` - } else if (isBlobLike(object)) { + } else if (object instanceof Blob) { // Blob // Set source to object. diff --git a/lib/web/fetch/file.js b/lib/web/fetch/file.js deleted file mode 100644 index 31ba40718ec..00000000000 --- a/lib/web/fetch/file.js +++ /dev/null @@ -1,126 +0,0 @@ -'use strict' - -const { Blob, File } = require('node:buffer') -const { kState } = require('./symbols') -const { webidl } = require('./webidl') - -// TODO(@KhafraDev): remove -class FileLike { - constructor (blobLike, fileName, options = {}) { - // TODO: argument idl type check - - // The File constructor is invoked with two or three parameters, depending - // on whether the optional dictionary parameter is used. When the File() - // constructor is invoked, user agents must run the following steps: - - // 1. Let bytes be the result of processing blob parts given fileBits and - // options. - - // 2. Let n be the fileName argument to the constructor. - const n = fileName - - // 3. Process FilePropertyBag dictionary argument by running the following - // substeps: - - // 1. If the type member is provided and is not the empty string, let t - // be set to the type dictionary member. If t contains any characters - // outside the range U+0020 to U+007E, then set t to the empty string - // and return from these substeps. - // TODO - const t = options.type - - // 2. Convert every character in t to ASCII lowercase. - // TODO - - // 3. If the lastModified member is provided, let d be set to the - // lastModified dictionary member. If it is not provided, set d to the - // current date and time represented as the number of milliseconds since - // the Unix Epoch (which is the equivalent of Date.now() [ECMA-262]). - const d = options.lastModified ?? Date.now() - - // 4. Return a new File object F such that: - // F refers to the bytes byte sequence. - // F.size is set to the number of total bytes in bytes. - // F.name is set to n. - // F.type is set to t. - // F.lastModified is set to d. - - this[kState] = { - blobLike, - name: n, - type: t, - lastModified: d - } - } - - stream (...args) { - webidl.brandCheck(this, FileLike) - - return this[kState].blobLike.stream(...args) - } - - arrayBuffer (...args) { - webidl.brandCheck(this, FileLike) - - return this[kState].blobLike.arrayBuffer(...args) - } - - slice (...args) { - webidl.brandCheck(this, FileLike) - - return this[kState].blobLike.slice(...args) - } - - text (...args) { - webidl.brandCheck(this, FileLike) - - return this[kState].blobLike.text(...args) - } - - get size () { - webidl.brandCheck(this, FileLike) - - return this[kState].blobLike.size - } - - get type () { - webidl.brandCheck(this, FileLike) - - return this[kState].blobLike.type - } - - get name () { - webidl.brandCheck(this, FileLike) - - return this[kState].name - } - - get lastModified () { - webidl.brandCheck(this, FileLike) - - return this[kState].lastModified - } - - get [Symbol.toStringTag] () { - return 'File' - } -} - -webidl.converters.Blob = webidl.interfaceConverter(Blob) - -// If this function is moved to ./util.js, some tools (such as -// rollup) will warn about circular dependencies. See: -// https://github.com/nodejs/undici/issues/1629 -function isFileLike (object) { - return ( - (object instanceof File) || - ( - object && - (typeof object.stream === 'function' || - typeof object.arrayBuffer === 'function') && - object[Symbol.toStringTag] === 'File' - ) - ) -} - -module.exports = { FileLike, isFileLike } diff --git a/lib/web/fetch/formdata-parser.js b/lib/web/fetch/formdata-parser.js index 7e567e9ec65..90d4d2c7817 100644 --- a/lib/web/fetch/formdata-parser.js +++ b/lib/web/fetch/formdata-parser.js @@ -3,7 +3,6 @@ const { isUSVString, bufferToLowerCasedHeaderName } = require('../../core/util') const { utf8DecodeBytes } = require('./util') const { HTTP_TOKEN_CODEPOINTS, isomorphicDecode } = require('./data-url') -const { isFileLike } = require('./file') const { makeEntry } = require('./formdata') const assert = require('node:assert') const { File: NodeFile } = require('node:buffer') @@ -195,7 +194,7 @@ function multipartFormDataParser (input, mimeType) { // 5.12. Assert: name is a scalar value string and value is either a scalar value string or a File object. assert(isUSVString(name)) - assert((typeof value === 'string' && isUSVString(value)) || isFileLike(value)) + assert((typeof value === 'string' && isUSVString(value)) || value instanceof File) // 5.13. Create an entry with name and value, and append it to entry list. entryList.push(makeEntry(name, value, filename)) diff --git a/lib/web/fetch/formdata.js b/lib/web/fetch/formdata.js index 1bfbce5994c..9feea1a0573 100644 --- a/lib/web/fetch/formdata.js +++ b/lib/web/fetch/formdata.js @@ -1,9 +1,8 @@ 'use strict' -const { isBlobLike, iteratorMixin } = require('./util') +const { iteratorMixin } = require('./util') const { kState } = require('./symbols') const { kEnumerableProperty } = require('../../core/util') -const { FileLike, isFileLike } = require('./file') const { webidl } = require('./webidl') const { File: NativeFile } = require('node:buffer') const nodeUtil = require('node:util') @@ -31,7 +30,7 @@ class FormData { const prefix = 'FormData.append' webidl.argumentLengthCheck(arguments, 2, prefix) - if (arguments.length === 3 && !isBlobLike(value)) { + if (arguments.length === 3 && !(value instanceof Blob)) { throw new TypeError( "Failed to execute 'append' on 'FormData': parameter 2 is not of type 'Blob'" ) @@ -40,7 +39,7 @@ class FormData { // 1. Let value be value if given; otherwise blobValue. name = webidl.converters.USVString(name) - value = isBlobLike(value) + value = value instanceof Blob ? webidl.converters.Blob(value, prefix, 'value') : webidl.converters.USVString(value) filename = arguments.length === 3 @@ -124,7 +123,7 @@ class FormData { const prefix = 'FormData.set' webidl.argumentLengthCheck(arguments, 2, prefix) - if (arguments.length === 3 && !isBlobLike(value)) { + if (arguments.length === 3 && !(value instanceof Blob)) { throw new TypeError( "Failed to execute 'set' on 'FormData': parameter 2 is not of type 'Blob'" ) @@ -136,7 +135,7 @@ class FormData { // 1. Let value be value if given; otherwise blobValue. name = webidl.converters.USVString(name) - value = isBlobLike(value) + value = value instanceof Blob ? webidl.converters.Blob(value, prefix, 'name') : webidl.converters.USVString(value) filename = arguments.length === 3 @@ -222,10 +221,8 @@ function makeEntry (name, value, filename) { // 1. If value is not a File object, then set value to a new File object, // representing the same bytes, whose name attribute value is "blob" - if (!isFileLike(value)) { - value = value instanceof Blob - ? new File([value], 'blob', { type: value.type }) - : new FileLike(value, 'blob', { type: value.type }) + if (!(value instanceof File)) { + value = new File([value], 'blob', { type: value.type }) } // 2. If filename is given, then set value to a new File object, @@ -237,9 +234,7 @@ function makeEntry (name, value, filename) { lastModified: value.lastModified } - value = value instanceof NativeFile - ? new File([value], filename, options) - : new FileLike(value, filename, options) + value = new File([value], filename, options) } } diff --git a/lib/web/fetch/index.js b/lib/web/fetch/index.js index f9b3629c07b..025248d9742 100644 --- a/lib/web/fetch/index.js +++ b/lib/web/fetch/index.js @@ -30,7 +30,6 @@ const { determineRequestsReferrer, coarsenedSharedCurrentTime, createDeferredPromise, - isBlobLike, sameOrigin, isCancelled, isAborted, @@ -810,7 +809,7 @@ function schemeFetch (fetchParams) { // 2. If request’s method is not `GET`, blobURLEntry is null, or blobURLEntry’s // object is not a Blob object, then return a network error. - if (request.method !== 'GET' || !isBlobLike(blob)) { + if (request.method !== 'GET' || !(blob instanceof Blob)) { return Promise.resolve(makeNetworkError('invalid method')) } diff --git a/lib/web/fetch/response.js b/lib/web/fetch/response.js index 0a3e35fd386..252addc2286 100644 --- a/lib/web/fetch/response.js +++ b/lib/web/fetch/response.js @@ -9,7 +9,6 @@ const { isValidReasonPhrase, isCancelled, isAborted, - isBlobLike, serializeJavascriptValueToJSONString, isErrorLike, isomorphicEncode, @@ -541,7 +540,7 @@ webidl.converters.XMLHttpRequestBodyInit = function (V, prefix, name) { return webidl.converters.USVString(V, prefix, name) } - if (isBlobLike(V)) { + if (V instanceof Blob) { return webidl.converters.Blob(V, prefix, name) } @@ -549,7 +548,7 @@ webidl.converters.XMLHttpRequestBodyInit = function (V, prefix, name) { return webidl.converters.BufferSource(V, prefix, name) } - if (util.isFormDataLike(V)) { + if (V instanceof FormData) { return webidl.converters.FormData(V, prefix, name) } diff --git a/lib/web/fetch/util.js b/lib/web/fetch/util.js index 623b475854f..28d54fe946c 100644 --- a/lib/web/fetch/util.js +++ b/lib/web/fetch/util.js @@ -6,7 +6,7 @@ const { redirectStatusSet, referrerPolicySet: referrerPolicyTokens, badPortsSet const { getGlobalOrigin } = require('./global') const { collectASequenceOfCodePoints, collectAnHTTPQuotedString, removeChars, parseMIMEType } = require('./data-url') const { performance } = require('node:perf_hooks') -const { isBlobLike, ReadableStreamFrom, isValidHTTPToken, normalizedMethodRecordsBase } = require('../../core/util') +const { ReadableStreamFrom, isValidHTTPToken, normalizedMethodRecordsBase } = require('../../core/util') const assert = require('node:assert') const { isUint8Array } = require('node:util/types') const { webidl } = require('./webidl') @@ -1061,13 +1061,6 @@ async function fullyReadBody (body, processBody, processBodyError) { } } -function isReadableStreamLike (stream) { - return stream instanceof ReadableStream || ( - stream[Symbol.toStringTag] === 'ReadableStream' && - typeof stream.tee === 'function' - ) -} - /** * @param {ReadableStreamController} controller */ @@ -1589,7 +1582,6 @@ module.exports = { requestCurrentURL, responseURL, responseLocationURL, - isBlobLike, isURLPotentiallyTrustworthy, isValidReasonPhrase, sameOrigin, @@ -1602,7 +1594,6 @@ module.exports = { isErrorLike, fullyReadBody, bytesMatch, - isReadableStreamLike, readableStreamClose, isomorphicEncode, urlIsLocal, diff --git a/lib/web/fetch/webidl.js b/lib/web/fetch/webidl.js index 2238337841c..ef0e6dc27f1 100644 --- a/lib/web/fetch/webidl.js +++ b/lib/web/fetch/webidl.js @@ -688,6 +688,8 @@ webidl.converters['record'] = webidl.recordConverter( webidl.converters.ByteString ) +webidl.converters.Blob = webidl.interfaceConverter(Blob) + module.exports = { webidl } diff --git a/lib/web/websocket/websocket.js b/lib/web/websocket/websocket.js index d62310b9643..96d6c4e453d 100644 --- a/lib/web/websocket/websocket.js +++ b/lib/web/websocket/websocket.js @@ -17,7 +17,7 @@ const { } = require('./util') const { establishWebSocketConnection, closeWebSocketConnection } = require('./connection') const { ByteParser } = require('./receiver') -const { kEnumerableProperty, isBlobLike } = require('../../core/util') +const { kEnumerableProperty } = require('../../core/util') const { getGlobalDispatcher } = require('../../global') const { types } = require('node:util') const { ErrorEvent, CloseEvent, createFastMessageEvent } = require('./events') @@ -324,7 +324,7 @@ class WebSocket extends EventTarget { this.#sendQueue.add(data, () => { this.#bufferedAmount -= data.byteLength }, sendHints.typedArray) - } else if (isBlobLike(data)) { + } else if (data instanceof Blob) { // If the WebSocket connection is established, and the WebSocket // closing handshake has not yet started, then the user agent must // send a WebSocket Message comprised of data using a binary frame @@ -793,7 +793,7 @@ webidl.converters['DOMString or sequence or WebSocketInit'] = functio webidl.converters.WebSocketSendData = function (V) { if (webidl.util.Type(V) === 'Object') { - if (isBlobLike(V)) { + if (V instanceof Blob) { return webidl.converters.Blob(V) }