# Mobius Model

## Description

Mobius Model 是一个建模导向的通用基础库（modeling-oriented general-purpose foundation library），面向 JavaScript / TypeScript 生态，用来承载一组可长期复用、可稳定组合、且尽量不与具体业务耦合的基础模型模块。

它的目标不是提供一批彼此孤立的零散工具，而是围绕“哪些能力值得被抽象为稳定模型”这一问题，逐步沉淀出一套跨平台、跨运行时、跨环境的通用基础能力。在具体环境中接入时，部分 API 仍可能需要自行处理兼容性，但这些模块的设计目标始终是尽量保持环境中立、语义清楚且可组合。

因此，这个包更适合被理解为一组模块化的基础模型集合，而不是一个以临时便捷函数堆叠而成的杂项工具箱。每个模块都应有明确的问题域、清楚的公共边界，以及能够长期维护的语义承诺。

## For Understanding

Mobius Model 由多个模块（Model）组成。更合适的理解方式是，把它看作一组可以按需选用、按需组合的基础模型集合，而不是一个必须整体接入的大型框架，也不是一个以零散便捷函数为主的杂项工具箱。

这里的核心判断标准不是“某段代码是否常用”，而是“某类能力是否值得被抽象为稳定模型”。只有当一个模块确实表达了清楚的问题域边界、能够提供可长期维护的公共语义，并且不依赖具体业务前提时，它才适合进入这个包。

因此，在理解模块时，不应只从零散 API 名称出发，而应优先通过模块级 `README.md` 理解它解决的是什么问题、适合放在什么边界内，以及哪些设计原则不能被破坏。文档首先负责建立模块语义，其次才负责帮助判断应如何使用或继续扩展。

## For Using

当你希望在应用程序或库中复用一类已经被明确建模的问题域能力，而不是每次都从业务代码里临时拼装一套实现时，可以从这里挑选合适的模块接入。不同模块各自聚焦于不同问题域，但都应遵守一致的设计原则：边界清楚、语义稳定，并尽量减少对具体业务场景的假设。

在接入这些模块时，不应把它们理解为“就地凑合可用”的临时工具；更合适的理解方式是，它们提供的是可以进入业务边界、并在长期演进中保持相对稳定的基础模型能力。是否采用某个模块，首先应看它的职责边界是否与你当前问题一致，而不是只看其中是否碰巧包含几个可复用函数。

每个模块目录下都带有自己的 `README.md`，用于说明该模块的定位、适用场景、推荐理解方式，以及继续扩展时需要遵守的边界。阅读模块级说明文件，应被视为判断该模块是否适合当前需求的第一入口；在接入之前，应优先通过这些说明理解模块职责，而不要直接从零散 API 名称反推设计意图。

## For Contributing

根 README 的职责不是替代各模块自己的说明文件，而是为整个 `packages/model` 包定义一套统一的文档与实现约束。任何模块级 README 都应在此基础上继续展开各自的领域说明，而不是脱离这些共用规则各写各的。

在扩展或调整模块时，既要保证接入者能够借助文档理解模块，也要保证后续演进不会破坏原有语义。这里的重点不是把当前实现过程记录下来，而是为后续的使用者和维护者保留一份长期有效的边界说明。

### 模块界定原则

`./src` 目录下的每一个文件夹都应被视为一个独立模块（Model）。贡献新模块或调整现有模块时，首要任务不是补齐某个局部功能点，而是先确认这个目录是否确实表达了一个边界清楚、值得长期维护的问题域。

在引入新模块或对现有模块进行重构时，应当以概念上的内聚性（conceptual cohesion）为主要依据，而不是以实现上的便利为依据。一个模块应当代表一个稳定的概念域（conceptual domain）或概念族（concept family），而不是仅仅承载某一个具体工具或实现机制。如果某个功能本质上是更一般概念的特例或派生形式，那么它应当归属于该更高层概念所在的模块，而不是单独形成一个模块。

一个简单的经验原则是：模块应该围绕“概念族”建立，而不是围绕“单个工具”建立。

许多底层机制（mechanism）虽然在实现上独立，但如果它们不构成一个稳定的抽象领域，通常更适合作为某个模块内部的一部分，而不是顶级模块。

在开发过程中，随着对问题领域理解的加深，抽象往往会逐渐收敛。例如，一个最初设计为 `cooldown` 的功能，表面上看似可以作为独立模块存在；但进一步分析会发现，它本质上只是**“某个对象在一段时间后失效”**这一更一般概念的一个具体表现。因此可以将抽象提升为 `Expiration`，并将其归入更广义的时间相关模块中，例如： `timer/expiration/index.ts`，而不是 `cooldown/index.ts`。

这种从具体用途向更一般概念收敛的重构是预期且鼓励的过程。随着库的发展，模块结构应当逐渐向更稳定、更通用的概念边界演化。

在判断某个功能是否值得成为独立模块时，可以参考以下几个问题：

