Skip to content

Commit

Permalink
Add Types (#375)
Browse files Browse the repository at this point in the history
* init type work

* add error types

* fix up error tests and add client.request

* complete client types

* complete pool and fix up some existing tests

* touch up types and add tests

* add type tests to action workflow

* fix dispatch return type

* export errors and fix tests
  • Loading branch information
Ethan-Arrowood authored and ronag committed Nov 12, 2020
1 parent a991989 commit 8ae990f
Show file tree
Hide file tree
Showing 11 changed files with 562 additions and 5 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,7 @@ jobs:
- name: Unit test
run: |
npm run coverage
- name: Test types
run: |
npm run test:typescript
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ The `data` parameter in `callback` is defined as follow:
Returns a promise if no callback is provided.
<a name='dispatch'></a>
#### `client.dispatch(opts, handler): Promise|Void`
#### `client.dispatch(opts, handler): Void`
This is the low level API which all the preceeding APIs are implemented on top of.
Expand Down Expand Up @@ -482,7 +482,7 @@ the `onConnect` handler has been invoked.
<a name='close'></a>
#### `client.close([callback]): Promise|Void`
Closes the client and gracefully waits fo enqueued requests to
Closes the client and gracefully waits for enqueued requests to
complete before invoking the callback.
Returns a promise if no callback is provided.
Expand Down
14 changes: 14 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Pool from './types/pool'
import Client from './types/client'
import Errors from './types/errors'

export { Pool, Client, Errors }
export default Undici

declare function Undici(url: string, opts: Pool.Options): Pool

declare namespace Undici {
var Pool: typeof import('./types/pool');
var Client: typeof import('./types/client');
var errors: typeof import('./types/errors');
}
16 changes: 13 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
"version": "2.1.1",
"description": "An HTTP/1.1 client, written from scratch for Node.js",
"main": "index.js",
"types": "index.d.ts",
"module": "wrapper.mjs",
"scripts": {
"lint": "standard | snazzy",
"test": "tap test/*.js --no-coverage && jest test/jest/test",
"test": "tap test/*.{mjs,js} --no-coverage && jest test/jest/test",
"test:typescript": "tsd",
"coverage": "standard | snazzy && tap test/*.js",
"bench": "npx concurrently -k -s first \"node benchmarks/server.js\" \"node -e 'setTimeout(() => {}, 1000)' && node benchmarks\""
},
Expand All @@ -29,6 +31,7 @@
"homepage": "https://github.com/nodejs/undici#readme",
"devDependencies": {
"@sinonjs/fake-timers": "^6.0.1",
"@types/node": "^14.6.2",
"abort-controller": "^3.0.0",
"benchmark": "^2.1.4",
"concurrently": "^5.2.0",
Expand All @@ -38,9 +41,16 @@
"proxyquire": "^2.0.1",
"snazzy": "^8.0.0",
"standard": "^14.3.4",
"tap": "^14.10.8"
"tap": "^14.10.8",
"tsd": "^0.13.1"
},
"pre-commit": [
"coverage"
]
],
"tsd": {
"directory": "test/types",
"compilerOptions": {
"esModuleInterop": true
}
}
}
77 changes: 77 additions & 0 deletions test/types/client.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Duplex, Readable, Writable } from 'stream'
import { expectAssignable } from 'tsd'
import { Client } from '../..'

expectAssignable<Client>(new Client(''))
expectAssignable<Client>(new Client('', {}))

