Skip to content

Architecture Guide

This guide explains ScriptIt's internal architecture, focusing on the unified script execution system and environment variable prompting capabilities introduced in recent versions.

Overview

ScriptIt is built with a modular architecture that separates concerns and enables code reuse across different interfaces (CLI and TUI). The core principle is to have shared execution logic with pluggable interface-specific components.

Core Architecture

┌─────────────────────────────────────────────────────────────────┐
│                        ScriptIt Architecture                    │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────┐                           ┌─────────────┐      │
│  │    CLI      │                           │    TUI      │      │
│  │ Interface   │                           │ Interface   │      │
│  └─────────────┘                           └─────────────┘      │
│        │                                         │              │
│        │                                         │              │
│  ┌─────────────┐                           ┌─────────────┐      │
│  │ CLI         │                           │ TUI         │      │
│  │ Prompter    │                           │ Prompter    │      │
│  └─────────────┘                           └─────────────┘      │
│        │                                         │              │
│        └─────────────────┬───────────────────────┘              │
│                          │                                      │
│                    ┌─────────────┐                              │
│                    │ Core Script │                              │
│                    │ Execution   │                              │
│                    │ Engine      │                              │
│                    └─────────────┘                              │
│                          │                                      │
│            ┌─────────────┼─────────────┐                        │
│            │             │             │                        │
│     ┌─────────────┐ ┌─────────────┐ ┌─────────────┐             │
│     │ Environment │ │ Script      │ │ Console     │             │
│     │ Management  │ │ Executor    │ │ Interceptor │             │
│     └─────────────┘ └─────────────┘ └─────────────┘             │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Core Components

1. Script Execution Engine (src/core/script-executor.ts)

The heart of ScriptIt is the unified script execution engine that handles:

  • Script Loading - Dynamic import with cache busting
  • Lifecycle Management - tearUp → execute → tearDown
  • Environment Preparation - Variable collection and injection
  • Console Interception - Colored output and logging
  • Error Handling - Comprehensive error reporting

Key Function: executeScriptWithEnvironment()

typescript
export async function executeScriptWithEnvironment(
  options: ScriptExecutionOptions
): Promise<unknown>

This function orchestrates the entire script execution process:

  1. Script Validation - Ensures script file exists
  2. Environment Discovery - Loads script to detect variable requirements
  3. Variable Collection - Uses appropriate prompter for missing variables
  4. Environment Assembly - Merges all environment sources with proper precedence
  5. Script Execution - Runs script with enhanced context
  6. Result Handling - Processes and returns script results

2. Environment Prompter Interface

The prompter interface enables different UIs to collect environment variables:

typescript
export interface EnvironmentPrompter {
  promptForVariables(
    variables: VariableDefinition[],
    existingEnv: Record<string, string | undefined>
  ): Promise<Record<string, string>>;
}

CLI Prompter (CLIEnvironmentPrompter)

Uses Node.js readline for terminal-based prompting:

  • Regular Input - Standard readline interface
  • Password Input - Raw mode with character masking
  • Interrupt Handling - Graceful Ctrl+C handling
  • Progress Feedback - Colored status messages

TUI Prompter (TUIEnvironmentPrompter)

Uses blessed widgets for modal-based prompting:

  • Modal Dialogs - Centered, bordered input dialogs
  • Visual Feedback - Icons and color coding for variable types
  • Keyboard Navigation - Enter/Escape/Tab controls
  • Integration - Seamless integration with TUI layout

3. Configuration System

Centralized configuration loading and management:

typescript
interface RunnerConfig {
  scriptsDir: string;
  tmpDir: string;
  envFiles: string[];
  excludePatterns: string[];
  defaultParams: Record<string, unknown>;
  loadedConfigPath?: string;
}

Environment Variable System

Variable Definition

Scripts can define required variables in multiple formats:

typescript
// Full definition with custom prompts and types
export const variables = [
  { name: 'API_KEY', message: 'Enter your API key:', type: 'password' },
  { name: 'USER_NAME', message: 'Enter your username:', type: 'input' },
];

// Shorthand definition for quick setup
export const variables = ['API_KEY', 'DATABASE_URL', 'SECRET_TOKEN'];

Variable Collection Flow

┌─────────────────────────────────────────────────────────────────┐
│                Variable Collection Process                       │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  1. Load Script Module                                          │
│     └─ Extract `variables` export                               │
│                                                                 │
│  2. Merge Variable Sources                                      │
│     ├─ CLI --env-prompts                                        │
│     ├─ Script variables export                                  │
│     └─ Remove duplicates (CLI takes precedence)                 │
│                                                                 │
│  3. Load Base Environment                                       │
│     ├─ .env files                                              │
│     ├─ Config defaultParams                                     │
│     └─ CLI --env arguments                                      │
│                                                                 │
│  4. Filter Missing Variables                                    │
│     └─ Only prompt for variables not already set                │
│                                                                 │
│  5. Prompt for Missing Variables                                │
│     ├─ CLI: readline prompting                                  │
│     └─ TUI: modal dialog prompting                              │
│                                                                 │
│  6. Assemble Final Environment                                  │
│     ├─ Base environment                                         │
│     ├─ CLI-provided values                                      │
│     ├─ Prompted values                                          │
│     └─ Set in process.env                                       │
│                                                                 │
│  7. Execute Script                                              │
│     └─ All variables available in context.env                   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Environment Precedence

