chore(lint): enable unnecessary type parameter rule

This commit is contained in:
Peter Steinberger
2026-04-18 18:28:20 +01:00
parent 630f2bcabe
commit df525b90f2
94 changed files with 186 additions and 152 deletions

View File

@@ -23,7 +23,7 @@
"typescript/no-meaningless-void-operator": "error",
"typescript/no-unnecessary-type-assertion": "error",
"typescript/no-unnecessary-type-conversion": "error",
"typescript/no-unnecessary-type-parameters": "off",
"typescript/no-unnecessary-type-parameters": "error",
"typescript/no-unsafe-type-assertion": "off",
"typescript/no-useless-default-assignment": "error",
"unicorn/consistent-function-scoping": "off",

View File

@@ -22,6 +22,7 @@ async function normalizeUploadPaths(paths: string[]): Promise<string[]> {
return result.paths;
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Browser request result type is shared between request and success formatter.
async function runBrowserPostAction<T>(params: {
parent: BrowserParentOpts;
profile: string | undefined;

View File

@@ -126,6 +126,7 @@ async function readBrowserProxyFile(filePath: string): Promise<BrowserProxyFile
return { path: filePath, base64: buffer.toString("base64"), mimeType };
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- CLI JSON params are typed by the invoked method.
function decodeParams<T>(raw?: string | null): T {
if (!raw) {
throw new Error("INVALID_REQUEST: paramsJSON required");

View File

@@ -118,7 +118,7 @@ export async function handleDiscordMessagingAction(
: accountId
? { accountId }
: undefined;
const withReactionRuntimeOptions = <T extends Record<string, unknown>>(extra?: T) => ({
const withReactionRuntimeOptions = (extra?: Record<string, unknown>) => ({
...(reactionRuntimeOptions ?? cfgOptions),
...extra,
});

View File

@@ -311,6 +311,7 @@ export function createMentionRequiredGuildConfig(overrides?: Partial<Config>): C
}
export function captureNextDispatchCtx<
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets assertions ascribe captured dispatch context shape.
T extends {
SessionKey?: string;
ParentSessionKey?: string;

View File

@@ -6,8 +6,8 @@ type ReplyThreadingContext = {
ReplyThreading?: ReplyThreadingPolicy;
};
export function applyImplicitReplyBatchGate<T extends object>(
ctx: T,
export function applyImplicitReplyBatchGate(
ctx: object,
replyToMode: ReplyToMode,
isBatched: boolean,
) {
@@ -15,5 +15,5 @@ export function applyImplicitReplyBatchGate<T extends object>(
if (!replyThreading) {
return;
}
(ctx as T & ReplyThreadingContext).ReplyThreading = replyThreading;
(ctx as ReplyThreadingContext).ReplyThreading = replyThreading;
}

View File

@@ -15,6 +15,7 @@ type DiscordSendModule = typeof import("./send.js");
type DiscordSendComponentsModule = typeof import("./send.components.js");
type DiscordThreadBindingsModule = typeof import("./monitor/thread-bindings.js");
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper preserves mock call and result types.
function invokeMock<TArgs extends unknown[], TResult>(
mock: (...args: unknown[]) => unknown,
...args: TArgs

View File

@@ -205,6 +205,7 @@ export function mockResolvedDiscordAccountConfig(overrides: Record<string, unkno
}));
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets assertions ascribe handler params shape.
export function getFirstDiscordMessageHandlerParams<T extends object>() {
expect(createDiscordMessageHandlerMock).toHaveBeenCalledTimes(1);
const firstCall = createDiscordMessageHandlerMock.mock.calls.at(0) as [T] | undefined;

View File

@@ -558,7 +558,10 @@ export function registerFeishuBitableTools(api: OpenClawPluginApi) {
const getClient = (params: AccountAwareParams | undefined, defaultAccountId?: string) =>
createFeishuToolClient({ api, executeParams: params, defaultAccountId });
const registerBitableTool = <TParams extends AccountAwareParams>(params: {
const registerBitableTool = <
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Tool params bind each schema-specific executor to its registered tool.
TParams extends AccountAwareParams,
>(params: {
name: string;
label: string;
description: string;

View File

@@ -413,13 +413,13 @@ async function loadMonitorSingleAccount() {
return module.monitorSingleAccount;
}
export async function setupFeishuLifecycleHandler<T extends RuntimeEnv>(params: {
export async function setupFeishuLifecycleHandler(params: {
createEventDispatcherMock: {
mockReturnValue: (value: unknown) => unknown;
mockReturnValueOnce: (value: unknown) => unknown;
};
onRegister: (registered: Record<string, (data: unknown) => Promise<void>>) => void;
runtime: T;
runtime: RuntimeEnv;
cfg: ClawdbotConfig;
account: ResolvedFeishuAccount;
handlerKey: string;

View File

@@ -32,11 +32,11 @@ type DeviceTokenResponse =
error_uri?: string;
};
function parseJsonResponse<T>(value: unknown): T {
function parseJsonResponse(value: unknown): Record<string, unknown> {
if (!value || typeof value !== "object") {
throw new Error("Unexpected response from GitHub");
}
return value as T;
return value as Record<string, unknown>;
}
async function requestDeviceCode(params: { scope: string }): Promise<DeviceCodeResponse> {
@@ -58,7 +58,7 @@ async function requestDeviceCode(params: { scope: string }): Promise<DeviceCodeR
throw new Error(`GitHub device code failed: HTTP ${res.status}`);
}
const json = parseJsonResponse<DeviceCodeResponse>(await res.json());
const json = parseJsonResponse(await res.json()) as DeviceCodeResponse;
if (!json.device_code || !json.user_code || !json.verification_uri) {
throw new Error("GitHub device code response missing fields");
}
@@ -90,7 +90,7 @@ async function pollForAccessToken(params: {
throw new Error(`GitHub device token failed: HTTP ${res.status}`);
}
const json = parseJsonResponse<DeviceTokenResponse>(await res.json());
const json = parseJsonResponse(await res.json()) as DeviceTokenResponse;
if ("access_token" in json && typeof json.access_token === "string") {
return json.access_token;
}

View File

@@ -22,12 +22,12 @@ vi.mock("openclaw/plugin-sdk/ssrf-runtime", async (importOriginal) => {
});
describe("lmstudio-models", () => {
const asFetch = <T>(mock: T) => mock as unknown as typeof fetch;
const parseJsonRequestBody = <T>(init: RequestInit | undefined): T => {
const asFetch = (mock: unknown) => mock as typeof fetch;
const parseJsonRequestBody = (init: RequestInit | undefined): unknown => {
if (typeof init?.body !== "string") {
throw new Error("Expected request body to be a JSON string");
}
return JSON.parse(init.body) as T;
return JSON.parse(init.body) as unknown;
};
afterEach(() => {
@@ -215,7 +215,7 @@ describe("lmstudio-models", () => {
const loadCall = fetchMock.mock.calls.find((call) => String(call[0]).endsWith("/models/load"));
expect(loadCall).toBeDefined();
const loadInit = loadCall?.[1] as RequestInit;
const loadBody = parseJsonRequestBody<{ context_length: number }>(loadInit);
const loadBody = parseJsonRequestBody(loadInit) as { context_length: number };
expect(loadBody.context_length).toBe(8192);
});
@@ -258,7 +258,7 @@ describe("lmstudio-models", () => {
const loadCall = fetchMock.mock.calls.find((call) => String(call[0]).endsWith("/models/load"));
expect(loadCall).toBeDefined();
const loadInit = loadCall?.[1] as RequestInit;
const loadBody = parseJsonRequestBody<{ context_length: number }>(loadInit);
const loadBody = parseJsonRequestBody(loadInit) as { context_length: number };
expect(loadBody.context_length).toBe(32768);
});
@@ -318,7 +318,7 @@ describe("lmstudio-models", () => {
}),
});
const loadInit = loadCall![1] as RequestInit;
const loadBody = parseJsonRequestBody<{ context_length: number }>(loadInit);
const loadBody = parseJsonRequestBody(loadInit) as { context_length: number };
expect(loadBody.context_length).not.toBe(LMSTUDIO_DEFAULT_LOAD_CONTEXT_LENGTH);
});
@@ -360,7 +360,7 @@ describe("lmstudio-models", () => {
const loadCall = fetchMock.mock.calls.find((call) => String(call[0]).endsWith("/models/load"));
expect(loadCall).toBeDefined();
const loadInit = loadCall?.[1] as unknown as RequestInit;
const loadBody = parseJsonRequestBody<{ context_length: number }>(loadInit);
const loadBody = parseJsonRequestBody(loadInit) as { context_length: number };
expect(loadBody.context_length).toBe(8192);
});

View File

@@ -23,6 +23,7 @@ const previousMatrixEnv = Object.fromEntries(
MATRIX_ENV_KEYS.map((key) => [key, process.env[key]]),
) as Record<(typeof MATRIX_ENV_KEYS)[number], string | undefined>;
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets callers ascribe plugin runtime shape.
function createNonExitingTypedRuntimeEnv<TRuntime>(): TRuntime {
return {
log: vi.fn(),

View File

@@ -124,8 +124,6 @@ export async function runMemoryEmbeddingRetryLoop<T>(params: {
}
}
export function buildTextEmbeddingInputs<T extends MemoryEmbeddingChunk>(
chunks: T[],
): MemoryEmbeddingInput[] {
export function buildTextEmbeddingInputs(chunks: MemoryEmbeddingChunk[]): MemoryEmbeddingInput[] {
return chunks.map((chunk) => chunk.embeddingInput ?? { text: chunk.text });
}

View File

@@ -7,6 +7,7 @@ import {
} from "./api.js";
import { contributeMistralResolvedModelCompat } from "./provider-compat.js";
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets assertions ascribe provider compat shape.
function readCompat<T>(model: unknown): T | undefined {
return (model as { compat?: T }).compat;
}

View File

@@ -119,12 +119,12 @@ export function buildMSTeamsGraphMessageUrls(params: {
return Array.from(new Set(urls));
}
async function fetchGraphCollection<T>(params: {
async function fetchGraphCollection(params: {
url: string;
accessToken: string;
fetchFn?: typeof fetch;
ssrfPolicy?: SsrFPolicy;
}): Promise<{ status: number; items: T[] }> {
}): Promise<{ status: number; items: unknown[] }> {
const fetchFn = params.fetchFn ?? fetch;
const { response, release } = await fetchWithSsrFGuard({
url: params.url,
@@ -141,7 +141,7 @@ async function fetchGraphCollection<T>(params: {
return { status, items: [] };
}
try {
const data = (await response.json()) as { value?: T[] };
const data = (await response.json()) as { value?: unknown[] };
return { status, items: Array.isArray(data.value) ? data.value : [] };
} catch {
return { status, items: [] };
@@ -182,12 +182,12 @@ async function downloadGraphHostedContent(params: {
ssrfPolicy?: SsrFPolicy;
logger?: MSTeamsAttachmentDownloadLogger;
}): Promise<{ media: MSTeamsInboundMedia[]; status: number; count: number }> {
const hosted = await fetchGraphCollection<GraphHostedContent>({
const hosted = (await fetchGraphCollection({
url: `${params.messageUrl}/hostedContents`,
accessToken: params.accessToken,
fetchFn: params.fetchFn,
ssrfPolicy: params.ssrfPolicy,
});
})) as { status: number; items: GraphHostedContent[] };
if (hosted.items.length === 0) {
return { media: [], status: hosted.status, count: 0 };
}

View File

@@ -83,7 +83,7 @@ function graphCollection<T>(...items: T[]) {
return { value: items };
}
function mockGraphCollection<T>(...items: T[]) {
function mockGraphCollection(...items: unknown[]) {
mockJsonFetchResponse(graphCollection(...items));
}

View File

@@ -160,7 +160,7 @@ function writeMatrixQaProgress(message: string) {
process.stderr.write(`[matrix-qa] ${message}\n`);
}
function countMatrixQaStatuses<T extends { status: "fail" | "pass" | "skip" }>(entries: T[]) {
function countMatrixQaStatuses(entries: Array<{ status: "fail" | "pass" | "skip" }>) {
return {
failed: entries.filter((entry) => entry.status === "fail").length,
passed: entries.filter((entry) => entry.status === "pass").length,

View File

@@ -379,10 +379,8 @@ export const slackPlugin: ChannelPlugin<ResolvedSlackAccount, SlackProbe> = crea
}),
resolver: {
resolveTargets: async ({ cfg, accountId, inputs, kind }) => {
const toResolvedTarget = <
T extends { input: string; resolved: boolean; id?: string; name?: string },
>(
entry: T,
const toResolvedTarget = (
entry: { input: string; resolved: boolean; id?: string; name?: string },
note?: string,
) => ({
input: entry.input,

View File

@@ -65,7 +65,10 @@ type SlackSocketShutdownClient = {
};
type Constructor = abstract new (...args: never[]) => unknown;
function isConstructorFunction<T extends Constructor>(value: unknown): value is T {
function isConstructorFunction<
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Constructor guard preserves the requested concrete Slack constructor type.
T extends Constructor,
>(value: unknown): value is T {
return typeof value === "function";
}

View File

@@ -36,7 +36,7 @@ export type XaiCodeExecutionResult = {
export function resolveXaiCodeExecutionConfig(
config?: Record<string, unknown>,
): XaiCodeExecutionConfig {
return coerceXaiToolConfig<XaiCodeExecutionConfig>(config);
return coerceXaiToolConfig(config) as XaiCodeExecutionConfig;
}
export function resolveXaiCodeExecutionModel(config?: Record<string, unknown>): string {

View File

@@ -3,17 +3,17 @@ import { normalizeXaiModelId } from "../model-id.js";
export { isRecord };
export function coerceXaiToolConfig<TConfig extends Record<string, unknown>>(
export function coerceXaiToolConfig(
config: Record<string, unknown> | undefined,
): TConfig {
return isRecord(config) ? (config as TConfig) : ({} as TConfig);
): Record<string, unknown> {
return isRecord(config) ? config : {};
}
export function resolveNormalizedXaiToolModel(params: {
config?: Record<string, unknown>;
defaultModel: string;
}): string {
const value = coerceXaiToolConfig<{ model?: unknown }>(params.config).model;
const value = coerceXaiToolConfig(params.config).model;
return typeof value === "string" && value.trim()
? normalizeXaiModelId(value.trim())
: params.defaultModel;
@@ -23,7 +23,7 @@ export function resolvePositiveIntegerToolConfig(
config: Record<string, unknown> | undefined,
key: string,
): number | undefined {
const raw = coerceXaiToolConfig<Record<string, unknown>>(config)[key];
const raw = coerceXaiToolConfig(config)[key];
if (typeof raw !== "number" || !Number.isFinite(raw)) {
return undefined;
}

View File

@@ -38,7 +38,7 @@ export type XaiXSearchResult = {
};
export function resolveXaiXSearchConfig(config?: Record<string, unknown>): XaiXSearchConfig {
return coerceXaiToolConfig<XaiXSearchConfig>(config);
return coerceXaiToolConfig(config) as XaiXSearchConfig;
}
export function resolveXaiXSearchModel(config?: Record<string, unknown>): string {

View File

@@ -130,6 +130,7 @@ export type ExtensionPackageBoundaryPackageJson = {
devDependencies?: Record<string, string>;
};
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Boundary helper lets callers ascribe JSON file shape.
function readJsonFile<T>(filePath: string): T {
return JSON.parse(readFileSync(filePath, "utf8")) as T;
}

View File

@@ -64,6 +64,7 @@ export type PublishablePluginPackageCandidate<
packageJson: TPackageJson;
};
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Release helper preserves caller-specific package.json shape.
function readPluginPackageJson<TPackageJson extends PluginPackageJson = PluginPackageJson>(
path: string,
): TPackageJson {

View File

@@ -119,8 +119,8 @@ function isStrictOpenAIJsonSchemaCompatibleRecursive(schema: unknown): boolean {
});
}
export function resolveOpenAIStrictToolFlagForInventory<T extends ToolWithParameters>(
tools: readonly T[],
export function resolveOpenAIStrictToolFlagForInventory(
tools: readonly ToolWithParameters[],
strict: boolean | null | undefined,
): boolean | undefined {
if (strict !== true) {

View File

@@ -555,7 +555,7 @@ vi.mock("../compaction-safety-timeout.js", () => ({
vi.mock("../history.js", () => ({
getDmHistoryLimitFromSessionKey: (sessionKey: string | undefined, config: unknown) =>
hoisted.getDmHistoryLimitFromSessionKeyMock(sessionKey, config),
limitHistoryTurns: <T>(messages: T, limit: number | undefined) =>
limitHistoryTurns: (messages: unknown, limit: number | undefined) =>
hoisted.limitHistoryTurnsMock(messages, limit),
}));

View File

@@ -161,10 +161,10 @@ export function resolveSelectedCapabilityProvider<T extends CapabilityProvider>(
});
}
export function resolveCapabilityModelCandidatesForTool<T extends CapabilityProvider>(params: {
export function resolveCapabilityModelCandidatesForTool(params: {
cfg?: OpenClawConfig;
agentDir?: string;
providers: T[];
providers: CapabilityProvider[];
}): string[] {
const providerDefaults = new Map<string, string>();
for (const provider of params.providers) {
@@ -206,11 +206,11 @@ export function resolveCapabilityModelCandidatesForTool<T extends CapabilityProv
return orderedRefs;
}
export function resolveCapabilityModelConfigForTool<T extends CapabilityProvider>(params: {
export function resolveCapabilityModelConfigForTool(params: {
cfg?: OpenClawConfig;
agentDir?: string;
modelConfig?: AgentModelConfig;
providers: T[];
providers: CapabilityProvider[];
}): ToolModelConfig | null {
const explicit = coerceToolModelConfig(params.modelConfig);
if (hasToolModelConfig(explicit)) {

View File

@@ -75,7 +75,7 @@ type WebFetchConfig = NonNullable<OpenClawConfig["tools"]>["web"] extends infer
: undefined;
function resolveFetchConfig(cfg?: OpenClawConfig): WebFetchConfig {
return resolveWebProviderConfig<"fetch", NonNullable<WebFetchConfig>>(cfg, "fetch");
return resolveWebProviderConfig(cfg, "fetch") as NonNullable<WebFetchConfig> | undefined;
}
function resolveFetchEnabled(params: { fetch?: WebFetchConfig; sandboxed?: boolean }): boolean {

View File

@@ -18,8 +18,8 @@ async function readSessionStore(storePath: string): Promise<Record<string, unkno
return JSON.parse(raw) as Record<string, unknown>;
}
function pickFirstStoreEntry<T>(store: Record<string, unknown>): T | undefined {
const entries = Object.values(store) as T[];
function pickFirstStoreEntry(store: Record<string, unknown>): unknown {
const entries = Object.values(store);
return entries[0];
}
@@ -145,7 +145,7 @@ export function registerTriggerHandlingUsageSummaryCases(params: {
expect(replyText(r3)).toContain("Usage footer: tokens");
const finalStore = await readSessionStore(usageStorePath);
expect(pickFirstStoreEntry<{ responseUsage?: string }>(finalStore)?.responseUsage).toBe(
expect((pickFirstStoreEntry(finalStore) as { responseUsage?: string })?.responseUsage).toBe(
"tokens",
);
expect(runEmbeddedPiAgentMock).not.toHaveBeenCalled();

View File

@@ -467,14 +467,14 @@ describe("parseAudioTag", () => {
});
describe("resolveResponsePrefixTemplate", () => {
function expectResolvedTemplateCases<
T extends ReadonlyArray<{
function expectResolvedTemplateCases(
cases: ReadonlyArray<{
name: string;
template: string | undefined;
values: Parameters<typeof resolveResponsePrefixTemplate>[1];
expected: string | undefined;
}>,
>(cases: T) {
) {
for (const testCase of cases) {
expect(resolveResponsePrefixTemplate(testCase.template, testCase.values), testCase.name).toBe(
testCase.expected,

View File

@@ -176,14 +176,14 @@ export function resolveMergedAccountConfig<TConfig extends Record<string, unknow
});
}
export function describeAccountSnapshot<
TAccount extends {
accountId?: string | null;
enabled?: boolean | null;
name?: string | null | undefined;
},
>(params: {
account: TAccount;
type AccountSnapshotInput = {
accountId?: string | null;
enabled?: boolean | null;
name?: string | null | undefined;
};
export function describeAccountSnapshot(params: {
account: AccountSnapshotInput;
configured?: boolean | undefined;
extra?: Record<string, unknown> | undefined;
}): ChannelAccountSnapshot {
@@ -196,14 +196,8 @@ export function describeAccountSnapshot<
};
}
export function describeWebhookAccountSnapshot<
TAccount extends {
accountId?: string | null;
enabled?: boolean | null;
name?: string | null | undefined;
},
>(params: {
account: TAccount;
export function describeWebhookAccountSnapshot(params: {
account: AccountSnapshotInput;
configured?: boolean | undefined;
mode?: string | undefined;
extra?: Record<string, unknown> | undefined;

View File

@@ -21,6 +21,7 @@ export type ChannelRuntimeContextRegistry = {
abortSignal?: AbortSignal;
},
) => { dispose: () => void };
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Runtime context values are caller-typed by key.
get: <T = unknown>(params: ChannelRuntimeContextKey) => T | undefined;
watch: (params: {
channelId?: string;

View File

@@ -29,14 +29,14 @@ export type ChatSenderAllowParams = {
chatIdentifier?: string | null;
};
function isAllowedParsedChatSender<TParsed extends ParsedChatAllowTarget>(params: {
function isAllowedParsedChatSender(params: {
allowFrom: Array<string | number>;
sender: string;
chatId?: number | null;
chatGuid?: string | null;
chatIdentifier?: string | null;
normalizeSender: (sender: string) => string;
parseAllowTarget: (entry: string) => TParsed;
parseAllowTarget: (entry: string) => ParsedChatAllowTarget;
}): boolean {
const allowFrom = normalizeStringEntries(params.allowFrom);
if (allowFrom.length === 0) {
@@ -224,9 +224,9 @@ export function resolveServicePrefixedOrChatAllowTarget<
return null;
}
export function createAllowedChatSenderMatcher<TParsed extends ParsedChatAllowTarget>(params: {
export function createAllowedChatSenderMatcher(params: {
normalizeSender: (sender: string) => string;
parseAllowTarget: (entry: string) => TParsed;
parseAllowTarget: (entry: string) => ParsedChatAllowTarget;
}): (input: ChatSenderAllowParams) => boolean {
return (input) =>
isAllowedParsedChatSender({

View File

@@ -18,9 +18,7 @@ type ExtendableZodObject = ZodTypeAny & {
export const AllowFromEntrySchema = z.union([z.string(), z.number()]);
export const AllowFromListSchema = z.array(AllowFromEntrySchema).optional();
export function buildNestedDmConfigSchema<TExtraShape extends ZodRawShape = {}>(
extraShape?: TExtraShape,
) {
export function buildNestedDmConfigSchema(extraShape?: ZodRawShape) {
const baseShape = {
enabled: z.boolean().optional(),
policy: DmPolicySchema.optional(),

View File

@@ -48,9 +48,9 @@ function listConfigWriteTargetScopes<TChannelId extends string>(
return [target.scope];
}
function resolveChannelConfig<TChannelId extends string>(
function resolveChannelConfig(
cfg: ConfigWritePolicyConfig,
channelId?: TChannelId | null,
channelId?: string | null,
): ChannelConfigWithAccounts | undefined {
if (!channelId) {
return undefined;
@@ -65,9 +65,9 @@ function resolveChannelAccountConfig(
return resolveAccountEntry(channelConfig.accounts, normalizeAccountId(accountId));
}
export function resolveChannelConfigWritesShared<TChannelId extends string>(params: {
export function resolveChannelConfigWritesShared(params: {
cfg: ConfigWritePolicyConfig;
channelId?: TChannelId | null;
channelId?: string | null;
accountId?: string | null;
}): boolean {
const channelConfig = resolveChannelConfig(params.cfg, params.channelId);

View File

@@ -4,6 +4,7 @@ import { normalizeChatType } from "../../chat-type.js";
import { resolveConversationLabel } from "../../conversation-label.js";
import { validateSenderIdentity } from "../../sender-identity.js";
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper preserves channel send mock arg types.
export function primeChannelOutboundSendMock<TArgs extends unknown[]>(
sendMock: Mock<(...args: TArgs) => Promise<unknown>>,
fallbackResult: Record<string, unknown>,

View File

@@ -48,6 +48,7 @@ export async function runCommandWithRuntime(
}
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Commander option values are typed by the caller.
export function resolveOptionFromCommand<T>(
command: Command | undefined,
key: string,

View File

@@ -17,6 +17,7 @@ function getOptionSource(command: Command, name: string): string | undefined {
// Defensive guardrail: allow expected parent/grandparent inheritance without unbounded deep traversal.
const MAX_INHERIT_DEPTH = 2;
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Commander option values are typed by the caller.
export function inheritOptionFromParent<T = unknown>(
command: Command | undefined,
name: string,

View File

@@ -147,6 +147,7 @@ function mockCronEditJobLookup(schedule: unknown): void {
);
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets each assertion ascribe expected RPC params.
function getGatewayCallParams<T>(method: string): T {
const call = callGatewayFromCli.mock.calls.find((entry) => entry[0] === method);
return (call?.[2] ?? {}) as T;

View File

@@ -147,6 +147,7 @@ async function runDaemonCommand(args: string[]) {
await daemonProgram.parseAsync(args, { from: "user" });
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets assertions ascribe logged JSON shape.
function parseFirstJsonRuntimeLine<T>() {
const jsonLine = runtimeLogs.find((line) => line.trim().startsWith("{"));
return JSON.parse(jsonLine ?? "{}") as T;

View File

@@ -30,6 +30,7 @@ let runServiceRestart: typeof import("./lifecycle-core.js").runServiceRestart;
let runServiceStart: typeof import("./lifecycle-core.js").runServiceStart;
let runServiceStop: typeof import("./lifecycle-core.js").runServiceStop;
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets assertions ascribe logged JSON shape.
function readJsonLog<T extends object>() {
const jsonLine = runtimeLogs.find((line) => line.trim().startsWith("{"));
return JSON.parse(jsonLine ?? "{}") as T;

View File

@@ -141,11 +141,11 @@ function parseEnumOption<T extends string>(
return (allowed as readonly string[]).includes(raw) ? (raw as T) : null;
}
function formatModeChoices<T extends string>(modes: readonly T[]): string {
function formatModeChoices(modes: readonly string[]): string {
return modes.map((mode) => `"${mode}"`).join("|");
}
function formatModeErrorList<T extends string>(modes: readonly T[]): string {
function formatModeErrorList(modes: readonly string[]): string {
const quoted = modes.map((mode) => `"${mode}"`);
if (quoted.length === 0) {
return "";

View File

@@ -15,6 +15,7 @@ type ListMarketplacePluginsFn =
type ResolveMarketplaceInstallShortcutFn =
(typeof import("../plugins/marketplace.js"))["resolveMarketplaceInstallShortcut"];
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper preserves mock call and result types.
function invokeMock<TArgs extends unknown[], TResult>(mock: unknown, ...args: TArgs): TResult {
return (mock as (...args: TArgs) => TResult)(...args);
}

View File

@@ -10,14 +10,12 @@ export type CommandDescriptorCatalog<TDescriptor extends NamedCommandDescriptor>
getCommandsWithSubcommands: () => string[];
};
export function getCommandDescriptorNames<TDescriptor extends CommandDescriptorLike>(
descriptors: readonly TDescriptor[],
): string[] {
export function getCommandDescriptorNames(descriptors: readonly CommandDescriptorLike[]): string[] {
return descriptors.map((descriptor) => descriptor.name);
}
export function getCommandsWithSubcommands<TDescriptor extends NamedCommandDescriptor>(
descriptors: readonly TDescriptor[],
export function getCommandsWithSubcommands(
descriptors: readonly NamedCommandDescriptor[],
): string[] {
return descriptors
.filter((descriptor) => descriptor.hasSubcommands)
@@ -52,9 +50,9 @@ export function defineCommandDescriptorCatalog<TDescriptor extends NamedCommandD
};
}
export function addCommandDescriptorsToProgram<TDescriptor extends CommandDescriptorLike>(
export function addCommandDescriptorsToProgram(
program: Command,
descriptors: readonly TDescriptor[],
descriptors: readonly CommandDescriptorLike[],
existingCommands: Set<string> = new Set(),
): Set<string> {
for (const descriptor of descriptors) {

View File

@@ -46,8 +46,8 @@ export function resolveCommandGroupEntries<TDescriptor extends NamedCommandDescr
}));
}
export function buildCommandGroupEntries<TDescriptor extends NamedCommandDescriptor, TRegister>(
descriptors: readonly TDescriptor[],
export function buildCommandGroupEntries<TRegister>(
descriptors: readonly NamedCommandDescriptor[],
specs: readonly CommandGroupDescriptorSpec<TRegister>[],
mapRegister: (register: TRegister) => CommandGroupEntry["register"],
): CommandGroupEntry[] {

View File

@@ -27,10 +27,10 @@ function getDeclaredCommandJsonMode(command: Command): JsonMode | null {
function commandSelectedJsonFlag(command: Command, argv: string[]): boolean {
const commandWithGlobals = command as Command & {
optsWithGlobals?: <T extends Record<string, unknown>>() => T;
optsWithGlobals?: () => Record<string, unknown>;
};
if (typeof commandWithGlobals.optsWithGlobals === "function") {
const resolved = commandWithGlobals.optsWithGlobals<Record<string, unknown>>().json;
const resolved = commandWithGlobals.optsWithGlobals().json;
if (resolved === true) {
return true;
}

View File

@@ -89,6 +89,7 @@ export function spyRuntimeJson(runtime: Pick<OutputRuntimeEnv, "writeJson">) {
return vi.spyOn(runtime, "writeJson").mockImplementation(() => {});
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets callers ascribe captured JSON shape.
export function firstWrittenJsonArg<T>(writeJson: MockCallsWithFirstArg): T | null {
return (writeJson.mock.calls[0]?.[0] ?? null) as T | null;
}

View File

@@ -313,6 +313,7 @@ describe("channels command", () => {
setMinimalChannelsCommandRegistryForTests();
});
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets assertions ascribe written config shape.
function getWrittenConfig<T>(): T {
expect(configMocks.writeConfigFile).toHaveBeenCalledTimes(1);
return configMocks.writeConfigFile.mock.calls[0]?.[0] as T;

View File

@@ -53,6 +53,7 @@ function resolveTestConfigPath() {
return path.join(stateDir, "openclaw.json");
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets assertions ascribe stored config shape.
function readTestConfig<T = OpenClawConfig>(): T {
return (testConfigStore.get(resolveTestConfigPath()) ?? {}) as T;
}

View File

@@ -28,10 +28,10 @@ export type ContainerItem = {
lastUsedAtMs: number;
};
export function countRunning<T extends { running: boolean }>(items: T[]): number {
export function countRunning(items: readonly { running: boolean }[]): number {
return items.filter((item) => item.running).length;
}
export function countMismatches<T extends { imageMatch: boolean }>(items: T[]): number {
export function countMismatches(items: readonly { imageMatch: boolean }[]): number {
return items.filter((item) => !item.imageMatch).length;
}

View File

@@ -35,7 +35,7 @@ type JsonSchemaObject = Record<string, unknown> & {
const LEGACY_HIDDEN_PUBLIC_PATHS = ["hooks.internal.handlers"] as const;
const asJsonSchemaObject = (value: unknown): JsonSchemaObject | null =>
asSchemaObject<JsonSchemaObject>(value);
asSchemaObject(value) as JsonSchemaObject | null;
function buildFieldDocumentation(): FieldDocumentation {
const titles: Record<string, string> = {};

View File

@@ -15,11 +15,11 @@ export function cloneSchema<T>(value: T): T {
return JSON.parse(JSON.stringify(value)) as T;
}
export function asSchemaObject<T extends object>(value: unknown): T | null {
export function asSchemaObject(value: unknown): object | null {
if (!value || typeof value !== "object" || Array.isArray(value)) {
return null;
}
return value as T;
return value;
}
export function schemaHasChildren(schema: JsonSchemaObject): boolean {

View File

@@ -28,7 +28,7 @@ type JsonSchemaObject = JsonSchemaNode & {
};
const asJsonSchemaObject = (value: unknown): JsonSchemaObject | null =>
asSchemaObject<JsonSchemaObject>(value);
asSchemaObject(value) as JsonSchemaObject | null;
const FORBIDDEN_LOOKUP_SEGMENTS = new Set(["__proto__", "prototype", "constructor"]);
const LOOKUP_SCHEMA_STRING_KEYS = new Set([

View File

@@ -1033,6 +1033,7 @@ export async function connectWebchatClient(params: {
return ws;
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Gateway test RPC helper lets callers ascribe response payload shape.
export async function rpcReq<T extends Record<string, unknown>>(
ws: WebSocket,
method: string,

View File

@@ -25,6 +25,7 @@ export async function importFileModule(params: {
return (await import(specifier)) as ModuleNamespace;
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Dynamic module exports are typed by the caller.
export function resolveFunctionModuleExport<T extends GenericFunction>(params: {
mod: ModuleNamespace;
exportName?: string;

View File

@@ -48,6 +48,7 @@ export function registerChannelRuntimeContext(
});
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Runtime context values are caller-typed by key.
export function getChannelRuntimeContext<T = unknown>(
params: ChannelRuntimeContextKey & {
channelRuntime?: ChannelRuntimeSurface;

View File

@@ -94,7 +94,10 @@ function formatRequestedSource(params: {
type ExecPolicyField = "security" | "ask" | "askFallback";
function resolveRequestedField<TValue extends ExecSecurity | ExecAsk>(params: {
function resolveRequestedField<
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Field-specific callers narrow the shared requested policy value.
TValue extends ExecSecurity | ExecAsk,
>(params: {
field: ExecPolicyRequestedField;
scopeExecConfig?: ExecPolicyConfig;
globalExecConfig?: ExecPolicyConfig;

View File

@@ -96,6 +96,7 @@ function writeTempJsonFile(pathname: string, payload: string) {
}
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- JSON loading helper lets callers ascribe the expected payload type.
export function loadJsonFile<T = unknown>(pathname: string): T | undefined {
try {
const raw = fs.readFileSync(pathname, "utf8");

View File

@@ -26,6 +26,7 @@ export type ResolveOutboundSendDepOptions = {
legacyKeys?: readonly string[];
};
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Channel-specific dependency lookup returns caller-typed values.
export function resolveOutboundSendDep<T>(
deps: OutboundSendDeps | null | undefined,
channelId: string,

View File

@@ -560,6 +560,7 @@ export async function handleInvoke(
});
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- CLI JSON params are typed by the invoked method.
function decodeParams<T>(raw?: string | null): T {
if (!raw) {
throw new Error("INVALID_REQUEST: paramsJSON required");

View File

@@ -81,14 +81,14 @@ type ParsedChatAllowTarget =
| { kind: "handle"; handle: string };
/** Match chat-aware allowlist entries against sender, chat id, guid, or identifier fields. */
export function isAllowedParsedChatSender<TParsed extends ParsedChatAllowTarget>(params: {
export function isAllowedParsedChatSender(params: {
allowFrom: Array<string | number>;
sender: string;
chatId?: number | null;
chatGuid?: string | null;
chatIdentifier?: string | null;
normalizeSender: (sender: string) => string;
parseAllowTarget: (entry: string) => TParsed;
parseAllowTarget: (entry: string) => ParsedChatAllowTarget;
}): boolean {
const allowFrom = params.allowFrom.map((entry) => String(entry).trim());
if (allowFrom.length === 0) {

View File

@@ -214,6 +214,7 @@ export function adaptScopedAccountAccessor<Result, Config extends OpenClawConfig
/** Build the shared allowlist/default target adapter surface for account-scoped channel configs. */
export function createScopedAccountConfigAccessors<
ResolvedAccount,
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Config preserves caller-specific config subtype for account resolvers.
Config extends OpenClawConfig = OpenClawConfig,
>(params: {
resolveAccount: (params: { cfg: Config; accountId?: string | null }) => ResolvedAccount;

View File

@@ -335,6 +335,7 @@ function loadBundledEntryModuleSync(importMetaUrl: string, specifier: string): u
return loaded;
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Dynamic entry export loaders use caller-supplied export types.
export function loadBundledEntryExportSync<T>(
importMetaUrl: string,
reference: BundledEntryModuleRef,

View File

@@ -56,9 +56,7 @@ export function buildPassiveProbedChannelStatusSummary<TExtra extends object>(
};
}
export function buildTrafficStatusSummary<TSnapshot extends TrafficStatusSnapshot>(
snapshot?: TSnapshot | null,
) {
export function buildTrafficStatusSummary(snapshot?: TrafficStatusSnapshot | null) {
return {
lastInboundAt: snapshot?.lastInboundAt ?? null,
lastOutboundAt: snapshot?.lastOutboundAt ?? null,

View File

@@ -257,6 +257,7 @@ export function loadFacadeModuleAtLocationSync<T extends object>(params: {
return sentinel;
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Dynamic facade loaders use caller-supplied module surface types.
export function loadBundledPluginPublicSurfaceModuleSync<T extends object>(params: {
dirName: string;
artifactBasename: string;

View File

@@ -239,6 +239,7 @@ function buildFacadeActivationCheckParams(
};
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Dynamic facade loaders use caller-supplied module surface types.
export function loadBundledPluginPublicSurfaceModuleSync<T extends object>(
params: BundledPluginPublicSurfaceParams,
): T {
@@ -269,6 +270,7 @@ export function canLoadActivatedBundledPluginPublicSurface(params: {
).allowed;
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Dynamic facade loaders use caller-supplied module surface types.
export function loadActivatedBundledPluginPublicSurfaceModuleSync<T extends object>(params: {
dirName: string;
artifactBasename: string;
@@ -280,6 +282,7 @@ export function loadActivatedBundledPluginPublicSurfaceModuleSync<T extends obje
return loadBundledPluginPublicSurfaceModuleSync<T>(params);
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Dynamic facade loaders use caller-supplied module surface types.
export function tryLoadActivatedBundledPluginPublicSurfaceModuleSync<T extends object>(params: {
dirName: string;
artifactBasename: string;

View File

@@ -62,6 +62,7 @@ export function loadQaRuntimeModule(): QaRuntimeSurface {
});
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- QA runtime loader uses caller-supplied test API surface type.
export function loadQaRunnerBundledPluginTestApi<T extends object>(pluginId: string): T {
const env = resolvePrivateQaBundledPluginsEnv();
return loadBundledPluginPublicSurfaceModuleSync<T>({

View File

@@ -40,6 +40,7 @@ const ALLOWED_PACKAGE_SUFFIXES = [
"-media-understanding",
] as const;
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets assertions ascribe JSON file shape.
function readJsonFile<T>(filePath: string): T {
return JSON.parse(fs.readFileSync(filePath, "utf8")) as T;
}

View File

@@ -12,25 +12,20 @@ type EnableStateParamsLike = {
type PluginKindLike = string | readonly string[] | undefined;
export function toEnableStateResult<TState extends EnableStateLike>(
state: TState,
): { enabled: boolean; reason?: string } {
export function toEnableStateResult(state: EnableStateLike): { enabled: boolean; reason?: string } {
return state.enabled ? { enabled: true } : { enabled: false, reason: state.reason };
}
export function resolveEnableStateResult<TParams, TState extends EnableStateLike>(
export function resolveEnableStateResult<TParams>(
params: TParams,
resolveState: (params: TParams) => TState,
resolveState: (params: TParams) => EnableStateLike,
): { enabled: boolean; reason?: string } {
return toEnableStateResult(resolveState(params));
}
export function resolveEnableStateShared<
TParams extends EnableStateParamsLike,
TState extends EnableStateLike,
>(
export function resolveEnableStateShared<TParams extends EnableStateParamsLike>(
params: TParams,
resolveState: (params: TParams) => TState,
resolveState: (params: TParams) => EnableStateLike,
): { enabled: boolean; reason?: string } {
return resolveEnableStateResult(params, resolveState);
}

View File

@@ -38,6 +38,7 @@ type PackageJson = {
devDependencies?: Record<string, string>;
};
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets assertions ascribe JSON file shape.
function readJsonFile<T>(relativePath: string): T {
return JSON.parse(readFileSync(resolve(REPO_ROOT, relativePath), "utf8")) as T;
}

View File

@@ -455,6 +455,7 @@ export function createHookRunner(
async function runClaimingHookForPluginOutcome<
K extends PluginHookName,
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Targeted hook outcomes preserve caller-specific handled result types.
TResult extends { handled: boolean },
>(
hookName: K,

View File

@@ -6,6 +6,7 @@ export type LazyPluginServiceHandle = {
stop: () => Promise<void>;
};
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Dynamic service exports are typed by the caller.
function resolveExport<T>(mod: LazyServiceModule, names: string[]): T | null {
for (const name of names) {
const value = mod[name];

View File

@@ -141,6 +141,7 @@ function getSharedBundledPublicSurfaceJiti(modulePath: string, tryNative: boolea
});
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Dynamic public artifact loaders use caller-supplied module surface types.
export function loadBundledPluginPublicArtifactModuleSync<T extends object>(params: {
dirName: string;
artifactBasename: string;

View File

@@ -654,14 +654,11 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) {
});
};
const registerUniqueProviderLike = <
T extends { id: string },
R extends PluginOwnedProviderRegistration<T>,
>(params: {
const registerUniqueProviderLike = <T extends { id: string }>(params: {
record: PluginRecord;
provider: T;
kindLabel: string;
registrations: R[];
registrations: Array<PluginOwnedProviderRegistration<T>>;
ownedIds: string[];
}) => {
const id = params.provider.id.trim();
@@ -694,7 +691,7 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) {
provider: params.provider,
source: record.source,
rootDir: record.rootDir,
} as R);
});
};
const registerSpeechProvider = (record: PluginRecord, provider: SpeechProviderPlugin) => {

View File

@@ -1,8 +1,4 @@
export function defineCachedValue<T extends object, K extends PropertyKey>(
target: T,
key: K,
create: () => unknown,
): void {
export function defineCachedValue(target: object, key: PropertyKey, create: () => unknown): void {
let cached: unknown;
let ready = false;
Object.defineProperty(target, key, {

View File

@@ -314,6 +314,7 @@ export function createRuntimeChannel(): PluginRuntime["channel"] {
});
return { dispose };
},
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Runtime context values are caller-typed by key.
get: <T = unknown>(params: PluginRuntimeChannelContextKey) => {
const normalized = normalizeRuntimeContextKey(params);
if (!normalized) {

View File

@@ -127,6 +127,7 @@ export function getPluginBoundaryJiti(modulePath: string, loaders: PluginJitiLoa
});
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Dynamic plugin boundary loaders use caller-supplied module types.
export function loadPluginBoundaryModuleWithJiti<TModule>(
modulePath: string,
loaders: PluginJitiLoaderCache,
@@ -134,6 +135,7 @@ export function loadPluginBoundaryModuleWithJiti<TModule>(
return getPluginBoundaryJiti(modulePath, loaders)(modulePath) as TModule;
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Dynamic plugin boundary loaders use caller-supplied module types.
export function createCachedPluginBoundaryModuleLoader<TModule>(
params: CachedPluginBoundaryLoaderParams,
): () => TModule | null {

View File

@@ -60,6 +60,7 @@ export type PluginRuntimeChannelContextRegistry = {
abortSignal?: AbortSignal;
},
) => { dispose: () => void };
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Runtime context values are caller-typed by key.
get: <T = unknown>(params: PluginRuntimeChannelContextKey) => T | undefined;
watch: (params: {
channelId?: string;

View File

@@ -164,11 +164,8 @@ export function buildWebProviderSnapshotCacheKey(params: {
});
}
export function mapRegistryProviders<
TProvider extends { id: string },
TEntry extends { pluginId: string; provider: TProvider },
>(params: {
entries: readonly TEntry[];
export function mapRegistryProviders<TProvider extends { id: string }>(params: {
entries: readonly { pluginId: string; provider: TProvider }[];
onlyPluginIds?: readonly string[];
sortProviders: (
providers: Array<TProvider & { pluginId: string }>,

View File

@@ -97,9 +97,9 @@ export function ensureObject(
return next;
}
export function normalizeKnownProvider<TProvider extends { id: string }>(
export function normalizeKnownProvider(
value: unknown,
providers: TProvider[],
providers: Array<{ id: string }>,
): string | undefined {
const normalized = normalizeOptionalLowercaseString(value);
if (!normalized) {

View File

@@ -60,6 +60,7 @@ function serializeJson(value: unknown): string | null {
return value === undefined ? null : JSON.stringify(value);
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Persisted JSON columns are typed by the receiving field.
function parseJsonValue<T>(raw: string | null): T | undefined {
if (!raw?.trim()) {
return undefined;

View File

@@ -78,6 +78,7 @@ function serializeJson(value: unknown): string | null {
return value == null ? null : JSON.stringify(value);
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Persisted JSON columns are typed by the receiving field.
function parseJsonValue<T>(raw: string | null): T | undefined {
if (!raw?.trim()) {
return undefined;

View File

@@ -102,6 +102,7 @@ function resolveWorkspacePackageDir(packageName: string): string {
throw new Error(`Unknown workspace package: ${packageName}`);
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test loaders use caller-supplied module surface types.
export function loadBundledPluginPublicSurfaceSync<T extends object>(params: {
pluginId: string;
artifactBasename: string;
@@ -113,6 +114,7 @@ export function loadBundledPluginPublicSurfaceSync<T extends object>(params: {
});
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test loaders use caller-supplied module surface types.
export function loadBundledPluginApiSync<T extends object>(pluginId: string): T {
return loadBundledPluginPublicSurfaceSync<T>({
pluginId,
@@ -120,6 +122,7 @@ export function loadBundledPluginApiSync<T extends object>(pluginId: string): T
});
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test loaders use caller-supplied module surface types.
export function loadBundledPluginContractApiSync<T extends object>(pluginId: string): T {
return loadBundledPluginPublicSurfaceSync<T>({
pluginId,
@@ -127,6 +130,7 @@ export function loadBundledPluginContractApiSync<T extends object>(pluginId: str
});
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test loaders use caller-supplied module surface types.
export function loadBundledPluginRuntimeApiSync<T extends object>(pluginId: string): T {
return loadBundledPluginPublicSurfaceSync<T>({
pluginId,
@@ -134,6 +138,7 @@ export function loadBundledPluginRuntimeApiSync<T extends object>(pluginId: stri
});
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test loaders use caller-supplied module surface types.
export function loadBundledPluginTestApiSync<T extends object>(pluginId: string): T {
return loadBundledPluginPublicSurfaceSync<T>({
pluginId,

View File

@@ -46,6 +46,7 @@ export function escapeRegExp(value: string): string {
/**
* Safely parse JSON, returning null on error instead of throwing.
*/
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- JSON parsing helper lets callers ascribe the expected payload type.
export function safeParseJson<T>(raw: string): T | null {
try {
return JSON.parse(raw) as T;

View File

@@ -42,7 +42,7 @@ export function resolveWebFetchEnabled(params: {
}
function resolveFetchConfig(config: OpenClawConfig | undefined): WebFetchConfig | undefined {
return resolveWebProviderConfig<"fetch", NonNullable<WebFetchConfig>>(config, "fetch");
return resolveWebProviderConfig(config, "fetch") as NonNullable<WebFetchConfig> | undefined;
}
function hasEntryCredential(
@@ -141,10 +141,9 @@ export function resolveWebFetchProviderId(params: {
export function resolveWebFetchDefinition(
options?: ResolveWebFetchDefinitionParams,
): { provider: PluginWebFetchProviderEntry; definition: WebFetchProviderToolDefinition } | null {
const fetch = resolveWebProviderConfig<"fetch", NonNullable<WebFetchConfig>>(
options?.config,
"fetch",
);
const fetch = resolveWebProviderConfig(options?.config, "fetch") as
| NonNullable<WebFetchConfig>
| undefined;
const runtimeWebFetch = options?.runtimeWebFetch ?? getActiveRuntimeWebToolsMetadata()?.fetch;
const providers = sortWebFetchProvidersForAutoDetect(
resolvePluginWebFetchProviders({

View File

@@ -38,7 +38,7 @@ export type {
} from "./runtime-types.js";
function resolveSearchConfig(cfg?: OpenClawConfig): WebSearchConfig {
return resolveWebProviderConfig<"search", NonNullable<WebSearchConfig>>(cfg, "search");
return resolveWebProviderConfig(cfg, "search") as NonNullable<WebSearchConfig> | undefined;
}
export function resolveWebSearchEnabled(params: {

View File

@@ -12,10 +12,10 @@ type ProviderWithCredential = {
requiresCredential?: boolean;
};
export function resolveWebProviderConfig<
TKind extends "search" | "fetch",
TConfig extends Record<string, unknown>,
>(cfg: OpenClawConfig | undefined, kind: TKind): TConfig | undefined {
export function resolveWebProviderConfig(
cfg: OpenClawConfig | undefined,
kind: "search" | "fetch",
): Record<string, unknown> | undefined {
const webConfig = cfg?.tools?.web;
if (!webConfig || typeof webConfig !== "object") {
return undefined;
@@ -24,7 +24,7 @@ export function resolveWebProviderConfig<
if (!toolConfig || typeof toolConfig !== "object") {
return undefined;
}
return toolConfig as TConfig;
return toolConfig as Record<string, unknown>;
}
export function readWebProviderEnvValue(

View File

@@ -9,8 +9,8 @@ function resolveMockOverrides<TModule extends object>(
return typeof factory === "function" ? factory(actual) : factory;
}
function resolveDefaultBase<TModule extends object>(actual: TModule): Record<string, unknown> {
const defaultExport = (actual as TModule & { default?: unknown }).default;
function resolveDefaultBase(actual: object): Record<string, unknown> {
const defaultExport = (actual as { default?: unknown }).default;
if (defaultExport && typeof defaultExport === "object") {
return defaultExport as Record<string, unknown>;
}

View File

@@ -22,6 +22,7 @@ type PackageManifestContractParams = {
minHostVersionBaseline?: string;
};
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets assertions ascribe package manifest shape.
function readJson<T>(relativePath: string): T {
const absolutePath = path.resolve(process.cwd(), relativePath);
return JSON.parse(fs.readFileSync(absolutePath, "utf8")) as T;
@@ -34,8 +35,8 @@ export function describePackageManifestContract(params: PackageManifestContractP
if (params.pluginLocalRuntimeDeps?.length) {
for (const dependencyName of params.pluginLocalRuntimeDeps) {
it(`keeps ${dependencyName} plugin-local`, () => {
const rootManifest = readJson<PackageManifest>("package.json");
const pluginManifest = readJson<PackageManifest>(packagePath);
const rootManifest = readJson("package.json") as PackageManifest;
const pluginManifest = readJson(packagePath) as PackageManifest;
const pluginSpec =
pluginManifest.dependencies?.[dependencyName] ??
pluginManifest.optionalDependencies?.[dependencyName];

View File

@@ -16,6 +16,7 @@ export function createRuntimeEnv(options?: { throwOnExit?: boolean }): OutputRun
};
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets plugin suites ascribe runtime extension shape.
export function createTypedRuntimeEnv<TRuntime>(options?: { throwOnExit?: boolean }): TRuntime {
return createRuntimeEnv(options) as TRuntime;
}
@@ -24,6 +25,7 @@ export function createNonExitingRuntimeEnv(): OutputRuntimeEnv {
return createRuntimeEnv({ throwOnExit: false });
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper lets plugin suites ascribe runtime extension shape.
export function createNonExitingTypedRuntimeEnv<TRuntime>(): TRuntime {
return createTypedRuntimeEnv<TRuntime>({ throwOnExit: false });
}

View File

@@ -151,6 +151,7 @@ export function createSetupWizardAdapter(params: SetupWizardAdapterParams) {
return buildChannelSetupWizardAdapterFromSetupWizard(params);
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper preserves plugin-specific setup wizard surface type.
export function createPluginSetupWizardAdapter<TPlugin extends SetupWizardTestPlugin>(
plugin: TPlugin,
) {
@@ -161,12 +162,14 @@ export function createPluginSetupWizardAdapter<TPlugin extends SetupWizardTestPl
});
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper preserves plugin-specific setup wizard surface type.
export function createPluginSetupWizardConfigure<TPlugin extends SetupWizardTestPlugin>(
plugin: TPlugin,
) {
return createPluginSetupWizardAdapter(plugin).configure;
}
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper preserves plugin-specific setup wizard surface type.
export function createPluginSetupWizardStatus<TPlugin extends SetupWizardTestPlugin>(
plugin: TPlugin,
) {

View File

@@ -1,3 +1,4 @@
// oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- Test helper preserves plugin-specific hook API type.
export function registerHookHandlersForTest<TApi>(params: {
config: Record<string, unknown>;
register: (api: TApi) => void;

View File

@@ -10,8 +10,8 @@ type OxlintTsconfig = {
exclude?: string[];
};
function readJson<T>(path: string): T {
return JSON.parse(fs.readFileSync(path, "utf8")) as T;
function readJson(path: string): unknown {
return JSON.parse(fs.readFileSync(path, "utf8")) as unknown;
}
describe("oxlint config", () => {