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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-dwy",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "description": "项目脚手架与 Claude Code 配置同步工具",
5
5
  "type": "module",
6
6
  "bin": {
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: "@dwydev/ekit 前端工具库速查(v0.7.0)。触发条件:HTTP 请求、dwyeapi 响应契约对接、localStorage/Cookie、表单校验、日期/时区、剪贴板、查询字符串、文件下载、PII 脱敏、Vue composable 工具。"
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
- // ❌ 错误:ekit 不导出底层类型
32
+ // 错误:ekit 不导出底层类型
33
33
  import type { AxiosInstance, IStringifyOptions, CookieAttributes } from '@dwydev/ekit'
34
34
  import { dayjs } from '@dwydev/ekit'
35
35
 
36
- // ✅ 正确:用 ekit 自有契约
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
- 每个模块源码在 `/Users/chances/WebstormProjects/dwy-shared/frontend/ekit/src/{module}/`,函数都很短可以直接读。
55
-
56
- ## 模块索引
57
-
58
- 按需读取对应 references 文件,避免一次性加载全部 API 文档。
59
-
60
- | 模块 | 用途 | 详见 |
61
- |---|---|---|
62
- | `request` | HTTP 客户端(防腐层 + 4 个内置插件 + dwyeapi 响应契约 + 业务错误捕获) | `references/request.md` |
63
- | `storage` | localStorage 封装(@vueuse/core 的 useStorage + 静态 storage 对象) | `references/data.md` § storage |
64
- | `cookie` | Cookie 读写(js-cookie 薄封装 + useCookie) | `references/data.md` § cookie |
65
- | `date` | 日期 / 时间 / 时区格式化(基于 dayjs,不暴露实例) | `references/data.md` § date |
66
- | `qs` | 查询字符串序列化 / 解析(基于 qs) | `references/data.md` § qs |
67
- | `validators` | 表单校验(布尔函数 + zod schema 配合 vee-validate) | `references/form.md` § validators |
68
- | `masking` | PII 数据脱敏(手机/邮箱/身份证/银行卡/姓名/地址等) | `references/form.md` § masking |
69
- | `copy` | 剪贴板(copyText + @vueuse/core 的 useClipboard) | `references/ui.md` § copy |
70
- | `file` | 文件下载 / Blob / 文件大小格式化 | `references/ui.md` § file |
71
- | `hooks` | Vue Composables(全部再导出 @vueuse/core) | `references/ui.md` § hooks |
72
-
73
- ## v0.6.0 迁移要点
74
-
75
- | 旧 | 新 |
76
- |----|----|
77
- | `createRequest(): AxiosInstance` | `createRequest(): HttpClient` |
78
- | `RequestPlugin` | `HttpPlugin` |
79
- | `import { dayjs } from '@dwydev/ekit'` | 用 `now / formatTimestamp / formatInTimezone / formatDateTime` 等函数 |
80
- | `CookieAttributes` | `CookieOptions` |
81
- | `IStringifyOptions / IParseOptions` | `StringifyOptions / ParseOptions` |
82
- | `DownloadOptions.requestInstance: AxiosInstance` | `DownloadOptions.requester: FileRequester` |
83
- | 自写 `useDebounce` 返回可写 `Ref` | VueUse `refDebounced` 返回 `Readonly<Ref>` |
84
- | 自写 `useClipboard` 返回 `{ text, copy, copied, isSupported }`(isSupported 是 boolean) | VueUse 版(isSupported 是 `ComputedRef<boolean>`) |
85
- | `refreshTokenPlugin` 自动重放 | 必须传 `retry: (cfg) => client.request(cfg)` |
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` 直接兼容。