首页/新闻资讯/正文详情

gsd-core CommandRoutingHub 错误结果类型化改造:从 `errorKind` 平铺字段到判别联合(Discriminated Union)

发布时间:2026/9/25 10:23:19 来源:云帆数科 栏目:资讯中心
gsd-core CommandRoutingHub 错误结果类型化改造:从 `errorKind` 平铺字段到判别联合(Discriminated Union)
【免费下载链接】gsd-coreGit. Ship. Done - Core项目地址https://gitcode.com/gh_mirrors/ge/gsd-core点击查看免费下载导读本文围绕 gsd-core 中CommandRoutingHub命令路由枢纽的一次内部重构展开变更集 176-typed-result-discriminated-union 将 Hub 分发的错误结果从一个errorKind字符串加泛化message/details逃生舱重构为按错误种类携带各自类型化负载的判别联合。读完本文你将掌握四种错误变体的精确字段契约、四个工厂函数的正确用法、Hub 对非法返回的运行时校验机制以及下游命令路由器如 phase-command-router.cts如何通过kind判别做分支消费。一、背景Hub 从设计到类型化收缩的演进脉络CommandRoutingHub是 gsd-core 中所有命令族phase、state、verify、validate、init 等的统一分发接缝。它的诞生背景记录在 ADR-0012 中当时七个*-command-router.cjs文件各自重复检查模式 → 调用处理器 → 映射错误的三段式分发逻辑策略变更需要同时改多处。ADR-0012 因此引入 Hub将**无抛出契约no-throw contract与封闭错误分类closed error taxonomy**集中到一个模块。随后 ADR-0174 将 SDK 双运行时折叠为单一 TypeScript 源码树Hub 被简化删除mode、sdkLoader、cjsRegistry与两个 SDK 错误种类只保留四项跨切面关注点统一错误契约、manifest 解析、参数形状归一、可观测性。正是这次单运行时折叠让 Hub 的错误类型从 ADR-0012 时期的{ ok: false, errorKind, message, details? }平铺结构有了收紧为紧类型判别联合的前提。变更集 #176 正是这一演进的关键落地步骤它将errorKind字段重命名为kind并为每种错误变体定义专属的类型化负载移除 Hub 发错误结果中泛化的message/details逃生舱。从源码结构看这次重构属于SDK-internal内部契约调整见 变更集注释不触碰公开文档表面但它是所有命令路由器与调用方必须跟随迁移的硬契约变更。二、核心变更从errorKind平铺字段到kind判别联合2.1 变更前后对照重构前ADR-0012 时代的错误结果形态Result { ok: true, data } | { ok: false, errorKind, message, details? }重构后#176 落地于 src/command-routing-hub.cts的形态type ResultT | { ok: true; data: T } | { ok: false; kind: UnknownCommand; command: string } | { ok: false; kind: InvalidArgs; arg: string; reason: string; exitReason?: string } | { ok: false; kind: HandlerRefusal; reason: string } | { ok: false; kind: HandlerFailure; message: string; cause?: Error };两处关键语义变化判别字段重命名errorKind→kind。kind既是运行时判别属性也是 TypeScript 的字面量类型让switch/if分支在编译期就能获得类型收窄narrowing。按变体携带类型化负载每个ok: false变体只带自己真正需要的字段泛化的message与details逃生舱被移除——UnknownCommand不再有messageInvalidArgs不再有details。2.2 封闭错误种类枚举四个错误种类以冻结对象ERROR_KINDS导出src/command-routing-hub.cts#L61-L70const ERROR_KINDS Object.freeze({ UnknownCommand: UnknownCommand, // 请求的 family/subcommand 组合不在 manifest 中 InvalidArgs: InvalidArgs, // 处理器在执行前拒绝了传入参数 HandlerRefusal: HandlerRefusal, // CJS 处理器显式返回拒绝如不支持的子命令 HandlerFailure: HandlerFailure, // 处理器抛出了意外异常 } as const);测试 command-routing-hub.test.cjs#L511-L516 专门断言ERROR_KINDS的值是与其键名一致的稳定字符串常量保证调用方switch (result.kind)时与ERROR_KINDS.X一一对应不依赖裸字符串字面量。三、四种错误变体详解3.1UnknownCommand— 未知命令interface UnknownCommandResult { ok: false; kind: UnknownCommand; command: string; }语义请求的 family/subcommand 组合不在 manifest 或 CJS registry 中。负载仅command非空字符串例如phase或phase nonexistent。触发路径manifest 缺失 family、manifest 不含 subcommand、registry 缺 family、registry 缺 handler 四种情况都会返回该变体见 src/command-routing-hub.cts#L375-L401。严格键集测试断言其结果恰好为[command, kind, ok]三个键不得多出message/detailstests/command-routing-hub.test.cjs#L380-L395。3.2InvalidArgs— 参数校验失败interface InvalidArgsResult { ok: false; kind: InvalidArgs; arg: string; reason: string; exitReason?: string; // 可选见 3.5 }语义处理器在执行前拒绝了传入参数参数缺失、不支持、类型错误等。负载arg指明是哪个参数如--dry-run、--phasereason给出人类可读的解释如phase insert does not support --dry-run。严格键集二参形式的结果恰好为[arg, kind, ok, reason]tests/command-routing-hub.test.cjs#L423-L446。3.3HandlerRefusal— 处理器显式拒绝interface HandlerRefusalResult { ok: false; kind: HandlerRefusal; reason: string; }语义处理器有意识地拒绝执行如子命令在当前路由器语境下不被支持区别于抛异常的HandlerFailure。负载仅reason字符串。严格键集恰好[kind, ok, reason]tests/command-routing-hub.test.cjs#L449-L470。3.4HandlerFailure— 处理器异常interface HandlerFailureResult { ok: false; kind: HandlerFailure; message: string; cause?: Error; }语义处理器在分发过程中抛出意外异常Hub 捕获后转换为结构化错误。负载message为人类可读失败描述cause为原始抛出的 Error若存在。非 Error 抛出值处理若处理器抛出字符串或普通对象工厂会用new Error(non-Error cause: ...)包装并将原值挂在wrapper.thrown上保证下游.cause.stack不会静默返回undefined见 src/command-routing-hub.cts#L159-L177。严格键集有 cause 时恰好[cause, kind, message, ok]tests/command-routing-hub.test.cjs#L473-L491。3.5InvalidArgs的可选扩展exitReason?ADR-0174 修订 #1642 为InvalidArgs增加了可选字段exitReason它单独携带一个ERROR_REASON枚举值如ERROR_REASON.USAGE与reason人类可读文本分离。这样从error(msg, ERROR_REASON.USAGE)直调迁移到makeInvalidArgs(...)Result 的路由器能在GSD_JSON_ERRORS1的 JSON 错误信封中保留类型化 reason供 CLI 测试与集成 harness 消费。工厂在第三个参数为undefined或空字符串时不写入该键维持严格键集不变量src/command-routing-hub.cts#L139-L147。测试 command-routing-hub.test.cjs#L846-L940 覆盖了二参省略、三参携带、undefined/视为缺省、以及 Hub 透传不变等全部情形。四、工厂函数唯一合法的变体构造入口四个工厂函数随 Hub 一并导出src/command-routing-hub.cts#L435-L442供处理器与调用方构造错误结果工厂函数签名返回值makeUnknownCommand(command: string)ReadonlyUnknownCommandResultmakeInvalidArgs(arg: string, reason: string, exitReason?: string)ReadonlyInvalidArgsResultmakeHandlerRefusal(reason: string)ReadonlyHandlerRefusalResultmakeHandlerFailure(message: string, cause?: unknown)HandlerFailureResult三个设计要点全部返回Object.freeze冻结对象调用方无法在返回后向变体追加字段、破坏类型化负载不变量。测试 command-routing-hub.test.cjs#L767-L768 断言makeInvalidArgs返回冻结对象。ok固定为false as const工厂产物在 TypeScript 层面即被收窄为对应错误变体杜绝把true结果误传给错误分支。cause参数为unknown而非ErrormakeHandlerFailure内部处理三种情况——Error实例原样保存非 Error 值包装进带.thrown的 Errornull/undefined则不写入cause键。五、运行时守卫对处理器返回值的合法性校验类型系统只在编译期生效运行时处理器仍可能返回非法形状。因此 Hub 在dispatch内部对处理器返回的ok: false结果执行运行时校验src/command-routing-hub.cts#L408-L419依据是每个变体的字段模式_VARIANT_SCHEMAsrc/command-routing-hub.cts#L186-L204变体必填字段允许字段全集UnknownCommandcommandok, kind, commandInvalidArgsarg, reasonok, kind, arg, reason, exitReason?HandlerRefusalreasonok, kind, reasonHandlerFailuremessageok, kind, message, cause校验器_validateErrResultsrc/command-routing-hub.cts#L211-L244检出三类违规并返回契约违例描述未知kind不在封闭枚举中直接拒绝。缺失必填字段如InvalidArgs少了reason。多余字段如HandlerFailure变体携带了不允许的details键。任何违规结果都会被强制转换为HandlerFailure消息形如handler returned malformed Result variant: ...并以违规结果本身作为cause。测试 command-routing-hub.test.cjs#L559-L672 系统验证了这一行为用message冒充reason的InvalidArgs、缺reason的HandlerRefusal、缺message且带多余details的HandlerFailure、以及携带旧式errorKind字段的SomeLegacyKind全部被收编为HandlerFailure而形状合法的结果则原样透传不做任何改写。这一机制意味着#176 的判别联合不仅在编译期收紧类型还在运行时兜底旧式{ ok: false, errorKind: ... }返回即使漏网传入也会在 Hub 边界被识别并归一化不会带着非法形状污染下游。六、唯一的例外ExitError故意重抛Hub 的无抛出契约存在一个被源码注释明确标注的例外src/command-routing-hub.cts#L24-L32 与 L354-L356若处理器抛出的是ExitError来自 cli-exit.cts 的进程退出接缝例如io.cts的error()触发Hub故意重抛而非捕获转换。原因是抛出方已经自行写好了 stderr 并携带特定退出码终止进程若将其包装为HandlerFailure会从ExitError的泛化构造默认值重新推导消息、打印第二条错误的 stderr 行。重抛让它一路穿透到 CLI 入口的runMain()——这是唯一被设计为捕获ExitError的位置。七、下游消费命令路由器如何基于kind分支phase-command-router.cts是迁移到新契约的代表性消费者。它从 Hub 导入createHub, ERROR_KINDS, makeInvalidArgssrc/phase-command-router.cts#L23在参数校验处用工厂函数构造错误结果例如makeInvalidArgs(--id, --id requires a value)makeInvalidArgs(token,phase add does not support ${token})makeInvalidArgs(--descriptions, --descriptions must be a JSON array)makeInvalidArgs(phase-number, phase remove accepts exactly one phase number)见 src/phase-command-router.cts#L124-L270 的批量使用。在结果消费端路由器按kind判别分支src/phase-command-router.cts#L310-L315if (result.kind ERROR_KINDS.UnknownCommand) { // 未知命令分支 } if (result.kind ERROR_KINDS.InvalidArgs || result.kind ERROR_KINDS.HandlerRefusal) { // 参数/拒绝分支 }从源码结构看其余命令族路由器intel、quick-batch、graphify、refactor-trigger、mcp-server等见 src 目录 下各*-command-router.cts同样导入了这些工厂与常量说明判别联合契约已在全仓库命令族中推广。八、测试证据与不变量清单专项测试区块 command-routing-hub.test.cjs#L378-L516 为 #176 的判别联合提供了完整的回归防护核心不变量可归纳为严格键集每个错误变体恰好携带其类型化负载键不得有多余或缺失四个变体各有专属断言。kind值稳定ERROR_KINDS字符串常量与其键名一致可安全用于switch。工厂冻结工厂产物不可变更。非法形状归一畸形处理器返回在 Hub 边界被强制转换为HandlerFailure。exitReason可选语义二参省略、三参携带、undefined/视为缺省且 Hub 透传不变。九、迁移要点与结论对 Hub 的调用方与处理器作者#176 带来的迁移动作集中在三点字段重命名所有读取result.errorKind的地方改为result.kind。按变体消费负载不再依赖泛化message/details——UnknownCommand读command、InvalidArgs读arg/reason、HandlerRefusal读reason、HandlerFailure读message/cause。用工厂构造错误处理器返回错误结果一律通过四个工厂函数生成绕开工厂直接拼对象会在运行时被校验器拒绝并归一化。CommandRoutingHub的错误结果类型化改造本质上是把错误从模糊的键值包升级为可被编译器收窄、可被运行时校验、可被稳定枚举判别的领域模型。它让命令分发链路上的每个环节——处理器、Hub、路由器、CLI 适配层——对错误的形状拥有唯一共识这正是 gsd-core 单运行时架构下错误契约收敛的关键一步。若需进一步追溯设计依据可阅读 ADR-0174判别联合形状的决策与被其取代的 ADR-0012Hub 无抛出契约的起源。赞分享【免费下载链接】gsd-coreGit. Ship. Done - Core项目地址https://gitcode.com/gh_mirrors/ge/gsd-core点击查看免费下载相关推荐TypeSpec 判别类型完全指南discriminated 判别联合与 discriminator 继承多态TypeSpec 判别类型完全指南discriminated 判别联合与 discriminator 继承多态 TypeSpec 原生支持联合union编程语言编译器后端The Concise TypeScript Book 精读Discriminated Unions 判别联合类型完全指南The Concise TypeScript Book 精读Discriminated Unions 判别联合类型完全指南 判别联合Discriminate文档教程The Concise TypeScript Book 精讲判别联合Discriminated Unions的类型收窄实战The Concise TypeScript Book 精讲判别联合Discriminated Unions的类型收窄实战 本篇为开源仓库 The Con文档教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

