feat(extensions): 取消原生暂存并暴露持久提交状态
This commit is contained in:
@@ -76,7 +76,7 @@ function install() {
|
||||
const selected = detail.value, selectedRegistry = source()
|
||||
void run(async (signal, current) => {
|
||||
const result = await installRelease(selectedRegistry, selected, signal)
|
||||
if (current()) { notice.value = result; refreshCandidates() }
|
||||
if (current() || (signal.aborted && controller?.signal === signal)) { notice.value = result; refreshCandidates() }
|
||||
})
|
||||
}
|
||||
function toggleSource() {
|
||||
|
||||
@@ -8,12 +8,12 @@ import type { CommunityRelease, CommunitySource } from '@/contracts/community'
|
||||
afterEach(() => { vi.unstubAllGlobals(); native.invoke.mockReset() })
|
||||
it('desktop stages through Host without renderer download or legacy installation', async () => {
|
||||
const fetch = vi.fn(); vi.stubGlobal('fetch', fetch)
|
||||
native.invoke.mockResolvedValue({ state: 'staged' })
|
||||
native.invoke.mockImplementation(async command => command === 'extension_stage_prepare' ? 'request-id' : { state: 'staged' })
|
||||
const source: CommunitySource = { id: 'fixture', url: 'https://catalog.example/', keys: [], enabled: true }
|
||||
const release = vector.release as CommunityRelease
|
||||
expect(await installRelease(source, release)).toContain('尚未安装或启用')
|
||||
expect(fetch).not.toHaveBeenCalled()
|
||||
const [command, args] = native.invoke.mock.calls[0]!
|
||||
const [command, args] = native.invoke.mock.calls.find(call => call[0] === 'extension_stage')!
|
||||
expect(command).toBe('extension_stage')
|
||||
expect(args.request.release.signature).toBe(release.signature)
|
||||
expect(args.request.release).not.toHaveProperty('withdrawn')
|
||||
@@ -21,3 +21,14 @@ it('desktop stages through Host without renderer download or legacy installation
|
||||
expect(args.request.release).not.toHaveProperty('release_id')
|
||||
expect(vector.release).toHaveProperty('release_id')
|
||||
})
|
||||
|
||||
it('cancels a prepared request when abort wins before dispatch', async () => {
|
||||
const controller = new AbortController()
|
||||
native.invoke.mockImplementation(async command => {
|
||||
if (command === 'extension_stage_prepare') { controller.abort(); return 'request-id' }
|
||||
return null
|
||||
})
|
||||
await expect(installRelease({ id: 'fixture', url: 'https://catalog.example/', keys: [], enabled: true }, vector.release as CommunityRelease, controller.signal)).rejects.toThrow()
|
||||
expect(native.invoke.mock.calls.some(call => call[0] === 'extension_stage')).toBe(false)
|
||||
expect(native.invoke).toHaveBeenCalledWith('extension_stage_cancel', { requestId: 'request-id' })
|
||||
})
|
||||
|
||||
@@ -96,8 +96,21 @@ export async function installRelease(source: CommunitySource, selected: Communit
|
||||
if (!source.enabled || selected.withdrawn) throw new Error('来源已停用或发行已撤回')
|
||||
const release = { ...selected } as Partial<CommunityRelease>
|
||||
delete release.release_id; delete release.withdrawn; delete release.download_path
|
||||
await invoke('extension_stage', { request: { operation_id: crypto.randomUUID(), source: source.url, release } })
|
||||
return '已校验并暂存到桌面安装库,尚未安装或启用'
|
||||
const operationId = crypto.randomUUID()
|
||||
const requestId = await invoke<string>('extension_stage_prepare')
|
||||
const cancel = () => { void invoke('extension_stage_cancel', { requestId }).catch(() => undefined) }
|
||||
signal?.addEventListener('abort', cancel, { once: true })
|
||||
try {
|
||||
if (signal?.aborted) { cancel(); signal.throwIfAborted() }
|
||||
await invoke('extension_stage', { request: { request_id: requestId, operation_id: operationId, source: source.url, release } })
|
||||
return '已校验并暂存到桌面安装库,尚未安装或启用'
|
||||
} catch (error) {
|
||||
if (signal?.aborted) {
|
||||
const receipt = await invoke<{ state: string } | null>('extension_stage_status', { operationId }).catch(() => null)
|
||||
if (receipt?.state === 'staged') return '取消前已完成暂存,尚未安装或启用'
|
||||
}
|
||||
throw error
|
||||
} finally { signal?.removeEventListener('abort', cancel); cancel() }
|
||||
}
|
||||
const catalog = await fetchCatalog(source, '', selected.type, signal)
|
||||
const release = catalog.items.find(item => item.release_id === selected.release_id)
|
||||
|
||||
Reference in New Issue
Block a user