Andromeda provides implementations of standard web APIs to ensure compatibility with existing web code and standards. This includes event handling, text encoding, and other essential web platform features.

Overview

The Web APIs in Andromeda follow WHATWG specifications and provide a familiar programming model for developers coming from browser environments.

DOMException API

The DOMException API provides a standardized way to represent errors that occur in web APIs, following the WHATWG DOM specification.

DOMException Class

DOMException represents an exception that occurs as a result of calling a method or accessing a property of a web API.

Constructor

new DOMException(message?: string, name?: string)

Creates a new DOMException object.

Parameters:

  • message (optional): Human-readable description of the error
  • name (optional): The name of the error (default: "Error")

Properties

  • name: The name of the exception
  • message: The error message
  • code: Legacy numeric error code (for compatibility)

Standard Exception Names

DOMException supports all standard exception names as defined in the WHATWG DOM specification:

// Network-related errors
const networkError = new DOMException("Network request failed", "NetworkError");
const timeoutError = new DOMException("Request timed out", "TimeoutError");

// Security-related errors
const securityError = new DOMException("Access denied", "SecurityError");

// Invalid state or argument errors
const invalidStateError = new DOMException(
  "Invalid state",
  "InvalidStateError",
);
const syntaxError = new DOMException("Invalid syntax", "SyntaxError");

// Data errors
const dataError = new DOMException("Invalid data", "DataError");
const quotaExceededError = new DOMException(
  "Quota exceeded",
  "QuotaExceededError",
);

// Operation errors
const abortError = new DOMException("Operation aborted", "AbortError");
const notSupportedError = new DOMException(
  "Operation not supported",
  "NotSupportedError",
);

Usage Examples

Basic Error Creation

// Create a basic DOMException
const basicError = new DOMException();
console.log(basicError.name); // "Error"
console.log(basicError.message); // ""

// Create with message
const messageError = new DOMException("Something went wrong");
console.log(messageError.name); // "Error"
console.log(messageError.message); // "Something went wrong"

// Create with message and name
const namedError = new DOMException("Invalid operation", "InvalidStateError");
console.log(namedError.name); // "InvalidStateError"
console.log(namedError.message); // "Invalid operation"

Error Handling in APIs

class WebAPIExample {
  private isInitialized = false;

  initialize() {
    this.isInitialized = true;
  }

  performOperation() {
    if (!this.isInitialized) {
      throw new DOMException(
        "Cannot perform operation before initialization",
        "InvalidStateError",
      );
    }

    // Simulate network operation
    if (Math.random() < 0.1) {
      throw new DOMException(
        "Network connection failed",
        "NetworkError",
      );
    }

    return "Operation completed successfully";
  }

  parseData(data: string) {
    if (!data || data.trim().length === 0) {
      throw new DOMException(
        "Data cannot be empty",
        "SyntaxError",
      );
    }

    try {
      return JSON.parse(data);
    } catch (error) {
      throw new DOMException(
        "Invalid JSON data format",
        "SyntaxError",
      );
    }
  }
}

// Usage with proper error handling
const api = new WebAPIExample();

try {
  api.performOperation(); // Will throw InvalidStateError
} catch (error) {
  if (error instanceof DOMException) {
    console.error(`${error.name}: ${error.message}`);

    // Handle specific error types
    switch (error.name) {
      case "InvalidStateError":
        console.log("Initializing API...");
        api.initialize();
        break;
      case "NetworkError":
        console.log("Retrying network operation...");
        break;
      default:
        console.log("Unknown error occurred");
    }
  }
}

Custom Error Hierarchy

class APIError extends DOMException {
  constructor(message: string, name: string = "APIError") {
    super(message, name);
  }
}

class ValidationError extends APIError {
  constructor(field: string, value: any) {
    super(`Invalid value for field '${field}': ${value}`, "ValidationError");
  }
}

class AuthenticationError extends APIError {
  constructor(message: string = "Authentication failed") {
    super(message, "SecurityError");
  }
}

// Usage
function validateUser(userData: any) {
  if (!userData.email || !userData.email.includes("@")) {
    throw new ValidationError("email", userData.email);
  }

  if (!userData.password || userData.password.length < 8) {
    throw new ValidationError("password", "too short");
  }

  if (!userData.token) {
    throw new AuthenticationError("Missing authentication token");
  }
}

try {
  validateUser({ email: "invalid", password: "123" });
} catch (error) {
  if (error instanceof DOMException) {
    console.error(`Validation failed - ${error.name}: ${error.message}`);
  }
}

Integration with Fetch API

