mirror of
https://github.com/murashit/codex-panel.git
synced 2026-07-22 06:57:10 +00:00
Hide app-server query observer details
This commit is contained in:
parent
f474ff49c7
commit
2ff5c89e2b
15 changed files with 130 additions and 126 deletions
|
|
@ -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<T>;
|
||||
}
|
||||
|
||||
export type AppServerObservedQueryResult<T> = Omit<QueryObserverResult<T>, "data" | "error"> & {
|
||||
readonly data: T | null;
|
||||
readonly error: Error | null;
|
||||
};
|
||||
|
||||
interface AppServerQueryOptions<T> {
|
||||
readonly queryKey: readonly unknown[];
|
||||
readonly queryFn: () => Promise<T>;
|
||||
|
|
@ -94,7 +90,7 @@ export class AppServerQueryCache {
|
|||
|
||||
observeActiveThreadsResult(
|
||||
context: AppServerQueryContext,
|
||||
listener: (result: AppServerObservedQueryResult<readonly Thread[]>) => void,
|
||||
listener: ObservedDataListener<readonly Thread[]>,
|
||||
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<readonly Thread[]>) => void,
|
||||
listener: ObservedDataListener<readonly Thread[]>,
|
||||
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<SharedServerMetadata>) => void,
|
||||
listener: ObservedDataListener<SharedServerMetadata>,
|
||||
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<readonly ModelMetadata[]>) => void,
|
||||
listener: ObservedDataListener<readonly ModelMetadata[]>,
|
||||
options: { emitCurrent?: boolean } = {},
|
||||
): () => void {
|
||||
return this.observeQueryResult(this.modelsQueryOptions(context), cloneModelMetadata, listener, options);
|
||||
|
|
@ -427,7 +423,7 @@ export class AppServerQueryCache {
|
|||
private observeQueryResult<T>(
|
||||
queryOptions: AppServerQueryOptions<T>,
|
||||
clone: (value: T) => T,
|
||||
listener: (result: AppServerObservedQueryResult<T>) => void,
|
||||
listener: ObservedDataListener<T>,
|
||||
options: { emitCurrent?: boolean },
|
||||
): () => void {
|
||||
const observer = new QueryObserver<T>(this.client, {
|
||||
|
|
@ -442,11 +438,11 @@ export class AppServerQueryCache {
|
|||
return unsubscribe;
|
||||
}
|
||||
|
||||
private cloneObservedResult<T>(result: QueryObserverResult<T>, clone: (value: T) => T): AppServerObservedQueryResult<T> {
|
||||
private cloneObservedResult<T>(result: QueryObserverResult<T>, clone: (value: T) => T): ObservedDataResult<T> {
|
||||
return {
|
||||
...result,
|
||||
data: result.data === undefined ? null : clone(result.data),
|
||||
error: result.error instanceof Error ? result.error : null,
|
||||
isFetching: result.isFetching,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
import type { AppServerObservedQueryResult } from "./cache";
|
||||
|
||||
export function observedQueryData<T>(result: AppServerObservedQueryResult<T>): T | null {
|
||||
return result.data;
|
||||
}
|
||||
|
||||
export function observedQueryInitialLoading<T>(result: AppServerObservedQueryResult<T>, currentData: T | null | undefined): boolean {
|
||||
return currentData == null && result.isFetching;
|
||||
}
|
||||
|
||||
export function observedQueryInitialError<T>(result: AppServerObservedQueryResult<T>, currentData: T | null | undefined): Error | null {
|
||||
return currentData == null ? result.error : null;
|
||||
}
|
||||
|
|
@ -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<readonly Thread[]>) => void,
|
||||
options?: { emitCurrent?: boolean },
|
||||
): () => void {
|
||||
observeActiveThreadsResult(listener: ObservedDataListener<readonly Thread[]>, 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<readonly Thread[]>) => void,
|
||||
options?: { emitCurrent?: boolean },
|
||||
): () => void {
|
||||
observeArchivedThreadsResult(listener: ObservedDataListener<readonly Thread[]>, 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<SharedServerMetadata>) => void,
|
||||
options?: { emitCurrent?: boolean },
|
||||
): () => void {
|
||||
observeAppServerMetadataResult(listener: ObservedDataListener<SharedServerMetadata>, 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<readonly ModelMetadata[]>) => void,
|
||||
options?: { emitCurrent?: boolean },
|
||||
): () => void {
|
||||
observeModelsResult(listener: ObservedDataListener<readonly ModelMetadata[]>, options?: { emitCurrent?: boolean }): () => void {
|
||||
return this.observeCurrentContext(
|
||||
(context, contextListener, observeOptions) => this.options.cache.observeModelsResult(context, contextListener, observeOptions),
|
||||
listener,
|
||||
|
|
|
|||
19
src/domain/observed-data.ts
Normal file
19
src/domain/observed-data.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
export interface ObservedDataResult<T> {
|
||||
readonly data: T | null;
|
||||
readonly error: Error | null;
|
||||
readonly isFetching: boolean;
|
||||
}
|
||||
|
||||
export type ObservedDataListener<T> = (result: ObservedDataResult<T>) => void;
|
||||
|
||||
export function observedData<T>(result: ObservedDataResult<T>): T | null {
|
||||
return result.data;
|
||||
}
|
||||
|
||||
export function observedInitialLoading<T>(result: ObservedDataResult<T>, currentData: T | null | undefined): boolean {
|
||||
return currentData == null && result.isFetching;
|
||||
}
|
||||
|
||||
export function observedInitialError<T>(result: ObservedDataResult<T>, currentData: T | null | undefined): Error | null {
|
||||
return currentData == null ? result.error : null;
|
||||
}
|
||||
|
|
@ -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<SharedServerMetadata | null>;
|
||||
observeAppServerMetadataResult(listener: ObservedDataListener<SharedServerMetadata>, options?: { emitCurrent?: boolean }): () => void;
|
||||
modelsSnapshot(): readonly ModelMetadata[] | null;
|
||||
fetchModels(): Promise<readonly ModelMetadata[]>;
|
||||
refreshModels(): Promise<readonly ModelMetadata[]>;
|
||||
observeModelsResult(listener: ObservedDataListener<readonly ModelMetadata[]>, options?: { emitCurrent?: boolean }): () => void;
|
||||
}
|
||||
|
||||
export interface ChatPanelEnvironment {
|
||||
obsidian: {
|
||||
|
|
|
|||
|
|
@ -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<readonly Thread[]>): void => {
|
||||
const data = observedQueryData(result);
|
||||
const receiveThreadResult = (result: ObservedDataResult<readonly Thread[]>): void => {
|
||||
const data = observedData(result);
|
||||
if (data) receiveThreads(data);
|
||||
};
|
||||
const receiveAppServerMetadata = (metadata: SharedServerMetadata): void => {
|
||||
serverActions.metadata.applyAppServerMetadata(metadata);
|
||||
};
|
||||
const receiveAppServerMetadataResult = (result: AppServerObservedQueryResult<SharedServerMetadata>): void => {
|
||||
const data = observedQueryData(result);
|
||||
const receiveAppServerMetadataResult = (result: ObservedDataResult<SharedServerMetadata>): 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<readonly ModelMetadata[]>): void => {
|
||||
const data = observedQueryData(result);
|
||||
const receiveModelsResult = (result: ObservedDataResult<readonly ModelMetadata[]>): void => {
|
||||
const data = observedData(result);
|
||||
if (data) receiveModels(data);
|
||||
};
|
||||
const unsubscribe = (): void => {
|
||||
|
|
|
|||
|
|
@ -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<readonly Thread[]>): void {
|
||||
const data = observedQueryData(result);
|
||||
private receiveObservedThreadsResult(result: ObservedDataResult<readonly Thread[]>): 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();
|
||||
|
|
|
|||
|
|
@ -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<readonly ModelMetadata[]>): void {
|
||||
const data = observedQueryData(result);
|
||||
private receiveObservedModelsResult(result: ObservedDataResult<readonly ModelMetadata[]>): void {
|
||||
const data = observedData(result);
|
||||
if (!data) return;
|
||||
this.models = [...data];
|
||||
this.callbacks.display("helper");
|
||||
}
|
||||
|
||||
private receiveObservedArchivedThreadsResult(result: AppServerObservedQueryResult<readonly Thread[]>): void {
|
||||
const data = observedQueryData(result);
|
||||
private receiveObservedArchivedThreadsResult(result: ObservedDataResult<readonly Thread[]>): void {
|
||||
const data = observedData(result);
|
||||
if (!data) return;
|
||||
this.archivedThreads = [...data];
|
||||
this.archivedThreadsLoaded = true;
|
||||
|
|
|
|||
|
|
@ -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<readonly ModelMetadata[]>, options?: { emitCurrent?: boolean }): () => void;
|
||||
fetchModels(): Promise<readonly ModelMetadata[]>;
|
||||
refreshModels(): Promise<readonly ModelMetadata[]>;
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<readonly Thread[]>) => void;
|
||||
type ThreadListObserver = ObservedDataListener<readonly Thread[]>;
|
||||
|
||||
interface ThreadCatalogQuerySource {
|
||||
activeThreadsSnapshot(): readonly Thread[] | null;
|
||||
archivedThreadsSnapshot(): readonly Thread[] | null;
|
||||
fetchActiveThreads(): Promise<readonly Thread[]>;
|
||||
fetchArchivedThreads(): Promise<readonly Thread[]>;
|
||||
refreshActiveThreads(): Promise<readonly Thread[]>;
|
||||
refreshArchivedThreads(): Promise<readonly Thread[]>;
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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<readonly string[]>({ 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<T>(
|
||||
overrides: Partial<AppServerObservedQueryResult<T>> & Pick<AppServerObservedQueryResult<T>, "data">,
|
||||
): AppServerObservedQueryResult<T> {
|
||||
function observedResult<T>(overrides: Partial<ObservedDataResult<T>> & Pick<ObservedDataResult<T>, "data">): ObservedDataResult<T> {
|
||||
return {
|
||||
error: null,
|
||||
isFetching: false,
|
||||
...overrides,
|
||||
} as AppServerObservedQueryResult<T>;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<readonly Thread[]>) => void;
|
||||
let observedThreadListener!: (result: ObservedDataResult<readonly Thread[]>) => 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<string, (result: AppServerObservedQueryResult<readonly Thread[]>) => void>();
|
||||
const listeners = new Map<string, (result: ObservedDataResult<readonly Thread[]>) => 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<SharedServerMetadata>) => void;
|
||||
let modelObserver!: (result: AppServerObservedQueryResult<readonly ModelMetadata[]>) => void;
|
||||
let metadataObserver!: (result: ObservedDataResult<SharedServerMetadata>) => void;
|
||||
let modelObserver!: (result: ObservedDataResult<readonly ModelMetadata[]>) => void;
|
||||
|
||||
queries.observeAppServerMetadataResult(metadataListener);
|
||||
queries.observeModelsResult(modelListener);
|
||||
|
|
@ -160,8 +161,8 @@ function cacheWith(overrides: Partial<AppServerQueryCache>): AppServerQueryCache
|
|||
} as unknown as AppServerQueryCache;
|
||||
}
|
||||
|
||||
function observedResult<T>(data: T): AppServerObservedQueryResult<T> {
|
||||
return { data, error: null } as AppServerObservedQueryResult<T>;
|
||||
function observedResult<T>(data: T): ObservedDataResult<T> {
|
||||
return { data, error: null, isFetching: false };
|
||||
}
|
||||
|
||||
function thread(id: string): Thread {
|
||||
|
|
|
|||
|
|
@ -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<readonly Thread[]>) => void>();
|
||||
const metadataResultListeners = new Set<(result: AppServerObservedQueryResult<SharedServerMetadata>) => void>();
|
||||
const modelResultListeners = new Set<(result: AppServerObservedQueryResult<readonly ModelMetadata[]>) => void>();
|
||||
const activeThreadResultListeners = new Set<(result: ObservedDataResult<readonly Thread[]>) => void>();
|
||||
const metadataResultListeners = new Set<(result: ObservedDataResult<SharedServerMetadata>) => void>();
|
||||
const modelResultListeners = new Set<(result: ObservedDataResult<readonly ModelMetadata[]>) => void>();
|
||||
const settings = {
|
||||
...DEFAULT_SETTINGS,
|
||||
codexPath: "codex",
|
||||
|
|
@ -1516,19 +1516,12 @@ function chatHost(overrides: ChatHostFixtureOverrides = {}): TestCodexChatHost {
|
|||
};
|
||||
}
|
||||
|
||||
function queryResult<T>(data: T | null): AppServerObservedQueryResult<T> {
|
||||
function queryResult<T>(data: T | null): ObservedDataResult<T> {
|
||||
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<T>;
|
||||
};
|
||||
}
|
||||
|
||||
async function chatView(
|
||||
|
|
|
|||
|
|
@ -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<readonly Thread[]>) => void;
|
||||
let observedThreads!: (result: ObservedDataResult<readonly Thread[]>) => 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<readonly Thread[]>) => void) => {
|
||||
observeActive: vi.fn((listener: (result: ObservedDataResult<readonly Thread[]>) => void) => {
|
||||
observedThreads = listener;
|
||||
return () => undefined;
|
||||
}),
|
||||
|
|
@ -587,12 +587,12 @@ function threadFromRecord(record: Record<string, unknown>): Thread {
|
|||
};
|
||||
}
|
||||
|
||||
function queryResult<T>(data: T | null, error: Error | null = null): AppServerObservedQueryResult<T> {
|
||||
function queryResult<T>(data: T | null, error: Error | null = null): ObservedDataResult<T> {
|
||||
return {
|
||||
data,
|
||||
error,
|
||||
isFetching: false,
|
||||
} as AppServerObservedQueryResult<T>;
|
||||
};
|
||||
}
|
||||
|
||||
function threadFixture(overrides: Record<string, unknown> = {}): Record<string, unknown> {
|
||||
|
|
|
|||
|
|
@ -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<readonly Thread[]>);
|
||||
listener({ data: threads, error: null, isFetching: false } satisfies ObservedDataResult<readonly Thread[]>);
|
||||
};
|
||||
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<readonly Thread[]>);
|
||||
listener({ data: threads, error: null, isFetching: false } satisfies ObservedDataResult<readonly Thread[]>);
|
||||
};
|
||||
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<readonly Thread[]>);
|
||||
listener({ data: threads, error: null, isFetching: false } satisfies ObservedDataResult<readonly Thread[]>);
|
||||
};
|
||||
return () => undefined;
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue