@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.
- package/CHANGELOG.md +50 -0
- package/oxlint.config.ts +1 -2
- package/package.json +29 -17
- package/scripts/build.ts +2 -52
- package/src/ai/README.md +1 -0
- package/src/ai/ai.ts +107 -0
- package/src/ai/chat-completion-ai/aihubmix-chat-completion.ts +78 -0
- package/src/ai/chat-completion-ai/chat-completion-ai.ts +270 -0
- package/src/ai/chat-completion-ai/chat-completion.ts +189 -0
- package/src/ai/chat-completion-ai/index.ts +7 -0
- package/src/ai/chat-completion-ai/lingyiwanwu-chat-completion.ts +78 -0
- package/src/ai/chat-completion-ai/ohmygpt-chat-completion.ts +78 -0
- package/src/ai/chat-completion-ai/openai-next-chat-completion.ts +78 -0
- package/src/ai/embedding-ai/embedding-ai.ts +63 -0
- package/src/ai/embedding-ai/embedding.ts +50 -0
- package/src/ai/embedding-ai/index.ts +4 -0
- package/src/ai/embedding-ai/openai-next-embedding.ts +23 -0
- package/src/ai/index.ts +4 -0
- package/src/aio/README.md +100 -0
- package/src/aio/content.ts +141 -0
- package/src/aio/index.ts +3 -0
- package/src/aio/json.ts +127 -0
- package/src/aio/prompt.ts +246 -0
- package/src/basic/README.md +20 -15
- package/src/basic/error.ts +19 -5
- package/src/basic/function.ts +2 -2
- package/src/basic/index.ts +1 -0
- package/src/basic/promise.ts +141 -71
- package/src/basic/schedule.ts +111 -0
- package/src/basic/stream.ts +135 -25
- package/src/credential/README.md +107 -0
- package/src/credential/api-key.ts +158 -0
- package/src/credential/bearer.ts +73 -0
- package/src/credential/index.ts +4 -0
- package/src/credential/json-web-token.ts +96 -0
- package/src/credential/password.ts +170 -0
- package/src/cron/README.md +86 -0
- package/src/cron/cron.ts +87 -0
- package/src/cron/index.ts +1 -0
- package/src/drizzle/README.md +1 -0
- package/src/drizzle/drizzle.ts +1 -0
- package/src/drizzle/helper.ts +47 -0
- package/src/drizzle/index.ts +5 -0
- package/src/drizzle/infer.ts +52 -0
- package/src/drizzle/kysely.ts +8 -0
- package/src/drizzle/pagination.ts +198 -0
- package/src/email/README.md +1 -0
- package/src/email/index.ts +1 -0
- package/src/email/resend.ts +25 -0
- package/src/event/class-event-proxy.ts +5 -6
- package/src/event/common.ts +13 -3
- package/src/event/event-manager.ts +3 -3
- package/src/event/instance-event-proxy.ts +5 -6
- package/src/event/internal.ts +4 -4
- package/src/exception/README.md +28 -19
- package/src/exception/error/error.ts +123 -0
- package/src/exception/error/index.ts +2 -0
- package/src/exception/error/match.ts +38 -0
- package/src/exception/error/must-fix.ts +17 -0
- package/src/exception/index.ts +2 -0
- package/src/file-system/find.ts +53 -0
- package/src/file-system/index.ts +2 -0
- package/src/file-system/path.ts +76 -0
- package/src/file-system/resolve.ts +22 -0
- package/src/form/README.md +25 -0
- package/src/form/index.ts +1 -0
- package/src/form/inputor-controller/base.ts +861 -0
- package/src/form/inputor-controller/boolean.ts +39 -0
- package/src/form/inputor-controller/file.ts +39 -0
- package/src/form/inputor-controller/form.ts +179 -0
- package/src/form/inputor-controller/helper.ts +117 -0
- package/src/form/inputor-controller/index.ts +17 -0
- package/src/form/inputor-controller/multi-select.ts +99 -0
- package/src/form/inputor-controller/number.ts +116 -0
- package/src/form/inputor-controller/select.ts +109 -0
- package/src/form/inputor-controller/text.ts +82 -0
- package/src/http/READMD.md +1 -0
- package/src/http/api/api-core.ts +84 -0
- package/src/http/api/api-handler.ts +79 -0
- package/src/http/api/api-host.ts +47 -0
- package/src/http/api/api-result.ts +56 -0
- package/src/http/api/api-schema.ts +154 -0
- package/src/http/api/api-server.ts +130 -0
- package/src/http/api/api-test.ts +142 -0
- package/src/http/api/api-type.ts +34 -0
- package/src/http/api/api.ts +81 -0
- package/src/http/api/index.ts +11 -0
- package/src/http/api-adapter/api-core-node-http.ts +260 -0
- package/src/http/api-adapter/api-host-node-http.ts +156 -0
- package/src/http/api-adapter/api-result-arktype.ts +294 -0
- package/src/http/api-adapter/api-result-zod.ts +286 -0
- package/src/http/api-adapter/index.ts +5 -0
- package/src/http/bin/gen-api-list/gen-api-list.ts +126 -0
- package/src/http/bin/gen-api-list/index.ts +1 -0
- package/src/http/bin/gen-api-test/gen-api-test.ts +136 -0
- package/src/http/bin/gen-api-test/index.ts +1 -0
- package/src/http/bin/gen-api-type/calc-code.ts +25 -0
- package/src/http/bin/gen-api-type/gen-api-type.ts +127 -0
- package/src/http/bin/gen-api-type/index.ts +2 -0
- package/src/http/bin/index.ts +2 -0
- package/src/http/index.ts +3 -0
- package/src/huawei/README.md +1 -0
- package/src/huawei/index.ts +2 -0
- package/src/huawei/moderation/index.ts +1 -0
- package/src/huawei/moderation/moderation.ts +355 -0
- package/src/huawei/obs/esdk-obs-nodejs.d.ts +87 -0
- package/src/huawei/obs/index.ts +1 -0
- package/src/huawei/obs/obs.ts +42 -0
- package/src/index.ts +21 -2
- package/src/json/README.md +92 -0
- package/src/json/index.ts +1 -0
- package/src/json/repair.ts +18 -0
- package/src/log/logger.ts +15 -4
- package/src/openai/README.md +1 -0
- package/src/openai/index.ts +1 -0
- package/src/openai/openai.ts +509 -0
- package/src/orchestration/README.md +9 -7
- package/src/orchestration/dispatching/dispatcher.ts +83 -0
- package/src/orchestration/dispatching/index.ts +2 -0
- package/src/orchestration/dispatching/selector/base-selector.ts +39 -0
- package/src/orchestration/dispatching/selector/down-count-selector.ts +119 -0
- package/src/orchestration/dispatching/selector/index.ts +2 -0
- package/src/orchestration/index.ts +2 -0
- package/src/orchestration/scheduling/index.ts +2 -0
- package/src/orchestration/scheduling/scheduler.ts +103 -0
- package/src/orchestration/scheduling/task.ts +32 -0
- package/src/random/README.md +8 -7
- package/src/random/base.ts +66 -0
- package/src/random/index.ts +5 -1
- package/src/random/random-boolean.ts +40 -0
- package/src/random/random-integer.ts +60 -0
- package/src/random/random-number.ts +72 -0
- package/src/random/random-string.ts +66 -0
- package/src/request/README.md +108 -0
- package/src/request/fetch/base.ts +108 -0
- package/src/request/fetch/browser.ts +280 -0
- package/src/request/fetch/general.ts +20 -0
- package/src/request/fetch/index.ts +4 -0
- package/src/request/fetch/nodejs.ts +280 -0
- package/src/request/index.ts +2 -0
- package/src/request/request/base.ts +246 -0
- package/src/request/request/general.ts +63 -0
- package/src/request/request/index.ts +3 -0
- package/src/request/request/resource.ts +68 -0
- package/src/result/README.md +4 -0
- package/src/result/controller.ts +58 -0
- package/src/result/either.ts +363 -0
- package/src/result/generator.ts +168 -0
- package/src/result/index.ts +3 -0
- package/src/route/README.md +105 -0
- package/src/route/adapter/browser.ts +122 -0
- package/src/route/adapter/driver.ts +56 -0
- package/src/route/adapter/index.ts +2 -0
- package/src/route/index.ts +3 -0
- package/src/route/router/index.ts +2 -0
- package/src/route/router/route.ts +630 -0
- package/src/route/router/router.ts +1641 -0
- package/src/route/uri/hash.ts +307 -0
- package/src/route/uri/index.ts +7 -0
- package/src/route/uri/pathname.ts +376 -0
- package/src/route/uri/search.ts +412 -0
- package/src/service/README.md +1 -0
- package/src/service/index.ts +1 -0
- package/src/service/service.ts +110 -0
- package/src/socket/README.md +105 -0
- package/src/socket/client/index.ts +2 -0
- package/src/socket/client/socket-unit.ts +658 -0
- package/src/socket/client/socket.ts +203 -0
- package/src/socket/common/index.ts +2 -0
- package/src/socket/common/socket-unit-common.ts +23 -0
- package/src/socket/common/socket-unit-heartbeat.ts +427 -0
- package/src/socket/index.ts +3 -0
- package/src/socket/server/index.ts +3 -0
- package/src/socket/server/server.ts +183 -0
- package/src/socket/server/socket-unit.ts +448 -0
- package/src/socket/server/socket.ts +264 -0
- package/src/storage/table.ts +3 -3
- package/src/timer/expiration/expiration-manager.ts +3 -3
- package/src/timer/expiration/remaining-manager.ts +3 -3
- package/src/tube/README.md +99 -0
- package/src/tube/helper.ts +137 -0
- package/src/tube/index.ts +2 -0
- package/src/tube/tube.ts +880 -0
- package/src/weixin/README.md +1 -0
- package/src/weixin/index.ts +2 -0
- package/src/weixin/official-account/authorization.ts +157 -0
- package/src/weixin/official-account/index.ts +2 -0
- package/src/weixin/official-account/js-api.ts +132 -0
- package/src/weixin/open/index.ts +1 -0
- package/src/weixin/open/oauth2.ts +131 -0
- package/tests/unit/ai/ai.spec.ts +85 -0
- package/tests/unit/aio/content.spec.ts +105 -0
- package/tests/unit/aio/json.spec.ts +146 -0
- package/tests/unit/aio/prompt.spec.ts +111 -0
- package/tests/unit/basic/error.spec.ts +16 -4
- package/tests/unit/basic/promise.spec.ts +158 -50
- package/tests/unit/basic/schedule.spec.ts +74 -0
- package/tests/unit/basic/stream.spec.ts +90 -37
- package/tests/unit/credential/api-key.spec.ts +36 -0
- package/tests/unit/credential/bearer.spec.ts +23 -0
- package/tests/unit/credential/json-web-token.spec.ts +23 -0
- package/tests/unit/credential/password.spec.ts +40 -0
- package/tests/unit/cron/cron.spec.ts +84 -0
- package/tests/unit/event/class-event-proxy.spec.ts +3 -3
- package/tests/unit/event/event-manager.spec.ts +3 -3
- package/tests/unit/event/instance-event-proxy.spec.ts +3 -3
- package/tests/unit/exception/error/error.spec.ts +83 -0
- package/tests/unit/exception/error/match.spec.ts +81 -0
- package/tests/unit/form/inputor-controller/base.spec.ts +458 -0
- package/tests/unit/form/inputor-controller/boolean.spec.ts +30 -0
- package/tests/unit/form/inputor-controller/file.spec.ts +27 -0
- package/tests/unit/form/inputor-controller/form.spec.ts +120 -0
- package/tests/unit/form/inputor-controller/helper.spec.ts +67 -0
- package/tests/unit/form/inputor-controller/multi-select.spec.ts +34 -0
- package/tests/unit/form/inputor-controller/number.spec.ts +36 -0
- package/tests/unit/form/inputor-controller/select.spec.ts +49 -0
- package/tests/unit/form/inputor-controller/text.spec.ts +34 -0
- package/tests/unit/http/api/api-core-host.spec.ts +207 -0
- package/tests/unit/http/api/api-schema.spec.ts +120 -0
- package/tests/unit/http/api/api-server.spec.ts +363 -0
- package/tests/unit/http/api/api-test.spec.ts +117 -0
- package/tests/unit/http/api/api.spec.ts +121 -0
- package/tests/unit/http/api-adapter/node-http.spec.ts +187 -0
- package/tests/unit/identifier/uuid.spec.ts +0 -1
- package/tests/unit/json/repair.spec.ts +11 -0
- package/tests/unit/log/logger.spec.ts +19 -4
- package/tests/unit/openai/openai.spec.ts +64 -0
- package/tests/unit/orchestration/dispatching/dispatcher.spec.ts +41 -0
- package/tests/unit/orchestration/dispatching/selector/down-count-selector.spec.ts +81 -0
- package/tests/unit/orchestration/scheduling/scheduler.spec.ts +103 -0
- package/tests/unit/random/base.spec.ts +58 -0
- package/tests/unit/random/random-boolean.spec.ts +25 -0
- package/tests/unit/random/random-integer.spec.ts +32 -0
- package/tests/unit/random/random-number.spec.ts +33 -0
- package/tests/unit/random/random-string.spec.ts +22 -0
- package/tests/unit/request/fetch/browser.spec.ts +222 -0
- package/tests/unit/request/fetch/general.spec.ts +43 -0
- package/tests/unit/request/fetch/nodejs.spec.ts +225 -0
- package/tests/unit/request/request/base.spec.ts +382 -0
- package/tests/unit/request/request/general.spec.ts +160 -0
- package/tests/unit/result/controller.spec.ts +82 -0
- package/tests/unit/result/either.spec.ts +377 -0
- package/tests/unit/result/generator.spec.ts +273 -0
- package/tests/unit/route/router/route.spec.ts +430 -0
- package/tests/unit/route/router/router.spec.ts +407 -0
- package/tests/unit/route/uri/hash.spec.ts +72 -0
- package/tests/unit/route/uri/pathname.spec.ts +146 -0
- package/tests/unit/route/uri/search.spec.ts +107 -0
- package/tests/unit/socket/client.spec.ts +208 -0
- package/tests/unit/socket/server.spec.ts +133 -0
- package/tests/unit/socket/socket-unit-heartbeat.spec.ts +214 -0
- package/tests/unit/tube/helper.spec.ts +139 -0
- package/tests/unit/tube/tube.spec.ts +501 -0
- package/vite.config.ts +2 -1
- package/dist/index.js +0 -50
- package/dist/index.js.map +0 -209
- package/src/random/string.ts +0 -35
- package/tests/unit/random/string.spec.ts +0 -11
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
import { expect, test } from "vitest"
|
|
2
|
+
|
|
3
|
+
import { BaseFetch, BaseRequest } from "#Source/request/index.ts"
|
|
4
|
+
import type { BaseFetchOptions, FetchFactory, Resource } from "#Source/request/index.ts"
|
|
5
|
+
|
|
6
|
+
type ExampleSuccessOutput = {
|
|
7
|
+
status: "success"
|
|
8
|
+
data: {
|
|
9
|
+
users: string[]
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type ExampleErrorOutput = {
|
|
14
|
+
status: "error"
|
|
15
|
+
data: {
|
|
16
|
+
message: string
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type ExamplePatchOutput = {
|
|
21
|
+
status: "patch"
|
|
22
|
+
errorKind: "network" | "unknown"
|
|
23
|
+
message: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type ExampleResource = Resource & {
|
|
27
|
+
baseUrl: "https://api.example.com"
|
|
28
|
+
path: "/users"
|
|
29
|
+
method: "get"
|
|
30
|
+
input: {
|
|
31
|
+
query: {
|
|
32
|
+
page: number
|
|
33
|
+
}
|
|
34
|
+
body?: undefined
|
|
35
|
+
}
|
|
36
|
+
output: ExampleSuccessOutput | ExampleErrorOutput
|
|
37
|
+
successOutput: ExampleSuccessOutput
|
|
38
|
+
successData: ExampleSuccessOutput["data"]
|
|
39
|
+
errorOutput: ExampleErrorOutput
|
|
40
|
+
errorData: ExampleErrorOutput["data"]
|
|
41
|
+
patchOutput: ExamplePatchOutput
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
class TestRequest extends BaseRequest<ExampleResource> {
|
|
45
|
+
//
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
class TestResolvedFetch<T> extends BaseFetch<
|
|
49
|
+
{ query: ExampleResource["input"]["query"]; body: ExampleResource["input"]["body"] },
|
|
50
|
+
{ type: "json"; data: T }
|
|
51
|
+
> {
|
|
52
|
+
constructor(value: T) {
|
|
53
|
+
const options: BaseFetchOptions<
|
|
54
|
+
{ query: ExampleResource["input"]["query"]; body: ExampleResource["input"]["body"] },
|
|
55
|
+
{ type: "json"; data: T }
|
|
56
|
+
> = {
|
|
57
|
+
baseUrl: "https://api.example.com",
|
|
58
|
+
path: "/users",
|
|
59
|
+
method: "get",
|
|
60
|
+
}
|
|
61
|
+
super(options)
|
|
62
|
+
this.value = value
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private value: T
|
|
66
|
+
|
|
67
|
+
async getJson(): Promise<T> {
|
|
68
|
+
return await Promise.resolve(this.value)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async getText(): Promise<never> {
|
|
72
|
+
await Promise.resolve()
|
|
73
|
+
throw new Error("Unused in request tests.")
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async getBlob(): Promise<never> {
|
|
77
|
+
await Promise.resolve()
|
|
78
|
+
throw new Error("Unused in request tests.")
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async getArrayBuffer(): Promise<never> {
|
|
82
|
+
await Promise.resolve()
|
|
83
|
+
throw new Error("Unused in request tests.")
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async getStream(): Promise<never> {
|
|
87
|
+
await Promise.resolve()
|
|
88
|
+
throw new Error("Unused in request tests.")
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
class TestRejectingFetch extends BaseFetch<
|
|
93
|
+
{ query: ExampleResource["input"]["query"]; body: ExampleResource["input"]["body"] },
|
|
94
|
+
{ type: "json"; data: never }
|
|
95
|
+
> {
|
|
96
|
+
constructor(exception: unknown) {
|
|
97
|
+
const options: BaseFetchOptions<
|
|
98
|
+
{ query: ExampleResource["input"]["query"]; body: ExampleResource["input"]["body"] },
|
|
99
|
+
{ type: "json"; data: never }
|
|
100
|
+
> = {
|
|
101
|
+
baseUrl: "https://api.example.com",
|
|
102
|
+
path: "/users",
|
|
103
|
+
method: "get",
|
|
104
|
+
}
|
|
105
|
+
super(options)
|
|
106
|
+
this.exception = exception
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private exception: unknown
|
|
110
|
+
|
|
111
|
+
async getJson(): Promise<never> {
|
|
112
|
+
await Promise.resolve()
|
|
113
|
+
throw this.exception
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async getText(): Promise<never> {
|
|
117
|
+
await Promise.resolve()
|
|
118
|
+
throw new Error("Unused in request tests.")
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async getBlob(): Promise<never> {
|
|
122
|
+
await Promise.resolve()
|
|
123
|
+
throw new Error("Unused in request tests.")
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async getArrayBuffer(): Promise<never> {
|
|
127
|
+
await Promise.resolve()
|
|
128
|
+
throw new Error("Unused in request tests.")
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async getStream(): Promise<never> {
|
|
132
|
+
await Promise.resolve()
|
|
133
|
+
throw new Error("Unused in request tests.")
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
test("BaseRequest.request emits lifecycle callbacks and events for success and error JSON outputs", async () => {
|
|
138
|
+
const successOutput: ExampleSuccessOutput = {
|
|
139
|
+
status: "success",
|
|
140
|
+
data: {
|
|
141
|
+
users: ["neo", "trinity"]
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const errorOutput: ExampleErrorOutput = {
|
|
145
|
+
status: "error",
|
|
146
|
+
data: {
|
|
147
|
+
message: "denied"
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
let getFetchCallCount: number = 0
|
|
151
|
+
const getFetchCalls: BaseFetchOptions[] = []
|
|
152
|
+
const getFetch = (options: BaseFetchOptions): TestResolvedFetch<ExampleSuccessOutput> | TestResolvedFetch<ExampleErrorOutput> => {
|
|
153
|
+
getFetchCallCount = getFetchCallCount + 1
|
|
154
|
+
getFetchCalls.push(options)
|
|
155
|
+
|
|
156
|
+
if (getFetchCallCount === 1) {
|
|
157
|
+
return new TestResolvedFetch(successOutput)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return new TestResolvedFetch(errorOutput)
|
|
161
|
+
}
|
|
162
|
+
let modifyRequestOptionsCallCount: number = 0
|
|
163
|
+
const modifyRequestOptions = (options: {
|
|
164
|
+
baseUrl: ExampleResource["baseUrl"]
|
|
165
|
+
path: ExampleResource["path"]
|
|
166
|
+
method: ExampleResource["method"]
|
|
167
|
+
headers?: Record<string, string> | undefined
|
|
168
|
+
query: ExampleResource["input"]["query"]
|
|
169
|
+
}): {
|
|
170
|
+
baseUrl: ExampleResource["baseUrl"]
|
|
171
|
+
path: ExampleResource["path"]
|
|
172
|
+
method: ExampleResource["method"]
|
|
173
|
+
headers: Record<string, string>
|
|
174
|
+
query: ExampleResource["input"]["query"]
|
|
175
|
+
} => {
|
|
176
|
+
modifyRequestOptionsCallCount = modifyRequestOptionsCallCount + 1
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
...options,
|
|
180
|
+
headers: {
|
|
181
|
+
...options.headers,
|
|
182
|
+
"X-Trace": "enabled"
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const request = new TestRequest({
|
|
187
|
+
fetchFactory: getFetch as unknown as FetchFactory,
|
|
188
|
+
modifyRequestOptions,
|
|
189
|
+
generatePatchOutput: ({ errorKind, error }) => {
|
|
190
|
+
return {
|
|
191
|
+
status: "patch",
|
|
192
|
+
errorKind,
|
|
193
|
+
message: error.message,
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
const emittedOutputs: Array<ExampleSuccessOutput | ExampleErrorOutput> = []
|
|
199
|
+
const emittedSuccessOutputs: ExampleSuccessOutput[] = []
|
|
200
|
+
const emittedSuccessData: Array<ExampleSuccessOutput["data"]> = []
|
|
201
|
+
const emittedErrorOutputs: ExampleErrorOutput[] = []
|
|
202
|
+
const emittedErrorData: Array<ExampleErrorOutput["data"]> = []
|
|
203
|
+
request.event.subscribe("output", (output) => {
|
|
204
|
+
emittedOutputs.push(output)
|
|
205
|
+
})
|
|
206
|
+
request.event.subscribe("successOutput", (output) => {
|
|
207
|
+
emittedSuccessOutputs.push(output)
|
|
208
|
+
})
|
|
209
|
+
request.event.subscribe("successData", (data) => {
|
|
210
|
+
emittedSuccessData.push(data)
|
|
211
|
+
})
|
|
212
|
+
request.event.subscribe("errorOutput", (output) => {
|
|
213
|
+
emittedErrorOutputs.push(output)
|
|
214
|
+
})
|
|
215
|
+
request.event.subscribe("errorData", (data) => {
|
|
216
|
+
emittedErrorData.push(data)
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
const callbackOutputs: Array<ExampleSuccessOutput | ExampleErrorOutput> = []
|
|
220
|
+
const callbackSuccessOutputs: ExampleSuccessOutput[] = []
|
|
221
|
+
const callbackSuccessData: Array<ExampleSuccessOutput["data"]> = []
|
|
222
|
+
const callbackErrorOutputs: ExampleErrorOutput[] = []
|
|
223
|
+
const callbackErrorData: Array<ExampleErrorOutput["data"]> = []
|
|
224
|
+
|
|
225
|
+
const successResult = await request.request({
|
|
226
|
+
baseUrl: "https://api.example.com",
|
|
227
|
+
path: "/users",
|
|
228
|
+
method: "get",
|
|
229
|
+
query: {
|
|
230
|
+
page: 1
|
|
231
|
+
},
|
|
232
|
+
headers: {
|
|
233
|
+
Authorization: "Bearer token"
|
|
234
|
+
},
|
|
235
|
+
onOutput: (output) => {
|
|
236
|
+
callbackOutputs.push(output)
|
|
237
|
+
},
|
|
238
|
+
onSuccessOutput: (output) => {
|
|
239
|
+
callbackSuccessOutputs.push(output)
|
|
240
|
+
},
|
|
241
|
+
onSuccessData: (data) => {
|
|
242
|
+
callbackSuccessData.push(data)
|
|
243
|
+
},
|
|
244
|
+
})
|
|
245
|
+
const errorResult = await request.request({
|
|
246
|
+
baseUrl: "https://api.example.com",
|
|
247
|
+
path: "/users",
|
|
248
|
+
method: "get",
|
|
249
|
+
query: {
|
|
250
|
+
page: 2
|
|
251
|
+
},
|
|
252
|
+
onOutput: (output) => {
|
|
253
|
+
callbackOutputs.push(output)
|
|
254
|
+
},
|
|
255
|
+
onErrorOutput: (output) => {
|
|
256
|
+
callbackErrorOutputs.push(output)
|
|
257
|
+
},
|
|
258
|
+
onErrorData: (data) => {
|
|
259
|
+
callbackErrorData.push(data)
|
|
260
|
+
},
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
expect(successResult).toEqual(successOutput)
|
|
264
|
+
expect(errorResult).toEqual(errorOutput)
|
|
265
|
+
expect(modifyRequestOptionsCallCount).toBe(2)
|
|
266
|
+
expect(getFetchCallCount).toBe(2)
|
|
267
|
+
expect(getFetchCalls[0]?.path).toBe("/users")
|
|
268
|
+
expect(getFetchCalls[0]?.headers?.["Authorization"]).toBe("Bearer token")
|
|
269
|
+
expect(getFetchCalls[0]?.headers?.["X-Trace"]).toBe("enabled")
|
|
270
|
+
expect(getFetchCalls[1]?.path).toBe("/users")
|
|
271
|
+
expect(getFetchCalls[1]?.headers?.["X-Trace"]).toBe("enabled")
|
|
272
|
+
expect(callbackOutputs).toEqual([successOutput, errorOutput])
|
|
273
|
+
expect(callbackSuccessOutputs).toEqual([successOutput])
|
|
274
|
+
expect(callbackSuccessData).toEqual([successOutput.data])
|
|
275
|
+
expect(callbackErrorOutputs).toEqual([errorOutput])
|
|
276
|
+
expect(callbackErrorData).toEqual([errorOutput.data])
|
|
277
|
+
expect(emittedOutputs).toEqual([successOutput, errorOutput])
|
|
278
|
+
expect(emittedSuccessOutputs).toEqual([successOutput])
|
|
279
|
+
expect(emittedSuccessData).toEqual([successOutput.data])
|
|
280
|
+
expect(emittedErrorOutputs).toEqual([errorOutput])
|
|
281
|
+
expect(emittedErrorData).toEqual([errorOutput.data])
|
|
282
|
+
})
|
|
283
|
+
|
|
284
|
+
test("BaseRequest.request turns unsupported response types into unknown patch outputs", async () => {
|
|
285
|
+
const request = new TestRequest({
|
|
286
|
+
generatePatchOutput: ({ errorKind, error }) => {
|
|
287
|
+
return {
|
|
288
|
+
status: "patch",
|
|
289
|
+
errorKind,
|
|
290
|
+
message: error.message,
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
const result = await request.request({
|
|
296
|
+
baseUrl: "https://api.example.com",
|
|
297
|
+
path: "/users",
|
|
298
|
+
method: "get",
|
|
299
|
+
query: {
|
|
300
|
+
page: 1
|
|
301
|
+
},
|
|
302
|
+
responseType: "text"
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
expect(result).toEqual({
|
|
306
|
+
status: "patch",
|
|
307
|
+
errorKind: "unknown",
|
|
308
|
+
message: "Unsupported response type, please use \"json\" or improve implementation of BaseRequest.",
|
|
309
|
+
})
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
test("BaseRequest.request converts network failures into patch outputs", async () => {
|
|
313
|
+
const getFetch = (): TestRejectingFetch => {
|
|
314
|
+
return new TestRejectingFetch(new TypeError("fetch failed"))
|
|
315
|
+
}
|
|
316
|
+
const request = new TestRequest({
|
|
317
|
+
fetchFactory: getFetch as unknown as FetchFactory,
|
|
318
|
+
generatePatchOutput: ({ errorKind, error }) => {
|
|
319
|
+
return {
|
|
320
|
+
status: "patch",
|
|
321
|
+
errorKind,
|
|
322
|
+
message: error.message,
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
})
|
|
326
|
+
const patchOutputs: ExamplePatchOutput[] = []
|
|
327
|
+
request.event.subscribe("patchOutput", (patchOutput) => {
|
|
328
|
+
patchOutputs.push(patchOutput)
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
const callbackPatchOutputs: ExamplePatchOutput[] = []
|
|
332
|
+
const result = await request.request({
|
|
333
|
+
baseUrl: "https://api.example.com",
|
|
334
|
+
path: "/users",
|
|
335
|
+
method: "get",
|
|
336
|
+
query: {
|
|
337
|
+
page: 1
|
|
338
|
+
},
|
|
339
|
+
onPatchOutput: (patchOutput) => {
|
|
340
|
+
callbackPatchOutputs.push(patchOutput)
|
|
341
|
+
},
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
expect(result).toEqual({
|
|
345
|
+
status: "patch",
|
|
346
|
+
errorKind: "network",
|
|
347
|
+
message: "fetch failed",
|
|
348
|
+
})
|
|
349
|
+
expect(callbackPatchOutputs).toEqual([result])
|
|
350
|
+
expect(patchOutputs).toEqual([result])
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
test("BaseRequest.request stringifies non-Error failures into unknown patch outputs", async () => {
|
|
354
|
+
const getFetch = (): TestRejectingFetch => {
|
|
355
|
+
return new TestRejectingFetch("boom")
|
|
356
|
+
}
|
|
357
|
+
const request = new TestRequest({
|
|
358
|
+
fetchFactory: getFetch as unknown as FetchFactory,
|
|
359
|
+
generatePatchOutput: ({ errorKind, error }) => {
|
|
360
|
+
return {
|
|
361
|
+
status: "patch",
|
|
362
|
+
errorKind,
|
|
363
|
+
message: error.message,
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
const result = await request.request({
|
|
369
|
+
baseUrl: "https://api.example.com",
|
|
370
|
+
path: "/users",
|
|
371
|
+
method: "get",
|
|
372
|
+
query: {
|
|
373
|
+
page: 1
|
|
374
|
+
},
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
expect(result).toEqual({
|
|
378
|
+
status: "patch",
|
|
379
|
+
errorKind: "unknown",
|
|
380
|
+
message: "boom",
|
|
381
|
+
})
|
|
382
|
+
})
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { expect, test } from "vitest"
|
|
2
|
+
|
|
3
|
+
import { BaseFetch, GeneralRequest } from "#Source/request/index.ts"
|
|
4
|
+
import type { BaseFetchOptions, FetchFactory, GeneralResource } from "#Source/request/index.ts"
|
|
5
|
+
|
|
6
|
+
type ExampleGeneralResource = GeneralResource & {
|
|
7
|
+
baseUrl: "https://api.example.com"
|
|
8
|
+
path: "/users"
|
|
9
|
+
method: "get"
|
|
10
|
+
input: {
|
|
11
|
+
query: {
|
|
12
|
+
page: number
|
|
13
|
+
}
|
|
14
|
+
body?: undefined
|
|
15
|
+
}
|
|
16
|
+
successData: {
|
|
17
|
+
users: string[]
|
|
18
|
+
}
|
|
19
|
+
errorData: {
|
|
20
|
+
message: string
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class TestGeneralResolvedFetch extends BaseFetch<
|
|
25
|
+
{ query: ExampleGeneralResource["input"]["query"]; body: ExampleGeneralResource["input"]["body"] },
|
|
26
|
+
{ type: "json"; data: { status: "success"; data: ExampleGeneralResource["successData"] } }
|
|
27
|
+
> {
|
|
28
|
+
constructor() {
|
|
29
|
+
const options: BaseFetchOptions<
|
|
30
|
+
{ query: ExampleGeneralResource["input"]["query"]; body: ExampleGeneralResource["input"]["body"] },
|
|
31
|
+
{ type: "json"; data: { status: "success"; data: ExampleGeneralResource["successData"] } }
|
|
32
|
+
> = {
|
|
33
|
+
baseUrl: "https://api.example.com",
|
|
34
|
+
path: "/users",
|
|
35
|
+
method: "get",
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
super(options)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async getJson(): Promise<{ status: "success"; data: ExampleGeneralResource["successData"] }> {
|
|
42
|
+
return await Promise.resolve({
|
|
43
|
+
status: "success",
|
|
44
|
+
data: {
|
|
45
|
+
users: ["neo", "trinity"]
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async getText(): Promise<never> {
|
|
51
|
+
await Promise.resolve()
|
|
52
|
+
throw new Error("Unused in GeneralRequest tests.")
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async getBlob(): Promise<never> {
|
|
56
|
+
await Promise.resolve()
|
|
57
|
+
throw new Error("Unused in GeneralRequest tests.")
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async getArrayBuffer(): Promise<never> {
|
|
61
|
+
await Promise.resolve()
|
|
62
|
+
throw new Error("Unused in GeneralRequest tests.")
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async getStream(): Promise<never> {
|
|
66
|
+
await Promise.resolve()
|
|
67
|
+
throw new Error("Unused in GeneralRequest tests.")
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
class TestGeneralRejectingFetch extends BaseFetch<
|
|
72
|
+
{ query: ExampleGeneralResource["input"]["query"]; body: ExampleGeneralResource["input"]["body"] },
|
|
73
|
+
{ type: "json"; data: never }
|
|
74
|
+
> {
|
|
75
|
+
constructor() {
|
|
76
|
+
const options: BaseFetchOptions<
|
|
77
|
+
{ query: ExampleGeneralResource["input"]["query"]; body: ExampleGeneralResource["input"]["body"] },
|
|
78
|
+
{ type: "json"; data: never }
|
|
79
|
+
> = {
|
|
80
|
+
baseUrl: "https://api.example.com",
|
|
81
|
+
path: "/users",
|
|
82
|
+
method: "get",
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
super(options)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async getJson(): Promise<never> {
|
|
89
|
+
await Promise.resolve()
|
|
90
|
+
throw new TypeError("fetch failed")
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async getText(): Promise<never> {
|
|
94
|
+
await Promise.resolve()
|
|
95
|
+
throw new Error("Unused in GeneralRequest tests.")
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async getBlob(): Promise<never> {
|
|
99
|
+
await Promise.resolve()
|
|
100
|
+
throw new Error("Unused in GeneralRequest tests.")
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async getArrayBuffer(): Promise<never> {
|
|
104
|
+
await Promise.resolve()
|
|
105
|
+
throw new Error("Unused in GeneralRequest tests.")
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async getStream(): Promise<never> {
|
|
109
|
+
await Promise.resolve()
|
|
110
|
+
throw new Error("Unused in GeneralRequest tests.")
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
test("GeneralRequest.request keeps success outputs and generates unknown patch outputs for failures", async () => {
|
|
115
|
+
let fetchFactoryCallCount: number = 0
|
|
116
|
+
const fetchFactory = (): TestGeneralResolvedFetch | TestGeneralRejectingFetch => {
|
|
117
|
+
fetchFactoryCallCount = fetchFactoryCallCount + 1
|
|
118
|
+
|
|
119
|
+
if (fetchFactoryCallCount === 1) {
|
|
120
|
+
return new TestGeneralResolvedFetch()
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return new TestGeneralRejectingFetch()
|
|
124
|
+
}
|
|
125
|
+
const request = new GeneralRequest<ExampleGeneralResource>({
|
|
126
|
+
fetchFactory: fetchFactory as unknown as FetchFactory,
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
const successResult = await request.request({
|
|
130
|
+
baseUrl: "https://api.example.com",
|
|
131
|
+
path: "/users",
|
|
132
|
+
method: "get",
|
|
133
|
+
query: {
|
|
134
|
+
page: 1
|
|
135
|
+
},
|
|
136
|
+
})
|
|
137
|
+
const patchResult = await request.request({
|
|
138
|
+
baseUrl: "https://api.example.com",
|
|
139
|
+
path: "/users",
|
|
140
|
+
method: "get",
|
|
141
|
+
query: {
|
|
142
|
+
page: 2
|
|
143
|
+
},
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
expect(successResult).toEqual({
|
|
147
|
+
status: "success",
|
|
148
|
+
data: {
|
|
149
|
+
users: ["neo", "trinity"]
|
|
150
|
+
}
|
|
151
|
+
})
|
|
152
|
+
expect(patchResult).toEqual({
|
|
153
|
+
status: "unknown",
|
|
154
|
+
data: {
|
|
155
|
+
type: "error",
|
|
156
|
+
errorKind: "network",
|
|
157
|
+
error: new TypeError("fetch failed")
|
|
158
|
+
}
|
|
159
|
+
})
|
|
160
|
+
})
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { expect, test } from "vitest"
|
|
2
|
+
|
|
3
|
+
import type { Either } from "#Source/result/index.ts"
|
|
4
|
+
import { Controller, controllerFromEitherType, Left, Right } from "#Source/result/index.ts"
|
|
5
|
+
|
|
6
|
+
test("Controller.isLeft currently overflows through recursive static dispatch", () => {
|
|
7
|
+
expect(() => Controller.isLeft(new Left<string, number>("stop"))).toThrow(RangeError)
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
test("Controller.isRight currently overflows through recursive static dispatch", () => {
|
|
11
|
+
expect(() => Controller.isRight(new Right<string, number>(2))).toThrow(RangeError)
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
test("Controller.value exposes the same pending promise until the controller resolves", async () => {
|
|
15
|
+
const controller = new Controller<string, number>()
|
|
16
|
+
const firstValue = controller.value
|
|
17
|
+
const secondValue = controller.value
|
|
18
|
+
|
|
19
|
+
expect(secondValue).toBe(firstValue)
|
|
20
|
+
|
|
21
|
+
const returned = await controller.returnRight(2)
|
|
22
|
+
const settled = await firstValue
|
|
23
|
+
|
|
24
|
+
expect(settled).toBe(returned)
|
|
25
|
+
expect(settled.isRight()).toBe(true)
|
|
26
|
+
expect(settled.getRight()).toBe(2)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test("Controller.returnRight resolves the controller with a Right value", async () => {
|
|
30
|
+
const controller = new Controller<string, number>()
|
|
31
|
+
|
|
32
|
+
const returned = await controller.returnRight(2)
|
|
33
|
+
const settled = await controller.value
|
|
34
|
+
|
|
35
|
+
expect(returned).toBeInstanceOf(Right)
|
|
36
|
+
expect(returned).toBe(settled)
|
|
37
|
+
expect(returned.isRight()).toBe(true)
|
|
38
|
+
expect(returned.getRight()).toBe(2)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test("Controller.returnLeft resolves the controller with a Left value", async () => {
|
|
42
|
+
const controller = new Controller<string, number>()
|
|
43
|
+
|
|
44
|
+
const returned = await controller.returnLeft("stop")
|
|
45
|
+
const settled = await controller.value
|
|
46
|
+
|
|
47
|
+
expect(returned).toBeInstanceOf(Left)
|
|
48
|
+
expect(returned).toBe(settled)
|
|
49
|
+
expect(returned.isLeft()).toBe(true)
|
|
50
|
+
expect(returned.getLeft()).toBe("stop")
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
test("Controller.throw resolves the controller with a Left value and rejects with the same Left", async () => {
|
|
54
|
+
const controller = new Controller<string, number>()
|
|
55
|
+
let caught: unknown
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
await controller.throw("stop")
|
|
59
|
+
} catch (error) {
|
|
60
|
+
caught = error
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const settled = await controller.value
|
|
64
|
+
|
|
65
|
+
expect(caught).toBeInstanceOf(Left)
|
|
66
|
+
expect(caught).toBe(settled)
|
|
67
|
+
expect(settled.isLeft()).toBe(true)
|
|
68
|
+
expect(settled.getLeft()).toBe("stop")
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
test("controllerFromEitherType creates a controller that matches the Either shape", async () => {
|
|
72
|
+
const controller = controllerFromEitherType<Either<string, number>>()
|
|
73
|
+
const typedController: Controller<string, number> = controller
|
|
74
|
+
|
|
75
|
+
const returned = await typedController.returnRight(3)
|
|
76
|
+
const settled = await controller.value
|
|
77
|
+
|
|
78
|
+
expect(controller).toBeInstanceOf(Controller)
|
|
79
|
+
expect(returned).toBe(settled)
|
|
80
|
+
expect(returned.isRight()).toBe(true)
|
|
81
|
+
expect(returned.getRight()).toBe(3)
|
|
82
|
+
})
|