Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add generic type for opaque object #3385

Merged
merged 1 commit into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion test/types/api.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Duplex, Readable, Writable } from 'stream'
import { expectAssignable } from 'tsd'
import { expectAssignable, expectType } from 'tsd'
import { Dispatcher, request, stream, pipeline, connect, upgrade } from '../..'

// request
Expand All @@ -10,12 +10,22 @@ expectAssignable<Promise<Dispatcher.ResponseData>>(request('', { method: 'GET',
// stream
expectAssignable<Promise<Dispatcher.StreamData>>(stream('', { method: 'GET' }, data => {
expectAssignable<Dispatcher.StreamFactoryData>(data)
expectType<null>(data.opaque)
return new Writable()
}))
expectAssignable<Promise<Dispatcher.StreamData<{ example: string }>>>(stream('', { method: 'GET', opaque: { example: '' } }, data => {
expectType<{ example: string }>(data.opaque)
return new Writable()
}))

// pipeline
expectAssignable<Duplex>(pipeline('', { method: 'GET' }, data => {
expectAssignable<Dispatcher.PipelineHandlerData>(data)
expectType<null>(data.opaque)
return new Readable()
}))
expectAssignable<Duplex>(pipeline('', { method: 'GET', opaque: { example: '' } }, data => {
expectType<{ example: string }>(data.opaque)
return new Readable()
}))

Expand Down
22 changes: 22 additions & 0 deletions test/types/dispatcher.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ expectAssignable<Dispatcher>(new Dispatcher())
}))
expectAssignable<Promise<Dispatcher.ResponseData>>(dispatcher.request({ origin: '', path: '', method: 'GET', responseHeaders: 'raw' }))
expectAssignable<Promise<Dispatcher.ResponseData>>(dispatcher.request({ origin: '', path: '', method: 'GET', responseHeaders: null }))
expectAssignable<Promise<Dispatcher.ResponseData<{ example: string }>>>(dispatcher.request({ origin: '', path: '', method: 'GET', opaque: { example: '' } }))

// pipeline
expectAssignable<Duplex>(dispatcher.pipeline({ origin: '', path: '', method: 'GET', maxRedirections: 0 }, data => {
Expand All @@ -84,6 +85,11 @@ expectAssignable<Dispatcher>(new Dispatcher())
expectAssignable<Dispatcher.PipelineHandlerData>(data)
return new Readable()
}))
expectAssignable<Duplex>(dispatcher.pipeline({ origin: '', path: '', method: 'GET', opaque: { example: '' } }, data => {
expectAssignable<Dispatcher.PipelineHandlerData<{ example: string }>>(data)
expectType<{ example: string }>(data.opaque)
return new Readable()
}))

