mirror of
https://github.com/stablyai/orca.git
synced 2026-10-24 00:04:01 +00:00
fix(cmd-j): surface cooperative palette failures
This commit is contained in:
@@ -9,10 +9,12 @@ import type { Worktree } from '../../../../shared/worktree/types'
|
||||
const EMPTY_DOCUMENTS: ReadonlyMap<string, PaletteDocument> = new Map()
|
||||
const DOCUMENT_BUILD_TIME_SLICE_MS = 16
|
||||
|
||||
type CompletedBuild = {
|
||||
request: WorktreePaletteDocumentBuildRequest
|
||||
documents: ReadonlyMap<string, PaletteDocument>
|
||||
}
|
||||
type CompletedBuild =
|
||||
| {
|
||||
request: WorktreePaletteDocumentBuildRequest
|
||||
documents: ReadonlyMap<string, PaletteDocument>
|
||||
}
|
||||
| { request: WorktreePaletteDocumentBuildRequest; error: unknown }
|
||||
|
||||
type WorktreePaletteDocumentBuildRequest = {
|
||||
worktrees: readonly Worktree[]
|
||||
@@ -31,17 +33,28 @@ export function useCooperativeWorktreePaletteDocuments(
|
||||
void buildWorktreePaletteDocumentsCooperatively(request.worktrees, request.sources, {
|
||||
shouldContinue: () => current,
|
||||
timeSliceMs: DOCUMENT_BUILD_TIME_SLICE_MS
|
||||
}).then((documents) => {
|
||||
if (current && documents) {
|
||||
setCompleted({ request, documents })
|
||||
}).then(
|
||||
(documents) => {
|
||||
if (current && documents) {
|
||||
setCompleted({ request, documents })
|
||||
}
|
||||
},
|
||||
(error: unknown) => {
|
||||
if (current) {
|
||||
setCompleted({ request, error })
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
return () => {
|
||||
current = false
|
||||
}
|
||||
}, [request])
|
||||
|
||||
return completed?.request === request
|
||||
? { documents: completed.documents, pending: false }
|
||||
: { documents: EMPTY_DOCUMENTS, pending: true }
|
||||
if (completed?.request !== request) {
|
||||
return { documents: EMPTY_DOCUMENTS, pending: true }
|
||||
}
|
||||
if ('error' in completed) {
|
||||
throw completed.error
|
||||
}
|
||||
return { documents: completed.documents, pending: false }
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import { act } from 'react'
|
||||
import { act, Component, type ReactNode } from 'react'
|
||||
import { createRoot, type Root } from 'react-dom/client'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { PaletteDocument } from '@/lib/palette-match/palette-document'
|
||||
@@ -101,6 +101,22 @@ function SearchProbe({ query }: { query: string }): React.JSX.Element {
|
||||
)
|
||||
}
|
||||
|
||||
class ProbeErrorBoundary extends Component<{ children: ReactNode }, { error: Error | null }> {
|
||||
state: { error: Error | null } = { error: null }
|
||||
|
||||
static getDerivedStateFromError(error: Error): { error: Error } {
|
||||
return { error }
|
||||
}
|
||||
|
||||
render(): ReactNode {
|
||||
return this.state.error ? (
|
||||
<output data-error>{this.state.error.message}</output>
|
||||
) : (
|
||||
this.props.children
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
describe('cooperative worktree palette hooks', () => {
|
||||
let container: HTMLDivElement
|
||||
let root: Root
|
||||
@@ -169,4 +185,36 @@ describe('cooperative worktree palette hooks', () => {
|
||||
expect(container.querySelector('output')?.dataset.pending).toBe('false')
|
||||
expect(container.textContent).toBe('current')
|
||||
})
|
||||
|
||||
it('surfaces a current document build failure to the palette error boundary', async () => {
|
||||
cooperativeMocks.buildDocuments.mockRejectedValueOnce(new Error('document build failed'))
|
||||
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => undefined)
|
||||
|
||||
await act(async () =>
|
||||
root.render(
|
||||
<ProbeErrorBoundary>
|
||||
<DocumentProbe worktrees={[makeWorktree(1)]} />
|
||||
</ProbeErrorBoundary>
|
||||
)
|
||||
)
|
||||
|
||||
expect(container.querySelector('[data-error]')?.textContent).toBe('document build failed')
|
||||
consoleError.mockRestore()
|
||||
})
|
||||
|
||||
it('surfaces a current search failure to the palette error boundary', async () => {
|
||||
cooperativeMocks.searchDocuments.mockRejectedValueOnce(new Error('search failed'))
|
||||
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => undefined)
|
||||
|
||||
await act(async () =>
|
||||
root.render(
|
||||
<ProbeErrorBoundary>
|
||||
<SearchProbe query="needle" />
|
||||
</ProbeErrorBoundary>
|
||||
)
|
||||
)
|
||||
|
||||
expect(container.querySelector('[data-error]')?.textContent).toBe('search failed')
|
||||
consoleError.mockRestore()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -9,10 +9,12 @@ import { parseCmdJTaskSourceUrl } from '@/lib/worktree-palette-task-url-match'
|
||||
|
||||
const COOPERATIVE_SEARCH_MIN_WORKTREES = 200
|
||||
|
||||
type CompletedSearch = {
|
||||
request: WorktreePaletteSearchArgs
|
||||
results: PaletteSearchResult[]
|
||||
}
|
||||
type CompletedSearch =
|
||||
| {
|
||||
request: WorktreePaletteSearchArgs
|
||||
results: PaletteSearchResult[]
|
||||
}
|
||||
| { request: WorktreePaletteSearchArgs; error: unknown }
|
||||
|
||||
type CooperativeWorktreePaletteSearch = {
|
||||
pending: boolean
|
||||
@@ -63,11 +65,18 @@ export function useCooperativeWorktreePaletteSearch(
|
||||
let current = true
|
||||
void searchWorktreeDocumentsCooperatively(request, {
|
||||
shouldContinue: () => current
|
||||
}).then((results) => {
|
||||
if (current && results) {
|
||||
setCompleted({ request, results })
|
||||
}).then(
|
||||
(results) => {
|
||||
if (current && results) {
|
||||
setCompleted({ request, results })
|
||||
}
|
||||
},
|
||||
(error: unknown) => {
|
||||
if (current) {
|
||||
setCompleted({ request, error })
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
return () => {
|
||||
current = false
|
||||
}
|
||||
@@ -76,7 +85,11 @@ export function useCooperativeWorktreePaletteSearch(
|
||||
if (immediateResults) {
|
||||
return { pending: false, results: immediateResults }
|
||||
}
|
||||
return completed?.request === request
|
||||
? { pending: false, results: completed.results }
|
||||
: { pending: true, results: [] }
|
||||
if (completed?.request !== request) {
|
||||
return { pending: true, results: [] }
|
||||
}
|
||||
if ('error' in completed) {
|
||||
throw completed.error
|
||||
}
|
||||
return { pending: false, results: completed.results }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user