ScriptIt follows a clear precedence order for environment variables:

  1. Prompted Variables - Collected interactively (Highest priority)
  2. CLI Arguments (--env NAME=value) - CLI-provided environment variables
  3. Config Default Params - From runner.config.js
  4. Environment Files - .env, .env.local, etc. (Lowest priority)

Console Interception

ScriptIt provides colored console output through a sophisticated interception system:

Console Enhancement

typescript
interface EnhancedContext extends ScriptContext {
  console: {
    log: (...args: unknown[]) => void;    // White
    info: (...args: unknown[]) => void;   // Blue
    warn: (...args: unknown[]) => void;   // Yellow
    error: (...args: unknown[]) => void;  // Red
    debug: (...args: unknown[]) => void;  // Gray
  };
}

Safe Serialization

The console interceptor includes safe serialization to prevent circular reference errors:

  • Primitive Types - Direct string conversion
  • Objects - Safe JSON serialization with fallbacks
  • Functions - [Function] placeholder
  • Circular References - [Object] placeholder
  • Error Objects - Special error message extraction

Interface Integration

CLI Integration (src/cli.ts)

The CLI integrates with the core execution engine through:

  1. Centralized Options - Reusable option definitions in CLI_OPTIONS
  2. Shared Execution - Uses executeScriptWithEnvironment()
  3. CLI Prompter - Readline-based environment variable collection
  4. Error Handling - Process exit codes and colored error messages

TUI Integration (src/ui/blessed-ui.ts)

The TUI integrates through:

  1. Modal Prompting - Blessed-based environment variable collection
  2. Real-time Logging - Live output streaming to TUI panels
  3. Shared Configuration - Uses same config system as CLI
  4. Event Handling - Keyboard navigation and script execution

Error Handling

Hierarchical Error Handling

  1. Script Level - Script-specific errors with context
  2. Execution Level - Environment and configuration errors
  3. Interface Level - CLI/TUI specific error display
  4. Process Level - Exit codes and termination

Error Types

  • Configuration Errors - Invalid config or missing files
  • Environment Errors - Missing directories or variables
  • Script Errors - Runtime errors within scripts
  • Interface Errors - TUI/CLI specific issues

Performance Considerations

Script Loading

  • Dynamic Imports - Fresh script loading with cache busting
  • Lazy Loading - Scripts loaded only when needed
  • Error Isolation - Script errors don't crash the system

Memory Management

  • Variable Cleanup - Prompted variables cleared after execution
  • Module Caching - Careful cache management for script updates
  • Resource Cleanup - Proper cleanup of temporary resources

Extension Points

The architecture is designed for extensibility:

New Prompter Implementations

typescript
class WebUIPrompter implements EnvironmentPrompter {
  async promptForVariables(
    variables: VariableDefinition[],
    existingEnv: Record<string, string | undefined>
  ): Promise<Record<string, string>> {
    // Web-based prompting implementation
  }
}

Custom Execution Contexts

typescript
const customExecutor = createScriptExecutorInstance({
  consoleInterception: {
    enabled: true,
    logFunction: customLogger,
    useColors: false
  }
});

Configuration Extensions

typescript
interface ExtendedConfig extends RunnerConfig {
  customFeatures: {
    enableMetrics: boolean;
    apiEndpoint: string;
  };
}

Best Practices

For Core Development

  1. Interface Abstraction - Keep UI-specific code out of core
  2. Error Propagation - Let errors bubble up with context
  3. Configuration Validation - Validate config early and clearly
  4. Type Safety - Use TypeScript interfaces consistently

For Interface Development

  1. Prompter Implementation - Implement consistent prompting behavior
  2. Error Display - Provide clear, actionable error messages
  3. User Feedback - Show progress and status information
  4. Resource Cleanup - Clean up UI resources properly

For Script Authors

  1. Variable Declaration - Declare required variables explicitly
  2. Error Handling - Provide meaningful error messages
  3. Context Usage - Use enhanced console for better output
  4. Type Annotations - Include TypeScript types for better DX

Migration Guide

From Previous Versions

The unified architecture is backward compatible, but new features are available:

typescript
// Old approach (still works)
export async function execute(context) {
  console.log('Hello world');
}

// New approach (recommended)
export const variables = ['API_KEY'];

export async function execute(context) {
  const console = context.console || global.console;
  console.info('Using enhanced console with colors');
  console.log('API Key:', context.env.API_KEY ? '***' : 'Not set');
}

Released under the MIT License.