diff --git a/package.json b/package.json index b4b5446..6d366d1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kevisual/query", - "version": "0.0.7-alpha.1", + "version": "0.0.7-alpha.2", "main": "dist/index.js", "module": "dist/index.js", "types": "dist/index.d.ts", @@ -8,7 +8,7 @@ "type": "module", "scripts": { "build": "npm run clean && rollup -c", - "test": "NODE_ENV=development node --experimental-vm-modules node_modules/jest/bin/jest.js --detectOpenHandles", + "build:app": "npm run build && rsync dist/* ../deploy/dist", "clean": "rm -rf dist" }, "files": [ @@ -27,8 +27,9 @@ "rollup": "^4.21.2", "ts-node": "^10.9.2", "tslib": "^2.7.0", + "typescript": "^5.5.4", "zustand": "^4.5.5", - "typescript": "^5.5.4" + "rollup-plugin-dts": "^6.1.1" }, "packageManager": "yarn@1.22.19+sha1.4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447", "publishConfig": { @@ -40,16 +41,17 @@ }, "exports": { ".": { - "import": "./dist/index.js", - "require": "./dist/index.js" + "import": "./dist/query-browser.js", + "require": "./dist/query-browser.js" }, - "./node": { - "import": "./dist/node-adapter.js", - "require": "./dist/node-adapter.js" + "./query": { + "import": "./dist/query.js", + "require": "./dist/query.js" }, "./ws": { - "import": "./dist/ws.js", - "require": "./dist/ws.js" + "import": "./dist/query-ws.js", + "require": "./dist/query-ws.js" } - } + }, + "dependencies": {} } \ No newline at end of file diff --git a/rollup.config.js b/rollup.config.js index 53a4a51..5025891 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -2,7 +2,7 @@ import typescript from '@rollup/plugin-typescript'; import resolve from '@rollup/plugin-node-resolve'; - +import { dts } from 'rollup-plugin-dts'; /** * @type {import('rollup').RollupOptions} */ @@ -10,45 +10,59 @@ export default [ { input: 'src/index.ts', // TypeScript 入口文件 output: { - file: 'dist/index.js', // 输出文件 + file: 'dist/query-browser.js', // 输出文件 format: 'es', // 输出格式设置为 ES 模块 }, plugins: [ resolve(), // 使用 @rollup/plugin-node-resolve 解析 node_modules 中的模块 - typescript({ - allowImportingTsExtensions: true, - noEmit: true, - exclude: ['src/node-adapter.ts'], - }), // 使用 @rollup/plugin-typescript 处理 TypeScript 文件 + typescript(), // 使用 @rollup/plugin-typescript 处理 TypeScript 文件 ], }, - // { - // input: 'src/node-adapter.ts', // TypeScript 入口文件 - // output: { - // file: 'dist/node-adapter.js', // 输出文件 - // format: 'es', // 输出格式设置为 ES 模块 - // }, - // plugins: [ - // resolve(), // 使用 @rollup/plugin-node-resolve 解析 node_modules 中的模块 - // typescript({ - // allowImportingTsExtensions: true, - // noEmit: true, - // }), // 使用 @rollup/plugin-typescript 处理 TypeScript 文件 - // ], - // }, + { + input: 'src/index.ts', // TypeScript 入口文件 + output: { + file: 'dist/query-browser.d.ts', // 输出文件 + format: 'es', // 输出格式设置为 ES 模块 + }, + plugins: [dts()], + }, + { + input: 'src/query.ts', + output: { + file: 'dist/query.js', + format: 'es', + }, + moduleSideEffects: false, // 确保无副作用的模块能被完全 tree-shake + propertyReadSideEffects: false, + + plugins: [resolve(), typescript()], + }, + { + input: 'src/query.ts', + output: { + file: 'dist/query.d.ts', + format: 'es', + }, + + plugins: [dts()], + }, { input: 'src/ws.ts', // TypeScript 入口文件 output: { - file: 'dist/ws.js', // 输出文件 + file: 'dist/query-ws.js', // 输出文件 format: 'es', // 输出格式设置为 ES 模块 }, plugins: [ resolve(), // 使用 @rollup/plugin-node-resolve 解析 node_modules 中的模块 - typescript({ - allowImportingTsExtensions: true, - noEmit: true, - declaration: false, - }), // 使用 @rollup/plugin-typescript 处理 TypeScript 文件 + typescript(), // 使用 @rollup/plugin-typescript 处理 TypeScript 文件 ], }, + { + input: 'src/ws.ts', // TypeScript 入口文件 + output: { + file: 'dist/query-ws.d.ts', // 输出文件 + format: 'es', // 输出格式设置为 ES 模块 + }, + plugins: [dts()], + }, ]; diff --git a/src/index.ts b/src/index.ts index 8de0d7d..eb7e33d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,134 +1 @@ -import { adapter } from './adapter.ts'; -import { QueryWs, QueryWsOpts } from './ws.ts'; -export { QueryOpts, QueryWs }; -type Fn = (opts: { - url?: string; - headers?: Record; - body?: Record; - [key: string]: any; - timeout?: number; -}) => Promise>; - -type QueryOpts = { - url?: string; - adapter?: typeof adapter; - headers?: Record; - timeout?: number; -}; -type Data = { - path?: string; - key?: string; - payload?: Record; - [key: string]: any; -}; -type Result = { - code: number; - data?: S; - message?: string; - success: boolean; -}; -// 额外功能 -type DataOpts = Partial & { - beforeRequest?: Fn; - afterResponse?: (result: Result) => Promise; -}; -/** - * const query = new Query(); - * const res = await query.post({ - * path: 'demo', - * key: '1', - * }); - * - * U是参数 V是返回值 - */ -export class Query { - adapter: typeof adapter; - url: string; - beforeRequest?: Fn; - afterResponse?: (result: Result) => Promise; - headers?: Record; - timeout?: number; - constructor(opts?: QueryOpts) { - this.adapter = opts?.adapter || adapter; - this.url = opts?.url || '/api/router'; - this.headers = opts?.headers || { - 'Content-Type': 'application/json', - }; - this.timeout = opts?.timeout || 60000 * 3; // 默认超时时间为 60s * 3 - } - async get(params: Record & Data & U & T, options?: DataOpts): Promise> { - return this.post(params, options); - } - async post(body: Record & Data & T, options?: DataOpts): Promise> { - const url = options?.url || this.url; - const headers = { ...this.headers, ...options?.headers }; - const adapter = options?.adapter || this.adapter; - const beforeRequest = options?.beforeRequest || this.beforeRequest; - const afterResponse = options?.afterResponse || this.afterResponse; - const timeout = options?.timeout || this.timeout; - const req = { - url: url, - headers: headers, - body, - timeout, - }; - if (beforeRequest) { - await beforeRequest(req); - } - return adapter(req).then(async (res) => { - res.success = res.code === 200; - if (afterResponse) { - return await afterResponse(res); - } - return res; - }); - } - before(fn: Fn) { - this.beforeRequest = fn; - } - after(fn: (result: Result) => Promise) { - this.afterResponse = fn; - } -} -/** - * 前端调用后端QueryRouter - */ -export class QueryClient extends Query { - tokenName: string; - storage: Storage; - token: string; - qws: QueryWs; - constructor(opts?: QueryOpts & { tokenName?: string; storage?: Storage; io?: boolean }) { - super(opts); - this.tokenName = opts?.tokenName || 'token'; - this.storage = opts?.storage || localStorage; - this.beforeRequest = async (opts) => { - const token = this.token || this.getToken(); - if (token) { - opts.headers = { - ...opts.headers, - Authorization: `Bearer ${token}`, - }; - } - return opts; - }; - if (opts?.io) { - this.createWs(); - } - } - createWs(opts?: QueryWsOpts) { - this.qws = new QueryWs({ url: this.url, ...opts }); - } - getToken() { - return this.storage.getItem(this.tokenName); - } - saveToken(token: string) { - this.storage.setItem(this.tokenName, token); - } - removeToken() { - this.storage.removeItem(this.tokenName); - } -} -export const client = new QueryClient(); - -export { adapter }; +export * from './query-browser.ts' \ No newline at end of file diff --git a/src/node-adapter.ts b/src/node-adapter.ts deleted file mode 100644 index 9082fc7..0000000 --- a/src/node-adapter.ts +++ /dev/null @@ -1,57 +0,0 @@ -import http from 'http'; - -type AdapterOpts = { - url: string; - headers?: Record; - body?: Record; -}; -export const nodeAdapter = async (opts: AdapterOpts): Promise => { - return new Promise((resolve, reject) => { - const postData = JSON.stringify(opts.body || ''); - const _url = new URL(opts.url); - const { hostname, port, pathname } = _url; - const options = { - hostname: hostname, - port: port, - path: pathname || '/api/router', - method: 'POST', // Assuming it's a POST request - headers: { - 'Content-Type': 'application/json', - 'Content-Length': Buffer.byteLength(postData), - ...opts.headers, - }, - }; - - const req = http.request(options, (res) => { - let data = ''; - - // Collect data chunks - res.on('data', (chunk) => { - data += chunk; - }); - - // Resolve when the response is complete - res.on('end', () => { - try { - const parsedData = JSON.parse(data); - resolve(parsedData); - } catch (error) { - reject(error); - } - }); - }); - - // Handle request errors - req.on('error', (error) => { - reject(error); - }); - - // Write the request body and end the request - if (opts.body) { - req.write(postData); - } - req.end(); - }); -}; - -export const adapter = nodeAdapter; diff --git a/src/query-browser.ts b/src/query-browser.ts new file mode 100644 index 0000000..3eba7e5 --- /dev/null +++ b/src/query-browser.ts @@ -0,0 +1,54 @@ +import { adapter } from './adapter.ts'; +import { QueryWs, QueryWsOpts } from './ws.ts'; +export { QueryOpts, QueryWs }; +import { Query } from './query.ts'; + +type QueryOpts = { + url?: string; + adapter?: typeof adapter; + headers?: Record; + timeout?: number; +}; + +/** + * 前端调用后端QueryRouter + */ +export class QueryClient extends Query { + tokenName: string; + storage: Storage; + token: string; + qws: QueryWs; + constructor(opts?: QueryOpts & { tokenName?: string; storage?: Storage; io?: boolean }) { + super(opts); + this.tokenName = opts?.tokenName || 'token'; + this.storage = opts?.storage || localStorage; + this.beforeRequest = async (opts) => { + const token = this.token || this.getToken(); + if (token) { + opts.headers = { + ...opts.headers, + Authorization: `Bearer ${token}`, + }; + } + return opts; + }; + if (opts?.io) { + this.createWs(); + } + } + createWs(opts?: QueryWsOpts) { + this.qws = new QueryWs({ url: this.url, ...opts }); + } + getToken() { + return this.storage.getItem(this.tokenName); + } + saveToken(token: string) { + this.storage.setItem(this.tokenName, token); + } + removeToken() { + this.storage.removeItem(this.tokenName); + } +} +export const client = new QueryClient(); + +export { adapter }; diff --git a/src/query.ts b/src/query.ts new file mode 100644 index 0000000..9c739d9 --- /dev/null +++ b/src/query.ts @@ -0,0 +1,92 @@ +import { adapter } from './adapter.ts'; +type Fn = (opts: { + url?: string; + headers?: Record; + body?: Record; + [key: string]: any; + timeout?: number; +}) => Promise>; + +export type QueryOpts = { + url?: string; + adapter?: typeof adapter; + headers?: Record; + timeout?: number; +}; +type Data = { + path?: string; + key?: string; + payload?: Record; + [key: string]: any; +}; +type Result = { + code: number; + data?: S; + message?: string; + success: boolean; +}; +// 额外功能 +type DataOpts = Partial & { + beforeRequest?: Fn; + afterResponse?: (result: Result) => Promise; +}; +/** + * const query = new Query(); + * const res = await query.post({ + * path: 'demo', + * key: '1', + * }); + * + * U是参数 V是返回值 + */ +export class Query { + adapter: typeof adapter; + url: string; + beforeRequest?: Fn; + afterResponse?: (result: Result) => Promise; + headers?: Record; + timeout?: number; + constructor(opts?: QueryOpts) { + this.adapter = opts?.adapter || adapter; + this.url = opts?.url || '/api/router'; + this.headers = opts?.headers || { + 'Content-Type': 'application/json', + }; + this.timeout = opts?.timeout || 60000 * 3; // 默认超时时间为 60s * 3 + } + async get(params: Record & Data & U & T, options?: DataOpts): Promise> { + return this.post(params, options); + } + async post(body: Record & Data & T, options?: DataOpts): Promise> { + const url = options?.url || this.url; + const headers = { ...this.headers, ...options?.headers }; + const adapter = options?.adapter || this.adapter; + const beforeRequest = options?.beforeRequest || this.beforeRequest; + const afterResponse = options?.afterResponse || this.afterResponse; + const timeout = options?.timeout || this.timeout; + const req = { + url: url, + headers: headers, + body, + timeout, + }; + if (beforeRequest) { + await beforeRequest(req); + } + return adapter(req).then(async (res) => { + res.success = res.code === 200; + if (afterResponse) { + return await afterResponse(res); + } + return res; + }); + } + before(fn: Fn) { + this.beforeRequest = fn; + } + after(fn: (result: Result) => Promise) { + this.afterResponse = fn; + } +} + +export { adapter }; diff --git a/tsconfig.json b/tsconfig.json index 7852621..359421b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,10 +8,10 @@ "allowJs": true, "newLine": "LF", "baseUrl": "./", + "declaration": false, "typeRoots": [ "node_modules/@types", ], - "declaration": true, "noEmit": true, "allowImportingTsExtensions": true, "moduleResolution": "NodeNext",