Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion packages/client-runtime/src/state/serverConfigProjection.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import type { ServerConfig, ServerConfigStreamEvent } from "@t3tools/contracts";
import {
DEFAULT_SERVER_SETTINGS,
ServerSettings,
type ServerConfig,
type ServerConfigStreamEvent,
} from "@t3tools/contracts";
import * as Option from "effect/Option";
import * as Schema from "effect/Schema";
import { describe, expect, it } from "vite-plus/test";

import { applyServerConfigProjection, reuseUnchanged } from "./serverConfigProjection.ts";

const encodeSettings = Schema.encodeSync(ServerSettings);
const decodeSettings = Schema.decodeSync(ServerSettings);

describe("reuseUnchanged", () => {
const provider = (checkedAt: string, skills: ReadonlyArray<string>) => ({
instanceId: "claude",
Expand Down Expand Up @@ -53,4 +62,20 @@ describe("applyServerConfigProjection settingsUpdated", () => {
expect(nextSettings.providerInstances.codex).toBe(settings.providerInstances.codex);
expect(nextSettings.providerInstances.claude.config.launchArgs).toBe("--verbose");
});

it("keeps the settings object when an update repeats the current settings", () => {
const wire = encodeSettings(DEFAULT_SERVER_SETTINGS);
const decode = () => decodeSettings(structuredClone(wire));
const settings = decode();
const current = Option.some({
config: { settings } as unknown as ServerConfig,
latestEvent: {} as ServerConfigStreamEvent,
source: "live" as const,
});
const next = applyServerConfigProjection(current, {
type: "settingsUpdated",
payload: { settings: decode() },
} as ServerConfigStreamEvent);
expect(Option.getOrThrow(next).config).toBe(Option.getOrThrow(current).config);
});
});
22 changes: 14 additions & 8 deletions packages/client-runtime/src/state/serverConfigProjection.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { ServerConfig, ServerConfigStreamEvent } from "@t3tools/contracts";
import * as Equal from "effect/Equal";
import * as Option from "effect/Option";

export interface ServerConfigProjection {
Expand Down Expand Up @@ -44,6 +45,8 @@ export function reuseUnchanged<T>(previous: unknown, next: T): T {
});
return (same ? previous : merged) as T;
}
// Decoded settings carry Durations, which are fresh instances on every push.
if (Equal.isEqual(next)) return (Equal.equals(previous, next) ? previous : next) as T;
if (Object.getPrototypeOf(next) !== Object.prototype) return next;
const before = previous as Record<string, unknown>;
const after = next as Record<string, unknown>;
Expand Down Expand Up @@ -104,14 +107,17 @@ export function applyServerConfigProjection(
source: "live",
}));
case "settingsUpdated":
return Option.map(current, (projection) => ({
config: {
...projection.config,
settings: reuseUnchanged(projection.config.settings, event.payload.settings),
},
latestEvent: event,
source: "live",
}));
return Option.map(current, (projection) => {
const settings = reuseUnchanged(projection.config.settings, event.payload.settings);
return {
config:
settings === projection.config.settings
? projection.config
: { ...projection.config, settings },
latestEvent: event,
source: "live",
};
});
case "environmentThemesUpdated":
return Option.map(current, (projection) => ({
config: {
Expand Down
Loading