Skip to content

dispatcher 的 /storage/upload 用 upload(file, {request}) 调用契约里的 upload(key, data, options?) —— 对任何实现都会 TypeError #4087

Description

@os-zhuang

按 Prime Directive #10 记录,#4058 / PR #4086 端到端验证时撞出来的域外问题。

现象

packages/runtime/src/domains/storage.tsPOST /storage/upload 分支:

const result = await storageService.upload(file, { request: context.request });

两个实参。但契约(packages/spec/src/contracts/storage-service.ts,以及 storage-service.test.ts 里的一致用法)是三个:

upload(key: string, data: Buffer | ReadableStream, options?: StorageUploadOptions): Promise<void>

所有真实实现都按契约来 —— s3-storage-adapter.ts:147local-storage-adapter.ts:133swappable-storage-service.ts:54,service-storage 自己的 storage-routes.ts:644 也是 upload(payload.k, data, { contentType })

于是 dispatcher 这条路把 key 传成了文件对象、data 传成了 { request }。实测(plugin-dev 的内存 storage 占槽时):

TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of
Buffer, ArrayBuffer, or Array or an Array-like Object. Received an instance of Object
    at Function.from (node:buffer:328:9)
    at Object.upload (packages/plugins/plugin-dev/dist/index.js:81:37)
    at handleStorageRequest (packages/runtime/dist/index.js:3657:41)

换成真实 adapter 不会更好:local-storage-adapter 会拿文件对象去 path.join,S3 会拿它当 object key。

为什么一直没暴露

装了 @objectstack/service-storage 时,它自己在 /api/v1/storagestorage-routes.ts:129basePath)挂了一整套真实路由,把 dispatcher 这个 bridge 遮蔽掉了。所以这条分支只在没有插件拥有 /api/v1/storage 时才可达 —— 也就是恰好只在 dev stub / 自装配栈里,那里它必然崩。

GET /storage/file/:id 不受影响:download(id, { request }) 的多余实参被忽略,行为正确。

#4058 无关

改动前后走到的是同一行:#4058 之前的门是 !storageService(stub 是 truthy → 进入),之后是 isServiceServeable(内存 storage 声明 degraded → 同样进入)。纯属既有缺陷,只是端到端验证时才被看见。

需要的决定

不是"给 dispatcher 加个 ?? 兼容两种签名"(Prime Directive #12:不要在消费端长出第二套事实上的契约)。真正的问题是这个 bridge 到底该不该存在:

  1. 按契约修调用点 —— 从请求里解析出 key + bytes(multipart / raw body),再 upload(key, data, { contentType })。要顺带定义 dispatcher 这层怎么拿 contentType 和文件名。
  2. 退役这条 bridge —— /storage 的 HTTP 面归 service-storage(它已经是唯一实现,且路由更完整:分片、签名 URL、生命周期)。空槽按 dispatcher 其余服务域仍只判槽位占用、不读 handlerReady —— #4000 在 analytics 一域落地后剩下的类推面 #4058 的规则答 501,与 analytics 的处理同形。倾向这条:一个 500 的 bridge 比没有 bridge 更糟,而"装 service-storage"本来就是唯一能真正用起来的路径。

关联:#4058#4000#3891、ADR-0076 D11/D12。

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions