create-dwy 0.6.2 → 0.6.3
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/package.json
CHANGED
package/src/sync.js
CHANGED
|
@@ -205,6 +205,22 @@ export async function syncClaude() {
|
|
|
205
205
|
process.exit(1)
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
+
// 如果当前目录没有 .claude,先确认是否在此创建
|
|
209
|
+
if (!await fs.pathExists(projectTargetDir)) {
|
|
210
|
+
const { shouldCreate } = await inquirer.prompt([
|
|
211
|
+
{
|
|
212
|
+
type: 'confirm',
|
|
213
|
+
name: 'shouldCreate',
|
|
214
|
+
message: `当前目录 ${chalk.yellow(process.cwd())} 未找到 .claude 目录,是否在此创建?`,
|
|
215
|
+
default: false,
|
|
216
|
+
},
|
|
217
|
+
])
|
|
218
|
+
if (!shouldCreate) {
|
|
219
|
+
console.log(chalk.yellow('\n已取消同步。'))
|
|
220
|
+
return
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
208
224
|
// 扫描可用资源
|
|
209
225
|
const skills = await scanSkills(sourceDir)
|
|
210
226
|
const rules = await scanRules(sourceDir)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: dwy-ekit
|
|
3
|
-
description: "
|
|
3
|
+
description: "【强制速查】只要涉及以下任何主题,必须调用本 skill,无论用户是否说ekit:HTTP 请求 / axios 封装 / token 刷新 / dwyeapi 响应契约对接 / localStorage / Cookie / useStorage / useCookie / 日期格式化 / 时区处理 / dayjs / 表单校验 / zod schema / vee-validate / PII 脱敏 / 剪贴板 / useClipboard / 文件下载 / Blob / 查询字符串 / qs / Vue composables(debounce / throttle / click-outside / event-listener / window-size / media-query / intersection-observer / resize-observer / useFormPersist)。本 skill 是 @dwydev/ekit(v0.7.0)的唯一权威来源,禁止绕过 ekit 直接手写底层 API。"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# @dwydev/ekit 工具库速查(v0.7.0)
|
|
@@ -29,11 +29,11 @@ import {
|
|
|
29
29
|
ekit 对外类型 **不允许** 出现底层库类名。下面这些导入 **会失败** —— 因为 ekit 不再 re-export:
|
|
30
30
|
|
|
31
31
|
```ts
|
|
32
|
-
//
|
|
32
|
+
// 错误:ekit 不导出底层类型
|
|
33
33
|
import type { AxiosInstance, IStringifyOptions, CookieAttributes } from '@dwydev/ekit'
|
|
34
34
|
import { dayjs } from '@dwydev/ekit'
|
|
35
35
|
|
|
36
|
-
//
|
|
36
|
+
// 正确:用 ekit 自有契约
|
|
37
37
|
import type { HttpClient, StringifyOptions, CookieOptions } from '@dwydev/ekit'
|
|
38
38
|
import { now, formatTimestamp } from '@dwydev/ekit' // 不需要 dayjs 实例
|
|
39
39
|
```
|
|
@@ -49,37 +49,65 @@ import { now, formatTimestamp } from '@dwydev/ekit' // 不需要 dayjs 实例
|
|
|
49
49
|
| `document.addEventListener('click', ...)` | `useEventListener()` |
|
|
50
50
|
| 手写 axios 实例 | `createRequest()` |
|
|
51
51
|
|
|
52
|
-
##
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
52
|
+
## 模块速查
|
|
53
|
+
|
|
54
|
+
### request — HTTP 客户端 + 4 内置插件 + dwyeapi 契约
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import {
|
|
58
|
+
createRequest, tokenPlugin, headerPlugin, unwrapPlugin, refreshTokenPlugin,
|
|
59
|
+
SUCCESS_CODE, isApiBusinessError, extractValidationErrors,
|
|
60
|
+
} from '@dwydev/ekit'
|
|
61
|
+
import type {
|
|
62
|
+
HttpClient, HttpConfig, HttpResponse, HttpError, HttpPlugin,
|
|
63
|
+
CreateRequestOptions, ApiResponse, PageData,
|
|
64
|
+
} from '@dwydev/ekit'
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**createRequest**
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
const http: HttpClient = createRequest({
|
|
71
|
+
baseURL: '/api',
|
|
72
|
+
timeout: 30000,
|
|
73
|
+
headers: { 'X-Client': 'web' },
|
|
74
|
+
plugins: [tokenPlugin({ getToken: () => localStorage.getItem('token') }), unwrapPlugin()],
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
const res = await http.get<User>('/users/1') // res: HttpResponse<User>
|
|
78
|
+
const list = await http.get<PageData<User>>('/users')
|
|
79
|
+
list.data.items // User[]
|
|
80
|
+
list.data.total // number
|
|
81
|
+
list.data.page // number
|
|
82
|
+
list.data.page_size // number
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**内置插件**
|
|
86
|
+
|
|
87
|
+
| 插件 | 参数 | 说明 |
|
|
88
|
+
|------|------|------|
|
|
89
|
+
| `tokenPlugin` | `{ getToken: () => string | null }` | 注入 `Authorization: Bearer <token>` |
|
|
90
|
+
| `headerPlugin` | `{ name, getValue }` | 动态自定义 header |
|
|
91
|
+
| `unwrapPlugin` | 无 | 解包 dwyeapi `{ code, message, data, timestamp }`,业务失败时 reject |
|
|
92
|
+
| `refreshTokenPlugin` | `{ getRefreshToken, refreshFn, onRefreshFail, retry, isLoginUrl? }` | 401 自动刷新并重放;`retry: (cfg) => http.request(cfg)` 必填 |
|
|
93
|
+
|
|
94
|
+
**响应契约 & 错误捕获**
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
// unwrap 后 data 是纯业务数据(单体)或 PageData<T>(分页)
|
|
98
|
+
const res = await http.get<User>('/users/1') // res.data: User
|
|
99
|
+
|
|
100
|
+
// 捕获业务错误
|
|
101
|
+
try {
|
|
102
|
+
await http.post('/orders', body)
|
|
103
|
+
} catch (err) {
|
|
104
|
+
if (!isApiBusinessError(err)) { message.error('网络异常'); return }
|
|
105
|
+
switch (err.businessCode) {
|
|
106
|
+
case 'INSUFFICIENT_BALANCE': showRecharge(); break
|
|
107
|
+
case 'VALIDATION_ERROR': form.setErrors(extractValidationErrors(err)); break
|
|
108
|
+
default: message.error(err.message)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`extractValidationErrors` 剥离 `body. / query. / path.` 前缀,返回 `{ field: message }` 扁平对象,与 vee-validate 的 `form.setErrors` 直接兼容。
|