Source code for domonic.webapi.serviceworker
"""
domonic.dom.serviceworker
====================================
https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API
"""
from __future__ import annotations
from typing import Any
from urllib.parse import urljoin
from domonic.events import Event, EventTarget, MessageEvent
def _create_promise():
from domonic.javascript import Promise
return Promise()
def _scope_from_script(scriptURL: str) -> str:
if "/" not in scriptURL:
return "/"
return scriptURL.rsplit("/", 1)[0] + "/"
[docs]
class ServiceWorker(EventTarget):
"""Registered service worker script handle."""
INSTALLING = "installing"
INSTALLED = "installed"
ACTIVATING = "activating"
ACTIVATED = "activated"
REDUNDANT = "redundant"
def __init__(self, scriptURL: str, state: str = ACTIVATED) -> None:
super().__init__()
self.scriptURL = scriptURL
self.state = state
self.onstatechange = None
self.onerror = None
[docs]
def postMessage(self, message, transfer=None) -> None:
self.dispatchEvent(
MessageEvent(
"message",
{"data": message, "source": self, "ports": transfer or []},
)
)
return None
def _set_state(self, state: str) -> None:
if self.state == state:
return None
self.state = state
self.dispatchEvent(Event("statechange"))
return None
[docs]
class ServiceWorkerRegistration(EventTarget):
"""Service worker registration for a scope."""
def __init__(
self,
scope: str,
scriptURL: str,
container: "ServiceWorkerContainer | None" = None,
) -> None:
super().__init__()
self.scope = scope
self.installing = None
self.waiting = None
self.active = ServiceWorker(scriptURL)
self.onupdatefound = None
self._container = container
[docs]
def update(self):
self.installing = ServiceWorker(self.active.scriptURL, ServiceWorker.INSTALLING)
self.dispatchEvent(Event("updatefound"))
self.installing._set_state(ServiceWorker.INSTALLED)
self.waiting = self.installing
self.installing = None
return _create_promise().resolve(self)
[docs]
def unregister(self):
if self.active is not None:
self.active._set_state(ServiceWorker.REDUNDANT)
if self._container is not None:
self._container._registrations.pop(self.scope, None)
if self._container.controller is self.active:
self._container.controller = None
return _create_promise().resolve(True)
[docs]
class ServiceWorkerContainer(EventTarget):
"""In-memory ServiceWorkerContainer implementation."""
def __init__(self, baseURL: str = "https://eventual.technology/") -> None:
super().__init__()
self.controller: Any = None
self.oncontrollerchange = None
self.onmessage = None
self.onmessageerror = None
self._baseURL = baseURL
self._registrations: dict[str, ServiceWorkerRegistration] = {}
@property
def ready(self):
registration = next(iter(self._registrations.values()), None)
return _create_promise().resolve(registration)
[docs]
def register(self, scriptURL: str, options: dict | None = None):
options = options or {}
absolute_script = urljoin(self._baseURL, scriptURL)
scope = options.get("scope") or _scope_from_script(scriptURL)
registration = ServiceWorkerRegistration(scope, absolute_script, self)
self._registrations[scope] = registration
previous = self.controller
self.controller = registration.active
if previous is not self.controller:
self.dispatchEvent(Event("controllerchange"))
return _create_promise().resolve(registration)
[docs]
def getRegistration(self, clientURL: str | None = None):
if clientURL is None:
registration = next(iter(self._registrations.values()), None)
else:
registration = None
for scope, candidate in self._registrations.items():
absolute_client = urljoin(self._baseURL, clientURL)
absolute_scope = urljoin(self._baseURL, scope)
if clientURL.startswith(scope) or absolute_client.startswith(absolute_scope):
registration = candidate
break
return _create_promise().resolve(registration)
[docs]
def getRegistrations(self):
return _create_promise().resolve(list(self._registrations.values()))
[docs]
def startMessages(self) -> None:
return None
[docs]
def postMessage(self, message, transfer=None) -> None:
if self.controller is not None:
self.controller.postMessage(message, transfer)
return None
__all__ = [
"ServiceWorker",
"ServiceWorkerContainer",
"ServiceWorkerRegistration",
]