async function safeFetch(
  url: string,
  options?: RequestInit,
): Promise<Response> {
  try {
    const response = await fetch(url, options);

    if (!response.ok) {
      // Convert HTTP errors to DOMExceptions
      switch (response.status) {
        case 400:
          throw new DOMException("Bad Request", "SyntaxError");
        case 401:
          throw new DOMException("Unauthorized", "SecurityError");
        case 403:
          throw new DOMException("Forbidden", "SecurityError");
        case 404:
          throw new DOMException("Not Found", "NotFoundError");
        case 408:
          throw new DOMException("Request Timeout", "TimeoutError");
        case 429:
          throw new DOMException("Too Many Requests", "QuotaExceededError");
        case 500:
          throw new DOMException("Internal Server Error", "NetworkError");
        default:
          throw new DOMException(`HTTP ${response.status}`, "NetworkError");
      }
    }

    return response;
  } catch (error) {
    // Convert network errors to DOMExceptions
    if (error instanceof TypeError) {
      throw new DOMException("Network connection failed", "NetworkError");
    }

    if (error.name === "AbortError") {
      throw new DOMException("Request was aborted", "AbortError");
    }

    // Re-throw DOMExceptions as-is
    if (error instanceof DOMException) {
      throw error;
    }

    // Convert other errors
    throw new DOMException(error.message || "Unknown error", "UnknownError");
  }
}

// Usage
try {
  const response = await safeFetch("https://api.example.com/data");
  const data = await response.json();
  console.log(data);
} catch (error) {
  if (error instanceof DOMException) {
    console.error(`API Error - ${error.name}: ${error.message}`);

    // Handle specific error types
    switch (error.name) {
      case "NetworkError":
        console.log("Check your internet connection");
        break;
      case "SecurityError":
        console.log("Check your authentication credentials");
        break;
      case "TimeoutError":
        console.log("Request timed out, try again");
        break;
    }
  }
}

Error Categories

DOMExceptions can be categorized by their purpose:

Network Errors

  • NetworkError: General network failures
  • TimeoutError: Request timeouts
  • AbortError: Cancelled operations

Security Errors

  • SecurityError: Access denied or authentication failures
  • NotAllowedError: Operation not permitted

State Errors

  • InvalidStateError: Object in wrong state for operation
  • InvalidAccessError: Invalid access to object

Data Errors

  • SyntaxError: Invalid syntax or format
  • DataError: Invalid data
  • QuotaExceededError: Storage or resource limits exceeded

Operational Errors

  • NotSupportedError: Feature not supported
  • NotFoundError: Resource not found
  • OperationError: General operation failure

Event API

Event Class

The Event API provides a standard way to handle events, following the WHATWG HTML Living Standard for event handling.

Constructor

new Event(type: string, eventInitDict?: EventInit)

Creates a new Event object.

Parameters:

  • type: The event type (e.g., "click", "load", "custom")
  • eventInitDict (optional): Configuration object with properties:
    • bubbles: Whether the event bubbles (default: false)
    • cancelable: Whether the event can be canceled (default: false)
    • composed: Whether the event will trigger listeners outside of a shadow root (default: false)

Properties

  • type: The event type string
  • target: The event target (read-only)
  • currentTarget: The current target during event propagation (read-only)
  • eventPhase: The current phase of event propagation (read-only)
  • bubbles: Whether the event bubbles (read-only)
  • cancelable: Whether the event can be canceled (read-only)
  • defaultPrevented: Whether preventDefault() was called (read-only)
  • isTrusted: Whether the event was generated by user action (read-only)
  • timeStamp: The time when the event was created (read-only)

Methods

preventDefault(): void

Cancels the event's default action if it's cancelable.

const event = new Event("submit", { cancelable: true });
event.preventDefault();
console.log(event.defaultPrevented); // true
stopPropagation(): void

Stops the event from propagating further through the event flow.

const event = new Event("click", { bubbles: true });
event.stopPropagation();
// Event will not bubble to parent elements
stopImmediatePropagation(): void

Stops the event from propagating and prevents other listeners on the same element from being called.

element.addEventListener("click", (event) => {
  event.stopImmediatePropagation();
  // Other click listeners on this element won't be called
});

Event Usage Examples

Creating Custom Events

// Create a simple custom event
const customEvent = new Event("myCustomEvent");

// Create an event with options
const configurableEvent = new Event("userAction", {
  bubbles: true,
  cancelable: true,
});

// Check event properties
console.log(customEvent.type); // "myCustomEvent"
console.log(customEvent.bubbles); // false
console.log(configurableEvent.bubbles); // true

Event Handling Patterns

