Skip to content

v0.3.0

Choose a tag to compare

@jcoder-stack jcoder-stack released this 07 Sep 05:36
· 19 commits to main since this release

代理层现在收得下 multipart 上传与裸字节直传。ABP [FromForm] 的导入端点此前拿到的是一个没有正文的请求。

修复

AbpProxyRequest.body 的类型是 string,而 abpFetchtypeof init?.body === "string" ? init.body : undefined 收窄调用方的 body——传 FormData 不报错,静默变成空 body。四层链路(abpFetchabpRequestFn 的 zod schema → AbpProxyRequestproxy.send)都把 body 钉在 string 上。

新增

AbpProxyRequest.body 拓宽为 string | Uint8Array | ArrayBuffer | FormData,新导出类型 AbpProxyBody

判据是可重发而不是「是不是二进制」:代理的核心能力是 401→刷新→重放与幂等重试,两者都要把同一个 body 再发一次。ReadableStream 刻意排除,并在客户端与代理两处显式拒绝——它只能消费一次,收下它会让那两条路径静默退化成「重放一个空正文」。

新增 server function abpUploadFn,二进制以原生 multipart 过桥,不进 JSON 载荷。这是量过之后的选择:

走法 实测
Uint8Array 交给 seroval 1MB 反序列化抛 SerovalDeserializationError(512KB 尚可)
base64 成字符串 10MB 能过,但文件变 13.3MB 字符串、两端各解析一次 JSON
原生 multipart 文件字节全程不进 JSON

TanStack Start 要求整个 payload 就是 FormData,混不进 JSON 对象,故为第二个 server fn。文本那条路径的 zod schema 一个字未动,所有既有 CRUD 调用不受影响

createAbpProxy 新增 maxBodyBytes(默认 10MB),在任何网络动作之前拦截。

一个会让人查很久的坑

body 是 FormData 时,调用方的 content-type 现在会被丢弃。它带的是调用方那份 FormData 的 boundary,而传输层重新编码时会生成新的;透传旧值会让上游按错的分隔符解析,一个字段都读不出来——症状是「请求到了、参数全空」,不是报错。content-type 本就在转发白名单内,所以这条不处理必然踩。

使用

调用方无需改动写法,abpFetch 对外仍是 typeof fetch

const form = new FormData();
form.append("file", file, file.name);
await abpFetch("/api/app/books/import", { method: "POST", body: form });

裸字节直传同样可用(content-type 按需自带):

await abpFetch("/api/app/files/1", {
  method: "PUT",
  headers: { "content-type": "application/octet-stream" },
  body: bytes,
});

升级须知

@jcoder-stack/registry 的分发物变了:jc-abp add app-shell 装到的 src/api/abp-fetch.ts 是新版,且 jc-abp init 会多铺一个 src/auth/upload-payload.ts。已有项目要吃到这个能力需重新 add / initAbpProxyRequest.body 是加法拓宽,既有调用无需改动。

验证

97 个测试文件 / 836 用例(新增 14 条)、typecheck(含 @ts-expect-error 类型契约)、lint、三处 build 全通过;registry 分发物已随块源码重建。