feat(web-probe): add multi-sentinel registry
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
// SPEC: PJ2026-01060505 Workbench Performance draft-2026-06-17-p0.
|
||||
// SPEC: PJ2026-01060508 Web哨兵 draft-2026-06-25-p0-web-probe-sentinel.
|
||||
// SPEC: PJ2026-01060508 Web哨兵 draft-2026-06-26-p9-multi-web-probe-sentinel.
|
||||
// SPEC: PJ2026-01060308 cicd-yaml-targets draft-2026-06-25-cicd-yaml-targets.
|
||||
// Responsibility: YAML source-of-truth parsing for HWLAB node/lane Workbench observability.
|
||||
import { readFileSync } from "node:fs";
|
||||
@@ -149,6 +150,12 @@ export interface HwlabRuntimeWebProbeSentinelSpec {
|
||||
readonly configRefs: Record<HwlabRuntimeWebProbeSentinelConfigRefKey, string>;
|
||||
}
|
||||
|
||||
export interface HwlabRuntimeWebProbeSentinelRegistryItemSpec {
|
||||
readonly id: string;
|
||||
readonly enabled: boolean;
|
||||
readonly configRef: string;
|
||||
}
|
||||
|
||||
export interface HwlabRuntimeWebProbeAlertThresholdsSpec {
|
||||
readonly sameOriginApiSlowMs: number;
|
||||
readonly partialApiSlowMs: number;
|
||||
@@ -189,6 +196,7 @@ export interface HwlabRuntimeObservabilitySpec {
|
||||
|
||||
export interface HwlabRuntimeObservabilityWebProbeSpec {
|
||||
readonly sentinel?: HwlabRuntimeWebProbeSentinelSpec;
|
||||
readonly sentinels?: readonly HwlabRuntimeWebProbeSentinelRegistryItemSpec[];
|
||||
}
|
||||
|
||||
export interface HwlabRuntimeObservabilityMetricsEndpointSpec {
|
||||
@@ -793,6 +801,35 @@ function webProbeSentinelConfig(value: unknown, path: string): HwlabRuntimeWebPr
|
||||
};
|
||||
}
|
||||
|
||||
function webProbeSentinelRegistryItemConfig(value: unknown, path: string): HwlabRuntimeWebProbeSentinelRegistryItemSpec {
|
||||
const raw = asRecord(value, path);
|
||||
const allowed = new Set(["id", "enabled", "configRef"]);
|
||||
for (const key of Object.keys(raw)) {
|
||||
if (!allowed.has(key)) throw new Error(`${path}.${key} is not allowed; sentinel registry items may only contain id/enabled/configRef`);
|
||||
}
|
||||
const id = stringField(raw, "id", path);
|
||||
if (!/^[a-z0-9][a-z0-9-]{1,80}$/u.test(id)) throw new Error(`${path}.id must be a stable lowercase sentinel id`);
|
||||
const configRef = stringField(raw, "configRef", path);
|
||||
validateConfigRef(configRef, `${path}.configRef`);
|
||||
return {
|
||||
id,
|
||||
enabled: booleanField(raw, "enabled", path),
|
||||
configRef,
|
||||
};
|
||||
}
|
||||
|
||||
function webProbeSentinelRegistryConfig(value: unknown, path: string): readonly HwlabRuntimeWebProbeSentinelRegistryItemSpec[] {
|
||||
if (!Array.isArray(value)) throw new Error(`${path} must be an array`);
|
||||
if (value.length === 0) throw new Error(`${path} must contain at least one sentinel`);
|
||||
const items = value.map((item, index) => webProbeSentinelRegistryItemConfig(item, `${path}[${index}]`));
|
||||
const ids = new Set<string>();
|
||||
for (const item of items) {
|
||||
if (ids.has(item.id)) throw new Error(`${path} contains duplicate sentinel id ${item.id}`);
|
||||
ids.add(item.id);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
function validateConfigRef(ref: string, path: string): void {
|
||||
const [file, fragment, extra] = ref.split("#");
|
||||
if (extra !== undefined || file === undefined || fragment === undefined || file.length === 0 || fragment.length === 0) {
|
||||
@@ -937,12 +974,14 @@ function observabilityConfig(value: unknown, path: string): HwlabRuntimeObservab
|
||||
function observabilityWebProbeConfig(value: unknown, path: string): HwlabRuntimeObservabilityWebProbeSpec | undefined {
|
||||
if (value === undefined) return undefined;
|
||||
const raw = asRecord(value, path);
|
||||
const allowed = new Set(["sentinel"]);
|
||||
const allowed = new Set(["sentinel", "sentinels"]);
|
||||
for (const key of Object.keys(raw)) {
|
||||
if (!allowed.has(key)) throw new Error(`${path}.${key} is not allowed; observability.webProbe currently only owns sentinel`);
|
||||
if (!allowed.has(key)) throw new Error(`${path}.${key} is not allowed; observability.webProbe currently only owns sentinel/sentinels`);
|
||||
}
|
||||
if (raw.sentinel !== undefined && raw.sentinels !== undefined) throw new Error(`${path} may declare sentinel or sentinels, not both`);
|
||||
return {
|
||||
...(raw.sentinel === undefined ? {} : { sentinel: webProbeSentinelConfig(raw.sentinel, `${path}.sentinel`) }),
|
||||
...(raw.sentinels === undefined ? {} : { sentinels: webProbeSentinelRegistryConfig(raw.sentinels, `${path}.sentinels`) }),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user