From 9b605846bb0f30caa0cd40fc231973e51727d7c1 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sat, 30 May 2026 08:53:37 +0200 Subject: [PATCH] refactor: share ACP metadata readers --- src/acp/meta.test.ts | 24 ++++++++++++++++ src/acp/meta.ts | 65 +++++++++++++++++--------------------------- 2 files changed, 49 insertions(+), 40 deletions(-) create mode 100644 src/acp/meta.test.ts diff --git a/src/acp/meta.test.ts b/src/acp/meta.test.ts new file mode 100644 index 00000000000..7e8e522fed0 --- /dev/null +++ b/src/acp/meta.test.ts @@ -0,0 +1,24 @@ +import { describe, expect, it } from "vitest"; +import { readBool, readNonNegativeInteger, readNumber, readString } from "./meta.js"; + +describe("ACP metadata readers", () => { + it("returns the first normalized string value", () => { + expect(readString({ old: " ", current: " session-1 " }, ["old", "current"])).toBe("session-1"); + }); + + it("preserves false boolean values", () => { + expect(readBool({ enabled: false, fallback: true }, ["enabled", "fallback"])).toBe(false); + }); + + it("accepts finite numbers and rejects non-numeric values", () => { + expect(readNumber({ first: "1", second: 0 }, ["first", "second"])).toBe(0); + expect(readNumber({ first: Number.POSITIVE_INFINITY }, ["first"])).toBeUndefined(); + }); + + it("accepts zero as a non-negative integer", () => { + expect(readNonNegativeInteger({ count: 0, fallback: 2 }, ["count", "fallback"])).toBe(0); + expect( + readNonNegativeInteger({ count: -1, fallback: 2.5 }, ["count", "fallback"]), + ).toBeUndefined(); + }); +}); diff --git a/src/acp/meta.ts b/src/acp/meta.ts index 9ecb171f3af..4a2b8824614 100644 --- a/src/acp/meta.ts +++ b/src/acp/meta.ts @@ -1,65 +1,50 @@ import { normalizeOptionalString } from "../shared/string-coerce.js"; +function readMetaValue( + meta: Record | null | undefined, + keys: string[], + normalize: (value: unknown) => T | undefined, +): T | undefined { + if (!meta) { + return undefined; + } + for (const key of keys) { + const normalized = normalize(meta[key]); + if (normalized !== undefined) { + return normalized; + } + } + return undefined; +} + export function readString( meta: Record | null | undefined, keys: string[], ): string | undefined { - if (!meta) { - return undefined; - } - for (const key of keys) { - const value = normalizeOptionalString(meta[key]); - if (value) { - return value; - } - } - return undefined; + return readMetaValue(meta, keys, normalizeOptionalString); } export function readBool( meta: Record | null | undefined, keys: string[], ): boolean | undefined { - if (!meta) { - return undefined; - } - for (const key of keys) { - const value = meta[key]; - if (typeof value === "boolean") { - return value; - } - } - return undefined; + return readMetaValue(meta, keys, (value) => (typeof value === "boolean" ? value : undefined)); } export function readNumber( meta: Record | null | undefined, keys: string[], ): number | undefined { - if (!meta) { - return undefined; - } - for (const key of keys) { - const value = meta[key]; - if (typeof value === "number" && Number.isFinite(value)) { - return value; - } - } - return undefined; + return readMetaValue(meta, keys, (value) => + typeof value === "number" && Number.isFinite(value) ? value : undefined, + ); } export function readNonNegativeInteger( meta: Record | null | undefined, keys: string[], ): number | undefined { - if (!meta) { - return undefined; - } - for (const key of keys) { - const value = meta[key]; - if (typeof value === "number" && Number.isSafeInteger(value) && value >= 0) { - return value; - } - } - return undefined; + return readMetaValue(meta, keys, (value) => + typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : undefined, + ); }