@planet-matrix/mobius-model 0.6.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 (233) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/index.js +706 -36
  3. package/dist/index.js.map +855 -59
  4. package/package.json +28 -16
  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/schedule.ts +111 -0
  29. package/src/basic/stream.ts +135 -25
  30. package/src/credential/README.md +107 -0
  31. package/src/credential/api-key.ts +158 -0
  32. package/src/credential/bearer.ts +73 -0
  33. package/src/credential/index.ts +4 -0
  34. package/src/credential/json-web-token.ts +96 -0
  35. package/src/credential/password.ts +170 -0
  36. package/src/cron/README.md +86 -0
  37. package/src/cron/cron.ts +87 -0
  38. package/src/cron/index.ts +1 -0
  39. package/src/drizzle/README.md +1 -0
  40. package/src/drizzle/drizzle.ts +1 -0
  41. package/src/drizzle/helper.ts +47 -0
  42. package/src/drizzle/index.ts +5 -0
  43. package/src/drizzle/infer.ts +52 -0
  44. package/src/drizzle/kysely.ts +8 -0
  45. package/src/drizzle/pagination.ts +200 -0
  46. package/src/email/README.md +1 -0
  47. package/src/email/index.ts +1 -0
  48. package/src/email/resend.ts +25 -0
  49. package/src/event/class-event-proxy.ts +6 -5
  50. package/src/event/common.ts +13 -3
  51. package/src/event/event-manager.ts +3 -3
  52. package/src/event/instance-event-proxy.ts +6 -5
  53. package/src/event/internal.ts +4 -4
  54. package/src/form/README.md +25 -0
  55. package/src/form/index.ts +1 -0
  56. package/src/form/inputor-controller/base.ts +874 -0
  57. package/src/form/inputor-controller/boolean.ts +39 -0
  58. package/src/form/inputor-controller/file.ts +39 -0
  59. package/src/form/inputor-controller/form.ts +181 -0
  60. package/src/form/inputor-controller/helper.ts +117 -0
  61. package/src/form/inputor-controller/index.ts +17 -0
  62. package/src/form/inputor-controller/multi-select.ts +99 -0
  63. package/src/form/inputor-controller/number.ts +116 -0
  64. package/src/form/inputor-controller/select.ts +109 -0
  65. package/src/form/inputor-controller/text.ts +82 -0
  66. package/src/http/READMD.md +1 -0
  67. package/src/http/api/api-core.ts +84 -0
  68. package/src/http/api/api-handler.ts +79 -0
  69. package/src/http/api/api-host.ts +47 -0
  70. package/src/http/api/api-result.ts +56 -0
  71. package/src/http/api/api-schema.ts +154 -0
  72. package/src/http/api/api-server.ts +130 -0
  73. package/src/http/api/api-test.ts +142 -0
  74. package/src/http/api/api-type.ts +37 -0
  75. package/src/http/api/api.ts +81 -0
  76. package/src/http/api/index.ts +11 -0
  77. package/src/http/api-adapter/api-core-node-http.ts +260 -0
  78. package/src/http/api-adapter/api-host-node-http.ts +156 -0
  79. package/src/http/api-adapter/api-result-arktype.ts +297 -0
  80. package/src/http/api-adapter/api-result-zod.ts +286 -0
  81. package/src/http/api-adapter/index.ts +5 -0
  82. package/src/http/bin/gen-api-list/gen-api-list.ts +126 -0
  83. package/src/http/bin/gen-api-list/index.ts +1 -0
  84. package/src/http/bin/gen-api-test/gen-api-test.ts +136 -0
  85. package/src/http/bin/gen-api-test/index.ts +1 -0
  86. package/src/http/bin/gen-api-type/calc-code.ts +25 -0
  87. package/src/http/bin/gen-api-type/gen-api-type.ts +127 -0
  88. package/src/http/bin/gen-api-type/index.ts +2 -0
  89. package/src/http/bin/index.ts +2 -0
  90. package/src/http/index.ts +3 -0
  91. package/src/huawei/README.md +1 -0
  92. package/src/huawei/index.ts +2 -0
  93. package/src/huawei/moderation/index.ts +1 -0
  94. package/src/huawei/moderation/moderation.ts +355 -0
  95. package/src/huawei/obs/esdk-obs-nodejs.d.ts +87 -0
  96. package/src/huawei/obs/index.ts +1 -0
  97. package/src/huawei/obs/obs.ts +42 -0
  98. package/src/index.ts +19 -2
  99. package/src/json/README.md +92 -0
  100. package/src/json/index.ts +1 -0
  101. package/src/json/repair.ts +18 -0
  102. package/src/log/logger.ts +15 -4
  103. package/src/openai/README.md +1 -0
  104. package/src/openai/index.ts +1 -0
  105. package/src/openai/openai.ts +510 -0
  106. package/src/orchestration/README.md +9 -7
  107. package/src/orchestration/dispatching/dispatcher.ts +83 -0
  108. package/src/orchestration/dispatching/index.ts +2 -0
  109. package/src/orchestration/dispatching/selector/base-selector.ts +39 -0
  110. package/src/orchestration/dispatching/selector/down-count-selector.ts +119 -0
  111. package/src/orchestration/dispatching/selector/index.ts +2 -0
  112. package/src/orchestration/index.ts +2 -0
  113. package/src/orchestration/scheduling/index.ts +2 -0
  114. package/src/orchestration/scheduling/scheduler.ts +103 -0
  115. package/src/orchestration/scheduling/task.ts +32 -0
  116. package/src/random/README.md +8 -7
  117. package/src/random/base.ts +66 -0
  118. package/src/random/index.ts +5 -1
  119. package/src/random/random-boolean.ts +40 -0
  120. package/src/random/random-integer.ts +60 -0
  121. package/src/random/random-number.ts +72 -0
  122. package/src/random/random-string.ts +66 -0
  123. package/src/request/README.md +108 -0
  124. package/src/request/fetch/base.ts +108 -0
  125. package/src/request/fetch/browser.ts +285 -0
  126. package/src/request/fetch/general.ts +20 -0
  127. package/src/request/fetch/index.ts +4 -0
  128. package/src/request/fetch/nodejs.ts +285 -0
  129. package/src/request/index.ts +2 -0
  130. package/src/request/request/base.ts +250 -0
  131. package/src/request/request/general.ts +64 -0
  132. package/src/request/request/index.ts +3 -0
  133. package/src/request/request/resource.ts +68 -0
  134. package/src/result/README.md +4 -0
  135. package/src/result/controller.ts +54 -0
  136. package/src/result/either.ts +193 -0
  137. package/src/result/index.ts +2 -0
  138. package/src/route/README.md +105 -0
  139. package/src/route/adapter/browser.ts +122 -0
  140. package/src/route/adapter/driver.ts +56 -0
  141. package/src/route/adapter/index.ts +2 -0
  142. package/src/route/index.ts +3 -0
  143. package/src/route/router/index.ts +2 -0
  144. package/src/route/router/route.ts +630 -0
  145. package/src/route/router/router.ts +1642 -0
  146. package/src/route/uri/hash.ts +308 -0
  147. package/src/route/uri/index.ts +7 -0
  148. package/src/route/uri/pathname.ts +376 -0
  149. package/src/route/uri/search.ts +413 -0
  150. package/src/socket/README.md +105 -0
  151. package/src/socket/client/index.ts +2 -0
  152. package/src/socket/client/socket-unit.ts +660 -0
  153. package/src/socket/client/socket.ts +203 -0
  154. package/src/socket/common/index.ts +2 -0
  155. package/src/socket/common/socket-unit-common.ts +23 -0
  156. package/src/socket/common/socket-unit-heartbeat.ts +427 -0
  157. package/src/socket/index.ts +3 -0
  158. package/src/socket/server/index.ts +3 -0
  159. package/src/socket/server/server.ts +183 -0
  160. package/src/socket/server/socket-unit.ts +449 -0
  161. package/src/socket/server/socket.ts +264 -0
  162. package/src/storage/table.ts +3 -3
  163. package/src/timer/expiration/expiration-manager.ts +3 -3
  164. package/src/timer/expiration/remaining-manager.ts +3 -3
  165. package/src/tube/README.md +99 -0
  166. package/src/tube/helper.ts +138 -0
  167. package/src/tube/index.ts +2 -0
  168. package/src/tube/tube.ts +880 -0
  169. package/src/weixin/README.md +1 -0
  170. package/src/weixin/index.ts +2 -0
  171. package/src/weixin/official-account/authorization.ts +159 -0
  172. package/src/weixin/official-account/index.ts +2 -0
  173. package/src/weixin/official-account/js-api.ts +134 -0
  174. package/src/weixin/open/index.ts +1 -0
  175. package/src/weixin/open/oauth2.ts +133 -0
  176. package/tests/unit/ai/ai.spec.ts +85 -0
  177. package/tests/unit/aio/content.spec.ts +105 -0
  178. package/tests/unit/aio/json.spec.ts +147 -0
  179. package/tests/unit/aio/prompt.spec.ts +111 -0
  180. package/tests/unit/basic/error.spec.ts +16 -4
  181. package/tests/unit/basic/schedule.spec.ts +74 -0
  182. package/tests/unit/basic/stream.spec.ts +90 -37
  183. package/tests/unit/credential/api-key.spec.ts +37 -0
  184. package/tests/unit/credential/bearer.spec.ts +23 -0
  185. package/tests/unit/credential/json-web-token.spec.ts +23 -0
  186. package/tests/unit/credential/password.spec.ts +41 -0
  187. package/tests/unit/cron/cron.spec.ts +84 -0
  188. package/tests/unit/event/class-event-proxy.spec.ts +3 -3
  189. package/tests/unit/event/event-manager.spec.ts +3 -3
  190. package/tests/unit/event/instance-event-proxy.spec.ts +3 -3
  191. package/tests/unit/form/inputor-controller/base.spec.ts +458 -0
  192. package/tests/unit/form/inputor-controller/boolean.spec.ts +30 -0
  193. package/tests/unit/form/inputor-controller/file.spec.ts +27 -0
  194. package/tests/unit/form/inputor-controller/form.spec.ts +120 -0
  195. package/tests/unit/form/inputor-controller/helper.spec.ts +67 -0
  196. package/tests/unit/form/inputor-controller/multi-select.spec.ts +34 -0
  197. package/tests/unit/form/inputor-controller/number.spec.ts +36 -0
  198. package/tests/unit/form/inputor-controller/select.spec.ts +49 -0
  199. package/tests/unit/form/inputor-controller/text.spec.ts +34 -0
  200. package/tests/unit/http/api/api-core-host.spec.ts +207 -0
  201. package/tests/unit/http/api/api-schema.spec.ts +120 -0
  202. package/tests/unit/http/api/api-server.spec.ts +363 -0
  203. package/tests/unit/http/api/api-test.spec.ts +117 -0
  204. package/tests/unit/http/api/api.spec.ts +121 -0
  205. package/tests/unit/http/api-adapter/node-http.spec.ts +191 -0
  206. package/tests/unit/json/repair.spec.ts +11 -0
  207. package/tests/unit/log/logger.spec.ts +19 -4
  208. package/tests/unit/openai/openai.spec.ts +64 -0
  209. package/tests/unit/orchestration/dispatching/dispatcher.spec.ts +41 -0
  210. package/tests/unit/orchestration/dispatching/selector/down-count-selector.spec.ts +81 -0
  211. package/tests/unit/orchestration/scheduling/scheduler.spec.ts +103 -0
  212. package/tests/unit/random/base.spec.ts +58 -0
  213. package/tests/unit/random/random-boolean.spec.ts +25 -0
  214. package/tests/unit/random/random-integer.spec.ts +32 -0
  215. package/tests/unit/random/random-number.spec.ts +33 -0
  216. package/tests/unit/random/random-string.spec.ts +22 -0
  217. package/tests/unit/request/fetch/browser.spec.ts +222 -0
  218. package/tests/unit/request/fetch/general.spec.ts +43 -0
  219. package/tests/unit/request/fetch/nodejs.spec.ts +225 -0
  220. package/tests/unit/request/request/base.spec.ts +385 -0
  221. package/tests/unit/request/request/general.spec.ts +161 -0
  222. package/tests/unit/route/router/route.spec.ts +431 -0
  223. package/tests/unit/route/router/router.spec.ts +407 -0
  224. package/tests/unit/route/uri/hash.spec.ts +72 -0
  225. package/tests/unit/route/uri/pathname.spec.ts +147 -0
  226. package/tests/unit/route/uri/search.spec.ts +107 -0
  227. package/tests/unit/socket/client.spec.ts +208 -0
  228. package/tests/unit/socket/server.spec.ts +135 -0
  229. package/tests/unit/socket/socket-unit-heartbeat.spec.ts +214 -0
  230. package/tests/unit/tube/helper.spec.ts +139 -0
  231. package/tests/unit/tube/tube.spec.ts +501 -0
  232. package/src/random/string.ts +0 -35
  233. 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,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,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
+ })