@planet-matrix/mobius-model 0.6.0 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/oxlint.config.ts +1 -2
  3. package/package.json +29 -17
  4. package/scripts/build.ts +2 -52
  5. package/src/ai/README.md +1 -0
  6. package/src/ai/ai.ts +107 -0
  7. package/src/ai/chat-completion-ai/aihubmix-chat-completion.ts +78 -0
  8. package/src/ai/chat-completion-ai/chat-completion-ai.ts +270 -0
  9. package/src/ai/chat-completion-ai/chat-completion.ts +189 -0
  10. package/src/ai/chat-completion-ai/index.ts +7 -0
  11. package/src/ai/chat-completion-ai/lingyiwanwu-chat-completion.ts +78 -0
  12. package/src/ai/chat-completion-ai/ohmygpt-chat-completion.ts +78 -0
  13. package/src/ai/chat-completion-ai/openai-next-chat-completion.ts +78 -0
  14. package/src/ai/embedding-ai/embedding-ai.ts +63 -0
  15. package/src/ai/embedding-ai/embedding.ts +50 -0
  16. package/src/ai/embedding-ai/index.ts +4 -0
  17. package/src/ai/embedding-ai/openai-next-embedding.ts +23 -0
  18. package/src/ai/index.ts +4 -0
  19. package/src/aio/README.md +100 -0
  20. package/src/aio/content.ts +141 -0
  21. package/src/aio/index.ts +3 -0
  22. package/src/aio/json.ts +127 -0
  23. package/src/aio/prompt.ts +246 -0
  24. package/src/basic/README.md +20 -15
  25. package/src/basic/error.ts +19 -5
  26. package/src/basic/function.ts +2 -2
  27. package/src/basic/index.ts +1 -0
  28. package/src/basic/promise.ts +141 -71
  29. package/src/basic/schedule.ts +111 -0
  30. package/src/basic/stream.ts +135 -25
  31. package/src/credential/README.md +107 -0
  32. package/src/credential/api-key.ts +158 -0
  33. package/src/credential/bearer.ts +73 -0
  34. package/src/credential/index.ts +4 -0
  35. package/src/credential/json-web-token.ts +96 -0
  36. package/src/credential/password.ts +170 -0
  37. package/src/cron/README.md +86 -0
  38. package/src/cron/cron.ts +87 -0
  39. package/src/cron/index.ts +1 -0
  40. package/src/drizzle/README.md +1 -0
  41. package/src/drizzle/drizzle.ts +1 -0
  42. package/src/drizzle/helper.ts +47 -0
  43. package/src/drizzle/index.ts +5 -0
  44. package/src/drizzle/infer.ts +52 -0
  45. package/src/drizzle/kysely.ts +8 -0
  46. package/src/drizzle/pagination.ts +198 -0
  47. package/src/email/README.md +1 -0
  48. package/src/email/index.ts +1 -0
  49. package/src/email/resend.ts +25 -0
  50. package/src/event/class-event-proxy.ts +5 -6
  51. package/src/event/common.ts +13 -3
  52. package/src/event/event-manager.ts +3 -3
  53. package/src/event/instance-event-proxy.ts +5 -6
  54. package/src/event/internal.ts +4 -4
  55. package/src/exception/README.md +28 -19
  56. package/src/exception/error/error.ts +123 -0
  57. package/src/exception/error/index.ts +2 -0
  58. package/src/exception/error/match.ts +38 -0
  59. package/src/exception/error/must-fix.ts +17 -0
  60. package/src/exception/index.ts +2 -0
  61. package/src/file-system/find.ts +53 -0
  62. package/src/file-system/index.ts +2 -0
  63. package/src/file-system/path.ts +76 -0
  64. package/src/file-system/resolve.ts +22 -0
  65. package/src/form/README.md +25 -0
  66. package/src/form/index.ts +1 -0
  67. package/src/form/inputor-controller/base.ts +861 -0
  68. package/src/form/inputor-controller/boolean.ts +39 -0
  69. package/src/form/inputor-controller/file.ts +39 -0
  70. package/src/form/inputor-controller/form.ts +179 -0
  71. package/src/form/inputor-controller/helper.ts +117 -0
  72. package/src/form/inputor-controller/index.ts +17 -0
  73. package/src/form/inputor-controller/multi-select.ts +99 -0
  74. package/src/form/inputor-controller/number.ts +116 -0
  75. package/src/form/inputor-controller/select.ts +109 -0
  76. package/src/form/inputor-controller/text.ts +82 -0
  77. package/src/http/READMD.md +1 -0
  78. package/src/http/api/api-core.ts +84 -0
  79. package/src/http/api/api-handler.ts +79 -0
  80. package/src/http/api/api-host.ts +47 -0
  81. package/src/http/api/api-result.ts +56 -0
  82. package/src/http/api/api-schema.ts +154 -0
  83. package/src/http/api/api-server.ts +130 -0
  84. package/src/http/api/api-test.ts +142 -0
  85. package/src/http/api/api-type.ts +34 -0
  86. package/src/http/api/api.ts +81 -0
  87. package/src/http/api/index.ts +11 -0
  88. package/src/http/api-adapter/api-core-node-http.ts +260 -0
  89. package/src/http/api-adapter/api-host-node-http.ts +156 -0
  90. package/src/http/api-adapter/api-result-arktype.ts +294 -0
  91. package/src/http/api-adapter/api-result-zod.ts +286 -0
  92. package/src/http/api-adapter/index.ts +5 -0
  93. package/src/http/bin/gen-api-list/gen-api-list.ts +126 -0
  94. package/src/http/bin/gen-api-list/index.ts +1 -0
  95. package/src/http/bin/gen-api-test/gen-api-test.ts +136 -0
  96. package/src/http/bin/gen-api-test/index.ts +1 -0
  97. package/src/http/bin/gen-api-type/calc-code.ts +25 -0
  98. package/src/http/bin/gen-api-type/gen-api-type.ts +127 -0
  99. package/src/http/bin/gen-api-type/index.ts +2 -0
  100. package/src/http/bin/index.ts +2 -0
  101. package/src/http/index.ts +3 -0
  102. package/src/huawei/README.md +1 -0
  103. package/src/huawei/index.ts +2 -0
  104. package/src/huawei/moderation/index.ts +1 -0
  105. package/src/huawei/moderation/moderation.ts +355 -0
  106. package/src/huawei/obs/esdk-obs-nodejs.d.ts +87 -0
  107. package/src/huawei/obs/index.ts +1 -0
  108. package/src/huawei/obs/obs.ts +42 -0
  109. package/src/index.ts +21 -2
  110. package/src/json/README.md +92 -0
  111. package/src/json/index.ts +1 -0
  112. package/src/json/repair.ts +18 -0
  113. package/src/log/logger.ts +15 -4
  114. package/src/openai/README.md +1 -0
  115. package/src/openai/index.ts +1 -0
  116. package/src/openai/openai.ts +509 -0
  117. package/src/orchestration/README.md +9 -7
  118. package/src/orchestration/dispatching/dispatcher.ts +83 -0
  119. package/src/orchestration/dispatching/index.ts +2 -0
  120. package/src/orchestration/dispatching/selector/base-selector.ts +39 -0
  121. package/src/orchestration/dispatching/selector/down-count-selector.ts +119 -0
  122. package/src/orchestration/dispatching/selector/index.ts +2 -0
  123. package/src/orchestration/index.ts +2 -0
  124. package/src/orchestration/scheduling/index.ts +2 -0
  125. package/src/orchestration/scheduling/scheduler.ts +103 -0
  126. package/src/orchestration/scheduling/task.ts +32 -0
  127. package/src/random/README.md +8 -7
  128. package/src/random/base.ts +66 -0
  129. package/src/random/index.ts +5 -1
  130. package/src/random/random-boolean.ts +40 -0
  131. package/src/random/random-integer.ts +60 -0
  132. package/src/random/random-number.ts +72 -0
  133. package/src/random/random-string.ts +66 -0
  134. package/src/request/README.md +108 -0
  135. package/src/request/fetch/base.ts +108 -0
  136. package/src/request/fetch/browser.ts +280 -0
  137. package/src/request/fetch/general.ts +20 -0
  138. package/src/request/fetch/index.ts +4 -0
  139. package/src/request/fetch/nodejs.ts +280 -0
  140. package/src/request/index.ts +2 -0
  141. package/src/request/request/base.ts +246 -0
  142. package/src/request/request/general.ts +63 -0
  143. package/src/request/request/index.ts +3 -0
  144. package/src/request/request/resource.ts +68 -0
  145. package/src/result/README.md +4 -0
  146. package/src/result/controller.ts +58 -0
  147. package/src/result/either.ts +363 -0
  148. package/src/result/generator.ts +168 -0
  149. package/src/result/index.ts +3 -0
  150. package/src/route/README.md +105 -0
  151. package/src/route/adapter/browser.ts +122 -0
  152. package/src/route/adapter/driver.ts +56 -0
  153. package/src/route/adapter/index.ts +2 -0
  154. package/src/route/index.ts +3 -0
  155. package/src/route/router/index.ts +2 -0
  156. package/src/route/router/route.ts +630 -0
  157. package/src/route/router/router.ts +1641 -0
  158. package/src/route/uri/hash.ts +307 -0
  159. package/src/route/uri/index.ts +7 -0
  160. package/src/route/uri/pathname.ts +376 -0
  161. package/src/route/uri/search.ts +412 -0
  162. package/src/service/README.md +1 -0
  163. package/src/service/index.ts +1 -0
  164. package/src/service/service.ts +110 -0
  165. package/src/socket/README.md +105 -0
  166. package/src/socket/client/index.ts +2 -0
  167. package/src/socket/client/socket-unit.ts +658 -0
  168. package/src/socket/client/socket.ts +203 -0
  169. package/src/socket/common/index.ts +2 -0
  170. package/src/socket/common/socket-unit-common.ts +23 -0
  171. package/src/socket/common/socket-unit-heartbeat.ts +427 -0
  172. package/src/socket/index.ts +3 -0
  173. package/src/socket/server/index.ts +3 -0
  174. package/src/socket/server/server.ts +183 -0
  175. package/src/socket/server/socket-unit.ts +448 -0
  176. package/src/socket/server/socket.ts +264 -0
  177. package/src/storage/table.ts +3 -3
  178. package/src/timer/expiration/expiration-manager.ts +3 -3
  179. package/src/timer/expiration/remaining-manager.ts +3 -3
  180. package/src/tube/README.md +99 -0
  181. package/src/tube/helper.ts +137 -0
  182. package/src/tube/index.ts +2 -0
  183. package/src/tube/tube.ts +880 -0
  184. package/src/weixin/README.md +1 -0
  185. package/src/weixin/index.ts +2 -0
  186. package/src/weixin/official-account/authorization.ts +157 -0
  187. package/src/weixin/official-account/index.ts +2 -0
  188. package/src/weixin/official-account/js-api.ts +132 -0
  189. package/src/weixin/open/index.ts +1 -0
  190. package/src/weixin/open/oauth2.ts +131 -0
  191. package/tests/unit/ai/ai.spec.ts +85 -0
  192. package/tests/unit/aio/content.spec.ts +105 -0
  193. package/tests/unit/aio/json.spec.ts +146 -0
  194. package/tests/unit/aio/prompt.spec.ts +111 -0
  195. package/tests/unit/basic/error.spec.ts +16 -4
  196. package/tests/unit/basic/promise.spec.ts +158 -50
  197. package/tests/unit/basic/schedule.spec.ts +74 -0
  198. package/tests/unit/basic/stream.spec.ts +90 -37
  199. package/tests/unit/credential/api-key.spec.ts +36 -0
  200. package/tests/unit/credential/bearer.spec.ts +23 -0
  201. package/tests/unit/credential/json-web-token.spec.ts +23 -0
  202. package/tests/unit/credential/password.spec.ts +40 -0
  203. package/tests/unit/cron/cron.spec.ts +84 -0
  204. package/tests/unit/event/class-event-proxy.spec.ts +3 -3
  205. package/tests/unit/event/event-manager.spec.ts +3 -3
  206. package/tests/unit/event/instance-event-proxy.spec.ts +3 -3
  207. package/tests/unit/exception/error/error.spec.ts +83 -0
  208. package/tests/unit/exception/error/match.spec.ts +81 -0
  209. package/tests/unit/form/inputor-controller/base.spec.ts +458 -0
  210. package/tests/unit/form/inputor-controller/boolean.spec.ts +30 -0
  211. package/tests/unit/form/inputor-controller/file.spec.ts +27 -0
  212. package/tests/unit/form/inputor-controller/form.spec.ts +120 -0
  213. package/tests/unit/form/inputor-controller/helper.spec.ts +67 -0
  214. package/tests/unit/form/inputor-controller/multi-select.spec.ts +34 -0
  215. package/tests/unit/form/inputor-controller/number.spec.ts +36 -0
  216. package/tests/unit/form/inputor-controller/select.spec.ts +49 -0
  217. package/tests/unit/form/inputor-controller/text.spec.ts +34 -0
  218. package/tests/unit/http/api/api-core-host.spec.ts +207 -0
  219. package/tests/unit/http/api/api-schema.spec.ts +120 -0
  220. package/tests/unit/http/api/api-server.spec.ts +363 -0
  221. package/tests/unit/http/api/api-test.spec.ts +117 -0
  222. package/tests/unit/http/api/api.spec.ts +121 -0
  223. package/tests/unit/http/api-adapter/node-http.spec.ts +187 -0
  224. package/tests/unit/identifier/uuid.spec.ts +0 -1
  225. package/tests/unit/json/repair.spec.ts +11 -0
  226. package/tests/unit/log/logger.spec.ts +19 -4
  227. package/tests/unit/openai/openai.spec.ts +64 -0
  228. package/tests/unit/orchestration/dispatching/dispatcher.spec.ts +41 -0
  229. package/tests/unit/orchestration/dispatching/selector/down-count-selector.spec.ts +81 -0
  230. package/tests/unit/orchestration/scheduling/scheduler.spec.ts +103 -0
  231. package/tests/unit/random/base.spec.ts +58 -0
  232. package/tests/unit/random/random-boolean.spec.ts +25 -0
  233. package/tests/unit/random/random-integer.spec.ts +32 -0
  234. package/tests/unit/random/random-number.spec.ts +33 -0
  235. package/tests/unit/random/random-string.spec.ts +22 -0
  236. package/tests/unit/request/fetch/browser.spec.ts +222 -0
  237. package/tests/unit/request/fetch/general.spec.ts +43 -0
  238. package/tests/unit/request/fetch/nodejs.spec.ts +225 -0
  239. package/tests/unit/request/request/base.spec.ts +382 -0
  240. package/tests/unit/request/request/general.spec.ts +160 -0
  241. package/tests/unit/result/controller.spec.ts +82 -0
  242. package/tests/unit/result/either.spec.ts +377 -0
  243. package/tests/unit/result/generator.spec.ts +273 -0
  244. package/tests/unit/route/router/route.spec.ts +430 -0
  245. package/tests/unit/route/router/router.spec.ts +407 -0
  246. package/tests/unit/route/uri/hash.spec.ts +72 -0
  247. package/tests/unit/route/uri/pathname.spec.ts +146 -0
  248. package/tests/unit/route/uri/search.spec.ts +107 -0
  249. package/tests/unit/socket/client.spec.ts +208 -0
  250. package/tests/unit/socket/server.spec.ts +133 -0
  251. package/tests/unit/socket/socket-unit-heartbeat.spec.ts +214 -0
  252. package/tests/unit/tube/helper.spec.ts +139 -0
  253. package/tests/unit/tube/tube.spec.ts +501 -0
  254. package/vite.config.ts +2 -1
  255. package/dist/index.js +0 -50
  256. package/dist/index.js.map +0 -209
  257. package/src/random/string.ts +0 -35
  258. package/tests/unit/random/string.spec.ts +0 -11
