summaryrefslogtreecommitdiffstats
path: root/scripts/cache/logger.ts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/cache/logger.ts')
-rw-r--r--scripts/cache/logger.ts152
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();