@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,83 @@
1
+ import type { LoggerFriendly, LoggerFriendlyOptions } from "#Source/log/index.ts"
2
+ import { generateUuidV4FromUrl } from "#Source/identifier/index.ts"
3
+ import { Logger } from "#Source/log/index.ts"
4
+
5
+ import type { BaseSelector } from "./selector/index.ts"
6
+ import { DownCountSelector } from "./selector/index.ts"
7
+
8
+ interface ItemStates<Target> {
9
+ item: Target
10
+ available: boolean
11
+ }
12
+
13
+ export interface GetSelectorOptions<Target> {
14
+ id?: string | undefined
15
+ filter?: ((item: Target) => boolean) | undefined
16
+ }
17
+
18
+ export interface GetSelectorResult<Target> {
19
+ selector: BaseSelector<Target>
20
+ destroy: () => void
21
+ }
22
+
23
+ export interface DispatcherOptions<Target> extends LoggerFriendlyOptions {
24
+ itemList: Target[]
25
+ }
26
+
27
+ export class Dispatcher<Target> implements LoggerFriendly {
28
+ readonly logger: Logger
29
+
30
+ private itemStatesMap: Map<Target, ItemStates<Target>>
31
+ private selectorMap: Map<string, BaseSelector<Target>>
32
+
33
+ constructor(options: DispatcherOptions<Target>) {
34
+ this.logger = Logger.fromOptions(options).setDefaultName("Dispatcher")
35
+
36
+ this.itemStatesMap = new Map()
37
+ options.itemList.forEach((item) => {
38
+ this.itemStatesMap.set(item, {
39
+ item,
40
+ available: true,
41
+ })
42
+ })
43
+ this.selectorMap = new Map()
44
+ }
45
+
46
+ getSelector(options: GetSelectorOptions<Target>): GetSelectorResult<Target> {
47
+ const {
48
+ id = generateUuidV4FromUrl(),
49
+ filter = (): boolean => true,
50
+ } = options
51
+
52
+ const isInitialized = this.selectorMap.has(id)
53
+ if (isInitialized === false) {
54
+ const list = Array.from(this.itemStatesMap.values())
55
+ .filter((itemStates) => {
56
+ return itemStates.available === true && filter(itemStates.item) === true
57
+ })
58
+ .map((itemStates) => {
59
+ return itemStates.item
60
+ })
61
+ // TODO: 将 selector 的类型作为参数,从而实现不同的 dispatcher 策略
62
+ const selector = new DownCountSelector({
63
+ id,
64
+ itemList: list,
65
+ logger: Logger.derive(this.logger).setName(`Selector-${id}`),
66
+ })
67
+ this.selectorMap.set(id, selector)
68
+ }
69
+
70
+ const selector = this.selectorMap.get(id)!
71
+ const result = {
72
+ selector,
73
+ destroy: (): void => {
74
+ this.destroySelector(id)
75
+ },
76
+ }
77
+ return result
78
+ }
79
+
80
+ destroySelector(key: string): void {
81
+ this.selectorMap.delete(key)
82
+ }
83
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./selector/index.ts"
2
+ export * from "./dispatcher.ts"
@@ -0,0 +1,39 @@
1
+ import type { LoggerFriendly, LoggerFriendlyOptions } from "#Source/log/index.ts"
2
+ import type { Either } from "#Source/result/index.ts"
3
+
4
+ import { Logger } from "#Source/log/index.ts"
5
+
6
+ export interface GetItemLeft {
7
+ code: "NO_CHOICE" | "NO_AVAILABLE"
8
+ }
9
+
10
+ export interface GetItemRight<Target> {
11
+ item: Target
12
+ markAvailable: () => void
13
+ markUnavailable: () => void
14
+ }
15
+
16
+ export type GetItemResult<Target> = Either<GetItemLeft, GetItemRight<Target>>
17
+
18
+ export interface BaseSelectorOptions<Target> extends LoggerFriendlyOptions {
19
+ id: string
20
+ itemList: Target[]
21
+ }
22
+
23
+ export abstract class BaseSelector<Target> implements LoggerFriendly {
24
+ readonly logger: Logger
25
+
26
+ private id: string
27
+
28
+ constructor(options: BaseSelectorOptions<Target>) {
29
+ this.logger = Logger.fromOptions(options).setDefaultName("BaseSelector")
30
+
31
+ this.id = options.id
32
+ }
33
+
34
+ getId(): string {
35
+ return this.id
36
+ }
37
+
38
+ abstract getItem(): Promise<GetItemResult<Target>>
39
+ }
@@ -0,0 +1,119 @@
1
+ import type { LoggerFriendly, LoggerFriendlyOptions } from "#Source/log/index.ts"
2
+ import { controllerFromEitherType } from "#Source/result/index.ts"
3
+
4
+ import type { BaseSelectorOptions, GetItemResult } from "./base-selector.ts"
5
+ import { BaseSelector } from "./base-selector.ts"
6
+
7
+ interface ItemStates<Target> {
8
+ item: Target
9
+ downCount: number
10
+ }
11
+
12
+ export interface DownCountSelectorOptions<Target> extends BaseSelectorOptions<Target>, LoggerFriendlyOptions {
13
+ /**
14
+ * @default 1
15
+ */
16
+ maxDownCount?: number | undefined
17
+ /**
18
+ * @default true
19
+ */
20
+ resetAllWhenNoAvailable?: boolean | undefined
21
+ }
22
+
23
+ export class DownCountSelector<Target> extends BaseSelector<Target> implements LoggerFriendly {
24
+ private maxDownCount: number
25
+ private resetAllWhenNoAvailable: boolean
26
+
27
+ private itemStatesMap: Map<Target, ItemStates<Target>>
28
+
29
+ constructor(options: DownCountSelectorOptions<Target>) {
30
+ super(options)
31
+ this.logger.setDefaultName("DownCountSelector")
32
+
33
+ this.maxDownCount = options.maxDownCount ?? 1
34
+ this.resetAllWhenNoAvailable = options.resetAllWhenNoAvailable ?? true
35
+
36
+ this.itemStatesMap = new Map()
37
+ options.itemList.forEach((item) => {
38
+ this.itemStatesMap.set(item, {
39
+ item,
40
+ downCount: 0,
41
+ })
42
+ })
43
+ }
44
+
45
+ private markItemAvailable(item: Target): void {
46
+ const states = this.itemStatesMap.get(item)
47
+ if (states === undefined) {
48
+ throw new Error(`Item not found: ${String(item)}`)
49
+ }
50
+ states.downCount = 0
51
+ }
52
+
53
+ private markItemUnavailable(item: Target): void {
54
+ const states = this.itemStatesMap.get(item)
55
+ if (states === undefined) {
56
+ throw new Error(`Item not found: ${String(item)}`)
57
+ }
58
+ states.downCount = states.downCount + 1
59
+ }
60
+
61
+ private resetAllItem(): void {
62
+ this.itemStatesMap.forEach((states) => {
63
+ states.downCount = 0
64
+ })
65
+ }
66
+
67
+ private getAvailableItemList(): Target[] {
68
+ const availableItemList: Target[] = []
69
+ this.itemStatesMap.forEach((states) => {
70
+ if (states.downCount < this.maxDownCount) {
71
+ availableItemList.push(states.item)
72
+ }
73
+ })
74
+ return availableItemList
75
+ }
76
+
77
+ async getItem(): Promise<GetItemResult<Target>> {
78
+ const controller = controllerFromEitherType<GetItemResult<Target>>()
79
+
80
+ if (this.itemStatesMap.size === 0) {
81
+ return await controller.returnLeft({
82
+ code: "NO_CHOICE",
83
+ })
84
+ }
85
+
86
+ const availableItemList = this.getAvailableItemList()
87
+ if (availableItemList.length === 0) {
88
+ if (this.resetAllWhenNoAvailable === false) {
89
+ return await controller.returnLeft({
90
+ code: "NO_AVAILABLE",
91
+ })
92
+ }
93
+ else {
94
+ this.resetAllItem()
95
+ return await this.getItem()
96
+ }
97
+ }
98
+
99
+ availableItemList.sort((a, b) => {
100
+ const aStates = this.itemStatesMap.get(a)!
101
+ const bStates = this.itemStatesMap.get(b)!
102
+ if (aStates.downCount === bStates.downCount) {
103
+ return 0
104
+ }
105
+ return aStates.downCount - bStates.downCount
106
+ })
107
+ const item = availableItemList[0]!
108
+
109
+ return await controller.returnRight({
110
+ item,
111
+ markAvailable: () => {
112
+ this.markItemAvailable(item)
113
+ },
114
+ markUnavailable: () => {
115
+ this.markItemUnavailable(item)
116
+ },
117
+ })
118
+ }
119
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./base-selector.ts"
2
+ export * from "./down-count-selector.ts"
@@ -0,0 +1,3 @@
1
+ export * from "./coordination/index.ts"
2
+ export * from "./dispatching/index.ts"
3
+ export * from "./scheduling/index.ts"
@@ -0,0 +1,2 @@
1
+ export * from "./task.ts"
2
+ export * from "./scheduler.ts"
@@ -0,0 +1,103 @@
1
+ import type { Task } from "./task.ts"
2
+
3
+ export interface RunLogEntry {
4
+ name: string
5
+ time: Date
6
+ }
7
+ export interface ErrLogEntry {
8
+ name: string
9
+ time: Date
10
+ reason: string
11
+ }
12
+ export interface SchedulerOptions {
13
+ tasks: Task[]
14
+ maxLogNumber?: number | undefined
15
+ }
16
+ export class Scheduler {
17
+ protected options: SchedulerOptions
18
+
19
+ protected isRunning: boolean
20
+ protected maxLogNumber: number
21
+ protected timers: Set<ReturnType<typeof setTimeout>>
22
+ protected runLog: RunLogEntry[] = []
23
+ protected errLog: ErrLogEntry[] = []
24
+
25
+ constructor(options: SchedulerOptions) {
26
+ this.options = options
27
+
28
+ this.isRunning = false
29
+ this.maxLogNumber = options.maxLogNumber ?? 100
30
+ this.timers = new Set()
31
+ }
32
+
33
+ addTask(task: Task): void {
34
+ this.options.tasks.push(task)
35
+ }
36
+
37
+ addRunLog(runLog: RunLogEntry): void {
38
+ this.runLog.push(runLog)
39
+ if (this.runLog.length > this.maxLogNumber) {
40
+ this.runLog = this.runLog.slice(this.maxLogNumber * -1)
41
+ }
42
+ }
43
+
44
+ addErrLog(errLog: ErrLogEntry): void {
45
+ this.errLog.push(errLog)
46
+ if (this.errLog.length > this.maxLogNumber) {
47
+ this.errLog = this.errLog.slice(this.maxLogNumber * -1)
48
+ }
49
+ }
50
+
51
+ getRunLog(): RunLogEntry[] {
52
+ return this.runLog
53
+ }
54
+
55
+ getErrLog(): ErrLogEntry[] {
56
+ return this.errLog
57
+ }
58
+
59
+ protected scheduleTask(task: Task): void {
60
+ const nextRun = task.getCron().nextRun()
61
+ if (nextRun === undefined) {
62
+ return
63
+ }
64
+
65
+ const delay = Math.max(nextRun.getTime() - Date.now(), 0)
66
+ const timer = setTimeout(() => {
67
+ this.timers.delete(timer)
68
+ if (this.isRunning !== true) {
69
+ return
70
+ }
71
+
72
+ this.scheduleTask(task)
73
+ void task.run()
74
+ .catch((reason: unknown) => {
75
+ this.addErrLog({
76
+ name: task.getName(),
77
+ time: new Date(),
78
+ reason: String(reason),
79
+ })
80
+ })
81
+ .finally(() => {
82
+ this.addRunLog({
83
+ name: task.getName(),
84
+ time: new Date(),
85
+ })
86
+ })
87
+ }, delay)
88
+
89
+ this.timers.add(timer)
90
+ }
91
+
92
+ run(): void {
93
+ if (this.isRunning === true) {
94
+ throw new Error("Scheduler is already running")
95
+ } else {
96
+ this.isRunning = true
97
+ }
98
+
99
+ for (const task of this.options.tasks) {
100
+ this.scheduleTask(task)
101
+ }
102
+ }
103
+ }
@@ -0,0 +1,32 @@
1
+ import { isCron, Cron } from "#Source/cron/index.ts"
2
+
3
+ export interface TaskOptions {
4
+ name: string
5
+ cron: Cron | string | Date
6
+ run: (() => void) | (() => Promise<void>)
7
+ }
8
+ export class Task {
9
+ protected options: TaskOptions
10
+ protected cron: Cron
11
+
12
+ constructor(options: TaskOptions) {
13
+ this.options = options
14
+ if (isCron(options.cron) === true) {
15
+ this.cron = options.cron
16
+ } else {
17
+ this.cron = new Cron({ pattern: options.cron })
18
+ }
19
+ }
20
+
21
+ getName(): string {
22
+ return this.options.name
23
+ }
24
+
25
+ getCron(): Cron {
26
+ return this.cron
27
+ }
28
+
29
+ async run(): Promise<void> {
30
+ await this.options.run()
31
+ }
32
+ }
@@ -1,109 +1,79 @@
1
1
  # Random
2
2
 
3
- Runtime utilities for generating random-oriented values with cross-runtime compatibility. This module currently focuses on UUID generation.
3
+ ## Description
4
4
 
5
- ## For Users
5
+ Random 模块用于提供随机值(random value)相关的基础能力,当前主要承载随机布尔值、随机字符串、随机数字与随机整数生成语义。
6
6
 
7
- This module provides lightweight helpers for random value generation scenarios.
7
+ 它关注的是“如何生成一类通用、可复用、语义清楚的随机值”,而不是各种标识格式校验、业务编码规则或安全承诺各异的随机机制集合。
8
8
 
9
- ### 1. Domain Areas
9
+ ## For Understanding
10
10
 
11
- 1. UUID: Generate RFC 4122 version-4 UUID strings with runtime-aware fallback behavior.
11
+ 理解 Random 模块时,应先把它放在应用边界附近。随机值通常意味着不可复现、带有环境依赖,或者会影响测试与调试行为,因此更合理的做法是把它看作边界输入的生成层,而不是让随机行为无约束地扩散到核心业务逻辑内部。
12
12
 
13
- Current public exports:
13
+ 当前这个模块的核心边界比较克制:它主要处理“随机布尔值如何生成”“随机字符串如何生成”“通用随机数字如何生成”与“通用随机整数如何生成”这一类问题。也就是说,它更关心 true 概率、字符集、长度、数值区间、整数边界、结果格式和重复概率,而不再负责 UUID 这类已经具有明确标识语义的格式模型。
14
14
 
15
- - `isUuid(input: string): boolean`
16
- - `assertUuid(input: string): void`
17
- - `getUuidVersion(input: string): number`
18
- - `generateUuid(): string`
15
+ 不适合放入本模块的能力,通常包括两类:一类是与密码学安全直接绑定、需要单独安全承诺的能力;另一类是已经形成稳定标识语义的格式模型,例如 UUID。前者应以更明确的安全语义单独建模,后者则应放入更贴近标识问题域的模块。
19
16
 
20
- ## For Contributors
17
+ ## For Using
21
18
 
22
- This guide documents conventions and best practices for implementing random utilities in this module.
19
+ 当你需要在应用或库的边界层生成随机布尔值、临时标识、关联值、会话键或其它非业务主键用途的随机值时,可以使用这个模块。它更适合被理解为“随机值生成接口”,而不是一个什么都往里塞的杂项工具箱。
23
20
 
24
- ### 1. Documentation and Comments
21
+ 从使用角度看,当前公共能力主要围绕随机布尔值、随机字符串、随机数字与随机整数生成展开。更合适的接入方式,是在边界层尽早生成这些随机值,再把得到的结果作为普通输入传给后续模块,从而避免内部逻辑直接依赖随机过程本身。
25
22
 
26
- #### 1.1 JSDoc Comment Format
23
+ 需要特别注意的是,本模块中的“随机”默认应被理解为通用应用层随机,而不是自动带有密码学强度承诺的安全随机。只要调用场景对安全性、可预测性、重复碰撞风险或跨环境一致性有更严格要求,就应先在设计上明确这些前提,而不要仅凭模块名称推断安全等级。
27
24
 
28
- Every exported function should include JSDoc in this form:
25
+ ## For Contributing
29
26
 
30
- ```typescript
31
- /**
32
- * Brief one-line description of what the function does.
33
- *
34
- * @example
35
- * ```
36
- * const example1 = generateUuid()
37
- * // Expect: true
38
- * const example2 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(example1)
39
- * ```
40
- */
41
- export const generateUuid = (): string => {
42
- ...
43
- }
44
- ```
27
+ 贡献 Random 模块时,应先确认新增能力表达的是稳定、清楚且可长期承诺的随机语义,而不是某种实现手段的直接暴露。对外公开的重点应该是“调用方需要什么随机值能力”,而不是“内部恰好如何生成这些值”。只要一个能力过度依赖当前实现细节,或者只在某个很窄的业务上下文中成立,就不适合进入这个模块。
45
28
 
46
- **Documentation Rules:**
47
- - First line: Clear, concise description starting with a verb (Generate, Check, Convert, etc.)
48
- - Add a blank line after the description
49
- - Use `@example` tag followed by triple backticks
50
- - Include multiple cases showing different scenarios
51
- - Use comment format: `// Expect: <result>`
52
- - Assign example results to variables like `example1`, `example2` to keep examples readable
53
- - Place `@see`(if has) after the `@example` block, separated by a blank line
54
- - Prefer deterministic examples; avoid asserting exact random outputs
55
- - If a function returns a structured string, show expected format characteristics
29
+ 在扩展时,应特别警惕两个方向的边界漂移。其一,不要把安全领域的问题用模糊措辞塞进来,例如把需要明确密码学承诺的能力混入通用随机工具中。其二,不要把已经形成稳定标识语义的格式模型重新拉回 Random。Random 模块应尽量保持为面向广泛调用方的随机值生成边界,而不是业务专用规则仓库。
56
30
 
57
- ### 2. Runtime Implementation Patterns
31
+ ### JSDoc 注释格式要求
58
32
 
59
- #### 2.1 Compatibility First
33
+ - 每个公开导出的目标(类型、函数、变量、类等)都应包含 JSDoc 注释,让人在不跳转实现的情况下就能理解用途。
34
+ - JSDoc 注释第一行应为清晰且简洁的描述,该描述优先使用中文(英文也可以)。
35
+ - 如果描述后还有其他内容,应在描述后加一个空行。
36
+ - 如果有示例,应使用 `@example` 标签,后接三重反引号代码块(不带语言标识)。
37
+ - 如果有示例,应包含多个场景,展示不同用法,尤其要覆盖常见组合方式或边界输入。
38
+ - 如果有示例,应使用注释格式说明每个场景:`// Expect: <result>`。
39
+ - 如果有示例,应将结果赋值给 `example1`、`example2` 之类的变量,以保持示例易读。
40
+ - 如果有示例,`// Expect: <result>` 应该位于 `example1`、`example2` 之前,以保持示例的逻辑清晰。
41
+ - 如果有示例,应优先使用确定性示例;避免断言精确的随机输出。
42
+ - 如果函数返回结构化字符串,应展示其预期格式特征。
43
+ - 如果有参考资料,应将 `@see` 放在 `@example` 代码块之后,并用一个空行分隔。
60
44
 
61
- - Prefer standard runtime APIs when available (for example, `crypto.randomUUID`).
62
- - Keep fallback logic for environments where modern APIs are unavailable.
45
+ ### 实现规范要求
63
46
 
64
- #### 2.2 Deterministic Interface
47
+ - 不同程序元素之间使用一个空行分隔,保持结构清楚。这里的程序元素,通常指函数、类型、常量,以及直接服务于它们的辅助元素。
48
+ - 某程序元素独占的辅助元素与该程序元素本身视为一个整体,不要在它们之间添加空行。
49
+ - 程序元素的辅助元素应该放置在该程序元素的上方,以保持阅读时的逻辑顺序。
50
+ - 若辅助元素被多个程序元素共享,则应将其视为独立的程序元素,放在这些程序元素中第一个相关目标的上方,并与后续程序元素之间保留一个空行。
51
+ - 辅助元素也应该像其它程序元素一样,保持清晰的命名和适当的注释,以便在需要阅读实现细节时能够快速理解它们的作用和使用方式。
52
+ - 辅助元素的命名必须以前缀 `internal` 开头(或 `Internal`,大小写不敏感)。
53
+ - 辅助元素永远不要公开导出。
54
+ - 被模块内多个不同文件中的程序元素共享的辅助元素,应该放在一个单独的文件中,例如 `./src/random/internal.ts`。
55
+ - 模块内可以包含子模块。只有当某个子目录表达一个稳定、可单独理解、且可能被父模块重导出的子问题域时,才应将其视为子模块。
56
+ - 子模块包含多个文件时,应该为其单独创建子文件夹,并为其创建单独的 Barrel 文件;父模块的 Barrel 文件再重导出子模块的 Barrel 文件。
57
+ - 子模块不需要有自己的 `README.md`。
58
+ - 子模块可以有自己的 `internal.ts` 文件,多个子模块共享的辅助元素应该放在父模块的 `internal.ts` 文件中,单个子模块共享的辅助元素应该放在该子模块的 `internal.ts` 文件中。
59
+ - 对模块依赖关系的要求(通常是不循环依赖或不反向依赖)与对 DRY 的要求可能产生冲突。此时,若复用的代码数量不大,可以适当牺牲 DRY,复制粘贴并保留必要的注释说明;若复用的代码数量较大,则可以将其抽象到新的文件或子模块中,如 `common.ts`,并在需要的地方导入使用。
60
+ - 与随机相关的实现应优先围绕结果格式、边界约束与调用方可理解的失败语义组织,不要把底层算法细节直接提升为公共承诺。
65
61
 
66
- - Public API shape should remain deterministic even if output values are random.
67
- - Return values should always conform to the documented output format.
62
+ ### 导出策略要求
68
63
 
69
- #### 2.3 Helper Placement
64
+ - 保持内部辅助项和内部符号为私有,不要让外部接入依赖临时性的内部结构。
65
+ - 每个模块都应有一个用于重导出所有公共 API 的 Barrel 文件。
66
+ - Barrel 文件应命名为 `index.ts`,放在模块目录根部,并且所有公共 API 都应从该文件导出。
67
+ - 新增公共能力时,应优先检查它是否表达稳定、清楚且值得长期维护的随机语义,而不是某段实现细节的便捷暴露;仅在确认需要长期对外承诺时再加入 Barrel 导出。
70
68
 
71
- - Place local helper constants/functions immediately before the utility they support.
72
- - Prefix non-exported helpers with `internal`.
73
- - Never export internal helpers.
69
+ ### 测试要求
74
70
 
75
- #### 2.4 Spacing
76
-
77
- - Separate different utility functions with a single blank line.
78
-
79
- ### 3. Naming Conventions
80
-
81
- #### 3.1 Function Name Format
82
-
83
- Use clear operation-oriented names in the `random` domain:
84
-
85
- - `isUuid` for UUID format validation checks
86
- - `assertUuid` for UUID validation that throws on malformed input
87
- - `getUuidVersion` for extracting UUID version numbers
88
- - `generateUuid` for UUID creation helpers
89
-
90
- Prefer names that clearly indicate output format and intent.
91
-
92
- ### 4. Export Strategy
93
-
94
- - Export all public utilities from domain files.
95
- - Re-export them through `index.ts`.
96
- - Do not export internal helpers.
97
-
98
- ### 5. Common Pitfalls to Avoid
99
-
100
- 1. Do not assume browser-only APIs are always available.
101
- 2. Do not couple random utilities to a specific runtime unless documented.
102
- 3. Do not assert exact random results in tests.
103
- 4. Do not mutate shared/global state permanently.
104
-
105
- ### 6. Testing Requirements
106
-
107
- - Write one test per function.
108
- - If multiple cases are needed, include them within the same test.
109
- - For random outputs, validate shape/constraints instead of exact value.
71
+ - 若程序元素是函数,则只为该函数编写一个测试,如果该函数需要测试多个用例,应放在同一个测试中。
72
+ - 若程序元素是类,则至少要为该类的每一个方法编写一个测试,如果该方法需要测试多个用例,应放在同一个测试中。
73
+ - 若程序元素是类,除了为该类的每一个方法编写至少一个测试之外,还可以为该类编写任意多个测试,以覆盖该类的不同使用场景或边界情况。
74
+ - 若编写测试时需要用到辅助元素(Mock 或 Spy 等),可以在测试文件中直接定义这些辅助元素。若辅助元素较为简单,则可以直接放在每一个测试内部,优先保证每个测试的独立性,而不是追求极致 DRY;若辅助元素较为复杂或需要在多个测试中复用,则可以放在测试文件顶部,供该测试文件中的所有测试使用。
75
+ - 测试顺序应与源文件中被测试目标的原始顺序保持一致。
76
+ - 若该模块不需要测试,必须在说明文件中明确说明该模块不需要测试,并说明理由。一般来说,只有在该模块没有可执行的公共函数、只承载类型层表达,或其语义已被上层模块的测试完整覆盖且重复测试几乎不再带来额外价值时,才适合这样处理。
77
+ - 模块的单元测试文件目录是 `./tests/unit/random`,若模块包含子模块,则子模块的单元测试文件目录为 `./tests/unit/random/<sub-module-name>`。
78
+ - 对随机布尔值相关能力,应优先测试概率边界、结果类型与极端输入语义,而不要把测试建立在脆弱的统计分布断言之上。
79
+ - 对随机字符串相关能力,应优先测试长度约束、字符集边界与结果特征,而不要把测试建立在脆弱的精确随机输出之上。
@@ -0,0 +1,66 @@
1
+ // Uint32 的取值空间大小为 2^32。
2
+ // Random 模块中的基础随机换算都会用到这一个范围常量。
3
+ const internalRandomUint32Range = 0x1_0000_0000
4
+
5
+ /**
6
+ * 取得一个随机 Uint32 值。
7
+ *
8
+ * 当运行时提供 `crypto.getRandomValues` 时,使用该随机源生成结果;否则回退到 `Math.random`。
9
+ * 该函数适合作为 Random 模块内外共享的基础随机整数入口。
10
+ *
11
+ * @example
12
+ * ```
13
+ * const value = getRandomUint32()
14
+ * // Expect: true
15
+ * const example1 = Number.isInteger(value) && value >= 0 && value < 0x1_0000_0000
16
+ * ```
17
+ */
18
+ export const getRandomUint32 = (): number => {
19
+ if (globalThis.crypto !== undefined && typeof globalThis.crypto.getRandomValues === "function") {
20
+ const buffer = new Uint32Array(1)
21
+ globalThis.crypto.getRandomValues(buffer)
22
+ return buffer[0]!
23
+ }
24
+
25
+ return Math.floor(Math.random() * internalRandomUint32Range)
26
+ }
27
+
28
+ /**
29
+ * 取得一个 `0` 到 `1` 之间的随机小数,包含下界,不包含上界。
30
+ *
31
+ * 该函数基于统一的 Uint32 随机源换算得到,适合作为随机数字与随机整数逻辑的公共基础。
32
+ *
33
+ * @example
34
+ * ```
35
+ * const value = getRandomUnitValue()
36
+ * // Expect: true
37
+ * const example1 = value >= 0 && value < 1
38
+ * ```
39
+ */
40
+ export const getRandomUnitValue = (): number => {
41
+ return getRandomUint32() / internalRandomUint32Range
42
+ }
43
+
44
+ /**
45
+ * 将随机整数均匀映射为索引。
46
+ *
47
+ * 若直接对 `alphabetLength` 取模,在长度不能整除 `2^32` 时会产生轻微偏差,因此这里使用拒绝采样,丢弃落在尾部残余区间的随机值。
48
+ *
49
+ * @example
50
+ * ```
51
+ * const index = getRandomIndex(3)
52
+ * // Expect: true
53
+ * const example1 = Number.isInteger(index) && index >= 0 && index < 3
54
+ * ```
55
+ */
56
+ export const getRandomIndex = (length: number): number => {
57
+ const limit = internalRandomUint32Range - (internalRandomUint32Range % length)
58
+
59
+ while (true) {
60
+ const value = getRandomUint32()
61
+
62
+ if (value < limit) {
63
+ return value % length
64
+ }
65
+ }
66
+ }
@@ -1 +1,5 @@
1
- export * from "./uuid.ts"
1
+ export * from "./base.ts"
2
+ export * from "./random-boolean.ts"
3
+ export * from "./random-integer.ts"
4
+ export * from "./random-number.ts"
5
+ export * from "./random-string.ts"
@@ -0,0 +1,40 @@
1
+ import { getRandomUnitValue } from "./base.ts"
2
+
3
+ // 默认 true 概率为 0.5。
4
+ // 当调用方未传入参数时,randomBoolean 会生成一个均匀分布的随机布尔值。
5
+ const internalDefaultRandomBooleanProbability = 0.5
6
+
7
+ // Step 1: 校验 true 概率。
8
+ //
9
+ // randomBoolean 只接受 0 到 1 之间的有限数值,避免把无效概率带入比较逻辑。
10
+ const internalAssertRandomBooleanProbability = (value: number): void => {
11
+ if (Number.isFinite(value) === false) {
12
+ throw new TypeError(`Expected probability to be a finite number, got: ${value}`)
13
+ }
14
+
15
+ if (value < 0 || value > 1) {
16
+ throw new RangeError(`Expected probability to be between 0 and 1 inclusive, got: ${value}`)
17
+ }
18
+ }
19
+
20
+ /**
21
+ * 生成随机布尔值,并可选指定返回 `true` 的概率。
22
+ *
23
+ * 当未传入参数时,`true` 与 `false` 的概率各为一半;当传入 `probability` 时,会将其解释为返回 `true` 的概率,其中 `0` 表示恒为 `false`,`1` 表示恒为 `true`。
24
+ *
25
+ * @example
26
+ * ```
27
+ * const sample1 = randomBoolean()
28
+ * // Expect: true
29
+ * const example1 = typeof sample1 === "boolean"
30
+ * // Expect: false
31
+ * const example2 = randomBoolean(0)
32
+ * // Expect: true
33
+ * const example3 = randomBoolean(1)
34
+ * ```
35
+ */
36
+ export const randomBoolean = (probability: number = internalDefaultRandomBooleanProbability): boolean => {
37
+ internalAssertRandomBooleanProbability(probability)
38
+
39
+ return getRandomUnitValue() < probability
40
+ }