fix(host,client): require fully qualified browse paths; clear the picker kind on close

ds-review-bot round 4. On Windows, isAbsolute admits rooted drive-less
forms (\foo, /foo) that resolve() then rebases onto the process's current
drive; both browse primitives now gate on a fullyQualified check (drive
letter or UNC on win32, POSIX-absolute elsewhere) with a platform test
seam, per-platform unit cases, and the contract wording updated on the
seam, the backend README pair, and the error messages.

The picker-kind effect also kept a resolved 'dialog' across close, so a
backend swapped while the menu was closed could paint the stale entry for
one frame on reopen; the close arm now clears the state, pinned by a
reopen-under-pending-read race test.
This commit is contained in:
creatixchu
2026-07-28 18:13:20 +08:00
parent c4bf919895
commit b211a80b1f
9 changed files with 74 additions and 22 deletions
+4 -3
View File
@@ -60,8 +60,9 @@ export interface DirectoryPickerBrowseCapability {
* List one directory level.
* @param path - absolute directory to list; absent lists the home directory.
* @returns the level's listing with ancestry.
* @throws {DirectoryPickerError} `directory-unreadable` when the target is not absolute
* (a wire value must never rebase under the host cwd) or cannot be listed.
* @throws {DirectoryPickerError} `directory-unreadable` when the target is not fully
* qualified (a wire value must never resolve against the host cwd or, on
* Windows, its current drive) or cannot be listed.
*/
list(path?: string): Promise<DirectoryListing>
/**
@@ -70,7 +71,7 @@ export interface DirectoryPickerBrowseCapability {
* @param name - single non-blank path segment (no separators, not `.`/`..`).
* @returns the created directory's absolute path.
* @throws {DirectoryPickerError} `directory-exists` for an existing child,
* `directory-create-failed` for a non-absolute parent or any other failure.
* `directory-create-failed` for a parent that is not fully qualified or any other failure.
*/
createDirectory(path: string, name: string): Promise<string>
}