mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-28 05:16:23 -06:00
refactor: centralize numeric coercion helpers
This commit is contained in:
@@ -1,8 +1,12 @@
|
||||
import type { TSchema } from "typebox";
|
||||
import { readLocalFileSafely } from "../../infra/fs-safe.js";
|
||||
import { parseStrictFiniteNumber } from "../../infra/parse-finite-number.js";
|
||||
import { detectMime } from "../../media/mime.js";
|
||||
import { readSnakeCaseParamRaw } from "../../param-key.js";
|
||||
import {
|
||||
asPositiveSafeInteger,
|
||||
asSafeIntegerInRange,
|
||||
parseStrictFiniteNumber,
|
||||
} from "../../shared/number-coercion.js";
|
||||
import { normalizeStringEntries } from "../../shared/string-normalization.js";
|
||||
import type { ImageSanitizationLimits } from "../image-sanitization.js";
|
||||
import type { AgentTool, AgentToolResult, AgentToolUpdateCallback } from "../runtime/index.js";
|
||||
@@ -194,10 +198,10 @@ export function readNumberParam(
|
||||
return undefined;
|
||||
}
|
||||
if (positiveInteger) {
|
||||
return Number.isSafeInteger(value) && value > 0 ? value : undefined;
|
||||
return asPositiveSafeInteger(value);
|
||||
}
|
||||
if (nonNegativeInteger) {
|
||||
return Number.isSafeInteger(value) && value >= 0 ? value : undefined;
|
||||
return asSafeIntegerInRange(value, { min: 0 });
|
||||
}
|
||||
return integer ? Math.trunc(value) : value;
|
||||
}
|
||||
|
||||
@@ -1,54 +1,7 @@
|
||||
function normalizeNumericString(value: string): string | undefined {
|
||||
const trimmed = value.trim();
|
||||
return trimmed ? trimmed : undefined;
|
||||
}
|
||||
|
||||
export function parseFiniteNumber(value: unknown): number | undefined {
|
||||
if (typeof value === "number" && Number.isFinite(value)) {
|
||||
return value;
|
||||
}
|
||||
if (typeof value === "string") {
|
||||
return parseStrictFiniteNumber(value);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function parseStrictInteger(value: unknown): number | undefined {
|
||||
if (typeof value === "number") {
|
||||
return Number.isSafeInteger(value) ? value : undefined;
|
||||
}
|
||||
if (typeof value !== "string") {
|
||||
return undefined;
|
||||
}
|
||||
const normalized = normalizeNumericString(value);
|
||||
if (!normalized || !/^[+-]?\d+$/.test(normalized)) {
|
||||
return undefined;
|
||||
}
|
||||
const parsed = Number(normalized);
|
||||
return Number.isSafeInteger(parsed) ? parsed : undefined;
|
||||
}
|
||||
|
||||
export function parseStrictFiniteNumber(value: unknown): number | undefined {
|
||||
if (typeof value === "number") {
|
||||
return Number.isFinite(value) ? value : undefined;
|
||||
}
|
||||
if (typeof value !== "string") {
|
||||
return undefined;
|
||||
}
|
||||
const normalized = normalizeNumericString(value);
|
||||
if (!normalized || !/^[+-]?(?:(?:\d+\.?\d*)|(?:\.\d+))(?:e[+-]?\d+)?$/i.test(normalized)) {
|
||||
return undefined;
|
||||
}
|
||||
const parsed = Number(normalized);
|
||||
return Number.isFinite(parsed) ? parsed : undefined;
|
||||
}
|
||||
|
||||
export function parseStrictPositiveInteger(value: unknown): number | undefined {
|
||||
const parsed = parseStrictInteger(value);
|
||||
return parsed !== undefined && parsed > 0 ? parsed : undefined;
|
||||
}
|
||||
|
||||
export function parseStrictNonNegativeInteger(value: unknown): number | undefined {
|
||||
const parsed = parseStrictInteger(value);
|
||||
return parsed !== undefined && parsed >= 0 ? parsed : undefined;
|
||||
}
|
||||
export {
|
||||
parseFiniteNumber,
|
||||
parseStrictFiniteNumber,
|
||||
parseStrictInteger,
|
||||
parseStrictNonNegativeInteger,
|
||||
parseStrictPositiveInteger,
|
||||
} from "../shared/number-coercion.js";
|
||||
|
||||
@@ -6,4 +6,4 @@ export {
|
||||
parseStrictFiniteNumber,
|
||||
parseStrictNonNegativeInteger,
|
||||
parseStrictPositiveInteger,
|
||||
} from "../infra/parse-finite-number.js";
|
||||
} from "../shared/number-coercion.js";
|
||||
|
||||
@@ -20,6 +20,10 @@ export {
|
||||
asPositiveSafeInteger,
|
||||
asSafeIntegerInRange,
|
||||
parseFiniteNumber,
|
||||
parseStrictFiniteNumber,
|
||||
parseStrictInteger,
|
||||
parseStrictNonNegativeInteger,
|
||||
parseStrictPositiveInteger,
|
||||
} from "../shared/number-coercion.js";
|
||||
export { asBoolean, parseBooleanValue } from "../utils/boolean.js";
|
||||
export {
|
||||
|
||||
@@ -4,6 +4,10 @@ import {
|
||||
asFiniteNumberInRange,
|
||||
asSafeIntegerInRange,
|
||||
parseFiniteNumber,
|
||||
parseStrictFiniteNumber,
|
||||
parseStrictInteger,
|
||||
parseStrictNonNegativeInteger,
|
||||
parseStrictPositiveInteger,
|
||||
} from "./number-coercion.js";
|
||||
|
||||
describe("number-coercion", () => {
|
||||
@@ -37,4 +41,28 @@ describe("number-coercion", () => {
|
||||
expect(parseFiniteNumber("")).toBeUndefined();
|
||||
expect(parseFiniteNumber("nope")).toBeUndefined();
|
||||
});
|
||||
|
||||
test("parseStrictInteger accepts only safe integer tokens", () => {
|
||||
expect(parseStrictInteger("42")).toBe(42);
|
||||
expect(parseStrictInteger(" -7 ")).toBe(-7);
|
||||
expect(parseStrictInteger("+9")).toBe(9);
|
||||
expect(parseStrictInteger("1.5")).toBeUndefined();
|
||||
expect(parseStrictInteger("1e3")).toBeUndefined();
|
||||
expect(parseStrictInteger(Number.MAX_SAFE_INTEGER + 1)).toBeUndefined();
|
||||
});
|
||||
|
||||
test("parseStrictFiniteNumber rejects partial numeric strings", () => {
|
||||
expect(parseStrictFiniteNumber("42")).toBe(42);
|
||||
expect(parseStrictFiniteNumber(".5")).toBe(0.5);
|
||||
expect(parseStrictFiniteNumber("1e3")).toBe(1000);
|
||||
expect(parseStrictFiniteNumber("3.14ms")).toBeUndefined();
|
||||
expect(parseStrictFiniteNumber("0x10")).toBeUndefined();
|
||||
});
|
||||
|
||||
test("strict integer range helpers enforce sign", () => {
|
||||
expect(parseStrictPositiveInteger("9")).toBe(9);
|
||||
expect(parseStrictPositiveInteger("0")).toBeUndefined();
|
||||
expect(parseStrictNonNegativeInteger("0")).toBe(0);
|
||||
expect(parseStrictNonNegativeInteger("-1")).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -47,21 +47,58 @@ export function asSafeIntegerInRange(
|
||||
return value;
|
||||
}
|
||||
|
||||
function normalizeNumericString(value: string): string | undefined {
|
||||
const trimmed = value.trim();
|
||||
return trimmed ? trimmed : undefined;
|
||||
}
|
||||
|
||||
export function parseFiniteNumber(value: unknown): number | undefined {
|
||||
if (typeof value === "number") {
|
||||
return Number.isFinite(value) ? value : undefined;
|
||||
}
|
||||
return parseStrictFiniteNumber(value);
|
||||
}
|
||||
|
||||
export function parseStrictInteger(value: unknown): number | undefined {
|
||||
if (typeof value === "number") {
|
||||
return Number.isSafeInteger(value) ? value : undefined;
|
||||
}
|
||||
if (typeof value !== "string") {
|
||||
return undefined;
|
||||
}
|
||||
const normalized = normalizeNumericString(value);
|
||||
if (!normalized || !/^[+-]?\d+$/.test(normalized)) {
|
||||
return undefined;
|
||||
}
|
||||
const parsed = Number(normalized);
|
||||
return Number.isSafeInteger(parsed) ? parsed : undefined;
|
||||
}
|
||||
|
||||
export function parseStrictFiniteNumber(value: unknown): number | undefined {
|
||||
if (typeof value === "number") {
|
||||
return Number.isFinite(value) ? value : undefined;
|
||||
}
|
||||
if (typeof value !== "string") {
|
||||
return undefined;
|
||||
}
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed || !/^[+-]?(?:(?:\d+\.?\d*)|(?:\.\d+))(?:e[+-]?\d+)?$/i.test(trimmed)) {
|
||||
const normalized = normalizeNumericString(value);
|
||||
if (!normalized || !/^[+-]?(?:(?:\d+\.?\d*)|(?:\.\d+))(?:e[+-]?\d+)?$/i.test(normalized)) {
|
||||
return undefined;
|
||||
}
|
||||
const parsed = Number(trimmed);
|
||||
const parsed = Number(normalized);
|
||||
return Number.isFinite(parsed) ? parsed : undefined;
|
||||
}
|
||||
|
||||
export function asPositiveSafeInteger(value: unknown): number | undefined {
|
||||
return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : undefined;
|
||||
}
|
||||
|
||||
export function parseStrictPositiveInteger(value: unknown): number | undefined {
|
||||
const parsed = parseStrictInteger(value);
|
||||
return parsed !== undefined && parsed > 0 ? parsed : undefined;
|
||||
}
|
||||
|
||||
export function parseStrictNonNegativeInteger(value: unknown): number | undefined {
|
||||
const parsed = parseStrictInteger(value);
|
||||
return parsed !== undefined && parsed >= 0 ? parsed : undefined;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user