Skip to main content
Glama
pushLog.ts3.55 kB
import { ANSIColors, colorize, getConfiguration, spinnerFrames, } from '@intlayer/config'; export type PushStatus = { dictionaryKey: string; status: 'pending' | 'pushing' | 'pushed' | 'modified' | 'error'; errorMessage?: string; }; export class PushLogger { private statuses: PushStatus[] = []; private spinnerTimer: NodeJS.Timeout | null = null; private spinnerIndex = 0; private renderedLines = 0; private readonly spinnerFrames = spinnerFrames; private isFinished = false; private readonly prefix: string; private lastRenderedState: string = ''; constructor() { const configuration = getConfiguration(); this.prefix = configuration.log.prefix; } update(newStatuses: PushStatus[]) { if (this.isFinished) return; for (const status of newStatuses) { const index = this.statuses.findIndex( (s) => s.dictionaryKey === status.dictionaryKey ); if (index >= 0) { this.statuses[index] = status; } else { this.statuses.push(status); } } this.startSpinner(); this.render(); } finish() { this.isFinished = true; this.stopSpinner(); this.render(); } private startSpinner() { if (this.spinnerTimer || this.isFinished) return; this.spinnerTimer = setInterval(() => { this.spinnerIndex = (this.spinnerIndex + 1) % this.spinnerFrames.length; this.render(); }, 100); } private stopSpinner() { if (!this.spinnerTimer) return; clearInterval(this.spinnerTimer); this.spinnerTimer = null; } private render() { const { total, done, pushed, modified, errors } = this.computeProgress(); const frame = this.spinnerFrames[this.spinnerIndex]; const lines: string[] = []; const isDone = done === total; const progressLabel = `dictionaries: ${done}/${total}`; const details: string[] = []; if (pushed > 0) details.push(`new: ${pushed}`); if (modified > 0) details.push(`modified: ${modified}`); if (errors > 0) details.push(colorize(`errors: ${errors}`, ANSIColors.RED)); const suffix = details.length > 0 ? ` (${details.join(', ')})` : ''; if (isDone) { lines.push( `${this.prefix} ${colorize('✔', ANSIColors.GREEN)} pushed ${progressLabel}${suffix}` ); } else { lines.push( `${this.prefix} ${colorize(frame, ANSIColors.BLUE)} pushing ${progressLabel}${suffix}` ); } const currentState = lines.join('\n'); if (currentState === this.lastRenderedState) { return; } this.lastRenderedState = currentState; if (this.renderedLines > 0) { process.stdout.write(`\x1b[${this.renderedLines}F`); } const totalLinesToClear = Math.max(this.renderedLines, lines.length); for (let i = 0; i < totalLinesToClear; i++) { process.stdout.write('\x1b[2K'); const line = lines[i]; if (line !== undefined) { process.stdout.write(line); } process.stdout.write('\n'); } this.renderedLines = lines.length; } private computeProgress() { const keys = new Set(this.statuses.map((s) => s.dictionaryKey)); const pushed = this.statuses.filter((s) => s.status === 'pushed').length; const modified = this.statuses.filter( (s) => s.status === 'modified' ).length; const errors = this.statuses.filter((s) => s.status === 'error').length; const done = pushed + modified + errors; return { total: keys.size, done, pushed, modified, errors, } as const; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/aymericzip/intlayer'

If you have feedback or need assistance with the MCP directory API, please join our Discord server