// Event listener function
function handleCustomEvent(event: Event) {
  console.log(`Received event: ${event.type}`);

  if (event.cancelable) {
    event.preventDefault();
    console.log("Default action prevented");
  }
}

// Create and dispatch custom event
const event = new Event("dataProcessed", {
  bubbles: false,
  cancelable: true,
});

// Simulate event handling
handleCustomEvent(event);

Event State Management

class EventProcessor {
  processEvent(event: Event): boolean {
    // Check if event is valid for processing
    if (!event.isTrusted) {
      console.warn("Untrusted event, skipping processing");
      return false;
    }

    // Process different event phases
    switch (event.eventPhase) {
      case Event.CAPTURING_PHASE:
        console.log("Event in capturing phase");
        break;
      case Event.AT_TARGET:
        console.log("Event at target");
        break;
      case Event.BUBBLING_PHASE:
        console.log("Event in bubbling phase");
        break;
    }

    return true;
  }
}

Text Encoding API

The Text Encoding API provides utilities for encoding and decoding text, implementing the WHATWG Encoding Standard.

TextEncoder

Encodes strings into UTF-8 byte sequences.

TextEncoder Constructor

new TextEncoder();

Creates a new TextEncoder instance. Always uses UTF-8 encoding.

TextEncoder Properties

  • encoding: Always returns "utf-8"

TextEncoder Methods

encode(input?: string): Uint8Array

Encodes a string into a Uint8Array of UTF-8 bytes.

const encoder = new TextEncoder();

// Encode ASCII text
const ascii = encoder.encode("Hello, World!");
console.log(ascii); // Uint8Array with UTF-8 bytes

// Encode Unicode text
const unicode = encoder.encode("你好世界");
console.log(unicode); // Uint8Array with UTF-8 bytes for Chinese characters

// Empty string
const empty = encoder.encode("");
console.log(empty.length); // 0
encodeInto(source: string, destination: Uint8Array): TextEncoderEncodeIntoResult

Encodes a string into an existing Uint8Array buffer.

const encoder = new TextEncoder();
const buffer = new Uint8Array(50);

const result = encoder.encodeInto("Hello, 世界!", buffer);
console.log(result.read); // Number of characters read
console.log(result.written); // Number of bytes written

TextDecoder

Decodes byte sequences into strings using specified encoding.

TextDecoder Constructor

new TextDecoder(label?: string, options?: TextDecoderOptions)

Creates a new TextDecoder instance.

Parameters:

  • label (optional): The encoding label (default: "utf-8")
  • options (optional): Configuration object:
    • fatal: Whether to throw on invalid sequences (default: false)
    • ignoreBOM: Whether to ignore byte order mark (default: false)

TextDecoder Properties

  • encoding: The encoding being used
  • fatal: Whether fatal error mode is enabled
  • ignoreBOM: Whether BOM is ignored

TextDecoder Methods

decode(input?: BufferSource, options?: TextDecodeOptions): string

Decodes a byte sequence into a string.

const decoder = new TextDecoder();

// Decode UTF-8 bytes
const bytes = new Uint8Array([72, 101, 108, 108, 111]); // "Hello"
const text = decoder.decode(bytes);
console.log(text); // "Hello"

// Streaming decode
const stream = { stream: true };
const partial1 = decoder.decode(bytes1, stream);
const partial2 = decoder.decode(bytes2, stream);
const final = decoder.decode(); // Finish stream

Text Encoding Examples

Basic Encoding/Decoding

const encoder = new TextEncoder();
const decoder = new TextDecoder();

// Round-trip encoding
const originalText = "Hello, 世界! 🌍";
const encoded = encoder.encode(originalText);
const decoded = decoder.decode(encoded);

console.log(originalText === decoded); // true
console.log(encoded.length); // Number of bytes (varies with Unicode)

Working with Different Text Types

const encoder = new TextEncoder();
const decoder = new TextDecoder();

// ASCII text
const ascii = "Simple ASCII text";
const asciiBytes = encoder.encode(ascii);
console.log(`ASCII: ${ascii.length} chars → ${asciiBytes.length} bytes`);

// Unicode text (2-byte characters)
const latin = "Café résumé naïve";
const latinBytes = encoder.encode(latin);
console.log(`Latin: ${latin.length} chars → ${latinBytes.length} bytes`);

// Unicode text (3-byte characters)
const chinese = "你好世界";
const chineseBytes = encoder.encode(chinese);
console.log(`Chinese: ${chinese.length} chars → ${chineseBytes.length} bytes`);

