Source code for anza.models.envelope

"""The fragment envelope (wire format v2)."""

import json
from dataclasses import dataclass
from typing import Any, Optional

from ..crypto.envelope import ENVELOPE_VERSION, signature_message


[docs] @dataclass class Envelope: """A rendered fragment on the wire, envelope format v2. Produced by :class:`~anza.Fragment`. The JSON form is ``{"v":2,"slot":...,"ts":...,"html":...,"sig":...}`` (``sig`` and ``css`` only when set), byte-identical to the Rust and TypeScript engines (see the `Engine Contract <https://github.com/anzaui/anza/blob/main/docs/engines/contract.md>`_). Example: >>> from anza import Envelope >>> env = Envelope(slot="feed", ts=1700000000, html="<p>Hi</p>") >>> env.to_json() '{"v":2,"slot":"feed","ts":1700000000,"html":"<p>Hi</p>"}' >>> env.to_dict()["v"] 2 """ #: The client slot the HTML is for. slot: str #: Render time, in Unix seconds. ts: int #: The rendered HTML. html: str #: Lowercase-hex signature over :meth:`message`, or ``None`` when the #: engine does not sign. sig: Optional[str] = None #: Optional CSS shipped with the HTML; covered by the signature. css: Optional[str] = None #: Wire-format version, always 2 for envelopes this engine produces. v: int = ENVELOPE_VERSION
[docs] def message(self) -> bytes: """The exact bytes the signature covers. Returns: bytes: ``signature_message(ts, slot, html, css)``, see :func:`~anza.signature_message`. """ return signature_message(self.ts, self.slot, self.html, self.css)
[docs] def to_dict(self) -> dict[str, Any]: """The envelope as a JSON-ready dict. Returns: dict: ``v``, ``slot``, ``ts`` and ``html``, plus ``sig`` and ``css`` when they are set. """ data: dict[str, Any] = { "v": self.v, "slot": self.slot, "ts": self.ts, "html": self.html, } if self.sig is not None: data["sig"] = self.sig if self.css is not None: data["css"] = self.css return data
[docs] def to_json(self) -> str: """Compact JSON, byte-identical to the Rust and TypeScript engines. Returns: str: The JSON text, ``v`` first, no whitespace. """ data = self.to_dict() # Identical output either way for ASCII text; the ASCII encoder is faster. ascii_only = all(v.isascii() for v in data.values() if isinstance(v, str)) return json.dumps(data, separators=(",", ":"), ensure_ascii=ascii_only)