Files
pikasTech-HWLAB/internal/cloud/workbench-kafka-debug-capability.ts
T
2026-07-10 13:48:15 +02:00

69 lines
3.0 KiB
TypeScript

// Responsibility: validate the independently YAML-owned Workbench Kafka debug stream.
// This capability is intentionally separate from the five product realtime capabilities.
export const WORKBENCH_KAFKA_DEBUG_ENVS = Object.freeze({
enabled: "HWLAB_WORKBENCH_KAFKA_DEBUG_REPLAY_ENABLED",
topic: "HWLAB_KAFKA_HWLAB_DEBUG_EVENT_TOPIC",
groupIdPrefix: "HWLAB_KAFKA_HWLAB_DEBUG_GROUP_PREFIX",
replayLimit: "HWLAB_WORKBENCH_KAFKA_DEBUG_REPLAY_LIMIT",
replayTimeoutMs: "HWLAB_WORKBENCH_KAFKA_DEBUG_REPLAY_TIMEOUT_MS"
});
export function workbenchKafkaDebugCapability(env = process.env) {
const enabled = requiredBoolean(env?.[WORKBENCH_KAFKA_DEBUG_ENVS.enabled], WORKBENCH_KAFKA_DEBUG_ENVS.enabled);
const topic = requiredIsolatedKafkaName(
env?.[WORKBENCH_KAFKA_DEBUG_ENVS.topic],
WORKBENCH_KAFKA_DEBUG_ENVS.topic,
249,
["hwlab.event.v1", env?.HWLAB_KAFKA_HWLAB_EVENT_TOPIC, env?.HWLAB_KAFKA_EVENT_TOPIC]
);
const groupIdPrefix = requiredIsolatedKafkaName(
env?.[WORKBENCH_KAFKA_DEBUG_ENVS.groupIdPrefix],
WORKBENCH_KAFKA_DEBUG_ENVS.groupIdPrefix,
180,
[env?.HWLAB_KAFKA_HWLAB_EVENT_GROUP_ID]
);
const replayLimit = requiredPositiveInteger(env?.[WORKBENCH_KAFKA_DEBUG_ENVS.replayLimit], WORKBENCH_KAFKA_DEBUG_ENVS.replayLimit);
const replayTimeoutMs = requiredPositiveInteger(env?.[WORKBENCH_KAFKA_DEBUG_ENVS.replayTimeoutMs], WORKBENCH_KAFKA_DEBUG_ENVS.replayTimeoutMs);
return { enabled, topic, groupIdPrefix, replayLimit, replayTimeoutMs, valuesPrinted: false };
}
function requiredBoolean(value, name) {
const normalized = String(value ?? "").trim().toLowerCase();
if (normalized === "true" || normalized === "1") return true;
if (normalized === "false" || normalized === "0") return false;
throw capabilityError(`${name} is required and must be explicitly true or false.`, name);
}
function requiredKafkaName(value, name, maxLength) {
const text = String(value ?? "").trim();
if (!text || text.length > maxLength || !/^[A-Za-z0-9._-]+$/u.test(text)) {
throw capabilityError(`${name} is required and must be a valid Kafka identifier.`, name);
}
return text;
}
function requiredIsolatedKafkaName(value, name, maxLength, productValues) {
const text = requiredKafkaName(value, name, maxLength);
const products = new Set(productValues.map((candidate) => String(candidate ?? "").trim()).filter(Boolean));
if (products.has(text) || !/(?:^|[.-])debug(?:[.-]|$)/u.test(text)) {
throw capabilityError(`${name} must identify an isolated debug Kafka resource and must not reuse a product topic or group.`, name);
}
return text;
}
function requiredPositiveInteger(value, name) {
const number = Number(value);
if (!Number.isInteger(number) || number <= 0) throw capabilityError(`${name} is required and must be a positive integer.`, name);
return number;
}
function capabilityError(message, envName) {
return Object.assign(new Error(message), {
code: "workbench_kafka_debug_capability_invalid",
envName,
statusCode: 503,
valuesRedacted: true
});
}