/** * Shared utilities for the Settings page. * Centralises constants, URL helpers, normalisation functions and validation * helpers that are consumed by +page.svelte and EnvironmentsTab.svelte. */ // --------------------------------------------------------------------------- // Constants // --------------------------------------------------------------------------- export const SETTINGS_TABS = [ "environments", "connections", "logging", "git", "llm", "migration", "storage", "features", "reports", "automation", "languages", "system", ] as const; export type SettingsTab = typeof SETTINGS_TABS[number]; interface LlmPrompts { documentation_prompt: string; git_commit_prompt: string; } interface LlmProviderBindings { documentation: string; git_commit: string; } interface LlmSettings { prompts?: Partial; provider_bindings?: Partial; [key: string]: unknown; } export const DEFAULT_LLM_PROMPTS: LlmPrompts = { documentation_prompt: 'Generate professional documentation for the following dataset and its columns.\nDataset: {dataset_name}\nColumns: {columns_json}\n\nProvide the documentation in JSON format:\n{\n \"dataset_description\": \"General description of the dataset\",\n \"column_descriptions\": [\n {\n \"name\": \"column_name\",\n \"description\": \"Generated description\"\n }\n ]\n}', git_commit_prompt: 'Generate a concise and professional git commit message based on the following diff and recent history.\nUse Conventional Commits format (e.g., feat: ..., fix: ..., docs: ...).\n\nRecent History:\n{history}\n\nDiff:\n{diff}\n\nCommit Message:', }; export const DEFAULT_LLM_PROVIDER_BINDINGS: LlmProviderBindings = { documentation: "", git_commit: "", }; // --------------------------------------------------------------------------- // URL tab helpers // --------------------------------------------------------------------------- /** * Read the current settings tab from the URL hash fragment. * Falls back to "environments" when the hash is missing or invalid. */ export function readTabFromUrl(): string { const hash = typeof window !== "undefined" ? window.location.hash : ""; const tab = hash.replace(/^#\/?settings\//, "") || ""; return normalizeTab(tab) || "environments"; } /** * Write the active settings tab to the URL hash fragment so it survives * browser refreshes and can be linked to directly. */ export function writeTabToUrl(tab: string): void { if (typeof window === "undefined") return; const normalized = normalizeTab(tab); if (normalized) { window.location.hash = `/settings/${normalized}`; } } /** * Normalise an arbitrary tab identifier to a valid tab key, or return null * when the identifier does not match any known tab. */ export function normalizeTab(tab: string): string | null { if (!tab || typeof tab !== "string") return null; const candidate = tab.trim().toLowerCase(); return SETTINGS_TABS.includes(candidate as SettingsTab) ? candidate : null; } // --------------------------------------------------------------------------- // LLM helpers // --------------------------------------------------------------------------- /** * Merge incoming LLM settings with the default prompts and bindings so that * individual fields are never accidentally set to `undefined`. */ export function normalizeLlmSettings(llm: LlmSettings | null | undefined): LlmSettings { if (!llm || typeof llm !== "object") { return { prompts: { ...DEFAULT_LLM_PROMPTS }, provider_bindings: { ...DEFAULT_LLM_PROVIDER_BINDINGS }, }; } return { ...llm, prompts: { ...DEFAULT_LLM_PROMPTS, ...llm.prompts }, provider_bindings: { ...DEFAULT_LLM_PROVIDER_BINDINGS, ...llm.provider_bindings, }, }; } // --------------------------------------------------------------------------- // Environment helpers // --------------------------------------------------------------------------- /** * Strip trailing slashes and the `/api/v1` suffix from a Superset base URL, * auto-prepend https:// if no scheme is present, * returning a clean base URL suitable for constructing dashboard links. */ export function normalizeSupersetBaseUrl(rawUrl: string): string | null { let baseUrl = String(rawUrl || "").trim().replace(/\/+$/, ""); if (!baseUrl) return null; if (baseUrl.endsWith("/api/v1")) { baseUrl = baseUrl.slice(0, -"/api/v1".length); } // Auto-prepend https:// if no scheme is present if (baseUrl && !/^https?:\/\//i.test(baseUrl)) { baseUrl = `https://${baseUrl}`; } return baseUrl; } /** * Classify an environment's deployment stage from its `name` or `url`. * Returns one of: `"PROD"`, `"PREPROD"`, or `"DEV"`. */ export function resolveEnvStage(env: { name?: string; url?: string } | null | undefined): string { if (!env) return "DEV"; const needle = ((env.name || "") + " " + (env.url || "")).toLowerCase(); if (/prod(uction)?/.test(needle) && !/preprod/.test(needle)) return "PROD"; if (/preprod|staging|stage/.test(needle)) return "PREPROD"; return "DEV"; } // --------------------------------------------------------------------------- // Connection draft helper // ---------------------------------------------------------------------------