# Exception

## Description

Exception 模块提供围绕异常（exception）的基础模型能力，用于承载宿主环境全局异常入口的监听与标准化，以及其中一类可判别、可匹配、可序列化的结构化错误建模。

它关注的是“异常从哪里进入系统、错误如何被稳定表达”，而不是“异常之后如何打印、上报、恢复、吞掉或终止进程”。

## For Understanding

理解 Exception 模块时，更合适的方式是把它视为“异常模型层”，而不是“异常处理策略层”。这个模块当前关注的是异常这一上位概念，其中既包括异常如何从宿主环境进入系统，也包括其中一类适合被稳定建模的异常形式，也就是结构化错误。

- 宿主异常入口：负责封装浏览器和 Node.js 暴露出来的全局异常入口，并把原始输入整理成稳定记录。
- 结构化错误：负责把应用内部需要长期存在的某些异常表达成带判别信息的 Error 变体，以便进行分派、组合和序列化。

之所以可以把它们放在同一模块内，不是因为它们的实现方式相似，而是因为它们都在回答同一类问题：系统中的异常应该以什么稳定模型进入后续流程。前者解决“异常从哪里来”，后者解决“某些异常在系统中应如何被表达与判别”。

因此，这个模块并不承担日志发送、监控上报、错误恢复、默认阻止、重试编排或进程退出等策略职责。它只负责把异常整理成更稳定、更清楚、更可组合的输入模型，让上层的 logger、telemetry、workflow、Result/Either 或业务恢复逻辑在其之上继续工作。

对于宿主异常入口部分，当前主要覆盖两类运行时：

- 浏览器侧异常入口，例如 `error` 事件、`window.onerror` 与 `unhandledrejection`。
- Node.js 侧异常入口，例如 `uncaughtExceptionMonitor`、`uncaughtException` 与 `unhandledRejection`。

对于异常建模中的结构化错误部分，当前采用 Tagged Error 的思路：这类错误既保留原生 Error 的 name、message、stack 与 `cause` 语义，也额外拥有稳定的 `tag` 与 `data` 字段，便于在联合类型中进行穷举或部分匹配。

## For Using

当应用需要从宿主环境统一接入全局异常，并把这些异常整理成稳定记录时，可以使用这个模块的异常入口能力，把分散在不同运行时 API 上的异常来源收敛成更清楚的订阅接口。

当应用需要在模块边界、基础设施层、工作流编排、Result/Either 风格流程或跨层协议中表达一组稳定、可判别、可序列化的异常时，也可以使用这个模块中的结构化错误能力，把其中适合显式建模的一类异常从“临时字符串或松散对象”提升为更清楚的公共模型。

当前公共能力大致可以按以下几类理解：

- 单一入口监听：用于单独监听浏览器或 Node.js 的某一种全局异常来源。
- 批量入口监听：用于按运行时一次性安装一组常见异常入口，并返回统一清理函数。
- 异常记录标准化：用于把浏览器或 Node.js 的原始输入整理成稳定的异常记录结构。
- Tagged Error 建模：用于定义一类带 `tag`、`data`、`cause` 与序列化能力的结构化异常类型。
- Tagged Error 匹配：用于按 tag 分派这一类结构化异常的处理逻辑，并在类型层保留分支信息。
- 类型与清理语义：用于表达异常来源种类、异常记录形状、结构化异常实例能力以及取消监听的统一返回类型。

使用时应把它接在 logger、遥测、监控、恢复策略或业务错误处理的上游，而不是期待它直接替代这些能力。更准确地说，它适合作为异常输入与异常表达的基础层，而不是最终处理层。

## For Contributing

贡献 Exception 模块时，应优先判断新增能力是否真正扩展了“异常模型”这个问题域，而不是只是在当前实现里顺手添加一个便捷函数。只有当某项能力表达了稳定、可复用、值得长期维护的异常来源语义，或表达了某类异常的稳定建模语义时，它才适合进入这个模块。

在扩展时，应优先遵守以下边界：

- 公共能力应围绕宿主异常入口、异常记录标准化，以及其中可被稳定表达的结构化异常建模与匹配语义展开。
- 若新增的是模块内稳定异常模型，且它适合以 Error 形式存在，应优先使用 Tagged Error 形式表达，而不是仅靠裸字符串或无判别字段的对象。
- 不要在监听逻辑或异常模型中默认执行日志输出、上报请求、`preventDefault`、`process.exit`、重试、恢复或其它强策略动作，除非 API 名称与文档已经明确承诺。
- 组合入口只负责批量安装监听器与汇总清理，不负责掩盖宿主差异或替调用方做策略决策。
- Tagged Error 的职责是提供一类稳定异常表达、判别和匹配语义，而不是承载某个单一应用的临时上下文容器。
- 与 Environment 的关系应保持清楚：Environment 可以帮助判断和获取宿主上下文，Exception 则负责异常入口与异常模型本身。

### JSDoc 注释格式要求

- 每个公开导出的目标（类型、函数、变量、类等）都应包含 JSDoc 注释，让人在不跳转实现的情况下就能理解用途。
- JSDoc 注释第一行应为清晰且简洁的描述，该描述优先使用中文（英文也可以）。
- 如果描述后还有其他内容，应在描述后加一个空行。
- 如果有示例，应使用 `@example` 标签，后接三重反引号代码块（不带语言标识）。
- 如果有示例，应包含多个场景，展示不同用法，尤其要覆盖常见组合方式或边界输入。
- 如果有示例，应使用注释格式说明每个场景：`// Expect: <result>`。
- 如果有示例，应将结果赋值给 `example1`、`example2` 之类的变量，以保持示例易读。
- 如果有示例，`// Expect: <result>` 应该位于 `example1`、`example2` 之前，以保持示例的逻辑清晰。
- 如果有示例，应优先使用确定性示例；避免断言精确的随机输出。
- 如果函数返回结构化字符串，应展示其预期格式特征。
- 如果有参考资料，应将 `@see` 放在 `@example` 代码块之后，并用一个空行分隔。

### 实现规范要求

- 不同程序元素之间使用一个空行分隔，保持结构清楚。这里的程序元素，通常指函数、类型、常量，以及直接服务于它们的辅助元素。
- 某程序元素独占的辅助元素与该程序元素本身视为一个整体，不要在它们之间添加空行。
- 程序元素的辅助元素应该放置在该程序元素的上方，以保持阅读时的逻辑顺序。
- 若辅助元素被多个程序元素共享，则应将其视为独立的程序元素，放在这些程序元素中第一个相关目标的上方，并与后续程序元素之间保留一个空行。
- 辅助元素也应该像其它程序元素一样，保持清晰的命名和适当的注释，以便在需要阅读实现细节时能够快速理解它们的作用和使用方式。
- 辅助元素的命名必须以前缀 `internal` 开头（或 `Internal`，大小写不敏感）。
- 辅助元素永远不要公开导出。
- 被模块内多个不同文件中的程序元素共享的辅助元素，应该放在一个单独的文件中，例如 `./src/exception/internal.ts`；若共享只存在于浏览器或 Node.js 子问题域内部，也可以放在对应文件中。
- 模块内可以包含子模块。只有当某个子目录表达一个稳定、可单独理解、且可能被父模块重导出的子问题域时，才应将其视为子模块。
- 子模块包含多个文件时，应该为其单独创建子文件夹，并为其创建单独的 Barrel 文件；父模块的 Barrel 文件再重导出子模块的 Barrel 文件。
- 子模块不需要有自己的 `README.md`。
- 子模块可以有自己的 `internal.ts` 文件，多个子模块共享的辅助元素应该放在父模块的 `internal.ts` 文件中，单个子模块共享的辅助元素应该放在该子模块的 `internal.ts` 文件中。
- 对模块依赖关系的要求（通常是不循环依赖或不反向依赖）与对 DRY 的要求可能产生冲突。此时，若复用的代码数量不大，可以适当牺牲 DRY，复制粘贴并保留必要的注释说明；若复用的代码数量较大，则可以将其抽象到新的文件或子模块中，如 `common.ts`，并在需要的地方导入使用。
- 监听注册逻辑、记录标准化逻辑和上层策略逻辑应保持分层，避免把一个模块做成过重入口。

### 导出策略要求

- 保持内部辅助项和内部符号为私有，不要让外部接入依赖临时性的内部结构。
- 每个模块都应有一个用于重导出所有公共 API 的 Barrel 文件。
- Barrel 文件应命名为 `index.ts`，放在模块目录根部，并且所有公共 API 都应从该文件导出。
- 新增公共能力时，应优先检查它是否表达稳定、清楚且值得长期维护的异常模型语义，而不是某段实现细节的便捷暴露；仅在确认需要长期对外承诺时再加入 Barrel 导出。

### 测试要求

- 若程序元素是函数，则只为该函数编写一个测试，如果该函数需要测试多个用例，应放在同一个测试中。
- 若程序元素是类，则至少要为该类的每一个方法编写一个测试，如果该方法需要测试多个用例，应放在同一个测试中。
- 若程序元素是类，除了为该类的每一个方法编写至少一个测试之外，还可以为该类编写任意多个测试，以覆盖该类的不同使用场景或边界情况。
- 若编写测试时需要用到辅助元素（Mock 或 Spy 等），可以在测试文件中直接定义这些辅助元素。若辅助元素较为简单，则可以直接放在每一个测试内部，优先保证每个测试的独立性，而不是追求极致 DRY；若辅助元素较为复杂或需要在多个测试中复用，则可以放在测试文件顶部，供该测试文件中的所有测试使用。
- 测试顺序应与源文件中被测试目标的原始顺序保持一致。
- 若该模块不需要测试，必须在说明文件中明确说明该模块不需要测试，并说明理由。一般来说，只有在该模块没有可执行的公共函数、只承载类型层表达，或其语义已被上层模块的测试完整覆盖且重复测试几乎不再带来额外价值时，才适合这样处理。
- 模块的单元测试文件目录是 `./tests/unit/exception`，若模块包含子模块，则子模块的单元测试文件目录为 `./tests/unit/exception/<sub-module-name>`。
- 测试应重点覆盖监听注册、清理行为、默认包含的入口集合、记录标准化结果，以及宿主不可用时的失败或空操作语义是否稳定。
- 若新增 Tagged Error 能力，应重点覆盖 tag 判别、message 与 `cause` 推导、序列化结果，以及按 tag 进行穷举或部分匹配时的返回语义是否稳定。
