webapi

The webapi package groups browser-flavoured APIs that sit alongside the DOM surface.

console

from domonic.webapi.console import console
console.log("Hello World")

encoding

from domonic.webapi.encoding import TextEncoder, TextDecoder

encoded = TextEncoder().encode("hello")
decoded = TextDecoder("utf-8").decode(encoded)
print(decoded)

fetch

from domonic.webapi.fetch import fetch

# fetch() returns a Promise, same as in the browser
fetch("https://example.com").then(lambda response: print(response.text()[:15]))
# <!doctype html>

XHR

XMLHttpRequest and FormData provide browser-shaped request helpers for code that was originally written against web APIs.

from domonic.webapi.xhr import FormData, XMLHttpRequest

data = FormData()
data.append("name", "domonic")

request = XMLHttpRequest()
request.open("POST", "https://example.com/api")
request.send(data)

URLPattern

URLPattern matches URLs against path, hostname, protocol, and search patterns.

from domonic.webapi.urlpattern import URLPattern

pattern = URLPattern({"pathname": "/users/:id"})
print(pattern.test("https://example.com/users/42"))

Window

Window – location, history, screen, matchMedia, animation frames, scrolling and native host attachment – has its own page: window.

Geolocation

The geolocation helper is deterministic and test-friendly: set coordinates, read the current position, or watch for position changes.

from domonic.webapi.geo import Geolocation

geo = Geolocation()
geo.setPosition({"latitude": 51.5072, "longitude": -0.1276, "accuracy": 10})
geo.getCurrentPosition(lambda position: print(position.coords.latitude))
# 51.5072

Web Crypto

Crypto provides secure random values, UUIDs, and digest hashes through a browser-like crypto object.

from domonic.javascript import Uint8Array
from domonic.webapi.crypto import crypto

token = Uint8Array(16)
crypto.getRandomValues(token)
print(crypto.randomUUID())
print(crypto.subtle.digest("SHA-256", b"domonic").data.hex())

Messaging

MessageChannel and BroadcastChannel provide browser-style in-process message wiring for worker-like code and tests.

from domonic.webapi.messaging import BroadcastChannel, MessageChannel

channel = MessageChannel()
channel.port1.onmessage = lambda event: print(event.data)
channel.port2.postMessage("hello")

updates = BroadcastChannel("updates")
updates.onmessage = lambda event: print(event.data)
BroadcastChannel("updates").postMessage({"ok": True})

Web Workers

Worker runs a local Python script or callable in a daemon thread with a browser-style DedicatedWorkerGlobalScope. Messages are cloned between the parent and worker, and both sides support onmessage, messageerror and error events.

from threading import Event

from domonic.webapi.webworkers import Worker

done = Event()

def worker_main(scope):
        scope.onmessage = lambda event: scope.postMessage(event.data.upper())

worker = Worker(worker_main)
worker.onmessage = lambda event: (print(event.data), done.set())
worker.postMessage("hello")
done.wait(2)
worker.terminate()

Scheduler

Scheduler and TaskController provide a small Prioritized Task Scheduling surface for ordered server-side work.

from domonic.webapi.scheduler import scheduler

scheduler.postTask(lambda: "done", {"priority": "user-visible"})

Streams

Readable, writable, transform, compression, and decompression streams are available for Web Streams-style examples and tests.

from domonic.webapi.streams import ReadableStream

stream = ReadableStream(b"hello world")
print(stream.getReader())
# b'hello world'
print(stream.read(5))
# b'hello'
print(stream.read())
# b' world'

ReadableStream also accepts an underlying source dict/object with start/pull/cancel callbacks. In that mode getReader() returns a ReadableStreamDefaultReader whose read() yields {"value": ..., "done": ...} (the reader is a plain Python iterator too), pull is driven by backpressure derived from an optional queuing strategy, and cancel(), tee() and locked behave as in the browser.

from domonic.webapi.streams import ReadableStream, CountQueuingStrategy

def make_source(items):
        def pull(controller):
                if items:
                        controller.enqueue(items.pop(0))
                else:
                        controller.close()
        return {"pull": pull}

stream = ReadableStream(
        make_source([b"a", b"b", b"c"]),
        CountQueuingStrategy({"highWaterMark": 2}),
)
reader = stream.getReader()
print(reader.read())
# {'value': b'a', 'done': False}
print(list(reader))
# [b'b', b'c']

CountQueuingStrategy measures the internal queue by chunk count and ByteLengthQueuingStrategy measures it by byteLength; both expose highWaterMark and size() and can be passed to ReadableStream or WritableStream. WritableStream.getWriter() returns a WritableStreamDefaultWriter with write(), close(), abort(), releaseLock() and a desiredSize derived from the strategy.

Canvas and WebGL

