fix(picker): pointer-width vtable offsets, COM apartment pairing, unconditional abort budget, and the full failure chain

Review round two on the in-process dialog:

- Vtable slots and out-pointers use koffi.sizeof('void *') instead of a
  hardcoded 8 - win32-ia32 (which Node and koffi both ship) would have read
  method pointers from the wrong address and crashed in-process before any
  fallback could run.
- runFolderDialog pairs every successful (incl. S_FALSE) CoInitializeEx
  with CoUninitialize in the outermost finally, releasing the dialog first;
  a failed init is deliberately unpaired. Pinned across fake-bindings and
  mocked-koffi suites.
- The abort close budget starts unconditionally: a worker hung before the
  showing notice (koffi import or COM init) now ends in terminate instead
  of a dangling promise; WM_CLOSE posting still waits for the thread id.
- A triple miss (dialog + pwsh + 5.1) surfaces an AggregateError carrying
  all three causes - the in-process tier's reason was previously
  unrecoverable from the final PowerShell error.
- The stray '=>{ ' formatter artifacts are normalized to real blocks.

Both stale note claims from the review are fixed: the DPI note's
Consequences no longer claims an ENOENT classification or zero new
dependencies, and the 2026-07-27 picker note's Windows bullet now names
the in-process primary and keeps the PowerShell chain as fallback (both
languages, pairings re-recorded).
This commit is contained in:
Huanqi Cao
2026-08-03 20:40:10 +08:00
parent fed3149ac4
commit 8500a21658
15 changed files with 147 additions and 50 deletions
@@ -97,10 +97,16 @@ describe('native directory picker', () => {
.mockResolvedValueOnce({ stdout: '', stderr: '' })
await expect(pickNativeDirectory(signal(), { platform: 'win32', run: cancelled, pickWin32Dialog: noDialog })).resolves.toBeNull()
// Triple miss: the surfaced AggregateError carries all three causes,
// including the otherwise-lost in-process dialog failure.
const failed = vi.fn<DirectoryPickerRunner>()
.mockRejectedValueOnce(failure('ENOENT'))
.mockRejectedValueOnce(failure(2))
await expect(pickNativeDirectory(signal(), { platform: 'win32', run: failed, pickWin32Dialog: noDialog })).rejects.toThrow('command failed')
const tripleMiss = await pickNativeDirectory(signal(), { platform: 'win32', run: failed, pickWin32Dialog: noDialog })
.then(() => { throw new Error('expected rejection') }, (error: unknown) => error as AggregateError)
expect(tripleMiss.message).toContain('the in-process dialog and both PowerShell hosts failed')
expect((tripleMiss.errors[0] as Error).message).toBe('dialog unavailable')
expect((tripleMiss.errors[2] as Error).message).toContain('command failed')
})
it('wires the real Win32 dialog as the default tier', async () => {
@@ -153,7 +159,9 @@ describe('native directory picker', () => {
execFileMock.mockImplementationOnce((_command, _args, _options, callback) => {
callback(commandError, 'partial output', 'failure details')
})
await expect(pickNativeDirectory(signal(), { platform: 'win32', pickWin32Dialog: noDialog })).rejects.toMatchObject({
const surfaced = await pickNativeDirectory(signal(), { platform: 'win32', pickWin32Dialog: noDialog })
.then(() => { throw new Error('expected rejection') }, (error: unknown) => error as AggregateError)
expect(surfaced.errors[2]).toMatchObject({
message: 'powershell failed', cause: commandError, code: 7,
stdout: 'partial output', stderr: 'failure details',
})