Metadata-Version: 2.4
Name: python-library-patch-bay
Version: 0.2.1
Requires-Python: >=3.10
Requires-Dist: aiohttp<4,>=3.9
Requires-Dist: msgpack<2,>=1
Requires-Dist: pydantic<3,>=2
Requires-Dist: python-library-express-evaluator==0.1.0
Provides-Extra: rich
Requires-Dist: rich<14,>=13; extra == 'rich'
Description-Content-Type: text/markdown

# patch_bay

> **一句话**：你写一张「谁在哪监听、线怎么连、线上什么条件才放行」的表；程序启动后**主动去连**每一个监听地址，帮你在这些点之间**转包**。

## 一分钟理解

1. **配置**：列出要连的 `host:port`、给它们起名字，再画有向边（从谁到谁）。  
2. **启动**：中央进程按表**拨号**，维持长连接；断线会按实现策略重试。  
3. **转发**：谁发来一包，就沿着从它出发的边往下试；边上可以挂**表达式**，只有算出来为真才真的发出去；若边上还配置了**补丁名列表**，则先通过规则，再按顺序改数据，最后发出。

---

## 谁干什么（对照表）

| 角色 | 在本配置里长什么样 | 本包帮你做什么 |
|------|-------------------|----------------|
| 对端进程 | 一行 `host:port` + 在 `jacks` 里的名字 | 主动去连这个地址 |
| 你（运维/开发） | 改 YAML/JSON 配置 | 改拓扑多半**只改配置**，不用改对端互联代码 |

---

## 设计特性

### 配置驱动的星型拓扑

- 用一张表说明「**名字 ↔ 监听地址**」和「**从谁到谁**的边」。  
- 对端之间**不直连**，都只跟中央进程说话；换拓扑主要是**改配置**。

### 主动拨号与长连接维持

1. 配置里出现几个地址，就去连几条线。  
2. 对端在表里就是 **`host:port` 字符串**；本包**不依赖**任何固定的「接入 SDK」。

### 边上表达式路由

| 步骤 | 发生了什么 |
|------|------------|
| 1 | 收到某一端发来的一包字节 |
| 2 | 看从它出发的边，按配置顺序试 |
| 3 | 边上的规则在「当前这包」上求值，为真才转发 |

求值失败或表达式为假，就**不沿这条边发**，继续试下一条（若有）。

### 同源多线与有序尝试

- 同一个「起点名字」可以拉出**多条**出线。  
- **按配置里的先后顺序**一条一条试；适合做主备、分流、逐步收紧条件。

### 字节透明的载荷模型

- 中央只管**字节流**：不替你假定一定是 JSON 或一定是某种对象。  
- 规则里能看到长度、尝试解析 JSON 等；**业务长什么样**留在各对端进程里。

### 可插拔的交换侧监听器

1. 提供一套**同步回调**（连上谁、断谁、转成功/失败等）。  
2. 附带一个**开箱即用**的日志实现，方便先跑起来再慢慢接自己的监控。

### 与接入侧对齐的有线帧语义

- 本包自带和对端约定一致的**帧打包/解包**，保证「握手、发、投递、确认、报错」这些控制意思在字节上对齐。  
- 以后协议要升级，记得**和对端实现一起**对表，别只改一边。

### 连线上的数据包补丁（patchs）

- 在配置顶层声明一组**具名补丁**：每个补丁用 `name` 标识，其 `patch` 下给出「参数名 → 目标值」的改写表（适用于可解析为对象的数据包，如 JSON 的顶层字段）。  
- 在 `wires` 上可选填写 `patchs`：**补丁名字的列表**。当该边已通过 `rule`、即将转发时，按列表**顺序依次**套用对应补丁，把数据改完再编码发出。  
- **严格存在性**：若某个补丁要求改写的参数名在当前数据包中**不存在**，视为错误：**记录错误并丢弃这一包**，该边**不再发出**本条消息（不当作“跳过该键”继续发）。  
- **严格类型一致**：对每个要改写的键，配置里给出的**目标值**与数据包中该键**当前值**须为同一 Python 类型（例如原值为 `int` 则补丁也必须是 `int`，`float`/`bool`/`str`/`list`/`dict`/`NoneType` 等同理；`1` 与 `1.0` 因类型不同也会判为错误）。类型不一致时与缺键相同：**记录错误、丢弃该边本条消息**。  
- 未配置 `patchs` 或列表为空时，转发路径上不对载荷做改写（仅路由与规则行为）。

---

## 配置

### `jacks`

数组；元素字段：

| 字段 | 说明 |
|---|---|
| `name` | 名称（`wires` 里用名字引用，便于配置复用） |
| `address` | 对端在本机 **监听** 的 `host:port`（中央节点向此地址发起长连接），须在列表内唯一 |

接入进程自行在本机绑定 `host:port` 并挂协议约定路径；本配置里的 `address` 须为中央节点可达的 IP:端口。业务代码**不必**与本字段同名，只需把对端实际监听地址填进配置。

### `wires`

数组；元素常用键名 `from`、`to`。字段：

| 字段 | 说明 |
|---|---|
| `from` | 源端在 `jacks` 里声明的 `name` |
| `to` | 目标端在 `jacks` 里声明的 `name` |
| `rule` | 在 `rules` 中已定义的规则 id |
| `patchs` | 可选；要应用的补丁名称列表（引用顶层 `patchs` 里某项的 `name`），按顺序套用；省略或空列表表示转发时不改写数据 |

### `patchs`

数组；元素描述一条**具名**的数据改写规则，供 `wires.patchs` 引用。

| 字段 | 说明 |
|---|---|
| `name` | 补丁名称，供 `wires` 中 `patchs` 列表引用 |
| `patch` | 映射表：键为数据包内要修改的**参数名**（实现上与可解析载荷的字段对应，例如 JSON/msgpack 顶层键），值为修改后的目标值；**类型**须与当前值一致（见上文「严格类型一致」） |

示例（YAML 片段）：

```yaml
patchs:
  - name: 这条补丁的名字
    patch:
      参数名1: 要改成什么值
      参数名2: 要改成什么值

wires:
  - from: a
    to: b
    rule: some_rule
    patchs:
      - 这条补丁的名字
```

### `rules`

| 项 | 说明 |
|---|---|
| 键 | 规则 id，供 `wires` 中 `rule` 引用 |
| 值 | 表达式字符串；求值时带入当前数据包相关变量（如原始字节、可解析出的 JSON 等） |
| 转发条件 | 仅表达式成立时沿该边发出；不成立或求值失败则不发出 |

### `listen`

| 默认 | 说明 |
|---|---|
| `8765` | 配置项保留；当前中央节点**不再**在本字段上接受对端经此端口入站，可忽略。 |
