项目:my-ts-axios | 版本:v1.1 | 生成日期:2026-10-06 | 依据:源码分析
技术债务的清点标准是"源码可验证":每一条债务都必须给出位置(文件与行号)、证据(代码或全库检索结论)、影响(谁会在什么场景被坑)与建议(修或不修、为什么)。第 03 篇第 4 节的差异清单是从"使用者视角"列官方差异;本篇从"维护者视角"列债务,两条清单有交叉但视角不同——差异是"与官方不同",债务是"无论是否对齐官方都值得处理"。
分级口径:A 级(语义债务)——声明与行为不一致,使用者会被误导;B 级(一致性债务)——同类行为在不同位置表现不同;C 级(工程债务)——工具链与流程的缺口;D 级(记录性债务)——只需文档显式登记、不建议改代码的取舍。当前没有发现 A 级中"行为错误"类的问题——这个库的债务大多是"声明超前于实现"或"三端不一致",没有发现逻辑错误,这与它 90% 覆盖率门槛下的测试密度是相符的。
条目 1:transitional.silentJSONParsing 声明且默认 true,但全库无消费点。
位置:src/types/index.ts 第 134 行(类型声明)、src/defaults.ts 第 70 行(默认值 true)。
证据:全库检索 silentJSONParsing 仅命中上述两处声明;实际控制响应解析的是 transitional.forcedJSONParsing(src/core/dispatchRequest.ts 第 3948 行)。且本库 silentJSONParsing 想表达的行为("JSON 解析失败时是否静默")与 forcedJSONParsing 已实现的语义("是否执行响应转换管道")粒度不同——官方 axios 中 silentJSONParsing=false 时解析失败会抛 24 行 catch 空块)。
影响:调用方配置 ERR_BAD_RESPONSE,本库解析失败恒静默(src/helpers/data.ts 第 15silentJSONParsing: false 期望"解析失败报错"却静默无效——A 级误导。
建议:二选一。补实现:在默认 transformResponse 里读取 transitional.silentJSONParsing === false 时把解析失败转为 ERR_BAD_RESPONSE 错误(错误来源与测试落点参照《11》SOP 四);或删除声明并在差异清单登记"不支持"。按"学习版对齐官方"的定位,建议补实现——它同时能消解《08》第 5.1 节的静默失败隐患。
条目 2:FormSerializerOptions.metaTokens 声明未消费。
位置:src/types/index.ts 第 122 行。证据:src/helpers/formData.ts(toFormData/formToJSON 全文)无任何读取;官方语义是"key 结尾的键不加 [] 后缀",需要改写 walk 的数组路径生成(第 85~90 行)才能表达。
影响:低——仅在使用 postForm + formSerializer.metaTokens 时无效;dots/indexes/visitor 三个兄弟选项都已实现并有测试(test/core/AxiosForm.spec.ts、test/helpers/formData.spec.ts),对比之下更显突兀。
建议:补实现成本低(walk 的 childPath 判定加一个 metaTokens 分支),或删除声明。倾向补实现,保持 formSerializer 选项面完整。
条目 3:FormSerializerOptions.maxDepth 声明未消费。
位置:src/types/index.ts 第 130 行。证据:formData.ts 的 walk(第 67~100 行)无深度参数、无深度计数。官方语义是"序列化嵌套的最大深度"。
影响:极低;但深嵌套对象在无深度限制下会生成超长路径键,配合不可信输入有生成异常长 FormData 键的理论面(《08》未将其列为隐患正是因为键长度有 HTTP 层护栏)。
建议:删除声明即可——学习版不需要为防御一个低危场景增加分支;若补,walk 增加 depth 参数并在超限时跳过子树。
条目 4:ERR_BAD_OPTION_VALUE、ERR_BAD_OPTION、ERR_DEPRECATED 三个码声明未使用。
位置:src/helpers/error.ts 第 4、5、10 行。证据:全库检索 createError/new AxiosError 的全部调用点(《04》第 1.3 节的错误来源表已穷举),无一使用这三个码。
证据性质说明:这是"对齐官方码表"的有意占位(官方在参数校验路径使用它们),但本库的参数校验路径本身也不存在——例如 adapter 非法时抛的是普通 Error 而非 ERR_BAD_OPTION 的 AxiosError(src/core/getAdapter.ts 第 45~52 行)。
影响:调用方按码表写分流逻辑不会出错(码不会出现),但码表与行为的对应关系是虚的;AxiosErrorCodes 的 13 个码里 3 个是"死码"。
建议:两个方向任选其一并保持一致——方向一(对齐优先):让 getAdapter 的失败走 createError('...', config, ERR_BAD_OPTION),formSerializer/paramsSerializer 的非法选项校验用 ERR_BAD_OPTION_VALUE,并为"已废弃配置被使用"设计 ERR_DEPRECATED 路径;方向二(最小化):删除三个死码,码表收缩到 10 个。倾向方向一,因为它顺带修复了"getAdapter 失败抛普通 Error、调用方无法用统一 catch 分流"的边界(《08》第 7 节未列但同源)。
条目 5:xhr 适配器就地修改 config.headers,http/fetch 浅拷贝。
位置:src/core/adapters/xhr.ts 第 16 行(解构得到同一引用)、第 137 行(FormData 删 Content-Type)、第 148 行(XSRF 头写入)、第 152 行(Authorization 写入);对照 src/core/adapters/http.ts 第 134 行、src/core/adapters/fetch.ts 第 138 行(均先 { ...headers } 浅拷贝)。
证据:三处代码可直接对照;《04》第 2 节与《12》第 2.1 节已给出行为推论(只有 xhr 的修改会出现在 response.config.headers)。
影响:跨端代码若依赖 response.config.headers 审计实际发送的头,浏览器与 Node 结论不同;更深一层,xhr 的就地修改还会污染调用方在并发场景复用的 config 对象(请求 A 删除的 Content-Type 会影响共享同一 headers 对象的请求 B——浅拷贝派无此问题)。
建议:统一为浅拷贝派——xhr 适配器开头 const headers = { ...(config.headers || {}) },后续修改作用于副本。改动极小,但会改变 response.config.headers 的可见行为(现有测试若依赖 xhr 回写需同步调整,test/xsrf.spec.ts 断言的是 jasmine-ajax 捕获的 requestHeaders,不受影响)。这是本清单里收益/成本比最高的一条。
条目 6:_requestForm 绕过 defaults.env.FormData。
位置:src/core/Axios.ts 第 193 行 toFormData(data, undefined, config && config.formSerializer),src/helpers/formData.ts 第 22 行 form || new FormData() 回退全局构造器。
证据:默认请求转换器(src/defaults.ts 第 42~44 行)显式优先 defaults.env.FormData 并有测试锁定(test/miscAlignment.spec.ts"multipart 自动序列化优先使用 defaults.env.FormData 注入的构造器");_requestForm 的第二个参数传 undefined,注入的构造器不会被使用。⚠️ 待确认:源码注释未说明是否有意。
影响:注入自定义 FormData 实现(测试替身、polyfill 环境)时,postForm 系与"headers 声明 multipart 的普通 post"两条路径行为不一致。
建议:改为 toFormData(data, this.defaults.env?.FormData ? new (this.defaults.env.FormData)() : undefined, ...),或给 toFormData 增加 Ctor 选项统一两处构造器解析逻辑;并在 AxiosForm.spec.ts 补注入一致性用例。
条目 7:ERR_PROXY 为本项目自增码,官方不存在。
位置:src/helpers/error.ts 第 16 行(码表定义)、src/core/adapters/http.ts 第 486 行(CONNECT 状态非 200)与第 524 行(CONNECT error)。
证据:《03》差异清单第 9 条已登记;官方 axios 对代理失败不设专门码(连接失败表现为 ERR_NETWORK 系或普通 Error)。
影响:依赖 error.code 的分流代码(如统一重试逻辑按官方码表白名单)会漏判 ERR_PROXY;反之它让"代理失败"可被精确识别,是自增码的收益面。
建议:保留但登记——它是有价值的细分(代理失败与网络失败的重试策略不同),需要在 README 与类型注释中显式声明"本项目专有码"。不建议改为 ERR_NETWORK:那会丢失信息。
条目 8:CI 已存在但有四个缺口(修正第 06 篇"无 CI ⚠️ 待确认")。
位置:.github/workflows/ci.yml。证据:该文件真实存在(Node 20/22 矩阵,lint → test → build → examples:build,npm ci 时 HUSKY: 0)。第 06 篇成文时的存疑已在本次回源中闭环(该篇相应条目已同步修订),本条目按"存在但缺口"重新登记:
缺口一,无依赖审计步骤(无 npm audit/dependabot,见《08》第 6 节);缺口二,ESM 冒烟是文本级(scripts/smoke-dist.cjs 第 41~47 行用正则检查 export default,未运行时加载);缺口三,无覆盖率产物化(报告仅留本地,PR 页不可见);缺口四,无发布自动化(prepublishOnly 链路完整但依赖手动 publish,无 tag 触发的 release workflow)。
建议:四个缺口都是"加配置"量级——CI 追加 audit 步骤;smoke-dist 改写为 mjs 并用动态 import() 加载 ESM 产物;CI 接 Codecov 或 job summary;增加 tag → npm publish 的 release workflow(把 smoke-dist 的通过记录留存在 Release 里)。
| 条目 | 位置 | 说明与建议 |
|---|---|---|
AxiosHeaders 为官方方法子集(无 forEach/match/asType/setCookie/getSetCookie/accessor) | src/core/AxiosHeaders.ts 全文 139 行 vs 官方实现 | 有意取舍(《02》第 7 节),方法面按"链路实际需要"收敛;登记于《03》差异清单第 6 条。若未来响应 headers AxiosHeaders 化(见条目 10)再扩方法面 |
Cancel 与 CanceledError 是两个独立类(官方 v1 中 Cancel 是 CanceledError 别名) | src/cancel/Cancel.ts、src/cancel/CanceledError.ts | isCancel 已统一判定语义(两轨兼容),仅 instanceof 行为不同(《12》2.4 节);改为别名会破坏"旧版取消原因"教学价值,建议保留并维持文档 |
| headers 内部以普通对象流转,响应 headers 是小写键普通对象(官方 v1 是 AxiosHeaders 实例) | 三适配器组装 response 处(xhr.ts 第 79 行、http.ts 第 233 行、fetch.ts 第 220 行) | 全库一致性良好,仅与官方不同;迁移成本高(涉及三适配器与全部响应测试),登记即可 |
InterceptorOptions(runWhen/synchronous)定义在 src/core/InterceptorManager.ts 而未进公共类型面 | InterceptorManager.ts 第 3~6 行;types/index.ts 的 AxiosInterceptorManager.use 签名不带 options 参数 | 使用者写 TS 时传第三参会有类型报错(尽管运行时支持)。低成本修复:把 options 参数并入 types/index.ts 的 use 签名。倾向修复,属类型面遗漏而非取舍 |
tsconfig moduleResolution: node + module: ES2015(未用 bundler 解析) | tsconfig.json | 对库产物无影响(构建走 rollup + tsc 声明),仅影响 IDE 解析现代导出语法;升级为 bundler 需同步评估 moduleResolution 与 lib 组合,收益低,登记 |
dist/index.esm.js 以 .js 后缀发布且 package.json 无 "type" 字段 | package.json、rollup.config.mjs | 打包器经 exports 条件消费不受影响;Node 原生 ESM 直载依赖语法自动探测(Node 22+)。改名为 .mjs 是零风险加固,可做 |
源码内 istanbul ignore 注释豁免的分支 | xhr.ts 第 171 行、fetch.ts 第 200 行、error.ts 第 27 行等 | 受控豁免(3 处),均有"结构上不可达"的理由;登记防止滥用扩散 |
| fetch 适配器无上传进度 | fetch.ts(onUploadProgress 解构后未消费) | 平台 API 限制(需要双 body 技巧,复杂度不值);登记于《03》差异清单第 4 条与《08》第 7 节 |
把第 2 节的条目按依赖关系排成三个阶段,每阶段给出验收标准:
阶段一:一致性收敛(约 1~2 天工作量,无行为契约破坏)
.mjs。npm test 全绿(含新增的一致性用例),三适配器在"修改是否回写 config.headers"与"FormData 构造器来源"两个维度行为一致,TS 用户传 runWhen/synchronous 无类型报错。response.config.headers 的使用(src 与 test),当前检索结论是仅 xhr 适配器自身与测试,风险可控。阶段二:语义补全(约 2~3 天,伴随新测试)
AxiosErrorCodes 每个码都有至少一个产生点与测试(可用脚本断言"码表 ⊆ 错误来源表");transitional 三项全部被消费。阶段三:工程增强(独立进行,随时可插队)
远期方向(按需评估,不排期):响应 headers 的 AxiosHeaders 化(官方 v1 形态,触碰三适配器与全部响应测试,仅当需要 getSetCookie 等官方 API 时启动);HTTP/2 适配器(走 SOP 一,注意 http2 是 Node 内置模块需同步 shim 与 EXTERNAL);请求级中间件缓存(mergeConfig/flattenHeaders 的实例级缓存,《10》第 7 节建议 2/3)。
这份债务清单的总体面貌是健康的:13 个条目里没有行为错误,A 级债务全部是"声明超前于实现"或"信息登记不足",B 级是两处可定位到行号的三端不一致,C 级是四个配置量级的 CI 缺口。更值得注意的是源码自带的"债务自白"传统——smoke-dist.cjs 头部的坏包事故记录、http.ts 第 105107 与 305311 行的历史修复注释、util.ts 的 esbuild 漏拷注释、settle.spec.ts 的错误码反转回归注释——这些注释让后来者不需要考古就能理解"为什么代码长这样"。维持这个传统(每次修复留一行注释 + 一个回归用例)比清空这份清单更重要;清单本身应随阶段一/二的完成而收缩,收缩后的空位留给未来新增的登记。