1. 概念范围（Conceptual Scope）：这个功能是否代表一个较大的概念领域，而不仅仅是一个具体工具？
2. 概念族（Concept Family）：是否存在多个自然相关的抽象可以归入同一个模块？
3. 语义稳定性（Semantic Stability）：这个名称在未来抽象演化后是否仍然合理？
4. 用户视角（User-facing Meaning）：使用者在理解或使用库时，是否会自然地以这个概念进行思考？

如果这些问题的答案大多是否定的，那么该功能通常更适合作为现有模块的一部分，而不是新的顶级模块。

简而言之：模块用于表达稳定的概念领域，而具体机制和特例应当作为这些领域内部的组成部分。

### 模块规范要求

每个模块都必须满足以下要求：

- 都有一个 Barrel 文件（`index.ts`），该模块的所有公共 API 都应统一通过该文件重导出（re-export）。
- Barrel 文件的路径为 `./src/<model-name>/index.ts`。
- 都有一个说明文件（`README.md`），用于说明该模块的基本情况和必要细节。
- 说明文件的路径为 `./src/<model-name>/README.md`。
- 说明文件的标题必须是模块名称，且必须使用一级标题（#）。
- 除明确要求外，说明文件的结构和内容都可以灵活组织。
- 说明文件必须使用中文撰写，并采用 Markdown 格式。
- 说明文件中涉及专业术语时，必须使用中文术语，并在括号中提供对应的英文术语（如果有的话）。英文术语只需在该文件中首次出现时提供一次，后续出现可以直接使用中文术语。
- 说明文件的第一个部分必须是 Description，且必须使用二级标题（##）。
- 说明文件的 Description 部分的第一句话必须是对该模块功能和主要用途的简短、清晰且无歧义的描述。
- 说明文件的第二个部分必须是 For Understanding，且必须使用二级标题（##）。
- 说明文件的 For Understanding 部分用于说明该模块解决什么问题、适合放在什么边界内，以及理解该模块时需要先把握的理念、原则或前提。
- 说明文件的 For Understanding 部分应为后续的使用和扩展提供共同的理解前提，不应把模块边界留到某一个动作里再解释。
- 说明文件的第三个部分必须是 For Using，且必须使用二级标题（##）。
- 说明文件的 For Using 部分必须包含对模块作用以及使用场景的说明。
- 说明文件的 For Using 部分永远不要罗列模块的公共 API，但可以进行大致分类并对每个类别进行简要说明。
- 说明文件的第四个部分必须是 For Contributing，且必须使用二级标题（##）。
- 说明文件的 For Contributing 部分必须包含对后续扩展、维护或新增能力时的说明或指引。
- 说明文件的 For Contributing 部分必须与代码解耦，避免陷入实现细节，并将焦点放在开发此模块的背景、目的、理念、思路、原则和规范上，而不是它具体如何实现。在扩展时，应通过阅读代码理解实现，通过阅读文档理解为什么这样设计，以及哪些边界不能被打破。文档不应重复代码已经明确表达的实现事实，也应避免写成只对某个历史实现版本成立的局部备注。
- 说明文件的 For Contributing 部分必须包含 JSDoc 注释格式要求（所有模块通用）。
  - 每个公开导出的目标（类型、函数、变量、类等）都应包含 JSDoc 注释，让人在不跳转实现的情况下就能理解用途。
  - JSDoc 注释第一行应为清晰且简洁的描述，该描述优先使用中文（英文也可以）。
  - 如果描述后还有其他内容，应在描述后加一个空行。
  - 如果有示例，应使用 `@example` 标签，后接三重反引号代码块（不带语言标识）。
  - 如果有示例，应包含多个场景，展示不同用法，尤其要覆盖常见组合方式或边界输入。
  - 如果有示例，应使用注释格式说明每个场景：`// Expect: <result>`。
  - 如果有示例，应将结果赋值给 `example1`、`example2` 之类的变量，以保持示例易读。
  - 如果有示例，`// Expect: <result>` 应该位于 `example1`、`example2` 之前，以保持示例的逻辑清晰。
  - 如果有示例，应优先使用确定性示例；避免断言精确的随机输出。
  - 如果函数返回结构化字符串，应展示其预期格式特征。
  - 如果有参考资料，应将 `@see` 放在 `@example` 代码块之后，并用一个空行分隔。