{
const client = new Client('')

// methods
expectAssignable<number>(client.pipelining)
expectAssignable<number>(client.pending)
expectAssignable<number>(client.running)
expectAssignable<number>(client.size)
expectAssignable<boolean>(client.connected)
expectAssignable<boolean>(client.busy)
expectAssignable<boolean>(client.closed)
expectAssignable<boolean>(client.destroyed)

// request
expectAssignable<PromiseLike<Client.ResponseData>>(client.request({ path: '', method: '' }))
expectAssignable<void>(client.request({ path: '', method: '' }, (err, data) => {
expectAssignable<Error | null>(err)
expectAssignable<Client.ResponseData>(data)
}))

// stream
expectAssignable<PromiseLike<Client.StreamData>>(client.stream({ path: '', method: '' }, data => {
expectAssignable<Client.StreamFactoryData>(data)
return new Writable()
}))
expectAssignable<void>(client.stream(
{ path: '', method: '' },
data => {
expectAssignable<Client.StreamFactoryData>(data)
return new Writable()
},
(err, data) => {
expectAssignable<Error | null>(err)
expectAssignable<Client.StreamData>(data)
}
))

// pipeline
expectAssignable<Duplex>(client.pipeline({ path: '', method: '' }, data => {
expectAssignable<Client.PipelineHandlerData>(data)
return new Readable()
}))

// upgrade
expectAssignable<PromiseLike<Client.UpgradeData>>(client.upgrade({ path: '' }))
expectAssignable<void>(client.upgrade({ path: '' }, (err, data) => {
expectAssignable<Error | null>(err)
expectAssignable<Client.UpgradeData>(data)
}))

// connect
expectAssignable<PromiseLike<Client.ConnectData>>(client.connect({ path: '' }))
expectAssignable<void>(client.connect({ path: '' }, (err, data) => {
expectAssignable<Error | null>(err)
expectAssignable<Client.ConnectData>(data)
}))

// dispatch
expectAssignable<void>(client.dispatch({ path: '', method: '' }, {}))

// close
expectAssignable<PromiseLike<void>>(client.close())
expectAssignable<void>(client.close(() => {}))

// destroy
expectAssignable<PromiseLike<void>>(client.destroy())
expectAssignable<PromiseLike<void>>(client.destroy(new Error()))
expectAssignable<void>(client.destroy(() => {}))
expectAssignable<void>(client.destroy(new Error(), () => {}))
}
72 changes: 72 additions & 0 deletions test/types/errors.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { expectAssignable } from 'tsd'
import { Errors } from '../..'

expectAssignable<Errors.UndiciError>(new Errors.UndiciError())

expectAssignable<Errors.UndiciError>(new Errors.HeadersTimeoutError())
expectAssignable<Errors.HeadersTimeoutError>(new Errors.HeadersTimeoutError())
expectAssignable<'HeadersTimeoutError'>(new Errors.HeadersTimeoutError().name)
expectAssignable<'UND_ERR_HEADERS_TIMEOUT'>(new Errors.HeadersTimeoutError().code)

expectAssignable<Errors.UndiciError>(new Errors.SocketTimeoutError())
expectAssignable<Errors.SocketTimeoutError>(new Errors.SocketTimeoutError())
expectAssignable<'SocketTimeoutError'>(new Errors.SocketTimeoutError().name)
expectAssignable<'UND_ERR_SOCKET_TIMEOUT'>(new Errors.SocketTimeoutError().code)

expectAssignable<Errors.UndiciError>(new Errors.RequestTimeoutError())
expectAssignable<Errors.RequestTimeoutError>(new Errors.RequestTimeoutError())
expectAssignable<'RequestTimeoutError'>(new Errors.RequestTimeoutError().name)
expectAssignable<'UND_ERR_REQUEST_TIMEOUT'>(new Errors.RequestTimeoutError().code)

expectAssignable<Errors.UndiciError>(new Errors.InvalidReturnError())
expectAssignable<Errors.InvalidReturnError>(new Errors.InvalidReturnError())
expectAssignable<'InvalidReturnError'>(new Errors.InvalidReturnError().name)
expectAssignable<'UND_ERR_INVALID_RETURN_VALUE'>(new Errors.InvalidReturnError().code)

expectAssignable<Errors.UndiciError>(new Errors.RequestAbortedError())
expectAssignable<Errors.RequestAbortedError>(new Errors.RequestAbortedError())
expectAssignable<'RequestAbortedError'>(new Errors.RequestAbortedError().name)
expectAssignable<'UND_ERR_ABORTED'>(new Errors.RequestAbortedError().code)

expectAssignable<Errors.UndiciError>(new Errors.InformationalError())
expectAssignable<Errors.InformationalError>(new Errors.InformationalError())
expectAssignable<'InformationalError'>(new Errors.InformationalError().name)
expectAssignable<'UND_ERR_INFO'>(new Errors.InformationalError().code)

expectAssignable<Errors.UndiciError>(new Errors.ContentLengthMismatchError())
expectAssignable<Errors.ContentLengthMismatchError>(new Errors.ContentLengthMismatchError())
expectAssignable<'ContentLengthMismatchError'>(new Errors.ContentLengthMismatchError().name)
expectAssignable<'UND_ERR_CONTENT_LENGTH_MISMATCH'>(new Errors.ContentLengthMismatchError().code)

expectAssignable<Errors.UndiciError>(new Errors.ClientDestroyedError())
expectAssignable<Errors.ClientDestroyedError>(new Errors.ClientDestroyedError())
expectAssignable<'ClientDestroyedError'>(new Errors.ClientDestroyedError().name)
expectAssignable<'UND_ERR_DESTROYED'>(new Errors.ClientDestroyedError().code)

