import type { ConnectionConfig, DatabaseInfo, TreeNode } from "@/types/database"; import { DEFAULT_DATABASE_TREE_LABEL } from "@/lib/sidebar/treeNodeContext"; const sidebarNameCollator = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" }); export function shouldIncludeDefaultDatabaseNode(connection: Pick | undefined, databases: DatabaseInfo[]): boolean { return connection?.db_type === "mysql" && databases.some((database) => !database.name.trim()); } export function compareSidebarNames(left: string, right: string): number { return sidebarNameCollator.compare(left, right); } export function sortSidebarNames(names: readonly string[]): string[] { return [...names].sort(compareSidebarNames); } export function sortSidebarDatabases(databases: readonly DatabaseInfo[]): DatabaseInfo[] { return [...databases].sort((left, right) => sidebarNameCollator.compare(left.name, right.name)); } export function buildDatabaseTreeNodes( connectionId: string, databases: DatabaseInfo[], // `displayLabel` only rewrites the visible text. `id` and `database` keep the // backend-reported name because that value round-trips to the agent — Cloud // Spanner reports the full `projects/../instances/../databases/..` resource // path and would break URL building if the shortened label were sent back. options: { includeDefaultWhenEmpty?: boolean; displayLabel?: (name: string) => string } = {}, ): TreeNode[] { const nodes = sortSidebarDatabases(databases).flatMap((db) => { const name = db.name; if (!name.trim()) return []; return [ { id: `${connectionId}:${name}`, label: options.displayLabel?.(name) || name, type: "database" as const, connectionId, database: name, isExpanded: false, children: [], }, ]; }); if (nodes.length > 0 || !options.includeDefaultWhenEmpty) return nodes; return [ { id: `${connectionId}:`, label: DEFAULT_DATABASE_TREE_LABEL, type: "database" as const, connectionId, database: "", isExpanded: false, children: [], }, ]; } export function buildDuckDbConnectionTreeNodes(connectionId: string, databases: DatabaseInfo[], primarySchemas: string[]): TreeNode[] { const schemaNodes = sortSidebarNames(primarySchemas).flatMap((schema) => { const name = schema.trim(); if (!name) return []; return [ { id: `${connectionId}:main:${name}`, label: name, type: "schema" as const, connectionId, database: "main", schema: name, isExpanded: false, children: [], }, ]; }); const attachedCatalogNodes = sortSidebarDatabases(databases).flatMap((db) => { const name = db.name; if (!name.trim() || name === "main") return []; return [ { id: `${connectionId}:${name}`, label: name, type: "database" as const, connectionId, database: name, isExpanded: false, children: [], }, ]; }); return [...schemaNodes, ...attachedCatalogNodes]; }