# Web

## Description

Web 模块用于提供面向浏览器环境（browser environment）的通用基础能力，主要承载位置、事件、文档对象模型（DOM, Document Object Model）交互、权限、剪贴板、下载、截图以及脚本加载等稳定语义。

它关注的是浏览器原生能力的通用抽象，而不是某个页面、某个框架或某个业务流程的局部实现。这个模块的价值在于把常见 Web 行为整理成边界清楚、可长期复用的公共模型，使调用方能够在不同页面和不同运行环境中复用同一套浏览器语义，而不是重复编写零散的 DOM 操作和宿主 API 调用。

## For Understanding

理解 Web 模块时，应先把它看作浏览器语义层，而不是页面逻辑层。它解决的是“浏览器和文档环境本身能提供哪些稳定能力，以及这些能力如何以较统一的方式被调用”这一类问题，因此适合放在前端应用、嵌入式 Web 视图或具备 DOM 能力的宿主环境边界中。

也因此，本模块不应承担业务页面逻辑、组件状态编排、具体框架生命周期或某一站点专用规则。适合进入这里的，应该是对位置跳转、事件监听、文档读写、脚本注入与脚本发现、资源下载、权限查询等浏览器行为的稳定抽象。只在某个业务页面中成立的选择器约定、交互流程或样式耦合逻辑，不应进入 Web 模块。

这个模块还天然带有环境前提。很多能力依赖浏览器 API、用户授权、文档对象存在与否、加载时机以及宿主兼容性。文档的职责不是掩盖这些前提，而是帮助调用方理解：Web 模块提供的是通用浏览器语义封装，环境差异、兼容策略和降级处理仍应由接入方根据实际运行条件负责。

## For Using

当你希望以更统一的方式访问浏览器能力，而不是把位置跳转、DOM 交互、事件订阅、脚本装载、权限调用等逻辑零散地散布在业务代码中时，可以使用这个模块。它适合那些需要跨页面、跨组件、跨项目复用浏览器基础语义的场景。

从使用角度看，可以把这个模块理解为若干能力类别的集合。它既包含围绕页面地址、文档标题、DOM 节点和事件订阅的基础交互能力，也包含围绕剪贴板、权限、下载与脚本处理的宿主能力封装。调用时应优先从“当前问题属于哪类浏览器语义”来理解模块，而不是直接从某个具体函数名反推设计意图。

更合理的使用方式，是把这些能力放在浏览器边界附近，先由 Web 模块完成对宿主 API 的基础整理，再由上层根据业务需要组合使用。这样既能减少页面层的重复代码，也能把兼容性检查、失败分支与资源清理等问题集中到更容易维护的位置。

## For Contributing

贡献 Web 模块时，应首先确认新增能力表达的是稳定、可复用的浏览器语义，而不是一次性的页面技巧。公共 API 的重点应是让调用方清楚理解“这里封装的是哪一种浏览器行为、适用于什么边界、失败时大致是什么语义”，而不是让模块变成存放各种 DOM 小技巧的收纳盒。

继续扩展时，应特别注意两个方向。其一，不要把业务页面逻辑、站点特定约定或框架专属行为混入 Web 模块。其二，不要为了追求表面简洁而隐藏重要的环境前提，例如用户授权、脚本加载顺序、文档可用性或资源清理责任。Web 模块可以帮助收敛这些问题，但不应伪装成“在任何环境中都无条件可靠”的黑箱。

### JSDoc 注释格式要求

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

### 实现规范要求

- 不同程序元素之间使用一个空行分隔，保持结构清楚。这里的程序元素，通常指函数、类型、常量，以及直接服务于它们的辅助元素。
- 某程序元素独占的辅助元素与该程序元素本身视为一个整体，不要在它们之间添加空行。
- 程序元素的辅助元素应该放置在该程序元素的上方，以保持阅读时的逻辑顺序。
- 若辅助元素被多个程序元素共享，则应将其视为独立的程序元素，放在这些程序元素中第一个相关目标的上方，并与后续程序元素之间保留一个空行。
- 辅助元素也应该像其它程序元素一样，保持清晰的命名和适当的注释，以便在需要阅读实现细节时能够快速理解它们的作用和使用方式。
- 辅助元素的命名必须以前缀 `internal` 开头（或 `Internal`，大小写不敏感）。
- 辅助元素永远不要公开导出。
- 被模块内多个不同文件中的程序元素共享的辅助元素，应该放在一个单独的文件中，例如 `./src/web/internal.ts`。
- 模块内可以包含子模块。只有当某个子目录表达一个稳定、可单独理解、且可能被父模块重导出的子问题域时，才应将其视为子模块。
- 子模块包含多个文件时，应该为其单独创建子文件夹，并为其创建单独的 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/web`，若模块包含子模块，则子模块的单元测试文件目录为 `./tests/unit/web/<sub-module-name>`。
- 此模块暂时不需要任何测试，因为它主要是对浏览器能力的封装，且这些能力本身已经由浏览器厂商维护测试覆盖。未来如果需要在模块内添加更复杂的逻辑或状态管理时，可以再考虑增加针对这些逻辑的单元测试。另外一个原因是，浏览器环境的测试通常需要特定的运行环境和工具链支持，开发和维护成本较高，因此在没有明确需求的情况下，先保持模块的纯粹封装性质也是合理的。
