type Translate = (key: string, params?: Record) => string // Exact lxc_apps validation/persistence messages; never change their API contract. // Anchored patterns leave unknown diagnostics intact, including paths and commands. const RULES: [RegExp, string, string[]][] = [ [/^an OCI operation on this container is running$/, "ociBusy", []], [/^([\s\S]+) is not installed via dpkg$/, "dpkgMissing", ["package"]], [/^dpkg\-query failed$/, "dpkgFailed", []], [/^no version matched in dpkg output$/, "dpkgNoVersion", []], [/^apk info failed$/, "apkFailed", []], [/^no version matched in apk output$/, "apkNoVersion", []], [/^could not read ([\s\S]+)$/, "fileRead", ["path"]], [/^file_regex did not match ([\s\S]+)$/, "fileNoMatch", ["path"]], [/^binary invocation failed$/, "binaryFailed", []], [/^no version matched in binary output$/, "binaryNoVersion", []], [/^python \-c importlib\.metadata failed$/, "pythonFailed", []], [/^no version matched in Python distribution output$/, "pythonNoVersion", []], [/^could not resolve the container's installed image$/, "dockerImageResolve", []], [/^docker inspect failed$/, "dockerInspectFailed", []], [/^container has no ([\s\S]+) label$/, "dockerLabelMissing", ["label"]], [/^docker label reports ([\s\S]+) \(mutable tag, not a version\)$/, "dockerLabelMutable", ["tag"]], [/^no version matched in docker label output$/, "dockerLabelNoVersion", []], [/^docker exec failed$/, "dockerExecFailed", []], [/^no version matched in docker exec output$/, "dockerExecNoVersion", []], [/^command_argv is empty$/, "commandEmpty", []], [/^command failed$/, "commandFailed", []], [/^no version matched in command output$/, "commandNoVersion", []], [/^unsupported method: ([\s\S]+)$/, "unsupportedMethod", ["method"]], [/^unknown upstream_type: ([\s\S]+)$/, "unknownUpstream", ["type"]], [/^([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+): not found$/, "githubNotFound", ["repo"]], [/^github rejected the request \(403\)$/, "githubRejected", []], [/^github error (\d+)$/, "githubError", ["code"]], [/^no tag \/ release name in response$/, "noRelease", []], [/^tag_regex did not match '([\s\S]+)'$/, "tagNoMatch", ["tag"]], [/^http error (\d+)$/, "httpError", ["code"]], [/^invalid JSON response: ([\s\S]+)$/, "invalidJson", ["detail"]], [/^json_path '([\s\S]+)' did not resolve$/, "jsonPathUnresolved", ["path"]], [/^empty value at json_path$/, "jsonEmpty", []], [/^no tags matched \(docker_hub\)$/, "dockerNoTags", []], [/^tag_regex matched only moving tags; use Docker image digest tracking$/, "dockerMovingOnly", []], [/^docker hub error (\d+)$/, "dockerHubError", ["code"]], [/^invalid docker hub JSON: ([\s\S]+)$/, "dockerHubJson", ["detail"]], [/^no tags returned by docker hub$/, "dockerHubEmpty", []], [/^payload must be a JSON object$/, "payload", []], [/^name is required and must be 1\-64 chars of letters\/digits\/spaces\/\._\+\-\(\)\/$/, "name", []], [/^installed_via must be one of: ([\s\S]+) or empty$/, "method", ["values"]], [/^package is required \(letters\/digits\/\._\+@:\/ up to 127 chars\)$/, "package", []], [/^file_path is required and must be an absolute path$/, "filePath", []], [/^file_regex is required$/, "fileRegex", []], [/^file_regex is not a valid regex: ([\s\S]+)$/, "fileRegexInvalid", ["detail"]], [/^binary_path is required and must be an absolute path$/, "binaryPath", []], [/^python_path is required and must be an absolute path$/, "pythonPath", []], [/^distribution is required \(PEP 503 name\)$/, "distribution", []], [/^container_name is required \(docker naming rules\)$/, "container", []], [/^label is required \(OCI label naming rules\)$/, "label", []], [/^binary_path is required \(absolute path or bare command\)$/, "dockerBinary", []], [/^installed_version is required for the manual method$/, "manualVersion", []], [/^installed_version exceeds (\d+) chars$/, "versionLength", ["max"]], [/^installed_version contains a forbidden control character$/, "versionControl", []], [/^installed_regex is not a valid regex: ([\s\S]+)$/, "installedRegexInvalid", ["detail"]], [/^upstream_type must be one of: ([\s\S]+)$/, "upstreamType", ["values"]], [/^tag_regex is not a valid regex: ([\s\S]+)$/, "tagRegexInvalid", ["detail"]], [/^repo is required for upstream_type=github$/, "repoRequired", []], [/^repo must be 'owner\/name'$/, "repoFormat", []], [/^github_source must be one of: ([\s\S]+)$/, "githubSource", ["values"]], [/^upstream_url is required for upstream_type=http_json$/, "upstreamUrlRequired", []], [/^upstream_url must be an http\(s\) URL$/, "upstreamUrl", []], [/^upstream_url exceeds (\d+) chars$/, "upstreamUrlLength", ["max"]], [/^upstream_json_path is required for upstream_type=http_json$/, "jsonPathRequired", []], [/^upstream_json_path exceeds (\d+) chars$/, "jsonPathLength", ["max"]], [/^upstream_json_path uses forbidden characters$/, "jsonPathCharacters", []], [/^docker_image is required for upstream_type=docker_hub$/, "dockerImageRequired", []], [/^docker_image exceeds (\d+) chars$/, "dockerImageLength", ["max"]], [/^docker_image must match Docker Hub naming \(owner\/name or name\)$/, "dockerImageFormat", []], [/^health_path must be a valid URL path \(max 255 chars\)$/, "healthPath", []], [/^logo_url must be an http\(s\) URL or this VM's cached local icon$/, "logoUrl", []], [/^invalid discovery_id$/, "discoveryId", []], [/^helper_slug must be a lowercase slug \(letters\/digits\/\._\-\)$/, "helperSlug", []], [/^update_via only accepts 'docker'$/, "updateVia", []], [/^update_via=docker requires a docker_label or docker_exec detector$/, "dockerDetector", []], [/^update_via=docker cannot be combined with ([\s\S]+)$/, "dockerConflict", ["field"]], [/^update_command must be a string$/, "updateCommandType", []], [/^update_command exceeds (\d+) chars$/, "updateCommandLength", ["max"]], [/^update_command contains a null byte$/, "updateCommandNull", []], [/^update_method must be none, helper or custom$/, "updateMethod", []], [/^update_command is required for update_method=custom$/, "updateCommandRequired", []], [/^update_command is only allowed for update_method=custom$/, "updateCommandOnlyCustom", []], [/^a supported helper_slug is required for update_method=helper$/, "helperRequired", []], [/^Docker\-delegated apps update through their image, not a separate app updater$/, "dockerUpdater", []], [/^command_argv is required \(non\-empty list\)$/, "commandRequired", []], [/^command_argv must be a non\-empty list of strings$/, "commandList", []], [/^command_argv accepts at most (\d+) entries$/, "commandCount", ["max"]], [/^command_argv\[(\d+)\] must be a non\-empty string$/, "commandEntry", ["index"]], [/^command_argv\[(\d+)\] exceeds (\d+) chars$/, "commandEntryLength", ["index", "max"]], [/^command_argv\[(\d+)\] contains a forbidden control character$/, "commandEntryControl", ["index"]], [/^binary_args must be a list of strings$/, "binaryArgsList", []], [/^binary_args accepts at most (\d+) entries$/, "binaryArgsCount", ["max"]], [/^binary_args\[(\d+)\] must be a non\-empty string$/, "binaryArgEntry", ["index"]], [/^binary_args\[(\d+)\] exceeds (\d+) chars$/, "binaryArgLength", ["index", "max"]], [/^binary_args\[(\d+)\] contains a forbidden control character$/, "binaryArgControl", ["index"]], [/^ports must be a list of \{port, description, scheme\}$/, "portsList", []], [/^ports\[(\d+)\] must be an object$/, "portObject", ["index"]], [/^ports\[(\d+)\]\.port is required$/, "portRequired", ["index"]], [/^ports\[(\d+)\]\.port must be an integer$/, "portInteger", ["index"]], [/^ports\[(\d+)\]\.port must be 1\-65535$/, "portRange", ["index"]], [/^port (\d+) appears more than once for this app$/, "portDuplicate", ["port"]], [/^ports\[(\d+)\]\.description has invalid characters$/, "portDescription", ["index"]], [/^ports\[(\d+)\]\.scheme must be 'http' or 'https'$/, "portScheme", ["index"]], [/^ports\[(\d+)\]\.web_path must be a valid URL path \(max 255 chars\)$/, "portPath", ["index"]], [/^ports\[(\d+)\]\.logo_url must be an http\(s\) URL \(max 512 chars\)$/, "portLogo", ["index"]], [/^ports\[(\d+)\]\.category has invalid characters or is too long$/, "portCategory", ["index"]], [/^ports\[(\d+)\]\.custom_url must be an http\(s\) URL \(max 512 chars\)$/, "portCustomUrl", ["index"]], [/^tag_regex exceeds 512 chars$/, "tagRegexLength", []], [/^could not persist sidecar \(permission\?\)$/, "persistPermission", []], [/^could not persist sidecar$/, "persist", []], [/^no apps registered for this vmid$/, "noApps", []], [/^app_id '([\s\S]+)' not found$/, "appMissing", ["appId"]], [/^app not found$/, "appNotFound", []], ] export function localizeAppEditorError(message: string | null | undefined, t: Translate): string { if (!message) return "" for (const [pattern, key, names] of RULES) { const match = pattern.exec(message) if (!match) continue const params = Object.fromEntries(names.map((name, index) => [name, match[index + 1]])) return t(`vmLxc.appEditor.errors.${key}`, params) } return message }