bidiwave 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. bidiwave-1.0.0/.github/workflows/ci.yml +19 -0
  2. bidiwave-1.0.0/.github/workflows/docs.yml +16 -0
  3. bidiwave-1.0.0/.github/workflows/release.yml +38 -0
  4. bidiwave-1.0.0/.github/workflows/test.yml +62 -0
  5. bidiwave-1.0.0/.gitignore +27 -0
  6. bidiwave-1.0.0/CHANGELOG.md +27 -0
  7. bidiwave-1.0.0/CONTEXT.md +233 -0
  8. bidiwave-1.0.0/PKG-INFO +127 -0
  9. bidiwave-1.0.0/README.md +90 -0
  10. bidiwave-1.0.0/bidiwave/__init__.py +71 -0
  11. bidiwave-1.0.0/bidiwave/_internal/__init__.py +0 -0
  12. bidiwave-1.0.0/bidiwave/_internal/logging.py +32 -0
  13. bidiwave-1.0.0/bidiwave/client.py +92 -0
  14. bidiwave-1.0.0/bidiwave/config.py +19 -0
  15. bidiwave-1.0.0/bidiwave/convenience/__init__.py +0 -0
  16. bidiwave-1.0.0/bidiwave/convenience/page.py +93 -0
  17. bidiwave-1.0.0/bidiwave/events/__init__.py +0 -0
  18. bidiwave-1.0.0/bidiwave/events/dispatcher.py +100 -0
  19. bidiwave-1.0.0/bidiwave/events/handlers.py +32 -0
  20. bidiwave-1.0.0/bidiwave/events/queue.py +62 -0
  21. bidiwave-1.0.0/bidiwave/exceptions.py +67 -0
  22. bidiwave-1.0.0/bidiwave/modules/__init__.py +0 -0
  23. bidiwave-1.0.0/bidiwave/modules/browsing.py +173 -0
  24. bidiwave-1.0.0/bidiwave/modules/script.py +59 -0
  25. bidiwave-1.0.0/bidiwave/modules/session.py +62 -0
  26. bidiwave-1.0.0/bidiwave/protocol/__init__.py +0 -0
  27. bidiwave-1.0.0/bidiwave/protocol/capabilities.py +45 -0
  28. bidiwave-1.0.0/bidiwave/protocol/commands.py +91 -0
  29. bidiwave-1.0.0/bidiwave/protocol/constants.py +36 -0
  30. bidiwave-1.0.0/bidiwave/protocol/events.py +86 -0
  31. bidiwave-1.0.0/bidiwave/protocol/remote_value.py +85 -0
  32. bidiwave-1.0.0/bidiwave/protocol/responses.py +45 -0
  33. bidiwave-1.0.0/bidiwave/protocol/results.py +38 -0
  34. bidiwave-1.0.0/bidiwave/transport/__init__.py +0 -0
  35. bidiwave-1.0.0/bidiwave/transport/connection.py +162 -0
  36. bidiwave-1.0.0/bidiwave/transport/correlation.py +37 -0
  37. bidiwave-1.0.0/bidiwave/transport/serializer.py +21 -0
  38. bidiwave-1.0.0/docs/api/browsing.md +5 -0
  39. bidiwave-1.0.0/docs/api/client.md +3 -0
  40. bidiwave-1.0.0/docs/api/config.md +3 -0
  41. bidiwave-1.0.0/docs/api/events.md +7 -0
  42. bidiwave-1.0.0/docs/api/exceptions.md +3 -0
  43. bidiwave-1.0.0/docs/api/remote-value.md +3 -0
  44. bidiwave-1.0.0/docs/api/script.md +3 -0
  45. bidiwave-1.0.0/docs/api/session.md +3 -0
  46. bidiwave-1.0.0/docs/cookbook.md +182 -0
  47. bidiwave-1.0.0/docs/error-handling.md +134 -0
  48. bidiwave-1.0.0/docs/index.md +46 -0
  49. bidiwave-1.0.0/docs/protocol-reference.md +331 -0
  50. bidiwave-1.0.0/docs/quick-start.md +105 -0
  51. bidiwave-1.0.0/docs/spike-notes.md +52 -0
  52. bidiwave-1.0.0/mkdocs.yml +67 -0
  53. bidiwave-1.0.0/pyproject.toml +69 -0
  54. bidiwave-1.0.0/spike.py +126 -0
  55. bidiwave-1.0.0/tests/__init__.py +0 -0
  56. bidiwave-1.0.0/tests/conftest.py +12 -0
  57. bidiwave-1.0.0/tests/contract/__init__.py +0 -0
  58. bidiwave-1.0.0/tests/integration/__init__.py +0 -0
  59. bidiwave-1.0.0/tests/integration/conftest.py +109 -0
  60. bidiwave-1.0.0/tests/integration/test_browsing.py +53 -0
  61. bidiwave-1.0.0/tests/integration/test_connection.py +23 -0
  62. bidiwave-1.0.0/tests/integration/test_ergonomics.py +63 -0
  63. bidiwave-1.0.0/tests/integration/test_events.py +69 -0
  64. bidiwave-1.0.0/tests/integration/test_hello_world.py +18 -0
  65. bidiwave-1.0.0/tests/integration/test_script.py +72 -0
  66. bidiwave-1.0.0/tests/integration/test_session.py +22 -0
  67. bidiwave-1.0.0/tests/unit/__init__.py +0 -0
  68. bidiwave-1.0.0/tests/unit/convenience/__init__.py +0 -0
  69. bidiwave-1.0.0/tests/unit/convenience/test_page.py +90 -0
  70. bidiwave-1.0.0/tests/unit/events/__init__.py +0 -0
  71. bidiwave-1.0.0/tests/unit/events/test_dispatcher.py +127 -0
  72. bidiwave-1.0.0/tests/unit/events/test_fluent_api.py +63 -0
  73. bidiwave-1.0.0/tests/unit/events/test_queue.py +73 -0
  74. bidiwave-1.0.0/tests/unit/protocol/__init__.py +0 -0
  75. bidiwave-1.0.0/tests/unit/protocol/test_capabilities.py +60 -0
  76. bidiwave-1.0.0/tests/unit/protocol/test_commands.py +73 -0
  77. bidiwave-1.0.0/tests/unit/protocol/test_events.py +58 -0
  78. bidiwave-1.0.0/tests/unit/protocol/test_remote_value.py +76 -0
  79. bidiwave-1.0.0/tests/unit/protocol/test_responses.py +59 -0
  80. bidiwave-1.0.0/tests/unit/test_context_managers.py +60 -0
  81. bidiwave-1.0.0/tests/unit/test_exceptions.py +50 -0
  82. bidiwave-1.0.0/tests/unit/transport/__init__.py +0 -0
  83. bidiwave-1.0.0/tests/unit/transport/test_correlation.py +51 -0
  84. bidiwave-1.0.0/tests/unit/transport/test_reconnect.py +94 -0
  85. bidiwave-1.0.0/tests/unit/transport/test_serializer.py +36 -0
