s-librarykit 0.5.1__tar.gz → 0.5.2__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 (106) hide show
  1. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/PKG-INFO +1 -1
  2. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/browser/capture_tap.py +41 -0
  3. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/pyproject.toml +1 -1
  4. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_capture_tap.py +24 -0
  5. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/.gitignore +0 -0
  6. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/.gitlab-ci.yml +0 -0
  7. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/AGENTS.md +0 -0
  8. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/CHANGELOG.md +0 -0
  9. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/LICENSE +0 -0
  10. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/README.md +0 -0
  11. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/docs/superpowers/specs/2026-06-24-unified-sessions-design.md +0 -0
  12. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/__init__.py +0 -0
  13. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/antibot/__init__.py +0 -0
  14. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/antibot/_helpers.py +0 -0
  15. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/antibot/cdp.py +0 -0
  16. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/antibot/curl_cffi.py +0 -0
  17. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/antibot/page_evaluate.py +0 -0
  18. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/antibot/warmup_minter.py +0 -0
  19. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/auth.py +0 -0
  20. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/blocks.py +0 -0
  21. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/browser/__init__.py +0 -0
  22. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/browser/_engine.py +0 -0
  23. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/browser/camoufox.py +0 -0
  24. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/browser/geometry.py +0 -0
  25. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/browser/launchers.py +0 -0
  26. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/browser/nodriver.py +0 -0
  27. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/browser/persistent.py +0 -0
  28. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/browser/state.py +0 -0
  29. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/bulk.py +0 -0
  30. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/checkpoint.py +0 -0
  31. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/config_util.py +0 -0
  32. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/contract.py +0 -0
  33. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/cookie_refresh.py +0 -0
  34. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/cookies.py +0 -0
  35. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/entities.py +0 -0
  36. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/enums.py +0 -0
  37. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/errmap.py +0 -0
  38. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/errors.py +0 -0
  39. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/factory.py +0 -0
  40. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/forms.py +0 -0
  41. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/graphql.py +0 -0
  42. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/limit.py +0 -0
  43. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/orchestration/__init__.py +0 -0
  44. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/orchestration/health.py +0 -0
  45. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/orchestration/onboarding.py +0 -0
  46. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/orchestration/session_loader.py +0 -0
  47. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/pagination.py +0 -0
  48. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/pool.py +0 -0
  49. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/ports.py +0 -0
  50. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/protocols.py +0 -0
  51. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/redaction.py +0 -0
  52. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/refresh.py +0 -0
  53. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/retry.py +0 -0
  54. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/rpc.py +0 -0
  55. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/secret_store.py +0 -0
  56. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/session_bridge.py +0 -0
  57. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/session_paths.py +0 -0
  58. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/sessions/__init__.py +0 -0
  59. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/sessions/_const.py +0 -0
  60. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/sessions/_fsutil.py +0 -0
  61. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/stream.py +0 -0
  62. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/transport/__init__.py +0 -0
  63. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/transport/_helpers.py +0 -0
  64. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/transport/http_client.py +0 -0
  65. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/transport/permissive.py +0 -0
  66. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/transport/rest_client.py +0 -0
  67. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/transport/transport_http.py +0 -0
  68. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/upload.py +0 -0
  69. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/librarykit/windows.py +0 -0
  70. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/conftest.py +0 -0
  71. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_antibot.py +0 -0
  72. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_auth.py +0 -0
  73. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_blocks.py +0 -0
  74. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_browser.py +0 -0
  75. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_bulk.py +0 -0
  76. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_checkpoint.py +0 -0
  77. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_config_util.py +0 -0
  78. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_cookie_refresh.py +0 -0
  79. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_cookies.py +0 -0
  80. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_errmap.py +0 -0
  81. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_errors.py +0 -0
  82. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_factory.py +0 -0
  83. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_forms.py +0 -0
  84. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_graphql.py +0 -0
  85. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_limit.py +0 -0
  86. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_limit_contract.py +0 -0
  87. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_nodriver_backend.py +0 -0
  88. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_orchestration.py +0 -0
  89. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_pagination.py +0 -0
  90. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_permissive_http.py +0 -0
  91. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_pool.py +0 -0
  92. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_redaction.py +0 -0
  93. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_refresh.py +0 -0
  94. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_retry.py +0 -0
  95. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_rpc.py +0 -0
  96. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_session_bridge.py +0 -0
  97. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_session_paths.py +0 -0
  98. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_sessions.py +0 -0
  99. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_stream.py +0 -0
  100. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_transport_client.py +0 -0
  101. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_transport_rest.py +0 -0
  102. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_transport_sync.py +0 -0
  103. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_upload.py +0 -0
  104. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_warmup_minter.py +0 -0
  105. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/tests/test_windows.py +0 -0
  106. {s_librarykit-0.5.1 → s_librarykit-0.5.2}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: s-librarykit
3
- Version: 0.5.1
3
+ Version: 0.5.2
4
4
  Summary: Жирный КОРЕНЬ китов: единая error-иерархия, config/retry/paths-утили, checkpoint/resume, транспорт/auth/sessions — общий код для clikit, adapterkit и доменных пакетов.
5
5
  Author: Dmitry
6
6
  License: MIT
@@ -206,6 +206,8 @@ class _PlaywrightAdapter(_BaseAdapter):
206
206
  self._context = page.context
