Skip to content

Commit

Permalink
refa: merge file into core
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Apr 26, 2024
1 parent e8a216a commit 6ca21e9
Show file tree
Hide file tree
Showing 18 changed files with 148 additions and 259 deletions.
20 changes: 10 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@root/undios",
"name": "@root/http",
"private": true,
"type": "module",
"version": "1.0.0",
Expand All @@ -20,21 +20,21 @@
"test:html": "shx rm -rf coverage && c8 -r html yarn test"
},
"devDependencies": {
"@cordisjs/eslint-config": "^1.0.4",
"@types/chai": "^4.3.11",
"@cordisjs/eslint-config": "^1.1.1",
"@types/chai": "^4.3.14",
"@types/chai-as-promised": "^7.1.8",
"@types/node": "^20.10.2",
"@types/node": "^20.11.30",
"c8": "^7.14.0",
"chai": "^4.3.10",
"chai": "^4.4.1",
"chai-as-promised": "^7.1.1",
"esbuild": "^0.18.20",
"esbuild-register": "^3.5.0",
"eslint": "^8.55.0",
"eslint": "^8.57.0",
"shx": "^0.3.4",
"tsx": "^4.7.0",
"typescript": "^5.3.2",
"yakumo": "^1.0.0-beta.7",
"yakumo-esbuild": "^1.0.0-beta.3",
"tsx": "^4.7.1",
"typescript": "^5.4.3",
"yakumo": "^1.0.0-beta.13",
"yakumo-esbuild": "^1.0.0-beta.5",
"yakumo-publish-sync": "^1.0.0-alpha.1",
"yakumo-tsc": "^1.0.0-alpha.2"
}
Expand Down
21 changes: 12 additions & 9 deletions packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "undios",
"name": "@cordisjs/plugin-http",
"description": "Fetch-based axios-style HTTP client",
"version": "0.3.3",
"version": "0.4.0",
"type": "module",
"main": "lib/index.js",
"types": "lib/index.d.ts",
Expand Down Expand Up @@ -30,13 +30,13 @@
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/cordiverse/undios.git",
"url": "git+https://github.com/cordiverse/http.git",
"directory": "packages/core"
},
"bugs": {
"url": "https://github.com/cordiverse/undios/issues"
"url": "https://github.com/cordiverse/http/issues"
},
"homepage": "https://github.com/cordiverse/undios",
"homepage": "https://github.com/cordiverse/http",
"keywords": [
"http",
"fetch",
Expand All @@ -49,14 +49,17 @@
"plugin"
],
"devDependencies": {
"cordis": "^3.13.3",
"undici": "^6.6.2"
"@types/mime-db": "^1.43.5",
"cordis": "^3.13.6",
"undici": "^6.10.1"
},
"peerDependencies": {
"cordis": "^3.13.3"
"cordis": "^3.13.6"
},
"dependencies": {
"cosmokit": "^1.5.2",
"cosmokit": "^1.6.2",
"file-type": "^16.5.4",
"mime-db": "^1.52.0",
"ws": "^8.16.0"
}
}
4 changes: 2 additions & 2 deletions packages/core/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Fetch-based axios-style HTTP client.
## Basic Usage

