Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@
"prepack": "npm run build",
"prepublishOnly": "npm run check && npm run typecheck && npm run pack:check && npm run types:check",
"types:check": "attw --pack . --profile esm-only --no-summary",
"typecheck": "tsc --noEmit"
"typecheck": "tsc --noEmit",
"test": "npm run build && node --test tests/*.test.mjs"

@augmentcode augmentcode Bot Jun 17, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The node --test tests/*.test.mjs argument relies on shell glob expansion, which can fail on Windows/npm environments where * isn’t expanded, causing tests not to run (or to error on a literal path).

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

},
"devDependencies": {
"@arethetypeswrong/cli": "^0.18.3",
Expand Down
26 changes: 23 additions & 3 deletions src/admin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -227,13 +227,33 @@ function renderChoiceCardLabel(choice: FieldsChoice) {
);
}

function readInputValue(event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) {
export function parseNumericInput(value: string, type: "number" | "integer") {
if (value.trim() === "") {
return undefined;
}

const numericValue = Number(value);
if (!Number.isFinite(numericValue)) {
return undefined;
}

if (type === "integer" && !Number.isInteger(numericValue)) {
return undefined;
}

return numericValue;
}

function readInputValue(
event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
type: FieldsSubField["type"],
) {
const target = event.currentTarget;
if (target instanceof HTMLInputElement && target.type === "checkbox") {
return target.checked;
}
if (target instanceof HTMLInputElement && target.type === "number") {
return target.value === "" ? undefined : Number(target.value);
return parseNumericInput(target.value, type === "integer" ? "integer" : "number");

@augmentcode augmentcode Bot Jun 17, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because parseNumericInput() returns undefined for non-finite/invalid strings, controlled <Input type="number"> edits that temporarily produce values like "-" (common while typing negative numbers) will be normalized to "" and can effectively prevent entering some valid numbers interactively.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

}
return target.value;
}
Expand All @@ -253,7 +273,7 @@ function renderSubField(
placeholder: field.placeholder,
value: typeof value === "string" || typeof value === "number" ? value : "",
onChange: (event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) =>
onChange(readInputValue(event)),
onChange(readInputValue(event, type)),
};

const selectChoices = choices(field.options);
Expand Down
27 changes: 27 additions & 0 deletions tests/numeric-input.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import assert from "node:assert/strict";
import { test } from "node:test";
import { parseNumericInput } from "../dist/admin.mjs";

test("numeric input emits undefined for empty values", () => {
assert.equal(parseNumericInput("", "number"), undefined);
assert.equal(parseNumericInput(" ", "integer"), undefined);
});

test("numeric input emits undefined for invalid values instead of NaN", () => {
assert.equal(parseNumericInput("not-a-number", "number"), undefined);
assert.equal(parseNumericInput("123abc", "integer"), undefined);
assert.equal(parseNumericInput("Infinity", "number"), undefined);
});

test("number input accepts integer, decimal, and boundary finite values", () => {
assert.equal(parseNumericInput("42", "number"), 42);
assert.equal(parseNumericInput("3.14", "number"), 3.14);
assert.equal(parseNumericInput(String(Number.MAX_SAFE_INTEGER), "number"), Number.MAX_SAFE_INTEGER);
assert.equal(parseNumericInput(String(Number.MIN_SAFE_INTEGER), "number"), Number.MIN_SAFE_INTEGER);
});

test("integer input accepts integers and rejects decimals", () => {
assert.equal(parseNumericInput("42", "integer"), 42);
assert.equal(parseNumericInput("-9007199254740991", "integer"), Number.MIN_SAFE_INTEGER);
assert.equal(parseNumericInput("3.14", "integer"), undefined);
});