// Emoji (4-byte characters)
const emoji = "🌍🚀⭐";
const emojiBytes = encoder.encode(emoji);
console.log(`Emoji: ${emoji.length} chars → ${emojiBytes.length} bytes`);

Streaming Text Processing

const decoder = new TextDecoder();

function processTextStream(chunks: Uint8Array[]): string {
  let result = "";

  // Process all chunks except the last with streaming
  for (let i = 0; i < chunks.length - 1; i++) {
    result += decoder.decode(chunks[i], { stream: true });
  }

  // Process final chunk without streaming
  if (chunks.length > 0) {
    result += decoder.decode(chunks[chunks.length - 1]);
  }

  return result;
}

// Example usage with chunked data
const textData = "This is a long text that might be received in chunks";
const encoded = new TextEncoder().encode(textData);

// Split into chunks
const chunk1 = encoded.slice(0, 10);
const chunk2 = encoded.slice(10, 20);
const chunk3 = encoded.slice(20);

const reconstructed = processTextStream([chunk1, chunk2, chunk3]);
console.log(textData === reconstructed); // true

Error Handling

// Fatal mode - throws on invalid sequences
const fatalDecoder = new TextDecoder("utf-8", { fatal: true });

try {
  // Invalid UTF-8 sequence
  const invalidBytes = new Uint8Array([0xFF, 0xFE, 0xFD]);
  const result = fatalDecoder.decode(invalidBytes);
} catch (error) {
  console.error("Decoding failed:", error.message);
}

// Non-fatal mode - replaces invalid sequences
const tolerantDecoder = new TextDecoder("utf-8", { fatal: false });
const invalidBytes = new Uint8Array([0xFF, 0xFE, 0xFD]);
const result = tolerantDecoder.decode(invalidBytes);
console.log(result); // Contains replacement characters

Performance Optimization

// Reuse encoder/decoder instances
const globalEncoder = new TextEncoder();
const globalDecoder = new TextDecoder();

function efficientTextProcessing(texts: string[]): Uint8Array[] {
  // Reuse the same encoder instance
  return texts.map((text) => globalEncoder.encode(text));
}

// Pre-allocate buffers for encodeInto
const encoder = new TextEncoder();
const buffer = new Uint8Array(1024); // Reusable buffer

function encodeWithBuffer(text: string): Uint8Array {
  const result = encoder.encodeInto(text, buffer);
  return buffer.slice(0, result.written);
}

Validation and Testing

Text Encoding Validation

function validateTextEncoding(): boolean {
  const encoder = new TextEncoder();
  const decoder = new TextDecoder();

  // Test cases
  const testCases = [
    "Hello, World!", // ASCII
    "Café résumé", // Latin-1 supplement
    "你好世界", // CJK
    "🌍🚀⭐", // Emoji
    "", // Empty string
    "\0\x01\x02", // Control characters
  ];

  for (const testCase of testCases) {
    const encoded = encoder.encode(testCase);
    const decoded = decoder.decode(encoded);

    if (testCase !== decoded) {
      console.error(`Failed for: "${testCase}"`);
      return false;
    }
  }

  console.log("✓ All text encoding tests passed");
  return true;
}

// Run validation
validateTextEncoding();

Event System Testing

function testEventSystem(): boolean {
  // Test basic event creation
  const event1 = new Event("test");
  if (event1.type !== "test" || event1.bubbles !== false) {
    return false;
  }

  // Test event with options
  const event2 = new Event("custom", { bubbles: true, cancelable: true });
  if (!event2.bubbles || !event2.cancelable) {
    return false;
  }

  // Test preventDefault
  event2.preventDefault();
  if (!event2.defaultPrevented) {
    return false;
  }

  console.log("✓ All event tests passed");
  return true;
}

// Run validation
testEventSystem();

Browser Compatibility

Andromeda's Web APIs are designed to be compatible with standard browser implementations:

  • Event API: Follows WHATWG HTML Living Standard
  • Text Encoding: Implements WHATWG Encoding Standard
  • Standard Behavior: Compatible error handling and edge cases
  • Performance: Optimized for server-side and CLI usage

Best Practices

Text Encoding

  1. Reuse Instances: Create encoder/decoder instances once and reuse them
  2. Handle Errors: Use fatal mode when data integrity is critical
  3. Stream Processing: Use streaming for large text data
  4. Buffer Management: Pre-allocate buffers for better performance

Events

  1. Use Standard Types: Use well-known event types when possible
  2. Configure Appropriately: Set bubbles and cancelable based on needs
  3. Handle Errors: Check event state before processing
  4. Performance: Avoid creating excessive event objects
Found an issue with this page?Edit on GitHub
Last updated: