Metadata-Version: 2.3
Name: lab-link
Version: 0.3.0
Summary: Generic server-authoritative sync library for ASGI apps (Starlette, FastAPI)
Author: Andrew Mueller
Author-email: Andrew Mueller <amueller@caltech.edu>
Requires-Dist: starlette>=0.40
Requires-Dist: jsonpatch>=1.33
Requires-Dist: pydantic>=2.7
Requires-Dist: websockets>=12.0
Requires-Dist: pytest ; extra == 'dev'
Requires-Dist: pytest-asyncio ; extra == 'dev'
Requires-Dist: httpx ; extra == 'dev'
Requires-Dist: uvicorn[standard] ; extra == 'dev'
Requires-Dist: numpy>=1.24 ; extra == 'numpy'
Requires-Dist: sqlmodel>=0.0.24 ; extra == 'persist'
Requires-Python: >=3.11
Project-URL: Homepage, https://sansseriff.github.io/lab-link/
Project-URL: Repository, https://github.com/sansseriff/lab-link
Project-URL: Documentation, https://sansseriff.github.io/lab-link/
Provides-Extra: dev
Provides-Extra: numpy
Provides-Extra: persist
Description-Content-Type: text/markdown

# lab-link Python

Starlette/Pydantic backend runtime and Python sync client for `lab-link`.

Bind a reactive state model, expose a WebSocket sync endpoint, run commands
with hardware side effects, broadcast versioned JSON Patch updates, or control
a `lab-link` server from Python.

```bash
uv add lab-link
```

```python
from lab_link import LabSync, ReactiveModel

class AppState(ReactiveModel):
    voltage: float = 0.0

sync = LabSync()
state = sync.bind_state(AppState())

@sync.command
def set_voltage(value: float):
    state.voltage = round(value, 3)  # validated, batched, broadcast

app = sync.create_app()
```

```python
from lab_link import LabLinkClient

with LabLinkClient("ws://127.0.0.1:8000/sync/ws") as sync:
    snapshot = sync.snapshot()
    ack = sync.send_command("set_voltage", {"value": 1.2})
```

Full docs: https://sansseriff.github.io/lab-link/
