refactor: consolidate coercion contracts (#122458)

* refactor: consolidate coercion contracts

Centralize exact string, record, numeric, date, Boolean, argument, and structured-error coercions while preserving call-site semantics.

Migrate canonical-name collisions and deprecated internal SDK bypasses, deleting 55 net production/tooling lines. Expand declaration ownership enforcement to 101 allowed helpers and add a narrow export-completeness audit.

* fix: preserve standalone script coercions

Keep copied Control UI tooling self-contained and retain the trusted release harness module-relative source seam when the harness runs against an old target cwd.
This commit is contained in:
Peter Steinberger
2026-08-11 23:26:37 -07:00
committed by GitHub
parent 66fe424590
commit b080dd1e76
276 changed files with 1685 additions and 1663 deletions
+69
View File
@@ -0,0 +1,69 @@
type StringOptions = {
allowEmpty?: boolean;
allowInline?: boolean;
missingValueMessage?: string;
rejectShortOptions?: boolean;
repeatable?: boolean;
transform?: (value: string) => unknown;
};
type ConsumedFlag<T extends Record<string, unknown>> = {
flag?: string;
nextIndex: number;
repeatable?: boolean;
apply(target: T): void;
};
type FlagSpec<T extends Record<string, unknown>> = {
consume(argv: readonly string[], index: number, args: T): ConsumedFlag<T> | null;
};
type ParseOptions<T extends Record<string, unknown>> = {
allowUnknownOptions?: boolean;
duplicateOptionMessage?: (flag: string) => string;
ignoreDoubleDash?: boolean;
onUnhandledArg?: (arg: string, args: T) => "handled" | void;
};
export type BoundedUnsignedDecimalResult =
| { kind: "syntax" }
| { kind: "below" }
| { kind: "above" }
| { kind: "value"; value: number };
export function readFlagValue(args: readonly string[], name: string): string | undefined;
export function stripLeadingPackageManagerSeparator(argv: string[]): string[];
export function parseStrictBooleanArg(value: unknown, label: string): boolean;
export function classifyBoundedUnsignedDecimal(
value: unknown,
min: number,
max: number,
): BoundedUnsignedDecimalResult;
export function parsePermissiveBooleanToken(value: unknown): boolean | undefined;
export function stringFlag<T extends Record<string, unknown>>(
flag: string,
key: string,
options?: StringOptions,
): FlagSpec<T>;
export function stringListFlag<T extends Record<string, unknown>>(
flag: string,
key: string,
options?: Omit<StringOptions, "repeatable" | "transform">,
): FlagSpec<T>;
export function intFlag<T extends Record<string, unknown>>(
flag: string,
key: string,
options?: { min?: number },
): FlagSpec<T>;
export function booleanFlag<T extends Record<string, unknown>>(
flag: string,
key: string,
value?: unknown,
options?: { repeatable?: boolean },
): FlagSpec<T>;
export function parseFlagArgs<T extends Record<string, unknown>>(
argv: readonly string[],
args: T,
specs: readonly FlagSpec<T>[],
options?: ParseOptions<T>,
): T;
+54
View File
@@ -47,6 +47,9 @@
* onUnhandledArg?: (arg: string, args: T) => "handled" | void,
* }} ParseOptions
*/
/**
* @typedef {{ kind: "syntax" } | { kind: "below" } | { kind: "above" } | { kind: "value", value: number }} BoundedUnsignedDecimalResult
*/
/** @param {string} message */
function failFlagParse(message) {
throw new Error(message);
@@ -170,6 +173,57 @@ function readFlagOptionValue(argv, index, flag) {
}
return { nextIndex: index + 1, value };
}
/**
* Parse the exact lowercase Boolean language used by strict script arguments.
* @param {unknown} value
* @param {string} label
*/
export function parseStrictBooleanArg(value, label) {
if (value === "true") {
return true;
}
if (value === "false") {
return false;
}
throw new Error(`${label} must be true or false.`);
}
/**
* Classify an ASCII unsigned-decimal token against inclusive bounds.
* @param {unknown} value
* @param {number} min
* @param {number} max
* @returns {BoundedUnsignedDecimalResult}
*/
export function classifyBoundedUnsignedDecimal(value, min, max) {
if (typeof value !== "string" || !/^\d+$/u.test(value)) {
return { kind: "syntax" };
}
const parsed = Number(value);
if (parsed < min) {
return { kind: "below" };
}
if (parsed > max) {
return { kind: "above" };
}
return { kind: "value", value: parsed };
}
const PERMISSIVE_BOOLEAN_TRUE_TOKENS = new Set(["1", "on", "true", "yes"]);
const PERMISSIVE_BOOLEAN_FALSE_TOKENS = new Set(["0", "false", "no", "off"]);
/**
* Parse the normalized Boolean token language shared by repository scripts.
* @param {unknown} value
* @returns {boolean | undefined}
*/
export function parsePermissiveBooleanToken(value) {
const normalized = typeof value === "string" ? value.trim().toLowerCase() : "";
if (!normalized) {
return undefined;
}
if (PERMISSIVE_BOOLEAN_TRUE_TOKENS.has(normalized)) {
return true;
}
return PERMISSIVE_BOOLEAN_FALSE_TOKENS.has(normalized) ? false : undefined;
}
/**
* @param {string} raw
* @param {string} flag
@@ -1,5 +1,6 @@
import type { ChildProcess } from "node:child_process";
import { basename, dirname, resolve, win32 as pathWin32 } from "node:path";
import { parsePermissiveBooleanToken } from "../arg-utils.mts";
import { trimForSummary } from "./shared.ts";
type CrossOsSuite = "packaged-fresh" | "installer-fresh" | "packaged-upgrade" | "dev-update";
@@ -314,11 +315,9 @@ function parseBooleanEnv(name: string, fallback: boolean, env = process.env): bo
if (!raw) {
return fallback;
}
if (/^(1|true|yes|on)$/iu.test(raw)) {
return true;
}
if (/^(0|false|no|off)$/iu.test(raw)) {
return false;
const parsed = parsePermissiveBooleanToken(raw);
if (parsed !== undefined) {
return parsed;
}
throw new Error(`${name} must be a boolean. Got: ${JSON.stringify(raw)}`);
}
@@ -15,6 +15,7 @@ import {
import { dirname } from "node:path";
import { StringDecoder } from "node:string_decoder";
import { buildCmdExeCommandLine, resolveWindowsCmdExePath } from "../../windows-cmd-helpers.mjs";
import { toStringifiedError } from "../error-format.mts";
import { resolveWindowsTaskkillPath } from "../windows-taskkill.mjs";
import type {
Cleanup,
@@ -559,8 +560,7 @@ export async function startStaticFileServer(params: {
server.close((error) => {
void (async () => {
const closeLogError = await finishStaticFileServerLog(logStream, logStreamError).catch(
(logError: unknown): Error =>
logError instanceof Error ? logError : new Error(String(logError)),
(logError: unknown): Error => toStringifiedError(logError),
);
if (error) {
rejectPromise(error);
@@ -51,4 +51,8 @@ export const BANNED_INTERNAL_PLUGIN_SDK_FACADE_MODULES: BannedInternalPluginSdkF
modulePath: "src/plugin-sdk/inbound-envelope",
canonical: "openclaw/plugin-sdk/channel-inbound",
},
{
modulePath: "src/plugin-sdk/text-runtime",
canonical: "the focused typed public Plugin SDK subpath for the imported helper",
},
];
+5 -6
View File
@@ -2,6 +2,7 @@
import path from "node:path";
import { truncateUtf16Safe } from "@openclaw/normalization-core/utf16-slice";
import { redactSensitiveText } from "../../src/logging/redact.js";
import { parsePermissiveBooleanToken } from "./arg-utils.mts";
export { parseStrictIntegerOption } from "./strict-integer-option.ts";
@@ -50,15 +51,13 @@ export function parseBooleanEnv(params: {
name: string;
raw: string | undefined;
}): boolean {
const raw = params.raw?.trim().toLowerCase();
const raw = params.raw?.trim();
if (!raw) {
return params.fallback;
}
if (["1", "true", "yes", "on"].includes(raw)) {
return true;
}
if (["0", "false", "no", "off"].includes(raw)) {
return false;
const parsed = parsePermissiveBooleanToken(raw);
if (parsed !== undefined) {
return parsed;
}
throw new Error(
`${params.name} must be one of 1,0,true,false,yes,no,on,off; got ${JSON.stringify(params.raw)}`,
+5
View File
@@ -12,6 +12,11 @@ export function coerceErrorMessage(value: unknown): string {
return value instanceof Error ? value.message : String(value);
}
/** Preserve Error values and stringify every other value without workspace dependencies. */
export function toStringifiedError(value: unknown): Error {
return value instanceof Error ? value : new Error(String(value));
}
/** Preserve structured non-Error failures without requiring built workspace packages. */
export function toErrorObject(value: unknown, fallbackMessage: string): Error {
if (value instanceof Error) {
+5 -6
View File
@@ -11,9 +11,9 @@ export type LocalVitestScheduling = {
};
import os from "node:os";
import { parsePermissiveBooleanToken } from "./arg-utils.mts";
const MAX_LOCAL_FULL_SUITE_PARALLELISM = 10;
const TRUTHY_ENV_VALUES = new Set(["1", "true", "yes", "on"]);
const clamp = (value: number, min: number, max: number) => Math.max(min, Math.min(max, value));
@@ -37,13 +37,12 @@ function isSystemThrottleDisabled(env: Record<string, string | undefined>) {
return normalized === "1" || normalized === "true";
}
function isTruthyEnvValue(value: string | undefined) {
return TRUTHY_ENV_VALUES.has(value?.trim().toLowerCase() ?? "");
}
/** @internal Shared repository-script contract. */
export function isCiLikeEnv(env: Record<string, string | undefined> = process.env) {
return isTruthyEnvValue(env.CI) || isTruthyEnvValue(env.GITHUB_ACTIONS);
return (
parsePermissiveBooleanToken(env.CI) === true ||
parsePermissiveBooleanToken(env.GITHUB_ACTIONS) === true
);
}
/** @internal Shared repository-script contract. */