pywebflx 0.1.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 (80) hide show
  1. pywebflx-0.1.0/.github/workflows/ci.yml +39 -0
  2. pywebflx-0.1.0/.github/workflows/publish.yml +33 -0
  3. pywebflx-0.1.0/.gitignore +9 -0
  4. pywebflx-0.1.0/CLAUDE.md +50 -0
  5. pywebflx-0.1.0/PKG-INFO +90 -0
  6. pywebflx-0.1.0/README.md +57 -0
  7. pywebflx-0.1.0/docs/api/browser.md +100 -0
  8. pywebflx-0.1.0/docs/api/browser.pt.md +100 -0
  9. pywebflx-0.1.0/docs/api/config.md +95 -0
  10. pywebflx-0.1.0/docs/api/config.pt.md +95 -0
  11. pywebflx-0.1.0/docs/api/exceptions.md +97 -0
  12. pywebflx-0.1.0/docs/api/exceptions.pt.md +97 -0
  13. pywebflx-0.1.0/docs/api/extraction.md +183 -0
  14. pywebflx-0.1.0/docs/api/extraction.pt.md +183 -0
  15. pywebflx-0.1.0/docs/api/inspect.md +103 -0
  16. pywebflx-0.1.0/docs/api/inspect.pt.md +103 -0
  17. pywebflx-0.1.0/docs/api/interaction.md +188 -0
  18. pywebflx-0.1.0/docs/api/interaction.pt.md +188 -0
  19. pywebflx-0.1.0/docs/api/javascript.md +54 -0
  20. pywebflx-0.1.0/docs/api/javascript.pt.md +54 -0
  21. pywebflx-0.1.0/docs/api/navigation.md +63 -0
  22. pywebflx-0.1.0/docs/api/navigation.pt.md +63 -0
  23. pywebflx-0.1.0/docs/api/synchronization.md +113 -0
  24. pywebflx-0.1.0/docs/api/synchronization.pt.md +113 -0
  25. pywebflx-0.1.0/docs/backlog.md +26 -0
  26. pywebflx-0.1.0/docs/examples/rpa.md +118 -0
  27. pywebflx-0.1.0/docs/examples/rpa.pt.md +118 -0
  28. pywebflx-0.1.0/docs/examples/scraping.md +116 -0
  29. pywebflx-0.1.0/docs/examples/scraping.pt.md +116 -0
  30. pywebflx-0.1.0/docs/index.md +68 -0
  31. pywebflx-0.1.0/docs/index.pt.md +68 -0
  32. pywebflx-0.1.0/docs/installation.md +55 -0
  33. pywebflx-0.1.0/docs/installation.pt.md +55 -0
  34. pywebflx-0.1.0/docs/superpowers/plans/2026-03-28-pywebflx-implementation.md +4341 -0
  35. pywebflx-0.1.0/docs/superpowers/specs/2026-03-28-pywebflx-design.md +604 -0
  36. pywebflx-0.1.0/extension/background.js +660 -0
  37. pywebflx-0.1.0/extension/icons/icon128.png +0 -0
  38. pywebflx-0.1.0/extension/icons/icon16.png +0 -0
  39. pywebflx-0.1.0/extension/icons/icon48.png +0 -0
  40. pywebflx-0.1.0/extension/manifest.json +24 -0
  41. pywebflx-0.1.0/mkdocs.yml +88 -0
  42. pywebflx-0.1.0/pyproject.toml +63 -0
  43. pywebflx-0.1.0/setup.cfg +4 -0
  44. pywebflx-0.1.0/src/pywebflx/__init__.py +58 -0
  45. pywebflx-0.1.0/src/pywebflx/__pycache__/__init__.cpython-314.pyc +0 -0
  46. pywebflx-0.1.0/src/pywebflx/actions/__init__.py +1 -0
  47. pywebflx-0.1.0/src/pywebflx/browser.py +591 -0
  48. pywebflx-0.1.0/src/pywebflx/cli.py +206 -0
  49. pywebflx-0.1.0/src/pywebflx/config.py +110 -0
  50. pywebflx-0.1.0/src/pywebflx/connection.py +200 -0
  51. pywebflx-0.1.0/src/pywebflx/exceptions.py +230 -0
  52. pywebflx-0.1.0/src/pywebflx/extension/background.js +660 -0
  53. pywebflx-0.1.0/src/pywebflx/extension/icons/icon128.png +0 -0
  54. pywebflx-0.1.0/src/pywebflx/extension/icons/icon16.png +0 -0
  55. pywebflx-0.1.0/src/pywebflx/extension/icons/icon48.png +0 -0
  56. pywebflx-0.1.0/src/pywebflx/extension/manifest.json +24 -0
  57. pywebflx-0.1.0/src/pywebflx/logging.py +187 -0
  58. pywebflx-0.1.0/src/pywebflx/protocol.py +107 -0
  59. pywebflx-0.1.0/src/pywebflx/selectors.py +143 -0
  60. pywebflx-0.1.0/src/pywebflx/tab_manager.py +83 -0
  61. pywebflx-0.1.0/src/pywebflx.egg-info/PKG-INFO +90 -0
  62. pywebflx-0.1.0/src/pywebflx.egg-info/SOURCES.txt +78 -0
  63. pywebflx-0.1.0/src/pywebflx.egg-info/dependency_links.txt +1 -0
  64. pywebflx-0.1.0/src/pywebflx.egg-info/entry_points.txt +2 -0
  65. pywebflx-0.1.0/src/pywebflx.egg-info/requires.txt +10 -0
  66. pywebflx-0.1.0/src/pywebflx.egg-info/top_level.txt +1 -0
  67. pywebflx-0.1.0/teste.py +24 -0
  68. pywebflx-0.1.0/teste_scraping.py +123 -0
  69. pywebflx-0.1.0/teste_toscrape.py +35 -0
  70. pywebflx-0.1.0/tests/conftest.py +1 -0
  71. pywebflx-0.1.0/tests/test_browser.py +59 -0
  72. pywebflx-0.1.0/tests/test_cli.py +28 -0
  73. pywebflx-0.1.0/tests/test_config.py +115 -0
  74. pywebflx-0.1.0/tests/test_connection.py +66 -0
  75. pywebflx-0.1.0/tests/test_exceptions.py +113 -0
  76. pywebflx-0.1.0/tests/test_logging.py +131 -0
  77. pywebflx-0.1.0/tests/test_protocol.py +84 -0
  78. pywebflx-0.1.0/tests/test_regression_e2e.py +464 -0
  79. pywebflx-0.1.0/tests/test_selectors.py +72 -0
  80. pywebflx-0.1.0/tests/test_tab_manager.py +114 -0
