Uh oh!
There was an error while loading. Please reload this page.
feat(fields): 采纳 file-as-reference 值形态 — ObjectStack ADR-0104 D3 wave 2 (PR-7) - #2828
Merged
Conversation
…R-0104 D3 wave 2)
A file/image field value now reaches the UI in one of three forms, and the rules
for reading them live in one place — the new `file-value` module — instead of
being re-derived in each widget:
1. Reference a bare sys_file id string, what the backend stores once
file-as-reference is adopted
2. Expanded { id, name, size, mimeType, url }, what the read path returns
after resolving a reference
3. Legacy blob { file_id?, name, original_name, size, mime_type, url }, the
pre-reference shape this package used to build itself
The casing split is the bug this fixes. The expanded form carries `mimeType`;
the legacy blob carries `mime_type`. FileField, FileCell and ImageField all read
only `mime_type`, so the moment a backend starts returning the expanded form
they stop recognising images — thumbnails silently degrade to a generic file
icon, and nothing about the symptom points at a value shape as the cause.
readFileValue() accepts both, and every widget goes through it.
Uploads now submit the reference form — the bare id — when the upload adapter
surfaced one, and the legacy blob when it did not (the object-URL fallback
adapter, or a backend predating file-as-reference), so the same build works
against both. Action params already POSTed a bare fileId; record field values
now use the same contract, and serializeParamValues shares the fileIdOf()
extractor so the two surfaces cannot drift on what counts as an id.
Because a bare id carries no name or URL of its own, each widget remembers the
display details of files it just uploaded, keyed by id, so an upload renders
immediately instead of showing a bare token until the next read enriches it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SHpGw3GBA9aFpfwVArRWfdThe latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
os-zhuang
marked this pull request as ready for review
July 27, 2026 04:08
Uh oh!
There was an error while loading. Please reload this page.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
配对 framework 侧的 ADR-0104 D3 wave 2(objectstack#3527 / #3534 / #3535,均已合并)。
三种形态,一处规则
file/image字段值现在会以三种形态之一到达 UI。读取规则收敛到@object-ui/fields新增的file-value模块,不再由每个 widget 各自重新推导:sys_fileid 字符串{ id, name, size, **mimeType**, url }{ file_id?, name, original_name, size, **mime_type**, url }这个 PR 修掉的真实 bug:大小写分裂
展开形态带的是
mimeType(camelCase,来自平台 spec 拥有的FileValueSchema),遗留 blob 带的是mime_type(snake_case)。而FileField、FileCell、ImageField只读mime_type。后果:后端一旦开始返回展开形态,这三个 widget 立刻认不出图片——缩略图静默退化成通用文件图标,而症状里没有任何东西指向"值形态"这个原因。
readFileValue()两种都接受,所有 widget 都走它。提交侧:上传后存引用
上传适配器给出 fileId 时,widget 提交裸 id(引用形态);没给出时(object-URL 兜底适配器、或早于 file-as-reference 的后端)提交遗留 blob。同一份构建对两种后端都工作。
action param 本来就已经 POST 裸 fileId(#2698/#2710)——这个 PR 让记录字段值走上同一份契约,并且让
serializeParamValues共用fileIdOf()提取器,两个面就不会在"什么算 id"上漂移。UX:刚上传的文件立即可见
裸 id 本身不带 name 和 url,而丰富形态要等下一次读取才回来。所以每个 widget 会按 id 记住自己刚上传的文件的展示信息(
withRecentUploads),上传完立刻正常渲染,而不是先显示一个裸 token 直到刷新。测试
file-value.test.ts新增 32 项:id token 的接受/拒绝矩阵(含 4 种 URL 形态)、fileIdOf的file_id → id优先级、展开形态与遗留 blob 两种大小写都识别成图片、name 回退链(name → original_name → URL 末段 → 本地化兜底)、裸引用诚实地不带 url、混合形态数组、提交两种模式并与readFileValue往返、meta.fileId不是 id 形态时拒绝、withRecentUploads只补裸引用且不改raw(展示增强绝不改变提交内容)。type-check✅ ·build✅ ·lintexit 0依赖与顺序
需要 framework 侧已合并的 PR-2(读路径解析 id)+ PR-3(写路径认领所有权)。因为提交侧带 fallback、读取侧三形态都认,这个 PR 可以先于 framework 的写切换(PR-5a)合并——合并后 PR-5a 就只是把已经在发生的事情正式收窄,而不是一次行为切换。
🤖 Generated with Claude Code
https://claude.ai/code/session_01SHpGw3GBA9aFpfwVArRWfd
Generated by Claude Code