expectAssignable<Errors.UndiciError>(new Errors.ClientClosedError())
expectAssignable<Errors.ClientClosedError>(new Errors.ClientClosedError())
expectAssignable<'ClientClosedError'>(new Errors.ClientClosedError().name)
expectAssignable<'UND_ERR_CLOSED'>(new Errors.ClientClosedError().code)

expectAssignable<Errors.UndiciError>(new Errors.SocketError())
expectAssignable<Errors.SocketError>(new Errors.SocketError())
expectAssignable<'SocketError'>(new Errors.SocketError().name)
expectAssignable<'UND_ERR_SOCKET'>(new Errors.SocketError().code)

expectAssignable<Errors.UndiciError>(new Errors.NotSupportedError())
expectAssignable<Errors.NotSupportedError>(new Errors.NotSupportedError())
expectAssignable<'NotSupportedError'>(new Errors.NotSupportedError().name)
expectAssignable<'UND_ERR_NOT_SUPPORTED'>(new Errors.NotSupportedError().code)

{
// @ts-ignore
function f (): Errors.HeadersTimeoutError | Errors.SocketTimeoutError { return }

const e = f()

if (e.code === 'UND_ERR_HEADERS_TIMEOUT') {
expectAssignable<Errors.HeadersTimeoutError>(e)
} else if (e.code === 'UND_ERR_SOCKET_TIMEOUT') {
expectAssignable<Errors.SocketTimeoutError>(e)
}
}
7 changes: 7 additions & 0 deletions test/types/index.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { expectAssignable } from 'tsd'
import Undici, { Pool, Client, Errors } from '../..'

expectAssignable<Pool>(Undici('', {}))
expectAssignable<Pool>(new Undici.Pool('', {}))
expectAssignable<Client>(new Undici.Client('', {}))
expectAssignable<typeof Errors>(Undici.errors)
77 changes: 77 additions & 0 deletions test/types/pool.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Duplex, Readable, Writable } from 'stream'
import { expectAssignable } from 'tsd'
import { Client, Pool } from '../..'

expectAssignable<Pool>(new Pool(''))
expectAssignable<Pool>(new Pool('', {}))

{
const pool = new Pool('', {})

// methods
expectAssignable<number>(pool.pipelining)
expectAssignable<number>(pool.pending)
expectAssignable<number>(pool.running)
expectAssignable<number>(pool.size)
expectAssignable<boolean>(pool.connected)
expectAssignable<boolean>(pool.busy)
expectAssignable<boolean>(pool.closed)
expectAssignable<boolean>(pool.destroyed)

// request
expectAssignable<PromiseLike<Client.ResponseData>>(pool.request({ path: '', method: '' }))
expectAssignable<void>(pool.request({ path: '', method: '' }, (err, data) => {
expectAssignable<Error | null>(err)
expectAssignable<Client.ResponseData>(data)
}))

// stream
expectAssignable<PromiseLike<Client.StreamData>>(pool.stream({ path: '', method: '' }, data => {
expectAssignable<Client.StreamFactoryData>(data)
return new Writable()
}))
expectAssignable<void>(pool.stream(
{ path: '', method: '' },
data => {
expectAssignable<Client.StreamFactoryData>(data)
return new Writable()
},
(err, data) => {
expectAssignable<Error | null>(err)
expectAssignable<Client.StreamData>(data)
}
))

// pipeline
expectAssignable<Duplex>(pool.pipeline({ path: '', method: '' }, data => {
expectAssignable<Client.PipelineHandlerData>(data)
return new Readable()
}))

// upgrade
expectAssignable<PromiseLike<Client.UpgradeData>>(pool.upgrade({ path: '' }))
expectAssignable<void>(pool.upgrade({ path: '' }, (err, data) => {
expectAssignable<Error | null>(err)
expectAssignable<Client.UpgradeData>(data)
}))

// connect
expectAssignable<PromiseLike<Client.ConnectData>>(pool.connect({ path: '' }))
expectAssignable<void>(pool.connect({ path: '' }, (err, data) => {
expectAssignable<Error | null>(err)
expectAssignable<Client.ConnectData>(data)
}))

// dispatch
expectAssignable<void>(pool.dispatch({ path: '', method: '' }, {}))

// close
expectAssignable<PromiseLike<void>>(pool.close())
expectAssignable<void>(pool.close(() => {}))

// destroy
expectAssignable<PromiseLike<void>>(pool.destroy())
expectAssignable<PromiseLike<void>>(pool.destroy(new Error()))
expectAssignable<void>(pool.destroy(() => {}))
expectAssignable<void>(pool.destroy(new Error(), () => {}))
}
Loading

0 comments on commit 8ae990f

Please sign in to comment.