Andromeda implements core web-platform APIs from the WHATWG and W3C specifications so existing code can run with minimal change.

Events

Andromeda implements EventTarget, Event, and CustomEvent following the WHATWG DOM specification.

const target = new EventTarget();

target.addEventListener("greet", (event) => {
  console.log(event.type, (event as CustomEvent).detail);
});

target.dispatchEvent(new CustomEvent("greet", { detail: { name: "Alice" } }));

Listener options: { once, passive, signal }. The signal option ties the listener to an AbortSignal so calling controller.abort() removes it.

const controller = new AbortController();
target.addEventListener("greet", handler, { signal: controller.signal });
controller.abort(); // removes the listener

AbortController and AbortSignal

const controller = new AbortController();

setTimeout(() => controller.abort("Timed out"), 1000);

try {
  await fetch("https://example.com/slow", { signal: controller.signal });
} catch (err) {
  if (err instanceof DOMException && err.name === "AbortError") {
    console.log("Aborted:", controller.signal.reason);
  }
}

Static helpers:

const aborted = AbortSignal.abort("immediate");
const timeout = AbortSignal.timeout(5000);
const any = AbortSignal.any([signal1, signal2]);

TextEncoder / TextDecoder

UTF-8 encode and decode following the WHATWG Encoding standard.

const encoder = new TextEncoder();
const bytes = encoder.encode("Hello, 世界!");
console.log(bytes); // Uint8Array
console.log(encoder.encoding); // "utf-8"

// encodeInto writes into an existing buffer
const buffer = new Uint8Array(16);
const { read, written } = encoder.encodeInto("hi", buffer);

const decoder = new TextDecoder("utf-8", { fatal: false, ignoreBOM: false });
console.log(decoder.decode(bytes)); // "Hello, 世界!"

The decoder supports streaming via { stream: true } on decode().

structuredClone

const original = { a: 1, b: [2, 3], when: new Date() };
const copy = structuredClone(original);

// Circular references are handled
const circular: any = {};
circular.self = circular;
structuredClone(circular);

// Transfers move ownership instead of copying
const buf = new ArrayBuffer(8);
const moved = structuredClone(buf, { transfer: [buf] });

Base64 (atob / btoa)

const encoded = btoa("Hello, World!"); // "SGVsbG8sIFdvcmxkIQ=="
const decoded = atob(encoded); // "Hello, World!"

Both functions operate on the binary-string (ISO-8859-1) representation per the HTML spec. For arbitrary bytes, encode through TextEncoder first.

URL Encoding

encodeURIComponent, decodeURIComponent, encodeURI, and decodeURI are provided as globals matching the WHATWG / ECMAScript behavior:

encodeURIComponent("hello world?"); // "hello%20world%3F"
decodeURIComponent("hello%20world"); // "hello world"

Console

See the Console API page. Andromeda implements console.log, console.error, console.warn, console.info, console.debug, console.trace, console.group, console.groupEnd, console.table, console.time / timeEnd, console.dir, and CSS-style formatting with %c.

DOMException

throw new DOMException("Invalid state", "InvalidStateError");

DOMException supports the standard error names (AbortError, NetworkError, NotFoundError, NotSupportedError, QuotaExceededError, SecurityError, SyntaxError, TimeoutError, etc.).

Andromeda implements the Navigator surface enough for feature detection:

navigator.userAgent; // "Andromeda/0.1.10 …"
navigator.platform;
navigator.appName; // "Netscape" (legacy)
navigator.appCodeName; // "Mozilla" (legacy)
navigator.appVersion;
navigator.product; // "Gecko"
navigator.productSub;
navigator.vendor;
navigator.vendorSub;

// User-Agent Client Hints
navigator.userAgentData.brands;
navigator.userAgentData.platform;
navigator.userAgentData.mobile;
await navigator.userAgentData.getHighEntropyValues(["architecture", "model"]);

navigator.locks is documented separately on the Web Locks API page.

queueMicrotask

queueMicrotask(() => {
  // runs after the current synchronous job, before the next task
});

Timers

setTimeout, setInterval, clearTimeout, clearInterval are covered in the Time API.

Patterns

Custom event bus

class Bus extends EventTarget {
  emit<T>(name: string, detail: T) {
    this.dispatchEvent(new CustomEvent(name, { detail }));
  }
  on<T>(name: string, handler: (detail: T) => void) {
    this.addEventListener(name, (e) => handler((e as CustomEvent).detail));
  }
}

const bus = new Bus();
bus.on<string>("hello", (msg) => console.log("got:", msg));
bus.emit("hello", "world");

Cancellable async work

async function withTimeout<T>(
  work: (signal: AbortSignal) => Promise<T>,
  ms: number,
) {
  const signal = AbortSignal.timeout(ms);
  return work(signal);
}

const data = await withTimeout(
  (signal) => fetch("https://example.com", { signal }).then((r) => r.text()),
  3000,
);

See Also

Found an issue with this page?Edit on GitHub
Last updated: