@planet-matrix/mobius-model 0.5.0 → 0.9.0

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 (379) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/README.md +123 -36
  3. package/dist/index.js +715 -4
  4. package/dist/index.js.map +981 -13
  5. package/oxlint.config.ts +6 -0
  6. package/package.json +36 -18
  7. package/src/abort/README.md +92 -0
  8. package/src/abort/abort-manager.ts +278 -0
  9. package/src/abort/abort-signal-listener-manager.ts +81 -0
  10. package/src/abort/index.ts +2 -0
  11. package/src/ai/README.md +1 -0
  12. package/src/ai/ai.ts +107 -0
  13. package/src/ai/chat-completion-ai/aihubmix-chat-completion.ts +78 -0
  14. package/src/ai/chat-completion-ai/chat-completion-ai.ts +270 -0
  15. package/src/ai/chat-completion-ai/chat-completion.ts +189 -0
  16. package/src/ai/chat-completion-ai/index.ts +7 -0
  17. package/src/ai/chat-completion-ai/lingyiwanwu-chat-completion.ts +78 -0
  18. package/src/ai/chat-completion-ai/ohmygpt-chat-completion.ts +78 -0
  19. package/src/ai/chat-completion-ai/openai-next-chat-completion.ts +78 -0
  20. package/src/ai/embedding-ai/embedding-ai.ts +63 -0
  21. package/src/ai/embedding-ai/embedding.ts +50 -0
  22. package/src/ai/embedding-ai/index.ts +4 -0
  23. package/src/ai/embedding-ai/openai-next-embedding.ts +23 -0
  24. package/src/ai/index.ts +4 -0
  25. package/src/aio/README.md +100 -0
  26. package/src/aio/content.ts +141 -0
  27. package/src/aio/index.ts +3 -0
  28. package/src/aio/json.ts +127 -0
  29. package/src/aio/prompt.ts +246 -0
  30. package/src/basic/README.md +72 -116
  31. package/src/basic/error.ts +19 -5
  32. package/src/basic/function.ts +83 -64
  33. package/src/basic/index.ts +1 -0
  34. package/src/basic/is.ts +152 -71
  35. package/src/basic/promise.ts +29 -8
  36. package/src/basic/schedule.ts +111 -0
  37. package/src/basic/stream.ts +135 -25
  38. package/src/basic/string.ts +2 -33
  39. package/src/color/README.md +105 -0
  40. package/src/color/index.ts +3 -0
  41. package/src/color/internal.ts +42 -0
  42. package/src/color/rgb/analyze.ts +236 -0
  43. package/src/color/rgb/construct.ts +130 -0
  44. package/src/color/rgb/convert.ts +227 -0
  45. package/src/color/rgb/derive.ts +303 -0
  46. package/src/color/rgb/index.ts +6 -0
  47. package/src/color/rgb/internal.ts +208 -0
  48. package/src/color/rgb/parse.ts +302 -0
  49. package/src/color/rgb/serialize.ts +144 -0
  50. package/src/color/types.ts +57 -0
  51. package/src/color/xyz/analyze.ts +80 -0
  52. package/src/color/xyz/construct.ts +19 -0
  53. package/src/color/xyz/convert.ts +71 -0
  54. package/src/color/xyz/index.ts +3 -0
  55. package/src/color/xyz/internal.ts +23 -0
  56. package/src/credential/README.md +107 -0
  57. package/src/credential/api-key.ts +158 -0
  58. package/src/credential/bearer.ts +73 -0
  59. package/src/credential/index.ts +4 -0
  60. package/src/credential/json-web-token.ts +96 -0
  61. package/src/credential/password.ts +170 -0
  62. package/src/cron/README.md +86 -0
  63. package/src/cron/cron.ts +87 -0
  64. package/src/cron/index.ts +1 -0
  65. package/src/css/README.md +93 -0
  66. package/src/css/class.ts +559 -0
  67. package/src/css/index.ts +1 -0
  68. package/src/drizzle/README.md +1 -0
  69. package/src/drizzle/drizzle.ts +1 -0
  70. package/src/drizzle/helper.ts +47 -0
  71. package/src/drizzle/index.ts +5 -0
  72. package/src/drizzle/infer.ts +52 -0
  73. package/src/drizzle/kysely.ts +8 -0
  74. package/src/drizzle/pagination.ts +200 -0
  75. package/src/email/README.md +1 -0
  76. package/src/email/index.ts +1 -0
  77. package/src/email/resend.ts +25 -0
  78. package/src/encoding/README.md +66 -79
  79. package/src/encoding/base64.ts +13 -4
  80. package/src/environment/README.md +97 -0
  81. package/src/environment/basic.ts +26 -0
  82. package/src/environment/device.ts +311 -0
  83. package/src/environment/feature.ts +285 -0
  84. package/src/environment/geo.ts +337 -0
  85. package/src/environment/index.ts +7 -0
  86. package/src/environment/runtime.ts +400 -0
  87. package/src/environment/snapshot.ts +60 -0
  88. package/src/environment/variable.ts +239 -0
  89. package/src/event/README.md +90 -0
  90. package/src/event/class-event-proxy.ts +229 -0
  91. package/src/event/common.ts +29 -0
  92. package/src/event/event-manager.ts +203 -0
  93. package/src/event/index.ts +4 -0
  94. package/src/event/instance-event-proxy.ts +187 -0
  95. package/src/event/internal.ts +24 -0
  96. package/src/exception/README.md +96 -0
  97. package/src/exception/browser.ts +219 -0
  98. package/src/exception/index.ts +4 -0
  99. package/src/exception/nodejs.ts +169 -0
  100. package/src/exception/normalize.ts +106 -0
  101. package/src/exception/types.ts +99 -0
  102. package/src/form/README.md +25 -0
  103. package/src/form/index.ts +1 -0
  104. package/src/form/inputor-controller/base.ts +874 -0
  105. package/src/form/inputor-controller/boolean.ts +39 -0
  106. package/src/form/inputor-controller/file.ts +39 -0
  107. package/src/form/inputor-controller/form.ts +181 -0
  108. package/src/form/inputor-controller/helper.ts +117 -0
  109. package/src/form/inputor-controller/index.ts +17 -0
  110. package/src/form/inputor-controller/multi-select.ts +99 -0
  111. package/src/form/inputor-controller/number.ts +116 -0
  112. package/src/form/inputor-controller/select.ts +109 -0
  113. package/src/form/inputor-controller/text.ts +82 -0
  114. package/src/http/READMD.md +1 -0
  115. package/src/http/api/api-core.ts +84 -0
  116. package/src/http/api/api-handler.ts +79 -0
  117. package/src/http/api/api-host.ts +47 -0
  118. package/src/http/api/api-result.ts +56 -0
  119. package/src/http/api/api-schema.ts +154 -0
  120. package/src/http/api/api-server.ts +130 -0
  121. package/src/http/api/api-test.ts +142 -0
  122. package/src/http/api/api-type.ts +37 -0
  123. package/src/http/api/api.ts +81 -0
  124. package/src/http/api/index.ts +11 -0
  125. package/src/http/api-adapter/api-core-node-http.ts +260 -0
  126. package/src/http/api-adapter/api-host-node-http.ts +156 -0
  127. package/src/http/api-adapter/api-result-arktype.ts +297 -0
  128. package/src/http/api-adapter/api-result-zod.ts +286 -0
  129. package/src/http/api-adapter/index.ts +5 -0
  130. package/src/http/bin/gen-api-list/gen-api-list.ts +126 -0
  131. package/src/http/bin/gen-api-list/index.ts +1 -0
  132. package/src/http/bin/gen-api-test/gen-api-test.ts +136 -0
  133. package/src/http/bin/gen-api-test/index.ts +1 -0
  134. package/src/http/bin/gen-api-type/calc-code.ts +25 -0
  135. package/src/http/bin/gen-api-type/gen-api-type.ts +127 -0
  136. package/src/http/bin/gen-api-type/index.ts +2 -0
  137. package/src/http/bin/index.ts +2 -0
  138. package/src/http/index.ts +3 -0
  139. package/src/huawei/README.md +1 -0
  140. package/src/huawei/index.ts +2 -0
  141. package/src/huawei/moderation/index.ts +1 -0
  142. package/src/huawei/moderation/moderation.ts +355 -0
  143. package/src/huawei/obs/esdk-obs-nodejs.d.ts +87 -0
  144. package/src/huawei/obs/index.ts +1 -0
  145. package/src/huawei/obs/obs.ts +42 -0
  146. package/src/identifier/README.md +92 -0
  147. package/src/identifier/id.ts +119 -0
  148. package/src/identifier/index.ts +2 -0
  149. package/src/identifier/uuid.ts +187 -0
  150. package/src/index.ts +33 -1
  151. package/src/json/README.md +92 -0
  152. package/src/json/index.ts +1 -0
  153. package/src/json/repair.ts +18 -0
  154. package/src/log/README.md +79 -0
  155. package/src/log/index.ts +5 -0
  156. package/src/log/log-emitter.ts +72 -0
  157. package/src/log/log-record.ts +10 -0
  158. package/src/log/log-scheduler.ts +74 -0
  159. package/src/log/log-type.ts +8 -0
  160. package/src/log/logger.ts +554 -0
  161. package/src/openai/README.md +1 -0
  162. package/src/openai/index.ts +1 -0
  163. package/src/openai/openai.ts +510 -0
  164. package/src/orchestration/README.md +91 -0
  165. package/src/orchestration/coordination/barrier.ts +214 -0
  166. package/src/orchestration/coordination/count-down-latch.ts +215 -0
  167. package/src/orchestration/coordination/errors.ts +98 -0
  168. package/src/orchestration/coordination/index.ts +16 -0
  169. package/src/orchestration/coordination/internal/wait-constraints.ts +95 -0
  170. package/src/orchestration/coordination/internal/wait-queue.ts +109 -0
  171. package/src/orchestration/coordination/keyed-lock.ts +168 -0
  172. package/src/orchestration/coordination/mutex.ts +257 -0
  173. package/src/orchestration/coordination/permit.ts +127 -0
  174. package/src/orchestration/coordination/read-write-lock.ts +444 -0
  175. package/src/orchestration/coordination/semaphore.ts +280 -0
  176. package/src/orchestration/dispatching/dispatcher.ts +83 -0
  177. package/src/orchestration/dispatching/index.ts +2 -0
  178. package/src/orchestration/dispatching/selector/base-selector.ts +39 -0
  179. package/src/orchestration/dispatching/selector/down-count-selector.ts +119 -0
  180. package/src/orchestration/dispatching/selector/index.ts +2 -0
  181. package/src/orchestration/index.ts +3 -0
  182. package/src/orchestration/scheduling/index.ts +2 -0
  183. package/src/orchestration/scheduling/scheduler.ts +103 -0
  184. package/src/orchestration/scheduling/task.ts +32 -0
  185. package/src/random/README.md +56 -86
  186. package/src/random/base.ts +66 -0
  187. package/src/random/index.ts +5 -1
  188. package/src/random/random-boolean.ts +40 -0
  189. package/src/random/random-integer.ts +60 -0
  190. package/src/random/random-number.ts +72 -0
  191. package/src/random/random-string.ts +66 -0
  192. package/src/reactor/README.md +4 -0
  193. package/src/reactor/reactor-core/primitive.ts +9 -9
  194. package/src/reactor/reactor-core/reactive-system.ts +5 -5
  195. package/src/request/README.md +108 -0
  196. package/src/request/fetch/base.ts +108 -0
  197. package/src/request/fetch/browser.ts +285 -0
  198. package/src/request/fetch/general.ts +20 -0
  199. package/src/request/fetch/index.ts +4 -0
  200. package/src/request/fetch/nodejs.ts +285 -0
  201. package/src/request/index.ts +2 -0
  202. package/src/request/request/base.ts +250 -0
  203. package/src/request/request/general.ts +64 -0
  204. package/src/request/request/index.ts +3 -0
  205. package/src/request/request/resource.ts +68 -0
  206. package/src/result/README.md +4 -0
  207. package/src/result/controller.ts +54 -0
  208. package/src/result/either.ts +193 -0
  209. package/src/result/index.ts +2 -0
  210. package/src/route/README.md +105 -0
  211. package/src/route/adapter/browser.ts +122 -0
  212. package/src/route/adapter/driver.ts +56 -0
  213. package/src/route/adapter/index.ts +2 -0
  214. package/src/route/index.ts +3 -0
  215. package/src/route/router/index.ts +2 -0
  216. package/src/route/router/route.ts +630 -0
  217. package/src/route/router/router.ts +1642 -0
  218. package/src/route/uri/hash.ts +308 -0
  219. package/src/route/uri/index.ts +7 -0
  220. package/src/route/uri/pathname.ts +376 -0
  221. package/src/route/uri/search.ts +413 -0
  222. package/src/singleton/README.md +79 -0
  223. package/src/singleton/factory.ts +55 -0
  224. package/src/singleton/index.ts +2 -0
  225. package/src/singleton/manager.ts +204 -0
  226. package/src/socket/README.md +105 -0
  227. package/src/socket/client/index.ts +2 -0
  228. package/src/socket/client/socket-unit.ts +660 -0
  229. package/src/socket/client/socket.ts +203 -0
  230. package/src/socket/common/index.ts +2 -0
  231. package/src/socket/common/socket-unit-common.ts +23 -0
  232. package/src/socket/common/socket-unit-heartbeat.ts +427 -0
  233. package/src/socket/index.ts +3 -0
  234. package/src/socket/server/index.ts +3 -0
  235. package/src/socket/server/server.ts +183 -0
  236. package/src/socket/server/socket-unit.ts +449 -0
  237. package/src/socket/server/socket.ts +264 -0
  238. package/src/storage/README.md +107 -0
  239. package/src/storage/index.ts +1 -0
  240. package/src/storage/table.ts +449 -0
  241. package/src/timer/README.md +86 -0
  242. package/src/timer/expiration/expiration-manager.ts +594 -0
  243. package/src/timer/expiration/index.ts +3 -0
  244. package/src/timer/expiration/min-heap.ts +208 -0
  245. package/src/timer/expiration/remaining-manager.ts +241 -0
  246. package/src/timer/index.ts +1 -0
  247. package/src/tube/README.md +99 -0
  248. package/src/tube/helper.ts +138 -0
  249. package/src/tube/index.ts +2 -0
  250. package/src/tube/tube.ts +880 -0
  251. package/src/type/README.md +54 -307
  252. package/src/type/class.ts +2 -2
  253. package/src/type/index.ts +14 -14
  254. package/src/type/is.ts +265 -2
  255. package/src/type/object.ts +37 -0
  256. package/src/type/string.ts +7 -2
  257. package/src/type/tuple.ts +6 -6
  258. package/src/type/union.ts +16 -0
  259. package/src/web/README.md +77 -0
  260. package/src/web/capture.ts +35 -0
  261. package/src/web/clipboard.ts +97 -0
  262. package/src/web/dom.ts +117 -0
  263. package/src/web/download.ts +16 -0
  264. package/src/web/event.ts +46 -0
  265. package/src/web/index.ts +10 -0
  266. package/src/web/local-storage.ts +113 -0
  267. package/src/web/location.ts +28 -0
  268. package/src/web/permission.ts +172 -0
  269. package/src/web/script-loader.ts +432 -0
  270. package/src/weixin/README.md +1 -0
  271. package/src/weixin/index.ts +2 -0
  272. package/src/weixin/official-account/authorization.ts +159 -0
  273. package/src/weixin/official-account/index.ts +2 -0
  274. package/src/weixin/official-account/js-api.ts +134 -0
  275. package/src/weixin/open/index.ts +1 -0
  276. package/src/weixin/open/oauth2.ts +133 -0
  277. package/tests/unit/abort/abort-manager.spec.ts +225 -0
  278. package/tests/unit/abort/abort-signal-listener-manager.spec.ts +62 -0
  279. package/tests/unit/ai/ai.spec.ts +85 -0
  280. package/tests/unit/aio/content.spec.ts +105 -0
  281. package/tests/unit/aio/json.spec.ts +147 -0
  282. package/tests/unit/aio/prompt.spec.ts +111 -0
  283. package/tests/unit/basic/array.spec.ts +1 -1
  284. package/tests/unit/basic/error.spec.ts +16 -4
  285. package/tests/unit/basic/schedule.spec.ts +74 -0
  286. package/tests/unit/basic/stream.spec.ts +91 -38
  287. package/tests/unit/basic/string.spec.ts +0 -9
  288. package/tests/unit/color/rgb/analyze.spec.ts +110 -0
  289. package/tests/unit/color/rgb/construct.spec.ts +56 -0
  290. package/tests/unit/color/rgb/convert.spec.ts +60 -0
  291. package/tests/unit/color/rgb/derive.spec.ts +103 -0
  292. package/tests/unit/color/rgb/parse.spec.ts +66 -0
  293. package/tests/unit/color/rgb/serialize.spec.ts +46 -0
  294. package/tests/unit/color/xyz/analyze.spec.ts +33 -0
  295. package/tests/unit/color/xyz/construct.spec.ts +10 -0
  296. package/tests/unit/color/xyz/convert.spec.ts +18 -0
  297. package/tests/unit/credential/api-key.spec.ts +37 -0
  298. package/tests/unit/credential/bearer.spec.ts +23 -0
  299. package/tests/unit/credential/json-web-token.spec.ts +23 -0
  300. package/tests/unit/credential/password.spec.ts +41 -0
  301. package/tests/unit/cron/cron.spec.ts +84 -0
  302. package/tests/unit/css/class.spec.ts +157 -0
  303. package/tests/unit/environment/basic.spec.ts +20 -0
  304. package/tests/unit/environment/device.spec.ts +146 -0
  305. package/tests/unit/environment/feature.spec.ts +388 -0
  306. package/tests/unit/environment/geo.spec.ts +111 -0
  307. package/tests/unit/environment/runtime.spec.ts +364 -0
  308. package/tests/unit/environment/snapshot.spec.ts +4 -0
  309. package/tests/unit/environment/variable.spec.ts +190 -0
  310. package/tests/unit/event/class-event-proxy.spec.ts +225 -0
  311. package/tests/unit/event/event-manager.spec.ts +246 -0
  312. package/tests/unit/event/instance-event-proxy.spec.ts +187 -0
  313. package/tests/unit/exception/browser.spec.ts +213 -0
  314. package/tests/unit/exception/nodejs.spec.ts +144 -0
  315. package/tests/unit/exception/normalize.spec.ts +57 -0
  316. package/tests/unit/form/inputor-controller/base.spec.ts +458 -0
  317. package/tests/unit/form/inputor-controller/boolean.spec.ts +30 -0
  318. package/tests/unit/form/inputor-controller/file.spec.ts +27 -0
  319. package/tests/unit/form/inputor-controller/form.spec.ts +120 -0
  320. package/tests/unit/form/inputor-controller/helper.spec.ts +67 -0
  321. package/tests/unit/form/inputor-controller/multi-select.spec.ts +34 -0
  322. package/tests/unit/form/inputor-controller/number.spec.ts +36 -0
  323. package/tests/unit/form/inputor-controller/select.spec.ts +49 -0
  324. package/tests/unit/form/inputor-controller/text.spec.ts +34 -0
  325. package/tests/unit/http/api/api-core-host.spec.ts +207 -0
  326. package/tests/unit/http/api/api-schema.spec.ts +120 -0
  327. package/tests/unit/http/api/api-server.spec.ts +363 -0
  328. package/tests/unit/http/api/api-test.spec.ts +117 -0
  329. package/tests/unit/http/api/api.spec.ts +121 -0
  330. package/tests/unit/http/api-adapter/node-http.spec.ts +191 -0
  331. package/tests/unit/identifier/id.spec.ts +71 -0
  332. package/tests/unit/identifier/uuid.spec.ts +85 -0
  333. package/tests/unit/json/repair.spec.ts +11 -0
  334. package/tests/unit/log/log-emitter.spec.ts +33 -0
  335. package/tests/unit/log/log-scheduler.spec.ts +40 -0
  336. package/tests/unit/log/log-type.spec.ts +7 -0
  337. package/tests/unit/log/logger.spec.ts +237 -0
  338. package/tests/unit/openai/openai.spec.ts +64 -0
  339. package/tests/unit/orchestration/coordination/barrier.spec.ts +96 -0
  340. package/tests/unit/orchestration/coordination/count-down-latch.spec.ts +63 -0
  341. package/tests/unit/orchestration/coordination/errors.spec.ts +29 -0
  342. package/tests/unit/orchestration/coordination/keyed-lock.spec.ts +109 -0
  343. package/tests/unit/orchestration/coordination/mutex.spec.ts +132 -0
  344. package/tests/unit/orchestration/coordination/permit.spec.ts +43 -0
  345. package/tests/unit/orchestration/coordination/read-write-lock.spec.ts +154 -0
  346. package/tests/unit/orchestration/coordination/semaphore.spec.ts +135 -0
  347. package/tests/unit/orchestration/dispatching/dispatcher.spec.ts +41 -0
  348. package/tests/unit/orchestration/dispatching/selector/down-count-selector.spec.ts +81 -0
  349. package/tests/unit/orchestration/scheduling/scheduler.spec.ts +103 -0
  350. package/tests/unit/random/base.spec.ts +58 -0
  351. package/tests/unit/random/random-boolean.spec.ts +25 -0
  352. package/tests/unit/random/random-integer.spec.ts +32 -0
  353. package/tests/unit/random/random-number.spec.ts +33 -0
  354. package/tests/unit/random/random-string.spec.ts +22 -0
  355. package/tests/unit/reactor/alien-signals-effect.spec.ts +11 -10
  356. package/tests/unit/reactor/preact-signal.spec.ts +1 -2
  357. package/tests/unit/request/fetch/browser.spec.ts +222 -0
  358. package/tests/unit/request/fetch/general.spec.ts +43 -0
  359. package/tests/unit/request/fetch/nodejs.spec.ts +225 -0
  360. package/tests/unit/request/request/base.spec.ts +385 -0
  361. package/tests/unit/request/request/general.spec.ts +161 -0
  362. package/tests/unit/route/router/route.spec.ts +431 -0
  363. package/tests/unit/route/router/router.spec.ts +407 -0
  364. package/tests/unit/route/uri/hash.spec.ts +72 -0
  365. package/tests/unit/route/uri/pathname.spec.ts +147 -0
  366. package/tests/unit/route/uri/search.spec.ts +107 -0
  367. package/tests/unit/singleton/singleton.spec.ts +49 -0
  368. package/tests/unit/socket/client.spec.ts +208 -0
  369. package/tests/unit/socket/server.spec.ts +135 -0
  370. package/tests/unit/socket/socket-unit-heartbeat.spec.ts +214 -0
  371. package/tests/unit/storage/table.spec.ts +620 -0
  372. package/tests/unit/timer/expiration/expiration-manager.spec.ts +464 -0
  373. package/tests/unit/timer/expiration/min-heap.spec.ts +71 -0
  374. package/tests/unit/timer/expiration/remaining-manager.spec.ts +234 -0
  375. package/tests/unit/tube/helper.spec.ts +139 -0
  376. package/tests/unit/tube/tube.spec.ts +501 -0
  377. package/.oxlintrc.json +0 -5
  378. package/src/random/uuid.ts +0 -103
  379. package/tests/unit/random/uuid.spec.ts +0 -37
