@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,156 @@
1
+ import type { IncomingMessage, Server, ServerResponse } from "node:http"
2
+ import { createServer } from "node:http"
3
+ import { networkInterfaces } from "node:os"
4
+
5
+ import type { ApiCoreHandler, ApiHostStartResult, ApiHostOptions } from "../api/api-host.ts"
6
+ import { ApiHost } from "../api/api-host.ts"
7
+
8
+ import { ApiCoreNodeHttp } from "./api-core-node-http.ts"
9
+
10
+ const internalGetListeningPort = (server: Server): number => {
11
+ const address = server.address()
12
+
13
+ if (address === null) {
14
+ throw new Error("Server is not running.")
15
+ }
16
+
17
+ if (typeof address === "string") {
18
+ throw new TypeError("HTTP server is not listening on a TCP port.")
19
+ }
20
+
21
+ return address.port
22
+ }
23
+ const internalFormatHttpHost = (address: string): string => {
24
+ const normalizedAddress = address.replaceAll("%", "%25")
25
+
26
+ if (normalizedAddress.includes(":")) {
27
+ return `[${normalizedAddress}]`
28
+ }
29
+
30
+ return normalizedAddress
31
+ }
32
+ const internalBuildHttpUrlList = (port: number): string[] => {
33
+ const urlSet = new Set<string>([
34
+ `http://127.0.0.1:${port}`,
35
+ `http://localhost:${port}`,
36
+ `http://[::1]:${port}`,
37
+ ])
38
+
39
+ for (const networkInterfaceInfo of Object.values(networkInterfaces()).flat()) {
40
+ if (
41
+ networkInterfaceInfo === undefined
42
+ || networkInterfaceInfo.address.length === 0
43
+ || networkInterfaceInfo.internal === true
44
+ || networkInterfaceInfo.address === "0.0.0.0"
45
+ || networkInterfaceInfo.address === "::"
46
+ ) {
47
+ continue
48
+ }
49
+
50
+ const address = internalFormatHttpHost(networkInterfaceInfo.address)
51
+ urlSet.add(`http://${address}:${port}`)
52
+ }
53
+
54
+ return [...urlSet]
55
+ }
56
+
57
+ export interface ApiHostNodeHttpOptions extends ApiHostOptions {
58
+ }
59
+ export class ApiHostNodeHttp extends ApiHost<Server> {
60
+ protected apiCoreHandler: ApiCoreHandler | undefined
61
+
62
+ constructor(options: ApiHostNodeHttpOptions) {
63
+ super(options)
64
+
65
+ this.apiCoreHandler = undefined
66
+ }
67
+
68
+ attachApiCoreHandler(apiCoreHandler: ApiCoreHandler): void {
69
+ this.apiCoreHandler = apiCoreHandler
70
+ }
71
+
72
+ async start(): Promise<ApiHostStartResult> {
73
+ if (this.server !== undefined) {
74
+ throw new Error("Server is already running.")
75
+ }
76
+
77
+ const server = createServer((request, response) => {
78
+ void this.handleRequest(request, response)
79
+ })
80
+
81
+ const result = await new Promise<ApiHostStartResult>((resolve, reject) => {
82
+ const handleError = (error: Error): void => {
83
+ server.off("listening", handleListening)
84
+ this.server = undefined
85
+ reject(error)
86
+ }
87
+
88
+ const handleListening = (): void => {
89
+ server.off("error", handleError)
90
+ this.server = server
91
+
92
+ server.once("close", () => {
93
+ if (this.server === server) {
94
+ this.server = undefined
95
+ }
96
+ })
97
+
98
+ const port = internalGetListeningPort(server)
99
+ resolve({
100
+ urlList: internalBuildHttpUrlList(port),
101
+ })
102
+ }
103
+
104
+ server.once("error", handleError)
105
+ server.once("listening", handleListening)
106
+ server.listen(this.options.port)
107
+ })
108
+
109
+ return result
110
+ }
111
+
112
+ async close(): Promise<void> {
113
+ if (this.isRunning() === false) {
114
+ return
115
+ }
116
+
117
+ const server = this.safeGetServer()
118
+
119
+ await new Promise<void>((resolve, reject) => {
120
+ server.close((error) => {
121
+ if (error !== undefined) {
122
+ reject(error)
123
+ return
124
+ }
125
+
126
+ resolve()
127
+ })
128
+ })
129
+ }
130
+
131
+ protected async handleRequest(
132
+ request: IncomingMessage,
133
+ response: ServerResponse<IncomingMessage>,
134
+ ): Promise<void> {
135
+ const apiCore = new ApiCoreNodeHttp({ request, response })
136
+ const apiCoreHandler = this.apiCoreHandler
137
+
138
+ if (apiCoreHandler === undefined) {
139
+ this.logger.error("Request handling skipped because no ApiCore handler is attached.")
140
+ await apiCore.error()
141
+ return
142
+ }
143
+
144
+ try {
145
+ await apiCoreHandler(apiCore)
146
+ }
147
+ catch (exception) {
148
+ this.logger.error(`Request handling failed, exception: ${String(exception)}`)
149
+ await apiCore.error()
150
+ }
151
+ }
152
+ }
153
+
154
+ export const createApiHostNodeHttp = (options: ApiHostNodeHttpOptions): ApiHostNodeHttp => {
155
+ return new ApiHostNodeHttp(options)
156
+ }
@@ -0,0 +1,297 @@
1
+ import type { Type } from "arktype"
2
+ import { type } from "arktype"
3
+
4
+ import { generateUuidV4 } from "#Source/identifier/uuid.ts"
5
+
6
+ import type { ApiCore, ApiCoreStream } from "../api/api-core.ts"
7
+ import type { SuccessResultOptions, ErrorResultOptions } from "../api/api-result.ts"
8
+ import type { ApiHandlerContext } from "../api/api-handler.ts"
9
+
10
+ import { SuccessResult, ErrorResult } from "../api/api-result.ts"
11
+
12
+ export interface JsonSuccessResultOptions<Data> extends SuccessResultOptions<Data> {
13
+ data: Data
14
+ }
15
+ export class JsonSuccessResult<Data> extends SuccessResult<Data> {
16
+ protected declare options: JsonSuccessResultOptions<Data>
17
+
18
+ constructor(options: JsonSuccessResultOptions<Data>) {
19
+ super(options)
20
+ this.logger.setDefaultName("JsonSuccessResult")
21
+ }
22
+
23
+ async handle(apiCore: ApiCore): Promise<void> {
24
+ const { data } = this.options
25
+
26
+ this.logger.log(`send, ${JSON.stringify(data, null, 2)}`)
27
+ await apiCore.json(data)
28
+ this.logger.log(`end`)
29
+ }
30
+ }
31
+
32
+ export interface CommonSuccessJson<Data> {
33
+ status: "success"
34
+ data: Data
35
+ }
36
+ export interface CommonJsonSuccessResultOptions<Data> extends
37
+ Omit<JsonSuccessResultOptions<CommonSuccessJson<Data>>, "data"> {
38
+ data: Data
39
+ }
40
+ export class CommonJsonSuccessResult<Data>
41
+ extends JsonSuccessResult<CommonSuccessJson<Data>> {
42
+ protected declare options: JsonSuccessResultOptions<CommonSuccessJson<Data>>
43
+
44
+ constructor(options: CommonJsonSuccessResultOptions<Data>) {
45
+ super({
46
+ ...options,
47
+ data: {
48
+ status: "success",
49
+ data: options.data,
50
+ },
51
+ })
52
+ this.logger.setDefaultName("CommonJsonSuccessResult")
53
+ }
54
+ }
55
+ export const commonJsonSuccessResult = <Data>(
56
+ options: CommonJsonSuccessResultOptions<Data>
57
+ ): CommonJsonSuccessResult<Data> => {
58
+ return new CommonJsonSuccessResult<Data>(options)
59
+ }
60
+
61
+ export type CommonJsonSuccessResultSchema<Data> = Type<{
62
+ status: "success"
63
+ data: Data
64
+ }>
65
+ export type GetDataSchemaFromCommonJsonSuccessResultSchema<Schema extends Type>
66
+ = Schema extends CommonJsonSuccessResultSchema<infer Data> ? Type<Data> : never
67
+ export type GetDataFromCommonJsonSuccessResultSchema<Schema extends Type>
68
+ = Schema extends CommonJsonSuccessResultSchema<infer Data> ? Data : never
69
+ export const commonJsonSuccessResultSchema = <Data extends Record<string, unknown>>(
70
+ dataSchema: Type<Data>
71
+ ): CommonJsonSuccessResultSchema<Data> => {
72
+ // arktype's instantiated object schema is compatible here but
73
+ // not assignable through its generic facade
74
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion
75
+ return type({
76
+ status: "'success'",
77
+ data: dataSchema,
78
+ }) as CommonJsonSuccessResultSchema<Data>
79
+ }
80
+
81
+ export type StreamWrite = (chunk: Buffer | string, encoding: BufferEncoding) => void | Promise<void>
82
+ export type StreamEnd = () => void | Promise<void>
83
+ export interface StreamSuccessResultOptions extends SuccessResultOptions<unknown> {
84
+ writeHandler: (write: StreamWrite) => Promise<void>
85
+ }
86
+ export class StreamSuccessResult extends SuccessResult<unknown> {
87
+ protected declare options: StreamSuccessResultOptions
88
+
89
+ protected streamId: string
90
+
91
+ constructor(options: StreamSuccessResultOptions) {
92
+ super(options)
93
+ this.logger.setDefaultName("StreamSuccessResult")
94
+ this.streamId = generateUuidV4()
95
+ }
96
+
97
+ async handle(apiCore: ApiCore): Promise<void> {
98
+ const apiCoreStream = await apiCore.stream()
99
+
100
+ const write = this.wrapWrite(apiCoreStream)
101
+ await this.options.writeHandler(write)
102
+ const end = this.wrapEnd(apiCoreStream)
103
+ await end()
104
+ }
105
+
106
+ /**
107
+ * 把 write 方法包装一下,以达到注入日志等目的。
108
+ */
109
+ protected wrapWrite(apiCoreStream: ApiCoreStream): StreamWrite {
110
+ return async (chunk, encoding): Promise<void> => {
111
+ this.logger.log(`send, id: ${this.streamId}, data: ${String(chunk)}`)
112
+ await apiCoreStream.write(chunk, encoding)
113
+ }
114
+ }
115
+
116
+ /**
117
+ * 把 end 方法包装一下,以达到注入日志等目的。
118
+ */
119
+ protected wrapEnd(apiCoreStream: ApiCoreStream): StreamEnd {
120
+ const end = async (): Promise<void> => {
121
+ this.logger.log(`end, id: ${this.streamId}`)
122
+ await apiCoreStream.end()
123
+ }
124
+ return end
125
+ }
126
+ }
127
+
128
+ export type CustomHandler = (apiCore: ApiCore) => Promise<void>
129
+ export interface CustomSuccessResultOptions extends SuccessResultOptions<unknown> {
130
+ customHandler: CustomHandler
131
+ }
132
+ export class CustomSuccessResult extends SuccessResult<unknown> {
133
+ protected declare options: CustomSuccessResultOptions
134
+
135
+ constructor(options: CustomSuccessResultOptions) {
136
+ super(options)
137
+ this.logger.setDefaultName("CustomSuccessResult")
138
+ }
139
+
140
+ async handle(apiCore: ApiCore): Promise<void> {
141
+ this.logger.log(`send`)
142
+ await this.options.customHandler(apiCore)
143
+ this.logger.log(`end`)
144
+ }
145
+ }
146
+
147
+ // ======================
148
+
149
+ export interface JsonErrorResultOptions<Data> extends ErrorResultOptions<Data> {
150
+ data: Data
151
+ }
152
+ export class JsonErrorResult<Data> extends ErrorResult<Data> {
153
+ protected declare options: JsonErrorResultOptions<Data>
154
+
155
+ constructor(options: JsonErrorResultOptions<Data>) {
156
+ super(options)
157
+ this.logger.setDefaultName("JsonErrorResult")
158
+ }
159
+
160
+ async handle(apiCore: ApiCore): Promise<void> {
161
+ const { data } = this.options
162
+
163
+ this.logger.log(`send, ${JSON.stringify(data, null, 2)}`)
164
+ await apiCore.json(data)
165
+ this.logger.log(`end`)
166
+ }
167
+ }
168
+
169
+ export interface CommonErrorJsonData<Reason extends string> {
170
+ reason: Reason
171
+ reasonDetail?: string | undefined
172
+ }
173
+ export interface CommonErrorJson<Reason extends string> {
174
+ status: "error"
175
+ data: CommonErrorJsonData<Reason>
176
+ }
177
+ export interface CommonJsonErrorResultOptions<Reason extends string>
178
+ extends Omit<JsonErrorResultOptions<CommonErrorJson<Reason>>, "data">,
179
+ CommonErrorJsonData<Reason> {
180
+ }
181
+ export class CommonJsonErrorResult<const Reason extends string>
182
+ extends JsonErrorResult<CommonErrorJson<Reason>> {
183
+ protected declare options: JsonErrorResultOptions<CommonErrorJson<Reason>>
184
+
185
+ constructor(options: CommonJsonErrorResultOptions<Reason>) {
186
+ super({
187
+ ...options,
188
+ data: {
189
+ status: "error",
190
+ data: {
191
+ reason: options.reason,
192
+ reasonDetail: options.reasonDetail,
193
+ },
194
+ },
195
+ })
196
+ this.logger.setDefaultName("CommonJsonErrorResult")
197
+ }
198
+ }
199
+ export const commonJsonErrorResult = <const Reason extends string>(
200
+ options: CommonJsonErrorResultOptions<Reason>
201
+ ): CommonJsonErrorResult<Reason> => {
202
+ return new CommonJsonErrorResult<Reason>(options)
203
+ }
204
+
205
+ export type CommonJsonErrorResultSchema<
206
+ Data extends {
207
+ reason: string
208
+ reasonDetail?: string | undefined
209
+ }
210
+ > = Type<{
211
+ status: "error"
212
+ data: Data & {
213
+ reasonDetail: string | undefined
214
+ }
215
+ }>
216
+ export type GetDataSchemaFromCommonJsonErrorResultSchema<Schema extends Type>
217
+ = Schema extends CommonJsonErrorResultSchema<infer Data> ? Type<Data> : never
218
+ export type GetDataFromCommonJsonErrorResultSchema<Schema extends Type>
219
+ = Schema extends CommonJsonErrorResultSchema<infer Data> ? Data : never
220
+ export type GetReasonFromCommonJsonErrorResultSchema<Schema extends Type>
221
+ = Schema extends CommonJsonErrorResultSchema<infer Data>
222
+ ? Data extends { reason: infer Reason } ? Reason : never
223
+ : never
224
+ export const commonJsonErrorResultSchema = <
225
+ Data extends {
226
+ reason: string
227
+ reasonDetail?: string | undefined
228
+ }
229
+ >(
230
+ dataSchema: Type<Data>
231
+ ): CommonJsonErrorResultSchema<Data> => {
232
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion
233
+ return type({
234
+ status: "'error'",
235
+ data: type.and(
236
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion
237
+ dataSchema as Type<{
238
+ reason: string
239
+ reasonDetail?: string | undefined
240
+ }>,
241
+ {
242
+ reasonDetail: type.string
243
+ }
244
+ ),
245
+ }) as CommonJsonErrorResultSchema<Data>
246
+ }
247
+
248
+ // ======================
249
+
250
+ export type ContextWithResultHandler<
251
+ Path extends string,
252
+ Method extends "get" | "post",
253
+ InputQuerySchema extends Type,
254
+ InputBodySchema extends Type,
255
+ OutputSuccessSchema extends Type,
256
+ OutputErrorSchema extends Type,
257
+ >
258
+ = ApiHandlerContext<Path, Method, InputQuerySchema, InputBodySchema, OutputSuccessSchema, OutputErrorSchema>
259
+ & {
260
+ commonJsonSuccessResult: (
261
+ options: CommonJsonSuccessResultOptions<GetDataFromCommonJsonSuccessResultSchema<OutputSuccessSchema>>
262
+ ) => CommonJsonSuccessResult<GetDataFromCommonJsonSuccessResultSchema<OutputSuccessSchema>>
263
+ commonJsonErrorResult: (
264
+ options: CommonJsonErrorResultOptions<GetReasonFromCommonJsonErrorResultSchema<OutputErrorSchema>>
265
+ ) => CommonJsonErrorResult<GetReasonFromCommonJsonErrorResultSchema<OutputErrorSchema>>
266
+ }
267
+
268
+ export const withResultHandler = <
269
+ Path extends string,
270
+ Method extends "get" | "post",
271
+ InputQuerySchema extends Type,
272
+ InputBodySchema extends Type,
273
+ OutputSuccessSchema extends Type,
274
+ OutputErrorSchema extends Type,
275
+ >(
276
+ context: ApiHandlerContext<Path, Method, InputQuerySchema, InputBodySchema, OutputSuccessSchema, OutputErrorSchema>,
277
+ ): ContextWithResultHandler<Path, Method, InputQuerySchema, InputBodySchema, OutputSuccessSchema, OutputErrorSchema> => {
278
+ return {
279
+ ...context,
280
+ commonJsonSuccessResult: (
281
+ options: CommonJsonSuccessResultOptions<GetDataFromCommonJsonSuccessResultSchema<OutputSuccessSchema>>,
282
+ ) => {
283
+ return commonJsonSuccessResult({
284
+ logger: context.logger,
285
+ ...options,
286
+ })
287
+ },
288
+ commonJsonErrorResult: (
289
+ options: CommonJsonErrorResultOptions<GetReasonFromCommonJsonErrorResultSchema<OutputErrorSchema>>,
290
+ ) => {
291
+ return commonJsonErrorResult<GetReasonFromCommonJsonErrorResultSchema<OutputErrorSchema>>({
292
+ logger: context.logger,
293
+ ...options,
294
+ })
295
+ },
296
+ }
297
+ }