Dagger TypeScript SDK ErrorValue 类完全指南:结构化错误附加值的读取与类型体系
【免费下载链接】daggerAutomation engine to build, test and ship any codebase. Runs locally, in CI, or directly in the cloud项目地址: https://gitcode.com/GitHub_Trending/da/dagger
导读
ErrorValue是 Dagger 核心 GraphQL API 中Error对象的一个组成单元,用于以“名称 + JSON 值”的形式为错误附加结构化上下文信息(如错误码、失败文件路径、重试信息等)。本文以@dagger.io/daggerTypeScript SDK 生成的客户端类 ErrorValue 为主线,完整讲解其构造方式、三个核心方法(id、name、value)的语义与调用链,并结合仓库中 GraphQL Schema、Go 核心实现与测试用例,深入剖析ErrorValue的底层数据模型,帮助你准确掌握如何在 Dagger 管道中读取和使用结构化错误扩展信息。
一、ErrorValue 是什么:错误扩展的结构化载体
在 Dagger 中,错误并不只是“一条字符串”。当模块或引擎在执行过程中抛出错误时,可以携带一组结构化的扩展信息(extensions)。从仓库核心实现看,Error对象包含两个字段(core/error.go):
type Error struct { Message string `field:"true" doc:"A description of the error."` Values []*ErrorValue `field:"true" doc:"The extensions of the error."` }其中Values是[]*ErrorValue,即一组错误附加值;ErrorValue本身的结构同样定义在 core/error.go:
type ErrorValue struct { Name string `field:"true" doc:"The name of the value."` Value JSON `field:"true" doc:"The value."` }也就是说,每个ErrorValue由两个字段构成:
name:值的名称,是扩展信息的键;value:任意 JSON 编码的值,是扩展信息的载荷。
ErrorValue与Error一样实现了dagql.PersistedObject与dagql.PersistedObjectDecoder接口(core/error.go),因此它可以像 Dagger 中其他核心对象一样被持久化、编码与解码——这也解释了为什么 TypeScript 客户端中它会拥有id方法。
二、TypeScript 客户端中的类签名总览
ErrorValue类在生成的 TypeScript SDK 中位于 sdk/typescript/src/api/client.gen.ts,完整的声明结构如下:
| 成员 | 签名 | 说明 |
|---|---|---|
| 继承 | extends BaseClient | 所有 Dagger API 客户端对象的公共基类 |
| 构造器 | new ErrorValue(ctx?, _id?, _name?, _value?) | 仅供内部使用,不要直接new创建对象 |
id() | Promise<ErrorValueID> | 返回该对象的唯一标识符 |
name() | Promise<string> | 返回错误附加值的名称 |
value() | Promise<JSON> | 返回错误附加值的 JSON 载荷 |
1. 类继承关系:BaseClient
ErrorValue直接继承自BaseClient。从生成的源码可见其私有字段与构造器(sdk/typescript/src/api/client.gen.ts):
export class ErrorValue extends BaseClient { private readonly _id?: ID = undefined private readonly _name?: string = undefined private readonly _value?: JSON = undefined /** * Constructor is used for internal usage only, do not create object from it. */ constructor(ctx?: Context, _id?: ID, _name?: string, _value?: JSON) { super(ctx) this._id = _id this._name = _name this._value = _value } ... }三个私有字段(_id、_name、_value)分别对应底层 GraphQL 对象的id、name、value字段,构造器接收这四个参数用于缓存已知值。这种“字段缓存 + 惰性查询”的模式是 Dagger 生成客户端的通用风格:如果客户端已经持有该字段的值,就直接返回,避免额外的 GraphQL 请求。
2. 构造器:仅供内部使用
官方文档明确指出:
Constructor is used for internal usage only, do not create object from it.
构造器参数ctx(Context)、_id(ErrorValueID)、_name(string)、_value(JSON)均为可选。日常开发中你不需要也不应该直接实例化ErrorValue——它是通过查询Error.values字段(或加载其 ID)由 SDK 自动构造的。
三、三个核心方法详解
1.id():获取唯一标识符
id = async (): Promise<ID> => { if (this._id) { return this._id } const ctx = this._ctx.select("id") const response: Awaited<ID> = await ctx.execute() return response }- 返回类型为
Promise<ErrorValueID>; - 若客户端已持有
_id则直接返回,否则通过 GraphQL 选择集select("id")惰性查询。
ErrorValueID是 Dagger 的标量类型之一,对应 GraphQL Schema 中的scalar ErrorValueID。其 TypeScript 类型别名定义在 type-aliases/ErrorValueID.md:
type ErrorValueID = string & { __ErrorValueID: never }即它是一个带 branded 标记的字符串,用于在类型层面区分普通字符串与 Dagger 对象标识符。在 GraphQL Schema 中(core/schema/testdata/base_schema.graphqls):
type ErrorValue implements Node { """A unique identifier for this ErrorValue.""" id: ID! """The name of the value.""" name: String! """The value.""" value: JSON! } """A unique identifier for an object.""" scalar ErrorValueID注意ErrorValue实现了Node接口,这与id()方法的存在直接对应。
2.name():读取附加值的名称
name = async (): Promise<string> => { if (this._name) { return this._name } const ctx = this._ctx.select("name") const response: Awaited<string> = await ctx.execute() return response }- 返回类型为
Promise<string>; - 对应核心 Go 结构体中
Name string字段(core/error.go),文档描述为 “The name of the value”。
name是扩展信息的键。例如错误扩展{"code": 42}中,键"code"就会成为一个ErrorValue.name。
3.value():读取 JSON 载荷
value = async (): Promise<JSON> => { if (this._value) { return this._value } const ctx = this._ctx.select("value") const response: Awaited<JSON> = await ctx.execute() return response }- 返回类型为
Promise<JSON>; - 对应核心 Go 结构体中
Value JSON字段(core/error.go),文档描述为 “The value”。
JSON是 Dagger 的另一个 branded 标量,定义在 type-aliases/JSON.md:
type JSON = string & { __JSON: never }其文档描述为 “An arbitrary JSON-encoded value”,即任意 JSON 编码的字符串。在 Go 端,它对应core.JSON类型,底层就是json.RawMessage语义。
四、从 GraphQL 到 TypeScript:ErrorValue 的完整数据链路
1. Schema 层:Error.values 与 ErrorValue
在 Dagger 的基础 GraphQL Schema 中(core/schema/testdata/base_schema.graphqls),Error类型声明了:
type Error implements Node { id: ID! message: String! """The extensions of the error.""" values: [ErrorValue!]! withValue(name: String!, value: JSON!): Error! }即:
Error.values返回一个[ErrorValue!]!列表——这是获取ErrorValue对象的唯一入口;Error.withValue(name, value)用于向错误追加一个新的附加值,返回值是新的Error;- 对应的
scalar ErrorID、scalar ErrorValueID分别作为两个对象类型的标识符。
2. 解析器层:withValue 如何产生 ErrorValue
Schema 注册位于 core/schema/error.go:
dagql.Fields[*core.Error]{ dagql.Func("withValue", s.withValue). Doc(`Add a value to the error.`), }.Install(dag) dagql.Fields[*core.ErrorValue]{}.Install(dag)withValue的实现(core/schema/error.go):
func (s *errorSchema) withValue(ctx context.Context, self *core.Error, args struct { Name string `doc:"The name of the value."` Value core.JSON `doc:"The value to store on the error."` }) (*core.Error, error) { return self.WithValue(args.Name, args.Value), nil }底层Error.WithValue采用不可变风格:克隆当前错误后追加一个ErrorValue并返回新错误对象(core/error.go):
func (e *Error) WithValue(name string, value JSON) *Error { cp := e.Clone() cp.Values = append(cp.Values, &ErrorValue{ Name: name, Value: value, }) return cp }3. 扩展信息如何最终暴露
ErrorValue在内部服务于错误扩展(extensions)机制。Error.Extensions()会把Values中每个ErrorValue反序列化为键值对(core/error.go):
func (e *Error) Extensions() map[string]any { ext := map[string]any{} for _, v := range e.Values { var val any json.Unmarshal(v.Value, &val) ext[v.Name] = val } return ext }此外,当引擎需要把普通 Go error 包装为 DaggerError对象时,NewErrorFromErr(core/error.go)会检查错误是否实现了dagql.ExtendedError接口;若实现了,就把其Extensions()中的每个键值对通过withValue选择器逐条写入ErrorValue,最终组合成一个 Dagger 查询序列。
从这些源码可以推断:ErrorValue既是 Dagger 内部错误扩展的持久化载体,也是用户通过 GraphQL/客户端查询错误附加信息的结构化入口。
五、JSON 载荷的取值语义与测试验证
value()返回的 JSON 可以承载任意 JSON 数据类型。仓库中的测试用例 core/error_test.go 系统验证了ErrorValue.Value对各种 JSON 类型的处理:
| 测试场景 | ErrorValue.Value | 反序列化结果 |
|---|---|---|
| 简单字符串 | "hello world" | "hello world" |
| 数字 | 42 | float64(42) |
| 布尔值 | true | true |
| null | null | nil |
| 对象 | {"file": "test.go", "line": 123} | map[string]any{...} |
| 数组 | ["a", "b", "c"] | []any{"a","b","c"} |
| 多值组合 | 多个 ErrorValue | 合并后的 map |
特别地,测试TestError_Extensions_PreventDoubleEncoding(core/error_test.go)确保Value中的 JSON 被正确反序列化为结构化对象,而不会以原始 JSON 字符串的形式再次编码(避免双重/三重编码问题)。
这些测试说明:当你在 TypeScript 端调用value()拿到JSON字符串后,可以安全地JSON.parse出原始结构;当你在 Go/引擎侧看到扩展信息时,它们已经是反序列化后的map[string]any。
六、典型使用方式:遍历 Error.values
虽然ErrorValue类本身不建议直接构造,但它是消费 Dagger 错误扩展信息的关键类型。典型的使用模式是从Error对象出发:
import { connect } from "@dagger.io/dagger" const result = await connect(async (client) => { // ... 执行可能失败的操作,捕获 dagger.Error 类型错误 try { await client.container() .from("alpine:3.20") .withExec(["sh", "-c", "exit 1"]) .stdout() } catch (e) { // 假设 e 是带有 values 扩展的 Dagger 错误 const err = e as any // values 为 ErrorValue[],每个元素可调用 id() / name() / value() for (const ev of err.values ?? []) { const name = await ev.name() const value = await ev.value() // JSON 字符串,可 JSON.parse console.log(`extension[${name}] =`, JSON.parse(value)) } } })几点实践提示:
Error.values在 Schema 中是非空数组[ErrorValue!]!,但具体某个错误是否携带扩展取决于产生错误的一方是否调用过withValue;- 每个
ErrorValue都可以独立调用id()获取标识符,之后可通过 Schema 中的loadErrorValueFromID(id: ErrorValueID!): ErrorValue!(见 core/schema/testdata/base_schema.graphqls)从 ID 重新加载对象; value()返回的JSON是 branded string 类型,取值后需要自行JSON.parse还原为运行时对象。
七、SDK 代码生成:ErrorValue 从何而来
ErrorValue类并非手写代码,而是由 Dagger 的 SDK 代码生成器从 GraphQL Schema 自动生成。TypeScript 客户端源文件 sdk/typescript/src/api/client.gen.ts 即产物之一,官方 API 参考文档(含本文所讲的 ErrorValue.md)也是基于同一 Schema 生成的。因此:
- 若上游 Schema 中
ErrorValue类型发生变更,生成的类与文档会同步更新; - 你看到的方法签名(
id/name/value)与 Schema 字段(id: ID!/name: String!/value: JSON!)一一对应,一一映射,不存在额外的隐藏行为。
八、总结
ErrorValue虽然只是Error对象的一个小组件,但它承载着 Dagger 结构化错误扩展的核心设计:
- 数据模型:
name+value(JSON)两个字段,定义于 core/error.go,实现持久化对象接口; - Schema 契约:
ErrorValue implements Node,包含id、name、value三字段,通过Error.values与withValue与Error关联(base_schema.graphqls); - 客户端体验:TypeScript 端
ErrorValue类继承BaseClient,以惰性查询方式暴露id()、name()、value()三个异步方法,构造器仅供内部使用(client.gen.ts); - 取值语义:
value是任意 JSON 编码的字符串,底层在 Go 端反序列化为map[string]any,测试用例完整覆盖了字符串、数字、布尔、null、对象、数组等场景(error_test.go)。
掌握ErrorValue,就掌握了在 Dagger 中读取结构化错误上下文的标准方式——无论是调试模块故障、透传自定义错误码,还是在引擎层面消费扩展信息,都能做到心中有数。
【免费下载链接】daggerAutomation engine to build, test and ship any codebase. Runs locally, in CI, or directly in the cloud项目地址: https://gitcode.com/GitHub_Trending/da/dagger
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考