# Cron

## Description

Cron 模块提供围绕 Cron 表达式与计划触发时点计算的基础建模能力，用于在不执行具体任务的前提下稳定描述某个计划何时匹配、何时已经触发以及何时将要触发。

它关注的是“计划语义如何被抽象为稳定模型”，而不是把某个运行时里的定时任务 API 直接包一层后暴露出去。对于使用者而言，这个模块更适合承载计划匹配、运行点推导与计划边界判断，而不是任务执行、副作用调度或分布式作业管理。

## For Understanding

理解 Cron 模块时，重点应放在“一个计划本身如何被表达与判断”，而不是“某段任务代码何时被调度执行”。Cron 在这里表达的是一种时间计划模型：给定某个计划模式后，外部可以稳定地询问它是否匹配某个时间点、它最近一次计划运行点是什么、以及未来的计划运行点是什么。

因此，这个模块适合放在以下边界中：

- 你需要复用一类与计划时点推导相关的基础能力，而不想在业务代码里反复处理 Cron 表达式解析与时间匹配。
- 你需要把“计划何时成立”与“计划成立后业务要做什么”明确分离，让上层自行决定副作用。
- 你需要一个环境中立的计划模型，以便在不同运行时或不同任务系统之间复用相同的计划语义。

同时也要守住几个边界：

- Cron 模块不负责执行任务，也不负责重试、并发控制、分布式锁、任务持久化或调度生命周期管理。
- 某个能力如果主要表达的是“任务如何运行”，通常更适合放在调度器、作业系统或编排模块中，而不是放在 Cron 模块中。
- 如果某种时间逻辑不以 Cron 计划语义为中心，而更偏向广义时间状态或过期管理，则通常应放在其它时间相关模块边界内理解。

## For Using

当你已经明确需要的是“计划时点模型”，而不是一个直接帮你启动任务的执行器时，可以接入 Cron 模块。它更适合被用来完成三类工作：一类是定义一个长期可复用的计划模式；一类是围绕该计划推导过去、当前与未来的运行点；另一类是判断某个给定时点是否满足该计划。

在实际接入时，更合理的方式是把它视为上层调度逻辑的基础判断器。业务系统、任务系统或编排系统可以依赖这里提供的计划语义来决定何时启动、跳过、补偿或展示某个任务，但这些决策本身不应内置在 Cron 模块里。

如果你的需求已经超出了“计划表达与时点判断”，例如需要内建回调执行、失败重试、任务注册表或长期运行的后台调度循环，那么应当在更上层的模块中组合这个计划模型，而不是把这些职责继续压进 Cron 模块。

## For Contributing

贡献 Cron 模块时，应优先确认新增内容表达的是不是稳定的计划语义，而不是某个调度实现中的临时便利逻辑。这里的核心问题始终是：某种能力是否仍然围绕 Cron 计划本身展开，还是已经开始转向任务执行与调度控制。如果是后者，就不应继续塞进这个模块。

扩展时尤其要避免两类偏差。一类是把 Cron 模块做成“带回调的任务执行器”，让它逐步承担不属于计划模型的职责；另一类是为了方便而把底层解析器的细碎内部能力直接公开，导致外部接入依赖不稳定的内部结构。更稳妥的做法，是围绕计划模式、计划匹配和计划运行点这些稳定语义组织公共 API，并把解析器适配细节保持在内部。

### JSDoc 注释格式要求

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

### 实现规范要求

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

### 导出策略要求

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

### 测试要求

- 若程序元素是函数，则只为该函数编写一个测试，如果该函数需要测试多个用例，应放在同一个测试中。
- 若程序元素是类，则至少要为该类的每一个方法编写一个测试，如果该方法需要测试多个用例，应放在同一个测试中。
- 若程序元素是类，除了为该类的每一个方法编写至少一个测试之外，还可以为该类编写任意多个测试，以覆盖该类的不同使用场景或边界情况。
- 若编写测试时需要用到辅助元素（Mock 或 Spy 等），可以在测试文件中直接定义这些辅助元素。若辅助元素较为简单，则可以直接放在每一个测试内部，优先保证每个测试的独立性，而不是追求极致 DRY；若辅助元素较为复杂或需要在多个测试中复用，则可以放在测试文件顶部，供该测试文件中的所有测试使用。
- 测试顺序应与源文件中被测试目标的原始顺序保持一致。
- 若该模块不需要测试，必须在说明文件中明确说明该模块不需要测试，并说明理由。一般来说，只有在该模块没有可执行的公共函数、只承载类型层表达，或其语义已被上层模块的测试完整覆盖且重复测试几乎不再带来额外价值时，才适合这样处理。
- 模块的单元测试文件目录是 `./tests/unit/cron`。
- 对 Cron 相关能力，应优先覆盖计划参数传递、过去与未来运行点推导、空结果规范化以及时间匹配判断等场景。
