From 2ff5c89e2bb4eb4dd2f4f92e72075265fe0fa11b Mon Sep 17 00:00:00 2001 From: murashit Date: Mon, 22 Jun 2026 20:35:16 +0900 Subject: [PATCH] Hide app-server query observer details --- src/app-server/query/cache.ts | 20 +++++++--------- src/app-server/query/observed-result.ts | 13 ----------- src/app-server/query/shared-queries.ts | 23 +++++------------- src/domain/observed-data.ts | 19 +++++++++++++++ src/features/chat/host/runtime.ts | 25 ++++++++++---------- src/features/chat/host/session-graph.ts | 16 ++++++------- src/features/threads-view/session.ts | 12 +++++----- src/settings/dynamic-data-controller.ts | 12 +++++----- src/settings/host.ts | 16 +++++++++---- src/workspace/thread-catalog.ts | 26 ++++++++++++++++----- tests/app-server/observed-result.test.ts | 22 ++++++++--------- tests/app-server/shared-queries.test.ts | 15 ++++++------ tests/features/chat/view-connection.test.ts | 19 +++++---------- tests/features/threads-view/view.test.ts | 10 ++++---- tests/settings/settings-tab.test.ts | 8 +++---- 15 files changed, 130 insertions(+), 126 deletions(-) delete mode 100644 src/app-server/query/observed-result.ts create mode 100644 src/domain/observed-data.ts diff --git a/src/app-server/query/cache.ts b/src/app-server/query/cache.ts index 442cd156..892ae362 100644 --- a/src/app-server/query/cache.ts +++ b/src/app-server/query/cache.ts @@ -7,6 +7,7 @@ import { readRateLimitMetadataProbe, readSkillMetadataProbe } from "./metadata-p import { runtimeConfigSnapshotFromAppServerConfig } from "../protocol/runtime-config"; import { listThreads } from "../threads"; import type { ModelMetadata } from "../../domain/catalog/metadata"; +import type { ObservedDataListener, ObservedDataResult } from "../../domain/observed-data"; import { createServerDiagnostics, diagnosticProbeError, diagnosticProbeOk, diagnosticsWithProbe } from "../../domain/server/diagnostics"; import type { SharedServerMetadata } from "../../domain/server/metadata"; import type { Thread } from "../../domain/threads/model"; @@ -34,11 +35,6 @@ export interface AppServerQueryClientRunner { ): Promise; } -export type AppServerObservedQueryResult = Omit, "data" | "error"> & { - readonly data: T | null; - readonly error: Error | null; -}; - interface AppServerQueryOptions { readonly queryKey: readonly unknown[]; readonly queryFn: () => Promise; @@ -94,7 +90,7 @@ export class AppServerQueryCache { observeActiveThreadsResult( context: AppServerQueryContext, - listener: (result: AppServerObservedQueryResult) => void, + listener: ObservedDataListener, options: { emitCurrent?: boolean } = {}, ): () => void { return this.observeQueryResult(this.threadListQueryOptions(context, "active"), cloneThreads, listener, options); @@ -102,7 +98,7 @@ export class AppServerQueryCache { observeArchivedThreadsResult( context: AppServerQueryContext, - listener: (result: AppServerObservedQueryResult) => void, + listener: ObservedDataListener, options: { emitCurrent?: boolean } = {}, ): () => void { return this.observeQueryResult(this.threadListQueryOptions(context, "archived"), cloneThreads, listener, options); @@ -187,7 +183,7 @@ export class AppServerQueryCache { observeAppServerMetadataResult( context: AppServerQueryContext, - listener: (result: AppServerObservedQueryResult) => void, + listener: ObservedDataListener, options: { emitCurrent?: boolean } = {}, ): () => void { return this.observeQueryResult(this.appServerMetadataQueryOptions(context), cloneSharedServerMetadata, listener, options); @@ -241,7 +237,7 @@ export class AppServerQueryCache { observeModelsResult( context: AppServerQueryContext, - listener: (result: AppServerObservedQueryResult) => void, + listener: ObservedDataListener, options: { emitCurrent?: boolean } = {}, ): () => void { return this.observeQueryResult(this.modelsQueryOptions(context), cloneModelMetadata, listener, options); @@ -427,7 +423,7 @@ export class AppServerQueryCache { private observeQueryResult( queryOptions: AppServerQueryOptions, clone: (value: T) => T, - listener: (result: AppServerObservedQueryResult) => void, + listener: ObservedDataListener, options: { emitCurrent?: boolean }, ): () => void { const observer = new QueryObserver(this.client, { @@ -442,11 +438,11 @@ export class AppServerQueryCache { return unsubscribe; } - private cloneObservedResult(result: QueryObserverResult, clone: (value: T) => T): AppServerObservedQueryResult { + private cloneObservedResult(result: QueryObserverResult, clone: (value: T) => T): ObservedDataResult { return { - ...result, data: result.data === undefined ? null : clone(result.data), error: result.error instanceof Error ? result.error : null, + isFetching: result.isFetching, }; } diff --git a/src/app-server/query/observed-result.ts b/src/app-server/query/observed-result.ts deleted file mode 100644 index efd458ab..00000000 --- a/src/app-server/query/observed-result.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { AppServerObservedQueryResult } from "./cache"; - -export function observedQueryData(result: AppServerObservedQueryResult): T | null { - return result.data; -} - -export function observedQueryInitialLoading(result: AppServerObservedQueryResult, currentData: T | null | undefined): boolean { - return currentData == null && result.isFetching; -} - -export function observedQueryInitialError(result: AppServerObservedQueryResult, currentData: T | null | undefined): Error | null { - return currentData == null ? result.error : null; -} diff --git a/src/app-server/query/shared-queries.ts b/src/app-server/query/shared-queries.ts index 1b359ba6..7a45fe17 100644 --- a/src/app-server/query/shared-queries.ts +++ b/src/app-server/query/shared-queries.ts @@ -1,7 +1,8 @@ import type { ModelMetadata } from "../../domain/catalog/metadata"; +import type { ObservedDataListener } from "../../domain/observed-data"; import type { SharedServerMetadata } from "../../domain/server/metadata"; import type { Thread } from "../../domain/threads/model"; -import type { AppServerObservedQueryResult, AppServerQueryCache } from "./cache"; +import type { AppServerQueryCache } from "./cache"; import { appServerQueryContextMatches, appServerQueryContextRawEquals, @@ -70,10 +71,7 @@ export class AppServerSharedQueries { return this.options.cache.updateArchivedThreads(this.context(), updater); } - observeActiveThreadsResult( - listener: (result: AppServerObservedQueryResult) => void, - options?: { emitCurrent?: boolean }, - ): () => void { + observeActiveThreadsResult(listener: ObservedDataListener, options?: { emitCurrent?: boolean }): () => void { return this.observeCurrentContext( (context, contextListener, observeOptions) => this.options.cache.observeActiveThreadsResult(context, contextListener, observeOptions), listener, @@ -81,10 +79,7 @@ export class AppServerSharedQueries { ); } - observeArchivedThreadsResult( - listener: (result: AppServerObservedQueryResult) => void, - options?: { emitCurrent?: boolean }, - ): () => void { + observeArchivedThreadsResult(listener: ObservedDataListener, options?: { emitCurrent?: boolean }): () => void { return this.observeCurrentContext( (context, contextListener, observeOptions) => this.options.cache.observeArchivedThreadsResult(context, contextListener, observeOptions), @@ -105,10 +100,7 @@ export class AppServerSharedQueries { return this.runForCurrentContext((context) => this.options.cache.refreshAppServerMetadata(context, options)); } - observeAppServerMetadataResult( - listener: (result: AppServerObservedQueryResult) => void, - options?: { emitCurrent?: boolean }, - ): () => void { + observeAppServerMetadataResult(listener: ObservedDataListener, options?: { emitCurrent?: boolean }): () => void { return this.observeCurrentContext( (context, contextListener, observeOptions) => this.options.cache.observeAppServerMetadataResult(context, contextListener, observeOptions), @@ -129,10 +121,7 @@ export class AppServerSharedQueries { return this.runForCurrentContext((context) => this.options.cache.refreshModels(context)); } - observeModelsResult( - listener: (result: AppServerObservedQueryResult) => void, - options?: { emitCurrent?: boolean }, - ): () => void { + observeModelsResult(listener: ObservedDataListener, options?: { emitCurrent?: boolean }): () => void { return this.observeCurrentContext( (context, contextListener, observeOptions) => this.options.cache.observeModelsResult(context, contextListener, observeOptions), listener, diff --git a/src/domain/observed-data.ts b/src/domain/observed-data.ts new file mode 100644 index 00000000..640cff94 --- /dev/null +++ b/src/domain/observed-data.ts @@ -0,0 +1,19 @@ +export interface ObservedDataResult { + readonly data: T | null; + readonly error: Error | null; + readonly isFetching: boolean; +} + +export type ObservedDataListener = (result: ObservedDataResult) => void; + +export function observedData(result: ObservedDataResult): T | null { + return result.data; +} + +export function observedInitialLoading(result: ObservedDataResult, currentData: T | null | undefined): boolean { + return currentData == null && result.isFetching; +} + +export function observedInitialError(result: ObservedDataResult, currentData: T | null | undefined): Error | null { + return currentData == null ? result.error : null; +} diff --git a/src/features/chat/host/runtime.ts b/src/features/chat/host/runtime.ts index 15a7070f..0401c500 100644 --- a/src/features/chat/host/runtime.ts +++ b/src/features/chat/host/runtime.ts @@ -1,6 +1,8 @@ import type { App, Component, EventRef } from "obsidian"; -import type { AppServerSharedQueries } from "../../../app-server/query/shared-queries"; +import type { ModelMetadata } from "../../../domain/catalog/metadata"; +import type { ObservedDataListener } from "../../../domain/observed-data"; +import type { SharedServerMetadata } from "../../../domain/server/metadata"; import type { ArchiveExportAdapter } from "../../../domain/threads/archive-markdown"; import type { CodexPanelSettings } from "../../../settings/model"; import type { ThreadCatalogActiveReader, ThreadCatalogChatEvents } from "../../../workspace/thread-catalog"; @@ -27,17 +29,16 @@ interface WorkspacePanels { type ChatThreadCatalog = ThreadCatalogActiveReader & ThreadCatalogChatEvents; -type ChatAppServerData = Pick< - AppServerSharedQueries, - | "updateAppServerMetadata" - | "appServerMetadataSnapshot" - | "refreshAppServerMetadata" - | "modelsSnapshot" - | "fetchModels" - | "refreshModels" - | "observeAppServerMetadataResult" - | "observeModelsResult" ->; +interface ChatAppServerData { + updateAppServerMetadata(updater: (metadata: SharedServerMetadata | null) => SharedServerMetadata | null): SharedServerMetadata | null; + appServerMetadataSnapshot(): SharedServerMetadata | null; + refreshAppServerMetadata(options?: { forceSkills?: boolean }): Promise; + observeAppServerMetadataResult(listener: ObservedDataListener, options?: { emitCurrent?: boolean }): () => void; + modelsSnapshot(): readonly ModelMetadata[] | null; + fetchModels(): Promise; + refreshModels(): Promise; + observeModelsResult(listener: ObservedDataListener, options?: { emitCurrent?: boolean }): () => void; +} export interface ChatPanelEnvironment { obsidian: { diff --git a/src/features/chat/host/session-graph.ts b/src/features/chat/host/session-graph.ts index 3ab3b2b0..dd1decba 100644 --- a/src/features/chat/host/session-graph.ts +++ b/src/features/chat/host/session-graph.ts @@ -2,8 +2,8 @@ import { Notice } from "obsidian"; import { ConnectionManager } from "../../../app-server/connection/connection-manager"; import type { AppServerClientAccess } from "../../../app-server/connection/client-access"; -import type { AppServerObservedQueryResult } from "../../../app-server/query/cache"; -import { observedQueryData } from "../../../app-server/query/observed-result"; +import type { ObservedDataResult } from "../../../domain/observed-data"; +import { observedData } from "../../../domain/observed-data"; import { isStaleAppServerSharedQueryContextError } from "../../../app-server/query/shared-queries"; import type { ModelMetadata } from "../../../domain/catalog/metadata"; import type { MessageStreamNoticeSection } from "../domain/message-stream/items"; @@ -404,22 +404,22 @@ function createChatPanelSharedStateBinding( serverActions.threads.applyThreadList(threads); refreshTabHeader(host); }; - const receiveThreadResult = (result: AppServerObservedQueryResult): void => { - const data = observedQueryData(result); + const receiveThreadResult = (result: ObservedDataResult): void => { + const data = observedData(result); if (data) receiveThreads(data); }; const receiveAppServerMetadata = (metadata: SharedServerMetadata): void => { serverActions.metadata.applyAppServerMetadata(metadata); }; - const receiveAppServerMetadataResult = (result: AppServerObservedQueryResult): void => { - const data = observedQueryData(result); + const receiveAppServerMetadataResult = (result: ObservedDataResult): void => { + const data = observedData(result); if (data) receiveAppServerMetadata(data); }; const receiveModels = (models: readonly ModelMetadata[]): void => { dispatch(host.stateStore, { type: "connection/metadata-applied", availableModels: models }); }; - const receiveModelsResult = (result: AppServerObservedQueryResult): void => { - const data = observedQueryData(result); + const receiveModelsResult = (result: ObservedDataResult): void => { + const data = observedData(result); if (data) receiveModels(data); }; const unsubscribe = (): void => { diff --git a/src/features/threads-view/session.ts b/src/features/threads-view/session.ts index 89576197..be4a92b6 100644 --- a/src/features/threads-view/session.ts +++ b/src/features/threads-view/session.ts @@ -1,10 +1,10 @@ import { Notice } from "obsidian"; import type { AppServerClientAccess } from "../../app-server/connection/client-access"; -import type { AppServerObservedQueryResult } from "../../app-server/query/cache"; -import { observedQueryData, observedQueryInitialError, observedQueryInitialLoading } from "../../app-server/query/observed-result"; import { isStaleAppServerSharedQueryContextError } from "../../app-server/query/shared-queries"; import type { ReasoningEffort } from "../../domain/catalog/metadata"; +import type { ObservedDataResult } from "../../domain/observed-data"; +import { observedData, observedInitialError, observedInitialLoading } from "../../domain/observed-data"; import type { Thread } from "../../domain/threads/model"; import type { OpenCodexPanelSnapshot } from "../../workspace/panel-coordinator"; import type { ThreadCatalogActiveReader, ThreadCatalogThreadManagementEvents } from "../../workspace/thread-catalog"; @@ -158,19 +158,19 @@ export class CodexThreadsSession { this.render(); } - private receiveObservedThreadsResult(result: AppServerObservedQueryResult): void { - const data = observedQueryData(result); + private receiveObservedThreadsResult(result: ObservedDataResult): void { + const data = observedData(result); if (data) { this.receiveObservedThreads(data); return; } const currentData = this.currentThreadsData(); - if (observedQueryInitialLoading(result, currentData)) { + if (observedInitialLoading(result, currentData)) { this.status = { kind: "loading", message: "Loading threads..." }; this.render(); return; } - const initialError = observedQueryInitialError(result, currentData); + const initialError = observedInitialError(result, currentData); if (initialError) { this.status = { kind: "error", message: initialError.message }; this.render(); diff --git a/src/settings/dynamic-data-controller.ts b/src/settings/dynamic-data-controller.ts index 1643443f..3aebcb13 100644 --- a/src/settings/dynamic-data-controller.ts +++ b/src/settings/dynamic-data-controller.ts @@ -1,11 +1,11 @@ import type { AppServerClient } from "../app-server/connection/client"; -import type { AppServerObservedQueryResult } from "../app-server/query/cache"; -import { observedQueryData } from "../app-server/query/observed-result"; import { isStaleAppServerSharedQueryContextError } from "../app-server/query/shared-queries"; import { setHookItemEnabled, trustHookItem } from "../app-server/catalog"; import { restoreArchivedThread as restoreArchivedThreadOnAppServer } from "../app-server/threads"; import type { HookItem, ModelMetadata, ReasoningEffort } from "../domain/catalog/metadata"; import { findModelMetadataByIdOrName, sortedModelMetadata, supportedEffortsForModelMetadata } from "../domain/catalog/metadata"; +import type { ObservedDataResult } from "../domain/observed-data"; +import { observedData } from "../domain/observed-data"; import type { Thread } from "../domain/threads/model"; import { threadArchiveDisplayTitle } from "../domain/threads/title"; import { errorMessage } from "../utils"; @@ -124,15 +124,15 @@ export class SettingsDynamicDataController { this.unsubscribeArchivedThreads = null; } - private receiveObservedModelsResult(result: AppServerObservedQueryResult): void { - const data = observedQueryData(result); + private receiveObservedModelsResult(result: ObservedDataResult): void { + const data = observedData(result); if (!data) return; this.models = [...data]; this.callbacks.display("helper"); } - private receiveObservedArchivedThreadsResult(result: AppServerObservedQueryResult): void { - const data = observedQueryData(result); + private receiveObservedArchivedThreadsResult(result: ObservedDataResult): void { + const data = observedData(result); if (!data) return; this.archivedThreads = [...data]; this.archivedThreadsLoaded = true; diff --git a/src/settings/host.ts b/src/settings/host.ts index ba81d1f3..2ef40a97 100644 --- a/src/settings/host.ts +++ b/src/settings/host.ts @@ -1,16 +1,22 @@ -import type { AppServerSharedQueries } from "../app-server/query/shared-queries"; import type { AppServerClientAccess } from "../app-server/connection/client-access"; +import type { ModelMetadata } from "../domain/catalog/metadata"; +import type { ObservedDataListener } from "../domain/observed-data"; import type { CodexPanelSettings } from "./model"; import type { ThreadCatalogArchivedReader, ThreadCatalogThreadDeletes, ThreadCatalogThreadRestores } from "../workspace/thread-catalog"; +interface SettingsAppServerData { + modelsSnapshot(): readonly ModelMetadata[] | null; + observeModelsResult(listener: ObservedDataListener, options?: { emitCurrent?: boolean }): () => void; + fetchModels(): Promise; + refreshModels(): Promise; + notifyContextChanged(): void; +} + export interface SettingsDynamicDataHost { settings: CodexPanelSettings; vaultPath: string; clientAccess: AppServerClientAccess; - appServerData: Pick< - AppServerSharedQueries, - "modelsSnapshot" | "observeModelsResult" | "fetchModels" | "refreshModels" | "notifyContextChanged" - >; + appServerData: SettingsAppServerData; threadCatalog: ThreadCatalogArchivedReader & ThreadCatalogThreadDeletes & ThreadCatalogThreadRestores; } diff --git a/src/workspace/thread-catalog.ts b/src/workspace/thread-catalog.ts index c288f00a..a7f83061 100644 --- a/src/workspace/thread-catalog.ts +++ b/src/workspace/thread-catalog.ts @@ -1,8 +1,22 @@ -import type { AppServerObservedQueryResult } from "../app-server/query/cache"; -import type { AppServerSharedQueries } from "../app-server/query/shared-queries"; +import type { ObservedDataListener } from "../domain/observed-data"; import type { Thread } from "../domain/threads/model"; -type ThreadListObserver = (result: AppServerObservedQueryResult) => void; +type ThreadListObserver = ObservedDataListener; + +interface ThreadCatalogQuerySource { + activeThreadsSnapshot(): readonly Thread[] | null; + archivedThreadsSnapshot(): readonly Thread[] | null; + fetchActiveThreads(): Promise; + fetchArchivedThreads(): Promise; + refreshActiveThreads(): Promise; + refreshArchivedThreads(): Promise; + observeActiveThreadsResult(observer: ThreadListObserver, options?: { emitCurrent?: boolean }): () => void; + observeArchivedThreadsResult(observer: ThreadListObserver, options?: { emitCurrent?: boolean }): () => void; + setActiveThreads(threads: readonly Thread[]): void; + setArchivedThreads(threads: readonly Thread[]): void; + updateActiveThreads(updater: (threads: readonly Thread[] | null) => readonly Thread[] | null): readonly Thread[] | null; + updateArchivedThreads(updater: (threads: readonly Thread[] | null) => readonly Thread[] | null): readonly Thread[] | null; +} interface ThreadSurfaceActions { applyThreadArchived(threadId: string, options?: { closeOpenPanels?: boolean }): void; @@ -10,7 +24,7 @@ interface ThreadSurfaceActions { } export interface ThreadCatalogOptions { - queries: AppServerSharedQueries; + queries: ThreadCatalogQuerySource; surfaces: ThreadSurfaceActions; } @@ -126,7 +140,7 @@ export function createThreadCatalog(options: ThreadCatalogOptions): ThreadCatalo }; } -function recordActiveThread(queries: AppServerSharedQueries, thread: Thread): void { +function recordActiveThread(queries: ThreadCatalogQuerySource, thread: Thread): void { queries.updateActiveThreads((current) => promoteThreadInList(current ?? [], thread)); } @@ -135,7 +149,7 @@ function promoteThreadInList(threads: readonly Thread[], thread: Thread): readon return [thread, ...withoutThread]; } -function refreshArchivedThreadsAfterUnknownArchive(queries: AppServerSharedQueries): void { +function refreshArchivedThreadsAfterUnknownArchive(queries: ThreadCatalogQuerySource): void { // A force refresh can join an older in-flight archived request. Run one more // refresh afterward so an archive recorded during that request is not lost. void queries diff --git a/tests/app-server/observed-result.test.ts b/tests/app-server/observed-result.test.ts index fb557fdd..711d44dd 100644 --- a/tests/app-server/observed-result.test.ts +++ b/tests/app-server/observed-result.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from "vitest"; -import type { AppServerObservedQueryResult } from "../../src/app-server/query/cache"; -import { observedQueryData, observedQueryInitialError, observedQueryInitialLoading } from "../../src/app-server/query/observed-result"; +import type { ObservedDataResult } from "../../src/domain/observed-data"; +import { observedData, observedInitialError, observedInitialLoading } from "../../src/domain/observed-data"; describe("observed query result helpers", () => { it("treats successful empty arrays as current data", () => { @@ -9,29 +9,27 @@ describe("observed query result helpers", () => { const error = new Error("boom"); const failed = observedResult({ data: null, error }); - expect(observedQueryInitialLoading(loading, [])).toBe(false); - expect(observedQueryInitialError(failed, [])).toBeNull(); + expect(observedInitialLoading(loading, [])).toBe(false); + expect(observedInitialError(failed, [])).toBeNull(); }); it("returns initial loading and error only before current data exists", () => { const error = new Error("boom"); - expect(observedQueryInitialLoading(observedResult({ data: null, isFetching: true }), null)).toBe(true); - expect(observedQueryInitialError(observedResult({ data: null, error }), null)).toBe(error); + expect(observedInitialLoading(observedResult({ data: null, isFetching: true }), null)).toBe(true); + expect(observedInitialError(observedResult({ data: null, error }), null)).toBe(error); }); it("projects nullable observed data without reinterpreting empty values", () => { - expect(observedQueryData(observedResult({ data: [] as readonly string[] }))).toEqual([]); - expect(observedQueryData(observedResult({ data: null }))).toBeNull(); + expect(observedData(observedResult({ data: [] as readonly string[] }))).toEqual([]); + expect(observedData(observedResult({ data: null }))).toBeNull(); }); }); -function observedResult( - overrides: Partial> & Pick, "data">, -): AppServerObservedQueryResult { +function observedResult(overrides: Partial> & Pick, "data">): ObservedDataResult { return { error: null, isFetching: false, ...overrides, - } as AppServerObservedQueryResult; + }; } diff --git a/tests/app-server/shared-queries.test.ts b/tests/app-server/shared-queries.test.ts index 3770e6b9..b0325903 100644 --- a/tests/app-server/shared-queries.test.ts +++ b/tests/app-server/shared-queries.test.ts @@ -1,7 +1,8 @@ import { describe, expect, it, vi } from "vitest"; -import type { AppServerObservedQueryResult, AppServerQueryCache } from "../../src/app-server/query/cache"; +import type { AppServerQueryCache } from "../../src/app-server/query/cache"; import { AppServerSharedQueries, StaleAppServerSharedQueryContextError } from "../../src/app-server/query/shared-queries"; +import type { ObservedDataResult } from "../../src/domain/observed-data"; import type { ModelMetadata } from "../../src/domain/catalog/metadata"; import { createServerDiagnostics, diagnosticProbeOk, diagnosticsWithProbe } from "../../src/domain/server/diagnostics"; import type { SharedServerMetadata } from "../../src/domain/server/metadata"; @@ -72,7 +73,7 @@ describe("AppServerSharedQueries", () => { }), context: () => context, }); - let observedThreadListener!: (result: AppServerObservedQueryResult) => void; + let observedThreadListener!: (result: ObservedDataResult) => void; queries.observeActiveThreadsResult(listener); context.codexPath = "codex-b"; @@ -83,7 +84,7 @@ describe("AppServerSharedQueries", () => { it("resubscribes observers when the app-server query context changes", () => { const context = { codexPath: "codex-a", vaultPath: "/vault" }; - const listeners = new Map) => void>(); + const listeners = new Map) => void>(); const queries = new AppServerSharedQueries({ cache: cacheWith({ observeActiveThreadsResult: (queryContext, listener) => { @@ -124,8 +125,8 @@ describe("AppServerSharedQueries", () => { }), context: () => ({ codexPath: "codex", vaultPath: "/vault" }), }); - let metadataObserver!: (result: AppServerObservedQueryResult) => void; - let modelObserver!: (result: AppServerObservedQueryResult) => void; + let metadataObserver!: (result: ObservedDataResult) => void; + let modelObserver!: (result: ObservedDataResult) => void; queries.observeAppServerMetadataResult(metadataListener); queries.observeModelsResult(modelListener); @@ -160,8 +161,8 @@ function cacheWith(overrides: Partial): AppServerQueryCache } as unknown as AppServerQueryCache; } -function observedResult(data: T): AppServerObservedQueryResult { - return { data, error: null } as AppServerObservedQueryResult; +function observedResult(data: T): ObservedDataResult { + return { data, error: null, isFetching: false }; } function thread(id: string): Thread { diff --git a/tests/features/chat/view-connection.test.ts b/tests/features/chat/view-connection.test.ts index cc0b700e..a02dcde5 100644 --- a/tests/features/chat/view-connection.test.ts +++ b/tests/features/chat/view-connection.test.ts @@ -4,9 +4,9 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { DEFAULT_SETTINGS } from "../../../src/settings/model"; import type { CodexChatHost } from "../../../src/features/chat/host/runtime"; -import type { AppServerObservedQueryResult } from "../../../src/app-server/query/cache"; import { StaleAppServerSharedQueryContextError } from "../../../src/app-server/query/shared-queries"; import { modelMetadataFromCatalogModels } from "../../../src/app-server/protocol/catalog"; +import type { ObservedDataResult } from "../../../src/domain/observed-data"; import { createServerDiagnostics } from "../../../src/domain/server/diagnostics"; import type { Thread } from "../../../src/domain/threads/model"; import type { ModelMetadata } from "../../../src/domain/catalog/metadata"; @@ -1358,9 +1358,9 @@ function chatHost(overrides: ChatHostFixtureOverrides = {}): TestCodexChatHost { let activeThreads = overrides.activeSnapshot?.() ?? null; let metadata = overrides.appServerMetadataSnapshot?.() ?? null; const models = overrides.modelsSnapshot?.() ?? null; - const activeThreadResultListeners = new Set<(result: AppServerObservedQueryResult) => void>(); - const metadataResultListeners = new Set<(result: AppServerObservedQueryResult) => void>(); - const modelResultListeners = new Set<(result: AppServerObservedQueryResult) => void>(); + const activeThreadResultListeners = new Set<(result: ObservedDataResult) => void>(); + const metadataResultListeners = new Set<(result: ObservedDataResult) => void>(); + const modelResultListeners = new Set<(result: ObservedDataResult) => void>(); const settings = { ...DEFAULT_SETTINGS, codexPath: "codex", @@ -1516,19 +1516,12 @@ function chatHost(overrides: ChatHostFixtureOverrides = {}): TestCodexChatHost { }; } -function queryResult(data: T | null): AppServerObservedQueryResult { +function queryResult(data: T | null): ObservedDataResult { return { data, error: null, isFetching: false, - isLoading: false, - isPending: data === null, - isSuccess: data !== null, - isError: false, - isStale: false, - status: data === null ? "pending" : "success", - fetchStatus: "idle", - } as AppServerObservedQueryResult; + }; } async function chatView( diff --git a/tests/features/threads-view/view.test.ts b/tests/features/threads-view/view.test.ts index 019aa732..82309e05 100644 --- a/tests/features/threads-view/view.test.ts +++ b/tests/features/threads-view/view.test.ts @@ -3,7 +3,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import { DEFAULT_SETTINGS } from "../../../src/settings/model"; -import type { AppServerObservedQueryResult } from "../../../src/app-server/query/cache"; +import type { ObservedDataResult } from "../../../src/domain/observed-data"; import type { TurnRecord } from "../../../src/app-server/protocol/turn"; import type { Thread } from "../../../src/domain/threads/model"; import type * as ThreadTitleGeneratorModule from "../../../src/app-server/services/thread-title-generation"; @@ -270,7 +270,7 @@ describe("CodexThreadsView", () => { }); it("keeps successful empty thread lists as last-known-good observed data", async () => { - let observedThreads!: (result: AppServerObservedQueryResult) => void; + let observedThreads!: (result: ObservedDataResult) => void; const view = await threadsView( threadsHost({ threadCatalog: { @@ -280,7 +280,7 @@ describe("CodexThreadsView", () => { // Keep the initial refresh pending; this test drives observed query results directly. }), ), - observeActive: vi.fn((listener: (result: AppServerObservedQueryResult) => void) => { + observeActive: vi.fn((listener: (result: ObservedDataResult) => void) => { observedThreads = listener; return () => undefined; }), @@ -587,12 +587,12 @@ function threadFromRecord(record: Record): Thread { }; } -function queryResult(data: T | null, error: Error | null = null): AppServerObservedQueryResult { +function queryResult(data: T | null, error: Error | null = null): ObservedDataResult { return { data, error, isFetching: false, - } as AppServerObservedQueryResult; + }; } function threadFixture(overrides: Record = {}): Record { diff --git a/tests/settings/settings-tab.test.ts b/tests/settings/settings-tab.test.ts index 675e8c93..e22501dc 100644 --- a/tests/settings/settings-tab.test.ts +++ b/tests/settings/settings-tab.test.ts @@ -3,7 +3,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import type { CatalogHookMetadata, CatalogModel } from "../../src/app-server/protocol/catalog"; -import type { AppServerObservedQueryResult } from "../../src/app-server/query/cache"; +import type { ObservedDataResult } from "../../src/domain/observed-data"; import type { AppServerClientAccessOptions } from "../../src/app-server/connection/client-access"; import type { ThreadRecord } from "../../src/app-server/protocol/thread"; import type { ModelMetadata, ReasoningEffort } from "../../src/domain/catalog/metadata"; @@ -324,7 +324,7 @@ describe("settings tab", () => { settingsTabHost({ observeArchived: (listener) => { emitArchived = (threads) => { - listener({ data: threads, error: null } as unknown as AppServerObservedQueryResult); + listener({ data: threads, error: null, isFetching: false } satisfies ObservedDataResult); }; return () => undefined; }, @@ -563,7 +563,7 @@ describe("settings tab", () => { archivedThreads: [panelThread({ id: "thread-old", preview: "Old archived", archived: true })], observeArchived: (listener) => { emitArchived = (threads) => { - listener({ data: threads, error: null } as unknown as AppServerObservedQueryResult); + listener({ data: threads, error: null, isFetching: false } satisfies ObservedDataResult); }; return () => undefined; }, @@ -610,7 +610,7 @@ describe("settings tab", () => { archivedThreads: [panelThread({ id: "thread-old", preview: "Old archived", archived: true })], observeArchived: (listener) => { emitArchived = (threads) => { - listener({ data: threads, error: null } as unknown as AppServerObservedQueryResult); + listener({ data: threads, error: null, isFetching: false } satisfies ObservedDataResult); }; return () => undefined; },