@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.
Files changed (63) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/oxlint.config.ts +1 -2
  3. package/package.json +5 -5
  4. package/scripts/build.ts +2 -52
  5. package/src/basic/promise.ts +141 -71
  6. package/src/drizzle/pagination.ts +0 -2
  7. package/src/event/class-event-proxy.ts +0 -2
  8. package/src/event/instance-event-proxy.ts +0 -2
  9. package/src/exception/README.md +28 -19
  10. package/src/exception/error/error.ts +123 -0
  11. package/src/exception/error/index.ts +2 -0
  12. package/src/exception/error/match.ts +38 -0
  13. package/src/exception/error/must-fix.ts +17 -0
  14. package/src/exception/index.ts +2 -0
  15. package/src/file-system/find.ts +53 -0
  16. package/src/file-system/index.ts +2 -0
  17. package/src/file-system/path.ts +76 -0
  18. package/src/file-system/resolve.ts +22 -0
  19. package/src/form/inputor-controller/base.ts +0 -13
  20. package/src/form/inputor-controller/form.ts +0 -2
  21. package/src/http/api/api-type.ts +0 -3
  22. package/src/http/api-adapter/api-result-arktype.ts +0 -3
  23. package/src/index.ts +2 -0
  24. package/src/openai/openai.ts +0 -1
  25. package/src/request/fetch/browser.ts +0 -5
  26. package/src/request/fetch/nodejs.ts +0 -5
  27. package/src/request/request/base.ts +0 -4
  28. package/src/request/request/general.ts +0 -1
  29. package/src/result/controller.ts +11 -7
  30. package/src/result/either.ts +230 -60
  31. package/src/result/generator.ts +168 -0
  32. package/src/result/index.ts +1 -0
  33. package/src/route/router/router.ts +0 -1
  34. package/src/route/uri/hash.ts +0 -1
  35. package/src/route/uri/search.ts +0 -1
  36. package/src/service/README.md +1 -0
  37. package/src/service/index.ts +1 -0
  38. package/src/service/service.ts +110 -0
  39. package/src/socket/client/socket-unit.ts +0 -2
  40. package/src/socket/server/socket-unit.ts +0 -1
  41. package/src/tube/helper.ts +0 -1
  42. package/src/weixin/official-account/authorization.ts +0 -2
  43. package/src/weixin/official-account/js-api.ts +0 -2
  44. package/src/weixin/open/oauth2.ts +0 -2
  45. package/tests/unit/aio/json.spec.ts +0 -1
  46. package/tests/unit/basic/promise.spec.ts +158 -50
  47. package/tests/unit/credential/api-key.spec.ts +0 -1
  48. package/tests/unit/credential/password.spec.ts +0 -1
  49. package/tests/unit/exception/error/error.spec.ts +83 -0
  50. package/tests/unit/exception/error/match.spec.ts +81 -0
  51. package/tests/unit/http/api-adapter/node-http.spec.ts +0 -4
  52. package/tests/unit/identifier/uuid.spec.ts +0 -1
  53. package/tests/unit/request/request/base.spec.ts +0 -3
  54. package/tests/unit/request/request/general.spec.ts +0 -1
  55. package/tests/unit/result/controller.spec.ts +82 -0
  56. package/tests/unit/result/either.spec.ts +377 -0
  57. package/tests/unit/result/generator.spec.ts +273 -0
  58. package/tests/unit/route/router/route.spec.ts +0 -1
  59. package/tests/unit/route/uri/pathname.spec.ts +0 -1
  60. package/tests/unit/socket/server.spec.ts +0 -2
  61. package/vite.config.ts +2 -1
  62. package/dist/index.js +0 -720
  63. package/dist/index.js.map +0 -1005
@@ -2,49 +2,57 @@
2
2
 
3
3
  ## Description
4
4
 
5
- Exception 模块提供围绕宿主环境全局异常入口(global exception entry)的监听、标准化与组合能力,用于把浏览器和 Node.js 暴露出来的异常来源整理成稳定、可清理、可组合的公共异常模型。
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
- 在建模上,这个模块采用“保留来源差异,同时提供标准化记录”的方式,而不是过早追求绝对统一。也就是说,浏览器与 Node.js 的来源种类、原始参数和补充字段可以不同,但它们都可以被整理成带有 runtimesource、exception、message、timestamp 等稳定字段的公共记录。这种分层有助于同时满足宿主特定处理与跨宿主消费。
25
+ 对于异常建模中的结构化错误部分,当前采用 Tagged Error 的思路:这类错误既保留原生 Error 的 name、message、stack 与 `cause` 语义,也额外拥有稳定的 `tag` 与 `data` 字段,便于在联合类型中进行穷举或部分匹配。
24
26
 
25
27
  ## For Using
26
28
 
27
- 当应用需要在入口层、基础设施层、日志层或诊断层统一接入宿主全局异常时,可以使用这个模块,把分散在不同宿主 API 上的异常来源整理成更清楚的订阅接口。
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
- - 不要在监听逻辑中默认执行日志输出、上报请求、`preventDefault`、`process.exit` 或其它强策略动作,除非 API 名称与文档已经明确承诺。
50
+ - 公共能力应围绕宿主异常入口、异常记录标准化,以及其中可被稳定表达的结构化异常建模与匹配语义展开。
51
+ - 若新增的是模块内稳定异常模型,且它适合以 Error 形式存在,应优先使用 Tagged Error 形式表达,而不是仅靠裸字符串或无判别字段的对象。
52
+ - 不要在监听逻辑或异常模型中默认执行日志输出、上报请求、`preventDefault`、`process.exit`、重试、恢复或其它强策略动作,除非 API 名称与文档已经明确承诺。
46
53
  - 组合入口只负责批量安装监听器与汇总清理,不负责掩盖宿主差异或替调用方做策略决策。
47
- - Environment 的关系应保持清楚:Environment 可以帮助判断和获取宿主上下文,Exception 则负责安装异常监听器。
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
- - 新增公共能力时,应优先检查它是否表达稳定、清楚且值得长期维护的异常入口语义,而不是某段实现细节的便捷暴露;仅在确认需要长期对外承诺时再加入 Barrel 导出。
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,2 @@
1
+ export * from "./error.ts"
2
+ export * from "./match.ts"
@@ -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
+ }
@@ -2,3 +2,5 @@ export type * from "./types.ts"
2
2
  export * from "./normalize.ts"
3
3
  export * from "./browser.ts"
4
4
  export * from "./nodejs.ts"
5
+
6
+ export * from "./error/index.ts"
@@ -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,2 @@
1
+ export * from "./path.ts"
2
+ export * from "./find.ts"
@@ -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, {
@@ -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"
@@ -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
  }