import * as plugins from './tstest.plugins.js'; import type { Runtime } from './tstest.classes.runtime.parser.js'; import { TapParser } from './tstest.classes.tap.parser.js'; /** * Runtime-specific configuration options */ export interface RuntimeOptions { /** * Environment variables to pass to the runtime */ env?: Record; /** * Additional command-line arguments */ extraArgs?: string[]; /** * Working directory for test execution */ cwd?: string; /** * Timeout in milliseconds (0 = no timeout) */ timeout?: number; } /** * Deno-specific configuration options */ export interface DenoOptions extends RuntimeOptions { /** * Permissions to grant to Deno * Default: ['--allow-read', '--allow-env'] */ permissions?: string[]; /** * Path to deno.json config file */ configPath?: string; /** * Path to import map file */ importMap?: string; } /** * Chromium-specific configuration options */ export interface ChromiumOptions extends RuntimeOptions { /** * Chromium launch arguments */ launchArgs?: string[]; /** * Headless mode (default: true) */ headless?: boolean; /** * Port range for HTTP server */ portRange?: { min: number; max: number }; } /** * Command configuration returned by createCommand() */ export interface RuntimeCommand { /** * The main command executable (e.g., 'node', 'deno', 'bun') */ command: string; /** * Command-line arguments */ args: string[]; /** * Environment variables */ env?: Record; /** * Working directory */ cwd?: string; } /** * Runtime availability check result */ export interface RuntimeAvailability { /** * Whether the runtime is available */ available: boolean; /** * Version string if available */ version?: string; /** * Error message if not available */ error?: string; } /** * Abstract base class for runtime adapters * Each runtime (Node, Chromium, Deno, Bun) implements this interface */ export abstract class RuntimeAdapter { /** * Runtime identifier */ abstract readonly id: Runtime; /** * Human-readable display name */ abstract readonly displayName: string; /** * Check if this runtime is available on the system * @returns Availability information including version */ abstract checkAvailable(): Promise; /** * Create the command configuration for executing a test * @param testFile - Absolute path to the test file * @param options - Runtime-specific options * @returns Command configuration */ abstract createCommand(testFile: string, options?: RuntimeOptions): RuntimeCommand; /** * Execute a test file and return a TAP parser * @param testFile - Absolute path to the test file * @param index - Test index (for display) * @param total - Total number of tests (for display) * @param options - Runtime-specific options * @returns TAP parser with test results */ abstract run( testFile: string, index: number, total: number, options?: RuntimeOptions ): Promise; /** * Get the default options for this runtime * Can be overridden by subclasses */ protected getDefaultOptions(): RuntimeOptions { return { timeout: 0, extraArgs: [], env: {}, }; } /** * Merge user options with defaults */ protected mergeOptions(userOptions?: T): T { const defaults = this.getDefaultOptions(); return { ...defaults, ...userOptions, env: { ...defaults.env, ...userOptions?.env }, extraArgs: [...(defaults.extraArgs || []), ...(userOptions?.extraArgs || [])], } as T; } } /** * Registry for runtime adapters * Manages all available runtime implementations */ export class RuntimeAdapterRegistry { private adapters: Map = new Map(); /** * Register a runtime adapter */ register(adapter: RuntimeAdapter): void { this.adapters.set(adapter.id, adapter); } /** * Get an adapter by runtime ID */ get(runtime: Runtime): RuntimeAdapter | undefined { return this.adapters.get(runtime); } /** * Get all registered adapters */ getAll(): RuntimeAdapter[] { return Array.from(this.adapters.values()); } /** * Check which runtimes are available on the system */ async checkAvailability(): Promise> { const results = new Map(); for (const [runtime, adapter] of this.adapters) { const availability = await adapter.checkAvailable(); results.set(runtime, availability); } return results; } /** * Get adapters for a list of runtimes, in order * @param runtimes - Ordered list of runtimes * @returns Adapters in the same order, skipping any that aren't registered */ getAdaptersForRuntimes(runtimes: Runtime[]): RuntimeAdapter[] { const adapters: RuntimeAdapter[] = []; for (const runtime of runtimes) { const adapter = this.get(runtime); if (adapter) { adapters.push(adapter); } } return adapters; } }