{"version":3,"sources":["../../src/log.ts"],"sourcesContent":["import readline from 'readline';\nimport { type IntlayerConfig, getConfiguration } from '@intlayer/config';\nimport { sortAlphabetically } from './utils';\n\nexport type State = {\n  type: 'local' | 'distant';\n  status: 'pending' | 'fetching' | 'fetched' | 'error' | 'imported' | 'built';\n  icon?: string;\n  error?: Error;\n  errorMessage?: string;\n  spinnerFrameIndex?: number;\n};\n\nexport type DictionariesStatus = {\n  dictionaryKey: string;\n  state: State[];\n};\n\nconst LINE_DETECTOR = '\\u200B\\u200B\\u200B'; // Three zero-width spaces\nconst SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];\n\n// ANSI color codes\nconst RESET = '\\x1b[0m';\nconst GREEN = '\\x1b[32m';\nconst RED = '\\x1b[31m';\nconst BLUE = '\\x1b[34m';\nconst GREY = '\\x1b[90m';\nconst GREY_DARK = '\\x1b[90m';\n\nclass Logger {\n  private dictionariesStatuses: DictionariesStatus[] = [];\n  private spinnerTimer: NodeJS.Timeout | null = null;\n  private maxDictionaryKeyLength: number = 0;\n  private previousLineCount: number = 0;\n  private lineDifCounter: number = 0;\n  private originalStdoutWrite:\n    | ((\n        chunk: string | Uint8Array, // `chunk` can be either a string or a Uint8Array\n        encoding?: BufferEncoding, // `encoding` is optional and should be a BufferEncoding\n        callback?: (err?: Error | null) => void // `callback` is optional and a function\n      ) => boolean)\n    | null = null;\n  private extraLines: number = 0;\n  private isUpdating: boolean = false;\n  private config?: IntlayerConfig;\n\n  // Singleton instance\n  private static instance: Logger;\n\n  private constructor() {}\n\n  public static getInstance(): Logger {\n    if (!Logger.instance) {\n      Logger.instance = new Logger();\n    }\n    return Logger.instance;\n  }\n\n  public init(\n    localDictionariesKeys: string[],\n    distantDictionariesKeys: string[],\n    configuration: IntlayerConfig = getConfiguration()\n  ) {\n    this.config = configuration;\n\n    const allKeys = Array.from(\n      new Set(\n        [...localDictionariesKeys, ...distantDictionariesKeys].sort(\n          sortAlphabetically\n        )\n      )\n    );\n\n    this.maxDictionaryKeyLength = allKeys.reduce(\n      (max, key) => (key.length > max ? key.length : max),\n      0\n    );\n\n    this.dictionariesStatuses = allKeys.map((dictionaryKey) => {\n      const states: State[] = [];\n\n      if (localDictionariesKeys.includes(dictionaryKey)) {\n        states.push({\n          type: 'local',\n          status: 'pending',\n          spinnerFrameIndex: 0,\n        });\n      }\n      if (distantDictionariesKeys.includes(dictionaryKey)) {\n        states.push({\n          type: 'distant',\n          status: 'pending',\n          spinnerFrameIndex: 0,\n        });\n      }\n\n      return {\n        dictionaryKey,\n        state: states,\n      };\n    });\n\n    // Start spinner timer\n    this.startSpinner();\n\n    // Update all status lines (this will output the initial statuses)\n    this.updateAllStatusLines();\n  }\n\n  // New method to add dictionary keys after initialization\n  public addDictionaryKeys(\n    type: 'local' | 'distant',\n    dictionaryKeys: string[]\n  ) {\n    for (const dictionaryKey of dictionaryKeys) {\n      // Update maxDictionaryKeyLength if the new key is longer\n      if (dictionaryKey.length > this.maxDictionaryKeyLength) {\n        this.maxDictionaryKeyLength = dictionaryKey.length;\n      }\n\n      let statusObj = this.dictionariesStatuses.find(\n        (ds) => ds.dictionaryKey === dictionaryKey\n      );\n\n      if (!statusObj) {\n        // If the dictionaryKey doesn't exist yet, create a new DictionariesStatus\n\n        statusObj = {\n          dictionaryKey,\n          state: [],\n        };\n        this.dictionariesStatuses.push(statusObj);\n\n        const newState: State = {\n          type,\n          status: 'pending',\n          spinnerFrameIndex: 0,\n        };\n        statusObj.state.push(newState);\n      } else {\n        const existingState = statusObj.state.find((s) => s.type === type);\n        if (!existingState) {\n          // Add new state for the type\n\n          const newState: State = {\n            type,\n            status: 'pending',\n            spinnerFrameIndex: 0,\n          };\n          statusObj.state.push(newState);\n        }\n      }\n    }\n\n    // Call updateAllStatusLines() to refresh the output\n    this.updateAllStatusLines();\n  }\n\n  private startSpinner() {\n    if (!this.spinnerTimer) {\n      this.spinnerTimer = setInterval(() => {\n        this.updateAllStatusLines();\n      }, 100); // Update every 100ms\n    }\n  }\n\n  private stopSpinner() {\n    if (this.spinnerTimer) {\n      clearInterval(this.spinnerTimer);\n      this.spinnerTimer = null;\n    }\n  }\n\n  // Method to update the terminal output\n  private updateOutput(content: string) {\n    if (this.config?.log.mode !== 'verbose') return;\n\n    // Monkey-patch process.stdout.write to keep track of extra lines\n    if (!this.originalStdoutWrite) {\n      this.originalStdoutWrite = process.stdout.write.bind(process.stdout);\n      this.extraLines = 0;\n\n      const write = (\n        chunk: string | Uint8Array, // `chunk` can be either a string or a Uint8Array\n        encoding?: BufferEncoding, // `encoding` is optional and should be a BufferEncoding\n        callback?: (err?: Error | null) => void // `callback` is optional and a function\n      ) => {\n        const str = typeof chunk === 'string' ? chunk : chunk.toString();\n        const newLines = (str.match(/\\n/g) ?? []).length;\n\n        // If the write is not initiated by Logger's updateOutput method\n        if (!this.isUpdating) {\n          this.extraLines += newLines;\n        }\n\n        return this.originalStdoutWrite!(chunk, encoding, callback);\n      };\n\n      process.stdout.write = write as typeof process.stdout.write;\n    }\n\n    // Set a flag to indicate that updateOutput is running\n    this.isUpdating = true;\n\n    // Adjust lineDifCounter if LINE_DETECTOR is not the first line\n    const contentLines = content.split('\\n');\n    const indexOfLineDetector = contentLines.indexOf(LINE_DETECTOR.trim());\n\n    if (indexOfLineDetector > 0) {\n      // LINE_DETECTOR is not at the first line\n      this.lineDifCounter = indexOfLineDetector;\n    } else {\n      this.lineDifCounter = 0;\n    }\n\n    // Calculate total lines to move up\n    const totalLinesToMoveUp =\n      this.previousLineCount + this.lineDifCounter + this.extraLines;\n\n    // Move cursor up by totalLinesToMoveUp\n    readline.moveCursor(process.stdout, 0, -totalLinesToMoveUp);\n\n    // Clear all lines downwards\n    readline.clearScreenDown(process.stdout);\n\n    // Write the updated content\n    contentLines.forEach((line) => {\n      process.stdout.write(`${line}\\x1b[K\\n`);\n    });\n\n    // Update previousLineCount\n    this.previousLineCount = contentLines.length;\n\n    // Reset extraLines counter and updating flag\n    this.extraLines = 0;\n    this.isUpdating = false;\n  }\n\n  public stop() {\n    this.stopSpinner();\n  }\n\n  public updateStatus(\n    dictionaries: {\n      dictionaryKey: string;\n      type: 'local' | 'distant';\n      status: Partial<State>;\n    }[]\n  ) {\n    for (const { dictionaryKey, type, status } of dictionaries) {\n      const statusObj = this.dictionariesStatuses.find(\n        (ds) => ds.dictionaryKey === dictionaryKey\n      );\n      if (statusObj) {\n        const state = statusObj.state.find((s) => s.type === type);\n        if (state) {\n          // Update existing state\n          Object.assign(state, status);\n        } else {\n          // If the state for this type doesn't exist yet, add it\n          if (status.status === undefined) {\n            status.status = 'pending'; // Provide default status\n          }\n          const newState: State = {\n            type,\n            status: status.status,\n            icon: status.icon ?? '',\n            error: status.error,\n            errorMessage: status.errorMessage,\n            spinnerFrameIndex: status.spinnerFrameIndex ?? 0,\n          };\n          statusObj.state.push(newState);\n        }\n      } else {\n        // If the status object doesn't exist, create it\n        const newState: State = {\n          type,\n          status: status.status ?? 'pending',\n          icon: status.icon ?? '',\n          error: status.error,\n          errorMessage: status.errorMessage,\n          spinnerFrameIndex: status.spinnerFrameIndex ?? 0,\n        };\n        this.dictionariesStatuses.push({\n          dictionaryKey,\n          state: [newState],\n        });\n      }\n    }\n\n    // Update the display after status change\n    this.updateAllStatusLines();\n  }\n\n  private getStatusIcon(status: string): string {\n    const statusIcons: Record<string, string> = {\n      pending: '⏲',\n      fetching: '', // Spinner handled separately\n      built: '✔',\n      imported: '✔',\n      error: '✖',\n    };\n    return statusIcons[status] ?? '';\n  }\n\n  private getStatusLine(statusObj: DictionariesStatus): string {\n    const keyPadding =\n      this.maxDictionaryKeyLength - statusObj.dictionaryKey.length;\n    const paddedKey = `${statusObj.dictionaryKey}${' '.repeat(keyPadding)}`;\n\n    const states = statusObj.state.map((state) => {\n      let colorStart = '';\n      let colorEnd = '';\n      let icon = this.getStatusIcon(state.status);\n      if (state.status === 'fetching') {\n        // Use spinner frame\n        icon = SPINNER_FRAMES[state.spinnerFrameIndex! % SPINNER_FRAMES.length];\n        colorStart = BLUE;\n        colorEnd = RESET;\n      } else if (state.status === 'error') {\n        colorStart = RED;\n        colorEnd = RESET;\n      } else if (state.status === 'imported' || state.status === 'built') {\n        colorStart = GREEN;\n        colorEnd = RESET;\n      } else {\n        colorStart = GREY;\n        colorEnd = RESET;\n      }\n\n      // Format the status block\n      const statusBlock = `${GREY_DARK}[${state.type}: ${colorStart}${icon} ${state.status}${GREY_DARK}]${colorEnd}`;\n\n      return `${colorStart}${statusBlock}${colorEnd}`;\n    });\n\n    return `${this.config?.log.prefix}- ${paddedKey} ${states.join(' ')}`;\n  }\n\n  // Replace logUpdate calls with your custom methods\n  private updateAllStatusLines() {\n    const terminalHeight = process.stdout.rows;\n    const maxVisibleLines = terminalHeight - 1;\n\n    const lines = this.dictionariesStatuses.map((statusObj) => {\n      statusObj.state.forEach((state) => {\n        if (state.status === 'fetching') {\n          // Update spinner frame\n          state.spinnerFrameIndex =\n            (state.spinnerFrameIndex! + 1) % SPINNER_FRAMES.length;\n        }\n      });\n      return this.getStatusLine(statusObj);\n    });\n\n    let content;\n\n    if (lines.length > maxVisibleLines) {\n      const visibleLines = lines.slice(0, maxVisibleLines - 5);\n      const summary = `${this.config?.log.prefix}... and ${lines.length - visibleLines.length} more`;\n      content = LINE_DETECTOR + visibleLines.join('\\n') + '\\n' + summary;\n    } else {\n      content = lines.join('\\n');\n    }\n\n    this.updateOutput(content);\n  }\n\n  public getStatuses() {\n    return this.dictionariesStatuses;\n  }\n}\n\nexport const logger = Logger.getInstance();\n"],"mappings":"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","names":["readline"]}