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 errorname
(optional): The name of the error (default: "Error")
Properties
name
: The name of the exceptionmessage
: The error messagecode
: 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 failuresTimeoutError
: Request timeoutsAbortError
: Cancelled operations
Security Errors
SecurityError
: Access denied or authentication failuresNotAllowedError
: Operation not permitted
State Errors
InvalidStateError
: Object in wrong state for operationInvalidAccessError
: Invalid access to object
Data Errors
SyntaxError
: Invalid syntax or formatDataError
: Invalid dataQuotaExceededError
: Storage or resource limits exceeded
Operational Errors
NotSupportedError
: Feature not supportedNotFoundError
: Resource not foundOperationError
: 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 stringtarget
: 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 usedfatal
: Whether fatal error mode is enabledignoreBOM
: 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
- Reuse Instances: Create encoder/decoder instances once and reuse them
- Handle Errors: Use fatal mode when data integrity is critical
- Stream Processing: Use streaming for large text data
- Buffer Management: Pre-allocate buffers for better performance
Events
- Use Standard Types: Use well-known event types when possible
- Configure Appropriately: Set bubbles and cancelable based on needs
- Handle Errors: Check event state before processing
- Performance: Avoid creating excessive event objects
Related APIs
- Console API - For debugging and logging
- Performance API - For timing and performance measurement
- Fetch API - For network requests using standard web APIs
- URL API - For URL parsing and manipulation