Skip to content

Commit

Permalink
fix(undios): fix request encoding
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Feb 17, 2024
1 parent 6dd109e commit 4ccbd3e
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 27 deletions.
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -33,7 +33,7 @@
"shx": "^0.3.4",
"tsx": "^4.7.0",
"typescript": "^5.3.2",
"yakumo": "^1.0.0-alpha.10",
"yakumo": "^1.0.0-beta.7",
"yakumo-esbuild": "^1.0.0-beta.3",
"yakumo-publish-sync": "^1.0.0-alpha.1",
"yakumo-tsc": "^1.0.0-alpha.2"
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
@@ -1,7 +1,7 @@
{
"name": "undios",
"description": "Fetch-based axios-style HTTP client",
"version": "0.1.5",
"version": "0.1.9",
"type": "module",
"main": "lib/index.js",
"types": "lib/index.d.ts",
Expand Down
64 changes: 46 additions & 18 deletions packages/core/src/index.ts
Expand Up @@ -21,7 +21,7 @@ declare module 'cordis' {
}
}

const kHTTPError = Symbol.for('cordis.http.error')
const kHTTPError = Symbol.for('undios.error')

class HTTPError extends Error {
[kHTTPError] = true
Expand All @@ -32,6 +32,18 @@ class HTTPError extends Error {
}
}

/**
* @see https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
*/
function encodeRequest(data: any): [string | null, any] {
if (data instanceof URLSearchParams) return [null, data]
if (data instanceof ArrayBuffer) return [null, data]
if (ArrayBuffer.isView(data)) return [null, data]
if (data instanceof Blob) return [null, data]
if (data instanceof FormData) return [null, data]
return ['application/json', JSON.stringify(data)]
}

