UNPKG

@settlemint/sdk-utils

Version:

Shared utilities and helper functions for SettleMint SDK modules

1 lines 44.2 kB
{"version":3,"file":"package-manager.cjs","names":["value","code: number","output: string[]","stdoutOutput: string[]","stderrOutput: string[]","text: string","items","originalError: Error","isInCi","spinner","resolve","table","Table","pkgjs","pkgjs"],"sources":["../src/filesystem/project-root.ts","../src/filesystem/exists.ts","../src/json.ts","../src/filesystem/mono-repo.ts","../src/package-manager/download-and-extract.ts","../src/package-manager/get-package-manager.ts","../src/package-manager/get-package-manager-executable.ts","../src/terminal/should-print.ts","../src/terminal/ascii.ts","../src/logging/mask-tokens.ts","../src/terminal/cancel.ts","../src/terminal/execute-command.ts","../src/terminal/intro.ts","../src/terminal/note.ts","../src/terminal/list.ts","../src/terminal/outro.ts","../src/terminal/spinner.ts","../src/string.ts","../src/terminal/table.ts","../src/package-manager/install-dependencies.ts","../src/package-manager/is-package-installed.ts","../src/package-manager/set-name.ts"],"sourcesContent":["import { dirname } from \"node:path\";\nimport { findUp } from \"find-up\";\n\n/**\n * Finds the root directory of the current project by locating the nearest package.json file\n *\n * @param fallbackToCwd - If true, will return the current working directory if no package.json is found\n * @param cwd - The directory to start searching for the package.json file from (defaults to process.cwd())\n * @returns Promise that resolves to the absolute path of the project root directory\n * @throws Will throw an error if no package.json is found in the directory tree\n * @example\n * import { projectRoot } from \"@settlemint/sdk-utils/filesystem\";\n *\n * // Get project root path\n * const rootDir = await projectRoot();\n * console.log(`Project root is at: ${rootDir}`);\n */\nexport async function projectRoot(fallbackToCwd = false, cwd?: string): Promise<string> {\n const packageJsonPath = await findUp(\"package.json\", { cwd });\n if (!packageJsonPath) {\n if (fallbackToCwd) {\n return process.cwd();\n }\n throw new Error(\"Unable to find project root (no package.json found)\");\n }\n return dirname(packageJsonPath);\n}\n","import type { PathLike } from \"node:fs\";\nimport { stat } from \"node:fs/promises\";\n\n/**\n * Checks if a file or directory exists at the given path\n *\n * @param path - The file system path to check for existence\n * @returns Promise that resolves to true if the path exists, false otherwise\n * @example\n * import { exists } from \"@settlemint/sdk-utils/filesystem\";\n *\n * // Check if file exists before reading\n * if (await exists('/path/to/file.txt')) {\n * // File exists, safe to read\n * }\n */\nexport async function exists(path: PathLike): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n}\n","/**\n * Attempts to parse a JSON string into a typed value, returning a default value if parsing fails.\n *\n * @param value - The JSON string to parse\n * @param defaultValue - The value to return if parsing fails or results in null/undefined\n * @returns The parsed JSON value as type T, or the default value if parsing fails\n *\n * @example\n * import { tryParseJson } from \"@settlemint/sdk-utils\";\n *\n * const config = tryParseJson<{ port: number }>(\n * '{\"port\": 3000}',\n * { port: 8080 }\n * );\n * // Returns: { port: 3000 }\n *\n * const invalid = tryParseJson<string[]>(\n * 'invalid json',\n * []\n * );\n * // Returns: []\n */\nexport function tryParseJson<T>(value: string, defaultValue: T | null = null): T | null {\n try {\n const parsed = JSON.parse(value) as T;\n if (parsed === undefined || parsed === null) {\n return defaultValue;\n }\n return parsed;\n } catch (_err) {\n // Invalid json\n return defaultValue;\n }\n}\n\n/**\n * Extracts a JSON object from a string.\n *\n * @param value - The string to extract the JSON object from\n * @returns The parsed JSON object, or null if no JSON object is found\n * @throws {Error} If the input string is too long (longer than 5000 characters)\n * @example\n * import { extractJsonObject } from \"@settlemint/sdk-utils\";\n *\n * const json = extractJsonObject<{ port: number }>(\n * 'port info: {\"port\": 3000}',\n * );\n * // Returns: { port: 3000 }\n */\nexport function extractJsonObject<T>(value: string): T | null {\n if (value.length > 5000) {\n throw new Error(\"Input too long\");\n }\n const result = /\\{([\\s\\S]*)\\}/.exec(value);\n if (!result) {\n return null;\n }\n return tryParseJson<T>(result[0]);\n}\n\n/**\n * Converts a value to a JSON stringifiable format.\n *\n * @param value - The value to convert\n * @returns The JSON stringifiable value\n *\n * @example\n * import { makeJsonStringifiable } from \"@settlemint/sdk-utils\";\n *\n * const json = makeJsonStringifiable<{ amount: bigint }>({ amount: BigInt(1000) });\n * // Returns: '{\"amount\":\"1000\"}'\n */\nexport function makeJsonStringifiable<T>(value: unknown): T {\n if (value === undefined || value === null) {\n return value as T;\n }\n return tryParseJson<T>(\n JSON.stringify(\n value,\n (_, value) => (typeof value === \"bigint\" ? value.toString() : value), // return everything else unchanged\n ),\n ) as T;\n}\n","import { readFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { findUp } from \"find-up\";\nimport { glob } from \"glob\";\nimport { exists } from \"@/filesystem.js\";\nimport { tryParseJson } from \"@/json.js\";\n\n/**\n * Finds the root directory of a monorepo\n *\n * @param startDir - The directory to start searching from\n * @returns The root directory of the monorepo or null if not found\n * @example\n * import { findMonoRepoRoot } from \"@settlemint/sdk-utils/filesystem\";\n *\n * const root = await findMonoRepoRoot(\"/path/to/your/project\");\n * console.log(root); // Output: /path/to/your/project/packages/core\n */\nexport async function findMonoRepoRoot(startDir: string): Promise<string | null> {\n const lockFilePath = await findUp([\"package-lock.json\", \"yarn.lock\", \"pnpm-lock.yaml\", \"bun.lockb\", \"bun.lock\"], {\n cwd: startDir,\n });\n if (lockFilePath) {\n const packageJsonPath = join(dirname(lockFilePath), \"package.json\");\n const hasWorkSpaces = await packageJsonHasWorkspaces(packageJsonPath);\n return hasWorkSpaces ? dirname(lockFilePath) : null;\n }\n\n let currentDir = startDir;\n\n while (currentDir !== \"/\") {\n const packageJsonPath = join(currentDir, \"package.json\");\n\n if (await packageJsonHasWorkspaces(packageJsonPath)) {\n return currentDir;\n }\n\n const parentDir = dirname(currentDir);\n if (parentDir === currentDir) {\n break; // We've reached the root\n }\n currentDir = parentDir;\n }\n\n return null;\n}\n\n/**\n * Finds all packages in a monorepo\n *\n * @param projectDir - The directory to start searching from\n * @returns An array of package directories\n * @example\n * import { findMonoRepoPackages } from \"@settlemint/sdk-utils/filesystem\";\n *\n * const packages = await findMonoRepoPackages(\"/path/to/your/project\");\n * console.log(packages); // Output: [\"/path/to/your/project/packages/core\", \"/path/to/your/project/packages/ui\"]\n */\nexport async function findMonoRepoPackages(projectDir: string): Promise<string[]> {\n try {\n const monoRepoRoot = await findMonoRepoRoot(projectDir);\n if (!monoRepoRoot) {\n return [projectDir];\n }\n\n const packageJsonPath = join(monoRepoRoot, \"package.json\");\n const packageJson = tryParseJson<{ workspaces: string[] }>(await readFile(packageJsonPath, \"utf-8\"));\n const workspaces = packageJson?.workspaces ?? [];\n\n const packagePaths = await Promise.all(\n workspaces.map(async (workspace: string) => {\n const matches = await glob(join(monoRepoRoot, workspace, \"package.json\"));\n return matches.map((match) => join(match, \"..\"));\n }),\n );\n\n const allPaths = packagePaths.flat();\n // If no packages found in workspaces, treat as non-monorepo\n return allPaths.length === 0 ? [projectDir] : [monoRepoRoot, ...allPaths];\n } catch (_error) {\n // If any error occurs, treat as non-monorepo\n return [projectDir];\n }\n}\n\nasync function packageJsonHasWorkspaces(packageJsonPath: string): Promise<boolean> {\n if (await exists(packageJsonPath)) {\n const packageJson = tryParseJson<{ workspaces: string[] }>(await readFile(packageJsonPath, \"utf-8\"));\n if (packageJson?.workspaces && Array.isArray(packageJson?.workspaces) && packageJson?.workspaces.length > 0) {\n return true;\n }\n }\n return false;\n}\n","import { readdir, rm } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { exists } from \"../filesystem.js\";\n\n/**\n * Formats a directory path by removing trailing slashes and whitespace\n *\n * @param targetDir - The directory path to format\n * @returns The formatted directory path\n * @example\n * import { formatTargetDir } from \"@settlemint/sdk-utils/package-manager\";\n *\n * const formatted = formatTargetDir(\"/path/to/dir/ \"); // \"/path/to/dir\"\n */\nexport function formatTargetDir(targetDir: string): string {\n return targetDir?.trim().replace(/\\/+$/g, \"\");\n}\n\n/**\n * Checks if a directory is empty or contains only a .git folder\n *\n * @param path - The directory path to check\n * @returns True if directory is empty or contains only .git, false otherwise\n * @example\n * import { isEmpty } from \"@settlemint/sdk-utils/package-manager\";\n *\n * if (await isEmpty(\"/path/to/dir\")) {\n * // Directory is empty\n * }\n */\nexport async function isEmpty(path: string): Promise<boolean> {\n const files = await readdir(path);\n return files.length === 0 || (files.length === 1 && files[0] === \".git\");\n}\n\n/**\n * Removes all contents of a directory except the .git folder\n *\n * @param dir - The directory path to empty\n * @example\n * import { emptyDir } from \"@settlemint/sdk-utils/package-manager\";\n *\n * await emptyDir(\"/path/to/dir\"); // Removes all contents except .git\n */\nexport async function emptyDir(dir: string): Promise<void> {\n if (!(await exists(dir))) return;\n for (const file of await readdir(dir)) {\n if (file === \".git\") continue;\n await rm(resolve(dir, file), { recursive: true, force: true });\n }\n}\n","import type { AgentName } from \"package-manager-detector\";\nimport { detect } from \"package-manager-detector/detect\";\n\n/**\n * Detects the package manager used in the current project\n *\n * @param targetDir - The directory to check for package manager (optional, defaults to process.cwd())\n * @returns The name of the package manager\n * @example\n * import { getPackageManager } from \"@settlemint/sdk-utils/package-manager\";\n *\n * const packageManager = await getPackageManager();\n * console.log(`Using ${packageManager}`);\n */\nexport async function getPackageManager(targetDir?: string): Promise<AgentName> {\n const packageManager = await detect({ cwd: targetDir || process.cwd() });\n\n return packageManager?.name ?? \"npm\";\n}\n","import { getPackageManager } from \"./get-package-manager.js\";\n\n/**\n * Retrieves the executable command and arguments for the package manager\n *\n * @param targetDir - The directory to check for package manager (optional, defaults to process.cwd())\n * @returns An object containing the command and arguments for the package manager\n * @example\n * import { getPackageManagerExecutable } from \"@settlemint/sdk-utils/package-manager\";\n *\n * const { command, args } = await getPackageManagerExecutable();\n * console.log(`Using ${command} with args: ${args.join(\" \")}`);\n */\nexport async function getPackageManagerExecutable(targetDir?: string): Promise<{ command: string; args: string[] }> {\n const packageManager = await getPackageManager(targetDir ?? process.cwd());\n\n switch (packageManager) {\n case \"pnpm\":\n return { command: \"pnpm\", args: [\"dlx\"] };\n case \"bun\":\n return { command: \"bunx\", args: [] };\n case \"yarn\":\n return { command: \"yarn\", args: [\"create\"] };\n }\n\n // Default to npm\n return { command: \"npx\", args: [] };\n}\n","/**\n * Determines whether terminal output should be printed based on environment variables.\n *\n * **Environment Variable Precedence:**\n * 1. `SETTLEMINT_DISABLE_TERMINAL=\"true\"` - Completely disables all terminal output (highest priority)\n * 2. `CLAUDECODE`, `REPL_ID`, or `AGENT` (any truthy value) - Enables quiet mode, suppressing info/debug/status messages\n *\n * **Quiet Mode Behavior:**\n * When quiet mode is active (Claude Code environments), this function returns `false` to suppress\n * informational output. However, warnings and errors are always displayed regardless of quiet mode,\n * as they are handled separately in the `note()` function with level-based filtering.\n *\n * @returns `true` if terminal output should be printed, `false` if suppressed\n */\nexport function shouldPrint(): boolean {\n if (process.env.SETTLEMINT_DISABLE_TERMINAL === \"true\") {\n return false;\n }\n // In quiet mode (Claude Code), suppress info/debug/status messages\n // Warnings and errors will still be displayed via note() with appropriate levels\n if (process.env.CLAUDECODE || process.env.REPL_ID || process.env.AGENT) {\n return false;\n }\n return true;\n}\n","import { magentaBright } from \"yoctocolors\";\nimport { shouldPrint } from \"./should-print.js\";\n\n/**\n * Prints the SettleMint ASCII art logo to the console in magenta color.\n * Used for CLI branding and visual identification.\n *\n * @example\n * import { ascii } from \"@settlemint/sdk-utils/terminal\";\n *\n * // Prints the SettleMint logo\n * ascii();\n */\nexport const ascii = (): void => {\n if (!shouldPrint()) {\n return;\n }\n console.log(\n magentaBright(`\n _________ __ __ .__ _____ .__ __\n / _____/ _____/ |__/ |_| | ____ / \\\\ |__| _____/ |_\n \\\\_____ \\\\_/ __ \\\\ __\\\\ __\\\\ | _/ __ \\\\ / \\\\ / \\\\| |/ \\\\ __\\\\\n / \\\\ ___/| | | | | |_\\\\ ___// Y \\\\ | | \\\\ |\n/_________/\\\\_____>__| |__| |____/\\\\_____>____|____/__|___|__/__|\n`),\n );\n};\n","/**\n * Masks sensitive SettleMint tokens in output text by replacing them with asterisks.\n * Handles personal access tokens (PAT), application access tokens (AAT), and service account tokens (SAT).\n *\n * @param output - The text string that may contain sensitive tokens\n * @returns The text with any sensitive tokens masked with asterisks\n * @example\n * import { maskTokens } from \"@settlemint/sdk-utils/terminal\";\n *\n * // Masks a token in text\n * const masked = maskTokens(\"Token: sm_pat_****\"); // \"Token: ***\"\n */\nexport const maskTokens = (output: string): string => {\n return output.replace(/sm_(pat|aat|sat)_[0-9a-zA-Z]+/g, \"***\");\n};\n","import { maskTokens } from \"@/logging/mask-tokens.js\";\nimport { inverse, redBright } from \"yoctocolors\";\n\n/**\n * Error class used to indicate that the operation was cancelled.\n * This error is used to signal that the operation should be aborted.\n */\nexport class CancelError extends Error {}\n\n/**\n * Displays an error message in red inverse text and throws a CancelError.\n * Used to terminate execution with a visible error message.\n * Any sensitive tokens in the message are masked before display.\n *\n * @param msg - The error message to display\n * @returns never - Function does not return as it throws an error\n * @example\n * import { cancel } from \"@settlemint/sdk-utils/terminal\";\n *\n * // Exits process with error message\n * cancel(\"An error occurred\");\n */\nexport const cancel = (msg: string): never => {\n console.log(\"\");\n console.log(inverse(redBright(maskTokens(msg))));\n console.log(\"\");\n throw new CancelError(msg);\n};\n","import { type SpawnOptionsWithoutStdio, spawn } from \"node:child_process\";\nimport { maskTokens } from \"../logging/mask-tokens.js\";\n\n/**\n * Options for executing a command, extending SpawnOptionsWithoutStdio\n */\nexport interface ExecuteCommandOptions extends SpawnOptionsWithoutStdio {\n /** Whether to suppress output to stdout/stderr */\n silent?: boolean;\n}\n\n/**\n * Error class for command execution errors\n * @extends Error\n */\nexport class CommandError extends Error {\n /**\n * Constructs a new CommandError\n * @param message - The error message\n * @param code - The exit code of the command\n * @param output - The output of the command\n */\n constructor(\n message: string,\n public readonly code: number,\n public readonly output: string[],\n ) {\n super(message);\n }\n}\n\n/**\n * Checks if we're in quiet mode (Claude Code environment)\n */\nfunction isQuietMode(): boolean {\n return !!(process.env.CLAUDECODE || process.env.REPL_ID || process.env.AGENT);\n}\n\n/**\n * Executes a command with the given arguments in a child process.\n * Pipes stdin to the child process and captures stdout/stderr output.\n * Masks any sensitive tokens in the output before displaying or returning.\n * In quiet mode (when CLAUDECODE, REPL_ID, or AGENT env vars are set),\n * output is suppressed unless the command errors out.\n *\n * @param command - The command to execute\n * @param args - Array of arguments to pass to the command\n * @param options - Options for customizing command execution\n * @returns Array of output strings from stdout and stderr\n * @throws {CommandError} If the process fails to start or exits with non-zero code\n * @example\n * import { executeCommand } from \"@settlemint/sdk-utils/terminal\";\n *\n * // Execute git clone\n * await executeCommand(\"git\", [\"clone\", \"repo-url\"]);\n *\n * // Execute silently\n * await executeCommand(\"npm\", [\"install\"], { silent: true });\n */\nexport async function executeCommand(\n command: string,\n args: string[],\n options?: ExecuteCommandOptions,\n): Promise<string[]> {\n const { silent, ...spawnOptions } = options ?? {};\n const quietMode = isQuietMode();\n // In quiet mode, suppress output unless explicitly overridden with silent: false\n const shouldSuppressOutput = quietMode ? silent !== false : !!silent;\n\n const child = spawn(command, args, { ...spawnOptions, env: { ...process.env, ...options?.env } });\n process.stdin.pipe(child.stdin);\n const output: string[] = [];\n const stdoutOutput: string[] = [];\n const stderrOutput: string[] = [];\n\n return new Promise((resolve, reject) => {\n child.stdout.on(\"data\", (data: Buffer | string) => {\n const maskedData = maskTokens(data.toString());\n if (!shouldSuppressOutput) {\n process.stdout.write(maskedData);\n }\n output.push(maskedData);\n stdoutOutput.push(maskedData);\n });\n child.stderr.on(\"data\", (data: Buffer | string) => {\n const maskedData = maskTokens(data.toString());\n if (!shouldSuppressOutput) {\n process.stderr.write(maskedData);\n }\n output.push(maskedData);\n stderrOutput.push(maskedData);\n });\n\n const showErrorOutput = () => {\n // In quiet mode, show output on error\n if (quietMode && shouldSuppressOutput && output.length > 0) {\n // Write stdout to stdout and stderr to stderr\n if (stdoutOutput.length > 0) {\n process.stdout.write(stdoutOutput.join(\"\"));\n }\n if (stderrOutput.length > 0) {\n process.stderr.write(stderrOutput.join(\"\"));\n }\n }\n };\n\n child.on(\"error\", (err) => {\n process.stdin.unpipe(child.stdin);\n showErrorOutput();\n reject(new CommandError(err.message, \"code\" in err && typeof err.code === \"number\" ? err.code : 1, output));\n });\n child.on(\"close\", (code) => {\n process.stdin.unpipe(child.stdin);\n if (code === 0 || code === null || code === 143) {\n resolve(output);\n return;\n }\n // In quiet mode, show output on error\n showErrorOutput();\n reject(new CommandError(`Command \"${command}\" exited with code ${code}`, code, output));\n });\n });\n}\n","import { maskTokens } from \"@/logging/mask-tokens.js\";\nimport { magentaBright } from \"yoctocolors\";\nimport { shouldPrint } from \"./should-print.js\";\n\n/**\n * Displays an introductory message in magenta text with padding.\n * Any sensitive tokens in the message are masked before display.\n *\n * @param msg - The message to display as introduction\n * @example\n * import { intro } from \"@settlemint/sdk-utils/terminal\";\n *\n * // Display intro message\n * intro(\"Starting deployment...\");\n */\nexport const intro = (msg: string): void => {\n if (!shouldPrint()) {\n return;\n }\n console.log(\"\");\n console.log(magentaBright(maskTokens(msg)));\n console.log(\"\");\n};\n","import { maskTokens } from \"@/logging/mask-tokens.js\";\nimport { redBright, yellowBright } from \"yoctocolors\";\nimport { shouldPrint } from \"./should-print.js\";\n\n/**\n * Applies color to a message if not already colored.\n * @param msg - The message to colorize\n * @param level - The severity level determining the color\n * @returns Colorized message (yellow for warnings, red for errors, unchanged for info)\n */\nfunction colorize(msg: string, level: \"info\" | \"warn\" | \"error\"): string {\n // Don't re-colorize messages that already contain ANSI escape codes\n if (msg.includes(\"\\u001b[\")) {\n return msg;\n }\n if (level === \"warn\") {\n return yellowBright(msg);\n }\n if (level === \"error\") {\n return redBright(msg);\n }\n return msg;\n}\n\n/**\n * Determines whether a message should be printed based on its level and quiet mode.\n * @param level - The severity level of the message\n * @returns true if the message should be printed, false otherwise\n */\nfunction canPrint(level: \"info\" | \"warn\" | \"error\"): boolean {\n // Warnings and errors always print, even in quiet mode\n if (level !== \"info\") {\n return true;\n }\n // Info messages respect shouldPrint() which checks for quiet mode\n return shouldPrint();\n}\n\n/**\n * Prepares a message for display by converting Error objects and masking tokens.\n * @param value - The message string or Error object\n * @param level - The severity level (stack traces are included for errors)\n * @returns Masked message text, optionally with stack trace\n */\nfunction prepareMessage(value: string | Error, level: \"info\" | \"warn\" | \"error\"): string {\n let text: string;\n if (value instanceof Error) {\n text = value.message;\n // For errors, automatically include stack trace\n if (level === \"error\" && value.stack) {\n text = `${text}\\n\\n${value.stack}`;\n }\n } else {\n text = value;\n }\n return maskTokens(text);\n}\n\n/**\n * Displays a note message with optional warning or error level formatting.\n * Regular notes are displayed in normal text, warnings are shown in yellow, and errors in red.\n * Any sensitive tokens in the message are masked before display.\n * Warnings and errors are always displayed, even in quiet mode (when CLAUDECODE, REPL_ID, or AGENT env vars are set).\n * When an Error object is provided with level \"error\", the stack trace is automatically included.\n *\n * @param message - The message to display as a note. Can be either:\n * - A string: Displayed directly with appropriate styling\n * - An Error object: The error message is displayed, and for level \"error\", the stack trace is automatically included\n * @param level - The note level: \"info\" (default), \"warn\" for warning styling, or \"error\" for error styling\n * @example\n * import { note } from \"@settlemint/sdk-utils/terminal\";\n *\n * // Display info note\n * note(\"Operation completed successfully\");\n *\n * // Display warning note\n * note(\"Low disk space remaining\", \"warn\");\n *\n * // Display error note (string)\n * note(\"Operation failed\", \"error\");\n *\n * // Display error with stack trace automatically (Error object)\n * try {\n * // some operation\n * } catch (error) {\n * // If error is an Error object and level is \"error\", stack trace is included automatically\n * note(error, \"error\");\n * }\n */\nexport const note = (message: string | Error, level: \"info\" | \"warn\" | \"error\" = \"info\"): void => {\n if (!canPrint(level)) {\n return;\n }\n\n const msg = prepareMessage(message, level);\n console.log(\"\");\n\n if (level === \"warn\") {\n console.warn(colorize(msg, level));\n } else if (level === \"error\") {\n console.error(colorize(msg, level));\n } else {\n console.log(msg);\n }\n};\n","import { note } from \"./note.js\";\n\n/**\n * Displays a list of items in a formatted manner, supporting nested items.\n *\n * @param title - The title of the list\n * @param items - The items to display, can be strings or arrays for nested items\n * @returns The formatted list\n * @example\n * import { list } from \"@settlemint/sdk-utils/terminal\";\n *\n * // Simple list\n * list(\"Use cases\", [\"use case 1\", \"use case 2\", \"use case 3\"]);\n *\n * // Nested list\n * list(\"Providers\", [\n * \"AWS\",\n * [\"us-east-1\", \"eu-west-1\"],\n * \"Azure\",\n * [\"eastus\", \"westeurope\"]\n * ]);\n */\nexport function list(title: string, items: Array<string | string[]>) {\n const formatItems = (items: Array<string | string[]>): string => {\n return items\n .map((item) => {\n if (Array.isArray(item)) {\n return item.map((subItem) => ` • ${subItem}`).join(\"\\n\");\n }\n return ` • ${item}`;\n })\n .join(\"\\n\");\n };\n\n return note(`${title}:\\n\\n${formatItems(items)}`);\n}\n","import { maskTokens } from \"@/logging/mask-tokens.js\";\nimport { shouldPrint } from \"@/terminal/should-print.js\";\nimport { greenBright, inverse } from \"yoctocolors\";\n\n/**\n * Displays a closing message in green inverted text with padding.\n * Any sensitive tokens in the message are masked before display.\n *\n * @param msg - The message to display as conclusion\n * @example\n * import { outro } from \"@settlemint/sdk-utils/terminal\";\n *\n * // Display outro message\n * outro(\"Deployment completed successfully!\");\n */\nexport const outro = (msg: string): void => {\n if (!shouldPrint()) {\n return;\n }\n console.log(\"\");\n console.log(inverse(greenBright(maskTokens(msg))));\n console.log(\"\");\n};\n","import isInCi from \"is-in-ci\";\nimport yoctoSpinner, { type Spinner } from \"yocto-spinner\";\nimport { redBright } from \"yoctocolors\";\nimport { note } from \"./note.js\";\nimport { shouldPrint } from \"./should-print.js\";\n\n/**\n * Error class used to indicate that the spinner operation failed.\n * This error is used to signal that the operation should be aborted.\n */\nexport class SpinnerError extends Error {\n constructor(\n message: string,\n public readonly originalError: Error,\n ) {\n super(message);\n this.name = \"SpinnerError\";\n }\n}\n\n/**\n * Options for configuring the spinner behavior\n */\nexport interface SpinnerOptions<R> {\n /** Message to display when spinner starts */\n startMessage: string;\n /** Async task to execute while spinner is active */\n task: (spinner?: Spinner) => Promise<R>;\n /** Message to display when spinner completes successfully */\n stopMessage: string;\n}\n\n/**\n * Displays a loading spinner while executing an async task.\n * Shows progress with start/stop messages and handles errors.\n * Spinner is disabled in CI environments.\n *\n * @param options - Configuration options for the spinner\n * @returns The result from the executed task\n * @throws Will exit process with code 1 if task fails\n * @example\n * import { spinner } from \"@settlemint/sdk-utils/terminal\";\n *\n * // Show spinner during async task\n * const result = await spinner({\n * startMessage: \"Deploying...\",\n * task: async () => {\n * // Async work here\n * return \"success\";\n * },\n * stopMessage: \"Deployed successfully!\"\n * });\n */\nexport const spinner = async <R>(options: SpinnerOptions<R>): Promise<R> => {\n const handleError = (error: Error) => {\n note(error, \"error\");\n throw new SpinnerError(error.message, error);\n };\n if (isInCi || !shouldPrint()) {\n try {\n return await options.task();\n } catch (err) {\n return handleError(err as Error);\n }\n }\n const spinner = yoctoSpinner({ stream: process.stdout }).start(options.startMessage);\n try {\n const result = await options.task(spinner);\n spinner.success(options.stopMessage);\n // Ensure spinner success message renders before proceeding to avoid\n // terminal output overlap issues with subsequent messages\n await new Promise((resolve) => process.nextTick(resolve));\n return result;\n } catch (err) {\n spinner.error(redBright(`${options.startMessage} --> Error!`));\n return handleError(err as Error);\n }\n};\n","/**\n * Capitalizes the first letter of a string.\n *\n * @param val - The string to capitalize\n * @returns The input string with its first letter capitalized\n *\n * @example\n * import { capitalizeFirstLetter } from \"@settlemint/sdk-utils\";\n *\n * const capitalized = capitalizeFirstLetter(\"hello\");\n * // Returns: \"Hello\"\n */\nexport function capitalizeFirstLetter(val: string) {\n return String(val).charAt(0).toUpperCase() + String(val).slice(1);\n}\n\n/**\n * Converts a camelCase string to a human-readable string.\n *\n * @param s - The camelCase string to convert\n * @returns The human-readable string\n *\n * @example\n * import { camelCaseToWords } from \"@settlemint/sdk-utils\";\n *\n * const words = camelCaseToWords(\"camelCaseString\");\n * // Returns: \"Camel Case String\"\n */\nexport function camelCaseToWords(s: string) {\n const result = s.replace(/([a-z])([A-Z])/g, \"$1 $2\");\n const withSpaces = result.replace(/([A-Z])([a-z])/g, \" $1$2\");\n const capitalized = capitalizeFirstLetter(withSpaces);\n return capitalized.replace(/\\s+/g, \" \").trim();\n}\n\n/**\n * Replaces underscores and hyphens with spaces.\n *\n * @param s - The string to replace underscores and hyphens with spaces\n * @returns The input string with underscores and hyphens replaced with spaces\n *\n * @example\n * import { replaceUnderscoresAndHyphensWithSpaces } from \"@settlemint/sdk-utils\";\n *\n * const result = replaceUnderscoresAndHyphensWithSpaces(\"Already_Spaced-Second\");\n * // Returns: \"Already Spaced Second\"\n */\nexport function replaceUnderscoresAndHyphensWithSpaces(s: string) {\n return s.replace(/[-_]/g, \" \");\n}\n\n/**\n * Truncates a string to a maximum length and appends \"...\" if it is longer.\n *\n * @param value - The string to truncate\n * @param maxLength - The maximum length of the string\n * @returns The truncated string or the original string if it is shorter than the maximum length\n *\n * @example\n * import { truncate } from \"@settlemint/sdk-utils\";\n *\n * const truncated = truncate(\"Hello, world!\", 10);\n * // Returns: \"Hello, wor...\"\n */\nexport function truncate(value: string, maxLength: number) {\n if (value.length <= maxLength) {\n return value;\n }\n return `${value.slice(0, maxLength)}...`;\n}\n","import { Table } from \"console-table-printer\";\nimport { whiteBright } from \"yoctocolors\";\nimport { camelCaseToWords } from \"@/string.js\";\nimport { note } from \"./note.js\";\nimport { shouldPrint } from \"./should-print.js\";\n/**\n * Displays data in a formatted table in the terminal.\n *\n * @param title - Title to display above the table\n * @param data - Array of objects to display in table format\n * @example\n * import { table } from \"@settlemint/sdk-utils/terminal\";\n *\n * const data = [\n * { name: \"Item 1\", value: 100 },\n * { name: \"Item 2\", value: 200 }\n * ];\n *\n * table(\"My Table\", data);\n */\nexport function table(title: string, data: unknown[]): void {\n if (!shouldPrint()) {\n return;\n }\n\n note(title);\n\n if (!data || data.length === 0) {\n note(\"No data to display\");\n return;\n }\n\n const columnKeys = Object.keys(data[0] as Record<string, unknown>);\n const table = new Table({\n columns: columnKeys.map((key) => ({\n name: key,\n title: whiteBright(camelCaseToWords(key)),\n alignment: \"left\",\n })),\n });\n // biome-ignore lint/suspicious/noExplicitAny: Data structure varies based on table content\n table.addRows(data as Array<Record<string, any>>);\n table.printTable();\n}\n","import { installPackage } from \"@antfu/install-pkg\";\nimport { note } from \"../terminal.js\";\n\n/**\n * Installs one or more packages as dependencies using the detected package manager\n *\n * @param pkgs - A single package name or array of package names to install\n * @param cwd - The directory to run the installation in\n * @returns A promise that resolves when installation is complete\n * @throws If package installation fails\n * @example\n * import { installDependencies } from \"@settlemint/sdk-utils/package-manager\";\n *\n * // Install a single package\n * await installDependencies(\"express\");\n *\n * // Install multiple packages\n * await installDependencies([\"express\", \"cors\"]);\n */\nexport async function installDependencies(pkgs: string | string[], cwd?: string) {\n try {\n await installPackage(pkgs, { silent: true, additionalArgs: [\"--exact\"], cwd });\n } catch (err) {\n const error = err instanceof Error ? err.message : \"Unknown error\";\n note(\n `Failed to install ${Array.isArray(pkgs) ? `dependencies '${pkgs.join(\", \")}'` : `dependency '${pkgs}'`}: ${error}`,\n \"warn\",\n );\n }\n}\n","import { projectRoot } from \"@/filesystem/project-root.js\";\nimport pkgjs from \"@npmcli/package-json\";\n\n/**\n * Checks if a package is installed in the project's dependencies, devDependencies, or peerDependencies.\n *\n * @param name - The name of the package to check\n * @param path - The path to the project root directory. If not provided, will be automatically determined\n * @returns Whether the package is installed\n * @throws If unable to read or parse the package.json file\n * @example\n * import { isPackageInstalled } from \"@settlemint/sdk-utils/package-manager\";\n *\n * const isInstalled = await isPackageInstalled(\"@settlemint/sdk-utils\");\n * console.log(`@settlemint/sdk-utils is installed: ${isInstalled}`);\n */\nexport async function isPackageInstalled(name: string, path?: string) {\n // Read the package.json file\n const pkgJson = await pkgjs.load(path ?? (await projectRoot()));\n\n const inDependencies = !!pkgJson.content.dependencies?.[name];\n const inDevDependencies = !!pkgJson.content.devDependencies?.[name];\n const inPeerDependencies = !!pkgJson.content.peerDependencies?.[name];\n\n return inDependencies || inDevDependencies || inPeerDependencies;\n}\n","import { projectRoot } from \"@/filesystem/project-root.js\";\nimport pkgjs from \"@npmcli/package-json\";\n\n/**\n * Sets the name field in the package.json file\n *\n * @param name - The new name to set in the package.json file\n * @param path - The path to the project root directory. If not provided, will be automatically determined\n * @returns A promise that resolves when the package.json has been updated\n * @throws If unable to read, update or save the package.json file\n * @example\n * import { setName } from \"@settlemint/sdk-utils/package-manager\";\n *\n * await setName(\"my-new-project-name\");\n */\nexport async function setName(name: string, path?: string) {\n // Read the package.json file\n const pkgJson = await pkgjs.load(path ?? (await projectRoot()));\n\n pkgJson.update({\n name,\n });\n\n await pkgJson.save();\n}\n"],"mappings":"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"}