/** * TOML Utility Functions * * Provides TOML parsing and serialization capabilities for OpenFang configuration files. * Supports environment variable interpolation in the format ${VAR_NAME}. * * @module toml-utils */ import TOML from 'smol-toml'; /** * Error class for TOML parsing errors */ export class TomlParseError extends Error { constructor( message: string, public readonly cause?: unknown ) { super(message); this.name = 'TomlParseError'; } } /** * Error class for TOML serialization errors */ export class TomlStringifyError extends Error { constructor( message: string, public readonly cause?: unknown ) { super(message); this.name = 'TomlStringifyError'; } } /** * TOML utility functions for parsing and serializing configuration files */ export const tomlUtils = { /** * Parse a TOML string into a JavaScript object * * @param content - The TOML string to parse * @returns The parsed JavaScript object * @throws TomlParseError if the TOML content is invalid * * @example * ```typescript * const config = tomlUtils.parse(` * [server] * host = "127.0.0.1" * port = 4200 * `); * // config = { server: { host: "127.0.0.1", port: 4200 } } * ``` */ parse: >(content: string): T => { try { return TOML.parse(content) as T; } catch (error) { console.error('[TOML] Parse error:', error); throw new TomlParseError( `TOML parse error: ${error instanceof Error ? error.message : String(error)}`, error ); } }, /** * Serialize a JavaScript object to a TOML string * * @param data - The JavaScript object to serialize * @returns The TOML string representation * @throws TomlStringifyError if the object cannot be serialized to TOML * * @example * ```typescript * const toml = tomlUtils.stringify({ * server: { host: "127.0.0.1", port: 4200 } * }); * ``` */ stringify: (data: Record): string => { try { return TOML.stringify(data); } catch (error) { console.error('[TOML] Stringify error:', error); throw new TomlStringifyError( `TOML stringify error: ${error instanceof Error ? error.message : String(error)}`, error ); } }, /** * Resolve environment variables in TOML content * * Replaces ${VAR_NAME} patterns with the corresponding environment variable values. * If the environment variable is not set, it's replaced with an empty string. * * Note: In browser/Tauri context, this function has limited access to environment * variables. For full resolution, use the Tauri backend to read env vars. * * @param content - The TOML content with potential ${VAR_NAME} patterns * @param envVars - Optional object containing environment variables (for testing or Tauri-provided values) * @returns The content with environment variables resolved * * @example * ```typescript * const content = 'api_key = "${OPENAI_API_KEY}"'; * const resolved = tomlUtils.resolveEnvVars(content, { OPENAI_API_KEY: 'sk-...' }); * // resolved = 'api_key = "sk-..."' * ``` */ resolveEnvVars: ( content: string, envVars?: Record ): string => { return content.replace(/\$\{([^}]+)\}/g, (_, varName: string) => { // If envVars provided, use them; otherwise try to access from window or return empty if (envVars) { return envVars[varName] ?? ''; } // In browser context, we can't access process.env directly // This will be handled by passing envVars from Tauri backend console.warn( `[TOML] Environment variable ${varName} not resolved - no envVars provided` ); return ''; }); }, /** * Parse TOML content with environment variable resolution * * Convenience method that combines resolveEnvVars and parse. * * @param content - The TOML content with potential ${VAR_NAME} patterns * @param envVars - Optional object containing environment variables * @returns The parsed and resolved JavaScript object * * @example * ```typescript * const config = tomlUtils.parseWithEnvVars(tomlContent, { * ZHIPU_API_KEY: 'your-api-key' * }); * ``` */ parseWithEnvVars: >( content: string, envVars?: Record ): T => { const resolved = tomlUtils.resolveEnvVars(content, envVars); return tomlUtils.parse(resolved); }, /** * Check if a string contains unresolved environment variable placeholders * * @param content - The content to check * @returns true if there are unresolved ${VAR_NAME} patterns */ hasUnresolvedEnvVars: (content: string): boolean => { return /\$\{[^}]+\}/.test(content); }, /** * Extract environment variable names from TOML content * * @param content - The TOML content to scan * @returns Array of environment variable names found */ extractEnvVarNames: (content: string): string[] => { const matches = content.matchAll(/\$\{([^}]+)\}/g); const names = new Set(); for (const match of matches) { names.add(match[1]); } return Array.from(names); }, }; export default tomlUtils;