From 3f858871beb0c1841b01b6fcb4b41e2c98b90429 Mon Sep 17 00:00:00 2001 From: Khafra <42794878+KhafraDev@users.noreply.github.com> Date: Sun, 8 May 2022 14:22:43 -0400 Subject: [PATCH] feat: implement `Headers Iterator` --- lib/fetch/headers.js | 24 +++++++++++++++--- test/fetch/headers-iterator.js | 46 ++++++++++++++++++++++++++++++++++ test/types/fetch.test-d.ts | 8 +++--- types/fetch.d.ts | 22 ++++++++++++---- 4 files changed, 88 insertions(+), 12 deletions(-) create mode 100644 test/fetch/headers-iterator.js diff --git a/lib/fetch/headers.js b/lib/fetch/headers.js index e0295be2250..762e15ea05d 100644 --- a/lib/fetch/headers.js +++ b/lib/fetch/headers.js @@ -77,6 +77,22 @@ function fill (headers, object) { } } +function makeHeadersIterator (iterator) { + const i = { + next: () => { + // TODO(@KhafraDev): brand check + return iterator.next() + }, + [Symbol.iterator]: () => { + // TODO(@KhafraDev): brand check + return makeHeadersIterator(iterator) + }, + [Symbol.toStringTag]: 'Headers Iterator' + } + + return Object.setPrototypeOf({}, i) +} + class HeadersList { constructor (init) { if (init instanceof HeadersList) { @@ -311,7 +327,7 @@ class Headers { throw new TypeError('Illegal invocation') } - return this[kHeadersSortedMap].keys() + return makeHeadersIterator(this[kHeadersSortedMap].keys()) } values () { @@ -319,7 +335,7 @@ class Headers { throw new TypeError('Illegal invocation') } - return this[kHeadersSortedMap].values() + return makeHeadersIterator(this[kHeadersSortedMap].values()) } entries () { @@ -327,7 +343,7 @@ class Headers { throw new TypeError('Illegal invocation') } - return this[kHeadersSortedMap].entries() + return makeHeadersIterator(this[kHeadersSortedMap].entries()) } [Symbol.iterator] () { @@ -335,7 +351,7 @@ class Headers { throw new TypeError('Illegal invocation') } - return this[kHeadersSortedMap] + return makeHeadersIterator(this[kHeadersSortedMap]) } forEach (...args) { diff --git a/test/fetch/headers-iterator.js b/test/fetch/headers-iterator.js new file mode 100644 index 00000000000..5712c6db2ea --- /dev/null +++ b/test/fetch/headers-iterator.js @@ -0,0 +1,46 @@ +'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]() + + 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(gen instanceof function * () {}.constructor) + // eslint-disable-next-line no-proto + t.ok(gen.__proto__.next.__proto__ === Function.prototype) + } + + 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.end() +}) diff --git a/test/types/fetch.test-d.ts b/test/types/fetch.test-d.ts index bff8e10b861..2b7dc5e7a5f 100644 --- a/test/types/fetch.test-d.ts +++ b/test/types/fetch.test-d.ts @@ -9,6 +9,8 @@ import { FormData, Headers, HeadersInit, + HeadersIterableIterator, + HeadersIterator, Request, RequestCache, RequestCredentials, @@ -115,9 +117,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/types/fetch.d.ts b/types/fetch.d.ts index 356c4f3262f..1b8692b251c 100644 --- a/types/fetch.d.ts +++ b/types/fetch.d.ts @@ -38,9 +38,21 @@ export interface BodyMixin { readonly text: () => Promise } +export interface HeadersIterator { + next(...args: [] | [TNext]): IteratorResult; +} + +export interface HeadersIterableIterator extends HeadersIterator { + [Symbol.iterator](): HeadersIterableIterator; +} + +export interface HeadersIterable { + [Symbol.iterator](): HeadersIterator; +} + export type HeadersInit = string[][] | Record> | Headers -export declare class Headers implements Iterable<[string, string]> { +export declare class Headers implements HeadersIterable<[string, string]> { constructor (init?: HeadersInit) readonly append: (name: string, value: string) => void readonly delete: (name: string) => void @@ -52,10 +64,10 @@ export declare class Headers implements Iterable<[string, string]> { thisArg?: unknown ) => void - readonly keys: () => IterableIterator - readonly values: () => IterableIterator - readonly entries: () => IterableIterator<[string, string]> - readonly [Symbol.iterator]: () => Iterator<[string, string]> + readonly keys: () => HeadersIterableIterator + readonly values: () => HeadersIterableIterator + readonly entries: () => HeadersIterableIterator<[string, string]> + readonly [Symbol.iterator]: () => HeadersIterator<[string, string]> } export type RequestCache =