# Tube

## Description

Tube 模块提供围绕运行时数据通道（runtime data conduit）的通用建模能力，用于把一段会经历开启、启动、接收数据、出现错误、结束与关闭等生命周期的数据传递过程，组织为可观察、可组合且可选择重放历史记录的稳定公共语义。

它关注的不是某个具体传输协议、宿主流实现或消息中间件接口本身，而是“当一段数据传递过程需要在运行时被明确建模为一个有生命周期、有事件面、可回放最近数据、并可与其它边界协作的通道对象时，应暴露什么样的长期语义”这一类更一般的问题。因此，这个模块更适合被理解为一组数据通道模型，而不是事件总线工具箱、Node.js Stream 包装层，或为了临时转发数据而拼接出来的便捷函数集合。

## For Understanding

理解 Tube 模块时，应先把它看作“有生命周期的数据通道”建模层，而不是把任何会发出事件或传递值的对象都简单归并到这里。它真正要表达的，不只是“某个值能不能被推送出去”，还包括一段通道是否已经打开、是否已经开始接收数据、是否已经变为非空、是否已经发生错误、是否已经结束，以及这些状态变化如何以清楚的事件面暴露给外部。

Tube 的核心价值，在于把一段原本容易散落在回调、标志位和局部变量里的通道过程，收束为一份稳定模型。这样做的重点不是单纯提供一个 `pushData` 方法，而是让“生命周期顺序”“数据到达语义”“错误传播语义”“历史回放语义”“与其它通道或宿主流互转的边界”都可以被统一理解和长期维护。

理解这个模块时，特别要守住几条边界：

- Tube 模块表达的是运行时数据通道及其生命周期，而不是传输协议、Socket 连接、HTTP 请求、文件读取或浏览器流 API 本身。
- 它适合描述通道何时开始可接收数据、何时真正进入流动状态、何时首次变为非空、何时结束以及如何对这些变化做出订阅。
- 它可以与 `ReadableStream` 等宿主流结构互相转换，也可以连接上下游通道，但这些都应被理解为围绕通道模型发生的集成接点，而不是让宿主 API 细节反过来定义 Tube 的公共语义。
- 它不应直接承担背压（backpressure）协商、传输重试、协议握手、序列化格式、连接保活、分布式消息确认或业务专属路由规则等职责。
- 数据历史回放的价值，在于帮助新订阅者接入一个已经运行中的通道，而不是把 Tube 退化成无限责任的缓存容器；因此扩展时应优先围绕“通道中的历史视图”思考，而不是围绕“持久存储”思考。

## For Using

当你希望在应用内部表达一段真正有生命周期的数据传递过程，而不是把数据流转逻辑拆散到多个事件回调、Promise 链或宿主流对象的临时包装里时，可以使用 Tube 模块。

从使用角度看，这个模块大致可以分为三类能力：

- 生命周期控制能力：用于表达通道的打开、启动、结束、关闭以及错误发生后的自动联动行为，适合把一段通道过程收束为稳定状态机。
- 事件与数据订阅能力：用于观察 `open`、`start`、`wet`、`error`、`end`、`close` 与数据到达本身，并根据需要为新订阅者重放最近的历史数据。
- 集成辅助能力：用于连接上下游 Tube、把 Tube 收束为 Promise，或与 `ReadableStream` 等宿主流结构互相转换。

更合适的接入方式，通常是先判断你的核心对象是不是“一段需要被长期观察和组合的数据通道过程”。如果你需要的是：

- 某个生产者持续把数据推给多个订阅方，并且这些订阅方还需要感知通道生命周期；
- 一个已经开始流动的通道要允许新接入者按需看到最近历史，而不是只看到未来数据；
- 你希望把宿主流结构转换为一份带稳定生命周期语义的对象，再与其它模型协作；
- 上下游两个处理阶段之间需要一个清楚的数据通道边界，而不是若干零散回调直接耦合；

那么这个模块就是合适的边界。

相反，如果你的需求只是宿主环境里某个具体流 API 的底层控制、传输协议细节、连接状态机、消息编码格式、流量控制或业务专属转发规则，那么这些通常不应直接落在 Tube 模块本身，而应放在对应宿主模块、适配层或更外层系统中。

## For Contributing

为 Tube 模块贡献内容时，优先判断新增能力是否真的在澄清“运行时数据通道如何被建模为稳定生命周期对象”这一问题，而不是只是在补一个临时好用的转发函数或宿主 API 包装。这个模块应长期服务于“通道状态如何表达”“数据与错误如何被送入并传播”“订阅者如何观察生命周期与数据”“历史如何被有边界地回放”“通道如何与其它模型形成集成接点”这几类问题。

扩展这个模块时，应特别警惕以下倾向：把 Tube 退化成普通事件总线；把某个宿主流实现的局部行为直接暴露为公共承诺；把连接、协议、背压、重试、路由或业务缓存逻辑无差别地下沉到 Tube 内部；或者为了当前实现方便而公开内部状态结构。文档应说明哪些通道语义是长期成立的，以及为什么这些语义成立，而不是复述某个版本里恰好采用的局部实现技巧。

当前已经落地的 `Tube` 应被理解为 Tube 模块下的核心通道模型，而 `connectTube`、`tubeToPromise`、`tubeToReadableStream` 与 `readableStreamToTube` 则是围绕这一模型形成的集成辅助函数。后续若增加子模块或更多辅助能力，应优先判断它们是否仍然服务于“清楚、稳定的数据通道语义”，而不是把其它更大的问题域误并到这个模块里。

### JSDoc 注释格式要求

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

### 实现规范要求

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

### 导出策略要求

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

### 测试要求

- 若程序元素是函数，则只为该函数编写一个测试，如果该函数需要测试多个用例，应放在同一个测试中。
- 若程序元素是类，则至少要为该类的每一个方法编写一个测试，如果该方法需要测试多个用例，应放在同一个测试中。
- 若程序元素是类，除了为该类的每一个方法编写至少一个测试之外，还可以为该类编写任意多个测试，以覆盖该类的不同使用场景或边界情况。
- 若编写测试时需要用到辅助元素（Mock 或 Spy 等），可以在测试文件中直接定义这些辅助元素。若辅助元素较为简单，则可以直接放在每一个测试内部，优先保证每个测试的独立性，而不是追求极致 DRY；若辅助元素较为复杂或需要在多个测试中复用，则可以放在测试文件顶部，供该测试文件中的所有测试使用。
- 测试顺序应与源文件中被测试目标的原始顺序保持一致。
- 若该模块不需要测试，必须在说明文件中明确说明该模块不需要测试，并说明理由。一般来说，只有在该模块没有可执行的公共函数、只承载类型层表达，或其语义已被上层模块的测试完整覆盖且重复测试几乎不再带来额外价值时，才适合这样处理。
- 模块的单元测试文件目录是 `./tests/unit/tube`。
- 对这个模块来说，测试应优先覆盖生命周期切换、自动联动配置、历史回放、错误传播、订阅与退订行为，以及 Tube 与其它 Tube 或宿主 `ReadableStream` 之间的集成协同行为。
