This commit is contained in:
2025-12-05 10:24:07 +08:00
parent b53968df8b
commit f3dbbe1c0f
6 changed files with 49 additions and 132 deletions

View File

@@ -1,9 +1,7 @@
{ {
"name": "@kevisual/query", "name": "@kevisual/query",
"version": "0.0.29", "version": "0.0.30",
"main": "dist/index.js", "main": "dist/query-browser.js",
"module": "dist/index.js",
"types": "dist/index.d.ts",
"private": false, "private": false,
"type": "module", "type": "module",
"scripts": { "scripts": {
@@ -23,16 +21,13 @@
"license": "ISC", "license": "ISC",
"description": "", "description": "",
"devDependencies": { "devDependencies": {
"@rollup/plugin-node-resolve": "^16.0.1", "@rollup/plugin-node-resolve": "^16.0.3",
"@rollup/plugin-typescript": "^12.1.2", "@rollup/plugin-typescript": "^12.3.0",
"rollup": "^4.41.1", "rollup": "^4.53.3",
"rollup-plugin-dts": "^6.2.1", "rollup-plugin-dts": "^6.3.0",
"ts-node": "^10.9.2", "typescript": "^5.9.3",
"tslib": "^2.8.1", "zustand": "^5.0.9"
"typescript": "^5.8.3",
"zustand": "^5.0.5"
}, },
"packageManager": "yarn@1.22.22",
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
}, },
@@ -57,8 +52,5 @@
"import": "./dist/query-ai.js", "import": "./dist/query-ai.js",
"require": "./dist/query-ai.js" "require": "./dist/query-ai.js"
} }
},
"dependencies": {
"openai": "^5.0.1"
} }
} }

View File

@@ -81,20 +81,4 @@ export default [
}, },
plugins: [dts()], plugins: [dts()],
}, },
{
input: 'src/query-ai.ts',
output: {
file: 'dist/query-ai.js',
format: 'es',
},
plugins: [resolve(), typescript()],
},
{
input: 'src/query-ai.ts', // TypeScript 入口文件
output: {
file: 'dist/query-ai.d.ts', // 输出文件
format: 'es', // 输出格式设置为 ES 模块
},
plugins: [dts()],
},
]; ];

View File

@@ -5,16 +5,31 @@ type SimpleObject = Record<string, any>;
export type AdapterOpts = { export type AdapterOpts = {
url?: string; url?: string;
headers?: Record<string, string>; headers?: Record<string, string>;
/**
* 只用户POST请求传递的查询参数
* GET请求默认方body自己转化为查询参数
*/
params?: Record<string, any>;
body?: Record<string, any> | FormData; // body 可以是对象、字符串或 FormData body?: Record<string, any> | FormData; // body 可以是对象、字符串或 FormData
timeout?: number; timeout?: number;
method?: Method; method?: Method;
/**
* @deprecated use responseType
*/
isBlob?: boolean; // 是否返回 Blob 对象, 第一优先 isBlob?: boolean; // 是否返回 Blob 对象, 第一优先
/**
* @deprecated use responseType
*/
isText?: boolean; // 是否返回文本内容, 第二优先 isText?: boolean; // 是否返回文本内容, 第二优先
/**
* 响应类型,
* */
responseType?: 'json' | 'text' | 'blob';
isPostFile?: boolean; // 是否为文件上传 isPostFile?: boolean; // 是否为文件上传
}; };
export const isTextForContentType = (contentType: string | null) => { export const isTextForContentType = (contentType: string | null) => {
if (!contentType) return false; if (!contentType) return false;
const textTypes = ['text/', 'xml', 'html', 'javascript', 'css', 'csv', 'plain', 'x-www-form-urlencoded']; const textTypes = ['text/', 'xml', 'html', 'javascript', 'css', 'csv', 'plain', 'x-www-form-urlencoded', 'md'];
return textTypes.some((type) => contentType.includes(type)); return textTypes.some((type) => contentType.includes(type));
}; };
/** /**
@@ -26,9 +41,14 @@ export const isTextForContentType = (contentType: string | null) => {
export const adapter = async (opts: AdapterOpts = {}, overloadOpts?: RequestInit) => { export const adapter = async (opts: AdapterOpts = {}, overloadOpts?: RequestInit) => {
const controller = new AbortController(); const controller = new AbortController();
const signal = controller.signal; const signal = controller.signal;
const isBlob = opts.isBlob || false; // 是否返回 Blob 对象
const isText = opts.isText || false; // 是否返回文本内容
const isPostFile = opts.isPostFile || false; // 是否为文件上传 const isPostFile = opts.isPostFile || false; // 是否为文件上传
let responseType = opts.responseType || 'json'; // 响应类型
if (opts.isBlob) {
responseType = 'blob';
} else if (opts.isText) {
responseType = 'text';
}
const timeout = opts.timeout || 60000 * 3; // 默认超时时间为 60s * 3 const timeout = opts.timeout || 60000 * 3; // 默认超时时间为 60s * 3
const timer = setTimeout(() => { const timer = setTimeout(() => {
controller.abort(); controller.abort();
@@ -46,6 +66,9 @@ export const adapter = async (opts: AdapterOpts = {}, overloadOpts?: RequestInit
const isGet = method === 'GET'; const isGet = method === 'GET';
if (isGet) { if (isGet) {
url.search = new URLSearchParams(opts.body as SimpleObject).toString(); url.search = new URLSearchParams(opts.body as SimpleObject).toString();
} else {
const params = opts.params || {};
url.search = new URLSearchParams(params as SimpleObject).toString();
} }
let body: string | FormData | undefined = undefined; let body: string | FormData | undefined = undefined;
if (isGet) { if (isGet) {
@@ -69,9 +92,10 @@ export const adapter = async (opts: AdapterOpts = {}, overloadOpts?: RequestInit
.then(async (response) => { .then(async (response) => {
// 获取 Content-Type 头部信息 // 获取 Content-Type 头部信息
const contentType = response.headers.get('Content-Type'); const contentType = response.headers.get('Content-Type');
if (isBlob) { if (responseType === 'blob') {
return await response.blob(); // 直接返回 Blob 对象 return await response.blob(); // 直接返回 Blob 对象
} }
const isText = responseType === 'text';
const isJson = contentType && contentType.includes('application/json'); const isJson = contentType && contentType.includes('application/json');
// 判断返回的数据类型 // 判断返回的数据类型
if (isJson && !isText) { if (isJson && !isText) {

View File

@@ -1,58 +0,0 @@
import OpenAI, { ClientOptions } from 'openai';
import type { RequestOptions } from 'openai/core.mjs';
type QueryOpts = {
/**
* OpenAI model name, example: deepseek-chat
*/
model: string;
/**
* OpenAI client options
* QueryAi.init() will be called with these options
*/
openAiOpts?: ClientOptions;
openai?: OpenAI;
};
export class QueryAI {
private openai: OpenAI;
model?: string;
constructor(opts?: QueryOpts) {
this.model = opts?.model;
if (opts?.openai) {
this.openai = opts.openai;
} else if (opts?.openAiOpts) {
this.init(opts?.openAiOpts);
}
}
init(opts: ClientOptions) {
this.openai = new OpenAI(opts);
}
async query(prompt: string, opts?: RequestOptions) {
return this.openai.chat.completions.create({
model: this.model,
messages: [
{
role: 'system',
content: prompt,
},
],
stream: false,
...opts,
});
}
async queryAsync(prompt: string, opts?: RequestOptions) {
return this.openai.chat.completions.create({
model: this.model,
messages: [
{
role: 'system',
content: prompt,
},
],
stream: true,
...opts,
});
}
}
export { OpenAI };