- 说明文件的 For Contributing 部分必须包含实现规范要求（所有模块通用）。
  - 不同程序元素之间使用一个空行分隔，保持结构清楚。这里的程序元素，通常指函数、类型、常量，以及直接服务于它们的辅助元素。
  - 某程序元素独占的辅助元素与该程序元素本身视为一个整体，不要在它们之间添加空行。
  - 程序元素的辅助元素应该放置在该程序元素的上方，以保持阅读时的逻辑顺序。
  - 若辅助元素被多个程序元素共享，则应将其视为独立的程序元素，放在这些程序元素中第一个相关目标的上方，并与后续程序元素之间保留一个空行。
  - 辅助元素也应该像其它程序元素一样，保持清晰的命名和适当的注释，以便在需要阅读实现细节时能够快速理解它们的作用和使用方式。
  - 辅助元素的命名必须以前缀 `internal` 开头（或 `Internal`，大小写不敏感）。
  - 辅助元素永远不要公开导出。
  - 被模块内多个不同文件中的程序元素共享的辅助元素，应该放在一个单独的文件中，例如 `./src/<model-name>/internal.ts`。
  - 模块内可以包含子模块。只有当某个子目录表达一个稳定、可单独理解、且可能被父模块重导出的子问题域时，才应将其视为子模块。
  - 子模块包含多个文件时，应该为其单独创建子文件夹，并为其创建单独的 Barrel 文件；父模块的 Barrel 文件再重导出子模块的 Barrel 文件。
  - 子模块不需要有自己的 `README.md`。
  - 子模块可以有自己的 `internal.ts` 文件，多个子模块共享的辅助元素应该放在父模块的 `internal.ts` 文件中，单个子模块共享的辅助元素应该放在该子模块的 `internal.ts` 文件中。
  - 对模块依赖关系的要求（通常是不循环依赖或不反向依赖）与对 DRY 的要求可能产生冲突。此时，若复用的代码数量不大，可以适当牺牲 DRY，复制粘贴并保留必要的注释说明；若复用的代码数量较大，则可以将其抽象到新的文件或子模块中，如 `common.ts`，并在需要的地方导入使用。
- 说明文件的 For Contributing 部分必须包含导出策略要求（所有模块通用）。
  - 保持内部辅助项和内部符号为私有，不要让外部接入依赖临时性的内部结构。
  - 每个模块都应有一个用于重导出所有公共 API 的 Barrel 文件。
  - Barrel 文件应命名为 `index.ts`，放在模块目录根部，并且所有公共 API 都应从该文件导出。
  - 新增公共能力时，应优先检查它是否表达稳定、清楚且值得长期维护的模块语义，而不是某段实现细节的便捷暴露；仅在确认需要长期对外承诺时再加入 Barrel 导出。
- 说明文件的 For Contributing 部分必须包含测试要求（所有模块通用）。
  - 若程序元素是函数，则只为该函数编写一个测试，如果该函数需要测试多个用例，应放在同一个测试中。
  - 若程序元素是类，则至少要为该类的每一个方法编写一个测试，如果该方法需要测试多个用例，应放在同一个测试中。
  - 若程序元素是类，除了为该类的每一个方法编写至少一个测试之外，还可以为该类编写任意多个测试，以覆盖该类的不同使用场景或边界情况。
  - 若编写测试时需要用到辅助元素（Mock 或 Spy 等），可以在测试文件中直接定义这些辅助元素。若辅助元素较为简单，则可以直接放在每一个测试内部，优先保证每个测试的独立性，而不是追求极致 DRY；若辅助元素较为复杂或需要在多个测试中复用，则可以放在测试文件顶部，供该测试文件中的所有测试使用。
  - 测试顺序应与源文件中被测试目标的原始顺序保持一致。
  - 若该模块不需要测试，必须在说明文件中明确说明该模块不需要测试，并说明理由。一般来说，只有在该模块没有可执行的公共函数、只承载类型层表达，或其语义已被上层模块的测试完整覆盖且重复测试几乎不再带来额外价值时，才适合这样处理。
  - 模块的单元测试文件目录是 `./tests/unit/<model-name>`，例如 `./tests/unit/reactor`，若模块包含子模块，则子模块的单元测试文件目录为 `./tests/unit/<model-name>/<sub-module-name>`，例如 `./tests/unit/reactor/operators`。

在编写模块级 README 时，应把它理解为该模块的长期语义说明，而不是一份临时开发备忘录。文档首先要回答“这个模块解决什么问题、适合放在什么边界内、哪些设计原则不能退让”，然后再分别回答“在使用时应如何理解它”和“在继续扩展时应如何约束它”。只要文档开始依赖具体实现细节来维持自身成立，就说明这份说明文件还没有真正完成抽象。

## Todos & Drafts

- [ ] reactor 支持作用域（scope）。
- [ ] reactor operators.
  - [ ] 参考 Angular Signals 的 `resource`（https://angular.dev/guide/signals/resource），并同时参考 SolidJS 的 `createResource`（https://docs.solidjs.com/reference/basic-reactivity/create-resource）。
  - [ ] delay operator。
  - [ ] timeInterval operator。
  - [ ] timestamp operator。
  - [ ] timeout operator。
  - [ ] timeoutWith operator。
  - [ ] toArray operator。
  - [ ] count operator。
  - [ ] clone operator（>2）。
  - [ ] iif operator。
  - [ ] pluck operator。
  - [ ] sequenceEqual operator。
  - [ ] skip operator。
  - [ ] skipWhile operator。
  - [ ] skipUntil operator。
- [ ] 修复 preact signal 测试。