HTMLCanvasElement.getContext() supports inspectable 2d, webgl and webgl2 contexts. These contexts record drawing/setup commands so generated examples and tests can verify canvas output without a browser renderer.

from domonic.html import canvas

surface = canvas(width=320, height=180)
ctx = surface.getContext("2d")
ctx.fillStyle = "#f00"
ctx.fillRect(0, 0, 20, 20)
print(ctx.commands[-1]["name"], ctx.commands[-1]["args"])
# fillRect [0, 0, 20, 20]

Each recorded command also carries a state snapshot (fillStyle, strokeStyle, globalAlpha, transform, …) of the paint state active when it was issued, so replaying ctx.commands later draws with the right style even if later commands changed it.

CSS Font Loading

FontFace and FontFaceSet model the browser font-loading surface and are available through document.fonts.

from domonic.dom import Document
from domonic.webapi.cssfontloading import FontFace

doc = Document()
doc.fonts.add(FontFace("Demo", "url(/demo.woff2)")).load("16px Demo")

File API

Blob, File, FileList and FileReader mirror the browser File API and work with fetch, FormData and drag-and-drop helpers.

from domonic.webapi.file import Blob, File, FileReader
from domonic.webapi.url import URL

file = File([b"hello"], "hello.txt", {"type": "text/plain"})
reader = FileReader()
reader.onload = lambda event: print(reader.result)
reader.readAsText(file)

object_url = URL.createObjectURL(file)

Sanitizer API

Sanitizer cleans HTML fragments into domonic nodes without evaluating the input. It supports the current elements/removeElements and attributes/removeAttributes configuration names, plus the older domonic aliases.

from domonic.html import div
from domonic.webapi.sanitizer import Sanitizer

clean = Sanitizer().sanitizeToString(
        '<p onclick="evil()">Hello <script>bad()</script></p>'
)
assert clean == "<p>Hello </p>"

target = div()
target.setHTML('<a href="javascript:evil()">link</a>')
assert str(target) == "<div><a>link</a></div>"

Notifications and Gamepad

Notification provides browser-style notification objects without OS side effects, while GamepadManager backs navigator.getGamepads() for tests and interactive examples.

from domonic.webapi.gamepad import Gamepad
from domonic.webapi.notifications import Notification
from domonic.window import Window

Notification.requestPermission()
notice = Notification("Done", {"body": "Build finished"})
notice.show()

win = Window()
win.navigator.connectGamepad(Gamepad("Pad"))
print(win.navigator.getGamepads())

Performance

performance.now() is milliseconds since the time origin, from a monotonic clock. requestAnimationFrame timestamps, Event.timeStamp, document.timeline.currentTime and Gamepad.timestamp are read from the same clock, so they compare with each other. Marks and measures feed PerformanceObserver.

from domonic.webapi.performance import PerformanceObserver, performance

performance.mark("parse-start")
# ... work ...
measure = performance.measure("parse", "parse-start")
print(round(measure.duration, 2), "ms")

observer = PerformanceObserver(lambda entries, obs: print(entries.getEntriesByType("measure")))
observer.observe({"entryTypes": ["measure"]})

Service Workers

ServiceWorkerContainer and registrations model the lifecycle enough for DOM tests and worker-style examples without requiring a browser runtime.

from domonic.webapi.serviceworker import ServiceWorkerContainer

container = ServiceWorkerContainer("https://example.com/app/")
registration = container.register("/sw.js")

URL

URL is a wrapper around Python’s urlparse and urlencode helpers.

from domonic.webapi.url import URL

myurl = URL("http://www.google.com/search?q=domonic")
print(myurl.host)
# www.google.com
print(myurl.search)
# ?q=domonic
print(myurl.searchParams)
# q=domonic
print(myurl.searchParams.get("q"))
# domonic

Object URLs work with the File API:

from domonic.webapi.file import Blob
from domonic.webapi.url import URL

blob = Blob(["hello"], {"type": "text/plain"})
url = URL.createObjectURL(blob)
print(url)
# blob:domonic/<uuid>
URL.revokeObjectURL(url)

For more information see the MDN URL API docs: https://developer.mozilla.org/en-US/docs/Web/API/URL

XPATH

Here’s a quick example of using XPath:

from domonic import domonic
from domonic.webapi.xpath import XPathEvaluator, XPathResult

somehtml = '''
<div>XPath example</div>
<div>Number of &lt;div&gt;s: <output></output></div>
'''
page = domonic.parseString(somehtml)
evaluator = XPathEvaluator()
expression = evaluator.createExpression("//div")
result = expression.evaluate(page, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE)
print(result.snapshotLength)
# 2
assert result.snapshotLength == 2

For more information, see the MDN Web API docs: https://developer.mozilla.org/en-US/docs/Web/API