System Overview#
The data donation task is a web application that runs entirely inside a participant’s browser. It guides a participant through uploading their data download package (DDP), extracts the relevant data locally using Python, shows the participant what will be shared, and — with their consent — sends the extracted data to a researcher’s storage.
Relationship with Eyra mono#
The task runs as an iframe embedded in Eyra mono,
the host platform. The two communicate via a MessageChannel established
at startup: mono sends a live-init message with a MessagePort; the
iframe uses that port for all subsequent communication.
flowchart LR
subgraph Browser
subgraph Iframe["Data donation task (iframe)"]
PY["Pyodide WebWorker\nPython"]
JS["JS Framework\nReact + TypeScript"]
BR["Bridge\nLiveBridge"]
PY -- "Commands" --> JS
JS -- "send / sendLogs" --> BR
end
Mono["Eyra mono\n(host page)"]
end
BR -- "port.postMessage()\nMessageChannel" --> Mono
Participant(("Participant")) -- "interacts" --> Iframe
Mono -- "stores donations\nserves pages" --> Storage[("Researcher\nstorage")]
In local development, FakeBridge replaces LiveBridge. It logs commands
to the console and POSTs donations to a local /data-submission endpoint,
so the full flow can be tested without a running Eyra instance.
Main components#
Pyodide WebWorker#
A standard browser Web Worker that loads Pyodide — a Python runtime compiled to WebAssembly. The port Python package is installed inside it at startup.
The worker runs the donation script (script.py). Because it is a separate
thread, it cannot block the UI.
Key file: packages/data-collector/public/py_worker.js
JS Framework (feldspar)#
The framework library. Runs on the browser’s main thread. Responsible for:
Managing the worker lifecycle (
WorkerProcessingEngine)Routing commands from Python to the bridge or to the React UI (
CommandRouter)Rendering interactive pages (
ReactEngine) via a pluggable factory systemCapturing and forwarding JavaScript-side log entries (
LogForwarder,WindowLogSource)Wiring all of the above together (
Assembly)Providing base UI components and default prompt factories
feldspar is a library — it does not run on its own.
Key files: packages/feldspar/src/framework/
Application layer (data-collector)#
The Vite app that gets built and served. This is the composition root that wires feldspar together with D3I-specific components:
Hosts
py_worker.js(the worker entry point) and the Python wheel (port-0.0.0.tar.gz) in itspublic/directoryRegisters custom prompt factories for D3I UI components (consent form with visualizations, multi-file input, questionnaire, error page, retry prompt)
Configures
ScriptHostComponentwith the worker URL, log level, and standalone/production toggleManages the iframe lifecycle (app-loaded signal, resize observer)
See Rendering and the factory system for how data-collector plugs into feldspar’s rendering pipeline.
Key files: packages/data-collector/src/App.tsx, packages/data-collector/public/
The bridge#
An abstraction layer with two implementations:
|
|
|
|---|---|---|
Used in |
Production |
Dev / test |
Transport |
|
Console log + HTTP POST to |
File |
|
|
Both implement the same Bridge interface: send(command) for Python-originated
commands, and sendLogs(entries) for JS-side log entries.
Python packages#
Package |
Purpose |
|---|---|
|
Entry point — |
|
|
|
|
|
File materialization and safety checks |
|
|
|
|
|
|
|
|
|
Core UI prop types ( |
|
Per-platform |
|
|
Next#
→ The run cycle — how Python and JavaScript take turns