ethan-skill 1.11.0 → 1.13.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/CHANGELOG.md +272 -0
- package/README.md +84 -33
- package/dist/agents/index.d.ts +14 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +37 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/orchestrator.d.ts +10 -0
- package/dist/agents/orchestrator.d.ts.map +1 -0
- package/dist/agents/orchestrator.js +233 -0
- package/dist/agents/orchestrator.js.map +1 -0
- package/dist/agents/orchestrator.test.d.ts +2 -0
- package/dist/agents/orchestrator.test.d.ts.map +1 -0
- package/dist/agents/orchestrator.test.js +151 -0
- package/dist/agents/orchestrator.test.js.map +1 -0
- package/dist/agents/presets.d.ts +16 -0
- package/dist/agents/presets.d.ts.map +1 -0
- package/dist/agents/presets.js +123 -0
- package/dist/agents/presets.js.map +1 -0
- package/dist/agents/types.d.ts +29 -0
- package/dist/agents/types.d.ts.map +1 -0
- package/dist/agents/types.js +7 -0
- package/dist/agents/types.js.map +1 -0
- package/dist/cli/index.js +1168 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/loader/custom-agent-loader.d.ts +13 -0
- package/dist/loader/custom-agent-loader.d.ts.map +1 -0
- package/dist/loader/custom-agent-loader.js +120 -0
- package/dist/loader/custom-agent-loader.js.map +1 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +278 -1
- package/dist/mcp/server.js.map +1 -1
- package/dist/skills/27-tech-debt.d.ts +3 -0
- package/dist/skills/27-tech-debt.d.ts.map +1 -0
- package/dist/skills/27-tech-debt.js +149 -0
- package/dist/skills/27-tech-debt.js.map +1 -0
- package/dist/skills/28-api-mock.d.ts +3 -0
- package/dist/skills/28-api-mock.d.ts.map +1 -0
- package/dist/skills/28-api-mock.js +272 -0
- package/dist/skills/28-api-mock.js.map +1 -0
- package/dist/skills/29-data-migration.d.ts +3 -0
- package/dist/skills/29-data-migration.d.ts.map +1 -0
- package/dist/skills/29-data-migration.js +331 -0
- package/dist/skills/29-data-migration.js.map +1 -0
- package/dist/skills/30-llm-feature.d.ts +3 -0
- package/dist/skills/30-llm-feature.d.ts.map +1 -0
- package/dist/skills/30-llm-feature.js +328 -0
- package/dist/skills/30-llm-feature.js.map +1 -0
- package/dist/skills/31-threat-model.d.ts +3 -0
- package/dist/skills/31-threat-model.d.ts.map +1 -0
- package/dist/skills/31-threat-model.js +240 -0
- package/dist/skills/31-threat-model.js.map +1 -0
- package/dist/skills/32-green-code.d.ts +3 -0
- package/dist/skills/32-green-code.d.ts.map +1 -0
- package/dist/skills/32-green-code.js +346 -0
- package/dist/skills/32-green-code.js.map +1 -0
- package/dist/skills/33-service-catalog.d.ts +3 -0
- package/dist/skills/33-service-catalog.d.ts.map +1 -0
- package/dist/skills/33-service-catalog.js +334 -0
- package/dist/skills/33-service-catalog.js.map +1 -0
- package/dist/skills/34-mobile-review.d.ts +3 -0
- package/dist/skills/34-mobile-review.d.ts.map +1 -0
- package/dist/skills/34-mobile-review.js +390 -0
- package/dist/skills/34-mobile-review.js.map +1 -0
- package/dist/skills/35-data-pipeline.d.ts +3 -0
- package/dist/skills/35-data-pipeline.d.ts.map +1 -0
- package/dist/skills/35-data-pipeline.js +392 -0
- package/dist/skills/35-data-pipeline.js.map +1 -0
- package/dist/skills/36-ml-experiment.d.ts +3 -0
- package/dist/skills/36-ml-experiment.d.ts.map +1 -0
- package/dist/skills/36-ml-experiment.js +415 -0
- package/dist/skills/36-ml-experiment.js.map +1 -0
- package/dist/skills/index.d.ts +10 -0
- package/dist/skills/index.d.ts.map +1 -1
- package/dist/skills/index.js +41 -1
- package/dist/skills/index.js.map +1 -1
- package/dist/skills/pipeline.d.ts.map +1 -1
- package/dist/skills/pipeline.js +35 -0
- package/dist/skills/pipeline.js.map +1 -1
- package/dist/skills/skills.test.js +3 -3
- package/dist/skills/skills.test.js.map +1 -1
- package/package.json +1 -1
- package/rules/claude-code/CLAUDE.md +2963 -3
- package/rules/cline/.clinerules +2805 -2
- package/rules/codebuddy/CODEBUDDY.md +2913 -2
- package/rules/continue/.continuerules +2805 -2
- package/rules/copilot/copilot-instructions.md +2883 -2
- package/rules/cursor/.cursorrules +2952 -2
- package/rules/cursor/smart-flow.mdc +2952 -2
- package/rules/jetbrains/smart-flow.md +2883 -2
- package/rules/lingma/smart-flow.md +2904 -3
- package/rules/windsurf/.windsurf/rules/smart-flow.md +2884 -3
- package/rules/zed/smart-flow.rules +2794 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"27-tech-debt.d.ts","sourceRoot":"","sources":["../../src/skills/27-tech-debt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,eAAO,MAAM,aAAa,EAAE,eAgJ3B,CAAC"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.techDebtSkill = void 0;
|
|
4
|
+
exports.techDebtSkill = {
|
|
5
|
+
id: 'tech-debt',
|
|
6
|
+
name: '技术债追踪',
|
|
7
|
+
nameEn: 'tech_debt',
|
|
8
|
+
order: 27,
|
|
9
|
+
description: '识别、量化、排序技术债,生成偿还路线图与预防机制',
|
|
10
|
+
descriptionEn: 'Identify, quantify and prioritize technical debt, generate repayment roadmap and prevention mechanisms',
|
|
11
|
+
detailDescription: `技术债如同金融债务,越早偿还利息越低。
|
|
12
|
+
本 Skill 帮助团队系统化地识别代码库中的技术债(TODO/FIXME 聚集、高圈复杂度、重复代码、高耦合模块),
|
|
13
|
+
通过"影响 × 修复成本"矩阵量化优先级,生成可落地的 Sprint 偿还计划,
|
|
14
|
+
并建立长效预防机制,让技术债不再悄悄累积。`,
|
|
15
|
+
triggers: [
|
|
16
|
+
'技术债',
|
|
17
|
+
'tech debt',
|
|
18
|
+
'technical debt',
|
|
19
|
+
'技术债追踪',
|
|
20
|
+
'技术债偿还',
|
|
21
|
+
'code smell',
|
|
22
|
+
'代码腐化',
|
|
23
|
+
'@ethan tech-debt',
|
|
24
|
+
'/tech-debt',
|
|
25
|
+
],
|
|
26
|
+
steps: [
|
|
27
|
+
{
|
|
28
|
+
title: '1. 技术债识别与分类',
|
|
29
|
+
content: `扫描代码库,从以下维度识别技术债:
|
|
30
|
+
|
|
31
|
+
**四大技术债类型**
|
|
32
|
+
|
|
33
|
+
| 类型 | 检测手段 | 典型表现 |
|
|
34
|
+
|------|---------|---------|
|
|
35
|
+
| **代码债** | TODO/FIXME/HACK 注释统计 | 注释标记超过 50 个/千行 |
|
|
36
|
+
| **设计债** | 圈复杂度 > 10,类长度 > 500 行 | 上帝类、面条代码、深层继承 |
|
|
37
|
+
| **测试债** | 覆盖率 < 60%,测试缺失模块 | 核心模块无测试,回归靠手工 |
|
|
38
|
+
| **依赖债** | 过时依赖、已弃用 API 调用 | 主版本落后 2+ 个版本 |
|
|
39
|
+
|
|
40
|
+
**识别命令参考**
|
|
41
|
+
\`\`\`bash
|
|
42
|
+
# 统计 TODO/FIXME
|
|
43
|
+
grep -rn "TODO\\|FIXME\\|HACK\\|XXX" src/ --include="*.ts" | wc -l
|
|
44
|
+
|
|
45
|
+
# 查找超长文件
|
|
46
|
+
find src -name "*.ts" | xargs wc -l | sort -rn | head -20
|
|
47
|
+
|
|
48
|
+
# 重复代码检测(需安装 jscpd)
|
|
49
|
+
npx jscpd src/ --min-lines 5 --reporters json
|
|
50
|
+
\`\`\`
|
|
51
|
+
|
|
52
|
+
**输出**:技术债清单(分类 + 位置 + 初步评估)`,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
title: '2. 债务量化评估',
|
|
56
|
+
content: `用"影响度 × 修复成本"矩阵评估每项技术债的优先级:
|
|
57
|
+
|
|
58
|
+
**评分维度**
|
|
59
|
+
|
|
60
|
+
| 维度 | 1分(低) | 3分(中) | 5分(高) |
|
|
61
|
+
|------|---------|---------|---------|
|
|
62
|
+
| **业务影响** | 边缘功能 | 常用功能 | 核心链路 |
|
|
63
|
+
| **出错频率** | 极少触发 | 偶尔出现 | 频繁发生 |
|
|
64
|
+
| **修复成本** | < 0.5天 | 0.5~2天 | > 2天 |
|
|
65
|
+
| **扩散风险** | 独立模块 | 少量依赖 | 多处依赖 |
|
|
66
|
+
|
|
67
|
+
**优先级 = 影响度(业务影响 × 出错频率) ÷ 修复成本**
|
|
68
|
+
|
|
69
|
+
**T恤 Size 对照**
|
|
70
|
+
- 🔴 **Critical(>8分)**:阻碍新功能开发,必须本 Sprint 处理
|
|
71
|
+
- 🟠 **High(5-8分)**:影响团队效率,下个 Sprint 安排
|
|
72
|
+
- 🟡 **Medium(3-5分)**:纳入季度技术改进计划
|
|
73
|
+
- 🟢 **Low(<3分)**:有时间再处理,记录即可
|
|
74
|
+
|
|
75
|
+
**输出**:技术债优先级矩阵(标注 T恤 Size)`,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
title: '3. 偿还路线图',
|
|
79
|
+
content: `按"高影响、低成本"优先原则,制定可落地的偿还计划:
|
|
80
|
+
|
|
81
|
+
**Sprint 规划原则**
|
|
82
|
+
- 每个 Sprint 分配 **15-20% 时间**用于技术债偿还(债务预算)
|
|
83
|
+
- Critical 债务:独立 Story,本 Sprint 必须完成
|
|
84
|
+
- High 债务:与新功能并行,2 个 Sprint 内完成
|
|
85
|
+
- Medium 债务:季度 Hackathon 集中处理
|
|
86
|
+
|
|
87
|
+
**路线图模板**
|
|
88
|
+
\`\`\`
|
|
89
|
+
Sprint N(当前)
|
|
90
|
+
🔴 [Critical] 拆分 UserService 上帝类 → 3个子服务 2天
|
|
91
|
+
🔴 [Critical] 修复登录模块 XXX 标记的并发 Bug 1天
|
|
92
|
+
|
|
93
|
+
Sprint N+1
|
|
94
|
+
🟠 [High] 提升 Payment 模块测试覆盖率至 80% 2天
|
|
95
|
+
🟠 [High] 替换废弃的 axios v0.21 → v1.x 1天
|
|
96
|
+
|
|
97
|
+
Q+1 季度计划
|
|
98
|
+
🟡 [Medium] 重构订单模块(圈复杂度>15的5个函数) 5天
|
|
99
|
+
🟡 [Medium] 清理 87 个 TODO 注释并创建对应 Issue 3天
|
|
100
|
+
\`\`\`
|
|
101
|
+
|
|
102
|
+
**输出**:按 Sprint 排期的技术债偿还路线图`,
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
title: '4. 预防机制',
|
|
106
|
+
content: `建立长效机制,让技术债不再悄悄累积:
|
|
107
|
+
|
|
108
|
+
**自动化门禁(CI/CD 集成)**
|
|
109
|
+
\`\`\`yaml
|
|
110
|
+
# .github/workflows/debt-check.yml
|
|
111
|
+
- name: 技术债扫描
|
|
112
|
+
run: |
|
|
113
|
+
# TODO 数量检查
|
|
114
|
+
TODO_COUNT=$(grep -rn "TODO\\|FIXME" src/ | wc -l)
|
|
115
|
+
if [ $TODO_COUNT -gt $TODO_THRESHOLD ]; then
|
|
116
|
+
echo "❌ TODO 数量超过阈值: $TODO_COUNT > $TODO_THRESHOLD"
|
|
117
|
+
exit 1
|
|
118
|
+
fi
|
|
119
|
+
# 圈复杂度检查(使用 complexity-report 或 ESLint)
|
|
120
|
+
npx eslint src/ --rule '{"complexity": ["error", 10]}'
|
|
121
|
+
\`\`\`
|
|
122
|
+
|
|
123
|
+
**团队规范**
|
|
124
|
+
- **Boy Scout 原则**:离开时让代码比你来时更干净(每次 PR 消灭 1 个技术债)
|
|
125
|
+
- **TODO 转 Issue**:所有 TODO 必须关联 GitHub Issue,不得孤立存在
|
|
126
|
+
- **债务预算制度**:每个 Sprint 强制预留 15% 时间偿还债务
|
|
127
|
+
- **月度债务评审**:每月 Review 技术债清单,更新优先级
|
|
128
|
+
|
|
129
|
+
**技术债仪表盘指标**
|
|
130
|
+
- TODO/FIXME 总数趋势(目标:每月下降 10%)
|
|
131
|
+
- 平均圈复杂度(目标:< 8)
|
|
132
|
+
- 测试覆盖率(目标:> 80%)
|
|
133
|
+
- 依赖过时率(目标:0 个 Critical 漏洞)
|
|
134
|
+
|
|
135
|
+
**输出**:CI 门禁配置 + 团队规范文档 + 仪表盘指标定义`,
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
outputFormat: '技术债地图(分级清单)+ 优先级矩阵(影响×成本)+ Sprint 偿还路线图 + CI 门禁配置 + 预防规范',
|
|
139
|
+
examples: [],
|
|
140
|
+
notes: [
|
|
141
|
+
'技术债不是"坏代码",是在特定时间压力下做出的有意识权衡,关键是要记录并计划偿还',
|
|
142
|
+
'建议在项目初期就建立债务预算制度(15-20% Sprint 时间),而不是等到债务失控再处理',
|
|
143
|
+
'优先处理"高影响低成本"的债务,避免在低价值债务上耗费资源',
|
|
144
|
+
'技术债清单应纳入项目文档,让所有成员可见,避免只有架构师知道',
|
|
145
|
+
],
|
|
146
|
+
category: '质量侧',
|
|
147
|
+
nextSkill: 'refactoring',
|
|
148
|
+
};
|
|
149
|
+
//# sourceMappingURL=27-tech-debt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"27-tech-debt.js","sourceRoot":"","sources":["../../src/skills/27-tech-debt.ts"],"names":[],"mappings":";;;AAEa,QAAA,aAAa,GAAoB;IAC5C,EAAE,EAAE,WAAW;IACf,IAAI,EAAE,OAAO;IACb,MAAM,EAAE,WAAW;IACnB,KAAK,EAAE,EAAE;IACT,WAAW,EAAE,0BAA0B;IACvC,aAAa,EAAE,wGAAwG;IACvH,iBAAiB,EAAE;;;sBAGC;IACpB,QAAQ,EAAE;QACR,KAAK;QACL,WAAW;QACX,gBAAgB;QAChB,OAAO;QACP,OAAO;QACP,YAAY;QACZ,MAAM;QACN,kBAAkB;QAClB,YAAY;KACb;IACD,KAAK,EAAE;QACL;YACE,KAAK,EAAE,aAAa;YACpB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;6BAuBc;SACxB;QACD;YACE,KAAK,EAAE,WAAW;YAClB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;4BAmBa;SACvB;QACD;YACE,KAAK,EAAE,UAAU;YACjB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;4BAuBa;SACvB;QACD;YACE,KAAK,EAAE,SAAS;YAChB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kCA6BmB;SAC7B;KACF;IACD,YAAY,EAAE,0DAA0D;IACxE,QAAQ,EAAE,EAAE;IACZ,KAAK,EAAE;QACL,0CAA0C;QAC1C,iDAAiD;QACjD,+BAA+B;QAC/B,gCAAgC;KACjC;IACD,QAAQ,EAAE,KAAK;IACf,SAAS,EAAE,aAAa;CACzB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"28-api-mock.d.ts","sourceRoot":"","sources":["../../src/skills/28-api-mock.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,eAAO,MAAM,YAAY,EAAE,eA2Q1B,CAAC"}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.apiMockSkill = void 0;
|
|
4
|
+
exports.apiMockSkill = {
|
|
5
|
+
id: 'api-mock',
|
|
6
|
+
name: 'API Mock 服务',
|
|
7
|
+
nameEn: 'api_mock',
|
|
8
|
+
order: 28,
|
|
9
|
+
description: '根据接口定义生成 MSW/JSON Server Mock 配置,支持动态数据与边界场景模拟',
|
|
10
|
+
descriptionEn: 'Generate MSW/JSON Server mock configs from API specs, supporting dynamic data and edge case simulation',
|
|
11
|
+
detailDescription: `前后端并行开发的核心痛点是接口未就绪时前端无法推进。
|
|
12
|
+
本 Skill 帮助团队快速搭建 Mock 服务:从 OpenAPI/Swagger 或接口描述生成 MSW handlers、
|
|
13
|
+
JSON Server 配置,集成 faker.js 生成真实感测试数据,并模拟网络错误、超时、边界场景,
|
|
14
|
+
让前端开发完全不依赖后端进度。`,
|
|
15
|
+
triggers: [
|
|
16
|
+
'api mock',
|
|
17
|
+
'mock service',
|
|
18
|
+
'mock 服务',
|
|
19
|
+
'接口 mock',
|
|
20
|
+
'msw',
|
|
21
|
+
'json server',
|
|
22
|
+
'mock 数据',
|
|
23
|
+
'接口模拟',
|
|
24
|
+
'@ethan api-mock',
|
|
25
|
+
'/api-mock',
|
|
26
|
+
],
|
|
27
|
+
steps: [
|
|
28
|
+
{
|
|
29
|
+
title: '1. 接口分析与 Mock 范围确定',
|
|
30
|
+
content: `分析需要 Mock 的接口,建立清单:
|
|
31
|
+
|
|
32
|
+
**接口信息收集**
|
|
33
|
+
- OpenAPI/Swagger 文档路径(如 \`api/swagger.json\`)
|
|
34
|
+
- 手工接口描述(方法、路径、请求/响应结构)
|
|
35
|
+
- 需要模拟的特殊场景(401/403/500/超时/慢响应)
|
|
36
|
+
|
|
37
|
+
**Mock 范围分类**
|
|
38
|
+
\`\`\`
|
|
39
|
+
📋 待 Mock 接口清单
|
|
40
|
+
├── 认证接口
|
|
41
|
+
│ ├── POST /api/auth/login ← 成功 / 密码错误 / 账号锁定
|
|
42
|
+
│ └── POST /api/auth/refresh ← 成功 / token 过期
|
|
43
|
+
├── 用户接口
|
|
44
|
+
│ ├── GET /api/users ← 列表 / 空列表 / 分页
|
|
45
|
+
│ └── GET /api/users/:id ← 成功 / 404
|
|
46
|
+
└── 业务接口
|
|
47
|
+
├── POST /api/orders ← 成功 / 库存不足 / 支付失败
|
|
48
|
+
└── GET /api/orders?status=... ← 各状态过滤
|
|
49
|
+
\`\`\`
|
|
50
|
+
|
|
51
|
+
**输出**:接口 Mock 清单(含边界场景枚举)`,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
title: '2. Mock 方案选型',
|
|
55
|
+
content: `根据项目特点选择最合适的 Mock 方案:
|
|
56
|
+
|
|
57
|
+
**选型矩阵**
|
|
58
|
+
|
|
59
|
+
| 方案 | 适用场景 | 优点 | 缺点 |
|
|
60
|
+
|------|---------|------|------|
|
|
61
|
+
| **MSW(推荐)** | React/Vue 前端项目 | 拦截真实网络请求,零侵入,支持 Jest/Vitest | 需要 Service Worker |
|
|
62
|
+
| **JSON Server** | REST API 快速原型 | 零代码,自动 CRUD | 功能有限,不支持复杂逻辑 |
|
|
63
|
+
| **Mirage.js** | Ember/复杂 SPA | 内置数据库,关系模型支持 | 包较大,配置复杂 |
|
|
64
|
+
| **Nock** | Node.js 单元测试 | 精确控制 HTTP 请求 | 仅限 Node 环境 |
|
|
65
|
+
|
|
66
|
+
**推荐组合**:
|
|
67
|
+
- 开发阶段:**MSW**(浏览器端拦截)
|
|
68
|
+
- 单元测试:**MSW + @mswjs/data**(内存数据库)
|
|
69
|
+
- 快速原型:**JSON Server**(5分钟启动)
|
|
70
|
+
|
|
71
|
+
**输出**:选型决策 + 安装命令`,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
title: '3. MSW handlers 生成',
|
|
75
|
+
content: `生成 MSW(Mock Service Worker)拦截处理器:
|
|
76
|
+
|
|
77
|
+
**安装**
|
|
78
|
+
\`\`\`bash
|
|
79
|
+
npm install msw --save-dev
|
|
80
|
+
npx msw init public/ --save
|
|
81
|
+
\`\`\`
|
|
82
|
+
|
|
83
|
+
**handlers.ts 模板**
|
|
84
|
+
\`\`\`typescript
|
|
85
|
+
// src/mocks/handlers.ts
|
|
86
|
+
import { http, HttpResponse, delay } from 'msw';
|
|
87
|
+
import { faker } from '@faker-js/faker';
|
|
88
|
+
|
|
89
|
+
export const handlers = [
|
|
90
|
+
// ─── 认证接口 ──────────────────────────────────────
|
|
91
|
+
http.post('/api/auth/login', async ({ request }) => {
|
|
92
|
+
const { email, password } = await request.json() as any;
|
|
93
|
+
|
|
94
|
+
// 模拟特殊场景
|
|
95
|
+
if (password === 'wrong') {
|
|
96
|
+
return HttpResponse.json(
|
|
97
|
+
{ code: 401, message: '密码错误' },
|
|
98
|
+
{ status: 401 }
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
if (email === 'locked@test.com') {
|
|
102
|
+
return HttpResponse.json(
|
|
103
|
+
{ code: 423, message: '账号已锁定,请联系管理员' },
|
|
104
|
+
{ status: 423 }
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 正常响应
|
|
109
|
+
return HttpResponse.json({
|
|
110
|
+
token: faker.string.uuid(),
|
|
111
|
+
user: { id: faker.string.uuid(), email, name: faker.person.fullName() },
|
|
112
|
+
});
|
|
113
|
+
}),
|
|
114
|
+
|
|
115
|
+
// ─── 用户列表(分页)──────────────────────────────
|
|
116
|
+
http.get('/api/users', ({ request }) => {
|
|
117
|
+
const url = new URL(request.url);
|
|
118
|
+
const page = Number(url.searchParams.get('page') ?? 1);
|
|
119
|
+
const pageSize = Number(url.searchParams.get('pageSize') ?? 10);
|
|
120
|
+
|
|
121
|
+
const total = 87;
|
|
122
|
+
const items = Array.from({ length: Math.min(pageSize, total - (page - 1) * pageSize) }, () => ({
|
|
123
|
+
id: faker.string.uuid(),
|
|
124
|
+
name: faker.person.fullName(),
|
|
125
|
+
email: faker.internet.email(),
|
|
126
|
+
createdAt: faker.date.past().toISOString(),
|
|
127
|
+
}));
|
|
128
|
+
|
|
129
|
+
return HttpResponse.json({ items, total, page, pageSize });
|
|
130
|
+
}),
|
|
131
|
+
|
|
132
|
+
// ─── 慢响应模拟 ────────────────────────────────────
|
|
133
|
+
http.get('/api/slow-endpoint', async () => {
|
|
134
|
+
await delay(3000); // 模拟 3 秒延迟
|
|
135
|
+
return HttpResponse.json({ data: 'slow response' });
|
|
136
|
+
}),
|
|
137
|
+
|
|
138
|
+
// ─── 网络错误模拟 ──────────────────────────────────
|
|
139
|
+
http.get('/api/network-error', () => {
|
|
140
|
+
return HttpResponse.error(); // 模拟网络断开
|
|
141
|
+
}),
|
|
142
|
+
];
|
|
143
|
+
\`\`\`
|
|
144
|
+
|
|
145
|
+
**browser.ts(浏览器初始化)**
|
|
146
|
+
\`\`\`typescript
|
|
147
|
+
// src/mocks/browser.ts
|
|
148
|
+
import { setupWorker } from 'msw/browser';
|
|
149
|
+
import { handlers } from './handlers';
|
|
150
|
+
export const worker = setupWorker(...handlers);
|
|
151
|
+
\`\`\`
|
|
152
|
+
|
|
153
|
+
**server.ts(Node.js/测试环境)**
|
|
154
|
+
\`\`\`typescript
|
|
155
|
+
// src/mocks/server.ts
|
|
156
|
+
import { setupServer } from 'msw/node';
|
|
157
|
+
import { handlers } from './handlers';
|
|
158
|
+
export const server = setupServer(...handlers);
|
|
159
|
+
\`\`\`
|
|
160
|
+
|
|
161
|
+
**输出**:完整 handlers.ts + browser.ts + server.ts`,
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
title: '4. JSON Server 配置',
|
|
165
|
+
content: `生成 JSON Server 快速 REST Mock 配置:
|
|
166
|
+
|
|
167
|
+
**安装与启动**
|
|
168
|
+
\`\`\`bash
|
|
169
|
+
npm install json-server --save-dev
|
|
170
|
+
# 启动:json-server --watch db.json --port 3001 --routes routes.json
|
|
171
|
+
\`\`\`
|
|
172
|
+
|
|
173
|
+
**db.json(数据库)**
|
|
174
|
+
\`\`\`json
|
|
175
|
+
{
|
|
176
|
+
"users": [
|
|
177
|
+
{ "id": "1", "name": "张三", "email": "zhang@test.com", "role": "admin" },
|
|
178
|
+
{ "id": "2", "name": "李四", "email": "li@test.com", "role": "user" }
|
|
179
|
+
],
|
|
180
|
+
"orders": [
|
|
181
|
+
{ "id": "101", "userId": "1", "status": "pending", "amount": 299.00, "createdAt": "2024-01-15" },
|
|
182
|
+
{ "id": "102", "userId": "2", "status": "completed", "amount": 599.00, "createdAt": "2024-01-16" }
|
|
183
|
+
],
|
|
184
|
+
"products": [
|
|
185
|
+
{ "id": "P001", "name": "商品A", "price": 99.00, "stock": 100 }
|
|
186
|
+
]
|
|
187
|
+
}
|
|
188
|
+
\`\`\`
|
|
189
|
+
|
|
190
|
+
**routes.json(路由重写)**
|
|
191
|
+
\`\`\`json
|
|
192
|
+
{
|
|
193
|
+
"/api/*": "/$1",
|
|
194
|
+
"/api/v1/*": "/$1",
|
|
195
|
+
"/api/users/:id/orders": "/orders?userId=:id"
|
|
196
|
+
}
|
|
197
|
+
\`\`\`
|
|
198
|
+
|
|
199
|
+
**package.json 脚本**
|
|
200
|
+
\`\`\`json
|
|
201
|
+
{
|
|
202
|
+
"scripts": {
|
|
203
|
+
"mock": "json-server --watch src/mocks/db.json --port 3001 --routes src/mocks/routes.json",
|
|
204
|
+
"dev:mock": "concurrently \\"npm run mock\\" \\"npm run dev\\""
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
\`\`\`
|
|
208
|
+
|
|
209
|
+
**输出**:db.json + routes.json + npm 脚本`,
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
title: '5. 动态数据与边界场景策略',
|
|
213
|
+
content: `用 faker.js 生成真实感数据,覆盖各类边界场景:
|
|
214
|
+
|
|
215
|
+
**faker.js 常用生成器速查**
|
|
216
|
+
\`\`\`typescript
|
|
217
|
+
import { faker } from '@faker-js/faker/locale/zh_CN'; // 中文数据
|
|
218
|
+
|
|
219
|
+
// 个人信息
|
|
220
|
+
faker.person.fullName() // 王小明
|
|
221
|
+
faker.internet.email() // user@example.com
|
|
222
|
+
faker.phone.number() // 138-1234-5678
|
|
223
|
+
faker.date.birthdate() // 生日
|
|
224
|
+
|
|
225
|
+
// 地址
|
|
226
|
+
faker.location.city() // 上海
|
|
227
|
+
faker.location.streetAddress() // 延安路 123 号
|
|
228
|
+
|
|
229
|
+
// 业务数据
|
|
230
|
+
faker.string.uuid() // UUID
|
|
231
|
+
faker.number.int({ min:1, max:100 }) // 随机整数
|
|
232
|
+
faker.helpers.arrayElement(['pending', 'active', 'closed']) // 随机枚举
|
|
233
|
+
faker.helpers.multiple(() => faker.person.fullName(), { count: 10 }) // 批量生成
|
|
234
|
+
\`\`\`
|
|
235
|
+
|
|
236
|
+
**边界场景 Checklist**
|
|
237
|
+
- [ ] 空列表(返回 \`{ items: [], total: 0 }\`)
|
|
238
|
+
- [ ] 单条数据(边界分页)
|
|
239
|
+
- [ ] 超长字符串(名称 > 100 字符)
|
|
240
|
+
- [ ] 特殊字符(\`<script>\`、SQL 注入字符串)
|
|
241
|
+
- [ ] 极大数字(金额 = 999999999.99)
|
|
242
|
+
- [ ] 时区边界(UTC+8 vs UTC)
|
|
243
|
+
- [ ] 并发响应竞态(两个请求同时返回)
|
|
244
|
+
- [ ] 401 token 过期 → 自动刷新
|
|
245
|
+
- [ ] 503 服务不可用 → 降级 UI
|
|
246
|
+
|
|
247
|
+
**测试集成(Vitest)**
|
|
248
|
+
\`\`\`typescript
|
|
249
|
+
// setupTests.ts
|
|
250
|
+
import { beforeAll, afterEach, afterAll } from 'vitest';
|
|
251
|
+
import { server } from './mocks/server';
|
|
252
|
+
|
|
253
|
+
beforeAll(() => server.listen({ onUnhandledRequest: 'warn' }));
|
|
254
|
+
afterEach(() => server.resetHandlers()); // 每个测试后重置
|
|
255
|
+
afterAll(() => server.close());
|
|
256
|
+
\`\`\`
|
|
257
|
+
|
|
258
|
+
**输出**:faker 数据工厂函数 + 边界场景 handlers + 测试集成配置`,
|
|
259
|
+
},
|
|
260
|
+
],
|
|
261
|
+
outputFormat: 'Mock 方案选型报告 + MSW handlers.ts + JSON Server db.json/routes.json + faker 数据工厂 + 边界场景 Checklist + 测试集成配置',
|
|
262
|
+
examples: [],
|
|
263
|
+
notes: [
|
|
264
|
+
'MSW 是目前最推荐的前端 Mock 方案,可在浏览器和 Node.js 两种环境无缝切换',
|
|
265
|
+
'Mock 数据应尽量真实(使用 faker.js),避免 "test"/"demo" 等无意义数据干扰开发体验',
|
|
266
|
+
'边界场景的 Mock 与正常流程同等重要,建议用命名 handler(override)覆写特定测试场景',
|
|
267
|
+
'生产环境应通过环境变量完全关闭 Mock,避免意外拦截真实请求',
|
|
268
|
+
],
|
|
269
|
+
category: '执行侧',
|
|
270
|
+
nextSkill: 'unit-testing',
|
|
271
|
+
};
|
|
272
|
+
//# sourceMappingURL=28-api-mock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"28-api-mock.js","sourceRoot":"","sources":["../../src/skills/28-api-mock.ts"],"names":[],"mappings":";;;AAEa,QAAA,YAAY,GAAoB;IAC3C,EAAE,EAAE,UAAU;IACd,IAAI,EAAE,aAAa;IACnB,MAAM,EAAE,UAAU;IAClB,KAAK,EAAE,EAAE;IACT,WAAW,EAAE,gDAAgD;IAC7D,aAAa,EAAE,wGAAwG;IACvH,iBAAiB,EAAE;;;gBAGL;IACd,QAAQ,EAAE;QACR,UAAU;QACV,cAAc;QACd,SAAS;QACT,SAAS;QACT,KAAK;QACL,aAAa;QACb,SAAS;QACT,MAAM;QACN,iBAAiB;QACjB,WAAW;KACZ;IACD,KAAK,EAAE;QACL;YACE,KAAK,EAAE,oBAAoB;YAC3B,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;2BAqBY;SACtB;QACD;YACE,KAAK,EAAE,cAAc;YACrB,OAAO,EAAE;;;;;;;;;;;;;;;;mBAgBI;SACd;QACD;YACE,KAAK,EAAE,oBAAoB;YAC3B,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+CAsFgC;SAC1C;QACD;YACE,KAAK,EAAE,mBAAmB;YAC1B,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sCA4CuB;SACjC;QACD;YACE,KAAK,EAAE,gBAAgB;YACvB,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6CA6C8B;SACxC;KACF;IACD,YAAY,EAAE,wGAAwG;IACtH,QAAQ,EAAE,EAAE;IACZ,KAAK,EAAE;QACL,+CAA+C;QAC/C,yDAAyD;QACzD,sDAAsD;QACtD,iCAAiC;KAClC;IACD,QAAQ,EAAE,KAAK;IACf,SAAS,EAAE,cAAc;CAC1B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"29-data-migration.d.ts","sourceRoot":"","sources":["../../src/skills/29-data-migration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,eAAO,MAAM,kBAAkB,EAAE,eAsUhC,CAAC"}
|