refactor: consolidate remaining coercion helpers (#122020)

This commit is contained in:
Peter Steinberger
2026-08-11 10:22:01 -07:00
committed by GitHub
parent 24bbb416b5
commit cad77fb39c
234 changed files with 1210 additions and 1572 deletions
@@ -472,7 +472,7 @@ describe("package-openclaw-for-docker", () => {
const packageJsonPath = path.join(sourceDir, "package.json");
const originalPackageJson = `${JSON.stringify(
{
dependencies: { "@openclaw/ai": "workspace:*", "dep-a": "1.2.3" },
dependencies: { "@openclaw/ai": "workspace:*", "dep-a": "workspace:1.2.3" },
devDependencies: { "@openclaw/session-url-contract": "workspace:*" },
files: ["dist"],
name: "openclaw",
@@ -506,7 +506,10 @@ describe("package-openclaw-for-docker", () => {
fs.writeFileSync(
path.join(destination, "package.json"),
`${JSON.stringify({
dependencies: { "dep-a": "1.2.3" },
dependencies: {
"@openclaw/private-runtime": "0.0.0-private",
"dep-a": "1.2.3",
},
name: "@openclaw/ai",
version: "2026.6.17",
})}\n`,
@@ -522,6 +525,8 @@ describe("package-openclaw-for-docker", () => {
devDependencies?: Record<string, string>;
};
expect(packageJson.dependencies["@openclaw/ai"]).toBe("2026.6.17");
expect(packageJson.dependencies["@openclaw/private-runtime"]).toBeUndefined();
expect(packageJson.dependencies["dep-a"]).toBe("1.2.3");
expect(packageJson.devDependencies?.["@openclaw/session-url-contract"]).toBe("workspace:*");
expect(packageJson.bundleDependencies).toContain("@openclaw/ai");
expect(fs.existsSync(path.join(installedAiPath, "original-marker"))).toBe(false);
@@ -15,6 +15,7 @@ const ZERO_INSTALL_FILES = [
"scripts/android-release-signing.mjs",
"scripts/lib/android-release-signing-process.mjs",
"scripts/lib/arg-utils.runtime.mjs",
"scripts/lib/record-shared.mjs",
"scripts/lib/repo-root.mjs",
"apps/android/Config/ReleaseSigning.json",
] as const;
@@ -1,14 +1,20 @@
import { describe, expect, it } from "vitest";
import fs from "node:fs";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import {
auditCoercionHelperDeclarations,
findBannedCoercionHelperDeclarations,
isGovernedCoercionHelperPath,
runCoercionHelperDeclarationGuard,
type CoercionHelperCarveOut,
type CoercionHelperDeclaration,
} from "../../scripts/check-coercion-helper-declarations.mts";
import { useAutoCleanupTempDirTracker } from "../helpers/temp-dir.js";
const tempDirs = useAutoCleanupTempDirTracker(afterEach);
describe("coercion helper declaration AST guard", () => {
it("finds nested, exported, async, and callable-variable declarations", () => {
it("finds functions, callable variables, methods, fields, and object properties", () => {
const source = [
"export async function readString() {}",
"if (true) {",
@@ -28,6 +34,18 @@ describe("coercion helper declaration AST guard", () => {
"const readOptionalString = normalizeOptionalString;",
"const optionalString = helpers.readStringValue;",
"const asObject = helpers.asOptionalRecord;",
"class Example {",
" readString() {}",
" toError = () => new Error();",
" asRecord = function () { return {}; };",
"}",
"const object = {",
" optionalString() {},",
" readBoolean: () => true,",
" readNumber: function () { return 1; },",
"};",
"function normalizeOptionalString() {}",
"const parseDateFirstTimestampMs = () => 0;",
].join("\n");
expect(findBannedCoercionHelperDeclarations(source, "src/example.ts")).toEqual([
@@ -44,19 +62,42 @@ describe("coercion helper declaration AST guard", () => {
{ file: "src/example.ts", kind: "function", line: 13, name: "normalizeString" },
{ file: "src/example.ts", kind: "variable", line: 14, name: "asString" },
{ file: "src/example.ts", kind: "function", line: 15, name: "asObject" },
{ file: "src/example.ts", kind: "variable", line: 16, name: "readOptionalString" },
{
file: "src/example.ts",
kind: "variable",
line: 16,
name: "readOptionalString",
},
{ file: "src/example.ts", kind: "variable", line: 17, name: "optionalString" },
{ file: "src/example.ts", kind: "variable", line: 18, name: "asObject" },
{ file: "src/example.ts", kind: "method", line: 20, name: "readString" },
{ file: "src/example.ts", kind: "field", line: 21, name: "toError" },
{ file: "src/example.ts", kind: "field", line: 22, name: "asRecord" },
{ file: "src/example.ts", kind: "method", line: 25, name: "optionalString" },
{ file: "src/example.ts", kind: "property", line: 26, name: "readBoolean" },
{ file: "src/example.ts", kind: "property", line: 27, name: "readNumber" },
{
file: "src/example.ts",
kind: "function",
line: 29,
name: "normalizeOptionalString",
},
{
file: "src/example.ts",
kind: "variable",
line: 30,
name: "parseDateFirstTimestampMs",
},
]);
});
it("ignores imports, aliases, methods, properties, callbacks, and inert text", () => {
it("ignores imports, non-callable properties, shorthand aliases, callback names, and inert text", () => {
const source = [
'import { isRecord, readString as importedReadString } from "./helpers.js";',
"const alias = isRecord;",
"const { asRecord } = helpers;",
"class Example { isRecord() {} readString = () => true; }",
"const object = { optionalString() {}, toError: () => new Error() };",
"const object = { isRecord, readString: 42, toError: importedToError };",
"const shorthand = { optionalString };",
"values.map(function readString(value) { return value; });",
"const aliasWithInternalName = function isRecord(value) { return value; };",
"const asRecord = raw as Record<string, unknown>;",
@@ -112,20 +153,61 @@ describe("coercion helper declaration AST guard", () => {
});
});
it("excludes declarations, fixtures, generated sources, and browser bundles", () => {
it("excludes structural fixtures and generated sources without hiding authored fixture-named files", () => {
expect(isGovernedCoercionHelperPath("src/runtime.ts")).toBe(true);
expect(isGovernedCoercionHelperPath("extensions/demo/runtime.jsx")).toBe(true);
expect(isGovernedCoercionHelperPath("src/runtime.d.ts")).toBe(false);
expect(isGovernedCoercionHelperPath("scripts/runtime.d.mts")).toBe(false);
expect(isGovernedCoercionHelperPath("test/fixtures/example.ts")).toBe(false);
expect(isGovernedCoercionHelperPath("extensions/demo/dist/index.js")).toBe(false);
expect(isGovernedCoercionHelperPath("src/example.test-fixtures.ts")).toBe(false);
expect(isGovernedCoercionHelperPath("src/example.test-fixtures.ts")).toBe(true);
expect(isGovernedCoercionHelperPath("extensions/demo/runtime-tool-fixture.ts")).toBe(true);
expect(isGovernedCoercionHelperPath("src/schema.generated.ts")).toBe(false);
expect(isGovernedCoercionHelperPath("ui/src/vendor.bundle.js")).toBe(false);
expect(
isGovernedCoercionHelperPath(
"extensions/browser/chrome-extension/modules/copilot-runtime.js",
),
).toBe(false);
).toBe(true);
expect(isGovernedCoercionHelperPath("root.config.ts")).toBe(true);
expect(isGovernedCoercionHelperPath(".github/actions/example/index.ts")).toBe(true);
});
it("scans a temporary repository and reports sorted, owner-specific diagnostics", () => {
const repoRoot = tempDirs.make("coercion-helper-guard-");
fs.mkdirSync(path.join(repoRoot, "src"), { recursive: true });
fs.mkdirSync(path.join(repoRoot, "extensions", "demo"), { recursive: true });
fs.mkdirSync(path.join(repoRoot, "config"), { recursive: true });
fs.writeFileSync(path.join(repoRoot, "src", "z.ts"), "function readString() {}\n");
fs.writeFileSync(
path.join(repoRoot, "extensions", "demo", "a.ts"),
"const asRecord = () => ({});\n",
);
fs.writeFileSync(
path.join(repoRoot, "config", "root.ts"),
"class Config { normalizeOptionalString() {} }\n",
);
const stdout: string[] = [];
const stderr: string[] = [];
expect(
runCoercionHelperDeclarationGuard({
carveOuts: [],
repoRoot,
io: {
stdout: { write: (value) => stdout.push(value) },
stderr: { write: (value) => stderr.push(value) },
},
}),
).toBe(1);
expect(stdout).toEqual([]);
const output = stderr.join("");
expect(output.indexOf("config/root.ts:1")).toBeLessThan(
output.indexOf("extensions/demo/a.ts:1"),
);
expect(output.indexOf("extensions/demo/a.ts:1")).toBeLessThan(output.indexOf("src/z.ts:1"));
expect(output).toContain("Core/package/UI/workspace-script code");
expect(output).toContain("Plugin production code");
expect(output).toContain("Dependency-free, copied, generated, or serialized code");
});
});
+7
View File
@@ -5003,6 +5003,8 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}"
"matrix.task == 'max-lines-ratchet' && github.event_name == 'workflow_dispatch' && inputs.release_gate",
);
expect(checksFastRun.run).toContain("max-lines-ratchet)");
expect(checksFastRun.run).toContain("coercion-helpers)");
expect(checksFastRun.run).toContain("pnpm check:coercion-helpers");
expect(checksFastRun.run).toContain('has_package_script "check:max-lines-ratchet"');
expect(checksFastRun.env.RATCHET_PR_HEAD_SHA).toBe(
"${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || '' }}",
@@ -5094,6 +5096,11 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}"
runtime: "node",
task: "max-lines-ratchet",
},
{
check_name: "checks-fast-coercion-helpers",
runtime: "node",
task: "coercion-helpers",
},
]);
});
+1 -14
View File
@@ -2,6 +2,7 @@
import { mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { toErrorObject as toLintErrorObject } from "@openclaw/normalization-core/error-coercion";
import { describe, expect, it } from "vitest";
import {
collectProdResolvedPackagesFromLockfile,
@@ -485,17 +486,3 @@ snapshots:
}
});
});
function toLintErrorObject(value: unknown, fallbackMessage: string): Error {
if (value instanceof Error) {
return value;
}
if (typeof value === "string") {
return new Error(value);
}
const error = new Error(fallbackMessage, { cause: value });
if ((typeof value === "object" && value !== null) || typeof value === "function") {
Object.assign(error, value);
}
return error;
}
@@ -1,5 +1,6 @@
// PR Context And Evidence Policy tests cover GitHub PR-body policy behavior.
import { readFileSync } from "node:fs";
import { toErrorObject as toLintErrorObject } from "@openclaw/normalization-core/error-coercion";
import { describe, expect, it, vi } from "vitest";
import {
NEEDS_PR_CONTEXT_LABEL,
@@ -689,17 +690,3 @@ describe("readBoundedGitHubApiJson", () => {
});
});
});
function toLintErrorObject(value: unknown, fallbackMessage: string): Error {
if (value instanceof Error) {
return value;
}
if (typeof value === "string") {
return new Error(value);
}
const error = new Error(fallbackMessage, { cause: value });
if ((typeof value === "object" && value !== null) || typeof value === "function") {
Object.assign(error, value);
}
return error;
}
@@ -5,6 +5,7 @@ import { access, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promise
import { tmpdir } from "node:os";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { toErrorObject as toLintErrorObject } from "@openclaw/normalization-core/error-coercion";
import { afterEach, describe, expect, it, vi } from "vitest";
import { resolveWindowsTaskkillPath } from "../../scripts/lib/windows-taskkill.mjs";
import {
@@ -1433,17 +1434,3 @@ describe("resolve-openclaw-package-candidate", () => {
);
});
});
function toLintErrorObject(value: unknown, fallbackMessage: string): Error {
if (value instanceof Error) {
return value;
}
if (typeof value === "string") {
return new Error(value);
}
const error = new Error(fallbackMessage, { cause: value });
if ((typeof value === "object" && value !== null) || typeof value === "function") {
Object.assign(error, value);
}
return error;
}