机器人视觉传感器选型:全局快门、卷帘快门与RGB-D深度相机实测对比
机器人视觉传感器选型:全局快门、卷帘快门与RGB-D深度相机实测对比

给机器人配“眼睛”这件事,我踩过的坑比很多人想象的要深。刚入行那会儿,我以为随便买个USB摄像头往机器人脑袋上一装,把OpenCV跑起来就算完事了。结果第一个项目就翻车了——机械臂带着相机快速移动到目标上方时,画面糊成一片&am… · 2026/9/25 10:23:19

WPS PIN算法逆向与离线破解原理详解
WPS PIN算法逆向与离线破解原理详解

简介:本资源是一份面向网络安全初学者与渗透测试爱好者的实战技术文档,聚焦WPS协议安全缺陷与PIN码算法逆向分析,解决无线路由器密码快速获取与防护加固的实际问题。文档以真实办公环境扫描案例切入,详细演示如何通过嗅探WIFI数据… · 2026/9/25 10:23:06

自研桌面型CRM:从客户管理到销售自动化的全流程实践
自研桌面型CRM:从客户管理到销售自动化的全流程实践

这玩意儿其实是我去年底开始在自己团队里搭建的一套桌面型CRM系统,一开始纯粹是想解决销售团队长期脱离流程运作的问题。我们团队从前用的工具平台太松散,客户资料散落在Excel和聊天记录里,每次复盘都要临时拼凑信息,效率奇低。后… · 2026/9/25 10:23:06