@@ -0,0 +1,135 @@
1
+ import { afterEach, expect, test, vi } from "vitest"
2
+
3
+ import { CoordinationAbortError, CoordinationTimeoutError, Semaphore } from "#Source/orchestration/index.ts"
4
+
5
+ afterEach(() => {
6
+ vi.clearAllTimers()
7
+ vi.useRealTimers()
8
+ vi.restoreAllMocks()
9
+ })
10
+
11
+ test("Semaphore tracks capacity and grants permits in FIFO order", async () => {
12
+ const semaphore = new Semaphore(2)
13
+
14
+ expect(semaphore.getMaxConcurrency()).toBe(2)
15
+ expect(semaphore.getActiveCount()).toBe(0)
16
+ expect(semaphore.getAvailableCount()).toBe(2)
17
+ expect(semaphore.isSaturated()).toBe(false)
18
+
19
+ const firstPermit = semaphore.tryAcquire()
20
+ const secondPermit = semaphore.tryAcquire()
21
+
22
+ expect(firstPermit).toBeDefined()
23
+ expect(secondPermit).toBeDefined()
24
+ expect(semaphore.isSaturated()).toBe(true)
25
+ expect(semaphore.getActiveCount()).toBe(2)
26
+ expect(semaphore.getAvailableCount()).toBe(0)
27
+
28
+ const thirdPermitPromise = semaphore.acquire()
29
+ const fourthPermitPromise = semaphore.acquire()
30
+
31
+ expect(semaphore.getPendingCount()).toBe(2)
32
+ expect(semaphore.tryAcquire()).toBeUndefined()
33
+
34
+ firstPermit?.release()
35
+
36
+ const thirdPermit = await thirdPermitPromise
37
+ let fourthResolved = false
38
+ void fourthPermitPromise.then(() => {
39
+ fourthResolved = true
40
+ })
41
+
42
+ await Promise.resolve()
43
+
44
+ expect(semaphore.getPendingCount()).toBe(1)
45
+ expect(fourthResolved).toBe(false)
46
+
47
+ secondPermit?.release()
48
+ const fourthPermit = await fourthPermitPromise
49
+
50
+ expect(semaphore.getPendingCount()).toBe(0)
51
+
52
+ thirdPermit.release()
53
+ fourthPermit.release()
54
+
55
+ expect(semaphore.getActiveCount()).toBe(0)
56
+ expect(semaphore.getAvailableCount()).toBe(2)
57
+ expect(semaphore.isSaturated()).toBe(false)
58
+ })
59
+
60
+ test("Semaphore acquire rejects on timeout and abort without leaking queue entries", async () => {
61
+ vi.useFakeTimers()
62
+
63
+ const semaphore = new Semaphore(1)
64
+ const blockingPermit = semaphore.tryAcquire()
65
+
66
+ expect(blockingPermit).toBeDefined()
67
+
68
+ const timeoutPromise = semaphore.acquire({ timeout: 15 })
69
+
70
+ expect(semaphore.getPendingCount()).toBe(1)
71
+
72
+ await vi.advanceTimersByTimeAsync(15)
73
+
74
+ await expect(timeoutPromise).rejects.toBeInstanceOf(CoordinationTimeoutError)
75
+ expect(semaphore.getPendingCount()).toBe(0)
76
+
77
+ const abortController = new AbortController()
78
+ const abortPromise = semaphore.acquire({ abortSignal: abortController.signal })
79
+
80
+ expect(semaphore.getPendingCount()).toBe(1)
81
+
82
+ abortController.abort("stopped")
83
+
84
+ await expect(abortPromise).rejects.toBeInstanceOf(CoordinationAbortError)
85
+ expect(semaphore.getPendingCount()).toBe(0)
86
+
87
+ blockingPermit?.release()
88
+ })
89
+
90
+ test("Semaphore runExclusive releases capacity after callback failure", async () => {
91
+ const semaphore = new Semaphore(1)
92
+ const error = new Error("boom")
93
+
94
+ await expect(semaphore.runExclusive(() => 1)).resolves.toBe(1)
95
+ expect(semaphore.getActiveCount()).toBe(0)
96
+
97
+ await expect(semaphore.runExclusive(() => {
98
+ throw error
99
+ })).rejects.toThrow(error)
100
+
101
+ expect(semaphore.getActiveCount()).toBe(0)
102
+ expect(semaphore.isSaturated()).toBe(false)
103
+ })
104
+
105
+ test("Semaphore duplicate release stays silent by default and uses custom handler when provided", () => {
106
+ const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {
107
+ // no-op to silence warnings during test
108
+ })
109
+ const silentSemaphore = new Semaphore(1)
110
+ const silentPermit = silentSemaphore.tryAcquire()
111
+
112
+ expect(silentPermit).toBeDefined()
113
+
114
+ silentPermit?.release()
115
+ silentPermit?.release()
116
+ silentPermit?.release()
117
+
118
+ expect(silentSemaphore.getActiveCount()).toBe(0)
119
+ expect(warnSpy).not.toHaveBeenCalled()
120
+
121
+ const onDuplicateRelease = vi.fn()
122
+ const customSemaphore = new Semaphore(1, { onDuplicateRelease })
123
+ const customPermit = customSemaphore.tryAcquire()
124
+
125
+ expect(customPermit).toBeDefined()
126
+
127
+ customPermit?.release()
128
+ customPermit?.release()
129
+ customPermit?.release()
130
+
131
+ expect(customSemaphore.getActiveCount()).toBe(0)
132
+ expect(onDuplicateRelease).toHaveBeenCalledTimes(2)
133
+ expect(onDuplicateRelease).toHaveBeenCalledWith("Semaphore permit release was called more than once.")
134
+ expect(warnSpy).not.toHaveBeenCalled()
135
+ })
@@ -0,0 +1,41 @@
1
+ import { expect, test } from "vitest"
2
+
3
+ import { Dispatcher, DownCountSelector } from "#Source/orchestration/index.ts"
4
+
5
+ test("Dispatcher getSelector reuses selectors by id and applies filter on first creation", async () => {
6
+ const dispatcher = new Dispatcher({
7
+ itemList: ["alpha", "beta", "gamma"],
8
+ })
9
+
10
+ const firstResult = dispatcher.getSelector({
11
+ id: "shared",
12
+ filter: (item) => item !== "alpha",
13
+ })
14
+ const secondResult = dispatcher.getSelector({
15
+ id: "shared",
16
+ filter: () => true,
17
+ })
18
+
19
+ expect(firstResult.selector).toBeInstanceOf(DownCountSelector)
20
+ expect(secondResult.selector).toBe(firstResult.selector)
21
+
22
+ const firstItem = await firstResult.selector.getItem()
23
+
24
+ expect(firstItem.isRight()).toBe(true)
25
+ expect(firstItem.assertRight().getRight().item).toBe("beta")
26
+ })
27
+
28
+ test("Dispatcher destroySelector discards an existing selector and allows recreation", () => {
29
+ const dispatcher = new Dispatcher({
30
+ itemList: ["alpha", "beta"],
31
+ })
32
+
33
+ const firstSelector = dispatcher.getSelector({ id: "shared" }).selector
34
+
35
+ dispatcher.destroySelector("missing")
36
+ dispatcher.destroySelector("shared")
37
+
38
+ const recreatedSelector = dispatcher.getSelector({ id: "shared" }).selector
39
+
40
+ expect(recreatedSelector).not.toBe(firstSelector)
41
+ })
@@ -0,0 +1,81 @@
1
+ import { expect, test } from "vitest"
2
+
3
+ import { DownCountSelector } from "#Source/orchestration/index.ts"
4
+
5
+ test("DownCountSelector getId returns the configured selector id", () => {
6
+ const selector = new DownCountSelector({
7
+ id: "selector-1",
8
+ itemList: ["alpha"],
9
+ })
10
+
11
+ expect(selector.getId()).toBe("selector-1")
12
+ })
13
+
14
+ test("DownCountSelector getItem distinguishes no choice, no available, and reset-driven reuse", async () => {
15
+ const emptySelector = new DownCountSelector<string>({
16
+ id: "empty",
17
+ itemList: [],
18
+ })
19
+
20
+ const emptyResult = await emptySelector.getItem()
21
+
22
+ expect(emptyResult.isLeft()).toBe(true)
23
+ expect(emptyResult.assertLeft().getLeft()).toEqual({
24
+ code: "NO_CHOICE",
25
+ })
26
+
27
+ const noAvailableSelector = new DownCountSelector({
28
+ id: "no-available",
29
+ itemList: ["alpha", "beta"],
30
+ resetAllWhenNoAvailable: false,
31
+ })
32
+
33
+ const firstResult = await noAvailableSelector.getItem()
34
+
35
+ expect(firstResult.isRight()).toBe(true)
36
+
37
+ firstResult.assertRight().getRight().markUnavailable()
38
+
39
+ const secondResult = await noAvailableSelector.getItem()
40
+
41
+ expect(secondResult.isRight()).toBe(true)
42
+ expect(secondResult.assertRight().getRight().item).toBe("beta")
43
+
44
+ secondResult.assertRight().getRight().markUnavailable()
45
+
46
+ const noAvailableResult = await noAvailableSelector.getItem()
47
+
48
+ expect(noAvailableResult.isLeft()).toBe(true)
49
+ expect(noAvailableResult.assertLeft().getLeft()).toEqual({
50
+ code: "NO_AVAILABLE",
51
+ })
52
+
53
+ const resetSelector = new DownCountSelector({
54
+ id: "reset",
55
+ itemList: ["only"],
56
+ })
57
+
58
+ const resetFirstResult = await resetSelector.getItem()
59
+
60
+ expect(resetFirstResult.isRight()).toBe(true)
61
+
62
+ resetFirstResult.assertRight().getRight().markUnavailable()
63
+
64
+ const resetSecondResult = await resetSelector.getItem()
65
+
66
+ expect(resetSecondResult.isRight()).toBe(true)
67
+ expect(resetSecondResult.assertRight().getRight().item).toBe("only")
68
+
69
+ resetSecondResult.assertRight().getRight().markUnavailable()
70
+
71
+ const recoveredResult = await resetSelector.getItem()
72
+
73
+ expect(recoveredResult.isRight()).toBe(true)
74
+
75
+ recoveredResult.assertRight().getRight().markAvailable()
76
+
77
+ const availableAgainResult = await resetSelector.getItem()
78
+
79
+ expect(availableAgainResult.isRight()).toBe(true)
80
+ expect(availableAgainResult.assertRight().getRight().item).toBe("only")
81
+ })
@@ -0,0 +1,103 @@
1
+ import { expect, test, vi } from "vitest"
2
+
3
+ import { Scheduler, Task } from "#Source/orchestration/index.ts"
4
+
5
+ test("Scheduler runs one-shot tasks and records run logs", async () => {
6
+ vi.useFakeTimers()
7
+ vi.setSystemTime(new Date(2_026, 0, 1, 0, 0, 0, 0))
8
+
9
+ try {
10
+ const run = vi.fn(() => undefined)
11
+ const runAt = new Date(Date.now() + 1_000)
12
+ const scheduler = new Scheduler({
13
+ tasks: [
14
+ new Task({
15
+ name: "alpha",
16
+ cron: runAt,
17
+ run,
18
+ }),
19
+ ],
20
+ })
21
+
22
+ scheduler.run()
23
+
24
+ await vi.advanceTimersByTimeAsync(999)
25
+ expect(run).toHaveBeenCalledTimes(0)
26
+
27
+ await vi.advanceTimersByTimeAsync(1)
28
+
29
+ expect(run).toHaveBeenCalledTimes(1)
30
+ expect(scheduler.getErrLog()).toEqual([])
31
+ expect(scheduler.getRunLog()).toHaveLength(1)
32
+ expect(scheduler.getRunLog()[0]).toEqual({
33
+ name: "alpha",
34
+ time: runAt,
35
+ })
36
+ }
37
+ finally {
38
+ vi.clearAllTimers()
39
+ vi.useRealTimers()
40
+ }
41
+ })
42
+
43
+ test("Scheduler records both run and error logs when a task fails", async () => {
44
+ vi.useFakeTimers()
45
+ vi.setSystemTime(new Date(2_026, 0, 1, 0, 0, 0, 0))
46
+
47
+ try {
48
+ const error = new Error("boom")
49
+ const run = vi.fn(async () => await Promise.reject(error))
50
+ const runAt = new Date(Date.now() + 1_000)
51
+ const scheduler = new Scheduler({
52
+ tasks: [
53
+ new Task({
54
+ name: "beta",
55
+ cron: runAt,
56
+ run,
57
+ }),
58
+ ],
59
+ })
60
+
61
+ scheduler.run()
62
+ await vi.advanceTimersByTimeAsync(1_000)
63
+
64
+ expect(run).toHaveBeenCalledTimes(1)
65
+ expect(scheduler.getRunLog()).toHaveLength(1)
66
+ expect(scheduler.getErrLog()).toEqual([
67
+ {
68
+ name: "beta",
69
+ time: runAt,
70
+ reason: "Error: boom",
71
+ },
72
+ ])
73
+ }
74
+ finally {
75
+ vi.clearAllTimers()
76
+ vi.useRealTimers()
77
+ }
78
+ })
79
+
80
+ test("Scheduler.run rejects duplicate starts", () => {
81
+ vi.useFakeTimers()
82
+ vi.setSystemTime(new Date(2_026, 0, 1, 0, 0, 0, 0))
83
+
84
+ try {
85
+ const scheduler = new Scheduler({
86
+ tasks: [
87
+ new Task({
88
+ name: "gamma",
89
+ cron: new Date(Date.now() + 1_000),
90
+ run: () => undefined,
91
+ }),
92
+ ],
93
+ })
94
+
95
+ scheduler.run()
96
+
97
+ expect(() => scheduler.run()).toThrow("Scheduler is already running")
98
+ }
99
+ finally {
100
+ vi.clearAllTimers()
101
+ vi.useRealTimers()
102
+ }
103
+ })
@@ -0,0 +1,58 @@
1
+ import { expect, test, vi } from "vitest"
2
+
3
+ import { getRandomIndex, getRandomUint32, getRandomUnitValue } from "#Source/random/index.ts"
4
+
5
+ test("getRandomUint32 returns an integer within uint32 range across available random sources", () => {
6
+ const cryptoBackedValue = getRandomUint32()
7
+ expect(Number.isInteger(cryptoBackedValue)).toBe(true)
8
+ expect(cryptoBackedValue).toBeGreaterThanOrEqual(0)
9
+ expect(cryptoBackedValue).toBeLessThan(0x1_0000_0000)
10
+
11
+ vi.stubGlobal("crypto", undefined)
12
+
13
+ try {
14
+ const fallbackValue = getRandomUint32()
15
+ expect(Number.isInteger(fallbackValue)).toBe(true)
16
+ expect(fallbackValue).toBeGreaterThanOrEqual(0)
17
+ expect(fallbackValue).toBeLessThan(0x1_0000_0000)
18
+ }
19
+ finally {
20
+ vi.unstubAllGlobals()
21
+ }
22
+ })
23
+
24
+ test("getRandomUnitValue returns a unit-interval float across available random sources", () => {
25
+ const cryptoBackedValue = getRandomUnitValue()
26
+ expect(cryptoBackedValue).toBeGreaterThanOrEqual(0)
27
+ expect(cryptoBackedValue).toBeLessThan(1)
28
+
29
+ vi.stubGlobal("crypto", undefined)
30
+
31
+ try {
32
+ const fallbackValue = getRandomUnitValue()
33
+ expect(fallbackValue).toBeGreaterThanOrEqual(0)
34
+ expect(fallbackValue).toBeLessThan(1)
35
+ }
36
+ finally {
37
+ vi.unstubAllGlobals()
38
+ }
39
+ })
40
+
41
+ test("getRandomIndex returns an integer index within alphabet bounds across available random sources", () => {
42
+ const cryptoBackedValue = getRandomIndex(3)
43
+ expect(Number.isInteger(cryptoBackedValue)).toBe(true)
44
+ expect(cryptoBackedValue).toBeGreaterThanOrEqual(0)
45
+ expect(cryptoBackedValue).toBeLessThan(3)
46
+
47
+ vi.stubGlobal("crypto", undefined)
48
+
49
+ try {
50
+ const fallbackValue = getRandomIndex(3)
51
+ expect(Number.isInteger(fallbackValue)).toBe(true)
52
+ expect(fallbackValue).toBeGreaterThanOrEqual(0)
53
+ expect(fallbackValue).toBeLessThan(3)
54
+ }
55
+ finally {
56
+ vi.unstubAllGlobals()
57
+ }
58
+ })
@@ -0,0 +1,25 @@
1
+ import { expect, test, vi } from "vitest"
2
+
3
+ import { randomBoolean } from "#Source/random/index.ts"
4
+
5
+ test("randomBoolean returns expected output across probabilities and available random sources", () => {
6
+ const defaultValue = randomBoolean()
7
+ expect(typeof defaultValue).toBe("boolean")
8
+
9
+ expect(randomBoolean(0)).toBe(false)
10
+ expect(randomBoolean(1)).toBe(true)
11
+ expect(() => randomBoolean(Number.NaN)).toThrow(TypeError)
12
+ expect(() => randomBoolean(Number.POSITIVE_INFINITY)).toThrow(TypeError)
13
+ expect(() => randomBoolean(-0.1)).toThrow(RangeError)
14
+ expect(() => randomBoolean(1.1)).toThrow(RangeError)
15
+
16
+ vi.stubGlobal("crypto", undefined)
17
+
18
+ try {
19
+ const fallbackValue = randomBoolean(0.5)
20
+ expect(typeof fallbackValue).toBe("boolean")
21
+ }
22
+ finally {
23
+ vi.unstubAllGlobals()
24
+ }
25
+ })
@@ -0,0 +1,32 @@
1
+ import { expect, test, vi } from "vitest"
2
+
3
+ import { randomInteger } from "#Source/random/index.ts"
4
+
5
+ test("randomInteger returns expected output across ranges and available random sources", () => {
6
+ const singleBoundValue = randomInteger(5)
7
+ expect(singleBoundValue).toBeGreaterThanOrEqual(0)
8
+ expect(singleBoundValue).toBeLessThanOrEqual(5)
9
+ expect(Number.isInteger(singleBoundValue)).toBe(true)
10
+
11
+ const reversedRangeValue = randomInteger(8, 3)
12
+ expect(reversedRangeValue).toBeGreaterThanOrEqual(3)
13
+ expect(reversedRangeValue).toBeLessThanOrEqual(8)
14
+ expect(Number.isInteger(reversedRangeValue)).toBe(true)
15
+
16
+ expect(randomInteger(4, 4)).toBe(4)
17
+ expect(() => randomInteger(1.5)).toThrow(TypeError)
18
+ expect(() => randomInteger(Number.NaN)).toThrow(TypeError)
19
+ expect(() => randomInteger(1, Number.POSITIVE_INFINITY)).toThrow(TypeError)
20
+
21
+ vi.stubGlobal("crypto", undefined)
22
+
23
+ try {
24
+ const fallbackValue = randomInteger(-2, 2)
25
+ expect(fallbackValue).toBeGreaterThanOrEqual(-2)
26
+ expect(fallbackValue).toBeLessThanOrEqual(2)
27
+ expect(Number.isInteger(fallbackValue)).toBe(true)
28
+ }
29
+ finally {
30
+ vi.unstubAllGlobals()
31
+ }
32
+ })
@@ -0,0 +1,33 @@
1
+ import { expect, test, vi } from "vitest"
2
+
3
+ import { randomNumber } from "#Source/random/index.ts"
4
+
5
+ test("randomNumber returns expected output across ranges and available random sources", () => {
6
+ const defaultValue = randomNumber()
7
+ expect(defaultValue).toBeGreaterThanOrEqual(0)
8
+ expect(defaultValue).toBeLessThan(1)
9
+
10
+ const singleBoundValue = randomNumber(5)
11
+ expect(singleBoundValue).toBeGreaterThanOrEqual(0)
12
+ expect(singleBoundValue).toBeLessThan(5)
13
+
14
+ const reversedRangeValue = randomNumber(10, 3)
15
+ expect(reversedRangeValue).toBeGreaterThanOrEqual(3)
16
+ expect(reversedRangeValue).toBeLessThan(10)
17
+
18
+ expect(randomNumber(4, 4)).toBe(4)
19
+ expect(() => randomNumber(Number.NaN)).toThrow(TypeError)
20
+ expect(() => randomNumber(Number.POSITIVE_INFINITY)).toThrow(TypeError)
21
+ expect(() => randomNumber(1, Number.NEGATIVE_INFINITY)).toThrow(TypeError)
22
+
23
+ vi.stubGlobal("crypto", undefined)
24
+
25
+ try {
26
+ const fallbackValue = randomNumber(-2, 2)
27
+ expect(fallbackValue).toBeGreaterThanOrEqual(-2)
28
+ expect(fallbackValue).toBeLessThan(2)
29
+ }
30
+ finally {
31
+ vi.unstubAllGlobals()
32
+ }
33
+ })
@@ -0,0 +1,22 @@
1
+ import { expect, test, vi } from "vitest"
2
+
3
+ import { randomString } from "#Source/random/index.ts"
4
+
5
+ test("randomString returns expected output across available random sources", () => {
6
+ const cryptoBackedValue = randomString(12)
7
+ expect(cryptoBackedValue).toHaveLength(12)
8
+
9
+ const constrainedValue = randomString(10, "ab")
10
+ expect(constrainedValue.split("").every(char => char === "a" || char === "b")).toBe(true)
11
+
12
+ vi.stubGlobal("crypto", undefined)
13
+
14
+ try {
15
+ const fallbackValue = randomString(10, "ab")
16
+ expect(fallbackValue).toHaveLength(10)
17
+ expect(fallbackValue.split("").every(char => char === "a" || char === "b")).toBe(true)
18
+ }
19
+ finally {
20
+ vi.unstubAllGlobals()
21
+ }
22
+ })
@@ -49,16 +49,17 @@ test('should not run untracked inner effect', () => {
49
49
  let outerEffectTruthyBranchRunTimes = 0;
50
50
  const a = signal(() => 3);
51
51
  const b = computed(() => a.get() > 0);
52
- const _outerEffect = effect(() => {
52
+ // outer effect
53
+ effect(() => {
53
54
  if (b.get() === true) {
54
55
  outerEffectTruthyBranchRunTimes = outerEffectTruthyBranchRunTimes + 1;
55
- const _innerEffect = effect(() => {
56
+ effect(() => {
56
57
  if (a.get() === 0) {
57
58
  throw new Error("bad");
58
59
  }
59
- });
60
+ }, { name: "inner effect" });
60
61
  }
61
- });
62
+ }, { name: "outer effect" });
62
63
 
63
64
  expect(outerEffectTruthyBranchRunTimes).toBe(1);
64
65
  a.set(2);
@@ -226,7 +227,7 @@ test('should duplicate subscribers do not affect the notify order', () => {
226
227
  const srcB = signal(() => 0, { name: "srcB" });
227
228
  const order: string[] = [];
228
229
 
229
- const _effectA = effect(() => {
230
+ effect(() => {
230
231
  order.push('a');
231
232
  reactiveSystem.setNoActiveNodeAsSub();
232
233
  const isOne = srcB.get() === 1;
@@ -237,7 +238,7 @@ test('should duplicate subscribers do not affect the notify order', () => {
237
238
  srcB.get();
238
239
  srcA.get();
239
240
  }, { name: "effect-a" });
240
- const _effectB = effect(() => {
241
+ effect(() => {
241
242
  order.push('b');
242
243
  srcA.get();
243
244
  }, { name: "effect-b" });
@@ -357,20 +358,20 @@ test('should not execute skipped effects from previous failed flush when updatin
357
358
 
358
359
  let effect3Executed = false;
359
360
 
360
- const _effect1 = effect(() => {
361
+ effect(() => {
361
362
  a.get();
362
363
  }, { name: "effect1" });
363
- const _effect2 = effect(() => {
364
+ effect(() => {
364
365
  if (a.get() === 2) {
365
366
  throw new Error('Error in effect 2');
366
367
  }
367
368
  }, { name: "effect2" });
368
- const _effect3 = effect(() => {
369
+ effect(() => {
369
370
  a.get();
370
371
  d.get();
371
372
  effect3Executed = true;
372
373
  }, { name: "effect3" });
373
- const _effect4 = effect(() => {
374
+ effect(() => {
374
375
  b.get();
375
376
  }, { name: "effect4" });
376
377
 
@@ -1,7 +1,6 @@
1
1
  import { describe, it, vi, expect } from "vitest";
2
2
 
3
- import type { Effect } from '#Source/index.ts';
4
- import { Signal, computed, effect, effectScope, endBatch, reactiveSystem, signal, startBatch } from '#Source/reactor/index.ts';
3
+ import { Signal, effect, signal } from "#Source/reactor/index.ts"
5
4
 
6
5
  /**
7
6
  * 这个测试文件应该与 https://github.com/preactjs/signals/blob/main/packages/core/test/signal.test.tsx 保持同步。