Skip to content

Commit 57868f6

Browse files
Copilotsawka
andcommitted
Add preview-only configui widgets
Co-authored-by: sawka <2722291+sawka@users.noreply.github.com>
1 parent 2265078 commit 57868f6

4 files changed

Lines changed: 762 additions & 0 deletions

File tree

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright 2026, Command Line Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { describe, expect, it } from "vitest";
5+
import {
6+
normalizeConfigStringInput,
7+
validateConfigNumberInput,
8+
validateConfigStringInput,
9+
} from "./configvalidation";
10+
11+
describe("configvalidation", () => {
12+
it("normalizes and validates config strings", () => {
13+
expect(normalizeConfigStringInput(" wave ")).toBe("wave");
14+
expect(validateConfigStringInput(" ", { required: true })).toBe("Required");
15+
expect(validateConfigStringInput("missing-placeholder", { validate: (value) => (!value.includes("{query}") ? "Must include {query}" : undefined) })).toBe(
16+
"Must include {query}"
17+
);
18+
expect(validateConfigStringInput("https://example.com/?q={query}", { validate: (value) => (!value.includes("{query}") ? "Must include {query}" : undefined) })).toBeUndefined();
19+
});
20+
21+
it("validates config numbers with integer and range constraints", () => {
22+
expect(validateConfigNumberInput("", { min: 1, max: 10 })).toEqual({ value: undefined });
23+
expect(validateConfigNumberInput("12", { min: 1, max: 10 })).toEqual({
24+
value: undefined,
25+
error: "Must be at most 10",
26+
});
27+
expect(validateConfigNumberInput("8.5", { integer: true })).toEqual({
28+
value: undefined,
29+
error: "Must be a whole number",
30+
});
31+
expect(validateConfigNumberInput("256", { min: 128, max: 10000, integer: true })).toEqual({
32+
value: 256,
33+
});
34+
});
35+
});
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright 2026, Command Line Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
export type ConfigStringValidationOptions = {
5+
required?: boolean;
6+
trim?: boolean;
7+
maxLength?: number;
8+
pattern?: RegExp;
9+
validate?: (value: string) => string | undefined;
10+
};
11+
12+
export type ConfigNumberValidationOptions = {
13+
min?: number;
14+
max?: number;
15+
integer?: boolean;
16+
};
17+
18+
export function normalizeConfigStringInput(value: string, options?: ConfigStringValidationOptions): string {
19+
if (options?.trim === false) {
20+
return value;
21+
}
22+
return value.trim();
23+
}
24+
25+
export function validateConfigStringInput(value: string, options?: ConfigStringValidationOptions): string | undefined {
26+
const normalizedValue = normalizeConfigStringInput(value, options);
27+
28+
if (options?.required && normalizedValue.length === 0) {
29+
return "Required";
30+
}
31+
if (normalizedValue.length === 0) {
32+
return;
33+
}
34+
if (options?.maxLength != null && normalizedValue.length > options.maxLength) {
35+
return `Must be ${options.maxLength} characters or less`;
36+
}
37+
if (options?.pattern != null && !options.pattern.test(normalizedValue)) {
38+
return "Invalid format";
39+
}
40+
return options?.validate?.(normalizedValue);
41+
}
42+
43+
export function validateConfigNumberInput(
44+
value: string,
45+
options?: ConfigNumberValidationOptions
46+
): { value: number | undefined; error?: string } {
47+
const trimmedValue = value.trim();
48+
if (trimmedValue.length === 0) {
49+
return { value: undefined };
50+
}
51+
52+
const parsedValue = Number(trimmedValue);
53+
if (!Number.isFinite(parsedValue)) {
54+
return { value: undefined, error: "Must be a number" };
55+
}
56+
if (options?.integer && !Number.isInteger(parsedValue)) {
57+
return { value: undefined, error: "Must be a whole number" };
58+
}
59+
if (options?.min != null && parsedValue < options.min) {
60+
return { value: undefined, error: `Must be at least ${options.min}` };
61+
}
62+
if (options?.max != null && parsedValue > options.max) {
63+
return { value: undefined, error: `Must be at most ${options.max}` };
64+
}
65+
66+
return { value: parsedValue };
67+
}

0 commit comments

Comments
 (0)