@@ -2,48 +2,53 @@
2
2
 
3
3
  ## Description
4
4
 
5
- Basic 模块提供面向 JavaScript 运行时值(runtime value)的通用基础能力,用于围绕基础类型、内建对象、常见容器与轻量流程控制建立稳定、可组合、低假设的公共语义。
5
+ Basic 模块提供面向 JavaScript 运行时值(runtime value)的通用基础能力,用于承载一组稳定、可组合、低假设的便捷操作。
6
6
 
7
- 它关注的不是某个具体业务领域,而是那些可以长期复用的值级操作:判断、转换、裁剪、组合、格式化、调度、异步组织,以及与少量宿主无关接口的桥接。
7
+ 这里所说的“运行时值”,既包括 primitive value,也包括 JavaScript 运行时原生提供的常见对象与接口,例如 `Function`、`Promise`、`ReadableStream`、`Date`、`Error`、`RegExp` 等。
8
+
9
+ 它关注的不是某个具体业务领域,而是围绕运行时值展开、可以长期复用的基础处理能力。
8
10
 
9
11
  ## For Understanding
10
12
 
11
- 理解 Basic 模块时,首先要把握它的核心边界:这里承载的是“值如何被稳定处理”,而不是“宿主环境如何被接入和驱动”。只要某项能力的主要价值在于处理字符串、数字、布尔值、BigInt、符号(Symbol)、数组、对象、函数、Promise、Error、正则表达式(regular expression)、时间值、流(stream)等运行时值,它通常就有机会属于 Basic。
13
+ 理解 Basic 模块时,首先要把握它的核心边界:这里承载的是“运行时直接提供的值如何被方便、稳定地处理”,而不是“围绕某个自定义对象系统、业务模型或宿主入口建立长期语义”。只要某项能力的主要价值在于处理字符串、数字、布尔值、BigInt、符号(Symbol)、数组、对象、函数、Promise、Error、正则表达式(regular expression)、时间值、流(stream)等运行时值,它通常就有机会属于 Basic。
14
+
15
+ 从这个角度看,`function`、`promise`、`stream` 与 `boolean`、`string`、`object`、`array`、`number` 没有本质区别,都是运行时已经给出的基础对象形态。
12
16
 