207
207
  # Request-объект → стабильный request_id (для сшивки request/response/failed).
208
208
  self._ids: "weakref.WeakKeyDictionary[Any, str]" = weakref.WeakKeyDictionary()
209
+ self._drain_stop: asyncio.Event | None = None
210
+ self._drain_task: Any = None
209
211
 
210
212
  def _id_for(self, request: Any) -> str:
211
213
  rid = self._ids.get(request)
@@ -222,6 +224,10 @@ class _PlaywrightAdapter(_BaseAdapter):
222
224
  await ctx.expose_binding(self._binding, self._on_binding)
223
225
  with contextlib.suppress(Exception):
224
226
  await ctx.add_init_script(self._init_script)
227
+ # Дренаж очереди — fallback для движков, где binding не виден из мира init-script
228
+ # (CamouFox/Firefox гонят его в изолированном мире). На chromium очередь пуста.
229
+ self._drain_stop = asyncio.Event()
230
+ self._drain_task = asyncio.ensure_future(self._drain_queue_loop())
225
231
  if self._h.on_request is not None:
226
232
  ctx.on("request", lambda r: self._spawn(self._handle_request(r)))
227
233
  if self._h.on_response is not None:
@@ -307,7 +313,42 @@ class _PlaywrightAdapter(_BaseAdapter):
307
313
  return await self._context.storage_state()
308
314
  return {"cookies": [], "origins": []}
309
315
 
316
+ async def _drain_queue_loop(self) -> None:
317
+ """Периодически дренит globalThis.__captureTraceQueue → on_action (fallback CamouFox)."""
318
+ assert self._drain_stop is not None
319
+ while not self._drain_stop.is_set():
320
+ try:
321
+ await asyncio.wait_for(self._drain_stop.wait(), timeout=0.3)
322
+ break
323
+ except asyncio.TimeoutError:
324
+ pass
325
+ await self._drain_queue_once()
326
+
327
+ async def _drain_queue_once(self) -> None:
328
+ try:
329
+ items = await self._page.evaluate(
330
+ "() => { const q = globalThis.__captureTraceQueue || [];"
331
+ " globalThis.__captureTraceQueue = []; return q; }"
332
+ )
333
+ except Exception:
334
+ return
335
+ for s in items or []:
336
+ try:
337
+ payload = json.loads(s) if isinstance(s, str) else s
338
+ except Exception:
339
+ continue
340
+ await self._emit(self._h.on_action, NormalizedAction(raw=payload))
341
+
310
342
  async def detach(self) -> None:
343
+ # Остановить дренаж очереди trace (CamouFox-fallback) + финальный дренаж.
344
+ if self._drain_stop is not None:
345
+ self._drain_stop.set()
346
+ with contextlib.suppress(Exception):
347
+ await self._drain_queue_once()
348
+ if self._drain_task is not None:
349
+ self._drain_task.cancel()
350
+ with contextlib.suppress(Exception, asyncio.CancelledError):
351
+ await self._drain_task
311
352
  # Слушатели не снимаем поимённо (Playwright требует ссылку на сам хендлер):
312
353
  # контекст всё равно закрывает backend.aclose() сразу после detach. Здесь —
313
354
  # только дренаж фоновых задач нормализации, чтобы не потерять последние тела.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "s-librarykit"
3
- version = "0.5.1"
3
+ version = "0.5.2"
4
4
  description = "Жирный КОРЕНЬ китов: единая error-иерархия, config/retry/paths-утили, checkpoint/resume, транспорт/auth/sessions — общий код для clikit, adapterkit и доменных пакетов."
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -106,10 +106,15 @@ class _MockContext:
106
106
  class _MockPage:
107
107
  def __init__(self, context):
108
108
  self.context = context
109
+ self.queue_items: list[str] = []
109
110
 
110
111
  def on(self, *_a):
111
112
  pass
112
113
 
114
+ async def evaluate(self, _script):
115
+ items, self.queue_items = self.queue_items, []
116
+ return items
117
+
113
118
 
114
119
  class _MockBackend:
115
120
  def __init__(self, page):
@@ -183,3 +188,22 @@ def test_attach_tap_playwright_flow():
183
188
  assert got_resp[0].status == 200 and got_resp[0].body == b'{"ok":true}'
184
189
  # request_id сшит между request и response
185
190
  assert got_req[0].request_id == got_resp[0].request_id
191
+
192
+
193
+ def test_attach_tap_drain_queue_fallback():
194
+ """CamouFox-fallback: событие из globalThis.__captureTraceQueue дренится page.evaluate → on_action."""
195
+ ctx = _MockContext()
196
+ page = _MockPage(ctx)
197
+ page.queue_items = ['{"kind":"action","type":"click"}', '{"kind":"mutation"}']
198
+ backend = _MockBackend(page)
199
+ got: list = []
200
+
201
+ async def run():
202
+ handle = await attach_capture_tap(
203
+ backend, on_action=got.append, init_script="/*trace*/", action_binding="__t",
204
+ )
205
+ await handle.detach() # финальный дренаж соберёт очередь
206
+
207
+ asyncio.run(run())
208
+ kinds = {a.raw.get("kind") for a in got}
209
+ assert kinds == {"action", "mutation"}
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes