feat: 重构配置生命周期为 Authoring/Normalized/Resolved 三层
将变量替换和 expect 简写展开统一放入 Normalized 阶段, 运行时 AJV 使用 Normalized schema,导出 schema 面向 Authoring Config。 主要变更: - 新增 normalizer.ts 实现 normalizeAuthoringConfig() - 拆分 Authoring/Normalized 双 schema,checker 接口支持 authoring/normalized 片段 - config-loader 流程:normalize → Normalized AJV → semantic → resolve - validator 兼容层自动分派 raw/normalized expect 形态 - 删除 rawExpect,store.expect 列写入 null - Authoring schema 对 integer/boolean/enum 字段接受变量引用 - 修复 DB/HTTP validate 入口守卫和 LLM options integer 变量引用 - 优化 compact() 避免 undefined 覆盖隐患 - 移除 content.ts 恒为 true 的前置条件 - 同步 5 个主规范并归档 change
This commit is contained in:
@@ -3,16 +3,11 @@ import { resolve } from "node:path";
|
||||
|
||||
import type { CheckResult, RawTargetConfig } from "../../types";
|
||||
import type { CheckerContext, CheckerDefinition, CheckerValidationInput, ResolveContext } from "../types";
|
||||
import type {
|
||||
CommandTargetConfig,
|
||||
RawCommandExpectConfig,
|
||||
ResolvedCommandExpectConfig,
|
||||
ResolvedCommandTarget,
|
||||
} from "./types";
|
||||
import type { CommandTargetConfig, ResolvedCommandExpectConfig, ResolvedCommandTarget } from "./types";
|
||||
|
||||
import { checkContentExpectations, resolveContentExpectations } from "../../expect/content";
|
||||
import { checkContentExpectations } from "../../expect/content";
|
||||
import { errorFailure } from "../../expect/failure";
|
||||
import { checkValueExpectation, resolveValueExpectation } from "../../expect/value";
|
||||
import { checkValueExpectation } from "../../expect/value";
|
||||
import { parseSize } from "../../utils";
|
||||
import { checkExitCode } from "./expect";
|
||||
import { commandCheckerSchemas } from "./schema";
|
||||
@@ -217,13 +212,11 @@ export class CommandChecker implements CheckerDefinition<ResolvedCommandTarget>
|
||||
|
||||
const env = { ...process.env, ...(t.cmd.env ?? {}) } as Record<string, string>;
|
||||
|
||||
const rawExpect = target.expect as RawCommandExpectConfig | undefined;
|
||||
const resolvedExpect: ResolvedCommandExpectConfig = rawExpect
|
||||
const expect = target.expect as ResolvedCommandExpectConfig | undefined;
|
||||
const resolvedExpect: ResolvedCommandExpectConfig = expect
|
||||
? {
|
||||
durationMs: resolveValueExpectation(rawExpect.durationMs),
|
||||
exitCode: rawExpect.exitCode ?? [0],
|
||||
stderr: resolveContentExpectations(rawExpect.stderr),
|
||||
stdout: resolveContentExpectations(rawExpect.stdout),
|
||||
...expect,
|
||||
exitCode: expect.exitCode ?? [0],
|
||||
}
|
||||
: { exitCode: [0] };
|
||||
|
||||
@@ -241,7 +234,6 @@ export class CommandChecker implements CheckerDefinition<ResolvedCommandTarget>
|
||||
id: t.id,
|
||||
intervalMs: context.defaultIntervalMs,
|
||||
name: t.name ?? null,
|
||||
rawExpect,
|
||||
timeoutMs: context.defaultTimeoutMs,
|
||||
type: "cmd",
|
||||
} satisfies ResolvedCommandTarget;
|
||||
|
||||
@@ -3,14 +3,27 @@ import { Type } from "@sinclair/typebox";
|
||||
import type { CheckerSchemas } from "../types";
|
||||
|
||||
import {
|
||||
createRawContentExpectationsSchema,
|
||||
createRawValueExpectationSchema,
|
||||
createAuthoringContentExpectationsSchema,
|
||||
createAuthoringValueExpectationSchema,
|
||||
createNormalizedContentExpectationsSchema,
|
||||
createNormalizedValueExpectationSchema,
|
||||
sizeSchema,
|
||||
stringMapSchema,
|
||||
} from "../../schema/fragments";
|
||||
|
||||
export const commandCheckerSchemas: CheckerSchemas = {
|
||||
config: Type.Object(
|
||||
authoring: {
|
||||
config: createCommandConfigSchema(),
|
||||
expect: createCommandExpectSchema("authoring"),
|
||||
},
|
||||
normalized: {
|
||||
config: createCommandConfigSchema(),
|
||||
expect: createCommandExpectSchema("normalized"),
|
||||
},
|
||||
};
|
||||
|
||||
function createCommandConfigSchema() {
|
||||
return Type.Object(
|
||||
{
|
||||
args: Type.Optional(Type.Array(Type.String())),
|
||||
cwd: Type.Optional(Type.String()),
|
||||
@@ -19,14 +32,23 @@ export const commandCheckerSchemas: CheckerSchemas = {
|
||||
maxOutputBytes: Type.Optional(sizeSchema),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
expect: Type.Object(
|
||||
);
|
||||
}
|
||||
|
||||
function createCommandExpectSchema(kind: "authoring" | "normalized") {
|
||||
return Type.Object(
|
||||
{
|
||||
durationMs: Type.Optional(createRawValueExpectationSchema()),
|
||||
durationMs: Type.Optional(
|
||||
kind === "authoring" ? createAuthoringValueExpectationSchema() : createNormalizedValueExpectationSchema(),
|
||||
),
|
||||
exitCode: Type.Optional(Type.Array(Type.Integer())),
|
||||
stderr: Type.Optional(createRawContentExpectationsSchema()),
|
||||
stdout: Type.Optional(createRawContentExpectationsSchema()),
|
||||
stderr: Type.Optional(
|
||||
kind === "authoring" ? createAuthoringContentExpectationsSchema() : createNormalizedContentExpectationsSchema(),
|
||||
),
|
||||
stdout: Type.Optional(
|
||||
kind === "authoring" ? createAuthoringContentExpectationsSchema() : createNormalizedContentExpectationsSchema(),
|
||||
),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
};
|
||||
);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,6 @@ export interface ResolvedCommandTarget extends ResolvedTargetBase {
|
||||
group: string;
|
||||
intervalMs: number;
|
||||
name: null | string;
|
||||
rawExpect?: RawCommandExpectConfig;
|
||||
timeoutMs: number;
|
||||
type: "cmd";
|
||||
}
|
||||
|
||||
@@ -3,12 +3,11 @@ import { isError } from "es-toolkit";
|
||||
|
||||
import type { CheckResult, RawTargetConfig } from "../../types";
|
||||
import type { CheckerContext, CheckerDefinition, CheckerValidationInput, ResolveContext } from "../types";
|
||||
import type { DbTargetConfig, RawDbExpectConfig, ResolvedDbExpectConfig, ResolvedDbTarget } from "./types";
|
||||
import type { DbTargetConfig, ResolvedDbExpectConfig, ResolvedDbTarget } from "./types";
|
||||
|
||||
import { checkContentExpectations, resolveContentExpectations } from "../../expect/content";
|
||||
import { checkContentExpectations } from "../../expect/content";
|
||||
import { errorFailure } from "../../expect/failure";
|
||||
import { resolveKeyedExpectations } from "../../expect/keyed";
|
||||
import { checkValueExpectation, resolveValueExpectation } from "../../expect/value";
|
||||
import { checkValueExpectation } from "../../expect/value";
|
||||
import { checkRowCount, checkRows } from "./expect";
|
||||
import { dbCheckerSchemas } from "./schema";
|
||||
import { validateDbConfig } from "./validate";
|
||||
@@ -227,15 +226,7 @@ export class DbChecker implements CheckerDefinition<ResolvedDbTarget> {
|
||||
resolve(target: RawTargetConfig, context: ResolveContext): ResolvedDbTarget {
|
||||
const t = target as RawTargetConfig & { db: DbTargetConfig; type: "db" };
|
||||
|
||||
const rawExpect = target.expect as RawDbExpectConfig | undefined;
|
||||
const resolvedExpect: ResolvedDbExpectConfig | undefined = rawExpect
|
||||
? {
|
||||
durationMs: resolveValueExpectation(rawExpect.durationMs),
|
||||
result: resolveContentExpectations(rawExpect.result),
|
||||
rowCount: resolveValueExpectation(rawExpect.rowCount),
|
||||
rows: rawExpect.rows?.map((r) => resolveKeyedExpectations(r)!),
|
||||
}
|
||||
: undefined;
|
||||
const resolvedExpect = target.expect as ResolvedDbExpectConfig | undefined;
|
||||
|
||||
return {
|
||||
db: {
|
||||
@@ -248,7 +239,6 @@ export class DbChecker implements CheckerDefinition<ResolvedDbTarget> {
|
||||
id: t.id,
|
||||
intervalMs: context.defaultIntervalMs,
|
||||
name: t.name ?? null,
|
||||
rawExpect,
|
||||
timeoutMs: context.defaultTimeoutMs,
|
||||
type: "db",
|
||||
} satisfies ResolvedDbTarget;
|
||||
|
||||
@@ -3,13 +3,27 @@ import { Type } from "@sinclair/typebox";
|
||||
import type { CheckerSchemas } from "../types";
|
||||
|
||||
import {
|
||||
createRawContentExpectationsSchema,
|
||||
createRawKeyedExpectationsSchema,
|
||||
createRawValueExpectationSchema,
|
||||
createAuthoringContentExpectationsSchema,
|
||||
createAuthoringKeyedExpectationsSchema,
|
||||
createAuthoringValueExpectationSchema,
|
||||
createNormalizedContentExpectationsSchema,
|
||||
createNormalizedKeyedExpectationsSchema,
|
||||
createNormalizedValueExpectationSchema,
|
||||
} from "../../schema/fragments";
|
||||
|
||||
export const dbCheckerSchemas: CheckerSchemas = {
|
||||
config: Type.Object(
|
||||
authoring: {
|
||||
config: createDbConfigSchema(),
|
||||
expect: createDbExpectSchema("authoring"),
|
||||
},
|
||||
normalized: {
|
||||
config: createDbConfigSchema(),
|
||||
expect: createDbExpectSchema("normalized"),
|
||||
},
|
||||
};
|
||||
|
||||
function createDbConfigSchema() {
|
||||
return Type.Object(
|
||||
{
|
||||
query: Type.Optional(
|
||||
Type.String({
|
||||
@@ -19,14 +33,27 @@ export const dbCheckerSchemas: CheckerSchemas = {
|
||||
url: Type.String({ minLength: 1 }),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
expect: Type.Object(
|
||||
);
|
||||
}
|
||||
|
||||
function createDbExpectSchema(kind: "authoring" | "normalized") {
|
||||
return Type.Object(
|
||||
{
|
||||
durationMs: Type.Optional(createRawValueExpectationSchema()),
|
||||
result: Type.Optional(createRawContentExpectationsSchema()),
|
||||
rowCount: Type.Optional(createRawValueExpectationSchema()),
|
||||
rows: Type.Optional(Type.Array(createRawKeyedExpectationsSchema())),
|
||||
durationMs: Type.Optional(
|
||||
kind === "authoring" ? createAuthoringValueExpectationSchema() : createNormalizedValueExpectationSchema(),
|
||||
),
|
||||
result: Type.Optional(
|
||||
kind === "authoring" ? createAuthoringContentExpectationsSchema() : createNormalizedContentExpectationsSchema(),
|
||||
),
|
||||
rowCount: Type.Optional(
|
||||
kind === "authoring" ? createAuthoringValueExpectationSchema() : createNormalizedValueExpectationSchema(),
|
||||
),
|
||||
rows: Type.Optional(
|
||||
Type.Array(
|
||||
kind === "authoring" ? createAuthoringKeyedExpectationsSchema() : createNormalizedKeyedExpectationsSchema(),
|
||||
),
|
||||
),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
};
|
||||
);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ export interface ResolvedDbTarget extends ResolvedTargetBase {
|
||||
group: string;
|
||||
intervalMs: number;
|
||||
name: null | string;
|
||||
rawExpect?: RawDbExpectConfig;
|
||||
timeoutMs: number;
|
||||
type: "db";
|
||||
}
|
||||
|
||||
@@ -27,12 +27,7 @@ export function validateDbConfig(input: CheckerValidationInput): ConfigValidatio
|
||||
function collectRowExpects(rows: unknown[], path: string, targetName?: string): ConfigValidationIssue[] {
|
||||
const issues: ConfigValidationIssue[] = [];
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const row = rows[i]!;
|
||||
if (!isPlainRecord(row)) {
|
||||
issues.push(issue("invalid-type", `${path}[${i}]`, "必须为对象", targetName));
|
||||
continue;
|
||||
}
|
||||
issues.push(...validateRawKeyedExpectations(row, `${path}[${i}]`, targetName));
|
||||
issues.push(...validateRawKeyedExpectations(rows[i], `${path}[${i}]`, targetName));
|
||||
}
|
||||
return issues;
|
||||
}
|
||||
|
||||
@@ -2,14 +2,13 @@ import { isError } from "es-toolkit";
|
||||
|
||||
import type { CheckResult, RawTargetConfig } from "../../types";
|
||||
import type { CheckerContext, CheckerDefinition, CheckerValidationInput, ResolveContext } from "../types";
|
||||
import type { HttpTargetConfig, RawHttpExpectConfig, ResolvedHttpExpectConfig, ResolvedHttpTarget } from "./types";
|
||||
import type { HttpTargetConfig, ResolvedHttpExpectConfig, ResolvedHttpTarget } from "./types";
|
||||
|
||||
import { checkContentExpectations, resolveContentExpectations } from "../../expect/content";
|
||||
import { checkContentExpectations } from "../../expect/content";
|
||||
import { errorFailure, mismatchFailure } from "../../expect/failure";
|
||||
import { checkHeaderExpectations } from "../../expect/headers";
|
||||
import { resolveKeyedExpectations } from "../../expect/keyed";
|
||||
import { checkStatusCode } from "../../expect/status";
|
||||
import { checkValueExpectation, displayValueExpectation, resolveValueExpectation } from "../../expect/value";
|
||||
import { checkValueExpectation, displayValueExpectation } from "../../expect/value";
|
||||
import { parseSize } from "../../utils";
|
||||
import { httpCheckerSchemas } from "./schema";
|
||||
import { validateHttpConfig } from "./validate";
|
||||
@@ -179,13 +178,11 @@ export class HttpChecker implements CheckerDefinition<ResolvedHttpTarget> {
|
||||
const method = t.http.method ?? "GET";
|
||||
const maxBodyBytes = parseSize(t.http.maxBodyBytes ?? "100MB");
|
||||
|
||||
const rawExpect = target.expect as RawHttpExpectConfig | undefined;
|
||||
const resolvedExpect: ResolvedHttpExpectConfig = rawExpect
|
||||
const expect = target.expect as ResolvedHttpExpectConfig | undefined;
|
||||
const resolvedExpect: ResolvedHttpExpectConfig = expect
|
||||
? {
|
||||
body: resolveContentExpectations(rawExpect.body),
|
||||
durationMs: resolveValueExpectation(rawExpect.durationMs),
|
||||
headers: resolveKeyedExpectations(rawExpect.headers),
|
||||
status: rawExpect.status ?? [200],
|
||||
...expect,
|
||||
status: expect.status ?? [200],
|
||||
}
|
||||
: { status: [200] };
|
||||
|
||||
@@ -205,7 +202,6 @@ export class HttpChecker implements CheckerDefinition<ResolvedHttpTarget> {
|
||||
id: t.id,
|
||||
intervalMs: context.defaultIntervalMs,
|
||||
name: t.name ?? null,
|
||||
rawExpect,
|
||||
timeoutMs: context.defaultTimeoutMs,
|
||||
type: "http",
|
||||
} satisfies ResolvedHttpTarget;
|
||||
|
||||
@@ -3,9 +3,14 @@ import { Type } from "@sinclair/typebox";
|
||||
import type { CheckerSchemas } from "../types";
|
||||
|
||||
import {
|
||||
createRawContentExpectationsSchema,
|
||||
createRawKeyedExpectationsSchema,
|
||||
createRawValueExpectationSchema,
|
||||
createAuthoringContentExpectationsSchema,
|
||||
createAuthoringFieldSchema,
|
||||
createAuthoringKeyedExpectationsSchema,
|
||||
createAuthoringStringMapSchema,
|
||||
createAuthoringValueExpectationSchema,
|
||||
createNormalizedContentExpectationsSchema,
|
||||
createNormalizedKeyedExpectationsSchema,
|
||||
createNormalizedValueExpectationSchema,
|
||||
httpMethodSchema,
|
||||
sizeSchema,
|
||||
statusCodePatternSchema,
|
||||
@@ -13,25 +18,47 @@ import {
|
||||
} from "../../schema/fragments";
|
||||
|
||||
export const httpCheckerSchemas: CheckerSchemas = {
|
||||
config: Type.Object(
|
||||
authoring: {
|
||||
config: createHttpConfigSchema("authoring"),
|
||||
expect: createHttpExpectSchema("authoring"),
|
||||
},
|
||||
normalized: {
|
||||
config: createHttpConfigSchema("normalized"),
|
||||
expect: createHttpExpectSchema("normalized"),
|
||||
},
|
||||
};
|
||||
|
||||
function createHttpConfigSchema(kind: "authoring" | "normalized") {
|
||||
const bool = Type.Boolean();
|
||||
const redirects = Type.Integer({ minimum: 0 });
|
||||
return Type.Object(
|
||||
{
|
||||
body: Type.Optional(Type.String()),
|
||||
headers: Type.Optional(stringMapSchema),
|
||||
ignoreSSL: Type.Optional(Type.Boolean()),
|
||||
headers: Type.Optional(kind === "authoring" ? createAuthoringStringMapSchema() : stringMapSchema),
|
||||
ignoreSSL: Type.Optional(kind === "authoring" ? createAuthoringFieldSchema(bool) : bool),
|
||||
maxBodyBytes: Type.Optional(sizeSchema),
|
||||
maxRedirects: Type.Optional(Type.Integer({ minimum: 0 })),
|
||||
method: Type.Optional(httpMethodSchema),
|
||||
maxRedirects: Type.Optional(kind === "authoring" ? createAuthoringFieldSchema(redirects) : redirects),
|
||||
method: Type.Optional(kind === "authoring" ? createAuthoringFieldSchema(httpMethodSchema) : httpMethodSchema),
|
||||
url: Type.String({ minLength: 1 }),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
expect: Type.Object(
|
||||
);
|
||||
}
|
||||
|
||||
function createHttpExpectSchema(kind: "authoring" | "normalized") {
|
||||
return Type.Object(
|
||||
{
|
||||
body: Type.Optional(createRawContentExpectationsSchema()),
|
||||
durationMs: Type.Optional(createRawValueExpectationSchema()),
|
||||
headers: Type.Optional(createRawKeyedExpectationsSchema()),
|
||||
body: Type.Optional(
|
||||
kind === "authoring" ? createAuthoringContentExpectationsSchema() : createNormalizedContentExpectationsSchema(),
|
||||
),
|
||||
durationMs: Type.Optional(
|
||||
kind === "authoring" ? createAuthoringValueExpectationSchema() : createNormalizedValueExpectationSchema(),
|
||||
),
|
||||
headers: Type.Optional(
|
||||
kind === "authoring" ? createAuthoringKeyedExpectationsSchema() : createNormalizedKeyedExpectationsSchema(),
|
||||
),
|
||||
status: Type.Optional(Type.Array(statusCodePatternSchema)),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
};
|
||||
);
|
||||
}
|
||||
|
||||
@@ -48,7 +48,6 @@ export interface ResolvedHttpTarget extends ResolvedTargetBase {
|
||||
http: ResolvedHttpConfig;
|
||||
intervalMs: number;
|
||||
name: null | string;
|
||||
rawExpect?: RawHttpExpectConfig;
|
||||
timeoutMs: number;
|
||||
type: "http";
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ function validateHttpExpect(target: Record<string, unknown>, path: string): Conf
|
||||
const issues: ConfigValidationIssue[] = [];
|
||||
const expectPath = joinPath(path, "expect");
|
||||
|
||||
if (isPlainRecord(expect["headers"])) {
|
||||
if (expect["headers"] !== undefined) {
|
||||
issues.push(
|
||||
...validateRawKeyedExpectations(expect["headers"], joinPath(expectPath, "headers"), targetName, {
|
||||
caseInsensitive: true,
|
||||
|
||||
@@ -2,16 +2,10 @@ import { isError } from "es-toolkit";
|
||||
|
||||
import type { CheckResult, RawTargetConfig } from "../../types";
|
||||
import type { CheckerContext, CheckerDefinition, CheckerValidationInput, ResolveContext } from "../types";
|
||||
import type {
|
||||
PingStats,
|
||||
PingTargetConfig,
|
||||
RawIcmpExpectConfig,
|
||||
ResolvedIcmpExpectConfig,
|
||||
ResolvedPingTarget,
|
||||
} from "./types";
|
||||
import type { PingStats, PingTargetConfig, ResolvedIcmpExpectConfig, ResolvedPingTarget } from "./types";
|
||||
|
||||
import { errorFailure } from "../../expect/failure";
|
||||
import { checkValueExpectation, resolveValueExpectation } from "../../expect/value";
|
||||
import { checkValueExpectation } from "../../expect/value";
|
||||
import { buildPingCommand } from "./command";
|
||||
import { checkAlive, checkAvgLatency, checkMaxLatency, checkPacketLoss } from "./expect";
|
||||
import { parsePingOutput } from "./parse";
|
||||
@@ -162,14 +156,11 @@ export class IcmpChecker implements CheckerDefinition<ResolvedPingTarget> {
|
||||
resolve(target: RawTargetConfig, context: ResolveContext): ResolvedPingTarget {
|
||||
const t = target as RawTargetConfig & { icmp: PingTargetConfig; type: "icmp" };
|
||||
|
||||
const rawExpect = target.expect as RawIcmpExpectConfig | undefined;
|
||||
const resolvedExpect: ResolvedIcmpExpectConfig = rawExpect
|
||||
const expect = target.expect as ResolvedIcmpExpectConfig | undefined;
|
||||
const resolvedExpect: ResolvedIcmpExpectConfig = expect
|
||||
? {
|
||||
alive: rawExpect.alive ?? true,
|
||||
avgLatencyMs: resolveValueExpectation(rawExpect.avgLatencyMs),
|
||||
durationMs: resolveValueExpectation(rawExpect.durationMs),
|
||||
maxLatencyMs: resolveValueExpectation(rawExpect.maxLatencyMs),
|
||||
packetLossPercent: resolveValueExpectation(rawExpect.packetLossPercent),
|
||||
...expect,
|
||||
alive: expect.alive ?? true,
|
||||
}
|
||||
: { alive: true };
|
||||
|
||||
@@ -185,7 +176,6 @@ export class IcmpChecker implements CheckerDefinition<ResolvedPingTarget> {
|
||||
id: t.id,
|
||||
intervalMs: context.defaultIntervalMs,
|
||||
name: t.name ?? null,
|
||||
rawExpect,
|
||||
timeoutMs: context.defaultTimeoutMs,
|
||||
type: "icmp",
|
||||
} satisfies ResolvedPingTarget;
|
||||
|
||||
@@ -2,25 +2,54 @@ import { Type } from "@sinclair/typebox";
|
||||
|
||||
import type { CheckerSchemas } from "../types";
|
||||
|
||||
import { createRawValueExpectationSchema } from "../../schema/fragments";
|
||||
import {
|
||||
createAuthoringFieldSchema,
|
||||
createAuthoringValueExpectationSchema,
|
||||
createNormalizedValueExpectationSchema,
|
||||
} from "../../schema/fragments";
|
||||
|
||||
export const icmpCheckerSchemas: CheckerSchemas = {
|
||||
config: Type.Object(
|
||||
{
|
||||
count: Type.Optional(Type.Integer({ maximum: 100, minimum: 1 })),
|
||||
host: Type.String({ minLength: 1 }),
|
||||
packetSize: Type.Optional(Type.Integer({ maximum: 65500, minimum: 1 })),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
expect: Type.Object(
|
||||
{
|
||||
alive: Type.Optional(Type.Boolean()),
|
||||
avgLatencyMs: Type.Optional(createRawValueExpectationSchema()),
|
||||
durationMs: Type.Optional(createRawValueExpectationSchema()),
|
||||
maxLatencyMs: Type.Optional(createRawValueExpectationSchema()),
|
||||
packetLossPercent: Type.Optional(createRawValueExpectationSchema()),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
authoring: {
|
||||
config: createIcmpConfigSchema("authoring"),
|
||||
expect: createIcmpExpectSchema("authoring"),
|
||||
},
|
||||
normalized: {
|
||||
config: createIcmpConfigSchema("normalized"),
|
||||
expect: createIcmpExpectSchema("normalized"),
|
||||
},
|
||||
};
|
||||
|
||||
function createIcmpConfigSchema(kind: "authoring" | "normalized") {
|
||||
const count = Type.Integer({ maximum: 100, minimum: 1 });
|
||||
const packetSize = Type.Integer({ maximum: 65500, minimum: 1 });
|
||||
return Type.Object(
|
||||
{
|
||||
count: Type.Optional(kind === "authoring" ? createAuthoringFieldSchema(count) : count),
|
||||
host: Type.String({ minLength: 1 }),
|
||||
packetSize: Type.Optional(kind === "authoring" ? createAuthoringFieldSchema(packetSize) : packetSize),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
);
|
||||
}
|
||||
|
||||
function createIcmpExpectSchema(kind: "authoring" | "normalized") {
|
||||
const bool = Type.Boolean();
|
||||
return Type.Object(
|
||||
{
|
||||
alive: Type.Optional(kind === "authoring" ? createAuthoringFieldSchema(bool) : bool),
|
||||
avgLatencyMs: Type.Optional(
|
||||
kind === "authoring" ? createAuthoringValueExpectationSchema() : createNormalizedValueExpectationSchema(),
|
||||
),
|
||||
durationMs: Type.Optional(
|
||||
kind === "authoring" ? createAuthoringValueExpectationSchema() : createNormalizedValueExpectationSchema(),
|
||||
),
|
||||
maxLatencyMs: Type.Optional(
|
||||
kind === "authoring" ? createAuthoringValueExpectationSchema() : createNormalizedValueExpectationSchema(),
|
||||
),
|
||||
packetLossPercent: Type.Optional(
|
||||
kind === "authoring" ? createAuthoringValueExpectationSchema() : createNormalizedValueExpectationSchema(),
|
||||
),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -45,7 +45,6 @@ export interface ResolvedPingTarget extends ResolvedTargetBase {
|
||||
icmp: ResolvedPingConfig;
|
||||
intervalMs: number;
|
||||
name: null | string;
|
||||
rawExpect?: RawIcmpExpectConfig;
|
||||
timeoutMs: number;
|
||||
type: "icmp";
|
||||
}
|
||||
|
||||
@@ -3,12 +3,10 @@ import { isError } from "es-toolkit";
|
||||
|
||||
import type { CheckResult, RawTargetConfig } from "../../types";
|
||||
import type { CheckerContext, CheckerDefinition, CheckerValidationInput, ResolveContext } from "../types";
|
||||
import type { LlmTargetConfig, RawLlmExpectConfig, ResolvedLlmExpectConfig, ResolvedLlmTarget } from "./types";
|
||||
import type { LlmTargetConfig, ResolvedLlmExpectConfig, ResolvedLlmTarget } from "./types";
|
||||
|
||||
import { resolveContentExpectations } from "../../expect/content";
|
||||
import { errorFailure } from "../../expect/failure";
|
||||
import { resolveKeyedExpectations } from "../../expect/keyed";
|
||||
import { checkValueExpectation, resolveValueExpectation } from "../../expect/value";
|
||||
import { checkValueExpectation } from "../../expect/value";
|
||||
import { runExpects } from "./expect";
|
||||
import {
|
||||
buildObservationFromApiCallError,
|
||||
@@ -155,26 +153,15 @@ export class LlmChecker implements CheckerDefinition<ResolvedLlmTarget> {
|
||||
url: t.llm.url,
|
||||
};
|
||||
|
||||
const rawExpect = target.expect as RawLlmExpectConfig | undefined;
|
||||
const resolvedExpect: ResolvedLlmExpectConfig = rawExpect
|
||||
const expect = target.expect as ResolvedLlmExpectConfig | undefined;
|
||||
const resolvedExpect: ResolvedLlmExpectConfig = expect
|
||||
? {
|
||||
durationMs: resolveValueExpectation(rawExpect.durationMs),
|
||||
finishReason: resolveValueExpectation(rawExpect.finishReason),
|
||||
headers: resolveKeyedExpectations(rawExpect.headers),
|
||||
output: resolveContentExpectations(rawExpect.output),
|
||||
rawFinishReason: resolveValueExpectation(rawExpect.rawFinishReason),
|
||||
status: rawExpect.status ?? [200],
|
||||
stream: rawExpect.stream
|
||||
...expect,
|
||||
status: expect.status ?? [200],
|
||||
stream: expect.stream
|
||||
? {
|
||||
completed: rawExpect.stream.completed ?? true,
|
||||
firstTokenMs: resolveValueExpectation(rawExpect.stream.firstTokenMs),
|
||||
}
|
||||
: undefined,
|
||||
usage: rawExpect.usage
|
||||
? {
|
||||
inputTokens: resolveValueExpectation(rawExpect.usage.inputTokens),
|
||||
outputTokens: resolveValueExpectation(rawExpect.usage.outputTokens),
|
||||
totalTokens: resolveValueExpectation(rawExpect.usage.totalTokens),
|
||||
...expect.stream,
|
||||
completed: expect.stream.completed ?? true,
|
||||
}
|
||||
: undefined,
|
||||
}
|
||||
@@ -188,7 +175,6 @@ export class LlmChecker implements CheckerDefinition<ResolvedLlmTarget> {
|
||||
intervalMs: context.defaultIntervalMs,
|
||||
llm: resolvedConfig,
|
||||
name: (target.name as null | string) ?? null,
|
||||
rawExpect,
|
||||
timeoutMs: context.defaultTimeoutMs,
|
||||
type: "llm",
|
||||
} satisfies ResolvedLlmTarget;
|
||||
|
||||
@@ -3,18 +3,26 @@ import { Type } from "@sinclair/typebox";
|
||||
import type { CheckerSchemas } from "../types";
|
||||
|
||||
import {
|
||||
createRawContentExpectationsSchema,
|
||||
createRawKeyedExpectationsSchema,
|
||||
createRawValueExpectationSchema,
|
||||
createAuthoringContentExpectationsSchema,
|
||||
createAuthoringFieldSchema,
|
||||
createAuthoringKeyedExpectationsSchema,
|
||||
createAuthoringStringMapSchema,
|
||||
createAuthoringValueExpectationSchema,
|
||||
createNormalizedContentExpectationsSchema,
|
||||
createNormalizedKeyedExpectationsSchema,
|
||||
createNormalizedValueExpectationSchema,
|
||||
statusCodePatternSchema,
|
||||
stringMapSchema,
|
||||
} from "../../schema/fragments";
|
||||
|
||||
function createLlmOptionsSchema() {
|
||||
function createLlmOptionsSchema(kind: "authoring" | "normalized") {
|
||||
const maxOutputTokens = Type.Integer({ minimum: 1 });
|
||||
return Type.Object(
|
||||
{
|
||||
frequencyPenalty: Type.Optional(Type.Number()),
|
||||
maxOutputTokens: Type.Optional(Type.Integer({ minimum: 1 })),
|
||||
maxOutputTokens: Type.Optional(
|
||||
kind === "authoring" ? createAuthoringFieldSchema(maxOutputTokens) : maxOutputTokens,
|
||||
),
|
||||
presencePenalty: Type.Optional(Type.Number()),
|
||||
seed: Type.Optional(Type.Number()),
|
||||
stopSequences: Type.Optional(Type.Array(Type.String())),
|
||||
@@ -27,35 +35,59 @@ function createLlmOptionsSchema() {
|
||||
}
|
||||
|
||||
export const llmCheckerSchemas: CheckerSchemas = {
|
||||
config: Type.Object(
|
||||
authoring: {
|
||||
config: createLlmConfigSchema("authoring"),
|
||||
expect: createLlmExpectSchema("authoring"),
|
||||
},
|
||||
normalized: {
|
||||
config: createLlmConfigSchema("normalized"),
|
||||
expect: createLlmExpectSchema("normalized"),
|
||||
},
|
||||
};
|
||||
|
||||
function createLlmConfigSchema(kind: "authoring" | "normalized") {
|
||||
const bool = Type.Boolean();
|
||||
const mode = createLlmModeSchema();
|
||||
const provider = createLlmProviderSchema();
|
||||
return Type.Object(
|
||||
{
|
||||
authToken: Type.Optional(Type.String()),
|
||||
headers: Type.Optional(stringMapSchema),
|
||||
ignoreSSL: Type.Optional(Type.Boolean()),
|
||||
headers: Type.Optional(kind === "authoring" ? createAuthoringStringMapSchema() : stringMapSchema),
|
||||
ignoreSSL: Type.Optional(kind === "authoring" ? createAuthoringFieldSchema(bool) : bool),
|
||||
key: Type.Optional(Type.String()),
|
||||
mode: Type.Optional(Type.Union([Type.Literal("http"), Type.Literal("stream")])),
|
||||
mode: Type.Optional(kind === "authoring" ? createAuthoringFieldSchema(mode) : mode),
|
||||
model: Type.String({ minLength: 1 }),
|
||||
options: Type.Optional(createLlmOptionsSchema()),
|
||||
options: Type.Optional(createLlmOptionsSchema(kind)),
|
||||
prompt: Type.String({ minLength: 1 }),
|
||||
provider: Type.Union([Type.Literal("openai"), Type.Literal("openai-responses"), Type.Literal("anthropic")]),
|
||||
provider: kind === "authoring" ? createAuthoringFieldSchema(provider) : provider,
|
||||
providerOptions: Type.Optional(Type.Record(Type.String(), Type.Object({}, { additionalProperties: true }))),
|
||||
url: Type.String({ minLength: 1 }),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
expect: Type.Object(
|
||||
);
|
||||
}
|
||||
|
||||
function createLlmExpectSchema(kind: "authoring" | "normalized") {
|
||||
const bool = Type.Boolean();
|
||||
const valueExpectation =
|
||||
kind === "authoring" ? createAuthoringValueExpectationSchema() : createNormalizedValueExpectationSchema();
|
||||
return Type.Object(
|
||||
{
|
||||
durationMs: Type.Optional(createRawValueExpectationSchema()),
|
||||
finishReason: Type.Optional(createRawValueExpectationSchema()),
|
||||
headers: Type.Optional(createRawKeyedExpectationsSchema()),
|
||||
output: Type.Optional(createRawContentExpectationsSchema()),
|
||||
rawFinishReason: Type.Optional(createRawValueExpectationSchema()),
|
||||
durationMs: Type.Optional(valueExpectation),
|
||||
finishReason: Type.Optional(valueExpectation),
|
||||
headers: Type.Optional(
|
||||
kind === "authoring" ? createAuthoringKeyedExpectationsSchema() : createNormalizedKeyedExpectationsSchema(),
|
||||
),
|
||||
output: Type.Optional(
|
||||
kind === "authoring" ? createAuthoringContentExpectationsSchema() : createNormalizedContentExpectationsSchema(),
|
||||
),
|
||||
rawFinishReason: Type.Optional(valueExpectation),
|
||||
status: Type.Optional(Type.Array(statusCodePatternSchema)),
|
||||
stream: Type.Optional(
|
||||
Type.Object(
|
||||
{
|
||||
completed: Type.Optional(Type.Boolean()),
|
||||
firstTokenMs: Type.Optional(createRawValueExpectationSchema()),
|
||||
completed: Type.Optional(kind === "authoring" ? createAuthoringFieldSchema(bool) : bool),
|
||||
firstTokenMs: Type.Optional(valueExpectation),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
@@ -63,14 +95,22 @@ export const llmCheckerSchemas: CheckerSchemas = {
|
||||
usage: Type.Optional(
|
||||
Type.Object(
|
||||
{
|
||||
inputTokens: Type.Optional(createRawValueExpectationSchema()),
|
||||
outputTokens: Type.Optional(createRawValueExpectationSchema()),
|
||||
totalTokens: Type.Optional(createRawValueExpectationSchema()),
|
||||
inputTokens: Type.Optional(valueExpectation),
|
||||
outputTokens: Type.Optional(valueExpectation),
|
||||
totalTokens: Type.Optional(valueExpectation),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
};
|
||||
);
|
||||
}
|
||||
|
||||
function createLlmModeSchema() {
|
||||
return Type.Union([Type.Literal("http"), Type.Literal("stream")]);
|
||||
}
|
||||
|
||||
function createLlmProviderSchema() {
|
||||
return Type.Union([Type.Literal("openai"), Type.Literal("openai-responses"), Type.Literal("anthropic")]);
|
||||
}
|
||||
|
||||
@@ -152,7 +152,6 @@ export interface ResolvedLlmTarget extends ResolvedTargetBase {
|
||||
intervalMs: number;
|
||||
llm: ResolvedLlmConfig;
|
||||
name: null | string;
|
||||
rawExpect?: RawLlmExpectConfig;
|
||||
timeoutMs: number;
|
||||
type: "llm";
|
||||
}
|
||||
|
||||
@@ -2,11 +2,10 @@ import { isError } from "es-toolkit";
|
||||
|
||||
import type { CheckResult, RawTargetConfig } from "../../types";
|
||||
import type { CheckerContext, CheckerDefinition, CheckerValidationInput, ResolveContext } from "../types";
|
||||
import type { RawTcpExpectConfig, ResolvedTcpExpectConfig, ResolvedTcpTarget, TcpTargetConfig } from "./types";
|
||||
import type { ResolvedTcpExpectConfig, ResolvedTcpTarget, TcpTargetConfig } from "./types";
|
||||
|
||||
import { resolveContentExpectations } from "../../expect/content";
|
||||
import { errorFailure } from "../../expect/failure";
|
||||
import { checkValueExpectation, resolveValueExpectation } from "../../expect/value";
|
||||
import { checkValueExpectation } from "../../expect/value";
|
||||
import { parseSize } from "../../utils";
|
||||
import { checkBanner, checkConnected } from "./expect";
|
||||
import { tcpCheckerSchemas } from "./schema";
|
||||
@@ -210,12 +209,11 @@ export class TcpChecker implements CheckerDefinition<ResolvedTcpTarget> {
|
||||
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
|
||||
const expect = target.expect as ResolvedTcpExpectConfig | undefined;
|
||||
const resolvedExpect: ResolvedTcpExpectConfig = expect
|
||||
? {
|
||||
banner: resolveContentExpectations(rawExpect.banner),
|
||||
connected: rawExpect.connected ?? true,
|
||||
durationMs: resolveValueExpectation(rawExpect.durationMs),
|
||||
...expect,
|
||||
connected: expect.connected ?? true,
|
||||
}
|
||||
: { connected: true };
|
||||
|
||||
@@ -226,7 +224,6 @@ export class TcpChecker implements CheckerDefinition<ResolvedTcpTarget> {
|
||||
id: t.id,
|
||||
intervalMs: context.defaultIntervalMs,
|
||||
name: t.name ?? null,
|
||||
rawExpect,
|
||||
tcp: {
|
||||
bannerReadTimeout,
|
||||
host: t.tcp.host,
|
||||
|
||||
@@ -3,28 +3,52 @@ import { Type } from "@sinclair/typebox";
|
||||
import type { CheckerSchemas } from "../types";
|
||||
|
||||
import {
|
||||
createRawContentExpectationsSchema,
|
||||
createRawValueExpectationSchema,
|
||||
createAuthoringContentExpectationsSchema,
|
||||
createAuthoringFieldSchema,
|
||||
createAuthoringValueExpectationSchema,
|
||||
createNormalizedContentExpectationsSchema,
|
||||
createNormalizedValueExpectationSchema,
|
||||
sizeSchema,
|
||||
} from "../../schema/fragments";
|
||||
|
||||
export const tcpCheckerSchemas: CheckerSchemas = {
|
||||
config: Type.Object(
|
||||
authoring: {
|
||||
config: createTcpConfigSchema("authoring"),
|
||||
expect: createTcpExpectSchema("authoring"),
|
||||
},
|
||||
normalized: {
|
||||
config: createTcpConfigSchema("normalized"),
|
||||
expect: createTcpExpectSchema("normalized"),
|
||||
},
|
||||
};
|
||||
|
||||
function createTcpConfigSchema(kind: "authoring" | "normalized") {
|
||||
const port = Type.Integer({ maximum: 65535, minimum: 1 });
|
||||
const readBanner = Type.Boolean();
|
||||
return Type.Object(
|
||||
{
|
||||
bannerReadTimeout: Type.Optional(Type.Number({ minimum: 0 })),
|
||||
host: Type.String({ minLength: 1 }),
|
||||
maxBannerBytes: Type.Optional(sizeSchema),
|
||||
port: Type.Integer({ maximum: 65535, minimum: 1 }),
|
||||
readBanner: Type.Optional(Type.Boolean()),
|
||||
port: kind === "authoring" ? createAuthoringFieldSchema(port) : port,
|
||||
readBanner: Type.Optional(kind === "authoring" ? createAuthoringFieldSchema(readBanner) : readBanner),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
expect: Type.Object(
|
||||
);
|
||||
}
|
||||
|
||||
function createTcpExpectSchema(kind: "authoring" | "normalized") {
|
||||
const connected = Type.Boolean();
|
||||
return Type.Object(
|
||||
{
|
||||
banner: Type.Optional(createRawContentExpectationsSchema()),
|
||||
connected: Type.Optional(Type.Boolean()),
|
||||
durationMs: Type.Optional(createRawValueExpectationSchema()),
|
||||
banner: Type.Optional(
|
||||
kind === "authoring" ? createAuthoringContentExpectationsSchema() : createNormalizedContentExpectationsSchema(),
|
||||
),
|
||||
connected: Type.Optional(kind === "authoring" ? createAuthoringFieldSchema(connected) : connected),
|
||||
durationMs: Type.Optional(
|
||||
kind === "authoring" ? createAuthoringValueExpectationSchema() : createNormalizedValueExpectationSchema(),
|
||||
),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
};
|
||||
);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ export interface ResolvedTcpTarget extends ResolvedTargetBase {
|
||||
group: string;
|
||||
intervalMs: number;
|
||||
name: null | string;
|
||||
rawExpect?: RawTcpExpectConfig;
|
||||
tcp: ResolvedTcpConfig;
|
||||
timeoutMs: number;
|
||||
type: "tcp";
|
||||
|
||||
@@ -20,11 +20,16 @@ export interface CheckerDefinition<TResolved extends ResolvedTargetBase = Resolv
|
||||
validate(input: CheckerValidationInput): ConfigValidationIssue[];
|
||||
}
|
||||
|
||||
export interface CheckerSchemas {
|
||||
export interface CheckerSchemaPair {
|
||||
config: TSchema;
|
||||
expect: TSchema;
|
||||
}
|
||||
|
||||
export interface CheckerSchemas {
|
||||
authoring: CheckerSchemaPair;
|
||||
normalized: CheckerSchemaPair;
|
||||
}
|
||||
|
||||
export interface CheckerValidationInput {
|
||||
targets: RawTargetConfig[];
|
||||
}
|
||||
|
||||
@@ -2,11 +2,10 @@ import { isError } from "es-toolkit";
|
||||
|
||||
import type { CheckResult, RawTargetConfig } from "../../types";
|
||||
import type { CheckerContext, CheckerDefinition, CheckerValidationInput, ResolveContext } from "../types";
|
||||
import type { RawUdpExpectConfig, ResolvedUdpExpectConfig, ResolvedUdpTarget, UdpTargetConfig } from "./types";
|
||||
import type { ResolvedUdpExpectConfig, ResolvedUdpTarget, UdpTargetConfig } from "./types";
|
||||
|
||||
import { resolveContentExpectations } from "../../expect/content";
|
||||
import { errorFailure } from "../../expect/failure";
|
||||
import { checkValueExpectation, resolveValueExpectation } from "../../expect/value";
|
||||
import { checkValueExpectation } from "../../expect/value";
|
||||
import { parseSize } from "../../utils";
|
||||
import { decodePayload, encodeResponse } from "./encoding";
|
||||
import { checkResponded, checkResponseSize, checkResponseText, checkSourceHost, checkSourcePort } from "./expect";
|
||||
@@ -303,15 +302,11 @@ export class UdpChecker implements CheckerDefinition<ResolvedUdpTarget> {
|
||||
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
|
||||
const expect = target.expect as ResolvedUdpExpectConfig | undefined;
|
||||
const resolvedExpect: ResolvedUdpExpectConfig = expect
|
||||
? {
|
||||
durationMs: resolveValueExpectation(rawExpect.durationMs),
|
||||
responded: rawExpect.responded ?? true,
|
||||
response: resolveContentExpectations(rawExpect.response),
|
||||
responseSize: resolveValueExpectation(rawExpect.responseSize),
|
||||
sourceHost: resolveValueExpectation(rawExpect.sourceHost),
|
||||
sourcePort: resolveValueExpectation(rawExpect.sourcePort),
|
||||
...expect,
|
||||
responded: expect.responded ?? true,
|
||||
}
|
||||
: { responded: true };
|
||||
|
||||
@@ -322,7 +317,6 @@ export class UdpChecker implements CheckerDefinition<ResolvedUdpTarget> {
|
||||
id: t.id,
|
||||
intervalMs: context.defaultIntervalMs,
|
||||
name: t.name ?? null,
|
||||
rawExpect,
|
||||
timeoutMs: context.defaultTimeoutMs,
|
||||
type: "udp",
|
||||
udp: {
|
||||
|
||||
@@ -3,32 +3,69 @@ import { Type } from "@sinclair/typebox";
|
||||
import type { CheckerSchemas } from "../types";
|
||||
|
||||
import {
|
||||
createRawContentExpectationsSchema,
|
||||
createRawValueExpectationSchema,
|
||||
createAuthoringContentExpectationsSchema,
|
||||
createAuthoringFieldSchema,
|
||||
createAuthoringValueExpectationSchema,
|
||||
createNormalizedContentExpectationsSchema,
|
||||
createNormalizedValueExpectationSchema,
|
||||
sizeSchema,
|
||||
} from "../../schema/fragments";
|
||||
|
||||
export const udpCheckerSchemas: CheckerSchemas = {
|
||||
config: Type.Object(
|
||||
authoring: {
|
||||
config: createUdpConfigSchema("authoring"),
|
||||
expect: createUdpExpectSchema("authoring"),
|
||||
},
|
||||
normalized: {
|
||||
config: createUdpConfigSchema("normalized"),
|
||||
expect: createUdpExpectSchema("normalized"),
|
||||
},
|
||||
};
|
||||
|
||||
function createEncodingSchema() {
|
||||
return Type.Union([Type.Literal("text"), Type.Literal("hex"), Type.Literal("base64")]);
|
||||
}
|
||||
|
||||
function createUdpConfigSchema(kind: "authoring" | "normalized") {
|
||||
const port = Type.Integer({ maximum: 65535, minimum: 1 });
|
||||
const encoding = createEncodingSchema();
|
||||
const responseEncoding = createEncodingSchema();
|
||||
return Type.Object(
|
||||
{
|
||||
encoding: Type.Optional(Type.Union([Type.Literal("text"), Type.Literal("hex"), Type.Literal("base64")])),
|
||||
encoding: Type.Optional(kind === "authoring" ? createAuthoringFieldSchema(encoding) : encoding),
|
||||
host: Type.String({ minLength: 1 }),
|
||||
maxResponseBytes: Type.Optional(sizeSchema),
|
||||
payload: Type.Optional(Type.String()),
|
||||
port: Type.Integer({ maximum: 65535, minimum: 1 }),
|
||||
responseEncoding: Type.Optional(Type.Union([Type.Literal("text"), Type.Literal("hex"), Type.Literal("base64")])),
|
||||
port: kind === "authoring" ? createAuthoringFieldSchema(port) : port,
|
||||
responseEncoding: Type.Optional(
|
||||
kind === "authoring" ? createAuthoringFieldSchema(responseEncoding) : responseEncoding,
|
||||
),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
expect: Type.Object(
|
||||
);
|
||||
}
|
||||
|
||||
function createUdpExpectSchema(kind: "authoring" | "normalized") {
|
||||
const responded = Type.Boolean();
|
||||
return Type.Object(
|
||||
{
|
||||
durationMs: Type.Optional(createRawValueExpectationSchema()),
|
||||
responded: Type.Optional(Type.Boolean()),
|
||||
response: Type.Optional(createRawContentExpectationsSchema()),
|
||||
responseSize: Type.Optional(createRawValueExpectationSchema()),
|
||||
sourceHost: Type.Optional(createRawValueExpectationSchema()),
|
||||
sourcePort: Type.Optional(createRawValueExpectationSchema()),
|
||||
durationMs: Type.Optional(
|
||||
kind === "authoring" ? createAuthoringValueExpectationSchema() : createNormalizedValueExpectationSchema(),
|
||||
),
|
||||
responded: Type.Optional(kind === "authoring" ? createAuthoringFieldSchema(responded) : responded),
|
||||
response: Type.Optional(
|
||||
kind === "authoring" ? createAuthoringContentExpectationsSchema() : createNormalizedContentExpectationsSchema(),
|
||||
),
|
||||
responseSize: Type.Optional(
|
||||
kind === "authoring" ? createAuthoringValueExpectationSchema() : createNormalizedValueExpectationSchema(),
|
||||
),
|
||||
sourceHost: Type.Optional(
|
||||
kind === "authoring" ? createAuthoringValueExpectationSchema() : createNormalizedValueExpectationSchema(),
|
||||
),
|
||||
sourcePort: Type.Optional(
|
||||
kind === "authoring" ? createAuthoringValueExpectationSchema() : createNormalizedValueExpectationSchema(),
|
||||
),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
};
|
||||
);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ export interface ResolvedUdpTarget extends ResolvedTargetBase {
|
||||
group: string;
|
||||
intervalMs: number;
|
||||
name: null | string;
|
||||
rawExpect?: RawUdpExpectConfig;
|
||||
timeoutMs: number;
|
||||
type: "udp";
|
||||
udp: ResolvedUdpConfig;
|
||||
|
||||
Reference in New Issue
Block a user