官方插件

出站 HTTP(Wretch)

从不可变的 Wretch 基础实例派生业务客户端,并统一应用宿主的出站策略。

@pluxel/wretch 面向插件作者的公开入口是 ../workbench。可安装版本以 npm registry 为准;仓库内边界见 Package 矩阵

@pluxel/wretch 提供一个原生、不可变的 Wretch 基础实例,并在请求真正发出前应用宿主级出站策略。URL 构造、addon、middleware、catcher 和响应链仍使用原生 Wretch API。

以下命令在快速开始生成的工作区根目录执行;按 添加插件 选择直接使用依赖的包,再运行 pnpm install

pnpm catalog:add -- @pluxel/wretch

第一个 HTTP consumer

import { , type  } from '@pluxel/wretch'
import { , ,  } from '@pluxel/runtime'

type  = { : string; : string }

const  = .({
	: .(.(), .()),
})

@({ : 'Customer' })
export class  extends  {
	private readonly  = this..()
	private !: 

	constructor(private readonly : ) {
		super()
	}

	protected override (): void {
		this. = this..client.url(this.., true)
	}

	(: string): <> {
		return this..get(`/customers/${()}`).json<>()
	}
}

host 安装 provider:

以下 host测试宿主,用于验证装配。应用入口按 添加插件 配置清单、配置记录和自动启动。

await host.start(WretchPlugin, {
	catalog: [CustomerPlugin],
	initialConfig: {
		timeoutMs: 30_000,
		maxConcurrentRequests: 64,
		maxQueuedRequests: 256,
		allowedOrigins: ['https://catalog.example'],
	},
})
await host.start(CustomerPlugin, {
	initialConfig: { baseUrl: 'https://catalog.example' },
})

这里的 hostcreateRuntimeTestHost() fixture;initialConfig 只用于首次 lifecycle,后续 config 更新使用 host.config.patch()。production deployment 通过自己的 ConfigService 管理相同 records。

client 本身就是 Wretch。Wretch 的 immutable 语义保证不同 consumer 通过 .url().options().headers().auth().addon().middlewares() 派生 client 时不会互相污染。

主入口只导出 WretchPluginWretch 类型,不重新导出裸 wretch() factory 或 addons。需要 query-string addon、retry middleware 等上游扩展时,由 consumer 直接安装 wretch

pnpm catalog:add -- wretch

宿主 outbound policy

WretchPlugin 只有四个 Plugin config 字段:

字段默认值语义
timeoutMs30000每次底层 fetch attempt 的超时;0 关闭 host timeout
maxConcurrentRequests64所有 consumer 共享的进行中 fetch attempt 上限
maxQueuedRequests256并发满时最多等待多少 attempt;0 禁止排队
allowedOrigins[]空数组不限制;非空时只允许列出的精确 HTTP(S) origin

allowedOrigins 中每项必须是没有 path、query、hash 或 credential 的 HTTP(S) origin。实际 request URL 也必须是 absolute HTTP(S) URL;不支持相对 URL 或其他协议。

policy 通过 Wretch defer() 在请求发送前安装。consumer 的 retry middleware 位于外层,所以每次 retry attempt 都会单独经过:

  1. absolute URL 与 origin 检查;
  2. 全局并发/队列 admission;
  3. timeout 与 lifecycle AbortSignal;
  4. 实际 fetch;
  5. 释放并发槽。

队列满时 reject Error('Outbound HTTP request queue is full');origin 不允许时 reject 包含 origin 的 Error;timeout 使用名为 TimeoutErrorDOMException。这些目前不是带稳定 code 的 Pluxel error hierarchy,transport 不应依赖完整 message 做协议映射。

retry、dedupe、缓存、认证刷新和业务错误解析不属于 host policy。它们应由 consumer 用 Wretch middleware/addon 明确组合;需要缓存时由业务显式组合缓存实现;仓库内部可以使用 Cache 预览,它目前不是公开安装包。

生命周期与 cancellation

取得的 client 与 consumer owner 和 provider generation 绑定:

  • consumer stop/replacement 后,旧 client 的新请求会失败;
  • provider stop/replacement 后,所有旧 client 都会失败;
  • 正在等待并发槽的请求会从队列移除并 reject;
  • 已进入 fetch 的请求会收到 lifecycle abort signal;
  • consumer 自己传入的 options.signal 会和 lifecycle/timeout signal 合并。

自定义 .fetchPolyfill() 必须遵守标准 AbortSignal,才能在 teardown 或 timeout 时及时结束。不要清空受管 client 的 deferred callbacks,否则会移除 provider policy。

可选的 managed settings

默认情况下,consumer 只受 host policy 管理。若希望操作者在 Workbench 为某个 consumer 配置普通 headers、HTTP(S) proxy 和更短 timeout,consumer 必须显式调用:

protected override async init(): Promise<void> {
	await this.http.enableManagedSettings()
	this.api = this.http.client.url(this.config.baseUrl, true)
}

managed settings 按 caller 的完整 Plugin node address 隔离并持久化。client 每次发送请求前读取最新状态,所以设置保存后,已缓存的 immutable client 也会自动生效。

WretchManagedSettings 的真实形状是:

type  = <{
	: <<string, string>>
	?: string
	?: number
}>
  • managed header 会覆盖请求中同名普通 header;最多 32 项,单值最长 4096 字符;
  • authorizationcookieproxy-authorizationset-cookiex-api-key 等 secret-bearing header 会被拒绝;
  • transport-owned header(如 hostcontent-lengthconnection)也会被拒绝;
  • proxy 必须是无 credential、path、query 或 hash 的 HTTP(S) origin;authenticated proxy 不受支持;
  • consumer timeoutMs 必须是正整数,并且只能收紧 host timeout;host timeout 为 0 时,它成为有效 timeout。

managed settings 使用 runtime persistence,而不是 secret store。不要把 access token、API key 或 cookie 放入该 contract。Workbench disabled 时不会创建 UI backend,但已显式启用的 persisted settings 仍会应用于核心 HTTP client。

放置 Workbench Attachment

@pluxel/wretch/workbench 导入 provider-owned Attachment,并在 consumer 的 Workbench 定义中选择 placement:

import { workbench } from '@pluxel/runtime/workbench'
import { WretchWorkbench } from '@pluxel/wretch/workbench'

export const CustomerWorkbench = workbench.define({
	http: WretchWorkbench.settings.place(
		workbench.tab({ label: 'HTTP', icon: workbench.icons.Settings }),
	),
})

consumer 启动时启用设置,并直接绑定 constructor-injected required dependency:

protected override async init(): Promise<void> {
	await this.http.enableManagedSettings()
	this.api = this.http.client.url(this.config.baseUrl, true)

	this.ctx.workbench?.publish(CustomerWorkbench, {
		http: { provider: this.http },
	})
}

View 打开时,WretchPlugin 根据 Workbench 提供的 exact consumer.node 找到已经初始化的 managed state, 并创建 fresh WretchSettingsApi capability。若没有先完成 enableManagedSettings(),打开 View 会失败。 provider-owned zero-props renderer 通过 descriptor-bound scope 的 query/mutation resources 取得 provider stub; 它提供 snapshot()update(settings)reset(),Framework 自动 detach 返回 DTO 并在 mutation settle 后刷新 snapshot。 snapshot 同时包含 hostTimeoutMs 和最终 effectiveTimeoutMs

同一 caller 并发执行 enableManagedSettings() 会共享一次初始化。View 关闭、session 结束或 caller/provider 停止后,旧 capability 被撤销;provider cleanup 也会关闭 managed ProxyAgent。UI 不是核心 HTTP 请求能够运行的前提。

最后更新于

本页目录