13
- 反过来说,如果某项能力的重点已经变成注册全局监听器、驱动宿主生命周期、读取运行时上下文、感知平台能力,或消费宿主级事件源,那么它通常就不再属于 Basic,即使它最后处理的对象仍然表现为某种值。Basic 关心的是值级语义,而不是环境级语义。
17
+ 反过来说,如果某项能力的重点已经变成注册全局监听器、读取运行时上下文、感知平台能力、消费宿主级事件源,或围绕某类自定义对象建立独立问题域,那么它通常就不再属于 BasicBasic 关心的是运行时值的通用处理语义,而不是环境级语义、业务对象语义或更高层的领域模型语义。
14
18
 
15
19
  这也意味着 Basic 不是“随手工具箱”。一个能力只有在满足以下条件时,才适合进入这个模块:
16
20
 
17
- - 它表达的是稳定、清楚、可复用的值级问题域。
21
+ - 它表达的是稳定、清楚、可复用的值级问题。
18
22
  - 它对调用方前提的假设足够少,能在不同项目中长期成立。
19
- - 它适合通过清楚的输入输出语义被组合,而不是依赖隐式上下文或宿主副作用。
23
+ - 它适合通过清楚的输入输出语义被组合,而不是依赖隐式上下文、特定业务约定或某个自定义对象体系的内部状态。
20
24
 
21
25
  ## For Using
22
26
 
23
- 当你需要把分散在业务代码中的基础判断、值转换、容器处理和轻量流程控制整理成稳定能力时,可以使用这个模块。它适合放在业务逻辑与语言原生能力之间,作为一层更可复用、也更可测试的基础模型层。
27
+ 当你需要把分散在业务代码中的基础判断、值转换、容器处理,以及围绕函数、Promise、流等运行时原生对象的便捷操作整理成稳定能力时,可以使用这个模块。它适合放在业务逻辑与语言原生能力之间,作为一层更可复用、更可测试的基础模型层。
24
28
 
25
29
  当前公共能力大致可以按以下几类理解:
26
30
 
27
- - 值判断与基础守卫:例如 primitive、对象、数组、空值、数字特征等运行时判断,用于减少重复的分支代码。
31
+ - 值判断与基础守卫:例如 primitive、对象、数组、空值、数字特征等运行时判断,用于减少重复分支。
28
32
  - 基础值处理:例如字符串、数字、布尔值、BigInt、符号等常见值类型的转换、裁剪、计算与格式化。
29
33
  - 容器处理:例如数组与对象的筛选、映射、分区、裁剪、合并、字段选择与标准化,适合在进入业务模型前先整理数据形态。
30
- - 函数与流程控制:例如一次性调用、防抖、节流、组合、管道、记忆化、条件执行等轻量控制能力。
34
+ - 函数相关:例如一次性调用、防抖、节流、组合、管道、记忆化、条件执行等围绕函数值展开的便捷能力。
31
35
  - 异步与流处理:例如 Promise 队列、重试、轮询、失败结果表达,以及围绕 `ReadableStream` 的基础消费与转换。
32
36
  - 时间、错误与正则辅助:例如时间格式化、异常字符串化、常见模式判断等值级辅助能力。
33
37
  - 显式增强能力:如果某个能力会对内建对象做增强,它必须保持显式调用、行为可审查,并且不应成为其它公共能力成立的隐含前提。
34
38
 
35
- 如果你的问题已经需要环境感知、平台能力探测、全局异常监听或更明确的领域语义,应优先考虑 Environment、Exception 或其它更贴近问题域的模块,而不是继续把能力堆进 Basic。
39
+ 如果你的问题已经需要环境感知、平台能力探测、全局异常监听,或需要围绕某类自定义对象建立独立问题域,应优先考虑 Environment、Exception 或其它更贴近问题域的模块,而不是继续把能力堆进 Basic。
36
40
 
37
41
  ## For Contributing
38
42
 
39
- 贡献 Basic 模块时,首要任务不是补齐一个方便函数,而是确认这项能力是否真的表达了稳定的值级语义。这里应长期承载的是对运行时值的清楚建模,而不是对某个调用点临时痛点的修补。
43
+ 贡献 Basic 模块时,首要任务不是补齐一个方便函数,而是确认这项能力是否真的表达了稳定、可复用的运行时基础语义。这里应长期承载的是对运行时值的清楚整理,而不是对某个调用点的临时修补。
40
44
 
41
45
  在扩展时,应优先遵守以下模块边界:
42
46
 
43
- - 只收纳围绕运行时值本身展开的判断、转换、组合、格式化与轻量流程控制。
47
+ - 只收纳围绕运行时值展开的判断、转换、组合、格式化与其它稳定便捷操作。
44
48
  - 不把环境探测、宿主监听、全局状态操纵或平台接入逻辑放入 Basic。
45
- - 不为了省几行调用代码而公开一次性便捷包装;只有值得长期承诺的公共语义才应进入模块。
49
+ - 不为了省几行调用代码就公开一次性便捷包装;只有值得长期承诺的公共语义才应进入模块。
46
50
  - 如果某项能力依赖显式增强内建对象,必须让增强保持可选,而不是让整个模块隐式建立在原型修改之上。
51
+ - 若新增能力主要是在围绕某类自定义对象、某套业务状态机、某个框架生命周期或某个宿主入口建立独立问题域,那么它通常应进入更贴近该问题域的模块,而不是留在 Basic。
47
52
 
48
53
  ### JSDoc 注释格式要求
49
54
 
@@ -74,14 +79,14 @@ Basic 模块提供面向 JavaScript 运行时值(runtime value)的通用基
74
79
  - 子模块不需要有自己的 `README.md`。
75
80
  - 子模块可以有自己的 `internal.ts` 文件,多个子模块共享的辅助元素应该放在父模块的 `internal.ts` 文件中,单个子模块共享的辅助元素应该放在该子模块的 `internal.ts` 文件中。
76
81
  - 对模块依赖关系的要求(通常是不循环依赖或不反向依赖)与对 DRY 的要求可能产生冲突。此时,若复用的代码数量不大,可以适当牺牲 DRY,复制粘贴并保留必要的注释说明;若复用的代码数量较大,则可以将其抽象到新的文件或子模块中,如 `common.ts`,并在需要的地方导入使用。
