@ahoo-wang/fetcher-react 3.9.2 → 3.9.5

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.
package/README.zh-CN.md CHANGED
@@ -19,6 +19,7 @@
19
19
  - ⚡ **性能优化**: 使用 useMemo、useCallback 和智能依赖管理进行优化
20
20
  - 🎯 **选项灵活性**: 支持静态选项和动态选项供应商
21
21
  - 🔧 **开发者体验**: 内置加载状态、错误处理和自动重新渲染
22
+ - 🏗️ **API Hooks 生成**: 从 API 对象自动生成类型安全的 React hooks
22
23
  - 📊 **高级查询 Hooks**: 专门用于列表、分页、单个、计数和流查询的 hooks,具有状态管理功能
23
24
 
24
25
  ## 目录
@@ -26,6 +27,7 @@
26
27
  - [安装](#安装)
27
28
  - [快速开始](#快速开始)
28
29
  - [使用方法](#使用方法)
30
+ - [API Hooks](#api-hooks)
29
31
  - [核心 Hooks](#核心-hooks)
30
32
  - [useExecutePromise](#useexecutepromise)
31
33
  - [usePromiseState](#usepromisestate)
@@ -109,6 +111,369 @@ function App() {
109
111
 
110
112
  ## 使用方法
111
113
 
114
+ ### API Hooks
115
+
116
+ #### createExecuteApiHooks
117
+
118
+ 🚀 **自动类型安全 API Hooks 生成** - 从 API 对象自动生成完全类型化的 React hooks,具有自动方法发现、类方法支持和高级执行控制。
119
+
120
+ `createExecuteApiHooks` 函数自动发现 API 对象中的所有函数方法(包括类实例的原型链),并使用命名模式 `use{首字母大写的方法名}` 创建相应的 React hooks。每个生成的 hook 都提供完整的状态管理、错误处理,并支持具有类型安全参数访问的自定义执行回调。
121
+
122
+ **主要特性:**
123
+
124
+ - **自动方法发现**:遍历对象属性和原型链
125
+ - **类型安全 Hook 生成**:参数和返回类型的完整 TypeScript 推断
126
+ - **类方法支持**:处理静态方法和具有 `this` 绑定的类实例
127
+ - **执行控制**:`onBeforeExecute` 回调用于参数检查/修改和中止控制器访问
128
+ - **自定义错误类型**:支持指定超出默认 `FetcherError` 的错误类型
129
+
130
+ ```typescript jsx
131
+ import { createExecuteApiHooks } from '@ahoo-wang/fetcher-react';
132
+ import { api, get, post, patch, path, body, autoGeneratedError } from '@ahoo-wang/fetcher-decorator';
133
+
134
+ // 使用装饰器定义您的 API 服务
135
+ import { api, get, post, patch, path, body, autoGeneratedError } from '@ahoo-wang/fetcher-decorator';
136
+
137
+ @api('/users')
138
+ class UserApi {
139
+ @get('/{id}')
140
+ getUser(@path('id') id: string): Promise<User> {
141
+ throw autoGeneratedError(id);
142
+ }
143
+
144
+ @post('')
145
+ createUser(@body() data: { name: string; email: string }): Promise<User> {
146
+ throw autoGeneratedError(data);
147
+ }
148
+
149
+ @patch('/{id}')
150
+ updateUser(@path('id') id: string, @body() updates: Partial<User>): Promise<User> {
151
+ throw autoGeneratedError(id, updates);
152
+ }
153
+ }
154
+
155
+ const userApi = new UserApi();
156
+
157
+ // 生成类型安全的 hooks
158
+ const apiHooks = createExecuteApiHooks({ api: userApi });
159
+
160
+ function UserComponent() {
161
+ // Hooks 自动生成,具有正确的类型
162
+ const { loading: getLoading, result: user, error: getError, execute: getUser } = apiHooks.useGetUser();
163
+ const { loading: createLoading, result: createdUser, error: createError, execute: createUser } = apiHooks.useCreateUser({
164
+ onBeforeExecute: (abortController, args) => {
165
+ // args 完全类型化为 [data: { name: string; email: string }]
166
+ const [data] = args;
167
+ // 如果需要,可以就地修改参数
168
+ data.email = data.email.toLowerCase();
169
+ // 访问中止控制器以进行自定义取消
170
+ abortController.signal.addEventListener('abort', () => {
171
+ console.log('用户创建已取消');
172
+ });
173
+ },
174
+ });
175
+
176
+ const handleFetchUser = (userId: string) => {
177
+ getUser(userId); // 完全类型化 - 仅接受字符串参数
178
+ };
179
+
180
+ const handleCreateUser = (userData: { name: string; email: string }) => {
181
+ createUser(userData); // 完全类型化 - 仅接受正确的数据形状
182
+ };
183
+
184
+ return (
185
+ <div>
186
+ <button onClick={() => handleFetchUser('123')}>
187
+ 获取用户
188
+ </button>
189
+ {getLoading && <div>正在加载用户...</div>}
190
+ {getError && <div>错误: {getError.message}</div>}
191
+ {user && <div>用户: {user.name}</div>}
192
+
193
+ <button onClick={() => handleCreateUser({ name: 'John', email: 'john@example.com' })}>
194
+ 创建用户
195
+ </button>
196
+ {createLoading && <div>正在创建用户...</div>}
197
+ {createError && <div>错误: {createError.message}</div>}
198
+ {createdUser && <div>已创建: {createdUser.name}</div>}
199
+ </div>
200
+ );
201
+ }
202
+ ```
203
+
204
+ **自定义错误类型:**
205
+
206
+ ```typescript jsx
207
+ import { createExecuteApiHooks } from '@ahoo-wang/fetcher-react';
208
+
209
+ // 定义自定义错误类型
210
+ class ApiError extends Error {
211
+ constructor(
212
+ public statusCode: number,
213
+ message: string,
214
+ ) {
215
+ super(message);
216
+ }
217
+ }
218
+
219
+ // 使用自定义错误类型生成 hooks
220
+ @api('/data')
221
+ class DataApi {
222
+ @get('/{id}')
223
+ getData(@path('id') id: string): Promise<Data> {
224
+ throw autoGeneratedError(id);
225
+ }
226
+ }
227
+
228
+ const apiHooks = createExecuteApiHooks<
229
+ { getData: (id: string) => Promise<Data> },
230
+ ApiError
231
+ >({
232
+ api: new DataApi(),
233
+ errorType: ApiError,
234
+ });
235
+
236
+ function MyComponent() {
237
+ const { error, execute } = apiHooks.useGetData();
238
+
239
+ // error 现在类型化为 ApiError | undefined
240
+ if (error) {
241
+ console.log('状态码:', error.statusCode); // TypeScript 知道 statusCode
242
+ }
243
+ }
244
+ ```
245
+
246
+ **具有类方法的高级用法:**
247
+
248
+ ```typescript jsx
249
+ import { createExecuteApiHooks } from '@ahoo-wang/fetcher-react';
250
+
251
+ class ApiClient {
252
+ private baseUrl: string;
253
+
254
+ constructor(baseUrl: string) {
255
+ this.baseUrl = baseUrl;
256
+ }
257
+
258
+ async get(endpoint: string): Promise<any> {
259
+ const response = await fetch(`${this.baseUrl}${endpoint}`);
260
+ return response.json();
261
+ }
262
+
263
+ async post(endpoint: string, data: any): Promise<any> {
264
+ const response = await fetch(`${this.baseUrl}${endpoint}`, {
265
+ method: 'POST',
266
+ headers: { 'Content-Type': 'application/json' },
267
+ body: JSON.stringify(data),
268
+ });
269
+ return response.json();
270
+ }
271
+
272
+ // 静态方法示例
273
+ static async healthCheck(): Promise<{ status: string }> {
274
+ const response = await fetch('/api/health');
275
+ return response.json();
276
+ }
277
+ }
278
+
279
+ const apiClient = new ApiClient('/api');
280
+ const apiHooks = createExecuteApiHooks({ api: apiClient });
281
+
282
+ // 生成的 hooks: useGet, usePost
283
+ // 静态方法也会被发现: useHealthCheck
284
+
285
+ function ApiComponent() {
286
+ const { execute: getData } = apiHooks.useGet();
287
+ const { execute: postData } = apiHooks.usePost();
288
+ const { execute: healthCheck } = apiHooks.useHealthCheck();
289
+
290
+ return (
291
+ <div>
292
+ <button onClick={() => getData('/users')}>获取用户</button>
293
+ <button onClick={() => postData('/users', { name: '新用户' })}>创建用户</button>
294
+ <button onClick={() => healthCheck()}>健康检查</button>
295
+ </div>
296
+ );
297
+ }
298
+ ```
299
+
300
+ #### createQueryApiHooks
301
+
302
+ 🚀 **自动类型安全查询 API Hooks 生成** - 从 API 对象自动生成完全类型化的 React 查询 hooks,具有自动查询状态管理、自动执行和高级执行控制。
303
+
304
+ `createQueryApiHooks` 函数自动发现 API 对象中的查询方法,并创建相应的扩展 `useQuery` 的 React hooks。每个生成的 hook 都提供自动查询参数管理、状态管理和对具有类型安全查询访问的自定义执行回调的支持。
305
+
306
+ **主要特性:**
307
+
308
+ - **自动方法发现**:遍历对象属性和原型链
309
+ - **类型安全查询 Hooks**:查询参数和返回类型的完整 TypeScript 推断
310
+ - **查询状态管理**:内置 `setQuery` 和 `getQuery` 进行参数管理
311
+ - **自动执行**:查询参数更改时可选的自动执行
312
+ - **执行控制**:`onBeforeExecute` 回调用于查询检查/修改和中止控制器访问
313
+ - **自定义错误类型**:支持指定超出默认 `FetcherError` 的错误类型
314
+
315
+ ```typescript jsx
316
+ import { createQueryApiHooks } from '@ahoo-wang/fetcher-react';
317
+ import { api, get, post, patch, path, body, autoGeneratedError } from '@ahoo-wang/fetcher-decorator';
318
+
319
+ // 使用装饰器定义您的 API 服务
320
+ @api('/users')
321
+ class UserApi {
322
+ @get('')
323
+ getUsers(query: UserListQuery, attributes?: Record<string, any>): Promise<User[]> {
324
+ throw autoGeneratedError(query, attributes);
325
+ }
326
+
327
+ @get('/{id}')
328
+ getUser(query: { id: string }, attributes?: Record<string, any>): Promise<User> {
329
+ throw autoGeneratedError(query, attributes);
330
+ }
331
+
332
+ @post('')
333
+ createUser(query: { name: string; email: string }, attributes?: Record<string, any>): Promise<User> {
334
+ throw autoGeneratedError(query, attributes);
335
+ }
336
+ }
337
+
338
+ const apiHooks = createQueryApiHooks({ api: new UserApi() });
339
+
340
+ function UserListComponent() {
341
+ const { loading, result, error, execute, setQuery, getQuery } = apiHooks.useGetUsers({
342
+ initialQuery: { page: 1, limit: 10 },
343
+ autoExecute: true,
344
+ onBeforeExecute: (abortController, query) => {
345
+ // query 是完全类型化的 UserListQuery
346
+ console.log('正在执行查询:', query);
347
+ // 如果需要,可以就地修改查询参数
348
+ query.page = Math.max(1, query.page);
349
+ },
350
+ });
351
+
352
+ const handlePageChange = (page: number) => {
353
+ // 自动更新查询并触发执行(如果 autoExecute: true)
354
+ setQuery({ ...getQuery(), page });
355
+ };
356
+
357
+ if (loading) return <div>正在加载...</div>;
358
+ if (error) return <div>错误: {error.message}</div>;
359
+
360
+ return (
361
+ <div>
362
+ <button onClick={() => handlePageChange(2)}>转到第2页</button>
363
+ {result?.map(user => (
364
+ <div key={user.id}>{user.name}</div>
365
+ ))}
366
+ </div>
367
+ );
368
+ }
369
+
370
+ function UserDetailComponent() {
371
+ const { result: user, execute } = apiHooks.useGetUser({
372
+ initialQuery: { id: '123' },
373
+ });
374
+
375
+ return (
376
+ <div>
377
+ <button onClick={execute}>加载用户</button>
378
+ {user && <div>用户: {user.name}</div>}
379
+ </div>
380
+ );
381
+ }
382
+ ```
383
+
384
+ **自定义错误类型:**
385
+
386
+ ```typescript jsx
387
+ import { createQueryApiHooks } from '@ahoo-wang/fetcher-react';
388
+
389
+ // 定义自定义错误类型
390
+ class ApiError extends Error {
391
+ constructor(
392
+ public statusCode: number,
393
+ message: string,
394
+ ) {
395
+ super(message);
396
+ }
397
+ }
398
+
399
+ // 使用自定义错误类型生成查询 hooks
400
+ @api('/data')
401
+ class DataApi {
402
+ @get('/{id}')
403
+ getData(
404
+ query: { id: string },
405
+ attributes?: Record<string, any>,
406
+ ): Promise<Data> {
407
+ throw autoGeneratedError(query, attributes);
408
+ }
409
+ }
410
+
411
+ const apiHooks = createQueryApiHooks<
412
+ {
413
+ getData: (
414
+ query: { id: string },
415
+ attributes?: Record<string, any>,
416
+ ) => Promise<Data>;
417
+ },
418
+ ApiError
419
+ >({
420
+ api: new DataApi(),
421
+ errorType: ApiError,
422
+ });
423
+
424
+ function MyComponent() {
425
+ const { error, execute } = apiHooks.useGetData();
426
+
427
+ // error 现在类型化为 ApiError | undefined
428
+ if (error) {
429
+ console.log('状态码:', error.statusCode); // TypeScript 知道 statusCode
430
+ }
431
+ }
432
+ ```
433
+
434
+ **高级用法与手动查询管理:**
435
+
436
+ ```typescript jsx
437
+ import { createQueryApiHooks } from '@ahoo-wang/fetcher-react';
438
+
439
+ const apiHooks = createQueryApiHooks({ api: userApi });
440
+
441
+ function SearchComponent() {
442
+ const { loading, result, setQuery, getQuery } = apiHooks.useGetUsers({
443
+ initialQuery: { search: '', page: 1 },
444
+ autoExecute: false, // 手动执行控制
445
+ });
446
+
447
+ const handleSearch = (searchTerm: string) => {
448
+ // 更新查询而不自动执行
449
+ setQuery({ search: searchTerm, page: 1 });
450
+ };
451
+
452
+ const handleSearchSubmit = () => {
453
+ // 使用当前查询手动执行
454
+ apiHooks.useGetUsers().execute();
455
+ };
456
+
457
+ const currentQuery = getQuery(); // 访问当前查询参数
458
+
459
+ return (
460
+ <div>
461
+ <input
462
+ value={currentQuery.search}
463
+ onChange={(e) => handleSearch(e.target.value)}
464
+ placeholder="搜索用户..."
465
+ />
466
+ <button onClick={handleSearchSubmit} disabled={loading}>
467
+ {loading ? '搜索中...' : '搜索'}
468
+ </button>
469
+ {result?.map(user => (
470
+ <div key={user.id}>{user.name}</div>
471
+ ))}
472
+ </div>
473
+ );
474
+ }
475
+ ```
476
+
112
477
  ### 核心 Hooks
113
478
 
114
479
  #### useExecutePromise
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Shared utilities and types for API hooks generation.
3
+ */
4
+ /**
5
+ * Converts a method name to a React hook name by prefixing with 'use' and capitalizing the first letter.
6
+ * @param methodName - The method name to convert.
7
+ * @returns The converted hook name.
8
+ * @throws {Error} If methodName is empty or null.
9
+ * @example
10
+ * methodNameToHookName('getUser') // 'useGetUser'
11
+ * methodNameToHookName('createPost') // 'useCreatePost'
12
+ * methodNameToHookName('APIGetData') // 'useAPIGetData'
13
+ */
14
+ export declare function methodNameToHookName(methodName: string): string;
15
+ /**
16
+ * Collects all function methods from an object and its prototype chain.
17
+ * @param obj - The object to collect methods from.
18
+ * @returns A map of method names to bound methods.
19
+ */
20
+ export declare function collectMethods<T extends (...args: any[]) => Promise<any>>(obj: Record<string, any>): Map<string, T>;
21
+ /**
22
+ * Common configuration options for API hooks creation functions.
23
+ * @template API - The API object type.
24
+ */
25
+ export interface CreateApiHooksOptions<API extends Record<string, any>> {
26
+ /**
27
+ * The API object containing methods to be wrapped into hooks.
28
+ */
29
+ api: API;
30
+ }
31
+ /**
32
+ * Utility type to extract the hook name from a method name.
33
+ * @template K - The method name key.
34
+ */
35
+ export type HookName<K extends string> = `use${Capitalize<K>}`;
36
+ /**
37
+ * Utility type to check if a value is a function that returns a Promise.
38
+ * @template T - The value to check.
39
+ */
40
+ export type IsPromiseFunction<T> = T extends (...args: any[]) => Promise<any> ? true : false;
41
+ /**
42
+ * Utility type to extract the parameters of a promise-returning function.
43
+ * @template T - The function type.
44
+ */
45
+ export type FunctionParameters<T> = T extends (...args: infer P) => Promise<any> ? P : never;
46
+ /**
47
+ * Utility type to extract the resolved return type of a promise-returning function.
48
+ * @template T - The function type.
49
+ */
50
+ export type FunctionReturnType<T> = T extends (...args: any[]) => Promise<infer R> ? Awaited<R> : never;
51
+ /**
52
+ * Utility type for API method signatures.
53
+ * @template TArgs - Parameter types.
54
+ * @template TReturn - Return type.
55
+ */
56
+ export type ApiMethod<TArgs extends any[] = any[], TReturn = any> = (...args: TArgs) => Promise<TReturn>;
57
+ /**
58
+ * Utility type for query method signatures (with query, attributes, abortController).
59
+ * @template Q - Query type.
60
+ * @template TReturn - Return type.
61
+ */
62
+ export type QueryMethod<Q = any, TReturn = any> = (query: Q, attributes?: Record<string, any>, abortController?: AbortController) => Promise<TReturn>;
63
+ /**
64
+ * Common callback type for pre-execution hooks.
65
+ * @template TParams - The parameters type passed to the callback.
66
+ */
67
+ export type OnBeforeExecuteCallback<TParams> = (abortController: AbortController | undefined, params: TParams) => void;
68
+ /**
69
+ * Utility type to create API hooks mapping.
70
+ * @template API - The API object type.
71
+ * @template MethodType - The method signature type.
72
+ * @template HookType - The hook function type.
73
+ * @template E - Error type.
74
+ */
75
+ export type ApiHooksMapping<API extends Record<string, any>, MethodType extends (...args: any[]) => Promise<any>, HookType> = {
76
+ [K in keyof API as API[K] extends MethodType ? HookName<string & K> : never]: API[K] extends MethodType ? HookType : never;
77
+ };
78
+ //# sourceMappingURL=apiHooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiHooks.d.ts","sourceRoot":"","sources":["../../src/api/apiHooks.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAQ/D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,EACvE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GACvB,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CA6BhB;AAMD;;;GAGG;AACH,MAAM,WAAW,qBAAqB,CAAC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IACpE;;OAEG;IACH,GAAG,EAAE,GAAG,CAAC;CACV;AAED;;;GAGG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,MAAM,IAAI,MAAM,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;AAE/D;;;GAGG;AACH,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,GACzE,IAAI,GACJ,KAAK,CAAC;AAEV;;;GAGG;AACH,MAAM,MAAM,kBAAkB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,KAAK,OAAO,CAAC,GAAG,CAAC,GAC5E,CAAC,GACD,KAAK,CAAC;AAEV;;;GAGG;AACH,MAAM,MAAM,kBAAkB,CAAC,CAAC,IAAI,CAAC,SAAS,CAC5C,GAAG,IAAI,EAAE,GAAG,EAAE,KACX,OAAO,CAAC,MAAM,CAAC,CAAC,GACjB,OAAO,CAAC,CAAC,CAAC,GACV,KAAK,CAAC;AAEV;;;;GAIG;AACH,MAAM,MAAM,SAAS,CAAC,KAAK,SAAS,GAAG,EAAE,GAAG,GAAG,EAAE,EAAE,OAAO,GAAG,GAAG,IAAI,CAClE,GAAG,IAAI,EAAE,KAAK,KACX,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB;;;;GAIG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,GAAG,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,CAChD,KAAK,EAAE,CAAC,EACR,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAChC,eAAe,CAAC,EAAE,eAAe,KAC9B,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB;;;GAGG;AACH,MAAM,MAAM,uBAAuB,CAAC,OAAO,IAAI,CAC7C,eAAe,EAAE,eAAe,GAAG,SAAS,EAC5C,MAAM,EAAE,OAAO,KACZ,IAAI,CAAC;AAEV;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,CACzB,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC/B,UAAU,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,EACnD,QAAQ,IACN;KACD,CAAC,IAAI,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,SAAS,UAAU,GACxC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,GACpB,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,SAAS,UAAU,GAAG,QAAQ,GAAG,KAAK;CACzD,CAAC"}
@@ -0,0 +1,105 @@
1
+ import { UseExecutePromiseReturn, UseExecutePromiseOptions } from '../core';
2
+ import { FetcherError } from '@ahoo-wang/fetcher';
3
+ import { CreateApiHooksOptions, HookName, ApiMethod, FunctionParameters, FunctionReturnType, OnBeforeExecuteCallback } from './apiHooks';
4
+ /**
5
+ * Configuration options for createExecuteApiHooks.
6
+ * @template API - The API object type containing methods that return promises.
7
+ */
8
+ export interface CreateExecuteApiHooksOptions<API extends Record<string, any>> extends CreateApiHooksOptions<API> {
9
+ }
10
+ /**
11
+ * Configuration options for createQueryApiHooks.
12
+ * @template API - The API object type containing query methods.
13
+ */
14
+ export interface CreateQueryApiHooksOptions<API extends Record<string, any>> extends CreateApiHooksOptions<API> {
15
+ }
16
+ /**
17
+ * Options for useApiMethodExecute hook.
18
+ * @template TArgs - The parameter types of the API method.
19
+ * @template TData - The return type of the API method (resolved).
20
+ * @template E - The error type.
21
+ */
22
+ export interface UseApiMethodExecuteOptions<TArgs = any[], TData = any, E = FetcherError> extends UseExecutePromiseOptions<TData, E> {
23
+ /**
24
+ * Callback executed before method invocation.
25
+ * Allows users to handle abortController and inspect/modify parameters.
26
+ * Note: Parameters can be modified in place for objects/arrays.
27
+ * @param abortController - The AbortController for the request.
28
+ * @param args - The arguments passed to the API method (type-safe).
29
+ *
30
+ * @example
31
+ * onBeforeExecute: (abortController, args) => {
32
+ * // args is now typed as Parameters<TMethod>
33
+ * const [id, options] = args;
34
+ * // Modify parameters in place
35
+ * if (options && typeof options === 'object') {
36
+ * options.timestamp = Date.now();
37
+ * }
38
+ * }
39
+ */
40
+ onBeforeExecute?: OnBeforeExecuteCallback<TArgs>;
41
+ }
42
+ /**
43
+ * The return type of createExecuteApiHooks.
44
+ * Creates a hook for each function method in the API object, prefixed with 'use' and capitalized.
45
+ * Each hook accepts optional useExecutepromise options and returns the useExecutepromise interface
46
+ * with a modified execute function that takes the API method parameters instead of a promise supplier.
47
+ * @template API - The API object type.
48
+ * @template E - The error type for all hooks (defaults to FetcherError).
49
+ */
50
+ export type APIHooks<API extends Record<string, any>, E = FetcherError> = {
51
+ [K in keyof API as API[K] extends ApiMethod ? HookName<string & K> : never]: API[K] extends ApiMethod ? (options?: UseApiMethodExecuteOptions<FunctionParameters<API[K]>, FunctionReturnType<API[K]>, E>) => UseExecutePromiseReturn<FunctionReturnType<API[K]>, E> & {
52
+ execute: (...params: FunctionParameters<API[K]>) => Promise<void>;
53
+ } : never;
54
+ };
55
+ /**
56
+ * Creates type-safe React hooks for API methods.
57
+ * Each API method that returns a Promise is wrapped into a hook that extends useExecutePromise.
58
+ * The generated hooks provide automatic state management, abort support, and error handling.
59
+ *
60
+ * @template API - The API object type containing methods that return promises.
61
+ * @param options - Configuration options including the API object.
62
+ * @returns An object containing hooks for each API method.
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * // Default behavior (no onBeforeExecute)
67
+ * const userApi = {
68
+ * getUser: (id: string) => fetch(`/api/users/${id}`).then(res => res.json()),
69
+ * createUser: (data: UserInput) => fetch('/api/users', {
70
+ * method: 'POST',
71
+ * body: JSON.stringify(data),
72
+ * }).then(res => res.json()),
73
+ * };
74
+ *
75
+ * const apiHooks = createExecuteApiHooks({ api: userApi });
76
+ *
77
+ * function UserComponent() {
78
+ * const { loading, result, error, execute } = apiHooks.useGetUser();
79
+ *
80
+ * const handleFetchUser = (userId: string) => {
81
+ * execute(userId); // Calls getUser(userId) directly
82
+ * };
83
+ *
84
+ * // Custom onBeforeExecute to handle abortController and modify parameters
85
+ * const { execute: customExecute } = apiHooks.useCreateUser({
86
+ * onBeforeExecute: (abortController, args) => {
87
+ * // args is now fully type-safe as Parameters<createUser>
88
+ * const [data] = args;
89
+ * // Modify parameters in place (assuming data is mutable)
90
+ * if (data && typeof data === 'object') {
91
+ * (data as any).timestamp = Date.now();
92
+ * }
93
+ * // Could also set up abortController.signal
94
+ * abortController.signal.addEventListener('abort', () => {
95
+ * console.log('Request aborted');
96
+ * });
97
+ * },
98
+ * });
99
+ *
100
+ * // ... component logic
101
+ * }
102
+ * ```
103
+ */
104
+ export declare function createExecuteApiHooks<API extends Record<string, any>, E = FetcherError>(options: CreateExecuteApiHooksOptions<API>): APIHooks<API, E>;
105
+ //# sourceMappingURL=createExecuteApiHooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createExecuteApiHooks.d.ts","sourceRoot":"","sources":["../../src/api/createExecuteApiHooks.ts"],"names":[],"mappings":"AAcA,OAAO,EAEL,uBAAuB,EACvB,wBAAwB,EAEzB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAGL,qBAAqB,EACrB,QAAQ,EACR,SAAS,EACT,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAEpB;;;GAGG;AACH,MAAM,WAAW,4BAA4B,CAC3C,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAC/B,SAAQ,qBAAqB,CAAC,GAAG,CAAC;CAAG;AAEvC;;;GAGG;AACH,MAAM,WAAW,0BAA0B,CACzC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAC/B,SAAQ,qBAAqB,CAAC,GAAG,CAAC;CAAG;AAEvC;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B,CACzC,KAAK,GAAG,GAAG,EAAE,EACb,KAAK,GAAG,GAAG,EACX,CAAC,GAAG,YAAY,CAChB,SAAQ,wBAAwB,CAAC,KAAK,EAAE,CAAC,CAAC;IAC1C;;;;;;;;;;;;;;;;OAgBG;IACH,eAAe,CAAC,EAAE,uBAAuB,CAAC,KAAK,CAAC,CAAC;CAClD;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,QAAQ,CAAC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,IAAI;KACvE,CAAC,IAAI,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,SAAS,SAAS,GACvC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,GACpB,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,SAAS,SAAS,GAChC,CACE,OAAO,CAAC,EAAE,0BAA0B,CAClC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAC1B,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAC1B,CAAC,CACF,KACE,uBAAuB,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG;QAC5D,OAAO,EAAE,CAAC,GAAG,MAAM,EAAE,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KACnE,GACD,KAAK;CACV,CAAC;AA4DF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC/B,CAAC,GAAG,YAAY,EAChB,OAAO,EAAE,4BAA4B,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,CAa9D"}
@@ -0,0 +1,97 @@
1
+ import { UseQueryReturn, UseQueryOptions } from '../core';
2
+ import { FetcherError } from '@ahoo-wang/fetcher';
3
+ import { CreateQueryApiHooksOptions } from './createExecuteApiHooks';
4
+ import { HookName, QueryMethod, OnBeforeExecuteCallback } from './apiHooks';
5
+ /**
6
+ * Options for useApiMethodQuery hook.
7
+ * @template Q - The query type.
8
+ * @template TData - The return type of the API method (resolved).
9
+ * @template E - The error type.
10
+ */
11
+ export interface UseApiMethodQueryOptions<Q, TData = any, E = FetcherError> extends Omit<UseQueryOptions<Q, TData, E>, 'execute'> {
12
+ /**
13
+ * Callback executed before query execution.
14
+ * Allows users to handle abortController and inspect/modify query parameters.
15
+ * Note: Query parameters can be modified in place for objects.
16
+ * @param abortController - The AbortController for the request.
17
+ * @param query - The query parameters passed to the API method (type-safe).
18
+ *
19
+ * @example
20
+ * onBeforeExecute: (abortController, query) => {
21
+ * // query is now typed as Q
22
+ * if (query && typeof query === 'object') {
23
+ * query.timestamp = Date.now();
24
+ * }
25
+ * }
26
+ */
27
+ onBeforeExecute?: OnBeforeExecuteCallback<Q>;
28
+ }
29
+ /**
30
+ * The return type of createQueryApiHooks.
31
+ * Creates a hook for each function method in the API object, prefixed with 'use' and capitalized.
32
+ * Each hook accepts query options and returns the useQuery interface with type-safe query management.
33
+ * @template API - The API object type.
34
+ * @template E - The error type for all hooks (defaults to FetcherError).
35
+ */
36
+ export type QueryAPIHooks<API extends Record<string, any>, E = FetcherError> = {
37
+ [K in keyof API as API[K] extends QueryMethod ? HookName<string & K> : never]: API[K] extends QueryMethod<infer Q, infer R> ? (options?: UseApiMethodQueryOptions<Q, R, E>) => UseQueryReturn<Q, R, E> : never;
38
+ };
39
+ /**
40
+ * Creates type-safe React hooks for API query methods.
41
+ * Each API method that follows the query pattern is wrapped into a hook that extends useQuery.
42
+ * The generated hooks provide automatic query state management, auto-execution, and error handling.
43
+ *
44
+ * @template API - The API object type containing query methods.
45
+ * @param options - Configuration options including the API object.
46
+ * @returns An object containing hooks for each API query method.
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * // Define your API service using decorators
51
+ * import { api, get, post, patch, path, body, autoGeneratedError } from '@ahoo-wang/fetcher-decorator';
52
+ *
53
+ * @api('/users')
54
+ * class UserApi {
55
+ * @get('')
56
+ * getUsers(query: UserListQuery, attributes?: Record<string, any>): Promise<User[]> {
57
+ * throw autoGeneratedError(query, attributes);
58
+ * }
59
+ *
60
+ * @get('/{id}')
61
+ * getUser(query: { id: string }, attributes?: Record<string, any>): Promise<User> {
62
+ * throw autoGeneratedError(query, attributes);
63
+ * }
64
+ * }
65
+ *
66
+ * const apiHooks = createQueryApiHooks({ api: new UserApi() });
67
+ *
68
+ * function UserListComponent() {
69
+ * const { loading, result, error, execute, setQuery, getQuery } = apiHooks.useGetUsers({
70
+ * initialQuery: { page: 1, limit: 10 },
71
+ * autoExecute: true,
72
+ * onBeforeExecute: (abortController, query) => {
73
+ * // query is fully typed as UserListQuery
74
+ * console.log('Executing query:', query);
75
+ * },
76
+ * });
77
+ *
78
+ * const handlePageChange = (page: number) => {
79
+ * setQuery({ ...getQuery(), page }); // Automatically executes
80
+ * };
81
+ *
82
+ * if (loading) return <div>Loading...</div>;
83
+ * if (error) return <div>Error: {error.message}</div>;
84
+ *
85
+ * return (
86
+ * <div>
87
+ * <button onClick={() => handlePageChange(2)}>Go to page 2</button>
88
+ * {result?.map(user => (
89
+ * <div key={user.id}>{user.name}</div>
90
+ * ))}
91
+ * </div>
92
+ * );
93
+ * }
94
+ * ```
95
+ */
96
+ export declare function createQueryApiHooks<API extends Record<string, any>, E = FetcherError>(options: CreateQueryApiHooksOptions<API>): QueryAPIHooks<API, E>;
97
+ //# sourceMappingURL=createQueryApiHooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createQueryApiHooks.d.ts","sourceRoot":"","sources":["../../src/api/createQueryApiHooks.ts"],"names":[],"mappings":"AAcA,OAAO,EAAY,cAAc,EAAE,eAAe,EAAa,MAAM,SAAS,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EAGL,QAAQ,EACR,WAAW,EACX,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAEpB;;;;;GAKG;AACH,MAAM,WAAW,wBAAwB,CACvC,CAAC,EACD,KAAK,GAAG,GAAG,EACX,CAAC,GAAG,YAAY,CAChB,SAAQ,IAAI,CAAC,eAAe,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,SAAS,CAAC;IACrD;;;;;;;;;;;;;;OAcG;IACH,eAAe,CAAC,EAAE,uBAAuB,CAAC,CAAC,CAAC,CAAC;CAC9C;AAED;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,CAAC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,IAAI;KAC5E,CAAC,IAAI,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,SAAS,WAAW,GACzC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,GACpB,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,SAAS,WAAW,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,GACpD,CAAC,OAAO,CAAC,EAAE,wBAAwB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GACxE,KAAK;CACV,CAAC;AAyCF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC/B,CAAC,GAAG,YAAY,EAChB,OAAO,EAAE,0BAA0B,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC,GAAG,EAAE,CAAC,CAAC,CAoBjE"}
@@ -0,0 +1,4 @@
1
+ export * from './apiHooks';
2
+ export * from './createExecuteApiHooks';
3
+ export * from './createQueryApiHooks';
4
+ //# sourceMappingURL=index.d.ts.map