diff --git a/.changeset/is-hex-string-empty-body.md b/.changeset/is-hex-string-empty-body.md new file mode 100644 index 00000000..ccde0b9a --- /dev/null +++ b/.changeset/is-hex-string-empty-body.md @@ -0,0 +1,5 @@ +--- +"@agentcommercekit/keys": patch +--- + +`isHexString` now returns `true` for a bare `0x` prefix with an empty body, matching its documented behavior. The check required at least one hex digit after the prefix, so `isHexString("0x")` returned `false` even though the JSDoc example states it returns `true`. A bare empty string with no prefix (`""`) continues to return `false`. diff --git a/packages/keys/src/encoding/hex.test.ts b/packages/keys/src/encoding/hex.test.ts index c66e3b36..9977fa97 100644 --- a/packages/keys/src/encoding/hex.test.ts +++ b/packages/keys/src/encoding/hex.test.ts @@ -44,6 +44,14 @@ describe("isHexString", () => { expect(isHexString("1234567890abcdef")).toBe(true) }) + test("returns true for a bare 0x prefix with an empty body", () => { + expect(isHexString("0x")).toBe(true) + }) + + test("returns false for an empty string with no prefix", () => { + expect(isHexString("")).toBe(false) + }) + test("returns false for invalid hex strings", () => { expect(isHexString("0x1234567890abcdefg")).toBe(false) expect(isHexString("not hex")).toBe(false) diff --git a/packages/keys/src/encoding/hex.ts b/packages/keys/src/encoding/hex.ts index aa4de17b..4a084790 100644 --- a/packages/keys/src/encoding/hex.ts +++ b/packages/keys/src/encoding/hex.ts @@ -47,6 +47,14 @@ export function isHexString(value: unknown): value is string { return false } - const hexWithoutPrefix = value.startsWith("0x") ? value.slice(2) : value + const hasPrefix = value.startsWith("0x") + const hexWithoutPrefix = hasPrefix ? value.slice(2) : value + + // A bare "0x" prefix has an empty body and is a valid (zero-length) hex + // string, as documented above. An empty string with no prefix is not. + if (hexWithoutPrefix.length === 0) { + return hasPrefix + } + return /^[0-9A-Fa-f]+$/.test(hexWithoutPrefix) }