refactor: 移除顶层 defaults 配置段,简化为 target 显式字段 > 代码内置默认值
- 移除 DefaultsConfig 类型、ProbeConfig.defaults 字段 - 移除 CheckerSchemas.defaults、ResolveContext.defaults、CheckerValidationInput.defaults - 更新所有 checker schema/resolve/validate 删除 defaults 合并逻辑 - 更新 config-loader 不再读取传递 defaults - 更新测试、README、DEVELOPMENT、probes.example.yaml - 重新生成 probe-config.schema.json(不含 defaults) - 同步 delta specs 到主规范 - 归档 openspec change
This commit is contained in:
@@ -209,12 +209,11 @@ export class CommandChecker implements CheckerDefinition<ResolvedCommandTarget>
|
||||
|
||||
resolve(target: RawTargetConfig, context: ResolveContext): ResolvedCommandTarget {
|
||||
const t = target as RawTargetConfig & { cmd: CommandTargetConfig; type: "cmd" };
|
||||
const cmdDefaults = context.defaults["cmd"] as undefined | { cwd?: string; maxOutputBytes?: string };
|
||||
|
||||
const cwd = t.cmd.cwd ?? cmdDefaults?.cwd ?? ".";
|
||||
const cwd = t.cmd.cwd ?? ".";
|
||||
const resolvedCwd = resolve(context.configDir, cwd);
|
||||
|
||||
const maxOutputBytes = parseSize(t.cmd.maxOutputBytes ?? cmdDefaults?.maxOutputBytes ?? "100MB");
|
||||
const maxOutputBytes = parseSize(t.cmd.maxOutputBytes ?? "100MB");
|
||||
|
||||
const env = { ...process.env, ...(t.cmd.env ?? {}) } as Record<string, string>;
|
||||
|
||||
|
||||
@@ -20,13 +20,6 @@ export const commandCheckerSchemas: CheckerSchemas = {
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
defaults: Type.Object(
|
||||
{
|
||||
cwd: Type.Optional(Type.String()),
|
||||
maxOutputBytes: Type.Optional(sizeSchema),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
expect: Type.Object(
|
||||
{
|
||||
durationMs: Type.Optional(createRawValueExpectationSchema()),
|
||||
|
||||
@@ -6,11 +6,6 @@ import type {
|
||||
} from "../../expect/types";
|
||||
import type { ResolvedTargetBase } from "../../types";
|
||||
|
||||
export interface CommandDefaultsConfig {
|
||||
cwd?: string;
|
||||
maxOutputBytes?: string;
|
||||
}
|
||||
|
||||
export interface CommandTargetConfig {
|
||||
args?: string[];
|
||||
cwd?: string;
|
||||
|
||||
@@ -9,12 +9,6 @@ import { parseSize } from "../../utils";
|
||||
|
||||
export function validateCommandConfig(input: CheckerValidationInput): ConfigValidationIssue[] {
|
||||
const issues: ConfigValidationIssue[] = [];
|
||||
const defaults =
|
||||
isPlainRecord(input.defaults) && isPlainRecord(input.defaults["cmd"]) ? input.defaults["cmd"] : undefined;
|
||||
|
||||
if (isSizeInput(defaults?.["maxOutputBytes"])) {
|
||||
issues.push(...validateSizeValue(defaults["maxOutputBytes"], "defaults.cmd.maxOutputBytes"));
|
||||
}
|
||||
|
||||
for (let i = 0; i < input.targets.length; i++) {
|
||||
const target = input.targets[i] as unknown;
|
||||
|
||||
@@ -20,7 +20,6 @@ export const dbCheckerSchemas: CheckerSchemas = {
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
defaults: Type.Object({}, { additionalProperties: false }),
|
||||
expect: Type.Object(
|
||||
{
|
||||
durationMs: Type.Optional(createRawValueExpectationSchema()),
|
||||
|
||||
@@ -175,12 +175,9 @@ export class HttpChecker implements CheckerDefinition<ResolvedHttpTarget> {
|
||||
|
||||
resolve(target: RawTargetConfig, context: ResolveContext): ResolvedHttpTarget {
|
||||
const t = target as RawTargetConfig & { http: HttpTargetConfig; type: "http" };
|
||||
const httpDefaults = context.defaults["http"] as
|
||||
| undefined
|
||||
| { headers?: Record<string, string>; maxBodyBytes?: string };
|
||||
|
||||
const method = t.http.method ?? "GET";
|
||||
const maxBodyBytes = parseSize(t.http.maxBodyBytes ?? httpDefaults?.maxBodyBytes ?? "100MB");
|
||||
const maxBodyBytes = parseSize(t.http.maxBodyBytes ?? "100MB");
|
||||
|
||||
const rawExpect = target.expect as RawHttpExpectConfig | undefined;
|
||||
const resolvedExpect: ResolvedHttpExpectConfig = rawExpect
|
||||
@@ -198,7 +195,7 @@ export class HttpChecker implements CheckerDefinition<ResolvedHttpTarget> {
|
||||
group: target.group ?? "default",
|
||||
http: {
|
||||
body: t.http.body,
|
||||
headers: { ...(httpDefaults?.headers ?? {}), ...(t.http.headers ?? {}) },
|
||||
headers: { ...(t.http.headers ?? {}) },
|
||||
ignoreSSL: t.http.ignoreSSL ?? false,
|
||||
maxBodyBytes,
|
||||
maxRedirects: t.http.maxRedirects ?? 0,
|
||||
|
||||
@@ -25,13 +25,6 @@ export const httpCheckerSchemas: CheckerSchemas = {
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
defaults: Type.Object(
|
||||
{
|
||||
headers: Type.Optional(stringMapSchema),
|
||||
maxBodyBytes: Type.Optional(sizeSchema),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
expect: Type.Object(
|
||||
{
|
||||
body: Type.Optional(createRawContentExpectationsSchema()),
|
||||
|
||||
@@ -8,12 +8,6 @@ import type {
|
||||
} from "../../expect/types";
|
||||
import type { ResolvedTargetBase } from "../../types";
|
||||
|
||||
export interface HttpDefaultsConfig {
|
||||
headers?: Record<string, string>;
|
||||
maxBodyBytes?: string;
|
||||
method?: string;
|
||||
}
|
||||
|
||||
export interface HttpTargetConfig {
|
||||
body?: string;
|
||||
headers?: Record<string, string>;
|
||||
|
||||
@@ -16,12 +16,6 @@ const ALLOWED_PROTOCOLS = new Set(["http:", "https:"]);
|
||||
|
||||
export function validateHttpConfig(input: CheckerValidationInput): ConfigValidationIssue[] {
|
||||
const issues: ConfigValidationIssue[] = [];
|
||||
const defaults =
|
||||
isPlainRecord(input.defaults) && isPlainRecord(input.defaults["http"]) ? input.defaults["http"] : undefined;
|
||||
|
||||
if (isSizeInput(defaults?.["maxBodyBytes"])) {
|
||||
issues.push(...validateSizeValue(defaults["maxBodyBytes"], "defaults.http.maxBodyBytes"));
|
||||
}
|
||||
|
||||
for (let i = 0; i < input.targets.length; i++) {
|
||||
const target = input.targets[i] as unknown;
|
||||
|
||||
@@ -13,7 +13,6 @@ export const icmpCheckerSchemas: CheckerSchemas = {
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
defaults: Type.Object({}, { additionalProperties: false }),
|
||||
expect: Type.Object(
|
||||
{
|
||||
alive: Type.Optional(Type.Boolean()),
|
||||
|
||||
@@ -9,19 +9,6 @@ import { issue, joinPath } from "../../schema/issues";
|
||||
export function validatePingConfig(input: CheckerValidationInput): ConfigValidationIssue[] {
|
||||
const issues: ConfigValidationIssue[] = [];
|
||||
|
||||
const defaults = input.defaults["icmp"];
|
||||
if (defaults !== undefined && defaults !== null) {
|
||||
const targetName = "defaults.icmp";
|
||||
if (!isPlainRecord(defaults)) {
|
||||
issues.push(issue("invalid-type", "defaults.icmp", "必须为对象", targetName));
|
||||
} else {
|
||||
const icmpDefaults = defaults;
|
||||
for (const key of Object.keys(icmpDefaults)) {
|
||||
issues.push(issue("unknown-field", joinPath("defaults.icmp", key), "是未知字段", targetName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < input.targets.length; i++) {
|
||||
const target = input.targets[i] as unknown;
|
||||
if (!isPlainRecord(target)) continue;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import type { JSONObject } from "@ai-sdk/provider";
|
||||
|
||||
import { APICallError, generateText, streamText } from "ai";
|
||||
import { isError } from "es-toolkit";
|
||||
|
||||
@@ -133,43 +131,27 @@ export class LlmChecker implements CheckerDefinition<ResolvedLlmTarget> {
|
||||
|
||||
resolve(target: RawTargetConfig, context: ResolveContext): ResolvedLlmTarget {
|
||||
const t = target as RawTargetConfig & { llm: LlmTargetConfig; type: "llm" };
|
||||
const llmDefaults = context.defaults["llm"] as
|
||||
| undefined
|
||||
| {
|
||||
headers?: Record<string, string>;
|
||||
ignoreSSL?: boolean;
|
||||
mode?: string;
|
||||
options?: Record<string, unknown>;
|
||||
providerOptions?: Record<string, Record<string, unknown>>;
|
||||
};
|
||||
|
||||
const resolvedConfig = {
|
||||
authToken: t.llm.authToken,
|
||||
headers: { ...(llmDefaults?.headers ?? {}), ...(t.llm.headers ?? {}) },
|
||||
ignoreSSL: t.llm.ignoreSSL ?? llmDefaults?.ignoreSSL ?? false,
|
||||
headers: { ...(t.llm.headers ?? {}) },
|
||||
ignoreSSL: t.llm.ignoreSSL ?? false,
|
||||
key: t.llm.key ?? "",
|
||||
mode: (t.llm.mode ?? llmDefaults?.mode ?? "http") as "http" | "stream",
|
||||
mode: t.llm.mode ?? "http",
|
||||
model: t.llm.model,
|
||||
options: {
|
||||
frequencyPenalty:
|
||||
t.llm.options?.frequencyPenalty ?? (llmDefaults?.options?.["frequencyPenalty"] as number | undefined),
|
||||
maxOutputTokens:
|
||||
t.llm.options?.maxOutputTokens ?? (llmDefaults?.options?.["maxOutputTokens"] as number | undefined) ?? 16,
|
||||
presencePenalty:
|
||||
t.llm.options?.presencePenalty ?? (llmDefaults?.options?.["presencePenalty"] as number | undefined),
|
||||
seed: t.llm.options?.seed ?? (llmDefaults?.options?.["seed"] as number | undefined),
|
||||
stopSequences:
|
||||
t.llm.options?.stopSequences ?? (llmDefaults?.options?.["stopSequences"] as string[] | undefined),
|
||||
temperature: t.llm.options?.temperature ?? (llmDefaults?.options?.["temperature"] as number | undefined) ?? 0,
|
||||
topK: t.llm.options?.topK ?? (llmDefaults?.options?.["topK"] as number | undefined),
|
||||
topP: t.llm.options?.topP ?? (llmDefaults?.options?.["topP"] as number | undefined),
|
||||
frequencyPenalty: t.llm.options?.frequencyPenalty,
|
||||
maxOutputTokens: t.llm.options?.maxOutputTokens ?? 16,
|
||||
presencePenalty: t.llm.options?.presencePenalty,
|
||||
seed: t.llm.options?.seed,
|
||||
stopSequences: t.llm.options?.stopSequences,
|
||||
temperature: t.llm.options?.temperature ?? 0,
|
||||
topK: t.llm.options?.topK,
|
||||
topP: t.llm.options?.topP,
|
||||
},
|
||||
prompt: t.llm.prompt,
|
||||
provider: t.llm.provider,
|
||||
providerOptions: {
|
||||
...((llmDefaults?.providerOptions ?? {}) as Record<string, JSONObject>),
|
||||
...(t.llm.providerOptions ?? {}),
|
||||
},
|
||||
providerOptions: { ...(t.llm.providerOptions ?? {}) },
|
||||
url: t.llm.url,
|
||||
};
|
||||
|
||||
|
||||
@@ -43,16 +43,6 @@ export const llmCheckerSchemas: CheckerSchemas = {
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
defaults: Type.Object(
|
||||
{
|
||||
headers: Type.Optional(stringMapSchema),
|
||||
ignoreSSL: Type.Optional(Type.Boolean()),
|
||||
mode: Type.Optional(Type.Union([Type.Literal("http"), Type.Literal("stream")])),
|
||||
options: Type.Optional(createLlmOptionsSchema()),
|
||||
providerOptions: Type.Optional(Type.Record(Type.String(), Type.Object({}, { additionalProperties: true }))),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
expect: Type.Object(
|
||||
{
|
||||
durationMs: Type.Optional(createRawValueExpectationSchema()),
|
||||
|
||||
@@ -23,14 +23,6 @@ export interface LlmCheckObservation {
|
||||
warnings: string[];
|
||||
}
|
||||
|
||||
export interface LlmDefaultsConfig {
|
||||
headers?: Record<string, string>;
|
||||
ignoreSSL?: boolean;
|
||||
mode?: LlmMode;
|
||||
options?: LlmOptions;
|
||||
providerOptions?: Record<string, JSONObject>;
|
||||
}
|
||||
|
||||
export interface LlmHttpMetadata {
|
||||
headers: Record<string, string>;
|
||||
status: number;
|
||||
|
||||
@@ -17,12 +17,6 @@ const ALLOWED_PROVIDERS = new Set(["anthropic", "openai", "openai-responses"]);
|
||||
|
||||
export function validateLlmConfig(input: CheckerValidationInput): ConfigValidationIssue[] {
|
||||
const issues: ConfigValidationIssue[] = [];
|
||||
const defaults =
|
||||
isPlainRecord(input.defaults) && isPlainRecord(input.defaults["llm"]) ? input.defaults["llm"] : undefined;
|
||||
|
||||
if (defaults) {
|
||||
issues.push(...validateLlmDefaults(defaults, "defaults.llm"));
|
||||
}
|
||||
|
||||
for (let i = 0; i < input.targets.length; i++) {
|
||||
const target = input.targets[i] as unknown;
|
||||
@@ -39,28 +33,6 @@ function getTargetName(target: Record<string, unknown>): string | undefined {
|
||||
return isString(target["id"]) ? target["id"] : undefined;
|
||||
}
|
||||
|
||||
function validateLlmDefaults(defaults: Record<string, unknown>, path: string): ConfigValidationIssue[] {
|
||||
const issues: ConfigValidationIssue[] = [];
|
||||
|
||||
if (defaults["mode"] !== undefined && !ALLOWED_MODES.has(defaults["mode"] as string)) {
|
||||
issues.push(issue("invalid-type", joinPath(path, "mode"), "必须为 http 或 stream"));
|
||||
}
|
||||
if (defaults["ignoreSSL"] !== undefined && !isBoolean(defaults["ignoreSSL"])) {
|
||||
issues.push(issue("invalid-type", joinPath(path, "ignoreSSL"), "必须为布尔值"));
|
||||
}
|
||||
if (defaults["headers"] !== undefined) {
|
||||
issues.push(...validateStringMap(defaults["headers"], joinPath(path, "headers")));
|
||||
}
|
||||
if (defaults["options"] !== undefined) {
|
||||
issues.push(...validateLlmOptions(defaults["options"], joinPath(path, "options")));
|
||||
}
|
||||
if (defaults["providerOptions"] !== undefined) {
|
||||
issues.push(...validateProviderOptions(defaults["providerOptions"], joinPath(path, "providerOptions")));
|
||||
}
|
||||
|
||||
return issues;
|
||||
}
|
||||
|
||||
function validateLlmExpect(
|
||||
target: Record<string, unknown>,
|
||||
path: string,
|
||||
|
||||
@@ -206,12 +206,9 @@ export class TcpChecker implements CheckerDefinition<ResolvedTcpTarget> {
|
||||
|
||||
resolve(target: RawTargetConfig, context: ResolveContext): ResolvedTcpTarget {
|
||||
const t = target as RawTargetConfig & { tcp: TcpTargetConfig; type: "tcp" };
|
||||
const tcpDefaults = context.defaults["tcp"] as
|
||||
| undefined
|
||||
| { bannerReadTimeout?: number; maxBannerBytes?: number | string };
|
||||
|
||||
const maxBannerBytes = parseSize(t.tcp.maxBannerBytes ?? tcpDefaults?.maxBannerBytes ?? DEFAULT_MAX_BANNER_BYTES);
|
||||
const bannerReadTimeout = t.tcp.bannerReadTimeout ?? tcpDefaults?.bannerReadTimeout ?? DEFAULT_BANNER_READ_TIMEOUT;
|
||||
const maxBannerBytes = parseSize(t.tcp.maxBannerBytes ?? DEFAULT_MAX_BANNER_BYTES);
|
||||
const bannerReadTimeout = t.tcp.bannerReadTimeout ?? DEFAULT_BANNER_READ_TIMEOUT;
|
||||
|
||||
const rawExpect = target.expect as RawTcpExpectConfig | undefined;
|
||||
const resolvedExpect: ResolvedTcpExpectConfig = rawExpect
|
||||
|
||||
@@ -19,13 +19,6 @@ export const tcpCheckerSchemas: CheckerSchemas = {
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
defaults: Type.Object(
|
||||
{
|
||||
bannerReadTimeout: Type.Optional(Type.Number({ minimum: 0 })),
|
||||
maxBannerBytes: Type.Optional(sizeSchema),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
expect: Type.Object(
|
||||
{
|
||||
banner: Type.Optional(createRawContentExpectationsSchema()),
|
||||
|
||||
@@ -37,11 +37,6 @@ export interface ResolvedTcpTarget extends ResolvedTargetBase {
|
||||
type: "tcp";
|
||||
}
|
||||
|
||||
export interface TcpDefaultsConfig {
|
||||
bannerReadTimeout?: number;
|
||||
maxBannerBytes?: number | string;
|
||||
}
|
||||
|
||||
export interface TcpTargetConfig {
|
||||
bannerReadTimeout?: number;
|
||||
host: string;
|
||||
|
||||
@@ -9,8 +9,6 @@ import { issue, joinPath } from "../../schema/issues";
|
||||
export function validateTcpConfig(input: CheckerValidationInput): ConfigValidationIssue[] {
|
||||
const issues: ConfigValidationIssue[] = [];
|
||||
|
||||
issues.push(...validateTcpDefaults(input));
|
||||
|
||||
for (let i = 0; i < input.targets.length; i++) {
|
||||
const target = input.targets[i] as unknown;
|
||||
if (!isPlainRecord(target)) continue;
|
||||
@@ -30,40 +28,6 @@ function isNonNegativeFiniteNumber(value: unknown): boolean {
|
||||
return isNumber(value) && Number.isFinite(value) && value >= 0;
|
||||
}
|
||||
|
||||
function validateTcpDefaults(input: CheckerValidationInput): ConfigValidationIssue[] {
|
||||
const issues: ConfigValidationIssue[] = [];
|
||||
const defaults = input.defaults["tcp"];
|
||||
if (defaults === undefined || defaults === null || !isPlainRecord(defaults)) return issues;
|
||||
|
||||
const targetName = "defaults.tcp";
|
||||
|
||||
if (defaults["bannerReadTimeout"] !== undefined && !isNonNegativeFiniteNumber(defaults["bannerReadTimeout"])) {
|
||||
issues.push(issue("invalid-type", "defaults.tcp.bannerReadTimeout", "必须为非负有限数字", targetName));
|
||||
}
|
||||
|
||||
if (defaults["maxBannerBytes"] !== undefined) {
|
||||
if (
|
||||
!isString(defaults["maxBannerBytes"]) &&
|
||||
!(
|
||||
isNumber(defaults["maxBannerBytes"]) &&
|
||||
Number.isFinite(defaults["maxBannerBytes"]) &&
|
||||
defaults["maxBannerBytes"] >= 0
|
||||
)
|
||||
) {
|
||||
issues.push(issue("invalid-value", "defaults.tcp.maxBannerBytes", "必须为合法 size 值", targetName));
|
||||
}
|
||||
}
|
||||
|
||||
const allowedKeys = new Set(["bannerReadTimeout", "maxBannerBytes"]);
|
||||
for (const key of Object.keys(defaults)) {
|
||||
if (!allowedKeys.has(key)) {
|
||||
issues.push(issue("unknown-field", joinPath("defaults.tcp", key), "是未知字段", targetName));
|
||||
}
|
||||
}
|
||||
|
||||
return issues;
|
||||
}
|
||||
|
||||
function validateTcpExpect(
|
||||
target: Record<string, unknown>,
|
||||
path: string,
|
||||
@@ -159,5 +123,3 @@ function validateTcpTarget(target: Record<string, unknown>, path: string): Confi
|
||||
|
||||
return issues;
|
||||
}
|
||||
|
||||
export { validateTcpDefaults };
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { TSchema } from "@sinclair/typebox";
|
||||
|
||||
import type { ConfigValidationIssue } from "../schema/issues";
|
||||
import type { CheckResult, DefaultsConfig, RawTargetConfig, ResolvedTargetBase } from "../types";
|
||||
import type { CheckResult, RawTargetConfig, ResolvedTargetBase } from "../types";
|
||||
|
||||
export type Checker<TResolved extends ResolvedTargetBase = ResolvedTargetBase> = CheckerDefinition<TResolved>;
|
||||
|
||||
@@ -22,18 +22,15 @@ export interface CheckerDefinition<TResolved extends ResolvedTargetBase = Resolv
|
||||
|
||||
export interface CheckerSchemas {
|
||||
config: TSchema;
|
||||
defaults: TSchema;
|
||||
expect: TSchema;
|
||||
}
|
||||
|
||||
export interface CheckerValidationInput {
|
||||
defaults: DefaultsConfig;
|
||||
targets: RawTargetConfig[];
|
||||
}
|
||||
|
||||
export interface ResolveContext {
|
||||
configDir: string;
|
||||
defaultIntervalMs: number;
|
||||
defaults: DefaultsConfig;
|
||||
defaultTimeoutMs: number;
|
||||
}
|
||||
|
||||
@@ -2,13 +2,7 @@ import { isError } from "es-toolkit";
|
||||
|
||||
import type { CheckResult, RawTargetConfig } from "../../types";
|
||||
import type { CheckerContext, CheckerDefinition, CheckerValidationInput, ResolveContext } from "../types";
|
||||
import type {
|
||||
RawUdpExpectConfig,
|
||||
ResolvedUdpExpectConfig,
|
||||
ResolvedUdpTarget,
|
||||
UdpDefaultsConfig,
|
||||
UdpTargetConfig,
|
||||
} from "./types";
|
||||
import type { RawUdpExpectConfig, ResolvedUdpExpectConfig, ResolvedUdpTarget, UdpTargetConfig } from "./types";
|
||||
|
||||
import { resolveContentExpectations } from "../../expect/content";
|
||||
import { errorFailure } from "../../expect/failure";
|
||||
@@ -304,13 +298,10 @@ export class UdpChecker implements CheckerDefinition<ResolvedUdpTarget> {
|
||||
|
||||
resolve(target: RawTargetConfig, context: ResolveContext): ResolvedUdpTarget {
|
||||
const t = target as RawTargetConfig & { type: "udp"; udp: UdpTargetConfig };
|
||||
const udpDefaults = context.defaults["udp"] as UdpDefaultsConfig | undefined;
|
||||
|
||||
const encoding = t.udp.encoding ?? udpDefaults?.encoding ?? "text";
|
||||
const responseEncoding = t.udp.responseEncoding ?? udpDefaults?.responseEncoding ?? "text";
|
||||
const maxResponseBytes = parseSize(
|
||||
t.udp.maxResponseBytes ?? udpDefaults?.maxResponseBytes ?? DEFAULT_MAX_RESPONSE_BYTES,
|
||||
);
|
||||
const encoding = t.udp.encoding ?? "text";
|
||||
const responseEncoding = t.udp.responseEncoding ?? "text";
|
||||
const maxResponseBytes = parseSize(t.udp.maxResponseBytes ?? DEFAULT_MAX_RESPONSE_BYTES);
|
||||
|
||||
const rawExpect = target.expect as RawUdpExpectConfig | undefined;
|
||||
const resolvedExpect: ResolvedUdpExpectConfig = rawExpect
|
||||
|
||||
@@ -20,14 +20,6 @@ export const udpCheckerSchemas: CheckerSchemas = {
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
defaults: Type.Object(
|
||||
{
|
||||
encoding: Type.Optional(Type.Union([Type.Literal("text"), Type.Literal("hex"), Type.Literal("base64")])),
|
||||
maxResponseBytes: Type.Optional(sizeSchema),
|
||||
responseEncoding: Type.Optional(Type.Union([Type.Literal("text"), Type.Literal("hex"), Type.Literal("base64")])),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
expect: Type.Object(
|
||||
{
|
||||
durationMs: Type.Optional(createRawValueExpectationSchema()),
|
||||
|
||||
@@ -44,12 +44,6 @@ export interface ResolvedUdpTarget extends ResolvedTargetBase {
|
||||
udp: ResolvedUdpConfig;
|
||||
}
|
||||
|
||||
export interface UdpDefaultsConfig {
|
||||
encoding?: UdpEncoding;
|
||||
maxResponseBytes?: number | string;
|
||||
responseEncoding?: UdpEncoding;
|
||||
}
|
||||
|
||||
export type UdpEncoding = "base64" | "hex" | "text";
|
||||
|
||||
export interface UdpTargetConfig {
|
||||
|
||||
@@ -11,8 +11,6 @@ const VALID_ENCODINGS = new Set(["base64", "hex", "text"]);
|
||||
export function validateUdpConfig(input: CheckerValidationInput): ConfigValidationIssue[] {
|
||||
const issues: ConfigValidationIssue[] = [];
|
||||
|
||||
issues.push(...validateUdpDefaults(input));
|
||||
|
||||
for (let i = 0; i < input.targets.length; i++) {
|
||||
const target = input.targets[i] as unknown;
|
||||
if (!isPlainRecord(target)) continue;
|
||||
@@ -44,29 +42,6 @@ function validateSize(value: unknown, path: string, targetName: string | undefin
|
||||
return [];
|
||||
}
|
||||
|
||||
function validateUdpDefaults(input: CheckerValidationInput): ConfigValidationIssue[] {
|
||||
const issues: ConfigValidationIssue[] = [];
|
||||
const defaults = input.defaults["udp"];
|
||||
if (defaults === undefined || defaults === null || !isPlainRecord(defaults)) return issues;
|
||||
|
||||
const targetName = "defaults.udp";
|
||||
|
||||
issues.push(...validateEncoding(defaults["encoding"], joinPath("defaults.udp", "encoding"), targetName));
|
||||
issues.push(
|
||||
...validateEncoding(defaults["responseEncoding"], joinPath("defaults.udp", "responseEncoding"), targetName),
|
||||
);
|
||||
issues.push(...validateSize(defaults["maxResponseBytes"], joinPath("defaults.udp", "maxResponseBytes"), targetName));
|
||||
|
||||
const allowedKeys = new Set(["encoding", "maxResponseBytes", "responseEncoding"]);
|
||||
for (const key of Object.keys(defaults)) {
|
||||
if (!allowedKeys.has(key)) {
|
||||
issues.push(issue("unknown-field", joinPath("defaults.udp", key), "是未知字段", targetName));
|
||||
}
|
||||
}
|
||||
|
||||
return issues;
|
||||
}
|
||||
|
||||
function validateUdpExpect(target: Record<string, unknown>, path: string): ConfigValidationIssue[] {
|
||||
const targetName = getTargetName(target);
|
||||
const expect = target["expect"];
|
||||
|
||||
Reference in New Issue
Block a user