@@ -0,0 +1,19 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ lint:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - uses: actions/setup-python@v5
15
+ with:
16
+ python-version: "3.12"
17
+ - run: pip install -e ".[dev]"
18
+ - run: ruff check .
19
+ - run: mypy bidiwave/
@@ -0,0 +1,16 @@
1
+ name: Docs
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+
7
+ jobs:
8
+ docs:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+ - uses: actions/setup-python@v5
13
+ with:
14
+ python-version: "3.12"
15
+ - run: pip install -e ".[docs]"
16
+ - run: mkdocs gh-deploy --force
@@ -0,0 +1,38 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ permissions:
9
+ id-token: write
10
+
11
+ jobs:
12
+ build:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: actions/setup-python@v5
17
+ with:
18
+ python-version: "3.12"
19
+ - run: pip install -e ".[dev]"
20
+ - run: ruff check .
21
+ - run: mypy bidiwave/
22
+ - run: pytest tests/unit/ -c pyproject.toml
23
+ - run: python -m build
24
+ - uses: actions/upload-artifact@v4
25
+ with:
26
+ name: dist
27
+ path: dist/
28
+
29
+ publish-pypi:
30
+ needs: build
31
+ runs-on: ubuntu-latest
32
+ environment: pypi
33
+ steps:
34
+ - uses: actions/download-artifact@v4
35
+ with:
36
+ name: dist
37
+ path: dist/
38
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,62 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ unit-tests:
11
+ runs-on: ${{ matrix.os }}
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ os: [ubuntu-latest, windows-latest]
16
+ python-version: ["3.11", "3.12", "3.13"]
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - uses: actions/setup-python@v5
20
+ with:
21
+ python-version: ${{ matrix.python-version }}
22
+ - run: pip install -e ".[dev]"
23
+ - run: ruff check .
24
+ - run: mypy bidiwave/
25
+ - run: pytest tests/unit/ --cov=bidiwave --cov-report=xml --cov-fail-under=90
26
+ - uses: codecov/codecov-action@v4
27
+ with:
28
+ file: ./coverage.xml
29
+
30
+ integration-chrome:
31
+ runs-on: ubuntu-latest
32
+ strategy:
33
+ fail-fast: false
34
+ matrix:
35
+ python-version: ["3.12"]
36
+ steps:
37
+ - uses: actions/checkout@v4
38
+ - uses: actions/setup-python@v5
39
+ with:
40
+ python-version: ${{ matrix.python-version }}
41
+ - uses: browser-actions/setup-chrome@latest
42
+ with:
43
+ chrome-version: stable
44
+ - run: pip install -e ".[dev]"
45
+ - run: pytest tests/integration/ -m integration --browser=chrome
46
+
47
+ integration-firefox:
48
+ runs-on: ubuntu-latest
49
+ strategy:
50
+ fail-fast: false
51
+ matrix:
52
+ python-version: ["3.12"]
53
+ steps:
54
+ - uses: actions/checkout@v4
55
+ - uses: actions/setup-python@v5
56
+ with:
57
+ python-version: ${{ matrix.python-version }}
58
+ - uses: browser-actions/setup-firefox@latest
59
+ with:
60
+ firefox-version: latest
61
+ - run: pip install -e ".[dev]"
62
+ - run: pytest tests/integration/ -m integration --browser=firefox
@@ -0,0 +1,27 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .eggs/
8
+ *.egg
9
+ .venv/
10
+ .venv-test/
11
+ venv/
12
+ env/
13
+ .mypy_cache/
14
+ .pytest_cache/
15
+ .ruff_cache/
16
+ .coverage
17
+ coverage.xml
18
+ htmlcov/
19
+ *.so
20
+ .idea/
21
+ .vscode/
22
+ *.swp
23
+ *.swo
24
+ site/
25
+
26
+ # Driver binaries (downloaded on demand)
27
+ bin/
@@ -0,0 +1,27 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.0] - 2025-07-03
9
+
10
+ ### Added
11
+
12
+ - `BiDiClient` con `connect()`, `close()`, context manager (`async with`)
13
+ - `SessionModule` — `new()`, `status()`, `subscribe()`, `unsubscribe()`
14
+ - `BrowsingModule` — `create_context()`, `navigate()`, `close()`, `screenshot()`, `get_tree()`, `wait_for_selector()`, `wait_for_function()`, `open()`
15
+ - `ScriptModule` — `evaluate()`, `call_function()`, `disown()`
16
+ - `EventDispatcher` — `on()`, `off()`, fluent API, decorator mode, error isolation
17
+ - `Page` object — convenience layer con `evaluate()`, `screenshot()`, `wait_for_selector()`, `wait_for_function()`, `close()`
18
+ - `RemoteValue` con subtipos: `StringValue`, `NumberValue`, `BooleanValue`, `NullValue`, `UndefinedValue`, `BigIntValue`, `ObjectValue`, `ArrayValue`, `HandleValue`
19
+ - `Capabilities` — detection desde `session.status`
20
+ - `ClientConfig` — configuración tipada con Pydantic
21
+ - Reconnect con backoff exponencial
22
+ - Backpressure con drop policies (oldest, newest, block)
23
+ - Jerarquía de excepciones: `BiDiError`, `ConnectionError`, `TimeoutError`, `CapabilityError`, `CommandError`, `InvalidArgumentError`, `NoSuchFrameError`, `JavaScriptError`
24
+ - Cross-browser: Chrome, Firefox, Edge
25
+ - Logging estructurado
26
+ - CI: GitHub Actions con matrix Chrome + Firefox, Python 3.11/3.12/3.13
27
+ - Coverage > 90%
@@ -0,0 +1,233 @@
1
+ # bidiwave — Contexto de desarrollo
2
+
3
+ ## Estado actual
4
+
5
+ | Fase | Estado | Commit |
6
+ |---|---|---|
7
+ | 0. Setup + Spike | ✅ Completa | 1a047b1 |
8
+ | 1. Hello World | ✅ Completa | fdc10f5 |
9
+ | 2. Eventos | ✅ Completa | — |
10
+ | 3. Robustez | ✅ Completa | — |
11
+ | 4. Ergonomics | ✅ Completa | — |
12
+ | 5. Integration + CI | ✅ Completa | — |
13
+ | 6. Docs + Release | ✅ Completa | — |
14
+
15
+ ## Phase 0 — Setup + Spike (completa)
16
+
17
+ - `pyproject.toml` con hatchling, ruff, mypy, pytest-asyncio
18
+ - Estructura: `bidiwave/{protocol,transport,modules}/`
19
+ - `spike.py` validó BiDi con Chrome y Edge via ChromeDriver/EdgeDriver
20
+ - `docs/spike-notes.md` con findings
21
+ - `bin/` gitignored (drivers descargados on-demand)
22
+
23
+ ## Phase 1 — Hello World (completa)
24
+
25
+ ### Archivos implementados
26
+
27
+ - `bidiwave/protocol/constants.py` — command names (`browsingContext.*`, `session.*`, `script.*`), event names, error codes
28
+ - `bidiwave/protocol/commands.py` — `Command`, `NewSessionParams`, `NavigateParams`, `EvaluateParams` (Pydantic v2, `extra="allow"`)
29
+ - `bidiwave/protocol/responses.py` — `SuccessResponse`, `ErrorResponse`, `ErrorData`, `parse_response` factory
30
+ - `bidiwave/protocol/capabilities.py` — `Capabilities` model, `detect_capabilities`
31
+ - `bidiwave/transport/serializer.py` — `serialize_command`, `deserialize_message`
32
+ - `bidiwave/transport/correlation.py` — `Correlator` (Future-based command/response matching)
33
+ - `bidiwave/transport/connection.py` — `Connection` (WebSocket receive loop, correlator, 30s timeout)
34
+ - `bidiwave/exceptions.py` — `BiDiError`, `ConnectionError`, `CommandError`
35
+ - `bidiwave/modules/session.py` — `SessionModule` (`new`, `status`)
36
+ - `bidiwave/modules/browsing.py` — `BrowsingModule` (`create_context`, `navigate`, `close`)
37
+ - `bidiwave/modules/script.py` — `ScriptModule` (`evaluate`)
38
+ - `bidiwave/client.py` — `BiDiClient.connect()` classmethod, `close()`
39
+ - `bidiwave/__init__.py` — exports públicos
40
+ - 28 unit tests en `tests/unit/`
41
+ - Integration test: `tests/integration/test_hello_world.py`
42
+
43
+ ### Verificación
44
+
45
+ - `ruff check .` — pasa
46
+ - `mypy bidiwave/` — pasa (28 source files)
47
+ - `pytest tests/unit/ -c pyproject.toml` — 28 passed
48
+ - Integration test: navega a example.com, evalúa `document.title` → "Example Domain"
49
+
50
+ ## Hallazgos clave
51
+
52
+ - Chrome/Edge necesitan ChromeDriver/EdgeDriver como proxy BiDi. El `--remote-debugging-port` expone CDP, no BiDi.
53
+ - Al conectar via ChromeDriver `webSocketUrl`, la sesión BiDi **ya existe**. `session.new` falla con "session already exists". Usar `session.status` para verificar.
54
+ - Los nombres de comandos son `browsingContext.*` (no `browsing.*` como decía el prompt original)
55
+ - `script.evaluate` anida el valor en `result["result"]["value"]`
56
+ - Chrome 149, Edge 149. ChromeDriver 149.0.7827.155, EdgeDriver 149.0.4022.98
57
+ - Driver paths: `bin/chromedriver-win64/chromedriver.exe`, `bin/edgedriver/msedgedriver.exe`
58
+ - pytest necesita `-c pyproject.toml` cuando se ejecuta desde `D:\Codigo\bidiwave` (el pyproject.toml de brainstorming interfiere)
59
+
60
+ ## Phase 2 — Eventos (completa)
61
+
62
+ ### Archivos implementados
63
+
64
+ - `protocol/events.py` — `Event`, `LogEntryAddedEvent`, `BrowsingContextCreatedEvent`, `BrowsingContextDestroyedEvent`, `BrowsingContextNavigatedEvent`, `ScriptMessageEvent`, `parse_event` factory
65
+ - `events/handlers.py` — `AsyncHandler` type, `Subscription` dataclass con weakref al dispatcher
66
+ - `events/queue.py` — `EventQueue` (asyncio.Queue wrapper, sin backpressure aún)
67
+ - `events/dispatcher.py` — `EventDispatcher` con `on()`, `off()`, `dispatch()`, error isolation
68
+ - `transport/connection.py` — modificado: receive loop ahora dispatcha eventos al `EventDispatcher`
69
+ - `modules/session.py` — añadido `subscribe()`, `unsubscribe()`
70
+ - `client.py` — añadido `on()`, `off()`, `on_log_entry()`, integración con `EventDispatcher`
71
+ - `__init__.py` — exports: `EventDispatcher`, `Subscription`, `AsyncHandler`
72
+ - 11 unit tests nuevos (6 dispatcher + 5 events) — total 39 passed
73
+
74
+ ### Verificación
75
+
76
+ - `ruff check .` — pasa
77
+ - `mypy bidiwave/` — pasa (28 source files)
78
+ - `pytest tests/unit/ -c pyproject.toml` — 39 passed
79
+ - Pendiente: integration test contra Chrome real (console logs en tiempo real)
80
+
81
+ ### Notas
82
+
83
+ - Los nombres de eventos se alinearon con `constants.py` y el spec real: `browsingContext.contextCreated` (no `browsing.contextCreated`)
84
+ - `BROWSING_CONTEXT_NAVIGATED` en constants.py es `browsingContext.navigationStarted` — el modelo `BrowsingContextNavigatedEvent` matchea ese nombre
85
+ - `Subscription` usa `weakref` al dispatcher para evitar reference cycles
86
+ - Error isolation: un handler que lanza excepción no afecta a otros handlers ni al receive loop
87
+
88
+ ## Phase 3 — Robustez (completa)
89
+
90
+ ### Archivos implementados / modificados
91
+
92
+ - `exceptions.py` — jerarquía completa: `TimeoutError`, `CapabilityError`, `ProtocolError`, `SessionError`, `InvalidArgumentError`, `NoSuchFrameError`, `NoSuchWindowError`, `JavaScriptError`, `ERROR_CODE_MAP`, `map_error()`
93
+ - `events/queue.py` — `EventQueue` con backpressure: drop policies `oldest`, `newest`, `block`, `dropped_count`
94
+ - `protocol/capabilities.py` — `Capabilities` con `supports_browsing/script/network/input`, `detect_capabilities` con heurística Firefox
95
+ - `transport/connection.py` — `TransportConfig` (timeout, max_retries, retry_delay, retry_backoff, max_queue, drop_policy), reconnect con backoff exponencial, `on_reconnect`/`on_disconnect` handlers, `map_error` en receive loop
96
+ - `protocol/commands.py` — `ScreenshotParams`, `CallFunctionParams`, `DisownParams`, `GetTreeParams`, `SubscribeParams`
97
+ - `protocol/constants.py` — añadido `SESSION_END`
98
+ - `modules/session.py` — añadido `end()`
99
+ - `modules/browsing.py` — añadido `screenshot()`, `get_tree()`
100
+ - `modules/script.py` — añadido `call_function()`, `disown()`
101
+ - `client.py` — `TransportConfig` param en `connect()`, `capabilities` property, `on_reconnect()`/`on_disconnect()`
102
+ - `__init__.py` — exports: `TransportConfig`, `Capabilities`, todas las excepciones nuevas
103
+ - 19 unit tests nuevos (5 queue + 4 capabilities + 6 exceptions + 4 reconnect) — total 58 passed
104
+
105
+ ### Verificación
106
+
107
+ - `ruff check .` — pasa
108
+ - `mypy bidiwave/` — pasa (28 source files)
109
+ - `pytest tests/unit/ -c pyproject.toml` — 58 passed
110
+ - Pendiente: integration tests contra Chrome real (screenshot, get_tree, call_function, disown, reconnect)
111
+
112
+ ### Notas
113
+
114
+ - `TransportConfig` usa Pydantic BaseModel para validación tipada
115
+ - Reconnect: backoff exponencial con `retry_delay * retry_backoff^attempt`, llama `on_reconnect` handlers tras reconectar
116
+ - `map_error` mapea códigos BiDi a subtipos de `CommandError` — el receive loop ahora usa `map_error` en vez de `CommandError` directo
117
+ - `detect_capabilities` usa heurística: Firefox soporta network/input, Chrome no (puede refinarse en Fase 5)
118
+ - `EventQueue` ahora es configurable pero no está integrada en el dispatcher aún (Fase 4 puede integrarla)
119
+
120
+ ## Phase 4 — API Ergonomics (completa)
121
+
122
+ ### Archivos implementados / modificados
123
+
124
+ - `protocol/remote_value.py` — `RemoteValue` base + subtipos: `StringValue`, `NumberValue`, `BooleanValue`, `NullValue`, `UndefinedValue`, `BigIntValue`, `ObjectValue`, `ArrayValue`, `HandleValue` con `parse()` factory usando `match`
125
+ - `protocol/results.py` — `Session`, `SessionStatus`, `Navigation`, `Screenshot` modelos tipados
126
+ - `config.py` — `ClientConfig` con Pydantic: timeout, max_retries, retry_delay, retry_backoff, max_queue, drop_policy, log_level
127
+ - `_internal/logging.py` — `setup_logging()` con formato estructurado opcional
128
+ - `modules/browsing.py` — `BrowsingContext` dataclass con `__aenter__`/`__aexit__`, `BrowsingModule` con `script_module` ref, `create_context()` retorna `BrowsingContext`, `navigate`/`screenshot`/`close` aceptan `BrowsingContext | str`, `wait_for_selector`, `wait_for_function`, `open()` retorna `Page`
129
+ - `modules/script.py` — `evaluate`/`call_function` retornan `RemoteValue`, aceptan `BrowsingContext | str`
130
+ - `modules/session.py` — `new()` retorna `Session`, `status()` retorna `SessionStatus`
131
+ - `convenience/page.py` — `Page` object: `evaluate`, `call`, `navigate`, `screenshot` (bytes), `wait_for_selector`, `wait_for_function`, `disown`, `close`, context manager
132
+ - `events/dispatcher.py` — Fluent API (`on()` retorna `Self`) + decorator mode (`@dispatcher.on("event")`)
133
+ - `client.py` — `__aenter__`/`__aexit__`, `ClientConfig` en `connect()`, `on_context_created`/`on_context_destroyed`, `BrowsingModule` con `script_module` ref
134
+ - `__init__.py` — exports: `ClientConfig`, `Page`, `BrowsingContext`, `RemoteValue` + todos los subtipos
135
+ - 25 unit tests nuevos (10 remote_value + 5 context_managers + 6 page + 4 fluent_api) — total 83 passed
136
+ - `pyproject.toml` — añadido `ignore = ["ASYNC109"]` (timeout es parámetro legítimo de la API pública)
137
+
138
+ ### Verificación
139
+
140
+ - `ruff check .` — pasa
141
+ - `mypy bidiwave/` — pasa (28 source files)
142
+ - `pytest tests/unit/ -c pyproject.toml` — 83 passed
143
+ - Pendiente: integration tests con Chrome real (context managers, Page object, wait helpers, type narrowing)
144
+
145
+ ### Notas
146
+
147
+ - `from __future__ import annotations` añadido a `browsing.py` y `remote_value.py` para resolver referencias forward
148
+ - `BrowsingModule` se define antes de `BrowsingContext` para que el dataclass pueda referenciarlo
149
+ - `Page` usa imports diferidos (`from bidiwave.convenience.page import Page` dentro de `open()`) para evitar circular imports
150
+ - `EventDispatcher.on()` ahora retorna `Self | Subscription | Callable` — fluent cuando se pasa handler, decorator cuando no
151
+ - `ClientConfig` reemplaza a `TransportConfig` en `BiDiClient.connect()` — construye `TransportConfig` internamente
152
+
153
+ ## Phase 5 — Integration + CI (completa)
154
+
155
+ ### Archivos implementados
156
+
157
+ - `tests/conftest.py` — markers: unit, integration, contract, slow, chrome, firefox
158
+ - `tests/integration/conftest.py` — fixtures: `chrome_bidi` (ChromeDriver Windows), `client` (indirect param), `context` (BrowsingContext auto-cleanup), `pytest_collection_modifyitems` para auto-marker integration
159
+ - `tests/integration/test_connection.py` — connect/close, context manager
160
+ - `tests/integration/test_session.py` — session.status, subscribe/unsubscribe
161
+ - `tests/integration/test_browsing.py` — create/close context, navigate, context manager, screenshot (PNG bytes), get_tree
162
+ - `tests/integration/test_script.py` — evaluate string/number/boolean/null, call_function, await_promise
163
+ - `tests/integration/test_events.py` — console log event, contextCreated, contextDestroyed (rewritten con nueva API)
164
+ - `tests/integration/test_ergonomics.py` — Page object, wait_for_selector, wait_for_function, type narrowing con match
165
+ - `tests/integration/test_hello_world.py` — rewritten con nueva API (StringValue, fixtures)
166
+ - `.github/workflows/test.yml` — CI matrix: unit (ubuntu+windows, py3.11/3.12/3.13, ruff+mypy+pytest+codecov), integration-chrome, integration-firefox
167
+
168
+ ### Verificación
169
+
170
+ - `ruff check .` — pasa
171
+ - `mypy bidiwave/` — pasa (28 source files)
172
+ - `pytest tests/unit/ -c pyproject.toml` — 83 passed
173
+ - Integration tests: pendientes de ejecutar contra Chrome real (requieren ChromeDriver activo)
174
+ - CI: configurado para GitHub Actions (unit + integration Chrome + Firefox)
175
+
176
+ ### Notas
177
+
178
+ - Fixtures usan indirect parametrization: `@pytest.mark.parametrize("client", ["chrome_bidi"], indirect=True)`
179
+ - `chrome_bidi` fixture lanza ChromeDriver, crea sesión WebDriver, retorna URL del WebSocket BiDi
180
+ - `client` fixture resuelve el nombre del fixture via `request.getfixturevalue()` para obtener la URL
181
+ - Integration tests auto-marcados con `@pytest.mark.integration` via `pytest_collection_modifyitems`
182
+ - CI usa `browser-actions/setup-chrome` y `browser-actions/setup-firefox` para integration tests
183
+ - Coverage: `--cov=bidiwave --cov-report=xml --cov-fail-under=90` en CI de unit tests
184
+
185
+ ## Phase 6 — Docs + Release (completa)
186
+
187
+ ### Archivos implementados
188
+
189
+ - `README.md` — reescrito con badges (CI, PyPI, Python, License), quick start con nueva API, console log monitoring, instrucciones para lanzar Chrome/Firefox
190
+ - `mkdocs.yml` — Material theme con dark/light mode, mkdocstrings auto-gen, nav completo
191
+ - `docs/index.md` — landing page con features y quick example
192
+ - `docs/quick-start.md` — instalación, lanzar browser, hello world, console logs, screenshot, configuración
193
+ - `docs/cookbook.md` — 7 recetas actualizadas a la nueva API (context managers, Page object)
194
+ - `docs/error-handling.md` — jerarquía de excepciones, escenarios, patrones, logging
195
+ - `docs/protocol-reference.md` — referencia completa de comandos, eventos y códigos de error
196
+ - `docs/api/` — 8 páginas mkdocstrings: client, session, browsing, script, events, remote-value, exceptions, config
197
+ - `CHANGELOG.md` — entrada v1.0.0 con todas las features
198
+ - `.github/workflows/docs.yml` — deploy a GitHub Pages on push to main
199
+ - `.github/workflows/release.yml` — build + publish a PyPI via Trusted Publishing on tag push
200
+ - `pyproject.toml` — version bumped to 1.0.0, classifier updated to Production/Stable
201
+ - `bidiwave/__init__.py` — `__version__ = "1.0.0"`
202
+
203
+ ### Verificación
204
+
205
+ - `ruff check .` — pasa
206
+ - `mypy bidiwave/` — pasa (28 source files)
207
+ - `pytest tests/unit/ -c pyproject.toml` — 83 passed
208
+ - Docs deps ya presentes en `pyproject.toml`: mkdocs, mkdocs-material, mkdocstrings[python]
209
+ - Pendiente: `mkdocs build` local, `python -m build`, `pip install` en env limpio, tag v1.0.0
210
+
211
+ ### Release checklist
212
+
213
+ - [ ] `mkdocs build` sin errores
214
+ - [ ] `python -m build` genera wheel y sdist
215
+ - [ ] `pip install dist/bidiwave-1.0.0-py3-none-any.whl` en env limpio
216
+ - [ ] Quick start guide reproduce el hello world
217
+ - [ ] Configurar Trusted Publishing en PyPI
218
+ - [ ] `git tag v1.0.0 && git push origin main --tags`
219
+
220
+ ## Rutas de referencia (en el repo brainstorming)
221
+
222
+ - **Plan de desarrollo**: `D:\Codigo\brainstorming\ideas\libraries\bidiwave\docs\development-plan.md`
223
+ - **Arquitectura**: `D:\Codigo\brainstorming\ideas\libraries\bidiwave\architecture\README.md`
224
+ - **Patrones de diseño**: `D:\Codigo\brainstorming\ideas\libraries\bidiwave\design\design-patterns.md`
225
+ - **Spec de módulos**: `D:\Codigo\brainstorming\ideas\libraries\bidiwave\docs\modules-spec.md`
226
+ - **Referencia del protocolo**: `D:\Codigo\brainstorming\ideas\libraries\bidiwave\docs\protocol-reference.md`
227
+ - **Prompts por fase**: `D:\Codigo\brainstorming\ideas\libraries\bidiwave\prompts\` (phase-0-setup.md, phase-1-hello-world.md, etc.)
228
+ - **Spike notes**: `D:\Codigo\bidiwave\docs\spike-notes.md`
229
+
230
+ ## Cómo continuar
231
+
232
+ Para continuar desarrollo, decirle a Cascade:
233
+ > "continúa con bidiwave, lee CONTEXT.md"
@@ -0,0 +1,127 @@
1
+ Metadata-Version: 2.4
2
+ Name: bidiwave
3
+ Version: 1.0.0
4
+ Summary: WebDriver BiDi for Python — talk to any browser via W3C standard
5
+ Project-URL: Homepage, https://github.com/MathiasPaulenko/bidiwave
6
+ Project-URL: Repository, https://github.com/MathiasPaulenko/bidiwave
7
+ Project-URL: Issues, https://github.com/MathiasPaulenko/bidiwave/issues
8
+ Author: Mathias Paulenko
9
+ License: MIT
10
+ Keywords: async,automation,bidi,browser,w3c,webdriver
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Framework :: AsyncIO
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
19
+ Classifier: Topic :: Software Development :: Testing
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: anyio>=4.0
22
+ Requires-Dist: pydantic>=2.5
23
+ Requires-Dist: websockets>=12.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: build>=1.2; extra == 'dev'
26
+ Requires-Dist: mypy>=1.10; extra == 'dev'
27
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
28
+ Requires-Dist: pytest-cov>=4.0; extra == 'dev'
29
+ Requires-Dist: pytest>=8.0; extra == 'dev'
30
+ Requires-Dist: ruff>=0.4; extra == 'dev'
31
+ Requires-Dist: twine>=5.0; extra == 'dev'
32
+ Provides-Extra: docs
33
+ Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
34
+ Requires-Dist: mkdocs>=1.6; extra == 'docs'
35
+ Requires-Dist: mkdocstrings[python]>=0.24; extra == 'docs'
36
+ Description-Content-Type: text/markdown
37
+
38
+ # bidiwave
39
+
40
+ WebDriver BiDi for Python — talk to any browser via W3C standard.
41
+
42
+ [![CI](https://github.com/MathiasPaulenko/bidiwave/actions/workflows/test.yml/badge.svg)](https://github.com/MathiasPaulenko/bidiwave/actions/workflows/test.yml)
43
+ [![PyPI](https://img.shields.io/pypi/v/bidiwave)](https://pypi.org/project/bidiwave/)
44
+ [![Python](https://img.shields.io/pypi/pyversions/bidiwave)](https://pypi.org/project/bidiwave/)
45
+ [![License](https://img.shields.io/github/license/MathiasPaulenko/bidiwave)](LICENSE)
46
+
47
+ ## Features
48
+
49
+ - **W3C WebDriver BiDi** — estándar, no CDP propietario
50
+ - **Cross-browser** — Chrome, Firefox, Edge (Safari cuando soporte BiDi)
51
+ - **Async-first** — `async/await` nativo con `asyncio`
52
+ - **Event streaming** — console logs, navegación, contexts en tiempo real
53
+ - **Type-safe** — Pydantic v2 models, type narrowing con `match`
54
+ - **Sin dependencias pesadas** — no requiere Selenium, no requiere Playwright
55
+
56
+ ## Install
57
+
58
+ ```bash
59
+ pip install bidiwave
60
+ ```
61
+
62
+ ## Quick start
63
+
64
+ ```python
65
+ import asyncio
66
+ from bidiwave import BiDiClient, StringValue
67
+
68
+ async def main():
69
+ async with await BiDiClient.connect("ws://localhost:9222/session") as client:
70
+ await client.session.new()
71
+
72
+ async with await client.browsing.open("https://example.com") as page:
73
+ # Evaluar JS
74
+ result = await page.evaluate("document.title")
75
+ match result:
76
+ case StringValue(value=title):
77
+ print(f"Title: {title}")
78
+
79
+ # Screenshot
80
+ screenshot = await page.screenshot()
81
+ with open("screenshot.png", "wb") as f:
82
+ f.write(screenshot)
83
+
84
+ asyncio.run(main())
85
+ ```
86
+
87
+ ## Console log monitoring
88
+
89
+ ```python
90
+ async with await BiDiClient.connect(url) as client:
91
+ await client.session.new()
92
+
93
+ async def on_log(entry):
94
+ print(f"[{entry.level}] {entry.text}")
95
+
96
+ client.on("log.entryAdded", on_log)
97
+ await client.session.subscribe(["log.entryAdded"])
98
+
99
+ async with await client.browsing.open("https://example.com") as page:
100
+ await page.evaluate("console.log('hello!')")
101
+ await asyncio.sleep(2)
102
+ ```
103
+
104
+ ## Lanzar un browser con BiDi
105
+
106
+ ### Chrome
107
+
108
+ ```bash
109
+ google-chrome --headless=new --remote-debugging-port=9222 --enable-bidi
110
+ ```
111
+
112
+ ### Firefox
113
+
114
+ ```bash
115
+ firefox --headless --remote-debugging-port=9223 --no-remote
116
+ ```
117
+
118
+ ## Documentation
119
+
120
+ - [Quick Start](https://bidiwave.readthedocs.io/quick-start/)
121
+ - [API Reference](https://bidiwave.readthedocs.io/api/)
122
+ - [Cookbook](https://bidiwave.readthedocs.io/cookbook/)
123
+ - [Error Handling](https://bidiwave.readthedocs.io/error-handling/)
124
+
125
+ ## License
126
+
127
+ MIT
@@ -0,0 +1,90 @@
1
+ # bidiwave
2
+
3
+ WebDriver BiDi for Python — talk to any browser via W3C standard.
4
+
5
+ [![CI](https://github.com/MathiasPaulenko/bidiwave/actions/workflows/test.yml/badge.svg)](https://github.com/MathiasPaulenko/bidiwave/actions/workflows/test.yml)
6
+ [![PyPI](https://img.shields.io/pypi/v/bidiwave)](https://pypi.org/project/bidiwave/)
7
+ [![Python](https://img.shields.io/pypi/pyversions/bidiwave)](https://pypi.org/project/bidiwave/)
8
+ [![License](https://img.shields.io/github/license/MathiasPaulenko/bidiwave)](LICENSE)
9
+
10
+ ## Features
11
+
12
+ - **W3C WebDriver BiDi** — estándar, no CDP propietario
13
+ - **Cross-browser** — Chrome, Firefox, Edge (Safari cuando soporte BiDi)
14
+ - **Async-first** — `async/await` nativo con `asyncio`
15
+ - **Event streaming** — console logs, navegación, contexts en tiempo real
16
+ - **Type-safe** — Pydantic v2 models, type narrowing con `match`
17
+ - **Sin dependencias pesadas** — no requiere Selenium, no requiere Playwright
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ pip install bidiwave
23
+ ```
24
+
25
+ ## Quick start
26
+
27
+ ```python
28
+ import asyncio
29
+ from bidiwave import BiDiClient, StringValue
30
+
31
+ async def main():
32
+ async with await BiDiClient.connect("ws://localhost:9222/session") as client:
33
+ await client.session.new()
34
+
35
+ async with await client.browsing.open("https://example.com") as page:
36
+ # Evaluar JS
37
+ result = await page.evaluate("document.title")
38
+ match result:
39
+ case StringValue(value=title):
40
+ print(f"Title: {title}")
41
+
42
+ # Screenshot
43
+ screenshot = await page.screenshot()
44
+ with open("screenshot.png", "wb") as f:
45
+ f.write(screenshot)
46
+
47
+ asyncio.run(main())
48
+ ```
49
+
50
+ ## Console log monitoring
51
+
52
+ ```python
53
+ async with await BiDiClient.connect(url) as client:
54
+ await client.session.new()
55
+
56
+ async def on_log(entry):
57
+ print(f"[{entry.level}] {entry.text}")
58
+
59
+ client.on("log.entryAdded", on_log)
60
+ await client.session.subscribe(["log.entryAdded"])
61
+
62
+ async with await client.browsing.open("https://example.com") as page:
63
+ await page.evaluate("console.log('hello!')")
64
+ await asyncio.sleep(2)
65
+ ```
66
+
67
+ ## Lanzar un browser con BiDi
68
+
69
+ ### Chrome
70
+
71
+ ```bash
72
+ google-chrome --headless=new --remote-debugging-port=9222 --enable-bidi
73
+ ```
74
+
75
+ ### Firefox
76
+
77
+ ```bash
78
+ firefox --headless --remote-debugging-port=9223 --no-remote
79
+ ```
80
+
81
+ ## Documentation
82
+
83
+ - [Quick Start](https://bidiwave.readthedocs.io/quick-start/)
84
+ - [API Reference](https://bidiwave.readthedocs.io/api/)
85
+ - [Cookbook](https://bidiwave.readthedocs.io/cookbook/)
86
+ - [Error Handling](https://bidiwave.readthedocs.io/error-handling/)
87
+
88
+ ## License
89
+
90
+ MIT