feat(tooling): enforce noUncheckedIndexedAccess in the scripts lane (NUIA phase 5) (#105180)

* feat(tooling): enforce noUncheckedIndexedAccess in the scripts lane

Burns down all 153 scripts-lane errors (bench aggregation, release
checks, i18n inventories, argv indexing) and flips the flag in
tsconfig.scripts.json. Direct-Node-executed release harness scripts use
local narrowing instead of workspace imports, which do not resolve
under plain node execution. Benchmark measured loops untouched.

* fix(scripts): import expect helpers via relative package sources

tsconfig path aliases resolve from cwd under tsx, so release wrapper
scripts running against old release target cwds could not resolve
@openclaw/normalization-core (not a linked root dependency). Relative
package-source imports match the established pattern on the adjacent
lines and are cwd-independent; old-target planning verified directly.
This commit is contained in:
Peter Steinberger
2026-07-12 10:17:00 +01:00
committed by GitHub
parent d79373fa14
commit e580275464
38 changed files with 204 additions and 97 deletions
+3 -1
View File
@@ -1,6 +1,7 @@
import { readdir, readFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { expectDefined } from "../packages/normalization-core/src/expect.js";
import { NATIVE_I18N_LOCALES } from "./native-app-i18n.ts";
const HERE = path.dirname(fileURLToPath(import.meta.url));
@@ -63,7 +64,8 @@ export async function checkAndroidAppI18n() {
readAndroidSource(),
Promise.all(LOCALES.map(readStrings)),
]);
const [base, ...translations] = localeStrings;
const base = expectDefined(localeStrings[0], "English Android string resources");
const translations = localeStrings.slice(1);
const baseKeys = new Set(base.keys());
const problems: Array<readonly [string, string[]]> = translations.flatMap((strings, index) => {
const locale = NATIVE_I18N_LOCALES[index];
+2 -1
View File
@@ -1,6 +1,7 @@
import { mkdir, readFile, writeFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import { expectDefined } from "../packages/normalization-core/src/expect.js";
import { NATIVE_I18N_LOCALES } from "./native-app-i18n.ts";
const HERE = path.dirname(fileURLToPath(import.meta.url));
@@ -196,7 +197,7 @@ export async function checkAppleAppI18n() {
export async function compileMacosLocalizations(outputDir: string) {
await checkAppleAppI18n();
const spec = CATALOGS[1];
const spec = expectDefined(CATALOGS[1], "macOS localization catalog specification");
const catalog = JSON.parse(await readFile(path.join(ROOT, spec.path), "utf8")) as Catalog;
if (!catalog.strings) {
throw new Error(`invalid Apple string catalog: ${spec.path}`);
+9 -4
View File
@@ -4,6 +4,7 @@ import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "nod
import os from "node:os";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { expectDefined } from "../packages/normalization-core/src/expect.js";
import { parseStrictIntegerOption } from "./lib/dev-tooling-safety.ts";
type CommandCase = {
@@ -474,7 +475,7 @@ function parseRepeatableFlag(flag: string): string[] {
for (let i = 0; i < process.argv.length; i += 1) {
const value = process.argv[i + 1];
if (process.argv[i] === flag && value && !value.startsWith("-")) {
values.push(process.argv[i + 1]);
values.push(value);
}
}
return values;
@@ -483,7 +484,7 @@ function parseRepeatableFlag(flag: string): string[] {
function validateCliArgs(argv: readonly string[] = process.argv.slice(2)): void {
const seenSingleValueFlags = new Set<string>();
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index];
const arg = expectDefined(argv[index], `CLI benchmark argument at index ${index}`);
if (VALUE_FLAGS.has(arg)) {
if (arg !== "--case") {
if (seenSingleValueFlags.has(arg)) {
@@ -575,9 +576,13 @@ function median(values: number[]): number {
const sorted = [...values].toSorted((a, b) => a - b);
const mid = Math.floor(sorted.length / 2);
if (sorted.length % 2 === 0) {
return (sorted[mid - 1] + sorted[mid]) / 2;
return (
(expectDefined(sorted[mid - 1], "lower middle CLI benchmark sample") +
expectDefined(sorted[mid], "upper middle CLI benchmark sample")) /
2
);
}
return sorted[mid];
return expectDefined(sorted[mid], "middle CLI benchmark sample");
}
function percentile(values: number[], p: number): number {
+19 -11
View File
@@ -6,6 +6,7 @@ import { tmpdir } from "node:os";
import path from "node:path";
import { performance } from "node:perf_hooks";
import { pathToFileURL } from "node:url";
import { expectDefined } from "../packages/normalization-core/src/expect.js";
import { writeGatewayRestartIntentSync } from "../src/infra/restart.js";
import { parseStrictIntegerOption } from "./lib/dev-tooling-safety.ts";
import { delay, stopChild, type StopChildResult } from "./lib/gateway-bench-child.ts";
@@ -342,7 +343,7 @@ function resolveOutputPath(raw: string | undefined): string | undefined {
function resolveCases(caseIds: string[]): GatewayBenchCase[] {
if (caseIds.length === 0) {
return [GATEWAY_CASES[0]];
return [expectDefined(GATEWAY_CASES[0], "default gateway restart benchmark case")];
}
const seenIds = new Set<string>();
const byId = new Map(GATEWAY_CASES.map((benchCase) => [benchCase.id, benchCase]));
@@ -412,7 +413,11 @@ function median(values: number[]): number {
const sorted = [...values].toSorted((a, b) => a - b);
const middle = Math.floor(sorted.length / 2);
if (sorted.length % 2 === 0) {
return (sorted[middle - 1] + sorted[middle]) / 2;
return (
(expectDefined(sorted[middle - 1], "lower middle gateway restart sample") +
expectDefined(sorted[middle], "upper middle gateway restart sample")) /
2
);
}
return sorted[middle] ?? 0;
}
@@ -510,8 +515,8 @@ function slope(values: Array<number | null>): number | null {
if (points.length < 2) {
return null;
}
const first = points[0];
const last = points[points.length - 1];
const first = expectDefined(points[0], "first gateway restart slope point");
const last = expectDefined(points[points.length - 1], "last gateway restart slope point");
const denominator = Math.max(1, last.index - first.index);
return (last.value - first.value) / denominator;
}
@@ -910,10 +915,11 @@ function collectTraceLine(
"u",
).exec(line);
if (phaseMatch) {
trace[phaseMatch[1]] = Number(phaseMatch[2]);
trace[`${phaseMatch[1]}.total`] = Number(phaseMatch[3]);
const phase = expectDefined(phaseMatch[1], `${prefix} phase name`);
trace[phase] = Number(expectDefined(phaseMatch[2], `${prefix} phase duration`));
trace[`${phase}.total`] = Number(expectDefined(phaseMatch[3], `${prefix} total duration`));
for (const metric of parseTraceMetrics(phaseMatch[4] ?? "")) {
trace[`${phaseMatch[1]}.${metric.key}`] = metric.value;
trace[`${phase}.${metric.key}`] = metric.value;
}
return true;
}
@@ -921,8 +927,10 @@ function collectTraceLine(
if (!detailMatch) {
return false;
}
for (const metric of parseTraceMetrics(detailMatch[2])) {
trace[`${detailMatch[1]}.${metric.key}`] = metric.value;
const phase = expectDefined(detailMatch[1], `${prefix} detail phase name`);
const metrics = expectDefined(detailMatch[2], `${prefix} detail metrics`);
for (const metric of parseTraceMetrics(metrics)) {
trace[`${phase}.${metric.key}`] = metric.value;
}
return true;
}
@@ -934,8 +942,8 @@ function parseTraceMetrics(raw: string): Array<{ key: string; value: number }> {
if (!metricMatch) {
continue;
}
const key = metricMatch[1];
const value = Number(metricMatch[2]);
const key = expectDefined(metricMatch[1], "gateway restart trace metric key");
const value = Number(expectDefined(metricMatch[2], `gateway restart ${key} metric value`));
if (!Number.isFinite(value)) {
continue;
}
+18 -8
View File
@@ -5,6 +5,7 @@ import { tmpdir } from "node:os";
import path from "node:path";
import { performance } from "node:perf_hooks";
import { pathToFileURL } from "node:url";
import { expectDefined } from "../packages/normalization-core/src/expect.js";
import { parseStrictIntegerOption } from "./lib/dev-tooling-safety.ts";
import { delay, stopChild } from "./lib/gateway-bench-child.ts";
import {
@@ -351,7 +352,11 @@ function median(values: number[]): number {
const sorted = [...values].toSorted((a, b) => a - b);
const middle = Math.floor(sorted.length / 2);
if (sorted.length % 2 === 0) {
return (sorted[middle - 1] + sorted[middle]) / 2;
return (
(expectDefined(sorted[middle - 1], "lower middle gateway startup sample") +
expectDefined(sorted[middle], "upper middle gateway startup sample")) /
2
);
}
return sorted[middle] ?? 0;
}
@@ -694,10 +699,13 @@ function sanitizedEnv(
function collectStartupTrace(line: string, startupTrace: Record<string, number>): void {
const phaseMatch = /startup trace: ([^ ]+) ([0-9.]+)ms total=([0-9.]+)ms(?: (.*))?/u.exec(line);
if (phaseMatch) {
startupTrace[phaseMatch[1]] = Number(phaseMatch[2]);
startupTrace[`${phaseMatch[1]}.total`] = Number(phaseMatch[3]);
const phase = expectDefined(phaseMatch[1], "gateway startup trace phase name");
startupTrace[phase] = Number(expectDefined(phaseMatch[2], `${phase} startup duration`));
startupTrace[`${phase}.total`] = Number(
expectDefined(phaseMatch[3], `${phase} total startup duration`),
);
for (const metric of parseStartupTraceMetrics(phaseMatch[4] ?? "")) {
startupTrace[`${phaseMatch[1]}.${metric.key}`] = metric.value;
startupTrace[`${phase}.${metric.key}`] = metric.value;
}
return;
}
@@ -705,8 +713,10 @@ function collectStartupTrace(line: string, startupTrace: Record<string, number>)
if (!detailMatch) {
return;
}
for (const metric of parseStartupTraceMetrics(detailMatch[2])) {
startupTrace[`${detailMatch[1]}.${metric.key}`] = metric.value;
const phase = expectDefined(detailMatch[1], "gateway startup detail phase name");
const detail = expectDefined(detailMatch[2], `${phase} startup detail metrics`);
for (const metric of parseStartupTraceMetrics(detail)) {
startupTrace[`${phase}.${metric.key}`] = metric.value;
}
}
@@ -727,8 +737,8 @@ function parseStartupTraceMetrics(raw: string): Array<{ key: string; value: numb
if (!metricMatch) {
continue;
}
const key = metricMatch[1];
const value = Number(metricMatch[2]);
const key = expectDefined(metricMatch[1], "gateway startup trace metric key");
const value = Number(expectDefined(metricMatch[2], `${key} startup trace metric value`));
if (
!Number.isFinite(value) ||
(key !== "eventLoopMax" &&
+7 -2
View File
@@ -1,6 +1,7 @@
// Bench Model script supports OpenClaw repository automation.
import { pathToFileURL } from "node:url";
import { completeSimple, type Model } from "openclaw/plugin-sdk/llm";
import { expectDefined } from "../packages/normalization-core/src/expect.js";
import { parseStrictIntegerOption } from "./lib/dev-tooling-safety.ts";
type Usage = {
@@ -111,9 +112,13 @@ function median(values: number[]): number {
const sorted = [...values].toSorted((a, b) => a - b);
const mid = Math.floor(sorted.length / 2);
if (sorted.length % 2 === 0) {
return Math.round((sorted[mid - 1] + sorted[mid]) / 2);
return Math.round(
(expectDefined(sorted[mid - 1], "lower middle model benchmark sample") +
expectDefined(sorted[mid], "upper middle model benchmark sample")) /
2,
);
}
return sorted[mid];
return expectDefined(sorted[mid], "middle model benchmark sample");
}
async function runModel(opts: {
+2 -1
View File
@@ -4,6 +4,7 @@ import os from "node:os";
import path from "node:path";
import type { DatabaseSync, SQLInputValue } from "node:sqlite";
import { pathToFileURL } from "node:url";
import { expectDefined } from "../packages/normalization-core/src/expect.js";
import {
openOpenClawAgentDatabase,
closeOpenClawAgentDatabasesForTest,
@@ -459,7 +460,7 @@ function percentile(values: number[], pct: number): number {
}
const sorted = values.toSorted((left, right) => left - right);
const index = Math.min(sorted.length - 1, Math.ceil((pct / 100) * sorted.length) - 1);
return Number(sorted[index].toFixed(3));
return Number(expectDefined(sorted[index], `SQLite benchmark percentile ${pct}`).toFixed(3));
}
function runTimedQuery(
+3 -2
View File
@@ -74,7 +74,7 @@ function findMatchingParen(source: string, openIndex: number): number {
let depth = 1;
const quoteState: QuoteScanState = { quote: null, escaped: false };
for (let i = openIndex + 1; i < source.length; i += 1) {
const ch = source[i];
const ch = source.charAt(i);
if (consumeQuotedChar(quoteState, ch)) {
continue;
}
@@ -192,7 +192,8 @@ function hasDynamicTmpdirJoin(source: string): boolean {
if (closeParenIndex !== -1) {
const argsSource = scanSource.slice(openParenIndex + 1, closeParenIndex);
const args = splitTopLevelArguments(argsSource);
if (args.length >= 2 && isOsTmpdirExpression(args[0])) {
const firstArg = args[0];
if (firstArg && isOsTmpdirExpression(firstArg)) {
for (const arg of args.slice(1)) {
const trimmed = arg.trim();
if (trimmed.startsWith("`") && trimmed.includes("${")) {
+3 -1
View File
@@ -7,6 +7,7 @@ import path from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import { completeSimple, type AssistantMessage, type Model } from "openclaw/plugin-sdk/llm";
import * as ts from "typescript";
import { expectDefined } from "../packages/normalization-core/src/expect.js";
import { formatErrorMessage } from "../src/infra/errors.ts";
import {
compareStringArrays,
@@ -1322,7 +1323,8 @@ function extractTranslationResult(message: AssistantMessage): string {
function parseTranslationReply(raw: string): Record<string, unknown> {
const trimmed = raw.trim();
const fenced = /^```(?:json)?\s*\n([\s\S]*?)\n```\s*$/.exec(trimmed);
return JSON.parse(fenced ? fenced[1] : trimmed) as Record<string, unknown>;
const json = fenced ? expectDefined(fenced[1], "fenced translation JSON body") : trimmed;
return JSON.parse(json);
}
export function parseTranslationBatchReply(
+2 -1
View File
@@ -4,6 +4,7 @@ import path from "node:path";
import { fileURLToPath } from "node:url";
import qrcode from "qrcode";
import { createServer, type Plugin, type ViteDevServer } from "vite";
import { expectDefined } from "../packages/normalization-core/src/expect.js";
import { CONTROL_UI_BOOTSTRAP_CONFIG_PATH } from "../src/gateway/control-ui-contract.js";
import {
createControlUiMockBootstrapConfig,
@@ -43,7 +44,7 @@ function mockFileHash(value: string): string {
function parseArgs(args: string[]): CliOptions {
const options: CliOptions = { allowedHosts: [], host: "127.0.0.1", port: 5187 };
for (let i = 0; i < args.length; i += 1) {
const arg = args[i];
const arg = expectDefined(args[i], `control UI mock argument at index ${i}`);
if (arg === "--allowed-host") {
const allowedHost = args[++i]?.trim();
if (allowedHost) {
+2 -1
View File
@@ -5,6 +5,7 @@ import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { expectDefined } from "../packages/normalization-core/src/expect.js";
import { normalizeOptionalString } from "../packages/normalization-core/src/string-coerce.js";
import { readBoundedResponseText as readBoundedResponseTextWithLimit } from "./lib/bounded-response.ts";
import {
@@ -44,7 +45,7 @@ const parseArgs = (args = process.argv.slice(2)): Args => {
let sessionKey: string | undefined;
for (let i = 0; i < args.length; i++) {
const arg = args[i];
const arg = expectDefined(args[i], `Claude usage argument at index ${i}`);
if (arg === "--agent") {
agentId = parseNonBlankArgValue(parseRequiredArgValue(args, i, "--agent"), "--agent");
i += 1;
@@ -283,7 +283,7 @@ function validateCliArgs(argv: string[]): void {
continue;
}
if (arg.startsWith("--") && arg.includes("=")) {
const [flag] = arg.split("=", 1);
const flag = arg.slice(0, arg.indexOf("="));
if (VALUE_OPTIONS.has(flag)) {
continue;
}
+2 -2
View File
@@ -176,7 +176,7 @@ export async function countSessionLogMentions(params: {
continue;
}
for (const [key, needle] of Object.entries(params.needles)) {
counts[key] += countOccurrences(scanText, needle);
counts[key] = (counts[key] ?? 0) + countOccurrences(scanText, needle);
}
}
}
@@ -246,7 +246,7 @@ async function countSqliteTranscriptMentions(params: {
continue;
}
for (const [key, needle] of Object.entries(params.needles)) {
counts[key] += countOccurrences(scanText, needle);
counts[key] = (counts[key] ?? 0) + countOccurrences(scanText, needle);
}
}
return counts;
+2 -1
View File
@@ -19,6 +19,7 @@
import * as fs from "node:fs";
import * as path from "node:path";
import * as v8 from "node:v8";
import { expectDefined } from "../packages/normalization-core/src/expect.js";
type Mode = "production" | "closure-extracted" | "closure-inline" | "synthetic-leak";
type Abortable = <T>(signal: AbortSignal, promise: Promise<T>) => Promise<T>;
@@ -65,7 +66,7 @@ function parseArgs(argv: string[]): Options {
};
const seenValueFlags = new Set<string>();
for (let i = 0; i < argv.length; i += 1) {
const arg = argv[i];
const arg = expectDefined(argv[i], `embedded abort benchmark argument at index ${i}`);
const next = argv[i + 1];
if (VALUE_FLAGS.has(arg)) {
if (seenValueFlags.has(arg)) {
+2 -1
View File
@@ -3,6 +3,7 @@ import { execFileSync, spawnSync } from "node:child_process";
import { createPrivateKey, createSign } from "node:crypto";
import { readFileSync } from "node:fs";
import { pathToFileURL } from "node:url";
import { expectDefined } from "../packages/normalization-core/src/expect.js";
import { readBoundedResponseText } from "./lib/bounded-response.ts";
import { parseStrictIntegerOption } from "./lib/dev-tooling-safety.ts";
@@ -50,7 +51,7 @@ type GitHubBodyReadOptions = {
export function parseRepoArg(args: string[]): string | null {
for (let i = 0; i < args.length; i += 1) {
const arg = args[i];
const arg = expectDefined(args[i], `GitHub CLI argument at index ${i}`);
if (arg === "-R" || arg === "--repo") {
return normalizeRepo(args[i + 1] ?? null);
}
@@ -2,6 +2,7 @@
import { spawnSync } from "node:child_process";
import fs from "node:fs/promises";
import path from "node:path";
import { expectDefined } from "../../packages/normalization-core/src/expect.js";
import { resolvePnpmRunner } from "../pnpm-runner.mjs";
import { stageCodexAppServerProtocolArtifacts } from "./codex-app-server-protocol-artifacts.js";
@@ -246,7 +247,7 @@ async function resolvePrimaryWorktreeSiblingCodex(repoRoot: string): Promise<str
return undefined;
}
const gitDir = path.resolve(repoRoot, match[1].trim());
const gitDir = path.resolve(repoRoot, expectDefined(match[1], "gitdir target").trim());
const worktreeMarker = `${path.sep}.git${path.sep}worktrees${path.sep}`;
const markerIndex = gitDir.indexOf(worktreeMarker);
if (markerIndex < 0) {
+4 -2
View File
@@ -1,3 +1,5 @@
import { expectDefined } from "../../packages/normalization-core/src/expect.js";
export interface TranslationMap {
[key: string]: string | TranslationMap;
}
@@ -266,7 +268,7 @@ function setNestedValue(root: TranslationMap, dottedKey: string, value: string):
const parts = dottedKey.split(".");
let cursor: TranslationMap = root;
for (let index = 0; index < parts.length - 1; index += 1) {
const key = parts[index];
const key = expectDefined(parts[index], `translation key segment at index ${index}`);
const next = cursor[key];
if (!next || typeof next === "string") {
const replacement: TranslationMap = {};
@@ -276,7 +278,7 @@ function setNestedValue(root: TranslationMap, dottedKey: string, value: string):
}
cursor = next;
}
cursor[parts.at(-1)!] = value;
cursor[expectDefined(parts.at(-1), "translation leaf key")] = value;
}
function renderTranslationValue(value: string | TranslationMap, indent = 0): string {
+17 -5
View File
@@ -2,6 +2,7 @@
import { spawnSync } from "node:child_process";
import { request } from "node:http";
import { createServer } from "node:net";
import { expectDefined } from "../../packages/normalization-core/src/expect.js";
export async function getFreePort(): Promise<number> {
return new Promise((resolve, reject) => {
@@ -98,9 +99,9 @@ export function readProcessTreeCpuMs(rootPid: number | undefined): number | null
if (!match) {
continue;
}
const pid = Number(match[1]);
const ppid = Number(match[2]);
const cpuMs = parsePsCpuTimeMs(match[3]);
const pid = Number(expectDefined(match[1], "process id from ps output"));
const ppid = Number(expectDefined(match[2], "parent process id from ps output"));
const cpuMs = parsePsCpuTimeMs(expectDefined(match[3], "CPU time from ps output"));
if (!Number.isInteger(pid) || !Number.isInteger(ppid) || cpuMs === null) {
continue;
}
@@ -153,10 +154,21 @@ function parsePsCpuTimeMs(raw: string): number | null {
return null;
}
if (parts.length === 2) {
return Math.round((parts[0] * 60 + parts[1]) * 1000);
const [minutes, seconds] = parts;
return Math.round(
(expectDefined(minutes, "process CPU minutes") * 60 +
expectDefined(seconds, "process CPU seconds")) *
1000,
);
}
if (parts.length === 3) {
return Math.round((parts[0] * 60 * 60 + parts[1] * 60 + parts[2]) * 1000);
const [hours, minutes, seconds] = parts;
return Math.round(
(expectDefined(hours, "process CPU hours") * 60 * 60 +
expectDefined(minutes, "process CPU minutes") * 60 +
expectDefined(seconds, "process CPU seconds")) *
1000,
);
}
return null;
}
+3 -2
View File
@@ -3,6 +3,7 @@ import { execFileSync } from "node:child_process";
import { mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { expectDefined } from "../../packages/normalization-core/src/expect.js";
import { normalizeOptionalString } from "../../packages/normalization-core/src/string-coerce.js";
import { validateExternalCodePluginPackageJson } from "../../packages/plugin-package-contract/src/index.ts";
import {
@@ -259,7 +260,7 @@ export function parsePluginReleaseArgs(argv: string[]): ParsedPluginReleaseArgs
let headRef: string | undefined;
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index];
const arg = expectDefined(argv[index], `plugin release argument at index ${index}`);
if (arg === "--") {
continue;
}
@@ -312,7 +313,7 @@ export function parsePluginNpmReleaseArgs(argv: string[]): ParsedPluginNpmReleas
const baseArgs: string[] = [];
let npmDistTag: "extended-stable" | undefined;
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index];
const arg = expectDefined(argv[index], `plugin npm release argument at index ${index}`);
if (arg !== "--npm-dist-tag") {
baseArgs.push(arg);
continue;
@@ -2,6 +2,7 @@
import { createHash } from "node:crypto";
import fs from "node:fs/promises";
import path from "node:path";
import { expectDefined } from "../../packages/normalization-core/src/expect.js";
/** Rendered baseline artifact for the sessions/transcripts SQLite schema. */
export type SqliteSessionSchemaBaselineRender = {
@@ -62,14 +63,14 @@ function readCreatedTableName(statement: string): string | null {
const match = statement.match(
/^CREATE\s+TABLE\s+IF\s+NOT\s+EXISTS\s+("[^"]+"|[A-Za-z_][A-Za-z0-9_]*)\b/iu,
);
return match ? normalizeIdentifier(match[1]) : null;
return match ? normalizeIdentifier(expectDefined(match[1], "created table name")) : null;
}
function readIndexedTableName(statement: string): string | null {
const match = statement.match(
/^CREATE\s+(?:UNIQUE\s+)?INDEX\s+IF\s+NOT\s+EXISTS\s+("[^"]+"|[A-Za-z_][A-Za-z0-9_]*)\s+ON\s+("[^"]+"|[A-Za-z_][A-Za-z0-9_]*)\b/isu,
);
return match ? normalizeIdentifier(match[2]) : null;
return match ? normalizeIdentifier(expectDefined(match[2], "indexed table name")) : null;
}
function isTargetSessionSchemaStatement(statement: string): boolean {
+7 -2
View File
@@ -1,6 +1,7 @@
// Analyze script supports OpenClaw repository automation.
import path from "node:path";
import ts from "typescript";
import { expectDefined } from "../../../packages/normalization-core/src/expect.js";
import {
canonicalSymbolInfo,
countIdentifierUsages,
@@ -298,8 +299,12 @@ function finalizeRecords(records: TopologyRecord[]) {
return byRefs;
}
return (
left.publicSpecifiers[0].localeCompare(right.publicSpecifiers[0]) ||
left.exportNames[0].localeCompare(right.exportNames[0])
expectDefined(left.publicSpecifiers[0], "left topology public specifier").localeCompare(
expectDefined(right.publicSpecifiers[0], "right topology public specifier"),
) ||
expectDefined(left.exportNames[0], "left topology export name").localeCompare(
expectDefined(right.exportNames[0], "right topology export name"),
)
);
});
}
+27 -17
View File
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import { expectDefined } from "../packages/normalization-core/src/expect.js";
import { translateNativeEntries } from "./control-ui-i18n.ts";
type NativeI18nSurface = "android" | "apple";
@@ -211,18 +212,18 @@ function isAsciiAlphaNumeric(character: string): boolean {
export function isConditionalBranchIdentifier(source: string): boolean {
let index = 0;
while (index < source.length && isAsciiLowercaseLetter(source[index])) {
while (index < source.length && isAsciiLowercaseLetter(source.charAt(index))) {
index += 1;
}
// Keep this scanner linear: PR-controlled native source passes through CI,
// so a backtracking regex here can become a cheap native-i18n DoS trigger.
if (index === 0 || index >= source.length || !isAsciiUppercaseLetter(source[index])) {
if (index === 0 || index >= source.length || !isAsciiUppercaseLetter(source.charAt(index))) {
return false;
}
for (index += 1; index < source.length; index += 1) {
if (!isAsciiAlphaNumeric(source[index])) {
if (!isAsciiAlphaNumeric(source.charAt(index))) {
return false;
}
}
@@ -622,15 +623,18 @@ function identifierBefore(source: string, offset: number): string | null {
cursor -= 1;
}
const end = cursor + 1;
while (cursor >= 0 && (isAsciiAlphaNumeric(source[cursor]) || source[cursor] === "_")) {
while (
cursor >= 0 &&
(isAsciiAlphaNumeric(source.charAt(cursor)) || source.charAt(cursor) === "_")
) {
cursor -= 1;
}
const start = cursor + 1;
if (
start === end ||
(!isAsciiLowercaseLetter(source[start]) &&
!isAsciiUppercaseLetter(source[start]) &&
source[start] !== "_")
(!isAsciiLowercaseLetter(source.charAt(start)) &&
!isAsciiUppercaseLetter(source.charAt(start)) &&
source.charAt(start) !== "_")
) {
return null;
}
@@ -739,18 +743,18 @@ function addCapturedLiteralCandidates(
function skipWhitespaceAndBrace(source: string, offset: number): number {
let cursor = offset;
while (cursor < source.length && /\s/u.test(source[cursor])) {
while (cursor < source.length && /\s/u.test(source.charAt(cursor))) {
cursor += 1;
}
if (source[cursor] === "{") {
if (source.charAt(cursor) === "{") {
cursor += 1;
while (cursor < source.length && /\s/u.test(source[cursor])) {
while (cursor < source.length && /\s/u.test(source.charAt(cursor))) {
cursor += 1;
}
}
if (source.startsWith("return", cursor) && !isAsciiAlphaNumeric(source[cursor + 6] ?? "")) {
cursor += 6;
while (cursor < source.length && /\s/u.test(source[cursor])) {
while (cursor < source.length && /\s/u.test(source.charAt(cursor))) {
cursor += 1;
}
}
@@ -1189,7 +1193,9 @@ async function mapWithConcurrency<T, R>(
if (index >= values.length) {
return;
}
results[index] = await run(values[index]);
results[index] = await run(
expectDefined(values[index], `native i18n concurrency input at index ${index}`),
);
}
}),
);
@@ -1306,10 +1312,14 @@ function adjacentDuplicateWords(value: string, locale: string): string[] {
const duplicates = new Set<string>();
for (let index = 1; index < words.length; index += 1) {
if (
words[index - 1].normalize("NFKC").toLocaleLowerCase(locale) ===
words[index].normalize("NFKC").toLocaleLowerCase(locale)
expectDefined(words[index - 1], `native i18n word before index ${index}`)
.normalize("NFKC")
.toLocaleLowerCase(locale) ===
expectDefined(words[index], `native i18n word at index ${index}`)
.normalize("NFKC")
.toLocaleLowerCase(locale)
) {
duplicates.add(words[index]);
duplicates.add(expectDefined(words[index], `duplicate native i18n word at index ${index}`));
}
}
return [...duplicates].toSorted(compareCodePoints);
@@ -1443,8 +1453,8 @@ export function validateNativeLocaleArtifact(
errors.push(`entry count must be ${inventory.length}, got ${rawEntries.length}`);
}
for (let index = 0; index < Math.min(entries.length, inventory.length); index += 1) {
const actual = entries[index];
const expected = inventory[index];
const actual = expectDefined(entries[index], `native locale entry at index ${index}`);
const expected = expectDefined(inventory[index], `native inventory entry at index ${index}`);
if (actual.id !== expected.id) {
errors.push(
`entries[${index}].id must be ${JSON.stringify(expected.id)}, got ${JSON.stringify(actual.id)}`,
+7 -4
View File
@@ -349,6 +349,9 @@ export function parseArgs(argv: string[]): ParsedArgs {
const parsed: ParsedArgs = {};
for (let index = 0; index < argv.length; index += 1) {
const token = argv[index];
if (token === undefined) {
throw new Error(`Missing cross-OS release argument at index ${index}`);
}
if (!token.startsWith("--")) {
continue;
}
@@ -4081,16 +4084,16 @@ function parseAgentPayloadTexts(stdout: string) {
: Array.isArray(payload?.result?.payloads)
? payload.result.payloads
: [];
const payloadTexts = Array.isArray(entries)
? entries.flatMap((entry) => (typeof entry?.text === "string" ? [entry.text] : []))
: [];
const payloadTexts = entries.flatMap((entry) =>
typeof entry?.text === "string" ? [entry.text] : [],
);
return [...directTexts, ...payloadTexts];
} catch {
const finalTextMatches = [
...stdout.matchAll(
/"(?:finalAssistantVisibleText|finalAssistantRawText|text)"\s*:\s*"([^"]*)"/gu,
),
].map((match) => match[1]);
].flatMap((match) => (match[1] === undefined ? [] : [match[1]]));
return finalTextMatches.length > 0 ? finalTextMatches : stdout.trim() ? [stdout] : [];
}
}
+6 -2
View File
@@ -24,6 +24,7 @@ import {
win32 as pathWin32,
} from "node:path";
import { pathToFileURL } from "node:url";
import { expectDefined } from "../packages/normalization-core/src/expect.js";
import { formatErrorMessage } from "../src/infra/errors.ts";
import { ALWAYS_ALLOWED_RUNTIME_DIR_NAMES } from "../src/plugin-sdk/facade-activation-contract.ts";
import { BUNDLED_RUNTIME_SIDECAR_PATHS } from "../src/plugins/runtime-sidecar-paths.ts";
@@ -459,7 +460,10 @@ export function collectInstalledBundledRuntimeSidecarPaths(packageRoot: string):
const installedExtensionIds = collectInstalledBundledExtensionIds(packageRoot);
return PUBLISHED_BUNDLED_RUNTIME_SIDECAR_PATHS.filter((relativePath) => {
const match = /^dist\/extensions\/([^/]+)\//u.exec(relativePath);
return match !== null && installedExtensionIds.has(match[1]);
return (
match !== null &&
installedExtensionIds.has(expectDefined(match[1], "bundled runtime extension id"))
);
});
}
@@ -896,7 +900,7 @@ function collectExpectedBundledExtensionPackageIds(): ReadonlySet<string> {
for (const relativePath of listBundledPluginPackArtifacts()) {
const match = /^dist\/extensions\/([^/]+)\/package\.json$/u.exec(relativePath);
if (match) {
ids.add(match[1]);
ids.add(expectDefined(match[1], "bundled package extension id"));
}
}
return ids;
+6 -1
View File
@@ -5,6 +5,7 @@ import { mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSy
import { tmpdir } from "node:os";
import { join } from "node:path";
import { pathToFileURL } from "node:url";
import { expectDefined } from "../packages/normalization-core/src/expect.js";
import { formatErrorMessage } from "../src/infra/errors.ts";
import { type NpmVerifyCommandInvocation, runNpmVerifyCommand } from "./lib/npm-verify-exec.ts";
import { runInstalledWorkspaceBootstrapSmoke } from "./lib/workspace-bootstrap-smoke.mjs";
@@ -105,7 +106,11 @@ function main(argv = process.argv.slice(2)): void {
{
private: true,
dependencies: {
"@openclaw/ai": pathToFileURL(realpathSync(args.dependencyTarballPaths[0])).href,
"@openclaw/ai": pathToFileURL(
realpathSync(
expectDefined(args.dependencyTarballPaths[0], "prepared dependency tarball"),
),
).href,
openclaw: pathToFileURL(realpathSync(args.tarballPath)).href,
},
},
+1 -1
View File
@@ -163,7 +163,7 @@ function isNodeModulesPackageRoot(segments: string[], index: number): boolean {
if (parent === "node_modules") {
return true;
}
return parent?.startsWith("@") && segments[index - 2] === "node_modules";
return parent !== undefined && parent.startsWith("@") && segments[index - 2] === "node_modules";
}
function pathContainsPackedTestCargo(packedPath: string): boolean {
+2 -1
View File
@@ -1,6 +1,7 @@
// Prepare Codex Ci Config script supports OpenClaw repository automation.
import fs from "node:fs/promises";
import path from "node:path";
import { expectDefined } from "../packages/normalization-core/src/expect.js";
function tomlString(value: string): string {
return JSON.stringify(value);
@@ -50,7 +51,7 @@ export async function writeCiSafeCodexConfig(params: {
}
if (path.basename(process.argv[1] ?? "") === "prepare-codex-ci-config.ts") {
const outputPath = process.argv[2];
const outputPath = expectDefined(process.argv[2], "Codex CI config output path");
const projectPath = process.argv[3] ?? process.cwd();
await writeCiSafeCodexConfig({ outputPath, projectPath });
}
+1 -1
View File
@@ -84,7 +84,7 @@ function camelCase(input: string) {
.trim()
.toLowerCase()
.split(/\s+/)
.map((p, i) => (i === 0 ? p : p[0].toUpperCase() + p.slice(1)))
.map((p, i) => (i === 0 ? p : p.charAt(0).toUpperCase() + p.slice(1)))
.join("");
}
+1 -1
View File
@@ -118,7 +118,7 @@ export async function main(
function isMainModule() {
const entry = process.argv[1];
return Boolean(entry) && import.meta.url === pathToFileURL(entry).href;
return entry !== undefined && import.meta.url === pathToFileURL(entry).href;
}
if (isMainModule()) {
+1 -1
View File
@@ -841,7 +841,7 @@ function copyStaticSourceAssets({
taxonomyPath: string;
}): string[] {
fs.mkdirSync(staticAssetsDir, { recursive: true });
const copied = [
const copied: Array<[string, string]> = [
[taxonomyPath, path.join(staticAssetsDir, "taxonomy.yaml")],
[scoresPath, path.join(staticAssetsDir, "maturity-scores.yaml")],
];
+10 -3
View File
@@ -18,6 +18,7 @@ import type { Dirent } from "node:fs";
import { tmpdir } from "node:os";
import { basename, dirname, join, resolve, win32 } from "node:path";
import { pathToFileURL } from "node:url";
import { expectDefined } from "../packages/normalization-core/src/expect.js";
import { COMPLETION_SKIP_PLUGIN_COMMANDS_ENV } from "../src/cli/completion-runtime.ts";
import {
isLegacyPluginDependencyInstallStagePath,
@@ -89,7 +90,12 @@ const requiredPathGroups = [
...listBundledPluginPackArtifacts(),
...listStaticExtensionAssetOutputs().filter((relativePath) => {
const match = /^dist\/extensions\/([^/]+)\//u.exec(relativePath);
return !match || !rootPackageExcludedExtensionDirs.has(match[1]);
return (
!match ||
!rootPackageExcludedExtensionDirs.has(
expectDefined(match[1], "release-check extension artifact id"),
)
);
}),
...WORKSPACE_TEMPLATE_PACK_PATHS,
"scripts/npm-runner.mjs",
@@ -422,7 +428,7 @@ export function resolvePackedTarballPath(packDestination: string, results: PackR
`release-check: npm pack produced ${filenames.length} tarballs; expected exactly one.`,
);
}
const filename = filenames[0];
const filename = expectDefined(filenames[0], "npm pack tarball filename");
const filenameBasename = basename(filename);
const resolvedDestination = resolve(packDestination);
const resolvedTarball = resolve(resolvedDestination, filenameBasename);
@@ -1112,7 +1118,8 @@ export function collectAppcastSparkleVersionErrors(xml: string): string[] {
errors.push("appcast.xml contains no <item> entries.");
}
for (const [, item] of itemMatches) {
for (const [index, match] of itemMatches.entries()) {
const item = expectDefined(match[1], `appcast item body at index ${index}`);
const title = extractTag(item, "title") ?? "unknown";
const shortVersion = extractTag(item, "sparkle:shortVersionString");
const sparkleVersion = extractTag(item, "sparkle:version");
+5 -1
View File
@@ -1,6 +1,7 @@
// Release Version keeps the core and explicitly selected native release trains aligned.
import fs from "node:fs";
import path from "node:path";
import { expectDefined } from "../packages/normalization-core/src/expect.js";
import {
canonicalAndroidVersionCode,
normalizeAndroidVersionCode,
@@ -131,7 +132,10 @@ export function applyReleaseVersionPlan(plan: ReleaseVersionPlan): void {
tempPaths.push(tempPath);
}
for (const [index, change] of plan.changes.entries()) {
fs.renameSync(tempPaths[index], change.path);
fs.renameSync(
expectDefined(tempPaths[index], `release temp path at index ${index}`),
change.path,
);
}
} finally {
for (const tempPath of tempPaths) {
+1 -1
View File
@@ -109,7 +109,7 @@ function resolveLabelMetadata(label: string): { color: string; description?: str
if (extraMetadata) {
return extraMetadata;
}
const prefix = label.includes(":") ? label.split(":", 1)[0].trim() : label.trim();
const prefix = label.includes(":") ? label.slice(0, label.indexOf(":")).trim() : label.trim();
return { color: COLOR_BY_PREFIX.get(prefix) ?? "ededed" };
}
+2 -1
View File
@@ -27,6 +27,7 @@ import os from "node:os";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { confirm, isCancel } from "@clack/prompts";
import { expectDefined } from "../packages/normalization-core/src/expect.js";
import { stylePromptMessage } from "../packages/terminal-core/src/prompt-style.js";
import { theme } from "../packages/terminal-core/src/theme.js";
import {
@@ -58,7 +59,7 @@ function parseArgs(args: string[]): Options {
};
for (let index = 0; index < args.length; index += 1) {
const arg = args[index];
const arg = expectDefined(args[index], `shell completion argument at index ${index}`);
if (arg === "--check-only") {
options.checkOnly = true;
} else if (arg === "--force") {
+2 -1
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env node
// Ts Topology script supports OpenClaw repository automation.
import path from "node:path";
import { expectDefined } from "../packages/normalization-core/src/expect.js";
import { formatErrorMessage } from "../src/infra/errors.ts";
import { parsePositiveInt } from "./lib/numeric-options.mjs";
import { analyzeTopology } from "./lib/ts-topology/analyze.js";
@@ -88,7 +89,7 @@ function parseArgs(argv: string[]): CliOptions {
options.report = (value as TopologyReportName | undefined) ?? options.report;
break;
case "--limit":
options.limit = parsePositiveInt(value, "--limit");
options.limit = parsePositiveInt(expectDefined(value, "--limit value"), "--limit");
break;
case "--repo-root":
options.repoRoot = path.resolve(value ?? options.repoRoot);
+14 -8
View File
@@ -2,6 +2,7 @@
import { execFileSync, execSync } from "node:child_process";
import { readFileSync, writeFileSync } from "node:fs";
import { resolve } from "node:path";
import { expectDefined } from "../packages/normalization-core/src/expect.js";
import type { ApiContributor, Entry, MapConfig, User } from "./update-clawtributors.types.js";
const REPO = "openclaw/openclaw";
@@ -99,13 +100,13 @@ for (const line of log.split("\n")) {
}
// Skip docs paths so bulk-generated i18n scaffolds don't inflate rankings
const filePath = parts[2];
const filePath = expectDefined(parts[2], "git numstat file path");
if (filePath.startsWith("docs/")) {
continue;
}
const adds = parseCount(parts[0]);
const dels = parseCount(parts[1]);
const adds = parseCount(expectDefined(parts[0], "git numstat additions"));
const dels = parseCount(expectDefined(parts[1], "git numstat deletions"));
const total = adds + dels;
if (!total) {
continue;
@@ -649,7 +650,10 @@ async function mapConcurrent<T, R>(
const workers = Array.from({ length: Math.max(1, Math.min(limit, items.length)) }, async () => {
while (nextIndex < items.length) {
const index = nextIndex++;
results[index] = await mapper(items[index], index);
results[index] = await mapper(
expectDefined(items[index], `clawtributor concurrency input at index ${index}`),
index,
);
}
});
await Promise.all(workers);
@@ -702,7 +706,7 @@ function readJpegDimensions(buffer: Buffer): { width: number; height: number } |
continue;
}
const marker = buffer[offset + 1];
const marker = expectDefined(buffer[offset + 1], `JPEG marker at byte ${offset + 1}`);
offset += 2;
if (marker === 0xd8 || marker === 0xd9) {
@@ -759,13 +763,15 @@ function resolveLogin(
}
if (email && email.endsWith("@users.noreply.github.com")) {
const local = email.split("@", 1)[0];
const login = local.includes("+") ? local.split("+")[1] : local;
const local = expectDefined(email.split("@", 1)[0], "GitHub noreply email local part");
const login = local.includes("+")
? expectDefined(local.split("+")[1], "GitHub noreply email login suffix")
: local;
return normalizeLogin(login);
}
if (email && email.endsWith("@github.com")) {
const login = email.split("@", 1)[0];
const login = expectDefined(email.split("@", 1)[0], "GitHub email local part");
if (apiByLoginValue.has(login.toLowerCase())) {
return normalizeLogin(login);
}
+4 -1
View File
@@ -4,6 +4,7 @@ import { createHash } from "node:crypto";
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import { expectDefined } from "../packages/normalization-core/src/expect.js";
import type { RootHelpRenderOptions } from "../src/cli/program/root-help.js";
import type { OpenClawConfig } from "../src/config/config.js";
import { resolveWindowsTaskkillPath } from "./lib/windows-taskkill.mjs";
@@ -364,7 +365,9 @@ async function mapWithConcurrency<T, R>(
if (index >= values.length) {
return;
}
results[index] = await run(values[index]);
results[index] = await run(
expectDefined(values[index], `CLI metadata concurrency input at index ${index}`),
);
}
}),
);
+1
View File
@@ -1,6 +1,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noUncheckedIndexedAccess": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"tsBuildInfoFile": ".artifacts/tsgo-cache/scripts.tsbuildinfo"