@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,159 @@
1
+ import { fetch } from "undici"
2
+
3
+ export interface GetAuthAccessTokenOptions {
4
+ appId: string
5
+ appSecret: string
6
+ code: string
7
+ }
8
+ export interface GetAuthAccessTokenResult {
9
+ accessToken: string
10
+ expiresAt: number
11
+ refreshToken: string
12
+ openid: string
13
+ scope: string
14
+ isSnapshotUser: boolean
15
+ unionid?: string | undefined
16
+ }
17
+ export const getAuthAccessToken = async (
18
+ options: GetAuthAccessTokenOptions
19
+ ): Promise<GetAuthAccessTokenResult> => {
20
+ const { appId, appSecret, code } = options
21
+
22
+ interface AccessTokenSuccessResponse {
23
+ access_token: string
24
+ expires_in: number
25
+ refresh_token: string
26
+ openid: string
27
+ scope: string
28
+ is_snapshotuser?: 1
29
+ unionid?: string
30
+ }
31
+ interface AccessTokenErrorResponse {
32
+ errcode: number
33
+ errmsg: string
34
+ }
35
+ const url = `https://api.weixin.qq.com/sns/oauth2/access_token?appid=${appId}&secret=${appSecret}&code=${code}&grant_type=authorization_code`
36
+
37
+ try {
38
+ const now = Date.now()
39
+ const result = await fetch(
40
+ url,
41
+ {
42
+ method: "get",
43
+ },
44
+ )
45
+
46
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion
47
+ const json = await result.json() as AccessTokenSuccessResponse | AccessTokenErrorResponse
48
+
49
+ if ("errcode" in json) {
50
+ throw new Error(`${json.errcode}: ${json.errmsg}`)
51
+ }
52
+
53
+ return {
54
+ accessToken: json.access_token,
55
+ expiresAt: now + json.expires_in * 1_000,
56
+ refreshToken: json.refresh_token,
57
+ openid: json.openid,
58
+ scope: json.scope,
59
+ isSnapshotUser: json.is_snapshotuser === 1,
60
+ unionid: json.unionid,
61
+ }
62
+ }
63
+ catch (exception) {
64
+ console.error("Error getting access token:", exception)
65
+ throw exception
66
+ }
67
+ }
68
+
69
+ export interface GetUserInfoOptions {
70
+ accessToken: string
71
+ openId: string
72
+ }
73
+ export interface GetUserInfoResult {
74
+ openid: string
75
+ nickname: string
76
+ sex: number
77
+ province: string
78
+ city: string
79
+ country: string
80
+ headimgurl: string
81
+ privilege: string[]
82
+ unionid: string
83
+ }
84
+ export const getUserInfo = async (
85
+ options: GetUserInfoOptions
86
+ ): Promise<GetUserInfoResult> => {
87
+ const { accessToken, openId } = options
88
+
89
+ interface UserInfoSuccessResponse {
90
+ openid: string
91
+ nickname: string
92
+ sex: number
93
+ province: string
94
+ city: string
95
+ country: string
96
+ headimgurl: string
97
+ privilege: string[]
98
+ unionid: string
99
+ }
100
+ interface UserInfoErrorResponse {
101
+ errcode: number
102
+ errmsg: string
103
+ }
104
+ const url = `https://api.weixin.qq.com/sns/userinfo?access_token=${accessToken}&openid=${openId}&lang=zh_CN`
105
+
106
+ try {
107
+ const result = await fetch(
108
+ url,
109
+ {
110
+ method: "get",
111
+ },
112
+ )
113
+
114
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion
115
+ const json = await result.json() as UserInfoSuccessResponse | UserInfoErrorResponse
116
+
117
+ if ("errcode" in json) {
118
+ throw new Error(`${json.errcode}: ${json.errmsg}`)
119
+ }
120
+ return json
121
+ }
122
+ catch (exception) {
123
+ console.error("Error getting user info:", exception)
124
+ throw exception
125
+ }
126
+ }
127
+
128
+ export interface WeixinOfficialAccountOauth2Options {
129
+ appId: string
130
+ appSecret: string
131
+ }
132
+ /**
133
+ * @see {@link https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html | 微信网页开发 / 网页授权}
134
+ */
135
+ export class WeixinOfficialAccountOauth2 {
136
+ private readonly appId: string
137
+ private readonly appSecret: string
138
+
139
+ constructor(options: WeixinOfficialAccountOauth2Options) {
140
+ const { appId, appSecret } = options
141
+ this.appId = appId
142
+ this.appSecret = appSecret
143
+ }
144
+
145
+ async getAccessToken(code: string): Promise<GetAuthAccessTokenResult> {
146
+ return await getAuthAccessToken({
147
+ appId: this.appId,
148
+ appSecret: this.appSecret,
149
+ code,
150
+ })
151
+ }
152
+
153
+ async getUserInfo(accessToken: string, openId: string): Promise<GetUserInfoResult> {
154
+ return await getUserInfo({
155
+ accessToken,
156
+ openId,
157
+ })
158
+ }
159
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./authorization.ts"
2
+ export * from "./js-api.ts"
@@ -0,0 +1,134 @@
1
+ import { hash } from "node:crypto"
2
+
3
+ import { fetch } from "undici"
4
+
5
+ export interface GetJsApiAccessTokenOptions {
6
+ appId: string
7
+ appSecret: string
8
+ }
9
+ export interface GetJsApiAccessTokenResult {
10
+ accessToken: string
11
+ /**
12
+ * in milliseconds
13
+ */
14
+ accessTokenExpiresAt: number
15
+ }
16
+ /**
17
+ * @see {@link https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/getStableAccessToken.html}
18
+ * @see {@link https://mp.weixin.qq.com/debug | 微信公众平台 - 接口调试工具}
19
+ */
20
+ export const getJsApiAccessToken = async (
21
+ options: GetJsApiAccessTokenOptions
22
+ ): Promise<GetJsApiAccessTokenResult | undefined> => {
23
+ const { appId, appSecret } = options
24
+
25
+ interface StableTokenResponse {
26
+ access_token: string
27
+ expires_in: number
28
+ }
29
+ try {
30
+ const now = Date.now()
31
+ const result = await fetch(
32
+ "https://api.weixin.qq.com/cgi-bin/stable_token",
33
+ {
34
+ method: "post",
35
+ body: JSON.stringify({
36
+ grant_type: "client_credential",
37
+ appid: appId,
38
+ secret: appSecret,
39
+ force_refresh: false,
40
+ }),
41
+ },
42
+ )
43
+
44
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion
45
+ const json = await result.json() as StableTokenResponse
46
+
47
+ return {
48
+ accessToken: json.access_token,
49
+ accessTokenExpiresAt: now + json.expires_in * 1_000,
50
+ }
51
+ }
52
+ catch (exception) {
53
+ console.error(exception)
54
+ return undefined
55
+ }
56
+ }
57
+
58
+ export interface GetJsApiTicketOptions {
59
+ accessToken: string
60
+ }
61
+ export interface GetJsApiTicketResult {
62
+ jsApiTicket: string
63
+ /**
64
+ * in milliseconds
65
+ */
66
+ expiresAt: number
67
+ }
68
+ /**
69
+ * @see {@link https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#62 | 附录 1 - JS-SDK 使用权限签名算法}
70
+ */
71
+ export const getJsApiTicket = async (
72
+ options: GetJsApiTicketOptions
73
+ ): Promise<GetJsApiTicketResult | undefined> => {
74
+ const { accessToken } = options
75
+
76
+ interface JsApiTicketResponse {
77
+ errcode: number
78
+ errmsg: string
79
+ ticket: string
80
+ expires_in: number
81
+ }
82
+ try {
83
+ const now = Date.now()
84
+ const result = await fetch(
85
+ `https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=${accessToken}&type=jsapi`,
86
+ {
87
+ method: "get",
88
+ },
89
+ )
90
+
91
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion
92
+ const json = await result.json() as JsApiTicketResponse
93
+
94
+ return {
95
+ jsApiTicket: json.ticket,
96
+ expiresAt: now + json.expires_in * 1_000,
97
+ }
98
+ }
99
+ catch (exception) {
100
+ console.error(exception)
101
+ return undefined
102
+ }
103
+ }
104
+
105
+ export interface SignJsSdkConfigOptions {
106
+ url: string
107
+ jsApiTicket: string
108
+ }
109
+ export interface SignJsSdkConfigResult {
110
+ timestamp: number
111
+ nonceStr: string
112
+ signature: string
113
+ }
114
+ /**
115
+ * @see {@link https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#62 | 签名算法}
116
+ * @see {@link https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign | 微信公众平台 - 微信 JS 接口签名校验工具}
117
+ */
118
+ export const signJsSdkConfig = (
119
+ options: SignJsSdkConfigOptions
120
+ ): SignJsSdkConfigResult => {
121
+ const { url, jsApiTicket } = options
122
+
123
+ const nonceStr = Math.random().toString(36).slice(2, 17)
124
+ const timestamp = Math.floor(Date.now() / 1_000)
125
+
126
+ const rawString = `jsapi_ticket=${jsApiTicket}&noncestr=${nonceStr}&timestamp=${timestamp}&url=${url}`
127
+ const signature = hash("sha1", rawString)
128
+
129
+ return {
130
+ timestamp,
131
+ nonceStr,
132
+ signature,
133
+ }
134
+ }
@@ -0,0 +1 @@
1
+ export * from "./oauth2.ts"
@@ -0,0 +1,133 @@
1
+ import { fetch } from "undici"
2
+
3
+ export interface WeixinAccessTokenResult {
4
+ accessToken: string
5
+ expiresAt: number
6
+ refreshToken: string
7
+ openid: string
8
+ scope: string
9
+ unionid?: string | undefined
10
+ }
11
+
12
+ export interface WeixinUserInfoResult {
13
+ openid: string
14
+ nickname: string
15
+ sex: number
16
+ province: string
17
+ city: string
18
+ country: string
19
+ headimgurl: string
20
+ privilege: string[]
21
+ unionid?: string | undefined
22
+ }
23
+
24
+ export interface WeixinOauth2Options {
25
+ appId: string
26
+ appSecret: string
27
+ }
28
+
29
+ export class WeixinOauth2 {
30
+ protected options: WeixinOauth2Options
31
+
32
+ constructor(options: WeixinOauth2Options) {
33
+ this.options = options
34
+ }
35
+
36
+ /**
37
+ * {@link https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html | 关于微信快速登录功能的说明}
38
+ */
39
+ public async getAccessToken(
40
+ code: string
41
+ ): Promise<WeixinAccessTokenResult> {
42
+ interface AccessTokenSuccessResponse {
43
+ access_token: string
44
+ expires_in: number
45
+ refresh_token: string
46
+ openid: string
47
+ scope: string
48
+ unionid?: string
49
+ }
50
+ interface AccessTokenErrorResponse {
51
+ errcode: number
52
+ errmsg: string
53
+ }
54
+ const { appId, appSecret } = this.options
55
+ const url = `https://api.weixin.qq.com/sns/oauth2/access_token?appid=${appId}&secret=${appSecret}&code=${code}&grant_type=authorization_code`
56
+
57
+ try {
58
+ const now = Date.now()
59
+ const result = await fetch(
60
+ url,
61
+ {
62
+ method: "get",
63
+ },
64
+ )
65
+
66
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion
67
+ const json = await result.json() as AccessTokenSuccessResponse | AccessTokenErrorResponse
68
+
69
+ if ("errcode" in json) {
70
+ throw new Error(`${json.errcode}: ${json.errmsg}`)
71
+ }
72
+
73
+ return {
74
+ accessToken: json.access_token,
75
+ expiresAt: now + json.expires_in * 1_000,
76
+ refreshToken: json.refresh_token,
77
+ openid: json.openid,
78
+ scope: json.scope,
79
+ unionid: json.unionid,
80
+ }
81
+ }
82
+ catch (exception) {
83
+ console.error("Error getting access token:", exception)
84
+ throw exception
85
+ }
86
+ }
87
+
88
+ /**
89
+ * {@link https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Authorized_Interface_Calling_UnionID.html | 获取用户个人信息(UnionID机制)}
90
+ */
91
+ public async getUserInfo(
92
+ accessToken: string,
93
+ openId: string,
94
+ ): Promise<WeixinUserInfoResult> {
95
+ interface UserInfoSuccessResponse {
96
+ openid: string
97
+ nickname: string
98
+ sex: number
99
+ province: string
100
+ city: string
101
+ country: string
102
+ headimgurl: string
103
+ privilege: string[]
104
+ unionid?: string
105
+ }
106
+ interface UserInfoErrorResponse {
107
+ errcode: number
108
+ errmsg: string
109
+ }
110
+ const url = `https://api.weixin.qq.com/sns/userinfo?access_token=${accessToken}&openid=${openId}&lang=zh_CN`
111
+
112
+ try {
113
+ const result = await fetch(
114
+ url,
115
+ {
116
+ method: "get",
117
+ },
118
+ )
119
+
120
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion
121
+ const json = await result.json() as UserInfoSuccessResponse | UserInfoErrorResponse
122
+
123
+ if ("errcode" in json) {
124
+ throw new Error(`${json.errcode}: ${json.errmsg}`)
125
+ }
126
+ return json
127
+ }
128
+ catch (exception) {
129
+ console.error("Error getting user info:", exception)
130
+ throw exception
131
+ }
132
+ }
133
+ }
@@ -0,0 +1,225 @@
1
+ import { expect, test } from "vitest"
2
+
3
+ import {
4
+ ABORT_MANAGER_MODE,
5
+ AbortManager,
6
+ all,
7
+ any,
8
+ fromAbortFriendlyOptions,
9
+ fromAbortable,
10
+ fromWithAbortSignal,
11
+ getAbortSignal,
12
+ isAborted,
13
+ } from "#Source/abort/index.ts"
14
+
15
+ test("getAbortSignal resolves AbortSignal from supported abortables", () => {
16
+ const abortController = new AbortController()
17
+ const abortManager = new AbortManager({})
18
+
19
+ expect(getAbortSignal(abortController.signal)).toBe(abortController.signal)
20
+ expect(getAbortSignal(abortController)).toBe(abortController.signal)
21
+ expect(getAbortSignal(abortManager)).toBe(abortManager.abortSignal)
22
+ })
23
+
24
+ test("isAborted reflects abort state from abortables", () => {
25
+ const abortController = new AbortController()
26
+
27
+ expect(isAborted(abortController.signal)).toBe(false)
28
+ expect(isAborted(abortController)).toBe(false)
29
+
30
+ abortController.abort("stop")
31
+
32
+ expect(isAborted(abortController.signal)).toBe(true)
33
+ expect(isAborted(abortController)).toBe(true)
34
+ })
35
+
36
+ test("AbortManager constructor links upstream abortManager from options", () => {
37
+ const upstream = new AbortManager({})
38
+ const abortManager = new AbortManager({ abortManager: upstream })
39
+
40
+ expect(abortManager.isAborted()).toBe(false)
41
+
42
+ upstream.abort("upstream")
43
+
44
+ expect(abortManager.isAborted()).toBe(true)
45
+ })
46
+
47
+ test("AbortManager.isAborted mirrors internal abortSignal", () => {
48
+ const abortManager = new AbortManager({})
49
+
50
+ expect(abortManager.isAborted()).toBe(false)
51
+
52
+ abortManager.abort("done")
53
+
54
+ expect(abortManager.isAborted()).toBe(true)
55
+ })
56
+
57
+ test("AbortManager.setMode sets mode and supports chaining", () => {
58
+ const abortManager = new AbortManager({})
59
+ const upstream1 = new AbortController()
60
+ const upstream2 = new AbortController()
61
+
62
+ const returned = abortManager
63
+ .setMode(ABORT_MANAGER_MODE.ANY)
64
+ .addUpstreams([upstream1.signal, upstream2.signal])
65
+
66
+ expect(returned).toBe(abortManager)
67
+
68
+ upstream1.abort("one")
69
+
70
+ expect(abortManager.isAborted()).toBe(true)
71
+ })
72
+
73
+ test("AbortManager.addUpstreams makes manager abort when upstream condition is met", () => {
74
+ const abortManager = new AbortManager({})
75
+ const upstream = new AbortController()
76
+
77
+ abortManager.addUpstreams([upstream.signal])
78
+
79
+ expect(abortManager.isAborted()).toBe(false)
80
+
81
+ upstream.abort("upstream")
82
+
83
+ expect(abortManager.isAborted()).toBe(true)
84
+ })
85
+
86
+ test("AbortManager.removeUpstreams detaches selected upstreams", () => {
87
+ const abortManager = new AbortManager({})
88
+ const upstream1 = new AbortController()
89
+ const upstream2 = new AbortController()
90
+
91
+ abortManager
92
+ .setMode(ABORT_MANAGER_MODE.ANY)
93
+ .addUpstreams([upstream1.signal, upstream2.signal])
94
+ .removeUpstreams([upstream1.signal])
95
+
96
+ upstream1.abort("ignored")
97
+
98
+ expect(abortManager.isAborted()).toBe(false)
99
+
100
+ upstream2.abort("active")
101
+
102
+ expect(abortManager.isAborted()).toBe(true)
103
+ })
104
+
105
+ test("AbortManager.removeAllUpstreams detaches all upstreams", () => {
106
+ const abortManager = new AbortManager({})
107
+ const upstream1 = new AbortController()
108
+ const upstream2 = new AbortController()
109
+
110
+ abortManager
111
+ .setMode(ABORT_MANAGER_MODE.ANY)
112
+ .addUpstreams([upstream1.signal, upstream2.signal])
113
+ .removeAllUpstreams()
114
+
115
+ upstream1.abort("ignored")
116
+ upstream2.abort("ignored")
117
+
118
+ expect(abortManager.isAborted()).toBe(false)
119
+ })
120
+
121
+ test("AbortManager.tryToAbort evaluates mode conditions", () => {
122
+ const abortManager = new AbortManager({})
123
+ const upstream1 = new AbortController()
124
+ const upstream2 = new AbortController()
125
+
126
+ abortManager
127
+ .setMode(ABORT_MANAGER_MODE.ALL)
128
+ .addUpstreams([upstream1.signal, upstream2.signal])
129
+
130
+ upstream1.abort("first")
131
+ abortManager.tryToAbort()
132
+
133
+ expect(abortManager.isAborted()).toBe(false)
134
+
135
+ upstream2.abort("second")
136
+ abortManager.tryToAbort()
137
+
138
+ expect(abortManager.isAborted()).toBe(true)
139
+ })
140
+
141
+ test("AbortManager.abort aborts and applies auto-remove policy", () => {
142
+ class TestAbortManager extends AbortManager {
143
+ removeAllUpstreamsCalled = 0
144
+
145
+ override removeAllUpstreams(): this {
146
+ this.removeAllUpstreamsCalled = this.removeAllUpstreamsCalled + 1
147
+ return super.removeAllUpstreams()
148
+ }
149
+ }
150
+
151
+ const enabled = new TestAbortManager({ autoRemoveUpstreamsAfterAbort: true })
152
+ enabled.addUpstreams([new AbortController().signal])
153
+ enabled.abort("enabled")
154
+
155
+ const disabled = new TestAbortManager({ autoRemoveUpstreamsAfterAbort: false })
156
+ disabled.addUpstreams([new AbortController().signal])
157
+ disabled.abort("disabled")
158
+
159
+ expect(enabled.isAborted()).toBe(true)
160
+ expect(enabled.removeAllUpstreamsCalled).toBe(1)
161
+ expect(disabled.isAborted()).toBe(true)
162
+ expect(disabled.removeAllUpstreamsCalled).toBe(0)
163
+ })
164
+
165
+ test("fromAbortable creates manager from a single upstream abortable", () => {
166
+ const upstream = new AbortController()
167
+ const abortManager = fromAbortable(upstream)
168
+
169
+ expect(abortManager.isAborted()).toBe(false)
170
+
171
+ upstream.abort("stop")
172
+
173
+ expect(abortManager.isAborted()).toBe(true)
174
+ })
175
+
176
+ test("any creates manager that aborts when any upstream aborts", () => {
177
+ const upstream1 = new AbortController()
178
+ const upstream2 = new AbortController()
179
+ const abortManager = any([upstream1.signal, null, undefined, upstream2.signal])
180
+
181
+ expect(abortManager.isAborted()).toBe(false)
182
+
183
+ upstream1.abort("first")
184
+
185
+ expect(abortManager.isAborted()).toBe(true)
186
+ })
187
+
188
+ test("all creates manager that aborts only when all upstreams abort", () => {
189
+ const upstream1 = new AbortController()
190
+ const upstream2 = new AbortController()
191
+ const abortManager = all([upstream1.signal, null, undefined, upstream2.signal])
192
+
193
+ upstream1.abort("first")
194
+
195
+ expect(abortManager.isAborted()).toBe(false)
196
+
197
+ upstream2.abort("second")
198
+
199
+ expect(abortManager.isAborted()).toBe(true)
200
+ })
201
+
202
+ test("fromWithAbortSignal creates manager from options.abortSignal", () => {
203
+ const upstream = new AbortController()
204
+ const withSignal = fromWithAbortSignal({ abortSignal: upstream.signal })
205
+ const withoutSignal = fromWithAbortSignal({})
206
+
207
+ expect(withSignal.isAborted()).toBe(false)
208
+ expect(withoutSignal.isAborted()).toBe(false)
209
+
210
+ upstream.abort("stop")
211
+
212
+ expect(withSignal.isAborted()).toBe(true)
213
+ expect(withoutSignal.isAborted()).toBe(false)
214
+ })
215
+
216
+ test("fromAbortFriendlyOptions creates manager using abort-friendly options", () => {
217
+ const upstream = new AbortManager({})
218
+ const abortManager = fromAbortFriendlyOptions({ abortManager: upstream })
219
+
220
+ expect(abortManager.isAborted()).toBe(false)
221
+
222
+ upstream.abort("stop")
223
+
224
+ expect(abortManager.isAborted()).toBe(true)
225
+ })