@planet-matrix/mobius-model 0.6.0 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/oxlint.config.ts +1 -2
  3. package/package.json +29 -17
  4. package/scripts/build.ts +2 -52
  5. package/src/ai/README.md +1 -0
  6. package/src/ai/ai.ts +107 -0
  7. package/src/ai/chat-completion-ai/aihubmix-chat-completion.ts +78 -0
  8. package/src/ai/chat-completion-ai/chat-completion-ai.ts +270 -0
  9. package/src/ai/chat-completion-ai/chat-completion.ts +189 -0
  10. package/src/ai/chat-completion-ai/index.ts +7 -0
  11. package/src/ai/chat-completion-ai/lingyiwanwu-chat-completion.ts +78 -0
  12. package/src/ai/chat-completion-ai/ohmygpt-chat-completion.ts +78 -0
  13. package/src/ai/chat-completion-ai/openai-next-chat-completion.ts +78 -0
  14. package/src/ai/embedding-ai/embedding-ai.ts +63 -0
  15. package/src/ai/embedding-ai/embedding.ts +50 -0
  16. package/src/ai/embedding-ai/index.ts +4 -0
  17. package/src/ai/embedding-ai/openai-next-embedding.ts +23 -0
  18. package/src/ai/index.ts +4 -0
  19. package/src/aio/README.md +100 -0
  20. package/src/aio/content.ts +141 -0
  21. package/src/aio/index.ts +3 -0
  22. package/src/aio/json.ts +127 -0
  23. package/src/aio/prompt.ts +246 -0
  24. package/src/basic/README.md +20 -15
  25. package/src/basic/error.ts +19 -5
  26. package/src/basic/function.ts +2 -2
  27. package/src/basic/index.ts +1 -0
  28. package/src/basic/promise.ts +141 -71
  29. package/src/basic/schedule.ts +111 -0
  30. package/src/basic/stream.ts +135 -25
  31. package/src/credential/README.md +107 -0
  32. package/src/credential/api-key.ts +158 -0
  33. package/src/credential/bearer.ts +73 -0
  34. package/src/credential/index.ts +4 -0
  35. package/src/credential/json-web-token.ts +96 -0
  36. package/src/credential/password.ts +170 -0
  37. package/src/cron/README.md +86 -0
  38. package/src/cron/cron.ts +87 -0
  39. package/src/cron/index.ts +1 -0
  40. package/src/drizzle/README.md +1 -0
  41. package/src/drizzle/drizzle.ts +1 -0
  42. package/src/drizzle/helper.ts +47 -0
  43. package/src/drizzle/index.ts +5 -0
  44. package/src/drizzle/infer.ts +52 -0
  45. package/src/drizzle/kysely.ts +8 -0
  46. package/src/drizzle/pagination.ts +198 -0
  47. package/src/email/README.md +1 -0
  48. package/src/email/index.ts +1 -0
  49. package/src/email/resend.ts +25 -0
  50. package/src/event/class-event-proxy.ts +5 -6
  51. package/src/event/common.ts +13 -3
  52. package/src/event/event-manager.ts +3 -3
  53. package/src/event/instance-event-proxy.ts +5 -6
  54. package/src/event/internal.ts +4 -4
  55. package/src/exception/README.md +28 -19
  56. package/src/exception/error/error.ts +123 -0
  57. package/src/exception/error/index.ts +2 -0
  58. package/src/exception/error/match.ts +38 -0
  59. package/src/exception/error/must-fix.ts +17 -0
  60. package/src/exception/index.ts +2 -0
  61. package/src/file-system/find.ts +53 -0
  62. package/src/file-system/index.ts +2 -0
  63. package/src/file-system/path.ts +76 -0
  64. package/src/file-system/resolve.ts +22 -0
  65. package/src/form/README.md +25 -0
  66. package/src/form/index.ts +1 -0
  67. package/src/form/inputor-controller/base.ts +861 -0
  68. package/src/form/inputor-controller/boolean.ts +39 -0
  69. package/src/form/inputor-controller/file.ts +39 -0
  70. package/src/form/inputor-controller/form.ts +179 -0
  71. package/src/form/inputor-controller/helper.ts +117 -0
  72. package/src/form/inputor-controller/index.ts +17 -0
  73. package/src/form/inputor-controller/multi-select.ts +99 -0
  74. package/src/form/inputor-controller/number.ts +116 -0
  75. package/src/form/inputor-controller/select.ts +109 -0
  76. package/src/form/inputor-controller/text.ts +82 -0
  77. package/src/http/READMD.md +1 -0
  78. package/src/http/api/api-core.ts +84 -0
  79. package/src/http/api/api-handler.ts +79 -0
  80. package/src/http/api/api-host.ts +47 -0
  81. package/src/http/api/api-result.ts +56 -0
  82. package/src/http/api/api-schema.ts +154 -0
  83. package/src/http/api/api-server.ts +130 -0
  84. package/src/http/api/api-test.ts +142 -0
  85. package/src/http/api/api-type.ts +34 -0
  86. package/src/http/api/api.ts +81 -0
  87. package/src/http/api/index.ts +11 -0
  88. package/src/http/api-adapter/api-core-node-http.ts +260 -0
  89. package/src/http/api-adapter/api-host-node-http.ts +156 -0
  90. package/src/http/api-adapter/api-result-arktype.ts +294 -0
  91. package/src/http/api-adapter/api-result-zod.ts +286 -0
  92. package/src/http/api-adapter/index.ts +5 -0
  93. package/src/http/bin/gen-api-list/gen-api-list.ts +126 -0
  94. package/src/http/bin/gen-api-list/index.ts +1 -0
  95. package/src/http/bin/gen-api-test/gen-api-test.ts +136 -0
  96. package/src/http/bin/gen-api-test/index.ts +1 -0
  97. package/src/http/bin/gen-api-type/calc-code.ts +25 -0
  98. package/src/http/bin/gen-api-type/gen-api-type.ts +127 -0
  99. package/src/http/bin/gen-api-type/index.ts +2 -0
  100. package/src/http/bin/index.ts +2 -0
  101. package/src/http/index.ts +3 -0
  102. package/src/huawei/README.md +1 -0
  103. package/src/huawei/index.ts +2 -0
  104. package/src/huawei/moderation/index.ts +1 -0
  105. package/src/huawei/moderation/moderation.ts +355 -0
  106. package/src/huawei/obs/esdk-obs-nodejs.d.ts +87 -0
  107. package/src/huawei/obs/index.ts +1 -0
  108. package/src/huawei/obs/obs.ts +42 -0
  109. package/src/index.ts +21 -2
  110. package/src/json/README.md +92 -0
  111. package/src/json/index.ts +1 -0
  112. package/src/json/repair.ts +18 -0
  113. package/src/log/logger.ts +15 -4
  114. package/src/openai/README.md +1 -0
  115. package/src/openai/index.ts +1 -0
  116. package/src/openai/openai.ts +509 -0
  117. package/src/orchestration/README.md +9 -7
  118. package/src/orchestration/dispatching/dispatcher.ts +83 -0
  119. package/src/orchestration/dispatching/index.ts +2 -0
  120. package/src/orchestration/dispatching/selector/base-selector.ts +39 -0
  121. package/src/orchestration/dispatching/selector/down-count-selector.ts +119 -0
  122. package/src/orchestration/dispatching/selector/index.ts +2 -0
  123. package/src/orchestration/index.ts +2 -0
  124. package/src/orchestration/scheduling/index.ts +2 -0
  125. package/src/orchestration/scheduling/scheduler.ts +103 -0
  126. package/src/orchestration/scheduling/task.ts +32 -0
  127. package/src/random/README.md +8 -7
  128. package/src/random/base.ts +66 -0
  129. package/src/random/index.ts +5 -1
  130. package/src/random/random-boolean.ts +40 -0
  131. package/src/random/random-integer.ts +60 -0
  132. package/src/random/random-number.ts +72 -0
  133. package/src/random/random-string.ts +66 -0
  134. package/src/request/README.md +108 -0
  135. package/src/request/fetch/base.ts +108 -0
  136. package/src/request/fetch/browser.ts +280 -0
  137. package/src/request/fetch/general.ts +20 -0
  138. package/src/request/fetch/index.ts +4 -0
  139. package/src/request/fetch/nodejs.ts +280 -0
  140. package/src/request/index.ts +2 -0
  141. package/src/request/request/base.ts +246 -0
  142. package/src/request/request/general.ts +63 -0
  143. package/src/request/request/index.ts +3 -0
  144. package/src/request/request/resource.ts +68 -0
  145. package/src/result/README.md +4 -0
  146. package/src/result/controller.ts +58 -0
  147. package/src/result/either.ts +363 -0
  148. package/src/result/generator.ts +168 -0
  149. package/src/result/index.ts +3 -0
  150. package/src/route/README.md +105 -0
  151. package/src/route/adapter/browser.ts +122 -0
  152. package/src/route/adapter/driver.ts +56 -0
  153. package/src/route/adapter/index.ts +2 -0
  154. package/src/route/index.ts +3 -0
  155. package/src/route/router/index.ts +2 -0
  156. package/src/route/router/route.ts +630 -0
  157. package/src/route/router/router.ts +1641 -0
  158. package/src/route/uri/hash.ts +307 -0
  159. package/src/route/uri/index.ts +7 -0
  160. package/src/route/uri/pathname.ts +376 -0
  161. package/src/route/uri/search.ts +412 -0
  162. package/src/service/README.md +1 -0
  163. package/src/service/index.ts +1 -0
  164. package/src/service/service.ts +110 -0
  165. package/src/socket/README.md +105 -0
  166. package/src/socket/client/index.ts +2 -0
  167. package/src/socket/client/socket-unit.ts +658 -0
  168. package/src/socket/client/socket.ts +203 -0
  169. package/src/socket/common/index.ts +2 -0
  170. package/src/socket/common/socket-unit-common.ts +23 -0
  171. package/src/socket/common/socket-unit-heartbeat.ts +427 -0
  172. package/src/socket/index.ts +3 -0
  173. package/src/socket/server/index.ts +3 -0
  174. package/src/socket/server/server.ts +183 -0
  175. package/src/socket/server/socket-unit.ts +448 -0
  176. package/src/socket/server/socket.ts +264 -0
  177. package/src/storage/table.ts +3 -3
  178. package/src/timer/expiration/expiration-manager.ts +3 -3
  179. package/src/timer/expiration/remaining-manager.ts +3 -3
  180. package/src/tube/README.md +99 -0
  181. package/src/tube/helper.ts +137 -0
  182. package/src/tube/index.ts +2 -0
  183. package/src/tube/tube.ts +880 -0
  184. package/src/weixin/README.md +1 -0
  185. package/src/weixin/index.ts +2 -0
  186. package/src/weixin/official-account/authorization.ts +157 -0
  187. package/src/weixin/official-account/index.ts +2 -0
  188. package/src/weixin/official-account/js-api.ts +132 -0
  189. package/src/weixin/open/index.ts +1 -0
  190. package/src/weixin/open/oauth2.ts +131 -0
  191. package/tests/unit/ai/ai.spec.ts +85 -0
  192. package/tests/unit/aio/content.spec.ts +105 -0
  193. package/tests/unit/aio/json.spec.ts +146 -0
  194. package/tests/unit/aio/prompt.spec.ts +111 -0
  195. package/tests/unit/basic/error.spec.ts +16 -4
  196. package/tests/unit/basic/promise.spec.ts +158 -50
  197. package/tests/unit/basic/schedule.spec.ts +74 -0
  198. package/tests/unit/basic/stream.spec.ts +90 -37
  199. package/tests/unit/credential/api-key.spec.ts +36 -0
  200. package/tests/unit/credential/bearer.spec.ts +23 -0
  201. package/tests/unit/credential/json-web-token.spec.ts +23 -0
  202. package/tests/unit/credential/password.spec.ts +40 -0
  203. package/tests/unit/cron/cron.spec.ts +84 -0
  204. package/tests/unit/event/class-event-proxy.spec.ts +3 -3
  205. package/tests/unit/event/event-manager.spec.ts +3 -3
  206. package/tests/unit/event/instance-event-proxy.spec.ts +3 -3
  207. package/tests/unit/exception/error/error.spec.ts +83 -0
  208. package/tests/unit/exception/error/match.spec.ts +81 -0
  209. package/tests/unit/form/inputor-controller/base.spec.ts +458 -0
  210. package/tests/unit/form/inputor-controller/boolean.spec.ts +30 -0
  211. package/tests/unit/form/inputor-controller/file.spec.ts +27 -0
  212. package/tests/unit/form/inputor-controller/form.spec.ts +120 -0
  213. package/tests/unit/form/inputor-controller/helper.spec.ts +67 -0
  214. package/tests/unit/form/inputor-controller/multi-select.spec.ts +34 -0
  215. package/tests/unit/form/inputor-controller/number.spec.ts +36 -0
  216. package/tests/unit/form/inputor-controller/select.spec.ts +49 -0
  217. package/tests/unit/form/inputor-controller/text.spec.ts +34 -0
  218. package/tests/unit/http/api/api-core-host.spec.ts +207 -0
  219. package/tests/unit/http/api/api-schema.spec.ts +120 -0
  220. package/tests/unit/http/api/api-server.spec.ts +363 -0
  221. package/tests/unit/http/api/api-test.spec.ts +117 -0
  222. package/tests/unit/http/api/api.spec.ts +121 -0
  223. package/tests/unit/http/api-adapter/node-http.spec.ts +187 -0
  224. package/tests/unit/identifier/uuid.spec.ts +0 -1
  225. package/tests/unit/json/repair.spec.ts +11 -0
  226. package/tests/unit/log/logger.spec.ts +19 -4
  227. package/tests/unit/openai/openai.spec.ts +64 -0
  228. package/tests/unit/orchestration/dispatching/dispatcher.spec.ts +41 -0
  229. package/tests/unit/orchestration/dispatching/selector/down-count-selector.spec.ts +81 -0
  230. package/tests/unit/orchestration/scheduling/scheduler.spec.ts +103 -0
  231. package/tests/unit/random/base.spec.ts +58 -0
  232. package/tests/unit/random/random-boolean.spec.ts +25 -0
  233. package/tests/unit/random/random-integer.spec.ts +32 -0
  234. package/tests/unit/random/random-number.spec.ts +33 -0
  235. package/tests/unit/random/random-string.spec.ts +22 -0
  236. package/tests/unit/request/fetch/browser.spec.ts +222 -0
  237. package/tests/unit/request/fetch/general.spec.ts +43 -0
  238. package/tests/unit/request/fetch/nodejs.spec.ts +225 -0
  239. package/tests/unit/request/request/base.spec.ts +382 -0
  240. package/tests/unit/request/request/general.spec.ts +160 -0
  241. package/tests/unit/result/controller.spec.ts +82 -0
  242. package/tests/unit/result/either.spec.ts +377 -0
  243. package/tests/unit/result/generator.spec.ts +273 -0
  244. package/tests/unit/route/router/route.spec.ts +430 -0
  245. package/tests/unit/route/router/router.spec.ts +407 -0
  246. package/tests/unit/route/uri/hash.spec.ts +72 -0
  247. package/tests/unit/route/uri/pathname.spec.ts +146 -0
  248. package/tests/unit/route/uri/search.spec.ts +107 -0
  249. package/tests/unit/socket/client.spec.ts +208 -0
  250. package/tests/unit/socket/server.spec.ts +133 -0
  251. package/tests/unit/socket/socket-unit-heartbeat.spec.ts +214 -0
  252. package/tests/unit/tube/helper.spec.ts +139 -0
  253. package/tests/unit/tube/tube.spec.ts +501 -0
  254. package/vite.config.ts +2 -1
  255. package/dist/index.js +0 -50
  256. package/dist/index.js.map +0 -209
  257. package/src/random/string.ts +0 -35
  258. package/tests/unit/random/string.spec.ts +0 -11
