diff --git a/packages/cli/src/cli/cmd/auth.ts b/packages/cli/src/cli/cmd/auth.ts index caeae089a..3ec54d41b 100644 --- a/packages/cli/src/cli/cmd/auth.ts +++ b/packages/cli/src/cli/cmd/auth.ts @@ -2,39 +2,14 @@ import { Command } from "interactive-commander"; import Ora from "ora"; import { getSettings, saveSettings } from "../utils/settings"; import { createAuthenticator } from "../utils/auth"; +import { exitGracefully } from "../utils/exit-gracefully"; export default new Command() .command("auth") .description("Show current authentication status and user email") .helpOption("-h, --help", "Show help") - // Deprecated options, safe to remove after September 2025 - .option( - "--login", - "DEPRECATED: Shows deprecation warning and exits. Use `lingo.dev login` instead", - ) - .option( - "--logout", - "DEPRECATED: Shows deprecation warning and exits. Use `lingo.dev logout` instead", - ) - .action(async (options) => { + .action(async () => { try { - // Handle deprecated login option - if (options.login) { - Ora().warn( - "⚠️ DEPRECATED: '--login' is deprecated. Please use 'lingo.dev login' instead.", - ); - process.exit(1); - } - - // Handle deprecated logout option - if (options.logout) { - Ora().warn( - "⚠️ DEPRECATED: '--logout' is deprecated. Please use 'lingo.dev logout' instead.", - ); - process.exit(1); - } - - // Default behavior: show authentication status const settings = await getSettings(undefined); const authenticator = createAuthenticator({ apiUrl: settings.auth.apiUrl, @@ -48,6 +23,6 @@ export default new Command() } } catch (error: any) { Ora().fail(error.message); - process.exit(1); + exitGracefully(1); } }); diff --git a/packages/cli/src/cli/cmd/login.ts b/packages/cli/src/cli/cmd/login.ts index beffb7b98..6d1447b30 100644 --- a/packages/cli/src/cli/cmd/login.ts +++ b/packages/cli/src/cli/cmd/login.ts @@ -11,6 +11,7 @@ import { renderBanner, renderHero, } from "../utils/ui"; +import { exitGracefully } from "../utils/exit-gracefully"; export default new Command() .command("login") @@ -33,7 +34,7 @@ export default new Command() Ora().succeed("Successfully logged in"); } catch (error: any) { Ora().fail(error.message); - process.exit(1); + exitGracefully(1); } }); diff --git a/packages/cli/src/cli/cmd/logout.ts b/packages/cli/src/cli/cmd/logout.ts index eb7686fa8..c910be9fc 100644 --- a/packages/cli/src/cli/cmd/logout.ts +++ b/packages/cli/src/cli/cmd/logout.ts @@ -7,6 +7,7 @@ import { renderBanner, renderHero, } from "../utils/ui"; +import { exitGracefully } from "../utils/exit-gracefully"; export default new Command() .command("logout") @@ -26,6 +27,6 @@ export default new Command() Ora().succeed("Successfully logged out"); } catch (error: any) { Ora().fail(error.message); - process.exit(1); + exitGracefully(1); } }); diff --git a/packages/cli/src/cli/index.ts b/packages/cli/src/cli/index.ts index 151b5c6b7..0b036345f 100644 --- a/packages/cli/src/cli/index.ts +++ b/packages/cli/src/cli/index.ts @@ -21,6 +21,7 @@ import mayTheFourthCmd from "./cmd/may-the-fourth"; import packageJson from "../../package.json"; import run from "./cmd/run"; import purgeCmd from "./cmd/purge"; +import { exitGracefully } from "./utils/exit-gracefully"; export default new InteractiveCommand() .name("lingo.dev") @@ -69,7 +70,7 @@ Star the the repo :) https://github.com/LingoDotDev/lingo.dev err.code === "commander.version" || err.code === "commander.help" ) { - process.exit(0); + exitGracefully(0); } - process.exit(1); + exitGracefully(1); }); diff --git a/packages/cli/src/cli/utils/exit-gracefully.spec.ts b/packages/cli/src/cli/utils/exit-gracefully.spec.ts index 195689353..9a81394e2 100644 --- a/packages/cli/src/cli/utils/exit-gracefully.spec.ts +++ b/packages/cli/src/cli/utils/exit-gracefully.spec.ts @@ -37,6 +37,16 @@ describe("exitGracefully", () => { expect(mockExit).toHaveBeenCalledWith(0); }); + it("should exit with specified exit code when no pending operations", () => { + // Mock no pending operations + (process as any)._getActiveHandles.mockReturnValue([]); + (process as any)._getActiveRequests.mockReturnValue([]); + + exitGracefully(1); + + expect(mockExit).toHaveBeenCalledWith(1); + }); + it("should wait and retry when there are pending operations", () => { vi.useFakeTimers(); @@ -142,8 +152,8 @@ describe("exitGracefully", () => { ]); (process as any)._getActiveRequests.mockReturnValue([]); - // Start with 1500ms already elapsed - exitGracefully(1500); + // Start with 1500ms already elapsed (exitCode=0, elapsedMs=1500) + exitGracefully(0, 1500); // Should exit after 500ms more (reaching 2000ms max) vi.advanceTimersByTime(500); @@ -158,12 +168,29 @@ describe("exitGracefully", () => { ]); (process as any)._getActiveRequests.mockReturnValue([]); - exitGracefully(2500); + exitGracefully(0, 2500); // Should exit immediately as elapsed time exceeds max wait interval expect(mockExit).toHaveBeenCalledWith(0); }); + it("should exit with non-zero exit code when specified", () => { + vi.useFakeTimers(); + + // Mock pending operations + (process as any)._getActiveHandles.mockReturnValue([ + { hasRef: () => true, close: () => {} }, + ]); + (process as any)._getActiveRequests.mockReturnValue([]); + + exitGracefully(1); + + // Fast-forward to max wait time + vi.advanceTimersByTime(2000); + + expect(mockExit).toHaveBeenCalledWith(1); + }); + it("should handle mixed types of pending operations", () => { vi.useFakeTimers(); diff --git a/packages/cli/src/cli/utils/exit-gracefully.ts b/packages/cli/src/cli/utils/exit-gracefully.ts index 67e1b1029..619cdcc50 100644 --- a/packages/cli/src/cli/utils/exit-gracefully.ts +++ b/packages/cli/src/cli/utils/exit-gracefully.ts @@ -1,19 +1,19 @@ const STEP_WAIT_INTERVAL = 250; const MAX_WAIT_INTERVAL = 2000; -export function exitGracefully(elapsedMs = 0) { +export function exitGracefully(exitCode: number = 0, elapsedMs = 0) { // Check if there are any pending operations const hasPendingOperations = checkForPendingOperations(); if (hasPendingOperations && elapsedMs < MAX_WAIT_INTERVAL) { // Wait a bit longer if there are pending operations setTimeout( - () => exitGracefully(elapsedMs + STEP_WAIT_INTERVAL), + () => exitGracefully(exitCode, elapsedMs + STEP_WAIT_INTERVAL), STEP_WAIT_INTERVAL, ); } else { - // Exit immediately if no pending operations - process.exit(0); + // Exit with the specified exit code + process.exit(exitCode); } }