77
- - Basic 的实现应优先保证输入输出语义直接、低副作用、可组合,而不是追求隐式魔法或宿主绑定行为。
82
+ - Basic 的实现应优先保证输入输出语义直接、低副作用、可组合,而不是追求隐式魔法、业务耦合或依赖特定框架约定的包装。
78
83
 
79
84
  ### 导出策略要求
80
85
 
81
86
  - 保持内部辅助项和内部符号为私有,不要让外部接入依赖临时性的内部结构。
82
87
  - 每个模块都应有一个用于重导出所有公共 API 的 Barrel 文件。
83
88
  - Barrel 文件应命名为 `index.ts`,放在模块目录根部,并且所有公共 API 都应从该文件导出。
84
- - 新增公共能力时,应优先检查它是否表达稳定、清楚且值得长期维护的值级语义,而不是某段实现细节的便捷暴露;仅在确认需要长期对外承诺时再加入 Barrel 导出。
89
+ - 新增公共能力时,应优先检查它是否表达稳定、清楚且值得长期维护的运行时基础语义,而不是某段实现细节、业务约定或一次性便捷暴露;仅在确认需要长期对外承诺时再加入 Barrel 导出。
85
90
 
86
91
  ### 测试要求
87
92
 
@@ -39,13 +39,27 @@ export const errorIsNetworkError = (error: unknown): error is Error => {
39
39
  }
40
40
 
41
41
  /**
42
- * Stringifies an exception into a readable format.
42
+ * Stringifies an error into a readable format.
43
43
  */
