diff options
| author | 2025-12-26 22:39:23 +0800 | |
|---|---|---|
| committer | 2025-12-26 22:39:23 +0800 | |
| commit | 32ca410f4edbff578d71781d943c41573912f476 (patch) | |
| tree | 49f7e1e5602657d23945082fe273fc4802959a40 /scripts/cache/logger.ts | |
Initial commitmain
Diffstat (limited to 'scripts/cache/logger.ts')
| -rw-r--r-- | scripts/cache/logger.ts | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/scripts/cache/logger.ts b/scripts/cache/logger.ts new file mode 100644 index 0000000..1743370 --- /dev/null +++ b/scripts/cache/logger.ts @@ -0,0 +1,152 @@ +/** + * Checks if the current environment supports interactive terminal features + * like carriage return (\r) for progress updates. + * + * Returns false for: + * - Non-TTY environments (CI/CD logs, file redirects) + * - Dumb terminals + * - Environments without cursor control support + */ +function isInteractiveTerminal(): boolean { + // Check if stdout is a TTY (interactive terminal) + if (!process.stdout.isTTY) return false; + // Check for dumb terminal + if (process.env.TERM === 'dumb') return false; + // Check for CI environments (most set CI=true) + if (process.env.CI === 'true' || process.env.CI === '1') return false; + + // Check for common CI environment variables + const ciEnvVars = [ + 'GITHUB_ACTIONS', + 'GITLAB_CI', + 'CIRCLECI', + 'TRAVIS', + 'JENKINS_HOME', + 'BUILDKITE', + 'DRONE', + 'RENDER', // Render.com + 'CF_PAGES', // Cloudflare Pages + 'VERCEL' // Vercel + ]; + + for (const envVar of ciEnvVars) { + if (process.env[envVar]) return false; + } + + return true; +} + +/** + * Formats transfer statistics (size and speed). + */ +function formatTransferStats(bytes: number, elapsedSeconds: number): string { + const sizeMB = (bytes / (1024 * 1024)).toFixed(2); + const speedMBps = (bytes / (1024 * 1024) / elapsedSeconds).toFixed(2); + return `${sizeMB} MB (${speedMBps} MB/s)`; +} + +class TimerLogger { + private isInteractive: boolean; + private startTime: number; + private lastLogTime: number; + private prefix: string; + + constructor(prefix: string) { + this.isInteractive = isInteractiveTerminal(); + this.startTime = Date.now(); + this.lastLogTime = this.startTime; + this.prefix = prefix; + } + + /** + * Logs timer progress at regular intervals (throttled to 1 second). + */ + progress(): this { + const now = Date.now(); + if (now - this.lastLogTime >= 1000) { + const elapsed = (now - this.startTime) / 1000; + const message = `${this.prefix} (${elapsed.toFixed(0)}s)...`; + + if (this.isInteractive) process.stdout.write(`\r${message}`); + else console.log(message); + + this.lastLogTime = now; + } + return this; + } + + /** + * Logs final timer completion message. + */ + complete(): void { + const elapsed = (Date.now() - this.startTime) / 1000; + const message = `${this.prefix} (${elapsed.toFixed(0)}s)`; + + if (this.isInteractive) process.stdout.write(`\r\x1b[K${message}\n`); + else console.log(message); + } +} + +class TransferLogger { + private isInteractive: boolean; + private startTime: number; + private lastLogTime: number; + private prefix: string; + + constructor(prefix: string) { + this.isInteractive = isInteractiveTerminal(); + this.startTime = Date.now(); + this.lastLogTime = this.startTime; + this.prefix = prefix; + } + + /** + * Logs transfer progress at regular intervals (throttled to 1 second). + */ + progress(bytes: number): this { + const now = Date.now(); + if (now - this.lastLogTime >= 1000) { + const elapsed = (now - this.startTime) / 1000; + const message = `${this.prefix} ${formatTransferStats(bytes, elapsed)}...`; + + if (this.isInteractive) process.stdout.write(`\r${message}`); + else console.log(message); + + this.lastLogTime = now; + } + return this; + } + + /** + * Logs final transfer completion message. + */ + complete(bytes: number): void { + if (bytes > 0) { + const elapsed = (Date.now() - this.startTime) / 1000; + const message = `${this.prefix} ${formatTransferStats(bytes, elapsed)}`; + + if (this.isInteractive) process.stdout.write(`\r\x1b[K${message}\n`); + else console.log(message); + } + } +} + +class Logger { + /** + * Creates a timer progress logger. + * Usage: log.timer('Uploading cache').progress().complete() + */ + timer(prefix: string): TimerLogger { + return new TimerLogger(prefix); + } + + /** + * Creates a transfer progress logger. + * Usage: log.transfer('Received').progress(bytes).complete(bytes) + */ + transfer(prefix: string): TransferLogger { + return new TransferLogger(prefix); + } +} + +export const log = new Logger(); |
