diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index aabf8b05..20f46e35 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,7 +12,6 @@ jobs: node-version: - 18 - 16 - - 14 steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 diff --git a/package.json b/package.json index 51a64747..e7395fce 100644 --- a/package.json +++ b/package.json @@ -56,14 +56,14 @@ "@types/busboy": "^0.3.1", "@types/express": "^4.17.14", "@types/node": "^18.11.7", - "@types/node-fetch": "^2.6.2", + "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", - "ava": "^4.3.3", + "ava": "^5.3.0", "body-parser": "^1.20.1", "busboy": "^0.3.1", "del-cli": "^4.0.1", "delay": "^5.0.0", - "expect-type": "^0.14.2", + "expect-type": "^0.16.0", "express": "^4.18.2", "form-data": "^4.0.0", "node-fetch": "^2.6.1", @@ -71,8 +71,8 @@ "playwright-chromium": "^1.27.1", "raw-body": "^2.5.1", "ts-node": "^10.8.1", - "typescript": "^4.8.4", - "xo": "^0.50.0" + "typescript": "^5.1.3", + "xo": "^0.54.2" }, "sideEffects": false, "xo": { diff --git a/source/core/Ky.ts b/source/core/Ky.ts index 6d7bc0fd..f45d1011 100644 --- a/source/core/Ky.ts +++ b/source/core/Ky.ts @@ -2,12 +2,12 @@ import {HTTPError} from '../errors/HTTPError.js'; import {TimeoutError} from '../errors/TimeoutError.js'; import type {Hooks} from '../types/hooks.js'; import type {Input, InternalOptions, NormalizedOptions, Options, SearchParamsInit} from '../types/options.js'; -import {ResponsePromise} from '../types/ResponsePromise.js'; +import {type ResponsePromise} from '../types/ResponsePromise.js'; import {deepMerge, mergeHeaders} from '../utils/merge.js'; import {normalizeRequestMethod, normalizeRetryOptions} from '../utils/normalize.js'; -import timeout, {TimeoutOptions} from '../utils/timeout.js'; +import timeout, {type TimeoutOptions} from '../utils/timeout.js'; import delay from '../utils/delay.js'; -import {ObjectEntries} from '../utils/types.js'; +import {type ObjectEntries} from '../utils/types.js'; import { maxSafeTimeout, responseTypes, @@ -19,12 +19,11 @@ import { } from './constants.js'; export class Ky { - // eslint-disable-next-line @typescript-eslint/promise-function-async static create(input: Input, options: Options): ResponsePromise { const ky = new Ky(input, options); const fn = async (): Promise => { - if (ky._options.timeout > maxSafeTimeout) { + if (typeof ky._options.timeout === 'number' && ky._options.timeout > maxSafeTimeout) { throw new RangeError(`The \`timeout\` option cannot be greater than ${maxSafeTimeout}`); } @@ -137,7 +136,7 @@ export class Ky { prefixUrl: String(options.prefixUrl || ''), retry: normalizeRetryOptions(options.retry), throwHttpErrors: options.throwHttpErrors !== false, - timeout: typeof options.timeout === 'undefined' ? 10_000 : options.timeout, + timeout: options.timeout ?? 10_000, fetch: options.fetch ?? globalThis.fetch.bind(globalThis), }; @@ -223,7 +222,7 @@ export class Ky { after *= 1000; } - if (typeof this._options.retry.maxRetryAfter !== 'undefined' && after > this._options.retry.maxRetryAfter) { + if (this._options.retry.maxRetryAfter !== undefined && after > this._options.retry.maxRetryAfter) { return 0; } @@ -253,7 +252,6 @@ export class Ky { protected async _retry Promise>(fn: T): Promise | void> { try { return await fn(); - // eslint-disable-next-line @typescript-eslint/no-implicit-any-catch } catch (error) { const ms = Math.min(this._calculateRetryDelay(error), maxSafeTimeout); if (ms !== 0 && this._retryCount > 0) { diff --git a/source/core/constants.ts b/source/core/constants.ts index 31187372..e162bc01 100644 --- a/source/core/constants.ts +++ b/source/core/constants.ts @@ -1,5 +1,5 @@ import type {Expect, Equal} from '@type-challenges/utils'; -import {HttpMethod} from '../types/options.js'; +import {type HttpMethod} from '../types/options.js'; export const supportsRequestStreams = (() => { let duplexAccessed = false; diff --git a/source/index.ts b/source/index.ts index a497a889..96446a7d 100644 --- a/source/index.ts +++ b/source/index.ts @@ -5,7 +5,7 @@ import {requestMethods, stop} from './core/constants.js'; import type {KyInstance} from './types/ky.js'; import type {Input, Options} from './types/options.js'; import {validateAndMerge} from './utils/merge.js'; -import {Mutable} from './utils/types.js'; +import {type Mutable} from './utils/types.js'; const createInstance = (defaults?: Partial): KyInstance => { // eslint-disable-next-line @typescript-eslint/promise-function-async diff --git a/source/types/ResponsePromise.ts b/source/types/ResponsePromise.ts index fc5ff4dd..95798154 100644 --- a/source/types/ResponsePromise.ts +++ b/source/types/ResponsePromise.ts @@ -1,9 +1,9 @@ /** Returns a `Response` object with `Body` methods added for convenience. So you can, for example, call `ky.get(input).json()` directly without having to await the `Response` first. When called like that, an appropriate `Accept` header will be set depending on the body method used. Unlike the `Body` methods of `window.Fetch`; these will throw an `HTTPError` if the response status is not in the range of `200...299`. Also, `.json()` will return an empty string if body is empty or the response status is `204` instead of throwing a parse error due to an empty body. */ -import {KyResponse} from './response.js'; +import {type KyResponse} from './response.js'; -export interface ResponsePromise extends Promise { +export type ResponsePromise = { arrayBuffer: () => Promise; blob: () => Promise; @@ -36,4 +36,4 @@ export interface ResponsePromise extends Promise { json: () => Promise; text: () => Promise; -} +} & Promise; diff --git a/source/types/hooks.ts b/source/types/hooks.ts index 2f91502b..1e060e8a 100644 --- a/source/types/hooks.ts +++ b/source/types/hooks.ts @@ -1,5 +1,5 @@ -import {stop} from '../core/constants.js'; -import {HTTPError} from '../index.js'; +import {type stop} from '../core/constants.js'; +import {type HTTPError} from '../index.js'; import type {NormalizedOptions} from './options.js'; export type BeforeRequestHook = ( @@ -23,7 +23,7 @@ export type AfterResponseHook = ( export type BeforeErrorHook = (error: HTTPError) => HTTPError | Promise; -export interface Hooks { +export type Hooks = { /** This hook enables you to modify the request right before it is sent. Ky will make no further changes to the request after this. The hook function receives normalized input and options as arguments. You could, forf example, modiy `options.headers` here. @@ -126,4 +126,4 @@ export interface Hooks { ``` */ beforeError?: BeforeErrorHook[]; -} +}; diff --git a/source/types/ky.ts b/source/types/ky.ts index 514276c1..931a2ac2 100644 --- a/source/types/ky.ts +++ b/source/types/ky.ts @@ -1,8 +1,8 @@ -import {stop} from '../core/constants.js'; +import {type stop} from '../core/constants.js'; import type {Input, Options} from './options.js'; import type {ResponsePromise} from './ResponsePromise.js'; -export interface KyInstance { +export type KyInstance = { /** Fetch the given `url`. @@ -117,4 +117,4 @@ export interface KyInstance { ``` */ readonly stop: typeof stop; -} +}; diff --git a/source/types/options.ts b/source/types/options.ts index 23847184..015ba7a7 100644 --- a/source/types/options.ts +++ b/source/types/options.ts @@ -12,7 +12,7 @@ export type HttpMethod = 'get' | 'post' | 'put' | 'patch' | 'head' | 'delete'; export type Input = string | URL | Request; -export interface DownloadProgress { +export type DownloadProgress = { percent: number; transferredBytes: number; @@ -20,14 +20,14 @@ export interface DownloadProgress { Note: If it's not possible to retrieve the body size, it will be `0`. */ totalBytes: number; -} +}; export type KyHeadersInit = HeadersInit | Record; /** Options are the same as `window.fetch`, with some exceptions. */ -export interface Options extends Omit { +export type Options = { /** HTTP method used to make the request. @@ -221,7 +221,7 @@ export interface Options extends Omit { ``` */ fetch?: (input: RequestInfo, init?: RequestInit) => Promise; -} +} & Omit; export type InternalOptions = Required< Omit, @@ -236,7 +236,7 @@ Omit, /** Normalized options passed to the `fetch` call and the `beforeRequest` hooks. */ -export interface NormalizedOptions extends RequestInit { +export type NormalizedOptions = { // Extended from `RequestInit`, but ensured to be set (not optional). method: RequestInit['method']; credentials: RequestInit['credentials']; @@ -245,6 +245,6 @@ export interface NormalizedOptions extends RequestInit { retry: RetryOptions; prefixUrl: string; onDownloadProgress: Options['onDownloadProgress']; -} +} & RequestInit; export type {RetryOptions} from './retry.js'; diff --git a/source/types/response.ts b/source/types/response.ts index 2ff09ea7..bdcae928 100644 --- a/source/types/response.ts +++ b/source/types/response.ts @@ -1,3 +1,3 @@ -export interface KyResponse extends Response { +export type KyResponse = { json: () => Promise; -} +} & Response; diff --git a/source/types/retry.ts b/source/types/retry.ts index 746a1ec6..f25b664e 100644 --- a/source/types/retry.ts +++ b/source/types/retry.ts @@ -1,4 +1,4 @@ -export interface RetryOptions { +export type RetryOptions = { /** The number of times to retry failed requests. @@ -49,4 +49,4 @@ export interface RetryOptions { @default Infinity */ backoffLimit?: number; -} +}; diff --git a/source/utils/delay.ts b/source/utils/delay.ts index c3d00e3a..c21858d2 100644 --- a/source/utils/delay.ts +++ b/source/utils/delay.ts @@ -1,11 +1,11 @@ // https://github.com/sindresorhus/delay/tree/ab98ae8dfcb38e1593286c94d934e70d14a4e111 import {composeAbortError} from '../errors/DOMException.js'; -import {InternalOptions} from '../types/options.js'; +import {type InternalOptions} from '../types/options.js'; -export interface DelayOptions { +export type DelayOptions = { signal?: InternalOptions['signal']; -} +}; export default async function delay( ms: number, diff --git a/source/utils/merge.ts b/source/utils/merge.ts index b09920c2..bcd148ac 100644 --- a/source/utils/merge.ts +++ b/source/utils/merge.ts @@ -3,7 +3,7 @@ import {isObject} from './is.js'; export const validateAndMerge = (...sources: Array | undefined>): Partial => { for (const source of sources) { - if ((!isObject(source) || Array.isArray(source)) && typeof source !== 'undefined') { + if ((!isObject(source) || Array.isArray(source)) && source !== undefined) { throw new TypeError('The `options` argument must be an object'); } } diff --git a/test/browser.ts b/test/browser.ts index 918e6e52..fdb50b6c 100644 --- a/test/browser.ts +++ b/test/browser.ts @@ -1,13 +1,14 @@ -import test, {ExecutionContext} from 'ava'; +import test, {type ExecutionContext} from 'ava'; import busboy from 'busboy'; import express from 'express'; -import {Page} from 'playwright-chromium'; -import ky from '../source/index.js'; -import {createHttpTestServer, ExtendedHttpTestServer, HttpServerOptions} from './helpers/create-http-test-server.js'; +import {type Page} from 'playwright-chromium'; +import type ky from '../source/index.js'; +import {createHttpTestServer, type ExtendedHttpTestServer, type HttpServerOptions} from './helpers/create-http-test-server.js'; import {parseRawBody} from './helpers/parse-body.js'; import {withPage} from './helpers/with-page.js'; declare global { + // eslint-disable-next-line @typescript-eslint/consistent-type-definitions interface Window { ky: typeof ky; } diff --git a/test/helpers/create-http-test-server.ts b/test/helpers/create-http-test-server.ts index 3e19311d..f05d89a6 100644 --- a/test/helpers/create-http-test-server.ts +++ b/test/helpers/create-http-test-server.ts @@ -1,5 +1,5 @@ import http from 'node:http'; -import net from 'node:net'; +import type net from 'node:net'; import express from 'express'; import pify from 'pify'; import bodyParser from 'body-parser'; @@ -8,13 +8,13 @@ export type HttpServerOptions = { bodyParser?: express.NextFunction | false; }; -export interface ExtendedHttpTestServer extends express.Express { +export type ExtendedHttpTestServer = { http: http.Server; url: string; port: number; hostname: string; close: () => Promise; -} +} & express.Express; export const createHttpTestServer = async (options: HttpServerOptions = {}): Promise => { const server = express() as ExtendedHttpTestServer; diff --git a/test/helpers/with-page.ts b/test/helpers/with-page.ts index 15ae3e62..230ffc23 100644 --- a/test/helpers/with-page.ts +++ b/test/helpers/with-page.ts @@ -1,6 +1,6 @@ import process from 'node:process'; import type {ExecutionContext, Implementation} from 'ava'; -import {chromium, Page} from 'playwright-chromium'; +import {chromium, type Page} from 'playwright-chromium'; type Run = (t: ExecutionContext, page: Page) => Promise; diff --git a/test/hooks.ts b/test/hooks.ts index 1bcd2db9..2273f668 100644 --- a/test/hooks.ts +++ b/test/hooks.ts @@ -1,7 +1,7 @@ import test from 'ava'; import delay from 'delay'; import ky, {HTTPError} from '../source/index.js'; -import {Options} from '../source/types/options.js'; +import {type Options} from '../source/types/options.js'; import {createHttpTestServer} from './helpers/create-http-test-server.js'; test('hooks can be async', async t => { diff --git a/test/http-error.ts b/test/http-error.ts index b0e7ab05..13003329 100644 --- a/test/http-error.ts +++ b/test/http-error.ts @@ -1,6 +1,6 @@ import test from 'ava'; import {HTTPError} from '../source/index.js'; -import {Mutable} from '../source/utils/types.js'; +import {type Mutable} from '../source/utils/types.js'; function createFakeResponse({status, statusText}: {status?: number; statusText?: string}): Response { // Start with a realistic fetch Response.