// 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 }); }