@kevisual/query 0.0.40 → 0.0.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,131 +1,114 @@
1
- const methods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS', 'HEAD'];
2
- const isTextForContentType = (contentType) => {
3
- if (!contentType)
4
- return false;
5
- const textTypes = ['text/', 'xml', 'html', 'javascript', 'css', 'csv', 'plain', 'x-www-form-urlencoded', 'md'];
6
- return textTypes.some((type) => contentType.includes(type));
1
+ // src/adapter.ts
2
+ var methods = ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"];
3
+ var isTextForContentType = (contentType) => {
4
+ if (!contentType)
5
+ return false;
6
+ const textTypes = ["text/", "xml", "html", "javascript", "css", "csv", "plain", "x-www-form-urlencoded", "md"];
7
+ return textTypes.some((type) => contentType.includes(type));
7
8
  };
8
- /**
9
- *
10
- * @param opts
11
- * @param overloadOpts 覆盖fetch的默认配置
12
- * @returns
13
- */
14
- const adapter = async (opts = {}, overloadOpts) => {
15
- const controller = new AbortController();
16
- const signal = controller.signal;
17
- const isPostFile = opts.isPostFile || false; // 是否为文件上传
18
- let responseType = opts.responseType || 'json'; // 响应类型
19
- if (opts.isBlob) {
20
- responseType = 'blob';
21
- }
22
- else if (opts.isText) {
23
- responseType = 'text';
24
- }
25
- const timeout = opts.timeout || 60000 * 3; // 默认超时时间为 60s * 3
26
- const timer = setTimeout(() => {
27
- controller.abort();
28
- }, timeout);
29
- let method = overloadOpts?.method || opts?.method || 'POST';
30
- let headers = { ...opts?.headers, ...overloadOpts?.headers };
31
- let origin = '';
32
- let url;
33
- if (opts?.url?.startsWith('http')) {
34
- url = new URL(opts.url);
35
- }
36
- else {
37
- origin = window?.location?.origin || 'http://localhost:51515';
38
- url = new URL(opts?.url || '', origin);
39
- }
40
- const isGet = method === 'GET';
41
- const oldSearchParams = url.searchParams;
42
- if (isGet) {
43
- let searchParams = new URLSearchParams({ ...Object.fromEntries(oldSearchParams), ...opts?.params, ...opts?.body });
44
- url.search = searchParams.toString();
45
- }
46
- else {
47
- const params = {
48
- ...Object.fromEntries(oldSearchParams),
49
- ...opts.params,
50
- };
51
- const searchParams = new URLSearchParams(params);
52
- if (typeof opts.body === 'object' && opts.body !== null) {
53
- // 浏览器环境下,自动将 body 中的 path 和 key 提取到查询参数中, 更容易排查问题
54
- let body = opts.body || {};
55
- if (!params.path && body?.path) {
56
- searchParams.set('path', body.path);
57
- if (body?.key) {
58
- searchParams.set('key', body.key);
59
- }
60
- }
9
+ var adapter = async (opts = {}, overloadOpts) => {
10
+ const controller = new AbortController;
11
+ const signal = controller.signal;
12
+ const isPostFile = opts.isPostFile || false;
13
+ let responseType = opts.responseType || "json";
14
+ if (opts.isBlob) {
15
+ responseType = "blob";
16
+ } else if (opts.isText) {
17
+ responseType = "text";
18
+ }
19
+ const timeout = opts.timeout || 60000 * 3;
20
+ const timer = setTimeout(() => {
21
+ controller.abort();
22
+ }, timeout);
23
+ let method = overloadOpts?.method || opts?.method || "POST";
24
+ let headers = { ...opts?.headers, ...overloadOpts?.headers };
25
+ let origin = "";
26
+ let url;
27
+ if (opts?.url?.startsWith("http")) {
28
+ url = new URL(opts.url);
29
+ } else {
30
+ origin = globalThis?.location?.origin || "http://localhost:51515";
31
+ url = new URL(opts?.url || "", origin);
32
+ }
33
+ const isGet = method === "GET";
34
+ const oldSearchParams = url.searchParams;
35
+ if (isGet) {
36
+ let searchParams = new URLSearchParams({ ...Object.fromEntries(oldSearchParams), ...opts?.params, ...opts?.body });
37
+ url.search = searchParams.toString();
38
+ } else {
39
+ const params = {
40
+ ...Object.fromEntries(oldSearchParams),
41
+ ...opts.params
42
+ };
43
+ const searchParams = new URLSearchParams(params);
44
+ if (typeof opts.body === "object" && opts.body !== null) {
45
+ let body2 = opts.body || {};
46
+ if (!params.path && body2?.path) {
47
+ searchParams.set("path", body2.path);
48
+ if (body2?.key) {
49
+ searchParams.set("key", body2.key);
61
50
  }
62
- url.search = searchParams.toString();
51
+ }
63
52
  }
64
- let body = undefined;
65
- if (isGet) {
66
- body = undefined;
53
+ url.search = searchParams.toString();
54
+ }
55
+ let body = undefined;
56
+ if (isGet) {
57
+ body = undefined;
58
+ } else if (isPostFile) {
59
+ body = opts.body;
60
+ } else {
61
+ if (opts.body && typeof opts.body === "object" && !(opts.body instanceof FormData)) {
62
+ headers = {
63
+ "Content-Type": "application/json",
64
+ ...headers
65
+ };
66
+ body = JSON.stringify(opts.body);
67
67
  }
68
- else if (isPostFile) {
69
- body = opts.body; // 如果是文件上传,直接使用 FormData
68
+ }
69
+ return fetch(url, {
70
+ method: method.toUpperCase(),
71
+ signal,
72
+ body,
73
+ ...overloadOpts,
74
+ headers
75
+ }).then(async (response) => {
76
+ const contentType = response.headers.get("Content-Type");
77
+ if (responseType === "blob") {
78
+ return await response.blob();
70
79
  }
71
- else {
72
- if (opts.body && typeof opts.body === 'object' && !(opts.body instanceof FormData)) {
73
- headers = {
74
- 'Content-Type': 'application/json',
75
- ...headers,
76
- };
77
- body = JSON.stringify(opts.body); // 否则将对象转换为 JSON 字符串
78
- }
80
+ const isText = responseType === "text";
81
+ const isJson = contentType && contentType.includes("application/json");
82
+ if (isJson && !isText) {
83
+ return await response.json();
84
+ } else if (isTextForContentType(contentType)) {
85
+ return {
86
+ code: response.status,
87
+ status: response.status,
88
+ data: await response.text()
89
+ };
90
+ } else {
91
+ return response;
79
92
  }
80
- return fetch(url, {
81
- method: method.toUpperCase(),
82
- signal,
83
- body: body,
84
- ...overloadOpts,
85
- headers: headers,
86
- })
87
- .then(async (response) => {
88
- // 获取 Content-Type 头部信息
89
- const contentType = response.headers.get('Content-Type');
90
- if (responseType === 'blob') {
91
- return await response.blob(); // 直接返回 Blob 对象
92
- }
93
- const isText = responseType === 'text';
94
- const isJson = contentType && contentType.includes('application/json');
95
- // 判断返回的数据类型
96
- if (isJson && !isText) {
97
- return await response.json(); // 解析为 JSON
98
- }
99
- else if (isTextForContentType(contentType)) {
100
- return {
101
- code: response.status,
102
- status: response.status,
103
- data: await response.text(), // 直接返回文本内容
104
- };
105
- }
106
- else {
107
- return response;
108
- }
109
- })
110
- .catch((err) => {
111
- if (err.name === 'AbortError') {
112
- return {
113
- code: 408,
114
- message: '请求超时',
115
- };
116
- }
117
- return {
118
- code: 500,
119
- message: err.message || '网络错误',
120
- };
121
- })
122
- .finally(() => {
123
- clearTimeout(timer);
124
- });
93
+ }).catch((err) => {
94
+ if (err.name === "AbortError") {
95
+ return {
96
+ code: 408,
97
+ message: "请求超时"
98
+ };
99
+ }
100
+ return {
101
+ code: 500,
102
+ message: err.message || "网络错误"
103
+ };
104
+ }).finally(() => {
105
+ clearTimeout(timer);
106
+ });
107
+ };
108
+ var queryFetch = adapter;
109
+ export {
110
+ queryFetch,
111
+ methods,
112
+ isTextForContentType,
113
+ adapter
125
114
  };
126
- /**
127
- * adapter
128
- */
129
- const queryFetch = adapter;
130
-
131
- export { adapter, isTextForContentType, methods, queryFetch };
@@ -0,0 +1,305 @@
1
+ import { StoreApi } from 'zustand/vanilla';
2
+ import { z } from 'zod';
3
+
4
+ declare const methods: readonly ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"];
5
+ type Method = (typeof methods)[number];
6
+ type AdapterOpts = {
7
+ url?: string;
8
+ headers?: Record<string, string>;
9
+ /**
10
+ * 只用户POST请求,传递的查询参数,
11
+ * GET请求默认方body自己转化为查询参数
12
+ */
13
+ params?: Record<string, any>;
14
+ body?: Record<string, any> | FormData;
15
+ timeout?: number;
16
+ method?: Method;
17
+ /**
18
+ * @deprecated use responseType
19
+ */
20
+ isBlob?: boolean;
21
+ /**
22
+ * @deprecated use responseType
23
+ */
24
+ isText?: boolean;
25
+ /**
26
+ * 响应类型,
27
+ * */
28
+ responseType?: 'json' | 'text' | 'blob';
29
+ isPostFile?: boolean;
30
+ };
31
+ /**
32
+ *
33
+ * @param opts
34
+ * @param overloadOpts 覆盖fetch的默认配置
35
+ * @returns
36
+ */
37
+ declare const adapter: (opts?: AdapterOpts, overloadOpts?: RequestInit) => Promise<any>;
38
+
39
+ type QueryWsStore = {
40
+ connected: boolean;
41
+ status: 'connecting' | 'connected' | 'disconnected';
42
+ setConnected: (connected: boolean) => void;
43
+ setStatus: (status: QuerySelectState) => void;
44
+ };
45
+ type QuerySelectState = 'connecting' | 'connected' | 'disconnected';
46
+ type QueryWsOpts = {
47
+ url?: string;
48
+ store?: StoreApi<QueryWsStore>;
49
+ ws?: WebSocket;
50
+ };
51
+ declare class QueryWs {
52
+ url: string;
53
+ store: StoreApi<QueryWsStore>;
54
+ ws: WebSocket;
55
+ constructor(opts?: QueryWsOpts);
56
+ /**
57
+ * 连接 WebSocket
58
+ */
59
+ connect(opts?: {
60
+ timeout?: number;
61
+ }): Promise<unknown>;
62
+ /**
63
+ * ws.onopen 必须用这个去获取,否者会丢失链接信息
64
+ * @param callback
65
+ * @returns
66
+ */
67
+ listenConnect(callback: () => void): () => void;
68
+ listenClose(callback: () => void): () => void;
69
+ onMessage<T = any, U = any>(fn: (data: U, event: MessageEvent) => void, opts?: {
70
+ /**
71
+ * 是否将数据转换为 JSON
72
+ */
73
+ isJson?: boolean;
74
+ /**
75
+ * 选择器
76
+ */
77
+ selector?: (data: T) => U;
78
+ }): () => void;
79
+ close(): void;
80
+ /**
81
+ * 发送消息
82
+ *
83
+ * @param data
84
+ * @param opts
85
+ * @returns
86
+ */
87
+ send<T = any, U = any>(data: T, opts?: {
88
+ /**
89
+ * 是否将数据转换为 JSON
90
+ */
91
+ isJson?: boolean;
92
+ /**
93
+ * 包装数据
94
+ */
95
+ wrapper?: (data: T) => U;
96
+ }): void;
97
+ getOpen(): boolean;
98
+ }
99
+
100
+ /**
101
+ * 请求前处理函数
102
+ * @param opts 请求配置
103
+ * @returns 请求配置
104
+ */
105
+ type Fn = (opts: {
106
+ url?: string;
107
+ headers?: Record<string, string>;
108
+ body?: Record<string, any>;
109
+ [key: string]: any;
110
+ timeout?: number;
111
+ }) => Promise<Record<string, any> | false>;
112
+ type QueryOpts = {
113
+ adapter?: typeof adapter;
114
+ [key: string]: any;
115
+ } & AdapterOpts;
116
+ type QueryOptions = {
117
+ url?: string;
118
+ adapter?: typeof adapter;
119
+ headers?: Record<string, string>;
120
+ timeout?: number;
121
+ isClient?: boolean;
122
+ beforeRequest?: Fn;
123
+ };
124
+ type Data = {
125
+ path?: string;
126
+ key?: string;
127
+ payload?: Record<string, any>;
128
+ [key: string]: any;
129
+ };
130
+ type Result<S = any, U = {}> = {
131
+ code: number;
132
+ data?: S;
133
+ message?: string;
134
+ } & U;
135
+ type DataOpts = Partial<QueryOpts> & {
136
+ beforeRequest?: Fn;
137
+ afterResponse?: <S = any>(result: Result<S>, ctx?: {
138
+ req?: any;
139
+ res?: any;
140
+ fetch?: any;
141
+ }) => Promise<Result<S>>;
142
+ /**
143
+ * 是否在stop的时候不请求
144
+ */
145
+ noStop?: boolean;
146
+ };
147
+ /**
148
+ * const query = new Query();
149
+ * const res = await query.post({
150
+ * path: 'demo',
151
+ * key: '1',
152
+ * });
153
+ *
154
+ * U是参数 V是返回值
155
+ */
156
+ declare class Query {
157
+ adapter: typeof adapter;
158
+ url: string;
159
+ /**
160
+ * 请求前处理函数
161
+ */
162
+ beforeRequest?: DataOpts['beforeRequest'];
163
+ /**
164
+ * 请求后处理函数
165
+ */
166
+ afterResponse?: DataOpts['afterResponse'];
167
+ headers?: Record<string, string>;
168
+ timeout?: number;
169
+ /**
170
+ * 需要突然停止请求,比如401的时候
171
+ */
172
+ stop?: boolean;
173
+ qws: QueryWs;
174
+ /**
175
+ * 默认是 /client/router或者 默认是 /api/router
176
+ */
177
+ isClient: boolean;
178
+ constructor(opts?: QueryOptions);
179
+ setQueryWs(qws: QueryWs): void;
180
+ /**
181
+ * 突然停止请求
182
+ */
183
+ setStop(stop: boolean): void;
184
+ /**
185
+ * 发送 get 请求,转到 post 请求
186
+ * T是请求类型自定义
187
+ * S是返回类型自定义
188
+ * @param params 请求参数
189
+ * @param options 请求配置
190
+ * @returns 请求结果
191
+ */
192
+ get<R = any, P = any>(params: Data & P, options?: DataOpts): Promise<Result<R>>;
193
+ /**
194
+ * 发送 post 请求
195
+ * T是请求类型自定义
196
+ * S是返回类型自定义
197
+ * @param body 请求体
198
+ * @param options 请求配置
199
+ * @returns 请求结果
200
+ */
201
+ post<R = any, P = any>(body: Data & P, options?: DataOpts): Promise<Result<R>>;
202
+ /**
203
+ * 设置请求前处理,设置请求前处理函数
204
+ * @param fn 处理函数
205
+ */
206
+ before(fn: DataOpts['beforeRequest']): void;
207
+ /**
208
+ * 设置请求后处理,设置请求后处理函数
209
+ * @param fn 处理函数
210
+ */
211
+ after(fn: DataOpts['afterResponse']): void;
212
+ fetchText(urlOrOptions?: string | QueryOpts, options?: QueryOpts): Promise<Result<any>>;
213
+ }
214
+
215
+ type RouteInfo = {
216
+ path: string;
217
+ key: string;
218
+ id: string;
219
+ description?: string;
220
+ metadata?: {
221
+ summary?: string;
222
+ args?: Record<string, any>;
223
+ };
224
+ };
225
+ declare const createQueryByRoutes: (list: RouteInfo[]) => string;
226
+
227
+ type Pos = {
228
+ path?: string;
229
+ key?: string;
230
+ id?: string;
231
+ metadata?: {
232
+ args?: Record<string, any>;
233
+ };
234
+ };
235
+ type InferFromJSONSchema<T> = T extends {
236
+ type: "string";
237
+ enum: readonly (infer E)[];
238
+ } ? E : T extends {
239
+ type: "string";
240
+ enum: (infer E)[];
241
+ } ? E : T extends {
242
+ type: "string";
243
+ } ? string : T extends {
244
+ type: "number";
245
+ } ? number : T extends {
246
+ type: "integer";
247
+ } ? number : T extends {
248
+ type: "boolean";
249
+ } ? boolean : T extends {
250
+ type: "object";
251
+ properties: infer P;
252
+ } ? {
253
+ [K in keyof P]: InferFromJSONSchema<P[K]>;
254
+ } : T extends {
255
+ type: "array";
256
+ items: infer I;
257
+ } ? Array<InferFromJSONSchema<I>> : unknown;
258
+ type InferType<T> = T extends z.ZodType<infer U> ? U : T extends {
259
+ type: infer TType;
260
+ } ? InferFromJSONSchema<T> : T;
261
+ type ExtractArgsFromMetadata<T> = T extends {
262
+ metadata?: {
263
+ args?: infer A;
264
+ };
265
+ } ? A extends Record<string, any> ? {
266
+ [K in keyof A]: InferType<A[K]>;
267
+ } : never : never;
268
+ type ApiMethods<P extends {
269
+ [path: string]: {
270
+ [key: string]: Pos;
271
+ };
272
+ }> = {
273
+ [Path in keyof P]: {
274
+ [Key in keyof P[Path]]: (data?: Partial<ExtractArgsFromMetadata<P[Path][Key]>>, opts?: DataOpts) => ReturnType<Query['post']>;
275
+ };
276
+ };
277
+ type QueryApiOpts<P extends {
278
+ [path: string]: {
279
+ [key: string]: Pos;
280
+ };
281
+ } = {}> = {
282
+ query?: Query;
283
+ api?: P;
284
+ };
285
+ declare class QueryApi<P extends {
286
+ [path: string]: {
287
+ [key: string]: Pos;
288
+ };
289
+ } = {}> {
290
+ query: Query;
291
+ constructor(opts?: QueryApiOpts<P>);
292
+ post<T extends Pos>(pos: T, data?: Partial<ExtractArgsFromMetadata<T>>, opts?: DataOpts): Promise<{
293
+ code: number;
294
+ data?: any;
295
+ message?: string;
296
+ }>;
297
+ createApi(api: P): asserts this is this & ApiMethods<P>;
298
+ }
299
+ declare function createQueryApi<P extends {
300
+ [path: string]: {
301
+ [key: string]: Pos;
302
+ };
303
+ }>(opts?: QueryApiOpts<P>): QueryApi<P> & ApiMethods<P>;
304
+
305
+ export { QueryApi, createQueryApi, createQueryByRoutes };