logsdx
Version:
log streaming with dx on the ðŸ§
1 lines • 20.8 kB
Source Map (JSON)
{"version":3,"sources":["/Users/jeff/code/oss/logsdx/dist/chunk-QBEC4U26.cjs","../src/cli/index.ts","../src/parsers/regex/line.ts","../src/parsers/json/index.ts","../src/parsers/default.ts","../src/parsers/registry.ts"],"names":[],"mappings":"AAAA;AACE;AACF,wDAA6B;AAC7B;AACE;AACA;AACA;AACF,wDAA6B;AAC7B;AACA;ACPA,sCAAwB;AACxB,gEAAe;ADSf;AACA;AEXO,SAAS,qBAAA,CAAsB,KAAA,EAAsC;AAC1E,EAAA,OAAO,CAAC,IAAA,EAAA,GAAS;AACf,IAAA,IAAI,MAAA;AAGJ,IAAA,IAAA,CAAA,MAAW,KAAA,GAAQ,KAAA,EAAO;AACxB,MAAA,MAAM,MAAA,EAAQ,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,KAAK,CAAA;AACnC,MAAA,GAAA,CAAI,KAAA,EAAO;AACT,QAAA,MAAM,UAAA,kBAAY,IAAA,mBAAK,OAAA,0BAAA,CAAU,IAAA,EAAM,KAAK,IAAA,GAAK,CAAC,CAAA;AAElD,QAAA,GAAA,CAAI,SAAA,CAAU,UAAA,GAAa,SAAA,CAAU,MAAA,GAAS,SAAA,CAAU,OAAA,EAAS;AAC/D,UAAA,OAAO,SAAA;AAAA,QACT;AAEA,QAAA,OAAA,EAAS;AAAA,UACP,GAAG,SAAA;AAAA,UACH,GAAG;AAAA;AAAA,QACL,CAAA;AAAA,MACF;AAAA,IACF;AAGA,IAAA,GAAA,CAAI,MAAA,EAAQ;AAEV,MAAA,GAAA,CAAI,CAAC,MAAA,CAAO,SAAA,EAAW;AACrB,QAAA,MAAM,eAAA,EAAiB,IAAA,CAAK,KAAA;AAAA,UAC1B;AAAA,QACF,CAAA;AACA,QAAA,GAAA,CAAI,cAAA,EAAgB;AAClB,UAAA,MAAA,CAAO,UAAA,EAAY,cAAA,CAAe,CAAC,CAAA;AAAA,QACrC;AAAA,MACF;AAGA,MAAA,GAAA,CAAI,CAAC,MAAA,CAAO,OAAA,EAAS;AAEnB,QAAA,MAAM,aAAA,EAAe,IAAA,CAAK,KAAA;AAAA,UACxB;AAAA,QACF,CAAA;AACA,QAAA,GAAA,CAAI,YAAA,EAAc;AAChB,UAAA,MAAA,CAAO,QAAA,EAAU,YAAA,CAAa,CAAC,CAAA;AAAA,QACjC;AAAA,MACF;AAEA,MAAA,OAAO,MAAA;AAAA,IACT;AAEA,IAAA,OAAO,KAAA,CAAA;AAAA,EACT,CAAA;AACF;AFEA;AACA;AGtDA;AACA,4EAAkB;AAUX,IAAM,mBAAA,EAAiC;AAAA,EAC5C;AAAA,IACE,KAAA,EAAO,UAAA;AAAA,IACP,IAAA,EAAM,MAAA;AAAA,IACN,KAAA,EAAO,MAAA;AAAA,IACP,IAAA,EAAM;AAAA,MACJ,OAAA,EAAS,SAAA;AAAA,MACT,SAAA,EAAW,WAAA;AAAA,MACX,OAAA,EAAS,SAAA;AAAA,MACT,KAAA,EAAO;AAAA,IACT;AAAA,EACF;AACF,CAAA;AAOO,SAAS,WAAA,CAAY,KAAA,EAAyB;AACnD,EAAA,MAAM,SAAA,EAAqC;AAAA;AAAA,IAEzC,KAAA,EAAO,OAAA;AAAA,IACP,IAAA,EAAM,MAAA;AAAA,IACN,IAAA,EAAM,MAAA;AAAA,IACN,OAAA,EAAS,MAAA;AAAA,IACT,KAAA,EAAO,OAAA;AAAA,IACP,GAAA,EAAK,OAAA;AAAA,IACL,OAAA,EAAS,SAAA;AAAA,IACT,KAAA,EAAO,OAAA;AAAA;AAAA,IAGP,KAAA,EAAO,OAAA;AAAA,IACP,QAAA,EAAU,OAAA;AAAA,IACV,MAAA,EAAQ,MAAA;AAAA,IACR,OAAA,EAAS,OAAA;AAAA,IACT,KAAA,EAAO,OAAA;AAAA;AAAA,IAGP,IAAA,EAAM,OAAA;AAAA,IACN,MAAA,EAAQ,OAAA;AAAA,IACR,OAAA,EAAS,OAAA;AAAA,IACT,OAAA,EAAS,MAAA;AAAA,IACT,WAAA,EAAa,MAAA;AAAA,IACb,SAAA,EAAW;AAAA,EACb,CAAA;AAEA,EAAA,MAAM,gBAAA,EAAkB,KAAA,CAAM,WAAA,CAAY,CAAA,CAAE,IAAA,CAAK,CAAA;AACjD,EAAA,OAAO,QAAA,CAAS,eAAe,EAAA,GAAK,MAAA;AACtC;AAOA,MAAA,SAAsB,aAAA,CAAc,QAAA,EAAwC;AAC1E,EAAA,IAAI,MAAA,EAAoB,kBAAA;AAGxB,EAAA,GAAA,CAAI,QAAA,EAAU;AACZ,IAAA,IAAI;AACF,MAAA,MAAM,KAAA,EAAO,YAAA,CAAG,YAAA,CAAa,QAAA,EAAU,OAAO,CAAA;AAC9C,MAAA,MAAM,YAAA,EAAc,eAAA,CAAM,KAAA,CAAM,IAAI,CAAA;AACpC,MAAA,GAAA,CAAI,KAAA,CAAM,OAAA,CAAQ,WAAW,EAAA,GAAK,WAAA,CAAY,OAAA,EAAS,CAAA,EAAG;AACxD,QAAA,MAAA,EAAQ,WAAA;AAAA,MACV;AAAA,IACF,EAAA,MAAA,CAAS,KAAA,EAAO;AACd,MAAA,wBAAA,CAAO,IAAA;AAAA,QACL,CAAA,kCAAA,EAAqC,eAAA,CAAM,SAAA,CAAU,KAAK,CAAC,CAAA;AAAA,MAAA;AAE7D,MAAA;AAA8C,IAAA;AAChD,EAAA;AAIF,EAAA;AACE,IAAA;AAEE,MAAA;AACE,QAAA;AAAO,MAAA;AAIT,MAAA;AAGA,MAAA;AAAgC,QAAA;AACvB,UAAA;AACyC,QAAA;AAChD,QAAA;AAE+D,QAAA;AACT,QAAA;AAC5C,MAAA;AAIZ,MAAA;AACE,QAAA;AACG,UAAA;AACC,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AAGF,UAAA;AAAc,QAAA;AAChB,MAAA;AAGF,MAAA;AAAO,IAAA;AAGP,MAAA;AAAO,IAAA;AACT,EAAA;AAEJ;AHgBA;AACA;AItJO;AACL,EAAA;AAEA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAEA,EAAA;AACF;AJsJA;AACA;AK7JO;AAAuB,EAAA;AACd,IAAA;AACZ,MAAA;AAEI,MAAA;AACmD,QAAA;AACjC,QAAA;AACU,QAAA;AACZ,MAAA;AAClB,IAAA;AACF,EAAA;AACF,EAAA;AACQ,IAAA;AACC,IAAA;AACD,IAAA;AACA,IAAA;AACC,EAAA;AAEX;AAGO;AAGH;AAAA,EAAA;AAEmB;AAAA,EAAA;AAInB,IAAA;AAAwD,EAAA;AAC1D,EAAA;AAGE,IAAA;AAAuC,EAAA;AAE3C;AAOO;AAIL,EAAA;AACF;AAQA;AAIE,EAAA;AACE,IAAA;AAAU,MAAA;AACgF,IAAA;AAC1F,EAAA;AAGF,EAAA;AACF;AAMO;AACL,EAAA;AACF;AL8HA;AACA;ACtMA;AAEA;AAKA;AAQG,EAAA;AACC,EAAA;AAEF;AAIE,EAAA;AAEE,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AAAc,IAAA;AAIhB,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACE,QAAA;AACA,QAAA;AAAqD,MAAA;AAEvD,MAAA;AAAc,IAAA;AAIhB,IAAA;AACE,MAAA;AAE6B,IAAA;AAI/B,IAAA;AACA,IAAA;AACE,MAAA;AACE,QAAA;AAEwC,MAAA;AAE1C,MAAA;AAAsB,IAAA;AAEtB,MAAA;AACE,QAAA;AAEmD,MAAA;AAErD,MAAA;AAAqB,IAAA;AAIvB,IAAA;AAIE,MAAA;AAAqD,IAAA;AAIvD,IAAA;AACA,IAAA;AACE,MAAA;AAEsC,IAAA;AAIxC,IAAA;AAGA,IAAA;AACE,MAAA;AAEgD,IAAA;AAIlD,IAAA;AAGA,IAAA;AACA,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AAEA,MAAA;AACE,QAAA;AAEA,QAAA;AAGA,QAAA;AAGA,QAAA;AAEE,UAAA;AAAmC,YAAA;AACjC,YAAA;AACA,YAAA;AACkB,UAAA;AAIpB,UAAA;AACE,YAAA;AAAyC,UAAA;AAEzC,YAAA;AAA2D,UAAA;AAC7D,QAAA;AACF,MAAA;AACF,IAAA;AAGF,IAAA;AACE,MAAA;AACE,QAAA;AACA,QAAA;AAEA,QAAA;AAEE,UAAA;AAAmC,YAAA;AACjC,YAAA;AACA,YAAA;AACkB,UAAA;AAIpB,UAAA;AACE,YAAA;AAAyC,UAAA;AAEzC,YAAA;AAA2D,UAAA;AAC7D,QAAA;AACF,MAAA;AAEF,MAAA;AACE,QAAA;AAAqC,MAAA;AACvC,IAAA;AAGF,IAAA;AACE,MAAA;AACA,MAAA;AAAc,IAAA;AACf,EAAA;AAED,IAAA;AACA,IAAA;AAAc,EAAA;AAElB;AAEF;AAGA;AACE,EAAA;AAEA,EAAA;AACE,IAAA;AACE,MAAA;AAEsC,IAAA;AAGxC,IAAA;AAAmC,MAAA;AACd,IAAA;AACpB,EAAA;AAED,IAAA;AACA,IAAA;AACA,IAAA;AAAc,EAAA;AAElB;AAEA;AAIE,EAAA;AAGA,EAAA;AAAgD,IAAA;AACvC,IAAA;AACD,IAAA;AACA,IAAA;AACC,IAAA;AACE,IAAA;AACF,EAAA;AAGT,EAAA;AACA,EAAA;AACA,EAAA;AACF;AD2HA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/Users/jeff/code/oss/logsdx/dist/chunk-QBEC4U26.cjs","sourcesContent":[null,"#!/usr/bin/env node\n\nimport { Command } from \"commander\";\nimport fs from \"fs\";\nimport { styleManager, setTheme } from \"@/src/themes/asci/styles\";\nimport { logger } from \"@/src/utils/logger\";\nimport type { LogLevel, CliOptions } from \"@/src/types\";\nimport { getParser, getRegisteredParsers } from \"@/src/parsers/registry\";\nimport { loadConfig } from \"@/src/themes/asci/loader\";\n\nconst program = new Command();\n\nprogram\n .name(\"logsdx\")\n .description(\"A powerful log parsing and formatting tool\")\n .version(\"0.0.1\");\n\nprogram\n .option(\"-q, --quiet\", \"Suppress all output except errors\")\n .option(\"-d, --debug\", \"Enable debug mode\")\n .option(\"-l, --level <level>\", \"Minimum log level to display\", \"info\")\n .option(\"-p, --parser <parser>\", \"Parser to use for log parsing\", \"default\")\n .option(\"-r, --rules <file>\", \"Path to custom rules file\")\n .option(\"-o, --output <file>\", \"Path to output file\")\n .option(\"--list-parsers\", \"List available parsers\")\n .option(\n \"-t, --theme <theme>\",\n \"Theme to use (default, dark, light, minimal, or custom theme name)\",\n )\n .option(\"--list-themes\", \"List available themes\")\n .argument(\"[input]\", \"Input file to process (defaults to stdin)\")\n .action(async (input: string, options: CliOptions) => {\n try {\n // Handle list parsers command\n if (options.listParsers) {\n const parsers = getRegisteredParsers();\n logger.info(\"Available parsers:\");\n parsers.forEach((parser) => logger.info(` - ${parser}`));\n process.exit(0);\n }\n\n // Handle list themes command\n if (options.listThemes) {\n const config = loadConfig();\n const themes = config ? Object.keys(config.customThemes || {}) : [];\n logger.info(\"Available themes:\");\n logger.info(\"Built-in themes:\");\n logger.info(\" - default\");\n logger.info(\" - dark\");\n logger.info(\" - light\");\n logger.info(\" - minimal\");\n if (themes.length > 0) {\n logger.info(\"Custom themes:\");\n themes.forEach((theme) => logger.info(` - ${theme}`));\n }\n process.exit(0);\n }\n\n // Set up debug logging\n if (options.debug) {\n logger\n .withConfig({ level: \"debug\", prefix: \"CLI\" })\n .debug(\"Debug mode enabled\");\n }\n\n // Load and apply theme\n const config = loadConfig();\n if (options.theme) {\n if (options.debug) {\n logger\n .withConfig({ level: \"debug\", prefix: \"CLI\" })\n .debug(`Using theme: ${options.theme}`);\n }\n setTheme(options.theme);\n } else if (config?.theme) {\n if (options.debug) {\n logger\n .withConfig({ level: \"debug\", prefix: \"CLI\" })\n .debug(`Using theme from config: ${config.theme}`);\n }\n setTheme(config.theme);\n }\n\n // Validate log level\n if (\n options.level &&\n ![\"debug\", \"info\", \"warn\", \"error\"].includes(options.level)\n ) {\n throw new Error(`Invalid log level: ${options.level}`);\n }\n\n // Create input stream\n const inputStream = input ? fs.createReadStream(input) : process.stdin;\n if (input && options.debug) {\n logger\n .withConfig({ level: \"debug\", prefix: \"CLI\" })\n .debug(`Input file set to: ${input}`);\n }\n\n // Create output stream\n const outputStream = options.output\n ? fs.createWriteStream(options.output)\n : process.stdout;\n if (options.output && options.debug) {\n logger\n .withConfig({ level: \"debug\", prefix: \"CLI\" })\n .debug(`Output file set to: ${options.output}`);\n }\n\n // Get parser\n const parser = await getParserForOptions(options);\n\n // Process input\n let buffer = \"\";\n inputStream.on(\"data\", (chunk: Buffer | string) => {\n buffer += chunk.toString();\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() || \"\";\n\n for (const line of lines) {\n if (!line.trim()) continue;\n\n const parsed = parser(line);\n\n // Determine the level for filtering, even if parsing failed\n const level = parsed?.level || \"info\"; // Default to 'info' if undefined\n\n // Check if the line should be rendered based on level\n if (shouldRender(level, options.level as LogLevel)) {\n // Always call styleLine, passing the original line, parsed result (can be undefined), and parser name\n const formattedLine = styleManager.styleLine(\n line,\n parsed,\n options.parser || \"default\",\n );\n\n // Use process.stdout.write for stdout, write for file streams\n if (outputStream === process.stdout) {\n process.stdout.write(formattedLine + \"\\n\");\n } else {\n (outputStream as fs.WriteStream).write(formattedLine + \"\\n\");\n }\n }\n }\n });\n\n inputStream.on(\"end\", () => {\n if (buffer.trim()) {\n const parsed = parser(buffer);\n const level = parsed?.level || \"info\";\n\n if (shouldRender(level, options.level as LogLevel)) {\n // Always call styleLine for the remaining buffer\n const formattedLine = styleManager.styleLine(\n buffer,\n parsed,\n options.parser || \"default\",\n );\n\n // Use process.stdout.write for stdout, write for file streams\n if (outputStream === process.stdout) {\n process.stdout.write(formattedLine + \"\\n\");\n } else {\n (outputStream as fs.WriteStream).write(formattedLine + \"\\n\");\n }\n }\n }\n if (options.output) {\n (outputStream as fs.WriteStream).end();\n }\n });\n\n inputStream.on(\"error\", (error: Error) => {\n logger.error(\"Error reading input:\", error);\n process.exit(1);\n });\n } catch (error) {\n logger.error(\"Error:\", error as Error);\n process.exit(1);\n }\n });\n\nprogram.parse();\n\n// Helper function to get parser for options\nasync function getParserForOptions(options: CliOptions) {\n const parserName = options.parser || \"default\";\n\n try {\n if (options.debug) {\n logger\n .withConfig({ level: \"debug\", prefix: \"CLI\" })\n .debug(`Using parser: ${parserName}`);\n }\n\n return await getParser(parserName, {\n rulesFile: options.rules,\n });\n } catch (error) {\n logger.error(`Error loading parser '${parserName}':`, error as Error);\n logger.info(`Available parsers: ${getRegisteredParsers().join(\", \")}`);\n process.exit(1);\n }\n}\n\nfunction shouldRender(\n level: string | undefined,\n minLevel: LogLevel | undefined,\n): boolean {\n if (!minLevel || !level) return true;\n\n // Define log level priorities\n const levelPriorities: Record<string, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n success: 1,\n trace: 0,\n };\n\n const current = levelPriorities[level] ?? 0;\n const min = levelPriorities[minLevel] ?? 0;\n return current >= min;\n}\n\nexport { program, getParserForOptions, shouldRender };\n","import { type RegexParserRule, type LineParser } from \"@/src/types\";\n\nexport function createRegexLineParser(rules: RegexParserRule[]): LineParser {\n return (line) => {\n let result: Record<string, any> | undefined;\n\n // First, try to match the full log format\n for (const rule of rules) {\n const match = line.match(rule.match);\n if (match) {\n const extracted = rule.extract?.(line, match) || {};\n // If we got a full match with timestamp, level, and message, return it\n if (extracted.timestamp && extracted.level && extracted.message) {\n return extracted;\n }\n // Otherwise, store the partial result, preserving existing values\n result = {\n ...extracted,\n ...result, // This ensures earlier matches take precedence\n };\n }\n }\n\n // If we have a partial result\n if (result) {\n // Try to extract timestamp if not already present\n if (!result.timestamp) {\n const timestampMatch = line.match(\n /(\\d{4}-\\d{2}-\\d{2}(?:T|\\s+)\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:?\\d{2})?)/,\n );\n if (timestampMatch) {\n result.timestamp = timestampMatch[1];\n }\n }\n\n // Try to extract message if not already present\n if (!result.message) {\n // Look for message after timestamp and level markers\n const messageMatch = line.match(\n /(?:\\]|\\d{2}(?:Z|[+-]\\d{2}:?\\d{2})?)\\s+(?:\\[\\w+\\])?\\s+(.+)$/,\n );\n if (messageMatch) {\n result.message = messageMatch[1];\n }\n }\n\n return result;\n }\n\n return undefined;\n };\n}\n","import fs from \"fs\";\nimport JSON5 from \"json5\";\nimport {\n type LineParser,\n type LineParseResult,\n type JSONRule,\n type LogLevel,\n} from \"@/src/types\";\nimport { logger } from \"@/src/utils/logger\";\n\n// Default rules for JSON parsing\nexport const DEFAULT_JSON_RULES: JSONRule[] = [\n {\n match: \"\\\\{.*\\\\}\",\n lang: \"json\",\n level: \"info\",\n meta: {\n service: \"service\",\n timestamp: \"timestamp\",\n message: \"message\",\n level: \"level\",\n },\n },\n];\n\n/**\n * Maps various log level formats to our standard log levels\n * @param level The log level to map\n * @returns A standardized log level\n */\nexport function mapLogLevel(level: string): LogLevel {\n const levelMap: Record<string, LogLevel> = {\n // Standard levels\n debug: \"debug\",\n info: \"info\",\n warn: \"warn\",\n warning: \"warn\",\n error: \"error\",\n err: \"error\",\n success: \"success\",\n trace: \"trace\",\n\n // Common variations\n fatal: \"error\",\n critical: \"error\",\n notice: \"info\",\n verbose: \"debug\",\n silly: \"debug\",\n\n // Status-based levels\n fail: \"error\",\n failed: \"error\",\n failure: \"error\",\n pending: \"warn\",\n in_progress: \"info\",\n completed: \"success\",\n };\n\n const normalizedLevel = level.toLowerCase().trim();\n return levelMap[normalizedLevel] || \"info\";\n}\n\n/**\n * Loads JSON rules from a file or uses default rules\n * @param filePath Optional path to a custom rules file\n * @returns A line parser function\n */\nexport async function loadJsonRules(filePath?: string): Promise<LineParser> {\n let rules: JSONRule[] = DEFAULT_JSON_RULES;\n\n // If a file path is provided, try to load custom rules\n if (filePath) {\n try {\n const data = fs.readFileSync(filePath, \"utf-8\");\n const customRules = JSON5.parse(data);\n if (Array.isArray(customRules) && customRules.length > 0) {\n rules = customRules;\n }\n } catch (error) {\n logger.warn(\n `Failed to load custom JSON rules: ${JSON5.stringify(error)}`,\n );\n logger.info(\"Using default JSON rules instead\");\n }\n }\n\n // Create a direct JSON parser using JSON5\n return (line: string): LineParseResult | undefined => {\n try {\n // Check if the line looks like JSON\n if (!line.trim().startsWith(\"{\") || !line.trim().endsWith(\"}\")) {\n return undefined;\n }\n\n // Parse the JSON using JSON5\n const json = JSON5.parse(line);\n\n // Extract common log fields\n const result: LineParseResult = {\n level: mapLogLevel(\n json.level || json.status || json.severity || \"info\",\n ),\n timestamp:\n json.timestamp || json.time || json.date || json[\"@timestamp\"],\n message: json.message || json.msg || json.log || json.text,\n language: \"json\",\n };\n\n // Add all other fields as metadata\n for (const [key, value] of Object.entries(json)) {\n if (\n ![\n \"level\",\n \"status\",\n \"severity\",\n \"timestamp\",\n \"time\",\n \"date\",\n \"@timestamp\",\n \"message\",\n \"msg\",\n \"log\",\n \"text\",\n ].includes(key)\n ) {\n result[key] = value;\n }\n }\n\n return result;\n } catch (error) {\n // If parsing fails, return undefined to let other parsers handle it\n return undefined;\n }\n };\n}\n","import { type LineParseResult, type LineParser } from \"../types\";\n\nexport const defaultLineParser: LineParser = (line) => {\n const result: LineParseResult = {};\n\n if (line.startsWith(\"[json]\")) result.lang = \"json\";\n if (line.startsWith(\"[sql]\")) result.lang = \"sql\";\n if (line.includes(\"ERROR\")) result.level = \"error\";\n if (line.includes(\"WARN\")) result.level = \"warn\";\n if (line.includes(\"INFO\")) result.level = \"info\";\n\n return result;\n};\n","import { type LineParser } from \"@/src/types\";\nimport { createRegexLineParser } from \"@/src/parsers/regex/line\";\nimport { loadJsonRules } from \"@/src/parsers/json\";\nimport { defaultLineParser } from \"@/src/parsers/default\";\n\n// Default configuration for regex parser\nexport const DEFAULT_CONFIG = {\n defaultRules: [\n {\n match:\n /^(\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}.\\d{3}Z)\\s+\\[(\\w+)\\]\\s+(.*)$/,\n extract: (line: string, match: RegExpMatchArray) => ({\n timestamp: match[1],\n level: match[2].toLowerCase() as any,\n message: match[3],\n }),\n },\n ],\n levels: {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n },\n};\n\n// Registry to store available parsers\nexport const parserRegistry: Record<\n string,\n (options?: any) => Promise<LineParser>\n> = {\n // Default parser\n default: async () => defaultLineParser,\n\n // Built-in parsers\n regex: async () => {\n return createRegexLineParser(DEFAULT_CONFIG.defaultRules);\n },\n\n json: async (options?: { rulesFile?: string }) => {\n return loadJsonRules(options?.rulesFile);\n },\n};\n\n/**\n * Register a new parser\n * @param name The name of the parser\n * @param factory The factory function that creates the parser\n */\nexport function registerParser(\n name: string,\n factory: (options?: any) => Promise<LineParser>,\n): void {\n parserRegistry[name] = factory;\n}\n\n/**\n * Get a parser by name\n * @param name The name of the parser\n * @param options Options to pass to the parser factory\n * @returns The parser function\n */\nexport async function getParser(\n name: string,\n options?: any,\n): Promise<LineParser> {\n if (!parserRegistry[name]) {\n throw new Error(\n `Parser '${name}' not found. Available parsers: ${Object.keys(parserRegistry).join(\", \")}`,\n );\n }\n\n return parserRegistry[name](options);\n}\n\n/**\n * Get all registered parser names\n * @returns Array of parser names\n */\nexport function getRegisteredParsers(): string[] {\n return Object.keys(parserRegistry);\n}\n"]}