# Getting Started (/docs) # Welcome to Visulima [#welcome-to-visulima] Visulima is a collection of **40+ production-ready TypeScript packages** that solve real problems — from building CLI tools and bundling libraries, to handling errors, working with file systems, and creating APIs. Every package is: * **TypeScript-first** with full type safety * **Zero or minimal dependencies** where possible * **Cross-runtime** — Node.js, Bun, Deno, and Edge support * **Well-tested** and maintained in a single monorepo ## Quick Start [#quick-start] Install any package with your preferred package manager: pnpm npm yarn bun ```bash pnpm add @visulima/ ``` ```bash npm install @visulima/ ``` ```bash yarn add @visulima/ ``` ```bash bun add @visulima/ ``` ## Popular Packages [#popular-packages] ## Browse by Category [#browse-by-category] ## Project Links [#project-links] * [GitHub Repository](https://github.com/visulima/visulima) — Source code, issues, and contributions * [Changelog](/changelog) — Release history and breaking changes * [All Packages](/docs/packages) — Full package directory with descriptions # API Reference (/docs/packages/ansi/api) # API Reference [#api-reference] Complete reference for all ANSI escape code functions organized by category. ## Cursor Control [#cursor-control] Functions for cursor positioning, movement, and styling. ### cursorTo [#cursorto] Move cursor to absolute position. **Signature:** ```typescript function cursorTo(x: number, y?: number): string; ``` **Parameters:** * `x` (`number`) - Column position (0-based) * `y` (`number`, optional) - Row position (0-based) **Returns:** ANSI escape sequence string **Example:** ```typescript import { cursorTo } from "@visulima/ansi"; process.stdout.write(cursorTo(10, 5)); // Move to column 10, row 5 ``` *** ### cursorMove [#cursormove] Move cursor relative to current position. **Signature:** ```typescript function cursorMove(x: number, y: number): string; ``` **Parameters:** * `x` (`number`) - Columns to move (positive = right, negative = left) * `y` (`number`) - Rows to move (positive = down, negative = up) **Returns:** ANSI escape sequence string *** ### cursorUp / cursorDown / cursorForward / cursorBackward [#cursorup--cursordown--cursorforward--cursorbackward] Move cursor in specific direction. **Signatures:** ```typescript function cursorUp(count?: number): string; function cursorDown(count?: number): string; function cursorForward(count?: number): string; function cursorBackward(count?: number): string; ``` **Parameters:** * `count` (`number`, optional) - Number of positions to move. Default: `1` **Returns:** ANSI escape sequence string *** ### cursorLeft [#cursorleft] Move cursor to leftmost position (column 0). **Signature:** ```typescript const cursorLeft: string; ``` **Example:** ```typescript import { cursorLeft } from "@visulima/ansi"; process.stdout.write(cursorLeft); ``` *** ### cursorSave / cursorRestore [#cursorsave--cursorrestore] Save and restore cursor position. **Signatures:** ```typescript const cursorSave: string; const cursorRestore: string; ``` **Example:** ```typescript import { cursorSave, cursorRestore } from "@visulima/ansi"; process.stdout.write(cursorSave); // ... do work ... process.stdout.write(cursorRestore); ``` *** ### cursorHide / cursorShow [#cursorhide--cursorshow] Control cursor visibility. **Signatures:** ```typescript const cursorHide: string; const cursorShow: string; ``` **Example:** ```typescript import { cursorHide, cursorShow } from "@visulima/ansi"; process.stdout.write(cursorHide); // ... do work ... process.stdout.write(cursorShow); ``` *** ### setCursorStyle [#setcursorstyle] Set cursor appearance. **Signature:** ```typescript function setCursorStyle(style: CursorStyle): string; ``` **Parameters:** * `style` (`CursorStyle`) - One of: * `"blinking-block"` * `"steady-block"` * `"blinking-underline"` * `"steady-underline"` * `"blinking-bar"` * `"steady-bar"` **Returns:** ANSI escape sequence string **Example:** ```typescript import { setCursorStyle } from "@visulima/ansi"; process.stdout.write(setCursorStyle("blinking-bar")); ``` *** ## Screen Manipulation [#screen-manipulation] Functions for clearing and manipulating the screen. ### clearScreen [#clearscreen] Clear the entire screen. **Signature:** ```typescript const clearScreen: string; ``` **Example:** ```typescript import { clearScreen } from "@visulima/ansi"; process.stdout.write(clearScreen); ``` *** ### eraseDown / eraseUp [#erasedown--eraseup] Erase from cursor to end/start of screen. **Signatures:** ```typescript const eraseDown: string; const eraseUp: string; ``` *** ### eraseLine / eraseLineEnd / eraseLineStart [#eraseline--eraselineend--eraselinestart] Erase line or part of line. **Signatures:** ```typescript const eraseLine: string; const eraseLineEnd: string; const eraseLineStart: string; ``` **Example:** ```typescript import { eraseLine } from "@visulima/ansi"; process.stdout.write(eraseLine); console.log("New line content"); ``` *** ### eraseLines [#eraselines] Erase multiple lines. **Signature:** ```typescript function eraseLines(count: number): string; ``` **Parameters:** * `count` (`number`) - Number of lines to erase **Returns:** ANSI escape sequence string *** ### clearScreenAndHomeCursor [#clearscreenandhomecursor] Clear screen and move cursor to home position (0, 0). **Signature:** ```typescript const clearScreenAndHomeCursor: string; ``` *** ### alternativeScreenOn / alternativeScreenOff [#alternativescreenon--alternativescreenoff] Switch between normal and alternative screen buffers. **Signatures:** ```typescript const alternativeScreenOn: string; const alternativeScreenOff: string; ``` **Example:** ```typescript import { alternativeScreenOn, alternativeScreenOff } from "@visulima/ansi"; process.stdout.write(alternativeScreenOn); // Full-screen app here process.stdout.write(alternativeScreenOff); ``` *** ### scrollUp / scrollDown [#scrollup--scrolldown] Scroll screen region. **Signatures:** ```typescript function scrollUp(count?: number): string; function scrollDown(count?: number): string; ``` **Parameters:** * `count` (`number`, optional) - Number of lines to scroll. Default: `1` **Returns:** ANSI escape sequence string *** ## Hyperlinks [#hyperlinks] ### hyperlink [#hyperlink] Create clickable hyperlink in terminal. **Signature:** ```typescript function hyperlink(url: string, text: string, options?: HyperlinkOptions): string; ``` **Parameters:** * `url` (`string`) - URL to link to * `text` (`string`) - Display text * `options` (`HyperlinkOptions`, optional) - Additional options * `id` (`string`, optional) - Unique ID for the link **Returns:** Formatted hyperlink string **Example:** ```typescript import { hyperlink } from "@visulima/ansi"; const link = hyperlink("https://github.com", "GitHub"); console.log(`Visit ${link} for more info`); ``` *** ## Images [#images] ### image [#image] Display image in iTerm2. **Signature:** ```typescript function image(buffer: Buffer | Uint8Array, options?: ImageOptions): string; ``` **Parameters:** * `buffer` (`Buffer | Uint8Array`) - Image data * `options` (`ImageOptions`, optional) - Display options * `width` (`number | string`, optional) - Width (pixels, percent, or "auto") * `height` (`number | string`, optional) - Height (pixels, percent, or "auto") * `preserveAspectRatio` (`boolean`, optional) - Preserve aspect ratio. Default: `true` * `inline` (`boolean`, optional) - Display inline with text. Default: `false` **Returns:** iTerm2 image escape sequence **Example:** ```typescript import { image } from "@visulima/ansi"; import { readFileSync } from "fs"; const img = readFileSync("logo.png"); process.stdout.write(image(img, { width: "50%", height: "auto" })); ``` *** ## Strip ANSI [#strip-ansi] ### strip [#strip] Remove ANSI escape codes from string. **Signature:** ```typescript function strip(text: string): string; ``` **Parameters:** * `text` (`string`) - String containing ANSI codes **Returns:** String with ANSI codes removed **Example:** ```typescript import { strip } from "@visulima/ansi"; import { cursorUp } from "@visulima/ansi"; const withAnsi = `Hello${cursorUp(2)} World`; const clean = strip(withAnsi); console.log(clean); // "Hello World" ``` *** ## Window Control [#window-control] ### setWindowTitle [#setwindowtitle] Set terminal window title. **Signature:** ```typescript function setWindowTitle(title: string): string; ``` **Parameters:** * `title` (`string`) - Window title **Returns:** ANSI escape sequence string **Example:** ```typescript import { setWindowTitle } from "@visulima/ansi"; process.stdout.write(setWindowTitle("My App")); ``` *** ### setIconName [#seticonname] Set window icon name. **Signature:** ```typescript function setIconName(name: string): string; ``` **Parameters:** * `name` (`string`) - Icon name **Returns:** ANSI escape sequence string *** ### setIconNameAndWindowTitle [#seticonnameandwindowtitle] Set both icon name and window title. **Signature:** ```typescript function setIconNameAndWindowTitle(title: string): string; ``` **Parameters:** * `title` (`string`) - Title for both icon and window **Returns:** ANSI escape sequence string *** ### Window Operations [#window-operations] Control window state and position. **Signatures:** ```typescript const minimizeWindow: string; const maximizeWindow: string; const raiseWindow: string; const lowerWindow: string; const restoreWindow: string; const refreshWindow: string; function moveWindow(x: number, y: number): string; function resizeTextAreaChars(width: number, height: number): string; function resizeTextAreaPixels(width: number, height: number): string; ``` **Example:** ```typescript import { maximizeWindow, moveWindow } from "@visulima/ansi"; process.stdout.write(maximizeWindow); process.stdout.write(moveWindow(100, 100)); ``` *** ## Mouse Events [#mouse-events] ### Enable/Disable Mouse Tracking [#enabledisable-mouse-tracking] **Signatures:** ```typescript const enableNormalMouse: string; const disableNormalMouse: string; const enableButtonEventMouse: string; const disableButtonEventMouse: string; const enableAnyEventMouse: string; const disableAnyEventMouse: string; const enableX10Mouse: string; const disableX10Mouse: string; ``` **Example:** ```typescript import { enableNormalMouse, disableNormalMouse } from "@visulima/ansi"; // Enable mouse tracking process.stdout.write(enableNormalMouse); // ... handle mouse events ... // Disable when done process.stdout.write(disableNormalMouse); ``` *** ## Terminal Modes [#terminal-modes] ### setMode / resetMode [#setmode--resetmode] Set or reset terminal modes. **Signatures:** ```typescript function setMode(mode: string): string; function resetMode(mode: string): string; ``` **Common Modes:** * `"IRM"` - Insert/Replace Mode * `"LNM"` - Line Feed/New Line Mode * `"SRM"` - Send/Receive Mode * `"KAM"` - Keyboard Action Mode **Example:** ```typescript import { setMode, resetMode } from "@visulima/ansi"; process.stdout.write(setMode("IRM")); // ... do work in insert mode ... process.stdout.write(resetMode("IRM")); ``` *** ## Color Control [#color-control] Set, query and reset the terminal's foreground, background and cursor colors (`OSC 10/11/12`, reset via `OSC 110/111/112`). Color strings may be X11 names (`"red"`), hex (`"#ff0000"`) or `XParseColor` strings (`"rgb:ff/00/00"`). Caller-supplied colors are sanitized to prevent escape-sequence injection. **Signatures:** ```typescript function setForegroundColor(color: string): string; function setBackgroundColor(color: string): string; function setCursorColor(color: string): string; const requestForegroundColor: string; const requestBackgroundColor: string; const requestCursorColor: string; const resetForegroundColor: string; const resetBackgroundColor: string; const resetCursorColor: string; ``` **Example:** ```typescript import { setBackgroundColor, resetBackgroundColor } from "@visulima/ansi/background"; process.stdout.write(setBackgroundColor("#1e1e2e")); // ... later ... process.stdout.write(resetBackgroundColor); ``` *** ## Shell Integration [#shell-integration] ### Working Directory (OSC 7) [#working-directory-osc-7] Report the shell's current working directory so terminals (iTerm2, WezTerm, …) can open new tabs in the same place. **Signature:** ```typescript function notifyWorkingDirectory(host: string, ...paths: string[]): string; const setWorkingDirectory: typeof notifyWorkingDirectory; ``` **Example:** ```typescript import { notifyWorkingDirectory } from "@visulima/ansi/cwd"; process.stdout.write(notifyWorkingDirectory("", process.cwd())); ``` ### FinalTerm Prompt Marks (OSC 133) [#finalterm-prompt-marks-osc-133] Mark the structure of shell output (prompt / command / output boundaries) so terminals can offer jump-to-prompt and command-status features. **Signatures:** ```typescript function finalTerm(...pm: string[]): string; function finalTermPrompt(...pm: string[]): string; // A function finalTermCmdStart(...pm: string[]): string; // B function finalTermCmdExecuted(...pm: string[]): string; // C function finalTermCmdFinished(...pm: string[]): string; // D ``` *** ## Notifications [#notifications] Trigger desktop notifications using the simple `OSC 9` protocol or the extensible `OSC 99` protocol. Message, payload and metadata are sanitized to prevent escape-sequence injection. **Signatures:** ```typescript function notify(message: string): string; // OSC 9 function desktopNotification(payload: string, ...metadata: string[]): string; // OSC 99 ``` **Example:** ```typescript import { notify } from "@visulima/ansi/notification"; process.stdout.write(notify("Build finished")); ``` ### urxvt Extensions (OSC 777) [#urxvt-extensions-osc-777] Invoke rxvt-unicode Perl extensions. ```typescript import urxvtExtension from "@visulima/ansi/urxvt"; process.stdout.write(urxvtExtension("notify", "title", "body")); ``` *** ## Keypad Modes [#keypad-modes] Switch the numeric keypad between application and numeric mode. **Signatures:** ```typescript const keypadApplicationMode: string; // DECKPAM, ESC = const keypadNumericMode: string; // DECKPNM, ESC > const DECKPAM: string; const DECKPNM: string; ``` *** ## Character Sets (SCS) [#character-sets-scs] Designate a character set into one of the G0–G3 slots, plus locking-shift constants. **Signatures:** ```typescript function selectCharacterSet(gset: string, charset: string): string; const SCS: typeof selectCharacterSet; // G-set designators const G0: string; // "(" const G1: string; // ")" const G2: string; // "*" const G3: string; // "+" // Common charset identifiers const DEC_SPECIAL_GRAPHICS: string; // "0" const UNITED_KINGDOM: string; // "A" const USASCII: string; // "B" // Locking shifts const LS0: string; // SI const LS1: string; // SO const LS1R: string; const LS2: string; const LS2R: string; const LS3: string; const LS3R: string; ``` **Example:** ```typescript import { selectCharacterSet, G0, DEC_SPECIAL_GRAPHICS, USASCII } from "@visulima/ansi/charset"; process.stdout.write(selectCharacterSet(G0, DEC_SPECIAL_GRAPHICS)); // line-drawing mode // ... process.stdout.write(selectCharacterSet(G0, USASCII)); // back to ASCII ``` *** ## Bracketed Paste [#bracketed-paste] Markers a terminal emits around pasted text (`CSI 200~` / `CSI 201~`), plus a wrap helper. **Signatures:** ```typescript const bracketedPasteStart: string; const bracketedPasteEnd: string; function wrapBracketedPaste(text: string): string; ``` *** ## Focus Events [#focus-events] Events a terminal emits when the window gains/loses focus (DEC private mode 1004). Enable/disable reporting via `enableFocusTracking` / `disableFocusTracking` (from `@visulima/ansi/mouse`). **Signatures:** ```typescript const FOCUS: string; // CSI I const BLUR: string; // CSI O const focusInEvent: string; const focusOutEvent: string; ``` *** ## Graphics [#graphics] Emit-only wrappers that frame already-encoded graphics payloads. They do **not** encode pixel/image data — the caller supplies encoded data (and, for Kitty, Base64 + chunking). **Signatures:** ```typescript // Sixel: DCS aspectRatio ; backgroundMode ; gridSize q payload ST function sixelGraphics(aspectRatio: number, backgroundMode: number, gridSize: number, payload: string): string; // Kitty graphics: APC _G options ; payload ST function kittyGraphics(payload: string, ...options: string[]): string; ``` **Example:** ```typescript import kittyGraphics from "@visulima/ansi/kitty-graphics"; // Transmit and display a Base64-encoded PNG already in `data`. process.stdout.write(kittyGraphics(data, "a=T", "f=100")); ``` *** ## Constants [#constants] ### Cursor Movement [#cursor-movement] ```typescript const CURSOR_UP_1: string; const CURSOR_DOWN_1: string; const CURSOR_FORWARD_1: string; const CURSOR_BACKWARD_1: string; const CURSOR_TO_COLUMN_1: string; ``` ### Screen Operations [#screen-operations] ```typescript const ALT_SCREEN_ON: string; const ALT_SCREEN_OFF: string; const SCROLL_UP_1: string; const SCROLL_DOWN_1: string; ``` ### Cursor State [#cursor-state] ```typescript const SAVE_CURSOR_DEC: string; const RESTORE_CURSOR_DEC: string; ``` ### Reset [#reset] ```typescript const RESET_INITIAL_STATE: string; const RIS: string; // Reset to Initial State ``` *** ## Submodule Reference [#submodule-reference] ### Import by Category [#import-by-category] All functions can be imported from specific submodules: **Cursor:** `@visulima/ansi/cursor` ```typescript import { cursorTo, cursorMove, cursorUp, cursorDown, cursorHide, cursorShow } from "@visulima/ansi/cursor"; ``` **Clear:** `@visulima/ansi/clear` ```typescript import { clearScreen, clearScreenAndHomeCursor, resetTerminal } from "@visulima/ansi/clear"; ``` **Erase:** `@visulima/ansi/erase` ```typescript import { eraseLine, eraseDown, eraseUp, eraseLines } from "@visulima/ansi/erase"; ``` **Hyperlink:** `@visulima/ansi/hyperlink` ```typescript import { hyperlink } from "@visulima/ansi/hyperlink"; ``` **Image:** `@visulima/ansi/image` ```typescript import { image } from "@visulima/ansi/image"; ``` **Strip:** `@visulima/ansi/strip` ```typescript import { strip } from "@visulima/ansi/strip"; ``` **Other submodules:** `alternative-screen`, `scroll`, `title`, `window-ops`, `mouse`, `mode`, `status`, `iterm2`, `xterm`, `screen`, `passthrough`, `reset`, `background`, `cwd`, `finalterm`, `notification`, `urxvt`, `keypad`, `charset`, `paste`, `focus`, `sixel`, `kitty-graphics` *** ## Function Summary by Category [#function-summary-by-category] | Category | Key Functions | | --------------------- | ------------------------------------------------------------ | | **Cursor Position** | `cursorTo`, `cursorMove`, `cursorSave`, `cursorRestore` | | **Cursor Movement** | `cursorUp`, `cursorDown`, `cursorForward`, `cursorBackward` | | **Cursor Visibility** | `cursorHide`, `cursorShow`, `setCursorStyle` | | **Screen Clear** | `clearScreen`, `eraseDown`, `eraseUp` | | **Line Erase** | `eraseLine`, `eraseLineEnd`, `eraseLineStart`, `eraseLines` | | **Screen Buffer** | `alternativeScreenOn`, `alternativeScreenOff` | | **Scrolling** | `scrollUp`, `scrollDown` | | **Hyperlinks** | `hyperlink` | | **Images** | `image` | | **Strip** | `strip` | | **Window** | `setWindowTitle`, `maximizeWindow`, `moveWindow` | | **Mouse** | `enableNormalMouse`, `disableNormalMouse` | | **Modes** | `setMode`, `resetMode` | | **Color** | `setForegroundColor`, `setBackgroundColor`, `setCursorColor` | | **Shell Integration** | `notifyWorkingDirectory`, `finalTermPrompt` | | **Notifications** | `notify`, `desktopNotification`, `urxvtExtension` | | **Keypad** | `keypadApplicationMode`, `keypadNumericMode` | | **Character Sets** | `selectCharacterSet`, `SCS` | | **Paste** | `bracketedPasteStart`, `wrapBracketedPaste` | | **Focus** | `FOCUS`, `BLUR` | | **Graphics** | `sixelGraphics`, `kittyGraphics` | *** ## Terminal Support [#terminal-support] | Feature | iTerm2 | Terminal.app | Windows Terminal | VS Code | | ------------------ | ------ | ------------ | ---------------- | ------- | | Cursor Control | ✅ | ✅ | ✅ | ✅ | | Screen Clearing | ✅ | ✅ | ✅ | ✅ | | Alternative Screen | ✅ | ✅ | ✅ | ✅ | | Hyperlinks | ✅ | ✅ | ✅ | ✅ | | Images | ✅ | ❌ | ❌ | ❌ | | Mouse Events | ✅ | ✅ | ✅ | ✅ | | Window Ops | ✅ | ⚠️ | ⚠️ | ❌ | ✅ Full support | ⚠️ Partial support | ❌ Not supported *** ## Next Steps [#next-steps] Learn how to use these functions with examples Return to package overview # Introduction (/docs/packages/ansi) # @visulima/ansi [#visulimaansi] Comprehensive ANSI escape codes for terminal control. Take full control of your terminal output with cursor positioning, screen clearing, hyperlinks, images, and much more—all through simple, composable functions. ## Key Features [#key-features] **Cursor Control** * Position cursor anywhere on screen * Move cursor in any direction * Show, hide, and style cursor * Save and restore cursor position **Screen Manipulation** * Clear screen or parts of it * Alternative screen buffer support * Scroll regions up and down * Erase lines and characters **Advanced Terminal Features** * Clickable hyperlinks in terminal * Display images (iTerm2 support) * Mouse event tracking * Terminal mode management * Foreground/background/cursor color control (OSC 10/11/12) * Shell integration (OSC 7 working directory, OSC 133 FinalTerm marks) * Desktop notifications (OSC 9 / OSC 99 / urxvt OSC 777) * Focus events, bracketed paste, keypad and character-set selection * Sixel and Kitty graphics sequence wrappers **Window and Title Control** * Set window and icon titles * Window operations (minimize, maximize, etc.) * Report window state and position * Status reporting **Utility Functions** * Strip ANSI codes from strings * tmux passthrough support * Terminal capability queries * Cross-platform compatibility ## Quick Start [#quick-start] Install the package: ```bash npm install @visulima/ansi ``` Control the cursor: ```typescript import { cursorUp, cursorLeft, cursorTo } from "@visulima/ansi"; // Move cursor up 2 rows and to the left process.stdout.write(cursorUp(2) + cursorLeft); // => '\u001B[2A\u001B[1000D' // Move cursor to column 10, row 5 process.stdout.write(cursorTo(10, 5)); ``` Clear the screen: ```typescript import { clearScreen, eraseDown } from "@visulima/ansi"; // Clear entire screen process.stdout.write(clearScreen); // Clear from cursor to end of screen process.stdout.write(eraseDown); ``` ## Use Cases [#use-cases] ### Build Interactive CLI Applications [#build-interactive-cli-applications] Create dynamic terminal UIs with precise cursor control: ```typescript import { cursorTo, eraseLine, cursorHide, cursorShow } from "@visulima/ansi"; function updateProgress(percent: number) { process.stdout.write(cursorHide); process.stdout.write(cursorTo(0)); process.stdout.write(eraseLine); process.stdout.write(`Progress: [${"=".repeat(percent / 2)}${" ".repeat(50 - percent / 2)}] ${percent}%`); if (percent === 100) { process.stdout.write("\n"); process.stdout.write(cursorShow); } } // Simulate progress for (let i = 0; i <= 100; i += 10) { updateProgress(i); await new Promise((resolve) => setTimeout(resolve, 200)); } ``` ### Create Clickable Terminal Links [#create-clickable-terminal-links] Add hyperlinks that work in modern terminals: ```typescript import { hyperlink } from "@visulima/ansi"; const link = hyperlink("https://github.com/visulima", "Visit Visulima on GitHub"); console.log(link); // Displays: Visit Visulima on GitHub (clickable in supported terminals) ``` ### Display Images in Terminal [#display-images-in-terminal] Show images directly in iTerm2: ```typescript import { image } from "@visulima/ansi"; import { readFileSync } from "fs"; const imageData = readFileSync("logo.png"); process.stdout.write( image(imageData, { width: "50%", height: "auto", }), ); ``` ### Clean ANSI Codes from Strings [#clean-ansi-codes-from-strings] Strip ANSI codes for logging or processing: ```typescript import { strip } from "@visulima/ansi"; import { cursorUp, cursorLeft } from "@visulima/ansi"; const withAnsi = `Hello ${cursorUp(2)}${cursorLeft} World`; const cleaned = strip(withAnsi); console.log(cleaned); // "Hello World" (no ANSI codes) ``` ## Next Steps [#next-steps] Learn about installation and import methods Explore ANSI codes organized by category Complete API documentation for all functions ## Browser and Server Support [#browser-and-server-support] This package is designed for Node.js terminal applications: * **Node.js:** ≥22.13 ≤25.x (full support) * **Browsers:** Not applicable (terminal-specific functionality) * **Deno/Bun:** Compatible with terminal-supporting runtimes Requires a terminal that supports ANSI escape codes (most modern terminals). # Installation (/docs/packages/ansi/installation) # Installation [#installation] Install the package using your preferred package manager: `bash npm install @visulima/ansi ` `bash pnpm add @visulima/ansi ` `bash yarn add @visulima/ansi ` `bash bun add @visulima/ansi ` ## Import Methods [#import-methods] ### Main Package Import [#main-package-import] Import everything from the main entry point: ```typescript import { cursorUp, cursorLeft, clearScreen, hyperlink } from "@visulima/ansi"; process.stdout.write(cursorUp(2) + cursorLeft); process.stdout.write(clearScreen); console.log(hyperlink("https://example.com", "Click here")); ``` ### Submodule Imports [#submodule-imports] Import from specific submodules for better tree-shaking: ```typescript // Cursor functions import { cursorUp, cursorLeft, cursorTo } from "@visulima/ansi/cursor"; // Screen clearing import { clearScreen, clearScreenFromTopLeft } from "@visulima/ansi/clear"; // Erase functions import { eraseLine, eraseDown } from "@visulima/ansi/erase"; // Hyperlinks import { hyperlink } from "@visulima/ansi/hyperlink"; // Strip ANSI codes import { strip } from "@visulima/ansi/strip"; ``` ### Available Submodules [#available-submodules] The package provides these submodules for targeted imports: * **`@visulima/ansi/cursor`** - Cursor positioning and movement * **`@visulima/ansi/clear`** - Screen and line clearing * **`@visulima/ansi/erase`** - Text erasure functions * **`@visulima/ansi/hyperlink`** - Clickable terminal links * **`@visulima/ansi/image`** - Image display (iTerm2) * **`@visulima/ansi/strip`** - Strip ANSI codes * **`@visulima/ansi/scroll`** - Scrolling control * **`@visulima/ansi/alternative-screen`** - Alternative screen buffer * **`@visulima/ansi/title`** - Window title control * **`@visulima/ansi/window-ops`** - Window operations * **`@visulima/ansi/mouse`** - Mouse event handling * **`@visulima/ansi/mode`** - Terminal mode management * **`@visulima/ansi/status`** - Status reporting * **`@visulima/ansi/iterm2`** - iTerm2-specific features * **`@visulima/ansi/xterm`** - XTerm-specific features * **`@visulima/ansi/screen`** - Screen operations * **`@visulima/ansi/passthrough`** - tmux/screen passthrough * **`@visulima/ansi/reset`** - Terminal reset ### CommonJS [#commonjs] For CommonJS environments: ```javascript const { cursorUp, cursorLeft, clearScreen } = require("@visulima/ansi"); // Or from submodules const { cursorUp } = require("@visulima/ansi/cursor"); ``` ## Requirements [#requirements] * **Node.js:** ≥22.13 ≤25.x * **Terminal:** ANSI escape code support (most modern terminals) * **TypeScript:** ≥5.0 (optional, for type definitions) ## Verify Installation [#verify-installation] Test the installation with a simple cursor movement: ```typescript import { cursorUp, cursorLeft } from "@visulima/ansi"; // Move cursor up 2 rows and to the left process.stdout.write(cursorUp(2) + cursorLeft); console.log("Cursor moved!"); ``` If you see the cursor move, installation was successful! ## TypeScript Support [#typescript-support] The package includes comprehensive TypeScript type definitions: ```typescript import type { CursorOptions } from "@visulima/ansi"; import { cursorTo, cursorMove } from "@visulima/ansi"; // Fully typed function calls const position: string = cursorTo(10, 5); const movement: string = cursorMove(2, 3); ``` ## Terminal Compatibility [#terminal-compatibility] ANSI escape codes work in most modern terminals: **Full Support:** * iTerm2 (macOS) - including images and extended features * Terminal.app (macOS) * GNOME Terminal (Linux) * Konsole (Linux) * Windows Terminal * VS Code integrated terminal * Hyper * Alacritty * Kitty **Partial Support:** * cmd.exe (Windows) - limited ANSI support * PowerShell - basic ANSI support **Not Supported:** * Very old terminal emulators * Non-interactive environments without TTY To check if your terminal supports ANSI codes: ```typescript import { cursorUp } from "@visulima/ansi"; const isTTY = process.stdout.isTTY; const supportsAnsi = process.env.TERM !== "dumb" && isTTY; if (supportsAnsi) { process.stdout.write(cursorUp(1)); } else { console.log("ANSI not supported"); } ``` ## Next Steps [#next-steps] Learn how to use ANSI codes with examples Explore the complete API documentation # Usage Guide (/docs/packages/ansi/usage) # Usage Guide [#usage-guide] Learn how to use ANSI escape codes to control your terminal output. ## Cursor Control [#cursor-control] ### Position Cursor [#position-cursor] Move the cursor to specific coordinates: ```typescript import { cursorTo } from "@visulima/ansi"; // Move to column 10, row 5 process.stdout.write(cursorTo(10, 5)); console.log("Text at position (10, 5)"); // Move to column 0 (start of line) process.stdout.write(cursorTo(0)); console.log("Back to line start"); ``` ### Move Cursor Relative [#move-cursor-relative] Move cursor relative to current position: ```typescript import { cursorUp, cursorDown, cursorForward, cursorBackward } from "@visulima/ansi"; // Move up 3 lines process.stdout.write(cursorUp(3)); // Move down 2 lines process.stdout.write(cursorDown(2)); // Move forward 5 columns process.stdout.write(cursorForward(5)); // Move backward 3 columns process.stdout.write(cursorBackward(3)); ``` Convenience shortcuts for single movements: ```typescript import { cursorLeft, CURSOR_UP_1, CURSOR_DOWN_1 } from "@visulima/ansi"; // Move left to column 0 process.stdout.write(cursorLeft); // Move up one line process.stdout.write(CURSOR_UP_1); // Move down one line process.stdout.write(CURSOR_DOWN_1); ``` ### Save and Restore Cursor [#save-and-restore-cursor] Save cursor position and restore later: ```typescript import { cursorSave, cursorRestore, cursorTo } from "@visulima/ansi"; // Save current position process.stdout.write(cursorSave); // Move somewhere else process.stdout.write(cursorTo(20, 10)); console.log("Temporary text"); // Restore original position process.stdout.write(cursorRestore); console.log("Back to saved position"); ``` ### Show and Hide Cursor [#show-and-hide-cursor] Control cursor visibility: ```typescript import { cursorHide, cursorShow } from "@visulima/ansi"; // Hide cursor during operation process.stdout.write(cursorHide); // Do work... console.log("Processing..."); await someLongOperation(); // Show cursor again process.stdout.write(cursorShow); ``` ### Style Cursor [#style-cursor] Change cursor appearance: ```typescript import { setCursorStyle } from "@visulima/ansi"; // Blinking block cursor process.stdout.write(setCursorStyle("blinking-block")); // Steady underline cursor process.stdout.write(setCursorStyle("steady-underline")); // Blinking bar cursor process.stdout.write(setCursorStyle("blinking-bar")); ``` ## Screen Manipulation [#screen-manipulation] ### Clear Screen [#clear-screen] Clear the entire screen or parts of it: ```typescript import { clearScreen, eraseDown, eraseUp } from "@visulima/ansi"; // Clear entire screen process.stdout.write(clearScreen); // Clear from cursor to end of screen process.stdout.write(eraseDown); // Clear from cursor to top of screen process.stdout.write(eraseUp); ``` Clear and move cursor home: ```typescript import { clearScreenAndHomeCursor, clearScreenFromTopLeft } from "@visulima/ansi"; // Clear and move to (0, 0) process.stdout.write(clearScreenAndHomeCursor); // Clear from top-left process.stdout.write(clearScreenFromTopLeft); ``` ### Clear Lines [#clear-lines] Erase lines or parts of lines: ```typescript import { eraseLine, eraseLineEnd, eraseLineStart } from "@visulima/ansi"; // Erase entire current line process.stdout.write(eraseLine); // Erase from cursor to end of line process.stdout.write(eraseLineEnd); // Erase from cursor to start of line process.stdout.write(eraseLineStart); ``` Erase multiple lines: ```typescript import { eraseLines } from "@visulima/ansi"; // Erase 3 lines process.stdout.write(eraseLines(3)); ``` ### Alternative Screen [#alternative-screen] Use alternative screen buffer (like vim/less): ```typescript import { alternativeScreenOn, alternativeScreenOff } from "@visulima/ansi"; // Switch to alternative screen process.stdout.write(alternativeScreenOn); // Your full-screen app here console.log("Full-screen interface"); // Switch back to normal screen process.stdout.write(alternativeScreenOff); ``` ### Scrolling [#scrolling] Scroll screen regions: ```typescript import { scrollUp, scrollDown } from "@visulima/ansi"; // Scroll up 5 lines process.stdout.write(scrollUp(5)); // Scroll down 3 lines process.stdout.write(scrollDown(3)); ``` ## Hyperlinks [#hyperlinks] ### Create Clickable Links [#create-clickable-links] Make terminal output clickable: ```typescript import { hyperlink } from "@visulima/ansi"; const link = hyperlink("https://github.com/visulima", "Visulima on GitHub"); console.log(`Check out ${link} for more info!`); ``` With IDs for duplicate links: ```typescript import { hyperlink } from "@visulima/ansi"; const link1 = hyperlink("https://example.com", "Example 1", { id: "link-1" }); const link2 = hyperlink("https://example.com", "Example 2", { id: "link-2" }); console.log(link1); console.log(link2); ``` ## Images (iTerm2) [#images-iterm2] ### Display Images [#display-images] Show images in iTerm2: ```typescript import { image } from "@visulima/ansi"; import { readFileSync } from "fs"; const imageData = readFileSync("logo.png"); // Display image process.stdout.write( image(imageData, { width: "50%", height: "auto", preserveAspectRatio: true, }), ); ``` Inline images: ```typescript import { image } from "@visulima/ansi"; import { readFileSync } from "fs"; const icon = readFileSync("icon.png"); // Display inline with text process.stdout.write(`Status: ${image(icon, { width: 20, height: 20, inline: true })} Complete`); ``` ## Strip ANSI Codes [#strip-ansi-codes] ### Remove ANSI from Strings [#remove-ansi-from-strings] Clean ANSI codes for logging or storage: ```typescript import { strip } from "@visulima/ansi"; import { cursorUp, clearScreen } from "@visulima/ansi"; const withAnsi = `${clearScreen}Hello${cursorUp(2)} World`; const cleaned = strip(withAnsi); console.log(cleaned); // "Hello World" (no ANSI codes) ``` Useful for measuring actual text length: ```typescript import { strip } from "@visulima/ansi"; import { hyperlink } from "@visulima/ansi"; const link = hyperlink("https://example.com", "Example"); const textOnly = strip(link); console.log(link.length); // Long (includes ANSI codes) console.log(textOnly.length); // 7 (just "Example") ``` ## Window Control [#window-control] ### Set Window Title [#set-window-title] Change terminal window title: ```typescript import { setWindowTitle, setIconName } from "@visulima/ansi"; // Set window title process.stdout.write(setWindowTitle("My CLI App")); // Set icon name process.stdout.write(setIconName("CLI")); ``` Set both at once: ```typescript import { setIconNameAndWindowTitle } from "@visulima/ansi"; process.stdout.write(setIconNameAndWindowTitle("CLI App")); ``` ### Window Operations [#window-operations] Control window state (terminal support varies): ```typescript import { minimizeWindow, maximizeWindow, raiseWindow, lowerWindow } from "@visulima/ansi"; // Minimize window process.stdout.write(minimizeWindow); // Maximize window process.stdout.write(maximizeWindow); // Bring window to front process.stdout.write(raiseWindow); // Send window to back process.stdout.write(lowerWindow); ``` Move and resize window: ```typescript import { moveWindow, resizeTextAreaChars } from "@visulima/ansi"; // Move window to position (100, 100) process.stdout.write(moveWindow(100, 100)); // Resize to 80 columns, 24 rows process.stdout.write(resizeTextAreaChars(80, 24)); ``` ## Mouse Events [#mouse-events] ### Enable Mouse Tracking [#enable-mouse-tracking] Track mouse events in terminal: ```typescript import { enableNormalMouse, disableNormalMouse } from "@visulima/ansi"; import { stdin } from "process"; // Enable mouse tracking process.stdout.write(enableNormalMouse); // Listen for mouse events stdin.setRawMode(true); stdin.on("data", (data) => { // Parse mouse event from data console.log("Mouse event:", data); }); // Disable when done process.on("exit", () => { process.stdout.write(disableNormalMouse); stdin.setRawMode(false); }); ``` Different mouse modes: ```typescript import { enableButtonEventMouse, enableAnyEventMouse, enableX10Mouse } from "@visulima/ansi"; // Track button presses and releases process.stdout.write(enableButtonEventMouse); // Track all mouse movement process.stdout.write(enableAnyEventMouse); // Simple X10 mouse protocol process.stdout.write(enableX10Mouse); ``` ## Terminal Modes [#terminal-modes] ### Control Terminal Behavior [#control-terminal-behavior] Enable and disable terminal modes: ```typescript import { setMode, resetMode } from "@visulima/ansi"; // Enable insert mode process.stdout.write(setMode("IRM")); // Disable insert mode process.stdout.write(resetMode("IRM")); ``` Common modes: ```typescript import { SetLineFeedNewLineMode, ResetLineFeedNewLineMode } from "@visulima/ansi"; // Enable line feed/new line mode process.stdout.write(SetLineFeedNewLineMode); // Disable it process.stdout.write(ResetLineFeedNewLineMode); ``` ## Real-World Examples [#real-world-examples] ### Progress Bar [#progress-bar] Create an updating progress bar: ```typescript import { cursorHide, cursorShow, cursorTo, eraseLine } from "@visulima/ansi"; function showProgress(current: number, total: number) { const percent = Math.floor((current / total) * 100); const filled = Math.floor(percent / 2); const bar = "█".repeat(filled) + "░".repeat(50 - filled); process.stdout.write(cursorHide); process.stdout.write(cursorTo(0)); process.stdout.write(eraseLine); process.stdout.write(`Progress: [${bar}] ${percent}%`); if (current === total) { process.stdout.write("\n"); process.stdout.write(cursorShow); } } // Use it for (let i = 0; i <= 100; i++) { showProgress(i, 100); await new Promise((resolve) => setTimeout(resolve, 50)); } ``` ### Spinner Animation [#spinner-animation] Create a loading spinner: ```typescript import { cursorHide, cursorShow, cursorBackward, eraseLine } from "@visulima/ansi"; const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]; async function spinner(message: string, duration: number) { process.stdout.write(cursorHide); let frame = 0; const interval = setInterval(() => { process.stdout.write(cursorBackward(100)); process.stdout.write(eraseLine); process.stdout.write(`${frames[frame]} ${message}`); frame = (frame + 1) % frames.length; }, 80); await new Promise((resolve) => setTimeout(resolve, duration)); clearInterval(interval); process.stdout.write(cursorBackward(100)); process.stdout.write(eraseLine); process.stdout.write(cursorShow); } // Use it await spinner("Loading...", 3000); console.log("✓ Complete!"); ``` ### Interactive Menu [#interactive-menu] Build a simple menu with cursor control: ```typescript import { cursorHide, cursorShow, cursorUp, cursorTo, eraseLine } from "@visulima/ansi"; const options = ["Option 1", "Option 2", "Option 3", "Exit"]; let selected = 0; function drawMenu() { console.log("\nSelect an option:"); options.forEach((option, index) => { const prefix = index === selected ? ">" : " "; console.log(`${prefix} ${option}`); }); } function updateMenu() { process.stdout.write(cursorUp(options.length + 1)); process.stdout.write(cursorTo(0)); options.forEach((option, index) => { process.stdout.write(eraseLine); const prefix = index === selected ? ">" : " "; console.log(`${prefix} ${option}`); }); } // Initial draw drawMenu(); // Handle keyboard input process.stdin.setRawMode(true); process.stdin.on("data", (data) => { const key = data.toString(); if (key === "\u001b[A") { // Up arrow selected = Math.max(0, selected - 1); updateMenu(); } else if (key === "\u001b[B") { // Down arrow selected = Math.min(options.length - 1, selected + 1); updateMenu(); } else if (key === "\r") { // Enter console.log(`\nYou selected: ${options[selected]}`); process.exit(0); } else if (key === "\u0003") { // Ctrl+C process.exit(0); } }); ``` ### Full-Screen App [#full-screen-app] Create a full-screen terminal application: ```typescript import { alternativeScreenOn, alternativeScreenOff, clearScreen, cursorTo, cursorHide, cursorShow } from "@visulima/ansi"; async function fullScreenApp() { // Enter alternative screen process.stdout.write(alternativeScreenOn); process.stdout.write(clearScreen); process.stdout.write(cursorHide); // Draw UI process.stdout.write(cursorTo(0, 0)); console.log("┌────────────────────────────┐"); console.log("│ My Full-Screen App │"); console.log("└────────────────────────────┘"); // Wait for input await new Promise((resolve) => { process.stdin.setRawMode(true); process.stdin.once("data", resolve); }); // Clean up process.stdout.write(cursorShow); process.stdout.write(alternativeScreenOff); process.stdin.setRawMode(false); } // Run it await fullScreenApp(); ``` ### Status Updates [#status-updates] Update status line without scrolling: ```typescript import { cursorSave, cursorRestore, cursorTo, eraseLine } from "@visulima/ansi"; let statusLine = 0; function setStatus(message: string) { process.stdout.write(cursorSave); process.stdout.write(cursorTo(0, statusLine)); process.stdout.write(eraseLine); process.stdout.write(`Status: ${message}`); process.stdout.write(cursorRestore); } // Use it console.log("Starting process..."); statusLine = 1; setStatus("Initializing..."); await delay(1000); console.log("Doing work..."); setStatus("Processing..."); await delay(2000); setStatus("Complete!"); ``` ## Next Steps [#next-steps] Complete API documentation for all functions Return to package overview # API Reference (/docs/packages/boxen/api) # API Reference [#api-reference] Complete API documentation for **@visulima/boxen**. ## Import [#import] ```typescript import { boxen } from "@visulima/boxen"; import type { Options, BorderStyle } from "@visulima/boxen"; ``` ## Main Function [#main-function] ### `boxen(text: string, options?: Options): string` [#boxentext-string-options-options-string] Create a box around text with customizable options. **Parameters:** * `text` (string): Content to display inside the box * `options` (Options, optional): Configuration options **Returns:** String containing the formatted box **Example:** ```typescript const box = boxen("Hello!", { padding: 1 }); console.log(box); ``` *** ## Options [#options] ### `borderStyle` [#borderstyle] Type: `string | BorderStyleObject` Default: `"single"` Border style to use. Can be a built-in style name or custom border characters. **Built-in Styles:** * `"single"` - Single line border (default) * `"double"` - Double line border * `"round"` - Rounded corners * `"bold"` - Bold border * `"singleDouble"` - Single horizontal, double vertical * `"doubleSingle"` - Double horizontal, single vertical * `"classic"` - Plus and pipe characters * `"arrow"` - Arrow characters **Custom Border:** ```typescript { topLeft: string; topRight: string; bottomLeft: string; bottomRight: string; top: string; bottom: string; left: string; right: string; } ``` **Example:** ```typescript // Built-in style boxen("text", { borderStyle: "double" }); // Custom border boxen("text", { borderStyle: { topLeft: "╭", topRight: "╮", bottomLeft: "╰", bottomRight: "╯", top: "─", bottom: "─", left: "│", right: "│", }, }); ``` *** ### `borderColor` [#bordercolor] Type: `(border: string, position: BorderPosition, length: number) => string` Function to colorize border characters. Called for each border segment. **Parameters:** * `border` (string): The border character(s) * `position` (BorderPosition): Position of the border * `length` (number): Length of the border segment **BorderPosition Type:** ```typescript type BorderPosition = "topLeft" | "top" | "topRight" | "left" | "right" | "bottomLeft" | "bottom" | "bottomRight"; ``` **Example:** ```typescript import { red, green } from "@visulima/colorize"; boxen("text", { borderColor: (border, position) => { if (position.includes("top")) { return red(border); } return green(border); }, }); ``` *** ### `padding` [#padding] Type: `number | number[] | PaddingObject` Default: `0` Space inside the box between content and border. **Number:** Apply same padding to all sides ```typescript { padding: 1; } ``` **Array:** ```typescript { padding: [1, 2]; } // top/bottom: 1, left/right: 2 { padding: [1, 2, 3]; } // top: 1, left/right: 2, bottom: 3 { padding: [1, 2, 3, 4]; } // top: 1, right: 2, bottom: 3, left: 4 ``` **Object:** ```typescript { padding: { top: 1, right: 2, bottom: 1, left: 2, } } ``` **Example:** ```typescript boxen("text", { padding: 1 }); boxen("text", { padding: [1, 2] }); boxen("text", { padding: { top: 1, bottom: 1 } }); ``` *** ### `margin` [#margin] Type: `number | number[] | MarginObject` Default: `0` Space outside the box. **Number:** Apply same margin to all sides ```typescript { margin: 1; } ``` **Array:** Same format as padding **Object:** ```typescript { margin: { top: 1, right: 2, bottom: 1, left: 2, } } ``` **Example:** ```typescript boxen("text", { margin: 1 }); boxen("text", { margin: [1, 0] }); // top/bottom only ``` *** ### `textAlignment` [#textalignment] Type: `"left" | "center" | "right"` Default: `"left"` Horizontal alignment of text within the box. **Example:** ```typescript boxen("Center me", { textAlignment: "center" }); boxen("Right align", { textAlignment: "right" }); ``` *** ### `headerText` [#headertext] Type: `string` Default: `undefined` Text to display in the top border. **Example:** ```typescript boxen("Content", { headerText: "Title", headerAlignment: "center", }); ``` *** ### `headerAlignment` [#headeralignment] Type: `"left" | "center" | "right"` Default: `"left"` Alignment of header text in the top border. **Example:** ```typescript boxen("Content", { headerText: "Status", headerAlignment: "center", }); ``` *** ### `footerText` [#footertext] Type: `string` Default: `undefined` Text to display in the bottom border. **Example:** ```typescript boxen("Content", { footerText: "Press Enter", footerAlignment: "center", }); ``` *** ### `footerAlignment` [#footeralignment] Type: `"left" | "center" | "right"` Default: `"left"` Alignment of footer text in the bottom border. **Example:** ```typescript boxen("Content", { footerText: "v1.0.0", footerAlignment: "right", }); ``` *** ### `width` [#width] Type: `number | "terminal"` Default: Automatic based on content Fixed width for the box. Set to `"terminal"` to use full terminal width. **Example:** ```typescript // Fixed width (content wraps) boxen("Long text that will wrap", { width: 20 }); // Full terminal width boxen("Full width", { width: "terminal" }); ``` *** ## TypeScript Types [#typescript-types] ### `Options` [#options-1] Complete options interface: ```typescript interface Options { borderStyle?: BorderStyle | BorderStyleObject; borderColor?: (border: string, position: BorderPosition, length: number) => string; padding?: number | number[] | PaddingObject; margin?: number | number[] | MarginObject; textAlignment?: "left" | "center" | "right"; headerText?: string; headerAlignment?: "left" | "center" | "right"; footerText?: string; footerAlignment?: "left" | "center" | "right"; width?: number | "terminal"; } ``` ### `BorderStyle` [#borderstyle-1] Built-in border style names: ```typescript type BorderStyle = "single" | "double" | "round" | "bold" | "singleDouble" | "doubleSingle" | "classic" | "arrow"; ``` ### `BorderStyleObject` [#borderstyleobject] Custom border characters: ```typescript interface BorderStyleObject { topLeft: string; topRight: string; bottomLeft: string; bottomRight: string; top: string; bottom: string; left: string; right: string; } ``` ### `BorderPosition` [#borderposition] Border segment positions: ```typescript type BorderPosition = "topLeft" | "top" | "topRight" | "left" | "right" | "bottomLeft" | "bottom" | "bottomRight"; ``` ### `PaddingObject` [#paddingobject] Padding configuration: ```typescript interface PaddingObject { top?: number; right?: number; bottom?: number; left?: number; } ``` ### `MarginObject` [#marginobject] Margin configuration: ```typescript interface MarginObject { top?: number; right?: number; bottom?: number; left?: number; } ``` ## Complete Example [#complete-example] ```typescript import { boxen } from "@visulima/boxen"; import type { Options } from "@visulima/boxen"; import { green, blue } from "@visulima/colorize"; const options: Options = { // Border borderStyle: "double", borderColor: (border, position) => { if (position.includes("top")) { return blue(border); } return green(border); }, // Layout padding: 1, margin: { top: 1, bottom: 1 }, width: 40, // Text textAlignment: "center", // Header/Footer headerText: "Status", headerAlignment: "center", footerText: "v1.0.0", footerAlignment: "right", }; console.log(boxen("All options used!", options)); ``` # Introduction (/docs/packages/boxen) # @visulima/boxen [#visulimaboxen] Create beautiful boxes in the terminal with customizable borders, padding, and alignment. Perfect for highlighting messages, creating banners, and improving CLI application aesthetics. ## Why @visulima/boxen? [#why-visulimaboxen] Terminal boxes make your CLI output more professional and easier to read. **@visulima/boxen** provides: * **Multiple border styles** - Single, double, round, bold, and more * **Custom coloring** - Color different parts of the border independently * **Flexible layout** - Configure padding, margins, and text alignment * **Headers and footers** - Add labeled sections to your boxes * **TypeScript support** - Full type definitions included * **Cross-platform** - Works on Windows, macOS, and Linux ## Key Features [#key-features] **Border Styles** * **8 built-in styles** - single, double, round, bold, and mixed styles * **Custom borders** - Define your own border characters * **Position-based coloring** - Different colors for each border section **Layout Control** * **Padding** - Add space inside the box (top, right, bottom, left) * **Margins** - Add space outside the box * **Text alignment** - left, center, or right alignment * **Fixed width** - Set maximum box width **Headers & Footers** * **Header text** - Add labeled headers with alignment * **Footer text** - Add labeled footers with alignment * **Custom positioning** - Align headers and footers independently **Advanced Features** * **Gradient borders** - Create colorful gradient effects * **Multi-line support** - Automatic handling of multi-line text * **Width calculation** - Automatic sizing based on content ## Quick Start [#quick-start] ### Installation [#installation] ```bash npm2yarn npm install @visulima/boxen ``` ### Basic Usage [#basic-usage] ```typescript import { boxen } from "@visulima/boxen"; // Simple box console.log(boxen("Hello, World!")); // Output: // ┌─────────────┐ // │Hello, World!│ // └─────────────┘ // With padding console.log(boxen("unicorn", { padding: 1 })); // Output: // ┌─────────────┐ // │ │ // │ unicorn │ // │ │ // └─────────────┘ ``` ## Use Cases [#use-cases] ### CLI Messages [#cli-messages] Highlight important messages in your CLI applications: ```typescript import { boxen } from "@visulima/boxen"; import { green, red, yellow } from "@visulima/colorize"; // Success message console.log( boxen(green("✓ Build completed successfully!"), { padding: 1, borderStyle: "round", borderColor: () => green("─"), }), ); // Error message console.log( boxen(red("✗ Build failed: Missing configuration"), { padding: 1, borderStyle: "double", borderColor: () => red("═"), }), ); // Warning message console.log( boxen(yellow("⚠ Deprecated API usage detected"), { padding: 1, borderStyle: "bold", }), ); ``` ### Application Banners [#application-banners] Create eye-catching banners for your CLI tools: ```typescript import { boxen } from "@visulima/boxen"; import { cyan, bold } from "@visulima/colorize"; const banner = boxen(bold(cyan("MyApp CLI v2.0.0\n")) + "Build amazing applications\n" + "https://myapp.com", { padding: 1, margin: 1, borderStyle: "double", textAlignment: "center", }); console.log(banner); ``` ### Update Notifications [#update-notifications] Display update notifications with clear formatting: ```typescript import { boxen } from "@visulima/boxen"; import { yellow, green } from "@visulima/colorize"; console.log( boxen(yellow("Update available: ") + "v2.0.0 → " + green("v2.1.0") + "\n" + "Run " + green("npm install -g myapp") + " to update", { padding: 1, margin: 1, borderStyle: "round", headerText: "Update Notification", headerAlignment: "center", }), ); ``` ### Configuration Summaries [#configuration-summaries] Display configuration or status information: ```typescript import { boxen } from "@visulima/boxen"; import { gray, cyan } from "@visulima/colorize"; const config = [ `${gray("Environment:")} ${cyan("production")}`, `${gray("Port:")} ${cyan("3000")}`, `${gray("Database:")} ${cyan("postgres://localhost:5432")}`, `${gray("Cache:")} ${cyan("enabled")}`, ].join("\n"); console.log( boxen(config, { padding: 1, headerText: "Server Configuration", headerAlignment: "center", footerText: "Press Ctrl+C to stop", footerAlignment: "center", }), ); ``` ## Visual Examples [#visual-examples] ### Border Styles [#border-styles] ```typescript import { boxen } from "@visulima/boxen"; // Single border (default) boxen("single", { borderStyle: "single" }); // ┌──────┐ // │single│ // └──────┘ // Double border boxen("double", { borderStyle: "double" }); // ╔══════╗ // ║double║ // ╚══════╝ // Round corners boxen("round", { borderStyle: "round" }); // ╭─────╮ // │round│ // ╰─────╯ // Bold border boxen("bold", { borderStyle: "bold" }); // ┏━━━━┓ // ┃bold┃ // ┗━━━━┛ ``` ### With Padding and Margins [#with-padding-and-margins] ```typescript import { boxen } from "@visulima/boxen"; // Padding adds space inside boxen("padded", { padding: 1 }); // ┌────────┐ // │ │ // │ padded │ // │ │ // └────────┘ // Margins add space outside boxen("margin", { margin: 1 }); // // ┌──────┐ // │margin│ // └──────┘ // ``` ### Text Alignment [#text-alignment] ```typescript import { boxen } from "@visulima/boxen"; // Left aligned (default) boxen("Left\nAligned\nText", { textAlignment: "left" }); // Center aligned boxen("Center\nAligned\nText", { textAlignment: "center" }); // Right aligned boxen("Right\nAligned\nText", { textAlignment: "right" }); ``` ## Comparison [#comparison] ### vs. boxen (original) [#vs-boxen-original] | Feature | @visulima/boxen | boxen | | ----------------------- | --------------- | ----- | | Border styles | ✅ | ✅ | | Custom colors | ✅ | ✅ | | Headers/footers | ✅ | ✅ | | TypeScript native | ✅ | ⚠️ | | Position-based coloring | ✅ | ❌ | | Gradient borders | ✅ | ❌ | | Modern ESM | ✅ | ⚠️ | | Active maintenance | ✅ | ⚠️ | ## Next Steps [#next-steps] Learn how to install and set up boxen Create your first terminal box in 5 minutes Explore all border styles and options Complete API documentation ## Browser and Server Support [#browser-and-server-support] ### Node.js [#nodejs] * ✅ Node.js 22.13 - 25.x * ✅ ESM and CommonJS support ### Operating Systems [#operating-systems] * ✅ Windows (including Command Prompt and PowerShell) * ✅ macOS * ✅ Linux * ✅ Any terminal with Unicode support # Installation (/docs/packages/boxen/installation) # Installation [#installation] ## Package Manager [#package-manager] Install **@visulima/boxen** using your preferred package manager: ```bash npm2yarn npm install @visulima/boxen ``` ## Requirements [#requirements] * **Node.js:** 22.13 or higher * **TypeScript:** 5.0+ (optional, for TypeScript projects) * **Terminal:** Unicode support recommended for best visual results ## Import Methods [#import-methods] ### Node.js (ESM) [#nodejs-esm] ```typescript // Named import (recommended) import { boxen } from "@visulima/boxen"; console.log(boxen("Hello, World!")); ``` ### Node.js (CommonJS) [#nodejs-commonjs] ```javascript // CommonJS import const { boxen } = require("@visulima/boxen"); console.log(boxen("Hello, World!")); ``` ### TypeScript [#typescript] TypeScript definitions are included automatically: ```typescript import { boxen } from "@visulima/boxen"; import type { Options } from "@visulima/boxen"; const options: Options = { padding: 1, borderStyle: "double", }; console.log(boxen("Typed!", options)); ``` ## Optional: Color Support [#optional-color-support] For colored borders, install **@visulima/colorize**: ```bash npm2yarn npm install @visulima/colorize ``` Then use colors in your boxes: ```typescript import { boxen } from "@visulima/boxen"; import { green, blue } from "@visulima/colorize"; console.log( boxen(green("Success!"), { borderColor: () => green("─"), }), ); ``` ## Verification [#verification] Verify your installation works correctly: ```typescript import { boxen } from "@visulima/boxen"; console.log( boxen("Installation successful!", { padding: 1, borderStyle: "double", }), ); // Output: // ╔═══════════════════════════╗ // ║ ║ // ║ Installation successful! ║ // ║ ║ // ╚═══════════════════════════╝ ``` If you see a box similar to above, installation is successful! ## Terminal Compatibility [#terminal-compatibility] ### Unicode Support [#unicode-support] For best results, use a terminal with full Unicode support: **Recommended Terminals:** * ✅ Windows Terminal (Windows 10+) * ✅ iTerm2 (macOS) * ✅ Terminal.app (macOS) * ✅ GNOME Terminal (Linux) * ✅ Konsole (Linux) * ✅ Hyper * ✅ Alacritty **Limited Support:** * ⚠️ Windows Command Prompt (Windows 10+ with Unicode enabled) * ⚠️ PowerShell (works with PowerShell 7+) ### Fallback for Limited Terminals [#fallback-for-limited-terminals] If your terminal doesn't support Unicode box-drawing characters, use ASCII-only border styles: ```typescript import { boxen } from "@visulima/boxen"; // Custom ASCII border console.log( boxen("ASCII only", { borderStyle: { topLeft: "+", topRight: "+", bottomLeft: "+", bottomRight: "+", top: "-", bottom: "-", left: "|", right: "|", }, }), ); // Output: // +----------+ // |ASCII only| // +----------+ ``` ## Troubleshooting [#troubleshooting] ### Characters Not Displaying Correctly [#characters-not-displaying-correctly] If box characters appear as question marks or squares: 1. **Check terminal encoding:** ```bash echo $LANG # Should show UTF-8 (e.g., en_US.UTF-8) ``` 2. **Set UTF-8 encoding:** ```bash export LANG=en_US.UTF-8 export LC_ALL=en_US.UTF-8 ``` 3. **Use a different terminal** with better Unicode support ### Module Not Found [#module-not-found] If you see `Cannot find module '@visulima/boxen'`: ```bash npm2yarn # Clear cache and reinstall rm -rf node_modules package-lock.json npm install ``` ### TypeScript Errors [#typescript-errors] Ensure your `tsconfig.json` includes: ```json { "compilerOptions": { "moduleResolution": "node", "esModuleInterop": true, "module": "ES2020" } } ``` ## Next Steps [#next-steps] Create your first box in 5 minutes Learn about all available options # Quick Start (/docs/packages/boxen/quick-start) # Quick Start [#quick-start] Learn the essentials of **@visulima/boxen** in 5 minutes. ## Installation [#installation] ```bash npm2yarn npm install @visulima/boxen ``` ## Basic Box [#basic-box] Create a simple box around text: ```typescript import { boxen } from "@visulima/boxen"; console.log(boxen("Hello, World!")); ``` Output: ``` ┌─────────────┐ │Hello, World!│ └─────────────┘ ``` ## Add Padding [#add-padding] Add space inside the box: ```typescript import { boxen } from "@visulima/boxen"; console.log(boxen("Padded", { padding: 1 })); ``` Output: ``` ┌────────┐ │ │ │ Padded │ │ │ └────────┘ ``` ## Change Border Style [#change-border-style] Use different border styles: ```typescript import { boxen } from "@visulima/boxen"; // Double border console.log(boxen("Double", { borderStyle: "double" })); // Output: // ╔══════╗ // ║Double║ // ╚══════╝ // Round corners console.log(boxen("Round", { borderStyle: "round" })); // Output: // ╭─────╮ // │Round│ // ╰─────╯ // Bold border console.log(boxen("Bold", { borderStyle: "bold" })); // Output: // ┏━━━━┓ // ┃Bold┃ // ┗━━━━┛ ``` ## Add Colors [#add-colors] Color your boxes with @visulima/colorize: ```bash npm2yarn npm install @visulima/colorize ``` ```typescript import { boxen } from "@visulima/boxen"; import { green, red, blue } from "@visulima/colorize"; // Colored text console.log(boxen(green("Success!"), { padding: 1 })); // Colored border console.log( boxen("Important", { borderColor: () => red("─"), padding: 1, }), ); // Both colored console.log( boxen(blue("Information"), { borderColor: () => blue("─"), padding: 1, }), ); ``` ## Add Margins [#add-margins] Add space outside the box: ```typescript import { boxen } from "@visulima/boxen"; console.log(boxen("With margin", { margin: 1 })); ``` Output: ``` ┌───────────┐ │With margin│ └───────────┘ ``` ## Text Alignment [#text-alignment] Control text alignment inside the box: ```typescript import { boxen } from "@visulima/boxen"; // Center aligned console.log( boxen("Center\nAligned", { textAlignment: "center", padding: 1, }), ); // Right aligned console.log( boxen("Right\nAligned", { textAlignment: "right", padding: 1, }), ); ``` ## Add Headers [#add-headers] Add a labeled header to your box: ```typescript import { boxen } from "@visulima/boxen"; console.log( boxen("Important message here", { headerText: "Alert", headerAlignment: "center", padding: 1, }), ); ``` Output: ``` ┌───── Alert ─────┐ │ │ │ Important │ │ message here │ │ │ └─────────────────┘ ``` ## Add Footers [#add-footers] Add a footer with additional information: ```typescript import { boxen } from "@visulima/boxen"; console.log( boxen("Build completed successfully", { headerText: "Status", footerText: "Press any key to continue", footerAlignment: "center", padding: 1, }), ); ``` ## Multi-line Content [#multi-line-content] Boxes automatically handle multi-line text: ```typescript import { boxen } from "@visulima/boxen"; const message = `Line 1 Line 2 Line 3`; console.log(boxen(message, { padding: 1 })); ``` Output: ``` ┌────────┐ │ │ │ Line 1 │ │ Line 2 │ │ Line 3 │ │ │ └────────┘ ``` ## Set Fixed Width [#set-fixed-width] Control the box width: ```typescript import { boxen } from "@visulima/boxen"; console.log( boxen("This text will wrap to fit within the width", { width: 20, padding: 1, }), ); ``` ## Common Patterns [#common-patterns] ### Success Message [#success-message] ```typescript import { boxen } from "@visulima/boxen"; import { green } from "@visulima/colorize"; console.log( boxen(green("✓ Build completed successfully!"), { padding: 1, borderStyle: "round", borderColor: () => green("─"), }), ); ``` ### Error Message [#error-message] ```typescript import { boxen } from "@visulima/boxen"; import { red } from "@visulima/colorize"; console.log( boxen(red("✗ Build failed: Missing dependencies"), { padding: 1, borderStyle: "double", borderColor: () => red("═"), }), ); ``` ### Warning Message [#warning-message] ```typescript import { boxen } from "@visulima/boxen"; import { yellow } from "@visulima/colorize"; console.log( boxen(yellow("⚠ Deprecation warning"), { padding: 1, borderStyle: "bold", borderColor: () => yellow("━"), }), ); ``` ### Application Banner [#application-banner] ```typescript import { boxen } from "@visulima/boxen"; import { cyan, bold } from "@visulima/colorize"; const banner = bold(cyan("MyApp CLI v1.0.0")); const tagline = "Build amazing applications"; console.log( boxen(`${banner}\n${tagline}`, { padding: 1, margin: 1, borderStyle: "double", textAlignment: "center", }), ); ``` ### Status Display [#status-display] ```typescript import { boxen } from "@visulima/boxen"; import { green, gray } from "@visulima/colorize"; const status = [`${gray("Server:")} ${green("Running")}`, `${gray("Port:")} ${green("3000")}`, `${gray("Environment:")} ${green("production")}`].join("\n"); console.log( boxen(status, { padding: 1, headerText: "Server Status", headerAlignment: "center", }), ); ``` ### Update Notification [#update-notification] ```typescript import { boxen } from "@visulima/boxen"; import { yellow, green } from "@visulima/colorize"; console.log( boxen(`${yellow("Update available:")} v1.0.0 → ${green("v1.1.0")}\n` + `Run ${green("npm update")} to upgrade`, { padding: 1, margin: 1, borderStyle: "round", headerText: "Update", headerAlignment: "center", }), ); ``` ## Next Steps [#next-steps] Explore all border styles and options Complete API documentation # Usage Guide (/docs/packages/boxen/usage) # Usage Guide [#usage-guide] Complete guide to using **@visulima/boxen** for creating terminal boxes. ## Border Styles [#border-styles] ### Built-in Styles [#built-in-styles] **single** (default): ```typescript import { boxen } from "@visulima/boxen"; console.log(boxen("single", { borderStyle: "single" })); ``` Output: ``` ┌──────┐ │single│ └──────┘ ``` **double**: ```typescript console.log(boxen("double", { borderStyle: "double" })); ``` Output: ``` ╔══════╗ ║double║ ╚══════╝ ``` **round**: ```typescript console.log(boxen("round", { borderStyle: "round" })); ``` Output: ``` ╭─────╮ │round│ ╰─────╯ ``` **bold**: ```typescript console.log(boxen("bold", { borderStyle: "bold" })); ``` Output: ``` ┏━━━━┓ ┃bold┃ ┗━━━━┛ ``` **singleDouble** (single horizontal, double vertical): ```typescript console.log(boxen("singleDouble", { borderStyle: "singleDouble" })); ``` Output: ``` ╓────────────╖ ║singleDouble║ ╙────────────╜ ``` **doubleSingle** (double horizontal, single vertical): ```typescript console.log(boxen("doubleSingle", { borderStyle: "doubleSingle" })); ``` Output: ``` ╒════════════╕ │doubleSingle│ ╘════════════╛ ``` **classic**: ```typescript console.log(boxen("classic", { borderStyle: "classic" })); ``` Output: ``` +───────+ |classic| +───────+ ``` **arrow**: ```typescript console.log(boxen("arrow", { borderStyle: "arrow" })); ``` Output: ``` ↘↓↓↓↓↓↙ →arrow← ↗↑↑↑↑↑↖ ``` ### Custom Border Characters [#custom-border-characters] Define your own border style: ```typescript import { boxen } from "@visulima/boxen"; console.log( boxen("custom", { borderStyle: { topLeft: "╭", topRight: "╮", bottomLeft: "╰", bottomRight: "╯", top: "─", bottom: "─", left: "│", right: "│", }, }), ); ``` ## Padding and Margins [#padding-and-margins] ### Padding (Space Inside Box) [#padding-space-inside-box] **Uniform padding:** ```typescript import { boxen } from "@visulima/boxen"; console.log(boxen("padded", { padding: 1 })); ``` Output: ``` ┌────────┐ │ │ │ padded │ │ │ └────────┘ ``` **Individual sides:** ```typescript console.log( boxen("custom padding", { padding: { top: 1, right: 2, bottom: 1, left: 2, }, }), ); ``` **Shorthand:** ```typescript // Same as { top: 1, right: 2, bottom: 1, left: 2 } boxen("text", { padding: [1, 2] }); // Same as { top: 1, right: 2, bottom: 3, left: 2 } boxen("text", { padding: [1, 2, 3] }); // Same as { top: 1, right: 2, bottom: 3, left: 4 } boxen("text", { padding: [1, 2, 3, 4] }); ``` ### Margins (Space Outside Box) [#margins-space-outside-box] **Uniform margin:** ```typescript import { boxen } from "@visulima/boxen"; console.log(boxen("margin", { margin: 1 })); ``` Output: ``` ┌──────┐ │margin│ └──────┘ ``` **Individual sides:** ```typescript console.log( boxen("custom margin", { margin: { top: 1, right: 2, bottom: 1, left: 2, }, }), ); ``` ## Text Alignment [#text-alignment] ### Horizontal Alignment [#horizontal-alignment] **Left aligned** (default): ```typescript import { boxen } from "@visulima/boxen"; console.log( boxen("Left\nAligned\nText", { textAlignment: "left", padding: 1, }), ); ``` **Center aligned:** ```typescript console.log( boxen("Center\nAligned\nText", { textAlignment: "center", padding: 1, }), ); ``` Output: ``` ┌─────────┐ │ │ │ Center │ │ Aligned │ │ Text │ │ │ └─────────┘ ``` **Right aligned:** ```typescript console.log( boxen("Right\nAligned\nText", { textAlignment: "right", padding: 1, }), ); ``` Output: ``` ┌─────────┐ │ │ │ Right │ │ Aligned │ │ Text │ │ │ └─────────┘ ``` ## Headers and Footers [#headers-and-footers] ### Header Text [#header-text] **Centered header:** ```typescript import { boxen } from "@visulima/boxen"; console.log( boxen("Content goes here", { headerText: "Title", headerAlignment: "center", }), ); ``` Output: ``` ┌───── Title ─────┐ │Content goes here│ └─────────────────┘ ``` **Left aligned header:** ```typescript console.log( boxen("Content", { headerText: "Left", headerAlignment: "left", }), ); ``` Output: ``` ┌─ Left ──────┐ │Content │ └─────────────┘ ``` **Right aligned header:** ```typescript console.log( boxen("Content", { headerText: "Right", headerAlignment: "right", }), ); ``` Output: ``` ┌───────── Right ─┐ │Content │ └─────────────────┘ ``` ### Footer Text [#footer-text] **Centered footer:** ```typescript console.log( boxen("Content", { footerText: "Footer", footerAlignment: "center", }), ); ``` Output: ``` ┌───────────────┐ │Content │ └─── Footer ────┘ ``` ### Combined Headers and Footers [#combined-headers-and-footers] ```typescript console.log( boxen("Main content here", { headerText: "Header", headerAlignment: "center", footerText: "Footer", footerAlignment: "center", padding: 1, }), ); ``` Output: ``` ┌──── Header ────┐ │ │ │ Main content │ │ here │ │ │ └──── Footer ────┘ ``` ## Border Coloring [#border-coloring] ### Simple Color [#simple-color] ```typescript import { boxen } from "@visulima/boxen"; import { green } from "@visulima/colorize"; console.log( boxen("Success!", { borderColor: () => green("─"), }), ); ``` ### Position-Based Coloring [#position-based-coloring] Color different parts of the border independently: ```typescript import { boxen } from "@visulima/boxen"; import { red, green, yellow, blue } from "@visulima/colorize"; console.log( boxen("Colorful!", { borderColor: (border, position) => { // Top border if (["top", "topLeft", "topRight"].includes(position)) { return red(border); } // Left border if (position === "left") { return yellow(border); } // Right border if (position === "right") { return green(border); } // Bottom border if (["bottom", "bottomLeft", "bottomRight"].includes(position)) { return blue(border); } return border; }, }), ); ``` **Border Positions:** * `topLeft` - Top left corner * `top` - Top horizontal line * `topRight` - Top right corner * `left` - Left vertical line * `right` - Right vertical line * `bottomLeft` - Bottom left corner * `bottom` - Bottom horizontal line * `bottomRight` - Bottom right corner ### Gradient Borders [#gradient-borders] ```typescript import { boxen } from "@visulima/boxen"; import { gradient } from "@visulima/colorize/gradient"; const gradientFn = gradient("cyan", "pink"); console.log( boxen("Gradient borders!", { borderColor: (border, position, length) => { return gradientFn(border); }, }), ); ``` ## Width Control [#width-control] ### Fixed Width [#fixed-width] Set maximum box width: ```typescript import { boxen } from "@visulima/boxen"; console.log( boxen("This is a longer text that will wrap", { width: 20, }), ); ``` Output: ``` ┌──────────────────┐ │This is a longer │ │text that will │ │wrap │ └──────────────────┘ ``` ### Full Width [#full-width] Use terminal width: ```typescript import { boxen } from "@visulima/boxen"; console.log( boxen("Full width box", { width: "terminal", textAlignment: "center", }), ); ``` ## Advanced Examples [#advanced-examples] ### Status Box with Icons [#status-box-with-icons] ```typescript import { boxen } from "@visulima/boxen"; import { green, red, yellow } from "@visulima/colorize"; function statusBox(status: "success" | "error" | "warning", message: string) { const configs = { success: { icon: "✓", color: green, border: "round", }, error: { icon: "✗", color: red, border: "double", }, warning: { icon: "⚠", color: yellow, border: "bold", }, }; const config = configs[status]; return boxen(`${config.icon} ${message}`, { padding: 1, borderStyle: config.border as any, borderColor: () => config.color("─"), }); } console.log(statusBox("success", "Build completed")); console.log(statusBox("error", "Build failed")); console.log(statusBox("warning", "Deprecated API")); ``` ### Information Panel [#information-panel] ```typescript import { boxen } from "@visulima/boxen"; import { gray, cyan } from "@visulima/colorize"; const info = [ `${gray("Name:")} ${cyan("MyApp")}`, `${gray("Version:")} ${cyan("1.0.0")}`, `${gray("Status:")} ${cyan("Running")}`, `${gray("Port:")} ${cyan("3000")}`, ].join("\n"); console.log( boxen(info, { padding: 1, headerText: "Application Info", headerAlignment: "center", borderStyle: "double", }), ); ``` ### Multi-Section Box [#multi-section-box] ```typescript import { boxen } from "@visulima/boxen"; import { bold, dim } from "@visulima/colorize"; const sections = [ bold("Section 1"), "Content for section 1", "", bold("Section 2"), "Content for section 2", "", dim("Note: This is a multi-section box"), ].join("\n"); console.log( boxen(sections, { padding: 1, headerText: "Documentation", footerText: "Press Enter to continue", footerAlignment: "center", }), ); ``` ## Next Steps [#next-steps] Complete API documentation with all options # API Reference (/docs/packages/bytes/api) # API Reference [#api-reference] Complete reference for all functions in the `@visulima/bytes` package. ## Array Operations [#array-operations] ### concat [#concat] Concatenates an array of byte slices into a single slice. **Signature:** ```typescript function concat(arrays: Uint8Array[]): Uint8Array; ``` **Parameters:** * `arrays` (`Uint8Array[]`) - Array of Uint8Arrays to concatenate **Returns:** `Uint8Array` - A new Uint8Array containing all bytes from the input arrays **Example:** ```typescript import { concat } from "@visulima/bytes"; const result = concat([new Uint8Array([0, 1, 2]), new Uint8Array([3, 4, 5])]); console.log(result); // Uint8Array([0, 1, 2, 3, 4, 5]) ``` *** ### copy [#copy] Copies bytes from the source array to the destination array and returns the number of bytes copied. **Signature:** ```typescript function copy(src: Uint8Array, dst: Uint8Array, offset?: number): number; ``` **Parameters:** * `src` (`Uint8Array`) - Source array to copy from * `dst` (`Uint8Array`) - Destination array to copy into * `offset` (`number`, optional) - Position in destination to start copying to. Default: `0` **Returns:** `number` - Number of bytes copied **Example:** ```typescript import { copy } from "@visulima/bytes"; const src = new Uint8Array([9, 8, 7]); const dst = new Uint8Array([0, 1, 2, 3, 4, 5]); const copied = copy(src, dst); console.log(copied); // 3 console.log(dst); // Uint8Array([9, 8, 7, 3, 4, 5]) ``` *** ### equals [#equals] Checks if two byte slices are equal. **Signature:** ```typescript function equals(a: Uint8Array, b: Uint8Array): boolean; ``` **Parameters:** * `a` (`Uint8Array`) - First array to compare * `b` (`Uint8Array`) - Second array to compare **Returns:** `boolean` - `true` if arrays are equal, `false` otherwise **Example:** ```typescript import { equals } from "@visulima/bytes"; const a = new Uint8Array([1, 2, 3]); const b = new Uint8Array([1, 2, 3]); console.log(equals(a, b)); // true ``` *** ### repeat [#repeat] Returns a new byte slice composed of count repetitions of the source array. **Signature:** ```typescript function repeat(source: Uint8Array, count: number): Uint8Array; ``` **Parameters:** * `source` (`Uint8Array`) - Array to repeat * `count` (`number`) - Number of times to repeat (must be ≥ 0) **Returns:** `Uint8Array` - New array with repeated bytes **Example:** ```typescript import { repeat } from "@visulima/bytes"; const result = repeat(new Uint8Array([0, 1, 2]), 3); console.log(result); // Uint8Array([0, 1, 2, 0, 1, 2, 0, 1, 2]) ``` *** ## Search Functions [#search-functions] ### indexOfNeedle [#indexofneedle] Returns the index of the first occurrence of the needle array in the source array, or -1 if not present. **Signature:** ```typescript function indexOfNeedle(source: Uint8Array, needle: Uint8Array, start?: number): number; ``` **Parameters:** * `source` (`Uint8Array`) - Array to search in * `needle` (`Uint8Array`) - Sequence to search for * `start` (`number`, optional) - Index to start searching from. Default: `0` **Returns:** `number` - Index of first occurrence, or `-1` if not found **Example:** ```typescript import { indexOfNeedle } from "@visulima/bytes"; const source = new Uint8Array([0, 1, 2, 1, 2, 1, 2, 3]); const needle = new Uint8Array([1, 2]); console.log(indexOfNeedle(source, needle)); // 1 console.log(indexOfNeedle(source, needle, 2)); // 3 ``` *** ### lastIndexOfNeedle [#lastindexofneedle] Returns the index of the last occurrence of the needle array in the source array, or -1 if not present. **Signature:** ```typescript function lastIndexOfNeedle(source: Uint8Array, needle: Uint8Array, start?: number): number; ``` **Parameters:** * `source` (`Uint8Array`) - Array to search in * `needle` (`Uint8Array`) - Sequence to search for * `start` (`number`, optional) - Index to search backwards from. Default: `source.length - 1` **Returns:** `number` - Index of last occurrence, or `-1` if not found **Example:** ```typescript import { lastIndexOfNeedle } from "@visulima/bytes"; const source = new Uint8Array([0, 1, 2, 1, 2, 1, 2, 3]); const needle = new Uint8Array([1, 2]); console.log(lastIndexOfNeedle(source, needle)); // 5 ``` *** ### includesNeedle [#includesneedle] Determines whether the source array contains the needle array. **Signature:** ```typescript function includesNeedle(source: Uint8Array, needle: Uint8Array, start?: number): boolean; ``` **Parameters:** * `source` (`Uint8Array`) - Array to search in * `needle` (`Uint8Array`) - Sequence to search for * `start` (`number`, optional) - Index to start searching from. Default: `0` **Returns:** `boolean` - `true` if needle is found, `false` otherwise **Example:** ```typescript import { includesNeedle } from "@visulima/bytes"; const source = new Uint8Array([0, 1, 2, 1, 2, 1, 2, 3]); const needle = new Uint8Array([1, 2]); console.log(includesNeedle(source, needle)); // true ``` *** ### startsWith [#startswith] Returns true if the prefix array appears at the start of the source array, false otherwise. **Signature:** ```typescript function startsWith(source: Uint8Array, prefix: Uint8Array): boolean; ``` **Parameters:** * `source` (`Uint8Array`) - Array to check * `prefix` (`Uint8Array`) - Prefix to look for **Returns:** `boolean` - `true` if source starts with prefix, `false` otherwise **Example:** ```typescript import { startsWith } from "@visulima/bytes"; const source = new Uint8Array([0, 1, 2, 1, 2, 1, 2, 3]); const prefix = new Uint8Array([0, 1, 2]); console.log(startsWith(source, prefix)); // true ``` *** ### endsWith [#endswith] Returns true if the suffix array appears at the end of the source array, false otherwise. **Signature:** ```typescript function endsWith(source: Uint8Array, suffix: Uint8Array): boolean; ``` **Parameters:** * `source` (`Uint8Array`) - Array to check * `suffix` (`Uint8Array`) - Suffix to look for **Returns:** `boolean` - `true` if source ends with suffix, `false` otherwise **Example:** ```typescript import { endsWith } from "@visulima/bytes"; const source = new Uint8Array([0, 1, 2, 1, 2, 1, 2, 3]); const suffix = new Uint8Array([1, 2, 3]); console.log(endsWith(source, suffix)); // true ``` *** ## Type Conversion [#type-conversion] ### bufferToUint8Array [#buffertouint8array] Converts a Node.js Buffer to a Uint8Array. Creates a new Uint8Array view on the Buffer's underlying ArrayBuffer, ensuring correct byteOffset and length. **Signature:** ```typescript function bufferToUint8Array(buf: Buffer): Uint8Array; ``` **Parameters:** * `buf` (`Buffer`) - The Buffer to convert **Returns:** `Uint8Array` - A Uint8Array view of the Buffer **Example:** ```typescript import { bufferToUint8Array } from "@visulima/bytes"; import { Buffer } from "node:buffer"; const buffer = Buffer.from("Hello"); const result = bufferToUint8Array(buffer); console.log(result); // Uint8Array([72, 101, 108, 108, 111]) ``` *** ### isUint8Array [#isuint8array] Checks if a value is a Uint8Array or, in a Node.js environment, a Buffer. **Signature:** ```typescript function isUint8Array(x: unknown): x is Uint8Array; ``` **Parameters:** * `x` (`unknown`) - The value to check **Returns:** `boolean` - `true` if x is a Uint8Array or Buffer, `false` otherwise **Example:** ```typescript import { isUint8Array } from "@visulima/bytes"; import { Buffer } from "node:buffer"; console.log(isUint8Array(new Uint8Array([1, 2]))); // true console.log(isUint8Array(Buffer.from("test"))); // true (in Node.js) console.log(isUint8Array([1, 2])); // false ``` *** ### asciiToUint8Array [#asciitouint8array] Converts an ASCII string, array of strings, or template literal to a Uint8Array. Each character is converted to its ASCII byte value (0-255). Characters outside this range will be truncated. **Signature:** ```typescript function asciiToUint8Array(txt: TemplateStringsArray | string | [string]): Uint8Array; ``` **Parameters:** * `txt` (`TemplateStringsArray | string | [string]`) - Input string or template literal **Returns:** `Uint8Array` - ASCII encoded byte array **Example:** ```typescript import { asciiToUint8Array } from "@visulima/bytes"; const result = asciiToUint8Array("Hello!"); console.log(result); // Uint8Array([72, 101, 108, 108, 111, 33]) // Template literal const name = "World"; const result2 = asciiToUint8Array(`Hello ${name}`); console.log(result2); // Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]) ``` *** ### utf8ToUint8Array [#utf8touint8array] Converts a UTF-8 string, array of strings, or template literal to a Uint8Array. Requires Node.js Buffer support. **Signature:** ```typescript function utf8ToUint8Array(txt: TemplateStringsArray | [string] | string): Uint8Array; ``` **Parameters:** * `txt` (`TemplateStringsArray | [string] | string`) - Input string or template literal **Returns:** `Uint8Array` - UTF-8 encoded byte array **Example:** ```typescript import { utf8ToUint8Array } from "@visulima/bytes"; // ASCII string const result1 = utf8ToUint8Array("Hello!"); console.log(result1); // Uint8Array([72, 101, 108, 108, 111, 33]) // Multi-byte UTF-8 characters const result2 = utf8ToUint8Array("你好"); console.log(result2); // Uint8Array([228, 189, 160, 229, 165, 189]) // Emoji const result3 = utf8ToUint8Array("🌍"); console.log(result3); // Uint8Array([240, 159, 140, 141]) ``` *** ### toUint8Array [#touint8array] Attempts to convert various data types to a Uint8Array. Supports Uint8Array (returns as is), ArrayBuffer, Array of numbers, and in Node.js also supports Buffer and strings. **Signature:** ```typescript function toUint8Array(data: unknown): Uint8Array; ``` **Parameters:** * `data` (`unknown`) - The data to convert **Returns:** `Uint8Array` - Converted byte array **Throws:** `Error` with message `'UINT8ARRAY_INCOMPATIBLE'` if the data cannot be converted **Example:** ```typescript import { toUint8Array } from "@visulima/bytes"; import { Buffer } from "node:buffer"; // From Uint8Array console.log(toUint8Array(new Uint8Array([1, 2, 3]))); // Uint8Array([1, 2, 3]) // From ArrayBuffer const buffer = new ArrayBuffer(3); console.log(toUint8Array(buffer)); // Uint8Array([0, 0, 0]) // From Array of numbers console.log(toUint8Array([4, 5, 6])); // Uint8Array([4, 5, 6]) // From Node.js Buffer console.log(toUint8Array(Buffer.from("Node"))); // Uint8Array([78, 111, 100, 101]) // From string (in Node.js) console.log(toUint8Array("String")); // Uint8Array([83, 116, 114, 105, 110, 103]) // Incompatible types throw error try { toUint8Array(123); } catch (error) { console.log(error.message); // "UINT8ARRAY_INCOMPATIBLE: Cannot convert data to Uint8Array" } ``` *** ## Function Summary [#function-summary] | Function | Category | Description | | -------------------- | ---------------- | --------------------------------- | | `concat` | Array Operations | Concatenate multiple byte arrays | | `copy` | Array Operations | Copy bytes between arrays | | `equals` | Array Operations | Compare arrays for equality | | `repeat` | Array Operations | Repeat byte sequence | | `indexOfNeedle` | Search | Find first occurrence of sequence | | `lastIndexOfNeedle` | Search | Find last occurrence of sequence | | `includesNeedle` | Search | Check if array contains sequence | | `startsWith` | Search | Check if array starts with prefix | | `endsWith` | Search | Check if array ends with suffix | | `bufferToUint8Array` | Conversion | Convert Buffer to Uint8Array | | `isUint8Array` | Conversion | Type check for Uint8Array | | `asciiToUint8Array` | Conversion | Convert ASCII string to bytes | | `utf8ToUint8Array` | Conversion | Convert UTF-8 string to bytes | | `toUint8Array` | Conversion | Universal converter to Uint8Array | ## TypeScript Types [#typescript-types] The package exports TypeScript types for all functions. All functions have full type safety: ```typescript import type { Uint8Array } from "@visulima/bytes"; // All functions are strongly typed const arrays: Uint8Array[] = [new Uint8Array([1, 2]), new Uint8Array([3, 4])]; ``` ## Next Steps [#next-steps] Learn how to use these functions with detailed examples Return to package overview # Introduction (/docs/packages/bytes) # @visulima/bytes [#visulimabytes] Utility functions to make dealing with Uint8Arrays easier. Work with binary data like a pro—concatenate, copy, search, and convert between Uint8Arrays, Buffers, strings, and more. ## Key Features [#key-features] **Array Operations** * Concatenate multiple byte arrays into one * Copy bytes between arrays with offset support * Repeat byte sequences any number of times * Compare byte arrays for equality **Search and Pattern Matching** * Find sequences within byte arrays (first/last occurrence) * Check if arrays start or end with specific patterns * Determine if byte sequences contain specific needles **Type Conversion** * Convert between Node.js Buffers and Uint8Arrays * Transform ASCII/UTF-8 strings to byte arrays * Universal converter for multiple data types * Type-safe checks for Uint8Array instances ## Quick Start [#quick-start] Install the package: ```bash npm install @visulima/bytes ``` Concatenate byte arrays: ```typescript import { concat } from "@visulima/bytes"; const a = new Uint8Array([0, 1, 2]); const b = new Uint8Array([3, 4, 5]); const result = concat([a, b]); console.log(result); // Uint8Array([0, 1, 2, 3, 4, 5]) ``` ## Use Cases [#use-cases] ### Processing Binary Data in APIs [#processing-binary-data-in-apis] Handle binary data from HTTP requests or file uploads: ```typescript import { concat, toUint8Array } from "@visulima/bytes"; async function processBinaryUpload(chunks: unknown[]) { // Convert all chunks to Uint8Arrays const arrays = chunks.map((chunk) => toUint8Array(chunk)); // Combine into single array const combined = concat(arrays); return combined; } ``` ### Search Binary Protocols [#search-binary-protocols] Find specific byte sequences in protocol data: ```typescript import { indexOfNeedle, startsWith } from "@visulima/bytes"; const data = new Uint8Array([0x00, 0x01, 0xff, 0xaa, 0xbb, 0x01, 0xff]); const header = new Uint8Array([0x00, 0x01]); const pattern = new Uint8Array([0x01, 0xff]); // Check protocol header if (startsWith(data, header)) { console.log("Valid protocol header"); } // Find pattern position const position = indexOfNeedle(data, pattern); console.log(`Pattern found at position: ${position}`); // 1 ``` ### Convert Between Text and Binary [#convert-between-text-and-binary] Work with string encodings in Node.js: ```typescript import { utf8ToUint8Array, asciiToUint8Array } from "@visulima/bytes"; // UTF-8 encoding (supports emojis and international characters) const utf8Data = utf8ToUint8Array("Hello 世界 🌍"); // ASCII encoding (fast, but only 0-255 range) const asciiData = asciiToUint8Array("Hello World"); console.log(utf8Data); // Uint8Array with UTF-8 bytes console.log(asciiData); // Uint8Array([72, 101, 108, 108, 111, ...]) ``` ## Next Steps [#next-steps] Learn how to install and import the package Explore all functions with detailed examples Complete API documentation with parameters and types ## Browser and Server Support [#browser-and-server-support] This package works in both Node.js and browser environments: * **Node.js:** Full support including Buffer conversion utilities * **Browsers:** Core Uint8Array operations (Buffer-specific functions require polyfills) * **Deno/Bun:** Compatible with all modern JavaScript runtimes Requires Node.js ≥22.13 for full functionality. # Installation (/docs/packages/bytes/installation) # Installation [#installation] Install the package using your preferred package manager: `bash npm install @visulima/bytes ` `bash pnpm add @visulima/bytes ` `bash yarn add @visulima/bytes ` `bash bun add @visulima/bytes ` ## Import Methods [#import-methods] ### ESM (Recommended) [#esm-recommended] Import specific functions you need: ```typescript import { concat, equals, toUint8Array } from "@visulima/bytes"; ``` Import everything: ```typescript import * as bytes from "@visulima/bytes"; bytes.concat([...]); ``` ### CommonJS [#commonjs] For CommonJS environments: ```javascript const { concat, equals, toUint8Array } = require("@visulima/bytes"); ``` ## Requirements [#requirements] * **Node.js:** ≥22.13 ≤25.x * **TypeScript:** ≥5.0 (optional, but recommended for type safety) ## Verify Installation [#verify-installation] Verify the package works correctly: ```typescript import { concat } from "@visulima/bytes"; const result = concat([new Uint8Array([1, 2]), new Uint8Array([3, 4])]); console.log(result); // Uint8Array([1, 2, 3, 4]) ``` If you see the expected output, installation was successful! ## TypeScript Support [#typescript-support] The package includes full TypeScript type definitions. No additional `@types` packages are needed. ```typescript import type { Uint8ArrayList } from "@visulima/bytes"; import { concat } from "@visulima/bytes"; const arrays: Uint8Array[] = [new Uint8Array([1, 2]), new Uint8Array([3, 4])]; const result: Uint8Array = concat(arrays); ``` ## Next Steps [#next-steps] Learn how to use all available functions Explore the complete API documentation # Usage Guide (/docs/packages/bytes/usage) # Usage Guide [#usage-guide] Learn how to use all functions from the `@visulima/bytes` package with detailed examples. ## Array Operations [#array-operations] ### Concatenate Byte Arrays [#concatenate-byte-arrays] Combine multiple Uint8Arrays into a single array: ```typescript import { concat } from "@visulima/bytes"; const a = new Uint8Array([0, 1, 2]); const b = new Uint8Array([3, 4, 5]); const c = new Uint8Array([6, 7, 8]); const result = concat([a, b, c]); console.log(result); // Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8]) ``` Works with empty arrays: ```typescript import { concat } from "@visulima/bytes"; const empty = new Uint8Array([]); const data = new Uint8Array([1, 2, 3]); console.log(concat([empty, data])); // Uint8Array([1, 2, 3]) console.log(concat([data, empty])); // Uint8Array([1, 2, 3]) ``` ### Copy Bytes Between Arrays [#copy-bytes-between-arrays] Copy bytes from one array to another: ```typescript import { copy } from "@visulima/bytes"; const src = new Uint8Array([9, 8, 7]); const dst = new Uint8Array([0, 1, 2, 3, 4, 5]); const bytesCopied = copy(src, dst); console.log(bytesCopied); // 3 console.log(dst); // Uint8Array([9, 8, 7, 3, 4, 5]) ``` Copy with offset to control destination position: ```typescript import { copy } from "@visulima/bytes"; const src = new Uint8Array([1, 1, 1, 1]); const dst = new Uint8Array([0, 0, 0, 0]); const bytesCopied = copy(src, dst, 1); // Start at index 1 console.log(bytesCopied); // 3 (only 3 bytes fit) console.log(dst); // Uint8Array([0, 1, 1, 1]) ``` ### Repeat Byte Sequences [#repeat-byte-sequences] Create repetitions of a byte array: ```typescript import { repeat } from "@visulima/bytes"; const source = new Uint8Array([0, 1, 2]); console.log(repeat(source, 3)); // Uint8Array([0, 1, 2, 0, 1, 2, 0, 1, 2]) console.log(repeat(source, 1)); // Uint8Array([0, 1, 2]) console.log(repeat(source, 0)); // Uint8Array([]) ``` ### Compare Arrays for Equality [#compare-arrays-for-equality] Check if two byte arrays are identical: ```typescript import { equals } from "@visulima/bytes"; const a = new Uint8Array([1, 2, 3]); const b = new Uint8Array([1, 2, 3]); const c = new Uint8Array([4, 5, 6]); console.log(equals(a, b)); // true console.log(equals(a, c)); // false ``` Works with different lengths: ```typescript import { equals } from "@visulima/bytes"; const a = new Uint8Array([1, 2]); const b = new Uint8Array([1, 2, 3]); console.log(equals(a, b)); // false ``` ## Search and Pattern Matching [#search-and-pattern-matching] ### Find First Occurrence [#find-first-occurrence] Find the index of the first occurrence of a byte sequence: ```typescript import { indexOfNeedle } from "@visulima/bytes"; const source = new Uint8Array([0, 1, 2, 1, 2, 1, 2, 3]); const needle = new Uint8Array([1, 2]); console.log(indexOfNeedle(source, needle)); // 1 ``` Start searching from a specific position: ```typescript import { indexOfNeedle } from "@visulima/bytes"; const source = new Uint8Array([0, 1, 2, 1, 2, 1, 2, 3]); const needle = new Uint8Array([1, 2]); console.log(indexOfNeedle(source, needle, 2)); // 3 console.log(indexOfNeedle(source, needle, 6)); // -1 (not found) ``` Pattern not found returns -1: ```typescript import { indexOfNeedle } from "@visulima/bytes"; const source = new Uint8Array([0, 1, 2, 3]); const needle = new Uint8Array([5, 6]); console.log(indexOfNeedle(source, needle)); // -1 ``` ### Find Last Occurrence [#find-last-occurrence] Find the index of the last occurrence of a byte sequence: ```typescript import { lastIndexOfNeedle } from "@visulima/bytes"; const source = new Uint8Array([0, 1, 2, 1, 2, 1, 2, 3]); const needle = new Uint8Array([1, 2]); console.log(lastIndexOfNeedle(source, needle)); // 5 ``` Search backwards from a specific position: ```typescript import { lastIndexOfNeedle } from "@visulima/bytes"; const source = new Uint8Array([0, 1, 2, 1, 2, 1, 2, 3]); const needle = new Uint8Array([1, 2]); console.log(lastIndexOfNeedle(source, needle, 2)); // 1 console.log(lastIndexOfNeedle(source, needle, 6)); // 5 ``` ### Check if Array Contains Sequence [#check-if-array-contains-sequence] Determine if a byte array contains a specific sequence: ```typescript import { includesNeedle } from "@visulima/bytes"; const source = new Uint8Array([0, 1, 2, 1, 2, 1, 2, 3]); const needle = new Uint8Array([1, 2]); console.log(includesNeedle(source, needle)); // true console.log(includesNeedle(source, new Uint8Array([5, 6]))); // false ``` Start checking from a specific position: ```typescript import { includesNeedle } from "@visulima/bytes"; const source = new Uint8Array([0, 1, 2, 1, 2, 1, 2, 3]); const needle = new Uint8Array([1, 2]); console.log(includesNeedle(source, needle, 3)); // true console.log(includesNeedle(source, needle, 6)); // false ``` ### Check Start Pattern [#check-start-pattern] Verify if an array starts with a specific prefix: ```typescript import { startsWith } from "@visulima/bytes"; const source = new Uint8Array([0, 1, 2, 1, 2, 1, 2, 3]); const prefix = new Uint8Array([0, 1, 2]); console.log(startsWith(source, prefix)); // true console.log(startsWith(source, new Uint8Array([1, 2]))); // false ``` Empty prefix always matches: ```typescript import { startsWith } from "@visulima/bytes"; const source = new Uint8Array([1, 2, 3]); const empty = new Uint8Array([]); console.log(startsWith(source, empty)); // true ``` ### Check End Pattern [#check-end-pattern] Verify if an array ends with a specific suffix: ```typescript import { endsWith } from "@visulima/bytes"; const source = new Uint8Array([0, 1, 2, 1, 2, 1, 2, 3]); const suffix = new Uint8Array([1, 2, 3]); console.log(endsWith(source, suffix)); // true console.log(endsWith(source, new Uint8Array([2, 3]))); // true console.log(endsWith(source, new Uint8Array([0, 1]))); // false ``` ## Type Conversion [#type-conversion] ### Convert Node.js Buffer to Uint8Array [#convert-nodejs-buffer-to-uint8array] Transform a Buffer into a Uint8Array: ```typescript import { bufferToUint8Array } from "@visulima/bytes"; import { Buffer } from "node:buffer"; const buffer = Buffer.from("Hello"); const uint8Array = bufferToUint8Array(buffer); console.log(uint8Array instanceof Uint8Array); // true console.log(uint8Array); // Uint8Array([72, 101, 108, 108, 111]) ``` Respects buffer offset and length: ```typescript import { bufferToUint8Array } from "@visulima/bytes"; import { Buffer } from "node:buffer"; const original = Buffer.from([1, 2, 3, 4, 5]); const sub = original.subarray(1, 4); const result = bufferToUint8Array(sub); console.log(result); // Uint8Array([2, 3, 4]) ``` ### Check if Value is Uint8Array [#check-if-value-is-uint8array] Type-safe check for Uint8Array (including Buffers in Node.js): ```typescript import { isUint8Array } from "@visulima/bytes"; import { Buffer } from "node:buffer"; console.log(isUint8Array(new Uint8Array([1, 2]))); // true console.log(isUint8Array(Buffer.from("test"))); // true (in Node.js) console.log(isUint8Array("not a Uint8Array")); // false console.log(isUint8Array([1, 2])); // false (plain array) console.log(isUint8Array(new ArrayBuffer(10))); // false ``` Use with TypeScript type guards: ```typescript import { isUint8Array } from "@visulima/bytes"; function processData(data: unknown) { if (isUint8Array(data)) { // TypeScript knows data is Uint8Array here console.log(data.length); } } ``` ### Convert ASCII Strings to Uint8Array [#convert-ascii-strings-to-uint8array] Transform ASCII strings into byte arrays: ```typescript import { asciiToUint8Array } from "@visulima/bytes"; const result = asciiToUint8Array("Hello!"); console.log(result); // Uint8Array([72, 101, 108, 108, 111, 33]) ``` Supports template literals: ```typescript import { asciiToUint8Array } from "@visulima/bytes"; const world = "World"; const result = asciiToUint8Array(`Hello ${world}!`); console.log(result); // Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33]) ``` Non-ASCII characters are truncated to 8 bits: ```typescript import { asciiToUint8Array } from "@visulima/bytes"; // Characters outside 0-255 range are truncated const result = asciiToUint8Array("你好€"); // Only lower 8 bits are kept for each character ``` ### Convert UTF-8 Strings to Uint8Array [#convert-utf-8-strings-to-uint8array] Transform UTF-8 strings into byte arrays (requires Node.js): ```typescript import { utf8ToUint8Array } from "@visulima/bytes"; const result = utf8ToUint8Array("Hello!"); console.log(result); // Uint8Array([72, 101, 108, 108, 111, 33]) // Properly handles multi-byte characters const chinese = utf8ToUint8Array("你好"); console.log(chinese); // Uint8Array([228, 189, 160, 229, 165, 189]) const emoji = utf8ToUint8Array("🌍"); console.log(emoji); // Uint8Array([240, 159, 140, 141]) ``` Supports template literals: ```typescript import { utf8ToUint8Array } from "@visulima/bytes"; const item = "你好"; const result = utf8ToUint8Array(`Item: ${item}`); console.log(result); // Uint8Array([73, 116, 101, 109, 58, 32, 228, 189, 160, 229, 165, 189]) ``` ### Universal Type Converter [#universal-type-converter] Convert various types to Uint8Array: ```typescript import { toUint8Array } from "@visulima/bytes"; import { Buffer } from "node:buffer"; // From Uint8Array (returns as-is) const u8 = new Uint8Array([1, 2, 3]); console.log(toUint8Array(u8) === u8); // true // From ArrayBuffer const buffer = new ArrayBuffer(3); const view = new Uint8Array(buffer); view[0] = 1; view[1] = 2; view[2] = 3; console.log(toUint8Array(buffer)); // Uint8Array([1, 2, 3]) // From Array of numbers console.log(toUint8Array([4, 5, 6])); // Uint8Array([4, 5, 6]) // From Node.js Buffer const nodeBuf = Buffer.from("Node"); console.log(toUint8Array(nodeBuf)); // Uint8Array([78, 111, 100, 101]) // From string (via Buffer.from in Node.js) console.log(toUint8Array("String")); // Uint8Array([83, 116, 114, 105, 110, 103]) ``` Throws error for incompatible types: ```typescript import { toUint8Array } from "@visulima/bytes"; try { toUint8Array(123); // Not convertible } catch (error) { console.log(error.message); // "UINT8ARRAY_INCOMPATIBLE: Cannot convert data to Uint8Array" } // Also throws for: // - booleans // - objects // - undefined // - arrays with non-number elements ``` ## Real-World Examples [#real-world-examples] ### Validate Binary File Headers [#validate-binary-file-headers] Check file types by examining their headers: ```typescript import { startsWith } from "@visulima/bytes"; function isPNG(data: Uint8Array): boolean { const pngHeader = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10]); return startsWith(data, pngHeader); } function isJPEG(data: Uint8Array): boolean { const jpegHeader = new Uint8Array([0xff, 0xd8, 0xff]); return startsWith(data, jpegHeader); } const fileData = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10 /* ... */]); console.log(isPNG(fileData)); // true console.log(isJPEG(fileData)); // false ``` ### Build Binary Protocol Messages [#build-binary-protocol-messages] Construct messages for binary protocols: ```typescript import { concat } from "@visulima/bytes"; function createMessage(type: number, payload: Uint8Array): Uint8Array { const header = new Uint8Array([ 0xff, 0xff, // Magic number type, // Message type payload.length >> 8, // Length high byte payload.length & 0xff, // Length low byte ]); return concat([header, payload]); } const payload = new Uint8Array([1, 2, 3, 4]); const message = createMessage(0x01, payload); console.log(message); // Uint8Array([255, 255, 1, 0, 4, 1, 2, 3, 4]) ``` ### Process Streaming Data [#process-streaming-data] Handle chunked binary data: ```typescript import { concat, indexOfNeedle } from "@visulima/bytes"; const delimiter = new Uint8Array([0x0a]); // newline let buffer = new Uint8Array([]); function processChunk(chunk: Uint8Array): string[] { buffer = concat([buffer, chunk]); const messages: string[] = []; let pos: number; while ((pos = indexOfNeedle(buffer, delimiter)) !== -1) { const message = buffer.slice(0, pos); messages.push(new TextDecoder().decode(message)); buffer = buffer.slice(pos + 1); } return messages; } // Simulate receiving chunks const chunk1 = new Uint8Array([72, 101, 108, 108, 111, 0x0a, 87]); // "Hello\nW" const chunk2 = new Uint8Array([111, 114, 108, 100, 0x0a]); // "orld\n" console.log(processChunk(chunk1)); // ["Hello"] console.log(processChunk(chunk2)); // ["World"] ``` ## Next Steps [#next-steps] Complete API documentation with all parameters and types Return to package overview # Cli (/docs/packages/cerebro/api/cli) # API: CLI [#api-cli] The main CLI class for creating and managing command-line applications. ## Creating a CLI Instance [#creating-a-cli-instance] ### Constructor [#constructor] ```typescript new Cerebro(name: string, options?: CliOptions) ``` **Parameters:** * `name` - The CLI application name * `options` - Optional configuration **Example:** ```typescript import { Cerebro } from "@visulima/cerebro"; const cli = new Cerebro("my-app", { packageName: "my-app", packageVersion: "1.0.0", cwd: process.cwd(), logger: console, argv: process.argv.slice(2), }); ``` ### Factory Function [#factory-function] ```typescript import { createCerebro } from "@visulima/cerebro"; const cli = createCerebro("my-app", { packageName: "my-app", packageVersion: "1.0.0", }); ``` ## CliOptions [#clioptions] ```typescript interface CliOptions { argv?: ReadonlyArray; cwd?: string; logger?: T; packageName?: string; packageVersion?: string; } ``` ## Methods [#methods] ### addCommand() [#addcommand] Add a command to the CLI: ```typescript cli.addCommand(command: Command): this ``` **Example:** ```typescript cli.addCommand({ name: "build", description: "Build the project", execute: ({ logger }) => { logger.info("Building..."); }, }); ``` ### addGlobalOption() [#addglobaloption] Add a global option available to all commands. Global options are parsed alongside command-specific options and displayed in the help output under "Global Options". ```typescript cli.addGlobalOption(option: OptionDefinition): this ``` **Example:** ```typescript cli.addGlobalOption({ name: "cwd", type: String, description: "Override working directory", }); cli.addGlobalOption({ name: "config", alias: "C", type: String, description: "Path to config file", }); ``` Throws an error if the option name or alias conflicts with a built-in global option (`verbose`, `debug`, `help`, `quiet`, `version`, `no-color`, `color`) or a previously added custom global option. Global options are accessible in commands via `toolbox.options`: ```typescript cli.addCommand({ name: "build", execute: ({ options }) => { const cwd = options.cwd as string | undefined; // ... }, }); ``` ### getGlobalOptions() [#getglobaloptions] Get all global options (built-in + custom): ```typescript cli.getGlobalOptions(): OptionDefinition[] ``` ### addPlugin() [#addplugin] Add a plugin to extend functionality: ```typescript cli.addPlugin(plugin: Plugin): this ``` **Example:** ```typescript cli.addPlugin({ name: "my-plugin", execute: (toolbox) => { toolbox.myFeature = () => {}; }, }); ``` ### run() [#run] Run the CLI application: ```typescript cli.run(options?: CliRunOptions): Promise ``` **Options:** ```typescript interface CliRunOptions { shouldExitProcess?: boolean; // Default: true autoDispose?: boolean; // Default: true [key: string]: any; // Additional options } ``` **Example:** ```typescript await cli.run({ shouldExitProcess: false, // Don't exit process autoDispose: false, // Don't cleanup }); ``` ### runCommand() [#runcommand] Execute a command programmatically: ```typescript cli.runCommand(commandName: string, options?: RunCommandOptions): Promise ``` **Options:** ```typescript interface RunCommandOptions { argv?: string[]; [key: string]: any; } ``` **Example:** ```typescript await runtime.runCommand("build", { argv: ["--production"], }); ``` ### setDefaultCommand() [#setdefaultcommand] Set the default command when none is provided: ```typescript cli.setDefaultCommand(commandName: string): this ``` **Example:** ```typescript cli.setDefaultCommand("help"); ``` ### setCommandSection() [#setcommandsection] Configure help output: ```typescript cli.setCommandSection(section: CommandSection): this ``` **Example:** ```typescript cli.setCommandSection({ header: "My CLI v1.0.0", footer: "Visit https://example.com for more info", }); ``` ### getCliName() [#getcliname] Get the CLI application name: ```typescript cli.getCliName(): string ``` ### getCommands() [#getcommands] Get all registered commands: ```typescript cli.getCommands(): Map ``` ### getCwd() [#getcwd] Get the current working directory: ```typescript cli.getCwd(): string ``` ### getPackageName() [#getpackagename] Get the package name: ```typescript cli.getPackageName(): string | undefined ``` ### getPackageVersion() [#getpackageversion] Get the package version: ```typescript cli.getPackageVersion(): string | undefined ``` ### getPluginManager() [#getpluginmanager] Get the plugin manager instance: ```typescript cli.getPluginManager(): PluginManager ``` ### dispose() [#dispose] Clean up resources: ```typescript cli.dispose(): void ``` ## Complete Example [#complete-example] ```typescript import { Cerebro } from "@visulima/cerebro"; const cli = new Cerebro("my-app", { packageName: "my-app", packageVersion: "1.0.0", }); cli.setCommandSection({ header: "My Awesome CLI v1.0.0", }); cli.setDefaultCommand("help"); cli.addCommand({ name: "hello", execute: ({ logger }) => { logger.info("Hello!"); }, }); await cli.run(); ``` # Command (/docs/packages/cerebro/api/command) # API: Command [#api-command] Command interface for defining CLI commands. ## Command Interface [#command-interface] ```typescript interface Command { name: string; /** Either `execute` or `loader` must be provided (but not both). */ execute?: (toolbox: TContext) => Promise | void; /** Lazily imports the handler module on first invocation. */ loader?: () => Promise<{ default: (toolbox: TContext) => Promise | void }>; description?: string; alias?: string | string[]; argument?: ArgumentDefinition; options?: OptionDefinition[]; env?: EnvDefinition[]; hidden?: boolean; group?: string; examples?: string[] | string[][]; usage?: Content[]; file?: string; commandPath?: string[]; } ``` ## Properties [#properties] ### name [#name] **Type:** `string`\ **Required:** Yes The command name used to invoke it: ```typescript { name: "build"; // Invoked as: cli build } ``` ### execute [#execute] **Type:** `(toolbox: TContext) => Promise | void`\ **Required:** Either `execute` or `loader` must be set — not both. The function that executes the command: ```typescript { execute: ({ logger, options, argument }) => { // Command logic }; } ``` ### loader [#loader] **Type:** `() => Promise<{ default: (toolbox: TContext) => Promise | void }>`\ **Required:** Either `execute` or `loader` must be set — not both. Lazily imports the handler module on first invocation. The module's **default export** is used as the handler. Help, completion, and option validation work entirely from the metadata declared on `addCommand` and never trigger the loader. The first invocation imports the module; subsequent calls reuse the cached handler. Use this for commands whose handler pulls in heavy dependencies — the import cost is paid only when the user actually invokes the command. ```typescript // commands/build.ts const build: Command = { name: "build", description: "Build the project", options: [{ name: "output", type: String }], loader: () => import("./build.handler"), }; // commands/build.handler.ts export default async ({ logger, options }) => { logger.info(`Building to ${options.output ?? "dist"}`); }; ``` For modules that group multiple subcommands' handlers as named exports, use the `lazyNamed` helper: ```typescript import { lazyNamed } from "@visulima/cerebro"; cli.addCommand({ name: "list", commandPath: ["cache"], loader: lazyNamed(() => import("./cache.handlers"), "cacheList"), }); ``` If the loader rejects or the resolved module has no default-exported function, a `CommandLoaderError` is thrown. ### description [#description] **Type:** `string`\ **Required:** No Short description shown in help: ```typescript { description: "Build the project for production"; } ``` ### alias [#alias] **Type:** `string | string[]`\ **Required:** No Alternative names for the command: ```typescript { alias: "b"; // Single alias // or alias: ["b", "compile"]; // Multiple aliases } ``` ### argument [#argument] **Type:** `ArgumentDefinition`\ **Required:** No Positional argument definition: ```typescript { argument: { name: "file", type: String, description: "File to process", defaultValue: "default.txt" } } ``` ### options [#options] **Type:** `OptionDefinition[]`\ **Required:** No Command options: ```typescript { options: [ { name: "production", type: Boolean, description: "Build for production", }, ]; } ``` ### env [#env] **Type:** `EnvDefinition[]`\ **Required:** No Environment variables supported by this command: ```typescript { env: [ { name: "API_KEY", type: String, description: "API key for authentication", }, { name: "TIMEOUT", type: Number, defaultValue: 5000, description: "Request timeout in milliseconds", }, { name: "VERBOSE", type: Boolean, defaultValue: false, description: "Enable verbose logging", }, ]; } ``` Environment variables are automatically: * Converted to camelCase (`API_KEY` → `apiKey`) * Type-transformed (String, Number, Boolean) * Applied with default values if not set * Displayed in help output (unless `hidden: true`) Access them in `execute` via `toolbox.env`: ```typescript execute: ({ env }) => { const apiKey = env.apiKey; // string | undefined const timeout = env.timeout; // number (5000 if not set) const verbose = env.verbose; // boolean (false if not set) }; ``` **See also:** [Environment Variables Guide](/docs/packages/cerebro/guides/options#environment-variables) ### hidden [#hidden] **Type:** `boolean`\ **Required:** No\ **Default:** `false` Hide command from help output: ```typescript { hidden: true; } ``` ### group [#group] **Type:** `string`\ **Required:** No Group commands in help output: ```typescript { group: "Build"; } ``` ### examples [#examples] **Type:** `string[] | string[][]`\ **Required:** No Usage examples: ```typescript { examples: ["build --production", "build --production --output dist"]; } ``` ### usage [#usage] **Type:** `Content[]`\ **Required:** No Custom usage documentation (advanced). ### file [#file] **Type:** `string`\ **Required:** No Path to command file (for metadata). ### commandPath [#commandpath] **Type:** `string[]`\ **Required:** No Command path for nested commands. Creates hierarchical command structures. When specified, the command can be invoked using the full path: `cli `. ```typescript { name: "staging", commandPath: ["deploy"], // Invoked as: cli deploy staging execute: ({ logger }) => { logger.info("Deploying to staging..."); } } ``` Multi-level nesting is supported: ```typescript { name: "up", commandPath: ["db", "migrate"], // Invoked as: cli db migrate up execute: ({ logger }) => { logger.info("Running migrations..."); } } ``` **Validation:** * Each path segment must follow the same rules as command names * Must start with a letter * Can contain letters, numbers, hyphens, and underscores * Cannot contain spaces or special characters **See also:** [Nested Commands Guide](/docs/packages/cerebro/guides/commands#nested-commands) ## ArgumentDefinition [#argumentdefinition] ```typescript interface ArgumentDefinition { name: string; type?: TypeConstructor; description?: string; defaultValue?: T; typeLabel?: string; } ``` ## OptionDefinition [#optiondefinition] ```typescript interface OptionDefinition { name: string; alias?: string | string[]; type?: TypeConstructor; description?: string; defaultValue?: T; required?: boolean; multiple?: boolean; lazyMultiple?: boolean; conflicts?: string | string[]; implies?: Record; hidden?: boolean; typeLabel?: string; group?: string; } ``` ## EnvDefinition [#envdefinition] ```typescript interface EnvDefinition { name: string; type?: EnvTypeConstructor; description?: string; defaultValue?: T; hidden?: boolean; typeLabel?: string; } ``` ### Properties [#properties-1] #### name [#name-1] **Type:** `string`\ **Required:** Yes The environment variable name (e.g., `API_KEY`, `LOG_LEVEL`). Will be converted to camelCase when accessed via `toolbox.env`. #### type [#type] **Type:** `EnvTypeConstructor`\ **Required:** No Type constructor for transforming the environment variable value: * `String` - Keep as string * `Number` - Parse as integer * `Boolean` - Parse as boolean (accepts `true`, `1`, `yes`, `on` for true) ```typescript { name: "PORT", type: Number, // Parses string to number } ``` #### description [#description-1] **Type:** `string`\ **Required:** No Description shown in help output. #### defaultValue [#defaultvalue] **Type:** `T`\ **Required:** No Default value used when the environment variable is not set: ```typescript { name: "TIMEOUT", type: Number, defaultValue: 5000, // Used if TIMEOUT is not set } ``` #### hidden [#hidden-1] **Type:** `boolean`\ **Required:** No\ **Default:** `false` Hide environment variable from help output: ```typescript { name: "SECRET_KEY", type: String, hidden: true, // Won't appear in help } ``` #### typeLabel [#typelabel] **Type:** `string`\ **Required:** No Custom type label for help output (advanced). ## Complete Example [#complete-example] ```typescript cli.addCommand({ name: "deploy", alias: "d", description: "Deploy the application", group: "Deployment", examples: ["deploy --env production", "deploy --env staging --dry-run"], argument: { name: "target", type: String, description: "Deployment target", defaultValue: "production", }, options: [ { name: "env", alias: "e", type: String, required: true, description: "Environment name", }, { name: "dry-run", type: Boolean, description: "Perform a dry run", }, ], env: [ { name: "API_KEY", type: String, description: "API key for deployment", }, { name: "TIMEOUT", type: Number, defaultValue: 30000, description: "Deployment timeout in milliseconds", }, { name: "FORCE_DEPLOY", type: Boolean, defaultValue: false, description: "Force deployment without confirmation", }, ], execute: async ({ argument, options, env, logger }) => { const target = argument[0]; const apiKey = env.apiKey; const timeout = env.timeout; // 30000 if not set const forceDeploy = env.forceDeploy; // false if not set logger.info(`Deploying to ${target} (${options.env})`); if (forceDeploy) { logger.info("Force deployment enabled"); } }, }); ``` # API Overview (/docs/packages/cerebro/api) # API Overview [#api-overview] Complete API reference for Cerebro CLI framework. ## Core API [#core-api] ### createCerebro() [#createcerebro] Create a new CLI instance: ```typescript function createCerebro(name: string, options?: CliOptions): Cerebro; ``` **Parameters:** * `name` - CLI application name * `options` - Configuration options (optional) **Returns:** Configured Cerebro instance **Example:** ```typescript const cli = createCerebro("my-cli", { packageName: "my-cli", packageVersion: "1.0.0", }); ``` ### Cerebro Class [#cerebro-class] Main CLI class with the following methods: #### addCommand() [#addcommand] Register a command: ```typescript cli.addCommand(command: Command): void ``` #### use() [#use] Register a plugin: ```typescript cli.use(plugin: Plugin): void ``` #### run() [#run] Execute the CLI: ```typescript await cli.run(argv?: string[]): Promise ``` #### setDefaultCommand() [#setdefaultcommand] Set default command: ```typescript cli.setDefaultCommand(name: string): void ``` #### getCommands() [#getcommands] Get all registered commands: ```typescript cli.getCommands(): Map ``` ## Built-in Commands [#built-in-commands] * `HelpCommand` - Display help text * `VersionCommand` - Show version * `CompletionCommand` - Shell completion * `ReadmeCommand` - Generate README ## Built-in Plugins [#built-in-plugins] * `errorHandlerPlugin` - Error handling * `runtimeVersionCheckPlugin` - Version validation * `updateNotifierPlugin` - Update notifications ## Type Definitions [#type-definitions] * `Command` - Command definition interface (supports `execute` or `loader`) * `CommandExecute` - Handler signature shared by `execute` and `loader` * `LazyCommandModule` - Module shape returned by a command `loader` * `Plugin` - Plugin definition interface * `Toolbox` - Execution context interface * `CliOptions` - CLI configuration options * `OptionDefinition` - Option definition interface * `ArgumentDefinition` - Argument definition interface * `EnvDefinition` - Environment variable definition * `CreateOptions` - Derive a typed options object from a kebab-case input * `CreateEnv` - Derive a typed env object from an UPPER\_SNAKE\_CASE input * `OptionNameToCamelCase` - String type that converts `output-dir` → `outputDir` ## Helpers [#helpers] * `lazyNamed(load, key)` - Builds a `loader` for handlers exported as named entries from a shared module * `createCerebro(name, options)` - Factory function for a `Cerebro` instance ## Errors [#errors] * `CerebroError` - Base error class * `CommandNotFoundError` - Command lookup failed * `CommandValidationError` - Command failed validation (e.g. missing required option) * `CommandLoaderError` - `loader` rejected or returned no default-exported function * `ConflictingOptionsError` - Two options that conflict were both passed * `PluginError` - A plugin threw during a lifecycle hook ## Constants [#constants] * `VERBOSITY_QUIET` - Quiet output level * `VERBOSITY_NORMAL` - Normal output level * `VERBOSITY_VERBOSE` - Verbose output level * `VERBOSITY_DEBUG` - Debug output level # Plugin (/docs/packages/cerebro/api/plugin) # API: Plugin [#api-plugin] Plugin interface for extending Cerebro functionality. ## Plugin Interface [#plugin-interface] ```typescript interface Plugin { name: string; description?: string; version?: string; dependencies?: string[]; init?: (context: PluginContext) => Promise | void; execute?: (toolbox: Toolbox) => Promise | void; beforeCommand?: (toolbox: Toolbox) => Promise | void; afterCommand?: (toolbox: Toolbox, result: unknown) => Promise | void; onError?: (error: Error, toolbox: Toolbox) => Promise | void; } ``` ## Properties [#properties] ### name [#name] **Type:** `string`\ **Required:** Yes Unique plugin name: ```typescript { name: "my-plugin"; } ``` ### description [#description] **Type:** `string`\ **Required:** No Plugin description: ```typescript { description: "Adds file system utilities"; } ``` ### version [#version] **Type:** `string`\ **Required:** No Plugin version: ```typescript { version: "1.0.0"; } ``` ### dependencies [#dependencies] **Type:** `string[]`\ **Required:** No Other plugins this plugin depends on: ```typescript { dependencies: ["logger", "filesystem"]; } ``` ## Lifecycle Hooks [#lifecycle-hooks] ### init [#init] Called once during plugin initialization: ```typescript { init: async ({ cli, cwd, logger }) => { // Initialize plugin logger.info("Plugin initialized"); }; } ``` ### execute [#execute] Called during command execution to extend toolbox: ```typescript { execute: (toolbox) => { toolbox.myFeature = () => { console.log("My feature!"); }; }; } ``` ### beforeCommand [#beforecommand] Called before each command executes: ```typescript { beforeCommand: async (toolbox) => { console.log(`Executing: ${toolbox.commandName}`); }; } ``` ### afterCommand [#aftercommand] Called after successful command execution: ```typescript { afterCommand: async (toolbox, result) => { console.log(`Command completed: ${result}`); }; } ``` ### onError [#onerror] Called when an error occurs: ```typescript { onError: async (error, toolbox) => { console.error(`Error in ${toolbox.commandName}:`, error); }; } ``` ## PluginContext [#plugincontext] Context provided to `init` hook: ```typescript interface PluginContext { cli: Cli; cwd: string; logger: T; } ``` ## Complete Example [#complete-example] ```typescript cli.addPlugin({ name: "http-client", version: "1.0.0", description: "HTTP client utilities", dependencies: ["logger"], init: async ({ logger }) => { logger.info("HTTP client plugin initialized"); }, execute: (toolbox) => { toolbox.http = { get: async (url: string) => { const response = await fetch(url); return response.json(); }, post: async (url: string, data: unknown) => { const response = await fetch(url, { method: "POST", body: JSON.stringify(data), }); return response.json(); }, }; }, beforeCommand: async (toolbox) => { toolbox.logger.debug(`Preparing HTTP client`); }, afterCommand: async (toolbox, result) => { toolbox.logger.debug(`Command completed`); }, onError: async (error, toolbox) => { toolbox.logger.error(`HTTP error:`, error); }, }); ``` # Types (/docs/packages/cerebro/api/types) # API: Types [#api-types] TypeScript type definitions for Cerebro. ## Core Types [#core-types] ### Cli [#cli] Main CLI class interface: ```typescript interface Cli { addCommand(command: Command): this; addPlugin(plugin: Plugin): this; run(options?: CliRunOptions): Promise; runCommand(commandName: string, options?: RunCommandOptions): Promise; setDefaultCommand(commandName: string): this; setCommandSection(section: CommandSection): this; getCliName(): string; getCommands(): Map; getCwd(): string; getPackageName(): string | undefined; getPackageVersion(): string | undefined; getPluginManager(): PluginManager; dispose(): void; } ``` ### Command [#command] Command definition. Either `execute` or `loader` must be set — not both. ```typescript interface Command { name: string; execute?: (toolbox: TContext) => Promise | void; loader?: () => Promise>; description?: string; alias?: string | string[]; argument?: ArgumentDefinition; options?: OptionDefinition[]; env?: EnvDefinition[]; hidden?: boolean; group?: string; examples?: string[] | string[][]; usage?: Content[]; file?: string; commandPath?: string[]; } ``` ### CommandExecute [#commandexecute] Handler signature, used by both `execute` and the resolved default export of `loader`. ```typescript type CommandExecute = ((toolbox: TContext) => Promise) | ((toolbox: TContext) => void); ``` ### LazyCommandModule [#lazycommandmodule] Module shape returned by a command `loader`. ```typescript interface LazyCommandModule { default: CommandExecute; } ``` ### Plugin [#plugin] Plugin definition: ```typescript interface Plugin { name: string; description?: string; version?: string; dependencies?: string[]; init?: (context: PluginContext) => Promise | void; execute?: (toolbox: Toolbox) => Promise | void; beforeCommand?: (toolbox: Toolbox) => Promise | void; afterCommand?: (toolbox: Toolbox, result: unknown) => Promise | void; onError?: (error: Error, toolbox: Toolbox) => Promise | void; } ``` ### Toolbox [#toolbox] Command execution context: ```typescript interface Toolbox extends Cerebro.ExtensionOverrides { argument: string[]; argv: Record; command: Command; commandName: string; logger: T; options: Options; runtime: Cli; } ``` ## Option Types [#option-types] ### OptionDefinition [#optiondefinition] ```typescript interface OptionDefinition { name: string; alias?: string | string[]; type?: TypeConstructor; description?: string; defaultValue?: T; required?: boolean; multiple?: boolean; lazyMultiple?: boolean; conflicts?: string | string[]; implies?: Record; hidden?: boolean; typeLabel?: string; group?: string; } ``` ### ArgumentDefinition [#argumentdefinition] ```typescript interface ArgumentDefinition { name: string; type?: TypeConstructor; description?: string; defaultValue?: T; typeLabel?: string; } ``` ## Configuration Types [#configuration-types] ### CliOptions [#clioptions] ```typescript interface CliOptions { argv?: ReadonlyArray; cwd?: string; logger?: T; packageName?: string; packageVersion?: string; } ``` ### CliRunOptions [#clirunoptions] ```typescript interface CliRunOptions { shouldExitProcess?: boolean; autoDispose?: boolean; [key: string]: any; } ``` ### RunCommandOptions [#runcommandoptions] ```typescript interface RunCommandOptions { argv?: string[]; [key: string]: any; } ``` ### CommandSection [#commandsection] ```typescript interface CommandSection { header?: string; footer?: string; } ``` ## Verbosity Levels [#verbosity-levels] ```typescript type VERBOSITY_LEVEL = 16 | 32 | 64 | 128 | 256; const VERBOSITY_QUIET = 16; const VERBOSITY_NORMAL = 32; const VERBOSITY_VERBOSE = 64; const VERBOSITY_DEBUG = 128; ``` ## Extending Types [#extending-types] ### Extending Toolbox [#extending-toolbox] ```typescript declare global { namespace Cerebro { interface ExtensionOverrides { myFeature: () => void; api: { get: (url: string) => Promise; }; } } } ``` ## Type Exports [#type-exports] All types are exported from the main package: ```typescript import type { Cli, CliOptions, CliRunOptions, RunCommandOptions, Command, CommandExecute, LazyCommandModule, EnvDefinition, OptionDefinition, ArgumentDefinition, Plugin, PluginContext, Toolbox, CreateOptions, CreateEnv, OptionNameToCamelCase, VERBOSITY_LEVEL, } from "@visulima/cerebro"; // Runtime exports import { lazyNamed } from "@visulima/cerebro"; ``` # Architecture (/docs/packages/cerebro/concepts/architecture) # Architecture [#architecture] Understand how Cerebro is structured and how its components work together. ## Core Components [#core-components] ### CLI Instance [#cli-instance] The `Cerebro` class is the main entry point. It manages commands, plugins, and the execution lifecycle: ```typescript import { createCerebro } from "@visulima/cerebro"; const cli = createCerebro("my-cli", { packageName: "my-cli", packageVersion: "1.0.0", logger: console, // Custom logger cwd: process.cwd(), // Working directory argv: process.argv.slice(2), // Command-line arguments }); ``` ### Command Registry [#command-registry] Commands are stored in a hierarchical registry that supports: * Flat commands (`build`, `deploy`) * Nested commands (`db migrate`, `server start`) * Command aliases and suggestions ### Plugin System [#plugin-system] Plugins extend the CLI's functionality through a lifecycle-based system: 1. **Registration**: Plugins are registered with `cli.use(plugin)` 2. **Initialization**: Plugins initialize before command execution 3. **Toolbox Extension**: Plugins can add properties to the execution context 4. **Lifecycle Hooks**: Plugins can hook into pre/post execution ### Toolbox Context [#toolbox-context] Every command receives a `toolbox` context with: ```typescript { argument: string[], // Positional arguments options: Record, // Parsed options env: Record, // Environment variables logger: Console, // Logger instance cwd: string, // Current working directory // ... plugin extensions } ``` ## Execution Flow [#execution-flow] ``` 1. CLI Initialization └─> Load configuration └─> Register built-in commands └─> Register plugins 2. Argument Parsing └─> Parse command name └─> Parse options and flags └─> Parse positional arguments 3. Command Resolution └─> Find matching command └─> Suggest alternatives if not found └─> Validate nested command paths 4. Plugin Initialization └─> Execute plugin setup └─> Extend toolbox context └─> Register lifecycle hooks 5. Validation └─> Check required options └─> Validate option types └─> Check conflicting options └─> Validate environment variables 6. Command Execution └─> Prepare toolbox context └─> Execute command function └─> Handle errors └─> Return exit code ``` ## Design Principles [#design-principles] ### Zero Dependencies Core [#zero-dependencies-core] Cerebro's core has minimal dependencies, with optional peer dependencies for features: * **Core**: Command parsing, validation, execution * **Optional**: Boxed output, advanced logging, shell completion ### Type Safety [#type-safety] Full TypeScript support with generics for type-safe toolbox extensions: ```typescript // Declare custom toolbox properties declare global { namespace Cerebro { interface ExtensionOverrides { http: { get: (url: string) => Promise; }; } } } // Now http is fully typed in commands cli.addCommand({ name: "fetch", execute: async ({ http }) => { const data = await http.get("/api/user"); // data is typed as User }, }); ``` ### Cross-Runtime Compatibility [#cross-runtime-compatibility] Cerebro works across Node.js, Deno, and Bun through runtime abstraction: * Process access (`argv`, `cwd`, `env`) * Exit handling * Exception management ### Extensibility [#extensibility] Three extension points: 1. **Commands**: Add new commands 2. **Plugins**: Extend functionality 3. **Toolbox**: Add custom context properties ## Command Hierarchy [#command-hierarchy] Commands can be organized in a tree structure: ``` my-cli ├── build # Flat command ├── deploy # Flat command └── db # Command group ├── migrate # Nested command ├── seed # Nested command └── backup # Nested command ``` Implementation: ```typescript cli.addCommand({ name: "db", description: "Database commands", commands: [ { name: "migrate", execute: ({ logger }) => {} }, { name: "seed", execute: ({ logger }) => {} }, { name: "backup", execute: ({ logger }) => {} }, ], }); ``` ## Error Handling [#error-handling] Cerebro provides comprehensive error handling: * **Command Not Found**: Suggests alternatives using Levenshtein distance * **Validation Errors**: Clear messages for invalid options * **Execution Errors**: Captures and formats runtime errors * **Exit Codes**: Standard Unix exit codes (0 = success, 1 = error) ```typescript import { errorHandlerPlugin } from "@visulima/cerebro/plugin/error-handler"; cli.use( errorHandlerPlugin({ exitOnError: true, showStackTrace: false, }), ); ``` # Commands (/docs/packages/cerebro/concepts/commands) # Commands [#commands] Learn about Cerebro's command system and how to create powerful CLI commands. ## Command Structure [#command-structure] A command is defined by its metadata and either an inline `execute` function or a `loader` that defers handler import until invocation: ```typescript { name: string, // Command name description: string, // Help text description argument?: ArgumentDef, // Positional argument options?: OptionDef[], // Named options/flags env?: EnvDef, // Environment variables commandPath?: string[], // Path for nested subcommands // Exactly one of: execute?: (toolbox) => void, // Inline handler loader?: () => Promise<{ default: handler }>, // Lazy-imported handler } ``` ## Basic Commands [#basic-commands] Simple command with no arguments: ```typescript cli.addCommand({ name: "hello", description: "Say hello", execute: ({ logger }) => { logger.log("Hello!"); }, }); ``` ## Arguments [#arguments] Positional arguments that come after the command name: ```typescript cli.addCommand({ name: "greet", description: "Greet someone", argument: { name: "name", description: "Person to greet", type: String, required: true, }, execute: ({ argument, logger }) => { logger.log(`Hello, ${argument[0]}!`); }, }); ``` Usage: ```bash $ my-cli greet Alice Hello, Alice! ``` ## Options [#options] Named options with values or flags: ```typescript cli.addCommand({ name: "build", description: "Build project", options: [ { name: "output", alias: "o", description: "Output directory", type: String, defaultValue: "./dist", }, { name: "production", alias: "p", description: "Production build", type: Boolean, }, { name: "minify", description: "Minify output", type: Boolean, defaultValue: true, }, ], execute: ({ options, logger }) => { logger.log(`Building to ${options.output}`); logger.log(`Production: ${options.production}`); logger.log(`Minify: ${options.minify}`); }, }); ``` Usage: ```bash $ my-cli build --output ./build --production Building to ./build Production: true Minify: true ``` ## Option Types [#option-types] Cerebro supports multiple option types: ### String Options [#string-options] ```typescript { name: "config", type: String, defaultValue: "config.json", } ``` ### Boolean Flags [#boolean-flags] ```typescript { name: "verbose", type: Boolean, } ``` ### Number Options [#number-options] ```typescript { name: "port", type: Number, defaultValue: 3000, } ``` ### Negatable Options [#negatable-options] Options that can be negated with `--no-` prefix: ```typescript { name: "color", type: Boolean, defaultValue: true, negatable: true, // Allows --no-color } ``` Usage: ```bash $ my-cli build --color # color = true $ my-cli build --no-color # color = false ``` ## Required Options [#required-options] Mark options as required: ```typescript cli.addCommand({ name: "deploy", description: "Deploy application", options: [ { name: "environment", alias: "e", description: "Target environment", type: String, required: true, // Must be provided }, ], execute: ({ options, logger }) => { logger.log(`Deploying to ${options.environment}`); }, }); ``` Error if not provided: ```bash $ my-cli deploy Error: Required option --environment is missing ``` ## Conflicting Options [#conflicting-options] Prevent incompatible options: ```typescript cli.addCommand({ name: "test", description: "Run tests", options: [ { name: "watch", type: Boolean, conflicts: ["ci"], // Can't use with --ci }, { name: "ci", type: Boolean, conflicts: ["watch"], // Can't use with --watch }, ], execute: ({ options }) => { // ... }, }); ``` ## Implied Options [#implied-options] Set default values for other options: ```typescript { name: "production", type: Boolean, implies: { minify: true, // Sets minify to true sourcemap: false, // Sets sourcemap to false }, } ``` ## Environment Variables [#environment-variables] Access typed environment variables: ```typescript cli.addCommand({ name: "deploy", description: "Deploy application", env: { API_KEY: { description: "API authentication key", required: true, }, API_URL: { description: "API endpoint URL", required: false, defaultValue: "https://api.example.com", }, }, execute: ({ env, logger }) => { logger.log(`Deploying to ${env.API_URL}`); logger.log(`Using key: ${env.API_KEY}`); }, }); ``` ## Nested Commands [#nested-commands] Create command hierarchies for better organization: ```typescript cli.addCommand({ name: "database", alias: "db", description: "Database management", commands: [ { name: "migrate", description: "Run migrations", options: [ { name: "rollback", type: Boolean, description: "Rollback migrations", }, ], execute: ({ options, logger }) => { if (options.rollback) { logger.log("Rolling back migrations..."); } else { logger.log("Running migrations..."); } }, }, { name: "seed", description: "Seed database", execute: ({ logger }) => { logger.log("Seeding database..."); }, }, ], }); ``` Usage: ```bash $ my-cli db migrate $ my-cli db migrate --rollback $ my-cli db seed ``` ## Default Commands [#default-commands] Set a default command when no command is specified: ```typescript cli.setDefaultCommand("help"); // or with options cli.addCommand({ name: "serve", description: "Start server", isDefault: true, execute: ({ logger }) => { logger.log("Starting server..."); }, }); ``` ## Command Sections [#command-sections] Organize help output with sections: ```typescript cli.addCommandSection({ name: "Database", description: "Database management commands", commands: ["db:migrate", "db:seed", "db:backup"], }); ``` ## Lazy Commands [#lazy-commands] For CLIs with many subcommands or heavy per-command dependencies, defer importing each handler until the command is actually invoked. Declare metadata inline and point `loader` at a dynamic `import()`: ```typescript // commands/build/index.ts const build: Command = { name: "build", description: "Build the project", options: [{ name: "output", alias: "o", type: String }], loader: () => import("./handler"), }; export default build; // commands/build/handler.ts export default ({ logger, options }) => { logger.info(`Building to ${options.output ?? "dist"}`); }; ``` `loader` is a zero-argument function that returns a promise resolving to a module with a default-exported handler. Help, completion, and option validation work entirely from the metadata you declared on `addCommand` and never trigger the loader. The first invocation imports the module; subsequent calls reuse the cached handler. A command must declare either `execute` or `loader`, but not both. If a loader rejects or returns a module without a default-exported function, a `CommandLoaderError` is thrown. For modules that export multiple subcommand handlers as named exports, use the `lazyNamed` helper: ```typescript import { lazyNamed } from "@visulima/cerebro"; cli.addCommand({ name: "list", commandPath: ["cache"], loader: lazyNamed(() => import("./cache.handlers"), "cacheList"), }); ``` # Plugins (/docs/packages/cerebro/concepts/plugins) # Plugins [#plugins] Extend Cerebro's functionality with plugins that hook into the CLI lifecycle and add custom features to the toolbox. ## Plugin Structure [#plugin-structure] A plugin is a function that returns a plugin definition: ```typescript import type { Plugin } from "@visulima/cerebro"; const myPlugin = (): Plugin => ({ name: "my-plugin", version: "1.0.0", register: async (context) => { // Initialize plugin context.toolbox.myFeature = { doSomething: () => console.log("Doing something!"), }; }, }); ``` ## Plugin Lifecycle [#plugin-lifecycle] ### Registration [#registration] Plugins are registered with the CLI instance: ```typescript import { createCerebro } from "@visulima/cerebro"; const cli = createCerebro("my-cli"); cli.use(myPlugin()); ``` ### Initialization [#initialization] Plugins initialize before command execution and receive a context object: ```typescript { register: async (context: PluginContext) => { const { toolbox, cli, logger } = context; // Extend toolbox for commands toolbox.http = { get: async (url) => { /* ... */ }, post: async (url, data) => { /* ... */ }, }; // Access CLI instance const commands = cli.getCommands(); // Use logger logger.log("Plugin initialized"); }, } ``` ### Toolbox Extension [#toolbox-extension] Plugins extend the toolbox that commands receive: ```typescript // In plugin toolbox.database = { connect: async () => { /* ... */ }, query: async (sql) => { /* ... */ }, }; // In command cli.addCommand({ name: "query", execute: async ({ database, logger }) => { await database.connect(); const result = await database.query("SELECT * FROM users"); logger.log(result); }, }); ``` ## Type-Safe Extensions [#type-safe-extensions] Declare custom toolbox properties for full type safety: ```typescript // Extend the Cerebro namespace declare global { namespace Cerebro { interface ExtensionOverrides { http: { get: (url: string) => Promise; post: (url: string, data: unknown) => Promise; }; database: { connect: () => Promise; query: (sql: string) => Promise; }; } } } // Create plugin const databasePlugin = (): Plugin => ({ name: "database", register: async ({ toolbox }) => { toolbox.database = { connect: async () => { /* ... */ }, query: async (sql) => { /* ... */ }, }; }, }); // Use in commands with full autocomplete cli.addCommand({ name: "users", execute: async ({ database }) => { // ✅ Full type safety and autocomplete await database.connect(); const users = await database.query("SELECT * FROM users"); }, }); ``` ## Built-in Plugins [#built-in-plugins] ### Error Handler Plugin [#error-handler-plugin] Graceful error handling with formatted output: ```typescript import { errorHandlerPlugin } from "@visulima/cerebro/plugin/error-handler"; cli.use( errorHandlerPlugin({ exitOnError: true, // Exit process on error showStackTrace: false, // Hide stack traces logErrors: true, // Log errors to console }), ); ``` ### Runtime Version Check Plugin [#runtime-version-check-plugin] Check Node.js/Deno/Bun version requirements: ```typescript import { runtimeVersionCheckPlugin } from "@visulima/cerebro/plugin/runtime-version-check"; cli.use( runtimeVersionCheckPlugin({ requiredVersion: ">=18.0.0", // Minimum Node.js version message: "Please upgrade to Node.js 18 or higher", }), ); ``` ### Update Notifier Plugin [#update-notifier-plugin] Notify users of available updates: ```typescript import { updateNotifierPlugin } from "@visulima/cerebro/plugin/update-notifier"; cli.use( updateNotifierPlugin({ packageName: "my-cli", packageVersion: "1.0.0", checkInterval: 86400000, // Check once per day updateMessage: "Update available: {latest} (current: {current})", }), ); ``` ## Custom Plugin Example [#custom-plugin-example] Create a plugin that adds HTTP utilities: ```typescript import type { Plugin } from "@visulima/cerebro"; // Type declaration declare global { namespace Cerebro { interface ExtensionOverrides { http: { get: (url: string) => Promise; post: (url: string, data: unknown) => Promise; }; } } } // Plugin implementation export const httpPlugin = (): Plugin => ({ name: "http", version: "1.0.0", register: async ({ toolbox, logger }) => { logger.log("Initializing HTTP plugin"); toolbox.http = { get: async (url: string): Promise => { const response = await fetch(url); return response.json() as Promise; }, post: async (url: string, data: unknown): Promise => { const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), }); return response.json() as Promise; }, }; }, }); // Usage cli.use(httpPlugin()); cli.addCommand({ name: "fetch-user", argument: { name: "id", type: String, }, execute: async ({ argument, http, logger }) => { const user = await http.get(`/api/users/${argument[0]}`); logger.log(`User: ${user.name}`); }, }); ``` ## Plugin Dependencies [#plugin-dependencies] Plugins can depend on other plugins: ```typescript const databasePlugin = (): Plugin => ({ name: "database", dependencies: ["config"], // Requires config plugin register: async ({ toolbox }) => { const { config } = toolbox; toolbox.database = createDatabase(config.databaseUrl); }, }); ``` ## Plugin Best Practices [#plugin-best-practices] ### Keep Plugins Focused [#keep-plugins-focused] Each plugin should have a single responsibility: ```typescript // ✅ Good: Focused plugin const configPlugin = (): Plugin => ({ name: "config", register: async ({ toolbox }) => { toolbox.config = loadConfig(); }, }); // ❌ Bad: Plugin does too much const megaPlugin = (): Plugin => ({ name: "mega", register: async ({ toolbox }) => { toolbox.config = loadConfig(); toolbox.database = connectDatabase(); toolbox.cache = createCache(); toolbox.logger = createLogger(); }, }); ``` ### Handle Errors Gracefully [#handle-errors-gracefully] Plugins should not crash the CLI: ```typescript const plugin = (): Plugin => ({ name: "my-plugin", register: async ({ toolbox, logger }) => { try { toolbox.feature = await initializeFeature(); } catch (error) { logger.error(`Failed to initialize feature: ${error.message}`); // Provide fallback toolbox.feature = createFallback(); } }, }); ``` ### Document Type Extensions [#document-type-extensions] Always provide type declarations for toolbox extensions: ```typescript // ✅ Good: Type-safe extension declare global { namespace Cerebro { interface ExtensionOverrides { myFeature: { doSomething: () => void; }; } } } ``` # Configuration (/docs/packages/cerebro/configuration) # Configuration [#configuration] Configure Cerebro CLI instances with various options and settings. ## CLI Options [#cli-options] Configure the CLI instance at creation: ```typescript import { createCerebro } from "@visulima/cerebro"; const cli = createCerebro("my-cli", { packageName: "@company/my-cli", packageVersion: "1.0.0", logger: console, cwd: process.cwd(), argv: process.argv.slice(2), }); ``` ### packageName [#packagename] The npm package name: ```typescript { packageName: "@company/my-cli"; } ``` Used by: * Update notifier plugin * Version command * Help text generation ### packageVersion [#packageversion] The package version: ```typescript { packageVersion: "1.0.0"; } ``` Displayed by `--version` flag. ### logger [#logger] Custom logger instance (default: `console`): ```typescript import { createPail } from "@visulima/pail"; const logger = createPail(); const cli = createCerebro("my-cli", { logger, }); ``` ### cwd [#cwd] Working directory (default: `process.cwd()`): ```typescript { cwd: "/custom/directory"; } ``` ### argv [#argv] Command-line arguments (default: `process.argv.slice(2)`): ```typescript { argv: ["build", "--production"]; } ``` ## Environment Variables [#environment-variables] ### CEREBRO\_OUTPUT\_LEVEL [#cerebro_output_level] Control output verbosity: ```bash CEREBRO_OUTPUT_LEVEL=quiet my-cli build # No output CEREBRO_OUTPUT_LEVEL=normal my-cli build # Normal output (default) CEREBRO_OUTPUT_LEVEL=verbose my-cli build # Verbose output CEREBRO_OUTPUT_LEVEL=debug my-cli build # Debug output ``` Values: * `quiet` - No output * `normal` - Standard output * `verbose` - Detailed output * `debug` - Debug information ### CEREBRO\_MIN\_NODE\_VERSION [#cerebro_min_node_version] Override minimum Node.js version check: ```bash CEREBRO_MIN_NODE_VERSION=16.0.0 my-cli build ``` ### CEREBRO\_TERMINAL\_WIDTH [#cerebro_terminal_width] Override terminal width detection: ```bash CEREBRO_TERMINAL_WIDTH=120 my-cli help ``` ## Plugin Configuration [#plugin-configuration] ### Error Handler [#error-handler] ```typescript import { errorHandlerPlugin } from "@visulima/cerebro/plugin/error-handler"; cli.use( errorHandlerPlugin({ exitOnError: true, // Exit process on errors showStackTrace: false, // Hide stack traces logErrors: true, // Log to console }), ); ``` ### Runtime Version Check [#runtime-version-check] ```typescript import { runtimeVersionCheckPlugin } from "@visulima/cerebro/plugin/runtime-version-check"; cli.use( runtimeVersionCheckPlugin({ requiredVersion: ">=18.0.0", message: "Custom error message", }), ); ``` ### Update Notifier [#update-notifier] ```typescript import { updateNotifierPlugin } from "@visulima/cerebro/plugin/update-notifier"; cli.use( updateNotifierPlugin({ packageName: "my-cli", packageVersion: "1.0.0", checkInterval: 86400000, // 24 hours in ms updateMessage: "Update available: {latest}", }), ); ``` ## Command Configuration [#command-configuration] ### Global Options [#global-options] Add options available to all commands: ```typescript cli.addGlobalOption({ name: "config", alias: "c", description: "Configuration file path", type: String, defaultValue: "./config.json", }); ``` ### Default Command [#default-command] Set command to run when none specified: ```typescript cli.setDefaultCommand("help"); ``` ### Help Sections [#help-sections] Organize commands in help output: ```typescript cli.addCommandSection({ name: "Database Commands", description: "Manage database operations", commands: ["db:migrate", "db:seed"], }); cli.addCommandSection({ name: "Build Commands", description: "Build and deploy", commands: ["build", "deploy"], }); ``` ## Configuration Files [#configuration-files] Create a configuration file system: ```typescript // my-cli.config.json { "output": "./dist", "minify": true, "sourcemap": false, "plugins": ["@my-cli/plugin-react"] } ``` Load configuration: ```typescript import { resolve } from "path"; import { readFileSync, existsSync } from "fs"; function loadConfig(cwd: string) { const configPath = resolve(cwd, "my-cli.config.json"); if (!existsSync(configPath)) { return {}; } return JSON.parse(readFileSync(configPath, "utf-8")); } cli.addCommand({ name: "build", execute: async ({ cwd, logger }) => { const config = loadConfig(cwd); logger.log(`Using config:`, config); }, }); ``` ## TypeScript Configuration [#typescript-configuration] Configure TypeScript for your CLI project: ```json // tsconfig.json { "compilerOptions": { "target": "ES2022", "module": "ES2022", "moduleResolution": "bundler", "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "declaration": true, "declarationMap": true }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"] } ``` # Advanced Features (/docs/packages/cerebro/guides/advanced) Learn about advanced Cerebro features for building complex CLIs. ## Command Composition [#command-composition] Call commands from within other commands using `runtime.runCommand()`: ```typescript cli.addCommand({ name: "deploy", execute: async ({ runtime, logger }) => { logger.info("Building..."); await runtime.runCommand("build", { argv: ["--production"], }); logger.info("Testing..."); await runtime.runCommand("test", { argv: ["--coverage"], }); logger.info("Deploying..."); }, }); ``` ### Passing Arguments [#passing-arguments] Pass arguments to called commands: ```typescript await runtime.runCommand("copy", { argv: ["file1.txt", "file2.txt"], }); ``` ### Passing Options [#passing-options] Pass options to called commands: ```typescript await runtime.runCommand("build", { argv: ["--production", "--output", "dist"], }); ``` ### Merging Options [#merging-options] Merge additional options: ```typescript await runtime.runCommand("build", { argv: ["--production"], customOption: "value", }); ``` ### Getting Return Values [#getting-return-values] Commands can return values: ```typescript cli.addCommand({ name: "calculate", execute: () => { return 42; }, }); cli.addCommand({ name: "use-calculation", execute: async ({ runtime }) => { const result = await runtime.runCommand("calculate"); console.log(`Result: ${result}`); // 42 }, }); ``` ## Custom CLI Configuration [#custom-cli-configuration] Configure your CLI instance: ```typescript const cli = new Cerebro("my-app", { packageName: "my-app", packageVersion: "1.0.0", cwd: "/custom/path", logger: customLogger, argv: process.argv.slice(2), // Custom argv }); ``` ### Custom Logger [#custom-logger] Provide a custom logger: ```typescript const customLogger = { info: (...args) => console.log("[INFO]", ...args), error: (...args) => console.error("[ERROR]", ...args), warn: (...args) => console.warn("[WARN]", ...args), debug: (...args) => console.debug("[DEBUG]", ...args), }; const cli = new Cerebro("my-app", { logger: customLogger, }); ``` ## Command Sections [#command-sections] Customize help output: ```typescript cli.setCommandSection({ header: "My Awesome CLI v1.0.0", footer: "For more info, visit https://example.com", }); ``` ## Default Command [#default-command] Set a default command when none is provided: ```typescript cli.setDefaultCommand("help"); // Shows help by default cli.setDefaultCommand("start"); // Runs start command by default ``` ## Lazy Command Loading [#lazy-command-loading] For CLIs with many commands or heavy per-command dependencies, the combined startup cost of evaluating every command's module at import time can dominate `--help` and short-running commands. Cerebro lets you defer the handler import until the command is actually invoked. ### When to use it [#when-to-use-it] You'll see a meaningful win when: * You have **10+ commands** registered. * Most commands pull in heavy transitive deps (rendering libs, AI clients, database drivers, …) that aren't needed for `--help` or unrelated commands. * Startup time matters — interactive CLIs, frequently-invoked dev tools, serverless contexts. For a tiny CLI with two subcommands and minimal deps, the difference is noise — leave them eager. ### How it works [#how-it-works] Replace `execute` with `loader: () => import("./handler")`. The loader returns a promise resolving to a module whose **default export** is the handler: ```typescript // commands/build.ts const build: Command = { name: "build", description: "Build the project", options: [{ name: "output", type: String, alias: "o" }], loader: () => import("./build.handler"), }; export default build; // commands/build.handler.ts export default async ({ logger, options }) => { logger.info(`Building to ${options.output ?? "dist"}`); }; ``` Help, completion, and option validation iterate metadata only — they never trigger loaders. The first invocation of the command imports the handler module; the resolved handler is memoised on the command for subsequent calls within the same process. ### Typed handlers [#typed-handlers] Co-locate the option type with the metadata using `CreateOptions`: ```typescript // commands/build.ts import type { Command, CreateOptions } from "@visulima/cerebro"; export type BuildOptions = CreateOptions<{ output: string | undefined; production: boolean | undefined; }>; const build: Command = { name: "build", options: [ { name: "output", type: String }, { name: "production", type: Boolean }, ], loader: () => import("./build.handler"), }; export default build; // commands/build.handler.ts import type { Toolbox } from "@visulima/cerebro"; import type { BuildOptions } from "./index"; export default async ({ options }: Toolbox) => { // options.output: string | undefined // options.production: boolean | undefined }; ``` ### Shared handler modules [#shared-handler-modules] When several subcommands of a parent share helpers, put their handlers together as **named exports** in one module and load each via `lazyNamed`: ```typescript // commands/cache.handlers.ts export const cacheList: CommandExecute = async (toolbox) => { /* … */ }; export const cacheClean: CommandExecute = async (toolbox) => { /* … */ }; // commands/cache.ts import { lazyNamed, type Command } from "@visulima/cerebro"; const cacheList: Command = { name: "list", commandPath: ["cache"], loader: lazyNamed(() => import("./cache.handlers"), "cacheList"), }; const cacheClean: Command = { name: "clean", commandPath: ["cache"], loader: lazyNamed(() => import("./cache.handlers"), "cacheClean"), }; ``` The shared module is imported once (when any cache subcommand runs); each subcommand resolves to its own named export. ### Recommended layout [#recommended-layout] For each command, two files in a directory: ``` commands/ build/ index.ts # metadata stub (cheap import: only types + the loader) handler.ts # heavy imports + default-exported handler ... ``` Node's module resolution maps `import build from "./commands/build"` to `./commands/build/index.ts`, so existing `cli.addCommand(buildCommand)` calls keep working unchanged. ### Errors [#errors] If `loader()` rejects, or the resolved module has no default-exported function, cerebro throws a `CommandLoaderError`. The original error is attached as `cause`. ```typescript import { CommandLoaderError } from "@visulima/cerebro"; try { await cli.run({ shouldExitProcess: false }); } catch (error) { if (error instanceof CommandLoaderError) { console.error(`Failed to load ${error.commandName}:`, error.cause); } } ``` ## Running Without Exiting [#running-without-exiting] For testing or programmatic use: ```typescript await cli.run({ shouldExitProcess: false, // Don't exit process autoDispose: false, // Don't cleanup automatically }); ``` ## Error Handling [#error-handling] ### Custom Error Handling [#custom-error-handling] Use plugins for custom error handling: ```typescript cli.addPlugin({ name: "error-handler", onError: async (error, toolbox) => { // Custom error formatting console.error(`❌ Error in ${toolbox.commandName}:`); console.error(error.message); // Log to file await logErrorToFile(error, toolbox); // Send to monitoring await sendToMonitoring(error); }, }); ``` ### Error Types [#error-types] Cerebro provides specific error types: ```typescript import { CommandNotFoundError, CommandValidationError } from "@visulima/cerebro"; // Handle specific errors try { await cli.run(); } catch (error) { if (error instanceof CommandNotFoundError) { console.error(`Command not found: ${error.command}`); console.error(`Did you mean: ${error.alternatives.join(", ")}?`); } else if (error instanceof CommandValidationError) { console.error(`Validation error: ${error.message}`); } } ``` ## Shell Completions [#shell-completions] Enable shell autocompletions for your CLI. ### Installation [#installation] Install the required dependency: `bash pnpm add @bomb.sh/tab ` `bash npm install @bomb.sh/tab ` `bash yarn add @bomb.sh/tab ` ### Setup [#setup] Add the completion command to your CLI: ```typescript import completionCommand from "@visulima/cerebro/command/completion"; cli.addCommand(completionCommand); ``` Then generate completions: ```bash cli completion --shell=zsh > ~/.zshrc cli completion --shell=bash > ~/.bashrc cli completion --shell=fish > ~/.config/fish/completions/cli.fish cli completion --shell=powershell > cli.ps1 ``` ## README Generation [#readme-generation] Generate comprehensive README documentation for your CLI commands automatically. ### Installation [#installation-1] First, install the required dependency: `bash pnpm add github-slugger ` `bash npm install github-slugger ` `bash yarn add github-slugger ` ### Setup [#setup-1] Add the readme command to your CLI: ```typescript import readmeCommand from "@visulima/cerebro/command/readme-generator"; cli.addCommand(readmeCommand); ``` ### Basic Usage [#basic-usage] Generate a README with all your commands: ```bash cli readme ``` This will: * Read or create a `README.md` file * Generate usage examples * List all commands with descriptions * Create a table of contents * Replace content between special tags: ``, ``, `` ### README Template [#readme-template] The command uses special HTML comment tags to mark sections: ```markdown # My CLI ``` The `readme` command will replace content between these tags. ### Options [#options] #### `--readme-path` [#--readme-path] Specify a custom README file path: ```bash cli readme --readme-path=DOCS.md ``` #### `--output-dir` [#--output-dir] Set output directory for multi-file mode (default: `docs`): ```bash cli readme --multi --output-dir=documentation ``` #### `--multi` [#--multi] Generate multi-file documentation grouped by command groups: ```bash cli readme --multi ``` This creates separate markdown files for each command group in the output directory. #### `--aliases` [#--aliases] Include command aliases in the command list: ```bash cli readme --aliases ``` #### `--dry-run` [#--dry-run] Preview what would be generated without writing files: ```bash cli readme --dry-run ``` #### `--version` [#--version] Specify a custom version for the generated documentation: ```bash cli readme --version=2.0.0 ``` #### `--nested-topics-depth` [#--nested-topics-depth] Control maximum depth for nested topics in multi-file mode: ```bash cli readme --multi --nested-topics-depth=2 ``` ### Example Workflow [#example-workflow] 1. **Create a README template** with the special tags: ```markdown # My Awesome CLI ``` 2. **Add the readme command** to your CLI: ```typescript import readmeCommand from "@visulima/cerebro/command/readme-generator"; cli.addCommand(readmeCommand); ``` 3. **Generate the README**: ```bash cli readme ``` 4. **The README will be updated** with: * Installation and usage instructions * Complete command list with descriptions * Command documentation with options and examples * Table of contents ### Multi-File Documentation [#multi-file-documentation] For larger CLIs, use multi-file mode to organize documentation: ```bash cli readme --multi --output-dir=docs ``` This creates: * `docs/Build.md` - Commands in the "Build" group * `docs/Deploy.md` - Commands in the "Deploy" group * And updates `README.md` with links to these files ### Cross-Platform Line Endings [#cross-platform-line-endings] The `readme` command automatically handles different line ending formats (LF, CRLF, CR) and normalizes them to LF for consistent output across platforms. ## Verbosity Levels [#verbosity-levels] Control output verbosity: ```typescript // In your CLI options { name: "verbose", type: Boolean, description: "Verbose output" } // Or use environment variable process.env.CEREBRO_OUTPUT_LEVEL = "64"; // VERBOSITY_VERBOSE ``` Available levels: * `VERBOSITY_QUIET` (16) - Minimal output * `VERBOSITY_NORMAL` (32) - Normal output * `VERBOSITY_VERBOSE` (64) - Verbose output * `VERBOSITY_DEBUG` (128) - Debug output ## Command Groups [#command-groups] Organize commands: ```typescript cli.addCommand({ name: "build", group: "Development", execute: () => {}, }); cli.addCommand({ name: "test", group: "Development", execute: () => {}, }); cli.addCommand({ name: "deploy", group: "Production", execute: () => {}, }); ``` Help output will group commands accordingly. ## Multiple Commands [#multiple-commands] Register multiple commands efficiently: ```typescript const commands = [ { name: "build", execute: () => {}, }, { name: "test", execute: () => {}, }, { name: "deploy", execute: () => {}, }, ]; commands.forEach((cmd) => cli.addCommand(cmd)); ``` ## TypeScript Best Practices [#typescript-best-practices] ### Extending Toolbox Type [#extending-toolbox-type] ```typescript declare global { namespace Cerebro { interface ExtensionOverrides { myFeature: () => void; api: { get: (url: string) => Promise; }; } } } ``` ### Typed Commands [#typed-commands] ```typescript interface BuildOptions { production: boolean; output: string; } cli.addCommand({ name: "build", options: [ { name: "production", type: Boolean }, { name: "output", type: String }, ], execute: ({ options }: { options: BuildOptions }) => { // TypeScript knows the options structure }, }); ``` ## Performance Helpers [#performance-helpers] Cerebro ships with two standalone helpers that run **before** the CLI framework loads. Call them at the very top of your entry point — before `createCerebro()` and any heavy imports. ```typescript // bin.ts import enableCompileCache from "@visulima/cerebro/compile-cache"; import { applyHeapTuning } from "@visulima/cerebro/heap-tuning"; // 1. Tune V8 heap (may re-spawn the process and never return) applyHeapTuning(); // 2. Enable compile cache for faster subsequent startups enableCompileCache(); // Now load the rest of your CLI import { createCerebro } from "@visulima/cerebro"; const cli = createCerebro("my-cli"); // ... ``` ### Heap Tuning [#heap-tuning] `applyHeapTuning()` computes optimal `--max-old-space-size` and `--max-semi-space-size` V8 flags based on system memory. Because these flags can only be set at process startup, the helper re-spawns the current process with the computed flags when they are missing. After re-spawn, the flags are already present and the call becomes a no-op. **How it works:** * Allocates a percentage of total system RAM to V8's old-space (default: 75%) * Applies tiered semi-space sizing for the young generation: | Old-space (MiB) | Semi-space (MiB) | | --------------- | ---------------- | | ≤ 512 | 4 | | ≤ 1024 | 8 | | ≤ 2048 | 16 | | ≤ 4096 | 32 | | ≤ 8192 | 64 | | > 8192 | log2-scaled | * Respects existing flags — if the user sets `NODE_OPTIONS="--max-old-space-size=4096"`, their value is kept **Custom allocation:** ```typescript // Use 50% of RAM instead of the default 75% applyHeapTuning({ maxOldSpacePercent: 0.5 }); ``` ### Compile Cache [#compile-cache] `enableCompileCache()` enables V8's compile cache so that subsequent runs of your CLI skip re-parsing and re-compiling JavaScript source files. This can reduce startup time by 30–70% for large CLI tools. **How it works:** 1. Tries `module.enableCompileCache()` — a native Node.js 22.8+ API that stores compiled bytecode alongside source files for instant reuse 2. Falls back to the [`v8-compile-cache`](https://www.npmjs.com/package/v8-compile-cache) npm package on older Node.js versions (must be installed as a dependency) 3. If neither is available, silently does nothing ## Performance Tips [#performance-tips] 1. **Use the performance helpers above** - Heap tuning and compile cache make the biggest difference 2. **Lazy load plugins** - Only load plugins when needed 3. **Use command groups** - Organize commands for faster lookups 4. **Cache expensive operations** - Cache results in plugin `init` 5. **Minimize toolbox extensions** - Only add what's needed ## Testing [#testing] Test your CLI commands: ```typescript import { describe, it, expect } from "vitest"; import { Cerebro } from "@visulima/cerebro"; describe("CLI", () => { it("should execute command", async () => { const cli = new Cerebro("test-cli", { argv: ["hello", "World"], }); const execute = vi.fn(); cli.addCommand({ name: "hello", execute, }); await cli.run({ shouldExitProcess: false }); expect(execute).toHaveBeenCalled(); }); }); ``` ## Best Practices [#best-practices] 1. **Use command composition** - Break complex operations into smaller commands 2. **Handle errors gracefully** - Use error hooks and provide helpful messages 3. **Document commands** - Always include descriptions and examples 4. **Type everything** - Use TypeScript for better DX 5. **Test your CLI** - Write tests for your commands 6. **Follow CLI conventions** - Use standard option names and patterns # Building a CLI Application (/docs/packages/cerebro/guides/building-cli) # Building a CLI Application [#building-a-cli-application] Learn how to build a production-ready CLI application with Cerebro from start to finish. ## Project Setup [#project-setup] Create a new project: ```bash mkdir my-cli && cd my-cli npm init -y npm install @visulima/cerebro @visulima/command-line-args @visulima/error ``` Create `src/cli.ts`: ```typescript #!/usr/bin/env node import { createCerebro } from "@visulima/cerebro"; const cli = createCerebro("my-cli", { packageName: "my-cli", packageVersion: "1.0.0", }); // Commands will go here await cli.run(); ``` Make it executable: ```bash chmod +x src/cli.ts ``` ## Add Commands [#add-commands] Create command files in `src/commands/`: ```typescript // src/commands/build.ts import type { Command } from "@visulima/cerebro"; export const buildCommand: Command = { name: "build", description: "Build the project", options: [ { name: "output", alias: "o", description: "Output directory", type: String, defaultValue: "./dist", }, { name: "production", alias: "p", description: "Production build", type: Boolean, }, ], execute: async ({ options, logger }) => { logger.log(`Building project...`); logger.log(`Output: ${options.output}`); logger.log(`Mode: ${options.production ? "production" : "development"}`); // Build logic here await performBuild(options); logger.log("Build complete!"); }, }; async function performBuild(options: any) { // Your build logic } ``` Register commands: ```typescript // src/cli.ts import { buildCommand } from "./commands/build"; import { deployCommand } from "./commands/deploy"; cli.addCommand(buildCommand); cli.addCommand(deployCommand); ``` ## Add Plugins [#add-plugins] Install and use plugins: ```bash npm install @visulima/fs @visulima/path ``` ```typescript // src/cli.ts import { errorHandlerPlugin } from "@visulima/cerebro/plugin/error-handler"; import { updateNotifierPlugin } from "@visulima/cerebro/plugin/update-notifier"; cli.use(errorHandlerPlugin({ exitOnError: true })); cli.use( updateNotifierPlugin({ packageName: "my-cli", packageVersion: "1.0.0", checkInterval: 86400000, // 24 hours }), ); ``` ## Configuration [#configuration] Create a configuration system: ```typescript // src/config.ts import { resolve } from "path"; import { readFileSync, existsSync } from "fs"; export interface Config { output: string; minify: boolean; sourcemap: boolean; } export function loadConfig(cwd: string): Config { const configPath = resolve(cwd, "my-cli.config.json"); if (existsSync(configPath)) { const content = readFileSync(configPath, "utf-8"); return JSON.parse(content); } return { output: "./dist", minify: false, sourcemap: true, }; } // Use in commands cli.addCommand({ name: "build", execute: async ({ cwd, logger }) => { const config = loadConfig(cwd); logger.log(`Config: ${JSON.stringify(config, null, 2)}`); }, }); ``` ## Package for Distribution [#package-for-distribution] Update `package.json`: ```json { "name": "my-cli", "version": "1.0.0", "type": "module", "bin": { "my-cli": "./dist/cli.js" }, "files": ["dist"], "scripts": { "build": "tsc", "prepublishOnly": "npm run build" }, "dependencies": { "@visulima/cerebro": "^1.0.0" } } ``` Build and test: ```bash npm run build node dist/cli.js --help ``` ## Link for Local Testing [#link-for-local-testing] Test your CLI locally: ```bash npm link my-cli --help ``` ## Publish to npm [#publish-to-npm] ```bash npm login npm publish ``` Users can install and use: ```bash npm install -g my-cli my-cli build --production ``` ## Complete Example [#complete-example] Here's a complete production CLI: ```typescript #!/usr/bin/env node import { createCerebro } from "@visulima/cerebro"; import { errorHandlerPlugin } from "@visulima/cerebro/plugin/error-handler"; const cli = createCerebro("project-cli", { packageName: "@company/project-cli", packageVersion: "2.1.0", }); // Use plugins cli.use(errorHandlerPlugin({ exitOnError: true })); // Build command cli.addCommand({ name: "build", description: "Build the project", options: [ { name: "output", alias: "o", type: String, defaultValue: "./dist", description: "Output directory", }, { name: "production", alias: "p", type: Boolean, description: "Production mode", }, ], execute: async ({ options, logger }) => { logger.log("Building..."); // Build logic logger.log("Done!"); }, }); // Deploy command with nested commands cli.addCommand({ name: "deploy", description: "Deploy application", commands: [ { name: "staging", description: "Deploy to staging", execute: async ({ logger }) => { logger.log("Deploying to staging..."); }, }, { name: "production", description: "Deploy to production", options: [ { name: "confirm", type: Boolean, required: true, description: "Confirm production deployment", }, ], execute: async ({ options, logger }) => { if (options.confirm) { logger.log("Deploying to production..."); } }, }, ], }); await cli.run(); ``` # Commands (/docs/packages/cerebro/guides/commands) # Commands [#commands] Commands are the core building blocks of your CLI. They define what actions users can perform. ## Basic Command [#basic-command] A command requires at minimum a `name` and an `execute` function: ```typescript cli.addCommand({ name: "hello", execute: ({ logger }) => { logger.info("Hello, world!"); }, }); ``` ## Command Properties [#command-properties] ### Name [#name] The command name is used to invoke the command: ```typescript cli.addCommand({ name: "build", // Command invoked as: cli build execute: () => {}, }); ``` ### Description [#description] A short description displayed in help output: ```typescript cli.addCommand({ name: "build", description: "Build the project for production", execute: () => {}, }); ``` ### Aliases [#aliases] Provide alternative names for your command: ```typescript cli.addCommand({ name: "build", alias: "b", // Can use: cli b // or multiple aliases: // alias: ["b", "compile"], execute: () => {}, }); ``` ### Hidden Commands [#hidden-commands] Hide commands from help output but still allow execution: ```typescript cli.addCommand({ name: "internal", hidden: true, // Won't appear in help execute: () => {}, }); ``` ### Grouped Commands [#grouped-commands] Organize commands into groups for better help organization: ```typescript cli.addCommand({ name: "build", group: "Build", // Groups commands in help output execute: () => {}, }); cli.addCommand({ name: "test", group: "Build", execute: () => {}, }); ``` ### Examples [#examples] Provide usage examples for your command: ```typescript cli.addCommand({ name: "deploy", examples: ["deploy --env production", "deploy --env staging --dry-run"], execute: () => {}, }); ``` ## Command Execution [#command-execution] The `execute` function receives a `toolbox` object with all available resources: ```typescript cli.addCommand({ name: "example", execute: ({ argument, // Positional arguments options, // Parsed options logger, // Logger instance runtime, // CLI runtime (for calling other commands) command, // Command definition commandName, // Command name argv, // Original argv }) => { // Your command logic }, }); ``` ### Async Commands [#async-commands] Commands can be async: ```typescript cli.addCommand({ name: "fetch", execute: async ({ logger }) => { const data = await fetch("https://api.example.com/data"); logger.info("Data fetched!"); }, }); ``` ### Lazy-Loaded Commands [#lazy-loaded-commands] Use `loader` instead of `execute` to defer importing the handler module until the command is actually invoked. Help, completion, and option validation work from the metadata alone — they never trigger the loader. See the [advanced guide](/docs/packages/cerebro/guides/advanced#lazy-command-loading) for the full pattern. ```typescript cli.addCommand({ name: "build", description: "Build the project", options: [{ name: "output", type: String }], loader: () => import("./commands/build.handler"), }); // commands/build.handler.ts export default async ({ logger, options }) => { logger.info(`Building to ${options.output ?? "dist"}`); }; ``` A command must declare either `execute` or `loader`, but not both. ## Positional Arguments [#positional-arguments] Define positional arguments using the `argument` property: ```typescript cli.addCommand({ name: "greet", argument: { name: "name", type: String, description: "Name to greet", defaultValue: "World", }, execute: ({ argument }) => { const name = argument[0]; // Access first argument console.log(`Hello, ${name}!`); }, }); ``` Usage: ```bash cli greet Alice # argument[0] = "Alice" cli greet # argument[0] = "World" (default) ``` ### Multiple Arguments [#multiple-arguments] Arguments accept multiple values by default: ```typescript cli.addCommand({ name: "copy", argument: { name: "files", type: String, description: "Files to copy", }, execute: ({ argument }) => { // argument is an array of all provided values argument.forEach((file) => { console.log(`Copying ${file}...`); }); }, }); ``` Usage: ```bash cli copy file1.txt file2.txt file3.txt # argument = ["file1.txt", "file2.txt", "file3.txt"] ``` ## Options [#options] Commands can accept options (flags): ```typescript cli.addCommand({ name: "build", options: [ { name: "production", type: Boolean, description: "Build for production", }, { name: "output", alias: "o", type: String, description: "Output directory", }, ], execute: ({ options }) => { if (options.production) { console.log("Building for production..."); } console.log(`Output: ${options.output || "dist"}`); }, }); ``` Usage: ```bash cli build --production --output build cli build -o build --production ``` See the [Options Guide](/docs/packages/cerebro/guides/options) for detailed option configuration. ## Nested Commands [#nested-commands] Cerebro supports nested commands (subcommands) using the `commandPath` property. This allows you to create hierarchical command structures like `cli deploy staging` or `cli db migrate up`. ### Basic Nested Commands [#basic-nested-commands] Create nested commands by specifying a `commandPath` array: ```typescript // Parent command path: ["deploy"] cli.addCommand({ name: "staging", commandPath: ["deploy"], description: "Deploy to staging environment", execute: ({ logger }) => { logger.info("Deploying to staging..."); }, }); cli.addCommand({ name: "production", commandPath: ["deploy"], description: "Deploy to production environment", execute: ({ logger }) => { logger.info("Deploying to production..."); }, }); ``` Usage: ```bash cli deploy staging cli deploy production ``` ### Multi-Level Nested Commands [#multi-level-nested-commands] You can nest commands multiple levels deep: ```typescript cli.addCommand({ name: "up", commandPath: ["db", "migrate"], description: "Run database migrations", execute: ({ logger }) => { logger.info("Running migrations..."); }, }); cli.addCommand({ name: "down", commandPath: ["db", "migrate"], description: "Rollback database migrations", execute: ({ logger }) => { logger.info("Rolling back migrations..."); }, }); cli.addCommand({ name: "status", commandPath: ["db", "migrate"], description: "Show migration status", execute: ({ logger }) => { logger.info("Migration status..."); }, }); ``` Usage: ```bash cli db migrate up cli db migrate down cli db migrate status ``` ### Nested Commands with Options [#nested-commands-with-options] Nested commands support all the same features as flat commands, including options: ```typescript cli.addCommand({ name: "production", commandPath: ["deploy"], description: "Deploy to production environment", options: [ { name: "dry-run", type: Boolean, description: "Show what would be deployed without deploying", }, { name: "tag", alias: "t", type: String, description: "Deployment tag", }, ], execute: ({ options, logger }) => { if (options["dry-run"]) { logger.info("Dry run: Would deploy to production"); return; } logger.info(`Deploying to production with tag: ${options.tag || "latest"}`); }, }); ``` Usage: ```bash cli deploy production --dry-run cli deploy production --tag v1.0.0 cli deploy production -t v1.0.0 --dry-run ``` ### Flat and Nested Commands Together [#flat-and-nested-commands-together] You can mix flat and nested commands in the same CLI: ```typescript // Flat command cli.addCommand({ name: "build", description: "Build the project", execute: ({ logger }) => { logger.info("Building..."); }, }); // Nested command with same name (different path) cli.addCommand({ name: "build", commandPath: ["docker"], description: "Build Docker image", execute: ({ logger }) => { logger.info("Building Docker image..."); }, }); ``` Usage: ```bash cli build # Runs flat "build" command cli docker build # Runs nested "docker build" command ``` ### Calling Nested Commands Programmatically [#calling-nested-commands-programmatically] Use `runtime.runCommand()` with the full command path: ```typescript cli.addCommand({ name: "deploy-all", execute: async ({ runtime, logger }) => { logger.info("Deploying to all environments..."); // Call nested commands programmatically await runtime.runCommand("deploy staging"); await runtime.runCommand("deploy production"); }, }); ``` You can also call nested commands from within other nested commands: ```typescript cli.addCommand({ name: "staging", commandPath: ["deploy"], execute: async ({ runtime, logger }) => { logger.info("Preparing staging deployment..."); await runtime.runCommand("build", { argv: ["--production"] }); logger.info("Deploying to staging..."); }, }); ``` ### Help Output [#help-output] Nested commands are automatically displayed in help with their full path: ```bash cli help ``` Output will show: ``` Available Commands deploy staging Deploy to staging environment deploy production Deploy to production environment db migrate up Run database migrations db migrate down Rollback database migrations ``` ### Command Path Validation [#command-path-validation] Command path segments are validated the same way as command names: * Must start with a letter * Can contain letters, numbers, hyphens, and underscores * Cannot contain spaces or special characters ```typescript // ✅ Valid cli.addCommand({ name: "staging", commandPath: ["deploy"], // Valid execute: () => {}, }); // ❌ Invalid - path segment must start with a letter cli.addCommand({ name: "test", commandPath: ["-invalid"], // Will throw error execute: () => {}, }); ``` ## Calling Other Commands [#calling-other-commands] Commands can call other commands programmatically: ```typescript cli.addCommand({ name: "deploy", execute: async ({ runtime, logger }) => { logger.info("Building..."); await runtime.runCommand("build", { argv: ["--production"], }); logger.info("Testing..."); await runtime.runCommand("test"); logger.info("Deploying..."); }, }); ``` The `runCommand` method allows you to: * Pass arguments via `argv` array * Merge additional options * Get return values from commands * Call nested commands using full path (e.g., `"deploy staging"`) ```typescript const result = await runtime.runCommand("build", { argv: ["--production"], customOption: "value", }); // Call nested commands await runtime.runCommand("deploy staging"); await runtime.runCommand("db migrate up"); ``` ## Error Handling [#error-handling] Commands can throw errors which are caught by Cerebro's error handling: ```typescript cli.addCommand({ name: "process", execute: ({ options }) => { if (!options.file) { throw new Error("File option is required"); } // Process file... }, }); ``` Errors are automatically handled and displayed to the user. See [Plugins Guide](/docs/packages/cerebro/guides/plugins) for custom error handling. ## Complete Example [#complete-example] ```typescript cli.addCommand({ name: "deploy", alias: "d", description: "Deploy the application", group: "Deployment", examples: ["deploy --env production", "deploy --env staging --dry-run"], argument: { name: "target", type: String, description: "Deployment target", defaultValue: "production", }, options: [ { name: "env", alias: "e", type: String, description: "Environment", required: true, }, { name: "dry-run", type: Boolean, description: "Perform a dry run", }, ], execute: async ({ argument, options, logger, runtime }) => { const target = argument[0]; logger.info(`Deploying to ${target} environment...`); if (options.dryRun) { logger.info("Dry run mode - no changes will be made"); return; } // Build first await runtime.runCommand("build", { argv: ["--production"] }); // Deploy logger.info("Deployment complete!"); }, }); ``` # Comparison with Popular CLIs (/docs/packages/cerebro/guides/comparison) When choosing a CLI framework, it's important to understand how Cerebro compares to other popular options. This guide provides a detailed comparison to help you make an informed decision. ## Quick Comparison Table [#quick-comparison-table] | Feature | Cerebro | Commander | Yargs | Oclif | Meow | CAC | Cleye | | ----------------------- | ------------------- | ----------- | ----------- | ----------- | ------ | ----------- | ----------- | | **TypeScript First** | ✅ Excellent | ⚠️ Partial | ⚠️ Partial | ✅ Good | ❌ No | ❌ No | ✅ Excellent | | **Plugin System** | ✅ Built-in | ❌ No | ❌ No | ✅ Built-in | ❌ No | ❌ No | ❌ No | | **Performance** | ✅ Fast | ⚠️ Moderate | ⚠️ Moderate | ⚠️ Moderate | ✅ Fast | ✅ Very Fast | ✅ Fast | | **Zero Config** | ✅ Yes | ✅ Yes | ✅ Yes | ❌ No | ✅ Yes | ✅ Yes | ✅ Yes | | **Shell Completions** | ✅ Built-in (Plugin) | ⚠️ Plugin | ⚠️ Plugin | ✅ Built-in | ❌ No | ❌ No | ❌ No | | **Command Composition** | ✅ Yes | ❌ No | ❌ No | ✅ Yes | ❌ No | ❌ No | ❌ No | | **Nested/Subcommands** | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes | ❌ No | ✅ Yes | ❌ No | | **Bundle Size** | ✅ Small | ⚠️ Medium | ⚠️ Large | ⚠️ Large | ✅ Tiny | ✅ Tiny | ✅ Small | | **Learning Curve** | ✅ Easy | ✅ Easy | ⚠️ Moderate | ⚠️ Steep | ✅ Easy | ✅ Easy | ✅ Easy | ## Detailed Comparisons [#detailed-comparisons] ### vs Commander.js [#vs-commanderjs] **Commander.js** is the most popular Node.js CLI framework, known for its simplicity and maturity. #### Advantages of Commander.js [#advantages-of-commanderjs] * **Widespread adoption** - Used by thousands of projects * **Large community** - Extensive documentation and examples * **Simple API** - Easy to get started * **Mature ecosystem** - Many plugins and extensions available #### Advantages of Cerebro [#advantages-of-cerebro] * **Better TypeScript support** - Superior type inference and autocomplete * **Built-in plugin system** - Extend functionality without external dependencies * **Command composition** - Call commands from within other commands * **Nested commands** - Support for hierarchical command structures * **Modern architecture** - Designed with TypeScript and async/await in mind * **Better performance** - Optimized for fast startup times #### When to Choose Commander.js [#when-to-choose-commanderjs] * You need maximum ecosystem compatibility * You're building a simple CLI without complex needs * Your team is already familiar with Commander.js #### When to Choose Cerebro [#when-to-choose-cerebro] * You want excellent TypeScript support out of the box * You need plugin extensibility * You want command composition capabilities * You need nested subcommands * Performance is important for your use case **Example Comparison:** ```typescript // Commander.js import { Command } from "commander"; const program = new Command(); program.name("my-app").version("1.0.0"); program .command("run") .option("-d, --debug", "output extra debugging") .action((options) => { if (options.debug) console.log("Debug mode"); }); program.parse(); ``` ```typescript // Cerebro import { Cerebro } from "@visulima/cerebro"; const cli = new Cerebro("my-app", { packageVersion: "1.0.0", }); cli.addCommand({ name: "run", options: [{ name: "debug", alias: "d", type: Boolean }], execute: ({ options, logger }) => { if (options.debug) logger.debug("Debug mode"); }, }); await cli.run(); ``` **Nested Commands Example:** ```typescript // Commander.js - Nested commands program .command("deploy") .command("staging") .action(() => { console.log("Deploying to staging..."); }); program .command("deploy") .command("production") .action(() => { console.log("Deploying to production..."); }); ``` ```typescript // Cerebro - Nested commands cli.addCommand({ name: "staging", commandPath: ["deploy"], description: "Deploy to staging environment", execute: ({ logger }) => { logger.info("Deploying to staging..."); }, }); cli.addCommand({ name: "production", commandPath: ["deploy"], description: "Deploy to production environment", execute: ({ logger }) => { logger.info("Deploying to production..."); }, }); // Usage: cli deploy staging // Usage: cli deploy production ``` ### vs Yargs [#vs-yargs] **Yargs** is a powerful argument parser with extensive features and a rich ecosystem. #### Advantages of Yargs [#advantages-of-yargs] * **Rich feature set** - Extensive argument parsing capabilities * **Middleware support** - Powerful middleware system * **Positional arguments** - Excellent support for complex argument patterns * **Validation** - Built-in validation and coercion #### Advantages of Cerebro [#advantages-of-cerebro-1] * **Simpler API** - More intuitive and easier to learn * **TypeScript-first** - Better type safety and inference * **Plugin architecture** - Built-in plugin system with lifecycle hooks * **Performance** - Faster startup and execution * **Command composition** - Built-in support for calling commands programmatically #### When to Choose Yargs [#when-to-choose-yargs] * You need complex argument parsing patterns * You require extensive middleware capabilities * Your CLI has very complex positional argument requirements #### When to Choose Cerebro [#when-to-choose-cerebro-1] * You want a cleaner, more modern API * TypeScript support is important * You need plugin extensibility * You prefer better performance **Example Comparison:** ```typescript // Yargs import yargs from "yargs"; import { hideBin } from "yargs/helpers"; const argv = yargs(hideBin(process.argv)) .option("input", { alias: "i", type: "string", description: "Input file", demandOption: true, }) .option("output", { alias: "o", type: "string", description: "Output file", }) .parse(); ``` ```typescript // Cerebro import { Cerebro } from "@visulima/cerebro"; const cli = new Cerebro("my-app"); cli.addCommand({ name: "process", options: [ { name: "input", alias: "i", type: String, description: "Input file", required: true, }, { name: "output", alias: "o", type: String, description: "Output file", }, ], execute: ({ options }) => { // Process files }, }); await cli.run(); ``` ### vs Oclif [#vs-oclif] **Oclif** is Salesforce's enterprise-grade CLI framework with extensive tooling and features. #### Advantages of Oclif [#advantages-of-oclif] * **Enterprise features** - Designed for large-scale CLI applications * **Built-in generators** - Scaffolding tools for commands and plugins * **Multi-command architecture** - Excellent for complex CLIs * **Plugin ecosystem** - Rich plugin marketplace * **Testing utilities** - Built-in testing helpers #### Advantages of Cerebro [#advantages-of-cerebro-2] * **Lighter weight** - Smaller bundle size and faster startup * **Simpler setup** - Zero configuration approach * **Better TypeScript** - Superior type inference * **Modern API** - Cleaner, more intuitive interface * **No generators needed** - Write commands directly without scaffolding #### When to Choose Oclif [#when-to-choose-oclif] * Building enterprise-grade CLIs with many commands * You need extensive scaffolding and generators * You want access to a plugin marketplace * Building CLIs that integrate with Salesforce #### When to Choose Cerebro [#when-to-choose-cerebro-2] * You want a lighter, faster framework * You prefer simplicity over enterprise features * TypeScript support is a priority * You want zero configuration **Example Comparison:** ```typescript // Oclif import { Command, Flags } from "@oclif/core"; export class Build extends Command { static flags = { production: Flags.boolean({ char: "p", description: "Production build", }), }; async run() { const { flags } = await this.parse(Build); // Build logic } } ``` ```typescript // Cerebro import { Cerebro } from "@visulima/cerebro"; const cli = new Cerebro("my-app"); cli.addCommand({ name: "build", options: [ { name: "production", alias: "p", type: Boolean, description: "Production build", }, ], execute: ({ options }) => { // Build logic }, }); await cli.run(); ``` ### vs Meow [#vs-meow] **Meow** is a minimal CLI helper built by Sindre Sorhus, focused on simplicity and zero dependencies. #### Advantages of Meow [#advantages-of-meow] * **Ultra-lightweight** - Zero dependencies, tiny bundle size * **Simple API** - Minimal and straightforward * **Fast** - Extremely fast startup * **No bloat** - Only essential features #### Advantages of Cerebro [#advantages-of-cerebro-3] * **More features** - Built-in plugin system, command composition * **Better TypeScript** - Full type safety and inference * **Help generation** - Automatic help text generation * **Shell completions** - Built-in completion support * **Command structure** - Better organization for multiple commands #### When to Choose Meow [#when-to-choose-meow] * Building extremely simple CLIs * Bundle size is critical * You only need basic argument parsing * Zero dependencies is a hard requirement #### When to Choose Cerebro [#when-to-choose-cerebro-3] * You need more than basic argument parsing * You want plugin extensibility * TypeScript support is important * You need help generation and completions **Example Comparison:** ```typescript // Meow import meow from "meow"; const cli = meow( ` Usage $ my-app Options --flag, -f Some flag Examples $ my-app unicorn `, { flags: { flag: { type: "boolean", alias: "f", }, }, }, ); ``` ```typescript // Cerebro import { Cerebro } from "@visulima/cerebro"; const cli = new Cerebro("my-app"); cli.addCommand({ name: "run", argument: { name: "input", type: String, }, options: [ { name: "flag", alias: "f", type: Boolean, }, ], execute: ({ argument, options }) => { // Command logic }, }); await cli.run(); ``` ### vs CAC [#vs-cac] **CAC** (Command And Conquer) is a super lightweight CLI framework with zero dependencies. #### Advantages of CAC [#advantages-of-cac] * **Zero dependencies** - Completely dependency-free * **Very fast** - Extremely lightweight and fast * **Simple** - Minimal API surface * **Small bundle** - Tiny footprint #### Advantages of Cerebro [#advantages-of-cerebro-4] * **TypeScript support** - Full type safety * **Plugin system** - Built-in extensibility * **More features** - Help generation, completions, command composition * **Better DX** - Superior developer experience * **Error handling** - Built-in error handling system #### When to Choose CAC [#when-to-choose-cac] * You need zero dependencies * Minimal bundle size is critical * You only need basic command parsing #### When to Choose Cerebro [#when-to-choose-cerebro-4] * You want TypeScript support * You need plugin capabilities * Better developer experience matters ### vs Cleye [#vs-cleye] **Cleye** is a modern TypeScript-first CLI library with strongly typed parameters. #### Advantages of Cleye [#advantages-of-cleye] * **TypeScript-first** - Excellent type safety * **Strong typing** - Strongly typed parameters * **Clean API** - Modern and intuitive * **Small bundle** - Lightweight #### Advantages of Cerebro [#advantages-of-cerebro-5] * **Plugin system** - Built-in plugin architecture * **Command composition** - Call commands programmatically * **More features** - Help generation, completions, toolbox * **Lifecycle hooks** - Plugin lifecycle management * **Better organization** - More structured for complex CLIs #### When to Choose Cleye [#when-to-choose-cleye] * You want a simple TypeScript-first CLI * You prefer minimal dependencies * Basic command parsing is sufficient #### When to Choose Cerebro [#when-to-choose-cerebro-5] * You need plugin extensibility * You want command composition * You need more advanced features ## Performance Comparison [#performance-comparison] Cerebro is optimized for performance, especially important for CLI tools that start fresh with every invocation: * **Fast startup** - Minimal initialization overhead * **Efficient parsing** - Optimized argument parsing * **Small memory footprint** - Lightweight runtime * **Quick command execution** - Optimized command dispatching For detailed performance benchmarks, see the [benchmark results](https://github.com/visulima/visulima/tree/main/packages/cerebro/__bench__). ## Migration Guide [#migration-guide] If you're considering migrating from another CLI framework, check out our [Migration Guide](https://github.com/visulima/visulima/blob/main/packages/cerebro/MIGRATION-GUIDE) for detailed examples and best practices. ## Features Comparison [#features-comparison] ### What Cerebro Has [#what-cerebro-has] * ✅ **Command composition** - Call commands programmatically * ✅ **Nested/Subcommands** - Support for `cli deploy staging` style commands using `commandPath` * ✅ **Plugin system** - Extensible architecture with lifecycle hooks * ✅ **TypeScript-first** - Excellent type safety and inference * ✅ **Shell completions** - Built-in completion support (via plugin) * ✅ **Help generation** - Automatic help text generation * ✅ **Error handling** - Built-in error types and handling * ✅ **Command grouping** - Organize commands in help output * ✅ **Option validation** - Required options, conflicts, implied options * ✅ **Negatable options** - `--no-` prefix support * ✅ **Command aliases** - Multiple aliases per command * ✅ **Global options** - Built-in help, version, verbose, quiet, debug * ✅ **Default command** - Set a default command when none provided ### What Cerebro Doesn't Have (Yet) [#what-cerebro-doesnt-have-yet] Cerebro focuses on simplicity and performance, which means some advanced features available in other frameworks are intentionally omitted or can be added via plugins: * ⚠️ **Multiple Named Arguments** - Single positional argument support only (can accept multiple values as array) * ⚠️ **Environment Variable Mapping** - No automatic `MYAPP_PORT` → `--port` mapping (can be added via plugin) * ⚠️ **Config File Support** - No built-in JSON/YAML/TOML config file reading (can be added via plugin) * ⚠️ **Middleware System** - No Yargs-style middleware for option preprocessing (plugins provide lifecycle hooks instead) * ⚠️ **Custom Option Coercion** - Basic type conversion only, no custom coercion functions * ⚠️ **Command Generators** - No scaffolding tools like Oclif (by design - prefer direct code) **Note:** Many missing features can be implemented via plugins. The plugin system is designed to be the extension mechanism for additional functionality. ## Summary [#summary] **Choose Cerebro if:** * ✅ You want excellent TypeScript support * ✅ You need plugin extensibility * ✅ Performance matters for your CLI * ✅ You want command composition * ✅ You need nested subcommands * ✅ You prefer modern, clean APIs * ✅ You need built-in shell completions * ✅ You want a simple, focused framework **Consider alternatives if:** * ❌ You need maximum ecosystem compatibility (→ Commander.js) * ❌ You require complex argument parsing patterns (→ Yargs) * ❌ You need environment variable/config file integration out of the box (→ Yargs) * ❌ You need zero dependencies (→ Meow, CAC) * ❌ You want minimal features (→ Cleye) * ❌ You need command generators/scaffolding (→ Oclif) **Note:** This comparison is based on features and typical use cases. Your specific requirements may vary. We recommend trying Cerebro and comparing it directly with your use case. Missing features can often be added via plugins or custom code. # Options (/docs/packages/cerebro/guides/options) # Options & Arguments [#options--arguments] Options (flags) and arguments are how users provide input to your commands. ## Options [#options] Options are named parameters passed with `--option` or `-o` syntax. ### Basic Options [#basic-options] ```typescript cli.addCommand({ name: "build", options: [ { name: "production", type: Boolean, description: "Build for production", }, ], execute: ({ options }) => { console.log(options.production); // true or false }, }); ``` Usage: ```bash cli build --production # options.production = true cli build # options.production = false ``` ### Option Types [#option-types] #### Boolean Options [#boolean-options] ```typescript { name: "verbose", type: Boolean, description: "Enable verbose output" } ``` #### String Options [#string-options] ```typescript { name: "output", type: String, description: "Output directory" } ``` Usage: `cli build --output dist` #### Number Options [#number-options] ```typescript { name: "port", type: Number, description: "Server port" } ``` Usage: `cli serve --port 3000` #### Array Options [#array-options] Options can accept multiple values: ```typescript { name: "files", type: String, // Will be an array multiple: true, description: "Files to process" } ``` Usage: `cli process --files file1.txt --files file2.txt` ### Aliases [#aliases] Provide short aliases for options: ```typescript { name: "output", alias: "o", type: String, description: "Output directory" } ``` Usage: ```bash cli build --output dist cli build -o dist # Same as above ``` ### Default Values [#default-values] Set default values for options: ```typescript { name: "port", type: Number, defaultValue: 3000, description: "Server port" } ``` ```typescript execute: ({ options }) => { console.log(options.port); // 3000 if not provided }; ``` ### Required Options [#required-options] Make options mandatory: ```typescript { name: "config", type: String, required: true, description: "Configuration file path" } ``` If not provided, Cerebro will throw an error with a helpful message. ### Negatable Options [#negatable-options] Boolean options can be negated with `--no-` prefix: ```typescript { name: "color", type: Boolean, defaultValue: true, description: "Enable colored output" } ``` Usage: ```bash cli build --color # options.color = true cli build --no-color # options.color = false cli build # options.color = true (default) ``` ### Conflicting Options [#conflicting-options] Prevent certain options from being used together: ```typescript cli.addCommand({ name: "serve", options: [ { name: "http", type: Boolean, conflicts: "https", description: "Use HTTP", }, { name: "https", type: Boolean, conflicts: "http", description: "Use HTTPS", }, ], execute: () => {}, }); ``` Using both will throw an error: ```bash cli serve --http --https # Error: Options "http" and "https" cannot be used together ``` ### Implied Options [#implied-options] Automatically set other options when one is used: ```typescript cli.addCommand({ name: "build", options: [ { name: "production", type: Boolean, implies: { minify: true, sourcemap: false }, description: "Build for production", }, { name: "minify", type: Boolean, description: "Minify output", }, { name: "sourcemap", type: Boolean, description: "Generate source maps", }, ], execute: ({ options }) => { // If --production is used, minify=true and sourcemap=false automatically }, }); ``` ### Boolean Options [#boolean-options-1] Boolean options are simple flags that are either present (true) or absent (false): ```typescript { name: "watch", type: Boolean, description: "Watch for changes" } ``` Usage: ```bash cli build --watch # options.watch = true cli build # options.watch = false ``` Note: If you need an option that can accept values, use a `String` type instead: ```typescript { name: "watch", type: String, description: "Watch mode (e.g., 'port' or 'file')" } ``` ### Hidden Options [#hidden-options] Hide options from help output: ```typescript { name: "debug", type: Boolean, hidden: true, // Won't appear in help description: "Enable debug mode" } ``` ### Type Labels [#type-labels] Customize the type label shown in help: ```typescript { name: "port", type: Number, typeLabel: "", // Instead of description: "Server port" } ``` ## Positional Arguments [#positional-arguments] Positional arguments are provided without option names: ```typescript cli.addCommand({ name: "copy", argument: { name: "source", type: String, description: "Source file", }, execute: ({ argument }) => { const source = argument[0]; console.log(`Copying ${source}...`); }, }); ``` Usage: `cli copy file.txt` ### Multiple Arguments [#multiple-arguments] Arguments accept multiple values: ```typescript cli.addCommand({ name: "copy", argument: { name: "files", type: String, description: "Files to copy", }, execute: ({ argument }) => { // argument is an array argument.forEach((file) => { console.log(`Copying ${file}...`); }); }, }); ``` Usage: `cli copy file1.txt file2.txt file3.txt` ### Argument Default Values [#argument-default-values] ```typescript argument: { name: "name", type: String, defaultValue: "World", description: "Name to greet" } ``` ### Argument Types [#argument-types] Arguments support the same types as options: * `String` - Text values * `Number` - Numeric values * `Boolean` - Boolean values (rare for arguments) ## Accessing Options and Arguments [#accessing-options-and-arguments] In your command's `execute` function: ```typescript execute: ({ options, argument }) => { // Options const isProduction = options.production; const outputDir = options.output || "dist"; // Arguments const fileName = argument[0]; const allFiles = argument; // Array of all arguments }; ``` ## Complete Example [#complete-example] ```typescript cli.addCommand({ name: "deploy", argument: { name: "target", type: String, defaultValue: "production", description: "Deployment target", }, options: [ { name: "env", alias: "e", type: String, required: true, description: "Environment name", }, { name: "force", alias: "f", type: Boolean, defaultValue: false, description: "Force deployment", }, { name: "dry-run", type: Boolean, description: "Perform a dry run", }, { name: "services", type: String, multiple: true, description: "Services to deploy", }, ], execute: ({ argument, options }) => { const target = argument[0]; const env = options.env; const force = options.force; const services = options.services || []; console.log(`Deploying to ${target} (${env})`); if (force) { console.log("Force mode enabled"); } if (services.length > 0) { console.log(`Services: ${services.join(", ")}`); } }, }); ``` Usage examples: ```bash # Basic usage cli deploy staging --env production # With multiple services cli deploy --env production --services api --services worker # Force deployment cli deploy --env production --force # Dry run cli deploy --env production --dry-run ``` ## Environment Variables [#environment-variables] Environment variables provide a way to configure commands without passing values on the command line. They're useful for: * Configuration that doesn't change frequently * Sensitive values that shouldn't appear in command history * CI/CD pipelines and automation * Default settings that can be overridden by options ### Defining Environment Variables [#defining-environment-variables] Add an `env` array to your command definition, similar to `options`: ```typescript cli.addCommand({ name: "build", env: [ { name: "API_KEY", type: String, description: "API key for authentication", }, { name: "TIMEOUT", type: Number, defaultValue: 5000, description: "Request timeout in milliseconds", }, { name: "VERBOSE", type: Boolean, defaultValue: false, description: "Enable verbose logging", }, ], execute: ({ env }) => { const apiKey = env.apiKey; // camelCase: API_KEY → apiKey const timeout = env.timeout; // 5000 if not set const verbose = env.verbose; // false if not set }, }); ``` ### Environment Variable Types [#environment-variable-types] Environment variables support the same types as options: #### String Environment Variables [#string-environment-variables] ```typescript { name: "API_URL", type: String, description: "API endpoint URL" } ``` Usage: `API_URL=https://api.example.com cli build` #### Number Environment Variables [#number-environment-variables] ```typescript { name: "PORT", type: Number, defaultValue: 3000, description: "Server port" } ``` Usage: `PORT=8080 cli serve` The value is parsed as an integer. Invalid values will use the default if provided. #### Boolean Environment Variables [#boolean-environment-variables] ```typescript { name: "ENABLE_CACHE", type: Boolean, defaultValue: false, description: "Enable caching" } ``` Boolean values are case-insensitive and accept: * `true`, `1`, `yes`, `on` → `true` * `false`, `0`, `no`, `off`, or any other value → `false` Usage: ```bash ENABLE_CACHE=true cli build ENABLE_CACHE=1 cli build ENABLE_CACHE=yes cli build ``` ### Default Values [#default-values-1] Set default values that are used when the environment variable is not set: ```typescript { name: "LOG_LEVEL", type: String, defaultValue: "info", description: "Logging level" } ``` ```typescript execute: ({ env }) => { // env.logLevel will be "info" if LOG_LEVEL is not set console.log(env.logLevel); }; ``` ### Hidden Environment Variables [#hidden-environment-variables] Hide environment variables from help output: ```typescript { name: "SECRET_KEY", type: String, hidden: true, // Won't appear in help description: "Secret key for encryption" } ``` ### CamelCase Conversion [#camelcase-conversion] Environment variable names are automatically converted to camelCase: * `API_KEY` → `env.apiKey` * `LOG_LEVEL` → `env.logLevel` * `ENABLE_SSL` → `env.enableSsl` ### Accessing Environment Variables [#accessing-environment-variables] Access environment variables through the `env` property in your command's `execute` function: ```typescript cli.addCommand({ name: "deploy", env: [ { name: "DEPLOY_ENV", type: String, defaultValue: "staging", description: "Deployment environment", }, { name: "FORCE_DEPLOY", type: Boolean, defaultValue: false, description: "Force deployment without confirmation", }, ], execute: ({ env, options }) => { const deployEnv = env.deployEnv; // "staging" if not set const forceDeploy = env.forceDeploy; // false if not set // Can combine with options const finalEnv = options.env || env.deployEnv; }, }); ``` ### Complete Example [#complete-example-1] ```typescript cli.addCommand({ name: "build", env: [ { name: "NODE_ENV", type: String, defaultValue: "development", description: "Node.js environment", }, { name: "BUILD_TIMEOUT", type: Number, defaultValue: 30000, description: "Build timeout in milliseconds", }, { name: "ENABLE_SOURCEMAPS", type: Boolean, defaultValue: true, description: "Generate source maps", }, ], options: [ { name: "output", type: String, description: "Output directory", }, ], execute: ({ env, options }) => { const nodeEnv = env.nodeEnv; // "development" or value from NODE_ENV const timeout = env.buildTimeout; // 30000 or value from BUILD_TIMEOUT const sourcemaps = env.enableSourcemaps; // true or value from ENABLE_SOURCEMAPS const output = options.output || "dist"; console.log(`Building for ${nodeEnv} with timeout ${timeout}ms`); if (sourcemaps) { console.log("Source maps enabled"); } }, }); ``` Usage examples: ```bash # Using defaults cli build # Override with environment variables NODE_ENV=production BUILD_TIMEOUT=60000 cli build # Combine with options ENABLE_SOURCEMAPS=false cli build --output build # Boolean values ENABLE_SOURCEMAPS=1 cli build # true ENABLE_SOURCEMAPS=0 cli build # false ``` ### Environment Variables in Help [#environment-variables-in-help] Environment variables are automatically displayed in the help output: ```bash cli help build ``` Output includes an "Environment Variables" section showing: * Variable name * Description * Default value (if set) Hidden environment variables are excluded from help output. ## Global Options [#global-options] Cerebro provides built-in global options available to all commands: * `--help, -h` - Show help * `--version, -V` - Show version * `--verbose, -v` - Verbose output * `--debug` - Debug output * `--quiet, -q` - Quiet output * `--no-color` - Disable colored output * `--color` - Force colored output These don't need to be defined in your commands. ### Custom Global Options [#custom-global-options] You can add your own global options using `addGlobalOption()`. These are available to every command and appear in the help output alongside the built-in ones: ```typescript const cli = new Cerebro("my-app"); cli.addGlobalOption({ name: "cwd", type: String, description: "Override working directory", }); cli.addGlobalOption({ name: "config", alias: "C", type: String, description: "Path to config file", }); ``` Custom global options work exactly like command options — they support `type`, `alias`, `description`, `defaultValue`, and all other `OptionDefinition` fields. They are parsed for every command and accessible via `toolbox.options`: ```typescript cli.addCommand({ name: "build", execute: ({ options }) => { const cwd = options.cwd as string | undefined; const config = options.config as string | undefined; // ... }, }); ``` > **Note:** You cannot override built-in global options. Attempting to add an option > with the same name or alias as a built-in will throw an error. ## Global Environment Variables [#global-environment-variables] Cerebro also provides built-in global environment variables: * `CEREBRO_OUTPUT_LEVEL` - Controls verbosity level (16=quiet, 32=normal, 64=verbose, 128=debug) * `CEREBRO_MIN_NODE_VERSION` - Overrides minimum Node.js version check * `NO_UPDATE_NOTIFIER` - Disables update notifier check * `NODE_ENV` - Standard Node.js environment variable * `DEBUG` - Enables debug output (same as --debug flag) These are available to all commands and appear in the general help output. # Plugins (/docs/packages/cerebro/guides/plugins) # Plugins [#plugins] Plugins extend Cerebro's functionality by hooking into the command lifecycle and extending the toolbox. ## What are Plugins? [#what-are-plugins] Plugins allow you to: * Add functionality to the toolbox (like file system operations, HTTP clients, etc.) * Hook into command lifecycle (before/after execution) * Handle errors globally * Share code across commands ## Basic Plugin [#basic-plugin] A plugin requires a `name` and can have lifecycle hooks: ```typescript cli.addPlugin({ name: "my-plugin", description: "My custom plugin", execute: (toolbox) => { // Extend toolbox toolbox.myFeature = () => { console.log("My feature!"); }; }, }); ``` ## Plugin Lifecycle [#plugin-lifecycle] Plugins have access to several lifecycle hooks: ### Init Hook [#init-hook] Called once during plugin initialization, before any commands run: ```typescript cli.addPlugin({ name: "database", init: async ({ cli, cwd, logger }) => { // Initialize database connection logger.info("Database plugin initialized"); }, }); ``` ### Execute Hook [#execute-hook] Called during command execution to extend the toolbox: ```typescript cli.addPlugin({ name: "filesystem", execute: (toolbox) => { // Add file system utilities to toolbox toolbox.fs = { readFile: async (path: string) => { // Implementation }, writeFile: async (path: string, content: string) => { // Implementation }, }; }, }); ``` Then use in commands: ```typescript cli.addCommand({ name: "read", execute: async ({ fs }) => { const content = await fs.readFile("file.txt"); console.log(content); }, }); ``` ### BeforeCommand Hook [#beforecommand-hook] Called before each command executes: ```typescript cli.addPlugin({ name: "analytics", beforeCommand: async (toolbox) => { console.log(`Executing command: ${toolbox.commandName}`); // Track command usage }, }); ``` ### AfterCommand Hook [#aftercommand-hook] Called after successful command execution: ```typescript cli.addPlugin({ name: "cleanup", afterCommand: async (toolbox, result) => { // Cleanup resources console.log("Command completed:", result); }, }); ``` ### OnError Hook [#onerror-hook] Called when an error occurs: ```typescript cli.addPlugin({ name: "error-handler", onError: async (error, toolbox) => { // Custom error handling console.error(`Error in ${toolbox.commandName}:`, error.message); // Send to error tracking service await sendToErrorTracking(error, toolbox); }, }); ``` ## Plugin Dependencies [#plugin-dependencies] Plugins can depend on other plugins: ```typescript cli.addPlugin({ name: "advanced-fs", dependencies: ["filesystem"], // Requires filesystem plugin execute: (toolbox) => { // Extend filesystem plugin toolbox.fs.copy = async (src, dest) => { // Implementation }; }, }); ``` Dependencies are loaded in the correct order automatically. ## Complete Plugin Example [#complete-plugin-example] ```typescript cli.addPlugin({ name: "http-client", version: "1.0.0", description: "HTTP client utilities", dependencies: ["logger"], init: async ({ logger }) => { logger.info("HTTP client plugin initialized"); }, execute: (toolbox) => { toolbox.http = { get: async (url: string) => { const response = await fetch(url); return response.json(); }, post: async (url: string, data: unknown) => { const response = await fetch(url, { method: "POST", body: JSON.stringify(data), }); return response.json(); }, }; }, beforeCommand: async (toolbox) => { toolbox.logger.debug(`Preparing HTTP client for ${toolbox.commandName}`); }, afterCommand: async (toolbox, result) => { toolbox.logger.debug(`Command ${toolbox.commandName} completed`); }, onError: async (error, toolbox) => { toolbox.logger.error(`HTTP error in ${toolbox.commandName}:`, error); }, }); ``` Usage in commands: ```typescript cli.addCommand({ name: "fetch", execute: async ({ http, logger }) => { const data = await http.get("https://api.example.com/data"); logger.info("Data fetched:", data); }, }); ``` ## Built-in Plugins [#built-in-plugins] Cerebro includes a built-in logger plugin that adds logging to the toolbox: ```typescript execute: ({ logger }) => { logger.info("Info message"); logger.error("Error message"); logger.warn("Warning message"); logger.debug("Debug message"); }; ``` ## TypeScript Support [#typescript-support] Extend the toolbox type for type safety: ```typescript declare global { namespace Cerebro { interface ExtensionOverrides { myFeature: () => void; fs: { readFile: (path: string) => Promise; }; } } } ``` Now TypeScript will provide autocomplete and type checking: ```typescript execute: ({ myFeature, fs }) => { myFeature(); // TypeScript knows this exists const content = await fs.readFile("file.txt"); // TypeScript knows the signature }; ``` ## Best Practices [#best-practices] 1. **Keep plugins focused** - Each plugin should have a single responsibility 2. **Use dependencies** - If your plugin extends another, declare it as a dependency 3. **Handle errors** - Use `onError` hook for error handling, not `execute` 4. **Initialize resources in `init`** - Set up connections, load configs, etc. 5. **Extend toolbox in `execute`** - Add utilities to toolbox here 6. **Document your plugin** - Always include description and version ## Plugin vs Command [#plugin-vs-command] * **Plugins** - Extend functionality, add to toolbox, lifecycle hooks * **Commands** - User-facing actions, invoked via CLI Use plugins for reusable functionality, commands for user actions. # Using Plugins (/docs/packages/cerebro/guides/using-plugins) # Using Plugins [#using-plugins] Learn how to use built-in and custom plugins to extend your CLI application. ## Installing Plugins [#installing-plugins] Built-in plugins are included with Cerebro: ```typescript import { errorHandlerPlugin } from "@visulima/cerebro/plugin/error-handler"; import { runtimeVersionCheckPlugin } from "@visulima/cerebro/plugin/runtime-version-check"; import { updateNotifierPlugin } from "@visulima/cerebro/plugin/update-notifier"; ``` ## Error Handler Plugin [#error-handler-plugin] Handle errors gracefully with formatted output: ```typescript import { createCerebro } from "@visulima/cerebro"; import { errorHandlerPlugin } from "@visulima/cerebro/plugin/error-handler"; const cli = createCerebro("my-cli"); cli.use( errorHandlerPlugin({ exitOnError: true, // Exit process on error (default: true) showStackTrace: false, // Show stack traces (default: false) logErrors: true, // Log errors to console (default: true) }), ); cli.addCommand({ name: "test", execute: async () => { throw new Error("Something went wrong!"); // Error will be caught and formatted by plugin }, }); await cli.run(); ``` Without plugin: ``` Error: Something went wrong! at execute (file:///cli.js:10:11) ...full stack trace... ``` With plugin: ``` ❌ Error: Something went wrong! ``` ## Runtime Version Check Plugin [#runtime-version-check-plugin] Ensure users have the correct Node.js/Deno/Bun version: ```typescript import { runtimeVersionCheckPlugin } from "@visulima/cerebro/plugin/runtime-version-check"; cli.use( runtimeVersionCheckPlugin({ requiredVersion: ">=18.0.0", message: "This CLI requires Node.js 18 or higher. Please upgrade.", }), ); ``` If user has Node.js 16: ``` ❌ This CLI requires Node.js 18 or higher. Please upgrade. Current version: 16.14.0 Required version: >=18.0.0 ``` ## Update Notifier Plugin [#update-notifier-plugin] Notify users when a new version is available: ```typescript import { updateNotifierPlugin } from "@visulima/cerebro/plugin/update-notifier"; cli.use( updateNotifierPlugin({ packageName: "my-cli", packageVersion: "1.0.0", checkInterval: 86400000, // Check once per day (24 hours) updateMessage: "Update available: {latest} (current: {current})\nRun: npm install -g my-cli@latest", }), ); await cli.run(); ``` Output when update available: ``` ╭───────────────────────────────────────╮ │ │ │ Update available: 2.0.0 (1.0.0) │ │ Run: npm install -g my-cli@latest │ │ │ ╰───────────────────────────────────────╯ ... command output ... ``` ## Combining Multiple Plugins [#combining-multiple-plugins] Use multiple plugins together: ```typescript import { createCerebro } from "@visulima/cerebro"; import { errorHandlerPlugin } from "@visulima/cerebro/plugin/error-handler"; import { runtimeVersionCheckPlugin } from "@visulima/cerebro/plugin/runtime-version-check"; import { updateNotifierPlugin } from "@visulima/cerebro/plugin/update-notifier"; const cli = createCerebro("my-cli", { packageName: "my-cli", packageVersion: "1.0.0", }); // Register plugins in order cli.use(runtimeVersionCheckPlugin({ requiredVersion: ">=18.0.0" })); cli.use(errorHandlerPlugin({ exitOnError: true })); cli.use( updateNotifierPlugin({ packageName: "my-cli", packageVersion: "1.0.0", }), ); // Add commands... await cli.run(); ``` ## Creating Custom Plugins [#creating-custom-plugins] Create your own plugins to extend functionality: ```typescript import type { Plugin } from "@visulima/cerebro"; // Type declaration declare global { namespace Cerebro { interface ExtensionOverrides { analytics: { track: (event: string, data?: Record) => void; }; } } } // Plugin implementation export const analyticsPlugin = (apiKey: string): Plugin => ({ name: "analytics", version: "1.0.0", register: async ({ toolbox, logger }) => { logger.log("Initializing analytics"); toolbox.analytics = { track: (event: string, data?: Record) => { // Send analytics event fetch("https://analytics.example.com/track", { method: "POST", headers: { "X-API-Key": apiKey, "Content-Type": "application/json", }, body: JSON.stringify({ event, data, timestamp: Date.now() }), }).catch((error) => { logger.error(`Analytics error: ${error.message}`); }); }, }; }, }); // Use plugin cli.use(analyticsPlugin("your-api-key")); // Track events in commands cli.addCommand({ name: "deploy", execute: async ({ analytics, logger }) => { analytics.track("deploy_started"); // Deploy logic... analytics.track("deploy_completed", { duration: 1234 }); logger.log("Deployed!"); }, }); ``` ## Plugin Loading Order [#plugin-loading-order] Plugins initialize in registration order: ```typescript cli.use(configPlugin()); // 1st: Load configuration cli.use(databasePlugin()); // 2nd: Connect to database (needs config) cli.use(analyticsPlugin()); // 3rd: Initialize analytics (needs config) ``` Dependencies between plugins: ```typescript const databasePlugin = (): Plugin => ({ name: "database", dependencies: ["config"], // Requires config plugin to run first register: async ({ toolbox }) => { const { config } = toolbox; toolbox.database = connectDatabase(config.databaseUrl); }, }); ``` ## Testing with Plugins [#testing-with-plugins] Test commands with plugins enabled: ```typescript import { createCerebro } from "@visulima/cerebro"; import { myPlugin } from "./plugins/my-plugin"; test("command works with plugin", async () => { const cli = createCerebro("test-cli"); cli.use(myPlugin()); cli.addCommand({ name: "test", execute: ({ myFeature }) => { myFeature.doSomething(); }, }); await cli.run(["test"]); }); ``` # Introduction (/docs/packages/cerebro) # @visulima/cerebro [#visulimacerebro] A lightweight, extensible CLI framework for building modern command-line applications with TypeScript support, plugins, nested commands, and cross-runtime compatibility (Node.js, Deno, Bun). ## Key Features [#key-features] **Framework & Architecture** * **Zero Dependencies Core** - Minimal footprint with optional peer dependencies * **TypeScript First** - Full type safety with comprehensive type definitions * **Cross-Runtime** - Works with Node.js, Deno, and Bun * **Plugin System** - Extensible architecture with built-in and custom plugins **Commands & Arguments** * **Nested Commands** - Support for subcommands and command hierarchies * **Argument Parsing** - Built-in parser with types (String, Boolean, Number) * **Option Validation** - Required options, conflicting options, implied options * **Auto-completion** - Shell completion for bash, zsh, and fish **Developer Experience** * **Auto Help Generation** - Automatic help text from command metadata * **Version Command** - Built-in version display * **README Generation** - Generate documentation from command definitions * **Error Handling** - Comprehensive error handling with helpful messages **Built-in Features** * **Verbosity Levels** - Quiet, normal, verbose, and debug output modes * **Environment Variables** - Type-safe environment variable handling * **Exception Handling** - Graceful error handling and exit codes * **Levenshtein Suggestions** - Command suggestions for typos ## Quick Start [#quick-start] ```typescript import { createCerebro } from "@visulima/cerebro"; const cli = createCerebro("my-cli", { packageName: "my-cli", packageVersion: "1.0.0", }); cli.addCommand({ name: "greet", description: "Greet someone", argument: { name: "name", description: "Name to greet", type: String, }, execute: ({ argument, logger }) => { logger.log(`Hello, ${argument[0]}!`); }, }); await cli.run(); ``` ## Use Cases [#use-cases] ### Build Production CLIs [#build-production-clis] Create professional command-line tools with nested commands, options, and help text: ```typescript import { createCerebro } from "@visulima/cerebro"; const cli = createCerebro("deploy-cli", { packageName: "@company/deploy-cli", packageVersion: "2.1.0", }); cli.addCommand({ name: "deploy", description: "Deploy application to environment", argument: { name: "environment", description: "Target environment (staging|production)", type: String, }, options: [ { name: "skip-tests", description: "Skip test suite", type: Boolean, }, { name: "timeout", description: "Deployment timeout in seconds", type: Number, defaultValue: 300, }, ], execute: async ({ argument, options, logger }) => { const env = argument[0]; logger.log(`Deploying to ${env}...`); // Deployment logic }, }); await cli.run(); ``` ### Plugin-Based Applications [#plugin-based-applications] Extend your CLI with plugins for error handling, version checks, and notifications: ```typescript import { createCerebro } from "@visulima/cerebro"; import { errorHandlerPlugin } from "@visulima/cerebro/plugin/error-handler"; import { updateNotifierPlugin } from "@visulima/cerebro/plugin/update-notifier"; const cli = createCerebro("my-tool", { packageName: "my-tool", packageVersion: "1.0.0", }); cli.use(errorHandlerPlugin()); cli.use(updateNotifierPlugin({ checkInterval: 86400000 })); // Add commands... await cli.run(); ``` ### Cross-Runtime Tools [#cross-runtime-tools] Build CLIs that work across Node.js, Deno, and Bun: ```typescript // Works in Node.js, Deno, and Bun import { createCerebro } from "@visulima/cerebro"; const cli = createCerebro("universal-cli"); cli.addCommand({ name: "info", description: "Show runtime information", execute: ({ logger, env }) => { logger.log(`Runtime: ${env.RUNTIME ?? "unknown"}`); logger.log(`Platform: ${process.platform}`); }, }); await cli.run(); ``` ## Next Steps [#next-steps] Set up Cerebro in your project Build your first CLI in 5 minutes Understand Cerebro's architecture Complete API documentation ## Browser and Server Support [#browser-and-server-support] Cerebro is designed for command-line environments and works with: * **Node.js** - Version 18.x and above * **Deno** - Latest stable version * **Bun** - Latest stable version * **Platforms** - Linux, macOS, Windows # Installation (/docs/packages/cerebro/installation) # Installation [#installation] Install Cerebro using your preferred package manager: ```bash npm2yarn npm install @visulima/cerebro ``` ## Peer Dependencies [#peer-dependencies] Cerebro has minimal dependencies by default. Install optional peer dependencies as needed: ### Required Dependencies [#required-dependencies] ```bash npm2yarn npm install @visulima/command-line-args @visulima/error ``` ### Optional Features [#optional-features] **Boxed Output (for formatted messages)**: ```bash npm2yarn npm install @visulima/boxen ``` **Advanced Logging (Pail integration)**: ```bash npm2yarn npm install @visulima/pail ``` **Shell Completion**: ```bash npm2yarn npm install @visulima/fs @visulima/path ``` **README Generation**: ```bash npm2yarn npm install @visulima/readgen ``` ## Import [#import] ### ESM (Recommended) [#esm-recommended] ```typescript import { createCerebro } from "@visulima/cerebro"; ``` ### CommonJS [#commonjs] ```typescript const { createCerebro } = require("@visulima/cerebro"); ``` ### Subpath Imports [#subpath-imports] Cerebro provides subpath imports for built-in commands and plugins: ```typescript // Built-in commands import { HelpCommand } from "@visulima/cerebro/command/help"; import { VersionCommand } from "@visulima/cerebro/command/version"; import { CompletionCommand } from "@visulima/cerebro/command/completion"; import { ReadmeCommand } from "@visulima/cerebro/command/readme"; // Plugins import { errorHandlerPlugin } from "@visulima/cerebro/plugin/error-handler"; import { runtimeVersionCheckPlugin } from "@visulima/cerebro/plugin/runtime-version-check"; import { updateNotifierPlugin } from "@visulima/cerebro/plugin/update-notifier"; ``` ## Requirements [#requirements] * **Node.js**: 18.x or higher * **TypeScript**: 5.0 or higher (for TypeScript projects) * **Package Manager**: npm, yarn, pnpm, or bun ## Verification [#verification] Verify your installation by creating a simple CLI: ```typescript import { createCerebro } from "@visulima/cerebro"; const cli = createCerebro("test-cli"); cli.addCommand({ name: "hello", description: "Say hello", execute: ({ logger }) => { logger.log("Hello from Cerebro!"); }, }); await cli.run(["hello"]); // Output: Hello from Cerebro! ``` # Introduction (/docs/packages/cerebro/introduction) Cerebro is a modern, TypeScript-first CLI framework that makes it easy to build powerful command-line applications. Built with performance and developer experience in mind, Cerebro provides everything you need to create awesome CLIs. Cerebro Output ## Why Cerebro? [#why-cerebro] ### Fast & Lightweight [#fast--lightweight] Cerebro is optimized for performance with minimal overhead. Your CLI starts quickly and runs efficiently, even with complex command structures. ### TypeScript First [#typescript-first] Full TypeScript support with excellent type inference. Get autocomplete, type safety, and better developer experience out of the box. ### Extensible [#extensible] Plugin system allows you to extend functionality without modifying core code. Add features like logging, prompts, file system operations, and more. ### Feature Rich [#feature-rich] Built-in support for: * Commands with arguments and options * Shell autocompletions (bash, zsh, fish, powershell) * Help generation * README documentation generation * Error handling * Plugin lifecycle hooks * Command composition ### Zero Configuration [#zero-configuration] Get started in minutes. Sensible defaults mean you can build a working CLI without any configuration. ## Quick Navigation [#quick-navigation] ## Key Features [#key-features] ### Commands [#commands] Define commands with descriptions, options, arguments, and aliases. Commands can be grouped, hidden, or configured with examples. ```typescript cli.addCommand({ name: "build", description: "Build your project", options: [ { name: "production", type: Boolean }, { name: "output", type: String }, ], execute: ({ options, logger }) => { logger.info(`Building ${options.production ? "production" : "development"}...`); }, }); ``` ### Options [#options] Powerful option handling with support for: * Type validation (String, Number, Boolean, Arrays) * Default values * Required options * Aliases * Negatable options (`--no-` prefix) * Conflicting options * Implied options ### Arguments [#arguments] Positional arguments with type checking and default values. ```typescript cli.addCommand({ name: "greet", argument: { name: "name", type: String, description: "Name to greet", }, execute: ({ argument }) => { console.log(`Hello, ${argument[0]}!`); }, }); ``` ### Plugins [#plugins] Extend functionality with plugins that hook into the command lifecycle: * `init` - Called once during plugin initialization * `execute` - Called during command execution (extends toolbox) * `beforeCommand` - Called before command execution * `afterCommand` - Called after successful command execution * `onError` - Called when errors occur ### Command Composition [#command-composition] Call commands from within other commands using `runtime.runCommand()`: ```typescript cli.addCommand({ name: "deploy", execute: async ({ runtime, logger }) => { logger.info("Building..."); await runtime.runCommand("build", { argv: ["--production"] }); logger.info("Testing..."); await runtime.runCommand("test"); logger.info("Deploying..."); }, }); ``` ## Installation [#installation] `bash pnpm add @visulima/cerebro ` `bash npm install @visulima/cerebro ` `bash yarn add @visulima/cerebro ` ## Next Steps [#next-steps] Ready to build your CLI? Head to the [Quick Start](/docs/packages/cerebro/quickstart) guide to create your first command in minutes! # Quick Start (/docs/packages/cerebro/quick-start) # Quick Start [#quick-start] Build your first CLI application with Cerebro in under 5 minutes. ## Basic CLI [#basic-cli] Create a simple CLI with a single command: ```typescript import { createCerebro } from "@visulima/cerebro"; // Create CLI instance const cli = createCerebro("greet-cli", { packageName: "greet-cli", packageVersion: "1.0.0", }); // Add a command cli.addCommand({ name: "hello", description: "Greet someone", execute: ({ logger }) => { logger.log("Hello, World!"); }, }); // Run the CLI await cli.run(); ``` Run it: ```bash $ node cli.js hello Hello, World! ``` ## Commands with Arguments [#commands-with-arguments] Add arguments to your commands: ```typescript cli.addCommand({ name: "greet", description: "Greet someone by name", argument: { name: "name", description: "Person to greet", type: String, }, execute: ({ argument, logger }) => { const name = argument[0]; logger.log(`Hello, ${name}!`); }, }); ``` ```bash $ node cli.js greet Alice Hello, Alice! ``` ## Commands with Options [#commands-with-options] Add options for more flexibility: ```typescript cli.addCommand({ name: "greet", description: "Greet someone", argument: { name: "name", description: "Person to greet", type: String, }, options: [ { name: "loud", alias: "l", description: "Make it loud", type: Boolean, }, { name: "times", alias: "t", description: "Number of times to greet", type: Number, defaultValue: 1, }, ], execute: ({ argument, options, logger }) => { const name = argument[0]; const message = `Hello, ${name}!`; const output = options.loud ? message.toUpperCase() : message; for (let i = 0; i < options.times; i++) { logger.log(output); } }, }); ``` ```bash $ node cli.js greet Alice --loud --times 3 HELLO, ALICE! HELLO, ALICE! HELLO, ALICE! ``` ## Automatic Help [#automatic-help] Cerebro automatically generates help text: ```bash $ node cli.js --help greet-cli v1.0.0 Usage: greet-cli [options] Commands: greet Greet someone Options: --help Show help --version Show version ``` ## Environment Variables [#environment-variables] Access environment variables in your commands: ```typescript cli.addCommand({ name: "env", description: "Show environment info", env: { API_KEY: { description: "API key for authentication", required: true, }, }, execute: ({ env, logger }) => { logger.log(`API Key: ${env.API_KEY}`); }, }); ``` ```bash $ API_KEY=secret123 node cli.js env API Key: secret123 ``` ## Nested Commands [#nested-commands] Create command hierarchies: ```typescript cli.addCommand({ name: "db", description: "Database commands", commands: [ { name: "migrate", description: "Run migrations", execute: ({ logger }) => { logger.log("Running migrations..."); }, }, { name: "seed", description: "Seed database", execute: ({ logger }) => { logger.log("Seeding database..."); }, }, ], }); ``` ```bash $ node cli.js db migrate Running migrations... $ node cli.js db seed Seeding database... ``` ## Next Steps [#next-steps] Learn how Cerebro works under the hood Deep dive into command creation Extend your CLI with plugins # Quick Start (/docs/packages/cerebro/quickstart) Get started with Cerebro and build your first CLI in under 2 minutes. ### Install Cerebro [#install-cerebro] Install the package using your preferred package manager: `bash pnpm add @visulima/cerebro ` `bash npm install @visulima/cerebro ` `bash yarn add @visulima/cerebro ` ### Create Your CLI File [#create-your-cli-file] Create a new file `cli.js` (or `cli.ts` for TypeScript): ```typescript title="cli.js" import { Cerebro } from "@visulima/cerebro"; const cli = new Cerebro("my-app", { packageName: "my-app", packageVersion: "1.0.0", }); cli.addCommand({ name: "hello", description: "Say hello to someone", argument: { name: "name", type: String, description: "Name to greet", defaultValue: "World", }, options: [ { name: "greeting", alias: "g", type: String, description: "Custom greeting", defaultValue: "Hello", }, ], execute: ({ argument, options, logger }) => { const name = argument?.[0] || "World"; logger.info(`${options.greeting}, ${name}!`); }, }); await cli.run(); ``` ### Make It Executable [#make-it-executable] Add the shebang and make it executable: ```bash chmod +x cli.js ``` Or if using TypeScript with tsx: ```bash chmod +x cli.ts ``` ### Run Your CLI [#run-your-cli] Now you can run your CLI: ```bash # Default greeting node cli.js hello # Output: Hello, World! # With a name node cli.js hello Alice # Output: Hello, Alice! # With custom greeting node cli.js hello Alice --greeting "Hi" # Output: Hi, Alice! # Using alias node cli.js hello Alice -g "Hey" # Output: Hey, Alice! ``` ### TypeScript Support [#typescript-support] For TypeScript, use the same code but with `.ts` extension: ```typescript title="cli.ts" import { Cerebro } from "@visulima/cerebro"; // ... same code ... ``` Run with `tsx` or compile first: ```bash # Using tsx tsx cli.ts hello Alice # Or compile and run tsc cli.ts && node cli.js hello Alice ``` **Congratulations!** You've created your first CLI with Cerebro. Now explore the [Commands Guide](/docs/packages/cerebro/guides/commands) to learn more about advanced features. ## What's Next? [#whats-next] * [Commands Guide](/docs/packages/cerebro/guides/commands) - Learn about command configuration * [Options & Arguments](/docs/packages/cerebro/guides/options) - Handle CLI options and arguments * [Plugins](/docs/packages/cerebro/guides/plugins) - Extend functionality with plugins * [API Reference](/docs/packages/cerebro/api/cli) - Complete API documentation # Logger (/docs/packages/cerebro/toolbox/logger) # Toolbox: Logger [#toolbox-logger] The `logger` provides methods for outputting messages at different log levels. ## Basic Usage [#basic-usage] ```typescript cli.addCommand({ name: "example", execute: ({ logger }) => { logger.info("Information message"); logger.error("Error message"); logger.warn("Warning message"); logger.debug("Debug message"); }, }); ``` ## Log Levels [#log-levels] ### Info [#info] General information messages: ```typescript logger.info("Building project..."); logger.info("Files processed:", fileCount); ``` ### Error [#error] Error messages: ```typescript logger.error("Failed to build"); logger.error("Error details:", error); ``` ### Warn [#warn] Warning messages: ```typescript logger.warn("Deprecated option used"); logger.warn("This feature will be removed in v2.0"); ``` ### Debug [#debug] Debug messages (only shown with `--debug` flag): ```typescript logger.debug("Processing file:", fileName); logger.debug("Options:", options); ``` ## Logging Objects [#logging-objects] Logger methods accept multiple arguments: ```typescript logger.info("User:", user.name, "Age:", user.age); logger.error("Error:", error.message, error.stack); ``` ## Verbosity Control [#verbosity-control] Log levels are controlled by verbosity flags: ```bash cli command # Shows info, warn, error cli command --verbose # Shows debug too cli command --debug # Shows all debug messages cli command --quiet # Minimal output ``` ## Custom Logger [#custom-logger] Provide a custom logger when creating the CLI: ```typescript const customLogger = { info: (...args) => console.log("[INFO]", ...args), error: (...args) => console.error("[ERROR]", ...args), warn: (...args) => console.warn("[WARN]", ...args), debug: (...args) => { if (process.env.DEBUG) { console.debug("[DEBUG]", ...args); } }, }; const cli = new Cerebro("my-app", { logger: customLogger, }); ``` ## Conditional Logging [#conditional-logging] Use log levels appropriately: ```typescript execute: ({ logger, options }) => { if (options.verbose) { logger.debug("Detailed processing information"); } logger.info("Starting operation..."); try { // Operation } catch (error) { logger.error("Operation failed:", error); } }; ``` ## Best Practices [#best-practices] 1. **Use appropriate levels** - info for normal output, error for errors 2. **Don't overuse debug** - Reserve for troubleshooting 3. **Include context** - Log relevant information 4. **Format consistently** - Use consistent message formats 5. **Respect verbosity** - Don't force output in quiet mode # Parameters (/docs/packages/cerebro/toolbox/parameters) # Toolbox: Parameters [#toolbox-parameters] The toolbox provides access to command parameters including arguments, options, and the original argv. ## Arguments [#arguments] Positional arguments passed to the command: ```typescript cli.addCommand({ name: "copy", argument: { name: "files", type: String, }, execute: ({ argument }) => { // argument is an array of strings argument.forEach((file) => { console.log(`Copying ${file}...`); }); }, }); ``` Usage: `cli copy file1.txt file2.txt` * `argument[0]` - First argument * `argument[1]` - Second argument * `argument` - Array of all arguments ## Options [#options] Parsed command-line options: ```typescript cli.addCommand({ name: "build", options: [ { name: "production", type: Boolean }, { name: "output", type: String }, ], execute: ({ options }) => { const isProduction = options.production; // boolean const outputDir = options.output; // string | undefined }, }); ``` Options are: * Type-safe based on option definitions * Camel-cased automatically (`--output-dir` → `options.outputDir`) * Include default values if set * Validated before execution ## Environment Variables [#environment-variables] Type-safe access to environment variables defined in your command: ```typescript cli.addCommand({ name: "build", env: [ { name: "API_KEY", type: String, description: "API key", }, { name: "TIMEOUT", type: Number, defaultValue: 5000, description: "Timeout in ms", }, { name: "VERBOSE", type: Boolean, defaultValue: false, description: "Enable verbose output", }, ], execute: ({ env }) => { const apiKey = env.apiKey; // string | undefined const timeout = env.timeout; // number (5000 if not set) const verbose = env.verbose; // boolean (false if not set) }, }); ``` Environment variables are: * Type-safe based on their type definitions * Automatically converted to camelCase (`API_KEY` → `apiKey`) * Include default values if not set * Transformed according to their type (String, Number, Boolean) ### Boolean Environment Variables [#boolean-environment-variables] Boolean environment variables accept multiple truthy values (case-insensitive): * `true`, `1`, `yes`, `on` → `true` * Any other value → `false` ```bash VERBOSE=true cli build # env.verbose = true VERBOSE=1 cli build # env.verbose = true VERBOSE=yes cli build # env.verbose = true VERBOSE=false cli build # env.verbose = false ``` ## Argv [#argv] Original command-line arguments: ```typescript execute: ({ argv }) => { // Raw argv as parsed by command-line-args console.log(argv); }; ``` Use `argv` when you need: * Raw argument values * Unknown options handling * Low-level argument access ## Accessing Parameters [#accessing-parameters] ```typescript cli.addCommand({ name: "deploy", argument: { name: "target", type: String, }, options: [ { name: "env", type: String, required: true }, { name: "force", type: Boolean }, ], env: [ { name: "API_KEY", type: String, description: "API key for deployment", }, { name: "TIMEOUT", type: Number, defaultValue: 30000, description: "Deployment timeout", }, ], execute: ({ argument, options, env, argv }) => { const target = argument[0]; // Positional argument const envName = options.env; // Option value const force = options.force || false; // Option with default const apiKey = env.apiKey; // Environment variable const timeout = env.timeout; // Environment variable with default // Access raw argv if needed console.log("Raw argv:", argv); }, }); ``` ## Type Safety [#type-safety] With TypeScript, you get full type safety: ```typescript interface DeployOptions { env: string; force: boolean; dryRun?: boolean; } cli.addCommand({ name: "deploy", options: [ { name: "env", type: String, required: true }, { name: "force", type: Boolean }, { name: "dry-run", type: Boolean }, ], execute: ({ options }: { options: DeployOptions }) => { // TypeScript knows the structure const env: string = options.env; // ✅ const force: boolean = options.force; // ✅ }, }); ``` # Print (/docs/packages/cerebro/toolbox/print) # Toolbox: Print [#toolbox-print] The `logger` provides print methods for formatted output (if using a logger that supports it, like Pail). ## Basic Printing [#basic-printing] ```typescript execute: ({ logger }) => { logger.info("Info message"); logger.error("Error message"); logger.warn("Warning message"); logger.debug("Debug message"); }; ``` ## Custom Print Methods [#custom-print-methods] Some loggers (like Pail) provide additional print methods: ```typescript execute: ({ logger }) => { // If using Pail logger if (logger.success) { logger.success("Operation successful!"); } if (logger.table) { logger.table([ { name: "Alice", age: 30 }, { name: "Bob", age: 25 }, ]); } }; ``` ## Raw Output [#raw-output] Some loggers support raw output: ```typescript execute: ({ logger }) => { if (logger.raw) { logger.raw("Raw output without formatting"); } }; ``` ## Using Pail Logger [#using-pail-logger] For enhanced printing capabilities, use the Pail logger: ```typescript import { pail } from "@visulima/pail"; const cli = new Cerebro("my-app", { logger: pail({ // Pail configuration }), }); ``` Pail provides: * Colored output * Tables * Success/error formatting * Progress bars * And more See [Pail documentation](https://github.com/visulima/visulima/tree/main/packages/pail) for details. # Runtime (/docs/packages/cerebro/toolbox/runtime) # Toolbox: Runtime [#toolbox-runtime] The `runtime` property provides access to the CLI instance, allowing you to call other commands programmatically. ## Accessing Runtime [#accessing-runtime] ```typescript execute: ({ runtime }) => { // runtime is the CLI instance const cliName = runtime.getCliName(); const commands = runtime.getCommands(); }; ``` ## Calling Other Commands [#calling-other-commands] Use `runtime.runCommand()` to execute other commands: ```typescript cli.addCommand({ name: "deploy", execute: async ({ runtime, logger }) => { logger.info("Building..."); await runtime.runCommand("build", { argv: ["--production"], }); logger.info("Deploying..."); }, }); ``` ### Passing Arguments [#passing-arguments] ```typescript await runtime.runCommand("copy", { argv: ["file1.txt", "file2.txt"], }); ``` ### Passing Options [#passing-options] ```typescript await runtime.runCommand("build", { argv: ["--production", "--output", "dist"], }); ``` ### Merging Options [#merging-options] ```typescript await runtime.runCommand("build", { argv: ["--production"], customOption: "value", }); ``` ### Getting Return Values [#getting-return-values] ```typescript const result = await runtime.runCommand("calculate"); console.log(result); // Command return value ``` ## Runtime Methods [#runtime-methods] ### getCliName() [#getcliname] Get the CLI application name: ```typescript const name = runtime.getCliName(); // "my-app" ``` ### getCommands() [#getcommands] Get all registered commands: ```typescript const commands = runtime.getCommands(); // Map commands.forEach((command, name) => { console.log(`Command: ${name}`); }); ``` ### getCwd() [#getcwd] Get the current working directory: ```typescript const cwd = runtime.getCwd(); // "/path/to/project" ``` ### getPackageName() [#getpackagename] Get the package name if configured: ```typescript const packageName = runtime.getPackageName(); // "my-app" | undefined ``` ### getPackageVersion() [#getpackageversion] Get the package version if configured: ```typescript const version = runtime.getPackageVersion(); // "1.0.0" | undefined ``` ### getPluginManager() [#getpluginmanager] Get the plugin manager instance: ```typescript const pluginManager = runtime.getPluginManager(); // Advanced plugin management ``` ## Command Composition Examples [#command-composition-examples] ### Build Pipeline [#build-pipeline] ```typescript cli.addCommand({ name: "ci", execute: async ({ runtime, logger }) => { logger.info("Running CI pipeline..."); await runtime.runCommand("lint"); await runtime.runCommand("test", { argv: ["--coverage"] }); await runtime.runCommand("build", { argv: ["--production"] }); logger.info("CI pipeline complete!"); }, }); ``` ### Conditional Execution [#conditional-execution] ```typescript cli.addCommand({ name: "release", options: [{ name: "skip-tests", type: Boolean }], execute: async ({ runtime, options, logger }) => { if (!options.skipTests) { logger.info("Running tests..."); await runtime.runCommand("test"); } logger.info("Building release..."); await runtime.runCommand("build", { argv: ["--production"] }); }, }); ``` ### Nested Command Calls [#nested-command-calls] ```typescript cli.addCommand({ name: "deploy", execute: async ({ runtime }) => { await runtime.runCommand("build"); }, }); cli.addCommand({ name: "build", execute: async ({ runtime }) => { await runtime.runCommand("compile"); await runtime.runCommand("bundle"); }, }); ``` ## Error Handling [#error-handling] Errors from called commands propagate: ```typescript cli.addCommand({ name: "deploy", execute: async ({ runtime, logger }) => { try { await runtime.runCommand("build"); } catch (error) { logger.error("Build failed:", error); throw error; // Re-throw to stop deployment } await runtime.runCommand("deploy-step"); }, }); ``` ## Best Practices [#best-practices] 1. **Use for composition** - Break complex operations into smaller commands 2. **Handle errors** - Wrap `runCommand` calls in try-catch when needed 3. **Pass options explicitly** - Use `argv` array for clarity 4. **Avoid circular calls** - Don't create command call loops 5. **Document dependencies** - Make it clear which commands depend on others # Troubleshooting (/docs/packages/cerebro/troubleshooting) # Troubleshooting [#troubleshooting] Common issues and solutions when working with Cerebro. ## Command Not Found [#command-not-found] **Problem**: CLI reports "Command not found" for existing command ```bash $ my-cli build Error: Command "build" not found ``` **Solutions**: 1. Verify command is registered: ```typescript cli.addCommand({ name: "build", execute: ({ logger }) => { logger.log("Building..."); }, }); ``` 2. Check for typos in command name 3. Check nested command syntax: ```bash $ my-cli db migrate # Correct $ my-cli db-migrate # Wrong ``` ## Option Validation Errors [#option-validation-errors] **Problem**: Required option errors ```bash $ my-cli deploy Error: Required option --environment is missing ``` **Solution**: Provide the required option: ```bash $ my-cli deploy --environment production ``` Or make it optional with a default value: ```typescript { name: "environment", type: String, defaultValue: "staging", // No longer required } ``` ## Conflicting Options Error [#conflicting-options-error] **Problem**: Using incompatible options together ```bash $ my-cli test --watch --ci Error: Options --watch and --ci conflict ``` **Solution**: Use only one of the conflicting options: ```bash $ my-cli test --watch # or $ my-cli test --ci ``` ## Type Errors [#type-errors] **Problem**: TypeScript errors for toolbox extensions ```typescript cli.addCommand({ execute: ({ myFeature }) => { // Error: Property 'myFeature' does not exist }, }); ``` **Solution**: Declare the extension type: ```typescript declare global { namespace Cerebro { interface ExtensionOverrides { myFeature: { doSomething: () => void; }; } } } ``` ## Plugin Initialization Issues [#plugin-initialization-issues] **Problem**: Plugin not working in commands ```typescript cli.use(myPlugin()); cli.addCommand({ execute: ({ myFeature }) => { // myFeature is undefined }, }); ``` **Solutions**: 1. Check plugin registration order (before commands) 2. Verify plugin adds to toolbox: ```typescript const myPlugin = (): Plugin => ({ name: "my-plugin", register: async ({ toolbox }) => { toolbox.myFeature = { doSomething: () => {} }; }, }); ``` 3. Check for plugin dependencies ## Environment Variable Issues [#environment-variable-issues] **Problem**: Required environment variable not found ```bash $ my-cli deploy Error: Required environment variable API_KEY is missing ``` **Solution**: Set the environment variable: ```bash $ API_KEY=secret123 my-cli deploy ``` Or use a `.env` file: ```bash # .env API_KEY=secret123 ``` ```typescript import "dotenv/config"; import { createCerebro } from "@visulima/cerebro"; // ... ``` ## Help Text Not Showing [#help-text-not-showing] **Problem**: `--help` doesn't display help text **Solutions**: 1. Verify help command is registered (automatic by default) 2. Add command descriptions: ```typescript cli.addCommand({ name: "build", description: "Build the project", // Required for help execute: ({ logger }) => {}, }); ``` 3. Manually register help command: ```typescript import { HelpCommand } from "@visulima/cerebro/command/help"; cli.addCommand(HelpCommand); ``` ## Exit Code Issues [#exit-code-issues] **Problem**: CLI exits with code 0 even on errors **Solution**: Use error handler plugin: ```typescript import { errorHandlerPlugin } from "@visulima/cerebro/plugin/error-handler"; cli.use(errorHandlerPlugin({ exitOnError: true })); ``` Or manually set exit code: ```typescript cli.addCommand({ name: "test", execute: async ({ logger }) => { try { await runTests(); } catch (error) { logger.error(error.message); process.exit(1); // Exit with error code } }, }); ``` ## Performance Issues [#performance-issues] **Problem**: CLI is slow to start **Solutions**: 1. Lazy-load heavy dependencies: ```typescript cli.addCommand({ name: "heavy", execute: async () => { // Load only when command runs const { heavyFunction } = await import("./heavy-module"); await heavyFunction(); }, }); ``` 2. Minimize plugin initialization work 3. Use dynamic imports for optional features ## Cross-Runtime Issues [#cross-runtime-issues] **Problem**: CLI works in Node.js but not in Deno/Bun **Solutions**: 1. Avoid Node.js-specific APIs 2. Use Cerebro's runtime abstractions: ```typescript import { getEnv, getCwd } from "@visulima/cerebro"; cli.addCommand({ execute: ({ logger }) => { const env = getEnv(); // Works in all runtimes const cwd = getCwd(); // Works in all runtimes logger.log(`CWD: ${cwd}`); }, }); ``` 3. Test in all target runtimes ## Debugging [#debugging] Enable debug output: ```bash CEREBRO_OUTPUT_LEVEL=debug my-cli build ``` Add debug logging: ```typescript cli.addCommand({ name: "build", execute: ({ logger }) => { if (process.env.CEREBRO_OUTPUT_LEVEL === "debug") { logger.log("Debug: Starting build..."); } // Build logic }, }); ``` # API Reference (/docs/packages/colorize/api) # API Reference [#api-reference] Complete API documentation for **@visulima/colorize**. ## Imports [#imports] ### Main Package [#main-package] ```typescript // Default import import colorize from "@visulima/colorize"; // Named imports import { red, green, blue, bold, hex, rgb } from "@visulima/colorize"; ``` ### Browser [#browser] ```typescript import colorize from "@visulima/colorize/browser"; import { red, green, blue } from "@visulima/colorize/browser"; ``` ### Template [#template] ```typescript import template from "@visulima/colorize/template"; ``` ### Gradient [#gradient] ```typescript import { gradient, multilineGradient } from "@visulima/colorize/gradient"; ``` ### Utils [#utils] ```typescript import { strip } from "@visulima/colorize/utils"; ``` ## Colors [#colors] ### Base Colors (Foreground) [#base-colors-foreground] Standard ANSI 16 colors for text: | Function | Description | Example | | --------------- | ------------------- | ------------------ | | `black` | Black text | `black\`text\`\` | | `red` | Red text | `red\`text\`\` | | `green` | Green text | `green\`text\`\` | | `yellow` | Yellow text | `yellow\`text\`\` | | `blue` | Blue text | `blue\`text\`\` | | `magenta` | Magenta text | `magenta\`text\`\` | | `cyan` | Cyan text | `cyan\`text\`\` | | `white` | White text | `white\`text\`\` | | `gray` / `grey` | Gray text (aliases) | `gray\`text\`\` | ### Bright Colors (Foreground) [#bright-colors-foreground] Brighter variants of base colors: | Function | Description | Example | | --------------- | ------------------- | ------------------------ | | `blackBright` | Bright black text | `blackBright\`text\`\` | | `redBright` | Bright red text | `redBright\`text\`\` | | `greenBright` | Bright green text | `greenBright\`text\`\` | | `yellowBright` | Bright yellow text | `yellowBright\`text\`\` | | `blueBright` | Bright blue text | `blueBright\`text\`\` | | `magentaBright` | Bright magenta text | `magentaBright\`text\`\` | | `cyanBright` | Bright cyan text | `cyanBright\`text\`\` | | `whiteBright` | Bright white text | `whiteBright\`text\`\` | ### Background Colors [#background-colors] Background colors using `bg` prefix: | Function | Description | Example | | ------------------- | ------------------ | -------------------- | | `bgBlack` | Black background | `bgBlack\`text\`\` | | `bgRed` | Red background | `bgRed\`text\`\` | | `bgGreen` | Green background | `bgGreen\`text\`\` | | `bgYellow` | Yellow background | `bgYellow\`text\`\` | | `bgBlue` | Blue background | `bgBlue\`text\`\` | | `bgMagenta` | Magenta background | `bgMagenta\`text\`\` | | `bgCyan` | Cyan background | `bgCyan\`text\`\` | | `bgWhite` | White background | `bgWhite\`text\`\` | | `bgGray` / `bgGrey` | Gray background | `bgGray\`text\`\` | ### Bright Background Colors [#bright-background-colors] | Function | Description | Example | | ----------------- | ------------------------- | -------------------------- | | `bgBlackBright` | Bright black background | `bgBlackBright\`text\`\` | | `bgRedBright` | Bright red background | `bgRedBright\`text\`\` | | `bgGreenBright` | Bright green background | `bgGreenBright\`text\`\` | | `bgYellowBright` | Bright yellow background | `bgYellowBright\`text\`\` | | `bgBlueBright` | Bright blue background | `bgBlueBright\`text\`\` | | `bgMagentaBright` | Bright magenta background | `bgMagentaBright\`text\`\` | | `bgCyanBright` | Bright cyan background | `bgCyanBright\`text\`\` | | `bgWhiteBright` | Bright white background | `bgWhiteBright\`text\`\` | ## Styles [#styles] ### Text Modifiers [#text-modifiers] | Function | Description | Example | | --------------- | ------------------------------------ | ------------------------ | | `bold` | Bold/bright text | `bold\`text\`\` | | `dim` | Dimmed/faint text | `dim\`text\`\` | | `italic` | Italic text | `italic\`text\`\` | | `underline` | Underlined text | `underline\`text\`\` | | `strikethrough` | Strikethrough text (alias: `strike`) | `strikethrough\`text\`\` | | `inverse` | Inverted colors | `inverse\`text\`\` | | `hidden` | Hidden text (invisible) | `hidden\`text\`\` | | `visible` | Always visible (no-op for always-on) | `visible\`text\`\` | | `reset` | Reset all styles | `reset\`text\`\` | ## Extended Colors [#extended-colors] ### ANSI 256 Colors [#ansi-256-colors] Access the 256-color palette (0-255): #### `ansi256(code: number)` [#ansi256code-number] Apply a 256-color foreground. Alias: `fg()` **Parameters:** * `code` (number): Color code from 0 to 255 **Returns:** Chainable style function **Example:** ```typescript import { ansi256, fg } from "@visulima/colorize"; console.log(ansi256(196)`Bright red`); // Color code 196 console.log(fg(21)`Deep blue`); // Alias usage console.log(ansi256(226).bold`Bold yellow`); // Chainable ``` #### `bgAnsi256(code: number)` [#bgansi256code-number] Apply a 256-color background. Alias: `bg()` **Parameters:** * `code` (number): Color code from 0 to 255 **Returns:** Chainable style function **Example:** ```typescript import { bgAnsi256, bg, white } from "@visulima/colorize"; console.log(white.bgAnsi256(196)`White on red`); console.log(white.bg(21)`White on blue`); // Alias usage ``` ### TrueColor (16M Colors) [#truecolor-16m-colors] #### `hex(color: string)` [#hexcolor-string] Apply a hex color to text foreground. **Parameters:** * `color` (string): Hex color code (e.g., `"#FF0000"`, `"#F00"`) **Returns:** Chainable style function **Example:** ```typescript import { hex } from "@visulima/colorize"; console.log(hex("#FF6B6B")`Coral red`); console.log(hex("#F00")`Red`); // Short notation console.log(hex("#4ECDC4").bold`Bold turquoise`); ``` #### `bgHex(color: string)` [#bghexcolor-string] Apply a hex color to text background. **Parameters:** * `color` (string): Hex color code **Returns:** Chainable style function **Example:** ```typescript import { bgHex, white } from "@visulima/colorize"; console.log(white.bgHex("#FF6B6B")`White on coral`); console.log(bgHex("#4ECDC4")`Turquoise background`); ``` #### `rgb(red: number, green: number, blue: number)` [#rgbred-number-green-number-blue-number] Apply an RGB color to text foreground. **Parameters:** * `red` (number): Red value (0-255) * `green` (number): Green value (0-255) * `blue` (number): Blue value (0-255) **Returns:** Chainable style function **Example:** ```typescript import { rgb } from "@visulima/colorize"; console.log(rgb(255, 107, 107)`Coral red`); console.log(rgb(78, 205, 196).bold`Bold turquoise`); ``` #### `bgRgb(red: number, green: number, blue: number)` [#bgrgbred-number-green-number-blue-number] Apply an RGB color to text background. **Parameters:** * `red` (number): Red value (0-255) * `green` (number): Green value (0-255) * `blue` (number): Blue value (0-255) **Returns:** Chainable style function **Example:** ```typescript import { bgRgb, white } from "@visulima/colorize"; console.log(white.bgRgb(255, 107, 107)`White on coral`); ``` ## Utility Functions [#utility-functions] ### `strip(text: string): string` [#striptext-string-string] Remove all ANSI escape codes from a string. **Parameters:** * `text` (string): String containing ANSI codes **Returns:** Plain string without ANSI codes **Example:** ```typescript import colorize from "@visulima/colorize"; // or import { strip } from "@visulima/colorize/utils"; const colored = colorize.red.bold`Error`; const plain = colorize.strip(colored); console.log(colored); // Colored output console.log(plain); // "Error" (plain text) ``` ## Template Functions [#template-functions] ### `template(strings, ...values): string` [#templatestrings-values-string] Tagged template literal for complex string interpolation with colors. **Usage:** ```typescript import template from "@visulima/colorize/template"; const cpu = 65; console.log(template`CPU: {red ${cpu}%}`); // Chained styles console.log(template`Status: {green.bold Success}`); // RGB/Hex colors console.log(template`{rgb(255,0,0) Red text}`); console.log(template`{#FF0000 Red text}`); // Background with shorthand console.log(template`{#FF0000:00FF00 Red on green}`); ``` ## Gradient Functions [#gradient-functions] ### `gradient(...colors: string[])` [#gradientcolors-string] Create a color gradient for text. **Parameters:** * `...colors` (string\[]): Two or more color names, hex codes, or RGB strings **Returns:** Function that applies gradient to text **Example:** ```typescript import { gradient } from "@visulima/colorize/gradient"; console.log(gradient("red", "blue")("Hello World")); console.log(gradient("red", "yellow", "green")("Rainbow")); console.log(gradient("#FF6B6B", "#4ECDC4")("Custom colors")); console.log(gradient("rgb(255,0,0)", "rgb(0,0,255)")("RGB gradient")); ``` ### `multilineGradient(colors: string[])` [#multilinegradientcolors-string] Create a gradient that maintains vertical alignment across multiple lines. **Parameters:** * `colors` (string\[]): Array of color names, hex codes, or RGB strings **Returns:** Function that applies gradient to multi-line text **Example:** ```typescript import { multilineGradient } from "@visulima/colorize/gradient"; const asciiArt = ` Line 1 Line 2 Line 3 `; console.log(multilineGradient(["cyan", "magenta"])(asciiArt)); ``` ## Properties [#properties] Each style function has the following properties: ### `open: string` [#open-string] The ANSI opening code for the style. **Example:** ```typescript import { red } from "@visulima/colorize"; console.log(red.open); // "\x1b[31m" ``` ### `close: string` [#close-string] The ANSI closing code for the style. **Example:** ```typescript import { red } from "@visulima/colorize"; console.log(red.close); // "\x1b[39m" ``` ### Usage with Properties [#usage-with-properties] ```typescript import { green, red, bold } from "@visulima/colorize"; console.log(`Hello ${green.open}World${green.close}!`); const customStyle = bold.red.bgYellow; console.log(`${customStyle.open}Styled text${customStyle.close}`); ``` ## TypeScript Types [#typescript-types] ### `ColorizeType` [#colorizetype] The type for the default import: ```typescript import type { ColorizeType } from "@visulima/colorize"; const colorize: ColorizeType = (await import("@visulima/colorize")).default; ``` ### Style Function Type [#style-function-type] ```typescript type StyleFunction = { (strings: TemplateStringsArray, ...values: any[]): string; (text: string): string; open: string; close: string; } & Record; ``` ## Chaining [#chaining] All colors, styles, and functions are chainable in any order: ```typescript import { red, bold, underline, hex, bgGreen } from "@visulima/colorize"; // Any order works console.log(red.bold.underline`Text`); console.log(bold.red.underline`Text`); console.log(underline.bold.red`Text`); // Mix everything console.log(hex("#FF6B6B").bold.bgGreen.italic`Complex styling`); ``` ## Browser Differences [#browser-differences] Browser exports return arrays instead of strings: ```typescript import { red } from "@visulima/colorize/browser"; const result = red("Error"); // Returns: ['%cError', 'color: red'] // Use spread operator console.log(...result); ``` All methods and properties are the same, only the return format differs. # Introduction (/docs/packages/colorize) # @visulima/colorize [#visulimacolorize] Terminal and console string styling done right. A high-performance, feature-rich library for colorizing terminal and browser console output with ANSI escape codes. ## Why Colorize? [#why-colorize] **@visulima/colorize** is a modern, performant alternative to Chalk and other color libraries, offering the best of both worlds: **Chalk-compatible API** with **superior performance** (up to 3x faster). ### Key Features [#key-features] **Compatibility & Performance** * **Chalk-compatible API** - Drop-in replacement with the same syntax * **Up to 3x faster** than Chalk in benchmarks * **Named imports** - `import { red, green } from '@visulima/colorize'` * **Zero dependencies** (except for color detection) **Multiple Runtimes** * **Node.js** 22.13+ support (ESM and CommonJS) * **Browser support** - Works in Chrome, Safari, Edge, and more * **Deno** and **Next.js** runtime compatibility **Rich Color Support** * **Base ANSI 16 colors** - Standard terminal colors (red, green, blue, etc.) * **ANSI 256 colors** - Extended color palette with `ansi256()` or `fg()`/`bg()` * **TrueColor (16M colors)** - RGB and HEX colors with `rgb()` and `hex()` * **Automatic fallback** - TrueColor → 256 → 16 → no colors **Developer Experience** * **Chained syntax** - `` red.bold.underline`text` `` * **Template literals** - `` red`Hello ${green`World`}` `` * **Nested templates** - Deep nesting support (unique among color libraries) * **TypeScript** - Full type definitions with IDE autocomplete **Advanced Features** * **Color gradients** - Single and multi-line gradient support * **Tagged templates** - String interpolation with `@visulima/colorize/template` * **Strip ANSI codes** - Remove colors with `strip()` method * **Environment awareness** - Respects `NO_COLOR`, `FORCE_COLOR`, `--no-color`, `--color` ## Quick Start [#quick-start] ### Installation [#installation] ```bash npm2yarn npm install @visulima/colorize ``` ### Basic Usage [#basic-usage] ```typescript import colorize, { red, green, blue } from "@visulima/colorize"; // Default import console.log(colorize.green("Success!")); // Named import console.log(red("Error!")); // Template literals console.log(blue`Information`); // Chained syntax console.log(green.bold`Success!`); // Nested templates console.log(red`The ${blue.underline`file.js`} not found!`); ``` ## Use Cases [#use-cases] ### CLI Tool Output [#cli-tool-output] Enhance command-line tool output with clear, colorful messages: ```typescript import { green, red, yellow, bold } from "@visulima/colorize"; console.log(green.bold("✓ Build completed successfully!")); console.log(yellow("⚠ Warning: Deprecated API usage detected")); console.log(red("✗ Error: File not found")); console.log(bold("=== Test Results ===")); ``` ### Structured Logging [#structured-logging] Create readable, color-coded logs for better debugging: ```typescript import { gray, cyan, yellow, red, bold } from "@visulima/colorize"; const logger = { debug: (msg: string) => console.log(gray(`[DEBUG] ${msg}`)), info: (msg: string) => console.log(cyan(`[INFO] ${msg}`)), warn: (msg: string) => console.log(yellow(`[WARN] ${msg}`)), error: (msg: string) => console.log(red.bold(`[ERROR] ${msg}`)), }; logger.info("Server started on port 3000"); logger.warn("High memory usage detected"); logger.error("Database connection failed"); ``` ### Terminal Dashboards [#terminal-dashboards] Build rich terminal UI with gradients and complex styling: ```typescript import { green, red, hex, inverse } from "@visulima/colorize"; const cpu = { totalPercent: 45 }; const ram = { used: 8, total: 16 }; const disk = { used: 120, total: 256 }; const orange = hex("#FFAB40"); console.log(inverse` CPU: ${red`${cpu.totalPercent}%`} RAM: ${green`${((ram.used / ram.total) * 100).toFixed(1)}%`} DISK: ${orange`${((disk.used / disk.total) * 100).toFixed(1)}%`} `); ``` ## Next Steps [#next-steps] Learn how to install and set up colorize in your project Get started with colorize in 5 minutes Explore common usage patterns and syntax Discover TrueColor, gradients, and advanced capabilities Complete API documentation with all methods and options ## Browser and Server Support [#browser-and-server-support] ### Node.js [#nodejs] * ✅ Node.js 22.13 - 25.x * ✅ ESM and CommonJS support ### Browsers [#browsers] * ✅ Chrome 69+ (TrueColor) * ✅ Safari 10+ (TrueColor) * ✅ Edge 79+ (TrueColor) * ✅ Opera 56+ (TrueColor) * ⚠️ Firefox (fallback to `%c` syntax) ### Runtimes [#runtimes] * ✅ Deno * ✅ Next.js (edge runtime) # Installation (/docs/packages/colorize/installation) # Installation [#installation] ## Package Manager [#package-manager] Install **@visulima/colorize** using your preferred package manager: ```bash npm2yarn npm install @visulima/colorize ``` ## Requirements [#requirements] * **Node.js:** 22.13 or higher * **TypeScript:** 5.0+ (optional, for TypeScript projects) ## Import Methods [#import-methods] ### Node.js (ESM) [#nodejs-esm] ```typescript // Default import import colorize from "@visulima/colorize"; // Named imports (recommended) import { red, green, blue, bold, hex } from "@visulima/colorize"; ``` ### Node.js (CommonJS) [#nodejs-commonjs] ```javascript // Default import const colorize = require("@visulima/colorize"); // Named imports const { red, green, blue, bold, hex } = require("@visulima/colorize"); ``` ### Browser [#browser] For browser environments, use the dedicated browser export: ```typescript // ESM default import import colorize from "@visulima/colorize/browser"; // ESM named imports import { red, green, blue } from "@visulima/colorize/browser"; ``` **Important:** Browser version returns an array format for console styling. Use the spread operator: ```typescript import { green } from "@visulima/colorize/browser"; console.log(...green("Success!")); // Note the spread operator ``` ### Subpath Exports [#subpath-exports] The package provides several specialized exports for advanced features: ```typescript // Template literals import template from "@visulima/colorize/template"; // Gradient support import { gradient, multilineGradient } from "@visulima/colorize/gradient"; // Utility functions import { strip } from "@visulima/colorize/utils"; ``` ## Verification [#verification] Verify your installation by running a simple color test: ```typescript import { green, red, blue } from "@visulima/colorize"; console.log(green("✓ Installation successful!")); console.log(blue("ℹ Colorize is ready to use")); console.log(red.bold("● Colors are working")); ``` If you see colored output, the installation is successful! ## TypeScript Support [#typescript-support] TypeScript definitions are included automatically. No additional `@types` packages are needed. ```typescript import type { ColorizeType } from "@visulima/colorize"; // Full type checking and autocomplete const colorize: ColorizeType = (await import("@visulima/colorize")).default; ``` ## Environment Variables [#environment-variables] Colorize automatically detects and respects color support through environment variables: * **`NO_COLOR`** - Disables all colors when set to any value * **`FORCE_COLOR`** - Forces color output (values: `0`, `1`, `2`, `3`) * **`COLORTERM`** - Detects TrueColor support (value: `truecolor`) CLI flags are also supported: * **`--no-color`** - Disables colors * **`--color`** - Forces color output ## Next Steps [#next-steps] Get up and running with colorize in 5 minutes Learn the fundamental usage patterns # Quick Start (/docs/packages/colorize/quick-start) # Quick Start [#quick-start] Learn the essentials of **@visulima/colorize** in 5 minutes. ## Installation [#installation] ```bash npm2yarn npm install @visulima/colorize ``` ## Basic Colors [#basic-colors] Use named color imports for the simplest syntax: ```typescript import { red, green, blue, yellow } from "@visulima/colorize"; console.log(red("Error message")); // Output: Error message (in red) console.log(green("Success message")); // Output: Success message (in green) console.log(blue("Information")); // Output: Information (in blue) console.log(yellow("Warning")); // Output: Warning (in yellow) ``` ## Template Literals [#template-literals] Use template literal syntax for cleaner code: ```typescript import { red, green, cyan } from "@visulima/colorize"; console.log(red`Error: File not found`); console.log(green`Build completed successfully`); console.log(cyan`Server running on port 3000`); ``` ## Styles and Modifiers [#styles-and-modifiers] Chain styles together for combined effects: ```typescript import { red, green, bold, italic, underline } from "@visulima/colorize"; console.log(red.bold`Bold error`); console.log(green.italic`Italic success`); console.log(underline.cyan`Underlined info`); // Multiple styles console.log(bold.underline.red`Important error`); ``` ## Nested Styling [#nested-styling] Nest colors within each other for complex output: ```typescript import { red, blue, green } from "@visulima/colorize"; console.log(red`Error in ${blue`file.js`} at line ${green`42`}`); // Output: Error in file.js at line 42 (with different colors) ``` ## Background Colors [#background-colors] Apply background colors using `bg` prefix: ```typescript import { bgRed, bgGreen, bgBlue, white, black } from "@visulima/colorize"; console.log(white.bgRed`Error`); console.log(black.bgGreen`Success`); console.log(white.bgBlue`Info`); ``` ## TrueColor (RGB/HEX) [#truecolor-rgbhex] Use custom colors with hex or RGB values: ```typescript import { hex, rgb } from "@visulima/colorize"; // Hex colors console.log(hex("#FF6B6B")`Custom red`); console.log(hex("#4ECDC4")`Custom teal`); // RGB colors console.log(rgb(255, 107, 107)`Custom red`); console.log(rgb(78, 205, 196)`Custom teal`); // With background import { bgHex, bgRgb } from "@visulima/colorize"; console.log(bgHex("#FF6B6B")`Red background`); console.log(bgRgb(78, 205, 196)`Teal background`); ``` ## Combining Everything [#combining-everything] Mix colors, styles, and nesting for rich terminal output: ```typescript import { bold, red, green, blue, hex } from "@visulima/colorize"; const success = green.bold`✓`; const error = red.bold`✗`; const info = blue`ℹ`; console.log(`${success} Build completed`); console.log(`${error} Tests failed`); console.log(`${info} ${blue`Server`} started on port ${hex("#FFB800")`3000`}`); ``` ## Strip ANSI Codes [#strip-ansi-codes] Remove colors from strings when needed: ```typescript import colorize, { red } from "@visulima/colorize"; const coloredText = red`Error message`; const plainText = colorize.strip(coloredText); console.log(coloredText); // Output: Error message (colored) console.log(plainText); // Output: Error message (plain) ``` ## Default Import [#default-import] Use the default import for Chalk-compatible syntax: ```typescript import colorize from "@visulima/colorize"; console.log(colorize.red.bold("Bold red text")); console.log(colorize.green("Green text")); console.log(colorize.hex("#FF6B6B")("Custom color")); ``` ## Common Patterns [#common-patterns] ### Log Levels [#log-levels] ```typescript import { gray, cyan, yellow, red, bold } from "@visulima/colorize"; const log = { debug: (msg: string) => console.log(gray(`[DEBUG] ${msg}`)), info: (msg: string) => console.log(cyan(`[INFO] ${msg}`)), warn: (msg: string) => console.log(yellow(`[WARN] ${msg}`)), error: (msg: string) => console.log(red.bold(`[ERROR] ${msg}`)), }; log.debug("Debugging application"); log.info("Server started"); log.warn("High memory usage"); log.error("Connection failed"); ``` ### Status Messages [#status-messages] ```typescript import { green, red, blue } from "@visulima/colorize"; console.log(green`✓ All tests passed`); console.log(red`✗ 2 tests failed`); console.log(blue`→ Running tests...`); ``` ### File Operations [#file-operations] ```typescript import { green, red, yellow } from "@visulima/colorize"; console.log(green`Created: ${yellow`src/index.ts`}`); console.log(red`Deleted: ${yellow`dist/old.js`}`); console.log(blue`Modified: ${yellow`package.json`}`); ``` ## Next Steps [#next-steps] Explore all base colors and styles Learn about gradients and 256 colors Complete API documentation # Troubleshooting (/docs/packages/colorize/troubleshooting) # Troubleshooting [#troubleshooting] Common issues and solutions for **@visulima/colorize**. ## Installation Issues [#installation-issues] ### Module Not Found Error [#module-not-found-error] **Problem:** `Cannot find module '@visulima/colorize'` **Solution:** ```bash npm2yarn # Reinstall the package npm install @visulima/colorize # Clear node_modules and reinstall rm -rf node_modules package-lock.json npm install ``` ### TypeScript Errors [#typescript-errors] **Problem:** TypeScript cannot find type definitions **Solution:** Type definitions are included. Ensure your `tsconfig.json` has: ```json { "compilerOptions": { "moduleResolution": "node", "esModuleInterop": true } } ``` ## Color Display Issues [#color-display-issues] ### Colors Not Showing [#colors-not-showing] **Problem:** No colors appear in terminal output **Possible Causes & Solutions:** 1. **Environment Variable Set:** ```bash # Check if NO_COLOR is set echo $NO_COLOR # Unset it unset NO_COLOR ``` 2. **Terminal Doesn't Support Colors:** ```bash # Force color output FORCE_COLOR=1 node script.js # Or use CLI flag node script.js --color ``` 3. **Output is Piped:** When output is piped or redirected, colors are automatically disabled. Force colors: ```typescript // Set before importing process.env.FORCE_COLOR = "1"; import { red } from "@visulima/colorize"; ``` 4. **CI Environment:** Most CI environments disable colors by default: ```bash # In CI config, set: FORCE_COLOR=1 ``` ### Wrong Colors Displayed [#wrong-colors-displayed] **Problem:** Colors appear incorrect or washed out **Solution:** Check terminal color support: ```bash # Check if terminal supports TrueColor echo $COLORTERM # Should output: truecolor ``` If your terminal doesn't support TrueColor, colorize automatically falls back to 256 or 16 colors. ### Colors Work Locally But Not in CI [#colors-work-locally-but-not-in-ci] **Problem:** Colors show locally but not in CI/CD pipelines **Solution:** ```yaml # GitHub Actions env: FORCE_COLOR: "1" # GitLab CI variables: FORCE_COLOR: "1" # CircleCI environment: FORCE_COLOR: "1" ``` ## Browser Issues [#browser-issues] ### Spread Operator Required Error [#spread-operator-required-error] **Problem:** Browser console shows `[Array]` instead of colored text **Solution:** Use the spread operator: ```typescript import { red } from "@visulima/colorize/browser"; // ❌ Wrong console.log(red("Error")); // ✅ Correct console.log(...red("Error")); ``` ### Firefox Colors Not Working [#firefox-colors-not-working] **Problem:** Colors don't appear in Firefox DevTools **Solution:** Firefox doesn't support ANSI codes natively. Colorize automatically falls back to `%c` syntax. Ensure you're using the spread operator: ```typescript import { red, green } from "@visulima/colorize/browser"; console.log(...red("Error")); // Works in Firefox console.log(...green("Success")); // Works in Firefox ``` ### Source File Location Lost [#source-file-location-lost] **Problem:** Console doesn't show file path and line numbers **Solution:** This happens when using the console override hack. Remove the hack to restore file paths: ```typescript // Remove this override to see file paths again // The trade-off is you need to use the spread operator console.log(...colorize.red("Error")); // Shows file path ``` ## Import Issues [#import-issues] ### Named Import Not Working [#named-import-not-working] **Problem:** `import { red } from '@visulima/colorize'` fails **Solution:** Ensure your environment supports ESM: **Node.js (ESM):** ```json // package.json { "type": "module" } ``` **TypeScript:** ```json // tsconfig.json { "compilerOptions": { "module": "ES2020", "moduleResolution": "node" } } ``` **CommonJS Alternative:** ```javascript const { red } = require("@visulima/colorize"); ``` ### Subpath Exports Not Resolved [#subpath-exports-not-resolved] **Problem:** `Cannot find module '@visulima/colorize/template'` **Solution:** Ensure you're using Node.js 22.13 or higher, which supports `exports` field in `package.json`. Update Node.js: ```bash nvm install 22.13 nvm use 22.13 ``` Or use the full path (not recommended): ```typescript import template from "@visulima/colorize/dist/template.mjs"; ``` ## Performance Issues [#performance-issues] ### Slow Rendering [#slow-rendering] **Problem:** Terminal output is slow when using colors **Solution:** 1. **Reduce color operations in loops:** ```typescript // ❌ Slow: Color inside loop for (let i = 0; i < 1000; i++) { console.log(red(`Item ${i}`)); } // ✅ Fast: Color once, reuse const redText = red("Item"); for (let i = 0; i < 1000; i++) { console.log(`${redText} ${i}`); } ``` 2. **Use template literals efficiently:** ```typescript // ❌ Slower red("Text " + variable + " more text"); // ✅ Faster red`Text ${variable} more text`; ``` ### Memory Leaks [#memory-leaks] **Problem:** Memory usage increases over time **Solution:** Don't store large numbers of colorize instances. Reuse colors: ```typescript // ❌ Don't do this const colors = []; for (let i = 0; i < 10000; i++) { colors.push(red(`Item ${i}`)); } // ✅ Do this const red = import { red } from "@visulima/colorize"; // Use red() directly when needed ``` ## TypeScript Issues [#typescript-issues] ### Type Errors with Chaining [#type-errors-with-chaining] **Problem:** TypeScript shows errors when chaining styles **Solution:** This is usually a version issue. Ensure you have TypeScript 5.0+: ```bash npm2yarn npm install -D typescript@latest ``` ### Return Type Errors [#return-type-errors] **Problem:** TypeScript complains about return types **Solution:** Explicitly type the function if needed: ```typescript import type { StyleFunction } from "@visulima/colorize"; import { red } from "@visulima/colorize"; const myStyle: StyleFunction = red.bold; ``` ## Windows-Specific Issues [#windows-specific-issues] ### Colors Don't Work in cmd.exe [#colors-dont-work-in-cmdexe] **Problem:** No colors in Windows Command Prompt **Solution:** Use Windows Terminal instead of cmd.exe: ```bash # Install Windows Terminal winget install Microsoft.WindowsTerminal # Or download from Microsoft Store ``` Alternatively, enable ANSI support in cmd.exe (Windows 10+): ```cmd reg add HKCU\Console /v VirtualTerminalLevel /t REG_DWORD /d 1 ``` ### PowerShell Colors Issue [#powershell-colors-issue] **Problem:** Colors look wrong in PowerShell **Solution:** Use PowerShell 7+ for best results: ```powershell # Install PowerShell 7 winget install Microsoft.PowerShell ``` ## Gradient Issues [#gradient-issues] ### Gradient Not Smooth [#gradient-not-smooth] **Problem:** Gradient appears blocky or with few colors **Solution:** Ensure your terminal supports TrueColor: ```bash # Set COLORTERM export COLORTERM=truecolor # Or force color level export FORCE_COLOR=3 ``` ### Multi-line Gradient Alignment Off [#multi-line-gradient-alignment-off] **Problem:** Multi-line gradient doesn't align properly **Solution:** Use `multilineGradient` instead of `gradient`: ```typescript import { multilineGradient } from "@visulima/colorize/gradient"; // ✅ Correct for multi-line console.log(multilineGradient(["cyan", "magenta"])(text)); // ❌ Wrong for multi-line (each line gets full gradient) console.log(gradient("cyan", "magenta")(text)); ``` ## Template Issues [#template-issues] ### Template Syntax Error [#template-syntax-error] **Problem:** Tagged template doesn't work **Solution:** Ensure you're using the correct syntax: ```typescript import template from "@visulima/colorize/template"; // ✅ Correct console.log(template`{red Error message}`); // ❌ Wrong (missing template tag) console.log(template("{red Error message}")); ``` ### Template Variables Not Interpolating [#template-variables-not-interpolating] **Problem:** Variables show as `[object Object]` **Solution:** Convert objects to strings: ```typescript import template from "@visulima/colorize/template"; const user = { name: "John" }; // ❌ Wrong console.log(template`User: {green ${user}}`); // ✅ Correct console.log(template`User: {green ${user.name}}`); console.log(template`User: {green ${JSON.stringify(user)}}`); ``` ## Environment Detection [#environment-detection] ### Color Support Not Detected [#color-support-not-detected] **Problem:** Colorize doesn't detect terminal capabilities correctly **Solution:** Manually set the color level: ```bash # Force TrueColor (24-bit) export FORCE_COLOR=3 # Force 256 colors export FORCE_COLOR=2 # Force 16 colors export FORCE_COLOR=1 # Disable colors export NO_COLOR=1 ``` Or programmatically: ```typescript // Before importing colorize process.env.FORCE_COLOR = "3"; import { red } from "@visulima/colorize"; ``` ## Getting Help [#getting-help] If you're still experiencing issues: 1. **Check Node.js version:** ```bash node --version # Should be 22.13 or higher ``` 2. **Check package version:** ```bash npm list @visulima/colorize ``` 3. **Enable debug output:** ```typescript import colorize from "@visulima/colorize"; console.log("Color support detected:", colorize.isColorSupported); ``` 4. **Report an issue:** * GitHub: [github.com/visulima/visulima/issues](https://github.com/visulima/visulima/issues) * Include: * Node.js version * Operating system * Terminal emulator * Minimal reproduction code ## Next Steps [#next-steps] Review the complete API documentation See working examples # Advanced Features (/docs/packages/colorize/usage/advanced) # Advanced Features [#advanced-features] This guide covers advanced capabilities of **@visulima/colorize** including ANSI 256 colors, TrueColor support, gradients, and tagged template literals. ## ANSI 256 Colors [#ansi-256-colors] Access the extended 256-color palette for more color options. ### Color Ranges [#color-ranges] The 256-color palette is divided into ranges: | Range | Description | | ------- | ---------------------------------------- | | 0-7 | Standard colors (same as base 16) | | 8-15 | Bright colors (same as base 16) | | 16-231 | 6×6×6 RGB cube (216 colors) | | 232-255 | Grayscale from black to white (24 steps) | ### Foreground Colors [#foreground-colors] Use `ansi256()` or its alias `fg()` for 256 foreground colors: ```typescript import { ansi256, fg } from "@visulima/colorize"; // Using ansi256() console.log(ansi256(196)`Bright red`); // Color code 196 console.log(ansi256(21)`Deep blue`); // Color code 21 console.log(ansi256(226)`Yellow`); // Color code 226 // Using fg() alias (shorter) console.log(fg(196)`Bright red`); console.log(fg(21)`Deep blue`); console.log(fg(226)`Yellow`); ``` ### Background Colors [#background-colors] Use `bgAnsi256()` or its alias `bg()` for 256 background colors: ```typescript import { bgAnsi256, bg, white } from "@visulima/colorize"; // Using bgAnsi256() console.log(white.bgAnsi256(196)`White on red`); console.log(white.bgAnsi256(21)`White on blue`); // Using bg() alias (shorter) console.log(white.bg(196)`White on red`); console.log(white.bg(21)`White on blue`); ``` ### Chaining with 256 Colors [#chaining-with-256-colors] Combine 256 colors with styles and other colors: ```typescript import { ansi256, bg, bold, italic } from "@visulima/colorize"; console.log(ansi256(196).bold`Bold bright red`); console.log(bold.ansi256(21).italic`Bold italic blue`); console.log(ansi256(226).bg(21)`Yellow text on blue background`); ``` ### Grayscale Colors [#grayscale-colors] Use grayscale range (232-255) for subtle effects: ```typescript import { ansi256 } from "@visulima/colorize"; // Darker grays console.log(ansi256(232)`Darkest gray`); console.log(ansi256(237)`Dark gray`); console.log(ansi256(242)`Medium gray`); console.log(ansi256(248)`Light gray`); console.log(ansi256(255)`Lightest gray (almost white)`); ``` ## TrueColor (16 Million Colors) [#truecolor-16-million-colors] Use RGB or HEX values for precise color control with 24-bit TrueColor. ### HEX Colors [#hex-colors] Define colors using hexadecimal notation: ```typescript import { hex, bgHex } from "@visulima/colorize"; // Foreground colors console.log(hex("#FF6B6B")`Coral red`); console.log(hex("#4ECDC4")`Turquoise`); console.log(hex("#FFE66D")`Sunshine yellow`); console.log(hex("#95E1D3")`Mint green`); // Short hex notation console.log(hex("#F00")`Red`); console.log(hex("#0F0")`Green`); console.log(hex("#00F")`Blue`); // Background colors console.log(bgHex("#FF6B6B")`Coral background`); console.log(bgHex("#4ECDC4")`Turquoise background`); ``` ### RGB Colors [#rgb-colors] Define colors using RGB values (0-255): ```typescript import { rgb, bgRgb } from "@visulima/colorize"; // Foreground colors console.log(rgb(255, 107, 107)`Coral red`); console.log(rgb(78, 205, 196)`Turquoise`); console.log(rgb(255, 230, 109)`Sunshine yellow`); // Background colors console.log(bgRgb(255, 107, 107)`Coral background`); console.log(bgRgb(78, 205, 196)`Turquoise background`); ``` ### Chaining TrueColor [#chaining-truecolor] Combine TrueColor with styles and other colors: ```typescript import { hex, bgHex, bold, italic } from "@visulima/colorize"; console.log(hex("#FF6B6B").bold`Bold coral`); console.log(bold.hex("#4ECDC4").italic`Bold italic turquoise`); console.log(hex("#FF6B6B").bgHex("#4ECDC4")`Coral on turquoise`); ``` ### Brand Colors [#brand-colors] Define brand colors as constants: ```typescript import { hex } from "@visulima/colorize"; const brandColors = { primary: hex("#007AFF"), success: hex("#34C759"), warning: hex("#FF9500"), danger: hex("#FF3B30"), info: hex("#5856D6"), }; console.log(brandColors.primary`Primary color text`); console.log(brandColors.success`Success message`); console.log(brandColors.warning`Warning message`); console.log(brandColors.danger`Error message`); ``` ## Color Fallback [#color-fallback] Colorize automatically falls back to supported color spaces: ``` TrueColor → ANSI 256 → ANSI 16 → No colors ``` If a terminal doesn't support TrueColor: * TrueColor is converted to the nearest 256-color * If 256 colors aren't supported, it converts to base 16 colors * If no colors are supported, ANSI codes are stripped No configuration needed - fallback is automatic! ## Gradients [#gradients] Create beautiful color gradients for text. ### Single-Line Gradients [#single-line-gradients] Apply gradients to single-line text: ```typescript import { gradient } from "@visulima/colorize/gradient"; // Two-color gradient console.log(gradient("red", "blue")("Hello World")); // Three-color gradient console.log(gradient("red", "yellow", "green")("Rainbow text")); // Using hex colors console.log(gradient("#FF6B6B", "#4ECDC4")("Custom gradient")); // Using RGB colors console.log(gradient("rgb(255,0,0)", "rgb(0,0,255)")("RGB gradient")); ``` ### Multi-Line Gradients [#multi-line-gradients] Apply gradients across multiple lines with vertical alignment: ```typescript import { multilineGradient } from "@visulima/colorize/gradient"; const asciiArt = ` ██████╗ ██████╗ ██╗ ██████╗ ██████╗ ██╗███████╗███████╗ ██╔════╝ ██╔═══██╗██║ ██╔═══██╗██╔══██╗██║╚══███╔╝██╔════╝ ██║ ██║ ██║██║ ██║ ██║██████╔╝██║ ███╔╝ █████╗ ██║ ██║ ██║██║ ██║ ██║██╔══██╗██║ ███╔╝ ██╔══╝ ╚██████╗ ╚██████╔╝███████╗╚██████╔╝██║ ██║██║███████╗███████╗ ╚═════╝ ╚═════╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝╚══════╝╚══════╝ `; console.log(multilineGradient(["cyan", "magenta"])(asciiArt)); ``` ### Gradient Examples [#gradient-examples] ```typescript import { gradient, multilineGradient } from "@visulima/colorize/gradient"; // Sunrise gradient console.log(gradient("purple", "orange", "yellow")("=== Sunrise ===")); // Ocean gradient console.log(gradient("#001F3F", "#0074D9", "#7FDBFF")("~~~ Ocean ~~~")); // Fire gradient console.log(gradient("red", "orange", "yellow", "white")("🔥 FIRE 🔥")); // Multi-line with color alignment const duck = ` __ <(o )___ ( ._> / \`---' `; console.log(multilineGradient(["orange", "yellow"])(duck)); ``` ## Tagged Template Literals [#tagged-template-literals] Use tagged template literals for complex string interpolation with colors. ### Basic Template Usage [#basic-template-usage] ```typescript import template from "@visulima/colorize/template"; const cpu = { totalPercent: 45 }; const ram = { used: 8, total: 16 }; const disk = { used: 120, total: 256 }; console.log(template` CPU: {red ${cpu.totalPercent}%} RAM: {green ${((ram.used / ram.total) * 100).toFixed(1)}%} DISK: {blue ${((disk.used / disk.total) * 100).toFixed(1)}%} `); ``` ### Chained Styles in Templates [#chained-styles-in-templates] Chain styles just like regular colorize: ```typescript import template from "@visulima/colorize/template"; console.log(template` Status: {green.bold Success} Error: {red.bold.underline Critical} Info: {blue.italic Note} `); ``` ### TrueColor in Templates [#truecolor-in-templates] Use RGB and hex colors in templates: ```typescript import template from "@visulima/colorize/template"; console.log(template` {rgb(255,107,107) Custom red color} {hex(#4ECDC4) Custom teal color} `); ``` ### Hex Shorthand [#hex-shorthand] Use shorthand hex notation in templates: ```typescript import template from "@visulima/colorize/template"; console.log(template` {#FF0000 Red foreground} {#:00FF00 Green background} {#FF0000:00FF00 Red on green} `); ``` ### Complex Templates [#complex-templates] ```typescript import template from "@visulima/colorize/template"; const miles = 18; const calculateFeet = (miles: number) => miles * 5280; console.log(template` There are {bold 5280 feet} in a mile. In {bold ${miles} miles}, there are {green.bold ${calculateFeet(miles)} feet}. `); ``` ## Browser Usage [#browser-usage] ### Basic Browser Support [#basic-browser-support] ```typescript import { red, green, blue } from "@visulima/colorize/browser"; // Note the spread operator console.log(...red("Error message")); console.log(...green("Success message")); console.log(...blue("Information")); ``` ### Browser Return Format [#browser-return-format] Browser version returns arrays for console `%c` syntax: ```typescript import { red } from "@visulima/colorize/browser"; const result = red("Error"); // Returns: ['%cError', 'color: red'] console.log(...result); // Correctly styled in browser console ``` ### Browser Compatibility [#browser-compatibility] | Browser | Version | Colors Supported | | ----------- | -------- | ---------------- | | **Chrome** | **69+** | TrueColor (16M) | | **Safari** | **10+** | TrueColor (16M) | | **Edge** | **79+** | TrueColor (16M) | | **Opera** | **56+** | TrueColor (16M) | | **Brave** | **1.0+** | TrueColor (16M) | | **Vivaldi** | **2.0+** | TrueColor (16M) | | **Firefox** | All | Fallback to `%c` | ### Console Hack (Optional) [#console-hack-optional] Override console for transparent usage (removes line numbers): ```typescript import { red, green } from "@visulima/colorize/browser"; let consoleOverwritten = false; if (typeof window !== "undefined" && !consoleOverwritten) { ["error", "log", "warn", "info"].forEach((method) => { const original = console[method]; console[method] = (...args: any[]) => { if (Array.isArray(args[0]) && args[0][0]?.includes("%c")) { original(...args[0]); } else { original(...args); } }; }); consoleOverwritten = true; } // Now can use without spread console.log(red("Error")); // Works without ... ``` **Warning:** This hack removes file paths and line numbers from console output. ## Next Steps [#next-steps] See real-world usage examples Complete API documentation Common issues and solutions # Basic Usage (/docs/packages/colorize/usage/basic) # Basic Usage [#basic-usage] This guide covers the fundamental usage patterns for **@visulima/colorize**. ## Import Styles [#import-styles] ### Named Import (Recommended) [#named-import-recommended] Import specific colors and styles for the most concise code: ```typescript import { red, green, blue, bold, italic } from "@visulima/colorize"; console.log(red("Error")); console.log(green.bold("Success")); ``` ### Default Import [#default-import] Use the default import for Chalk-compatible syntax: ```typescript import colorize from "@visulima/colorize"; console.log(colorize.red("Error")); console.log(colorize.green.bold("Success")); ``` ### Combined Imports [#combined-imports] Mix default and named imports: ```typescript import colorize, { red, strip } from "@visulima/colorize"; const coloredText = red("Error"); const plainText = colorize.strip(coloredText); ``` ## Syntax Options [#syntax-options] Colorize supports three syntax styles for maximum flexibility: ### 1. Function Syntax [#1-function-syntax] Pass strings as function arguments: ```typescript import { red, green, blue } from "@visulima/colorize"; console.log(red("Error message")); console.log(green("Success message")); console.log(blue("Information")); ``` ### 2. Template Literal Syntax [#2-template-literal-syntax] Use template literals for cleaner code: ```typescript import { red, green, blue } from "@visulima/colorize"; console.log(red`Error message`); console.log(green`Success message`); console.log(blue`Information`); ``` ### 3. Template Literal with Variables [#3-template-literal-with-variables] Interpolate variables within template literals: ```typescript import { red, blue, green } from "@visulima/colorize"; const filename = "config.json"; const line = 42; console.log(red`Error in ${filename} at line ${line}`); // Output: Error in config.json at line 42 (all in red) ``` ## Base ANSI 16 Colors [#base-ansi-16-colors] ### Foreground Colors [#foreground-colors] Standard terminal colors for text: ```typescript import { black, red, green, yellow, blue, magenta, cyan, white, gray, // alias: grey } from "@visulima/colorize"; console.log(black`Black text`); console.log(red`Red text`); console.log(green`Green text`); console.log(yellow`Yellow text`); console.log(blue`Blue text`); console.log(magenta`Magenta text`); console.log(cyan`Cyan text`); console.log(white`White text`); console.log(gray`Gray text`); // or grey ``` ### Bright Colors [#bright-colors] Brighter variants of standard colors: ```typescript import { blackBright, redBright, greenBright, yellowBright, blueBright, magentaBright, cyanBright, whiteBright } from "@visulima/colorize"; console.log(redBright`Bright red`); console.log(greenBright`Bright green`); console.log(blueBright`Bright blue`); ``` ### Background Colors [#background-colors] Apply colors to text background using `bg` prefix: ```typescript import { bgBlack, bgRed, bgGreen, bgYellow, bgBlue, bgMagenta, bgCyan, bgWhite, white, // for foreground } from "@visulima/colorize"; console.log(white.bgRed`White text on red background`); console.log(white.bgBlue`White text on blue background`); console.log(bgGreen`Green background`); ``` ### Bright Background Colors [#bright-background-colors] ```typescript import { bgBlackBright, bgRedBright, bgGreenBright, bgYellowBright, bgBlueBright, bgMagentaBright, bgCyanBright, bgWhiteBright } from "@visulima/colorize"; console.log(bgRedBright`Bright red background`); console.log(bgGreenBright`Bright green background`); ``` ## Text Styles [#text-styles] ### Basic Styles [#basic-styles] ```typescript import { bold, dim, italic, underline, strikethrough } from "@visulima/colorize"; console.log(bold`Bold text`); console.log(dim`Dimmed text`); console.log(italic`Italic text`); console.log(underline`Underlined text`); console.log(strikethrough`Strikethrough text`); // alias: strike ``` ### Special Styles [#special-styles] ```typescript import { inverse, hidden, visible, reset } from "@visulima/colorize"; console.log(inverse`Inverted colors`); console.log(hidden`Hidden text`); // Text is invisible console.log(visible`Always visible`); console.log(reset`Reset all styles`); ``` ## Chained Syntax [#chained-syntax] Chain multiple colors and styles together: ### Color + Style [#color--style] ```typescript import { red, green, blue, bold, italic, underline } from "@visulima/colorize"; console.log(red.bold`Bold red text`); console.log(green.italic`Italic green text`); console.log(blue.underline`Underlined blue text`); ``` ### Multiple Styles [#multiple-styles] ```typescript import { red, bold, italic, underline } from "@visulima/colorize"; console.log(red.bold.italic`Bold italic red`); console.log(bold.underline.green`Bold underlined green`); console.log(red.bold.underline.italic`All styles combined`); ``` ### Foreground + Background [#foreground--background] ```typescript import { white, red, green, bgRed, bgGreen } from "@visulima/colorize"; console.log(white.bgRed`White text on red background`); console.log(red.bgGreen`Red text on green background`); ``` ### Any Order [#any-order] Styles can be chained in any order: ```typescript import { bold, red, underline } from "@visulima/colorize"; console.log(bold.red.underline`Style order 1`); console.log(red.bold.underline`Style order 2`); console.log(underline.bold.red`Style order 3`); // All produce the same result ``` ## Nested Styling [#nested-styling] Nest colored text within other colored text: ### Basic Nesting [#basic-nesting] ```typescript import { red, blue } from "@visulima/colorize"; console.log(red`Error: ${blue`file.js`} not found`); // Output: "Error: file.js not found" with different colors ``` ### Deep Nesting [#deep-nesting] ```typescript import { green, yellow, magenta, cyan, red } from "@visulima/colorize"; console.log(green`Level 1 ${yellow`Level 2 ${magenta`Level 3 ${cyan`Level 4 ${red`Level 5`}`}`}`} back to Level 1`); ``` ### Nested with Styles [#nested-with-styles] ```typescript import { red, blue, bold, italic, underline } from "@visulima/colorize"; console.log(red`Error in ${bold.blue`file.js`} at ${italic`line 42`}`); console.log(bold`Important: ${underline.red`Critical error`} detected`); ``` ## Handling Edge Cases [#handling-edge-cases] Colorize handles edge cases gracefully: ### Empty and Null Values [#empty-and-null-values] ```typescript import { red } from "@visulima/colorize"; console.log(red()); // Returns: "" console.log(red("")); // Returns: "" console.log(red(undefined)); // Returns: "" console.log(red(null)); // Returns: "" ``` ### Newlines [#newlines] Colors properly break at newlines: ```typescript import { bgGreen, red } from "@visulima/colorize"; console.log(bgGreen`First line Second line Third line`); // Each line has green background properly applied ``` ## Strip ANSI Codes [#strip-ansi-codes] Remove all ANSI color codes from strings: ```typescript import colorize, { red, green, bold } from "@visulima/colorize"; // or import { strip } from "@visulima/colorize/utils"; const styledText = red.bold`Error message`; const plainText = colorize.strip(styledText); console.log(styledText); // Output: Error message (colored and bold) console.log(plainText); // Output: Error message (plain text) ``` ## Using ANSI Codes Directly [#using-ansi-codes-directly] Access raw ANSI codes for custom implementations: ```typescript import { green, red, bold } from "@visulima/colorize"; // Each style has 'open' and 'close' properties console.log(`Hello ${green.open}World${green.close}!`); // Define custom styles const myStyle = bold.italic.red.bgYellow; console.log(`Custom: ${myStyle.open}styled text${myStyle.close}`); console.log("Open code:", green.open); // \x1b[32m console.log("Close code:", green.close); // \x1b[39m ``` ## Next Steps [#next-steps] Explore 256 colors, TrueColor, and gradients See real-world usage examples Complete API documentation # Real-World Examples (/docs/packages/colorize/usage/examples) # Real-World Examples [#real-world-examples] Practical examples demonstrating how to use **@visulima/colorize** in real-world scenarios. ## CLI Tool Output [#cli-tool-output] ### Build Status Reporter [#build-status-reporter] ```typescript import { green, red, yellow, blue, bold, dim } from "@visulima/colorize"; class BuildReporter { success(message: string) { console.log(green.bold(`✓ ${message}`)); } error(message: string, details?: string) { console.log(red.bold(`✗ ${message}`)); if (details) { console.log(dim(` ${details}`)); } } warning(message: string) { console.log(yellow(`⚠ ${message}`)); } info(message: string) { console.log(blue(`ℹ ${message}`)); } step(current: number, total: number, message: string) { console.log(dim(`[${current}/${total}]`) + ` ${message}`); } } const reporter = new BuildReporter(); reporter.step(1, 3, "Compiling TypeScript..."); reporter.success("TypeScript compilation completed"); reporter.step(2, 3, "Running tests..."); reporter.error("Tests failed", "2 tests failed in user.test.ts"); reporter.step(3, 3, "Building bundle..."); reporter.warning("Bundle size exceeds 1MB"); reporter.success("Build completed"); ``` ### Package Manager Output [#package-manager-output] ```typescript import { green, cyan, yellow, gray, bold } from "@visulima/colorize"; function installPackage(name: string, version: string) { console.log(bold(`Installing ${name}@${version}...`)); console.log(gray(` → Resolving dependencies`)); console.log(gray(` → Fetching packages`)); console.log(gray(` → Linking dependencies`)); console.log(green(` ✓ ${name}@${version} installed`)); } function packageInfo(name: string, description: string) { console.log(cyan.bold(name)); console.log(gray(description)); } installPackage("@visulima/colorize", "2.0.0"); packageInfo("@visulima/colorize", "Terminal string styling done right"); ``` ## Structured Logging [#structured-logging] ### Logger with Log Levels [#logger-with-log-levels] ```typescript import { gray, cyan, yellow, red, bold } from "@visulima/colorize"; enum LogLevel { DEBUG = 0, INFO = 1, WARN = 2, ERROR = 3, } class Logger { constructor(private level: LogLevel = LogLevel.INFO) {} debug(message: string, data?: any) { if (this.level <= LogLevel.DEBUG) { console.log(gray(`[DEBUG] ${message}`), data || ""); } } info(message: string, data?: any) { if (this.level <= LogLevel.INFO) { console.log(cyan(`[INFO] ${message}`), data || ""); } } warn(message: string, data?: any) { if (this.level <= LogLevel.WARN) { console.log(yellow(`[WARN] ${message}`), data || ""); } } error(message: string, error?: Error) { if (this.level <= LogLevel.ERROR) { console.log(red.bold(`[ERROR] ${message}`)); if (error) { console.log(red(error.stack || error.message)); } } } success(message: string) { console.log(green.bold(`[OK] ${message}`)); } } // Usage const logger = new Logger(LogLevel.DEBUG); logger.debug("Application starting"); logger.info("Server listening on port 3000"); logger.warn("Deprecated API usage detected"); logger.error("Database connection failed", new Error("Connection timeout")); logger.success("All migrations completed"); ``` ### Request Logger [#request-logger] ```typescript import { green, yellow, red, cyan, gray, bold } from "@visulima/colorize"; interface RequestLog { method: string; path: string; status: number; duration: number; } function logRequest({ method, path, status, duration }: RequestLog) { const methodColor = { GET: cyan, POST: green, PUT: yellow, DELETE: red, PATCH: yellow, }[method] || gray; const statusColor = status >= 500 ? red : status >= 400 ? yellow : status >= 300 ? cyan : green; console.log(methodColor.bold(method.padEnd(6)) + gray(path.padEnd(30)) + statusColor(status.toString().padEnd(5)) + gray(`${duration}ms`)); } // Usage logRequest({ method: "GET", path: "/api/users", status: 200, duration: 45 }); logRequest({ method: "POST", path: "/api/users", status: 201, duration: 120 }); logRequest({ method: "GET", path: "/api/posts", status: 404, duration: 12 }); logRequest({ method: "PUT", path: "/api/users/123", status: 500, duration: 2500 }); ``` ## Terminal Dashboards [#terminal-dashboards] ### System Monitor [#system-monitor] ```typescript import { green, red, yellow, hex, inverse, bold } from "@visulima/colorize"; interface SystemStats { cpu: number; memory: { used: number; total: number }; disk: { used: number; total: number }; } function displaySystemStats(stats: SystemStats) { const cpuColor = stats.cpu > 80 ? red : stats.cpu > 50 ? yellow : green; const memPercent = (stats.memory.used / stats.memory.total) * 100; const memColor = memPercent > 80 ? red : memPercent > 50 ? yellow : green; const diskPercent = (stats.disk.used / stats.disk.total) * 100; const diskColor = diskPercent > 80 ? red : diskPercent > 50 ? yellow : green; console.log(inverse(bold(" SYSTEM MONITOR "))); console.log(); console.log(`CPU: ${cpuColor`${stats.cpu.toFixed(1)}%`}`.padEnd(20) + createProgressBar(stats.cpu, 100)); console.log(`RAM: ${memColor`${memPercent.toFixed(1)}%`}`.padEnd(20) + createProgressBar(stats.memory.used, stats.memory.total)); console.log(`DISK: ${diskColor`${diskPercent.toFixed(1)}%`}`.padEnd(20) + createProgressBar(stats.disk.used, stats.disk.total)); } function createProgressBar(current: number, total: number, width: number = 20): string { const percent = current / total; const filled = Math.round(width * percent); const empty = width - filled; const bar = "█".repeat(filled) + "░".repeat(empty); return percent > 0.8 ? red(bar) : percent > 0.5 ? yellow(bar) : green(bar); } // Usage displaySystemStats({ cpu: 65.4, memory: { used: 8.2, total: 16 }, disk: { used: 120, total: 256 }, }); ``` ### Progress Indicators [#progress-indicators] ```typescript import { green, blue, yellow, gray } from "@visulima/colorize"; class ProgressBar { private current: number = 0; constructor( private total: number, private width: number = 40, ) {} update(current: number, message?: string) { this.current = current; this.render(message); } private render(message?: string) { const percent = Math.min(100, (this.current / this.total) * 100); const filled = Math.round((this.width * percent) / 100); const empty = this.width - filled; const bar = green("█".repeat(filled)) + gray("░".repeat(empty)); const percentText = blue(`${percent.toFixed(1)}%`); const status = message ? yellow(` ${message}`) : ""; process.stdout.write(`\r${bar} ${percentText}${status}`); if (this.current >= this.total) { console.log(); // New line when complete } } } // Usage const progress = new ProgressBar(100); let current = 0; const interval = setInterval(() => { current += 5; progress.update(current, `Processing item ${current}...`); if (current >= 100) { clearInterval(interval); } }, 100); ``` ## File Operations [#file-operations] ### File Tree Display [#file-tree-display] ```typescript import { blue, green, yellow, gray } from "@visulima/colorize"; interface FileNode { name: string; type: "file" | "directory"; children?: FileNode[]; } function displayTree(node: FileNode, prefix: string = "", isLast: boolean = true) { const connector = isLast ? "└── " : "├── "; const color = node.type === "directory" ? blue.bold : green; const icon = node.type === "directory" ? "📁" : "📄"; console.log(gray(prefix + connector) + icon + " " + color(node.name)); if (node.children) { const childPrefix = prefix + (isLast ? " " : "│ "); node.children.forEach((child, index) => { const isLastChild = index === node.children!.length - 1; displayTree(child, childPrefix, isLastChild); }); } } // Usage const tree: FileNode = { name: "project", type: "directory", children: [ { name: "src", type: "directory", children: [ { name: "index.ts", type: "file" }, { name: "utils.ts", type: "file" }, ], }, { name: "package.json", type: "file" }, { name: "README.md", type: "file" }, ], }; displayTree(tree); ``` ### File Diff Display [#file-diff-display] ```typescript import { green, red, gray } from "@visulima/colorize"; interface DiffLine { type: "add" | "remove" | "unchanged"; content: string; } function displayDiff(lines: DiffLine[]) { lines.forEach((line) => { switch (line.type) { case "add": console.log(green(`+ ${line.content}`)); break; case "remove": console.log(red(`- ${line.content}`)); break; case "unchanged": console.log(gray(` ${line.content}`)); break; } }); } // Usage const diff: DiffLine[] = [ { type: "unchanged", content: "function hello() {" }, { type: "remove", content: ' console.log("Hello");' }, { type: "add", content: ' console.log("Hello World");' }, { type: "unchanged", content: "}" }, ]; displayDiff(diff); ``` ## Test Runners [#test-runners] ### Test Results Display [#test-results-display] ```typescript import { green, red, yellow, gray, bold } from "@visulima/colorize"; interface TestResult { name: string; status: "pass" | "fail" | "skip"; duration: number; error?: string; } interface TestSuite { name: string; tests: TestResult[]; } function displayTestResults(suite: TestSuite) { console.log(bold(`\n${suite.name}`)); suite.tests.forEach((test) => { const icon = test.status === "pass" ? green("✓") : test.status === "fail" ? red("✗") : yellow("○"); const duration = gray(`(${test.duration}ms)`); console.log(` ${icon} ${test.name} ${duration}`); if (test.error) { console.log(red(` ${test.error}`)); } }); const passed = suite.tests.filter((t) => t.status === "pass").length; const failed = suite.tests.filter((t) => t.status === "fail").length; const skipped = suite.tests.filter((t) => t.status === "skip").length; console.log(); console.log(bold("Summary:")); console.log(` ${green(`Passed: ${passed}`)}`); if (failed > 0) console.log(` ${red(`Failed: ${failed}`)}`); if (skipped > 0) console.log(` ${yellow(`Skipped: ${skipped}`)}`); } // Usage displayTestResults({ name: "User Service Tests", tests: [ { name: "should create user", status: "pass", duration: 45 }, { name: "should validate email", status: "pass", duration: 12 }, { name: "should reject invalid email", status: "fail", duration: 8, error: "Expected false but got true" }, { name: "should update user", status: "skip", duration: 0 }, ], }); ``` ## Interactive Prompts [#interactive-prompts] ### Question Prompt [#question-prompt] ```typescript import { cyan, green, yellow } from "@visulima/colorize"; import * as readline from "readline"; async function ask(question: string): Promise { const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); return new Promise((resolve) => { rl.question(cyan(`? ${question} `), (answer) => { rl.close(); resolve(answer); }); }); } async function confirm(question: string): Promise { const answer = await ask(`${question} ${yellow("(y/n)")}`); return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes"; } // Usage async function runInteractive() { const name = await ask("What is your name?"); console.log(green(`Hello, ${name}!`)); const proceed = await confirm("Do you want to continue?"); if (proceed) { console.log(green("Continuing...")); } else { console.log(yellow("Cancelled")); } } ``` ## Next Steps [#next-steps] Complete API documentation Common issues and solutions # API Reference (/docs/packages/command-line-args/api) # API Reference [#api-reference] ## Functions [#functions] ### `parseArgs(definitions, options?)` [#parseargsdefinitions-options] Parse command-line arguments. **Parameters:** * `definitions` (OptionDefinition\[]): Option definitions * `options` (ParseOptions, optional): Parsing options **Returns:** Parsed options object ### `commandLineArgs(definitions, options?)` [#commandlineargsdefinitions-options] Alias for `parseArgs()` (backward compatibility). ## Types [#types] ### `OptionDefinition` [#optiondefinition] ```typescript interface OptionDefinition { name: string; // Option name alias?: string; // Short alias type?: TypeConstructor; // String, Number, Boolean defaultValue?: any; // Default value multiple?: boolean; // Allow multiple values defaultOption?: boolean; // Capture positional args } ``` ### `ParseOptions` [#parseoptions] ```typescript interface ParseOptions { partial?: boolean; // Preserve unknown options stopAtFirstUnknown?: boolean; // Stop at unknown option camelCase?: boolean; // Convert to camelCase argv?: string[]; // Custom argv array } ``` ## Examples [#examples] **Basic:** ```typescript parseArgs([{ name: "file", type: String }]); ``` **With alias:** ```typescript parseArgs([{ name: "verbose", alias: "v", type: Boolean }]); ``` **Multiple values:** ```typescript parseArgs([{ name: "include", type: String, multiple: true }]); ``` **Default value:** ```typescript parseArgs([{ name: "port", type: Number, defaultValue: 3000 }]); ``` **Positional:** ```typescript parseArgs([{ name: "files", type: String, multiple: true, defaultOption: true }]); ``` # Introduction (/docs/packages/command-line-args) # @visulima/command-line-args [#visulimacommand-line-args] A mature, feature-complete library to parse command-line options with TypeScript support, automatic type conversion, and intuitive API. ## Key Features [#key-features] * **Type-safe parsing** - Automatic conversion to String, Number, Boolean * **Multiple values** - Array support for repeated options * **Aliases** - Short (-v) and long (--verbose) option names * **Default values** - Fallback values when options not provided * **Positional arguments** - Capture non-option arguments * **Flexible parsing** - Partial parsing, stop-early modes * **TypeScript native** - Full type definitions included ## Quick Start [#quick-start] ```typescript import { parseArgs } from "@visulima/command-line-args"; const options = parseArgs([ { name: "file", alias: "f", type: String }, { name: "verbose", alias: "v", type: Boolean }, { name: "count", alias: "c", type: Number }, ]); // Usage: node script.js --file input.txt -v --count 5 console.log(options); // { file: "input.txt", verbose: true, count: 5 } ``` ## Use Cases [#use-cases] **CLI Tools:** Build professional command-line interfaces **Build Scripts:** Parse configuration flags **Development Tools:** Create flexible dev tooling **Automation:** Script with configurable parameters Install and set up the package Parse your first CLI arguments Explore all parsing features Complete API documentation # Installation (/docs/packages/command-line-args/installation) # Installation [#installation] ```bash npm2yarn npm install @visulima/command-line-args ``` ## Import [#import] ```typescript import { parseArgs } from "@visulima/command-line-args"; // or import { commandLineArgs } from "@visulima/command-line-args"; ``` ## Verification [#verification] ```typescript import { parseArgs } from "@visulima/command-line-args"; const options = parseArgs([{ name: "test", type: Boolean }]); console.log("✓ Installation successful"); ``` # Quick Start (/docs/packages/command-line-args/quick-start) # Quick Start [#quick-start] ## Basic Parsing [#basic-parsing] ```typescript import { parseArgs } from "@visulima/command-line-args"; const options = parseArgs([ { name: "file", alias: "f", type: String }, { name: "verbose", alias: "v", type: Boolean }, ]); // node script.js --file input.txt -v // { file: "input.txt", verbose: true } ``` ## With Types [#with-types] ```typescript const options = parseArgs([ { name: "count", type: Number }, { name: "name", type: String }, { name: "flag", type: Boolean }, ]); // node script.js --count 42 --name "Test" --flag // { count: 42, name: "Test", flag: true } ``` ## Multiple Values [#multiple-values] ```typescript const options = parseArgs([{ name: "include", type: String, multiple: true }]); // node script.js --include src --include lib // { include: ["src", "lib"] } ``` ## Default Values [#default-values] ```typescript const options = parseArgs([{ name: "port", type: Number, defaultValue: 3000 }]); // node script.js // { port: 3000 } ``` # Usage Guide (/docs/packages/command-line-args/usage) # Usage Guide [#usage-guide] ## Option Definitions [#option-definitions] ```typescript import { parseArgs } from "@visulima/command-line-args"; const definitions = [ { name: "file", // Long option: --file alias: "f", // Short option: -f type: String, // Type conversion defaultValue: "in.txt", // Default value multiple: false, // Single value (default) }, ]; const options = parseArgs(definitions); ``` ## Type Conversion [#type-conversion] **String:** ```typescript { name: "file", type: String } // --file input.txt → { file: "input.txt" } ``` **Number:** ```typescript { name: "count", type: Number } // --count 42 → { count: 42 } ``` **Boolean:** ```typescript { name: "verbose", type: Boolean } // --verbose → { verbose: true } // (no flag) → { verbose: false } ``` ## Multiple Values [#multiple-values] ```typescript const options = parseArgs([{ name: "include", type: String, multiple: true }]); // node script.js --include src --include lib --include test // { include: ["src", "lib", "test"] } ``` ## Default Option (Positional) [#default-option-positional] ```typescript const options = parseArgs([{ name: "files", type: String, multiple: true, defaultOption: true }]); // node script.js file1.txt file2.txt file3.txt // { files: ["file1.txt", "file2.txt", "file3.txt"] } ``` ## Partial Parsing [#partial-parsing] ```typescript const options = parseArgs(definitions, { partial: true }); // Unknown options are preserved in _unknown array ``` ## Stop Early [#stop-early] ```typescript const options = parseArgs(definitions, { stopAtFirstUnknown: true }); // Stops parsing at first unknown option ``` ## Camel Case [#camel-case] ```typescript const options = parseArgs(definitions, { camelCase: true }); // --my-option becomes myOption ``` ## Complete Example [#complete-example] ```typescript import { parseArgs } from "@visulima/command-line-args"; const options = parseArgs([ { name: "input", alias: "i", type: String, defaultValue: "stdin" }, { name: "output", alias: "o", type: String, defaultValue: "stdout" }, { name: "verbose", alias: "v", type: Boolean }, { name: "port", alias: "p", type: Number, defaultValue: 3000 }, { name: "include", type: String, multiple: true }, { name: "files", type: String, multiple: true, defaultOption: true }, ]); // node script.js -i data.json -o result.json -v -p 8080 --include lib --include src file1.js file2.js // Result: // { // input: "data.json", // output: "result.json", // verbose: true, // port: 8080, // include: ["lib", "src"], // files: ["file1.js", "file2.js"] // } ``` # API Reference (/docs/packages/content-safety/api) # API Reference [#api-reference] Complete TypeScript API documentation for @visulima/content-safety. ## Functions [#functions] ### checkBannedWords() [#checkbannedwords] Checks text for banned words across all configured languages. ```typescript function checkBannedWords(text: string): BannedWordsResult; ``` #### Parameters [#parameters] * **text** (`string`) - The text to check for banned words. Can be any length, including empty strings. #### Returns [#returns] `BannedWordsResult` - Object containing: * `hasBannedWords` (`boolean`) - Whether any banned words were found * `matches` (`BannedWordMatch[]`) - Array of all matches with position information #### Behavior [#behavior] * **Case-insensitive**: Matches regardless of capitalization * **Unicode-normalized**: Converts text to NFC form before checking * **Multi-language**: Checks all 19 language dictionaries simultaneously * **Word boundaries**: Respects word boundaries (except CJK scripts) * **Empty input**: Returns empty result for empty or whitespace-only strings * **Performance**: Uses pre-compiled regex (no first-call penalty) #### Examples [#examples] ##### Basic Usage [#basic-usage] ```typescript import { checkBannedWords } from "@visulima/content-safety"; const result = checkBannedWords("Hello world"); console.log(result); // { // hasBannedWords: false, // matches: [] // } ``` ##### With Banned Words [#with-banned-words] ```typescript const result = checkBannedWords("This contains badword"); console.log(result); // { // hasBannedWords: true, // matches: [ // { // word: "badword", // startIndex: 14, // endIndex: 21, // language: "en" // } // ] // } ``` ##### Censoring Text [#censoring-text] ```typescript const text = "This contains badword"; const result = checkBannedWords(text); if (result.hasBannedWords) { let censored = text; for (const match of [...result.matches].reverse()) { const replacement = "*".repeat(match.word.length); censored = censored.slice(0, match.startIndex) + replacement + censored.slice(match.endIndex); } console.log(censored); // "This contains *******" } ``` ##### Multi-Language Detection [#multi-language-detection] ```typescript const result = checkBannedWords(` English bad word German bad word 日本語 bad word `); result.matches.forEach((match) => { console.log(`${match.word} (${match.language})`); }); ``` ##### Case Insensitivity [#case-insensitivity] ```typescript checkBannedWords("BADWORD"); // detected checkBannedWords("badword"); // detected checkBannedWords("BaDwOrD"); // detected ``` ## Types [#types] ### BannedWordsResult [#bannedwordsresult] The result of checking text for banned words. ```typescript interface BannedWordsResult { hasBannedWords: boolean; matches: BannedWordMatch[]; } ``` #### Properties [#properties] * **hasBannedWords** (`boolean`) * `true` if one or more banned words were found * `false` otherwise * Convenience property to avoid checking `matches.length > 0` * **matches** (`BannedWordMatch[]`) * Array of all matched banned words * Empty array `[]` when no matches found * Sorted by appearance order in text #### Example [#example] ```typescript const result: BannedWordsResult = { hasBannedWords: true, matches: [ { word: "badword", startIndex: 0, endIndex: 7, language: "en", }, ], }; ``` *** ### BannedWordMatch [#bannedwordmatch] Represents a single banned word match found in text. ```typescript interface BannedWordMatch { word: string; startIndex: number; endIndex: number; language: string; } ``` #### Properties [#properties-1] * **word** (`string`) * The matched word or phrase exactly as it appears in the text * Preserves original capitalization from input text * Example: `"BADWORD"`, `"badword"`, `"BaDwOrD"` * **startIndex** (`number`) * Zero-based start index in the original text * Inclusive (points to first character of match) * Example: For `"hello badword world"`, startIndex = 6 * **endIndex** (`number`) * Zero-based end index in the original text * Exclusive (points to character after match) * Example: For `"hello badword world"`, endIndex = 13 * Use for slicing: `text.slice(startIndex, endIndex)` * **language** (`string`) * ISO 639-1 language code (2 letters) * Indicates which dictionary the word was matched from * Examples: `"en"`, `"de"`, `"ja"` * If a word appears in multiple languages, first one alphabetically is used #### Example [#example-1] ```typescript const match: BannedWordMatch = { word: "badword", startIndex: 10, endIndex: 17, language: "en", }; // Extract matched text const text = "This is a badword in text"; const extracted = text.slice(match.startIndex, match.endIndex); console.log(extracted); // "badword" ``` ## Constants [#constants] ### BANNED\_WORDS [#banned_words] Comprehensive banned words dictionary organized by language code. ```typescript const BANNED_WORDS: Record; ``` #### Structure [#structure] Object where: * **Keys**: ISO 639-1 language codes (`string`) * **Values**: Read-only arrays of banned words (`readonly string[]`) #### Supported Languages [#supported-languages] * `ar` - Arabic * `az` - Azerbaijani * `de` - German * `en` - English * `es` - Spanish * `fa` - Persian/Farsi * `fr` - French * `ga` - Irish * `hi` - Hindi * `it` - Italian * `ja` - Japanese * `ko` - Korean * `nl` - Dutch * `pl` - Polish * `pt` - Portuguese * `ru` - Russian * `sv` - Swedish * `tr` - Turkish * `zh` - Chinese #### Examples [#examples-1] ##### View Available Languages [#view-available-languages] ```typescript import { BANNED_WORDS } from "@visulima/content-safety"; console.log(Object.keys(BANNED_WORDS)); // ['ar', 'az', 'de', 'en', 'es', 'fa', 'fr', 'ga', 'hi', 'it', 'ja', 'ko', 'nl', 'pl', 'pt', 'ru', 'sv', 'tr', 'zh'] ``` ##### Check Word Count [#check-word-count] ```typescript console.log(BANNED_WORDS.en.length); // Number of English banned words console.log(BANNED_WORDS.ja.length); // Number of Japanese banned words ``` ##### Check if Word is Banned [#check-if-word-is-banned] ```typescript const word = "badword"; const isBanned = Object.values(BANNED_WORDS) .flat() .some((w) => w.toLowerCase() === word.toLowerCase()); console.log(isBanned); // true or false ``` ##### Custom Filtering [#custom-filtering] ```typescript // Get all English words starting with 'a' const enWordsStartingWithA = BANNED_WORDS.en.filter((w) => w.startsWith("a")); // Count total words across all languages const totalWords = Object.values(BANNED_WORDS).reduce((sum, words) => sum + words.length, 0); ``` #### Notes [#notes] * **Read-only**: Arrays are marked as `readonly` to prevent modification * **Lowercase**: Most words are stored in lowercase * **Multi-word phrases**: Some entries contain spaces (e.g., "white trash") * **Variants**: Includes leet-speak and common variations * **Not customizable at runtime**: Word lists are compiled at build time ## Usage Patterns [#usage-patterns] ### Pattern: Simple Validation [#pattern-simple-validation] ```typescript import { checkBannedWords } from "@visulima/content-safety"; function isClean(text: string): boolean { return !checkBannedWords(text).hasBannedWords; } ``` ### Pattern: Detailed Validation [#pattern-detailed-validation] ```typescript import { checkBannedWords, type BannedWordsResult } from "@visulima/content-safety"; interface ValidationResult { valid: boolean; errors: string[]; bannedWords?: number; } function validateContent(text: string): ValidationResult { const result = checkBannedWords(text); if (!result.hasBannedWords) { return { valid: true, errors: [] }; } return { valid: false, errors: ["Content contains inappropriate language"], bannedWords: result.matches.length, }; } ``` ### Pattern: Text Censoring [#pattern-text-censoring] ```typescript import { checkBannedWords } from "@visulima/content-safety"; function censorText(text: string): string { const result = checkBannedWords(text); if (!result.hasBannedWords) { return text; } let censored = text; for (const match of [...result.matches].reverse()) { const replacement = "*".repeat(match.word.length); censored = censored.slice(0, match.startIndex) + replacement + censored.slice(match.endIndex); } return censored; } ``` ### Pattern: Highlighting [#pattern-highlighting] ```typescript import { checkBannedWords, type BannedWordMatch } from "@visulima/content-safety"; interface TextSegment { text: string; banned: boolean; language?: string; } function segmentText(text: string): TextSegment[] { const result = checkBannedWords(text); if (!result.hasBannedWords) { return [{ text, banned: false }]; } const segments: TextSegment[] = []; let lastIndex = 0; for (const match of result.matches) { // Clean text before match if (match.startIndex > lastIndex) { segments.push({ text: text.slice(lastIndex, match.startIndex), banned: false, }); } // Banned word segments.push({ text: match.word, banned: true, language: match.language, }); lastIndex = match.endIndex; } // Remaining text if (lastIndex < text.length) { segments.push({ text: text.slice(lastIndex), banned: false, }); } return segments; } ``` ### Pattern: Analytics [#pattern-analytics] ```typescript import { checkBannedWords } from "@visulima/content-safety"; interface ContentAnalysis { text: string; clean: boolean; bannedWordCount: number; languagesDetected: string[]; severity: "none" | "low" | "medium" | "high"; } function analyzeContent(text: string): ContentAnalysis { const result = checkBannedWords(text); const languages = [...new Set(result.matches.map((m) => m.language))]; const count = result.matches.length; let severity: "none" | "low" | "medium" | "high" = "none"; if (count > 0) severity = "low"; if (count > 3) severity = "medium"; if (count > 10) severity = "high"; return { text, clean: !result.hasBannedWords, bannedWordCount: count, languagesDetected: languages, severity, }; } ``` ## TypeScript Support [#typescript-support] The library is fully typed with TypeScript: ```typescript import type { BannedWordMatch, BannedWordsResult } from "@visulima/content-safety"; // Type-safe function signatures function handleResult(result: BannedWordsResult): void { // TypeScript knows the exact shape result.hasBannedWords; // boolean result.matches; // BannedWordMatch[] } // Type-safe match handling function handleMatch(match: BannedWordMatch): void { match.word; // string match.startIndex; // number match.endIndex; // number match.language; // string } ``` ## Browser and Runtime Support [#browser-and-runtime-support] The library works in all modern JavaScript environments: * Node.js 18+ * Browsers (ES2020+) * Deno * Bun * Edge Functions (Vercel, Cloudflare Workers) * React Native * Electron No polyfills required. ## Next Steps [#next-steps] * [Usage Guide](/docs/packages/content-safety/usage) - Detailed usage examples * [Supported Languages](/docs/packages/content-safety/languages) - View language coverage * [Installation](/docs/packages/content-safety/installation) - Setup instructions * [GitHub Repository](https://github.com/visulima/visulima) - Source code and issues # Getting Started (/docs/packages/content-safety) # Getting Started [#getting-started] @visulima/content-safety is a robust TypeScript library for detecting banned words, profanity, slurs, hate speech, and inappropriate content across multiple languages. It provides accurate, Unicode-aware text analysis with position information for censoring or highlighting. ## Key Features [#key-features] ### Multi-Language Support [#multi-language-support] Comprehensive banned word detection across **19 languages**: * **European**: English, German, Spanish, French, Italian, Dutch, Polish, Portuguese, Swedish, Russian, Irish * **Middle Eastern**: Arabic, Persian/Farsi, Turkish, Azerbaijani * **Asian**: Japanese, Korean, Chinese, Hindi ### Unicode-Aware [#unicode-aware] Full support for international scripts and complex text: * **CJK Scripts**: Japanese, Korean, Chinese (no word boundaries required) * **RTL Scripts**: Arabic, Persian, Hebrew * **Complex Scripts**: Devanagari (Hindi), Thai, and others * **Unicode Normalization**: Consistent matching with NFC normalization ### High Performance [#high-performance] Optimized for production use: * **Eager Regex Compilation**: Cache built at module load (no first-call lag) * **Single Pass Checking**: All languages checked simultaneously * **Efficient Pattern Matching**: Longest phrases matched first ### Precise Position Tracking [#precise-position-tracking] Detailed match information for flexible handling: * Start and end indices for each match * Language attribution (which dictionary matched) * Support for highlighting, censoring, or rejection ### Advanced Features [#advanced-features] * **Case-Insensitive**: Matches regardless of capitalization * **Multi-Word Phrases**: Detects compound expressions (e.g., "white trash") * **Leet-Speak Detection**: Includes common variants (e.g., "b1tch") * **Word Boundary Aware**: Prevents false positives in compound words * **Zero Dependencies**: Pure TypeScript, works in browser and Node.js ## Quick Start [#quick-start] ### Installation [#installation] ```bash npm install @visulima/content-safety ``` ```bash yarn add @visulima/content-safety ``` ```bash pnpm add @visulima/content-safety ``` ### Basic Usage [#basic-usage] ```typescript import { checkBannedWords } from "@visulima/content-safety"; // Check clean text const result = checkBannedWords("Hello, how are you today?"); console.log(result.hasBannedWords); // false console.log(result.matches); // [] // Check text with banned words const result2 = checkBannedWords("This contains badword"); console.log(result2.hasBannedWords); // true console.log(result2.matches[0]); // { // word: "badword", // startIndex: 14, // endIndex: 21, // language: "en" // } ``` ## Use Cases [#use-cases] ### Content Moderation [#content-moderation] Filter user-generated content in forums, comments, and chat applications: ```typescript import { checkBannedWords } from "@visulima/content-safety"; function moderateContent(userInput: string) { const result = checkBannedWords(userInput); if (result.hasBannedWords) { return { allowed: false, reason: `Content contains ${result.matches.length} inappropriate word(s)`, }; } return { allowed: true }; } ``` ### Text Censoring [#text-censoring] Automatically censor inappropriate content: ```typescript function censorText(text: string): string { const result = checkBannedWords(text); if (!result.hasBannedWords) { return text; } let censored = text; // Process in reverse to maintain indices for (const match of result.matches.reverse()) { const replacement = "*".repeat(match.word.length); censored = censored.slice(0, match.startIndex) + replacement + censored.slice(match.endIndex); } return censored; } ``` ### Real-Time Validation [#real-time-validation] Provide instant feedback in forms and text editors: ```typescript function validateInput(text: string) { const result = checkBannedWords(text); return { isValid: !result.hasBannedWords, matches: result.matches.map((m) => ({ position: m.startIndex, length: m.word.length, language: m.language, })), }; } ``` ## Next Steps [#next-steps] Detailed installation and setup instructions Learn how to use the library with examples View all supported languages and word lists Complete API documentation ## Browser and Server Support [#browser-and-server-support] Works in all modern environments: * Node.js 18+ * Browsers (Chrome, Firefox, Safari, Edge) * Deno * Bun * Edge Functions (Vercel, Cloudflare Workers) * React Native * Electron # Installation (/docs/packages/content-safety/installation) # Installation [#installation] ## Package Managers [#package-managers] Install @visulima/content-safety using your preferred package manager: ### npm [#npm] ```bash npm install @visulima/content-safety ``` ### yarn [#yarn] ```bash yarn add @visulima/content-safety ``` ### pnpm [#pnpm] ```bash pnpm add @visulima/content-safety ``` ### bun [#bun] ```bash bun add @visulima/content-safety ``` ## Requirements [#requirements] ### Runtime Support [#runtime-support] @visulima/content-safety works in all modern JavaScript environments: * **Node.js**: 18.x or higher * **Browsers**: All modern browsers with ES2020+ support * Chrome 80+ * Firefox 72+ * Safari 14+ * Edge 80+ * **Deno**: 1.x or higher * **Bun**: 1.x or higher ### TypeScript Support [#typescript-support] The library is written in TypeScript and includes full type definitions: * **TypeScript**: 5.0 or higher (recommended) * Types are included in the package (no separate @types package needed) ## Import Methods [#import-methods] ### ES Modules (Recommended) [#es-modules-recommended] ```typescript import { checkBannedWords, BANNED_WORDS } from "@visulima/content-safety"; import type { BannedWordMatch, BannedWordsResult } from "@visulima/content-safety"; ``` ### CommonJS [#commonjs] ```javascript const { checkBannedWords, BANNED_WORDS } = require("@visulima/content-safety"); ``` ### Named Imports [#named-imports] Import only what you need for optimal tree-shaking: ```typescript // Import just the checker function import { checkBannedWords } from "@visulima/content-safety"; // Import just the word list import { BANNED_WORDS } from "@visulima/content-safety"; // Import types import type { BannedWordMatch, BannedWordsResult } from "@visulima/content-safety"; ``` ## Verification [#verification] Verify your installation by running a simple check: ```typescript import { checkBannedWords } from "@visulima/content-safety"; const result = checkBannedWords("Hello world"); console.log(result); // { hasBannedWords: false, matches: [] } ``` ## Build Tools [#build-tools] ### Webpack [#webpack] Works out of the box with Webpack 5: ```javascript // No special configuration needed import { checkBannedWords } from "@visulima/content-safety"; ``` ### Vite [#vite] Works seamlessly with Vite: ```typescript import { checkBannedWords } from "@visulima/content-safety"; ``` ### Rollup [#rollup] Compatible with Rollup: ```javascript import { checkBannedWords } from "@visulima/content-safety"; ``` ### esbuild [#esbuild] Works with esbuild: ```typescript import { checkBannedWords } from "@visulima/content-safety"; ``` ## Framework Integration [#framework-integration] ### React [#react] ```tsx import { useState } from "react"; import { checkBannedWords } from "@visulima/content-safety"; function CommentForm() { const [text, setText] = useState(""); const [error, setError] = useState(""); const handleChange = (e: React.ChangeEvent) => { const value = e.target.value; setText(value); const result = checkBannedWords(value); if (result.hasBannedWords) { setError("Content contains inappropriate language"); } else { setError(""); } }; return (