fix(host): review round 22 — note enumeration includes the create path; cross-method equality pinned; NFD-volume boundary recorded
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/host/directory-picker-browse/README.md
|
||||
README.md: 23153881b84dcb71dfb05d4f297a5818c410ca77
|
||||
README.zh.md: d7010e2941a801ba6358082824330eaae46e42b7
|
||||
README.md: 01e2b9e5afcfd7c47a3f42a76cc1388a25477334
|
||||
README.zh.md: 8cb63048713dc964f92762546de783d2ce6ce5a7
|
||||
|
||||
@@ -19,5 +19,6 @@ None; this package neither assembles nor sends a provider request.
|
||||
## Known Limitations and Deferred Work
|
||||
|
||||
- **Windows hidden attribute is not read** — Node dirents do not expose `FILE_ATTRIBUTE_HIDDEN`, so `hidden` means dot-prefixed on every platform until a native probe is worth its cost.
|
||||
- **Name-normalizing volumes void the create-path equality** — `createDirectory` promises its return verbatim-equal to the child's next `entries[].path`; Node's namespaced Win32 paths store even trailing-dot/space segments literally, but a volume that rewrites names on storage (NFD normalization on HFS+-style volumes) breaks the match, and the create landing degrades to the documented single-pane / edit-zone fallback.
|
||||
- **No drive-root enumeration** — on Windows the ancestry stops at the drive root; crossing drives waits for the browser UI's path-entry affordance rather than an enumeration primitive here.
|
||||
- **Whole-filesystem scope** — no per-deployment browse-root restriction; `workspace.create` accepts arbitrary paths today, so a root here would be UX scoping, not a boundary — deferred until a deployment needs it.
|
||||
|
||||
@@ -19,5 +19,6 @@
|
||||
## 已知限制与延期工作
|
||||
|
||||
- **不读取 Windows 隐藏属性**——Node 的 dirent 不暴露 `FILE_ATTRIBUTE_HIDDEN`,因此在所有平台上 `hidden` 都意味着点前缀,直到原生探测值回其成本为止。
|
||||
- **名称规范化的卷会使创建路径等式失效**——`createDirectory` 承诺其返回值与该子项下一次的 `entries[].path` 逐字相等;Node 带命名空间的 Win32 路径连末尾点/空格段都按字面存储,但在存储时改写名称的卷(HFS+ 风格卷上的 NFD 规范化)会破坏这一匹配,创建落地随之退化为文档所述的单栏/编辑区回退。
|
||||
- **不枚举盘符根**——Windows 上祖先链止于盘符根;跨盘依赖浏览器 UI 的路径输入入口,而不是这里的枚举原语。
|
||||
- **全盘可浏览**——没有按部署限定的浏览根;`workspace.create` 今天就接受任意路径,这里的根只会是 UX 范围而非边界——等到有部署需要时再做。
|
||||
|
||||
@@ -85,12 +85,12 @@ function foldSeparatorsFor(sep: '\\' | '/'): (value: string) => string {
|
||||
* Lexically normalizes a typed absolute path for comparisons against the
|
||||
* backend's resolved ones (every listing path arrives in the
|
||||
* DirectoryListing contract's canonical shape; only the DRAFT side, the
|
||||
* one path a user types, needs this): collapses
|
||||
* repeated and trailing separators, drops `.` segments, and applies `..`
|
||||
* without ever crossing the root — POSIX's `/`, a drive's `C:`, or UNC's
|
||||
* `\\server\share` pair — mirroring resolve()'s lexical behavior. Expects
|
||||
* separators already folded to `sep` (foldSeparatorsFor); a lexical mirror
|
||||
* only, symlinks are the backend's business.
|
||||
* one path a user types, needs this): collapses repeated and trailing
|
||||
* separators, drops `.` segments, and applies `..` without ever crossing
|
||||
* the root — POSIX's `/`, a drive's `C:`, or UNC's `\\server\share` pair —
|
||||
* mirroring resolve()'s lexical behavior. Expects separators already
|
||||
* folded to `sep` (foldSeparatorsFor); a lexical mirror only, symlinks are
|
||||
* the backend's business.
|
||||
*/
|
||||
function normalizePathFor(sep: '\\' | '/'): (value: string) => string {
|
||||
return (value) => {
|
||||
|
||||
@@ -38,5 +38,6 @@ it('resolves a decorated homedir before stamping listing.home', async () => {
|
||||
const listing = await picked.list()
|
||||
expect(listing.home).toBe(resolve(scratch))
|
||||
expect(listing.path).toBe(listing.home)
|
||||
expect(listing.crumbs.at(-1)!.path).toBe(listing.home)
|
||||
await fiber.dispose()
|
||||
})
|
||||
|
||||
@@ -207,6 +207,10 @@ describe('BrowseDirectoryPicker', () => {
|
||||
expect(created).toBe(join(root, 'fresh'))
|
||||
const listing = await capability.list(root)
|
||||
expect(listing.entries.map(entry => entry.name)).toContain('fresh')
|
||||
// The contract's cross-method equality: the returned path is verbatim
|
||||
// the child's entries[].path (clients anchor the create landing's
|
||||
// selection and focus on it).
|
||||
expect(listing.entries.find(entry => entry.name === 'fresh')!.path).toBe(created)
|
||||
})
|
||||
|
||||
it('refuses an existing child with directory-exists', async () => {
|
||||
|
||||
Reference in New Issue
Block a user