From b74198f9b37e45f76d86ec6d002efd25a3f99120 Mon Sep 17 00:00:00 2001 From: Khafra <42794878+KhafraDev@users.noreply.github.com> Date: Tue, 31 May 2022 17:07:58 -0400 Subject: [PATCH 1/3] feat: implement `FormData Iterator` --- lib/fetch/formdata.js | 53 ++++++++----- lib/fetch/headers.js | 34 +------- lib/fetch/util.js | 30 ++++++- test/fetch/headers-iterator.js | 71 ----------------- test/fetch/iterators.js | 140 +++++++++++++++++++++++++++++++++ 5 files changed, 205 insertions(+), 123 deletions(-) delete mode 100644 test/fetch/headers-iterator.js create mode 100644 test/fetch/iterators.js diff --git a/lib/fetch/formdata.js b/lib/fetch/formdata.js index 40df747e843..77cfec0e02c 100644 --- a/lib/fetch/formdata.js +++ b/lib/fetch/formdata.js @@ -1,6 +1,6 @@ 'use strict' -const { isBlobLike, isFileLike, toUSVString } = require('./util') +const { isBlobLike, isFileLike, toUSVString, makeIterator } = require('./util') const { kState } = require('./symbols') const { File, FileLike } = require('./file') const { Blob } = require('buffer') @@ -187,45 +187,42 @@ class FormData { return this.constructor.name } - * entries () { + entries () { if (!(this instanceof FormData)) { throw new TypeError('Illegal invocation') } - for (const pair of this) { - yield pair - } + return makeIterator( + makeIterable(this[kState], 'entries'), + 'FormData' + ) } - * keys () { + keys () { if (!(this instanceof FormData)) { throw new TypeError('Illegal invocation') } - for (const [key] of this) { - yield key - } + return makeIterator( + makeIterable(this[kState], 'keys'), + 'FormData' + ) } - * values () { + values () { if (!(this instanceof FormData)) { throw new TypeError('Illegal invocation') } - for (const [, value] of this) { - yield value - } - } - - * [Symbol.iterator] () { - // The value pairs to iterate over are this’s entry list’s entries with - // the key being the name and the value being the value. - for (const { name, value } of this[kState]) { - yield [name, value] - } + return makeIterator( + makeIterable(this[kState], 'values'), + 'FormData' + ) } } +FormData.prototype[Symbol.iterator] = FormData.prototype.entries + function makeEntry (name, value, filename) { // To create an entry for name, value, and optionally a filename, run these // steps: @@ -267,4 +264,18 @@ function makeEntry (name, value, filename) { return entry } +function * makeIterable (entries, type) { + // The value pairs to iterate over are this’s entry list’s entries + // with the key being the name and the value being the value. + for (const { name, value } of entries) { + if (type === 'entries') { + yield [name, value] + } else if (type === 'values') { + yield value + } else { + yield name + } + } +} + module.exports = { FormData } diff --git a/lib/fetch/headers.js b/lib/fetch/headers.js index 0c6e5584d33..582e0c0f576 100644 --- a/lib/fetch/headers.js +++ b/lib/fetch/headers.js @@ -6,6 +6,7 @@ const { validateHeaderName, validateHeaderValue } = require('http') const { kHeadersList } = require('../core/symbols') const { kGuard } = require('./symbols') const { kEnumerableProperty } = require('../core/util') +const { makeIterator } = require('./util') const kHeadersMap = Symbol('headers map') const kHeadersSortedMap = Symbol('headers map sorted') @@ -73,33 +74,6 @@ function fill (headers, object) { } } -// https://tc39.es/ecma262/#sec-%25iteratorprototype%25-object -const esIteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())) - -// https://webidl.spec.whatwg.org/#dfn-iterator-prototype-object -function makeHeadersIterator (iterator) { - const i = { - next () { - if (Object.getPrototypeOf(this) !== i) { - throw new TypeError( - '\'next\' called on an object that does not implement interface Headers Iterator.' - ) - } - - return iterator.next() - }, - // The class string of an iterator prototype object for a given interface is the - // result of concatenating the identifier of the interface and the string " Iterator". - [Symbol.toStringTag]: 'Headers Iterator' - } - - // The [[Prototype]] internal slot of an iterator prototype object must be %IteratorPrototype%. - Object.setPrototypeOf(i, esIteratorPrototype) - // esIteratorPrototype needs to be the prototype of i - // which is the prototype of an empty object. Yes, it's confusing. - return Object.setPrototypeOf({}, i) -} - class HeadersList { constructor (init) { if (init instanceof HeadersList) { @@ -306,7 +280,7 @@ class Headers { throw new TypeError('Illegal invocation') } - return makeHeadersIterator(this[kHeadersSortedMap].keys()) + return makeIterator(this[kHeadersSortedMap].keys(), 'Headers') } values () { @@ -314,7 +288,7 @@ class Headers { throw new TypeError('Illegal invocation') } - return makeHeadersIterator(this[kHeadersSortedMap].values()) + return makeIterator(this[kHeadersSortedMap].values(), 'Headers') } entries () { @@ -322,7 +296,7 @@ class Headers { throw new TypeError('Illegal invocation') } - return makeHeadersIterator(this[kHeadersSortedMap].entries()) + return makeIterator(this[kHeadersSortedMap].entries(), 'Headers') } /** diff --git a/lib/fetch/util.js b/lib/fetch/util.js index 1c67f9c7c84..3c088fbc6e9 100644 --- a/lib/fetch/util.js +++ b/lib/fetch/util.js @@ -361,6 +361,33 @@ function serializeJavascriptValueToJSONString (value) { return result } +// https://tc39.es/ecma262/#sec-%25iteratorprototype%25-object +const esIteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())) + +// https://webidl.spec.whatwg.org/#dfn-iterator-prototype-object +function makeIterator (iterator, name) { + const i = { + next () { + if (Object.getPrototypeOf(this) !== i) { + throw new TypeError( + `'next' called on an object that does not implement interface ${name} Iterator.` + ) + } + + return iterator.next() + }, + // The class string of an iterator prototype object for a given interface is the + // result of concatenating the identifier of the interface and the string " Iterator". + [Symbol.toStringTag]: `${name} Iterator` + } + + // The [[Prototype]] internal slot of an iterator prototype object must be %IteratorPrototype%. + Object.setPrototypeOf(i, esIteratorPrototype) + // esIteratorPrototype needs to be the prototype of i + // which is the prototype of an empty object. Yes, it's confusing. + return Object.setPrototypeOf({}, i) +} + module.exports = { isAborted, isCancelled, @@ -390,5 +417,6 @@ module.exports = { isValidReasonPhrase, sameOrigin, normalizeMethod, - serializeJavascriptValueToJSONString + serializeJavascriptValueToJSONString, + makeIterator } diff --git a/test/fetch/headers-iterator.js b/test/fetch/headers-iterator.js deleted file mode 100644 index 7c71af8162e..00000000000 --- a/test/fetch/headers-iterator.js +++ /dev/null @@ -1,71 +0,0 @@ -'use strict' - -const { test } = require('tap') -const { Headers } = require('../..') - -test('Implements "Headers Iterator" properly', (t) => { - t.test('all iterators implement Headers Iterator', (t) => { - const headers = new Headers([['a', 'b'], ['c', 'd']]) - - for (const iterable of ['keys', 'values', 'entries', Symbol.iterator]) { - const gen = headers[iterable]() - // https://tc39.es/ecma262/#sec-%25iteratorprototype%25-object - const IteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())) - const iteratorProto = Object.getPrototypeOf(gen) - - t.ok(gen.constructor === Object) - t.ok(gen.prototype === undefined) - // eslint-disable-next-line no-proto - t.equal(gen.__proto__[Symbol.toStringTag], 'Headers Iterator') - // https://github.com/node-fetch/node-fetch/issues/1119#issuecomment-100222049 - t.notOk(Headers.prototype[iterable] instanceof function * () {}.constructor) - // eslint-disable-next-line no-proto - t.ok(gen.__proto__.next.__proto__ === Function.prototype) - // https://webidl.spec.whatwg.org/#dfn-iterator-prototype-object - // "The [[Prototype]] internal slot of an iterator prototype object must be %IteratorPrototype%." - t.equal(gen[Symbol.iterator], IteratorPrototype[Symbol.iterator]) - t.equal(Object.getPrototypeOf(iteratorProto), IteratorPrototype) - } - - t.end() - }) - - t.test('Headers Iterator symbols are properly set', (t) => { - const headers = new Headers([['a', 'b'], ['c', 'd']]) - const gen = headers.entries() - - t.equal(typeof gen[Symbol.toStringTag], 'string') - t.equal(typeof gen[Symbol.iterator], 'function') - t.end() - }) - - t.test('Headers Iterator does not inherit Generator prototype methods', (t) => { - const headers = new Headers([['a', 'b'], ['c', 'd']]) - const gen = headers.entries() - - t.equal(gen.return, undefined) - t.equal(gen.throw, undefined) - t.equal(typeof gen.next, 'function') - t.end() - }) - - t.test('Symbol.iterator', (t) => { - const values = new Headers([['a', 'b']]).entries()[Symbol.iterator]() - - t.same(Array.from(values), [['a', 'b']]) - t.end() - }) - - t.test('brand check', (t) => { - const gen = new Headers().entries() - - t.throws(() => { - // eslint-disable-next-line no-proto - gen.__proto__.next() - }, TypeError) - - t.end() - }) - - t.end() -}) diff --git a/test/fetch/iterators.js b/test/fetch/iterators.js new file mode 100644 index 00000000000..6c6761d2edf --- /dev/null +++ b/test/fetch/iterators.js @@ -0,0 +1,140 @@ +'use strict' + +const { test } = require('tap') +const { Headers, FormData } = require('../..') + +test('Implements " Iterator" properly', (t) => { + t.test('all Headers iterators implement Headers Iterator', (t) => { + const headers = new Headers([['a', 'b'], ['c', 'd']]) + + for (const iterable of ['keys', 'values', 'entries', Symbol.iterator]) { + const gen = headers[iterable]() + // https://tc39.es/ecma262/#sec-%25iteratorprototype%25-object + const IteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())) + const iteratorProto = Object.getPrototypeOf(gen) + + t.ok(gen.constructor === Object) + t.ok(gen.prototype === undefined) + // eslint-disable-next-line no-proto + t.equal(gen.__proto__[Symbol.toStringTag], 'Headers Iterator') + // https://github.com/node-fetch/node-fetch/issues/1119#issuecomment-100222049 + t.notOk(Headers.prototype[iterable] instanceof function * () {}.constructor) + // eslint-disable-next-line no-proto + t.ok(gen.__proto__.next.__proto__ === Function.prototype) + // https://webidl.spec.whatwg.org/#dfn-iterator-prototype-object + // "The [[Prototype]] internal slot of an iterator prototype object must be %IteratorPrototype%." + t.equal(gen[Symbol.iterator], IteratorPrototype[Symbol.iterator]) + t.equal(Object.getPrototypeOf(iteratorProto), IteratorPrototype) + } + + t.end() + }) + + t.test('all FormData iterators implement FormData Iterator', (t) => { + const fd = new FormData() + + for (const iterable of ['keys', 'values', 'entries', Symbol.iterator]) { + const gen = fd[iterable]() + // https://tc39.es/ecma262/#sec-%25iteratorprototype%25-object + const IteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())) + const iteratorProto = Object.getPrototypeOf(gen) + + t.ok(gen.constructor === Object) + t.ok(gen.prototype === undefined) + // eslint-disable-next-line no-proto + t.equal(gen.__proto__[Symbol.toStringTag], 'FormData Iterator') + // https://github.com/node-fetch/node-fetch/issues/1119#issuecomment-100222049 + t.notOk(Headers.prototype[iterable] instanceof function * () {}.constructor) + // eslint-disable-next-line no-proto + t.ok(gen.__proto__.next.__proto__ === Function.prototype) + // https://webidl.spec.whatwg.org/#dfn-iterator-prototype-object + // "The [[Prototype]] internal slot of an iterator prototype object must be %IteratorPrototype%." + t.equal(gen[Symbol.iterator], IteratorPrototype[Symbol.iterator]) + t.equal(Object.getPrototypeOf(iteratorProto), IteratorPrototype) + } + + t.end() + }) + + t.test('Iterator symbols are properly set', (t) => { + t.test('Headers', (t) => { + const headers = new Headers([['a', 'b'], ['c', 'd']]) + const gen = headers.entries() + + t.equal(typeof gen[Symbol.toStringTag], 'string') + t.equal(typeof gen[Symbol.iterator], 'function') + t.end() + }) + + t.test('FormData', (t) => { + const fd = new FormData() + const gen = fd.entries() + + t.equal(typeof gen[Symbol.toStringTag], 'string') + t.equal(typeof gen[Symbol.iterator], 'function') + t.end() + }) + + t.end() + }) + + t.test('Iterator does not inherit Generator prototype methods', (t) => { + t.test('Headers', (t) => { + const headers = new Headers([['a', 'b'], ['c', 'd']]) + const gen = headers.entries() + + t.equal(gen.return, undefined) + t.equal(gen.throw, undefined) + t.equal(typeof gen.next, 'function') + + t.end() + }) + + t.test('FormData', (t) => { + const fd = new FormData() + const gen = fd.entries() + + t.equal(gen.return, undefined) + t.equal(gen.throw, undefined) + t.equal(typeof gen.next, 'function') + + t.end() + }) + + t.end() + }) + + t.test('Symbol.iterator', (t) => { + // Headers + const headerValues = new Headers([['a', 'b']]).entries()[Symbol.iterator]() + t.same(Array.from(headerValues), [['a', 'b']]) + + // FormData + const formdata = new FormData() + formdata.set('a', 'b') + const formdataValues = formdata.entries()[Symbol.iterator]() + t.same(Array.from(formdataValues), [['a', 'b']]) + + t.end() + }) + + t.test('brand check', (t) => { + // Headers + t.throws(() => { + const gen = new Headers().entries() + // eslint-disable-next-line no-proto + gen.__proto__.next() + }, TypeError) + + // FormData + t.throws(() => { + const gen = new FormData().entries() + // eslint-disable-next-line no-proto + gen.__proto__.next() + }, TypeError) + + t.end() + }) + + t.end() +}) From e875f63aff4edd4f804dbc1b357ca2611ec70562 Mon Sep 17 00:00:00 2001 From: Khafra <42794878+KhafraDev@users.noreply.github.com> Date: Wed, 1 Jun 2022 17:59:09 -0400 Subject: [PATCH 2/3] feat: add `FormData.prototype.forEach` --- lib/fetch/formdata.js | 26 +++++++++++++++ test/fetch/formdata.js | 59 +++++++++++++++++++++++++++++++++++ test/types/fetch.test-d.ts | 10 +++--- test/types/formdata.test-d.ts | 10 +++--- types/fetch.d.ts | 20 ++++++------ types/formdata.d.ts | 26 ++++++++------- 6 files changed, 120 insertions(+), 31 deletions(-) diff --git a/lib/fetch/formdata.js b/lib/fetch/formdata.js index 77cfec0e02c..8d49a7359ff 100644 --- a/lib/fetch/formdata.js +++ b/lib/fetch/formdata.js @@ -219,6 +219,32 @@ class FormData { 'FormData' ) } + + /** + * @param {(value: string, key: string, self: FormData) => void} callbackFn + * @param {unknown} thisArg + */ + forEach (callbackFn, thisArg = globalThis) { + if (!(this instanceof FormData)) { + throw new TypeError('Illegal invocation') + } + + if (arguments.length < 1) { + throw new TypeError( + `Failed to execute 'forEach' on 'Headers': 1 argument required, but only ${arguments.length} present.` + ) + } + + if (typeof callbackFn !== 'function') { + throw new TypeError( + "Failed to execute 'forEach' on 'Headers': parameter 1 is not of type 'Function'." + ) + } + + for (const [key, value] of this) { + callbackFn.apply(thisArg, [value, key, this]) + } + } } FormData.prototype[Symbol.iterator] = FormData.prototype.entries diff --git a/test/fetch/formdata.js b/test/fetch/formdata.js index 22d8f5f822f..a3f2f6acae9 100644 --- a/test/fetch/formdata.js +++ b/test/fetch/formdata.js @@ -213,6 +213,65 @@ test('formData.values', (t) => { }) }) +test('formData forEach', (t) => { + t.test('invalid arguments', (t) => { + t.throws(() => { + FormData.prototype.forEach.call({}) + }, TypeError('Illegal invocation')) + + t.throws(() => { + const fd = new FormData() + + fd.forEach({}) + }, TypeError) + + t.end() + }) + + t.test('with a callback', (t) => { + const fd = new FormData() + + fd.set('a', 'b') + fd.set('c', 'd') + + let i = 0 + fd.forEach((value, key, self) => { + if (i++ === 0) { + t.equal(value, 'b') + t.equal(key, 'a') + } else { + t.equal(value, 'd') + t.equal(key, 'c') + } + + t.equal(fd, self) + }) + + t.end() + }) + + t.test('with a thisArg', (t) => { + const fd = new FormData() + fd.set('b', 'a') + + fd.forEach(function (value, key, self) { + t.equal(this, globalThis) + t.equal(fd, self) + t.equal(key, 'b') + t.equal(value, 'a') + }) + + const thisArg = Symbol('thisArg') + fd.forEach(function () { + t.equal(this, thisArg) + }, thisArg) + + t.end() + }) + + t.end() +}) + test('formData toStringTag', (t) => { const form = new FormData() t.equal(form[Symbol.toStringTag], 'FormData') diff --git a/test/types/fetch.test-d.ts b/test/types/fetch.test-d.ts index 43092d62023..5d7d8db2fd4 100644 --- a/test/types/fetch.test-d.ts +++ b/test/types/fetch.test-d.ts @@ -9,8 +9,8 @@ import { FormData, Headers, HeadersInit, - HeadersIterableIterator, - HeadersIterator, + SpecIterableIterator, + SpecIterator, Request, RequestCache, RequestCredentials, @@ -127,9 +127,9 @@ expectType(headers.delete('key')) expectType(headers.get('key')) expectType(headers.has('key')) expectType(headers.set('key', 'value')) -expectType>(headers.keys()) -expectType>(headers.values()) -expectType>(headers.entries()) +expectType>(headers.keys()) +expectType>(headers.values()) +expectType>(headers.entries()) expectType(request.cache) expectType(request.credentials) diff --git a/test/types/formdata.test-d.ts b/test/types/formdata.test-d.ts index d17ff6c923f..f5c4f8a5063 100644 --- a/test/types/formdata.test-d.ts +++ b/test/types/formdata.test-d.ts @@ -1,7 +1,7 @@ import { Blob } from 'buffer' import { Readable } from 'stream' import { expectAssignable, expectType } from 'tsd' -import { File, FormData } from '../..' +import { File, FormData, SpecIterableIterator } from '../..' import { DispatchOptions } from '../../types/dispatcher' declare const dispatcherOptions: DispatchOptions @@ -20,8 +20,8 @@ expectType>(formData.getAll('key')) expectType>(formData.getAll('key')) expectType(formData.has('key')) expectType(formData.delete('key')) -expectAssignable>(formData.keys()) -expectAssignable>(formData.values()) -expectAssignable>(formData.entries()) -expectAssignable>(formData[Symbol.iterator]()) +expectAssignable>(formData.keys()) +expectAssignable>(formData.values()) +expectAssignable>(formData.entries()) +expectAssignable>(formData[Symbol.iterator]()) expectAssignable(dispatcherOptions.body) diff --git a/types/fetch.d.ts b/types/fetch.d.ts index 47a08dd0510..87980bed40b 100644 --- a/types/fetch.d.ts +++ b/types/fetch.d.ts @@ -38,21 +38,21 @@ export interface BodyMixin { readonly text: () => Promise } -export interface HeadersIterator { +export interface SpecIterator { next(...args: [] | [TNext]): IteratorResult; } -export interface HeadersIterableIterator extends HeadersIterator { - [Symbol.iterator](): HeadersIterableIterator; +export interface SpecIterableIterator extends SpecIterator { + [Symbol.iterator](): SpecIterableIterator; } -export interface HeadersIterable { - [Symbol.iterator](): HeadersIterator; +export interface SpecIterable { + [Symbol.iterator](): SpecIterator; } export type HeadersInit = string[][] | Record> | Headers -export declare class Headers implements HeadersIterable<[string, string]> { +export declare class Headers implements SpecIterable<[string, string]> { constructor (init?: HeadersInit) readonly append: (name: string, value: string) => void readonly delete: (name: string) => void @@ -64,10 +64,10 @@ export declare class Headers implements HeadersIterable<[string, string]> { thisArg?: unknown ) => void - readonly keys: () => HeadersIterableIterator - readonly values: () => HeadersIterableIterator - readonly entries: () => HeadersIterableIterator<[string, string]> - readonly [Symbol.iterator]: () => HeadersIterator<[string, string]> + readonly keys: () => SpecIterableIterator + readonly values: () => SpecIterableIterator + readonly entries: () => SpecIterableIterator<[string, string]> + readonly [Symbol.iterator]: () => SpecIterator<[string, string]> } export type RequestCache = diff --git a/types/formdata.d.ts b/types/formdata.d.ts index 2e652bacdb7..df29a572d6b 100644 --- a/types/formdata.d.ts +++ b/types/formdata.d.ts @@ -2,6 +2,7 @@ /// import { File } from './file' +import { SpecIterator, SpecIterableIterator } from './fetch' /** * A `string` or `File` that represents a single value from a set of `FormData` key-value pairs. @@ -73,32 +74,35 @@ export declare class FormData { delete(name: string): void /** - * Returns an [`iterator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) allowing to go through all keys contained in this `FormData` object. - * Each key is a `string`. + * Executes given callback function for each field of the FormData instance */ - keys(): Generator + forEach: ( + callbackfn: (value: FormDataEntryValue, key: string, iterable: FormData) => void, + thisArg?: unknown + ) => void /** - * Returns an [`iterator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) allowing to go through the `FormData` key/value pairs. - * The key of each pair is a string; the value is a [`FormDataValue`](https://developer.mozilla.org/en-US/docs/Web/API/FormDataEntryValue). + * Returns an [`iterator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) allowing to go through all keys contained in this `FormData` object. + * Each key is a `string`. */ - entries(): Generator<[string, FormDataEntryValue]> + keys: () => SpecIterableIterator /** * Returns an [`iterator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) allowing to go through all values contained in this object `FormData` object. * Each value is a [`FormDataValue`](https://developer.mozilla.org/en-US/docs/Web/API/FormDataEntryValue). */ - values(): Generator + values: () => SpecIterableIterator /** - * An alias for FormData#entries() + * Returns an [`iterator`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) allowing to go through the `FormData` key/value pairs. + * The key of each pair is a string; the value is a [`FormDataValue`](https://developer.mozilla.org/en-US/docs/Web/API/FormDataEntryValue). */ - [Symbol.iterator](): Generator<[string, FormDataEntryValue], void> + entries: () => SpecIterableIterator<[string, FormDataEntryValue]> /** - * Executes given callback function for each field of the FormData instance + * An alias for FormData#entries() */ - forEach(callback: (value: FormDataEntryValue, key: string, formData: FormData) => void, thisArg?: unknown): void + [Symbol.iterator]: () => SpecIterableIterator<[string, FormDataEntryValue]> readonly [Symbol.toStringTag]: string } From e194c040d98abd2e58f6cc931ec2b6bf3a11516b Mon Sep 17 00:00:00 2001 From: Khafra <42794878+KhafraDev@users.noreply.github.com> Date: Wed, 1 Jun 2022 18:01:11 -0400 Subject: [PATCH 3/3] fix: update error messages --- lib/fetch/formdata.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/fetch/formdata.js b/lib/fetch/formdata.js index 8d49a7359ff..965e47c3a7f 100644 --- a/lib/fetch/formdata.js +++ b/lib/fetch/formdata.js @@ -231,13 +231,13 @@ class FormData { if (arguments.length < 1) { throw new TypeError( - `Failed to execute 'forEach' on 'Headers': 1 argument required, but only ${arguments.length} present.` + `Failed to execute 'forEach' on 'FormData': 1 argument required, but only ${arguments.length} present.` ) } if (typeof callbackFn !== 'function') { throw new TypeError( - "Failed to execute 'forEach' on 'Headers': parameter 1 is not of type 'Function'." + "Failed to execute 'forEach' on 'FormData': parameter 1 is not of type 'Function'." ) }