fix(session-query): preserve sync error typing
This commit is contained in:
@@ -166,12 +166,18 @@ export class SessionProviderCoordinator {
|
|||||||
private _syncAll(state: ProviderState): Promise<void> {
|
private _syncAll(state: ProviderState): Promise<void> {
|
||||||
// Capture the direct source before awaiting: only searches that observed
|
// Capture the direct source before awaiting: only searches that observed
|
||||||
// the same live corpus may share an in-flight full synchronization.
|
// the same live corpus may share an in-flight full synchronization.
|
||||||
const liveSessions = this._corpus().listLive()
|
let liveSessions: Session[]
|
||||||
const liveKey = JSON.stringify(liveSessions.map(session => this._snapshotLive(session)).map(snapshot => [
|
let liveKey: string
|
||||||
snapshot.session.header.id,
|
try {
|
||||||
snapshot.fingerprint,
|
liveSessions = this._corpus().listLive()
|
||||||
snapshot.session.persisted,
|
liveKey = JSON.stringify(liveSessions.map(session => this._snapshotLive(session)).map(snapshot => [
|
||||||
]))
|
snapshot.session.header.id,
|
||||||
|
snapshot.fingerprint,
|
||||||
|
snapshot.session.persisted,
|
||||||
|
]))
|
||||||
|
} catch (error: unknown) {
|
||||||
|
return Promise.reject(this._synchronizationError(state, error))
|
||||||
|
}
|
||||||
if (state.fullSync?.liveKey === liveKey) return state.fullSync.promise
|
if (state.fullSync?.liveKey === liveKey) return state.fullSync.promise
|
||||||
const promise = this._enqueue(state, async () => {
|
const promise = this._enqueue(state, async () => {
|
||||||
/* v8 ignore next -- a provider can be disposed while queued behind an in-flight update */
|
/* v8 ignore next -- a provider can be disposed while queued behind an in-flight update */
|
||||||
@@ -242,12 +248,16 @@ export class SessionProviderCoordinator {
|
|||||||
const next = state.chain.then(operation, operation)
|
const next = state.chain.then(operation, operation)
|
||||||
state.chain = next.then(() => undefined, () => undefined)
|
state.chain = next.then(() => undefined, () => undefined)
|
||||||
return next.catch((error: unknown) => {
|
return next.catch((error: unknown) => {
|
||||||
/* v8 ignore next -- service-created typed synchronization errors pass through unchanged */
|
throw this._synchronizationError(state, error)
|
||||||
if (error instanceof SessionQueryError) throw error
|
|
||||||
throw new SessionQueryError(`session-query provider "${state.provider.id}" synchronization failed: ${errorMessage(error)}`, 'SESSION_QUERY_INDEX_FAILED', { cause: error })
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _synchronizationError(state: ProviderState, error: unknown): SessionQueryError {
|
||||||
|
/* v8 ignore next -- service-created typed synchronization errors pass through unchanged */
|
||||||
|
if (error instanceof SessionQueryError) return error
|
||||||
|
return new SessionQueryError(`session-query provider "${state.provider.id}" synchronization failed: ${errorMessage(error)}`, 'SESSION_QUERY_INDEX_FAILED', { cause: error })
|
||||||
|
}
|
||||||
|
|
||||||
private _resolveProvider(): ProviderState {
|
private _resolveProvider(): ProviderState {
|
||||||
if (this._configuredProviderId !== undefined) {
|
if (this._configuredProviderId !== undefined) {
|
||||||
const state = this._providers.get(this._configuredProviderId)
|
const state = this._providers.get(this._configuredProviderId)
|
||||||
|
|||||||
@@ -673,6 +673,30 @@ describe('provider selection and synchronization', () => {
|
|||||||
await expect(ctx.sessionQuery.searchSessions({ query: 'x' })).resolves.toMatchObject({ providerId: provider.id })
|
await expect(ctx.sessionQuery.searchSessions({ query: 'x' })).resolves.toMatchObject({ providerId: provider.id })
|
||||||
expect(provider.persisted.get(persisted.id)?.documents[0]?.text).toBe('retry')
|
expect(provider.persisted.get(persisted.id)?.documents[0]?.text).toBe('retry')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('types synchronous extractor failures during full-search key construction', async () => {
|
||||||
|
const ctx = await liveContext()
|
||||||
|
const session = ctx.sessions.create(SessionId('throwing-extractor'))
|
||||||
|
session.append('test/note', { note: 'unreachable' })
|
||||||
|
const provider = new FakeProvider()
|
||||||
|
ctx.sessionQuery.registerSearchProvider(provider)
|
||||||
|
const cause = new Error('custom extractor failed')
|
||||||
|
ctx.sessionQuery.registerEventTextExtractor('test/note', {
|
||||||
|
version: 'throwing-v1',
|
||||||
|
extract: () => { throw cause },
|
||||||
|
})
|
||||||
|
|
||||||
|
let thrown: unknown
|
||||||
|
try {
|
||||||
|
await ctx.sessionQuery.searchSessions({ query: 'x' })
|
||||||
|
} catch (error: unknown) {
|
||||||
|
thrown = error
|
||||||
|
}
|
||||||
|
expect(thrown).toBeInstanceOf(SessionQueryError)
|
||||||
|
expect(thrown).toMatchObject({ code: 'SESSION_QUERY_INDEX_FAILED', cause })
|
||||||
|
expect(asError(thrown).message).toContain(`provider "${provider.id}"`)
|
||||||
|
expect(provider.sessionRequests).toEqual([])
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('semantic text extractors', () => {
|
describe('semantic text extractors', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user