@planet-matrix/mobius-model 0.9.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 +13 -0
- package/oxlint.config.ts +1 -2
- package/package.json +5 -5
- package/scripts/build.ts +2 -52
- package/src/basic/promise.ts +141 -71
- package/src/drizzle/pagination.ts +0 -2
- package/src/event/class-event-proxy.ts +0 -2
- package/src/event/instance-event-proxy.ts +0 -2
- 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/inputor-controller/base.ts +0 -13
- package/src/form/inputor-controller/form.ts +0 -2
- package/src/http/api/api-type.ts +0 -3
- package/src/http/api-adapter/api-result-arktype.ts +0 -3
- package/src/index.ts +2 -0
- package/src/openai/openai.ts +0 -1
- package/src/request/fetch/browser.ts +0 -5
- package/src/request/fetch/nodejs.ts +0 -5
- package/src/request/request/base.ts +0 -4
- package/src/request/request/general.ts +0 -1
- package/src/result/controller.ts +11 -7
- package/src/result/either.ts +230 -60
- package/src/result/generator.ts +168 -0
- package/src/result/index.ts +1 -0
- package/src/route/router/router.ts +0 -1
- package/src/route/uri/hash.ts +0 -1
- package/src/route/uri/search.ts +0 -1
- package/src/service/README.md +1 -0
- package/src/service/index.ts +1 -0
- package/src/service/service.ts +110 -0
- package/src/socket/client/socket-unit.ts +0 -2
- package/src/socket/server/socket-unit.ts +0 -1
- package/src/tube/helper.ts +0 -1
- package/src/weixin/official-account/authorization.ts +0 -2
- package/src/weixin/official-account/js-api.ts +0 -2
- package/src/weixin/open/oauth2.ts +0 -2
- package/tests/unit/aio/json.spec.ts +0 -1
- package/tests/unit/basic/promise.spec.ts +158 -50
- package/tests/unit/credential/api-key.spec.ts +0 -1
- package/tests/unit/credential/password.spec.ts +0 -1
- package/tests/unit/exception/error/error.spec.ts +83 -0
- package/tests/unit/exception/error/match.spec.ts +81 -0
- package/tests/unit/http/api-adapter/node-http.spec.ts +0 -4
- package/tests/unit/identifier/uuid.spec.ts +0 -1
- package/tests/unit/request/request/base.spec.ts +0 -3
- package/tests/unit/request/request/general.spec.ts +0 -1
- 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 +0 -1
- package/tests/unit/route/uri/pathname.spec.ts +0 -1
- package/tests/unit/socket/server.spec.ts +0 -2
- package/vite.config.ts +2 -1
- package/dist/index.js +0 -720
- package/dist/index.js.map +0 -1005
package/src/exception/README.md
CHANGED
|
@@ -2,49 +2,57 @@
|
|
|
2
2
|
|
|
3
3
|
## Description
|
|
4
4
|
|
|
5
|
-
Exception
|
|
5
|
+
Exception 模块提供围绕异常(exception)的基础模型能力,用于承载宿主环境全局异常入口的监听与标准化,以及其中一类可判别、可匹配、可序列化的结构化错误建模。
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
它关注的是“异常从哪里进入系统、错误如何被稳定表达”,而不是“异常之后如何打印、上报、恢复、吞掉或终止进程”。
|
|
8
8
|
|
|
9
9
|
## For Understanding
|
|
10
10
|
|
|
11
|
-
理解 Exception
|
|
11
|
+
理解 Exception 模块时,更合适的方式是把它视为“异常模型层”,而不是“异常处理策略层”。这个模块当前关注的是异常这一上位概念,其中既包括异常如何从宿主环境进入系统,也包括其中一类适合被稳定建模的异常形式,也就是结构化错误。
|
|
12
12
|
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
- 它负责提供可清理的监听语义与批量组合入口。
|
|
16
|
-
- 它不负责日志发送、监控上报、默认阻止、错误恢复或退出进程等策略决策。
|
|
13
|
+
- 宿主异常入口:负责封装浏览器和 Node.js 暴露出来的全局异常入口,并把原始输入整理成稳定记录。
|
|
14
|
+
- 结构化错误:负责把应用内部需要长期存在的某些异常表达成带判别信息的 Error 变体,以便进行分派、组合和序列化。
|
|
17
15
|
|
|
18
|
-
|
|
16
|
+
之所以可以把它们放在同一模块内,不是因为它们的实现方式相似,而是因为它们都在回答同一类问题:系统中的异常应该以什么稳定模型进入后续流程。前者解决“异常从哪里来”,后者解决“某些异常在系统中应如何被表达与判别”。
|
|
17
|
+
|
|
18
|
+
因此,这个模块并不承担日志发送、监控上报、错误恢复、默认阻止、重试编排或进程退出等策略职责。它只负责把异常整理成更稳定、更清楚、更可组合的输入模型,让上层的 logger、telemetry、workflow、Result/Either 或业务恢复逻辑在其之上继续工作。
|
|
19
|
+
|
|
20
|
+
对于宿主异常入口部分,当前主要覆盖两类运行时:
|
|
19
21
|
|
|
20
22
|
- 浏览器侧异常入口,例如 `error` 事件、`window.onerror` 与 `unhandledrejection`。
|
|
21
23
|
- Node.js 侧异常入口,例如 `uncaughtExceptionMonitor`、`uncaughtException` 与 `unhandledRejection`。
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
对于异常建模中的结构化错误部分,当前采用 Tagged Error 的思路:这类错误既保留原生 Error 的 name、message、stack 与 `cause` 语义,也额外拥有稳定的 `tag` 与 `data` 字段,便于在联合类型中进行穷举或部分匹配。
|
|
24
26
|
|
|
25
27
|
## For Using
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
当应用需要从宿主环境统一接入全局异常,并把这些异常整理成稳定记录时,可以使用这个模块的异常入口能力,把分散在不同运行时 API 上的异常来源收敛成更清楚的订阅接口。
|
|
30
|
+
|
|
31
|
+
当应用需要在模块边界、基础设施层、工作流编排、Result/Either 风格流程或跨层协议中表达一组稳定、可判别、可序列化的异常时,也可以使用这个模块中的结构化错误能力,把其中适合显式建模的一类异常从“临时字符串或松散对象”提升为更清楚的公共模型。
|
|
28
32
|
|
|
29
33
|
当前公共能力大致可以按以下几类理解:
|
|
30
34
|
|
|
31
|
-
- 单一入口监听:用于单独监听浏览器或 Node.js
|
|
32
|
-
-
|
|
35
|
+
- 单一入口监听:用于单独监听浏览器或 Node.js 的某一种全局异常来源。
|
|
36
|
+
- 批量入口监听:用于按运行时一次性安装一组常见异常入口,并返回统一清理函数。
|
|
33
37
|
- 异常记录标准化:用于把浏览器或 Node.js 的原始输入整理成稳定的异常记录结构。
|
|
34
|
-
-
|
|
38
|
+
- Tagged Error 建模:用于定义一类带 `tag`、`data`、`cause` 与序列化能力的结构化异常类型。
|
|
39
|
+
- Tagged Error 匹配:用于按 tag 分派这一类结构化异常的处理逻辑,并在类型层保留分支信息。
|
|
40
|
+
- 类型与清理语义:用于表达异常来源种类、异常记录形状、结构化异常实例能力以及取消监听的统一返回类型。
|
|
35
41
|
|
|
36
|
-
使用时应把它接在 logger
|
|
42
|
+
使用时应把它接在 logger、遥测、监控、恢复策略或业务错误处理的上游,而不是期待它直接替代这些能力。更准确地说,它适合作为异常输入与异常表达的基础层,而不是最终处理层。
|
|
37
43
|
|
|
38
44
|
## For Contributing
|
|
39
45
|
|
|
40
|
-
贡献 Exception
|
|
46
|
+
贡献 Exception 模块时,应优先判断新增能力是否真正扩展了“异常模型”这个问题域,而不是只是在当前实现里顺手添加一个便捷函数。只有当某项能力表达了稳定、可复用、值得长期维护的异常来源语义,或表达了某类异常的稳定建模语义时,它才适合进入这个模块。
|
|
41
47
|
|
|
42
48
|
在扩展时,应优先遵守以下边界:
|
|
43
49
|
|
|
44
|
-
-
|
|
45
|
-
-
|
|
50
|
+
- 公共能力应围绕宿主异常入口、异常记录标准化,以及其中可被稳定表达的结构化异常建模与匹配语义展开。
|
|
51
|
+
- 若新增的是模块内稳定异常模型,且它适合以 Error 形式存在,应优先使用 Tagged Error 形式表达,而不是仅靠裸字符串或无判别字段的对象。
|
|
52
|
+
- 不要在监听逻辑或异常模型中默认执行日志输出、上报请求、`preventDefault`、`process.exit`、重试、恢复或其它强策略动作,除非 API 名称与文档已经明确承诺。
|
|
46
53
|
- 组合入口只负责批量安装监听器与汇总清理,不负责掩盖宿主差异或替调用方做策略决策。
|
|
47
|
-
-
|
|
54
|
+
- Tagged Error 的职责是提供一类稳定异常表达、判别和匹配语义,而不是承载某个单一应用的临时上下文容器。
|
|
55
|
+
- 与 Environment 的关系应保持清楚:Environment 可以帮助判断和获取宿主上下文,Exception 则负责异常入口与异常模型本身。
|
|
48
56
|
|
|
49
57
|
### JSDoc 注释格式要求
|
|
50
58
|
|
|
@@ -82,7 +90,7 @@ Exception 模块提供围绕宿主环境全局异常入口(global exception en
|
|
|
82
90
|
- 保持内部辅助项和内部符号为私有,不要让外部接入依赖临时性的内部结构。
|
|
83
91
|
- 每个模块都应有一个用于重导出所有公共 API 的 Barrel 文件。
|
|
84
92
|
- Barrel 文件应命名为 `index.ts`,放在模块目录根部,并且所有公共 API 都应从该文件导出。
|
|
85
|
-
-
|
|
93
|
+
- 新增公共能力时,应优先检查它是否表达稳定、清楚且值得长期维护的异常模型语义,而不是某段实现细节的便捷暴露;仅在确认需要长期对外承诺时再加入 Barrel 导出。
|
|
86
94
|
|
|
87
95
|
### 测试要求
|
|
88
96
|
|
|
@@ -94,3 +102,4 @@ Exception 模块提供围绕宿主环境全局异常入口(global exception en
|
|
|
94
102
|
- 若该模块不需要测试,必须在说明文件中明确说明该模块不需要测试,并说明理由。一般来说,只有在该模块没有可执行的公共函数、只承载类型层表达,或其语义已被上层模块的测试完整覆盖且重复测试几乎不再带来额外价值时,才适合这样处理。
|
|
95
103
|
- 模块的单元测试文件目录是 `./tests/unit/exception`,若模块包含子模块,则子模块的单元测试文件目录为 `./tests/unit/exception/<sub-module-name>`。
|
|
96
104
|
- 测试应重点覆盖监听注册、清理行为、默认包含的入口集合、记录标准化结果,以及宿主不可用时的失败或空操作语义是否稳定。
|
|
105
|
+
- 若新增 Tagged Error 能力,应重点覆盖 tag 判别、message 与 `cause` 推导、序列化结果,以及按 tag 进行穷举或部分匹配时的返回语义是否稳定。
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
|
|
2
|
+
const isString = (value: unknown): value is string => {
|
|
3
|
+
return typeof value === "string"
|
|
4
|
+
}
|
|
5
|
+
const isObject = (value: unknown): value is object => {
|
|
6
|
+
return typeof value === "object" && value !== null
|
|
7
|
+
}
|
|
8
|
+
const extractMessage = (data: unknown): string | undefined => {
|
|
9
|
+
if (isString(data)) {
|
|
10
|
+
return data
|
|
11
|
+
} else if (isObject(data) && "message" in data && isString(data.message)) {
|
|
12
|
+
return data.message
|
|
13
|
+
} else {
|
|
14
|
+
return undefined
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const extractCause = (data: unknown): unknown => {
|
|
18
|
+
if (isObject(data) && "cause" in data) {
|
|
19
|
+
return data.cause
|
|
20
|
+
} else {
|
|
21
|
+
return undefined
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const formatCauseStack = (cause: unknown): string => {
|
|
25
|
+
if ((cause instanceof Error) === false || cause.stack === undefined) {
|
|
26
|
+
return ""
|
|
27
|
+
}
|
|
28
|
+
const indented = cause.stack.replaceAll("\n", "\n ")
|
|
29
|
+
return `\nCaused by: ${indented}`
|
|
30
|
+
}
|
|
31
|
+
const serializeCause = (cause: unknown): unknown => {
|
|
32
|
+
if (cause instanceof Error) {
|
|
33
|
+
return { name: cause.name, message: cause.message, stack: cause.stack };
|
|
34
|
+
}
|
|
35
|
+
return cause
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface SerializedTaggedError<Tag extends string, Data = unknown> {
|
|
39
|
+
version: "1" // for future compatibility
|
|
40
|
+
tag: Tag
|
|
41
|
+
data: Data
|
|
42
|
+
name: string
|
|
43
|
+
message: string
|
|
44
|
+
cause: unknown | undefined
|
|
45
|
+
stack: string | undefined
|
|
46
|
+
timestamp: number
|
|
47
|
+
}
|
|
48
|
+
export interface TaggedErrorAugments<Tag extends string, Data = unknown> {
|
|
49
|
+
readonly tag: Tag
|
|
50
|
+
readonly data: Data
|
|
51
|
+
toJSON(): SerializedTaggedError<Tag, Data>
|
|
52
|
+
}
|
|
53
|
+
export type TaggedErrorInstance<Tag extends string, Data = unknown> =
|
|
54
|
+
& Error
|
|
55
|
+
& TaggedErrorAugments<Tag, Data>
|
|
56
|
+
export type TaggedErrorConstructor<Tag extends string, Data = unknown> = {
|
|
57
|
+
new(data: Data): TaggedErrorInstance<Tag, Data>
|
|
58
|
+
is(target: unknown): target is TaggedErrorInstance<Tag, Data>
|
|
59
|
+
}
|
|
60
|
+
export const createTaggedError = <const Tag extends string>(tag: Tag) => {
|
|
61
|
+
return <Data = unknown>(): TaggedErrorConstructor<Tag, Data> => {
|
|
62
|
+
return class Impl extends Error {
|
|
63
|
+
readonly tag: Tag
|
|
64
|
+
readonly data: Data
|
|
65
|
+
|
|
66
|
+
constructor(data: Data) {
|
|
67
|
+
const message = extractMessage(data)
|
|
68
|
+
const cause = extractCause(data)
|
|
69
|
+
|
|
70
|
+
super(message, cause !== undefined ? { cause } : undefined)
|
|
71
|
+
Object.setPrototypeOf(this, new.target.prototype)
|
|
72
|
+
this.name = tag
|
|
73
|
+
this.stack = `${this.stack}${formatCauseStack(cause)}`
|
|
74
|
+
|
|
75
|
+
this.tag = tag
|
|
76
|
+
this.data = data
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
static is(target: unknown): target is TaggedErrorInstance<Tag, Data> {
|
|
80
|
+
if (target instanceof Impl) {
|
|
81
|
+
return true
|
|
82
|
+
}
|
|
83
|
+
if (isTaggedError(target) && target.tag === tag) {
|
|
84
|
+
return true
|
|
85
|
+
}
|
|
86
|
+
return false
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
toJSON(): SerializedTaggedError<Tag, Data> {
|
|
90
|
+
return {
|
|
91
|
+
version: "1",
|
|
92
|
+
tag: this.tag,
|
|
93
|
+
data: this.data,
|
|
94
|
+
name: this.name,
|
|
95
|
+
message: this.message,
|
|
96
|
+
cause: serializeCause(this.cause),
|
|
97
|
+
stack: this.stack,
|
|
98
|
+
timestamp: Date.now(),
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export type AnyTaggedError = TaggedErrorInstance<string, unknown>
|
|
106
|
+
export const isTaggedError = (target: unknown): target is AnyTaggedError => {
|
|
107
|
+
if (typeof target !== "object" || target === null) {
|
|
108
|
+
return false
|
|
109
|
+
}
|
|
110
|
+
if (target instanceof Error === false) {
|
|
111
|
+
return false
|
|
112
|
+
}
|
|
113
|
+
if (!("tag" in target) || typeof target.tag !== "string") {
|
|
114
|
+
return false
|
|
115
|
+
}
|
|
116
|
+
if (!("data" in target)) {
|
|
117
|
+
return false
|
|
118
|
+
}
|
|
119
|
+
if (!("toJSON" in target) || typeof target.toJSON !== "function") {
|
|
120
|
+
return false
|
|
121
|
+
}
|
|
122
|
+
return true
|
|
123
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { AnyTaggedError } from "./error.ts"
|
|
2
|
+
|
|
3
|
+
export type MatchTaggedErrorHandlers<E extends AnyTaggedError, R> = {
|
|
4
|
+
[Tag in E["tag"]]: (error: Extract<E, { tag: Tag }>) => R
|
|
5
|
+
}
|
|
6
|
+
export const matchTaggedError = <E extends AnyTaggedError, R>(
|
|
7
|
+
taggedError: E,
|
|
8
|
+
handlers: MatchTaggedErrorHandlers<E, R>,
|
|
9
|
+
): R => {
|
|
10
|
+
const handler = handlers[taggedError.tag as E["tag"]]
|
|
11
|
+
if (handler === undefined) {
|
|
12
|
+
throw new Error(`No handler for tag: ${taggedError.tag}`)
|
|
13
|
+
}
|
|
14
|
+
return handler(taggedError as Extract<E, { tag: E["tag"] }>)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type MatchTaggedErrorPartialHandlers<E extends AnyTaggedError, R> = {
|
|
18
|
+
[Tag in E["tag"]]?: ((error: Extract<E, { tag: Tag }>) => R) | undefined
|
|
19
|
+
}
|
|
20
|
+
type UnhandledTaggedError<E extends AnyTaggedError, H> = Exclude<
|
|
21
|
+
E,
|
|
22
|
+
{ tag: keyof H }
|
|
23
|
+
>
|
|
24
|
+
export const matchTaggedErrorPartial = <
|
|
25
|
+
E extends AnyTaggedError,
|
|
26
|
+
R,
|
|
27
|
+
const Handlers extends MatchTaggedErrorPartialHandlers<E, R> = MatchTaggedErrorPartialHandlers<E, R>
|
|
28
|
+
>(
|
|
29
|
+
taggedError: E,
|
|
30
|
+
handlers: Handlers,
|
|
31
|
+
fallback: (error: UnhandledTaggedError<E, Handlers>) => R,
|
|
32
|
+
): R => {
|
|
33
|
+
const handler = handlers[taggedError.tag as E["tag"]]
|
|
34
|
+
if (handler !== undefined) {
|
|
35
|
+
return handler(taggedError as Extract<E, { tag: E["tag"] }>)
|
|
36
|
+
}
|
|
37
|
+
return fallback(taggedError as UnhandledTaggedError<E, Handlers>)
|
|
38
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createTaggedError } from "./error.ts"
|
|
2
|
+
import type { TaggedErrorConstructor } from "./error.ts"
|
|
3
|
+
|
|
4
|
+
const MustFixErrorBase: TaggedErrorConstructor<"MustFix", {
|
|
5
|
+
message: string
|
|
6
|
+
cause?: unknown
|
|
7
|
+
}> = createTaggedError("MustFix")()
|
|
8
|
+
export class MustFixError extends MustFixErrorBase {
|
|
9
|
+
//
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const mustFixError = (message: string, options?: { cause?: unknown }): MustFixError => {
|
|
13
|
+
return new MustFixError({ message, cause: options?.cause })
|
|
14
|
+
}
|
|
15
|
+
export const isMustFixError = (error: unknown): error is MustFixError => {
|
|
16
|
+
return MustFixError.is(error)
|
|
17
|
+
}
|
package/src/exception/index.ts
CHANGED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { statSync } from "node:fs"
|
|
2
|
+
import { dirname, join, parse, resolve } from "node:path"
|
|
3
|
+
import type { ResolveFriendlyOptions } from "./resolve.ts"
|
|
4
|
+
import { resolveStartPath } from "./resolve.ts"
|
|
5
|
+
|
|
6
|
+
export interface GetClosestFilePathOptions extends ResolveFriendlyOptions {
|
|
7
|
+
targetBase: string
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 从 {@link ResolveFriendlyOptions.startPath} 开始,逐级向上查找,直到找到指定的文件为止,并返回该文件的路径。
|
|
11
|
+
* 若未找到指定的文件,则返回 undefined。
|
|
12
|
+
*/
|
|
13
|
+
export const getClosestFilePath = (
|
|
14
|
+
options: GetClosestFilePathOptions
|
|
15
|
+
): string | undefined => {
|
|
16
|
+
const { targetBase } = options
|
|
17
|
+
|
|
18
|
+
const startPath = resolveStartPath(options)
|
|
19
|
+
|
|
20
|
+
let currentDir = startPath
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const stat = statSync(startPath)
|
|
24
|
+
if (stat.isFile()) {
|
|
25
|
+
currentDir = dirname(startPath)
|
|
26
|
+
}
|
|
27
|
+
} catch {
|
|
28
|
+
currentDir = dirname(startPath)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const { root } = parse(currentDir)
|
|
32
|
+
|
|
33
|
+
while (true) {
|
|
34
|
+
const filePath = resolve(join(currentDir, targetBase))
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const stat = statSync(filePath)
|
|
38
|
+
if (stat.isFile()) {
|
|
39
|
+
return filePath
|
|
40
|
+
}
|
|
41
|
+
} catch {
|
|
42
|
+
//
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (currentDir === root) {
|
|
46
|
+
break
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
currentDir = dirname(currentDir)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return undefined
|
|
53
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { join, parse } from "node:path"
|
|
2
|
+
import { fileURLToPath } from "node:url"
|
|
3
|
+
|
|
4
|
+
export type PathString = string
|
|
5
|
+
export interface PathObject {
|
|
6
|
+
path: string
|
|
7
|
+
root: string
|
|
8
|
+
dir: string
|
|
9
|
+
base: string
|
|
10
|
+
ext: string
|
|
11
|
+
name: string
|
|
12
|
+
separator: string
|
|
13
|
+
}
|
|
14
|
+
export type PathUnion = PathString | PathObject
|
|
15
|
+
|
|
16
|
+
export const pathStringToObject = (pathString: string): PathObject => {
|
|
17
|
+
const pathObject = parse(pathString)
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
root: pathObject.root,
|
|
21
|
+
path: pathString,
|
|
22
|
+
dir: pathObject.dir,
|
|
23
|
+
base: pathObject.base,
|
|
24
|
+
ext: pathObject.ext,
|
|
25
|
+
name: pathObject.name,
|
|
26
|
+
separator: join("a", "b").replaceAll('a', "").replaceAll('b', "")
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const pathObjectToString = (pathObject: PathObject): string => {
|
|
31
|
+
return pathObject.path
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const toPathObject = (path: PathUnion): PathObject => {
|
|
35
|
+
if (typeof path === "string") {
|
|
36
|
+
if (isFileUrl(path)) {
|
|
37
|
+
return fileUrlToPathObject(path)
|
|
38
|
+
} else {
|
|
39
|
+
return pathStringToObject(path)
|
|
40
|
+
}
|
|
41
|
+
} else {
|
|
42
|
+
return path
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const toPathString = (path: PathUnion): string => {
|
|
47
|
+
if (typeof path === "string") {
|
|
48
|
+
if (isFileUrl(path)) {
|
|
49
|
+
return fileUrlToPathString(path)
|
|
50
|
+
} else {
|
|
51
|
+
return path
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
return pathObjectToString(path)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const isFileUrl = (url: string): boolean => {
|
|
59
|
+
try {
|
|
60
|
+
const parsedUrl = new URL(url)
|
|
61
|
+
return parsedUrl.protocol === "file:"
|
|
62
|
+
} catch {
|
|
63
|
+
return false
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const fileUrlToPathString = (fileUrl: string): PathString => {
|
|
68
|
+
return fileURLToPath(fileUrl)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const fileUrlToPathObject = (fileUrl: string): PathObject => {
|
|
72
|
+
const pathString = fileUrlToPathString(fileUrl)
|
|
73
|
+
const pathObject = pathStringToObject(pathString)
|
|
74
|
+
|
|
75
|
+
return pathObject
|
|
76
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { isAbsolute, join, resolve } from "node:path"
|
|
2
|
+
|
|
3
|
+
export interface ResolveFriendlyOptions {
|
|
4
|
+
startPath?: string | undefined
|
|
5
|
+
}
|
|
6
|
+
export const resolveStartPath = (options: ResolveFriendlyOptions): string => {
|
|
7
|
+
const { startPath } = options
|
|
8
|
+
|
|
9
|
+
if (startPath === undefined) {
|
|
10
|
+
return getCurrentWorkingDirectory()
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (isAbsolute(startPath)) {
|
|
14
|
+
return startPath
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return resolve(join(getCurrentWorkingDirectory(), startPath))
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const getCurrentWorkingDirectory = (): string => {
|
|
21
|
+
return process.cwd()
|
|
22
|
+
}
|
|
@@ -469,7 +469,6 @@ export abstract class BaseInputorController<
|
|
|
469
469
|
|
|
470
470
|
async getSchema(): Promise<Schema> {
|
|
471
471
|
await this.waitForReady()
|
|
472
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
473
472
|
const schema: Schema = {
|
|
474
473
|
inputorTypeName: this.inputorTypeName,
|
|
475
474
|
id: this.id,
|
|
@@ -488,7 +487,6 @@ export abstract class BaseInputorController<
|
|
|
488
487
|
|
|
489
488
|
async getContext(): Promise<Context> {
|
|
490
489
|
const schema = await this.getSchema()
|
|
491
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
492
490
|
const context: Context = {
|
|
493
491
|
...schema,
|
|
494
492
|
internalValue: this.getInternalValue(),
|
|
@@ -529,21 +527,16 @@ export abstract class BaseInputorController<
|
|
|
529
527
|
if (typeof schema !== "object" || schema === null) {
|
|
530
528
|
return false
|
|
531
529
|
}
|
|
532
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
533
530
|
const externalSchema = schema as Schema
|
|
534
531
|
const internalSchema = await this.getSchema()
|
|
535
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
536
532
|
const partialExternalSchema = {} as Schema
|
|
537
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
538
533
|
const partialInternalSchema = {} as Schema
|
|
539
534
|
Object.keys(externalSchema).forEach((key) => {
|
|
540
535
|
if (key in internalSchema) {
|
|
541
536
|
Object.assign(partialInternalSchema, {
|
|
542
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
543
537
|
[key]: (internalSchema as Record<string, unknown>)[key],
|
|
544
538
|
})
|
|
545
539
|
Object.assign(partialExternalSchema, {
|
|
546
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
547
540
|
[key]: (externalSchema as Record<string, unknown>)[key],
|
|
548
541
|
})
|
|
549
542
|
}
|
|
@@ -561,12 +554,10 @@ export abstract class BaseInputorController<
|
|
|
561
554
|
}
|
|
562
555
|
|
|
563
556
|
protected async internalValueToExternalValue(internalValue: InternalValue): Promise<ExternalValue> {
|
|
564
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
565
557
|
return await Promise.resolve(internalValue as unknown as ExternalValue)
|
|
566
558
|
}
|
|
567
559
|
|
|
568
560
|
protected async externalValueToInternalValue(externalValue: ExternalValue): Promise<InternalValue> {
|
|
569
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
570
561
|
return await Promise.resolve(externalValue as unknown as InternalValue)
|
|
571
562
|
}
|
|
572
563
|
|
|
@@ -659,17 +650,13 @@ export abstract class BaseInputorController<
|
|
|
659
650
|
}
|
|
660
651
|
|
|
661
652
|
const isSameSchema = await this.isSameSchema(
|
|
662
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
663
653
|
nextContext as unknown as Schema,
|
|
664
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
665
654
|
prevContext as unknown as Schema,
|
|
666
655
|
)
|
|
667
656
|
if (isSameSchema === false) {
|
|
668
657
|
this.log("[doUpdate] schema changed", "prevSchema", prevContext, "nextSchema", nextContext)
|
|
669
658
|
this.emitSchemaChange({
|
|
670
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
671
659
|
newSchema: nextContext as unknown as Schema,
|
|
672
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
673
660
|
oldSchema: prevContext as unknown as Schema,
|
|
674
661
|
})
|
|
675
662
|
}
|
|
@@ -113,7 +113,6 @@ export class FormInputorController<
|
|
|
113
113
|
this.voidExternalValue = options.voidExternalValue
|
|
114
114
|
}
|
|
115
115
|
else {
|
|
116
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
117
116
|
this.voidExternalValue = VOID_EXTERNAL_VALUE as unknown as ExternalValue
|
|
118
117
|
}
|
|
119
118
|
this.items = options.items
|
|
@@ -139,7 +138,6 @@ export class FormInputorController<
|
|
|
139
138
|
}
|
|
140
139
|
|
|
141
140
|
protected async getSchemaDict(): Promise<GetSchemaDict<Items>> {
|
|
142
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
143
141
|
const schemaDict = {} as GetSchemaDict<Items>
|
|
144
142
|
await Promise.all(Object.entries(this.items).map(async ([key, item]) => {
|
|
145
143
|
Object.assign(schemaDict, {
|
package/src/http/api/api-type.ts
CHANGED
|
@@ -18,7 +18,6 @@ export type ApiType<Target extends AnyApiSchema> = Target extends ApiSchema<
|
|
|
18
18
|
outputError: StandardSchemaV1.InferOutput<OutputErrorSchema>
|
|
19
19
|
} : never
|
|
20
20
|
export const apiType = <const Target extends AnyApiSchema>(apiSchema: Target): ApiType<Target> => {
|
|
21
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
22
21
|
return apiSchema as unknown as ApiType<Target>
|
|
23
22
|
}
|
|
24
23
|
|
|
@@ -26,12 +25,10 @@ export type ApiTypesInTuple<Target extends readonly AnyApiSchema[]> = {
|
|
|
26
25
|
[Index in keyof Target]: ApiType<Target[Index]>
|
|
27
26
|
}
|
|
28
27
|
export const apiTypesInTuple = <const Target extends readonly AnyApiSchema[]>(apiSchemas: Target): ApiTypesInTuple<Target> => {
|
|
29
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
30
28
|
return apiSchemas.map(apiSchema => apiType(apiSchema)) as unknown as ApiTypesInTuple<Target>
|
|
31
29
|
}
|
|
32
30
|
|
|
33
31
|
export type ApiTypesInUnion<Target extends readonly AnyApiSchema[]> = ApiType<Target[number]>
|
|
34
32
|
export const apiTypesInUnion = <const Target extends readonly AnyApiSchema[]>(apiSchemas: Target): ApiTypesInUnion<Target> => {
|
|
35
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
36
33
|
return apiSchemas.map(apiSchema => apiType(apiSchema)) as unknown as ApiTypesInUnion<Target>
|
|
37
34
|
}
|
|
@@ -71,7 +71,6 @@ export const commonJsonSuccessResultSchema = <Data extends Record<string, unknow
|
|
|
71
71
|
): CommonJsonSuccessResultSchema<Data> => {
|
|
72
72
|
// arktype's instantiated object schema is compatible here but
|
|
73
73
|
// not assignable through its generic facade
|
|
74
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
75
74
|
return type({
|
|
76
75
|
status: "'success'",
|
|
77
76
|
data: dataSchema,
|
|
@@ -229,11 +228,9 @@ export const commonJsonErrorResultSchema = <
|
|
|
229
228
|
>(
|
|
230
229
|
dataSchema: Type<Data>
|
|
231
230
|
): CommonJsonErrorResultSchema<Data> => {
|
|
232
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
233
231
|
return type({
|
|
234
232
|
status: "'error'",
|
|
235
233
|
data: type.and(
|
|
236
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
237
234
|
dataSchema as Type<{
|
|
238
235
|
reason: string
|
|
239
236
|
reasonDetail?: string | undefined
|
package/src/index.ts
CHANGED
|
@@ -16,6 +16,7 @@ export * as Route from "./route/index.ts"
|
|
|
16
16
|
export * as Timer from "./timer/index.ts"
|
|
17
17
|
export * as Cron from "./cron/index.ts"
|
|
18
18
|
export * as Orchestration from "./orchestration/index.ts"
|
|
19
|
+
export * as Service from "./service/index.ts"
|
|
19
20
|
export * as Socket from "./socket/index.ts"
|
|
20
21
|
export * as Http from "./http/index.ts"
|
|
21
22
|
|
|
@@ -35,3 +36,4 @@ export * as Huawei from "./huawei/index.ts"
|
|
|
35
36
|
export * as Openai from "./openai/index.ts"
|
|
36
37
|
export * as Weixin from "./weixin/index.ts"
|
|
37
38
|
export * as Ai from "./ai/index.ts"
|
|
39
|
+
export * as FileSystem from "./file-system/index.ts"
|
package/src/openai/openai.ts
CHANGED
|
@@ -292,7 +292,6 @@ export class Openai implements LoggerFriendly {
|
|
|
292
292
|
}
|
|
293
293
|
const textDecoder = new TextDecoder()
|
|
294
294
|
const decoded = textDecoder.decode(chunk.value)
|
|
295
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
296
295
|
const parsed = JSON.parse(decoded) as OriginalChatCompletionChunkResult
|
|
297
296
|
// logger.log("Original chat completion api's stream chunk:", JSON.stringify(parsed))
|
|
298
297
|
controller.enqueue(parsed)
|
|
@@ -229,7 +229,6 @@ export class BrowserFetch<
|
|
|
229
229
|
*/
|
|
230
230
|
async getJson(): Promise<FetchOutputJsonData<Output>> {
|
|
231
231
|
const response = await this.getResponse()
|
|
232
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
233
232
|
return await response.json() as FetchOutputJsonData<Output>
|
|
234
233
|
}
|
|
235
234
|
|
|
@@ -238,7 +237,6 @@ export class BrowserFetch<
|
|
|
238
237
|
*/
|
|
239
238
|
async getText(): Promise<FetchOutputTextData<Output>> {
|
|
240
239
|
const response = await this.getResponse()
|
|
241
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
242
240
|
return await response.text() as FetchOutputTextData<Output>
|
|
243
241
|
}
|
|
244
242
|
|
|
@@ -247,7 +245,6 @@ export class BrowserFetch<
|
|
|
247
245
|
*/
|
|
248
246
|
async getBlob(): Promise<FetchOutputBlobData<Output>> {
|
|
249
247
|
const response = await this.getResponse()
|
|
250
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
251
248
|
return await response.blob() as FetchOutputBlobData<Output>
|
|
252
249
|
}
|
|
253
250
|
|
|
@@ -256,7 +253,6 @@ export class BrowserFetch<
|
|
|
256
253
|
*/
|
|
257
254
|
async getArrayBuffer(): Promise<FetchOutputArrayBufferData<Output>> {
|
|
258
255
|
const response = await this.getResponse()
|
|
259
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
260
256
|
return await response.arrayBuffer() as FetchOutputArrayBufferData<Output>
|
|
261
257
|
}
|
|
262
258
|
|
|
@@ -270,7 +266,6 @@ export class BrowserFetch<
|
|
|
270
266
|
throw new Error("Response body stream is unavailable.")
|
|
271
267
|
}
|
|
272
268
|
|
|
273
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion
|
|
274
269
|
return response.body as FetchOutputStreamData<Output>
|
|
275
270
|
}
|
|
276
271
|
}
|