STM32不是单片机,是可裁剪的嵌入式操作系统级硬件平台
STM32不是单片机,是可裁剪的嵌入式操作系统级硬件平台

1. 这不是一块“单片机”,而是一套可裁剪的嵌入式操作系统级硬件平台很多人第一次看到“STM32简介”这个标题,下意识会想:哦,又一个单片机入门科普?翻两页寄存器手册、点个LED、串口打印个“Hello World”就完事了&… · 2026/9/25 10:49:25

2026年1-6月最新全球微信小程序制作工具排名:深度测评5个,附TaoToken统一Key接入配置
2026年1-6月最新全球微信小程序制作工具排名:深度测评5个,附TaoToken统一Key接入配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 10:49:13

i2c i2s spi uart四大接口工程实战对比
i2c i2s spi uart四大接口工程实战对比

1. 为什么这四种接口总被放在一起对比?——不是技术选型,而是系统级生存策略i2c、i2s、spi、uart,这四个缩写在嵌入式开发、硬件设计、驱动调试甚至芯片数据手册的引脚定义页里,几乎从不单独出现。它们像电路板上四条并行的动脉&a… · 2026/9/25 10:49:01

宝马新世代电子架构深度解析:中央计算+区域控制如何对标特斯拉
宝马新世代电子架构深度解析:中央计算+区域控制如何对标特斯拉

这两年聊智能汽车,大家的注意力基本都集中在芯片算力、激光雷达、大屏交互这些单品上。我作为长期跟踪整车电子电气架构的人,反而觉得真正决定一台车智能化上限的,是平时看不见摸不着的电子架构本身。宝马在新世代车型上推出的新一代电子架构… · 2026/9/25 10:48:55

STC ARM转型困局:生态错位比技术短板更致命
STC ARM转型困局:生态错位比技术短板更致命

1. STC的ARM转型不是技术路线选错,而是生态位卡在了“三不管地带”“STC的ARM转型困局:低端不能做,中高端做不出来”——这句话最近在嵌入式开发者圈子里传得挺快,但很多人只把它当一句吐槽,没真去拆解背后到底卡在哪。… · 2026/9/25 10:48:55

Claude Code 进阶使用指南:TaoToken 统一 Key 接入与 CLAUDE.md 配置实战
Claude Code 进阶使用指南:TaoToken 统一 Key 接入与 CLAUDE.md 配置实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 10:48:48

数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)
数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码