View File

@@ -1,9 +1,9 @@
import { adapter } from './adapter.ts'; import { adapter } from './adapter.ts';
import { QueryWs, QueryWsOpts } from './ws.ts'; import { QueryWs, QueryWsOpts } from './ws.ts';
import { Query, ClientQuery } from './query.ts'; import { Query } from './query.ts';
import { BaseQuery, wrapperError } from './query.ts'; import { wrapperError } from './query.ts';
export { QueryOpts, QueryWs, ClientQuery, Query, QueryWsOpts, adapter, BaseQuery, wrapperError }; export { QueryOpts, QueryWs, Query, QueryWsOpts, adapter, wrapperError };
export type { DataOpts, Result, Data } from './query.ts'; export type { DataOpts, Result, Data } from './query.ts';

View File

@@ -1,4 +1,4 @@
import { adapter, isTextForContentType, Method, AdapterOpts } from './adapter.ts'; import { adapter, Method, AdapterOpts } from './adapter.ts';
import type { QueryWs } from './ws.ts'; import type { QueryWs } from './ws.ts';
/** /**
* 请求前处理函数 * 请求前处理函数
@@ -92,7 +92,13 @@ export const wrapperError = ({ code, message }: { code?: number; message?: strin
export class Query { export class Query {
adapter: typeof adapter; adapter: typeof adapter;
url: string; url: string;
/**
* 请求前处理函数
*/
beforeRequest?: DataOpts['beforeRequest']; beforeRequest?: DataOpts['beforeRequest'];
/**
* 请求后处理函数
*/
afterResponse?: DataOpts['afterResponse']; afterResponse?: DataOpts['afterResponse'];
headers?: Record<string, string>; headers?: Record<string, string>;
timeout?: number; timeout?: number;
@@ -215,14 +221,14 @@ export class Query {
}); });
} }
/** /**
* 请求前处理,设置请求前处理函数 * 设置请求前处理,设置请求前处理函数
* @param fn 处理函数 * @param fn 处理函数
*/ */
before(fn: DataOpts['beforeRequest']) { before(fn: DataOpts['beforeRequest']) {
this.beforeRequest = fn; this.beforeRequest = fn;
} }
/** /**
* 请求后处理,设置请求后处理函数 * 设置请求后处理,设置请求后处理函数
* @param fn 处理函数 * @param fn 处理函数
*/ */
after(fn: DataOpts['afterResponse']) { after(fn: DataOpts['afterResponse']) {
@@ -249,34 +255,3 @@ export class Query {
} }
export { adapter }; export { adapter };
export class BaseQuery<T extends Query = Query, R extends { queryChain?: any; query?: any } = { queryChain: any; query?: T }> {
query: T;
queryDefine: R;
constructor(opts?: { query?: T; queryDefine?: R; clientQuery?: T }) {
if (opts?.clientQuery) {
this.query = opts.clientQuery;
} else {
this.query = opts?.query;
}
if (opts.queryDefine) {
this.queryDefine = opts.queryDefine;
this.queryDefine.query = this.query;
}
}
get chain(): R['queryChain'] {
return this.queryDefine.queryChain;
}
post<R = any, P = any>(data: P, options?: DataOpts): Promise<Result<R>> {
return this.query.post(data, options);
}
get<R = any, P = any>(data: P, options?: DataOpts): Promise<Result<R>> {
return this.query.get(data, options);
}
}
export class ClientQuery extends Query {
constructor(opts?: QueryOpts) {
super({ ...opts, url: opts?.url || '/client/router' });
}
}