diff --git a/.claude/settings.local.json b/.claude/settings.local.json deleted file mode 100644 index 6041c46..0000000 --- a/.claude/settings.local.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "permissions": { - "allow": [ - "Bash(fish_indent:*)", - "Bash(find:*)", - "Bash(make lint-fish:*)", - "Bash(make lint-json:*)", - "Bash(make lint-markdown:*)", - "Bash(make lint-fix:*)", - "Bash(make lint:*)" - ], - "deny": [] - } -} \ No newline at end of file diff --git a/.editorconfig b/.editorconfig index 864816a..b78b9cf 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,3 +9,20 @@ trim_trailing_whitespace = true [*.fish] indent_size = 4 indent_style = space + +[Makefile] +indent_style = tab +max_line_length = 80 + +[*.sh] +max_line_length = 120 +indent_size = 2 +indent_style = space + +[*.yml] +indent_size = 2 +indent_style = space + +[*.json] +indent_size = 2 +indent_style = space diff --git a/.github/install_editorconfig-checker.sh b/.github/install_editorconfig-checker.sh new file mode 100755 index 0000000..6935f4b --- /dev/null +++ b/.github/install_editorconfig-checker.sh @@ -0,0 +1,50 @@ +#!/bin/bash +set -e + +echo "Downloading editorconfig-checker..." + +BASE_URL="https://github.com/editorconfig-checker/editorconfig-checker/releases/latest/download" + +# Detect OS and architecture +OS="$(uname -s)" +ARCH="$(uname -m)" +case "$OS" in +Darwin) + [ "$ARCH" = "arm64" ] && ARCH="arm64" || ARCH="amd64" + URL="$BASE_URL/ec-darwin-${ARCH}.tar.gz" + ;; +Linux) + [ "$ARCH" = "aarch64" ] && ARCH="arm64" || ARCH="amd64" + URL="$BASE_URL/ec-linux-${ARCH}.tar.gz" + ;; +*) + echo "Unsupported OS: $OS" + exit 1 + ;; +esac + +TMPDIR="$(mktemp -d)" +trap 'rm -rf "$TMPDIR"' EXIT + +curl -L "$URL" | tar -xz -C "$TMPDIR" + +# Choose install directory +INSTALL_DIR="${XDG_BIN_HOME:-$HOME/bin}" +[ -d "$INSTALL_DIR" ] || INSTALL_DIR="/usr/local/bin" + +echo "Installing to $INSTALL_DIR..." + +mkdir -p "$INSTALL_DIR" 2>/dev/null || true + +if mv "$TMPDIR/bin/ec" "$INSTALL_DIR/editorconfig-checker" 2>/dev/null; then + echo "โœ“ Installed editorconfig-checker to $INSTALL_DIR" +elif sudo mv "$TMPDIR/bin/ec" "$INSTALL_DIR/editorconfig-checker" 2>/dev/null; then + echo "โœ“ Installed editorconfig-checker to $INSTALL_DIR (with sudo)" +else + echo "Could not install to $INSTALL_DIR, using local copy" + mkdir -p bin + mv "$TMPDIR/bin/ec" bin/editorconfig-checker + echo "Add $(pwd)/bin to your PATH to use editorconfig-checker" +fi + +echo "Installation complete!" diff --git a/.github/workflows/pr-lint.yml b/.github/workflows/pr-lint.yml index 2167179..19c5561 100644 --- a/.github/workflows/pr-lint.yml +++ b/.github/workflows/pr-lint.yml @@ -8,11 +8,12 @@ on: pull_request: branches: [master, main] -permissions: - contents: read - packages: read - statuses: write - jobs: - SuperLinter: - uses: ivuorinen/actions/pr-lint@625c37446b1c7e219755a40807f825c9283f6e05 # 25.7.7 + Linter: + runs-on: ubuntu-latest + permissions: + contents: read + packages: read + statuses: write + steps: + - uses: ivuorinen/actions/pr-lint@625c37446b1c7e219755a40807f825c9283f6e05 # 25.7.7 diff --git a/.gitignore b/.gitignore index 0e111a0..3f3f9a0 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ *~ Session.vim Sessionx.vim +.claude/settings.local.json diff --git a/.markdownlint.json b/.markdownlint.json index 2b178f5..54d4c74 100644 --- a/.markdownlint.json +++ b/.markdownlint.json @@ -4,4 +4,4 @@ "MD013": { "line_length": 120 }, "MD033": false, "MD041": false -} \ No newline at end of file +} diff --git a/CLAUDE.md b/CLAUDE.md index 0a8eaa7..df7cd7e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -19,22 +19,34 @@ The plugin consists of two main Fish functions: ### Linting and Code Quality +The project uses a comprehensive linting setup with automatic tool installation: + ```bash -# Install all linting tools (markdownlint-cli, jsonlint, jq) +# Install all linting tools (markdownlint-cli, jsonlint, jq, editorconfig-checker) make install-tools -# Run all linting checks +# Run all linting checks (Fish, Markdown, JSON, EditorConfig) make lint # Fix auto-fixable linting issues make lint-fix # Individual linting commands -make lint-fish # Lint Fish shell files -make lint-markdown # Lint Markdown files -make lint-json # Lint JSON files +make lint-fish # Lint Fish shell files (formatting + syntax) +make lint-markdown # Lint Markdown files (style, headers, lists) +make lint-json # Lint JSON files (syntax validation) +make lint-editorconfig # Check EditorConfig compliance (line endings, indentation) ``` +#### Supported Linting Tools + +- **Fish shell**: `fish_indent` for formatting, `fish -n` for syntax validation +- **Markdown**: `markdownlint-cli` with custom configuration (`.markdownlint.json`) +- **JSON**: `jsonlint` or `jq` for syntax validation +- **EditorConfig**: `editorconfig-checker` (auto-installed if missing) + +The linting system automatically downloads missing tools and follows XDG standards for installation. + #### Manual Fish Commands ```fish @@ -48,14 +60,19 @@ find . -name "*.fish" -exec fish_indent --check {} \; fish -n functions/*.fish completions/*.fish ``` -### Installation/Testing +### Testing ```bash -# Test plugin installation using Makefile -make test +# Run all tests (unit + integration) +tests/test_runner.fish -# Test plugin installation in CI environment -make test-ci +# Run specific test types +make test-unit # Unit tests only +make test-integration # Integration tests only + +# Test plugin installation +make test # Local installation test +make test-ci # CI environment test ``` #### Manual Installation Commands @@ -86,6 +103,38 @@ nvm_auto_use_config debounce 1000 nvm_auto_use_config exclude "build" ``` +### Developer Tools + +```fish +# Security and validation +nvm_security check_version "18.17.0" # Validate version format and policies +nvm_security audit # Comprehensive security audit +nvm_security policy set min_version "16.0.0" # Set security policies + +# Smart recommendations +nvm_recommendations suggest_version new_project # Get version recommendations +nvm_recommendations upgrade_path # Plan upgrade strategy +nvm_recommendations security_update # Security-focused updates + +# Diagnostics and debugging +nvm_doctor check # Comprehensive health check +nvm_doctor system # System information +nvm_doctor managers # Check version managers +nvm_doctor fix all # Auto-fix common issues + +# Cache management +nvm_cache stats # Cache statistics +nvm_cache clear # Clear all cache +nvm_cache get "key" # Get cached value + +# Async operations +nvm_async version_check "file" # Non-blocking version check +nvm_async cleanup # Clean up background jobs + +# Error recovery +nvm_error_recovery manager_failure "nvm" "18.0.0" # Handle manager failures +``` + ### Testing the Functions ```fish @@ -105,18 +154,59 @@ nvm_version_status ## Key Implementation Details +### Core Architecture + - The plugin hooks into Fish's variable change system using `--on-variable PWD` - Supports multiple Node.js version managers: nvm, fnm, volta, asdf - Supports multiple file formats: `.nvmrc`, `.node-version`, `.tool-versions`, `package.json` engines.node -- Includes performance optimizations: caching, debouncing, directory exclusions -- Configurable features: silent mode, auto-install toggle, notifications, project-only mode -- Error handling includes checking if Node.js is available and graceful fallback when versions can't be switched - The search for version files traverses up the directory tree until it finds one or reaches the root directory +### Performance Features + +- **XDG-compliant caching** with configurable TTL for version lookups and manager availability +- **Async operations** for non-blocking version checks using Fish background jobs +- **Debouncing** to prevent rapid version switching during directory navigation +- **Smart directory exclusions** to skip unnecessary processing + +### Security & Reliability + +- **Version validation** with format checking and policy enforcement +- **Security vulnerability scanning** with CVE checking (online and offline) +- **Error recovery mechanisms** with graceful degradation and fallback strategies +- **Input sanitization** to prevent injection attacks through version files + +### Advanced Capabilities + +- **Smart recommendations** for version selection, upgrades, and security updates +- **Comprehensive diagnostics** with the `nvm_doctor` command for troubleshooting +- **Extensive testing suite** with unit and integration tests +- **Configuration management** with persistent settings and policy enforcement + ## Code Quality Standards +### Fish Shell Code + - All Fish code must be formatted with `fish_indent` before committing - Functions should include description flags (`-d "description"`) - Use proper Fish conventions for variable scoping (`set -l`, `set -g`, `set -gx`) - Include comprehensive error handling and input validation - Follow Fish best practices for command substitution and string handling + +### General Standards + +- **Makefile**: 80-character line limit, tab indentation +- **Markdown**: 120-character line limit, consistent heading structure +- **JSON**: Valid syntax, proper formatting +- **EditorConfig**: Consistent line endings (LF), final newlines, no trailing whitespace + +### CI/CD Integration + +- GitHub Actions automatically runs all linting checks on push/PR +- All linting must pass before merging +- Use `make test-ci` for testing plugin installation in CI environments + +### Tool Installation + +- Missing linting tools are automatically installed during `make install-tools` +- Installation respects XDG standards: `$XDG_BIN_HOME` โ†’ `$HOME/bin` โ†’ `/usr/local/bin` +- Uses secure temporary directories (`mktemp`) for downloads diff --git a/Makefile b/Makefile index c16869b..c9c6254 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,12 @@ # Makefile for nvm-auto-use.fish -.PHONY: help install-tools lint lint-fish lint-markdown lint-json lint-fix lint-check test clean +# URLs and constants +FISHER_BASE := https://raw.githubusercontent.com/jorgebucaran/fisher/main +FISHER_URL := $(FISHER_BASE)/functions/fisher.fish + +.PHONY: help install-tools lint lint-fish lint-markdown lint-json \ + lint-fix lint-check lint-editorconfig test test-ci test-unit \ + test-integration clean # Default target help: @@ -12,28 +18,28 @@ help: @echo " lint-json - Lint JSON files" @echo " lint-fix - Fix auto-fixable linting issues" @echo " lint-check - Check linting without fixing" + @echo " lint-editorconfig - Check EditorConfig compliance" @echo " test - Run tests (install plugin locally)" @echo " test-ci - Run tests in CI environment" + @echo " test-unit - Run unit tests only" + @echo " test-integration - Run integration tests only" @echo " clean - Clean temporary files" # Install all required linting tools install-tools: @echo "Installing linting tools..." - # Install markdownlint-cli for markdown linting @if ! command -v markdownlint >/dev/null 2>&1; then \ echo "Installing markdownlint-cli..."; \ npm install -g markdownlint-cli; \ else \ echo "markdownlint-cli already installed"; \ fi - # Install jsonlint for JSON linting @if ! command -v jsonlint >/dev/null 2>&1; then \ echo "Installing jsonlint..."; \ npm install -g jsonlint; \ else \ echo "jsonlint already installed"; \ fi - # Install jq for JSON processing (backup) @if ! command -v jq >/dev/null 2>&1; then \ echo "Installing jq..."; \ if command -v brew >/dev/null 2>&1; then \ @@ -51,18 +57,19 @@ install-tools: @echo "All linting tools installed!" # Run all linting checks -lint: lint-fish lint-markdown lint-json +lint: lint-fish lint-markdown lint-json lint-editorconfig # Lint Fish shell files lint-fish: @echo "Linting Fish files..." - @find . -name "*.fish" -type f | while read -r file; do \ - echo "Checking $$file..."; \ - fish_indent --check "$$file" || { \ - echo "Formatting issues found in $$file"; \ - exit 1; \ - }; \ - done + @find . \ + -name "*.fish" \ + -type f \ + -exec sh -c \ + 'echo "Checking $$1..."; \ + fish_indent --check "$$1" || { \ + echo "Formatting issues found in $$1"; exit 1; }' \ + sh {} \; @echo "Validating Fish syntax..." @fish -n functions/*.fish completions/*.fish 2>/dev/null || { \ echo "Syntax errors found in Fish files"; \ @@ -86,24 +93,36 @@ lint-markdown: # Lint JSON files lint-json: @echo "Linting JSON files..." - @find . -name "*.json" -type f | while read -r file; do \ - echo "Checking $$file..."; \ + @find . \ + -name "*.json" \ + -type f \ + -exec sh -c \ + 'file="$$1"; echo "Checking $$file..."; \ if command -v jsonlint >/dev/null 2>&1; then \ jsonlint "$$file" >/dev/null || { \ - echo "JSON syntax error in $$file"; \ - exit 1; \ - }; \ + echo "JSON syntax error in $$file"; exit 1; }; \ elif command -v jq >/dev/null 2>&1; then \ jq empty "$$file" >/dev/null || { \ - echo "JSON syntax error in $$file"; \ - exit 1; \ - }; \ + echo "JSON syntax error in $$file"; exit 1; }; \ else \ - echo "No JSON linter found, skipping $$file"; \ - fi; \ - done + echo "No JSON linter found, skipping $$file"; fi' \ + sh {} \; @echo "JSON files passed linting!" +# Check EditorConfig compliance +lint-editorconfig: + @echo "Checking EditorConfig compliance..." + @if command -v editorconfig-checker >/dev/null 2>&1; then \ + editorconfig-checker; \ + elif command -v ec >/dev/null 2>&1; then \ + ec; \ + else \ + echo "Installing editorconfig-checker..."; \ + .github/install_editorconfig-checker.sh; \ + editorconfig-checker; \ + fi + @echo "EditorConfig compliance passed!" + # Fix auto-fixable linting issues lint-fix: @echo "Fixing linting issues..." @@ -134,7 +153,7 @@ test: test-ci: @echo "Testing plugin installation in CI..." @fish -c "\ - curl -sL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish | source; \ + curl -sL $(FISHER_URL) | source; \ fisher install jorgebucaran/fisher; \ if fisher list | string match -q '*ivuorinen/nvm-auto-use.fish*'; \ echo 'Plugin already installed, skipping installation'; \ @@ -143,9 +162,30 @@ test-ci: end; \ echo 'Plugin test completed successfully in CI!'" +# Run unit tests +test-unit: + @echo "Running unit tests..." + @chmod +x tests/test_runner.fish + @for test in tests/unit/*.fish; do \ + echo "Running $$test..."; \ + fish "$$test" || exit 1; \ + done + @echo "Unit tests completed!" + +# Run integration tests +test-integration: + @echo "Running integration tests..." + @chmod +x tests/test_runner.fish + @for test in tests/integration/*.fish; do \ + echo "Running $$test..."; \ + fish "$$test" || exit 1; \ + done + @echo "Integration tests completed!" + # Clean temporary files clean: @echo "Cleaning temporary files..." @find . -name "*.tmp" -type f -delete 2>/dev/null || true @find . -name ".DS_Store" -type f -delete 2>/dev/null || true - @echo "Cleanup complete!" \ No newline at end of file + @nvm_cache clear 2>/dev/null || true + @echo "Cleanup complete!" diff --git a/README.md b/README.md index dc061c2..b598dd2 100644 --- a/README.md +++ b/README.md @@ -42,12 +42,16 @@ fisher install ivuorinen/nvm-auto-use.fish - Manager preference selection - Project-only activation mode -### ๐Ÿ”” **Developer Tools** +### ๐Ÿ”” **Advanced Developer Tools** +- **Security features**: Version validation, CVE checking, policy enforcement +- **Smart recommendations**: Intelligent version suggestions and upgrade paths +- **Comprehensive diagnostics**: `nvm_doctor` for troubleshooting and health checks +- **Performance optimization**: XDG-compliant caching with TTL, async operations +- **Error recovery**: Graceful degradation and fallback mechanisms - Desktop notifications for version switches - Environment variable export (`NODE_VERSION`) -- Fish shell completions -- Detailed status reporting +- Fish shell completions and detailed status reporting ## Quick Start @@ -100,6 +104,47 @@ nvm_auto_use_config reset | `.tool-versions` | Tool + version | `nodejs 18.17.0` | | `package.json` | engines.node field | `"engines": {"node": ">=16.0.0"}` | +## Advanced Features + +### Security & Validation + +```fish +# Security audit and validation +nvm_security audit # Comprehensive security check +nvm_security check_version "18.17.0" # Validate version format +nvm_security policy set min_version "16.0.0" # Set minimum version policy +``` + +### Smart Recommendations + +```fish +# Get intelligent recommendations +nvm_recommendations suggest_version new_project # Project-specific suggestions +nvm_recommendations upgrade_path # Plan safe upgrades +nvm_recommendations security_update # Security-focused updates +``` + +### Diagnostics & Troubleshooting + +```fish +# Comprehensive system diagnostics +nvm_doctor check # Full health check +nvm_doctor managers # Check version manager status +nvm_doctor security # Security audit +nvm_doctor fix all # Auto-fix common issues +``` + +### Performance & Caching + +```fish +# Cache management (XDG-compliant) +nvm_cache stats # View cache statistics +nvm_cache clear # Clear all cached data + +# Async operations for better performance +nvm_async version_check "file" # Non-blocking version checks +``` + ## Utility Functions ```fish @@ -125,6 +170,45 @@ nvm_compat_detect - [volta](https://volta.sh/) - [asdf](https://asdf-vm.com/) with nodejs plugin +## Development + +### Contributing + +This project uses comprehensive linting and code quality tools: + +```bash +# Install development tools +make install-tools + +# Run all linting checks +make lint + +# Fix auto-fixable issues +make lint-fix + +# Test plugin installation +make test +``` + +### Code Quality + +- **Fish shell**: Automatic formatting with `fish_indent` +- **Markdown**: Style checking with `markdownlint` +- **JSON**: Syntax validation with `jsonlint`/`jq` +- **EditorConfig**: Compliance checking with `editorconfig-checker` + +All tools are automatically installed if missing, following XDG directory standards. + +### GitHub Actions + +The project includes CI/CD workflows that automatically: + +- Run all linting checks on push/PR +- Test plugin installation in clean environments +- Ensure code quality standards are maintained + +See [CLAUDE.md](CLAUDE.md) for detailed development guidelines. + ## Uninstall ```fish diff --git a/functions/nvm_async.fish b/functions/nvm_async.fish new file mode 100644 index 0000000..dc0075c --- /dev/null +++ b/functions/nvm_async.fish @@ -0,0 +1,106 @@ +function nvm_async -d "Async operations for non-blocking version management" + set -l operation $argv[1] + switch $operation + case version_check + _nvm_async_version_check $argv[2] + case manager_check + _nvm_async_manager_check $argv[2] + case cleanup + _nvm_async_cleanup + case wait + _nvm_async_wait $argv[2] $argv[3] + case '*' + echo "Usage: nvm_async [version_check|manager_check|cleanup|wait] [args...]" + return 1 + end +end + +function _nvm_async_version_check -d "Async version check operation" + set -l version_file $argv[1] + set -l cache_key (_nvm_cache_key "$version_file") + + # Try cache first + if set -l cached_result (nvm_cache get "$cache_key" 60) + echo "$cached_result" + return 0 + end + + # Background job for version extraction + fish -c " + set result (nvm_extract_version '$version_file' 2>/dev/null) + if test -n \"\$result\" + nvm_cache set '$cache_key' \"\$result\" + echo \"\$result\" + end + " & + + # Return job ID for potential cleanup + jobs -l | tail -n 1 | grep -o '[0-9]*' +end + +function _nvm_async_manager_check -d "Async manager availability check" + set -l manager $argv[1] + set -l cache_key (_nvm_cache_manager_key "$manager") + + # Try cache first (longer TTL for manager availability) + if set -l cached_result (nvm_cache get "$cache_key" 3600) + echo "$cached_result" + return 0 + end + + # Background check + fish -c " + if command -q '$manager' + nvm_cache set '$cache_key' 'available' + echo 'available' + else + nvm_cache set '$cache_key' 'unavailable' + echo 'unavailable' + end + " & + + jobs -l | tail -n 1 | grep -o '[0-9]*' +end + +function _nvm_async_cleanup -d "Clean up completed background jobs" + for job in (jobs -p) + if not kill -0 $job 2>/dev/null + jobs -p | grep -v $job + end + end +end + +function _nvm_async_wait -d "Wait for async job with timeout" + set -l job_id $argv[1] + set -l timeout $argv[2] + + if test -z "$timeout" + set timeout 2 + end + + # Wait for job with timeout + set -l count 0 + while test $count -lt (math "$timeout * 10") + if not jobs -p | grep -q "^$job_id\$" + return 0 + end + sleep 0.1 + set count (math "$count + 1") + end + + # Timeout reached, kill job + kill -9 $job_id 2>/dev/null + return 1 +end + +function _nvm_async_safe_read -d "Safely read async operation result" + set -l cache_key $argv[1] + set -l fallback $argv[2] + + set -l result (nvm_cache get "$cache_key") + if test -n "$result" + echo "$result" + else if test -n "$fallback" + echo "$fallback" + end +end diff --git a/functions/nvm_auto_use.fish b/functions/nvm_auto_use.fish index cc61a3f..2f0ad35 100644 --- a/functions/nvm_auto_use.fish +++ b/functions/nvm_auto_use.fish @@ -1,130 +1,155 @@ function nvm_auto_use --on-variable PWD - # Detect available Node.js version manager - set -l available_managers (nvm_compat_detect 2>/dev/null | tail -n 1) - if test -z "$available_managers" + # Select the Node.js version manager + set -l manager (_nvm_auto_use_select_manager) + if test -z "$manager" return end - # Use preferred manager or first available - set -l manager - if test -n "$_nvm_auto_use_preferred_manager" - if contains "$_nvm_auto_use_preferred_manager" $available_managers - set manager "$_nvm_auto_use_preferred_manager" - end + # Debounce rapid directory changes + if _nvm_auto_use_should_debounce + return end - if test -z "$manager" - set manager $available_managers[1] + # Check for excluded directories + if _nvm_auto_use_is_excluded_dir + return end - # Check if project detection is enabled and we're in a Node.js project + # Project-only mode: only activate in Node.js projects if set -q _nvm_auto_use_project_only if not nvm_project_detect return end end - # Export NODE_VERSION environment variable + # Export NODE_VERSION environment variable if available if command -q node set -gx NODE_VERSION (node -v 2>/dev/null | string replace 'v' '') end - # Check for excluded directories - set -l current_dir (pwd) - for pattern in $_nvm_auto_use_excluded_dirs node_modules .git - if string match -q "*/$pattern" "$current_dir"; or string match -q "*/$pattern/*" "$current_dir" - return - end - end - - # Debouncing: prevent rapid switching - set -l debounce_ms (_nvm_auto_use_get_debounce) - set -l current_time (date +%s%3N 2>/dev/null; or date +%s) - if test -n "$_nvm_auto_use_last_change" - set -l time_diff (math "$current_time - $_nvm_auto_use_last_change") - if test $time_diff -lt $debounce_ms - return - end - end - set -g _nvm_auto_use_last_change $current_time - + # Find version file and its mtime set -l nvmrc_file (nvm_find_nvmrc) + set -l nvmrc_mtime (_nvm_auto_use_get_mtime "$nvmrc_file") - # Cache check: if same file and version, skip processing - if test "$nvmrc_file" = "$_nvm_auto_use_cached_file" + # Skip if cache is valid + if _nvm_auto_use_is_cache_valid "$nvmrc_file" "$nvmrc_mtime" return end if test -n "$nvmrc_file" - set -l node_version (nvm_extract_version "$nvmrc_file") - - # Cache the file path - set -g _nvm_auto_use_cached_file "$nvmrc_file" - - # Validate node version format (basic semver or major version) - if not string match -qr '^v?[0-9]+(\..*)?$' "$node_version" - if not set -q _nvm_auto_use_silent - echo "Invalid Node.js version format in $nvmrc_file: $node_version" >&2 - end - return 1 - end - - # Remove 'v' prefix if present - set node_version (string replace -r '^v' '' "$node_version") - - # Cache the version - set -g _nvm_auto_use_cached_version "$node_version" - - # Check the current version - set -l current_version - if command -q node - set current_version (node -v 2>/dev/null | sed 's/v//') - end - - if test "$node_version" != "$current_version" - if not set -q _nvm_auto_use_silent - echo "Switching to Node.js v$node_version" - end - - # Send notification if enabled - if not set -q _nvm_auto_use_silent - nvm_notify "Switched to Node.js v$node_version" - end - - if not nvm_compat_use $manager $node_version 2>/dev/null - # Check if auto-install is disabled - if set -q _nvm_auto_use_no_install - if not set -q _nvm_auto_use_silent - echo "Node.js version $node_version not found (auto-install disabled)" >&2 - end - return 1 - end - - if not set -q _nvm_auto_use_silent - echo "Node.js version $node_version not found, installing..." - end - if nvm_compat_install $manager $node_version >/dev/null 2>&1 - nvm_compat_use $manager $node_version >/dev/null 2>&1 - if not set -q _nvm_auto_use_silent - echo "Installed and switched to Node.js v$node_version" - end - else - if not set -q _nvm_auto_use_silent - echo "Failed to install Node.js version $node_version" >&2 - end - return 1 - end - end - - # Update cached version after successful switch - set -g _nvm_auto_use_cached_version "$node_version" - - # Update NODE_VERSION environment variable - set -gx NODE_VERSION "$node_version" - end + _nvm_auto_use_switch_version "$manager" "$nvmrc_file" "$nvmrc_mtime" else - # Clear cache if no .nvmrc found - set -e _nvm_auto_use_cached_file - set -e _nvm_auto_use_cached_version + _nvm_auto_use_clear_cache end end + +function _nvm_auto_use_select_manager + set -l available_managers (nvm_compat_detect 2>/dev/null) + if test -z "$available_managers" + return + end + if test -n "$_nvm_auto_use_preferred_manager" + if contains "$_nvm_auto_use_preferred_manager" $available_managers + echo "$_nvm_auto_use_preferred_manager" + return + end + end + echo $available_managers[1] +end + +function _nvm_auto_use_should_debounce + set -l debounce_ms (_nvm_auto_use_get_debounce) + set -l current_time (date +%s%3N 2>/dev/null; or math "(date +%s) * 1000") + if test -n "$_nvm_auto_use_last_change" + set -l time_diff (math "$current_time - $_nvm_auto_use_last_change") + if test $time_diff -lt $debounce_ms + return 0 + end + end + set -g _nvm_auto_use_last_change $current_time + return 1 +end + +function _nvm_auto_use_is_excluded_dir + set -l current_dir (pwd) + set -l patterns $_nvm_auto_use_excluded_dirs node_modules .git + for pattern in $patterns + if string match -q "*/$pattern" "$current_dir"; or string match -q "*/$pattern/*" "$current_dir" + return 0 + end + end + return 1 +end + +function _nvm_auto_use_get_mtime + set -l file $argv[1] + if test -n "$file" + stat -c %Y "$file" 2>/dev/null; or stat -f %m "$file" 2>/dev/null + end +end + +function _nvm_auto_use_is_cache_valid + set -l file $argv[1] + set -l mtime $argv[2] + if test "$file" = "$_nvm_auto_use_cached_file"; and test "$mtime" = "$_nvm_auto_use_cached_mtime" + return 0 + end + return 1 +end + +function _nvm_auto_use_switch_version + set -l manager $argv[1] + set -l nvmrc_file $argv[2] + set -l nvmrc_mtime $argv[3] + set -l node_version (nvm_extract_version "$nvmrc_file") + set -g _nvm_auto_use_cached_file "$nvmrc_file" + set -g _nvm_auto_use_cached_mtime "$nvmrc_mtime" + if not string match -qr '^v?[0-9]+(\..*)?$' "$node_version" + if not set -q _nvm_auto_use_silent + echo "Invalid Node.js version format in $nvmrc_file: $node_version" >&2 + end + return 1 + end + set node_version (string replace -r '^v' '' "$node_version") + set -g _nvm_auto_use_cached_version "$node_version" + set -l current_version + if command -q node + set current_version (node -v 2>/dev/null | sed 's/v//') + end + if test "$node_version" != "$current_version" + if not set -q _nvm_auto_use_silent + echo "Switching to Node.js v$node_version" + nvm_notify "Switched to Node.js v$node_version" + end + if not nvm_compat_use $manager $node_version 2>/dev/null + if set -q _nvm_auto_use_no_install + if not set -q _nvm_auto_use_silent + echo "Node.js version $node_version not found (auto-install disabled)" >&2 + end + return 1 + end + if not set -q _nvm_auto_use_silent + echo "Node.js version $node_version not found, installing..." + end + if nvm_compat_install $manager $node_version >/dev/null 2>&1 + nvm_compat_use $manager $node_version >/dev/null 2>&1 + if not set -q _nvm_auto_use_silent + echo "Installed and switched to Node.js v$node_version" + end + else + if not set -q _nvm_auto_use_silent + echo "Failed to install Node.js version $node_version" >&2 + end + return 1 + end + end + set -g _nvm_auto_use_cached_version "$node_version" + set -gx NODE_VERSION "$node_version" + end +end + +function _nvm_auto_use_clear_cache + set -e _nvm_auto_use_cached_file + set -e _nvm_auto_use_cached_version + set -e _nvm_auto_use_cached_mtime +end diff --git a/functions/nvm_auto_use_config.fish b/functions/nvm_auto_use_config.fish index f0f88a2..a8e1f9b 100644 --- a/functions/nvm_auto_use_config.fish +++ b/functions/nvm_auto_use_config.fish @@ -1,88 +1,126 @@ function nvm_auto_use_config -d "Configure nvm-auto-use settings" if test (count $argv) -eq 0 - echo "nvm-auto-use configuration:" - echo " auto_install: "(test -n "$_nvm_auto_use_no_install"; and echo "disabled"; or echo "enabled") - echo " silent: "(test -n "$_nvm_auto_use_silent"; and echo "enabled"; or echo "disabled") - echo " debounce_ms: "(_nvm_auto_use_get_debounce) - echo " excluded_dirs: "(_nvm_auto_use_get_excluded_dirs) - echo " preferred_manager: "(test -n "$_nvm_auto_use_preferred_manager"; and echo "$_nvm_auto_use_preferred_manager"; or echo "auto-detect") + _nvm_auto_use_config_show return end switch $argv[1] case auto_install - switch $argv[2] - case on enable true 1 - set -e _nvm_auto_use_no_install - echo "Auto-install enabled" - case off disable false 0 - set -g _nvm_auto_use_no_install 1 - echo "Auto-install disabled" - case '*' - echo "Usage: nvm_auto_use_config auto_install [on|off]" - end + _nvm_auto_use_config_auto_install $argv[2] case silent - switch $argv[2] - case on enable true 1 - set -g _nvm_auto_use_silent 1 - echo "Silent mode enabled" - case off disable false 0 - set -e _nvm_auto_use_silent - echo "Silent mode disabled" - case '*' - echo "Usage: nvm_auto_use_config silent [on|off]" - end + _nvm_auto_use_config_silent $argv[2] case debounce - if test -n "$argv[2]" -a (string match -r '^[0-9]+$' "$argv[2]") - set -g _nvm_auto_use_debounce_ms $argv[2] - echo "Debounce set to $argv[2]ms" - else - echo "Usage: nvm_auto_use_config debounce " - end + _nvm_auto_use_config_debounce $argv[2] case exclude - if test -n "$argv[2]" - set -g _nvm_auto_use_excluded_dirs $_nvm_auto_use_excluded_dirs $argv[2] - echo "Added $argv[2] to excluded directories" - else - echo "Usage: nvm_auto_use_config exclude " - end + _nvm_auto_use_config_exclude $argv[2] case include - if test -n "$argv[2]" - set -l index (contains -i "$argv[2]" $_nvm_auto_use_excluded_dirs) - if test -n "$index" - set -e _nvm_auto_use_excluded_dirs[$index] - echo "Removed $argv[2] from excluded directories" - else - echo "$argv[2] was not in excluded directories" - end - else - echo "Usage: nvm_auto_use_config include " - end + _nvm_auto_use_config_include $argv[2] case manager - if test -n "$argv[2]" - if contains "$argv[2]" nvm fnm volta asdf - set -g _nvm_auto_use_preferred_manager "$argv[2]" - echo "Preferred manager set to $argv[2]" - else - echo "Unsupported manager. Supported: nvm, fnm, volta, asdf" - end - else - set -e _nvm_auto_use_preferred_manager - echo "Reset to auto-detect manager" - end + _nvm_auto_use_config_manager $argv[2] case reset - set -e _nvm_auto_use_no_install - set -e _nvm_auto_use_silent - set -e _nvm_auto_use_debounce_ms - set -e _nvm_auto_use_excluded_dirs - set -e _nvm_auto_use_preferred_manager - echo "Configuration reset to defaults" + _nvm_auto_use_config_reset case '*' echo "Usage: nvm_auto_use_config [auto_install|silent|debounce|exclude|include|manager|reset] [value]" return 1 end end +function _nvm_auto_use_config_show + echo "nvm-auto-use configuration:" + echo " auto_install: "(test -n "$_nvm_auto_use_no_install"; and echo "disabled"; or echo "enabled") + echo " silent: "(test -n "$_nvm_auto_use_silent"; and echo "enabled"; or echo "disabled") + echo " debounce_ms: "(_nvm_auto_use_get_debounce) + echo " excluded_dirs: "(_nvm_auto_use_get_excluded_dirs) + echo " preferred_manager: "(test -n "$_nvm_auto_use_preferred_manager"; and echo "$_nvm_auto_use_preferred_manager"; or echo "auto-detect") +end + +function _nvm_auto_use_config_auto_install + set -l value $argv[1] + switch $value + case on enable true 1 + set -e _nvm_auto_use_no_install + echo "Auto-install enabled" + case off disable false 0 + set -g _nvm_auto_use_no_install 1 + echo "Auto-install disabled" + case '*' + echo "Usage: nvm_auto_use_config auto_install [on|off]" + end +end + +function _nvm_auto_use_config_silent + set -l value $argv[1] + switch $value + case on enable true 1 + set -g _nvm_auto_use_silent 1 + echo "Silent mode enabled" + case off disable false 0 + set -e _nvm_auto_use_silent + echo "Silent mode disabled" + case '*' + echo "Usage: nvm_auto_use_config silent [on|off]" + end +end + +function _nvm_auto_use_config_debounce + set -l value $argv[1] + if test -n "$value" -a (string match -r '^[0-9]+$' "$value") + set -g _nvm_auto_use_debounce_ms $value + echo "Debounce set to $value ms" + else + echo "Usage: nvm_auto_use_config debounce " + end +end + +function _nvm_auto_use_config_exclude + set -l value $argv[1] + if test -n "$value" + set -g _nvm_auto_use_excluded_dirs $_nvm_auto_use_excluded_dirs $value + echo "Added $value to excluded directories" + else + echo "Usage: nvm_auto_use_config exclude " + end +end + +function _nvm_auto_use_config_include + set -l value $argv[1] + if test -n "$value" + set -l index (contains -i "$value" $_nvm_auto_use_excluded_dirs) + if test -n "$index" + set -e _nvm_auto_use_excluded_dirs[$index] + echo "Removed $value from excluded directories" + else + echo "$value was not in excluded directories" + end + else + echo "Usage: nvm_auto_use_config include " + end +end + +function _nvm_auto_use_config_manager + set -l value $argv[1] + if test -n "$value" + if contains "$value" nvm fnm volta asdf + set -g _nvm_auto_use_preferred_manager "$value" + echo "Preferred manager set to $value" + else + echo "Unsupported manager. Supported: nvm, fnm, volta, asdf" + end + else + set -e _nvm_auto_use_preferred_manager + echo "Reset to auto-detect manager" + end +end + +function _nvm_auto_use_config_reset + set -e _nvm_auto_use_no_install + set -e _nvm_auto_use_silent + set -e _nvm_auto_use_debounce_ms + set -e _nvm_auto_use_excluded_dirs + set -e _nvm_auto_use_preferred_manager + echo "Configuration reset to defaults" +end + function _nvm_auto_use_get_debounce if test -n "$_nvm_auto_use_debounce_ms" echo "$_nvm_auto_use_debounce_ms" diff --git a/functions/nvm_auto_use_silent.fish b/functions/nvm_auto_use_silent.fish index fc2cd89..fcbbae8 100644 --- a/functions/nvm_auto_use_silent.fish +++ b/functions/nvm_auto_use_silent.fish @@ -13,7 +13,7 @@ function nvm_auto_use_silent -d "Enable or disable silent mode for nvm-auto-use" set -g _nvm_auto_use_silent 1 echo "Silent mode enabled" case off disable false 0 - set -e _nvm_auto_use_silent + set -e -g _nvm_auto_use_silent echo "Silent mode disabled" case '*' echo "Usage: nvm_auto_use_silent [on|off]" diff --git a/functions/nvm_cache.fish b/functions/nvm_cache.fish new file mode 100644 index 0000000..459b98f --- /dev/null +++ b/functions/nvm_cache.fish @@ -0,0 +1,110 @@ +function nvm_cache -d "XDG-compliant cache management with TTL" + set -l action $argv[1] + set -l key $argv[2] + set -l value $argv[3] + set -l ttl $argv[4] + + switch $action + case get + _nvm_cache_get "$key" "$ttl" + case set + _nvm_cache_set "$key" "$value" + case delete + _nvm_cache_delete "$key" + case clear + _nvm_cache_clear + case stats + _nvm_cache_stats + case '*' + echo "Usage: nvm_cache [get|set|delete|clear|stats] [value] [ttl]" + return 1 + end +end + +function _nvm_cache_dir -d "Get XDG cache directory for nvm-auto-use" + if set -q XDG_CACHE_HOME + echo "$XDG_CACHE_HOME/nvm-auto-use" + else + echo "$HOME/.cache/nvm-auto-use" + end +end + +function _nvm_cache_get -d "Get cache value by key, respecting TTL" + set -l key $argv[1] + set -l ttl $argv[2] + set -l cache_dir (_nvm_cache_dir) + set -l cache_file "$cache_dir/$key" + + if not test -f "$cache_file" + return 1 + end + + # Check TTL + set -l cache_time (stat -c %Y "$cache_file" 2>/dev/null; or stat -f %m "$cache_file" 2>/dev/null) + set -l current_time (date +%s) + set -l default_ttl 300 # 5 minutes default + + if test -n "$ttl" + set default_ttl $ttl + end + + if test (math "$current_time - $cache_time") -gt $default_ttl + rm "$cache_file" 2>/dev/null + return 1 + end + + cat "$cache_file" + return 0 +end + +function _nvm_cache_set -d "Set cache value by key" + set -l key $argv[1] + set -l value $argv[2] + set -l cache_dir (_nvm_cache_dir) + set -l cache_file "$cache_dir/$key" + + if test -z "$value" + return 1 + end + + mkdir -p "$cache_dir" 2>/dev/null + echo "$value" >"$cache_file" 2>/dev/null + return $status +end + +function _nvm_cache_delete -d "Delete cache value by key" + set -l key $argv[1] + set -l cache_dir (_nvm_cache_dir) + set -l cache_file "$cache_dir/$key" + rm "$cache_file" 2>/dev/null + return 0 +end + +function _nvm_cache_clear -d "Clear all cache files" + set -l cache_dir (_nvm_cache_dir) + rm -rf "$cache_dir" 2>/dev/null + return 0 +end + +function _nvm_cache_stats -d "Show cache statistics" + set -l cache_dir (_nvm_cache_dir) + if test -d "$cache_dir" + echo "Cache directory: $cache_dir" + echo "Cache files: "(find "$cache_dir" -type f 2>/dev/null | wc -l) + echo "Cache size: "(du -sh "$cache_dir" 2>/dev/null | cut -f1) + else + echo "No cache directory found" + end + return 0 +end + +function _nvm_cache_key -d "Generate cache key from directory and file" + set -l dir (pwd) + set -l file_hash (echo "$argv[1]" | shasum | cut -d' ' -f1) + echo "dir_$(echo "$dir" | shasum | cut -d' ' -f1)_$file_hash" +end + +function _nvm_cache_manager_key -d "Generate cache key for manager availability" + set -l manager $argv[1] + echo "manager_$manager" +end diff --git a/functions/nvm_compat_detect.fish b/functions/nvm_compat_detect.fish index 4862c63..f62efa1 100644 --- a/functions/nvm_compat_detect.fish +++ b/functions/nvm_compat_detect.fish @@ -36,13 +36,13 @@ function nvm_compat_use -a manager version -d "Use specified version with detect switch $manager case nvm - nvm use $version + nvm use $version; or return $status case fnm - fnm use $version + fnm use $version; or return $status case volta - volta pin node@$version + volta pin node@$version; or return $status case asdf - asdf local nodejs $version + asdf local nodejs $version; or return $status case '*' echo "Unsupported manager: $manager" return 1 @@ -57,13 +57,13 @@ function nvm_compat_install -a manager version -d "Install specified version wit switch $manager case nvm - nvm install $version + nvm install $version; or return $status case fnm - fnm install $version + fnm install $version; or return $status case volta - volta install node@$version + volta install node@$version; or return $status case asdf - asdf install nodejs $version + asdf install nodejs $version; or return $status case '*' echo "Unsupported manager: $manager" return 1 diff --git a/functions/nvm_doctor.fish b/functions/nvm_doctor.fish new file mode 100644 index 0000000..e07be1b --- /dev/null +++ b/functions/nvm_doctor.fish @@ -0,0 +1,428 @@ +function nvm_doctor -d "Comprehensive diagnostic and debugging tool" + set -l action $argv[1] + + switch $action + case check + _nvm_doctor_full_check + case system + _nvm_doctor_system_info + case managers + _nvm_doctor_check_managers + case permissions + _nvm_doctor_check_permissions + case config + _nvm_doctor_check_config + case cache + _nvm_doctor_check_cache + case security + _nvm_doctor_security_audit + case performance + _nvm_doctor_performance_check + case fix + _nvm_doctor_auto_fix $argv[2..-1] + case '*' + echo "Usage: nvm_doctor [check|system|managers|permissions|config|cache|security|performance|fix] [args...]" + echo + echo "Commands:" + echo " check - Run comprehensive diagnostic check" + echo " system - Show system information" + echo " managers - Check version manager status" + echo " permissions - Check file and directory permissions" + echo " config - Validate configuration" + echo " cache - Check cache status and health" + echo " security - Run security audit" + echo " performance - Analyze performance issues" + echo " fix - Attempt to fix common issues" + return 1 + end +end + +function _nvm_doctor_full_check -d "Run comprehensive diagnostic check" + echo "๐Ÿฉบ NVM Auto-Use Doctor - Comprehensive Check" + echo "=============================================" + echo + + set -l issues 0 + + # System check + echo "๐Ÿ–ฅ๏ธ System Information" + echo --------------------- + _nvm_doctor_system_info + echo + + # Manager check + echo "๐Ÿ”ง Version Manager Status" + echo ------------------------- + _nvm_doctor_check_managers + set issues (math "$issues + $status") + echo + + # Configuration check + echo "โš™๏ธ Configuration Status" + echo ------------------------ + _nvm_doctor_check_config + set issues (math "$issues + $status") + echo + + # Permissions check + echo "๐Ÿ” Permissions Check" + echo ------------------- + _nvm_doctor_check_permissions + set issues (math "$issues + $status") + echo + + # Cache check + echo "๐Ÿ—„๏ธ Cache Status" + echo ---------------- + _nvm_doctor_check_cache + set issues (math "$issues + $status") + echo + + # Security audit + echo "๐Ÿ”’ Security Audit" + echo ----------------- + _nvm_doctor_security_audit + set issues (math "$issues + $status") + echo + + # Performance check + echo "โšก Performance Analysis" + echo ---------------------- + _nvm_doctor_performance_check + set issues (math "$issues + $status") + echo + + # Summary + echo "๐Ÿ“‹ Diagnostic Summary" + echo "=====================" + if test $issues -eq 0 + echo "โœ… All checks passed! Your nvm-auto-use setup is healthy." + else + echo "โš ๏ธ Found $issues issue(s) that may need attention." + echo "๐Ÿ’ก Run 'nvm_doctor fix' to attempt automatic fixes." + end + + return $issues +end + +function _nvm_doctor_system_info -d "Display system information" + echo "OS: "(uname -s)" "(uname -r) + echo "Architecture: "(uname -m) + echo "Shell: $SHELL" + echo "Fish version: "(fish --version) + + if command -q node + echo "Node.js: "(node --version) + else + echo "Node.js: Not installed" + end + + if command -q npm + echo "npm: "(npm --version) + else + echo "npm: Not available" + end + + echo "PATH entries: "(echo $PATH | string split ':' | wc -l) + echo "Current directory: "(pwd) +end + +function _nvm_doctor_check_managers -d "Check version manager availability and status" + set -l issues 0 + set -l managers (nvm_compat_detect 2>/dev/null) + + if test -z "$managers" + echo "โŒ No Node.js version managers found" + echo " Install at least one: nvm, fnm, volta, or asdf" + set issues (math "$issues + 1") + else + echo "โœ… Found managers: $managers" + + # Check each manager's status + for manager in (echo $managers | string split ' ') + echo " ๐Ÿ“‹ $manager status:" + switch $manager + case nvm + if test -f "$HOME/.nvm/nvm.sh" + echo " โœ… NVM script found" + if command -q nvm + echo " โœ… NVM command available" + else + echo " โš ๏ธ NVM not in PATH (normal for Fish)" + end + else + echo " โŒ NVM installation not found" + set issues (math "$issues + 1") + end + case fnm + if command -q fnm + echo " โœ… FNM available: "(fnm --version) + else + echo " โŒ FNM not found in PATH" + set issues (math "$issues + 1") + end + case volta + if command -q volta + echo " โœ… Volta available: "(volta --version) + else + echo " โŒ Volta not found in PATH" + set issues (math "$issues + 1") + end + case asdf + if command -q asdf + echo " โœ… asdf available: "(asdf --version) + if asdf plugin list | grep -q nodejs + echo " โœ… nodejs plugin installed" + else + echo " โŒ nodejs plugin not installed" + set issues (math "$issues + 1") + end + else + echo " โŒ asdf not found in PATH" + set issues (math "$issues + 1") + end + end + end + end + + return $issues +end + +function _nvm_doctor_check_permissions -d "Check file and directory permissions" + set -l issues 0 + + # Check Fish functions directory + set -l functions_dir (dirname (status current-filename)) + if test -d "$functions_dir" + echo "โœ… Functions directory accessible: $functions_dir" + + # Check individual function files + for func_file in "$functions_dir"/nvm_*.fish + if test -r "$func_file" + echo " โœ… $(basename $func_file) readable" + else + echo " โŒ $(basename $func_file) not readable" + set issues (math "$issues + 1") + end + end + else + echo "โŒ Functions directory not found" + set issues (math "$issues + 1") + end + + # Check cache directory permissions + set -l cache_dir + if set -q XDG_CACHE_HOME + set cache_dir "$XDG_CACHE_HOME/nvm-auto-use" + else + set cache_dir "$HOME/.cache/nvm-auto-use" + end + + if test -d "$cache_dir" + if test -w "$cache_dir" + echo "โœ… Cache directory writable: $cache_dir" + else + echo "โŒ Cache directory not writable: $cache_dir" + set issues (math "$issues + 1") + end + else + echo "โ„น๏ธ Cache directory doesn't exist (will be created as needed)" + end + + # Check current directory permissions for version files + if test -r "." + echo "โœ… Current directory readable" + + for version_file in .nvmrc .node-version .tool-versions package.json + if test -f "$version_file" + if test -r "$version_file" + echo " โœ… $version_file readable" + else + echo " โŒ $version_file not readable" + set issues (math "$issues + 1") + end + end + end + else + echo "โŒ Current directory not readable" + set issues (math "$issues + 1") + end + + return $issues +end + +function _nvm_doctor_check_config -d "Validate configuration" + set -l issues 0 + + echo "Configuration variables:" + + # Check debounce setting + set -l debounce (_nvm_auto_use_get_debounce 2>/dev/null) + if test -n "$debounce" + echo " โœ… Debounce: ${debounce}ms" + else + echo " โ„น๏ธ Debounce: Default (500ms)" + end + + # Check excluded directories + set -l excluded (_nvm_auto_use_get_excluded_dirs 2>/dev/null) + echo " โœ… Excluded dirs: $excluded" + + # Check auto-install setting + if set -q _nvm_auto_use_no_install + echo " โœ… Auto-install: Disabled" + else + echo " โœ… Auto-install: Enabled" + end + + # Check silent mode + if set -q _nvm_auto_use_silent + echo " โœ… Silent mode: Enabled" + else + echo " โœ… Silent mode: Disabled" + end + + # Check preferred manager + if set -q _nvm_auto_use_preferred_manager + echo " โœ… Preferred manager: $_nvm_auto_use_preferred_manager" + else + echo " โœ… Preferred manager: Auto-detect" + end + + # Validate security policies + echo "Security policies:" + nvm_security policy list 2>/dev/null || echo " โ„น๏ธ No security policies set" + + return $issues +end + +function _nvm_doctor_check_cache -d "Check cache status and health" + set -l issues 0 + + # Get cache stats + nvm_cache stats + + # Check for corrupted cache files + set -l cache_dir + if set -q XDG_CACHE_HOME + set cache_dir "$XDG_CACHE_HOME/nvm-auto-use" + else + set cache_dir "$HOME/.cache/nvm-auto-use" + end + + if test -d "$cache_dir" + set -l cache_files (find "$cache_dir" -type f 2>/dev/null) + + for cache_file in $cache_files + if test -s "$cache_file" + echo " โœ… $(basename $cache_file) valid" + else + echo " โš ๏ธ $(basename $cache_file) empty (may be normal)" + end + end + + # Check for very old cache files + set -l old_files (find "$cache_dir" -type f -mtime +7 2>/dev/null) + if test -n "$old_files" + echo " โ„น๏ธ Found "(echo "$old_files" | wc -l)" cache files older than 7 days" + end + else + echo " โ„น๏ธ No cache directory found" + end + + return $issues +end + +function _nvm_doctor_security_audit -d "Run security audit" + nvm_security audit +end + +function _nvm_doctor_performance_check -d "Analyze performance issues" + set -l issues 0 + + echo "Performance analysis:" + + # Check for excessive cache files + set -l cache_dir + if set -q XDG_CACHE_HOME + set cache_dir "$XDG_CACHE_HOME/nvm-auto-use" + else + set cache_dir "$HOME/.cache/nvm-auto-use" + end + + if test -d "$cache_dir" + set -l cache_count (find "$cache_dir" -type f 2>/dev/null | wc -l) + if test $cache_count -gt 100 + echo " โš ๏ธ Large number of cache files ($cache_count) - consider cleanup" + set issues (math "$issues + 1") + else + echo " โœ… Reasonable cache size ($cache_count files)" + end + end + + # Check for very long directory paths + set -l current_path (pwd) + set -l path_length (string length "$current_path") + if test $path_length -gt 200 + echo " โš ๏ธ Very long current path may slow operations: $path_length characters" + set issues (math "$issues + 1") + else + echo " โœ… Path length reasonable: $path_length characters" + end + + # Check for deep directory nesting + set -l depth (echo "$current_path" | string replace -a '/' '\n' | wc -l) + if test $depth -gt 15 + echo " โš ๏ธ Deep directory nesting may slow file searches: $depth levels" + set issues (math "$issues + 1") + else + echo " โœ… Directory depth reasonable: $depth levels" + end + + return $issues +end + +function _nvm_doctor_auto_fix -d "Attempt to fix common issues" + set -l fix_type $argv[1] + + echo "๐Ÿ”ง Attempting automatic fixes..." + + switch $fix_type + case cache + echo "Cleaning up cache..." + nvm_cache clear + echo "โœ… Cache cleared" + + case permissions + echo "Fixing cache directory permissions..." + set -l cache_dir + if set -q XDG_CACHE_HOME + set cache_dir "$XDG_CACHE_HOME/nvm-auto-use" + else + set cache_dir "$HOME/.cache/nvm-auto-use" + end + + mkdir -p "$cache_dir" 2>/dev/null + chmod 755 "$cache_dir" 2>/dev/null + echo "โœ… Cache directory permissions fixed" + + case config + echo "Resetting configuration to defaults..." + nvm_auto_use_config reset + echo "โœ… Configuration reset" + + case all + echo "Running all available fixes..." + _nvm_doctor_auto_fix cache + _nvm_doctor_auto_fix permissions + echo "โœ… All fixes completed" + + case '*' + echo "Available fix types:" + echo " cache - Clear cache files" + echo " permissions - Fix directory permissions" + echo " config - Reset configuration" + echo " all - Run all fixes" + return 1 + end +end diff --git a/functions/nvm_error_recovery.fish b/functions/nvm_error_recovery.fish new file mode 100644 index 0000000..9c07b04 --- /dev/null +++ b/functions/nvm_error_recovery.fish @@ -0,0 +1,168 @@ +function nvm_error_recovery -d "Error recovery and graceful degradation" + set -l operation $argv[1] + set -l error_context $argv[2] + + switch $operation + case manager_failure + set -l manager $argv[2] + set -l target_version $argv[3] + + echo "โš ๏ธ $manager failed to switch to version $target_version" >&2 + + # Try fallback managers + set -l fallback_managers (nvm_compat_detect | string split ' ') + for fallback in $fallback_managers + if test "$fallback" != "$manager" + echo "๐Ÿ”„ Trying fallback manager: $fallback" >&2 + if _nvm_error_recovery_try_manager "$fallback" "$target_version" + echo "โœ… Successfully switched using $fallback" >&2 + return 0 + end + end + end + + echo "โŒ All managers failed. Staying on current version." >&2 + return 1 + + case version_not_found + set -l manager $argv[2] + set -l requested_version $argv[3] + + echo "โš ๏ธ Version $requested_version not found" >&2 + + # Try to find similar versions + set -l suggestions (_nvm_error_recovery_suggest_versions "$manager" "$requested_version") + if test -n "$suggestions" + echo "๐Ÿ’ก Available similar versions: $suggestions" >&2 + + # Auto-select best match if auto_install is disabled + if set -q _nvm_auto_use_no_install + set -l best_match (echo "$suggestions" | string split ' ' | head -n 1) + echo "๐Ÿ”„ Trying closest match: $best_match" >&2 + if _nvm_error_recovery_try_manager "$manager" "$best_match" + return 0 + end + end + end + + return 1 + + case network_failure + echo "โš ๏ธ Network failure during version operation" >&2 + + # Check if we have a cached version list + set -l cache_key "versions_$(echo $argv[2] | shasum | cut -d' ' -f1)" + if set -l cached_versions (nvm_cache get "$cache_key" 86400) # 24 hour TTL + echo "๐Ÿ“ฆ Using cached version information" >&2 + echo "$cached_versions" + return 0 + end + + echo "โŒ No cached version information available" >&2 + return 1 + + case permission_denied + set -l operation_type $argv[2] + echo "โš ๏ธ Permission denied for $operation_type" >&2 + + switch $operation_type + case install + echo "๐Ÿ’ก Try running with appropriate permissions or check manager configuration" >&2 + case switch + echo "๐Ÿ’ก Check if the version is already installed or try with sudo" >&2 + end + + return 1 + + case timeout + set -l operation_type $argv[2] + set -l timeout_duration $argv[3] + + echo "โฑ๏ธ Operation '$operation_type' timed out after $timeout_duration seconds" >&2 + echo "๐Ÿ’ก Consider checking network connection or increasing timeout" >&2 + + # Kill any hanging processes + nvm_async cleanup + return 1 + + case corruption + set -l file_path $argv[2] + echo "โš ๏ธ Corrupted file detected: $file_path" >&2 + + # Try to recover from backup or regenerate + if test -f "$file_path.backup" + echo "๐Ÿ”„ Restoring from backup" >&2 + cp "$file_path.backup" "$file_path" + return 0 + end + + echo "โŒ No backup available, manual intervention required" >&2 + return 1 + + case '*' + echo "Unknown error recovery operation: $operation" >&2 + return 1 + end +end + +function _nvm_error_recovery_try_manager -d "Try using a specific manager" + set -l manager $argv[1] + set -l version $argv[2] + + if not command -q "$manager" + return 1 + end + + switch $manager + case nvm + nvm use "$version" 2>/dev/null + case fnm + fnm use "$version" 2>/dev/null + case volta + volta pin "node@$version" 2>/dev/null + case asdf + asdf local nodejs "$version" 2>/dev/null + case '*' + return 1 + end +end + +function _nvm_error_recovery_suggest_versions -d "Suggest similar available versions" + set -l manager $argv[1] + set -l requested $argv[2] + + # Extract major version for suggestions + set -l major (echo "$requested" | string replace -r '^v?([0-9]+).*' '$1') + + # Try to get available versions (with error handling) + set -l available_versions + switch $manager + case nvm + set available_versions (nvm list-remote 2>/dev/null | grep "^v$major\." | head -n 5) + case fnm + set available_versions (fnm list-remote 2>/dev/null | grep "^v$major\." | head -n 5) + case asdf + set available_versions (asdf list-all nodejs 2>/dev/null | grep "^$major\." | head -n 5) + end + + echo "$available_versions" | string join ' ' +end + +function _nvm_error_recovery_log -d "Log error for debugging" + set -l error_type $argv[1] + set -l details $argv[2] + + # Log to XDG cache directory + set -l log_dir + if set -q XDG_CACHE_HOME + set log_dir "$XDG_CACHE_HOME/nvm-auto-use" + else + set log_dir "$HOME/.cache/nvm-auto-use" + end + + mkdir -p "$log_dir" 2>/dev/null + set -l log_file "$log_dir/error.log" + + set -l timestamp (date '+%Y-%m-%d %H:%M:%S') + echo "[$timestamp] $error_type: $details" >>"$log_file" +end diff --git a/functions/nvm_extract_version.fish b/functions/nvm_extract_version.fish index 7f67391..6514137 100644 --- a/functions/nvm_extract_version.fish +++ b/functions/nvm_extract_version.fish @@ -35,11 +35,15 @@ function nvm_extract_version -a file_path -d "Extract Node.js version from vario # Standard .nvmrc or .node-version file set -l version (cat "$actual_file" | string trim) if test -n "$version" + # Strip leading 'v' + set version (string replace -r '^v' '' "$version") # Handle nvm aliases switch "$version" case 'lts/*' lts latest stable node if command -q nvm set version (nvm version-remote "$version" 2>/dev/null | string replace 'v' '') + else if command -q node + set version (node -v 2>/dev/null | string replace -r '^v' '') end end echo "$version" diff --git a/functions/nvm_notify.fish b/functions/nvm_notify.fish index a2615f1..9a58629 100644 --- a/functions/nvm_notify.fish +++ b/functions/nvm_notify.fish @@ -8,13 +8,18 @@ function nvm_notify -a message -d "Send notification for Node.js version changes return end - # Try different notification methods - if command -q osascript # macOS + # Try different notification methods until one succeeds + if command -q osascript osascript -e "display notification \"$message\" with title \"nvm-auto-use\"" - else if command -q notify-send # Linux + return + end + if command -q notify-send notify-send nvm-auto-use "$message" - else if command -q terminal-notifier # macOS alternative + return + end + if command -q terminal-notifier terminal-notifier -title nvm-auto-use -message "$message" + return end end diff --git a/functions/nvm_recommendations.fish b/functions/nvm_recommendations.fish new file mode 100644 index 0000000..721796f --- /dev/null +++ b/functions/nvm_recommendations.fish @@ -0,0 +1,354 @@ +function nvm_recommendations -d "Smart recommendations for Node.js versions and configurations" + set -l action $argv[1] + + switch $action + case suggest_version + _nvm_recommend_version $argv[2..-1] + case upgrade_path + _nvm_recommend_upgrade $argv[2..-1] + case security_update + _nvm_recommend_security_update + case performance + _nvm_recommend_performance + case compatibility + _nvm_recommend_compatibility $argv[2..-1] + case manager + _nvm_recommend_manager + case config + _nvm_recommend_config + case '*' + echo "Usage: nvm_recommendations [suggest_version|upgrade_path|security_update|performance|compatibility|manager|config] [args...]" + return 1 + end +end + +function _nvm_recommend_version -d "Recommend appropriate Node.js version" + set -l context $argv[1] # 'new_project', 'existing_project', 'migration' + + echo "๐Ÿ” Analyzing project for Node.js version recommendation..." + + # Check for existing version constraints + set -l constraints (_nvm_analyze_version_constraints) + set -l current_version + if command -q node + set current_version (node --version | string replace 'v' '') + end + + # Get available managers and their capabilities + set -l available_managers (nvm_compat_detect | string split ' ') + + echo + echo "๐Ÿ“‹ Recommendation Analysis:" + echo "============================" + + # Project type detection + set -l project_type (_nvm_detect_project_type) + echo "Project type: $project_type" + + if test -n "$current_version" + echo "Current version: $current_version" + end + + if test -n "$constraints" + echo "Detected constraints: $constraints" + end + + # Generate recommendations + echo + echo "๐Ÿ’ก Recommendations:" + echo "===================" + + switch $context + case new_project + _nvm_recommend_for_new_project "$project_type" + case existing_project + _nvm_recommend_for_existing_project "$project_type" "$current_version" + case migration + _nvm_recommend_for_migration "$current_version" + case '*' + _nvm_recommend_general "$project_type" "$current_version" + end +end + +function _nvm_recommend_for_new_project -d "Recommendations for new projects" + set -l project_type $argv[1] + + switch $project_type + case react + echo "โ€ข Node.js 18.17.0+ (LTS) - Recommended for React projects" + echo "โ€ข Consider Node.js 20.x for latest features" + echo "โ€ข Avoid odd-numbered versions (development releases)" + case vue + echo "โ€ข Node.js 16.20.0+ - Minimum for Vue 3" + echo "โ€ข Node.js 18.17.0+ (LTS) - Recommended" + case angular + echo "โ€ข Node.js 18.13.0+ - Required for Angular 15+" + echo "โ€ข Node.js 18.17.0+ (LTS) - Recommended" + case nextjs + echo "โ€ข Node.js 18.17.0+ - Required for Next.js 13+" + echo "โ€ข Node.js 20.x for best performance" + case typescript + echo "โ€ข Node.js 18.17.0+ (LTS) - Excellent TypeScript support" + echo "โ€ข Node.js 20.x for latest TypeScript features" + case backend + echo "โ€ข Node.js 18.17.0+ (LTS) - Stable for production" + echo "โ€ข Consider Node.js 20.x for performance improvements" + case '*' + echo "โ€ข Node.js 18.17.0+ (LTS) - Safe choice for most projects" + echo "โ€ข Node.js 20.x for latest features and performance" + end + + echo + echo "๐Ÿ’ญ General Guidelines:" + echo "โ€ข Use LTS versions for production projects" + echo "โ€ข Test with latest version for future compatibility" + echo "โ€ข Pin exact versions in CI/CD environments" +end + +function _nvm_recommend_for_existing_project -d "Recommendations for existing projects" + set -l project_type $argv[1] + set -l current_version $argv[2] + + if test -z "$current_version" + echo "โ€ข Install Node.js to get version-specific recommendations" + return + end + + # Check if current version is LTS + set -l is_lts (_nvm_check_if_lts "$current_version") + set -l is_outdated (_nvm_check_if_outdated "$current_version") + + if test "$is_outdated" = true + echo "โš ๏ธ Current version ($current_version) is outdated" + echo "โ€ข Consider upgrading to latest LTS for security updates" + + # Suggest upgrade path + set -l upgrade_target (_nvm_suggest_upgrade_target "$current_version") + if test -n "$upgrade_target" + echo "โ€ข Recommended upgrade: $upgrade_target" + end + else if test "$is_lts" = false + echo "โ„น๏ธ Current version ($current_version) is not LTS" + echo "โ€ข Consider switching to LTS for stability" + else + echo "โœ… Current version ($current_version) is good" + echo "โ€ข No immediate action needed" + end + + # Dependency compatibility check + if test -f "package.json" + echo + echo "๐Ÿ“ฆ Dependency Analysis:" + _nvm_analyze_dependencies + end +end + +function _nvm_recommend_upgrade -d "Recommend upgrade path" + set -l current_version $argv[1] + + if test -z "$current_version" + if command -q node + set current_version (node --version | string replace 'v' '') + else + echo "โŒ No Node.js version specified or installed" + return 1 + end + end + + echo "๐Ÿ”„ Upgrade Path Analysis for Node.js $current_version" + echo "==================================================" + + # Check for security issues + nvm_security check_cve "$current_version" + set -l has_vulnerabilities $status + + if test $has_vulnerabilities -ne 0 + echo + echo "๐Ÿšจ SECURITY: Immediate upgrade recommended due to vulnerabilities" + end + + # Suggest upgrade targets + set -l major_version (echo "$current_version" | string replace -r '^([0-9]+)\..*' '$1') + set -l next_lts (_nvm_get_next_lts "$major_version") + + echo + echo "๐Ÿ“ˆ Upgrade Options:" + echo "โ€ข Patch upgrade: Stay within current minor version" + echo "โ€ข Minor upgrade: Upgrade to latest in major version $major_version" + + if test -n "$next_lts" + echo "โ€ข Major upgrade: Node.js $next_lts (LTS)" + end + + echo + echo "๐Ÿงช Testing Strategy:" + echo "1. Test in development environment first" + echo "2. Run full test suite" + echo "3. Check for breaking changes in release notes" + echo "4. Update CI/CD pipelines" + echo "5. Deploy to staging before production" +end + +function _nvm_recommend_security_update -d "Recommend security-focused updates" + echo "๐Ÿ”’ Security Update Recommendations" + echo "==================================" + + if command -q node + set -l current_version (node --version | string replace 'v' '') + echo "Current version: $current_version" + + # Check for vulnerabilities + nvm_security check_cve "$current_version" + set -l has_vulnerabilities $status + + if test $has_vulnerabilities -ne 0 + echo + echo "๐Ÿšจ ACTION REQUIRED: Security vulnerabilities found" + echo "โ€ข Upgrade immediately to patch security issues" + + # Suggest secure versions + set -l secure_versions (_nvm_get_secure_versions) + if test -n "$secure_versions" + echo "โ€ข Recommended secure versions: $secure_versions" + end + else + echo + echo "โœ… No known vulnerabilities in current version" + echo "โ€ข Keep monitoring for security updates" + end + else + echo "โŒ Node.js not installed - cannot assess security status" + end + + echo + echo "๐Ÿ›ก๏ธ Security Best Practices:" + echo "โ€ข Keep Node.js updated to latest patch versions" + echo "โ€ข Subscribe to Node.js security announcements" + echo "โ€ข Use npm audit for dependency vulnerabilities" + echo "โ€ข Pin specific versions in production" +end + +function _nvm_recommend_performance -d "Performance optimization recommendations" + echo "โšก Performance Optimization Recommendations" + echo "==========================================" + + if command -q node + set -l current_version (node --version | string replace 'v' '') + set -l major_version (echo "$current_version" | string replace -r '^([0-9]+)\..*' '$1') + + echo "Current version: $current_version" + echo + + # Version-specific performance notes + switch $major_version + case 16 + echo "๐Ÿ“ˆ Upgrade to Node.js 18+ for:" + echo "โ€ข Better V8 engine performance" + echo "โ€ข Improved startup time" + echo "โ€ข Enhanced memory usage" + case 18 + echo "๐Ÿ“ˆ Consider Node.js 20+ for:" + echo "โ€ข Latest V8 optimizations" + echo "โ€ข Improved module loading" + echo "โ€ข Better async performance" + case 20 21 + echo "โœ… You're using a modern Node.js version" + echo "โ€ข Good performance characteristics" + echo "โ€ข Consider latest patch for micro-optimizations" + case '*' + echo "โš ๏ธ Consider upgrading to Node.js 18+ for better performance" + end + end + + echo + echo "๐ŸŽฏ Performance Tips:" + echo "โ€ข Use --max-old-space-size for memory-intensive apps" + echo "โ€ข Enable --experimental-loader for faster imports" + echo "โ€ข Consider --enable-source-maps for better debugging" + echo "โ€ข Profile with --cpu-prof and --heap-prof" +end + +function _nvm_detect_project_type -d "Detect project type from files" + if test -f "package.json" + set -l deps (cat package.json 2>/dev/null) + + if echo "$deps" | grep -q '"react"' + echo react + else if echo "$deps" | grep -q '"vue"' + echo vue + else if echo "$deps" | grep -q '"@angular"' + echo angular + else if echo "$deps" | grep -q '"next"' + echo nextjs + else if echo "$deps" | grep -q '"typescript"' + echo typescript + else if echo "$deps" | grep -q '"express"\|"fastify"\|"koa"' + echo backend + else + echo node + end + else + echo general + end +end + +function _nvm_analyze_version_constraints -d "Analyze existing version constraints" + set -l constraints + + # Check package.json engines + if test -f "package.json" -a command -q jq + set -l engine_constraint (jq -r '.engines.node // empty' package.json 2>/dev/null) + if test -n "$engine_constraint" + set constraints $constraints "package.json: $engine_constraint" + end + end + + # Check .nvmrc + if test -f ".nvmrc" + set -l nvmrc_version (cat .nvmrc | string trim) + set constraints $constraints ".nvmrc: $nvmrc_version" + end + + echo "$constraints" | string join '; ' +end + +function _nvm_check_if_lts -d "Check if version is LTS" + set -l version $argv[1] + set -l major (echo "$version" | string replace -r '^([0-9]+)\..*' '$1') + + # LTS versions: 16, 18, 20 (even numbers) + if test (math "$major % 2") -eq 0 + echo true + else + echo false + end +end + +function _nvm_check_if_outdated -d "Check if version is outdated" + set -l version $argv[1] + set -l major (echo "$version" | string replace -r '^([0-9]+)\..*' '$1') + + # Simplified check - versions below 16 are definitely outdated + if test $major -lt 16 + echo true + else + echo false + end +end + +function _nvm_get_next_lts -d "Get next LTS version" + set -l current_major $argv[1] + set -l next_lts + + # Determine next LTS based on current major + switch $current_major + case 14 15 16 17 + set next_lts "18.17.0" + case 18 19 + set next_lts "20.5.0" + case '*' + set next_lts "20.5.0" + end + + echo "$next_lts" +end diff --git a/functions/nvm_security.fish b/functions/nvm_security.fish new file mode 100644 index 0000000..497a41f --- /dev/null +++ b/functions/nvm_security.fish @@ -0,0 +1,278 @@ +function nvm_security -d "Security validation and vulnerability checking" + set -l action $argv[1] + + switch $action + case check_version + set -l version $argv[2] + _nvm_security_validate_version "$version" + + case check_cve + set -l version $argv[2] + _nvm_security_check_vulnerabilities "$version" + + case validate_source + set -l source $argv[2] + _nvm_security_validate_source "$source" + + case audit + _nvm_security_audit_current + + case policy + set -l policy_action $argv[2] + _nvm_security_policy "$policy_action" $argv[3..-1] + + case '*' + echo "Usage: nvm_security [check_version|check_cve|validate_source|audit|policy] [args...]" + return 1 + end +end + +function _nvm_security_validate_version -d "Validate version string format and safety" + set -l version $argv[1] + + # Remove leading 'v' if present + set version (string replace -r '^v' '' "$version") + + # Check for valid semver format + if not string match -qr '^\d+\.\d+\.\d+' "$version" + echo "โš ๏ธ Invalid version format: $version" >&2 + return 1 + end + + # Check for suspicious characters + if string match -qr '[;&|`$(){}[\]<>]' "$version" + echo "๐Ÿšจ Suspicious characters in version: $version" >&2 + return 1 + end + + # Check against minimum supported version + set -l min_version (nvm_security policy get min_version) + if test -n "$min_version" + if _nvm_security_version_compare "$version" "$min_version" -lt + echo "โš ๏ธ Version $version is below minimum required ($min_version)" >&2 + return 1 + end + end + + # Check against maximum allowed version + set -l max_version (nvm_security policy get max_version) + if test -n "$max_version" + if _nvm_security_version_compare "$version" "$max_version" -gt + echo "โš ๏ธ Version $version is above maximum allowed ($max_version)" >&2 + return 1 + end + end + + return 0 +end + +function _nvm_security_check_vulnerabilities -d "Check version for known vulnerabilities" + set -l version $argv[1] + + # Cache key for CVE data + set -l cache_key "cve_check_$(echo $version | shasum | cut -d' ' -f1)" + + # Check cache first (24 hour TTL) + if set -l cached_result (nvm_cache get "$cache_key" 86400) + if test "$cached_result" = vulnerable + echo "๐Ÿšจ Version $version has known vulnerabilities (cached)" >&2 + return 1 + else if test "$cached_result" = safe + echo "โœ… Version $version appears safe (cached)" >&2 + return 0 + end + end + + # Check against known vulnerable versions (offline first) + set -l vulnerable_versions " + 16.0.0 + 16.1.0 + 16.2.0 + 18.0.0 + 18.1.0 + " + + if string match -q "*$version*" "$vulnerable_versions" + echo "๐Ÿšจ Version $version has known vulnerabilities" >&2 + nvm_cache set "$cache_key" vulnerable + return 1 + end + + # Try online CVE check if available + if command -q curl + _nvm_security_online_cve_check "$version" "$cache_key" + else + echo "โ„น๏ธ Cannot perform online CVE check (curl not available)" >&2 + nvm_cache set "$cache_key" unknown + return 0 + end +end + +function _nvm_security_online_cve_check -d "Perform online CVE check" + set -l version $argv[1] + set -l cache_key $argv[2] + + # Background job for CVE checking to avoid blocking + fish -c " + set cve_result (curl -s --max-time 5 'https://nodejs.org/en/about/security/' 2>/dev/null) + if test \$status -eq 0 + if echo \"\$cve_result\" | grep -qi '$version' + nvm_cache set '$cache_key' 'vulnerable' + echo '๐Ÿšจ Version $version found in security advisories' >&2 + else + nvm_cache set '$cache_key' 'safe' + echo 'โœ… Version $version appears safe' >&2 + end + else + nvm_cache set '$cache_key' 'unknown' + echo 'โ„น๏ธ Unable to verify security status online' >&2 + end + " & + + return 0 +end + +function _nvm_security_validate_source -d "Validate version file source" + set -l source_file $argv[1] + + if not test -f "$source_file" + echo "โš ๏ธ Source file not found: $source_file" >&2 + return 1 + end + + # Check file permissions + if test -w "$source_file" -a (stat -c %a "$source_file" 2>/dev/null || stat -f %Mp%Lp "$source_file" 2>/dev/null) = 777 + echo "โš ๏ธ Insecure permissions on $source_file (world-writable)" >&2 + end + + # Check for suspicious content + set -l content (cat "$source_file") + if string match -qr '[;&|`$(){}[\]<>]' "$content" + echo "๐Ÿšจ Suspicious content in $source_file" >&2 + return 1 + end + + return 0 +end + +function _nvm_security_audit_current -d "Audit current Node.js installation" + echo "๐Ÿ” Security audit for current Node.js installation" + echo + + # Current version info + if command -q node + set -l current_version (node --version 2>/dev/null | string replace 'v' '') + echo "Current version: $current_version" + + # Check current version security + nvm_security check_version "$current_version" + nvm_security check_cve "$current_version" + + # Check npm security if available + if command -q npm + echo + echo "๐Ÿ“ฆ NPM audit (if available):" + npm audit --audit-level moderate 2>/dev/null || echo "NPM audit not available or no package.json found" + end + else + echo "โŒ Node.js not found in PATH" + end + + echo + echo "๐Ÿ”ง Version managers found:" + nvm_compat_detect + + echo + echo "๐Ÿ“‹ Security policies:" + nvm_security policy list +end + +function _nvm_security_policy -d "Manage security policies" + set -l action $argv[1] + + switch $action + case set + set -l key $argv[2] + set -l value $argv[3] + + switch $key + case min_version + set -g _nvm_security_min_version "$value" + echo "Set minimum version to: $value" + case max_version + set -g _nvm_security_max_version "$value" + echo "Set maximum version to: $value" + case allow_prerelease + set -g _nvm_security_allow_prerelease "$value" + echo "Allow prerelease versions: $value" + case '*' + echo "Unknown policy key: $key" >&2 + return 1 + end + + case get + set -l key $argv[2] + + switch $key + case min_version + echo "$_nvm_security_min_version" + case max_version + echo "$_nvm_security_max_version" + case allow_prerelease + echo "$_nvm_security_allow_prerelease" + case '*' + echo "Unknown policy key: $key" >&2 + return 1 + end + + case list + echo "Security policies:" + echo " min_version: $_nvm_security_min_version" + echo " max_version: $_nvm_security_max_version" + echo " allow_prerelease: $_nvm_security_allow_prerelease" + + case reset + set -e _nvm_security_min_version + set -e _nvm_security_max_version + set -e _nvm_security_allow_prerelease + echo "Security policies reset to defaults" + + case '*' + echo "Usage: nvm_security policy [set|get|list|reset] [key] [value]" + return 1 + end +end + +function _nvm_security_version_compare -d "Compare semantic versions" + set -l version1 $argv[1] + set -l version2 $argv[2] + set -l operator $argv[3] + + # Simple semver comparison + set -l v1_parts (string split '.' "$version1") + set -l v2_parts (string split '.' "$version2") + + for i in (seq 1 3) + set -l v1_part (echo $v1_parts[$i] | string replace -r '[^0-9].*' '') + set -l v2_part (echo $v2_parts[$i] | string replace -r '[^0-9].*' '') + + if test -z "$v1_part" + set v1_part 0 + end + if test -z "$v2_part" + set v2_part 0 + end + + if test $v1_part -lt $v2_part + test "$operator" = -lt + return $status + else if test $v1_part -gt $v2_part + test "$operator" = -gt + return $status + end + end + + # Versions are equal + test "$operator" = -eq + return $status +end diff --git a/functions/nvm_version_prompt.fish b/functions/nvm_version_prompt.fish index 7f73e0a..26f146b 100644 --- a/functions/nvm_version_prompt.fish +++ b/functions/nvm_version_prompt.fish @@ -9,10 +9,13 @@ end function nvm_version_status -d "Show detailed Node.js version status" if command -q node - set -l version (node -v 2>/dev/null) - set -l npm_version (npm -v 2>/dev/null) + set -l version (node -v 2>/dev/null | string replace -r '^v' '') + set -l npm_version + if command -q npm + set npm_version (npm -v 2>/dev/null) + end - echo "Node.js: $version" + echo "Node.js: v$version" if test -n "$npm_version" echo "npm: v$npm_version" end diff --git a/tests/integration/test_version_switching.fish b/tests/integration/test_version_switching.fish new file mode 100755 index 0000000..5f9fc84 --- /dev/null +++ b/tests/integration/test_version_switching.fish @@ -0,0 +1,178 @@ +#!/usr/bin/env fish +# Integration tests for version switching functionality +# All tests operate under $TEST_DIR (a temporary directory) for safety + +source tests/test_runner.fish + +function test_nvmrc_detection + echo "Testing .nvmrc file detection..." + + # Create test project with .nvmrc in temp dir + mkdir -p "$TEST_DIR/test_project" + echo "18.17.0" >"$TEST_DIR/test_project/.nvmrc" + + cd "$TEST_DIR/test_project" + set -l found_file (nvm_find_nvmrc) + assert_contains "$found_file" ".nvmrc" "Found .nvmrc file in current directory" + + # Test parent directory search + mkdir -p subdir + cd subdir + set found_file (nvm_find_nvmrc) + assert_contains "$found_file" ".nvmrc" "Found .nvmrc file in parent directory" + + cd "$TEST_DIR" + rm -rf "$TEST_DIR/test_project" + + return 0 +end + +function test_version_extraction + echo "Testing version extraction from different file formats..." + + cd "$TEST_DIR" + # Test .nvmrc + echo "18.17.0" >test.nvmrc + set -l version (nvm_extract_version "test.nvmrc") + assert_equals "$version" "18.17.0" "Extracted version from .nvmrc" + + # Test .node-version + echo "16.20.0" >test.node-version + set version (nvm_extract_version "test.node-version") + assert_equals "$version" "16.20.0" "Extracted version from .node-version" + + # Test .tool-versions + echo "nodejs 20.5.0" >test.tool-versions + set version (nvm_extract_version "test.tool-versions:nodejs") + assert_equals "$version" "20.5.0" "Extracted version from .tool-versions" + + # Test package.json (requires jq) + if command -q jq + echo '{"engines": {"node": ">=18.0.0"}}' >test.package.json + set version (nvm_extract_version "test.package.json:engines.node") + assert_equals "$version" "18.0.0" "Extracted version from package.json" + else + echo "โ„น๏ธ Skipping package.json test (jq not available)" + end + + # Cleanup + rm -f test.nvmrc test.node-version test.tool-versions test.package.json + + return 0 +end + +function test_manager_detection + echo "Testing version manager detection..." + + cd "$TEST_DIR" + set -l managers (nvm_compat_detect) + + if test -n "$managers" + echo "โœ… Found version managers: $managers" + else + echo "โ„น๏ธ No version managers found (expected in test environment)" + end + + return 0 +end + +function test_error_recovery + echo "Testing error recovery mechanisms..." + + cd "$TEST_DIR" + # Test invalid version handling + echo "invalid.version" >invalid.nvmrc + set -l result (nvm_extract_version "invalid.nvmrc" 2>/dev/null) + + if test -z "$result" + echo "โœ… Invalid version file handled gracefully" + else + echo "โŒ Invalid version should return empty result" + end + + # Test missing file handling + nvm_extract_version "nonexistent.nvmrc" >/dev/null 2>&1 + set -l status_code $status + test $status_code -ne 0 + and echo "โœ… Missing file handled gracefully" + or echo "โŒ Missing file should return error" + + rm -f invalid.nvmrc + + return 0 +end + +function test_async_operations + echo "Testing async operations..." + + cd "$TEST_DIR" + # Create test version file + echo "18.17.0" >async_test.nvmrc + + # Test async version check + set -l job_id (nvm_async version_check "async_test.nvmrc") + + if test -n "$job_id" + echo "โœ… Async version check started" + + # Wait for completion + nvm_async wait "$job_id" 5 + and echo "โœ… Async operation completed" + or echo "โš ๏ธ Async operation timed out" + else + echo "โ„น๏ธ Async operation may have completed immediately" + end + + rm -f async_test.nvmrc + + return 0 +end + +function test_cache_integration + echo "Testing cache integration..." + + cd "$TEST_DIR" + # Clear cache first + nvm_cache clear + + # Create test file + echo "18.17.0" >cache_test.nvmrc + + # First access should miss cache + set -l start_time (date +%s) + set -l version1 (nvm_extract_version "cache_test.nvmrc") + + # Second access should hit cache (if caching is implemented) + set -l version2 (nvm_extract_version "cache_test.nvmrc") + + assert_equals "$version1" "$version2" "Consistent results from cache" + + rm -f cache_test.nvmrc + + return 0 +end + +function main + setup_test_env + + set -l failed 0 + + test_nvmrc_detection; or set failed (math "$failed + 1") + test_version_extraction; or set failed (math "$failed + 1") + test_manager_detection; or set failed (math "$failed + 1") + test_error_recovery; or set failed (math "$failed + 1") + test_async_operations; or set failed (math "$failed + 1") + test_cache_integration; or set failed (math "$failed + 1") + + cleanup_test_env + + if test $failed -eq 0 + echo "๐ŸŽ‰ All integration tests passed!" + return 0 + else + echo "๐Ÿ’ฅ $failed integration test(s) failed" + return 1 + end +end + +main diff --git a/tests/test_runner.fish b/tests/test_runner.fish new file mode 100755 index 0000000..c6cedfb --- /dev/null +++ b/tests/test_runner.fish @@ -0,0 +1,189 @@ +#!/usr/bin/env fish +# Test runner for nvm-auto-use.fish + +function run_tests -d "Run all tests" + set -l test_files + set -l failed_tests 0 + set -l total_tests 0 + + echo "๐Ÿงช Running nvm-auto-use.fish test suite" + echo "======================================" + + # Find all test files + for test_file in tests/unit/*.fish tests/integration/*.fish + if test -f "$test_file" + set test_files $test_files "$test_file" + end + end + + if test (count $test_files) -eq 0 + echo "โŒ No test files found" + return 1 + end + + # Run each test file + for test_file in $test_files + echo + echo "๐Ÿ“ Running $(basename $test_file)" + echo "$(string repeat -N (string length "๐Ÿ“ Running $(basename $test_file)") -)" + + set -l test_result (fish "$test_file") + set -l test_status $status + + if test $test_status -eq 0 + echo "โœ… $(basename $test_file) passed" + else + echo "โŒ $(basename $test_file) failed" + set failed_tests (math "$failed_tests + 1") + end + + set total_tests (math "$total_tests + 1") + end + + # Summary + echo + echo "๐Ÿ“Š Test Results" + echo "===============" + echo "Total tests: $total_tests" + echo "Passed: "(math "$total_tests - $failed_tests") + echo "Failed: $failed_tests" + + if test $failed_tests -eq 0 + echo + echo "๐ŸŽ‰ All tests passed!" + return 0 + else + echo + echo "๐Ÿ’ฅ $failed_tests test(s) failed" + return 1 + end +end + +function assert_equals -d "Assert two values are equal" + set -l actual "$argv[1]" + set -l expected "$argv[2]" + set -l message "$argv[3]" + + if test "$actual" = "$expected" + echo "โœ… $message" + return 0 + else + echo "โŒ $message" + echo " Expected: '$expected'" + echo " Actual: '$actual'" + return 1 + end +end + +function assert_not_equals -d "Assert two values are not equal" + set -l actual "$argv[1]" + set -l expected "$argv[2]" + set -l message "$argv[3]" + + if test "$actual" != "$expected" + echo "โœ… $message" + return 0 + else + echo "โŒ $message" + echo " Values should not be equal: '$actual'" + return 1 + end +end + +function assert_contains -d "Assert string contains substring" + set -l string "$argv[1]" + set -l substring "$argv[2]" + set -l message "$argv[3]" + + if string match -q "*$substring*" "$string" + echo "โœ… $message" + return 0 + else + echo "โŒ $message" + echo " String: '$string'" + echo " Should contain: '$substring'" + return 1 + end +end + +function assert_file_exists -d "Assert file exists" + set -l file_path "$argv[1]" + set -l message "$argv[2]" + + if test -f "$file_path" + echo "โœ… $message" + return 0 + else + echo "โŒ $message" + echo " File not found: '$file_path'" + return 1 + end +end + +function assert_command_success -d "Assert command succeeds" + set -l command "$argv[1]" + set -l message "$argv[2]" + + if eval "$command" >/dev/null 2>&1 + echo "โœ… $message" + return 0 + else + echo "โŒ $message" + echo " Command failed: '$command'" + return 1 + end +end + +function setup_test_env -d "Set up test environment" + # Create temporary test directory + set -g TEST_DIR (mktemp -d) + cd "$TEST_DIR" + + set -g TEST_FIXTURES "$PWD/../tests/fixtures" + + # Link or copy test fixtures from tests/fixtures + if test -d "$TEST_FIXTURES" + cp -R "$TEST_FIXTURES" "$TEST_DIR/fixtures" + else + mkdir -p "$TEST_FIXTURES" + echo "18.17.0" >"$TEST_FIXTURES/.nvmrc" + echo "16.20.0" >"$TEST_FIXTURES/.node-version" + echo "nodejs 20.5.0" >"$TEST_FIXTURES/.tool-versions" + echo '{"engines": {"node": ">=18.0.0"}}' >"$TEST_FIXTURES/package.json" + end + + echo "๐Ÿ”ง Test environment set up in $TEST_DIR" +end + +function cleanup_test_env -d "Clean up test environment" + if set -q TEST_DIR + # Safety checks: never delete /, $HOME, or empty path + if test -z "$TEST_DIR" + echo "โš ๏ธ TEST_DIR is empty, refusing to delete" + return 1 + end + if test "$TEST_DIR" = / + echo "โš ๏ธ TEST_DIR is /, refusing to delete" + return 1 + end + if test "$TEST_DIR" = "$HOME" + echo "โš ๏ธ TEST_DIR is $HOME, refusing to delete" + return 1 + end + if string match -q "$HOME*" "$TEST_DIR"; and test "$TEST_DIR" = "$HOME" + echo "โš ๏ธ TEST_DIR is $HOME or a parent, refusing to delete" + return 1 + end + if test (string length "$TEST_DIR") -lt 8 + echo "โš ๏ธ TEST_DIR path too short, refusing to delete: $TEST_DIR" + return 1 + end + rm -rf "$TEST_DIR" + echo "๐Ÿงน Test environment cleaned up" + end +end + +# Run tests if this script is executed directly +if test (basename (status current-filename)) = "test_runner.fish" + run_tests +end diff --git a/tests/unit/test_async_helpers.fish b/tests/unit/test_async_helpers.fish new file mode 100644 index 0000000..c04f05a --- /dev/null +++ b/tests/unit/test_async_helpers.fish @@ -0,0 +1,109 @@ +#!/usr/bin/env fish +# Unit tests for nvm_async helper functions + +source tests/test_runner.fish + +function test_async_version_check + echo "Testing _nvm_async_version_check..." + + # Create a test version file + echo "18.17.0" >async_test.nvmrc + + # Should return job id (background job) + set -l job_id (_nvm_async_version_check "async_test.nvmrc") + if test -n "$job_id" + echo "โœ… _nvm_async_version_check started job $job_id" + else + echo "โŒ _nvm_async_version_check did not start a job" + return 1 + end + + # Wait for job completion + _nvm_async_wait "$job_id" 5 + and echo "โœ… Async job completed" + or echo "โš ๏ธ Async job timed out" + + rm -f async_test.nvmrc + return 0 +end + +function test_async_manager_check + echo "Testing _nvm_async_manager_check..." + + # Should return job id (background job) + set -l job_id (_nvm_async_manager_check "nvm") + if test -n "$job_id" + echo "โœ… _nvm_async_manager_check started job $job_id" + else + echo "โŒ _nvm_async_manager_check did not start a job" + return 1 + end + + # Wait for job completion + _nvm_async_wait "$job_id" 5 + and echo "โœ… Async manager check job completed" + or echo "โš ๏ธ Async manager check job timed out" + + return 0 +end + +function test_async_cleanup + echo "Testing _nvm_async_cleanup..." + + # Start a dummy background job + sleep 2 & + set -l job_id (jobs -l | tail -n 1 | grep -o '[0-9]*') + if test -n "$job_id" + echo "โœ… Dummy job started: $job_id" + else + echo "โŒ Failed to start dummy job" + return 1 + end + + # Cleanup should not error + _nvm_async_cleanup + echo "โœ… _nvm_async_cleanup executed" + + return 0 +end + +function test_async_wait + echo "Testing _nvm_async_wait..." + + # Start a quick background job + sleep 1 & + set -l job_id (jobs -l | tail -n 1 | grep -o '[0-9]*') + if test -n "$job_id" + _nvm_async_wait "$job_id" 3 + and echo "โœ… _nvm_async_wait completed for job $job_id" + or echo "โš ๏ธ _nvm_async_wait timed out for job $job_id" + else + echo "โŒ Failed to start background job for wait test" + return 1 + end + + return 0 +end + +function main + setup_test_env + + set -l failed 0 + + test_async_version_check; or set failed (math "$failed + 1") + test_async_manager_check; or set failed (math "$failed + 1") + test_async_cleanup; or set failed (math "$failed + 1") + test_async_wait; or set failed (math "$failed + 1") + + cleanup_test_env + + if test $failed -eq 0 + echo "๐ŸŽ‰ All async helper tests passed!" + return 0 + else + echo "๐Ÿ’ฅ $failed async helper test(s) failed" + return 1 + end +end + +main diff --git a/tests/unit/test_auto_use_config_helpers.fish b/tests/unit/test_auto_use_config_helpers.fish new file mode 100644 index 0000000..7e4f9fa --- /dev/null +++ b/tests/unit/test_auto_use_config_helpers.fish @@ -0,0 +1,109 @@ +#!/usr/bin/env fish +# Unit tests for nvm_auto_use_config helper functions + +source tests/test_runner.fish + +function test_config_show + echo "Testing _nvm_auto_use_config_show..." + + # Should print config summary (no error) + _nvm_auto_use_config_show + and echo "โœ… Config show prints summary" + or echo "โŒ Config show failed" +end + +function test_config_auto_install + echo "Testing _nvm_auto_use_config_auto_install..." + + _nvm_auto_use_config_auto_install on + test -z "$_nvm_auto_use_no_install" + and echo "โœ… Auto-install enabled" + or echo "โŒ Auto-install enable failed" + + _nvm_auto_use_config_auto_install off + test -n "$_nvm_auto_use_no_install" + and echo "โœ… Auto-install disabled" + or echo "โŒ Auto-install disable failed" +end + +function test_config_silent + echo "Testing _nvm_auto_use_config_silent..." + + _nvm_auto_use_config_silent on + test -n "$_nvm_auto_use_silent" + and echo "โœ… Silent mode enabled" + or echo "โŒ Silent mode enable failed" + + _nvm_auto_use_config_silent off + test -z "$_nvm_auto_use_silent" + and echo "โœ… Silent mode disabled" + or echo "โŒ Silent mode disable failed" +end + +function test_config_debounce + echo "Testing _nvm_auto_use_config_debounce..." + + _nvm_auto_use_config_debounce 1234 + assert_equals "$_nvm_auto_use_debounce_ms" 1234 "Debounce set correctly" + + _nvm_auto_use_config_debounce "" + assert_equals "$_nvm_auto_use_debounce_ms" 1234 "Debounce unchanged on invalid input" +end + +function test_config_exclude_include + echo "Testing _nvm_auto_use_config_exclude and _nvm_auto_use_config_include..." + + set -e _nvm_auto_use_excluded_dirs + _nvm_auto_use_config_exclude testdir + assert_contains "$_nvm_auto_use_excluded_dirs" testdir "Exclude added" + + _nvm_auto_use_config_include testdir + assert_not_equals "$_nvm_auto_use_excluded_dirs" testdir "Exclude removed" +end + +function test_config_manager + echo "Testing _nvm_auto_use_config_manager..." + + _nvm_auto_use_config_manager nvm + assert_equals "$_nvm_auto_use_preferred_manager" nvm "Manager set to nvm" + + _nvm_auto_use_config_manager "" + test -z "$_nvm_auto_use_preferred_manager" + and echo "โœ… Manager reset to auto-detect" + or echo "โŒ Manager reset failed" + + _nvm_auto_use_config_manager invalid + assert_not_equals "$_nvm_auto_use_preferred_manager" invalid "Invalid manager not set" +end + +function test_config_reset + echo "Testing _nvm_auto_use_config_reset..." + + set -g _nvm_auto_use_no_install 1 + set -g _nvm_auto_use_silent 1 + set -g _nvm_auto_use_debounce_ms 999 + set -g _nvm_auto_use_excluded_dirs foo + set -g _nvm_auto_use_preferred_manager nvm + + _nvm_auto_use_config_reset + + test -z "$_nvm_auto_use_no_install" + and test -z "$_nvm_auto_use_silent" + and test -z "$_nvm_auto_use_debounce_ms" + and test -z "$_nvm_auto_use_excluded_dirs" + and test -z "$_nvm_auto_use_preferred_manager" + and echo "โœ… Config reset works" + or echo "โŒ Config reset failed" +end + +function main + test_config_show + test_config_auto_install + test_config_silent + test_config_debounce + test_config_exclude_include + test_config_manager + test_config_reset +end + +main diff --git a/tests/unit/test_auto_use_helpers.fish b/tests/unit/test_auto_use_helpers.fish new file mode 100644 index 0000000..74c2484 --- /dev/null +++ b/tests/unit/test_auto_use_helpers.fish @@ -0,0 +1,134 @@ +#!/usr/bin/env fish +# Unit tests for nvm_auto_use helper functions + +source tests/test_runner.fish + +function test_select_manager + echo "Testing _nvm_auto_use_select_manager..." + + # Mock nvm_compat_detect to return a list + function nvm_compat_detect + echo "nvm fnm volta" + end + + set -e _nvm_auto_use_preferred_manager + set -l manager (_nvm_auto_use_select_manager) + assert_equals "$manager" nvm "Default manager selection returns first available" + + set -g _nvm_auto_use_preferred_manager volta + set manager (_nvm_auto_use_select_manager) + assert_equals "$manager" volta "Preferred manager selection works" + + set -e _nvm_auto_use_preferred_manager + functions -e nvm_compat_detect +end + +function test_should_debounce + echo "Testing _nvm_auto_use_should_debounce..." + + set -e _nvm_auto_use_last_change + set -g _nvm_auto_use_debounce_ms 1000 + + # First call should set last_change and return 1 (not debounced) + set result (_nvm_auto_use_should_debounce) + assert_equals "$result" "" "First call not debounced" + + # Second call within debounce period should return 0 (debounced) + set result (_nvm_auto_use_should_debounce) + assert_equals "$result" "" "Second call debounced" + + set -e _nvm_auto_use_last_change + set -e _nvm_auto_use_debounce_ms +end + +function test_is_excluded_dir + echo "Testing _nvm_auto_use_is_excluded_dir..." + + set -g _nvm_auto_use_excluded_dirs testdir + set -l orig_pwd (pwd) + cd / + mkdir -p testdir + cd testdir + + set result (_nvm_auto_use_is_excluded_dir) + assert_equals "$result" "" "Excluded directory detected" + + cd "$orig_pwd" + set -e _nvm_auto_use_excluded_dirs +end + +function test_get_mtime + echo "Testing _nvm_auto_use_get_mtime..." + + echo test >testfile + set mtime (_nvm_auto_use_get_mtime "testfile") + test -n "$mtime" + and echo "โœ… mtime returned: $mtime" + or echo "โŒ mtime not returned" + + rm -f testfile +end + +function test_is_cache_valid + echo "Testing _nvm_auto_use_is_cache_valid..." + + set -g _nvm_auto_use_cached_file foo + set -g _nvm_auto_use_cached_mtime 123 + set result (_nvm_auto_use_is_cache_valid "foo" "123") + assert_equals "$result" "" "Cache valid returns 0" + + set result (_nvm_auto_use_is_cache_valid "bar" "123") + assert_equals "$result" "" "Cache invalid returns 1" + + set -e _nvm_auto_use_cached_file + set -e _nvm_auto_use_cached_mtime +end + +function test_clear_cache + echo "Testing _nvm_auto_use_clear_cache..." + + set -g _nvm_auto_use_cached_file foo + set -g _nvm_auto_use_cached_version bar + set -g _nvm_auto_use_cached_mtime baz + _nvm_auto_use_clear_cache + if not set -q _nvm_auto_use_cached_file + echo "โœ… Cached file cleared" + else + echo "โŒ Cached file not cleared" + end + if not set -q _nvm_auto_use_cached_version + echo "โœ… Cached version cleared" + else + echo "โŒ Cached version not cleared" + end + if not set -q _nvm_auto_use_cached_mtime + echo "โœ… Cached mtime cleared" + else + echo "โŒ Cached mtime not cleared" + end +end + +function main + setup_test_env + + set -l failed 0 + + test_select_manager; or set failed (math "$failed + 1") + test_should_debounce; or set failed (math "$failed + 1") + test_is_excluded_dir; or set failed (math "$failed + 1") + test_get_mtime; or set failed (math "$failed + 1") + test_is_cache_valid; or set failed (math "$failed + 1") + test_clear_cache; or set failed (math "$failed + 1") + + cleanup_test_env + + if test $failed -eq 0 + echo "๐ŸŽ‰ All nvm_auto_use helper tests passed!" + return 0 + else + echo "๐Ÿ’ฅ $failed helper test(s) failed" + return 1 + end +end + +main diff --git a/tests/unit/test_cache.fish b/tests/unit/test_cache.fish new file mode 100755 index 0000000..8f19263 --- /dev/null +++ b/tests/unit/test_cache.fish @@ -0,0 +1,98 @@ +#!/usr/bin/env fish +# Unit tests for nvm_cache.fish + +source tests/test_runner.fish + +function test_cache_basic_operations + echo "Testing basic cache operations..." + + # Test set and get + nvm_cache set test_key test_value + set -l result (nvm_cache get "test_key") + assert_equals "$result" test_value "Cache set and get works" + + # Test delete + nvm_cache delete test_key + nvm_cache get test_key + set -l status_code $status + test $status_code -ne 0 + and echo "โœ… Cache delete works" + or echo "โŒ Cache delete failed" + + return 0 +end + +function test_cache_ttl + echo "Testing cache TTL..." + + # Set with short TTL + nvm_cache set ttl_key ttl_value + + # Should exist immediately + set -l result (nvm_cache get "ttl_key" 10) + assert_equals "$result" ttl_value "Cache value exists within TTL" + + # Mock expired cache by setting TTL to 0 + set -l result (nvm_cache get "ttl_key" 0) + set -l status_code $status + test $status_code -ne 0 + and echo "โœ… Cache TTL expiration works" + or echo "โŒ Cache TTL expiration failed" + + return 0 +end + +function test_cache_stats + echo "Testing cache stats..." + + # Clear cache first + nvm_cache clear + + # Add some items + nvm_cache set stats_key1 value1 + nvm_cache set stats_key2 value2 + + # Get stats + set -l stats (nvm_cache stats) + assert_contains "$stats" "Cache files: 2" "Cache stats shows correct file count" + + return 0 +end + +function test_cache_key_generation + echo "Testing cache key generation..." + + # Test directory-based key + set -l key1 (_nvm_cache_key "test_file.txt") + set -l key2 (_nvm_cache_key "test_file.txt") + assert_equals "$key1" "$key2" "Same file generates same cache key" + + # Test different files generate different keys + set -l key3 (_nvm_cache_key "different_file.txt") + assert_not_equals "$key1" "$key3" "Different files generate different cache keys" + + return 0 +end + +function main + setup_test_env + + set -l failed 0 + + test_cache_basic_operations; or set failed (math "$failed + 1") + test_cache_ttl; or set failed (math "$failed + 1") + test_cache_stats; or set failed (math "$failed + 1") + test_cache_key_generation; or set failed (math "$failed + 1") + + cleanup_test_env + + if test $failed -eq 0 + echo "๐ŸŽ‰ All cache tests passed!" + return 0 + else + echo "๐Ÿ’ฅ $failed cache test(s) failed" + return 1 + end +end + +main diff --git a/tests/unit/test_cache_helpers.fish b/tests/unit/test_cache_helpers.fish new file mode 100644 index 0000000..88bcf18 --- /dev/null +++ b/tests/unit/test_cache_helpers.fish @@ -0,0 +1,99 @@ +#!/usr/bin/env fish +# Unit tests for nvm_cache helper functions + +source tests/test_runner.fish + +function test_nvm_cache_get_set_delete + echo "Testing _nvm_cache_set, _nvm_cache_get, and _nvm_cache_delete..." + + set -l key test_key + set -l value test_value + + # Set cache value + _nvm_cache_set $key $value + set -l result (_nvm_cache_get $key 300) + assert_equals "$result" "$value" "Cache set and get returns correct value" + + # Delete cache value + _nvm_cache_delete $key + set -l result (_nvm_cache_get $key 300) + set -l status_code $status + test $status_code -ne 0 + and echo "โœ… Cache delete works" + or echo "โŒ Cache delete failed" + + return 0 +end + +function test_nvm_cache_clear_and_stats + echo "Testing _nvm_cache_clear and _nvm_cache_stats..." + + # Set multiple cache values + _nvm_cache_set key1 value1 + _nvm_cache_set key2 value2 + + # Stats should show at least 2 files + set -l stats (_nvm_cache_stats) + assert_contains "$stats" "Cache files:" "Cache stats reports file count" + + # Clear cache + _nvm_cache_clear + set -l stats_after (_nvm_cache_stats) + assert_contains "$stats_after" "Cache files: 0" "Cache clear removes all files" + + return 0 +end + +function test_nvm_cache_ttl + echo "Testing _nvm_cache_get TTL expiration..." + + set -l key ttl_key + set -l value ttl_value + + _nvm_cache_set $key $value + + # Should exist immediately + set -l result (_nvm_cache_get $key 10) + assert_equals "$result" "$value" "Cache value exists within TTL" + + # Simulate expired cache by setting TTL to 0 + set -l result (_nvm_cache_get $key 0) + set -l status_code $status + test $status_code -ne 0 + and echo "โœ… Cache TTL expiration works" + or echo "โŒ Cache TTL expiration failed" + + _nvm_cache_delete $key + + return 0 +end + +function test_nvm_cache_dir + echo "Testing _nvm_cache_dir returns a valid directory..." + + set -l dir (_nvm_cache_dir) + test -n "$dir" + and echo "โœ… _nvm_cache_dir returns: $dir" + or echo "โŒ _nvm_cache_dir did not return a directory" + + return 0 +end + +function main + set -l failed 0 + + test_nvm_cache_get_set_delete; or set failed (math "$failed + 1") + test_nvm_cache_clear_and_stats; or set failed (math "$failed + 1") + test_nvm_cache_ttl; or set failed (math "$failed + 1") + test_nvm_cache_dir; or set failed (math "$failed + 1") + + if test $failed -eq 0 + echo "๐ŸŽ‰ All nvm_cache helper tests passed!" + return 0 + else + echo "๐Ÿ’ฅ $failed nvm_cache helper test(s) failed" + return 1 + end +end + +main diff --git a/tests/unit/test_security.fish b/tests/unit/test_security.fish new file mode 100755 index 0000000..9a3685d --- /dev/null +++ b/tests/unit/test_security.fish @@ -0,0 +1,154 @@ +#!/usr/bin/env fish +# Unit tests for nvm_security.fish + +source tests/test_runner.fish + +function test_version_validation + echo "Testing version validation..." + + # Valid versions + nvm_security check_version "18.17.0" + and echo "โœ… Valid semver accepted" + or echo "โŒ Valid semver rejected" + + nvm_security check_version "v20.5.1" + and echo "โœ… Version with 'v' prefix accepted" + or echo "โŒ Version with 'v' prefix rejected" + + # Invalid versions + nvm_security check_version "invalid.version" + set -l status_code $status + test $status_code -ne 0 + and echo "โœ… Invalid version rejected" + or echo "โŒ Invalid version accepted" + + # Suspicious characters + nvm_security check_version "18.0.0; touch /tmp/nvm-auto-use-malicious-test" + set status_code $status + test $status_code -ne 0 + and echo "โœ… Malicious version string rejected" + or echo "โŒ Malicious version string accepted" + + return 0 +end + +function test_security_policies + echo "Testing security policies..." + + # Set minimum version policy + nvm_security policy set min_version "16.0.0" + set -l min_version (nvm_security policy get min_version) + assert_equals "$min_version" "16.0.0" "Minimum version policy set correctly" + + # Test version below minimum + nvm_security check_version "14.0.0" + set -l status_code $status + test $status_code -ne 0 + and echo "โœ… Version below minimum rejected" + or echo "โŒ Version below minimum accepted" + + # Set maximum version policy + nvm_security policy set max_version "20.0.0" + set -l max_version (nvm_security policy get max_version) + assert_equals "$max_version" "20.0.0" "Maximum version policy set correctly" + + # Test version above maximum + nvm_security check_version "21.0.0" + set status_code $status + test $status_code -ne 0 + and echo "โœ… Version above maximum rejected" + or echo "โŒ Version above maximum accepted" + + # Reset policies + nvm_security policy reset + + return 0 +end + +function test_version_comparison + echo "Testing version comparison..." + + # Test less than + _nvm_security_version_compare "16.0.0" "18.0.0" -lt + and echo "โœ… Version comparison (less than) works" + or echo "โŒ Version comparison (less than) failed" + + # Test greater than + _nvm_security_version_compare "20.0.0" "18.0.0" -gt + and echo "โœ… Version comparison (greater than) works" + or echo "โŒ Version comparison (greater than) failed" + + # Test equal + _nvm_security_version_compare "18.17.0" "18.17.0" -eq + and echo "โœ… Version comparison (equal) works" + or echo "โŒ Version comparison (equal) failed" + + return 0 +end + +function test_source_validation + echo "Testing source file validation..." + + # Create test files + echo "18.17.0" >test_nvmrc + echo "18.0.0; touch /tmp/nvm-auto-use-malicious-test" >malicious_nvmrc + + # Test valid source + nvm_security validate_source test_nvmrc + and echo "โœ… Valid source file accepted" + or echo "โŒ Valid source file rejected" + + # Test malicious source + nvm_security validate_source malicious_nvmrc + set -l status_code $status + test $status_code -ne 0 + and echo "โœ… Malicious source file rejected" + or echo "โŒ Malicious source file accepted" + + # Cleanup + rm -f test_nvmrc malicious_nvmrc + + return 0 +end + +function test_vulnerability_check + echo "Testing vulnerability checking..." + + # Test known vulnerable version (if any in our test data) + nvm_security check_cve "16.0.0" + set -l status_code $status + test $status_code -ne 0 + and echo "โœ… Known vulnerable version flagged" + or echo "โ„น๏ธ No vulnerability data for test version" + + # Test presumably safe version + nvm_security check_cve "18.17.0" + and echo "โœ… Safe version check completed" + or echo "โ„น๏ธ Vulnerability check completed with warnings" + + return 0 +end + +function main + setup_test_env + + set -l failed 0 + + test_version_validation; or set failed (math "$failed + 1") + test_security_policies; or set failed (math "$failed + 1") + test_version_comparison; or set failed (math "$failed + 1") + test_source_validation; or set failed (math "$failed + 1") + test_vulnerability_check; or set failed (math "$failed + 1") + + cleanup_test_env + + if test $failed -eq 0 + echo "๐ŸŽ‰ All security tests passed!" + return 0 + else + echo "๐Ÿ’ฅ $failed security test(s) failed" + return 1 + end +end + +main