@gz-fronted/gz-pc 0.1.0
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.md +431 -0
- package/dist/fetch/index.cjs +201 -0
- package/dist/fetch/index.d.cts +78 -0
- package/dist/fetch/index.d.ts +78 -0
- package/dist/fetch/index.js +190 -0
- package/dist/hooks/index.cjs +12 -0
- package/dist/hooks/index.d.cts +1 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +1 -0
- package/dist/utils/index.cjs +87 -0
- package/dist/utils/index.d.cts +12 -0
- package/dist/utils/index.d.ts +12 -0
- package/dist/utils/index.js +82 -0
- package/package.json +99 -0
package/README.md
ADDED
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
# @gz-fronted/gz-pc
|
|
2
|
+
|
|
3
|
+
面向 GZ 前端项目的请求、Hooks 和轻量工具函数包。各子路径独立构建;只使用
|
|
4
|
+
`utils` 时不会加载 Axios、React、ahooks 或 gg-ui。
|
|
5
|
+
|
|
6
|
+
## 安装
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm install @gz-fronted/gz-pc react @chenhui996/gg-ui
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
宿主项目需要提供以下 peer dependencies:
|
|
13
|
+
|
|
14
|
+
- React 18 或 19
|
|
15
|
+
- `@chenhui996/gg-ui` 0.1
|
|
16
|
+
|
|
17
|
+
Axios 和 ahooks 是 gz-pc 的普通 dependencies,安装 gz-pc 时会自动安装,宿主
|
|
18
|
+
无需手工添加。React 和 gg-ui 不会打入本包产物。
|
|
19
|
+
|
|
20
|
+
本包刻意不提供聚合根入口,请始终从 `/fetch`、`/hooks` 或 `/utils` 子路径导入,
|
|
21
|
+
避免加载无关模块。
|
|
22
|
+
|
|
23
|
+
## 请求协议
|
|
24
|
+
|
|
25
|
+
gz-fetch **只有在 HTTP Status 为 200 时才认为请求成功**。201、204 以及其他
|
|
26
|
+
所有状态都会进入统一错误处理。
|
|
27
|
+
|
|
28
|
+
成功时直接返回后端原始的 `response.data`,不会返回 `AxiosResponse`,也不会:
|
|
29
|
+
|
|
30
|
+
- 判断业务 `code`
|
|
31
|
+
- 自动解包 `data`
|
|
32
|
+
- 转换分页、字段、日期或任何服务端数据
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { configureGzFetch, gzFetch } from '@gz-fronted/gz-pc/fetch';
|
|
36
|
+
|
|
37
|
+
// 应用初始化入口只调用一次;微前端重新挂载时可以覆盖上一次配置。
|
|
38
|
+
configureGzFetch({
|
|
39
|
+
baseURL: '/api',
|
|
40
|
+
timeout: 15_000,
|
|
41
|
+
getToken: () => runtimeToken,
|
|
42
|
+
showErrorMessage: true,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
interface ListResult {
|
|
46
|
+
records: Array<{ id: string }>;
|
|
47
|
+
total: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const result = await gzFetch<ListResult>({
|
|
51
|
+
url: '/users',
|
|
52
|
+
method: 'GET',
|
|
53
|
+
params: {
|
|
54
|
+
page: 1,
|
|
55
|
+
pageSize: 20,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
result.records;
|
|
59
|
+
result.total;
|
|
60
|
+
|
|
61
|
+
interface CreateUserParams {
|
|
62
|
+
name: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
await gzFetch<ListResult, CreateUserParams>({
|
|
66
|
+
url: '/users',
|
|
67
|
+
method: 'POST',
|
|
68
|
+
params: {
|
|
69
|
+
name: 'Alice',
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
业务模块直接导入并调用 `gzFetch`,不需要自行创建请求实例。gz-pc 统一使用
|
|
75
|
+
配置对象式请求调用,业务代码不得混用 `gzFetch(config)` 与
|
|
76
|
+
`gzFetch.get/post/put/delete` 两种风格。快捷方法仅作为内部兼容层保留,不作为
|
|
77
|
+
业务开发规范。
|
|
78
|
+
|
|
79
|
+
如果在 `configureGzFetch` 之前调用 `gzFetch`,会明确抛出错误,不会静默使用
|
|
80
|
+
空配置,也不会读取 `localStorage`。
|
|
81
|
+
|
|
82
|
+
### 类型签名
|
|
83
|
+
|
|
84
|
+
`gzFetch` 的第一个泛型是响应数据类型,第二个泛型是请求参数类型:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
function gzFetch<TResponse, TRequestParams = unknown>(
|
|
88
|
+
config: GzRequestConfig<TRequestParams>,
|
|
89
|
+
): Promise<TResponse>;
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
成功时直接返回 `response.data`,因此调用结果类型是 `TResponse`,不是
|
|
93
|
+
`AxiosResponse<TResponse>`。
|
|
94
|
+
|
|
95
|
+
### 单次请求配置
|
|
96
|
+
|
|
97
|
+
| 字段 | 类型 | 必填 | 说明 |
|
|
98
|
+
| ------------------ | -------------------------------------- | ---- | ------------------------------------------ |
|
|
99
|
+
| `url` | `string` | 是 | 请求地址;配置了 `baseURL` 时会与其组合 |
|
|
100
|
+
| `method` | `'GET' \| 'POST' \| 'PUT' \| 'DELETE'` | 是 | 请求方法,统一使用大写 |
|
|
101
|
+
| `params` | `TRequestParams` | 否 | 统一请求入参,由第二个泛型约束 |
|
|
102
|
+
| `headers` | `Record<string, string>` | 否 | 本次请求附加的请求头 |
|
|
103
|
+
| `timeout` | `number` | 否 | 本次请求超时时间,单位毫秒;优先于实例配置 |
|
|
104
|
+
| `showErrorMessage` | `boolean` | 否 | 是否调用 gg-ui 的 `message.error` |
|
|
105
|
+
| `skipAuth` | `boolean` | 否 | 为 `true` 时跳过 Token 注入 |
|
|
106
|
+
| `responseType` | `'json' \| 'blob' \| 'text'` | 否 | 响应数据类型,默认 `json` |
|
|
107
|
+
| `signal` | `AbortSignal` | 否 | 取消请求的标准 AbortSignal |
|
|
108
|
+
|
|
109
|
+
请求入参字段统一使用 `params`,业务代码不需要区分 Axios 的 `params` 和 `data`:
|
|
110
|
+
|
|
111
|
+
- GET、DELETE:`params` 转为 URL Query。
|
|
112
|
+
- POST、PUT:`params` 转为 Request Body。
|
|
113
|
+
|
|
114
|
+
业务请求配置不支持 `data`,也不得混用 `params` 和 `data`。
|
|
115
|
+
|
|
116
|
+
完整示例:
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
interface SaveParams {
|
|
120
|
+
id: string;
|
|
121
|
+
name: string;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
interface SaveResult {
|
|
125
|
+
success: boolean;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const controller = new AbortController();
|
|
129
|
+
|
|
130
|
+
const result = await gzFetch<SaveResult, SaveParams>({
|
|
131
|
+
url: '/user/save',
|
|
132
|
+
method: 'POST',
|
|
133
|
+
params: {
|
|
134
|
+
id: '1001',
|
|
135
|
+
name: '张三',
|
|
136
|
+
},
|
|
137
|
+
headers: {
|
|
138
|
+
'X-Request-Source': 'gz-pc',
|
|
139
|
+
},
|
|
140
|
+
timeout: 10_000,
|
|
141
|
+
showErrorMessage: true,
|
|
142
|
+
skipAuth: false,
|
|
143
|
+
responseType: 'json',
|
|
144
|
+
signal: controller.signal,
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### 应用初始化配置
|
|
149
|
+
|
|
150
|
+
`configureGzFetch` 只在应用初始化层调用。它接收以下配置:
|
|
151
|
+
|
|
152
|
+
| 字段 | 类型 | 默认值 | 说明 |
|
|
153
|
+
| ------------------ | ----------------------------------------------------------- | ----------------- | -------------------------- |
|
|
154
|
+
| `baseURL` | `string` | 无 | 所有业务请求的基础地址 |
|
|
155
|
+
| `timeout` | `number` | `15000` | 默认请求超时时间,单位毫秒 |
|
|
156
|
+
| `getToken` | `() => string \| undefined \| Promise<string \| undefined>` | 无 | 每次请求前动态获取 Token |
|
|
157
|
+
| `showErrorMessage` | `boolean` | `true` | 实例级错误提示开关 |
|
|
158
|
+
| `auth.headerName` | `string` | `Authorization` | Token 请求头名称 |
|
|
159
|
+
| `auth.formatToken` | `(token: string) => string` | `Bearer ${token}` | Token 格式化函数 |
|
|
160
|
+
| `middlewares` | `readonly GzFetchMiddleware[]` | `[]` | 请求、响应和错误中间件 |
|
|
161
|
+
|
|
162
|
+
当前稳定 API 没有开放 Axios 的 `withCredentials`、`paramsSerializer`、
|
|
163
|
+
`onUploadProgress`、`validateStatus` 等配置,也暂不支持 `PATCH`。业务代码只应传入
|
|
164
|
+
上表列出的字段;如果后续确有通用场景,再通过 gz-pc 统一扩展类型和请求核心。
|
|
165
|
+
|
|
166
|
+
## Token
|
|
167
|
+
|
|
168
|
+
`getToken` 会在每次请求前动态执行。gz-fetch 不读取 Props、React Context 或
|
|
169
|
+
`localStorage`。
|
|
170
|
+
|
|
171
|
+
默认请求头为 `Authorization: Bearer <token>`:
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
configureGzFetch({
|
|
175
|
+
getToken: async () => tokenStore.get(),
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
await gzFetch<PublicConfig>({
|
|
179
|
+
url: '/public/config',
|
|
180
|
+
method: 'GET',
|
|
181
|
+
skipAuth: true,
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
也可以修改 Header 名称和格式:
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
configureGzFetch({
|
|
189
|
+
getToken: () => token,
|
|
190
|
+
auth: {
|
|
191
|
+
headerName: 'X-Token',
|
|
192
|
+
formatToken: (value) => `Token ${value}`,
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## 错误提示
|
|
198
|
+
|
|
199
|
+
HTTP 非 200 时仅尝试读取响应体的 `msg`。没有有效 `msg` 时使用默认文案,最终
|
|
200
|
+
抛出 `GzFetchError`。
|
|
201
|
+
|
|
202
|
+
错误类型包括:
|
|
203
|
+
|
|
204
|
+
- `HTTP_ERROR`
|
|
205
|
+
- `NETWORK_ERROR`
|
|
206
|
+
- `TIMEOUT_ERROR`
|
|
207
|
+
- `CANCELED_ERROR`
|
|
208
|
+
- `UNKNOWN_ERROR`
|
|
209
|
+
|
|
210
|
+
错误 Message 默认开启,优先级为“单请求配置 > 实例配置 > 默认值 true”:
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
configureGzFetch({ showErrorMessage: true });
|
|
214
|
+
|
|
215
|
+
await gzFetch<void, SaveParams>({
|
|
216
|
+
url: '/save',
|
|
217
|
+
method: 'POST',
|
|
218
|
+
params,
|
|
219
|
+
showErrorMessage: false,
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
取消请求会转换成 `CANCELED_ERROR`,且默认不调用 `message.error`:
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
const controller = new AbortController();
|
|
227
|
+
|
|
228
|
+
const promise = gzFetch<UserDetail>({
|
|
229
|
+
url: '/users',
|
|
230
|
+
method: 'GET',
|
|
231
|
+
signal: controller.signal,
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
controller.abort();
|
|
235
|
+
await promise;
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## 中间件
|
|
239
|
+
|
|
240
|
+
中间件的 `onRequest`、`onResponse` 和 `onError` 都按注册顺序执行,不采用洋葱
|
|
241
|
+
模型:
|
|
242
|
+
|
|
243
|
+
```ts
|
|
244
|
+
configureGzFetch({
|
|
245
|
+
middlewares: [
|
|
246
|
+
{
|
|
247
|
+
onRequest(config) {
|
|
248
|
+
return {
|
|
249
|
+
...config,
|
|
250
|
+
headers: {
|
|
251
|
+
...config.headers,
|
|
252
|
+
'X-Trace-ID': crypto.randomUUID(),
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
},
|
|
256
|
+
onResponse(data, context) {
|
|
257
|
+
console.debug(context.status);
|
|
258
|
+
return data;
|
|
259
|
+
},
|
|
260
|
+
onError(error) {
|
|
261
|
+
console.error(error.type);
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
],
|
|
265
|
+
});
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## 独立请求实例
|
|
269
|
+
|
|
270
|
+
普通业务模块禁止反复调用 `createGzFetch`。它只用于确实需要独立请求实例的高级
|
|
271
|
+
场景,例如多后端服务、独立 `baseURL`、独立 Token 或独立中间件链:
|
|
272
|
+
|
|
273
|
+
```ts
|
|
274
|
+
import { createGzFetch } from '@gz-fronted/gz-pc/fetch';
|
|
275
|
+
|
|
276
|
+
const reportingFetch = createGzFetch({
|
|
277
|
+
baseURL: '/reporting-api',
|
|
278
|
+
getToken: () => reportingToken,
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
const report = await reportingFetch<ReportResult>({
|
|
282
|
+
url: '/reports/latest',
|
|
283
|
+
method: 'GET',
|
|
284
|
+
});
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
默认 `gzFetch` 和独立实例都复用同一套请求核心、错误处理、Token 注入和中间件
|
|
288
|
+
实现。
|
|
289
|
+
|
|
290
|
+
## Blob
|
|
291
|
+
|
|
292
|
+
gz-fetch 只返回 Blob,不创建下载链接、不解析文件名:
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
const file = await gzFetch<Blob, ExportParams>({
|
|
296
|
+
url: '/export',
|
|
297
|
+
method: 'POST',
|
|
298
|
+
params,
|
|
299
|
+
responseType: 'blob',
|
|
300
|
+
});
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## Hooks
|
|
304
|
+
|
|
305
|
+
```ts
|
|
306
|
+
import {
|
|
307
|
+
useDebounce,
|
|
308
|
+
useDebounceFn,
|
|
309
|
+
usePagination,
|
|
310
|
+
useRequest,
|
|
311
|
+
} from '@gz-fronted/gz-pc/hooks';
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
`gz-pc/hooks` 通过 `export * from 'ahooks'` 完整透传 ahooks 的公开 API,作为团队
|
|
315
|
+
统一的 Hooks 使用入口。后续自定义 Hook 也从同一入口导出:
|
|
316
|
+
|
|
317
|
+
```ts
|
|
318
|
+
export * from 'ahooks';
|
|
319
|
+
export * from './use-table-height';
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
新增自定义 Hook 时不得与 ahooks 已有导出重名。
|
|
323
|
+
|
|
324
|
+
## formatDate
|
|
325
|
+
|
|
326
|
+
```ts
|
|
327
|
+
import { formatDate } from '@gz-fronted/gz-pc/utils';
|
|
328
|
+
|
|
329
|
+
formatDate(new Date()); // YYYY-MM-DD HH:mm:ss
|
|
330
|
+
formatDate(Date.now(), 'YYYY-MM-DD');
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
支持 `string | number | Date | null | undefined`。`null`、`undefined`、空字符串和
|
|
334
|
+
无效日期统一返回 `--`。该入口不依赖 React、ahooks、Axios 或 gg-ui。
|
|
335
|
+
|
|
336
|
+
## Query 参数
|
|
337
|
+
|
|
338
|
+
```ts
|
|
339
|
+
import { objectToQuery, queryToObject } from '@gz-fronted/gz-pc/utils';
|
|
340
|
+
|
|
341
|
+
queryToObject('https://example.com/list?page=1&tag=a&tag=b');
|
|
342
|
+
// { page: '1', tag: ['a', 'b'] }
|
|
343
|
+
|
|
344
|
+
objectToQuery({
|
|
345
|
+
page: 1,
|
|
346
|
+
keyword: 'audit log',
|
|
347
|
+
tag: ['a', 'b'],
|
|
348
|
+
});
|
|
349
|
+
// page=1&keyword=audit+log&tag=a&tag=b
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
`queryToObject` 支持完整 URL、带 `?` 的 query 和纯 query 字符串;重复参数返回
|
|
353
|
+
字符串数组。`objectToQuery` 返回不带前导 `?` 的字符串,跳过 `undefined`,
|
|
354
|
+
将 `null` 转为空值,并将数组序列化为重复参数。
|
|
355
|
+
|
|
356
|
+
## MSW Mock
|
|
357
|
+
|
|
358
|
+
完整接入样例位于 [`examples/msw`](./examples/msw)。核心启动顺序如下:
|
|
359
|
+
|
|
360
|
+
```ts
|
|
361
|
+
async function bootstrap(): Promise<void> {
|
|
362
|
+
if (import.meta.env.VITE_USE_MOCK === 'true') {
|
|
363
|
+
const { startMock } = await import('./mock/browser');
|
|
364
|
+
await startMock();
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
renderApp();
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
void bootstrap();
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
```ts
|
|
374
|
+
worker.start({
|
|
375
|
+
onUnhandledRequest: 'bypass',
|
|
376
|
+
});
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
必须等待 `worker.start()` 完成后再渲染应用,避免首次请求漏拦截。具体 Handler
|
|
380
|
+
和 Mock 数据由业务项目维护,gz-pc 不提供业务 Mock。
|
|
381
|
+
|
|
382
|
+
## 本地接入
|
|
383
|
+
|
|
384
|
+
先在本仓库构建并生成 tarball:
|
|
385
|
+
|
|
386
|
+
```bash
|
|
387
|
+
npm run build
|
|
388
|
+
npm pack
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
然后在业务项目安装生成的 `.tgz`:
|
|
392
|
+
|
|
393
|
+
```bash
|
|
394
|
+
npm install ../gz-pc/gz-fronted-gz-pc-0.1.0.tgz
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
也可以在联调期间使用业务项目支持的 workspace 或本地 `file:` 依赖。发布前建议
|
|
398
|
+
优先用 tarball 验证,因为它与 npm 实际安装内容最接近。
|
|
399
|
+
|
|
400
|
+
## 提交规范
|
|
401
|
+
|
|
402
|
+
项目使用 Husky、lint-staged、Commitlint 和 Conventional Commits。安装依赖时
|
|
403
|
+
`prepare` 脚本会初始化 Husky。
|
|
404
|
+
|
|
405
|
+
`pre-commit` 在提交前只检查 Git 暂存区中的文件:
|
|
406
|
+
|
|
407
|
+
- JavaScript、JSX、TypeScript、TSX:依次执行 `eslint --fix` 和
|
|
408
|
+
`prettier --write`
|
|
409
|
+
- JSON、Markdown、YAML、CSS、Less:执行 `prettier --write`
|
|
410
|
+
|
|
411
|
+
可以自动修复的问题会写回暂存文件;仍存在的 ESLint 错误会阻止提交。lint-staged
|
|
412
|
+
只负责提交前的快速增量检查,不替代完整的 `lint`、`typecheck`、`test` 和
|
|
413
|
+
`build`。
|
|
414
|
+
|
|
415
|
+
Prettier 对 JavaScript、TypeScript 和 JSX 统一使用单引号;JSON 按标准继续使用
|
|
416
|
+
双引号。
|
|
417
|
+
|
|
418
|
+
`commit-msg` 使用 Commitlint 校验提交信息:
|
|
419
|
+
|
|
420
|
+
```text
|
|
421
|
+
feat: add request client
|
|
422
|
+
fix: handle canceled request
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
缺少合法 type、subject 或其他不符合 Conventional Commits 的提交信息会被拒绝。
|
|
426
|
+
|
|
427
|
+
## 第一版边界
|
|
428
|
+
|
|
429
|
+
第一版不包含 Token 自动刷新、自动重试、缓存、请求去重、并发控制、业务 code
|
|
430
|
+
判断、`data` 解包、分页/日期转换、错误上报、自动文件下载、内置业务 Mock、
|
|
431
|
+
React Context、Zustand 或复杂洋葱中间件。
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var ggUi = require('@chenhui996/gg-ui');
|
|
4
|
+
var axios = require('axios');
|
|
5
|
+
|
|
6
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
|
+
|
|
8
|
+
var axios__default = /*#__PURE__*/_interopDefault(axios);
|
|
9
|
+
|
|
10
|
+
// src/fetch/client.ts
|
|
11
|
+
var GzFetchError = class extends Error {
|
|
12
|
+
constructor(message2, options) {
|
|
13
|
+
super(message2);
|
|
14
|
+
this.name = "GzFetchError";
|
|
15
|
+
this.type = options.type;
|
|
16
|
+
this.status = options.status;
|
|
17
|
+
this.responseData = options.responseData;
|
|
18
|
+
this.originalError = options.originalError;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
function getResponseMessage(data) {
|
|
22
|
+
if (typeof data !== "object" || data === null || !("msg" in data)) {
|
|
23
|
+
return void 0;
|
|
24
|
+
}
|
|
25
|
+
const { msg } = data;
|
|
26
|
+
return typeof msg === "string" && msg.length > 0 ? msg : void 0;
|
|
27
|
+
}
|
|
28
|
+
var DEFAULT_HTTP_ERROR_MESSAGE = "请求失败,请稍后重试";
|
|
29
|
+
var DEFAULT_NETWORK_ERROR_MESSAGE = "网络异常,请检查网络连接";
|
|
30
|
+
var DEFAULT_TIMEOUT_ERROR_MESSAGE = "请求超时,请稍后重试";
|
|
31
|
+
var DEFAULT_CANCELED_ERROR_MESSAGE = "请求已取消";
|
|
32
|
+
var DEFAULT_UNKNOWN_ERROR_MESSAGE = "请求失败,请稍后重试";
|
|
33
|
+
function toGzFetchError(error) {
|
|
34
|
+
var _a;
|
|
35
|
+
if (error instanceof GzFetchError) {
|
|
36
|
+
return error;
|
|
37
|
+
}
|
|
38
|
+
if (axios__default.default.isCancel(error)) {
|
|
39
|
+
return new GzFetchError(DEFAULT_CANCELED_ERROR_MESSAGE, {
|
|
40
|
+
type: "CANCELED_ERROR",
|
|
41
|
+
originalError: error
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
if (axios__default.default.isAxiosError(error)) {
|
|
45
|
+
if (error.code === axios.AxiosError.ECONNABORTED || error.code === axios.AxiosError.ETIMEDOUT) {
|
|
46
|
+
return new GzFetchError(DEFAULT_TIMEOUT_ERROR_MESSAGE, {
|
|
47
|
+
type: "TIMEOUT_ERROR",
|
|
48
|
+
originalError: error
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
if (error.response) {
|
|
52
|
+
return new GzFetchError(
|
|
53
|
+
(_a = getResponseMessage(error.response.data)) != null ? _a : DEFAULT_HTTP_ERROR_MESSAGE,
|
|
54
|
+
{
|
|
55
|
+
type: "HTTP_ERROR",
|
|
56
|
+
status: error.response.status,
|
|
57
|
+
responseData: error.response.data,
|
|
58
|
+
originalError: error
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
return new GzFetchError(DEFAULT_NETWORK_ERROR_MESSAGE, {
|
|
63
|
+
type: "NETWORK_ERROR",
|
|
64
|
+
originalError: error
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return new GzFetchError(DEFAULT_UNKNOWN_ERROR_MESSAGE, {
|
|
68
|
+
type: "UNKNOWN_ERROR",
|
|
69
|
+
originalError: error
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/fetch/middleware.ts
|
|
74
|
+
async function runRequestMiddlewares(initialConfig, middlewares) {
|
|
75
|
+
let config = initialConfig;
|
|
76
|
+
for (const middleware of middlewares) {
|
|
77
|
+
if (middleware.onRequest) {
|
|
78
|
+
config = await middleware.onRequest(config);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return config;
|
|
82
|
+
}
|
|
83
|
+
async function runResponseMiddlewares(initialData, context, middlewares) {
|
|
84
|
+
let data = initialData;
|
|
85
|
+
for (const middleware of middlewares) {
|
|
86
|
+
if (middleware.onResponse) {
|
|
87
|
+
data = await middleware.onResponse(data, context);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return data;
|
|
91
|
+
}
|
|
92
|
+
async function runErrorMiddlewares(error, context, middlewares) {
|
|
93
|
+
var _a;
|
|
94
|
+
for (const middleware of middlewares) {
|
|
95
|
+
await ((_a = middleware.onError) == null ? void 0 : _a.call(middleware, error, context));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// src/fetch/client.ts
|
|
100
|
+
var DEFAULT_TIMEOUT = 15e3;
|
|
101
|
+
var DEFAULT_AUTH_HEADER = "Authorization";
|
|
102
|
+
function toHeaderRecord(headers) {
|
|
103
|
+
const result = {};
|
|
104
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
105
|
+
if (value !== void 0) {
|
|
106
|
+
result[name] = String(value);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
function createGzFetch(options = {}) {
|
|
112
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
113
|
+
const middlewares = (_a = options.middlewares) != null ? _a : [];
|
|
114
|
+
const showInstanceErrorMessage = (_b = options.showErrorMessage) != null ? _b : true;
|
|
115
|
+
const headerName = (_d = (_c = options.auth) == null ? void 0 : _c.headerName) != null ? _d : DEFAULT_AUTH_HEADER;
|
|
116
|
+
const formatToken = (_f = (_e = options.auth) == null ? void 0 : _e.formatToken) != null ? _f : ((token) => `Bearer ${token}`);
|
|
117
|
+
const instance = axios__default.default.create({
|
|
118
|
+
...options.baseURL === void 0 ? {} : { baseURL: options.baseURL },
|
|
119
|
+
timeout: (_g = options.timeout) != null ? _g : DEFAULT_TIMEOUT,
|
|
120
|
+
validateStatus: (status) => status === 200
|
|
121
|
+
});
|
|
122
|
+
async function request(initialConfig) {
|
|
123
|
+
var _a2;
|
|
124
|
+
let currentConfig = initialConfig;
|
|
125
|
+
try {
|
|
126
|
+
const headers = axios.AxiosHeaders.from(initialConfig.headers);
|
|
127
|
+
if (!initialConfig.skipAuth && options.getToken) {
|
|
128
|
+
const token = await options.getToken();
|
|
129
|
+
if (token) {
|
|
130
|
+
headers.set(headerName, formatToken(token));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
currentConfig = await runRequestMiddlewares(
|
|
134
|
+
{ ...initialConfig, headers },
|
|
135
|
+
middlewares
|
|
136
|
+
);
|
|
137
|
+
const usesRequestBody = currentConfig.method === "POST" || currentConfig.method === "PUT";
|
|
138
|
+
const axiosConfig = {
|
|
139
|
+
url: currentConfig.url,
|
|
140
|
+
method: currentConfig.method,
|
|
141
|
+
...!usesRequestBody || currentConfig.params === void 0 ? {} : { data: currentConfig.params },
|
|
142
|
+
...currentConfig.headers === void 0 ? {} : { headers: currentConfig.headers },
|
|
143
|
+
...usesRequestBody || currentConfig.params === void 0 ? {} : { params: currentConfig.params },
|
|
144
|
+
...currentConfig.timeout === void 0 ? {} : { timeout: currentConfig.timeout },
|
|
145
|
+
...currentConfig.responseType === void 0 ? {} : { responseType: currentConfig.responseType },
|
|
146
|
+
...currentConfig.signal === void 0 ? {} : { signal: currentConfig.signal }
|
|
147
|
+
};
|
|
148
|
+
const response = await instance.request(axiosConfig);
|
|
149
|
+
const context = {
|
|
150
|
+
config: currentConfig,
|
|
151
|
+
status: response.status,
|
|
152
|
+
headers: toHeaderRecord(response.headers)
|
|
153
|
+
};
|
|
154
|
+
return await runResponseMiddlewares(response.data, context, middlewares);
|
|
155
|
+
} catch (originalError) {
|
|
156
|
+
const error = toGzFetchError(originalError);
|
|
157
|
+
await runErrorMiddlewares(error, { config: currentConfig }, middlewares);
|
|
158
|
+
const shouldShowError = (_a2 = currentConfig.showErrorMessage) != null ? _a2 : showInstanceErrorMessage;
|
|
159
|
+
if (shouldShowError && error.type !== "CANCELED_ERROR") {
|
|
160
|
+
ggUi.message.error(error.message);
|
|
161
|
+
}
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const gzFetch2 = ((config) => request(config));
|
|
166
|
+
gzFetch2.get = (url, config = {}) => gzFetch2({ ...config, url, method: "GET" });
|
|
167
|
+
gzFetch2.post = (url, params, config = {}) => gzFetch2({
|
|
168
|
+
...config,
|
|
169
|
+
url,
|
|
170
|
+
method: "POST",
|
|
171
|
+
...params === void 0 ? {} : { params }
|
|
172
|
+
});
|
|
173
|
+
gzFetch2.put = (url, params, config = {}) => gzFetch2({
|
|
174
|
+
...config,
|
|
175
|
+
url,
|
|
176
|
+
method: "PUT",
|
|
177
|
+
...params === void 0 ? {} : { params }
|
|
178
|
+
});
|
|
179
|
+
gzFetch2.delete = (url, config = {}) => gzFetch2({ ...config, url, method: "DELETE" });
|
|
180
|
+
return gzFetch2;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// src/fetch/default-client.ts
|
|
184
|
+
var NOT_CONFIGURED_MESSAGE = "gzFetch 尚未配置,请先在应用初始化时调用 configureGzFetch。";
|
|
185
|
+
var defaultClient;
|
|
186
|
+
function configureGzFetch(options) {
|
|
187
|
+
defaultClient = createGzFetch(options);
|
|
188
|
+
}
|
|
189
|
+
async function gzFetch(config) {
|
|
190
|
+
if (!defaultClient) {
|
|
191
|
+
throw new Error(NOT_CONFIGURED_MESSAGE);
|
|
192
|
+
}
|
|
193
|
+
return defaultClient(config);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
exports.GzFetchError = GzFetchError;
|
|
197
|
+
exports.configureGzFetch = configureGzFetch;
|
|
198
|
+
exports.createGzFetch = createGzFetch;
|
|
199
|
+
exports.getResponseMessage = getResponseMessage;
|
|
200
|
+
exports.gzFetch = gzFetch;
|
|
201
|
+
exports.toGzFetchError = toGzFetchError;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { AxiosHeaders } from 'axios';
|
|
2
|
+
|
|
3
|
+
type GzFetchErrorType = 'HTTP_ERROR' | 'NETWORK_ERROR' | 'TIMEOUT_ERROR' | 'CANCELED_ERROR' | 'UNKNOWN_ERROR';
|
|
4
|
+
interface GzFetchErrorOptions {
|
|
5
|
+
type: GzFetchErrorType;
|
|
6
|
+
status?: number;
|
|
7
|
+
responseData?: unknown;
|
|
8
|
+
originalError?: unknown;
|
|
9
|
+
}
|
|
10
|
+
declare class GzFetchError extends Error {
|
|
11
|
+
readonly type: GzFetchErrorType;
|
|
12
|
+
readonly status: number | undefined;
|
|
13
|
+
readonly responseData?: unknown;
|
|
14
|
+
readonly originalError?: unknown;
|
|
15
|
+
constructor(message: string, options: GzFetchErrorOptions);
|
|
16
|
+
}
|
|
17
|
+
declare function getResponseMessage(data: unknown): string | undefined;
|
|
18
|
+
declare function toGzFetchError(error: unknown): GzFetchError;
|
|
19
|
+
|
|
20
|
+
type GzRequestMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
21
|
+
type GzResponseType = 'json' | 'blob' | 'text';
|
|
22
|
+
type GzRequestParams = Record<string, unknown>;
|
|
23
|
+
type GzRequestHeaders = Record<string, string | number | boolean | null> | AxiosHeaders;
|
|
24
|
+
interface GzRequestConfig<TRequestParams = unknown> {
|
|
25
|
+
url: string;
|
|
26
|
+
method: GzRequestMethod;
|
|
27
|
+
params?: TRequestParams;
|
|
28
|
+
headers?: Record<string, string>;
|
|
29
|
+
timeout?: number;
|
|
30
|
+
showErrorMessage?: boolean;
|
|
31
|
+
skipAuth?: boolean;
|
|
32
|
+
responseType?: GzResponseType;
|
|
33
|
+
signal?: AbortSignal;
|
|
34
|
+
}
|
|
35
|
+
interface GzInternalRequestConfig extends Omit<GzRequestConfig<unknown>, 'headers'> {
|
|
36
|
+
headers?: GzRequestHeaders;
|
|
37
|
+
}
|
|
38
|
+
type GzRequestOptions<TRequestParams = unknown> = Omit<GzRequestConfig<TRequestParams>, 'url' | 'method'>;
|
|
39
|
+
interface GzResponseContext {
|
|
40
|
+
config: Readonly<GzInternalRequestConfig>;
|
|
41
|
+
status: number;
|
|
42
|
+
headers: Readonly<Record<string, string>>;
|
|
43
|
+
}
|
|
44
|
+
interface GzErrorContext {
|
|
45
|
+
config: Readonly<GzInternalRequestConfig>;
|
|
46
|
+
}
|
|
47
|
+
interface GzFetchMiddleware {
|
|
48
|
+
onRequest?: (config: GzInternalRequestConfig) => GzInternalRequestConfig | Promise<GzInternalRequestConfig>;
|
|
49
|
+
onResponse?: <T>(data: T, context: GzResponseContext) => T | Promise<T>;
|
|
50
|
+
onError?: (error: GzFetchError, context: GzErrorContext) => void | Promise<void>;
|
|
51
|
+
}
|
|
52
|
+
interface GzFetchAuthOptions {
|
|
53
|
+
headerName?: string;
|
|
54
|
+
formatToken?: (token: string) => string;
|
|
55
|
+
}
|
|
56
|
+
interface CreateGzFetchOptions {
|
|
57
|
+
baseURL?: string;
|
|
58
|
+
timeout?: number;
|
|
59
|
+
getToken?: () => string | undefined | Promise<string | undefined>;
|
|
60
|
+
showErrorMessage?: boolean;
|
|
61
|
+
auth?: GzFetchAuthOptions;
|
|
62
|
+
middlewares?: readonly GzFetchMiddleware[];
|
|
63
|
+
}
|
|
64
|
+
interface GzFetchClient {
|
|
65
|
+
<TResponse, TRequestParams = unknown>(config: GzRequestConfig<TRequestParams>): Promise<TResponse>;
|
|
66
|
+
get<T>(url: string, config?: GzRequestOptions): Promise<T>;
|
|
67
|
+
post<T, TRequestParams = unknown>(url: string, params?: TRequestParams, config?: Omit<GzRequestOptions, 'params'>): Promise<T>;
|
|
68
|
+
put<T, TRequestParams = unknown>(url: string, params?: TRequestParams, config?: Omit<GzRequestOptions, 'params'>): Promise<T>;
|
|
69
|
+
delete<T>(url: string, config?: GzRequestOptions): Promise<T>;
|
|
70
|
+
}
|
|
71
|
+
type GzFetch = GzFetchClient;
|
|
72
|
+
|
|
73
|
+
declare function createGzFetch(options?: CreateGzFetchOptions): GzFetchClient;
|
|
74
|
+
|
|
75
|
+
declare function configureGzFetch(options: CreateGzFetchOptions): void;
|
|
76
|
+
declare function gzFetch<TResponse, TRequestParams = unknown>(config: GzRequestConfig<TRequestParams>): Promise<TResponse>;
|
|
77
|
+
|
|
78
|
+
export { type CreateGzFetchOptions, type GzErrorContext, type GzFetch, type GzFetchAuthOptions, type GzFetchClient, GzFetchError, type GzFetchErrorOptions, type GzFetchErrorType, type GzFetchMiddleware, type GzInternalRequestConfig, type GzRequestConfig, type GzRequestHeaders, type GzRequestMethod, type GzRequestOptions, type GzRequestParams, type GzResponseContext, type GzResponseType, configureGzFetch, createGzFetch, getResponseMessage, gzFetch, toGzFetchError };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { AxiosHeaders } from 'axios';
|
|
2
|
+
|
|
3
|
+
type GzFetchErrorType = 'HTTP_ERROR' | 'NETWORK_ERROR' | 'TIMEOUT_ERROR' | 'CANCELED_ERROR' | 'UNKNOWN_ERROR';
|
|
4
|
+
interface GzFetchErrorOptions {
|
|
5
|
+
type: GzFetchErrorType;
|
|
6
|
+
status?: number;
|
|
7
|
+
responseData?: unknown;
|
|
8
|
+
originalError?: unknown;
|
|
9
|
+
}
|
|
10
|
+
declare class GzFetchError extends Error {
|
|
11
|
+
readonly type: GzFetchErrorType;
|
|
12
|
+
readonly status: number | undefined;
|
|
13
|
+
readonly responseData?: unknown;
|
|
14
|
+
readonly originalError?: unknown;
|
|
15
|
+
constructor(message: string, options: GzFetchErrorOptions);
|
|
16
|
+
}
|
|
17
|
+
declare function getResponseMessage(data: unknown): string | undefined;
|
|
18
|
+
declare function toGzFetchError(error: unknown): GzFetchError;
|
|
19
|
+
|
|
20
|
+
type GzRequestMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
21
|
+
type GzResponseType = 'json' | 'blob' | 'text';
|
|
22
|
+
type GzRequestParams = Record<string, unknown>;
|
|
23
|
+
type GzRequestHeaders = Record<string, string | number | boolean | null> | AxiosHeaders;
|
|
24
|
+
interface GzRequestConfig<TRequestParams = unknown> {
|
|
25
|
+
url: string;
|
|
26
|
+
method: GzRequestMethod;
|
|
27
|
+
params?: TRequestParams;
|
|
28
|
+
headers?: Record<string, string>;
|
|
29
|
+
timeout?: number;
|
|
30
|
+
showErrorMessage?: boolean;
|
|
31
|
+
skipAuth?: boolean;
|
|
32
|
+
responseType?: GzResponseType;
|
|
33
|
+
signal?: AbortSignal;
|
|
34
|
+
}
|
|
35
|
+
interface GzInternalRequestConfig extends Omit<GzRequestConfig<unknown>, 'headers'> {
|
|
36
|
+
headers?: GzRequestHeaders;
|
|
37
|
+
}
|
|
38
|
+
type GzRequestOptions<TRequestParams = unknown> = Omit<GzRequestConfig<TRequestParams>, 'url' | 'method'>;
|
|
39
|
+
interface GzResponseContext {
|
|
40
|
+
config: Readonly<GzInternalRequestConfig>;
|
|
41
|
+
status: number;
|
|
42
|
+
headers: Readonly<Record<string, string>>;
|
|
43
|
+
}
|
|
44
|
+
interface GzErrorContext {
|
|
45
|
+
config: Readonly<GzInternalRequestConfig>;
|
|
46
|
+
}
|
|
47
|
+
interface GzFetchMiddleware {
|
|
48
|
+
onRequest?: (config: GzInternalRequestConfig) => GzInternalRequestConfig | Promise<GzInternalRequestConfig>;
|
|
49
|
+
onResponse?: <T>(data: T, context: GzResponseContext) => T | Promise<T>;
|
|
50
|
+
onError?: (error: GzFetchError, context: GzErrorContext) => void | Promise<void>;
|
|
51
|
+
}
|
|
52
|
+
interface GzFetchAuthOptions {
|
|
53
|
+
headerName?: string;
|
|
54
|
+
formatToken?: (token: string) => string;
|
|
55
|
+
}
|
|
56
|
+
interface CreateGzFetchOptions {
|
|
57
|
+
baseURL?: string;
|
|
58
|
+
timeout?: number;
|
|
59
|
+
getToken?: () => string | undefined | Promise<string | undefined>;
|
|
60
|
+
showErrorMessage?: boolean;
|
|
61
|
+
auth?: GzFetchAuthOptions;
|
|
62
|
+
middlewares?: readonly GzFetchMiddleware[];
|
|
63
|
+
}
|
|
64
|
+
interface GzFetchClient {
|
|
65
|
+
<TResponse, TRequestParams = unknown>(config: GzRequestConfig<TRequestParams>): Promise<TResponse>;
|
|
66
|
+
get<T>(url: string, config?: GzRequestOptions): Promise<T>;
|
|
67
|
+
post<T, TRequestParams = unknown>(url: string, params?: TRequestParams, config?: Omit<GzRequestOptions, 'params'>): Promise<T>;
|
|
68
|
+
put<T, TRequestParams = unknown>(url: string, params?: TRequestParams, config?: Omit<GzRequestOptions, 'params'>): Promise<T>;
|
|
69
|
+
delete<T>(url: string, config?: GzRequestOptions): Promise<T>;
|
|
70
|
+
}
|
|
71
|
+
type GzFetch = GzFetchClient;
|
|
72
|
+
|
|
73
|
+
declare function createGzFetch(options?: CreateGzFetchOptions): GzFetchClient;
|
|
74
|
+
|
|
75
|
+
declare function configureGzFetch(options: CreateGzFetchOptions): void;
|
|
76
|
+
declare function gzFetch<TResponse, TRequestParams = unknown>(config: GzRequestConfig<TRequestParams>): Promise<TResponse>;
|
|
77
|
+
|
|
78
|
+
export { type CreateGzFetchOptions, type GzErrorContext, type GzFetch, type GzFetchAuthOptions, type GzFetchClient, GzFetchError, type GzFetchErrorOptions, type GzFetchErrorType, type GzFetchMiddleware, type GzInternalRequestConfig, type GzRequestConfig, type GzRequestHeaders, type GzRequestMethod, type GzRequestOptions, type GzRequestParams, type GzResponseContext, type GzResponseType, configureGzFetch, createGzFetch, getResponseMessage, gzFetch, toGzFetchError };
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { message } from '@chenhui996/gg-ui';
|
|
2
|
+
import axios, { AxiosError, AxiosHeaders } from 'axios';
|
|
3
|
+
|
|
4
|
+
// src/fetch/client.ts
|
|
5
|
+
var GzFetchError = class extends Error {
|
|
6
|
+
constructor(message2, options) {
|
|
7
|
+
super(message2);
|
|
8
|
+
this.name = "GzFetchError";
|
|
9
|
+
this.type = options.type;
|
|
10
|
+
this.status = options.status;
|
|
11
|
+
this.responseData = options.responseData;
|
|
12
|
+
this.originalError = options.originalError;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
function getResponseMessage(data) {
|
|
16
|
+
if (typeof data !== "object" || data === null || !("msg" in data)) {
|
|
17
|
+
return void 0;
|
|
18
|
+
}
|
|
19
|
+
const { msg } = data;
|
|
20
|
+
return typeof msg === "string" && msg.length > 0 ? msg : void 0;
|
|
21
|
+
}
|
|
22
|
+
var DEFAULT_HTTP_ERROR_MESSAGE = "请求失败,请稍后重试";
|
|
23
|
+
var DEFAULT_NETWORK_ERROR_MESSAGE = "网络异常,请检查网络连接";
|
|
24
|
+
var DEFAULT_TIMEOUT_ERROR_MESSAGE = "请求超时,请稍后重试";
|
|
25
|
+
var DEFAULT_CANCELED_ERROR_MESSAGE = "请求已取消";
|
|
26
|
+
var DEFAULT_UNKNOWN_ERROR_MESSAGE = "请求失败,请稍后重试";
|
|
27
|
+
function toGzFetchError(error) {
|
|
28
|
+
var _a;
|
|
29
|
+
if (error instanceof GzFetchError) {
|
|
30
|
+
return error;
|
|
31
|
+
}
|
|
32
|
+
if (axios.isCancel(error)) {
|
|
33
|
+
return new GzFetchError(DEFAULT_CANCELED_ERROR_MESSAGE, {
|
|
34
|
+
type: "CANCELED_ERROR",
|
|
35
|
+
originalError: error
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
if (axios.isAxiosError(error)) {
|
|
39
|
+
if (error.code === AxiosError.ECONNABORTED || error.code === AxiosError.ETIMEDOUT) {
|
|
40
|
+
return new GzFetchError(DEFAULT_TIMEOUT_ERROR_MESSAGE, {
|
|
41
|
+
type: "TIMEOUT_ERROR",
|
|
42
|
+
originalError: error
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
if (error.response) {
|
|
46
|
+
return new GzFetchError(
|
|
47
|
+
(_a = getResponseMessage(error.response.data)) != null ? _a : DEFAULT_HTTP_ERROR_MESSAGE,
|
|
48
|
+
{
|
|
49
|
+
type: "HTTP_ERROR",
|
|
50
|
+
status: error.response.status,
|
|
51
|
+
responseData: error.response.data,
|
|
52
|
+
originalError: error
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
return new GzFetchError(DEFAULT_NETWORK_ERROR_MESSAGE, {
|
|
57
|
+
type: "NETWORK_ERROR",
|
|
58
|
+
originalError: error
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return new GzFetchError(DEFAULT_UNKNOWN_ERROR_MESSAGE, {
|
|
62
|
+
type: "UNKNOWN_ERROR",
|
|
63
|
+
originalError: error
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// src/fetch/middleware.ts
|
|
68
|
+
async function runRequestMiddlewares(initialConfig, middlewares) {
|
|
69
|
+
let config = initialConfig;
|
|
70
|
+
for (const middleware of middlewares) {
|
|
71
|
+
if (middleware.onRequest) {
|
|
72
|
+
config = await middleware.onRequest(config);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return config;
|
|
76
|
+
}
|
|
77
|
+
async function runResponseMiddlewares(initialData, context, middlewares) {
|
|
78
|
+
let data = initialData;
|
|
79
|
+
for (const middleware of middlewares) {
|
|
80
|
+
if (middleware.onResponse) {
|
|
81
|
+
data = await middleware.onResponse(data, context);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return data;
|
|
85
|
+
}
|
|
86
|
+
async function runErrorMiddlewares(error, context, middlewares) {
|
|
87
|
+
var _a;
|
|
88
|
+
for (const middleware of middlewares) {
|
|
89
|
+
await ((_a = middleware.onError) == null ? void 0 : _a.call(middleware, error, context));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// src/fetch/client.ts
|
|
94
|
+
var DEFAULT_TIMEOUT = 15e3;
|
|
95
|
+
var DEFAULT_AUTH_HEADER = "Authorization";
|
|
96
|
+
function toHeaderRecord(headers) {
|
|
97
|
+
const result = {};
|
|
98
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
99
|
+
if (value !== void 0) {
|
|
100
|
+
result[name] = String(value);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
function createGzFetch(options = {}) {
|
|
106
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
107
|
+
const middlewares = (_a = options.middlewares) != null ? _a : [];
|
|
108
|
+
const showInstanceErrorMessage = (_b = options.showErrorMessage) != null ? _b : true;
|
|
109
|
+
const headerName = (_d = (_c = options.auth) == null ? void 0 : _c.headerName) != null ? _d : DEFAULT_AUTH_HEADER;
|
|
110
|
+
const formatToken = (_f = (_e = options.auth) == null ? void 0 : _e.formatToken) != null ? _f : ((token) => `Bearer ${token}`);
|
|
111
|
+
const instance = axios.create({
|
|
112
|
+
...options.baseURL === void 0 ? {} : { baseURL: options.baseURL },
|
|
113
|
+
timeout: (_g = options.timeout) != null ? _g : DEFAULT_TIMEOUT,
|
|
114
|
+
validateStatus: (status) => status === 200
|
|
115
|
+
});
|
|
116
|
+
async function request(initialConfig) {
|
|
117
|
+
var _a2;
|
|
118
|
+
let currentConfig = initialConfig;
|
|
119
|
+
try {
|
|
120
|
+
const headers = AxiosHeaders.from(initialConfig.headers);
|
|
121
|
+
if (!initialConfig.skipAuth && options.getToken) {
|
|
122
|
+
const token = await options.getToken();
|
|
123
|
+
if (token) {
|
|
124
|
+
headers.set(headerName, formatToken(token));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
currentConfig = await runRequestMiddlewares(
|
|
128
|
+
{ ...initialConfig, headers },
|
|
129
|
+
middlewares
|
|
130
|
+
);
|
|
131
|
+
const usesRequestBody = currentConfig.method === "POST" || currentConfig.method === "PUT";
|
|
132
|
+
const axiosConfig = {
|
|
133
|
+
url: currentConfig.url,
|
|
134
|
+
method: currentConfig.method,
|
|
135
|
+
...!usesRequestBody || currentConfig.params === void 0 ? {} : { data: currentConfig.params },
|
|
136
|
+
...currentConfig.headers === void 0 ? {} : { headers: currentConfig.headers },
|
|
137
|
+
...usesRequestBody || currentConfig.params === void 0 ? {} : { params: currentConfig.params },
|
|
138
|
+
...currentConfig.timeout === void 0 ? {} : { timeout: currentConfig.timeout },
|
|
139
|
+
...currentConfig.responseType === void 0 ? {} : { responseType: currentConfig.responseType },
|
|
140
|
+
...currentConfig.signal === void 0 ? {} : { signal: currentConfig.signal }
|
|
141
|
+
};
|
|
142
|
+
const response = await instance.request(axiosConfig);
|
|
143
|
+
const context = {
|
|
144
|
+
config: currentConfig,
|
|
145
|
+
status: response.status,
|
|
146
|
+
headers: toHeaderRecord(response.headers)
|
|
147
|
+
};
|
|
148
|
+
return await runResponseMiddlewares(response.data, context, middlewares);
|
|
149
|
+
} catch (originalError) {
|
|
150
|
+
const error = toGzFetchError(originalError);
|
|
151
|
+
await runErrorMiddlewares(error, { config: currentConfig }, middlewares);
|
|
152
|
+
const shouldShowError = (_a2 = currentConfig.showErrorMessage) != null ? _a2 : showInstanceErrorMessage;
|
|
153
|
+
if (shouldShowError && error.type !== "CANCELED_ERROR") {
|
|
154
|
+
message.error(error.message);
|
|
155
|
+
}
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const gzFetch2 = ((config) => request(config));
|
|
160
|
+
gzFetch2.get = (url, config = {}) => gzFetch2({ ...config, url, method: "GET" });
|
|
161
|
+
gzFetch2.post = (url, params, config = {}) => gzFetch2({
|
|
162
|
+
...config,
|
|
163
|
+
url,
|
|
164
|
+
method: "POST",
|
|
165
|
+
...params === void 0 ? {} : { params }
|
|
166
|
+
});
|
|
167
|
+
gzFetch2.put = (url, params, config = {}) => gzFetch2({
|
|
168
|
+
...config,
|
|
169
|
+
url,
|
|
170
|
+
method: "PUT",
|
|
171
|
+
...params === void 0 ? {} : { params }
|
|
172
|
+
});
|
|
173
|
+
gzFetch2.delete = (url, config = {}) => gzFetch2({ ...config, url, method: "DELETE" });
|
|
174
|
+
return gzFetch2;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// src/fetch/default-client.ts
|
|
178
|
+
var NOT_CONFIGURED_MESSAGE = "gzFetch 尚未配置,请先在应用初始化时调用 configureGzFetch。";
|
|
179
|
+
var defaultClient;
|
|
180
|
+
function configureGzFetch(options) {
|
|
181
|
+
defaultClient = createGzFetch(options);
|
|
182
|
+
}
|
|
183
|
+
async function gzFetch(config) {
|
|
184
|
+
if (!defaultClient) {
|
|
185
|
+
throw new Error(NOT_CONFIGURED_MESSAGE);
|
|
186
|
+
}
|
|
187
|
+
return defaultClient(config);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export { GzFetchError, configureGzFetch, createGzFetch, getResponseMessage, gzFetch, toGzFetchError };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var ahooks = require('ahooks');
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Object.keys(ahooks).forEach(function (k) {
|
|
8
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
9
|
+
enumerable: true,
|
|
10
|
+
get: function () { return ahooks[k]; }
|
|
11
|
+
});
|
|
12
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from 'ahooks';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from 'ahooks';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from 'ahooks';
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/utils/format-date.ts
|
|
4
|
+
var DEFAULT_DATE_FORMAT = "YYYY-MM-DD HH:mm:ss";
|
|
5
|
+
var INVALID_DATE_PLACEHOLDER = "--";
|
|
6
|
+
function pad(value, length = 2) {
|
|
7
|
+
return String(value).padStart(length, "0");
|
|
8
|
+
}
|
|
9
|
+
function formatDate(value, format = DEFAULT_DATE_FORMAT) {
|
|
10
|
+
if (value === null || value === void 0 || typeof value === "string" && value.trim() === "") {
|
|
11
|
+
return INVALID_DATE_PLACEHOLDER;
|
|
12
|
+
}
|
|
13
|
+
const date = value instanceof Date ? new Date(value.getTime()) : new Date(value);
|
|
14
|
+
if (Number.isNaN(date.getTime())) {
|
|
15
|
+
return INVALID_DATE_PLACEHOLDER;
|
|
16
|
+
}
|
|
17
|
+
const tokens = {
|
|
18
|
+
YYYY: pad(date.getFullYear(), 4),
|
|
19
|
+
MM: pad(date.getMonth() + 1),
|
|
20
|
+
DD: pad(date.getDate()),
|
|
21
|
+
HH: pad(date.getHours()),
|
|
22
|
+
mm: pad(date.getMinutes()),
|
|
23
|
+
ss: pad(date.getSeconds()),
|
|
24
|
+
SSS: pad(date.getMilliseconds(), 3)
|
|
25
|
+
};
|
|
26
|
+
return format.replace(
|
|
27
|
+
/YYYY|SSS|MM|DD|HH|mm|ss/g,
|
|
28
|
+
(token) => {
|
|
29
|
+
var _a;
|
|
30
|
+
return (_a = tokens[token]) != null ? _a : token;
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// src/utils/query.ts
|
|
36
|
+
function isQueryPrimitiveArray(value) {
|
|
37
|
+
return Array.isArray(value);
|
|
38
|
+
}
|
|
39
|
+
function extractQuery(value) {
|
|
40
|
+
var _a, _b;
|
|
41
|
+
const questionMarkIndex = value.indexOf("?");
|
|
42
|
+
if (questionMarkIndex >= 0) {
|
|
43
|
+
return (_a = value.slice(questionMarkIndex + 1).split("#", 1)[0]) != null ? _a : "";
|
|
44
|
+
}
|
|
45
|
+
if (/^[a-z][a-z\d+.-]*:\/\//i.test(value) || value.startsWith("/")) {
|
|
46
|
+
return "";
|
|
47
|
+
}
|
|
48
|
+
return (_b = value.replace(/^\?/, "").split("#", 1)[0]) != null ? _b : "";
|
|
49
|
+
}
|
|
50
|
+
function queryToObject(value) {
|
|
51
|
+
const entries = /* @__PURE__ */ new Map();
|
|
52
|
+
const searchParams = new URLSearchParams(extractQuery(value));
|
|
53
|
+
for (const [key, item] of searchParams) {
|
|
54
|
+
const existingValue = entries.get(key);
|
|
55
|
+
if (existingValue === void 0) {
|
|
56
|
+
entries.set(key, item);
|
|
57
|
+
} else if (Array.isArray(existingValue)) {
|
|
58
|
+
existingValue.push(item);
|
|
59
|
+
} else {
|
|
60
|
+
entries.set(key, [existingValue, item]);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return Object.fromEntries(entries);
|
|
64
|
+
}
|
|
65
|
+
function objectToQuery(value) {
|
|
66
|
+
const searchParams = new URLSearchParams();
|
|
67
|
+
const appendValue = (key, item) => {
|
|
68
|
+
if (item !== void 0) {
|
|
69
|
+
searchParams.append(key, item === null ? "" : String(item));
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
for (const [key, item] of Object.entries(value)) {
|
|
73
|
+
if (isQueryPrimitiveArray(item)) {
|
|
74
|
+
for (const arrayItem of item) {
|
|
75
|
+
appendValue(key, arrayItem);
|
|
76
|
+
}
|
|
77
|
+
} else {
|
|
78
|
+
appendValue(key, item);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return searchParams.toString();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
exports.DEFAULT_DATE_FORMAT = DEFAULT_DATE_FORMAT;
|
|
85
|
+
exports.formatDate = formatDate;
|
|
86
|
+
exports.objectToQuery = objectToQuery;
|
|
87
|
+
exports.queryToObject = queryToObject;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
type DateValue = string | number | Date | null | undefined;
|
|
2
|
+
declare const DEFAULT_DATE_FORMAT = "YYYY-MM-DD HH:mm:ss";
|
|
3
|
+
declare function formatDate(value: DateValue, format?: string): string;
|
|
4
|
+
|
|
5
|
+
type QueryPrimitive = string | number | boolean | null | undefined;
|
|
6
|
+
type QueryValue = QueryPrimitive | readonly QueryPrimitive[];
|
|
7
|
+
type QueryInput = Readonly<Record<string, QueryValue>>;
|
|
8
|
+
type QueryObject = Record<string, string | string[]>;
|
|
9
|
+
declare function queryToObject(value: string): QueryObject;
|
|
10
|
+
declare function objectToQuery(value: QueryInput): string;
|
|
11
|
+
|
|
12
|
+
export { DEFAULT_DATE_FORMAT, type DateValue, type QueryInput, type QueryObject, type QueryPrimitive, type QueryValue, formatDate, objectToQuery, queryToObject };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
type DateValue = string | number | Date | null | undefined;
|
|
2
|
+
declare const DEFAULT_DATE_FORMAT = "YYYY-MM-DD HH:mm:ss";
|
|
3
|
+
declare function formatDate(value: DateValue, format?: string): string;
|
|
4
|
+
|
|
5
|
+
type QueryPrimitive = string | number | boolean | null | undefined;
|
|
6
|
+
type QueryValue = QueryPrimitive | readonly QueryPrimitive[];
|
|
7
|
+
type QueryInput = Readonly<Record<string, QueryValue>>;
|
|
8
|
+
type QueryObject = Record<string, string | string[]>;
|
|
9
|
+
declare function queryToObject(value: string): QueryObject;
|
|
10
|
+
declare function objectToQuery(value: QueryInput): string;
|
|
11
|
+
|
|
12
|
+
export { DEFAULT_DATE_FORMAT, type DateValue, type QueryInput, type QueryObject, type QueryPrimitive, type QueryValue, formatDate, objectToQuery, queryToObject };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// src/utils/format-date.ts
|
|
2
|
+
var DEFAULT_DATE_FORMAT = "YYYY-MM-DD HH:mm:ss";
|
|
3
|
+
var INVALID_DATE_PLACEHOLDER = "--";
|
|
4
|
+
function pad(value, length = 2) {
|
|
5
|
+
return String(value).padStart(length, "0");
|
|
6
|
+
}
|
|
7
|
+
function formatDate(value, format = DEFAULT_DATE_FORMAT) {
|
|
8
|
+
if (value === null || value === void 0 || typeof value === "string" && value.trim() === "") {
|
|
9
|
+
return INVALID_DATE_PLACEHOLDER;
|
|
10
|
+
}
|
|
11
|
+
const date = value instanceof Date ? new Date(value.getTime()) : new Date(value);
|
|
12
|
+
if (Number.isNaN(date.getTime())) {
|
|
13
|
+
return INVALID_DATE_PLACEHOLDER;
|
|
14
|
+
}
|
|
15
|
+
const tokens = {
|
|
16
|
+
YYYY: pad(date.getFullYear(), 4),
|
|
17
|
+
MM: pad(date.getMonth() + 1),
|
|
18
|
+
DD: pad(date.getDate()),
|
|
19
|
+
HH: pad(date.getHours()),
|
|
20
|
+
mm: pad(date.getMinutes()),
|
|
21
|
+
ss: pad(date.getSeconds()),
|
|
22
|
+
SSS: pad(date.getMilliseconds(), 3)
|
|
23
|
+
};
|
|
24
|
+
return format.replace(
|
|
25
|
+
/YYYY|SSS|MM|DD|HH|mm|ss/g,
|
|
26
|
+
(token) => {
|
|
27
|
+
var _a;
|
|
28
|
+
return (_a = tokens[token]) != null ? _a : token;
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// src/utils/query.ts
|
|
34
|
+
function isQueryPrimitiveArray(value) {
|
|
35
|
+
return Array.isArray(value);
|
|
36
|
+
}
|
|
37
|
+
function extractQuery(value) {
|
|
38
|
+
var _a, _b;
|
|
39
|
+
const questionMarkIndex = value.indexOf("?");
|
|
40
|
+
if (questionMarkIndex >= 0) {
|
|
41
|
+
return (_a = value.slice(questionMarkIndex + 1).split("#", 1)[0]) != null ? _a : "";
|
|
42
|
+
}
|
|
43
|
+
if (/^[a-z][a-z\d+.-]*:\/\//i.test(value) || value.startsWith("/")) {
|
|
44
|
+
return "";
|
|
45
|
+
}
|
|
46
|
+
return (_b = value.replace(/^\?/, "").split("#", 1)[0]) != null ? _b : "";
|
|
47
|
+
}
|
|
48
|
+
function queryToObject(value) {
|
|
49
|
+
const entries = /* @__PURE__ */ new Map();
|
|
50
|
+
const searchParams = new URLSearchParams(extractQuery(value));
|
|
51
|
+
for (const [key, item] of searchParams) {
|
|
52
|
+
const existingValue = entries.get(key);
|
|
53
|
+
if (existingValue === void 0) {
|
|
54
|
+
entries.set(key, item);
|
|
55
|
+
} else if (Array.isArray(existingValue)) {
|
|
56
|
+
existingValue.push(item);
|
|
57
|
+
} else {
|
|
58
|
+
entries.set(key, [existingValue, item]);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return Object.fromEntries(entries);
|
|
62
|
+
}
|
|
63
|
+
function objectToQuery(value) {
|
|
64
|
+
const searchParams = new URLSearchParams();
|
|
65
|
+
const appendValue = (key, item) => {
|
|
66
|
+
if (item !== void 0) {
|
|
67
|
+
searchParams.append(key, item === null ? "" : String(item));
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
for (const [key, item] of Object.entries(value)) {
|
|
71
|
+
if (isQueryPrimitiveArray(item)) {
|
|
72
|
+
for (const arrayItem of item) {
|
|
73
|
+
appendValue(key, arrayItem);
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
appendValue(key, item);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return searchParams.toString();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export { DEFAULT_DATE_FORMAT, formatDate, objectToQuery, queryToObject };
|
package/package.json
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gz-fronted/gz-pc",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Shared fetch, hooks and utility primitives for GZ frontend applications.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
12
|
+
"exports": {
|
|
13
|
+
"./fetch": {
|
|
14
|
+
"import": {
|
|
15
|
+
"types": "./dist/fetch/index.d.ts",
|
|
16
|
+
"default": "./dist/fetch/index.js"
|
|
17
|
+
},
|
|
18
|
+
"require": {
|
|
19
|
+
"types": "./dist/fetch/index.d.cts",
|
|
20
|
+
"default": "./dist/fetch/index.cjs"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"./hooks": {
|
|
24
|
+
"import": {
|
|
25
|
+
"types": "./dist/hooks/index.d.ts",
|
|
26
|
+
"default": "./dist/hooks/index.js"
|
|
27
|
+
},
|
|
28
|
+
"require": {
|
|
29
|
+
"types": "./dist/hooks/index.d.cts",
|
|
30
|
+
"default": "./dist/hooks/index.cjs"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"./utils": {
|
|
34
|
+
"import": {
|
|
35
|
+
"types": "./dist/utils/index.d.ts",
|
|
36
|
+
"default": "./dist/utils/index.js"
|
|
37
|
+
},
|
|
38
|
+
"require": {
|
|
39
|
+
"types": "./dist/utils/index.d.cts",
|
|
40
|
+
"default": "./dist/utils/index.cjs"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"./package.json": "./package.json"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "node scripts/build.mjs",
|
|
47
|
+
"lint": "eslint .",
|
|
48
|
+
"typecheck": "tsc --noEmit",
|
|
49
|
+
"test": "vitest run",
|
|
50
|
+
"test:watch": "vitest",
|
|
51
|
+
"verify:exports": "node tests/exports/smoke.mjs",
|
|
52
|
+
"prepublishOnly": "npm run build",
|
|
53
|
+
"prepare": "husky"
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"ahooks": "^3.9.7",
|
|
57
|
+
"axios": "^1.13.6"
|
|
58
|
+
},
|
|
59
|
+
"peerDependencies": {
|
|
60
|
+
"@chenhui996/gg-ui": "^0.1.1",
|
|
61
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@chenhui996/gg-ui": "0.1.1",
|
|
65
|
+
"@commitlint/cli": "21.2.1",
|
|
66
|
+
"@commitlint/config-conventional": "21.2.0",
|
|
67
|
+
"@eslint/js": "9.39.4",
|
|
68
|
+
"@types/node": "24.10.0",
|
|
69
|
+
"@types/react": "19.2.14",
|
|
70
|
+
"@types/react-dom": "19.2.3",
|
|
71
|
+
"eslint": "9.39.4",
|
|
72
|
+
"globals": "16.5.0",
|
|
73
|
+
"husky": "9.1.7",
|
|
74
|
+
"lint-staged": "17.2.0",
|
|
75
|
+
"msw": "2.12.7",
|
|
76
|
+
"prettier": "3.9.6",
|
|
77
|
+
"react": "19.2.4",
|
|
78
|
+
"react-dom": "19.2.4",
|
|
79
|
+
"tsup": "8.5.1",
|
|
80
|
+
"typescript": "5.9.3",
|
|
81
|
+
"typescript-eslint": "8.56.1",
|
|
82
|
+
"vitest": "4.1.4"
|
|
83
|
+
},
|
|
84
|
+
"engines": {
|
|
85
|
+
"node": ">=20"
|
|
86
|
+
},
|
|
87
|
+
"publishConfig": {
|
|
88
|
+
"access": "public"
|
|
89
|
+
},
|
|
90
|
+
"lint-staged": {
|
|
91
|
+
"*.{js,jsx,ts,tsx}": [
|
|
92
|
+
"eslint --fix",
|
|
93
|
+
"prettier --write"
|
|
94
|
+
],
|
|
95
|
+
"*.{json,md,yml,yaml,css,less}": [
|
|
96
|
+
"prettier --write"
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
}
|