refactor: share sdk lazy config and cli test helpers

This commit is contained in:
Peter Steinberger
2026-04-04 16:52:45 +09:00
parent 6a55556b83
commit a81cf1da1f
8 changed files with 138 additions and 137 deletions

View File

@@ -0,0 +1,20 @@
import { describe, expect, it, vi } from "vitest";
import { createCachedLazyValueGetter } from "./lazy-value.js";
describe("createCachedLazyValueGetter", () => {
it("memoizes lazy factories", () => {
const resolveSchema = vi.fn(() => ({ type: "object" as const }));
const getSchema = createCachedLazyValueGetter(resolveSchema);
expect(getSchema()).toEqual({ type: "object" });
expect(getSchema()).toEqual({ type: "object" });
expect(resolveSchema).toHaveBeenCalledTimes(1);
});
it("uses the fallback when the lazy value resolves nullish", () => {
const fallback = { type: "object" as const, properties: {} };
const getSchema = createCachedLazyValueGetter<typeof fallback>(() => undefined, fallback);
expect(getSchema()).toBe(fallback);
});
});

View File

@@ -0,0 +1,24 @@
type LazyValue<T> = T | (() => T);
export function createCachedLazyValueGetter<T>(value: LazyValue<T>): () => T;
export function createCachedLazyValueGetter<T>(
value: LazyValue<T | null | undefined>,
fallback: T,
): () => T;
export function createCachedLazyValueGetter<T>(
value: LazyValue<T | null | undefined>,
fallback?: T,
): () => T | undefined {
let resolved = false;
let cached: T | undefined;
return () => {
if (!resolved) {
const nextValue =
typeof value === "function" ? (value as () => T | null | undefined)() : value;
cached = nextValue ?? fallback;
resolved = true;
}
return cached;
};
}

View File

@@ -65,6 +65,7 @@ import type {
SpeechProviderPlugin,
PluginCommandContext,
} from "../plugins/types.js";
import { createCachedLazyValueGetter } from "./lazy-value.js";
export type {
AnyAgentTool,
@@ -154,13 +155,6 @@ type DefinedPluginEntry = {
register: NonNullable<OpenClawPluginDefinition["register"]>;
} & Pick<OpenClawPluginDefinition, "kind">;
/** Resolve either a concrete config schema or a lazy schema factory. */
function resolvePluginConfigSchema(
configSchema: DefinePluginEntryOptions["configSchema"] = emptyPluginConfigSchema,
): OpenClawPluginConfigSchema {
return typeof configSchema === "function" ? configSchema() : configSchema;
}
/**
* Canonical entry helper for non-channel plugins.
*
@@ -176,11 +170,7 @@ export function definePluginEntry({
configSchema = emptyPluginConfigSchema,
register,
}: DefinePluginEntryOptions): DefinedPluginEntry {
let resolvedConfigSchema: OpenClawPluginConfigSchema | undefined;
const getConfigSchema = (): OpenClawPluginConfigSchema => {
resolvedConfigSchema ??= resolvePluginConfigSchema(configSchema);
return resolvedConfigSchema;
};
const getConfigSchema = createCachedLazyValueGetter(configSchema);
return {
id,
name,