export namespace HTTP {
export type Method =
| 'get' | 'GET'
Expand All @@ -45,23 +57,21 @@ export namespace HTTP {
| 'link' | 'LINK'
| 'unlink' | 'UNLINK'

export type ResponseType =
| 'arraybuffer'
| 'json'
| 'text'
| 'stream'
export interface ResponseTypes {
text: string
stream: ReadableStream<Uint8Array>
blob: Blob
formdata: FormData
arraybuffer: ArrayBuffer
}

export interface Request1 {
(url: string, config?: HTTP.RequestConfig & { responseType: 'arraybuffer' }): Promise<ArrayBuffer>
(url: string, config?: HTTP.RequestConfig & { responseType: 'stream' }): Promise<ReadableStream<Uint8Array>>
(url: string, config?: HTTP.RequestConfig & { responseType: 'text' }): Promise<string>
<K extends keyof ResponseTypes>(url: string, config: HTTP.RequestConfig & { responseType: K }): Promise<ResponseTypes[K]>
<T = any>(url: string, config?: HTTP.RequestConfig): Promise<T>
}

export interface Request2 {
(url: string, data?: any, config?: HTTP.RequestConfig & { responseType: 'arraybuffer' }): Promise<ArrayBuffer>
(url: string, data?: any, config?: HTTP.RequestConfig & { responseType: 'stream' }): Promise<ReadableStream<Uint8Array>>
(url: string, data?: any, config?: HTTP.RequestConfig & { responseType: 'text' }): Promise<string>
<K extends keyof ResponseTypes>(url: string, data: any, config: HTTP.RequestConfig & { responseType: K }): Promise<ResponseTypes[K]>
<T = any>(url: string, data?: any, config?: HTTP.RequestConfig): Promise<T>
}

Expand All @@ -78,7 +88,8 @@ export namespace HTTP {
params?: Dict
data?: any
keepAlive?: boolean
responseType?: ResponseType
redirect?: RequestRedirect
responseType?: keyof ResponseTypes
}

export interface Response<T = any> {
Expand All @@ -93,6 +104,7 @@ export namespace HTTP {
}

export interface HTTP {
<K extends keyof HTTP.ResponseTypes>(url: string, 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>>
config: HTTP.Config
Expand All @@ -111,14 +123,14 @@ export class HTTP extends Service {
static {
for (const method of ['get', 'delete'] as const) {
defineProperty(HTTP.prototype, method, async function (this: HTTP, url: string, config?: HTTP.Config) {
const response = await this(method, url, config)
const response = await this(url, { method, ...config })
return response.data
})
}

for (const method of ['patch', 'post', 'put'] as const) {
defineProperty(HTTP.prototype, method, async function (this: HTTP, url: string, data?: any, config?: HTTP.Config) {
const response = await this(method, url, { data, ...config })
const response = await this(url, { method, data, ...config })
return response.data
})
}
Expand Down Expand Up @@ -176,7 +188,7 @@ export class HTTP extends Service {
}

decodeResponse(response: Response) {
const type = response.headers.get('content-type')
const type = response.headers.get('Content-Type')
if (type?.startsWith('application/json')) {
return response.json()
} else if (type?.startsWith('text/')) {
Expand All @@ -194,6 +206,7 @@ export class HTTP extends Service {
}
const config = this.resolveConfig(args[1])
const url = this.resolveURL(args[0], config)
method ??= config.method ?? 'GET'

const controller = new AbortController()
let timer: NodeJS.Timeout | number | undefined
Expand All @@ -208,13 +221,22 @@ export class HTTP extends Service {
}

try {
const headers = new Headers(config.headers)
const init: RequestInit = {
method,
headers,
body: config.data,
keepalive: config.keepAlive,
headers: config.headers,
redirect: config.redirect,
signal: controller.signal,
}
if (config.data && typeof config.data === 'object') {
const [type, body] = encodeRequest(config.data)
init.body = body
if (type && !headers.has('Content-Type')) {
headers.append('Content-Type', type)
}
}
caller.emit('http/fetch-init', init, config)
const raw = await fetch(url, init).catch((cause) => {
const error = new HTTP.Error(`fetch ${url} failed`)
Expand All @@ -241,6 +263,12 @@ export class HTTP extends Service {

if (config.responseType === 'arraybuffer') {
response.data = await raw.arrayBuffer()
} else if (config.responseType === 'text') {
response.data = await raw.text()
} else if (config.responseType === 'blob') {
response.data = await raw.blob()
} else if (config.responseType === 'formdata') {
response.data = await raw.formData()
} else if (config.responseType === 'stream') {
response.data = raw.body
} else {
Expand All @@ -253,7 +281,7 @@ export class HTTP extends Service {
}

async head(url: string, config?: HTTP.Config) {
const response = await this('HEAD', url, config)
const response = await this(url, { method: 'HEAD', ...config })
return response.headers
}

Expand Down
4 changes: 2 additions & 2 deletions packages/file/package.json
@@ -1,7 +1,7 @@
{
"name": "undios-file",
"description": "File support for undios",
"version": "0.1.1",
"version": "0.1.2",
"type": "module",
"main": "lib/index.js",
"types": "lib/index.d.ts",
Expand Down Expand Up @@ -56,7 +56,7 @@
},
"peerDependencies": {
"cordis": "^3.10.4",
"undios": "^0.1.5"
"undios": "^0.1.9"
},
"dependencies": {
"cosmokit": "^1.5.2",
Expand Down
6 changes: 3 additions & 3 deletions packages/file/src/index.ts
Expand Up @@ -30,7 +30,7 @@ export const Config: z<Config> = z.object({})

export function apply(ctx: Context, config: Config) {
ctx.provide('http.file')
ctx.provide('http.local')
ctx.provide('http.isLocal')

function createName(mime: string | undefined) {
let name = 'file'
Expand All @@ -57,7 +57,7 @@ export function apply(ctx: Context, config: Config) {
return { mime, filename: name, data }
}

ctx['http.local'] = async function isLocal(url: string) {
ctx['http.isLocal'] = async function isLocal(url: string) {
let { hostname, protocol } = new URL(url)
if (protocol !== 'http:' && protocol !== 'https:') return true
if (/^\[.+\]$/.test(hostname)) {
Expand All @@ -73,6 +73,6 @@ export function apply(ctx: Context, config: Config) {

ctx.on('dispose', () => {
ctx['http.file'] = undefined
ctx['http.local'] = undefined
ctx['http.isLocal'] = undefined
})
}
4 changes: 2 additions & 2 deletions packages/proxy-agent/package.json
@@ -1,7 +1,7 @@
{
"name": "undios-proxy-agent",
"description": "Proxy agent support for undios",
"version": "0.1.3",
"version": "0.1.4",
"type": "module",
"main": "lib/index.js",
"types": "lib/index.d.ts",
Expand Down Expand Up @@ -48,7 +48,7 @@
},
"peerDependencies": {
"cordis": "^3.10.4",
"undios": "^0.1.5"
"undios": "^0.1.9"
},
"dependencies": {
"http-proxy-agent": "^7.0.0",
Expand Down

0 comments on commit 4ccbd3e

Please sign in to comment.