@planet-matrix/mobius-model 0.6.0 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/oxlint.config.ts +1 -2
  3. package/package.json +29 -17
  4. package/scripts/build.ts +2 -52
  5. package/src/ai/README.md +1 -0
  6. package/src/ai/ai.ts +107 -0
  7. package/src/ai/chat-completion-ai/aihubmix-chat-completion.ts +78 -0
  8. package/src/ai/chat-completion-ai/chat-completion-ai.ts +270 -0
  9. package/src/ai/chat-completion-ai/chat-completion.ts +189 -0
  10. package/src/ai/chat-completion-ai/index.ts +7 -0
  11. package/src/ai/chat-completion-ai/lingyiwanwu-chat-completion.ts +78 -0
  12. package/src/ai/chat-completion-ai/ohmygpt-chat-completion.ts +78 -0
  13. package/src/ai/chat-completion-ai/openai-next-chat-completion.ts +78 -0
  14. package/src/ai/embedding-ai/embedding-ai.ts +63 -0
  15. package/src/ai/embedding-ai/embedding.ts +50 -0
  16. package/src/ai/embedding-ai/index.ts +4 -0
  17. package/src/ai/embedding-ai/openai-next-embedding.ts +23 -0
  18. package/src/ai/index.ts +4 -0
  19. package/src/aio/README.md +100 -0
  20. package/src/aio/content.ts +141 -0
  21. package/src/aio/index.ts +3 -0
  22. package/src/aio/json.ts +127 -0
  23. package/src/aio/prompt.ts +246 -0
  24. package/src/basic/README.md +20 -15
  25. package/src/basic/error.ts +19 -5
  26. package/src/basic/function.ts +2 -2
  27. package/src/basic/index.ts +1 -0
  28. package/src/basic/promise.ts +141 -71
  29. package/src/basic/schedule.ts +111 -0
  30. package/src/basic/stream.ts +135 -25
  31. package/src/credential/README.md +107 -0
  32. package/src/credential/api-key.ts +158 -0
  33. package/src/credential/bearer.ts +73 -0
  34. package/src/credential/index.ts +4 -0
  35. package/src/credential/json-web-token.ts +96 -0
  36. package/src/credential/password.ts +170 -0
  37. package/src/cron/README.md +86 -0
  38. package/src/cron/cron.ts +87 -0
  39. package/src/cron/index.ts +1 -0
  40. package/src/drizzle/README.md +1 -0
  41. package/src/drizzle/drizzle.ts +1 -0
  42. package/src/drizzle/helper.ts +47 -0
  43. package/src/drizzle/index.ts +5 -0
  44. package/src/drizzle/infer.ts +52 -0
  45. package/src/drizzle/kysely.ts +8 -0
  46. package/src/drizzle/pagination.ts +198 -0
  47. package/src/email/README.md +1 -0
  48. package/src/email/index.ts +1 -0
  49. package/src/email/resend.ts +25 -0
  50. package/src/event/class-event-proxy.ts +5 -6
  51. package/src/event/common.ts +13 -3
  52. package/src/event/event-manager.ts +3 -3
  53. package/src/event/instance-event-proxy.ts +5 -6
  54. package/src/event/internal.ts +4 -4
  55. package/src/exception/README.md +28 -19
  56. package/src/exception/error/error.ts +123 -0
  57. package/src/exception/error/index.ts +2 -0
  58. package/src/exception/error/match.ts +38 -0
  59. package/src/exception/error/must-fix.ts +17 -0
  60. package/src/exception/index.ts +2 -0
  61. package/src/file-system/find.ts +53 -0
  62. package/src/file-system/index.ts +2 -0
  63. package/src/file-system/path.ts +76 -0
  64. package/src/file-system/resolve.ts +22 -0
  65. package/src/form/README.md +25 -0
  66. package/src/form/index.ts +1 -0
  67. package/src/form/inputor-controller/base.ts +861 -0
  68. package/src/form/inputor-controller/boolean.ts +39 -0
  69. package/src/form/inputor-controller/file.ts +39 -0
  70. package/src/form/inputor-controller/form.ts +179 -0
  71. package/src/form/inputor-controller/helper.ts +117 -0
  72. package/src/form/inputor-controller/index.ts +17 -0
  73. package/src/form/inputor-controller/multi-select.ts +99 -0
  74. package/src/form/inputor-controller/number.ts +116 -0
  75. package/src/form/inputor-controller/select.ts +109 -0
  76. package/src/form/inputor-controller/text.ts +82 -0
  77. package/src/http/READMD.md +1 -0
  78. package/src/http/api/api-core.ts +84 -0
  79. package/src/http/api/api-handler.ts +79 -0
  80. package/src/http/api/api-host.ts +47 -0
  81. package/src/http/api/api-result.ts +56 -0
  82. package/src/http/api/api-schema.ts +154 -0
  83. package/src/http/api/api-server.ts +130 -0
  84. package/src/http/api/api-test.ts +142 -0
  85. package/src/http/api/api-type.ts +34 -0
  86. package/src/http/api/api.ts +81 -0
  87. package/src/http/api/index.ts +11 -0
  88. package/src/http/api-adapter/api-core-node-http.ts +260 -0
  89. package/src/http/api-adapter/api-host-node-http.ts +156 -0
  90. package/src/http/api-adapter/api-result-arktype.ts +294 -0
  91. package/src/http/api-adapter/api-result-zod.ts +286 -0
  92. package/src/http/api-adapter/index.ts +5 -0
  93. package/src/http/bin/gen-api-list/gen-api-list.ts +126 -0
  94. package/src/http/bin/gen-api-list/index.ts +1 -0
  95. package/src/http/bin/gen-api-test/gen-api-test.ts +136 -0
  96. package/src/http/bin/gen-api-test/index.ts +1 -0
  97. package/src/http/bin/gen-api-type/calc-code.ts +25 -0
  98. package/src/http/bin/gen-api-type/gen-api-type.ts +127 -0
  99. package/src/http/bin/gen-api-type/index.ts +2 -0
  100. package/src/http/bin/index.ts +2 -0
  101. package/src/http/index.ts +3 -0
  102. package/src/huawei/README.md +1 -0
  103. package/src/huawei/index.ts +2 -0
  104. package/src/huawei/moderation/index.ts +1 -0
  105. package/src/huawei/moderation/moderation.ts +355 -0
  106. package/src/huawei/obs/esdk-obs-nodejs.d.ts +87 -0
  107. package/src/huawei/obs/index.ts +1 -0
  108. package/src/huawei/obs/obs.ts +42 -0
  109. package/src/index.ts +21 -2
  110. package/src/json/README.md +92 -0
  111. package/src/json/index.ts +1 -0
  112. package/src/json/repair.ts +18 -0
  113. package/src/log/logger.ts +15 -4
  114. package/src/openai/README.md +1 -0
  115. package/src/openai/index.ts +1 -0
  116. package/src/openai/openai.ts +509 -0
  117. package/src/orchestration/README.md +9 -7
  118. package/src/orchestration/dispatching/dispatcher.ts +83 -0
  119. package/src/orchestration/dispatching/index.ts +2 -0
  120. package/src/orchestration/dispatching/selector/base-selector.ts +39 -0
  121. package/src/orchestration/dispatching/selector/down-count-selector.ts +119 -0
  122. package/src/orchestration/dispatching/selector/index.ts +2 -0
  123. package/src/orchestration/index.ts +2 -0
  124. package/src/orchestration/scheduling/index.ts +2 -0
  125. package/src/orchestration/scheduling/scheduler.ts +103 -0
  126. package/src/orchestration/scheduling/task.ts +32 -0
  127. package/src/random/README.md +8 -7
  128. package/src/random/base.ts +66 -0
  129. package/src/random/index.ts +5 -1
  130. package/src/random/random-boolean.ts +40 -0
  131. package/src/random/random-integer.ts +60 -0
  132. package/src/random/random-number.ts +72 -0
  133. package/src/random/random-string.ts +66 -0
  134. package/src/request/README.md +108 -0
  135. package/src/request/fetch/base.ts +108 -0
  136. package/src/request/fetch/browser.ts +280 -0
  137. package/src/request/fetch/general.ts +20 -0
  138. package/src/request/fetch/index.ts +4 -0
  139. package/src/request/fetch/nodejs.ts +280 -0
  140. package/src/request/index.ts +2 -0
  141. package/src/request/request/base.ts +246 -0
  142. package/src/request/request/general.ts +63 -0
  143. package/src/request/request/index.ts +3 -0
  144. package/src/request/request/resource.ts +68 -0
  145. package/src/result/README.md +4 -0
  146. package/src/result/controller.ts +58 -0
  147. package/src/result/either.ts +363 -0
  148. package/src/result/generator.ts +168 -0
  149. package/src/result/index.ts +3 -0
  150. package/src/route/README.md +105 -0
  151. package/src/route/adapter/browser.ts +122 -0
  152. package/src/route/adapter/driver.ts +56 -0
  153. package/src/route/adapter/index.ts +2 -0
  154. package/src/route/index.ts +3 -0
  155. package/src/route/router/index.ts +2 -0
  156. package/src/route/router/route.ts +630 -0
  157. package/src/route/router/router.ts +1641 -0
  158. package/src/route/uri/hash.ts +307 -0
  159. package/src/route/uri/index.ts +7 -0
  160. package/src/route/uri/pathname.ts +376 -0
  161. package/src/route/uri/search.ts +412 -0
  162. package/src/service/README.md +1 -0
  163. package/src/service/index.ts +1 -0
  164. package/src/service/service.ts +110 -0
  165. package/src/socket/README.md +105 -0
  166. package/src/socket/client/index.ts +2 -0
  167. package/src/socket/client/socket-unit.ts +658 -0
  168. package/src/socket/client/socket.ts +203 -0
  169. package/src/socket/common/index.ts +2 -0
  170. package/src/socket/common/socket-unit-common.ts +23 -0
  171. package/src/socket/common/socket-unit-heartbeat.ts +427 -0
  172. package/src/socket/index.ts +3 -0
  173. package/src/socket/server/index.ts +3 -0
  174. package/src/socket/server/server.ts +183 -0
  175. package/src/socket/server/socket-unit.ts +448 -0
  176. package/src/socket/server/socket.ts +264 -0
  177. package/src/storage/table.ts +3 -3
  178. package/src/timer/expiration/expiration-manager.ts +3 -3
  179. package/src/timer/expiration/remaining-manager.ts +3 -3
  180. package/src/tube/README.md +99 -0
  181. package/src/tube/helper.ts +137 -0
  182. package/src/tube/index.ts +2 -0
  183. package/src/tube/tube.ts +880 -0
  184. package/src/weixin/README.md +1 -0
  185. package/src/weixin/index.ts +2 -0
  186. package/src/weixin/official-account/authorization.ts +157 -0
  187. package/src/weixin/official-account/index.ts +2 -0
  188. package/src/weixin/official-account/js-api.ts +132 -0
  189. package/src/weixin/open/index.ts +1 -0
  190. package/src/weixin/open/oauth2.ts +131 -0
  191. package/tests/unit/ai/ai.spec.ts +85 -0
  192. package/tests/unit/aio/content.spec.ts +105 -0
  193. package/tests/unit/aio/json.spec.ts +146 -0
  194. package/tests/unit/aio/prompt.spec.ts +111 -0
  195. package/tests/unit/basic/error.spec.ts +16 -4
  196. package/tests/unit/basic/promise.spec.ts +158 -50
  197. package/tests/unit/basic/schedule.spec.ts +74 -0
  198. package/tests/unit/basic/stream.spec.ts +90 -37
  199. package/tests/unit/credential/api-key.spec.ts +36 -0
  200. package/tests/unit/credential/bearer.spec.ts +23 -0
  201. package/tests/unit/credential/json-web-token.spec.ts +23 -0
  202. package/tests/unit/credential/password.spec.ts +40 -0
  203. package/tests/unit/cron/cron.spec.ts +84 -0
  204. package/tests/unit/event/class-event-proxy.spec.ts +3 -3
  205. package/tests/unit/event/event-manager.spec.ts +3 -3
  206. package/tests/unit/event/instance-event-proxy.spec.ts +3 -3
  207. package/tests/unit/exception/error/error.spec.ts +83 -0
  208. package/tests/unit/exception/error/match.spec.ts +81 -0
  209. package/tests/unit/form/inputor-controller/base.spec.ts +458 -0
  210. package/tests/unit/form/inputor-controller/boolean.spec.ts +30 -0
  211. package/tests/unit/form/inputor-controller/file.spec.ts +27 -0
  212. package/tests/unit/form/inputor-controller/form.spec.ts +120 -0
  213. package/tests/unit/form/inputor-controller/helper.spec.ts +67 -0
  214. package/tests/unit/form/inputor-controller/multi-select.spec.ts +34 -0
  215. package/tests/unit/form/inputor-controller/number.spec.ts +36 -0
  216. package/tests/unit/form/inputor-controller/select.spec.ts +49 -0
  217. package/tests/unit/form/inputor-controller/text.spec.ts +34 -0
  218. package/tests/unit/http/api/api-core-host.spec.ts +207 -0
  219. package/tests/unit/http/api/api-schema.spec.ts +120 -0
  220. package/tests/unit/http/api/api-server.spec.ts +363 -0
  221. package/tests/unit/http/api/api-test.spec.ts +117 -0
  222. package/tests/unit/http/api/api.spec.ts +121 -0
  223. package/tests/unit/http/api-adapter/node-http.spec.ts +187 -0
  224. package/tests/unit/identifier/uuid.spec.ts +0 -1
  225. package/tests/unit/json/repair.spec.ts +11 -0
  226. package/tests/unit/log/logger.spec.ts +19 -4
  227. package/tests/unit/openai/openai.spec.ts +64 -0
  228. package/tests/unit/orchestration/dispatching/dispatcher.spec.ts +41 -0
  229. package/tests/unit/orchestration/dispatching/selector/down-count-selector.spec.ts +81 -0
  230. package/tests/unit/orchestration/scheduling/scheduler.spec.ts +103 -0
  231. package/tests/unit/random/base.spec.ts +58 -0
  232. package/tests/unit/random/random-boolean.spec.ts +25 -0
  233. package/tests/unit/random/random-integer.spec.ts +32 -0
  234. package/tests/unit/random/random-number.spec.ts +33 -0
  235. package/tests/unit/random/random-string.spec.ts +22 -0
  236. package/tests/unit/request/fetch/browser.spec.ts +222 -0
  237. package/tests/unit/request/fetch/general.spec.ts +43 -0
  238. package/tests/unit/request/fetch/nodejs.spec.ts +225 -0
  239. package/tests/unit/request/request/base.spec.ts +382 -0
  240. package/tests/unit/request/request/general.spec.ts +160 -0
  241. package/tests/unit/result/controller.spec.ts +82 -0
  242. package/tests/unit/result/either.spec.ts +377 -0
  243. package/tests/unit/result/generator.spec.ts +273 -0
  244. package/tests/unit/route/router/route.spec.ts +430 -0
  245. package/tests/unit/route/router/router.spec.ts +407 -0
  246. package/tests/unit/route/uri/hash.spec.ts +72 -0
  247. package/tests/unit/route/uri/pathname.spec.ts +146 -0
  248. package/tests/unit/route/uri/search.spec.ts +107 -0
  249. package/tests/unit/socket/client.spec.ts +208 -0
  250. package/tests/unit/socket/server.spec.ts +133 -0
  251. package/tests/unit/socket/socket-unit-heartbeat.spec.ts +214 -0
  252. package/tests/unit/tube/helper.spec.ts +139 -0
  253. package/tests/unit/tube/tube.spec.ts +501 -0
  254. package/vite.config.ts +2 -1
  255. package/dist/index.js +0 -50
  256. package/dist/index.js.map +0 -209
  257. package/src/random/string.ts +0 -35
  258. package/tests/unit/random/string.spec.ts +0 -11