// stream
expectAssignable<Promise<Dispatcher.StreamData>>(dispatcher.stream({ origin: '', path: '', method: 'GET', maxRedirections: 0 }, data => {
Expand All @@ -94,6 +100,10 @@ expectAssignable<Dispatcher>(new Dispatcher())
expectAssignable<Dispatcher.StreamFactoryData>(data)
return new Writable()
}))
expectAssignable<Promise<Dispatcher.StreamData<{ example: string }>>>(dispatcher.stream({ origin: '', path: '', method: 'GET', opaque: { example: '' } }, data => {
expectType<{ example: string }>(data.opaque);
return new Writable();
}));
expectAssignable<void>(dispatcher.stream(
{ origin: '', path: '', method: 'GET', reset: false },
data => {
Expand All @@ -116,6 +126,18 @@ expectAssignable<Dispatcher>(new Dispatcher())
expectAssignable<Dispatcher.StreamData>(data)
}
))
expectAssignable<void>(dispatcher.stream(
{ origin: new URL('http://localhost'), path: '', method: 'GET', opaque: { example: '' } },
data => {
expectAssignable<Dispatcher.StreamFactoryData<{ example: string }>>(data)
return new Writable()
},
(err, data) => {
expectAssignable<Error | null>(err)
expectAssignable<Dispatcher.StreamData<{ example: string }>>(data)
expectType<{ example: string }>(data.opaque)
}
))
expectAssignable<Promise<Dispatcher.StreamData>>(dispatcher.stream({ origin: '', path: '', method: 'GET', responseHeaders: 'raw' }, data => {
expectAssignable<Dispatcher.StreamFactoryData>(data)
return new Writable()
Expand Down
26 changes: 13 additions & 13 deletions types/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,30 @@ export {
}

/** Performs an HTTP request. */
declare function request(
declare function request<TOpaque = null>(
url: string | URL | UrlObject,
options?: { dispatcher?: Dispatcher } & Omit<Dispatcher.RequestOptions, 'origin' | 'path' | 'method'> & Partial<Pick<Dispatcher.RequestOptions, 'method'>>,
): Promise<Dispatcher.ResponseData>;
options?: { dispatcher?: Dispatcher } & Omit<Dispatcher.RequestOptions<TOpaque>, 'origin' | 'path' | 'method'> & Partial<Pick<Dispatcher.RequestOptions, 'method'>>,
): Promise<Dispatcher.ResponseData<TOpaque>>;

/** A faster version of `request`. */
declare function stream(
declare function stream<TOpaque = null>(
url: string | URL | UrlObject,
options: { dispatcher?: Dispatcher } & Omit<Dispatcher.RequestOptions, 'origin' | 'path'>,
factory: Dispatcher.StreamFactory
): Promise<Dispatcher.StreamData>;
options: { dispatcher?: Dispatcher } & Omit<Dispatcher.RequestOptions<TOpaque>, 'origin' | 'path'>,
factory: Dispatcher.StreamFactory<TOpaque>
): Promise<Dispatcher.StreamData<TOpaque>>;

/** For easy use with `stream.pipeline`. */
declare function pipeline(
declare function pipeline<TOpaque = null>(
url: string | URL | UrlObject,
options: { dispatcher?: Dispatcher } & Omit<Dispatcher.PipelineOptions, 'origin' | 'path'>,
handler: Dispatcher.PipelineHandler
options: { dispatcher?: Dispatcher } & Omit<Dispatcher.PipelineOptions<TOpaque>, 'origin' | 'path'>,
handler: Dispatcher.PipelineHandler<TOpaque>
): Duplex;

/** Starts two-way communications with the requested resource. */
declare function connect(
declare function connect<TOpaque = null>(
url: string | URL | UrlObject,
options?: { dispatcher?: Dispatcher } & Omit<Dispatcher.ConnectOptions, 'origin' | 'path'>
): Promise<Dispatcher.ConnectData>;
options?: { dispatcher?: Dispatcher } & Omit<Dispatcher.ConnectOptions<TOpaque>, 'origin' | 'path'>
): Promise<Dispatcher.ConnectData<TOpaque>>;

/** Upgrade to a different protocol. */
declare function upgrade(
Expand Down
52 changes: 26 additions & 26 deletions types/dispatcher.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,19 @@ declare class Dispatcher extends EventEmitter {
/** Dispatches a request. This API is expected to evolve through semver-major versions and is less stable than the preceding higher level APIs. It is primarily intended for library developers who implement higher level APIs on top of this. */
dispatch(options: Dispatcher.DispatchOptions, handler: Dispatcher.DispatchHandlers): boolean;
/** Starts two-way communications with the requested resource. */
connect(options: Dispatcher.ConnectOptions): Promise<Dispatcher.ConnectData>;
connect(options: Dispatcher.ConnectOptions, callback: (err: Error | null, data: Dispatcher.ConnectData) => void): void;
connect<TOpaque = null>(options: Dispatcher.ConnectOptions<TOpaque>): Promise<Dispatcher.ConnectData<TOpaque>>;
connect<TOpaque = null>(options: Dispatcher.ConnectOptions<TOpaque>, callback: (err: Error | null, data: Dispatcher.ConnectData<TOpaque>) => void): void;
/** Compose a chain of dispatchers */
compose(dispatchers: Dispatcher.DispatcherComposeInterceptor[]): Dispatcher.ComposedDispatcher;
compose(...dispatchers: Dispatcher.DispatcherComposeInterceptor[]): Dispatcher.ComposedDispatcher;
/** Performs an HTTP request. */
request(options: Dispatcher.RequestOptions): Promise<Dispatcher.ResponseData>;
request(options: Dispatcher.RequestOptions, callback: (err: Error | null, data: Dispatcher.ResponseData) => void): void;
request<TOpaque = null>(options: Dispatcher.RequestOptions<TOpaque>): Promise<Dispatcher.ResponseData<TOpaque>>;
request<TOpaque = null>(options: Dispatcher.RequestOptions<TOpaque>, callback: (err: Error | null, data: Dispatcher.ResponseData<TOpaque>) => void): void;
/** For easy use with `stream.pipeline`. */
pipeline(options: Dispatcher.PipelineOptions, handler: Dispatcher.PipelineHandler): Duplex;
pipeline<TOpaque = null>(options: Dispatcher.PipelineOptions<TOpaque>, handler: Dispatcher.PipelineHandler<TOpaque>): Duplex;
/** A faster version of `Dispatcher.request`. */
stream(options: Dispatcher.RequestOptions, factory: Dispatcher.StreamFactory): Promise<Dispatcher.StreamData>;
stream(options: Dispatcher.RequestOptions, factory: Dispatcher.StreamFactory, callback: (err: Error | null, data: Dispatcher.StreamData) => void): void;
stream<TOpaque = null>(options: Dispatcher.RequestOptions<TOpaque>, factory: Dispatcher.StreamFactory<TOpaque>): Promise<Dispatcher.StreamData<TOpaque>>;
stream<TOpaque = null>(options: Dispatcher.RequestOptions<TOpaque>, factory: Dispatcher.StreamFactory<TOpaque>, callback: (err: Error | null, data: Dispatcher.StreamData<TOpaque>) => void): void;
/** Upgrade to a different protocol. */
upgrade(options: Dispatcher.UpgradeOptions): Promise<Dispatcher.UpgradeData>;
upgrade(options: Dispatcher.UpgradeOptions, callback: (err: Error | null, data: Dispatcher.UpgradeData) => void): void;
Expand Down Expand Up @@ -125,25 +125,25 @@ declare namespace Dispatcher {
/** For H2, it appends the expect: 100-continue header, and halts the request body until a 100-continue is received from the remote server*/
expectContinue?: boolean;
}
export interface ConnectOptions {
export interface ConnectOptions<TOpaque = null> {
origin: string | URL;
path: string;
/** Default: `null` */
headers?: IncomingHttpHeaders | string[] | null;
/** Default: `null` */
signal?: AbortSignal | EventEmitter | null;
/** This argument parameter is passed through to `ConnectData` */
opaque?: unknown;
opaque?: TOpaque;
/** Default: 0 */
maxRedirections?: number;
/** Default: false */
redirectionLimitReached?: boolean;
/** Default: `null` */
responseHeaders?: 'raw' | null;
}
export interface RequestOptions extends DispatchOptions {
export interface RequestOptions<TOpaque = null> extends DispatchOptions {
/** Default: `null` */
opaque?: unknown;
opaque?: TOpaque;
/** Default: `null` */
signal?: AbortSignal | EventEmitter | null;
/** Default: 0 */
Expand All @@ -157,7 +157,7 @@ declare namespace Dispatcher {
/** Default: `64 KiB` */
highWaterMark?: number;
}
export interface PipelineOptions extends RequestOptions {
export interface PipelineOptions<TOpaque = null> extends RequestOptions<TOpaque> {
/** `true` if the `handler` will return an object stream. Default: `false` */
objectMode?: boolean;
}
Expand All @@ -178,43 +178,43 @@ declare namespace Dispatcher {
/** Default: `null` */
responseHeaders?: 'raw' | null;
}
export interface ConnectData {
export interface ConnectData<TOpaque = null> {
statusCode: number;
headers: IncomingHttpHeaders;
socket: Duplex;
opaque: unknown;
opaque: TOpaque;
}
export interface ResponseData {
export interface ResponseData<TOpaque = null> {
statusCode: number;
headers: IncomingHttpHeaders;
body: BodyReadable & BodyMixin;
trailers: Record<string, string>;
opaque: unknown;
opaque: TOpaque;
context: object;
}
export interface PipelineHandlerData {
export interface PipelineHandlerData<TOpaque = null> {
statusCode: number;
headers: IncomingHttpHeaders;
opaque: unknown;
opaque: TOpaque;
body: BodyReadable;
context: object;
}
export interface StreamData {
opaque: unknown;
export interface StreamData<TOpaque = null> {
opaque: TOpaque;
trailers: Record<string, string>;
}
export interface UpgradeData {
export interface UpgradeData<TOpaque = null> {
headers: IncomingHttpHeaders;
socket: Duplex;
opaque: unknown;
opaque: TOpaque;
}
export interface StreamFactoryData {
export interface StreamFactoryData<TOpaque = null> {
statusCode: number;
headers: IncomingHttpHeaders;
opaque: unknown;
opaque: TOpaque;
context: object;
}
export type StreamFactory = (data: StreamFactoryData) => Writable;
export type StreamFactory<TOpaque = null> = (data: StreamFactoryData<TOpaque>) => Writable;
export interface DispatchHandlers {
/** Invoked before request is dispatched on socket. May be invoked multiple times when a request is retried when the request at the head of the pipeline fails. */
onConnect?(abort: (err?: Error) => void): void;
Expand All @@ -233,7 +233,7 @@ declare namespace Dispatcher {
/** Invoked when a body chunk is sent to the server. May be invoked multiple times for chunked requests */
onBodySent?(chunkSize: number, totalBytesSent: number): void;
}
export type PipelineHandler = (data: PipelineHandlerData) => Readable;
export type PipelineHandler<TOpaque = null> = (data: PipelineHandlerData<TOpaque>) => Readable;
export type HttpMethod = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'CONNECT' | 'OPTIONS' | 'TRACE' | 'PATCH';

/**
Expand Down
Loading