@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,246 @@
1
+ import type { BuildEvents } from "#Source/event/index.ts"
2
+ import { EventManager } from "#Source/event/index.ts"
3
+ import { isError, errorIsNetworkError, errorStringify } from "#Source/basic/index.ts"
4
+
5
+ import type { BaseFetch, FetchResponseType, FetchFactory } from "../fetch/index.ts"
6
+ import { generalFetch } from "../fetch/index.ts"
7
+ import type {
8
+ Resource,
9
+ ResourceOutput,
10
+ ResourceSuccessOutput,
11
+ ResourceErrorOutput,
12
+ ResourceSuccessData,
13
+ ResourceErrorData,
14
+ ResourcePatchOutput,
15
+ } from "./resource.ts"
16
+
17
+ /**
18
+ * 描述请求执行过程中会派发的标准事件集合。
19
+ */
20
+ export type BaseRequestEvents<R extends Resource> = BuildEvents<{
21
+ output: (output: ResourceOutput<R>) => void
22
+ successOutput: (successOutput: ResourceSuccessOutput<R>) => void
23
+ successData: (successData: ResourceSuccessData<R>) => void
24
+ errorOutput: (errorOutput: ResourceErrorOutput<R>) => void
25
+ errorData: (errorData: ResourceErrorData<R>) => void
26
+ patchOutput: (patchOutput: ResourcePatchOutput<R>) => void
27
+ }>
28
+
29
+ /**
30
+ * 实际执行请求动作的选项。
31
+ *
32
+ * 该类型会根据资源声明中 `input.query` 与 `input.body` 是否可选,
33
+ * 自动收紧当前路径对应请求选项里的必填性。
34
+ */
35
+ export type RequestOptions<Path, R extends Resource>
36
+ = {
37
+ baseUrl: Extract<R, { path: Path }>["baseUrl"]
38
+ path: Extract<R, { path: Path }>["path"]
39
+ method: Extract<R, { path: Path }>["method"]
40
+ headers?: Record<string, string> | undefined
41
+ responseType?: FetchResponseType | undefined
42
+ timeout?: number | undefined
43
+ abortSignal?: AbortSignal | undefined
44
+ } & (
45
+ undefined extends Extract<R, { path: Path }>["input"]["query"]
46
+ ? { query?: Extract<R, { path: Path }>["input"]["query"] | undefined }
47
+ : { query: Extract<R, { path: Path }>["input"]["query"] }
48
+ ) & (
49
+ undefined extends Extract<R, { path: Path }>["input"]["body"]
50
+ ? { body?: Extract<R, { path: Path }>["input"]["body"] | undefined }
51
+ : { body: Extract<R, { path: Path }>["input"]["body"] }
52
+ ) & {
53
+ onOutput?: ((output: ResourceOutput<Extract<R, { path: Path }>>) => void) | undefined
54
+ onSuccessOutput?: ((successOutput: ResourceSuccessOutput<Extract<R, { path: Path }>>) => void) | undefined
55
+ onSuccessData?: ((successData: ResourceSuccessData<Extract<R, { path: Path }>>) => void) | undefined
56
+ onErrorOutput?: ((errorOutput: ResourceErrorOutput<Extract<R, { path: Path }>>) => void) | undefined
57
+ onErrorData?: ((errorData: ResourceErrorData<Extract<R, { path: Path }>>) => void) | undefined
58
+ onPatchOutput?: ((patchOutput: ResourcePatchOutput<Extract<R, { path: Path }>>) => void) | undefined
59
+ }
60
+
61
+ /**
62
+ * 实际执行请求动作的结果。
63
+ */
64
+ export type RequestResult<Path, R extends Resource>
65
+ = ResourceOutput<Extract<R, { path: Path }>>
66
+ | ResourcePatchOutput<Extract<R, { path: Path }>>
67
+
68
+ /**
69
+ * 表示请求失败时的错误归类。
70
+ */
71
+ export type ErrorKind = "network" | "unknown"
72
+
73
+ /**
74
+ * 生成补丁输出时可使用的上下文。
75
+ */
76
+ export type GeneratePatchOutputContext = {
77
+ type: "error"
78
+ errorKind: ErrorKind
79
+ error: Error
80
+ }
81
+
82
+ /**
83
+ * 构造专用请求类的选项。
84
+ */
85
+ export interface BaseRequestOptions<R extends Resource> {
86
+ /**
87
+ * 自定义请求执行器工厂;未提供时使用默认的通用运行时分派实现。
88
+ */
89
+ fetchFactory?: FetchFactory | undefined
90
+
91
+ /**
92
+ * 在真正发起请求前,对标准化请求选项做最后一次统一调整。
93
+ */
94
+ modifyRequestOptions?: ((options: RequestOptions<R["path"], R>) => RequestOptions<R["path"], R>) | undefined
95
+
96
+ /**
97
+ * 在请求失败时生成仍可被上层继续消费的补丁输出。
98
+ */
99
+ generatePatchOutput: (context: GeneratePatchOutputContext) => ResourcePatchOutput<R>
100
+ }
101
+
102
+ /**
103
+ * 基于资源声明组织请求、输出分类与失败补丁的抽象请求类。
104
+ *
105
+ * `Resource` 类型参数应传入资源声明的联合类型或元组成员联合。
106
+ * 默认实现假定 JSON 输出可通过 `status` 字段区分为 `success` 与 `error`,
107
+ * 并通过 `data` 字段提取真正的成功数据或错误数据;若资源输出不满足这组约定,
108
+ * 则应由子类覆写对应的分类与提取方法。
109
+ */
110
+ export abstract class BaseRequest<R extends Resource = Resource> {
111
+ protected options: BaseRequestOptions<R>
112
+
113
+ /**
114
+ * 对外暴露请求生命周期事件的事件管理器。
115
+ */
116
+ event: EventManager<BaseRequestEvents<R>>
117
+
118
+ constructor(options: BaseRequestOptions<R>) {
119
+ this.options = options
120
+ this.event = new EventManager()
121
+ }
122
+
123
+ /**
124
+ * 判断当前输出是否应被视为成功输出。
125
+ */
126
+ protected isSuccessDataResponse(response: ResourceOutput<R>): boolean {
127
+ return response['status'] === "success"
128
+ }
129
+
130
+ /**
131
+ * 从成功输出中提取成功数据。
132
+ */
133
+ protected extractSuccessData(response: ResourceSuccessOutput<R>): ResourceSuccessData<R> {
134
+ return response['data'] as ResourceSuccessData<R>
135
+ }
136
+
137
+ /**
138
+ * 判断当前输出是否应被视为错误输出。
139
+ */
140
+ protected isErrorDataResponse(response: ResourceOutput<R>): boolean {
141
+ return response['status'] === "error"
142
+ }
143
+
144
+ /**
145
+ * 从错误输出中提取错误数据。
146
+ */
147
+ protected extractErrorData(response: ResourceErrorOutput<R>): ResourceErrorData<R> {
148
+ return response['data'] as ResourceErrorData<R>
149
+ }
150
+
151
+ /**
152
+ * 按资源路径约束执行一次请求,并在成功或失败时派发对应事件。
153
+ */
154
+ async request<Path extends R["path"]>(
155
+ options: RequestOptions<Path, R>,
156
+ ): Promise<RequestResult<Path, R>> {
157
+ const { fetchFactory, modifyRequestOptions } = this.options
158
+
159
+ const preparedOptions = modifyRequestOptions !== undefined
160
+ ? modifyRequestOptions(options as RequestOptions<R["path"], R>)
161
+ : options
162
+
163
+ const {
164
+ responseType = "json",
165
+ onOutput,
166
+ onSuccessOutput,
167
+ onSuccessData,
168
+ onErrorOutput,
169
+ onErrorData,
170
+ onPatchOutput,
171
+ } = preparedOptions
172
+
173
+ try {
174
+ type FetchInput = {
175
+ query: Extract<R, { path: Path }>["input"]["query"]
176
+ body: Extract<R, { path: Path }>["input"]["body"]
177
+ }
178
+ type FetchOutput = {
179
+ [K in typeof responseType]: {
180
+ type: K
181
+ data: ResourceOutput<Extract<R, { path: Path }>>
182
+ }
183
+ }[typeof responseType]
184
+ const preparedFetch = (fetchFactory ?? generalFetch)(preparedOptions) as BaseFetch<FetchInput, FetchOutput>
185
+
186
+ if (responseType === "json") {
187
+ const json = await preparedFetch.getJson()
188
+ onOutput?.(json)
189
+ this.event.emit("output", json)
190
+
191
+ // do we have a better way to consume the success data?
192
+ if (this.isSuccessDataResponse(json)) {
193
+ onSuccessOutput?.(json)
194
+ this.event.emit("successOutput", json)
195
+ const successData = this.extractSuccessData(json)
196
+ onSuccessData?.(successData)
197
+ this.event.emit("successData", successData)
198
+ }
199
+
200
+ if (this.isErrorDataResponse(json)) {
201
+ onErrorOutput?.(json)
202
+ this.event.emit("errorOutput", json)
203
+ const errorData = this.extractErrorData(json)
204
+ onErrorData?.(errorData)
205
+ this.event.emit("errorData", errorData)
206
+ }
207
+
208
+ return json
209
+ }
210
+
211
+ throw new Error("Unsupported response type, please use \"json\" or improve implementation of BaseRequest.")
212
+ }
213
+ catch (exception) {
214
+ if (errorIsNetworkError(exception)) {
215
+ const patchOutput = this.options.generatePatchOutput({
216
+ type: "error",
217
+ errorKind: "network",
218
+ error: exception
219
+ })
220
+ onPatchOutput?.(patchOutput)
221
+ this.event.emit("patchOutput", patchOutput)
222
+ return patchOutput
223
+ }
224
+ else if (isError(exception)) {
225
+ const patchOutput = this.options.generatePatchOutput({
226
+ type: "error",
227
+ errorKind: "unknown",
228
+ error: exception
229
+ })
230
+ onPatchOutput?.(patchOutput)
231
+ this.event.emit("patchOutput", patchOutput)
232
+ return patchOutput
233
+ }
234
+ else {
235
+ const patchOutput = this.options.generatePatchOutput({
236
+ type: "error",
237
+ errorKind: "unknown",
238
+ error: new Error(errorStringify(exception))
239
+ })
240
+ onPatchOutput?.(patchOutput)
241
+ this.event.emit("patchOutput", patchOutput)
242
+ return patchOutput
243
+ }
244
+ }
245
+ }
246
+ }
@@ -0,0 +1,63 @@
1
+ import type { ResourcePatchOutput } from "./resource.ts"
2
+ import type { BaseRequestOptions, GeneratePatchOutputContext } from "./base.ts"
3
+ import { BaseRequest } from "./base.ts"
4
+
5
+ /**
6
+ * 描述适用于通用 `{ status, data }` 输出结构的资源声明。
7
+ */
8
+ export interface GeneralResource {
9
+ baseUrl: string
10
+ path: string
11
+ method: "get" | "post"
12
+ input: {
13
+ query?: Record<string, unknown> | undefined
14
+ body?: Record<string, unknown> | undefined
15
+ }
16
+ successData: Record<string, unknown>
17
+ errorData: Record<string, unknown>
18
+ }
19
+
20
+ /**
21
+ * 把通用资源声明补齐为 `BaseRequest` 可直接消费的完整资源契约。
22
+ */
23
+ export type GeneralResourceToResource<R extends GeneralResource> = R & {
24
+ output: { status: "success", data: R["successData"] } | { status: "error", data: R["errorData"] }
25
+ successOutput: { status: "success", data: R["successData"] }
26
+ errorOutput: { status: "error", data: R["errorData"] }
27
+ patchOutput: { status: "unknown", data: GeneratePatchOutputContext }
28
+ }
29
+
30
+ /**
31
+ * 构造通用请求类的选项。
32
+ *
33
+ * 该类型会沿用 `BaseRequestOptions` 的配置能力,只是由 `GeneralRequest`
34
+ * 自动补齐失败时的补丁输出生成逻辑。
35
+ */
36
+ export interface GeneralRequestOptions<R extends GeneralResource>
37
+ extends Omit<BaseRequestOptions<GeneralResourceToResource<R>>, "generatePatchOutput"> {
38
+ }
39
+
40
+ /**
41
+ * 面向通用 `{ status, data }` 输出结构的请求实现。
42
+ *
43
+ * `GeneralRequest` 仅适用于返回结构严格符合特定情况的目标资源,特定情况指:
44
+ *
45
+ * 1. 业务结果仅包含成功(success)和错误(error)两种状态。
46
+ * 2. 业务结果通过一个名为 `status` 的字段区分状态,并通过一个名为 `data` 的字段承载数据:
47
+ * `{ status: "success" | "error", data: Record<string, unknown> }`。
48
+ */
49
+ export class GeneralRequest<R extends GeneralResource = GeneralResource>
50
+ extends BaseRequest<GeneralResourceToResource<R>> {
51
+ constructor(options: GeneralRequestOptions<R>) {
52
+ const generatePatchOutput = (context: GeneratePatchOutputContext): ResourcePatchOutput<GeneralResourceToResource<R>> => {
53
+ return {
54
+ status: "unknown",
55
+ data: context
56
+ } as ResourcePatchOutput<GeneralResourceToResource<R>>
57
+ }
58
+ super({
59
+ ...options,
60
+ generatePatchOutput
61
+ })
62
+ }
63
+ }
@@ -0,0 +1,3 @@
1
+ export type * from "./resource.ts"
2
+ export * from "./base.ts"
3
+ export * from "./general.ts"
@@ -0,0 +1,68 @@
1
+ /**
2
+ * 描述一个远程资源契约。
3
+ *
4
+ * 这个接口用于把某一路径、方法、输入、输出、成功数据、错误数据与失败补丁输出
5
+ * 收束为一份可长期维护的类型层声明,供 `BaseRequest` 及其派生实现消费。
6
+ */
7
+ export interface Resource {
8
+ baseUrl: string
9
+ path: string
10
+ method: "get" | "post"
11
+ input: {
12
+ query?: Record<string, unknown> | undefined
13
+ body?: Record<string, unknown> | undefined
14
+ }
15
+ output: Record<string, unknown>
16
+
17
+ successOutput: Record<string, unknown>
18
+ successData: Record<string, unknown>
19
+ errorOutput: Record<string, unknown>
20
+ errorData: Record<string, unknown>
21
+
22
+ patchOutput: Record<string, unknown>
23
+ }
24
+
25
+ /**
26
+ * 提取资源声明中的基础 URL 类型。
27
+ */
28
+ export type ResourceBaseUrl<R extends Resource> = R["baseUrl"]
29
+
30
+ /**
31
+ * 提取资源声明中的路径类型。
32
+ */
33
+ export type ResourcePath<R extends Resource> = R["path"]
34
+
35
+ /**
36
+ * 提取资源声明中的请求方法类型。
37
+ */
38
+ export type ResourceMethod<R extends Resource> = R["method"]
39
+
40
+ /**
41
+ * 提取资源声明中的完整输出类型。
42
+ */
43
+ export type ResourceOutput<R extends Resource> = R["output"]
44
+
45
+ /**
46
+ * 提取资源声明中的成功输出类型。
47
+ */
48
+ export type ResourceSuccessOutput<R extends Resource> = R["successOutput"]
49
+
50
+ /**
51
+ * 提取资源声明中的错误输出类型。
52
+ */
53
+ export type ResourceErrorOutput<R extends Resource> = R["errorOutput"]
54
+
55
+ /**
56
+ * 提取资源声明中的成功数据类型。
57
+ */
58
+ export type ResourceSuccessData<R extends Resource> = R["successData"]
59
+
60
+ /**
61
+ * 提取资源声明中的错误数据类型。
62
+ */
63
+ export type ResourceErrorData<R extends Resource> = R["errorData"]
64
+
65
+ /**
66
+ * 提取资源声明中的补丁输出类型。
67
+ */
68
+ export type ResourcePatchOutput<R extends Resource> = R["patchOutput"]
@@ -0,0 +1,4 @@
1
+ # Result
2
+
3
+ @see https://github.com/supermacro/neverthrow
4
+ @see https://better-result.dev/introduction
@@ -0,0 +1,58 @@
1
+ import type { Either } from "./either.ts"
2
+ import { Left, Right } from "./either.ts"
3
+
4
+ export class Controller<L, R> {
5
+ static isLeft<L, R>(target: unknown): target is Left<L, R> {
6
+ return Left.isLeft(target)
7
+ }
8
+
9
+ static isRight<L, R>(target: unknown): target is Right<L, R> {
10
+ return Right.isRight(target)
11
+ }
12
+
13
+ protected promise: Promise<Either<L, R>>
14
+ protected resolve: (value: Either<L, R>) => void
15
+ protected reject: (value: L) => void
16
+
17
+ constructor() {
18
+ let _resolve: (value: Either<L, R>) => void
19
+ let _reject: (value: L) => void
20
+ this.promise = new Promise((resolve, reject) => {
21
+ _resolve = resolve
22
+ _reject = reject
23
+ })
24
+ this.resolve = _resolve!
25
+ this.reject = _reject!
26
+ }
27
+
28
+ get value(): Promise<Either<L, R>> {
29
+ return this.promise
30
+ }
31
+
32
+ async returnRight(value: R): Promise<Right<L, R>> {
33
+ const right = new Right<L, R>(value)
34
+ this.resolve(right)
35
+ return await Promise.resolve(right)
36
+ }
37
+
38
+ async returnLeft(value: L): Promise<Left<L, R>> {
39
+ const left = new Left<L, R>(value)
40
+ this.resolve(left)
41
+ return await Promise.resolve(left)
42
+ }
43
+
44
+ // oxlint-disable-next-line require-await
45
+ async throw(value: L): Promise<void> {
46
+ const left = new Left<L, R>(value)
47
+ this.resolve(left)
48
+ // oxlint-disable-next-line typescript/only-throw-error
49
+ throw left
50
+ }
51
+ }
52
+
53
+ type GetLeft<E> = E extends Either<infer L, infer _> ? L : never
54
+ type GetRight<E> = E extends Either<infer _, infer R> ? R : never
55
+ export const controllerFromEitherType = <E extends Either<unknown, unknown>>(
56
+ ): Controller<GetLeft<E>, GetRight<E>> => {
57
+ return new Controller<GetLeft<E>, GetRight<E>>()
58
+ }