44
- export const errorStringifyException = (exception: unknown): string => {
45
- if (isError(exception)) {
46
- return `${exception.name}: ${exception.message}`
44
+ export const errorStringify = (error: unknown): string => {
45
+ if (isError(error)) {
46
+ return `${error.name}: ${error.message}`
47
47
  }
48
48
  else {
49
- return `${String(exception)}`
49
+ return `${String(error)}`
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Converts a value to an Error object. If the value is already an Error,
55
+ * it is returned as is. Otherwise, a new Error is created with the
56
+ * stringified value as its message.
57
+ */
58
+ export const toError = (value: unknown): Error => {
59
+ if (isError(value)) {
60
+ return value
61
+ }
62
+ else {
63
+ return new Error(String(value))
50
64
  }
51
65
  }
@@ -95,7 +95,7 @@ export const functionDebounceSimple = <T extends AnyFunction>(
95
95
  fn: T,
96
96
  ms: number
97
97
  ): FunctionDebouncedSimple<T> => {
98
- let timer: NodeJS.Timeout
98
+ let timer: ReturnType<typeof setTimeout>
99
99
  return (...args) => {
100
100
  clearTimeout(timer)
101
101
  timer = setTimeout(() => {
@@ -127,7 +127,7 @@ export const debounce = <T extends AnyFunction>(
127
127
  fn: T,
128
128
  ms: number
129
129
  ): FunctionDebounced<T> => {
130
- let timer: NodeJS.Timeout
130
+ let timer: ReturnType<typeof setTimeout>
131
131
  let waiting: AnyFunction[] = []
132
132
  return async (...args) => {
133
133
  clearTimeout(timer)
@@ -13,5 +13,6 @@ export * from "./error.ts"
13
13
  export * from "./regexp.ts"
14
14
  export * from "./promise.ts"
15
15
  export * from "./stream.ts"
16
+ export * from "./schedule.ts"
16
17
 
17
18
  export * from "./enhance.ts"
@@ -6,16 +6,16 @@ import { isPlainObject } from "./is.ts"
6
6
  * @example
7
7
  * ```
8
8
  * // Expect: 6
9
- * const example1 = await promiseThen((value: number) => value * 2, Promise.resolve(3))
9
+ * const example1 = await promiseThen(Promise.resolve(3), (value: number) => value * 2)
10
10
  * // Expect: "ok!"
11
- * const example2 = await promiseThen((value: string) => `${value}!`, Promise.resolve("ok"))
11
+ * const example2 = await promiseThen(Promise.resolve("ok"), (value: string) => `${value}!`)
12
12
  * ```
13
13
  */
14
14
  export const promiseThen = async <V, R>(
15
- doSomething: (value: V) => R | PromiseLike<R>,
16
15
  target: Promise<V>,
16
+ doSomething: (value: V) => R | PromiseLike<R>,
17
17
  ): Promise<R> => {
18
- return await target.then(doSomething)
18
+ return await target.then((value) => doSomething(value))
19
19
  }
20
20
 
21
21
  /**
@@ -24,16 +24,16 @@ export const promiseThen = async <V, R>(
24
24
  * @example
25
25
  * ```
26
26
  * // Expect: "fallback"
27
- * const example1 = await promiseCatch(() => "fallback", Promise.reject(new Error("x")))
27
+ * const example1 = await promiseCatch(Promise.reject(new Error("x")), () => "fallback")
28
28
  * // Expect: 3
29
- * const example2 = await promiseCatch(() => 0, Promise.resolve(3))
29
+ * const example2 = await promiseCatch(Promise.resolve(3), () => 0)
30
30
  * ```
31
31
  */
32
32
  export const promiseCatch = async <V, R>(
33
- doSomething: (reason: unknown) => R | PromiseLike<R>,
34
33
  target: Promise<V>,
34
+ doSomething: (reason: unknown) => R | PromiseLike<R>,
35
35
  ): Promise<V | R> => {
36
- return await target.catch(doSomething)
36
+ return await target.catch((reason: unknown) => doSomething(reason))
37
37
  }
38
38
 
39
39
  /**
@@ -43,25 +43,54 @@ export const promiseCatch = async <V, R>(
43
43
  * ```
44
44
  * let cleaned = false
45
45
  * // Expect: 10
46
- * const example1 = await promiseFinally(() => { cleaned = true }, Promise.resolve(10))
46
+ * const example1 = await promiseFinally(Promise.resolve(10), () => { cleaned = true })
47
47
  * // Expect: true
48
48
  * const example2 = cleaned
49
49
  * ```
50
50
  */
51
51
  export const promiseFinally = async <V>(
52
- doSomething: () => void,
53
52
  target: Promise<V>,
53
+ doSomething: () => void,
54
54
  ): Promise<V> => {
55
- return await target.finally(doSomething)
55
+ return await target.finally(() => doSomething())
56
+ }
57
+
58
+ export interface PromiseDeferred<V> {
59
+ promise: Promise<V>
60
+ resolve: (value: V) => void
61
+ reject: (reason: unknown) => void
62
+ }
63
+ /**
64
+ * Create a deferred promise with external resolve and reject functions.
65
+ *
66
+ * @example
67
+ * ```
68
+ * const deferred = promiseDeferred<number>()
69
+ * // Expect: typeof deferred.resolve === "function"
70
+ * const example1 = typeof deferred.resolve
71
+ * deferred.resolve(42)
72
+ * const example2 = await deferred.promise
73
+ * // Expect: 42
74
+ * ```
75
+ */
76
+ export const promiseDeferred = <V>(): PromiseDeferred<V> => {
77
+ let externalResolve!: (value: V) => void
78
+ let externalReject!: (reason: unknown) => void
79
+ const promise = new Promise<V>((resolve, reject) => {
80
+ externalResolve = resolve
81
+ externalReject = reject
82
+ })
83
+ return { promise, resolve: externalResolve, reject: externalReject }
56
84
  }
57
85
 
58
- const INTERNAL_PROMISE_FAIL_RESULT_TYPE: symbol = Symbol("fail")
86
+ const INTERNAL_PROMISE_FAIL_RESULT_TYPE: unique symbol = Symbol("fail")
87
+ type InternalPromiseFailResultType = typeof INTERNAL_PROMISE_FAIL_RESULT_TYPE
59
88
 
60
89
  /**
61
90
  * 表示标准化的 Promise 失败结果。
62
91
  */
63
92
  export interface PromiseFailResult {
64
- __type__: typeof INTERNAL_PROMISE_FAIL_RESULT_TYPE
93
+ __type__: InternalPromiseFailResultType
65
94
  reason: unknown
66
95
  }
67
96
 
@@ -75,14 +104,23 @@ export interface PromiseIndexedFailResult extends PromiseFailResult {
75
104
  /**
76
105
  * 根据拒因构造标准化的 Promise 失败结果。
77
106
  */
78
- export const promiseConstructFailResult = (reason: unknown): PromiseFailResult => {
107
+ export const promiseCreateFailResult = (reason: unknown): PromiseFailResult => {
79
108
  return { __type__: INTERNAL_PROMISE_FAIL_RESULT_TYPE, reason }
80
109
  }
81
110
  /**
82
111
  * 判断目标值是否为标准化的 Promise 失败结果。
83
112
  */
84
- export const isPromiseFailResult = (target: unknown): target is PromiseFailResult => {
85
- return isPlainObject(target) && target["__type__"] === INTERNAL_PROMISE_FAIL_RESULT_TYPE
113
+ export const promiseIsFailResult = (target: unknown): target is PromiseFailResult => {
114
+ if (isPlainObject(target) === false) {
115
+ return false
116
+ }
117
+ if ("__type__" in target === false) {
118
+ return false
119
+ }
120
+ if (target["__type__"] !== INTERNAL_PROMISE_FAIL_RESULT_TYPE) {
121
+ return false
122
+ }
123
+ return true
86
124
  }
87
125
  /**
88
126
  * 用作 `Promise.catch` 的 `onrejected` 回调,并返回标准化失败结果。
@@ -98,9 +136,8 @@ export const promiseFilterSuccessResults = <V>(
98
136
  results: Array<V | PromiseFailResult>,
99
137
  ): V[] => {
100
138
  const filtered = results.filter(result => {
101
- return isPromiseFailResult(result) === false
139
+ return promiseIsFailResult(result) === false
102
140
  })
103
- // oxlint-disable-next-line no-unsafe-type-assertion
104
141
  return filtered as V[]
105
142
  }
106
143
  /**
@@ -112,7 +149,7 @@ export const promiseFilterFailResults = <V>(
112
149
  const filtered = results.reduce<PromiseIndexedFailResult[]>((
113
150
  accumulatedResults, currentResult, index
114
151
  ) => {
115
- if (isPromiseFailResult(currentResult)) {
152
+ if (promiseIsFailResult(currentResult)) {
116
153
  accumulatedResults.push({ ...currentResult, index })
117
154
  }
118
155
  return accumulatedResults
@@ -131,8 +168,10 @@ interface PromiseQueueRestPromiseMakerContext<V, S = unknown> {
131
168
  previousResult: V | PromiseFailResult
132
169
  state: S
133
170
  }
134
- type PromiseQueueFirstPromiseMaker<T, S = unknown> = (context: PromiseQueueFirstPromiseMakerContext<S>) => Promise<T>
135
- type PromiseQueueRestPromiseMaker<T, S = unknown> = (context: PromiseQueueRestPromiseMakerContext<T, S>) => Promise<T>
171
+ type PromiseQueueFirstPromiseMaker<T, S = unknown>
172
+ = (context: PromiseQueueFirstPromiseMakerContext<S>) => Promise<T>
173
+ type PromiseQueueRestPromiseMaker<T, S = unknown>
174
+ = (context: PromiseQueueRestPromiseMakerContext<T, S>) => Promise<T>
136
175
  type PromiseQueuePromiseMakers<T, S = unknown> = [
137
176
  PromiseQueueFirstPromiseMaker<T, S>,
138
177
  ...Array<PromiseQueueRestPromiseMaker<T, S>>
@@ -155,7 +194,7 @@ export interface PromiseQueueOptions {
155
194
  * // Expect: [1, 2]
156
195
  * const example1 = await promiseQueue<number>([
157
196
  * async () => 1,
158
- * async ({ previousResult }) => (isPromiseFailResult(previousResult) ? 0 : previousResult + 1),
197
+ * async ({ previousResult }) => (promiseIsFailResult(previousResult) ? 0 : previousResult + 1),
159
198
  * ])
160
199
  * ```
161
200
  */
@@ -169,7 +208,6 @@ export const promiseQueue = async <T, S = unknown>(
169
208
  let context: PromiseQueueFirstPromiseMakerContext<S> | PromiseQueueRestPromiseMakerContext<T, S> = {
170
209
  index: 0,
171
210
  hasPreviousResult: false,
172
- // oxlint-disable-next-line no-unsafe-type-assertion
173
211
  state: undefined as unknown as S
174
212
  }
175
213
 
@@ -205,18 +243,20 @@ export const promiseQueue = async <T, S = unknown>(
205
243
  }
206
244
 
207
245
  interface PromiseRetryFirstPromiseMakerContext<S = unknown> {
208
- time: number
246
+ index: number
209
247
  hasPreviousResult: false
210
248
  state: S
211
249
  }
212
250
  interface PromiseRetryRestPromiseMakerContext<T, S = unknown> {
213
- time: number
251
+ index: number
214
252
  hasPreviousResult: true
215
253
  previousResult: T | PromiseFailResult
216
254
  state: S
217
255
  }
218
256
  type PromiseRetryPromiseMaker<T, S = unknown> = (
219
- context: PromiseRetryFirstPromiseMakerContext<S> | PromiseRetryRestPromiseMakerContext<T, S>
257
+ context:
258
+ | PromiseRetryFirstPromiseMakerContext<S>
259
+ | PromiseRetryRestPromiseMakerContext<T, S>
220
260
  ) => Promise<T>
221
261
 
222
262
  /**
@@ -228,9 +268,10 @@ export interface PromiseRetryOptions {
228
268
  */
229
269
  breakTime?: number
230
270
  /**
271
+ * `0` 表示仅执行首次尝试,不进行额外重试。
231
272
  * @default Infinity
232
273
  */
233
- maxTryTimes?: number
274
+ maxTryIndex?: number | undefined
234
275
  }
235
276
  /**
236
277
  * Retry while the predicate indicates the result is not acceptable.
@@ -257,34 +298,33 @@ export const promiseRetryWhile = async <T, S = unknown>(
257
298
  ): Promise<T | PromiseFailResult> => {
258
299
  const {
259
300
  breakTime = 0,
260
- maxTryTimes = Infinity,
301
+ maxTryIndex = Infinity,
261
302
  } = options ?? {}
262
303
 
263
- if (maxTryTimes < 1) {
264
- throw new Error("`maxTryTimes` must be greater than 0.")
304
+ if (maxTryIndex < 0) {
305
+ throw new Error("`maxTryIndex` must be greater than or equal to 0.")
265
306
  }
266
307
 
267
- let time = 1
308
+ let index = 0
268
309
  // oxlint-disable-next-line no-accumulating-spread
269
310
  let context: PromiseRetryFirstPromiseMakerContext<S> | PromiseRetryRestPromiseMakerContext<T, S> = {
270
- time,
311
+ index,
271
312
  hasPreviousResult: false,
272
- // oxlint-disable-next-line no-unsafe-type-assertion
273
313
  state: undefined as unknown as S
274
314
  }
275
315
  let result = await promiseMaker(context).catch(promiseCatchToFailResult)
276
316
 
277
317
  while (
278
- (isPromiseFailResult(result) || (await predicate(result)))
279
- && time < maxTryTimes
318
+ (promiseIsFailResult(result) || (await predicate(result)))
319
+ && index < maxTryIndex
280
320
  ) {
281
- time = time + 1
321
+ index = index + 1
282
322
  // oxlint-disable-next-line no-loop-func
283
323
  result = await new Promise<T | PromiseFailResult>((resolve) => {
284
324
  setTimeout(() => {
285
325
  context = {
286
326
  ...context,
287
- time,
327
+ index,
288
328
  hasPreviousResult: true,
289
329
  previousResult: result,
290
330
  }
@@ -315,40 +355,39 @@ export const promiseRetryWhile = async <T, S = unknown>(
315
355
  * @see {@link promiseRetryWhile}
316
356
  */
317
357
  export const promiseRetryUntil = async <T, S = unknown>(
318
- predicate: (value: T, time: number) => boolean | Promise<boolean>,
358
+ predicate: (value: T, index: number) => boolean | Promise<boolean>,
319
359
  promiseMaker: PromiseRetryPromiseMaker<T, S>,
320
360
  options?: PromiseRetryOptions | undefined,
321
361
  ): Promise<T | PromiseFailResult> => {
322
362
  const {
323
363
  breakTime = 0,
324
- maxTryTimes = Infinity,
364
+ maxTryIndex = Infinity,
325
365
  } = options ?? {}
326
366
 
327
- if (maxTryTimes < 1) {
328
- throw new Error("`maxTryTimes` must be greater than 0.")
367
+ if (maxTryIndex < 0) {
368
+ throw new Error("`maxTryIndex` must be greater than or equal to 0.")
329
369
  }
330
370
 
331
- let time = 1
371
+ let index = 0
332
372
  // oxlint-disable-next-line no-accumulating-spread
333
373
  let context: PromiseRetryFirstPromiseMakerContext<S> | PromiseRetryRestPromiseMakerContext<T, S> = {
334
- time,
374
+ index,
335
375
  hasPreviousResult: false,
336
- // oxlint-disable-next-line no-unsafe-type-assertion
337
376
  state: undefined as unknown as S
338
377
  }
339
378
  let result = await promiseMaker(context).catch(promiseCatchToFailResult)
340
379
 
341
380
  while (
342
- (isPromiseFailResult(result) || !(await predicate(result, time)))
343
- && time < maxTryTimes
381
+ (promiseIsFailResult(result) || !(await predicate(result, index)))
382
+ && index < maxTryIndex
344
383
  ) {
345
- time = time + 1
384
+ index = index + 1
346
385
  // oxlint-disable-next-line no-loop-func
347
386
  result = await new Promise<T | PromiseFailResult>((resolve) => {
348
387
  setTimeout(() => {
349
388
  context = {
350
389
  ...context,
351
- time,
390
+ index,
352
391
  hasPreviousResult: true,
353
392
  previousResult: result
354
393
  }
@@ -362,13 +401,14 @@ export const promiseRetryUntil = async <T, S = unknown>(
362
401
 
363
402
  /**
364
403
  * Start periodic asynchronous execution and return a function to stop it.
404
+ * Rejected runs are logged and ignored to avoid unhandled promise rejections.
365
405
  *
366
406
  * @example
367
407
  * ```
368
408
  * const records: number[] = []
369
- * const stop = promiseInterval(10, async (time) => {
370
- * records.push(time)
371
- * return time
409
+ * const stop = promiseInterval(10, async (index) => {
410
+ * records.push(index)
411
+ * return index
372
412
  * })
373
413
  * // Later: stop()
374
414
  * // Expect: typeof stop === "function"
@@ -377,13 +417,15 @@ export const promiseRetryUntil = async <T, S = unknown>(
377
417
  */
378
418
  export const promiseInterval = <T>(
379
419
  interval: number,
380
- promiseMaker: (time: number) => Promise<T>,
420
+ promiseMaker: (index: number) => Promise<T>,
381
421
  ): (() => void) => {
382
- let time = 1
422
+ let index = 0
383
423
 
384
424
  const intervalID = setInterval(() => {
385
- void promiseMaker(time)
386
- time = time + 1
425
+ void promiseMaker(index).catch((error: unknown) => {
426
+ console.error("[promiseInterval] unexpected error occurred:", error)
427
+ })
428
+ index = index + 1
387
429
  }, interval)
388
430
 
389
431
  return () => {
@@ -392,18 +434,20 @@ export const promiseInterval = <T>(
392
434
  }
393
435
 
394
436
  interface PromiseForeverFirstPromiseMakerContext<S = unknown> {
395
- time: number
437
+ index: number
396
438
  hasPreviousResult: false
397
439
  state: S
398
440
  }
399
441
  interface PromiseForeverRestPromiseMakerContext<T, S = unknown> {
400
- time: number
442
+ index: number
401
443
  hasPreviousResult: true
402
444
  previousResult: T | PromiseFailResult
403
445
  state: S
404
446
  }
405
447
  type PromiseForeverPromiseMaker<T, S = unknown> = (
406
- context: PromiseForeverFirstPromiseMakerContext<S> | PromiseForeverRestPromiseMakerContext<T, S>
448
+ context:
449
+ | PromiseForeverFirstPromiseMakerContext<S>
450
+ | PromiseForeverRestPromiseMakerContext<T, S>
407
451
  ) => Promise<T>
408
452
 
409
453
  /**
@@ -414,60 +458,80 @@ export interface PromiseForeverOptions {
414
458
  * @default 0
415
459
  */
416
460
  breakTime?: number | undefined
417
- onRejected?: ((time: number, result: PromiseFailResult) => void) | undefined
461
+ onRejected?: ((index: number, result: PromiseFailResult) => void) | undefined
462
+ }
463
+ export interface PromiseForeverResult {
464
+ index: number
465
+ isStopped: boolean
466
+ stop: () => void
418
467
  }
419
468
  /**
420
469
  * Continuously execute an async maker forever with optional delay and rejection hook.
421
470
  *
471
+ * - 至少会执行一次,`stop()` 可以用来提前停止后续执行。
472
+ * - `index` 从 `0` 开始,表示当前执行轮次。
473
+ * - 如果某一轮结束后已经排入了下一轮的定时器,调用 `stop()` 不会取消那一轮;
474
+ * 它只会阻止后续继续排入新的轮次。
475
+ *
422
476
  * @example
423
477
  * ```
424
478
  * let times = 0
425
- * promiseForever(async () => {
479
+ * const controller = promiseForever(async () => {
426
480
  * times = times + 1
427
481
  * return times
428
482
  * }, { breakTime: 0 })
429
- * // Expect: no return value
430
- * const example1 = promiseForever(async () => 1)
483
+ * controller.stop()
484
+ * // Expect: typeof controller.stop === "function"
485
+ * const example1 = typeof controller.stop
431
486
  * ```
432
487
  */
433
488
  export const promiseForever = <T, S = unknown>(
434
489
  promiseMaker: PromiseForeverPromiseMaker<T, S>,
435
490
  options?: PromiseForeverOptions | undefined,
436
- ): void => {
491
+ ): PromiseForeverResult => {
437
492
  const {
438
493
  breakTime = 0,
439
494
  onRejected,
440
495
  } = options ?? {}
441
496
 
442
- let time = 1
497
+ let index = 0
443
498
  let context: PromiseForeverFirstPromiseMakerContext<S> | PromiseForeverRestPromiseMakerContext<T, S> = {
444
- time,
499
+ index,
445
500
  hasPreviousResult: false,
446
- // oxlint-disable-next-line no-unsafe-type-assertion
447
501
  state: undefined as unknown as S
448
502
  }
503
+ const foreverResult: PromiseForeverResult = {
504
+ index,
505
+ isStopped: false,
506
+ stop: () => {
507
+ foreverResult.isStopped = true
508
+ }
509
+ }
449
510
 
450
511
  const handlePromise = (result: T | PromiseFailResult): void => {
451
- time = time + 1
452
512
  setTimeout(() => {
513
+ index = index + 1
453
514
  context = {
454
515
  ...context,
455
- time,
516
+ index,
456
517
  hasPreviousResult: true,
457
518
  previousResult: result
458
519
  }
520
+ foreverResult.index = index
459
521
  runPromise(context)
460
522
  }, breakTime)
461
523
  }
462
524
  const runPromise = (
463
- context: PromiseForeverFirstPromiseMakerContext<S> | PromiseForeverRestPromiseMakerContext<T, S>
525
+ context:
526
+ | PromiseForeverFirstPromiseMakerContext<S>
527
+ | PromiseForeverRestPromiseMakerContext<T, S>
464
528
  ): void => {
465
529
  void promiseMaker(context)
466
530
  .catch((error: unknown) => {
467
531
  const failedResult = promiseCatchToFailResult(error)
468
532
  if (onRejected !== undefined) {
469
533
  try {
470
- onRejected(time, failedResult)
534
+ onRejected(index, failedResult)
471
535
  }
472
536
  catch {
473
537
  // omit exceptions from execution of `onRejected`
@@ -478,8 +542,14 @@ export const promiseForever = <T, S = unknown>(
478
542
  }
479
543
  return failedResult
480
544
  })
481
- .then(result => handlePromise(result))
545
+ .then(result => {
546
+ if (foreverResult.isStopped === false) {
547
+ handlePromise(result)
548
+ }
549
+ })
482
550
  }
483
551
 
484
552
  runPromise(context)
553
+
554
+ return foreverResult
485
555
  }