@@ -0,0 +1,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
- streamConsumeInAsyncMacroTask,
5
- streamConsumeInSyncMacroTask,
6
- streamFromArray,
7
- streamTransformInAsyncMacroTask,
4
+ streamConsumeInMacroTask,
5
+ streamConsumeInMicroTask,
6
+ streamFromArrayEager,
7
+ streamFromArrayLazy,
8
+ streamTransformInMacroTask,
8
9
  } from "#Source/basic/index.ts"
9
10
 
10
- test("streamFromArray creates a readable stream from array values", async () => {
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 = streamFromArray(source)
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("streamConsumeInSyncMacroTask consumes stream and handles callback errors", async () => {
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 streamConsumeInSyncMacroTask<number>({
27
- readableStream: streamFromArray([1, 2, 3]),
28
- onValue: (chunk) => {
29
- consumed.push(chunk)
30
- },
31
- onDone: () => {
32
- doneCalled = true
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 streamConsumeInSyncMacroTask<number>({
42
- readableStream: streamFromArray([1, 2]),
43
- onValue: (chunk) => {
44
- if (chunk === 2) {
45
- throw new Error("boom")
46
- }
47
- },
48
- onDone: () => {
49
- doneCalledInErrorCase = true
50
- },
51
- onError: (error) => {
52
- errorMessage = error.message
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("streamConsumeInAsyncMacroTask consumes stream and forwards callback failures", async () => {
84
+ test("streamConsumeInMacroTask consumes stream and forwards callback failures", async () => {
61
85
  const consumed: number[] = []
62
86
  await new Promise<void>((resolve, reject) => {
63
- streamConsumeInAsyncMacroTask<number>({
64
- readableStream: streamFromArray([1, 2, 3]),
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
- streamConsumeInAsyncMacroTask<number>({
82
- readableStream: streamFromArray([1]),
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("streamTransformInAsyncMacroTask transforms values and handles invalid inputs/errors", async () => {
123
+ test("streamTransformInMacroTask transforms values and handles invalid inputs/errors", async () => {
100
124
  expect(() => {
101
- streamTransformInAsyncMacroTask<number, number>({})
125
+ streamTransformInMacroTask<number, number>({})
102
126
  }).toThrow("Either readableStream or reader must be provided")
103
127
 
104
- const transformed = streamTransformInAsyncMacroTask<number, number>({
105
- readableStream: streamFromArray([1, 2, 3]),
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 { BaseEvents, ClassEventProxyOptions } from "#Source/event/index.ts"
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
- interface TestEvents extends BaseEvents {
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 { BaseEvents } from "#Source/event/index.ts"
3
+ import type { BuildEvents } from "#Source/event/index.ts"
4
4
  import { EventManager } from "#Source/event/index.ts"
5
5
 
6
- interface TestEvents extends BaseEvents {
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 { BaseEvents, InstanceEventProxyOptions } from "#Source/event/index.ts"
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
- interface TestEvents extends BaseEvents {
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
+ })