Metadata-Version: 2.4
Name: s-netkit
Version: 0.0.8
Summary: СЕТЕВОЙ слой китов: транспорт (async+sync httpx), WS/RPC/GraphQL, лимиты и повторы, лестница деградации, пагинация, аплоад, form-кодек. Декларативное объявление транспорта с ЕДИНЫМ поведением лимитов и ретраев.
Author: Dmitry
License: MIT
License-File: LICENSE
Requires-Python: >=3.11
Requires-Dist: httpx>=0.27
Requires-Dist: s-corekit>=0.0.4
Requires-Dist: stamina>=24.3
Provides-Extra: curl
Requires-Dist: curl-cffi>=0.7; extra == 'curl'
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Requires-Dist: ruff>=0.8; extra == 'dev'
Provides-Extra: ws
Requires-Dist: websockets>=12.0; extra == 'ws'
Description-Content-Type: text/markdown

# netkit (`s-netkit`)

**Сетевой слой китов.** Всё, чем интеграция разговаривает с чужим сервером:
транспорт, темп, живучесть, деградация. Ставится и работает без оркестратора —
зависимости идут строго вниз: `netkit → corekit`.

```
     clikit        adapterkit        <- ветки-оболочки
          \           /
           librarykit                <- ОРКЕСТРАТОР (сессии, браузер, склад)
               |
            netkit                   <- СЕТЬ (этот пакет)
               |
            corekit                  <- ОСНОВАНИЕ (значения и правила)
```

## Что внутри

| модуль | ответственность |
|---|---|
| `netkit.transport` | исполнители запроса поверх httpx (async + sync близнец), choke-point `HttpClient`/`SyncHttpClient`, `RestHttpClient` к своему backend, permissive-рецепты |
| `netkit.stream` | persistent-каналы: `StreamTransport`, WS-реализация (extra `[ws]`) |
| `netkit.rpc` | codec-слой RPC: `JsonCodec` / `PrefixedJsonCodec` / `RpcClient` |
| `netkit.graphql` | GraphQL-клиент поверх транспорта кита |
| `netkit.limit` | `RateLimiter` + token-bucket: ПРОАКТИВНЫЙ темп, а не «поймал 429 — поспал» |
| `netkit.retry` | политики повторов: header-driven (`RetryPolicy`) и фиксированная (`SimpleRetryPolicy`) |
| `netkit.ladder` | лестница деградации: чем выполнять запросы и чем добывать состояние, память ступени, события спуска |
| `netkit.pagination` / `netkit.upload` / `netkit.forms` | листание ресурса, resumable-догрузка, form-urlencoded кодек |
| `netkit.errmap` | ответ сервера → доменная ошибка (декларативная таблица) |
| `netkit.declare` | **объявление транспорта** (`http`/`ws`/`rpc`/`graphql`) с ЕДИНЫМ поведением лимитов и повторов |
| `netkit.providers` | СЛОТЫ верхнего слоя: браузерный минт, склад состояния, диагностика, egress |

## Объявить транспорт декларативно

Один и тот же лимит и одна и та же политика повторов — на любом виде транспорта.
Интеграция объявляет, а не пишет обвязку:

```python
from netkit.declare import KIND_HTTP, KIND_WS, TransportSpec, declare
from netkit.limit import LimitPolicy, LimitScope
from netkit.retry import SimpleRetryPolicy

limit = LimitPolicy(rate=5, per=1.0)          # 5 обменов в секунду
retry = SimpleRetryPolicy(backoff=(0.0, 0.0))  # 2 повтора без пауз
scope = LimitScope(service="acme")

api = declare(TransportSpec(kind=KIND_HTTP, url="https://api.acme.io",
                            limit=limit, scope=scope, retry=retry))
live = declare(TransportSpec(kind=KIND_WS, channel=my_ws_channel,
                            limit=limit, scope=scope, retry=retry))

await api.call("GET", "/v1/items")   # ждёт квоту, повторяет 429/5xx и сбои
await live.call('{"op":"ping"}')     # ТОТ ЖЕ темп и ТЕ ЖЕ повторы — без своего кода
```

Свой вид (`grpc`, `sse`, …) добавляется `register_kind(kind, builder)` и сразу
получает то же поведение.

## Слоты: как netkit зовёт то, что живёт выше

Последняя ступень лестницы поднимает браузер, а браузер — чужой кит, который сам
зависит от сети. Прямой импорт дал бы цикл, поэтому направление разворачивается:
netkit **объявляет слот**, верхний слой **заполняет** его на своём импорте.

```python
from netkit.providers import SLOT_BROWSER_MINT, register_provider
register_provider(SLOT_BROWSER_MINT, my_mint_session)
```

Слоты со своим дефолтом (`json_store`, `file_lock`, `state_root`, `path_slug`,
`transport_factory`, `diagnose`, `egress_proxy`) никогда не роняют вызов — netkit
умеет их сам, верхний слой лишь уточняет. Слоты без дефолта (браузерные) при
обращении поднимают `ProviderMissing` с инструкцией: молчаливой деградации
«ступень тихо ничего не сделала» здесь нет.

Если в окружении стоит `librarykit`, его импорт заполняет все слоты сам —
отдельная регистрация не нужна.

## Совместимость

`librarykit` остаётся фасадом: `librarykit.transport`, `librarykit.ladder`,
`librarykit.limit`, `librarykit.retry`, `librarykit.pagination`,
`librarykit.upload`, `librarykit.forms`, `librarykit.rpc`, `librarykit.stream`,
`librarykit.graphql`, `librarykit.errmap` реэкспортируют ТЕ ЖЕ объекты (не
копии) — `isinstance` / `except` / `is` работают через любой из путей.

## Стоимость импорта

`import netkit` не исполняет ни одного подмодуля: ни httpx, ни stamina, ни
asyncio. Имена резолвятся по PEP 562 при первом обращении — платит тот, кому
нужно.

## Установка

```bash
pip install s-netkit          # ядро: corekit + httpx + stamina
pip install s-netkit[ws]      # + websockets для WS-транспорта
```