@@ -0,0 +1,39 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [master, main]
6
+ pull_request:
7
+ branches: [master, main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ${{ matrix.os }}
12
+ strategy:
13
+ matrix:
14
+ os: [ubuntu-latest, windows-latest]
15
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Set up Python ${{ matrix.python-version }}
21
+ uses: actions/setup-python@v5
22
+ with:
23
+ python-version: ${{ matrix.python-version }}
24
+
25
+ - name: Install dependencies
26
+ run: |
27
+ python -m pip install --upgrade pip
28
+ python -m pip install -e ".[dev]"
29
+
30
+ - name: Run unit tests
31
+ run: python -m pytest tests/ -v --ignore=tests/test_regression_e2e.py
32
+
33
+ - name: Build package
34
+ run: |
35
+ python -m pip install build
36
+ python -m build
37
+
38
+ - name: Verify extension in wheel
39
+ run: python -c "import pywebflx.cli; print(pywebflx.cli._get_extension_dir())"
@@ -0,0 +1,33 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ id-token: write
9
+
10
+ jobs:
11
+ publish:
12
+ runs-on: ubuntu-latest
13
+
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+
17
+ - name: Set up Python
18
+ uses: actions/setup-python@v5
19
+ with:
20
+ python-version: "3.13"
21
+
22
+ - name: Install build tools
23
+ run: |
24
+ python -m pip install --upgrade pip
25
+ python -m pip install build
26
+
27
+ - name: Build package
28
+ run: python -m build
29
+
30
+ - name: Publish to PyPI
31
+ uses: pypa/gh-action-pypi-publish@release/v1
32
+ with:
33
+ password: ${{ secrets.PYPI_API_TOKEN }}
@@ -0,0 +1,9 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.pyo
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .pytest_cache/
8
+ *.log
9
+ site/
@@ -0,0 +1,50 @@
1
+ # PyWebFlx - Project Instructions
2
+
3
+ ## Overview
4
+
5
+ PyWebFlx is a Python browser automation library that connects to already-open Chrome pages via Chrome Extension (Manifest V3) + WebSocket. The Python side runs a WebSocket server; the extension connects as a client.
6
+
7
+ ## Architecture
8
+
9
+ - **Python package**: `src/pywebflx/` (async/await API)
10
+ - **Chrome Extension**: `extension/` (Manifest V3, service worker)
11
+ - **Documentation**: `docs/` (MkDocs Material, bilingual EN + PT-BR)
12
+ - **Tests**: `tests/`
13
+
14
+ ## Documentation Rules
15
+
16
+ - **ALWAYS** update documentation when changing the API, adding features, or modifying behavior.
17
+ - Documentation lives in `docs/` with MkDocs Material + mkdocs-static-i18n.
18
+ - Every `.md` file has a `.pt.md` counterpart (PT-BR translation). **Always update both**.
19
+ - PT-BR docs must use proper accents (acentuacao correta).
20
+ - After updating docs, deploy with: `"C:/Program Files/Python313/python.exe" -m mkdocs gh-deploy --force`
21
+ - Do NOT mention UiPath in any documentation.
22
+
23
+ ## Key Commands
24
+
25
+ ```bash
26
+ # Run tests
27
+ "C:/Program Files/Python313/python.exe" -m pytest tests/ -v
28
+
29
+ # Deploy docs
30
+ "C:/Program Files/Python313/python.exe" -m mkdocs gh-deploy --force
31
+
32
+ # Install in dev mode
33
+ "C:/Program Files/Python313/python.exe" -m pip install -e ".[dev]"
34
+ ```
35
+
36
+ ## Conventions
37
+
38
+ - Python path on this machine: `C:/Program Files/Python313/python.exe`
39
+ - WebSocket port: 9819
40
+ - All Python code uses async/await with asyncio
41
+ - Extension uses chrome.scripting.executeScript (no eval/new Function due to CSP)
42
+ - The `open` parameter in `use_browser()` follows: `"if_not_open"` (default), `"open"`, `"never"`
43
+ - URL and title matching is partial (contains), not exact
44
+
45
+ ## Git
46
+
47
+ - Remote: https://github.com/theflexa/pywebflx
48
+ - Author: Flexa <the.flexa@outlook.com>
49
+ - Docs site: https://theflexa.github.io/pywebflx/
50
+ - Main branch: `master`
@@ -0,0 +1,90 @@
1
+ Metadata-Version: 2.4
2
+ Name: pywebflx
3
+ Version: 0.1.0
4
+ Summary: Browser automation for already-open Chrome pages via Extension + WebSocket
5
+ Author-email: Flexa <the.flexa@outlook.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/theflexa/pywebflx
8
+ Project-URL: Documentation, https://theflexa.github.io/pywebflx/
9
+ Project-URL: Repository, https://github.com/theflexa/pywebflx
10
+ Project-URL: Issues, https://github.com/theflexa/pywebflx/issues
11
+ Keywords: browser,automation,chrome,rpa,websocket,scraping
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development :: Testing
20
+ Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
21
+ Classifier: Framework :: AsyncIO
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/markdown
24
+ Requires-Dist: websockets>=12.0
25
+ Requires-Dist: loguru>=0.7.0
26
+ Requires-Dist: click>=8.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest>=8.0; extra == "dev"
29
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
30
+ Requires-Dist: pytest-mock>=3.12; extra == "dev"
31
+ Requires-Dist: build; extra == "dev"
32
+ Requires-Dist: twine; extra == "dev"
33
+
34
+ # PyWebFlx
35
+
36
+ **Automação de navegador para páginas Chrome já abertas.**
37
+
38
+ PyWebFlx conecta a páginas já abertas no Chrome via uma extensão (Manifest V3) + WebSocket. Diferente do Selenium, não cria sandbox — você automatiza o navegador real.
39
+
40
+ ## Instalacao
41
+
42
+ ```bash
43
+ pip install pywebflx
44
+ pywebflx install-extension
45
+ ```
46
+
47
+ ## Quick Start
48
+
49
+ ```python
50
+ import asyncio
51
+ from pywebflx import use_browser
52
+
53
+ async def main():
54
+ async with use_browser(url="https://quotes.toscrape.com/") as browser:
55
+ # Inspect — entender a pagina
56
+ print(await browser.inspect(depth=5))
57
+
58
+ # Extrair dados
59
+ quotes = await browser.extract_data(
60
+ container="body",
61
+ row=".quote",
62
+ columns={"texto": ".text", "autor": ".author"}
63
+ )
64
+ for q in quotes:
65
+ print(f"{q['autor']}: {q['texto'][:60]}...")
66
+
67
+ asyncio.run(main())
68
+ ```
69
+
70
+ ## Principais funcionalidades
71
+
72
+ | Metodo | Descricao |
73
+ |--------|-----------|
74
+ | `use_browser()` | Conecta a aba por URL/titulo, abre se nao existir |
75
+ | `click()` | Clica (CSS, XPath ou texto) |
76
+ | `type_into()` | Digita em campos |
77
+ | `get_text()` | Extrai texto de elemento |
78
+ | `extract_data()` | Extrai dados estruturados (cards, listas) |
79
+ | `extract_table()` | Extrai tabelas HTML com paginacao |
80
+ | `inspect()` | Visao resumida do DOM (otimizado pra IA) |
81
+ | `wait_element()` | Espera elemento aparecer |
82
+ | `execute_js()` | Executa JavaScript na pagina |
83
+
84
+ ## Documentacao
85
+
86
+ [https://theflexa.github.io/pywebflx/](https://theflexa.github.io/pywebflx/)
87
+
88
+ ## Licenca
89
+
90
+ MIT
@@ -0,0 +1,57 @@
1
+ # PyWebFlx
2
+
3
+ **Automação de navegador para páginas Chrome já abertas.**
4
+
5
+ PyWebFlx conecta a páginas já abertas no Chrome via uma extensão (Manifest V3) + WebSocket. Diferente do Selenium, não cria sandbox — você automatiza o navegador real.
6
+
7
+ ## Instalacao
8
+
9
+ ```bash
10
+ pip install pywebflx
11
+ pywebflx install-extension
12
+ ```
13
+
14
+ ## Quick Start
15
+
16
+ ```python
17
+ import asyncio
18
+ from pywebflx import use_browser
19
+
20
+ async def main():
21
+ async with use_browser(url="https://quotes.toscrape.com/") as browser:
22
+ # Inspect — entender a pagina
23
+ print(await browser.inspect(depth=5))
24
+
25
+ # Extrair dados
26
+ quotes = await browser.extract_data(
27
+ container="body",
28
+ row=".quote",
29
+ columns={"texto": ".text", "autor": ".author"}
30
+ )
31
+ for q in quotes:
32
+ print(f"{q['autor']}: {q['texto'][:60]}...")
33
+
34
+ asyncio.run(main())
35
+ ```
36
+
37
+ ## Principais funcionalidades
38
+
39
+ | Metodo | Descricao |
40
+ |--------|-----------|
41
+ | `use_browser()` | Conecta a aba por URL/titulo, abre se nao existir |
42
+ | `click()` | Clica (CSS, XPath ou texto) |
43
+ | `type_into()` | Digita em campos |
44
+ | `get_text()` | Extrai texto de elemento |
45
+ | `extract_data()` | Extrai dados estruturados (cards, listas) |
46
+ | `extract_table()` | Extrai tabelas HTML com paginacao |
47
+ | `inspect()` | Visao resumida do DOM (otimizado pra IA) |
48
+ | `wait_element()` | Espera elemento aparecer |
49
+ | `execute_js()` | Executa JavaScript na pagina |
50
+
51
+ ## Documentacao
52
+
53
+ [https://theflexa.github.io/pywebflx/](https://theflexa.github.io/pywebflx/)
54
+
55
+ ## Licenca
56
+
57
+ MIT
@@ -0,0 +1,100 @@
1
+ # use_browser
2
+
3
+ Main entry point for PyWebFlx. Connects to a Chrome tab and returns a `BrowserContext` with all automation methods.
4
+
5
+ ## Signature
6
+
7
+ ```python
8
+ async with use_browser(
9
+ url: str = None,
10
+ title: str = None,
11
+ open: str = "if_not_open",
12
+ config: PyWebFlxConfig = None,
13
+ ) as browser:
14
+ ...
15
+ ```
16
+
17
+ ## Parameters
18
+
19
+ | Parameter | Type | Default | Description |
20
+ |-----------|------|---------|-------------|
21
+ | `url` | `str` | `None` | URL to search (partial match) and/or open |
22
+ | `title` | `str` | `None` | Tab title to search (partial match) |
23
+ | `open` | `str` | `"if_not_open"` | Opening behavior (see below) |
24
+ | `config` | `PyWebFlxConfig` | `None` | Custom configuration |
25
+
26
+ !!! info "Partial match"
27
+ Both `url` and `title` use **partial match** (contains). You don't need the full URL — just the fixed part is enough. This is especially useful for dynamic URLs:
28
+
29
+ ```python
30
+ # Matches any tab at mycon.gupy.io/candidates/...
31
+ async with use_browser(url="mycon.gupy.io/candidates") as browser:
32
+ ...
33
+ ```
34
+
35
+ !!! tip "Combining `url` and `title`"
36
+ You can use both parameters together. The search uses **AND** logic — the tab must match both criteria:
37
+
38
+ ```python
39
+ # Tab must contain "gupy.io" in URL AND "Candidatos" in title
40
+ async with use_browser(url="gupy.io", title="Candidatos") as browser:
41
+ ...
42
+ ```
43
+
44
+ ### `open` parameter
45
+
46
+ | Value | Behavior |
47
+ |-------|----------|
48
+ | `"if_not_open"` | Searches for an existing tab. If not found, opens the `url`. **(default)** |
49
+ | `"open"` | Always opens a new tab with the `url`. |
50
+ | `"never"` | Only connects to an existing tab. Never opens. |
51
+
52
+ ## Examples
53
+
54
+ ### Basic -- opens if not found
55
+
56
+ ```python
57
+ async with use_browser(url="https://quotes.toscrape.com/") as browser:
58
+ await browser.click("#btn")
59
+ ```
60
+
61
+ ### Search by title
62
+
63
+ ```python
64
+ async with use_browser(title="Portal Sicoob", open="never") as browser:
65
+ balance = await browser.get_text(".balance")
66
+ ```
67
+
68
+ ### Always open a new tab
69
+
70
+ ```python
71
+ async with use_browser(url="https://example.com", open="open") as browser:
72
+ await browser.type_into("#email", text="user@email.com")
73
+ ```
74
+
75
+ ### With custom configuration
76
+
77
+ ```python
78
+ from pywebflx import PyWebFlxConfig
79
+
80
+ config = PyWebFlxConfig(default_timeout=30, retry_count=3)
81
+
82
+ async with use_browser(url="https://example.com", config=config) as browser:
83
+ await browser.click("#btn", timeout=60) # 60 overrides 30
84
+ ```
85
+
86
+ ### Multiple tabs
87
+
88
+ ```python
89
+ async with use_browser(url="https://portal.com") as tab1:
90
+ async with use_browser(url="https://email.com") as tab2:
91
+ data = await tab1.get_text(".balance")
92
+ await tab2.type_into("#body", text=data)
93
+ ```
94
+
95
+ ## Exceptions
96
+
97
+ | Exception | When |
98
+ |-----------|------|
99
+ | `BrowserNotFoundError` | Tab not found and `open="never"` |
100
+ | `ExtensionNotConnectedError` | Chrome extension is not connected |
@@ -0,0 +1,100 @@
1
+ # use_browser
2
+
3
+ Ponto de entrada principal do PyWebFlx. Conecta a uma aba do Chrome e retorna um `BrowserContext` com todos os métodos de automação.
4
+
5
+ ## Assinatura
6
+
7
+ ```python
8
+ async with use_browser(
9
+ url: str = None,
10
+ title: str = None,
11
+ open: str = "if_not_open",
12
+ config: PyWebFlxConfig = None,
13
+ ) as browser:
14
+ ...
15
+ ```
16
+
17
+ ## Parâmetros
18
+
19
+ | Parâmetro | Tipo | Padrão | Descrição |
20
+ |-----------|------|--------|-----------|
21
+ | `url` | `str` | `None` | URL para buscar (correspondência parcial) e/ou abrir |
22
+ | `title` | `str` | `None` | Título da aba para buscar (correspondência parcial) |
23
+ | `open` | `str` | `"if_not_open"` | Comportamento de abertura (veja abaixo) |
24
+ | `config` | `PyWebFlxConfig` | `None` | Configuração personalizada |
25
+
26
+ !!! info "Correspondência parcial"
27
+ Tanto `url` quanto `title` usam **correspondência parcial** (contém). Não é necessário a URL completa — basta o trecho fixo. Isso é especialmente útil para URLs dinâmicas:
28
+
29
+ ```python
30
+ # Encontra qualquer aba em mycon.gupy.io/candidates/...
31
+ async with use_browser(url="mycon.gupy.io/candidates") as browser:
32
+ ...
33
+ ```
34
+
35
+ !!! tip "Combinando `url` e `title`"
36
+ Você pode usar ambos os parâmetros juntos. A busca usa lógica **AND** — a aba precisa corresponder aos dois critérios:
37
+
38
+ ```python
39
+ # Aba deve conter "gupy.io" na URL E "Candidatos" no título
40
+ async with use_browser(url="gupy.io", title="Candidatos") as browser:
41
+ ...
42
+ ```
43
+
44
+ ### Parâmetro `open`
45
+
46
+ | Valor | Comportamento |
47
+ |-------|---------------|
48
+ | `"if_not_open"` | Busca uma aba existente. Se não encontrar, abre a `url`. **(padrão)** |
49
+ | `"open"` | Sempre abre uma nova aba com a `url`. |
50
+ | `"never"` | Apenas conecta a uma aba existente. Nunca abre. |
51
+
52
+ ## Exemplos
53
+
54
+ ### Básico -- abre se não encontrar
55
+
56
+ ```python
57
+ async with use_browser(url="https://quotes.toscrape.com/") as browser:
58
+ await browser.click("#btn")
59
+ ```
60
+
61
+ ### Buscar por título
62
+
63
+ ```python
64
+ async with use_browser(title="Portal Sicoob", open="never") as browser:
65
+ balance = await browser.get_text(".balance")
66
+ ```
67
+
68
+ ### Sempre abrir uma nova aba
69
+
70
+ ```python
71
+ async with use_browser(url="https://example.com", open="open") as browser:
72
+ await browser.type_into("#email", text="user@email.com")
73
+ ```
74
+
75
+ ### Com configuração personalizada
76
+
77
+ ```python
78
+ from pywebflx import PyWebFlxConfig
79
+
80
+ config = PyWebFlxConfig(default_timeout=30, retry_count=3)
81
+
82
+ async with use_browser(url="https://example.com", config=config) as browser:
83
+ await browser.click("#btn", timeout=60) # 60 sobrescreve 30
84
+ ```
85
+
86
+ ### Múltiplas abas
87
+
88
+ ```python
89
+ async with use_browser(url="https://portal.com") as tab1:
90
+ async with use_browser(url="https://email.com") as tab2:
91
+ data = await tab1.get_text(".balance")
92
+ await tab2.type_into("#body", text=data)
93
+ ```
94
+
95
+ ## Exceções
96
+
97
+ | Exceção | Quando |
98
+ |---------|--------|
99
+ | `BrowserNotFoundError` | Aba não encontrada e `open="never"` |
100
+ | `ExtensionNotConnectedError` | Extensão do Chrome não está conectada |
@@ -0,0 +1,95 @@
1
+ # Configuration
2
+
3
+ `PyWebFlxConfig` controls timeouts, retries, WebSocket port, and logging.
4
+
5
+ ## Signature
6
+
7
+ ```python
8
+ from pywebflx import PyWebFlxConfig
9
+
10
+ config = PyWebFlxConfig(
11
+ default_timeout=10,
12
+ delay_between_actions=0.3,
13
+ retry_count=0,
14
+ on_error="raise",
15
+ ws_port=9819,
16
+ log_level="INFO",
17
+ )
18
+ ```
19
+
20
+ ## Parameters
21
+
22
+ | Parameter | Type | Default | Description |
23
+ |-----------|------|---------|-------------|
24
+ | `default_timeout` | `float` | `10` | Default timeout in seconds |
25
+ | `delay_between_actions` | `float` | `0.3` | Delay between consecutive actions |
26
+ | `retry_count` | `int` | `0` | Default retry attempts on failure |
27
+ | `on_error` | `str` | `"raise"` | `"raise"` or `"continue"` |
28
+ | `ws_port` | `int` | `9819` | WebSocket server port |
29
+ | `log_level` | `str` | `"INFO"` | Log level |
30
+
31
+ ## Priority
32
+
33
+ Parameter resolution follows this order:
34
+
35
+ ```
36
+ action parameter > use_browser config > global defaults
37
+ ```
38
+
39
+ ```python
40
+ # Global: timeout = 10 (default)
41
+ # Config: timeout = 20
42
+ # Action: timeout = 60 (wins)
43
+
44
+ config = PyWebFlxConfig(default_timeout=20)
45
+
46
+ async with use_browser(url="https://example.com", config=config) as browser:
47
+ await browser.click("#btn", timeout=60) # uses 60
48
+ await browser.click("#btn2") # uses 20
49
+ ```
50
+
51
+ ## Global defaults
52
+
53
+ ```python
54
+ # Affect all new instances
55
+ PyWebFlxConfig.set_defaults(default_timeout=15, retry_count=3)
56
+
57
+ # Reset to original values
58
+ PyWebFlxConfig.reset_defaults()
59
+ ```
60
+
61
+ ## Logging
62
+
63
+ ```python
64
+ from pywebflx import configure_logging
65
+
66
+ configure_logging(level="INFO") # console (default)
67
+ configure_logging(level="DEBUG", sink="automation.log") # file
68
+ configure_logging(level="DISABLED") # silent
69
+ ```
70
+
71
+ ### Levels
72
+
73
+ | Level | What it logs |
74
+ |-------|-------------|
75
+ | `ERROR` | Final failure (after all retries) |
76
+ | `WARN` | Retry, reconnection, fallback |
77
+ | `INFO` | Action executed successfully |
78
+ | `DEBUG` | Internal details (selector, JSON payload) |
79
+ | `TRACE` | Everything (WebSocket bytes, partial DOM) |
80
+ | `DISABLED` | Nothing |
81
+
82
+ ### Format
83
+
84
+ ```
85
+ 2026-03-28 14:32:05.123 [INFO] [browser.click] #btn-save -> tab:42 -> success (120ms)
86
+ 2026-03-28 14:32:06.102 [WARN] [browser.click] #btn-send -> tab:42 -> retry 1/3
87
+ 2026-03-28 14:32:08.001 [ERROR] [browser.extract_table] #table -> tab:42 -> failed: TabClosedError
88
+ ```
89
+
90
+ ### Per-action override
91
+
92
+ ```python
93
+ await browser.click("#critical-btn", log_level="DEBUG") # more detail
94
+ await browser.click(row_selector, log_level="WARN") # less noise in loops
95
+ ```
@@ -0,0 +1,95 @@
1
+ # Configuração
2
+
3
+ `PyWebFlxConfig` controla timeouts, retentativas, porta WebSocket e logging.
4
+
5
+ ## Assinatura
6
+
7
+ ```python
8
+ from pywebflx import PyWebFlxConfig
9
+
10
+ config = PyWebFlxConfig(
11
+ default_timeout=10,
12
+ delay_between_actions=0.3,
13
+ retry_count=0,
14
+ on_error="raise",
15
+ ws_port=9819,
16
+ log_level="INFO",
17
+ )
18
+ ```
19
+
20
+ ## Parâmetros
21
+
22
+ | Parâmetro | Tipo | Padrão | Descrição |
23
+ |-----------|------|--------|-----------|
24
+ | `default_timeout` | `float` | `10` | Timeout padrão em segundos |
25
+ | `delay_between_actions` | `float` | `0.3` | Atraso entre ações consecutivas |
26
+ | `retry_count` | `int` | `0` | Tentativas de retentativa padrão em caso de falha |
27
+ | `on_error` | `str` | `"raise"` | `"raise"` ou `"continue"` |
28
+ | `ws_port` | `int` | `9819` | Porta do servidor WebSocket |
29
+ | `log_level` | `str` | `"INFO"` | Nível de log |
30
+
31
+ ## Prioridade
32
+
33
+ A resolução de parâmetros segue esta ordem:
34
+
35
+ ```
36
+ parâmetro da ação > config do use_browser > padrões globais
37
+ ```
38
+
39
+ ```python
40
+ # Global: timeout = 10 (padrão)
41
+ # Config: timeout = 20
42
+ # Ação: timeout = 60 (vence)
43
+
44
+ config = PyWebFlxConfig(default_timeout=20)
45
+
46
+ async with use_browser(url="https://example.com", config=config) as browser:
47
+ await browser.click("#btn", timeout=60) # usa 60
48
+ await browser.click("#btn2") # usa 20
49
+ ```
50
+
51
+ ## Padrões globais
52
+
53
+ ```python
54
+ # Afeta todas as novas instancias
55
+ PyWebFlxConfig.set_defaults(default_timeout=15, retry_count=3)
56
+
57
+ # Resetar para valores originais
58
+ PyWebFlxConfig.reset_defaults()
59
+ ```
60
+
61
+ ## Logging
62
+
63
+ ```python
64
+ from pywebflx import configure_logging
65
+
66
+ configure_logging(level="INFO") # console (padrão)
67
+ configure_logging(level="DEBUG", sink="automation.log") # arquivo
68
+ configure_logging(level="DISABLED") # silencioso
69
+ ```
70
+
71
+ ### Níveis
72
+
73
+ | Nível | O que registra |
74
+ |-------|----------------|
75
+ | `ERROR` | Falha final (após todas as retentativas) |
76
+ | `WARN` | Retentativa, reconexão, fallback |
77
+ | `INFO` | Ação executada com sucesso |
78
+ | `DEBUG` | Detalhes internos (seletor, payload JSON) |
79
+ | `TRACE` | Tudo (bytes WebSocket, DOM parcial) |
80
+ | `DISABLED` | Nada |
81
+
82
+ ### Formato
83
+
84
+ ```
85
+ 2026-03-28 14:32:05.123 [INFO] [browser.click] #btn-save -> tab:42 -> sucesso (120ms)
86
+ 2026-03-28 14:32:06.102 [WARN] [browser.click] #btn-send -> tab:42 -> retentativa 1/3
87
+ 2026-03-28 14:32:08.001 [ERROR] [browser.extract_table] #table -> tab:42 -> falhou: TabClosedError
88
+ ```
89
+
90
+ ### Override por ação
91
+
92
+ ```python
93
+ await browser.click("#critical-btn", log_level="DEBUG") # mais detalhes
94
+ await browser.click(row_selector, log_level="WARN") # menos ruído em loops
95
+ ```