@cordisjs/plugin-http
Advanced tools
+12
-14
@@ -19,6 +19,6 @@ import { Context, Service, z } from 'cordis'; | ||
| code?: HTTP.Error.Code | undefined; | ||
| response?: Response | undefined; | ||
| [kHTTPError]: boolean; | ||
| response?: HTTP.Response; | ||
| static is(error: any): error is HTTPError; | ||
| constructor(message?: string, code?: HTTP.Error.Code | undefined); | ||
| constructor(message?: string, code?: HTTP.Error.Code | undefined, response?: Response | undefined); | ||
| } | ||
@@ -34,2 +34,3 @@ export declare namespace HTTP { | ||
| arraybuffer: ArrayBuffer; | ||
| headers: Headers; | ||
| } | ||
@@ -55,3 +56,3 @@ interface Request1 { | ||
| interface Intercept { | ||
| baseURL?: string; | ||
| baseUrl?: string; | ||
| headers?: Dict; | ||
@@ -90,3 +91,3 @@ timeout?: number; | ||
| namespace Error { | ||
| type Code = 'ETIMEDOUT'; | ||
| type Code = 'TIMEOUT' | 'STATUS_ERROR'; | ||
| } | ||
@@ -98,7 +99,3 @@ } | ||
| export interface HTTP { | ||
| <K extends keyof HTTP.ResponseTypes>(url: string | URL, config: HTTP.RequestConfig & { | ||
| responseType: K; | ||
| }): Promise<HTTP.Response<HTTP.ResponseTypes[K]>>; | ||
| <T = any>(url: string | URL, config?: HTTP.RequestConfig): Promise<HTTP.Response<T>>; | ||
| <T = any>(method: HTTP.Method, url: string | URL, config?: HTTP.RequestConfig): Promise<HTTP.Response<T>>; | ||
| (url: string | URL, config?: HTTP.RequestConfig): Promise<Response>; | ||
| config: HTTP.Config; | ||
@@ -126,8 +123,8 @@ get: HTTP.Request1; | ||
| }; | ||
| baseURL?: string; | ||
| baseUrl?: string; | ||
| timeout?: number; | ||
| proxyAgent?: string; | ||
| }; | ||
| decoder<K extends keyof HTTP.ResponseTypes>(type: K, decoder: HTTP.Decoder<HTTP.ResponseTypes[K]>): import("cordis").Disposable<void | Promise<void>>; | ||
| proxy(name: string[], factory: (url: URL) => Dispatcher): import("cordis").Disposable<void | Promise<void>>; | ||
| decoder<K extends keyof HTTP.ResponseTypes>(type: K, decoder: HTTP.Decoder<HTTP.ResponseTypes[K]>): import("cordis").Disposable<Promise<void>>; | ||
| proxy(name: string[], factory: (url: URL) => Dispatcher): import("cordis").Disposable<Promise<void>>; | ||
| extend(config?: HTTP.Config): any; | ||
@@ -137,6 +134,7 @@ resolveConfig(init?: HTTP.RequestConfig): HTTP.RequestConfig; | ||
| defaultDecoder(response: Response): Promise<any>; | ||
| [Service.invoke](...args: any[]): Promise<HTTP.Response<any>>; | ||
| head(url: string | URL, config?: HTTP.Config): Promise<Headers>; | ||
| [Service.invoke](...args: any[]): Promise<Response>; | ||
| private _decode; | ||
| head(url: string | URL, config?: HTTP.RequestConfig): Promise<any>; | ||
| ws(url: string | URL, _config?: HTTP.Config): import("undici").WebSocket; | ||
| } | ||
| export default HTTP; |
+33
-41
@@ -55,6 +55,8 @@ var __create = Object.create; | ||
| var kHTTPError = Symbol.for("cordis.http.error"); | ||
| var kHTTPConfig = Symbol.for("cordis.http.config"); | ||
| var HTTPError = class extends Error { | ||
| constructor(message, code) { | ||
| constructor(message, code, response) { | ||
| super(message); | ||
| this.code = code; | ||
| this.response = response; | ||
| } | ||
@@ -65,3 +67,2 @@ static { | ||
| [kHTTPError] = true; | ||
| response; | ||
| static is(error) { | ||
@@ -93,2 +94,3 @@ return !!error?.[kHTTPError]; | ||
| this.decoder("stream", (raw) => raw.body); | ||
| this.decoder("headers", (raw) => raw.headers); | ||
| this.proxy(["http", "https"], (url) => { | ||
@@ -146,3 +148,3 @@ return new this.undici.ProxyAgent(url.href); | ||
| const response = await this(url, { method, validateStatus, ...config }); | ||
| return response.data; | ||
| return this._decode(response); | ||
| }); | ||
@@ -153,3 +155,3 @@ } | ||
| const response = await this(url, { method, data, validateStatus, ...config }); | ||
| return response.data; | ||
| return this._decode(response); | ||
| }); | ||
@@ -164,3 +166,3 @@ } | ||
| Config = z.object({ | ||
| baseURL: z.string().description("基础 URL。"), | ||
| baseUrl: z.string().description("基础 URL。"), | ||
| timeout: z.natural().role("ms").description("等待请求的最长时间。"), | ||
@@ -213,3 +215,3 @@ keepAlive: z.boolean().description("是否保持连接。"), | ||
| try { | ||
| url = new URL(url, config.baseURL); | ||
| url = new URL(url, config.baseUrl); | ||
| } catch (error) { | ||
@@ -254,3 +256,3 @@ throw new TypeError(`Invalid URL: ${url}`); | ||
| const timer = config.timeout && setTimeout(() => { | ||
| controller.abort(new HTTPError("request timeout", "ETIMEDOUT")); | ||
| controller.abort(new HTTPError("request timeout", "TIMEOUT")); | ||
| }, config.timeout); | ||
@@ -285,3 +287,3 @@ return () => { | ||
| } | ||
| const raw = await this.ctx.waterfall("http/fetch", url, init, config, () => { | ||
| const response = await this.ctx.waterfall("http/fetch", url, init, config, () => { | ||
| return this.undici.fetch(url, init); | ||
@@ -294,33 +296,3 @@ }).catch((cause) => { | ||
| }); | ||
| const response = { | ||
| data: null, | ||
| url: raw.url, | ||
| status: raw.status, | ||
| statusText: raw.statusText, | ||
| headers: raw.headers | ||
| }; | ||
| const validateStatus2 = config.validateStatus ?? (() => true); | ||
| if (!validateStatus2(raw.status)) { | ||
| const error = new _HTTP.Error(raw.statusText); | ||
| error.response = response; | ||
| try { | ||
| response.data = await this.defaultDecoder(raw); | ||
| } catch { | ||
| } | ||
| throw error; | ||
| } | ||
| if (config.responseType) { | ||
| let decoder; | ||
| if (typeof config.responseType === "function") { | ||
| decoder = config.responseType; | ||
| } else { | ||
| decoder = this._decoders[config.responseType]; | ||
| if (!decoder) { | ||
| throw new TypeError(`Unknown responseType: ${config.responseType}`); | ||
| } | ||
| } | ||
| response.data = await decoder(raw); | ||
| } else { | ||
| response.data = await this.defaultDecoder(raw); | ||
| } | ||
| response[kHTTPConfig] = config; | ||
| return response; | ||
@@ -331,5 +303,25 @@ } finally { | ||
| } | ||
| async _decode(response) { | ||
| const config = response[kHTTPConfig]; | ||
| const validateStatus2 = config.validateStatus ?? (() => true); | ||
| if (!validateStatus2(response.status)) { | ||
| throw new _HTTP.Error(response.statusText, "STATUS_ERROR", response); | ||
| } | ||
| if (!config.responseType) { | ||
| return this.defaultDecoder(response); | ||
| } | ||
| let decoder; | ||
| if (typeof config.responseType === "function") { | ||
| decoder = config.responseType; | ||
| } else { | ||
| decoder = this._decoders[config.responseType]; | ||
| if (!decoder) { | ||
| throw new TypeError(`Unknown responseType: ${config.responseType}`); | ||
| } | ||
| } | ||
| return decoder(response); | ||
| } | ||
| async head(url, config) { | ||
| const response = await this(url, { method: "HEAD", validateStatus, ...config }); | ||
| return response.headers; | ||
| const response = await this(url, { method: "HEAD", responseType: "headers", ...config }); | ||
| return this._decode(response); | ||
| } | ||
@@ -336,0 +328,0 @@ ws(url, _config) { |
+4
-3
| { | ||
| "name": "@cordisjs/plugin-http", | ||
| "description": "Fetch-based axios-style HTTP client", | ||
| "version": "1.1.0", | ||
| "version": "1.2.0", | ||
| "type": "module", | ||
@@ -41,3 +41,3 @@ "main": "lib/index.js", | ||
| "peerDependencies": { | ||
| "cordis": "^4.0.0-beta.0", | ||
| "cordis": "^4.0.0-beta.3", | ||
| "undici": "^7.5.0" | ||
@@ -51,3 +51,4 @@ }, | ||
| "devDependencies": { | ||
| "cordis": "^4.0.0-beta.0", | ||
| "@cordisjs/plugin-logger": "^1.0.0", | ||
| "cordis": "^4.0.0-beta.3", | ||
| "undici": "^7.5.0" | ||
@@ -54,0 +55,0 @@ }, |
+18
-29
@@ -1,9 +0,5 @@ | ||
| # Undios | ||
| # @cordisjs/http | ||
| Fetch-based axios-style HTTP client. | ||
| Fetch-based HTTP client for [Cordis](https://cordis.io). | ||
| > "und" comes from undici, an HTTP/1.1 client officially supported by Node.js team. | ||
| > | ||
| > "ios" comes from axios, a popular HTTP client for browser and Node.js. | ||
| ## Features | ||
@@ -15,12 +11,14 @@ | ||
| ## Basic Usage | ||
| ## Usage | ||
| ```ts | ||
| import Undios from '@cordisjs/plugin-http' | ||
| import { Context } from 'cordis' | ||
| import HTTP from '@cordisjs/plugin-http' | ||
| const http = new Undios() | ||
| const ctx = new Context() | ||
| ctx.plugin(HTTP) | ||
| const data = await http.get('https://example.com') | ||
| const data = await http.post('https://example.com', body) | ||
| const { status, data } = await http('https://example.com', { method: 'GET' }) | ||
| const data = await ctx.http.get('https://example.com') | ||
| const data = await ctx.http.post('https://example.com', body) | ||
| const { status, data } = await ctx.http('https://example.com', { method: 'GET' }) | ||
| ``` | ||
@@ -36,3 +34,2 @@ | ||
| interface HTTP { | ||
| <K extends keyof ResponseTypes>(url: string, config: Config & { responseType: K }): Promise<Response<ResponseTypes[K]>> | ||
| <T = any>(url: string | URL, config?: Config): Promise<Response<T>> | ||
@@ -92,2 +89,8 @@ } | ||
| #### http.Error.is(error) | ||
| ```ts | ||
| function is(error: any): error is HTTP.Error | ||
| ``` | ||
| ### Config | ||
@@ -97,3 +100,3 @@ | ||
| interface Config { | ||
| baseURL?: string | ||
| baseUrl?: string | ||
| method?: Method | ||
@@ -110,3 +113,3 @@ headers?: Record<string, string> | ||
| #### config.baseURL | ||
| #### config.baseUrl | ||
@@ -199,15 +202,1 @@ The base URL of the request. If it is set, the `url` will be resolved against it. | ||
| The decoded response body. | ||
| ### Static Methods | ||
| ```ts | ||
| class Undios { | ||
| constructor(config?: Config) | ||
| } | ||
| ``` | ||
| #### Undios.Error.is(error) | ||
| ```ts | ||
| function is(error: any): error is Undios.Error | ||
| ``` |
24080
-2.3%3
50%481
-2.04%197
-5.29%