@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
@@ -1,17 +1,18 @@
1
1
  import { expect, test } from "vitest"
2
2
 
3
3
  import {
4
- streamConsumeInAsyncMacroTask,
5
- streamConsumeInSyncMacroTask,
6
- streamFromArray,
7
- streamTransformInAsyncMacroTask,
4
+ streamConsumeInMacroTask,
5
+ streamConsumeInMicroTask,
6
+ streamFromArrayEager,
7
+ streamFromArrayLazy,
8
+ streamTransformInMacroTask,
8
9
  } from "#Source/basic/index.ts"
9
10
 
10
- test("streamFromArray creates a readable stream from array values", async () => {
11
+ test("streamFromArrayEager creates a readable stream from array values", async () => {
11
12
  const source = [1, 2, 3]
12
13
  const values: number[] = []
13
14
 
14
- const stream = streamFromArray(source)
15
+ const stream = streamFromArrayEager(source)
15
16
  for await (const value of stream) {
16
17
  values.push(value)
17
18
  }
@@ -19,18 +20,38 @@ test("streamFromArray creates a readable stream from array values", async () =>
19
20
  expect(values).toEqual(source)
20
21
  })
21
22
 
22
- test("streamConsumeInSyncMacroTask consumes stream and handles callback errors", async () => {
23
+ test("streamFromArrayLazy creates a lazily consumed readable stream", async () => {
24
+ const source = [1, 2, 3]
25
+ const stream = streamFromArrayLazy(source)
26
+ const reader = stream.getReader()
27
+
28
+ const firstChunk = await reader.read()
29
+ const secondChunk = await reader.read()
30
+ const thirdChunk = await reader.read()
31
+ const doneChunk = await reader.read()
32
+
33
+ expect(firstChunk).toEqual({ done: false, value: 1 })
34
+ expect(secondChunk).toEqual({ done: false, value: 2 })
35
+ expect(thirdChunk).toEqual({ done: false, value: 3 })
36
+ expect(doneChunk).toEqual({ done: true, value: undefined })
37
+ })
38
+
39
+ test("streamConsumeInMicroTask consumes stream and handles callback errors", async () => {
23
40
  const consumed: number[] = []
24
41
  let doneCalled = false
25
42
 
26
- await streamConsumeInSyncMacroTask<number>({
27
- readableStream: streamFromArray([1, 2, 3]),
28
- onValue: (chunk) => {
29
- consumed.push(chunk)
30
- },
31
- onDone: () => {
32
- doneCalled = true
33
- },
43
+ await new Promise<void>((resolve, reject) => {
44
+ streamConsumeInMicroTask<number>({
45
+ readableStream: streamFromArrayEager([1, 2, 3]),
46
+ onValue: (chunk) => {
47
+ consumed.push(chunk)
48
+ },
49
+ onDone: () => {
50
+ doneCalled = true
51
+ resolve()
52
+ },
53
+ onError: reject,
54
+ })
34
55
  })
35
56
 
36
57
  expect(consumed).toEqual([1, 2, 3])
@@ -38,30 +59,33 @@ test("streamConsumeInSyncMacroTask consumes stream and handles callback errors",
38
59
 
39
60
  let errorMessage = ""
40
61
  let doneCalledInErrorCase = false
41
- await streamConsumeInSyncMacroTask<number>({
42
- readableStream: streamFromArray([1, 2]),
43
- onValue: (chunk) => {
44
- if (chunk === 2) {
45
- throw new Error("boom")
46
- }
47
- },
48
- onDone: () => {
49
- doneCalledInErrorCase = true
50
- },
51
- onError: (error) => {
52
- errorMessage = error.message
53
- },
62
+ await new Promise<void>((resolve) => {
63
+ streamConsumeInMicroTask<number>({
64
+ readableStream: streamFromArrayEager([1, 2]),
65
+ onValue: (chunk) => {
66
+ if (chunk === 2) {
67
+ throw new Error("boom")
68
+ }
69
+ },
70
+ onDone: () => {
71
+ doneCalledInErrorCase = true
72
+ },
73
+ onError: (error) => {
74
+ errorMessage = error.message
75
+ resolve()
76
+ },
77
+ })
54
78
  })
55
79
 
56
80
  expect(errorMessage).toContain("boom")
57
81
  expect(doneCalledInErrorCase).toBe(false)
58
82
  })
59
83
 
60
- test("streamConsumeInAsyncMacroTask consumes stream and forwards callback failures", async () => {
84
+ test("streamConsumeInMacroTask consumes stream and forwards callback failures", async () => {
61
85
  const consumed: number[] = []
62
86
  await new Promise<void>((resolve, reject) => {
63
- streamConsumeInAsyncMacroTask<number>({
64
- readableStream: streamFromArray([1, 2, 3]),
87
+ streamConsumeInMacroTask<number>({
88
+ readableStream: streamFromArrayEager([1, 2, 3]),
65
89
  onValue: (chunk) => {
66
90
  consumed.push(chunk)
67
91
  },
@@ -78,8 +102,8 @@ test("streamConsumeInAsyncMacroTask consumes stream and forwards callback failur
78
102
 
79
103
  let errorMessage = ""
80
104
  await new Promise<void>((resolve, reject) => {
81
- streamConsumeInAsyncMacroTask<number>({
82
- readableStream: streamFromArray([1]),
105
+ streamConsumeInMacroTask<number>({
106
+ readableStream: streamFromArrayEager([1]),
83
107
  onValue: () => {
84
108
  throw new Error("async-boom")
85
109
  },
@@ -96,13 +120,13 @@ test("streamConsumeInAsyncMacroTask consumes stream and forwards callback failur
96
120
  expect(errorMessage).toContain("async-boom")
97
121
  })
98
122
 
99
- test("streamTransformInAsyncMacroTask transforms values and handles invalid inputs/errors", async () => {
123
+ test("streamTransformInMacroTask transforms values and handles invalid inputs/errors", async () => {
100
124
  expect(() => {
101
- streamTransformInAsyncMacroTask<number, number>({})
102
- }).toThrowError("Either readableStream or reader must be provided")
125
+ streamTransformInMacroTask<number, number>({})
126
+ }).toThrow("Either readableStream or reader must be provided")
103
127
 
104
- const transformed = streamTransformInAsyncMacroTask<number, number>({
105
- readableStream: streamFromArray([1, 2, 3]),
128
+ const transformed = streamTransformInMacroTask<number, number>({
129
+ readableStream: streamFromArrayEager([1, 2, 3]),
106
130
  onChunk: (chunk, controller) => {
107
131
  if (chunk.done === true) {
108
132
  controller.close()
@@ -117,4 +141,33 @@ test("streamTransformInAsyncMacroTask transforms values and handles invalid inpu
117
141
  values.push(value)
118
142
  }
119
143
  expect(values).toEqual([10, 20, 30])
144
+
145
+ const reader = streamFromArrayEager([4]).getReader()
146
+ const transformedFromReader = streamTransformInMacroTask<number, number>({
147
+ reader,
148
+ onChunk: (chunk, controller) => {
149
+ if (chunk.done === true) {
150
+ controller.close()
151
+ return
152
+ }
153
+ controller.enqueue(chunk.value + 1)
154
+ },
155
+ })
156
+
157
+ expect(await Array.fromAsync(transformedFromReader)).toEqual([5])
158
+
159
+ let refinedError: Error | undefined
160
+ const errored = streamTransformInMacroTask<number, number>({
161
+ readableStream: streamFromArrayEager([1]),
162
+ onChunk: () => {
163
+ throw new Error("transform-boom")
164
+ },
165
+ onError: (error) => {
166
+ refinedError = new Error(`wrapped:${error.message}`)
167
+ return refinedError
168
+ },
169
+ })
170
+
171
+ await expect(Array.fromAsync(errored)).rejects.toThrow("wrapped:Error: transform-boom")
172
+ expect(refinedError?.message).toBe("wrapped:Error: transform-boom")
120
173
  })
@@ -5,7 +5,6 @@ import {
5
5
  stringCamelCaseToKebabCase,
6
6
  stringHelloWord,
7
7
  stringKebabCaseToCamelCase,
8
- stringRandom,
9
8
  stringSliceByUnits,
10
9
  stringSmartSplit,
11
10
  stringSplit,
@@ -13,14 +12,6 @@ import {
13
12
  stringTruncateByUnits,
14
13
  } from "#Source/basic/index.ts"
15
14
 
16
- test("stringRandom returns expected output", () => {
17
- const value = stringRandom(12)
18
- expect(value).toHaveLength(12)
19
-
20
- const constrained = stringRandom(10, "ab")
21
- expect(constrained.split("").every(char => char === "a" || char === "b")).toBe(true)
22
- })
23
-
24
15
  test("stringCamelCaseToKebabCase converts correctly", () => {
25
16
  expect(stringCamelCaseToKebabCase("helloWorld")).toBe("hello-world")
26
17
  expect(stringCamelCaseToKebabCase("ab2Cd")).toBe("ab2-cd")
@@ -0,0 +1,110 @@
1
+ import { expect, test } from "vitest"
2
+
3
+ import {
4
+ doSrgbColorValuesMeetContrastRatio,
5
+ isLightSrgbColorValue,
6
+ linearRgbColorValueToRelativeLuminance,
7
+ linearRgbColorValueToTristimulusSum,
8
+ linearRgbColorValueToXyChromaticityValue,
9
+ pickReadableSrgbTextColorValue,
10
+ srgbColorValueToRelativeLuminance,
11
+ srgbColorValueToTristimulusSum,
12
+ srgbColorValueToXyChromaticityValue,
13
+ srgbColorValuesToContrastRatio,
14
+ } from "#Source/color/index.ts"
15
+
16
+ test("linearRgbColorValueToRelativeLuminance follows the explicit RGB branch pipeline", () => {
17
+ expect(linearRgbColorValueToRelativeLuminance({ red: 1, green: 0, blue: 0, alpha: 1 })).toBe(0.212_673)
18
+ expect(linearRgbColorValueToRelativeLuminance({ red: 1, green: 1, blue: 1, alpha: 1 })).toBe(1)
19
+ })
20
+
21
+ test("srgbColorValueToRelativeLuminance follows the explicit RGB branch pipeline", () => {
22
+ expect(srgbColorValueToRelativeLuminance({ red: 51, green: 102, blue: 153, alpha: 1 })).toBe(0.125_053)
23
+ expect(srgbColorValueToRelativeLuminance({ red: 255, green: 255, blue: 255, alpha: 1 })).toBe(1)
24
+ })
25
+
26
+ test("srgbColorValuesToContrastRatio supports accessibility-oriented comparisons", () => {
27
+ expect(srgbColorValuesToContrastRatio(
28
+ { red: 0, green: 0, blue: 0, alpha: 1 },
29
+ { red: 255, green: 255, blue: 255, alpha: 1 },
30
+ )).toBe(21)
31
+
32
+ expect(srgbColorValuesToContrastRatio(
33
+ { red: 51, green: 102, blue: 153, alpha: 1 },
34
+ { red: 51, green: 102, blue: 153, alpha: 1 },
35
+ )).toBe(1)
36
+ })
37
+
38
+ test("doSrgbColorValuesMeetContrastRatio validates thresholds and comparisons", () => {
39
+ expect(() => doSrgbColorValuesMeetContrastRatio(
40
+ { red: 0, green: 0, blue: 0, alpha: 1 },
41
+ { red: 255, green: 255, blue: 255, alpha: 1 },
42
+ 0.99,
43
+ )).toThrow(RangeError)
44
+
45
+ expect(doSrgbColorValuesMeetContrastRatio(
46
+ { red: 0, green: 0, blue: 0, alpha: 1 },
47
+ { red: 255, green: 255, blue: 255, alpha: 1 },
48
+ 7,
49
+ )).toBe(true)
50
+
51
+ expect(doSrgbColorValuesMeetContrastRatio(
52
+ { red: 120, green: 120, blue: 120, alpha: 1 },
53
+ { red: 255, green: 255, blue: 255, alpha: 1 },
54
+ 4.5,
55
+ )).toBe(false)
56
+ })
57
+
58
+ test("pickReadableSrgbTextColorValue chooses the more readable candidate", () => {
59
+ expect(pickReadableSrgbTextColorValue({ red: 0, green: 102, blue: 204, alpha: 1 })).toEqual({
60
+ red: 255,
61
+ green: 255,
62
+ blue: 255,
63
+ alpha: 1,
64
+ })
65
+
66
+ expect(pickReadableSrgbTextColorValue(
67
+ { red: 250, green: 240, blue: 210, alpha: 1 },
68
+ {
69
+ darkColor: { red: 32, green: 32, blue: 32, alpha: 1 },
70
+ lightColor: { red: 255, green: 255, blue: 255, alpha: 1 },
71
+ },
72
+ )).toEqual({
73
+ red: 32,
74
+ green: 32,
75
+ blue: 32,
76
+ alpha: 1,
77
+ })
78
+ })
79
+
80
+ test("linearRgbColorValueToTristimulusSum follows the explicit RGB branch pipeline", () => {
81
+ expect(linearRgbColorValueToTristimulusSum({ red: 1, green: 0, blue: 0, alpha: 1 })).toBe(0.644_463)
82
+ expect(linearRgbColorValueToTristimulusSum({ red: 1, green: 1, blue: 1, alpha: 1 })).toBe(3.039_3)
83
+ })
84
+
85
+ test("srgbColorValueToTristimulusSum follows the explicit RGB branch pipeline", () => {
86
+ expect(srgbColorValueToTristimulusSum({ red: 51, green: 102, blue: 153, alpha: 1 })).toBe(0.562_889)
87
+ expect(srgbColorValueToTristimulusSum({ red: 255, green: 255, blue: 255, alpha: 1 })).toBe(3.039_3)
88
+ })
89
+
90
+ test("linearRgbColorValueToXyChromaticityValue follows the explicit RGB branch pipeline", () => {
91
+ expect(linearRgbColorValueToXyChromaticityValue({ red: 1, green: 0, blue: 0, alpha: 1 })).toEqual({
92
+ x: 0.64,
93
+ y: 0.33,
94
+ })
95
+ })
96
+
97
+ test("srgbColorValueToXyChromaticityValue follows the explicit RGB branch pipeline", () => {
98
+ expect(srgbColorValueToXyChromaticityValue({ red: 51, green: 102, blue: 153, alpha: 1 })).toEqual({
99
+ x: 0.210_775,
100
+ y: 0.222_163,
101
+ })
102
+ })
103
+
104
+ test("isLightSrgbColorValue uses relative luminance threshold", () => {
105
+ expect(isLightSrgbColorValue({ red: 255, green: 255, blue: 255, alpha: 1 })).toBe(true)
106
+ expect(isLightSrgbColorValue({ red: 0, green: 0, blue: 0, alpha: 1 })).toBe(false)
107
+ expect(isLightSrgbColorValue({ red: 128, green: 128, blue: 128, alpha: 1 }, 0.2)).toBe(true)
108
+
109
+ expect(() => isLightSrgbColorValue({ red: 128, green: 128, blue: 128, alpha: 1 }, 1.1)).toThrow(RangeError)
110
+ })
@@ -0,0 +1,56 @@
1
+ import { expect, test } from "vitest"
2
+
3
+ import {
4
+ createHslColorValue,
5
+ createHslColorValueByString,
6
+ createHsvColorValue,
7
+ createHsvColorValueByString,
8
+ createLinearRgbColorValue,
9
+ createSrgbColorValue,
10
+ createSrgbColorValueByString,
11
+ } from "#Source/color/index.ts"
12
+
13
+ test("createLinearRgbColorValue accepts finite linear channels", () => {
14
+ expect(createLinearRgbColorValue({ red: -0.25, green: 0.4, blue: 1.2, alpha: 1 }))
15
+ .toEqual({ red: -0.25, green: 0.4, blue: 1.2, alpha: 1 })
16
+
17
+ expect(createLinearRgbColorValue({ red: 0, green: 0, blue: 0, alpha: 0.5 }))
18
+ .toEqual({ red: 0, green: 0, blue: 0, alpha: 0.5 })
19
+
20
+ expect(() => createLinearRgbColorValue({ red: Number.NaN, green: 0.4, blue: 1.2, alpha: 1 })).toThrow(RangeError)
21
+ })
22
+
23
+ test("createSrgbColorValue validates byte channels", () => {
24
+ expect(createSrgbColorValue({ red: 51, green: 102, blue: 153, alpha: 0.5 }))
25
+ .toEqual({ red: 51, green: 102, blue: 153, alpha: 0.5 })
26
+
27
+ expect(() => createSrgbColorValue({ red: -1, green: 0, blue: 0, alpha: 1 })).toThrow(RangeError)
28
+ expect(() => createSrgbColorValue({ red: 0, green: 0, blue: 256, alpha: 1 })).toThrow(RangeError)
29
+ })
30
+
31
+ test("createHslColorValue normalizes hue and validates units", () => {
32
+ expect(createHslColorValue({ hue: -30, saturation: 0.5, lightness: 0.4, alpha: 1 }))
33
+ .toEqual({ hue: 330, saturation: 0.5, lightness: 0.4, alpha: 1 })
34
+ expect(() => createHslColorValue({ hue: 0, saturation: 1.5, lightness: 0.4, alpha: 1 })).toThrow(RangeError)
35
+ })
36
+
37
+ test("createHsvColorValue normalizes hue and validates units", () => {
38
+ expect(createHsvColorValue({ hue: 390, saturation: 0.5, value: 0.6, alpha: 1 }))
39
+ .toEqual({ hue: 30, saturation: 0.5, value: 0.6, alpha: 1 })
40
+ expect(() => createHsvColorValue({ hue: 0, saturation: 0.5, value: 1.5, alpha: 1 })).toThrow(RangeError)
41
+ })
42
+
43
+ test("createSrgbColorValueByString stays deterministic", () => {
44
+ expect(createSrgbColorValueByString("mobius")).toEqual(createSrgbColorValueByString("mobius"))
45
+ expect(createSrgbColorValueByString("mobius")).not.toEqual(createSrgbColorValueByString("planet"))
46
+ })
47
+
48
+ test("createHslColorValueByString stays deterministic", () => {
49
+ expect(createHslColorValueByString("mobius")).toEqual(createHslColorValueByString("mobius"))
50
+ expect(createHslColorValueByString("mobius")).not.toEqual(createHslColorValueByString("planet"))
51
+ })
52
+
53
+ test("createHsvColorValueByString stays deterministic", () => {
54
+ expect(createHsvColorValueByString("mobius")).toEqual(createHsvColorValueByString("mobius"))
55
+ expect(createHsvColorValueByString("mobius")).not.toEqual(createHsvColorValueByString("planet"))
56
+ })
@@ -0,0 +1,60 @@
1
+ import { expect, test } from "vitest"
2
+
3
+ import {
4
+ hslColorValueToSrgbColorValue,
5
+ hsvColorValueToSrgbColorValue,
6
+ linearRgbColorValueToSrgbColorValue,
7
+ srgbColorValueToHslColorValue,
8
+ srgbColorValueToHsvColorValue,
9
+ srgbColorValueToLinearRgbColorValue,
10
+ } from "#Source/color/index.ts"
11
+
12
+ test("linearRgbColorValueToSrgbColorValue converts in-gamut linear values to sRGB", () => {
13
+ expect(linearRgbColorValueToSrgbColorValue({ red: 1, green: 0, blue: 0, alpha: 0.5 }))
14
+ .toEqual({ red: 255, green: 0, blue: 0, alpha: 0.5 })
15
+
16
+ expect(linearRgbColorValueToSrgbColorValue({ red: 0.5, green: 0.5, blue: 0.5, alpha: 0.5 }))
17
+ .toEqual({ red: 188, green: 188, blue: 188, alpha: 0.5 })
18
+
19
+ expect(() => linearRgbColorValueToSrgbColorValue({ red: 1.2, green: 0, blue: 0, alpha: 1 })).toThrow(RangeError)
20
+ })
21
+
22
+ test("srgbColorValueToLinearRgbColorValue converts sRGB values to linear values", () => {
23
+ expect(srgbColorValueToLinearRgbColorValue({ red: 255, green: 0, blue: 0, alpha: 0.5 }))
24
+ .toEqual({ red: 1, green: 0, blue: 0, alpha: 0.5 })
25
+
26
+ expect(srgbColorValueToLinearRgbColorValue({ red: 128, green: 128, blue: 128, alpha: 0.5 }))
27
+ .toEqual({ red: 0.215_861, green: 0.215_861, blue: 0.215_861, alpha: 0.5 })
28
+ })
29
+
30
+ test("srgbColorValueToHslColorValue converts sRGB values to HSL", () => {
31
+ expect(srgbColorValueToHslColorValue({ red: 51, green: 102, blue: 153, alpha: 1 }))
32
+ .toEqual({ hue: 210, saturation: 0.5, lightness: 0.4, alpha: 1 })
33
+
34
+ expect(srgbColorValueToHslColorValue({ red: 128, green: 128, blue: 128, alpha: 1 }))
35
+ .toEqual({ hue: 0, saturation: 0, lightness: 0.501_961, alpha: 1 })
36
+ })
37
+
38
+ test("hslColorValueToSrgbColorValue converts HSL values to sRGB", () => {
39
+ expect(hslColorValueToSrgbColorValue({ hue: 210, saturation: 0.5, lightness: 0.4, alpha: 1 }))
40
+ .toEqual({ red: 51, green: 102, blue: 153, alpha: 1 })
41
+
42
+ expect(hslColorValueToSrgbColorValue({ hue: 0, saturation: 0, lightness: 0.501_961, alpha: 1 }))
43
+ .toEqual({ red: 128, green: 128, blue: 128, alpha: 1 })
44
+ })
45
+
46
+ test("srgbColorValueToHsvColorValue converts sRGB values to HSV", () => {
47
+ expect(srgbColorValueToHsvColorValue({ red: 51, green: 102, blue: 153, alpha: 1 }))
48
+ .toEqual({ hue: 210, saturation: 0.666_667, value: 0.6, alpha: 1 })
49
+
50
+ expect(srgbColorValueToHsvColorValue({ red: 128, green: 128, blue: 128, alpha: 1 }))
51
+ .toEqual({ hue: 0, saturation: 0, value: 0.501_961, alpha: 1 })
52
+ })
53
+
54
+ test("hsvColorValueToSrgbColorValue converts HSV values to sRGB", () => {
55
+ expect(hsvColorValueToSrgbColorValue({ hue: 210, saturation: 0.666_667, value: 0.6, alpha: 1 }))
56
+ .toEqual({ red: 51, green: 102, blue: 153, alpha: 1 })
57
+
58
+ expect(hsvColorValueToSrgbColorValue({ hue: 0, saturation: 0, value: 0.501_961, alpha: 1 }))
59
+ .toEqual({ red: 128, green: 128, blue: 128, alpha: 1 })
60
+ })
@@ -0,0 +1,103 @@
1
+ import { expect, test } from "vitest"
2
+
3
+ import {
4
+ compositeLinearRgbColorValueOverBackground,
5
+ compositeSrgbColorValueOverBackground,
6
+ darkenSrgbColorValue,
7
+ fadeSrgbColorValue,
8
+ lightenSrgbColorValue,
9
+ mixLinearRgbColorValues,
10
+ mixSrgbColorValues,
11
+ shadeSrgbColorValue,
12
+ tintSrgbColorValue,
13
+ toneSrgbColorValue,
14
+ } from "#Source/color/index.ts"
15
+
16
+ test("mixLinearRgbColorValues mixes directly on the linear layer", () => {
17
+ expect(mixLinearRgbColorValues(
18
+ { red: 1, green: 0, blue: 0, alpha: 1 },
19
+ { red: 0, green: 0, blue: 1, alpha: 1 },
20
+ 0.5,
21
+ )).toEqual({ red: 0.5, green: 0, blue: 0.5, alpha: 1 })
22
+
23
+ expect(() => mixLinearRgbColorValues(
24
+ { red: 1, green: 0, blue: 0, alpha: 1 },
25
+ { red: 0, green: 0, blue: 1, alpha: 1 },
26
+ -0.1,
27
+ )).toThrow(RangeError)
28
+ })
29
+
30
+ test("mixSrgbColorValues mixes through the explicit sRGB to linear pipeline", () => {
31
+ expect(mixSrgbColorValues(
32
+ { red: 255, green: 0, blue: 0, alpha: 1 },
33
+ { red: 0, green: 0, blue: 255, alpha: 1 },
34
+ 0.5,
35
+ )).toEqual({ red: 188, green: 0, blue: 188, alpha: 1 })
36
+
37
+ expect(mixSrgbColorValues(
38
+ { red: 255, green: 0, blue: 0, alpha: 1 },
39
+ { red: 255, green: 255, blue: 255, alpha: 1 },
40
+ 0.5,
41
+ )).toEqual({ red: 255, green: 188, blue: 188, alpha: 1 })
42
+ })
43
+
44
+ test("compositeLinearRgbColorValueOverBackground composites directly on the linear layer", () => {
45
+ expect(compositeLinearRgbColorValueOverBackground(
46
+ { red: 1, green: 1, blue: 1, alpha: 0.5 },
47
+ { red: 0, green: 0, blue: 1, alpha: 1 },
48
+ )).toEqual({ red: 0.5, green: 0.5, blue: 1, alpha: 1 })
49
+
50
+ expect(compositeLinearRgbColorValueOverBackground(
51
+ { red: 1, green: 0, blue: 0, alpha: 1 },
52
+ { red: 0, green: 0, blue: 1, alpha: 1 },
53
+ )).toEqual({ red: 1, green: 0, blue: 0, alpha: 1 })
54
+ })
55
+
56
+ test("compositeSrgbColorValueOverBackground composites through the explicit sRGB to linear pipeline", () => {
57
+ expect(compositeSrgbColorValueOverBackground(
58
+ { red: 255, green: 255, blue: 255, alpha: 0.5 },
59
+ { red: 0, green: 0, blue: 255, alpha: 1 },
60
+ )).toEqual({ red: 188, green: 188, blue: 255, alpha: 1 })
61
+
62
+ expect(compositeSrgbColorValueOverBackground(
63
+ { red: 255, green: 0, blue: 0, alpha: 1 },
64
+ { red: 0, green: 0, blue: 255, alpha: 1 },
65
+ )).toEqual({ red: 255, green: 0, blue: 0, alpha: 1 })
66
+ })
67
+
68
+ test("fadeSrgbColorValue updates alpha while keeping channels stable", () => {
69
+ expect(fadeSrgbColorValue({ red: 51, green: 102, blue: 153, alpha: 1 }, 0.4)).toEqual({
70
+ red: 51,
71
+ green: 102,
72
+ blue: 153,
73
+ alpha: 0.4,
74
+ })
75
+
76
+ expect(() => fadeSrgbColorValue({ red: 51, green: 102, blue: 153, alpha: 1 }, -0.1)).toThrow(RangeError)
77
+ })
78
+
79
+ test("tintSrgbColorValue stays on the legal RGB branch pipeline", () => {
80
+ expect(tintSrgbColorValue({ red: 51, green: 102, blue: 153, alpha: 1 }, 0.5)).toEqual({ red: 190, green: 198, blue: 212, alpha: 1 })
81
+ expect(tintSrgbColorValue({ red: 51, green: 102, blue: 153, alpha: 1 }, 0)).toEqual({ red: 51, green: 102, blue: 153, alpha: 1 })
82
+ expect(() => tintSrgbColorValue({ red: 51, green: 102, blue: 153, alpha: 1 }, 1.1)).toThrow(RangeError)
83
+ })
84
+
85
+ test("shadeSrgbColorValue stays on the legal RGB branch pipeline", () => {
86
+ expect(shadeSrgbColorValue({ red: 51, green: 102, blue: 153, alpha: 1 }, 0.5)).toEqual({ red: 35, green: 73, blue: 111, alpha: 1 })
87
+ expect(shadeSrgbColorValue({ red: 51, green: 102, blue: 153, alpha: 1 }, 0)).toEqual({ red: 51, green: 102, blue: 153, alpha: 1 })
88
+ })
89
+
90
+ test("toneSrgbColorValue stays on the legal RGB branch pipeline", () => {
91
+ expect(toneSrgbColorValue({ red: 51, green: 102, blue: 153, alpha: 1 }, 0.5)).toEqual({ red: 141, green: 153, blue: 171, alpha: 1 })
92
+ expect(toneSrgbColorValue({ red: 51, green: 102, blue: 153, alpha: 1 }, 0)).toEqual({ red: 51, green: 102, blue: 153, alpha: 1 })
93
+ })
94
+
95
+ test("lightenSrgbColorValue stays on the legal RGB branch pipeline", () => {
96
+ expect(lightenSrgbColorValue({ red: 51, green: 102, blue: 153, alpha: 1 }, 0.25)).toEqual({ red: 143, green: 160, blue: 186, alpha: 1 })
97
+ expect(lightenSrgbColorValue({ red: 51, green: 102, blue: 153, alpha: 1 }, 0)).toEqual({ red: 51, green: 102, blue: 153, alpha: 1 })
98
+ })
99
+
100
+ test("darkenSrgbColorValue stays on the legal RGB branch pipeline", () => {
101
+ expect(darkenSrgbColorValue({ red: 51, green: 102, blue: 153, alpha: 1 }, 0.25)).toEqual({ red: 44, green: 89, blue: 134, alpha: 1 })
102
+ expect(darkenSrgbColorValue({ red: 51, green: 102, blue: 153, alpha: 1 }, 0)).toEqual({ red: 51, green: 102, blue: 153, alpha: 1 })
103
+ })
@@ -0,0 +1,66 @@
1
+ import { expect, test } from "vitest"
2
+
3
+ import {
4
+ colorStringToSrgbColorValue,
5
+ hexColorStringToSrgbColorValue,
6
+ hslColorStringToHslColorValue,
7
+ hslaColorStringToHslColorValue,
8
+ hsvColorStringToHsvColorValue,
9
+ hsvaColorStringToHsvColorValue,
10
+ isHexColorString,
11
+ rgbColorStringToSrgbColorValue,
12
+ rgbaColorStringToSrgbColorValue,
13
+ } from "#Source/color/index.ts"
14
+
15
+ test("isHexColorString validates supported hex formats", () => {
16
+ expect(isHexColorString("#F0A")).toBe(true)
17
+ expect(isHexColorString("11223344")).toBe(true)
18
+ expect(isHexColorString("#12")).toBe(false)
19
+ })
20
+
21
+ test("hexColorStringToSrgbColorValue supports short and long hex formats", () => {
22
+ expect(hexColorStringToSrgbColorValue("#F0A")).toEqual({ red: 255, green: 0, blue: 170, alpha: 1 })
23
+ expect(hexColorStringToSrgbColorValue("#112233")).toEqual({ red: 17, green: 34, blue: 51, alpha: 1 })
24
+ expect(hexColorStringToSrgbColorValue("#11223344")).toEqual({ red: 17, green: 34, blue: 51, alpha: 0.266_7 })
25
+ expect(() => hexColorStringToSrgbColorValue("#12")).toThrow(TypeError)
26
+ })
27
+
28
+ test("rgbColorStringToSrgbColorValue returns sRGB values", () => {
29
+ expect(rgbColorStringToSrgbColorValue("rgb(51, 102, 153)")).toEqual({ red: 51, green: 102, blue: 153, alpha: 1 })
30
+ expect(rgbColorStringToSrgbColorValue("rgb(0, 0, 0)")).toEqual({ red: 0, green: 0, blue: 0, alpha: 1 })
31
+ })
32
+
33
+ test("rgbaColorStringToSrgbColorValue returns sRGB values", () => {
34
+ expect(rgbaColorStringToSrgbColorValue("rgba(51, 102, 153, 0.5)")).toEqual({ red: 51, green: 102, blue: 153, alpha: 0.5 })
35
+ expect(() => rgbaColorStringToSrgbColorValue("rgba(51, 102, 153, 1.5)")).toThrow(RangeError)
36
+ })
37
+
38
+ test("hslColorStringToHslColorValue returns HSL values", () => {
39
+ expect(hslColorStringToHslColorValue("hsl(210, 50%, 40%)")).toEqual({ hue: 210, saturation: 0.5, lightness: 0.4, alpha: 1 })
40
+ expect(hslColorStringToHslColorValue("hsl(-30, 50%, 40%)")).toEqual({ hue: 330, saturation: 0.5, lightness: 0.4, alpha: 1 })
41
+ expect(() => hslColorStringToHslColorValue("hsl(210, 120%, 40%)")).toThrow(RangeError)
42
+ })
43
+
44
+ test("hslaColorStringToHslColorValue returns HSL values", () => {
45
+ expect(hslaColorStringToHslColorValue("hsla(210, 50%, 40%, 0.5)")).toEqual({ hue: 210, saturation: 0.5, lightness: 0.4, alpha: 0.5 })
46
+ expect(hslaColorStringToHslColorValue("hsla(0, 0%, 100%, 1)")).toEqual({ hue: 0, saturation: 0, lightness: 1, alpha: 1 })
47
+ })
48
+
49
+ test("hsvColorStringToHsvColorValue returns HSV values", () => {
50
+ expect(hsvColorStringToHsvColorValue("hsv(210, 66.67%, 60%)")).toEqual({ hue: 210, saturation: 0.666_7, value: 0.6, alpha: 1 })
51
+ expect(hsvColorStringToHsvColorValue("hsv(390, 50%, 60%)")).toEqual({ hue: 30, saturation: 0.5, value: 0.6, alpha: 1 })
52
+ })
53
+
54
+ test("hsvaColorStringToHsvColorValue returns HSV values", () => {
55
+ expect(hsvaColorStringToHsvColorValue("hsva(210, 66.67%, 60%, 0.5)")).toEqual({ hue: 210, saturation: 0.666_7, value: 0.6, alpha: 0.5 })
56
+ expect(hsvaColorStringToHsvColorValue("hsva(0, 0%, 100%, 1)")).toEqual({ hue: 0, saturation: 0, value: 1, alpha: 1 })
57
+ expect(() => hsvaColorStringToHsvColorValue("hsva(210, 66.67%, 60%, -0.1)")).toThrow(RangeError)
58
+ })
59
+
60
+ test("colorStringToSrgbColorValue folds supported string inputs into sRGB", () => {
61
+ expect(colorStringToSrgbColorValue("#336699")).toEqual({ red: 51, green: 102, blue: 153, alpha: 1 })
62
+ expect(colorStringToSrgbColorValue("hsl(210, 50%, 40%)")).toEqual({ red: 51, green: 102, blue: 153, alpha: 1 })
63
+ expect(colorStringToSrgbColorValue("hsv(210, 66.67%, 60%)")).toEqual({ red: 51, green: 102, blue: 153, alpha: 1 })
64
+ expect(colorStringToSrgbColorValue("rgb(51, 102, 153)")).toEqual({ red: 51, green: 102, blue: 153, alpha: 1 })
65
+ expect(() => colorStringToSrgbColorValue("foo(0, 0%, 0%)")).toThrow(TypeError)
66
+ })
@@ -0,0 +1,46 @@
1
+ import { expect, test } from "vitest"
2
+
3
+ import {
4
+ hslColorValueToHslColorString,
5
+ hslColorValueToHslaColorString,
6
+ hsvColorValueToHsvColorString,
7
+ hsvColorValueToHsvaColorString,
8
+ srgbColorValueToHexColorString,
9
+ srgbColorValueToRgbColorString,
10
+ srgbColorValueToRgbaColorString,
11
+ } from "#Source/color/index.ts"
12
+
13
+ test("srgbColorValueToHexColorString serializes hex output", () => {
14
+ expect(srgbColorValueToHexColorString({ red: 51, green: 102, blue: 153, alpha: 0.5 })).toBe("#336699")
15
+ expect(srgbColorValueToHexColorString({ red: 51, green: 102, blue: 153, alpha: 0.5 }, { includeAlpha: true })).toBe("#33669980")
16
+ })
17
+
18
+ test("srgbColorValueToRgbColorString serializes rgb output", () => {
19
+ expect(srgbColorValueToRgbColorString({ red: 51, green: 102, blue: 153, alpha: 0.5 })).toBe("rgb(51, 102, 153)")
20
+ expect(srgbColorValueToRgbColorString({ red: 0, green: 0, blue: 0, alpha: 1 })).toBe("rgb(0, 0, 0)")
21
+ })
22
+
23
+ test("srgbColorValueToRgbaColorString serializes rgba output", () => {
24
+ expect(srgbColorValueToRgbaColorString({ red: 51, green: 102, blue: 153, alpha: 0.5 })).toBe("rgba(51, 102, 153, 0.5)")
25
+ expect(srgbColorValueToRgbaColorString({ red: 255, green: 255, blue: 255, alpha: 1 })).toBe("rgba(255, 255, 255, 1)")
26
+ })
27
+
28
+ test("hslColorValueToHslColorString serializes hsl output", () => {
29
+ expect(hslColorValueToHslColorString({ hue: 210, saturation: 0.5, lightness: 0.4, alpha: 0.5 })).toBe("hsl(210, 50%, 40%)")
30
+ expect(hslColorValueToHslColorString({ hue: 0, saturation: 0, lightness: 1, alpha: 1 })).toBe("hsl(0, 0%, 100%)")
31
+ })
32
+
33
+ test("hslColorValueToHslaColorString serializes hsla output", () => {
34
+ expect(hslColorValueToHslaColorString({ hue: 210, saturation: 0.5, lightness: 0.4, alpha: 0.5 })).toBe("hsla(210, 50%, 40%, 0.5)")
35
+ expect(hslColorValueToHslaColorString({ hue: 0, saturation: 0, lightness: 1, alpha: 1 })).toBe("hsla(0, 0%, 100%, 1)")
36
+ })
37
+
38
+ test("hsvColorValueToHsvColorString serializes hsv output", () => {
39
+ expect(hsvColorValueToHsvColorString({ hue: 210, saturation: 0.666_7, value: 0.6, alpha: 0.5 })).toBe("hsv(210, 66.67%, 60%)")
40
+ expect(hsvColorValueToHsvColorString({ hue: 0, saturation: 0, value: 1, alpha: 1 })).toBe("hsv(0, 0%, 100%)")
41
+ })
42
+
43
+ test("hsvColorValueToHsvaColorString serializes hsva output", () => {
44
+ expect(hsvColorValueToHsvaColorString({ hue: 210, saturation: 0.666_7, value: 0.6, alpha: 0.5 })).toBe("hsva(210, 66.67%, 60%, 0.5)")
45
+ expect(hsvColorValueToHsvaColorString({ hue: 0, saturation: 0, value: 1, alpha: 1 })).toBe("hsva(0, 0%, 100%, 1)")
46
+ })