```ts
import Undios from 'undios'
import Undios from '@cordisjs/plugin-http'

const http = new Undios()

Expand Down Expand Up @@ -163,7 +163,7 @@ The request timeout in milliseconds.

> [!NOTE]
>
> In order to use a proxy agent, you need to install `undios-proxy-agent`.
> In order to use a proxy agent, you need to install `@cordisjs/plugin-proxy-agent`.
### Response

Expand Down
34 changes: 34 additions & 0 deletions packages/core/src/adapter/browser.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,36 @@
// Modified from https://github.com/sindresorhus/ip-regex/blob/3e220cae3eb66ecfdf4f7678bea7306ceaa41c76/index.js

import { LookupAddress } from 'dns'
import { FileResponse } from '../index.js'

const v4 = /^(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)){3}$/

const v6seg = '[a-fA-F\\d]{1,4}'

/* eslint-disable no-multi-spaces */
const v6core = [
`(?:${v6seg}:){7}(?:${v6seg}|:)`, // 1:2:3:4:5:6:7:: 1:2:3:4:5:6:7:8
`(?:${v6seg}:){6}(?:${v4}|:${v6seg}|:)`, // 1:2:3:4:5:6:: 1:2:3:4:5:6::8 1:2:3:4:5:6::8 1:2:3:4:5:6::1.2.3.4
`(?:${v6seg}:){5}(?::${v4}|(?::${v6seg}){1,2}|:)`, // 1:2:3:4:5:: 1:2:3:4:5::7:8 1:2:3:4:5::8 1:2:3:4:5::7:1.2.3.4
`(?:${v6seg}:){4}(?:(?::${v6seg}){0,1}:${v4}|(?::${v6seg}){1,3}|:)`, // 1:2:3:4:: 1:2:3:4::6:7:8 1:2:3:4::8 1:2:3:4::6:7:1.2.3.4
`(?:${v6seg}:){3}(?:(?::${v6seg}){0,2}:${v4}|(?::${v6seg}){1,4}|:)`, // 1:2:3:: 1:2:3::5:6:7:8 1:2:3::8 1:2:3::5:6:7:1.2.3.4
`(?:${v6seg}:){2}(?:(?::${v6seg}){0,3}:${v4}|(?::${v6seg}){1,5}|:)`, // 1:2:: 1:2::4:5:6:7:8 1:2::8 1:2::4:5:6:7:1.2.3.4
`(?:${v6seg}:){1}(?:(?::${v6seg}){0,4}:${v4}|(?::${v6seg}){1,6}|:)`, // 1:: 1::3:4:5:6:7:8 1::8 1::3:4:5:6:7:1.2.3.4
`(?::(?:(?::${v6seg}){0,5}:${v4}|(?::${v6seg}){1,7}|:))`, // ::2:3:4:5:6:7:8 ::2:3:4:5:6:7:8 ::8 ::1.2.3.4
]
/* eslint-enable no-multi-spaces */

const v6 = new RegExp(`^(?:${v6core.join('|')})(?:%[0-9a-zA-Z]{1,})?$`)

export async function lookup(address: string): Promise<LookupAddress> {
if (v4.test(address)) return { address, family: 4 }
if (v6.test(address)) return { address, family: 6 }
throw new Error('Invalid IP address')
}

export async function loadFile(url: string): Promise<FileResponse | undefined> {
return undefined
}

const { WebSocket } = globalThis
export { WebSocket }
18 changes: 18 additions & 0 deletions packages/core/src/adapter/node.ts
Original file line number Diff line number Diff line change
@@ -1 +1,19 @@
import { fileURLToPath } from 'node:url'
import { basename } from 'node:path'
import { fromBuffer } from 'file-type'
import { FileResponse } from '../index.js'
import { readFile } from 'node:fs/promises'

export { lookup } from 'node:dns/promises'

export async function loadFile(url: string): Promise<FileResponse | undefined> {
if (url.startsWith('file://')) {
const data = await readFile(fileURLToPath(url))
const result = await fromBuffer(data)
// https://stackoverflow.com/questions/8609289/convert-a-binary-nodejs-buffer-to-javascript-arraybuffer#answer-31394257
const buffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength)
return { mime: result?.mime, filename: basename(url), data: buffer }
}
}

export { WebSocket } from 'ws'
55 changes: 51 additions & 4 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Context, Service } from 'cordis'
import { Awaitable, defineProperty, Dict, isNullable, trimSlash } from 'cosmokit'
import { Awaitable, Binary, defineProperty, Dict, isNullable, trimSlash } from 'cosmokit'
import { ClientOptions } from 'ws'
import { WebSocket } from 'undios/adapter'
import { loadFile, lookup, WebSocket } from '@cordisjs/plugin-http/adapter'
import { ReadableStream } from 'node:stream/web'
import { isLocalAddress } from './utils.ts'
import mimedb from 'mime-db'

export type { WebSocket } from 'undios/adapter'
export type { WebSocket } from '@cordisjs/plugin-http/adapter'

declare module 'cordis' {
interface Context {
Expand All @@ -22,7 +24,7 @@ declare module 'cordis' {
}
}

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

class HTTPError extends Error {
[kHTTPError] = true
Expand Down Expand Up @@ -115,6 +117,16 @@ export namespace HTTP {
}
}

export interface FileConfig {
timeout?: number | string
}

export interface FileResponse {
mime?: string
filename: string
data: ArrayBuffer
}

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>>
Expand Down Expand Up @@ -363,6 +375,41 @@ export class HTTP extends Service<HTTP.Config> {
})
return socket
}

async file(this: HTTP, url: string, options: FileConfig = {}): Promise<FileResponse> {
const result = await loadFile(url)
if (result) return result
const capture = /^data:([\w/-]+);base64,(.*)$/.exec(url)
if (capture) {
const [, mime, base64] = capture
let name = 'file'
const ext = mime && mimedb[mime]?.extensions?.[0]
if (ext) name += `.${ext}`
return { mime, data: Binary.fromBase64(base64), filename: name }
}
const { headers, data, url: responseUrl } = await this<ArrayBuffer>(url, {
method: 'GET',
responseType: 'arraybuffer',
timeout: +options.timeout! || undefined,
})
const mime = headers.get('content-type') ?? undefined
const [, name] = responseUrl.match(/.+\/([^/?]*)(?=\?)?/)!
return { mime, filename: name, data }
}

async isLocal(url: string) {
let { hostname, protocol } = new URL(url)
if (protocol !== 'http:' && protocol !== 'https:') return true
if (/^\[.+\]$/.test(hostname)) {
hostname = hostname.slice(1, -1)
}
try {
const address = await lookup(hostname)
return isLocalAddress(address)
} catch {
return false
}
}
}

export default HTTP
2 changes: 1 addition & 1 deletion packages/file/src/utils.ts → packages/core/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { LookupAddress } from 'dns'
import { LookupAddress } from 'node:dns'

/* eslint-disable no-multi-spaces */
const bogonV4 = [
Expand Down
66 changes: 0 additions & 66 deletions packages/file/package.json

This file was deleted.

3 changes: 0 additions & 3 deletions packages/file/readme.md

This file was deleted.

33 changes: 0 additions & 33 deletions packages/file/src/adapter/browser.ts

This file was deleted.

5 changes: 0 additions & 5 deletions packages/file/src/adapter/index.d.ts

This file was deleted.

17 changes: 0 additions & 17 deletions packages/file/src/adapter/node.ts

This file was deleted.

Loading

0 comments on commit 6ca21e9

Please sign in to comment.