@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,74 @@
|
|
|
1
|
+
import { expect, test, vi } from "vitest"
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
scheduleMacroTask,
|
|
5
|
+
scheduleMacroTaskSimple,
|
|
6
|
+
scheduleMicroTask,
|
|
7
|
+
scheduleMicroTaskSimple,
|
|
8
|
+
} from "#Source/basic/index.ts"
|
|
9
|
+
|
|
10
|
+
test("scheduleMicroTaskSimple schedules a task in the microtask queue", async () => {
|
|
11
|
+
const values: string[] = []
|
|
12
|
+
|
|
13
|
+
scheduleMicroTaskSimple(() => {
|
|
14
|
+
values.push("micro")
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
expect(values).toEqual([])
|
|
18
|
+
|
|
19
|
+
await Promise.resolve()
|
|
20
|
+
|
|
21
|
+
expect(values).toEqual(["micro"])
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
test("scheduleMacroTaskSimple schedules a task in the macrotask queue", async () => {
|
|
25
|
+
vi.useFakeTimers()
|
|
26
|
+
const values: string[] = []
|
|
27
|
+
|
|
28
|
+
scheduleMacroTaskSimple(() => {
|
|
29
|
+
values.push("macro")
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
expect(values).toEqual([])
|
|
33
|
+
|
|
34
|
+
await vi.runAllTimersAsync()
|
|
35
|
+
|
|
36
|
+
expect(values).toEqual(["macro"])
|
|
37
|
+
vi.useRealTimers()
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test("scheduleMicroTask schedules async-capable tasks in the microtask queue", async () => {
|
|
41
|
+
const values: string[] = []
|
|
42
|
+
|
|
43
|
+
scheduleMicroTask({
|
|
44
|
+
task: () => {
|
|
45
|
+
values.push("micro")
|
|
46
|
+
},
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
expect(values).toEqual([])
|
|
50
|
+
|
|
51
|
+
await Promise.resolve()
|
|
52
|
+
await Promise.resolve()
|
|
53
|
+
|
|
54
|
+
expect(values).toEqual(["micro"])
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test("scheduleMacroTask schedules async-capable tasks with timeout", async () => {
|
|
58
|
+
vi.useFakeTimers()
|
|
59
|
+
const values: string[] = []
|
|
60
|
+
|
|
61
|
+
scheduleMacroTask({
|
|
62
|
+
timeout: 25,
|
|
63
|
+
task: () => {
|
|
64
|
+
values.push("macro")
|
|
65
|
+
},
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
await vi.advanceTimersByTimeAsync(24)
|
|
69
|
+
expect(values).toEqual([])
|
|
70
|
+
|
|
71
|
+
await vi.advanceTimersByTimeAsync(1)
|
|
72
|
+
expect(values).toEqual(["macro"])
|
|
73
|
+
vi.useRealTimers()
|
|
74
|
+
})
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { expect, test } from "vitest"
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
streamConsumeInMacroTask,
|
|
5
|
+
streamConsumeInMicroTask,
|
|
6
|
+
streamFromArrayEager,
|
|
7
|
+
streamFromArrayLazy,
|
|
8
|
+
streamTransformInMacroTask,
|
|
8
9
|
} from "#Source/basic/index.ts"
|
|
9
10
|
|
|
10
|
-
test("
|
|
11
|
+
test("streamFromArrayEager creates a readable stream from array values", async () => {
|
|
11
12
|
const source = [1, 2, 3]
|
|
12
13
|
const values: number[] = []
|
|
13
14
|
|
|
14
|
-
const stream =
|
|
15
|
+
const stream = streamFromArrayEager(source)
|
|
15
16
|
for await (const value of stream) {
|
|
16
17
|
values.push(value)
|
|
17
18
|
}
|
|
@@ -19,18 +20,38 @@ test("streamFromArray creates a readable stream from array values", async () =>
|
|
|
19
20
|
expect(values).toEqual(source)
|
|
20
21
|
})
|
|
21
22
|
|
|
22
|
-
test("
|
|
23
|
+
test("streamFromArrayLazy creates a lazily consumed readable stream", async () => {
|
|
24
|
+
const source = [1, 2, 3]
|
|
25
|
+
const stream = streamFromArrayLazy(source)
|
|
26
|
+
const reader = stream.getReader()
|
|
27
|
+
|
|
28
|
+
const firstChunk = await reader.read()
|
|
29
|
+
const secondChunk = await reader.read()
|
|
30
|
+
const thirdChunk = await reader.read()
|
|
31
|
+
const doneChunk = await reader.read()
|
|
32
|
+
|
|
33
|
+
expect(firstChunk).toEqual({ done: false, value: 1 })
|
|
34
|
+
expect(secondChunk).toEqual({ done: false, value: 2 })
|
|
35
|
+
expect(thirdChunk).toEqual({ done: false, value: 3 })
|
|
36
|
+
expect(doneChunk).toEqual({ done: true, value: undefined })
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test("streamConsumeInMicroTask consumes stream and handles callback errors", async () => {
|
|
23
40
|
const consumed: number[] = []
|
|
24
41
|
let doneCalled = false
|
|
25
42
|
|
|
26
|
-
await
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
43
|
+
await new Promise<void>((resolve, reject) => {
|
|
44
|
+
streamConsumeInMicroTask<number>({
|
|
45
|
+
readableStream: streamFromArrayEager([1, 2, 3]),
|
|
46
|
+
onValue: (chunk) => {
|
|
47
|
+
consumed.push(chunk)
|
|
48
|
+
},
|
|
49
|
+
onDone: () => {
|
|
50
|
+
doneCalled = true
|
|
51
|
+
resolve()
|
|
52
|
+
},
|
|
53
|
+
onError: reject,
|
|
54
|
+
})
|
|
34
55
|
})
|
|
35
56
|
|
|
36
57
|
expect(consumed).toEqual([1, 2, 3])
|
|
@@ -38,30 +59,33 @@ test("streamConsumeInSyncMacroTask consumes stream and handles callback errors",
|
|
|
38
59
|
|
|
39
60
|
let errorMessage = ""
|
|
40
61
|
let doneCalledInErrorCase = false
|
|
41
|
-
await
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
62
|
+
await new Promise<void>((resolve) => {
|
|
63
|
+
streamConsumeInMicroTask<number>({
|
|
64
|
+
readableStream: streamFromArrayEager([1, 2]),
|
|
65
|
+
onValue: (chunk) => {
|
|
66
|
+
if (chunk === 2) {
|
|
67
|
+
throw new Error("boom")
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
onDone: () => {
|
|
71
|
+
doneCalledInErrorCase = true
|
|
72
|
+
},
|
|
73
|
+
onError: (error) => {
|
|
74
|
+
errorMessage = error.message
|
|
75
|
+
resolve()
|
|
76
|
+
},
|
|
77
|
+
})
|
|
54
78
|
})
|
|
55
79
|
|
|
56
80
|
expect(errorMessage).toContain("boom")
|
|
57
81
|
expect(doneCalledInErrorCase).toBe(false)
|
|
58
82
|
})
|
|
59
83
|
|
|
60
|
-
test("
|
|
84
|
+
test("streamConsumeInMacroTask consumes stream and forwards callback failures", async () => {
|
|
61
85
|
const consumed: number[] = []
|
|
62
86
|
await new Promise<void>((resolve, reject) => {
|
|
63
|
-
|
|
64
|
-
readableStream:
|
|
87
|
+
streamConsumeInMacroTask<number>({
|
|
88
|
+
readableStream: streamFromArrayEager([1, 2, 3]),
|
|
65
89
|
onValue: (chunk) => {
|
|
66
90
|
consumed.push(chunk)
|
|
67
91
|
},
|
|
@@ -78,8 +102,8 @@ test("streamConsumeInAsyncMacroTask consumes stream and forwards callback failur
|
|
|
78
102
|
|
|
79
103
|
let errorMessage = ""
|
|
80
104
|
await new Promise<void>((resolve, reject) => {
|
|
81
|
-
|
|
82
|
-
readableStream:
|
|
105
|
+
streamConsumeInMacroTask<number>({
|
|
106
|
+
readableStream: streamFromArrayEager([1]),
|
|
83
107
|
onValue: () => {
|
|
84
108
|
throw new Error("async-boom")
|
|
85
109
|
},
|
|
@@ -96,13 +120,13 @@ test("streamConsumeInAsyncMacroTask consumes stream and forwards callback failur
|
|
|
96
120
|
expect(errorMessage).toContain("async-boom")
|
|
97
121
|
})
|
|
98
122
|
|
|
99
|
-
test("
|
|
123
|
+
test("streamTransformInMacroTask transforms values and handles invalid inputs/errors", async () => {
|
|
100
124
|
expect(() => {
|
|
101
|
-
|
|
125
|
+
streamTransformInMacroTask<number, number>({})
|
|
102
126
|
}).toThrow("Either readableStream or reader must be provided")
|
|
103
127
|
|
|
104
|
-
const transformed =
|
|
105
|
-
readableStream:
|
|
128
|
+
const transformed = streamTransformInMacroTask<number, number>({
|
|
129
|
+
readableStream: streamFromArrayEager([1, 2, 3]),
|
|
106
130
|
onChunk: (chunk, controller) => {
|
|
107
131
|
if (chunk.done === true) {
|
|
108
132
|
controller.close()
|
|
@@ -117,4 +141,33 @@ test("streamTransformInAsyncMacroTask transforms values and handles invalid inpu
|
|
|
117
141
|
values.push(value)
|
|
118
142
|
}
|
|
119
143
|
expect(values).toEqual([10, 20, 30])
|
|
144
|
+
|
|
145
|
+
const reader = streamFromArrayEager([4]).getReader()
|
|
146
|
+
const transformedFromReader = streamTransformInMacroTask<number, number>({
|
|
147
|
+
reader,
|
|
148
|
+
onChunk: (chunk, controller) => {
|
|
149
|
+
if (chunk.done === true) {
|
|
150
|
+
controller.close()
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
controller.enqueue(chunk.value + 1)
|
|
154
|
+
},
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
expect(await Array.fromAsync(transformedFromReader)).toEqual([5])
|
|
158
|
+
|
|
159
|
+
let refinedError: Error | undefined
|
|
160
|
+
const errored = streamTransformInMacroTask<number, number>({
|
|
161
|
+
readableStream: streamFromArrayEager([1]),
|
|
162
|
+
onChunk: () => {
|
|
163
|
+
throw new Error("transform-boom")
|
|
164
|
+
},
|
|
165
|
+
onError: (error) => {
|
|
166
|
+
refinedError = new Error(`wrapped:${error.message}`)
|
|
167
|
+
return refinedError
|
|
168
|
+
},
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
await expect(Array.fromAsync(errored)).rejects.toThrow("wrapped:Error: transform-boom")
|
|
172
|
+
expect(refinedError?.message).toBe("wrapped:Error: transform-boom")
|
|
120
173
|
})
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { expect, test, vi } from "vitest"
|
|
2
|
+
|
|
3
|
+
import { generateApiKey, maskApiKey } from "#Source/credential/index.ts"
|
|
4
|
+
|
|
5
|
+
test("generateApiKey creates a stable sk-prefixed key shape", () => {
|
|
6
|
+
vi.spyOn(crypto, "getRandomValues").mockImplementation(buffer => {
|
|
7
|
+
if (buffer === null) {
|
|
8
|
+
return buffer
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const target = buffer as Uint8Array
|
|
12
|
+
target.forEach((_, index) => {
|
|
13
|
+
target[index] = index
|
|
14
|
+
})
|
|
15
|
+
return buffer
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const apiKey = generateApiKey({})
|
|
19
|
+
const customApiKey = generateApiKey({ prefix: "pk_", alphabet: "ABC123", bodyLength: 6 })
|
|
20
|
+
|
|
21
|
+
expect(apiKey.startsWith("sk-")).toBe(true)
|
|
22
|
+
expect(apiKey).toHaveLength(51)
|
|
23
|
+
expect(apiKey).toMatch(/^sk-[0-9A-Za-z]{48}$/)
|
|
24
|
+
expect(customApiKey).toBe("pk_ABC123")
|
|
25
|
+
expect(() => generateApiKey({ alphabet: "", bodyLength: 6 })).toThrow("Expected alphabet to contain at least one character")
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test("maskApiKey hides the middle of a valid key and rejects malformed input", () => {
|
|
29
|
+
const apiKey = `sk-${"ab"}${"0".repeat(44)}yz`
|
|
30
|
+
const customOptions = { prefix: "pk_", alphabet: "ABC123", bodyLength: 6 } as const
|
|
31
|
+
|
|
32
|
+
expect(maskApiKey(apiKey)).toBe("sk-ab********************************************yz")
|
|
33
|
+
expect(maskApiKey("pk_ABC123", customOptions)).toBe("pk_AB**23")
|
|
34
|
+
expect(() => maskApiKey("not-an-api-key")).toThrow("Invalid API key format")
|
|
35
|
+
expect(() => maskApiKey(`sk-${"#".repeat(48)}`)).toThrow("Invalid API key format")
|
|
36
|
+
})
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { expect, test } from "vitest"
|
|
2
|
+
|
|
3
|
+
import { formatBearerCredential, isBearerCredential, parseBearerCredential } from "#Source/credential/index.ts"
|
|
4
|
+
|
|
5
|
+
test("isBearerCredential recognizes bearer credential strings", () => {
|
|
6
|
+
expect(isBearerCredential("Bearer token-value")).toBe(true)
|
|
7
|
+
expect(isBearerCredential("bearer token-value")).toBe(true)
|
|
8
|
+
expect(isBearerCredential("Basic token-value")).toBe(false)
|
|
9
|
+
expect(isBearerCredential("token-value")).toBe(false)
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
test("formatBearerCredential prefixes a raw token with the Bearer scheme", () => {
|
|
13
|
+
expect(formatBearerCredential("token-value")).toBe("Bearer token-value")
|
|
14
|
+
expect(formatBearerCredential(" token-value ")).toBe("Bearer token-value")
|
|
15
|
+
expect(() => formatBearerCredential(" ")).toThrow("Expected token to contain at least one non-whitespace character")
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test("parseBearerCredential extracts the raw token only from bearer credentials", () => {
|
|
19
|
+
expect(parseBearerCredential("Bearer token-value")).toBe("token-value")
|
|
20
|
+
expect(parseBearerCredential("bearer token-value")).toBe("token-value")
|
|
21
|
+
expect(parseBearerCredential("Basic token-value")).toBeUndefined()
|
|
22
|
+
expect(parseBearerCredential(undefined)).toBeUndefined()
|
|
23
|
+
})
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { expect, test } from "vitest"
|
|
2
|
+
|
|
3
|
+
import { formatBearerCredential, JsonWebToken } from "#Source/credential/index.ts"
|
|
4
|
+
|
|
5
|
+
test("JsonWebToken.signToken creates a compact JWT string", async () => {
|
|
6
|
+
const jwt = new JsonWebToken({ secret: "credential-secret", expiresIn: 3_600 })
|
|
7
|
+
|
|
8
|
+
const token = await jwt.signToken("user-1")
|
|
9
|
+
|
|
10
|
+
expect(token.split(".")).toHaveLength(3)
|
|
11
|
+
expect(typeof token).toBe("string")
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
test("JsonWebToken.parseToken returns userId for valid tokens and undefined for invalid input", async () => {
|
|
15
|
+
const jwt = new JsonWebToken({ secret: "credential-secret", expiresIn: 3_600 })
|
|
16
|
+
const token = await jwt.signToken("user-1")
|
|
17
|
+
|
|
18
|
+
await expect(jwt.parseToken(token)).resolves.toBe("user-1")
|
|
19
|
+
await expect(jwt.parseToken(formatBearerCredential(token))).resolves.toBeUndefined()
|
|
20
|
+
await expect(jwt.parseToken(undefined)).resolves.toBeUndefined()
|
|
21
|
+
await expect(jwt.parseToken("broken.token.value")).resolves.toBeUndefined()
|
|
22
|
+
await expect(new JsonWebToken({ secret: "other-secret", expiresIn: 3_600 }).parseToken(token)).resolves.toBeUndefined()
|
|
23
|
+
})
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { expect, test, vi } from "vitest"
|
|
2
|
+
|
|
3
|
+
import { Password } from "#Source/credential/index.ts"
|
|
4
|
+
|
|
5
|
+
test("Password.generateSalt returns a hexadecimal salt string of the requested byte length", () => {
|
|
6
|
+
vi.spyOn(crypto, "getRandomValues").mockImplementation(buffer => {
|
|
7
|
+
if (buffer === null) {
|
|
8
|
+
return buffer
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const target = buffer as Uint8Array
|
|
12
|
+
target.fill(0xAB)
|
|
13
|
+
return buffer
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
const salt = Password.generateSalt(8)
|
|
17
|
+
|
|
18
|
+
expect(salt).toBe("abababababababab")
|
|
19
|
+
expect(salt).toHaveLength(16)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test("Password.hashPasswordWithSalt derives a stable PBKDF2-SHA-512 hash for the same input", async () => {
|
|
23
|
+
const salt = "00112233445566778899AABBCCDDEEFF"
|
|
24
|
+
const hash1 = await Password.hashPasswordWithSalt("secret", salt)
|
|
25
|
+
const hash2 = await Password.hashPasswordWithSalt("secret", salt)
|
|
26
|
+
const hash3 = await Password.hashPasswordWithSalt("other", salt)
|
|
27
|
+
|
|
28
|
+
expect(hash1).toHaveLength(128)
|
|
29
|
+
expect(hash1).toMatch(/^[0-9A-F]+$/i)
|
|
30
|
+
expect(hash1).toBe(hash2)
|
|
31
|
+
expect(hash1).not.toBe(hash3)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test("Password.comparePassword distinguishes matching and mismatching passwords", async () => {
|
|
35
|
+
const salt = "00112233445566778899AABBCCDDEEFF"
|
|
36
|
+
const hash = await Password.hashPasswordWithSalt("secret", salt)
|
|
37
|
+
|
|
38
|
+
await expect(Password.comparePassword("secret", hash, salt)).resolves.toBe(true)
|
|
39
|
+
await expect(Password.comparePassword("other", hash, salt)).resolves.toBe(false)
|
|
40
|
+
})
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { expect, test } from "vitest"
|
|
2
|
+
|
|
3
|
+
import { Cron } from "#Source/cron/index.ts"
|
|
4
|
+
|
|
5
|
+
const internalCreateFutureMinute = (minutesAhead: number): Date => {
|
|
6
|
+
const date = new Date(Date.now() + minutesAhead * 60_000)
|
|
7
|
+
date.setSeconds(0, 0)
|
|
8
|
+
return date
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const internalCreatePastMinute = (minutesAgo: number): Date => {
|
|
12
|
+
const date = new Date(Date.now() - minutesAgo * 60_000)
|
|
13
|
+
date.setSeconds(0, 0)
|
|
14
|
+
return date
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
test("Cron creates schedule models from both cron expressions and one-shot dates", () => {
|
|
18
|
+
const expressionCron = new Cron({ pattern: "*/5 * * * *" })
|
|
19
|
+
const singleShotCron = new Cron({ pattern: internalCreateFutureMinute(5) })
|
|
20
|
+
|
|
21
|
+
expect(expressionCron).toBeInstanceOf(Cron)
|
|
22
|
+
expect(singleShotCron).toBeInstanceOf(Cron)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
test("Cron.prevRun returns undefined before a one-shot schedule is reached", () => {
|
|
26
|
+
const scheduledAt = internalCreateFutureMinute(5)
|
|
27
|
+
const cron = new Cron({ pattern: scheduledAt })
|
|
28
|
+
|
|
29
|
+
expect(cron.prevRun()).toBeUndefined()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test("Cron.prevRuns returns historical one-shot runs that have already happened", () => {
|
|
33
|
+
const scheduledAt = internalCreatePastMinute(5)
|
|
34
|
+
const cron = new Cron({ pattern: scheduledAt })
|
|
35
|
+
const previousRuns = cron.prevRuns(2)
|
|
36
|
+
|
|
37
|
+
expect(previousRuns).toHaveLength(1)
|
|
38
|
+
expect(previousRuns[0]?.getTime()).toBe(scheduledAt.getTime())
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test("Cron.currentRun stays undefined when the model is only queried and not executing a task", () => {
|
|
42
|
+
const cron = new Cron({ pattern: internalCreateFutureMinute(5) })
|
|
43
|
+
|
|
44
|
+
expect(cron.currentRun()).toBeUndefined()
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test("Cron.nextRun returns the next upcoming one-shot run", () => {
|
|
48
|
+
const scheduledAt = internalCreateFutureMinute(5)
|
|
49
|
+
const cron = new Cron({ pattern: scheduledAt })
|
|
50
|
+
|
|
51
|
+
expect(cron.nextRun()?.getTime()).toBe(scheduledAt.getTime())
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test("Cron.nextRuns returns upcoming expression runs in ascending order", () => {
|
|
55
|
+
const cron = new Cron({ pattern: "*/5 * * * *" })
|
|
56
|
+
const nextRuns = cron.nextRuns(2)
|
|
57
|
+
|
|
58
|
+
expect(nextRuns).toHaveLength(2)
|
|
59
|
+
expect(nextRuns[0]?.getTime()).toBeLessThan(nextRuns[1]?.getTime() ?? 0)
|
|
60
|
+
expect(cron.match(nextRuns[0]!)).toBe(true)
|
|
61
|
+
expect(cron.match(nextRuns[1]!)).toBe(true)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
test("Cron.match reflects cron-expression semantics including domAndDow behavior", () => {
|
|
65
|
+
const yearlyCron = new Cron({ pattern: "0 0 1 1 *" })
|
|
66
|
+
const andCron = new Cron({ pattern: "0 0 1 * MON", domAndDow: true })
|
|
67
|
+
const orCron = new Cron({ pattern: "0 0 1 * MON", domAndDow: false })
|
|
68
|
+
const yearlyHit = new Date(2_026, 0, 1, 0, 0, 0, 0)
|
|
69
|
+
const yearlyMiss = new Date(2_026, 0, 1, 0, 1, 0, 0)
|
|
70
|
+
const mondayFirst = new Date(2_026, 5, 1, 0, 0, 0, 0)
|
|
71
|
+
const mondayNotFirst = new Date(2_026, 5, 8, 0, 0, 0, 0)
|
|
72
|
+
const firstNotMonday = new Date(2_026, 8, 1, 0, 0, 0, 0)
|
|
73
|
+
|
|
74
|
+
expect(yearlyCron.match(yearlyHit)).toBe(true)
|
|
75
|
+
expect(yearlyCron.match(yearlyMiss)).toBe(false)
|
|
76
|
+
|
|
77
|
+
expect(andCron.match(mondayFirst)).toBe(true)
|
|
78
|
+
expect(andCron.match(mondayNotFirst)).toBe(false)
|
|
79
|
+
expect(andCron.match(firstNotMonday)).toBe(false)
|
|
80
|
+
|
|
81
|
+
expect(orCron.match(mondayFirst)).toBe(true)
|
|
82
|
+
expect(orCron.match(mondayNotFirst)).toBe(true)
|
|
83
|
+
expect(orCron.match(firstNotMonday)).toBe(true)
|
|
84
|
+
})
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { expect, test, vi } from "vitest"
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type { BuildEvents, ClassEventProxyOptions } from "#Source/event/index.ts"
|
|
4
4
|
import {
|
|
5
5
|
ClassEventProxy,
|
|
6
6
|
EventManager,
|
|
7
7
|
} from "#Source/event/index.ts"
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
type TestEvents = BuildEvents<{
|
|
10
10
|
change: (value: number) => void
|
|
11
11
|
close: (reason: string) => void
|
|
12
|
-
}
|
|
12
|
+
}>
|
|
13
13
|
|
|
14
14
|
interface TestTarget {
|
|
15
15
|
events: EventManager<TestEvents>
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { expect, test, vi } from "vitest"
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type { BuildEvents } from "#Source/event/index.ts"
|
|
4
4
|
import { EventManager } from "#Source/event/index.ts"
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
type TestEvents = BuildEvents<{
|
|
7
7
|
ready: (value: number) => void | Promise<void>
|
|
8
8
|
done: (label: string) => void | Promise<void>
|
|
9
|
-
}
|
|
9
|
+
}>
|
|
10
10
|
const createEventManager = (): EventManager<TestEvents> => {
|
|
11
11
|
return new EventManager<TestEvents>()
|
|
12
12
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { expect, test, vi } from "vitest"
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type { BuildEvents, InstanceEventProxyOptions } from "#Source/event/index.ts"
|
|
4
4
|
import {
|
|
5
5
|
EventManager,
|
|
6
6
|
InstanceEventProxy,
|
|
7
7
|
} from "#Source/event/index.ts"
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
type TestEvents = BuildEvents<{
|
|
10
10
|
change: (value: number) => void
|
|
11
11
|
error: (message: string) => void
|
|
12
|
-
}
|
|
12
|
+
}>
|
|
13
13
|
|
|
14
14
|
const createInstanceEventProxyHarness = (options?: {
|
|
15
15
|
onSubscriberError?: (error: unknown) => void
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { expect, test, vi } from "vitest"
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
createTaggedError,
|
|
5
|
+
isTaggedError,
|
|
6
|
+
} from "#Source/exception/index.ts"
|
|
7
|
+
|
|
8
|
+
test("createTaggedError creates tagged errors with inferred message, cause and serialization", () => {
|
|
9
|
+
const dateNowSpy = vi.spyOn(Date, "now").mockReturnValue(123)
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
const ExampleError = createTaggedError("Example")<{
|
|
13
|
+
message: string
|
|
14
|
+
cause: Error
|
|
15
|
+
extra: number
|
|
16
|
+
}>()
|
|
17
|
+
const MessageOnlyError = createTaggedError("MessageOnly")<string>()
|
|
18
|
+
const cause = new Error("root")
|
|
19
|
+
|
|
20
|
+
cause.stack = "Error: root\n at cause"
|
|
21
|
+
|
|
22
|
+
const example1 = new ExampleError({
|
|
23
|
+
message: "boom",
|
|
24
|
+
cause,
|
|
25
|
+
extra: 1,
|
|
26
|
+
})
|
|
27
|
+
const example2 = new MessageOnlyError("plain-message")
|
|
28
|
+
const example3 = ExampleError.is(example1)
|
|
29
|
+
const example4 = ExampleError.is(
|
|
30
|
+
new (createTaggedError("Example")<{ message: string }>())({
|
|
31
|
+
message: "compatible",
|
|
32
|
+
}),
|
|
33
|
+
)
|
|
34
|
+
const example5 = example1.toJSON()
|
|
35
|
+
|
|
36
|
+
expect(example1).toBeInstanceOf(Error)
|
|
37
|
+
expect(example1.name).toBe("Example")
|
|
38
|
+
expect(example1.tag).toBe("Example")
|
|
39
|
+
expect(example1.data.extra).toBe(1)
|
|
40
|
+
expect(example1.message).toBe("boom")
|
|
41
|
+
expect(example1.cause).toBe(cause)
|
|
42
|
+
expect(example1.stack).toContain("Caused by: Error: root")
|
|
43
|
+
expect(example2.message).toBe("plain-message")
|
|
44
|
+
expect(example3).toBe(true)
|
|
45
|
+
expect(example4).toBe(true)
|
|
46
|
+
expect(example5).toMatchObject({
|
|
47
|
+
version: "1",
|
|
48
|
+
tag: "Example",
|
|
49
|
+
data: {
|
|
50
|
+
message: "boom",
|
|
51
|
+
cause,
|
|
52
|
+
extra: 1,
|
|
53
|
+
},
|
|
54
|
+
name: "Example",
|
|
55
|
+
message: "boom",
|
|
56
|
+
cause: {
|
|
57
|
+
name: "Error",
|
|
58
|
+
message: "root",
|
|
59
|
+
stack: "Error: root\n at cause",
|
|
60
|
+
},
|
|
61
|
+
timestamp: 123,
|
|
62
|
+
})
|
|
63
|
+
} finally {
|
|
64
|
+
dateNowSpy.mockRestore()
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test("isTaggedError distinguishes tagged errors from plain values", () => {
|
|
69
|
+
const ExampleError = createTaggedError("Example")<{ message: string }>()
|
|
70
|
+
|
|
71
|
+
const example1 = new ExampleError({ message: "boom" })
|
|
72
|
+
const example2 = new Error("boom")
|
|
73
|
+
const example3 = {
|
|
74
|
+
tag: "Example",
|
|
75
|
+
data: { message: "boom" },
|
|
76
|
+
toJSON: () => ({ message: "boom" }),
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
expect(isTaggedError(example1)).toBe(true)
|
|
80
|
+
expect(isTaggedError(example2)).toBe(false)
|
|
81
|
+
expect(isTaggedError(example3)).toBe(false)
|
|
82
|
+
expect(isTaggedError(null)).toBe(false)
|
|
83
|
+
})
|