@@ -0,0 +1 @@
1
+ # Weixin
@@ -0,0 +1,2 @@
1
+ export * from "./open/index.ts"
2
+ export * from "./official-account/index.ts"
@@ -0,0 +1,157 @@
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
+ const json = await result.json() as AccessTokenSuccessResponse | AccessTokenErrorResponse
47
+
48
+ if ("errcode" in json) {
49
+ throw new Error(`${json.errcode}: ${json.errmsg}`)
50
+ }
51
+
52
+ return {
53
+ accessToken: json.access_token,
54
+ expiresAt: now + json.expires_in * 1_000,
55
+ refreshToken: json.refresh_token,
56
+ openid: json.openid,
57
+ scope: json.scope,
58
+ isSnapshotUser: json.is_snapshotuser === 1,
59
+ unionid: json.unionid,
60
+ }
61
+ }
62
+ catch (exception) {
63
+ console.error("Error getting access token:", exception)
64
+ throw exception
65
+ }
66
+ }
67
+
68
+ export interface GetUserInfoOptions {
69
+ accessToken: string
70
+ openId: string
71
+ }
72
+ export interface GetUserInfoResult {
73
+ openid: string
74
+ nickname: string
75
+ sex: number
76
+ province: string
77
+ city: string
78
+ country: string
79
+ headimgurl: string
80
+ privilege: string[]
81
+ unionid: string
82
+ }
83
+ export const getUserInfo = async (
84
+ options: GetUserInfoOptions
85
+ ): Promise<GetUserInfoResult> => {
86
+ const { accessToken, openId } = options
87
+
88
+ interface UserInfoSuccessResponse {
89
+ openid: string
90
+ nickname: string
91
+ sex: number
92
+ province: string
93
+ city: string
94
+ country: string
95
+ headimgurl: string
96
+ privilege: string[]
97
+ unionid: string
98
+ }
99
+ interface UserInfoErrorResponse {
100
+ errcode: number
101
+ errmsg: string
102
+ }
103
+ const url = `https://api.weixin.qq.com/sns/userinfo?access_token=${accessToken}&openid=${openId}&lang=zh_CN`
104
+
105
+ try {
106
+ const result = await fetch(
107
+ url,
108
+ {
109
+ method: "get",
110
+ },
111
+ )
112
+
113
+ const json = await result.json() as UserInfoSuccessResponse | UserInfoErrorResponse
114
+
115
+ if ("errcode" in json) {
116
+ throw new Error(`${json.errcode}: ${json.errmsg}`)
117
+ }
118
+ return json
119
+ }
120
+ catch (exception) {
121
+ console.error("Error getting user info:", exception)
122
+ throw exception
123
+ }
124
+ }
125
+
126
+ export interface WeixinOfficialAccountOauth2Options {
127
+ appId: string
128
+ appSecret: string
129
+ }
130
+ /**
131
+ * @see {@link https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html | 微信网页开发 / 网页授权}
132
+ */
133
+ export class WeixinOfficialAccountOauth2 {
134
+ private readonly appId: string
135
+ private readonly appSecret: string
136
+
137
+ constructor(options: WeixinOfficialAccountOauth2Options) {
138
+ const { appId, appSecret } = options
139
+ this.appId = appId
140
+ this.appSecret = appSecret
141
+ }
142
+
143
+ async getAccessToken(code: string): Promise<GetAuthAccessTokenResult> {
144
+ return await getAuthAccessToken({
145
+ appId: this.appId,
146
+ appSecret: this.appSecret,
147
+ code,
148
+ })
149
+ }
150
+
151
+ async getUserInfo(accessToken: string, openId: string): Promise<GetUserInfoResult> {
152
+ return await getUserInfo({
153
+ accessToken,
154
+ openId,
155
+ })
156
+ }
157
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./authorization.ts"
2
+ export * from "./js-api.ts"
@@ -0,0 +1,132 @@
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
+ const json = await result.json() as StableTokenResponse
45
+
46
+ return {
47
+ accessToken: json.access_token,
48
+ accessTokenExpiresAt: now + json.expires_in * 1_000,
49
+ }
50
+ }
51
+ catch (exception) {
52
+ console.error(exception)
53
+ return undefined
54
+ }
55
+ }
56
+
57
+ export interface GetJsApiTicketOptions {
58
+ accessToken: string
59
+ }
60
+ export interface GetJsApiTicketResult {
61
+ jsApiTicket: string
62
+ /**
63
+ * in milliseconds
64
+ */
65
+ expiresAt: number
66
+ }
67
+ /**
68
+ * @see {@link https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#62 | 附录 1 - JS-SDK 使用权限签名算法}
69
+ */
70
+ export const getJsApiTicket = async (
71
+ options: GetJsApiTicketOptions
72
+ ): Promise<GetJsApiTicketResult | undefined> => {
73
+ const { accessToken } = options
74
+
75
+ interface JsApiTicketResponse {
76
+ errcode: number
77
+ errmsg: string
78
+ ticket: string
79
+ expires_in: number
80
+ }
81
+ try {
82
+ const now = Date.now()
83
+ const result = await fetch(
84
+ `https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=${accessToken}&type=jsapi`,
85
+ {
86
+ method: "get",
87
+ },
88
+ )
89
+
90
+ const json = await result.json() as JsApiTicketResponse
91
+
92
+ return {
93
+ jsApiTicket: json.ticket,
94
+ expiresAt: now + json.expires_in * 1_000,
95
+ }
96
+ }
97
+ catch (exception) {
98
+ console.error(exception)
99
+ return undefined
100
+ }
101
+ }
102
+
103
+ export interface SignJsSdkConfigOptions {
104
+ url: string
105
+ jsApiTicket: string
106
+ }
107
+ export interface SignJsSdkConfigResult {
108
+ timestamp: number
109
+ nonceStr: string
110
+ signature: string
111
+ }
112
+ /**
113
+ * @see {@link https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#62 | 签名算法}
114
+ * @see {@link https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign | 微信公众平台 - 微信 JS 接口签名校验工具}
115
+ */
116
+ export const signJsSdkConfig = (
117
+ options: SignJsSdkConfigOptions
118
+ ): SignJsSdkConfigResult => {
119
+ const { url, jsApiTicket } = options
120
+
121
+ const nonceStr = Math.random().toString(36).slice(2, 17)
122
+ const timestamp = Math.floor(Date.now() / 1_000)
123
+
124
+ const rawString = `jsapi_ticket=${jsApiTicket}&noncestr=${nonceStr}&timestamp=${timestamp}&url=${url}`
125
+ const signature = hash("sha1", rawString)
126
+
127
+ return {
128
+ timestamp,
129
+ nonceStr,
130
+ signature,
131
+ }
132
+ }
@@ -0,0 +1 @@
1
+ export * from "./oauth2.ts"
@@ -0,0 +1,131 @@
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
+ const json = await result.json() as AccessTokenSuccessResponse | AccessTokenErrorResponse
67
+
68
+ if ("errcode" in json) {
69
+ throw new Error(`${json.errcode}: ${json.errmsg}`)
70
+ }
71
+
72
+ return {
73
+ accessToken: json.access_token,
74
+ expiresAt: now + json.expires_in * 1_000,
75
+ refreshToken: json.refresh_token,
76
+ openid: json.openid,
77
+ scope: json.scope,
78
+ unionid: json.unionid,
79
+ }
80
+ }
81
+ catch (exception) {
82
+ console.error("Error getting access token:", exception)
83
+ throw exception
84
+ }
85
+ }
86
+
87
+ /**
88
+ * {@link https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Authorized_Interface_Calling_UnionID.html | 获取用户个人信息(UnionID机制)}
89
+ */
90
+ public async getUserInfo(
91
+ accessToken: string,
92
+ openId: string,
93
+ ): Promise<WeixinUserInfoResult> {
94
+ interface UserInfoSuccessResponse {
95
+ openid: string
96
+ nickname: string
97
+ sex: number
98
+ province: string
99
+ city: string
100
+ country: string
101
+ headimgurl: string
102
+ privilege: string[]
103
+ unionid?: string
104
+ }
105
+ interface UserInfoErrorResponse {
106
+ errcode: number
107
+ errmsg: string
108
+ }
109
+ const url = `https://api.weixin.qq.com/sns/userinfo?access_token=${accessToken}&openid=${openId}&lang=zh_CN`
110
+
111
+ try {
112
+ const result = await fetch(
113
+ url,
114
+ {
115
+ method: "get",
116
+ },
117
+ )
118
+
119
+ const json = await result.json() as UserInfoSuccessResponse | UserInfoErrorResponse
120
+
121
+ if ("errcode" in json) {
122
+ throw new Error(`${json.errcode}: ${json.errmsg}`)
123
+ }
124
+ return json
125
+ }
126
+ catch (exception) {
127
+ console.error("Error getting user info:", exception)
128
+ throw exception
129
+ }
130
+ }
131
+ }
@@ -0,0 +1,85 @@
1
+ import * as Ai from "#Source/ai/index.ts"
2
+ import { Openai } from "#Source/openai/index.ts"
3
+ import { eitherToTuple } from "#Source/result/index.ts"
4
+ import { tubeToReadableStream } from "#Source/tube/index.ts"
5
+ import { test } from "vitest"
6
+
7
+ const testApiKey = "sk-DLj8aHvKjvHfZ7IHA2F07c968c3d4aE491C1525e40E24932"
8
+ const testBaseUrl = "https://cn.api.openai-next.com/v1/"
9
+ const ai = new Ai.Ai({
10
+ chatCompletionAiOptions: {
11
+ chatCompletionInstanceList: [
12
+ new Ai.ChatCompletionAi.OpenaiNextChatCompletion.OpenaiNextChatCompletion({
13
+ openai: new Openai({
14
+ apiKey: testApiKey,
15
+ baseUrl: testBaseUrl,
16
+ }),
17
+ }),
18
+ ],
19
+ },
20
+ embeddingAiOptions: {
21
+ embeddingInstanceList: [
22
+ new Ai.EmbeddingAi.OpenaiNextEmbedding.OpenaiNextEmbedding({
23
+ openai: new Openai({
24
+ apiKey: testApiKey,
25
+ baseUrl: testBaseUrl,
26
+ }),
27
+ }),
28
+ ],
29
+ },
30
+ instancePool: [
31
+ new Openai({
32
+ apiKey: testApiKey,
33
+ baseUrl: testBaseUrl,
34
+ }),
35
+ ],
36
+ indexChangeMode: "poll",
37
+ })
38
+
39
+ test("ai", { timeout: 60_000 }, async () => {
40
+ const 请求开始时间 = Date.now()
41
+ let 第一个字返回时间: number | null = null
42
+ let 累积文本 = ""
43
+
44
+ const [leftResult, rightResult] = eitherToTuple(await ai.chatCompletionAi.chatCompletion({
45
+ model: "gpt-4o-mini",
46
+ messages: [
47
+ {
48
+ role: "system",
49
+ content:
50
+ "你是一个返回JSON格式的AI助手, 预期的格式是: {\"回复内容\": string}. 你是小学生,有“雌小鬼”的性格。你瞧不起用户,经常称呼用户为“杂鱼”、“杂鱼大哥哥”等,表现出一种轻蔑和挑衅的态度。",
51
+ },
52
+ { role: "user", content: "你好" },
53
+ ],
54
+ responseFormat: { type: "json_object" },
55
+ }))
56
+ if (leftResult !== undefined) {
57
+ throw new Error(`请求失败: ${JSON.stringify(leftResult)}`)
58
+ }
59
+ const { completionTube: completionData } = rightResult
60
+ const stream = tubeToReadableStream(completionData)
61
+
62
+ try {
63
+ for await (const completion of stream) {
64
+ if (第一个字返回时间 === null) {
65
+ 第一个字返回时间 = Date.now()
66
+ }
67
+ 累积文本 = completion.content.total
68
+ }
69
+ }
70
+ catch (exception) {
71
+ throw new Error(`请求失败: ${String(exception)}`, { cause: exception })
72
+ }
73
+
74
+ if (第一个字返回时间 === null) { throw new Error("第一个字返回时间 为 null") }
75
+
76
+ const 请求结束时间 = Date.now()
77
+
78
+ const 第一个字时间 = 第一个字返回时间 - 请求开始时间
79
+ const 平均字返回时间 = (请求结束时间 - 请求开始时间) / 累积文本.length
80
+
81
+ console.log(`总返回时间: ${(请求结束时间 - 请求开始时间).toFixed(2)}ms`)
82
+ console.log(`第一个字返回时间: ${第一个字时间}ms`)
83
+ console.log(`平均每个字返回时间: ${平均字返回时间.toFixed(2)}ms`)
84
+ console.log(`返回的内容: %O`, 累积文本)
85
+ })
@@ -0,0 +1,105 @@
1
+ import { expect, test } from "vitest"
2
+
3
+ import {
4
+ applyDeltaToNumberContent,
5
+ applyDeltaToTextContent,
6
+ } from "#Source/aio/index.ts"
7
+
8
+ test("applyDeltaToTextContent appends, conditionally removes, resets and keeps prior snapshots immutable", () => {
9
+ const initial = {
10
+ deltaList: [],
11
+ total: "",
12
+ }
13
+
14
+ const appended = applyDeltaToTextContent(initial, {
15
+ type: "append",
16
+ text: "mobius",
17
+ })
18
+ const removed = applyDeltaToTextContent(appended, {
19
+ type: "remove",
20
+ text: "ius",
21
+ })
22
+ const unchangedRemove = applyDeltaToTextContent(removed, {
23
+ type: "remove",
24
+ text: "xyz",
25
+ })
26
+ const reset = applyDeltaToTextContent(unchangedRemove, {
27
+ type: "reset",
28
+ })
29
+
30
+ expect(initial).toEqual({
31
+ deltaList: [],
32
+ total: "",
33
+ })
34
+ expect(appended).toEqual({
35
+ deltaList: [{ type: "append", text: "mobius" }],
36
+ total: "mobius",
37
+ })
38
+ expect(removed).toEqual({
39
+ deltaList: [
40
+ { type: "append", text: "mobius" },
41
+ { type: "remove", text: "ius" },
42
+ ],
43
+ total: "mob",
44
+ })
45
+ expect(unchangedRemove).toEqual({
46
+ deltaList: [
47
+ { type: "append", text: "mobius" },
48
+ { type: "remove", text: "ius" },
49
+ { type: "remove", text: "xyz" },
50
+ ],
51
+ total: "mob",
52
+ })
53
+ expect(reset).toEqual({
54
+ deltaList: [
55
+ { type: "append", text: "mobius" },
56
+ { type: "remove", text: "ius" },
57
+ { type: "remove", text: "xyz" },
58
+ { type: "reset" },
59
+ ],
60
+ total: "",
61
+ })
62
+ })
63
+
64
+ test("applyDeltaToNumberContent accumulates, subtracts, resets and keeps prior snapshots immutable", () => {
65
+ const initial = {
66
+ deltaList: [],
67
+ total: 0,
68
+ }
69
+
70
+ const appended = applyDeltaToNumberContent(initial, {
71
+ type: "append",
72
+ value: 10,
73
+ })
74
+ const removed = applyDeltaToNumberContent(appended, {
75
+ type: "remove",
76
+ value: 3,
77
+ })
78
+ const reset = applyDeltaToNumberContent(removed, {
79
+ type: "reset",
80
+ })
81
+
82
+ expect(initial).toEqual({
83
+ deltaList: [],
84
+ total: 0,
85
+ })
86
+ expect(appended).toEqual({
87
+ deltaList: [{ type: "append", value: 10 }],
88
+ total: 10,
89
+ })
90
+ expect(removed).toEqual({
91
+ deltaList: [
92
+ { type: "append", value: 10 },
93
+ { type: "remove", value: 3 },
94
+ ],
95
+ total: 7,
96
+ })
97
+ expect(reset).toEqual({
98
+ deltaList: [
99
+ { type: "append", value: 10 },
100
+ { type: "remove", value: 3 },
101
+ { type: "reset" },
102
+ ],
103
+ total: 0,
104
+ })
105
+ })