From 977dec5aaa059a1b7eef18e13b68d3681f2cee3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E7=A7=80?= Date: Sat, 18 Jul 2026 19:26:57 +0800 Subject: [PATCH 1/6] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=E5=9F=BA?= =?UTF-8?q?=E7=A1=80=E5=B7=A5=E5=85=B7=E5=BA=93=E7=A8=B3=E5=AE=9A=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- content/docs/ecosystem/libraries/cli.mdx | 175 +++++++++++---- .../docs/ecosystem/libraries/collections.mdx | 160 +++++++++---- content/docs/ecosystem/libraries/datetime.mdx | 178 ++++++++------- content/docs/ecosystem/libraries/jwt.mdx | 156 +++++++++---- content/docs/ecosystem/libraries/log.mdx | 174 +++++++++------ content/docs/ecosystem/libraries/markdown.mdx | 166 ++++++++++---- content/docs/ecosystem/libraries/retry.mdx | 152 +++++++++---- content/docs/ecosystem/libraries/semver.mdx | 140 ++++++++---- content/docs/ecosystem/libraries/test.mdx | 210 ++++++++++-------- content/docs/ecosystem/libraries/validate.mdx | 172 +++++++++----- 10 files changed, 1121 insertions(+), 562 deletions(-) diff --git a/content/docs/ecosystem/libraries/cli.mdx b/content/docs/ecosystem/libraries/cli.mdx index c8b91e2..d8b233f 100644 --- a/content/docs/ecosystem/libraries/cli.mdx +++ b/content/docs/ecosystem/libraries/cli.mdx @@ -1,78 +1,161 @@ --- title: 言令:命令行解析 -description: 声明选项、参数与子命令,生成中文帮助并获得结构化解析结果。 +description: 言令 1.0 的声明式 argv 解析、结果模式 v1、帮助和上下文补全。 --- -言令(`yanxu-cli`)是纯言序的声明式命令行解析库。它支持长短选项、组合短开关、`--name=value`、`--no-name`、重复选项、位置参数、余项和子命令。 +言令(`yanxu-cli`)`1.0.0` 是纯言序的声明式命令行解析库。它处理已经由宿主分词的 argv,支持长短选项、组合短开关、类型转换、位置参数、子命令、帮助和确定性上下文补全,但不会执行 shell 文本或业务操作。 -## 安装 +## 版本与安装 + +| 项目 | 稳定值 | +| --- | --- | +| 包名 | `言令` | +| 版本 / 公开标签 | `1.0.0` / `v1.0.0` | +| 最低言序 | `1.1.6` | +| 依赖 | 无 | +| 构建目标 | 字节码 | ```sh -yanbao add cli --package 言令 --version "^0.1" +yanbao add 言令 \ + --git https://github.com/yanxulang/yanxu-cli.git \ + --rev v1.0.0 \ + --version '^1.0' +``` + +```toml +[依赖] +言令 = { git = "https://github.com/yanxulang/yanxu-cli.git", 修订 = "v1.0.0", 版 = "^1.0" } ``` -## 声明命令 +提交生成的 `言序.lock`,固定公开标签对应的修订与内容校验。 + +## 核心能力:声明并解析命令 ```yanxu 引「包:言令」为 言令; 定 程序 为 言令.命令(「构建器」,「构建言序项目」); -程序.加开关(「verbose」,「v」,「显示详细输出」) - .加默认选项(「port」,「p」,「端口」,「数」,「监听端口」,3000) - .加多值选项(「define」,「D」,「键值」,「文」,「构建变量」) - .加参数(「入口」,「入口文件」,真) - .加余项(「其他」,「传给目标程序的参数」); -定 结果 为 程序.解析(【「-v」,「--port=8080」,「主.yx」】); -言 结果【「选项」】【「port」】; -言 结果【「参数」】【「入口」】; +程序.加开关(「verbose」,「v」,「输出详细日志」) + .加默认选项(「jobs」,「j」,「数量」,「数」,「并行数量」,4) + .加多值选项(「feature」,「f」,「能力」,「文」,「启用能力」) + .加参数(「入口」,「入口文件」,真) + .加余项(「文件」,「其余文件」); + +定 尝试:典 为 程序.尝试解析( + 【「-v」,「--jobs=8」,「主.yx」,「附加.yx」】 +); + +若 尝试【「成功」】 则 + 定 结果:典 为 尝试【「结果」】; + 言 结果【「选项」】【「jobs」】; + 言 结果【「参数」】【「入口」】; +否则 + 言 尝试【「错误详情」】【「代码」】; + 言 尝试【「帮助文」】; +终 ``` -真实命令行使用 `解析当前()`,它读取宿主参数。库本身不需要额外环境或进程权限。 +真实命令行使用 `解析当前`或`尝试解析当前`。库只返回数据,不打印帮助、不选择退出码,也不调用业务逻辑。 -## 声明接口 +## 声明接口与值类型 -| 方法 | 用途 | +| 入口 | 结果语义 | | --- | --- | -| `加开关(名称, 短名, 说明)` | 布尔开关,同时支持 `--no-name` | -| `加选项(..., 类型, 说明, 必需)` | 单值选项 | -| `加默认选项(..., 默认值)` | 带默认值的单值选项 | -| `加多值选项(...)` | 可重复出现并汇总为列 | -| `加参数(名称, 说明, 必需)` | 有顺序的位置参数 | -| `加余项(名称, 说明)` | 收集剩余位置参数 | -| `加子命令(命令)` | 组合嵌套命令 | +| `加开关` | 逻辑值,默认假,可用 `--no-name`否定 | +| `加选项` | 单值,可标记为必需;未提供且无默认时为空 | +| `加默认选项` | 未提供时返回声明默认值的深复制 | +| `加多值选项` | 重复出现并按顺序汇总为列 | +| `加参数` | 必需或可选的文本位置参数 | +| `加余项` | 收集剩余全部位置参数 | +| `加子命令` | 注册嵌套命令对象 | + +取值类型支持: -值类型支持 `文`、`数`、`理` 和 `JSON`。短名必须唯一,命令定义阶段就会检查冲突。 +- `文`:保留 argv 原文; +- `数`:按 JSON 数字语法解析,拒绝逻辑、空和容器; +- `理`:接受 `true/false`、`1/0`和`真/假`; +- `JSON`:恢复任意合法 JSON 值。 -## 解析结果 +支持的选项形式包括 `--name value`、`--name=value`、`-n value`、`-nvalue`、组合短开关和显式 `--`终止。真实声明的 `--no-color`优先于合成否定语法。 -`解析` 返回典,包含: +长名和值名须为 1–128 字,不能以连字符开头,也不能含空白、控制符或等号。短名为空或单个有效字符;`help`、`帮助`和短名 `h`由内建帮助保留。 -- `命令`:当前命令名; -- `帮助` 与 `帮助文`:是否请求帮助及生成的文字; -- `选项`:完成类型转换和默认值填充的选项典; -- `参数`:位置参数典; -- `子命令` 与 `子结果`:嵌套命令信息。 +## 子命令与结果模式 v1 + +```yanxu +定 根 为 言令.命令(「包」,「管理依赖」) + .加开关(「verbose」,「v」,「详细输出」); -遇到 `--` 后不再解析选项。`--help`、`--帮助` 与 `-h` 都会返回帮助结果。 +定 添加 为 言令.命令(「add」,「添加依赖」) + .加开关(「dev」,「d」,「开发依赖」) + .加参数(「包」,「包名」,真); + +根.加子命令(添加); + +定 结果:典 为 根.解析(【「-v」,「add」,「-d」,「http」】); +言 结果【「子结果」】【「参数」】【「包」】; +``` -## 面向应用的错误分支 +普通解析结果固定包含: -`解析` 失败时抛出 `YANLING_` 前缀错误。命令行入口通常更适合使用 `尝试解析`: +- `模式版本`:当前为 1; +- `命令路径`与`命令`; +- `帮助`与`帮助文`; +- 当前层的`选项`与`参数`; +- `子命令`与下一层`子结果`。 + +`尝试解析`固定返回 `模式版本/成功/结果/错误/错误详情/帮助文`六个键。程序应按`错误详情.代码`分支;完整消息可以改进,不是兼容协议。 + +必需位置参数必须先于可选参数,余项必须最后且只能有一个。同一命令不能同时声明必需根位置参数与子命令;可选位置参数或余项可以与子命令并存,但两者是互斥入口。 + +## 解析配置与补全 + +`按配置解析`、`按配置解析当前`、`尝试按配置解析`和`按配置补全`接受这些逻辑覆盖: + +| 键 | 默认 | 作用 | +| --- | --- | --- | +| `帮助短路` | 真 | `-h`、`--help`、`--帮助`直接返回帮助结果 | +| `允许穿插选项` | 真 | 位置参数之后仍识别选项 | +| `允许开关否定` | 真 | 接受合成 `--no-name` | +| `拒绝重复开关` | 假 | 同一开关第二次出现时报错 | +| `未知选项建议` | 真 | 为未知长选项返回最多三个近似候选 | + +未知配置键或非逻辑值会失败,不会静默忽略。 ```yanxu -法 执行命令(程序) 则 - 定 所解 为 程序.尝试解析(环境.参数()); - 若 所解【「成功」】 则 - 归 执行业务(所解【「结果」】); - 否则 - 言 所解【「错误」】; - 言 所解【「帮助文」】; - 归 2; - 终 -终 +定 补全:典 为 程序.补全(【「--jo」】); +言 补全【「命令路径」】; +言 补全【「上下文」】; +言 补全【「候选」】; ``` -`尝试解析` 返回 `{成功, 结果, 错误}`,失败时额外包含 `帮助文`,便于顶层程序统一选择退出码。业务层应接收解析后的典,不必再次理解命令行语法。 +补全把最后一个元素视为正在输入的前缀;刚输入空格时,外层适配器应追加空文。它只理解 argv 词元,不拆分引号和转义,不读取文件系统,也不执行候选。 + +## 资源、权限与安全边界 + +| 项目 | 硬上限 | +| --- | ---: | +| 单命令选项 / 位置参数 | 256 / 256 | +| 直接子命令 / 子命令深度 | 64 / 32 | +| 命令树节点 | 1024 | +| argv 项数 | 4096 | +| 单 argv / argv 总字符 | 65536 / 1048576 | +| 默认值容器深度 / 节点 | 64 / 1024 | +| 单次补全候选 | 64 | +| 帮助文本 | 1048576 字符 | + +言令无第三方依赖,不申请文件、网络、监听、环境、进程或原生扩展权限。规格快照、默认容器和解析结果都会深复制,调用方修改返回值不会回写命令声明。 + +## 已知限制与安全责任 + +- 言令不是 shell:不展开引号、变量、通配符或命令替换,也不执行参数。 +- 位置参数统一为文字;路径安全、权限、业务类型和授权必须在解析后另行验证。 +- 帮助、错误和补全可能回显名称、说明或参数片段,不能把秘密写入规格说明或默认值。 +- 补全候选仍须由适配器按目标 shell 正确转义,不能直接拼成命令执行。 +- 命令对象包含可变声明状态,构建完成后应按只读方式共享;库不提供并发修改协调。 + +## 项目链接 -仓库:[yanxulang/yanxu-cli](https://github.com/yanxulang/yanxu-cli)。 +- [GitHub 仓库](https://github.com/yanxulang/yanxu-cli) +- [言令 1.0.0 Release](https://github.com/yanxulang/yanxu-cli/releases/tag/v1.0.0) diff --git a/content/docs/ecosystem/libraries/collections.mdx b/content/docs/ecosystem/libraries/collections.mdx index 93198ca..66fb4ac 100644 --- a/content/docs/ecosystem/libraries/collections.mdx +++ b/content/docs/ecosystem/libraries/collections.mdx @@ -1,73 +1,153 @@ --- title: 言容:容器与集合 -description: 使用栈、队列、优先队列、LRU 缓存和不改写输入的集合算法。 +description: 言容 1.0 的有界容器、稳定优先队列、LRU 缓存与受限集合算法。 --- -言容(`yanxu-collections`)为言序补充常用工程容器。实现全部使用言序,不申请权限,也没有传递依赖。 +言容(`yanxu-collections`)是纯言序实现的通用容器与集合算法库。稳定版 `1.0.0` 为容器、批量算法和回调建立了明确的顺序契约、无异常结果与资源预算。 -## 安装 +## 版本与安装 + +| 项目 | 稳定值 | +| --- | --- | +| 包名 | `言容` | +| 版本 / 公开标签 | `1.0.0` / `v1.0.0` | +| 最低言序 | `1.1.6` | +| 依赖 | 无 | +| 构建目标 | 字节码 | ```sh -yanbao add collections --package 言容 --version "^0.1" +yanbao add 言容 \ + --git https://github.com/yanxulang/yanxu-collections.git \ + --rev v1.0.0 \ + --version '^1.0' +``` + +```toml +[依赖] +言容 = { git = "https://github.com/yanxulang/yanxu-collections.git", 修订 = "v1.0.0", 版 = "^1.0" } ``` -## 稳定优先队列 +提交生成的 `言序.lock`,以固定标签解析出的修订与内容校验。 + +## 核心能力:选择容器 -数值越小,优先级越高;相同优先级按加入顺序取出: +| 容器 | 主要入口 | 顺序与用途 | +| --- | --- | --- | +| `栈()` | `压入`、`弹出`、`尝试弹出`、`查看` | 后进先出 | +| `队列()` | `入队`、`出队`、`尝试出队`、`队首` | 先进先出 | +| `双端队列()` | `加首`、`加尾`、`移首`、`移尾` | 两端操作,快照按首到尾 | +| `有序集合()` | `加入`、`删除`、`含有`、`转列` | 去重并保留首次加入顺序 | +| `优先队列()` | `加入`、`取出项`、`转项列` | 数值越小越优先,同级保持加入顺序 | +| `最近最少使用缓存(容量)` | `写入`、`读取`、`查看`、`尝试读取` | 固定容量,读取触碰、查看不触碰 | + +### 稳定优先队列 ```yanxu 引「包:言容」为 言容; 定 待办 为 言容.优先队列(); -待办.加入(「普通任务」,10) - .加入(「紧急任务」,1) - .加入(「同级任务」,10); - -当 非 待办.是否为空() 则 - 言 待办.取出(); -终 +待办.限制为(100); +待办.加入(「常规任务」,10) + .加入(「紧急任务甲」,1) + .加入(「紧急任务乙」,1); + +定 下一项:典 为 待办.取出项(); +言 下一项【「值」】; +言 下一项【「优先级」】; ``` -## LRU 缓存 +输出的第一项是“紧急任务甲”。`取出项`返回值和优先级,`取出`只返回值。 + +### LRU 缓存 ```yanxu 定 缓存 为 言容.最近最少使用缓存(2); 缓存.写入(「甲」,1).写入(「乙」,2); -言 缓存.读取(「甲」); -缓存.写入(「丙」,3); +缓存.读取(「甲」); # 命中并触碰甲 +缓存.写入(「丙」,3); # 淘汰乙 -言 缓存.含有(「乙」); # 假;乙是最久未使用项 -言 缓存.键列(); # 从较新到较旧的当前键 +言 缓存.含有(「乙」); # 假 +言 缓存.键列(); # 从最少使用到最近使用 ``` -容量必须是正整数。读取会更新最近使用顺序;写入超过容量时只淘汰一个最久未使用项。 +旧式 `读取`在缺失时返回空。若缓存允许保存真实空值,应使用 `尝试读取`或不触碰顺序的 `尝试查看`,并读取结果中的`成功`字段。 -## 容器接口 +## 容量和无异常操作 -| 容器 | 主要方法 | 顺序特征 | -| --- | --- | --- | -| `栈()` | `压入`、`弹出`、`查看`、`转列` | 后进先出 | -| `队列()` | `入队`、`出队`、`队首`、`转列` | 先进先出,内部游标可压缩 | -| `双端队列()` | `加首`、`加尾`、`移首`、`移尾` | 两端读写 | -| `有序集合()` | `加入`、`删除`、`含有`、`转列` | 去重并保持首次插入顺序 | -| `优先队列()` | `加入(值, 优先级)`、`取出`、`查看` | 稳定优先级 | -| `最近最少使用缓存(容量)` | `写入`、`读取`、`删除`、`键列` | 固定容量 LRU | +栈、队列、双端队列、有序集合和优先队列默认最多保存 100000 项,可以按实例收紧: + +```yanxu +定 事件队 为 言容.队列(); +事件队.限制为(256); + +定 结果:典 为 事件队.尝试出队(); +若 结果【「成功」】 则 + 言 结果【「值」】; +否则 + 言 结果【「错误」】【「代码」】; +终 +``` + +上限必须为正整数,不能超过库硬上限,也不能低于当前项目数。超限加入会在修改容器前以 `YANRONG_LIMIT`失败。 -所有容器都提供 `数量` 和 `是否为空`;适用的容器还提供 `清空`。从空容器读取或删除不存在的必需项会给出明确错误,而不会返回一个容易混淆的业务空值。 +## 集合与列算法 -## 集合算法 +```yanxu +法 数值先于(甲:数,乙:数):理 则 + 归 (甲 小于 乙); +终 + +定 来源:列<数> 为 【5,2,3,2,1】; + +言 言容.去重(来源); +言 言容.并集(来源,【8,2】); +言 言容.对称差(【1,2】,【2,3】); +言 言容.窗口(来源,3); +言 言容.频次(来源); +言 言容.稳定排序(来源,数值先于); +``` + +还提供 `交集`、`差集`、`分块`、`压平一级`、`拉链`、`拉链全部`、`划分`、`分组`和`旋转`。所有算法返回新的外层列,不改写输入;分组和频次按键或值首次出现顺序输出。 + +每个批量算法都有同名的“受限”入口,最后一个参数为限制典: ```yanxu -言 言容.去重(【1,1,2,3】); -言 言容.并集(【1,2】,【2,3】); -言 言容.交集(【1,2】,【2,3】); -言 言容.差集(【1,2,3】,【2】); -言 言容.分块(【1,2,3,4,5】,2); -言 言容.窗口(【1,2,3,4】,3); -言 言容.压平一级(【【1,2】,【3】】); -言 言容.拉链(【「甲」,「乙」】,【1,2】); +定 限制:典 为 { + 「最大输入项」:200, + 「最大输出项」:1000, + 「最大比较次数」:4000 +}; + +言 言容.窗口受限(来源,3,限制); ``` -这些算法都返回新列,不改写调用方传入的列。`窗口` 只生成完整窗口;`拉链` 以较短输入为界。 +默认预算为 1024 个输入项、100000 个输出项目和 8192 次通用相等比较。窗口、分块、拉链和分组生成的子列或记录也计入输出预算。 + +## 错误、权限与安全边界 + +稳定错误前缀为 `YANRONG_`。常见代码包括: + +| 代码 | 含义 | +| --- | --- | +| `YANRONG_EMPTY` | 从空容器读取或移出 | +| `YANRONG_NOT_FOUND` | 缓存键不存在 | +| `YANRONG_LIMIT` | 容量、输入、输出或比较预算耗尽 | +| `YANRONG_LIMIT_CONFIG` | 限制配置不合法 | +| `YANRONG_CAPACITY` | LRU 容量不合法 | +| `YANRONG_PRIORITY` | 优先级超出安全数值范围 | +| `YANRONG_CALLBACK` / `YANRONG_ORDER` | 回调返回类型或排序关系不合法 | + +捕获错误后用 `错误详情`取代码,不要比较中文消息。言容无第三方依赖,不申请文件、网络、监听、环境、进程或原生扩展权限。 + +## 已知限制 + +- 通用相等使用言序值相等语义,不依赖哈希;有序集合、LRU、去重、分组和频次在大量互异值下最坏为二次时间。 +- 快照和算法结果只复制外层列,复合项目仍是浅引用;需要隔离可变敏感对象时由调用方先复制或冻结。 +- 通用比较的可配置硬上限为 16384 次;大型可信数据也应优先分批,而不是盲目提高预算。 +- 排序比较器必须返回逻辑值、不可令值先于自身,也不可同时令甲先于乙且乙先于甲;库不能证明任意回调的传递性或纯度。 +- 单实例容器是内存对象,不提供持久化、跨进程同步或并发协调。 + +## 项目链接 -仓库:[yanxulang/yanxu-collections](https://github.com/yanxulang/yanxu-collections)。 +- [GitHub 仓库](https://github.com/yanxulang/yanxu-collections) +- [言容 1.0.0 Release](https://github.com/yanxulang/yanxu-collections/releases/tag/v1.0.0) diff --git a/content/docs/ecosystem/libraries/datetime.mdx b/content/docs/ecosystem/libraries/datetime.mdx index 86e46d8..fed13b3 100644 --- a/content/docs/ecosystem/libraries/datetime.mdx +++ b/content/docs/ecosystem/libraries/datetime.mdx @@ -1,140 +1,150 @@ --- title: 言时:日期与时间 -description: 使用不可变日期时间、固定偏移时区、时间段和可注入时钟编写可重复业务逻辑。 +description: 言时 1.0 的不可变日期时间、固定偏移、固定时长、严格解析与可注入时钟。 --- -言时(`yanxu-datetime`)统一公历日期、日内时间、毫秒精度日期时间、固定偏移时区、时间段和时钟。值对象的运算返回新值,适合跨时区日程、账期、截止时间和确定性测试。 +言时(`yanxu-datetime`)以不可变值对象表示公历日期、日内时间、固定偏移时区、毫秒精度日期时间和固定时长。稳定版 `1.0.0` 还提供结构化无异常解析、稳定排序、定时器和可注入时钟,适合编写可重复测试的业务时间逻辑。 -## 安装 +## 版本与安装 + +| 项目 | 稳定值 | +| --- | --- | +| 包名 | `言时` | +| 版本 / 公开标签 | `1.0.0` / `v1.0.0` | +| 最低言序 | `1.1.6` | +| 依赖 | 无 | +| 构建目标 | 字节码 | ```sh -yanbao add datetime --package 言时 --version "^0.1" +yanbao add 言时 \ + --git https://github.com/yanxulang/yanxu-datetime.git \ + --rev v1.0.0 \ + --version '^1.0' +``` + +```toml +[依赖] +言时 = { git = "https://github.com/yanxulang/yanxu-datetime.git", 修订 = "v1.0.0", 版 = "^1.0" } ``` -言时没有传递依赖和额外权限。 +提交生成的 `言序.lock`,不要把临时分支或移动修订用作发布依赖。 -## 解析与输出 +## 核心能力:值对象 + +| 需求 | 类型或构造 | 关键语义 | +| --- | --- | --- | +| 公历某日 | `日期(年, 月, 日)` | 年份 1–9999,不含时区或日内时间 | +| 某日内的时间 | `时间(时, 分, 秒, 毫秒)` | `00:00:00.000`–`23:59:59.999` | +| UTC 或固定偏移 | `UTC()`、`东八区()`、`固定时区()` | 只表示固定偏移,不含夏令时规则 | +| 确定时刻 | `日期时间(日期, 时间, 时区)` | 可比较、换区和转换 Unix 时间戳 | +| 固定时长 | `毫秒段`、`秒段`、`分段`、`时段`、`日段` | 固定毫秒数,不包含月或年 | +| 时间来源 | `系统时钟`、`虚拟时钟`、`时钟协议` | 生产与测试使用同一业务接口 | + +## 解析、换区与输出 ```yanxu 引「包:言时」为 言时; -定 上海时间 为 言时.解析RFC3339(「2026-07-15T09:30:00+08:00」); -定 UTC时间 为 上海时间.转UTC(); +定 上海 为 言时.解析RFC3339(「2026-07-16T09:30:00+08:00」); +定 UTC时刻 为 上海.转UTC(); -言 上海时间.转RFC3339(); -言 UTC时间.转ISO(); -言 上海时间.Unix秒(); +言 上海.转RFC3339(); +言 UTC时刻.转RFC3339(); +言 上海.是同一时刻(UTC时刻); +言 上海.Unix毫秒(); ``` -`解析RFC3339` 要求明确时区。`解析ISO` 接受库支持的 ISO 8601 日期时间形式;只解析独立值时可使用 `解析日期`、`解析时间` 和 `解析时区`。 +`解析RFC3339`要求 `T`或`t`分隔符和明确偏移。`解析ISO`还允许空格分隔,但同样必须带 `Z`或 `±HH:mm`。转换时区保持 Unix 毫秒不变,只改变本地日期、时间和偏移显示。 -## 值对象 - -| 类型 | 构造 | 主要能力 | -| --- | --- | --- | -| `日期` | `日期(年, 月, 日)` | 加日/月/年、月初末、周初末、季度、比较、相差日 | -| `时间` | `时间(时, 分, 秒, 毫秒)` | 日内毫秒、加时分秒、比较 | -| `时区` | `UTC()`、`东八区()`、`固定时区()` | 固定偏移和 RFC 3339 偏移输出 | -| `日期时间` | `日期时间(日期, 时间, 时区)` | 时间戳、换区、历法/时间段运算、格式化 | -| `时间段` | `毫秒段`、`秒段`、`分段`、`时段`、`日段` | 相加、相减、取反、单位换算、ISO 输出 | +不可信文字应使用无异常入口: ```yanxu -定 所日 为 言时.日期(2024,2,29); -言 所日.加年(1).转ISO(); -言 所日.月末().星期(); -言 所日.季初().转ISO(); +定 结果:典 为 言时.尝试解析日期(「2026-02-30」); -定 持续 为 言时.时段(2).相加(言时.分段(30)); -言 持续.总分(); -言 持续.转ISO(); +若 结果【「成功」】 则 + 言 结果【「值」】; +否则 + 言 结果【「错误」】【「代码」】; +终 ``` -加月和加年会在目标月份没有原日号时收敛到月末,例如闰日加一年得到下一年二月末。`周初` 以星期一为一周开始。 +日期、时间、时区、ISO、RFC 3339、自定义格式和时间段均有对应的 `尝试解析…`入口。 -## 时区与时间戳 +## 历法运算与固定时长 ```yanxu -定 北京 为 言时.固定时区(「北京时间」,480); -定 发生于 为 言时.自Unix毫秒(0,北京); -言 发生于.转RFC3339(); -言 发生于.在时区(言时.UTC()).转RFC3339(); -``` +定 月末 为 言时.日期(2024,1,31).加月(1); +言 月末.转ISO(); # 2024-02-29 + +定 持续 为 言时.时段(2).相加(言时.分段(30)); +言 持续.转ISO(); -转换时区保持同一个时间点,只改变本地日期、时间和偏移表示。Unix 时间戳始终表示 UTC 时间线上的秒或毫秒。 +定 往返 为 言时.解析时间段(「P1DT2H30M」); +言 往返.转ISO(); +``` -言序标准库目前不提供 IANA 时区数据库,因此言时只建模 UTC 与固定偏移,不猜测夏令时规则。需要 `Asia/Shanghai`、`America/New_York` 等地域规则时,应在应用边界通过受维护的时区数据确定当时偏移,再传给 `固定时区`。 +`加日`、`加月`和`加年`是历法运算;目标月份没有原日号时会收敛到月末。日期时间的`加时间段`是固定毫秒运算。时间段语法为 `[-]P[nD][T[nH][nM][n[.fff]S]]`,明确不接受依赖上下文的年、月和周单位。 -## 格式化与严格解析 +### 自定义格式 ```yanxu -定 正文 为 上海时间.格式化(「YYYY年MM月DD日 HH:mm:ss.SSS Z」); -定 还原 为 言时.解析格式( - 正文, - 「YYYY年MM月DD日 HH:mm:ss.SSS Z」, - 言时.UTC() -); +定 格式:文 为 「YYYY/MM/DD HH:mm:ss.SSS Z」; +定 文本:文 为 上海.格式化(格式); +定 往返 为 言时.解析格式(文本,格式,言时.UTC()); ``` -支持令牌 `YYYY`、`MM`、`DD`、`HH`、`mm`、`ss`、`SSS` 与 `Z`。严格解析要求完整年月日时分秒;格式不含 `Z` 时使用调用者传入的默认时区。 +支持 `YYYY`、`MM`、`DD`、`HH`、`mm`、`ss`、`SSS`和`Z`。解析必须包含完整年月日时分秒;`SSS`和`Z`可选,缺少`Z`时使用调用者给出的默认时区。同一令牌不得重复。 -## 比较、排序与相对时间 +## 排序与资源上限 ```yanxu -言 上海时间.比较(UTC时间); # 0,同一个时间点 -言 言时.排序日期时间(【上海时间,上海时间.加日(-1)】,真); -言 言时.相对时间(上海时间.加分(5),上海时间); # 5分钟后 +定 已排:列 为 言时.排序日期时间(来源,真); +定 小批:列 为 言时.排序日期时间受限(来源,真,200); ``` -日期时间比较基于 Unix 毫秒,而不是显示时区。排序返回新列,不改写输入。 +排序按 Unix 毫秒比较,对同一时刻保持输入顺序,并返回新列。默认最多 4096 项;受限入口只能收紧,不能超过库硬上限。 -## 注入时钟 +## 可测试时钟与超时 ```yanxu 定 时钟 为 言时.虚拟时钟(0); -言时.等待(言时.秒段(5),时钟); -言 时钟.毫秒(); # 5000 +定 定时 为 言时.一次定时器(言时.秒段(5),时钟); + +定时.等待(); +言 时钟.毫秒(); # 5000 -法 推进时钟():文 则 - 时钟.推进(250); +法 作业(截止):文 则 + 截止.检查(); + # 长循环可在安全点重复检查 归 「完成」; 终 -定 结果 为 言时.计时(推进时钟,时钟); +言 言时.限时(言时.秒段(2),作业,时钟); ``` -`系统时钟` 读取宿主时间并真实等待;`虚拟时钟` 的等待只推进内部毫秒。业务服务应接收 `时钟协议`,生产注入系统时钟,测试注入虚拟时钟。 +重复定时器会按原计划推进节拍,并以常数步跨过漏失周期,不会把所有遗漏回调集中触发。`限时`是协作式检查:它不会创建线程,也不能强制中断阻塞中的同步 I/O;需要硬取消时必须结合驱动自身的取消接口。 -## 一次与重复定时器 +## 权限与安全边界 -```yanxu -定 一次 为 言时.一次定时器(言时.秒段(5),时钟); -言 一次.剩余().总秒(); -一次.等待(); -言 一次.是否活跃(); # 假 - -定 心跳 为 言时.重复定时器(言时.秒段(30),时钟); -若 心跳.是否到期() 则 - 心跳.触发(); - 发送心跳(); -终 -心跳.重置(); -心跳.取消(); -``` +言时是纯言序、零第三方依赖库。清单拒绝文件、网络、TCP、UDP、环境、进程和原生扩展权限;`系统时钟`只使用言序标准时间 API 读取时间和阻塞等待。 -定时器提供 `剩余`、`是否到期`、`触发`、`等待`、`重置`、`取消` 和 `是否活跃`。重复定时器根据原计划推进下次时间;如果调用方错过多个周期,会跨过旧周期并保持节拍,不把多次遗漏集中触发。 +主要边界如下: -## 协作式超时 +- 年份为 1–9999,固定时区偏移为 `-14:00`–`+14:00`; +- 时间戳、时钟和时间段使用绝对值不大于 `9007199254740991`的安全整数毫秒; +- 所有公开解析器与格式器的单个文本上限为 4096 字符; +- RFC 3339 小数秒只接受 1–3 位,并归一化到毫秒; +- 错误使用稳定 `YANSHI_`代码;完整诊断中的位置和踪迹应先过滤再返回客户端。 -```yanxu -法 可取消工作(截止):文 则 - 截止.检查(); - # 分段执行可取消工作 - 归 「完成」; -终 +## 已知限制 -定 值 为 言时.限时(言时.秒段(2),可取消工作,时钟); -``` +- 只建模 UTC 与固定偏移,不内置 IANA 时区数据库,也不推测夏令时或历史政策。`东八区()`不等同于完整的 `Asia/Shanghai`规则。 +- 不表示闰秒,最高精度为毫秒。 +- 固定时长不支持年、月、周;这些单位必须在明确的日期上下文中用历法操作处理。 +- 虚拟时钟不是安全时间源;鉴权、凭据过期和账务截止应比较可信时钟下的 Unix 时刻。 +- 计时、定时器和断点仍是同步进程内能力,不提供跨进程调度。 -`截止` 提供 `剩余`、`已超时` 和 `检查`。限时会在调用前后检查,但不会强制中断不可取消的同步代码;长操作应在安全边界主动检查截止。 +## 项目链接 -错误使用 `YANSHI_` 前缀。仓库:[yanxulang/yanxu-datetime](https://github.com/yanxulang/yanxu-datetime)。 +- [GitHub 仓库](https://github.com/yanxulang/yanxu-datetime) +- [言时 1.0.0 Release](https://github.com/yanxulang/yanxu-datetime/releases/tag/v1.0.0) diff --git a/content/docs/ecosystem/libraries/jwt.mdx b/content/docs/ecosystem/libraries/jwt.mdx index 705fd46..3539f62 100644 --- a/content/docs/ecosystem/libraries/jwt.mdx +++ b/content/docs/ecosystem/libraries/jwt.mdx @@ -1,87 +1,147 @@ --- title: 言签:JWT -description: 使用固定 HS256、安全默认值和可重复时间配置签发与验证 JWT。 +description: 言签 1.0 的固定 HS256、二进制密钥、标准声明策略和本地 kid 轮换。 --- -言签(`yanxu-jwt`)专注于可审计的 HS256 JSON Web Token。它实现严格的无填充 URL Base64,使用标准库 HMAC-SHA-256 和恒时签名比较,并拒绝 `none` 与算法混淆。 +言签(`yanxu-jwt`)`1.0.0` 是纯言序的 JSON Web Token 库。它固定使用 HS256,不根据不可信头部选择算法;签名基于标准库 HMAC-SHA-256 与恒时比较,并支持文字或二进制密钥、本地 `kid`密钥集轮换和严格标准声明策略。 -## 安装 +## 版本与安装 + +| 项目 | 稳定值 | +| --- | --- | +| 包名 | `言签` | +| 版本 / 公开标签 | `1.0.0` / `v1.0.0` | +| 最低言序 | `1.1.6` | +| 依赖 | 无 | +| 构建目标 | 字节码 | ```sh -yanbao add jwt --package 言签 --version "^0.1" +yanbao add 言签 \ + --git https://github.com/yanxulang/yanxu-jwt.git \ + --rev v1.0.0 \ + --version '^1.0' +``` + +```toml +[依赖] +言签 = { git = "https://github.com/yanxulang/yanxu-jwt.git", 修订 = "v1.0.0", 版 = "^1.0" } ``` -言签没有传递依赖和额外权限。 +提交生成的 `言序.lock`。生产密钥不应出现在清单、锁文件、源码或日志中。 -## 签发标准令牌 +## 核心能力:签发带标识的标准令牌 ```yanxu 引「包:言签」为 言签; -定 密钥 为 「0123456789abcdef0123456789abcdef」; +# 仅为文档示例;生产密钥必须由密码学安全随机源生成。 +定 当前密钥:文 为 「abcdef0123456789abcdef0123456789」; -定 令牌 为 言签.签发标准( +定 令牌:文 为 言签.签发标准带标识( {「role」:「editor」}, - 密钥, + 当前密钥, + 「2026-07」, { 「当前秒」:1700000000, 「有效秒」:900, - 「iss」:「内容服务」, - 「aud」:「管理后台」, - 「sub」:「user-42」 + 「iss」:「auth.example」, + 「aud」:【「web」,「mobile」】, + 「sub」:「user-42」, + 「jti」:「session-42」 } ); ``` -`签发标准` 根据配置生成 `iat`、`nbf` 和 `exp`,并合并可选的 `iss`、`aud`、`sub` 与 `jti`。密钥至少需要 32 字节。 +`签发标准…`自动构造 `iat`、`nbf`和`exp`,并合并可选的 `iss`、`aud`、`sub`与`jti`。第一个附加声明参数不得覆盖这些标准声明,避免业务载荷绕过签发策略。 + +若密钥来自 KMS、二进制配置或安全随机源,应保留为`字节串`并使用带`字节密钥`后缀的签发与验证入口;只有明确是 UTF-8 文本的密钥才使用文字密钥入口。 -## 可重复验证 +## 验证与安全轮换 ```yanxu -定 声明 为 言签.验证配置( - 令牌, - 密钥, - { - 「当前秒」:1700000100, - 「时差秒」:30, - 「签发者」:「内容服务」, - 「受众」:「管理后台」, - 「需要过期」:真, - 「最大寿命秒」:3600 - } -); +定 密钥集:典 为 { + 「2026-01」:旧密钥, + 「2026-07」:当前密钥 +}; + +定 声明:典 为 言签.验证密钥集(令牌,密钥集,{ + 「当前秒」:1700000100, + 「时差秒」:30, + 「签发者」:「auth.example」, + 「受众」:「web」, + 「主题」:「user-42」, + 「密钥标识」:「2026-07」, + 「需要过期」:真, + 「需要签发时间」:真, + 「需要生效时间」:真, + 「需要编号」:真, + 「最大寿命秒」:900, + 「必需声明」:【「role」】 +}); 言 声明【「role」】; ``` -生产代码可调用 `验证` 使用系统当前时间;测试和回放场景应调用 `验证配置` 并传入固定的 `当前秒`。 +密钥集必须来自受信任本地配置,并包含 1–64 项。未知或缺失 `kid`立即失败,不会回退到任意密钥。安全轮换顺序是: + +1. 先把新密钥加入所有验证方; +2. 再让签发方改用新的 `kid`; +3. 等待旧令牌最大寿命加允许时差; +4. 最后从验证方移除旧密钥。 + +验证成功才返回声明。测试和回放应显式传`当前秒`;生产可使用读取系统时间的便捷验证入口,但仍应配置固定签发者、受众、主题、必需时间声明和最大寿命。 + +## 公共接口 + +| 能力 | 入口 | +| --- | --- | +| URL Base64 | `编码字节`、`解码字节`、`编码文字`、`解码文字` | +| 直接签发 | `签发`、`签发字节密钥`、`签发带标识`及字节密钥版本 | +| 标准声明签发 | `签发标准`、`签发标准带标识`及字节密钥版本 | +| 解析 | `解析未验证` | +| 单密钥验证 | `验证`、`验证配置`及字节密钥版本 | +| 轮换验证 | `验证密钥集` | +| 错误 | `错误详情` | + +URL Base64 使用无填充规范形式;解码会拒绝 `=`填充、非法字符、错误长度和非规范尾位。紧凑令牌固定为三段,头部和载荷必须是 JSON 对象。 + +## 错误、权限与安全边界 + +```yanxu +试 则 + 定 声明 为 言签.验证密钥集(令牌,密钥集,策略); +救 所误 则 + 定 详情:典 为 言签.错误详情(所误); + 言 详情【「代码」】; +终 +``` -## 配置字段 +错误使用稳定 `YANQIAN_*`代码。服务可以把代码统一映射为 401 或 403,并只在脱敏的服务端日志中保留必要诊断;不要回显详细解析差异、完整令牌或密钥。 -| 场景 | 字段 | 说明 | -| --- | --- | --- | -| 签发 | `当前秒`、`有效秒` | 必需;确定签发时间与有效期 | -| 签发 | `iss`、`aud`、`sub`、`jti` | 可选标准声明 | -| 验证 | `当前秒` | 可选;省略时由便捷入口使用系统时间 | -| 验证 | `时差秒` | 允许有限的时钟偏差 | -| 验证 | `签发者`、`受众` | 校验 `iss` 与 `aud` | -| 验证 | `需要过期` | 要求令牌必须包含 `exp` | -| 验证 | `最大寿命秒` | 限制从签发到过期的最长时间 | +| 边界 | 1.0 值 | +| --- | ---: | +| 令牌最大长度 | 32768 字符 | +| HS256 密钥 | 32–4096 字节 | +| `kid` | 1–128 个受限 ASCII 字符 | +| 允许时差 | 0–300 秒 | +| NumericDate | 非负安全整数,最大 `9007199254740991` | +| 必需声明 | 最多 32 项 | -## 其他接口 +言签无第三方依赖,不申请文件、网络、监听、环境、进程或原生扩展权限。Python 互操作测试只用于发布验证,不是运行时依赖。 -- `编码字节` / `解码字节`:严格 URL Base64 字节接口; -- `编码文字` / `解码文字`:UTF-8 便捷接口; -- `签发`:直接签发调用方提供的声明; -- `解析未验证`:只解码头部与载荷; -- `验证` / `验证配置`:验签并校验标准声明。 +## `解析未验证`与已知限制 -`解析未验证` 只适合诊断或显示信息。它没有证明令牌来源,返回值绝不能参与授权、权限判断或数据库过滤。 +`解析未验证`只检查紧凑格式、URL Base64、UTF-8 与 JSON 对象,返回的头部、声明和分段仍完全受攻击者控制。它只适合诊断或互操作测试,绝不能参与认证、授权、数据库过滤或远程密钥选择。 -## 安全默认值 +此外: -言签固定算法为 `HS256`,拒绝关键头部 `crit`,限制令牌最大长度为 32,768 字符,并严格检查 `exp`、`nbf` 和 `iat`。错误使用 `YANQIAN_` 前缀。 +- 1.0 只支持 HS256,不提供 RSA、ECDSA、EdDSA、`none`或算法协商。 +- 不读取远程 JWK,也拒绝依赖 `jku`、`jwk`、`x5u`或`x5c`的密钥选择。 +- 密钥存储、分发、撤销、会话状态和重放防护由应用负责。 +- `aud`可为文字或非空文字列;其他身份声明要求非空文字,时间声明拒绝小数、负数和溢出。 +- 生产中应使用短寿命令牌,并把验证后的声明与服务端授权和撤销状态结合。 -密钥应来自专用密钥管理边界,不要写入源码、日志字段或错误消息。需要非对称算法、密钥轮换头或 JWK 时,应选择专门驱动,不要把 HS256 密钥当作公钥体系使用。 +## 项目链接 -仓库:[yanxulang/yanxu-jwt](https://github.com/yanxulang/yanxu-jwt)。 +- [GitHub 仓库](https://github.com/yanxulang/yanxu-jwt) +- [言签 1.0.0 Release](https://github.com/yanxulang/yanxu-jwt/releases/tag/v1.0.0) diff --git a/content/docs/ecosystem/libraries/log.mdx b/content/docs/ecosystem/libraries/log.mdx index 73a6a18..dcca7df 100644 --- a/content/docs/ecosystem/libraries/log.mdx +++ b/content/docs/ecosystem/libraries/log.mdx @@ -1,32 +1,35 @@ --- title: 言录:结构化日志 -description: 输出六级结构化日志,组合上下文、脱敏、错误踪迹、格式化器与处理器。 +description: 言录 1.0 的六级结构化日志、脱敏、采样、轮转处理器与集成字段边界。 --- -言录(`yanxu-log`)把日志记录、格式化和输出处理分开。应用可以同时写可读控制台、JSON Lines、言据流或自定义目标,并在输出前递归脱敏结构化字段。 +言录(`yanxu-log`)`1.0.0` 把日志记录、格式化和输出处理分开,提供稳定记录模式 v1、六级日志、上下文、递归脱敏、采样、容错处理器和请求/数据库字段构造器。只有文件处理器会使用包清单声明的文件能力。 -## 安装 +## 版本与安装 + +| 项目 | 稳定值 | +| --- | --- | +| 包名 | `言录` | +| 版本 / 公开标签 | `1.0.0` / `v1.0.0` | +| 最低言序 | `1.1.6` | +| 稳定依赖 | 言据 `1.2.0`,标签 `v1.2.0`,范围 `^1.2` | +| 构建目标 | 字节码 | ```sh -yanbao add log --package 言录 --version "^0.1" +yanbao add 言录 \ + --git https://github.com/yanxulang/yanxu-log.git \ + --rev v1.0.0 \ + --version '^1.0' ``` -言包会自动锁定言据依赖。只写控制台时不需要文件权限;写文件时由顶层应用授权目标路径。 - -## 等级 - -| 常量 | 数值 | 典型用途 | -| --- | ---: | --- | -| `追踪级` | 10 | 高频执行细节 | -| `调试级` | 20 | 开发诊断 | -| `信息级` | 30 | 正常业务事件 | -| `警告级` | 40 | 可恢复异常或退化 | -| `错误级` | 50 | 当前操作失败 | -| `致命级` | 60 | 进程或关键子系统无法继续 | +```toml +[依赖] +言录 = { git = "https://github.com/yanxulang/yanxu-log.git", 修订 = "v1.0.0", 版 = "^1.0" } +``` -记录器与每个处理器都有最低等级。记录先通过记录器阈值,再由各处理器决定是否输出。 +言录自身的清单把言据固定为公开 `v1.2.0` 标签。应用应提交 `言序.lock`,继续固定完整依赖图、修订和内容校验。 -## 控制台与文件 +## 核心能力:创建日志器 ```yanxu 引「包:言录」为 言录; @@ -46,81 +49,120 @@ yanbao add log --package 言录 --version "^0.1" {「环境」:「生产」} ); -日志.信息于(「订单已创建」,{ - 「请求号」:「req-42」, - 「订单号」:「o-100」 -}); +日志.子日志器(「创建订单」,{「请求号」:「r-42」}) + .信息于(「创建成功」,{ + 「订单号」:「o-1」, + 「Authorization」:「Bearer secret」 + }); ``` -`JSON文件处理器` 每条记录写一行 JSON;`言据文件处理器` 写言据流风格。下一行会越过阈值时先轮转,完整记录不会被拆开。备份名为 `路径.1` 到 `路径.N`。 +每条记录包含`模式版本`、时间戳、等级、等级值、消息、模块、频道、字段和可选错误。上下文与输入会深复制;处理器和采样器各自收到隔离副本,不能通过修改参数污染最终记录。 -## 上下文、模块和频道 +## 等级、上下文与错误 + +| 常量 | 值 | 典型用途 | +| --- | ---: | --- | +| `追踪级` | 10 | 高频执行细节 | +| `调试级` | 20 | 开发诊断 | +| `信息级` | 30 | 正常业务事件 | +| `警告级` | 40 | 可恢复异常或退化 | +| `错误级` | 50 | 当前操作失败 | +| `致命级` | 60 | 关键子系统无法继续 | + +每一级都有纯消息入口和带字段入口,例如 `信息`与`信息于`。低于日志器最低等级的调用直接返回空,不创建记录,也不推进采样器。 ```yanxu 定 请求日志 为 日志 - .子日志器(「创建订单」,{「请求号」:「req-42」}) + .子日志器(「创建账户」,{「请求号」:「r-42」}) .频道(「业务」) - .上下文({「租户」:「north」}); + .上下文({「追踪号」:「trace-7」}); -请求日志.调试(「开始校验」); -请求日志.信息于(「创建完成」,{「耗时毫秒」:18}); +试 则 + 执行业务(); +救 所误 则 + 请求日志.记错误(「执行失败」,所误,{「订单号」:「o-1」}); +终 ``` -子日志器继承处理器、等级和脱敏配置;模块名以点连接。上下文按创建顺序合并,本次调用字段拥有最后覆盖机会。 +`记错误`保留言序错误的代码、类别、消息、位置和踪迹;不要只记录消息而丢失诊断,也不要把未过滤的完整错误详情直接返回给客户端。 -## 记录结构 +## 脱敏与采样 -每条记录包含: +默认敏感键覆盖常见中英文密码、令牌、认证首部、Cookie、API Key、客户端密钥、DSN 和连接串,按大小写不敏感匹配并递归处理列与典: -- `时间戳`:Unix 毫秒; -- `等级` 与 `等级值`; -- `消息`; -- `模块` 与 `频道`; -- `字段`:合并并脱敏后的结构化字段; -- `错误`:使用 `记错误` 时附加的代码、类别、位置和踪迹。 +```yanxu +日志.设脱敏( + 【「密码」,「访问令牌」,「Authorization」】, + 「[已隐藏]」 +); -低于最低等级的调用返回空;已输出的调用返回同一记录典,便于测试和二次处理。 +日志.固定采样(10,言录.警告级); +``` -## 错误踪迹 +固定间隔采样器保留每个周期的第一条低等级记录,并始终保留达到保护等级的记录。也可用 `固定间隔采样器`和`设采样器`显式管理计数,或提供实现 `保留(记录):理`的自定义采样器。 -```yanxu -试 则 - 执行订单(); -救 所误 则 - 日志.记错误(「订单执行失败」,所误,{「订单号」:「o-100」}); -终 -``` +脱敏只按结构化键工作,不扫描消息、错误消息或 SQL 模板。凭据必须放在可脱敏字段中,不能先拼入自由文本。 + +## 处理器 + +| 处理器 | 用途与边界 | +| --- | --- | +| `控制台处理器` | 输出一行可读文本 | +| `内存处理器` | 保存文本与记录副本,最多 4096 条,适合测试 | +| `函数处理器` | 把格式化法和写出法接到应用拥有的目标 | +| 文件处理器 | 控制台、JSON Lines 或言据格式的文件输出与轮转 | +| `容错处理器` | 隔离可选输出端故障,并公开失败数与最近错误 | -不要只记录 `所误.消息`,否则会丢失错误代码、类别、位置与完整调用踪迹。 +文件轮转阈值按 UTF-8 字节计算。下一条完整记录会越过阈值时,当前文件备份为 `.1`,旧备份依次推进到 `.N`;单条记录不会拆开,但仍受 1 MiB 单条预算约束。`轮转字节=0`关闭轮转,`保留份数=0`表示轮转时只清空当前文件。 -## 字段脱敏 +默认情况下处理器错误向调用方传播。只应把`容错处理器`用于可以丢失的遥测输出;主审计日志故障不应被静默掩盖。 -默认敏感键包含 `密码`、`口令`、`令牌`、`token`、`authorization` 和 `密钥`,匹配后递归替换为 `***`。可按应用词汇调整: +## 请求与数据库日志 ```yanxu -日志.设脱敏(【「密码」,「会话」,「secret」】,「[已遮蔽]」); -``` +日志.记请求( + 「请求完成」,「POST」,「/orders?token=secret」,201,18, + {「Authorization」:「Bearer secret」}, + {「请求号」:「req-1」} +); -脱敏针对结构化字段。消息正文是不可解析文字,因此敏感数据应放在字段中,不要拼接进消息。 +日志.记数据库( + 「查询完成」,「PostgreSQL」,「查询」, + 「SELECT id FROM orders WHERE id = $1」,7,1, + {「id」:42},{「事务号」:「tx-1」} +); +``` -## 自定义格式与处理器 +`请求字段`会移除查询和片段;绝对 HTTP 地址还会移除整个权限部分。`数据库字段`把 SQL 模板和参数分开,并递归脱敏参数与扩展字段。调用方仍应传路由模板和参数化 SQL,不记录正文、完整 Cookie、连接地址或拼接后的 SQL。 -```yanxu -法 格式化(记录:典):文 则 - 归 (记录【「等级」】 加 「:」 加 记录【「消息」】); -终 +## 权限与安全边界 -法 发送(正文:文,记录:典):空 则 - # 交给应用拥有的队列或传输层 - 言 正文; - 归 空; -终 +最终清单声明: -日志.加处理器(言录.函数处理器(言录.警告级,格式化,发送)); +```toml +[权限] +文件 = ["."] +网络 = [] +TCP监听 = [] +UDP绑定 = [] +环境 = [] +进程 = false +原生扩展 = false ``` -内建格式化器为 `控制台格式`、`JSON行格式` 和 `言据格式`。`内存处理器` 提供 `各文本`、`各记录` 与 `清空`,适合测试日志行为。 +文件权限服务于显式启用的文件处理器;只用内存、控制台或函数处理器时,运行路径不会访问文件。顶层应用仍应只授权专用日志目录,不能让不可信输入决定路径。言录不创建父目录,也不管理目录权限。 + +消息、名称、字段容器、嵌套深度、处理器数、内存记录、单条字节数、路径和轮转配置都有硬上限;超限使用稳定 `YANLU_`错误。捕获后调用 `错误详情`并按`代码`分支,不要解析中文消息。 + +## 已知限制 + +- 默认脱敏不是数据防泄漏扫描器,无法清除已经进入消息、错误文字或 SQL 模板的秘密。 +- 内置轮转不是原子事务,也没有跨进程锁;一个路径只应由一个进程写入,不适合作为不可丢失或追加证明的审计存储。 +- 文件路径、符号链接、挂载点、只读权限和文件占用遵循宿主操作系统行为。 +- 控制台展示文字、错误踪迹布局和典的调试顺序不是跨版本机器协议;机器消费应使用记录模式 v1、JSON Lines 或言据结构。 +- 自定义格式化器、写出器、采样器和时间源是受信回调,其资源消耗及顶层权限由应用负责。 -便捷入口 `新建(最低等级)` 创建单控制台日志器,`静默(最低等级)` 创建无处理器日志器。 +## 项目链接 -仓库:[yanxulang/yanxu-log](https://github.com/yanxulang/yanxu-log)。 +- [GitHub 仓库](https://github.com/yanxulang/yanxu-log) +- [言录 1.0.0 Release](https://github.com/yanxulang/yanxu-log/releases/tag/v1.0.0) diff --git a/content/docs/ecosystem/libraries/markdown.mdx b/content/docs/ecosystem/libraries/markdown.mdx index 3d45889..41415e0 100644 --- a/content/docs/ecosystem/libraries/markdown.mdx +++ b/content/docs/ecosystem/libraries/markdown.mdx @@ -1,79 +1,157 @@ --- title: 言章:Markdown -description: 解析 Markdown、生成结构化节点与目录,并安全渲染 HTML。 +description: 言章 1.0.1 的安全 Markdown 节点、HTML 渲染、目录、预算和可信扩展边界。 --- -言章(`yanxu-markdown`)是纯言序 Markdown 库。它面向文档站、博客、评论预览和内容流水线,默认不接受原始 HTML,并对普通文字、代码和属性执行转义。 +言章(`yanxu-markdown`)是言序的安全 Markdown 解析、结构化节点、目录和 HTML 渲染库。当前稳定版为 `1.0.1`:普通文字、代码、链接属性与标题锚点都通过言页的安全构造边界输出,原始 HTML 不会直接透传。 -## 安装 +## 版本与安装 + +| 项目 | 稳定值 | +| --- | --- | +| 包名 | `言章` | +| 版本 / 公开标签 | `1.0.1` / `v1.0.1` | +| 最低言序 | `1.1.12` | +| 稳定依赖 | 言页 `1.0.0`,标签 `v1.0.0`,范围 `^1.0` | +| 构建目标 | 字节码 | ```sh -yanbao add markdown --package 言章 --version "^0.1" +yanbao add 言章 \ + --git https://github.com/yanxulang/yanxu-markdown.git \ + --rev v1.0.1 \ + --version '^1.0' +``` + +```toml +[依赖] +言章 = { git = "https://github.com/yanxulang/yanxu-markdown.git", 修订 = "v1.0.1", 版 = "^1.0" } ``` +`v1.0.1` 是修复锁文件内容校验漂移后的正式补丁标签;不要继续固定旧的 `v1.0.0`。应提交生成的 `言序.lock`,让言章、言页及其修订与内容校验一同锁定。 + +## 核心能力:解析、渲染与目录 + ```yanxu 引「包:言章」为 言章; + +定 源码:文 为 「# 言章 <安全>\n\n欢迎使用 **言序** 和 [文档](https://yanxu.dev)。」; + +定 各节点:列<典> 为 言章.解析(源码); +定 HTML:文 为 言章.渲染节点(各节点); +定 各目录:列<典> 为 言章.目录(源码); + +言 HTML; +言 各目录; ``` -言章没有传递依赖,也不申请文件、网络、环境、进程或原生扩展权限。 +HTML 中的 `<安全>`会被转义,受支持的 Markdown 标记正常渲染。只需要 HTML 时可直接调用 `渲染HTML`;需要检查或改写块结构时先调用 `解析`,再用 `渲染节点`。 -## 解析与渲染 +重复标题会在单份文档内生成确定且唯一的 Unicode 锚点: ```yanxu -引「包:言章」为 言章; +定 各项 为 言章.目录(「# 入门\n\n## 入门\n\n## 入门」); +``` -定 原文 为 「# 使用指南 +三个锚点依次为 `入门`、`入门-2`和`入门-3`。跨文档全局唯一仍需要调用方增加命名空间。 -欢迎使用 **言序**。 +## 支持的语法与入口 -- 安全渲染 -- 自动目录」; +支持范围包括: -定 节点 为 言章.解析(原文); -定 HTML 为 言章.渲染节点(节点); -定 目录项 为 言章.目录(原文); +- 一至六级标题、段落和分隔线; +- 平坦的有序/无序列表与引用; +- 严格闭合的三个反引号围栏代码和行内代码; +- 粗体、强调、删除线、链接和图片; +- 唯一标题锚点与目录。 -言 HTML; -言 目录项; +| 场景 | 入口 | +| --- | --- | +| 直接得到安全 HTML | `渲染HTML` | +| 为不可信输入收紧预算 | `渲染受限` | +| 检查或改写块节点 | `解析`、`解析受限` | +| 渲染调用方节点 | `渲染节点`、`渲染节点受限` | +| 只处理内联标记 | `渲染内联`、`渲染内联受限` | +| 生成目录 | `目录`、`目录受限` | +| 逐片写出 HTML | `流式渲染`、`流式渲染节点` | +| 不使用异常控制流 | `尝试解析`、`尝试渲染` | + +手工节点不是受信输入。渲染器会拒绝未知节点类型、缺失字段、错误字段类型、越界标题层级和非文字列表项目。 + +## 对不可信输入设置预算 + +```yanxu +定 限制:典 为 { + 「最大输入字符」:65536, + 「最大行数」:4096, + 「最大块节点」:2048, + 「最大列表项目」:512, + 「最大内联深度」:16, + 「最大输出字符」:262144, + 「最大片段」:8192 +}; + +定 HTML:文 为 言章.渲染受限(源码,限制); ``` -需要一步完成时使用 `渲染HTML`。需要修改节点、过滤内容或生成其他目标格式时,先调用 `解析`,再消费结构化节点。 +面向外部内容应使用受限入口,而不是把库的较大硬上限当作业务配额。输出预算按 Unicode 字符计数,网络响应或文件写出还需在应用层限制 UTF-8 字节与总耗时。 -## 支持范围 +## 流式输出 -- 一至六级标题、唯一 Unicode 锚点和目录; -- 段落与分隔线; -- 有序列表、无序列表与引用; -- 围栏代码和行内代码; -- 粗体、强调与删除线; -- 链接与图片。 +```yanxu +法 写响应片段(片段:文):空 则 + # 交给调用方拥有的有界写入器 + 归 空; +终 + +定 统计:典 为 言章.流式渲染(源码,写响应片段,限制); +言 统计【「块节点数」】; +言 统计【「字符数」】; +言 统计【「片段数」】; +``` -同名标题会得到唯一锚点,目录项会保留标题层级、文字与锚点。`锚点` 只生成单个基础锚点;需要处理整篇文档中的重复标题时应使用 `目录` 或完整解析流程。 +流式 API 先完成 Markdown 块解析,再逐块输出 HTML;它减少完整 HTML 的额外收集,但不会消除完整节点列。回调是同步的,背压、取消、超时和外部写入错误由调用方处理。若后续片段失败,已经写出的片段不会自动撤回;需要原子正文时使用 `渲染受限`。 -## 公共接口 +## 链接与可信扩展 -| 接口 | 返回 | 用途 | -| --- | --- | --- | -| `解析(原文)` | `列<典>` | 生成结构化块节点 | -| `渲染内联(原文)` | `文` | 只处理行内标记 | -| `渲染节点(节点列)` | `文` | 渲染已经处理过的节点 | -| `渲染HTML(原文)` | `文` | 解析并渲染的便捷入口 | -| `目录(原文)` | `列<典>` | 提取层级、文字和唯一锚点 | -| `锚点(标题)` | `文` | 生成 Unicode 基础锚点 | -| `是否安全链接(地址)` | `理` | 检查链接协议白名单 | +普通链接允许 HTTP、HTTPS、邮件、电话、相对地址和片段,并拒绝控制字符、脚本和数据协议。图片地址比普通链接更严格,还拒绝邮件、电话和片段地址。协议检查不验证主机信誉、重定向、同源或业务授权。 -## 安全边界 +`扩展块`是显式信任升级: -允许的链接为 HTTP、HTTPS、邮件地址和相对地址。脚本协议、数据协议和其他未列入白名单的协议会被拒绝。原始 HTML 不会直接进入输出,因此内容作者不能借此注入标签或事件属性。 +```yanxu +法 写可信片段(写入法:法):空 则 + 写入法(「已审计常量」); + 归 空; +终 -单份输入上限为 1,048,576 个字符。超限或语法处理错误使用 `YANZHANG_` 前缀。言章提供的是安全渲染基础;把结果嵌入带有额外模板语义的系统时,仍应遵守该系统自己的输出编码规则。 +定 节点 为 言章.扩展块(写可信片段); +``` -## 开发验证 +扩展输出仍受总输出预算约束,但不会被清洗或转义。生成器及其输入必须全部可信,不能把用户文字直接插入扩展片段。 -```sh -yanbao check -yanbao test -yanbao build +## 错误、权限与安全边界 + +```yanxu +定 结果:典 为 言章.尝试渲染(源码); + +若 非 结果【「成功」】 则 + 言 结果【「错误」】【「代码」】; +终 ``` -仓库:[yanxulang/yanxu-markdown](https://github.com/yanxulang/yanxu-markdown)。 +公开失败使用稳定 `YANZHANG_*`代码;捕获抛出型入口后也可调用 `错误详情`。完整详情包含位置和踪迹,返回不可信客户端前应过滤。 + +言章清单不申请文件、网络、监听、环境、进程或原生扩展权限。它依赖的言页也固定在公开稳定标签;依赖图中的权限不会被页面代码动态扩大。 + +## 已知限制 + +- 不是完整 CommonMark 或 GFM 实现,不支持嵌套列表、表格、脚注、任务列表和引用式链接。 +- 不解析、清洗或透传原始 HTML;未知 HTML 写法按普通文字转义。 +- 围栏代码只支持三个反引号,结束行修整后必须严格等于三个反引号。 +- URL 策略只判断字符与协议,不检查实际目标、重定向、同源或访问权限。 +- 流式接口不是增量 Markdown 解析器,也不提供异步背压、取消或事务式回滚。 +- `扩展块`不会自动净化输入,只能用于已审计生成器和可信数据。 + +## 项目链接 + +- [GitHub 仓库](https://github.com/yanxulang/yanxu-markdown) +- [言章 1.0.1 Release](https://github.com/yanxulang/yanxu-markdown/releases/tag/v1.0.1) diff --git a/content/docs/ecosystem/libraries/retry.mdx b/content/docs/ecosystem/libraries/retry.mdx index 30491d4..868d3bd 100644 --- a/content/docs/ecosystem/libraries/retry.mdx +++ b/content/docs/ecosystem/libraries/retry.mdx @@ -1,85 +1,151 @@ --- title: 言韧:重试与断路器 -description: 将重试策略、指数退避、错误判定和三态断路器与业务操作分离。 +description: 言韧 1.0 的确定性退避、可观察执行、停止控制与三态断路器。 --- -言韧(`yanxu-retry`)提供同步重试、指数退避和关闭/开启/半开三态断路器。策略与执行分离,测试可以跳过真实等待而复用相同次数和判定逻辑。 +言韧(`yanxu-retry`)`1.0.0` 把重试策略、执行控制和断路状态分开。生产环境可以真实等待,测试或外部调度器可以注入等待器、观察器、停止判定与单调时间源,同时复用相同的尝试次数、退避和断路逻辑。 -## 安装 +## 版本与安装 + +| 项目 | 稳定值 | +| --- | --- | +| 包名 | `言韧` | +| 版本 / 公开标签 | `1.0.0` / `v1.0.0` | +| 最低言序 | `1.1.6` | +| 依赖 | 无 | +| 构建目标 | 字节码 | ```sh -yanbao add retry --package 言韧 --version "^0.1" +yanbao add 言韧 \ + --git https://github.com/yanxulang/yanxu-retry.git \ + --rev v1.0.0 \ + --version '^1.0' +``` + +```toml +[依赖] +言韧 = { git = "https://github.com/yanxulang/yanxu-retry.git", 修订 = "v1.0.0", 版 = "^1.0" } ``` -## 重试操作 +应把生成的 `言序.lock`与应用一同提交。 + +## 核心能力与快速开始 ```yanxu 引「包:言韧」为 言韧; 法 请求一次(次数:数):文 则 - 若 (次数 小于 3) 则 抛 「服务暂不可用」;终 - 归 「完成」; + 若 (次数 小于 3) 则 + 抛 「YANQIU_TIMEOUT:请求超时」; + 终 + 归 「响应成功」; 终 -定 结果 为 言韧.执行( - 言韧.重试策略(4,0.1,2,2), - 请求一次, - 言韧.总是重试 -); +定 策略 为 言韧.重试策略(4,0.1,2,1) + .设抖动(0.5,2026); +定 可恢复:法 为 言韧.可重试代码(【「YANQIU_TIMEOUT」】); + +言 言韧.执行(策略,请求一次,可恢复); ``` -操作接收从 1 开始的尝试次数。判定器接收捕获到的 `误` 与本次次数,并决定是否继续。 +`最大次数`包含首次调用;操作收到从 1 开始的尝试编号。判定器只在操作失败且还有下一次机会时执行。 ## 策略与退避 -| 接口 | 说明 | +`重试策略(最大次数, 初始延迟秒, 倍率, 最大延迟秒)`描述完整计划。无抖动时,第 `n`次失败后的延迟为: + +```text +min(初始延迟秒 × 倍率^(n - 1), 最大延迟秒) +``` + +| 入口 | 用途 | | --- | --- | -| `重试策略(最大次数, 初始延迟秒, 倍率, 最大延迟秒)` | 显式策略 | | `默认策略()` | 通用指数退避默认值 | -| `立即策略(最大次数)` | 延迟全部为零 | -| `延迟秒(策略, 已失败次数)` | 计算下一次等待 | -| `延迟表(策略)` | 查看完整退避序列 | -| `执行` | 真实等待后重试 | -| `立即执行` | 跳过等待,适合测试或外部调度 | +| `立即策略(次数)` | 延迟为零的计划 | +| `基础延迟秒` / `延迟秒` | 查看无抖动 / 实际延迟 | +| `延迟表` / `计划表` | 查看完整等待及累计计划 | +| `执行` | 使用真实等待,成功返回值、最终失败传播业务误 | +| `立即执行` | 复用计划但跳过真实等待 | +| `尝试执行` | 返回固定结构化结果而不抛最终业务错误 | -`最大次数` 包含第一次调用,不是“额外重试次数”。延迟由初始值乘以倍率,并被最大延迟截断。 +`设抖动(比例, 种子)`采用确定性向下抖动。同一策略、种子和失败次数在树解释器与字节码 VM 中得到相同结果;种子不是秘密,也不是密码学随机数。不同客户端应使用不同种子,测试则应固定种子。 -## 结构化结果 +生产代码应优先用 `可重试代码`白名单识别暂时性错误。`总是重试`和`从不重试`只适合受控边界,不应把错误消息文字当稳定分类协议。 -不希望把最终错误抛给调用方时使用 `尝试执行`: +## 可观察与可停止执行 ```yanxu -定 结果 为 言韧.尝试执行(策略,请求一次,言韧.总是重试,假); -若 结果【「成功」】 则 - 言 结果【「值」】; -否则 - 言 结果【「错误」】【「代码」】; +法 外部等待(秒:数,上下文:典):空 则 + # 交给调度器;测试也可以只记录延迟 + 归 空; 终 + +法 观察(事件:典):空 则 + 言 事件【「阶段」】; + 归 空; +终 + +定 控制 为 言韧.执行控制() + .设等待器(外部等待) + .设观察器(观察); + +定 结果 为 言韧.受控执行(策略,请求一次,可恢复,控制); ``` -返回值始终包含 `成功` 与 `次数`;成功时包含 `值`,失败时包含由代码、类别和消息组成的结构化 `错误`。 +还可用 `设停止判定`在每次尝试前协作式停止。生命周期事件模式 v1 的阶段为`开始`、`失败`、`等待`、`成功`、`放弃`、`耗尽`和`停止`,字段包括次数、最大次数、累计等待、当前等待与错误。 + +判定、等待、观察和停止回调失败分别使用 `YANREN_PREDICATE`、`YANREN_WAITER`、`YANREN_OBSERVER`和`YANREN_STOP_CALLBACK`,不会被误当成业务失败再次重试。成功事件的观察器即使失败,已经成功的业务操作也不会重做。 -## 断路器 +## 三态断路器 ```yanxu -定 断路 为 言韧.断路器(3,30000); +定 断路 为 言韧.断路器(5,30000); -试 则 - 定 值 为 断路.调用(读取上游); -救 所误 则 - 言 断路.状态(); +法 调用上游():文 则 + 归 「上游结果」; 终 + +言 断路.调用(调用上游); ``` -连续失败达到阈值后进入“开启”,冷却期内 `是否允许` 为假;冷却结束后第一次状态检查转为“半开”。半开调用成功会复位到“关闭”,失败则重新开启。 +连续失败达到阈值后从关闭进入开启;冷却到期后进入半开,并且只允许一个在途探针。探针成功关闭断路,失败重新开启并重置冷却。 + +`是否允许()`只查看状态,不占用半开许可。手工编排必须调用 `获取许可()`,并保证随后调用 `记成功()`或`记失败()`;通常应优先使用自动完成记录的 `调用()`。拒绝调用使用 `YANREN_OPEN`。 + +重试与断路器可以直接组合: + +```yanxu +定 值 为 言韧.受控执行经断路器( + 策略,断路,请求一次,可恢复,控制 +); +``` + +每次业务尝试都通过同一个断路器许可。若允许重试 `YANREN_OPEN`,等待计划必须覆盖断路冷却,并由上层总截止时间约束。 + +## 资源、权限与安全边界 + +| 项目 | 硬上限 | +| --- | ---: | +| 尝试次数 | 4096 | +| 单次延迟 | 86400 秒 | +| 计划累计等待 | 604800 秒(7 天) | +| 退避倍率 | 1000 | +| 错误代码白名单 | 64 | +| 断路失败阈值 | 4096 | +| 恢复时间 | 604800000 毫秒(7 天) | + +言韧无第三方依赖,不申请文件、网络、监听、环境、进程或原生扩展权限。应用传入的操作与回调仍在顶层应用的权限范围内运行,不能把不可信脚本直接用作回调。 -可使用 `记成功`、`记失败` 和 `失败数` 与外部调度器集成。`调用` 会透传原操作错误;断路器拒绝调用时使用 `YANREN_OPEN` 类错误。 +## 安全使用与已知限制 -## 使用建议 +- 只重试明确暂时且可安全重放的操作;写操作必须使用幂等键、唯一约束、事务或业务去重。 +- 避免在客户端、代理和服务多层叠加重试,否则请求与写入会乘法放大。 +- 停止判定是协作式的,不会抢占正在执行或阻塞中的业务操作。 +- 断路器是同步、进程内状态机,不提供跨线程、跨进程或分布式原子协调,也不替代认证、限流或配额。 +- 自定义时间源必须返回非负、安全、单调不减的毫秒;等待器与断路器应使用同一时间域。 +- `错误详情`包含位置与踪迹,写入遥测或返回客户端前必须按数据策略过滤。 -- 只重试幂等操作,或为写操作提供幂等键; -- 判定器应排除验证错误、认证错误等永久失败; -- 在上层设置总截止时间,避免多个重试层叠加成过长等待; -- 测试使用 `立即执行`,避免真实休眠拖慢套件。 +## 项目链接 -仓库:[yanxulang/yanxu-retry](https://github.com/yanxulang/yanxu-retry)。 +- [GitHub 仓库](https://github.com/yanxulang/yanxu-retry) +- [言韧 1.0.0 Release](https://github.com/yanxulang/yanxu-retry/releases/tag/v1.0.0) diff --git a/content/docs/ecosystem/libraries/semver.mdx b/content/docs/ecosystem/libraries/semver.mdx index 3cb3915..620f07d 100644 --- a/content/docs/ecosystem/libraries/semver.mdx +++ b/content/docs/ecosystem/libraries/semver.mdx @@ -1,73 +1,133 @@ --- title: 言版:语义版本 -description: 严格解析 SemVer 2.0.0,并比较、匹配范围与选择最高兼容版本。 +description: 言版 1.0 的 SemVer 2.0.0 严格解析、范围匹配、稳定排序与资源边界。 --- -言版(`yanxu-semver`)实现 SemVer 2.0.0 的严格版本解析和优先级规则,并提供工程中常用的版本范围匹配。 +言版(`yanxu-semver`)是纯言序实现的语义版本库。当前稳定版为 `1.0.0`,严格实现 SemVer 2.0.0 的版本文本与优先级规则,并提供工程常用的范围、筛选和版本选择能力。 -## 安装 +## 版本与安装 + +| 项目 | 稳定值 | +| --- | --- | +| 包名 | `言版` | +| 版本 / 公开标签 | `1.0.0` / `v1.0.0` | +| 最低言序 | `1.1.6` | +| 依赖 | 无 | +| 构建目标 | 字节码 | + +从公开附注标签安装: ```sh -yanbao add semver --package 言版 --version "^0.1" +yanbao add 言版 \ + --git https://github.com/yanxulang/yanxu-semver.git \ + --rev v1.0.0 \ + --version '^1.0' +``` + +等价的 `言序.toml` 依赖为: + +```toml +[依赖] +言版 = { git = "https://github.com/yanxulang/yanxu-semver.git", 修订 = "v1.0.0", 版 = "^1.0" } ``` -言版是无权限、无传递依赖的纯计算库。 +应提交生成的 `言序.lock`,让来源修订和内容校验也进入版本控制。 -## 解析与比较 +## 快速开始 ```yanxu 引「包:言版」为 言版; -定 版本 为 言版.解析(「1.4.0-beta.2+build.9」); -言 版本; -言 言版.规范化(「1.4.0-beta.2+build.9」); -言 言版.比较(「1.4.0-beta.2」,「1.4.0」); # -1 +定 候选:列<文> 为 【 + 「1.2.0」,「1.8.4」,「2.0.0-rc.1」,「2.0.0」 +】; + +言 言版.规范化(「1.2.3-alpha.1+linux」); +言 言版.比较(「1.8.4」,「2.0.0-rc.1」); +言 言版.满足(「1.8.4」,「^1.2」); +言 言版.最大满足(候选,「^1.0.0」); +言 言版.递增(「1.8.4」,「次」); ``` -`比较` 返回 `-1`、`0` 或 `1`。构建标识不参与优先级;预发布标识按 SemVer 数字与文字规则逐段比较。 +`比较`返回 `-1`、`0` 或 `1`。构建标识不参与优先级,所以 `1.0.0+a` 与 `1.0.0+b` 在比较意义上等价;`规范化`仍保留构建标识。 + +## 核心能力 -## 范围匹配 +| 能力 | 主要入口 | 语义 | +| --- | --- | --- | +| 严格解析 | `解析`、`尝试解析`、`是否合法` | 拒绝前导零、`v` 前缀、首尾空白和非法标识 | +| 规范与比较 | `规范化`、`比较`、`等价` | 完整 SemVer 2.0.0 预发布优先级 | +| 范围 | `满足`、`满足选项`、`尝试满足` | 比较器、交集、并集、尖帽、波浪、部分、通配和连字范围 | +| 候选选择 | `筛选满足`、`最小满足`、`最大满足` | 保持输入顺序筛选,未命中返回空 | +| 排序 | `排序版本` | 稳定归并排序,返回新列,不改写输入 | +| 递增 | `递增` | 接受 `主`、`次`、`修`,并移除预发布与构建标识 | + +### 范围语法 ```yanxu -言 言版.满足(「1.4.0」,「^1.2.0」); -言 言版.满足(「2.0.0-beta.1」,「>=2.0.0-beta.1 <2.0.0」); -言 言版.满足(「3.1.5」,「2.x || 3.1.x」); +言 言版.满足(「1.6.0」,「>=1.2.0 <2.0.0」); +言 言版.满足(「2.3.4」,「^1.0.0 || ~2.3」); +言 言版.满足(「2.2.0」,「1.2 - 2.3」); +言 言版.满足(「3.1.5」,「2.x || 3.1.*」); ``` -支持: +空格表示交集,`||`表示并集。连字范围必须是单个分支中以三个空格分隔的形式;若还要追加条件,应改写成显式比较器。 + +普通稳定范围默认排除预发布候选。范围显式提及同一主、次、修订核心的预发布时可以命中;需要在整个范围放行预发布时使用显式选项: -- `=`、`>`、`>=`、`<`、`<=`; -- 空格连接的交集,例如 `>=1.2 <2.0`; -- `||` 连接的并集; -- 尖帽范围 `^`; -- 波浪范围 `~`; -- 部分版本与 `x`、`X`、`*` 通配。 +```yanxu +言 言版.满足(「1.2.3-beta.2」,「>=1.2.3-alpha <2.0.0」); +言 言版.满足选项( + 「1.5.0-beta.1」, + 「^1.2.0」, + {「包含预发布」:真} +); +``` -范围语法刻意保持确定,不接受连字符范围和括号。 +## 结构化失败 -## 选择兼容版本 +外部清单和索引输入应优先使用无异常入口: ```yanxu -定 可用 为 【「1.2.0」,「1.7.3」,「2.0.0-beta.1」,「2.0.0」】; +定 结果:典 为 言版.尝试解析(外部版本); -言 言版.排序版本(可用); -言 言版.最大满足(可用,「^1.2.0」); # 1.7.3 +若 结果【「成功」】 则 + 言 结果【「值」】; +否则 + 言 结果【「错误」】【「代码」】; +终 ``` -`排序版本` 返回新列,不改写输入。`最大满足` 没有候选时返回空,因此调用方应显式处理未命中分支。 +稳定错误代码包括: -## 公共接口 +- `YANBAN_PARSE`:版本文本不合法; +- `YANBAN_RANGE`:范围语法不合法; +- `YANBAN_LIMIT`:超过文本、条件或候选预算; +- `YANBAN_ARGUMENT`:选项或递增级别不合法。 -| 接口 | 说明 | -| --- | --- | -| `解析` | 严格解析为结构化典 | -| `是否合法` | 不抛错的合法性判断 | -| `规范化` | 返回标准版本文字 | -| `比较` | 比较两个版本优先级 | -| `满足` | 检查候选是否匹配范围 | -| `排序版本` | 按优先级升序返回新列 | -| `最大满足` | 选择范围内最高版本或空 | +捕获抛出型入口的错误后可调用 `错误详情`。程序分支只应依赖代码,不应匹配完整中文消息。 + +## 权限与安全边界 + +言版无第三方依赖,不读写文件,不访问网络、监听端口、环境或进程,也不加载原生扩展。库不保存输入,也没有全局可变状态。 + +处理不可信输入时还应了解这些硬边界: + +- 单个版本最多 256 个 Unicode 字符; +- 单个范围最多 2048 个 Unicode 字符; +- 每个范围最多 32 个并集分支,每分支最多 64 个交集条件; +- 单次排序或选择最多 4096 个候选版本。 + +超限会完整失败,不会返回截断后的结果。业务入口仍宜设置更小的请求级配额。 + +## 已知限制 + +- 主、次和修订号最大为 `9007199254740991`;数字预发布标识按文本长度和 ASCII 次序比较,不受该浮点安全整数上限影响。 +- 范围是言版的工程扩展,不属于 SemVer 2.0.0 标准本身;不支持括号或隐式逗号交集。 +- 范围分词只把 ASCII 空格当作交集分隔,不接受制表符替代。 +- `比较`和`等价`忽略构建标识;需要比较完整文本身份时应比较两边的规范化结果。 -解析与范围错误使用 `YANBAN_` 前缀。接收不可信范围文字时,可先调用 `是否合法` 检查单个版本;范围本身仍应在应用边界捕获并转换为用户可读错误。 +## 项目链接 -仓库:[yanxulang/yanxu-semver](https://github.com/yanxulang/yanxu-semver)。 +- [GitHub 仓库](https://github.com/yanxulang/yanxu-semver) +- [言版 1.0.0 Release](https://github.com/yanxulang/yanxu-semver/releases/tag/v1.0.0) diff --git a/content/docs/ecosystem/libraries/test.mdx b/content/docs/ecosystem/libraries/test.mdx index 2bc14e8..da53341 100644 --- a/content/docs/ecosystem/libraries/test.mdx +++ b/content/docs/ecosystem/libraries/test.mdx @@ -1,103 +1,93 @@ --- title: 言试:测试工具 -description: 使用组合断言、数据驱动、夹具钩子、Mock/Spy、快照和可注入外部边界测试言序代码。 +description: 言试 1.0 的有界断言、套件夹具、Mock/Spy、快照及 HTTP、数据库测试适配。 --- -言试(`yanxu-test`)在标准 `测试` 模块之上补充工程化测试能力。它不会替代言序测试运行器;测试文件中调用言试,断言失败会抛出结构化错误并由运行器报告。 +言试(`yanxu-test`)`1.0.0` 在言序标准`测试`模块之上提供工程化测试能力:有界断言与深比较、数据驱动套件、惰性夹具、Mock/Spy、快照、临时目录、内存 HTTP 传输、数据库回滚事务和仓储 CRUD 契约。它不会替代测试运行器。 -## 安装为开发依赖 +## 版本与安装 -```sh -yanbao add test --package 言试 --version "^0.1" --dev -``` +| 项目 | 稳定值 | +| --- | --- | +| 包名 | `言试` | +| 版本 / 公开标签 | `1.0.0` / `v1.0.0` | +| 最低言序 | `1.1.12` | +| 稳定依赖 | 言时 `1.0`、言据 `1.2`、言访 `1.0`、言库 `1.0` | +| 构建目标 | 字节码 | -言试会传递安装言时,以共享虚拟时钟。 +测试工具应作为开发依赖安装: -```yanxu -引「包:言试」为 言试; +```sh +yanbao add 言试 \ + --git https://github.com/yanxulang/yanxu-test.git \ + --rev v1.0.0 \ + --version '^1.0' \ + --dev ``` -## 断言 +等价的 `言序.toml`清单: -```yanxu -言试.断言(真,「条件应成立」); -言试.相等(2 加 2,4); -言试.深相等({「用户」:{「姓名」:「子衿」}},{「用户」:{「姓名」:「子衿」}}); -言试.近似相等(0.1 加 0.2,0.3,0.000001); -言试.应含(【「甲」,「乙」】,「乙」); -言试.应匹配(「user-42」,「^user-[0-9]+$」); -言试.类型断言(「正文」,「文」); +```toml +[开发依赖] +言试 = { git = "https://github.com/yanxulang/yanxu-test.git", 修订 = "v1.0.0", 版 = "^1.0" } ``` -`相等` 适合标量和按语言相等语义比较的值;嵌套列和典使用 `深相等`。`深差异(实际, 期望)` 返回路径化差异列,便于构造自定义报告。 +言试自身把四个依赖分别固定在公开标签:言时 `v1.0.0`、言据 `v1.2.0`、言访 `v1.0.0`和言库 `v1.0.0`。测试项目仍应提交自己的 `言序.lock`。 -```yanxu -法 会失败():空 则 抛 「输入无效」;终 - -定 所误 为 言试.应当抛错(会失败); -言试.应当抛错匹配(会失败,「输入无效」); -``` +## 核心能力与快速开始 -`符合(值, 判定, 名称)` 可复用领域判定器。 +```yanxu +引「包:言试」为 言试; -## 表格与参数化测试 +法 用户夹具():典 则 + 归 {「编号」:42,「姓名」:「子衿」}; +终 -```yanxu -法 检查一行(行:典):空 则 - 言试.相等(行【「输入」】 乘 2,行【「期望」】); +法 清理用户(用户):空 则 归 空; 终 -定 报告 为 言试.表格测试(「加倍」,【 - {「输入」:1,「期望」:2}, - {「输入」:3,「期望」:6} -】,检查一行); -``` - -`参数化测试` 把每个参数列展开传给测试法。两个入口都返回名称、总数、通过数与结构化结果,失败仍会抛出,确保运行器正确标红。 - -## 套件、钩子与夹具 - -```yanxu -法 打开资源() 则 归 建立资源();终 -法 关闭资源(资源):空 则 资源.关闭();归 空;终 - -法 测试创建(上下文):空 则 - 定 资源 为 上下文.夹具(「资源」); - 言试.相等(资源.创建(),「完成」); +法 验证用户(上下文):空 则 + 定 用户:典 为 上下文.夹具(「用户」); + 言试.相等(用户【「编号」】,42); + 言试.应匹配(用户【「姓名」】,「^子」); 归 空; 终 -定 套 为 言试.套件(「服务」) - .测试前(准备全局) - .测试后(清理全局) - .每项前(准备每项) - .每项后(清理每项) - .夹具(「资源」,打开资源,关闭资源) - .测试(「创建资源」,测试创建); - -定 报告 为 套.运行(); +言试.套件(「用户服务」) + .夹具(「用户」,用户夹具,清理用户) + .测试(「读取用户」,验证用户) + .运行(); ``` -每个测试得到独立 `测试上下文`。夹具第一次读取时才创建,并在该测试结束后按反序清理。测试体失败后仍会执行每项后钩子和夹具清理;清理失败也会进入报告。 +夹具第一次读取时才创建,并在当前测试结束后按反序清理。测试体失败后仍会执行每项后钩子与夹具清理;清理失败也会进入报告。 + +## 断言与数据驱动 -## 临时目录与环境隔离 +| 能力 | 入口示例 | +| --- | --- | +| 基础与相等 | `断言`、`相等`、`不相等`、`深相等`、`近似相等` | +| 内容与类型 | `应含`、`应匹配`、`类型断言`、空值断言 | +| 领域谓词 | `符合` | +| 错误 | `应当抛错`、`应当抛错匹配`、`应当抛错代码` | +| 诊断 | `深差异`、`错误详情` | ```yanxu -定 临时 为 言试.默认临时目录(); -定 路径 为 临时.合并(「结果.json」); -# 使用路径 -临时.清理(); - -定 环境 为 言试.隔离环境({「模式」:「生产」}); -环境.设置(「模式」,「测试」).删除(「旧变量」); -言 环境.读取(「模式」); -环境.恢复(); +言试.相等(2 加 2,4); +言试.深相等( + {「用户」:{「姓名」:「子衿」}}, + {「用户」:{「姓名」:「子衿」}} +); +言试.近似相等(0.1 加 0.2,0.3,0.000001); ``` -`环境隔离` 是显式注入的覆盖视图,不修改进程全局环境,因此并发测试互不污染。`隔离系统环境(名称列)` 可读取真实变量作为基础,但顶层测试项目必须授权这些变量名。 +嵌套列和典应使用 `深相等`。深比较、复制和记录都有预算;大型合法样本应显式使用受限 API,并在测试中说明提高上限的理由。 + +`表格测试`使用行典组织可读案例,`参数化测试`把参数列按位置展开。两者都返回结构化运行结果,并在案例失败时让测试运行器正确标红。 -## Mock、Spy 与虚拟时钟 +## 套件、Mock 与隔离 + +套件提供`测试前`、`测试后`、`每项前`、`每项后`、`夹具`、`测试`、`尝试运行`和`运行`。`尝试运行`聚合通过、失败、跳过、逐项错误和生命周期错误;`运行`在结果不成功时抛 `YANSHI_TEST_SUITE`。 ```yanxu 定 时钟 为 言试.虚拟时钟(0); @@ -109,49 +99,79 @@ yanbao add test --package 言试 --version "^0.1" --dev 言 模拟.调用(【1】); 言试.相等(模拟.调用次数(),1); 言试.断言(模拟.被调用于(【1】),「应记录参数」); - -定 侦听 为 言试.Spy(原函数,时钟); -定 值 为 侦听.调用(【「输入」】); ``` -Mock 可配置固定返回、返回队列、错误队列或自定义实现;每次调用记录参数、时间、结果或错误。Spy 始终调用原函数并记录相同信息。`函数()` 可把绑定调用法注入只接受普通法的业务对象。 +Mock 可配置固定返回、队列返回、错误队列或自定义实现;Spy 调用原函数并记录同样的信息。调用参数、结果与记录会深复制。套件、Mock、Spy、快照库和 HTTP Mock 都有可变状态,不应跨并发测试共享实例。 + +快照使用单个 JSON 对象文件;更新模式应显式开启,并由评审检查变化。临时目录的子路径必须使用正斜线相对路径,拒绝 `..`和反斜线。环境隔离是内存覆盖视图,不修改进程全局环境。 -## 快照 +## HTTP 客户端测试 ```yanxu -定 快照 为 言试.快照库(「tests/__snapshots__/用户.snap.json」,真); -快照.匹配(「用户详情」,{「姓名」:「子衿」,「权限」:【「读」,「写」】}); +引「包:言访」为 言访; + +定 HTTP 为 言试.HTTPMock() + .响应JSON( + 「GET」, + 「https://api.example.test/users/42」, + 200, + {「编号」:42} + ); + +定 客户 为 言访.客户端() + .禁用重试() + .使用传输(HTTP.言访传输()); + +定 响应 为 客户.取(「https://api.example.test/users/42」).发送(); +言试.相等(响应.解析JSON()【「编号」】,42); ``` -更新模式会创建或刷新命名快照并立即保存;验证模式遇到缺失或深层差异时失败。快照是单个 JSON 对象文件,适合稳定结构,不应用来掩盖每次运行都会变化的时间戳或随机值。 +`HTTPMock`在内存中按精确或正则地址匹配路由,不劫持全局网络。`言访传输()`返回正式的言访自定义传输适配;未匹配请求使用 `YANSHI_TEST_HTTP_UNMATCHED`失败。HTTP 非 2xx 是正常响应,只有网络型错误与未匹配路由抛错。 -## HTTP Mock +## 数据库与仓储测试 ```yanxu -定 HTTP 为 言试.HTTPMock() - .响应JSON(「GET」,「https://api.example.test/health」,200,{「ok」:真}) - .一次响应(「POST」,「https://api.example.test/jobs」,201,{},「created」); +法 建用户(事务):数 则 + 事务.执行(「INSERT INTO users(name) VALUES (?)」,【「子衿」】); + 归 1; +终 -定 响应 为 HTTP.请求(「GET」,「https://api.example.test/health」,{},空); -言试.相等(响应【「状态」】,200); -言试.相等(HTTP.请求次数(),1); +言试.数据库测试事务(连接,建用户); ``` -支持精确地址、正则地址、无限次响应、一次响应和 JSON 响应。`传输()` 返回可注入业务客户端的法。它不劫持标准网络模块;未匹配请求会以 `YANSHI_TEST_HTTP_UNMATCHED` 失败。 +连接必须支持言库托管事务。无论回调成功还是失败,`数据库测试事务`都会回滚并恢复连接;回调不能自行提交。回滚只能覆盖同一事务连接内的操作,消息、文件和其他外部副作用仍须由测试清理。 -## 数据库测试事务 +仓储协议、函数适配器和 CRUD 契约用于让 SQLite、PostgreSQL、MySQL 与测试替身共享语义。契约会调用`重置()`并清空数据,只能用于隔离测试仓储,绝不能指向开发或生产数据。 -```yanxu -法 创建用户(事务):文 则 - 事务.执行(「INSERT INTO users(name) VALUES (?)」,【「子衿」】); - 归 「完成」; -终 +## 权限与安全边界 + +最终包清单声明: -定 结果 为 言试.测试事务(连接,创建用户); +```toml +[权限] +文件 = ["."] +网络 = [] +TCP监听 = [] +UDP绑定 = [] +环境 = [] +进程 = false +原生扩展 = false ``` -无论操作成功或抛错,`测试事务` 都会回滚;成功值会原样返回,失败时保留原错误。连接只需提供兼容的 `开始事务`,因此可用于言库连接或测试替身。 +文件能力服务于快照和临时目录 API。依赖权限不会自动传递,顶层测试项目仍须显式授权自己实际使用的路径;真实数据库连接、系统环境读取和其他外部能力同样由测试项目负责。`HTTPMock`不需要网络权限,`数据库测试事务`也不会创建连接或读取凭据。 + +错误使用稳定 `YANSHI_TEST_*`代码。快照、HTTP 记录、Mock 调用和错误可能含敏感值,不要把真实令牌、Cookie、个人数据或数据库凭据写入测试产物。 + +## 已知限制 + +- 套件是同步执行模型,不调度异步任务。 +- 快照只支持 JSON 可表示的值,不承诺与其他快照框架互通。 +- Mock/Spy 调用记录和快照会随用例规模线性占用内存,不适合无限压力记录。 +- HTTP Mock 不模拟 DNS、TLS、连接分段或真实网络时序。 +- 数据库回滚不能撤销事务连接外的副作用;仓储契约会执行破坏性重置。 +- 有状态测试对象不应跨并发测试共享;需要共享依赖缓存时可让运行器使用 `--并发 1`。 -快照与临时目录需要文件权限。HTTP Mock 和环境隔离采用显式注入,不申请网络或全局环境写权限。错误统一使用 `YANSHI_TEST_` 前缀。 +## 项目链接 -仓库:[yanxulang/yanxu-test](https://github.com/yanxulang/yanxu-test)。 +- [GitHub 仓库](https://github.com/yanxulang/yanxu-test) +- [言试 1.0.0 Release](https://github.com/yanxulang/yanxu-test/releases/tag/v1.0.0) diff --git a/content/docs/ecosystem/libraries/validate.mdx b/content/docs/ecosystem/libraries/validate.mdx index de044d7..1849ccd 100644 --- a/content/docs/ecosystem/libraries/validate.mdx +++ b/content/docs/ecosystem/libraries/validate.mdx @@ -1,19 +1,35 @@ --- title: 言验:数据验证 -description: 使用可组合中文规则验证、转换输入,并一次获得完整路径化问题。 +description: 言验 1.0 的可组合规则、转换、结构化问题及言据、ORM、HTTP 适配边界。 --- -言验(`yanxu-validate`)用于配置、HTTP 载荷、命令行结果和数据库写入前的数据边界。规则既能检查类型与约束,也能执行明确的预处理、默认值和输出转换。 +言验(`yanxu-validate`)`1.0.0` 用中文链式 API 描述不可信输入边界。它可以在一次解析中收集路径化问题,并提供外部验证执行器、言据 Schema、ORM 模型和 HTTP 请求适配器;验证本身不替代授权、事务或传输层限制。 -## 安装 +## 版本与安装 + +| 项目 | 稳定值 | +| --- | --- | +| 包名 | `言验` | +| 版本 / 公开标签 | `1.0.0` / `v1.0.0` | +| 最低言序 | `1.1.6` | +| 依赖 | 无 | +| 构建目标 | 字节码 | ```sh -yanbao add validate --package 言验 --version "^0.1" +yanbao add 言验 \ + --git https://github.com/yanxulang/yanxu-validate.git \ + --rev v1.0.0 \ + --version '^1.0' +``` + +```toml +[依赖] +言验 = { git = "https://github.com/yanxulang/yanxu-validate.git", 修订 = "v1.0.0", 版 = "^1.0" } ``` -言验没有传递依赖,也不申请任何权限。 +应提交生成的 `言序.lock`,固定公开标签解析出的提交和内容校验。 -## 定义对象规则 +## 核心能力:对象规则与安全解析 ```yanxu 引「包:言验」为 言验; @@ -22,79 +38,123 @@ yanbao add validate --package 言验 --version "^0.1" 「姓名」:言验.文本().至少(2).至多(50), 「年龄」:言验.整数().至少(0), 「邮箱」:言验.文本().是邮件().可选(), - 「角色」:言验.枚举(【「读者」,「编辑」】).默认(「读者」), - 「标签」:言验.列表(言验.文本().至少(1)).默认(【】) + 「角色」:言验.枚举(【「读者」,「作者」】).默认(「读者」) }).拒绝未知(); 定 结果 为 用户规则.安全解析({ 「姓名」:「子衿」, 「年龄」:18 }); + +若 结果.成功 则 + 言 结果.值; +否则 + 逐 问题 于 结果.问题列 则 + 言 问题【「路径」】; + 言 问题【「代码」】; + 言 问题【「消息」】; + 终 +终 ``` -对象规则默认移除未知字段。对 API 入口通常选择 `拒绝未知`,对向前兼容的配置读取可选择 `保留未知`。 +对象默认移除未知键,可显式选择 `保留未知`或`拒绝未知`。每项问题包含稳定代码、完整对象/列表路径、消息、期望和实际类型;错误记录实际类型而不复制原始敏感值。 -## 规则构造器 +## 规则与转换 -| 构造器 | 接受和输出 | +| 类别 | 入口 | | --- | --- | -| `任选()` | 任意非空值 | -| `文本()` | 文字 | -| `数字()` / `整数()` | 数值 / 无小数数值 | -| `数字自文()` | 数值或 JSON 数值文字,输出数值 | -| `布尔()` / `典型()` | 布尔 / 典 | -| `字面(值)` | 与给定值相等 | -| `枚举(候选列)` | 候选集合中的值 | -| `列表(项规则)` | 逐项验证并保留下标路径 | -| `对象型(形状)` | 按字段规则构造输出典 | -| `联合(规则列)` | 依次尝试分支 | - -## 链式约束 - -所有构造器返回 `规则`,可以继续调用: - -- `至少`、`至多`:数字边界或文字/列表长度; -- `匹配`、`是邮件`、`是网址`、`是日期`; -- `其中`:限制候选值; -- `可选`、`可空`、`默认`; -- `预处理`:基础类型检查之前执行; -- `转化`:全部检查通过后执行; -- `自定义(判定, 代码, 消息)`:加入领域规则。 +| 基础 | `任选`、`文本`、`数字`、`整数`、`数字自文`、`布尔`、`典型` | +| 精确值 | `字面`、`枚举` | +| 复合 | `列表`、`对象型`、`联合`、`交叉`、`条件` | +| 约束 | `至少`、`至多`、`匹配`、`是邮件`、`是网址`、`是日期`、`其中` | +| 值处理 | `可选`、`可空`、`默认`、`预处理`、`转化`、`自定义` | + +多个预处理和转换按登记顺序执行。预处理在空值和基础类型检查前运行,转换在基础规则与同步约束成功后运行。列和典默认值会在每次使用时深复制,不会因调用方修改一次结果而污染后续解析。 ```yanxu -法 去空白(值) 则 归 文字.修整(值);终 -法 转显示名(值) 则 归 (「用户:」 加 值);终 +定 端口规则 为 言验.数字自文() + .至少(1) + .至多(65535); -定 名称规则 为 言验.文本() - .预处理(去空白) - .至少(2) - .转化(转显示名); +定 端口结果 为 端口规则.安全解析(「8080」); ``` -预处理或转换抛出的错误会转成 `PREPROCESS` 或 `TRANSFORM` 结构化问题,不会丢失输入路径。 +需要失败即中断时使用 `规则.解析(值)`或`言验.断验(规则, 值)`。抛出错误使用 `YANYAN_`前缀;捕获后用 `错误详情`取稳定代码,不要匹配完整消息。 -## 安全解析结果 +## 外部验证执行器 -`安全解析` 返回 `验证结果`: +数据库唯一性、远程策略或批量权限检查可以登记为外部验证任务: ```yanxu -若 结果.成功 则 - 言 结果.值; -否则 - 逐 问题 于 结果.问题列 则 - 言 问题【「路径」】; - 言 问题【「代码」】; - 言 问题【「消息」】; - 终 -终 +定 用户名 为 言验.文本() + .异步自定义(未被占用,「TAKEN」,「用户名已占用」); + +定 结果 为 用户名.异步安全解析( + 「子衿」, + 言验.顺序验证执行器() +); +``` + +同步入口遇到任何嵌套的外部验证器时返回 `ASYNC_REQUIRED`,不会静默跳过检查。1.0 内建执行器按顺序确定性执行;并行、取消和超时由调用方注入的执行器负责。执行器必须为每个任务返回同位置的问题典或空,否则失败关闭。 + +## 言据、ORM 与 HTTP 适配 + +### 言据 Schema + +```yanxu +定 模式 为 { + 「类型」:「据」, + 「必需」:【「姓名」】, + 「允许额外」:假, + 「属性」:{ + 「姓名」:{「类型」:「文」,「最短」:2} + } +}; + +定 结果 为 言验.验证言据({「姓名」:「子衿」},模式); +``` + +`言据模式(Schema典)`把言据 1.2 Schema 编译为普通规则。它只处理已经解析的值和 Schema 典,不读取 `.yj`文件,也不解析言据源文本。 + +### ORM 模型 + +`模型规则(字段规则典)`提供 `验证创建`、`验证更新`、`验证字段`、`登记场景`和`验证场景`,并有接受执行器的对应入口。预检查不能消除竞争条件:唯一性、外键和引用完整性最终仍须由事务与数据库约束保证。 + +### HTTP 请求 + +```yanxu +定 请求规则 为 言验.HTTP请求规则() + .查询参数(查询规则) + .正文(言验.言据模式(正文模式)); + +定 结果 为 请求规则.验证请求(请求典); ``` -每项问题包含 `路径`、`代码`、`消息`、`期望` 和 `实际类型`。嵌套对象与列表会保留完整字段和下标路径。`首项问题()` 用于只显示第一项,`转典()` 适合序列化响应。 +适配器可组合路径参数、查询参数、首部、Cookie 和正文规则。它验证的是已解析请求典,不读取网络流,不协商内容类型,也不执行身份认证;传输层必须先限制原始首部和正文大小。 + +## 权限与安全边界 + +言验无第三方依赖,清单拒绝文件、网络、监听、环境、进程和原生扩展权限。外部验证器需要网络或数据库时,其能力、凭据、超时和取消均由顶层应用与注入执行器承担。 + +默认资源预算包括: + +- 文字、列或典最多 4096 个字符或项目; +- 单次最多返回 256 个问题,Schema 最多 256 项; +- 联合或交叉最多 64 个分支; +- 每条规则最多 128 个同步约束和 128 个外部约束; +- Schema 与深复制值最多嵌套 8 层。 + +`限制项数`只能把单条规则收紧到库硬上限以内。正则模式、自定义验证器与执行器都应来自受信任应用代码;库会结构化其异常,但不会隔离其 CPU、内存或宿主权限。 -## 抛错解析 +## 已知限制 -确定失败应立即中断时,使用 `规则.解析(值)`、`言验.断验(规则, 值)`。失败错误使用 `YANYAN_<代码>` 前缀,并携带首项问题 JSON。业务 API 通常优先使用 `安全解析`,以便一次返回所有可修正问题。 +- `异步安全解析`会等待执行器返回完整结果列;核心库不提供内建并行调度、取消或超时。 +- HTTP 适配器不是正文读取器、认证器或限流器,Schema 适配器不是文件解析器。 +- 验证成功只说明值符合规则;授权、唯一性、外键、事务一致性和业务状态仍需在各自边界检查。 +- 未知言据 Schema 注解会被忽略以容纳上层元数据;已知字段的类型错误会返回 `YANYAN_YANJU_SCHEMA`。 +- 问题消息可本地化和改进,机器分支只能依赖代码与结构字段。 -验证不等于授权或数据库约束。验证后的值仍应经过权限判断,唯一性和引用完整性仍由事务与数据库保证。 +## 项目链接 -仓库:[yanxulang/yanxu-validate](https://github.com/yanxulang/yanxu-validate)。 +- [GitHub 仓库](https://github.com/yanxulang/yanxu-validate) +- [言验 1.0.0 Release](https://github.com/yanxulang/yanxu-validate/releases/tag/v1.0.0) From 94157b8058adca9dcc5e16f0fb4d8d116845ce6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E7=A7=80?= Date: Sat, 18 Jul 2026 19:27:09 +0800 Subject: [PATCH 2/6] =?UTF-8?q?docs:=20=E5=AE=8C=E5=96=84=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=BA=93=E4=B8=8E=20ORM=20=E7=A8=B3=E5=AE=9A=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- content/docs/ecosystem/libraries/db.mdx | 283 ++++++++++----- content/docs/ecosystem/libraries/mysql.mdx | 282 +++++++++++++++ content/docs/ecosystem/libraries/orm.mdx | 334 ++++++++++++++---- content/docs/ecosystem/libraries/postgres.mdx | 280 +++++++++++++++ content/docs/ecosystem/libraries/sqlite.mdx | 232 +++++++++--- 5 files changed, 1210 insertions(+), 201 deletions(-) create mode 100644 content/docs/ecosystem/libraries/mysql.mdx create mode 100644 content/docs/ecosystem/libraries/postgres.mdx diff --git a/content/docs/ecosystem/libraries/db.mdx b/content/docs/ecosystem/libraries/db.mdx index 79381b4..cc9936b 100644 --- a/content/docs/ecosystem/libraries/db.mdx +++ b/content/docs/ecosystem/libraries/db.mdx @@ -1,142 +1,263 @@ --- -title: 言库:数据库协议 -description: 统一连接、事务、结果、连接池、参数绑定和 SQLite/PostgreSQL/MySQL 方言。 +title: 言库:数据库核心协议 +description: 使用稳定的配置、驱动、参数化 SQL、结果、事务、连接池、诊断与言据交换边界。 --- -言库(`yanxu-db`)定义同步数据库边界,不绑定某个厂商客户端。驱动可以来自原生扩展、网络客户端、外部进程、测试替身或言舟,业务层始终面向同一套中文协议。 +言库(包名 `言库`,仓库 `yanxu-db`)是言序数据库生态的稳定核心。它统一配置、驱动发现、连接、查询结果、预编译语句、事务、连接池、观测与言据交换,但不在核心包里实现 PostgreSQL、MySQL、SQLite 线协议,也不是 ORM。 -## 安装 +| 项目 | 1.0 稳定边界 | +| --- | --- | +| 当前版本 | `1.0.0` | +| 最低言序 | `1.1.11` | +| 清单格式 | 2 | +| 稳定依赖 | 言据 `v1.2.0`、言录 `v1.0.0`、言时 `v1.0.0` | +| 运行方式 | 纯言序;树解释器与字节码 VM 均可使用 | +| 许可 | MIT | + +## 安装并固定公开标签 + +使用言包安装兼容的 1.x: ```sh -yanbao add db --package 言库 --version "^0.1" +yanbao add db --package 言库 --version "^1.0" +``` + +需要可复现来源时,在应用清单中同时固定公开附注标签和兼容版本范围: + +```toml +[依赖] +言库 = { git = "https://github.com/yanxulang/yanxu-db.git", 修订 = "v1.0.0", 版 = "^1.0" } ``` -言库本身没有传递依赖,也不申请文件、网络、进程或原生扩展权限。具体驱动能力由顶层应用授权。 +修改清单后应重新生成并提交格式 2 锁文件;发布和 CI 应验证离线锁,不要依赖可变的默认分支。 -## 协议层 +## 核心对象与驱动选择 -| 协议 | 责任 | +| 对象 | 责任 | | --- | --- | -| `方言协议` | 占位符、标识引用、分页和布尔字面 | -| `查询结果协议` | 全部、首行、标量、数量 | -| `预编译协议` | 保存 SQL 模板并重复执行/查询 | -| `事务协议` | 执行、查询、保存点、提交和回滚 | -| `连接协议` | 执行、查询、预编译、事务与关闭 | -| `连接池协议` | 取得与归还连接、状态和关闭 | -| `驱动协议` | 从配置建立连接 | +| `数据库配置` | 保存驱动、地址、主机、端口、数据库、用户、TLS 与选项 | +| `驱动注册表` / `数据库` | 显式注册驱动并按配置建立连接,不使用隐式全局状态 | +| `驱动能力` | 报告事务、保存点、原生结构、路径查询、取消等真实能力 | +| `连接协议` / `预编译协议` | 参数化执行、查询、预编译、事务与生命周期 | +| `查询结果` / `数据库行` | 行、列元数据、影响行数、最后插入号与稳定列访问 | +| `事务协议` | 手动、托管、保存点、隔离、只读、超时与提交后钩子 | +| `连接池` | 有界等待、健康检查、空闲/寿命淘汰和优雅关闭 | -库或领域服务应在参数中接收协议实例,不要在内部创建厂商驱动。这样测试可以替换执行器,部署也可以更换数据库。 - -## 方言与参数绑定 +生产应用通常直接使用具体驱动的 `打开配置`。需要依赖注入或同时管理多个驱动时,可使用注册表: ```yanxu 引「包:言库」为 言库; -定 方言 为 言库.PostgreSQL方言(); -定 参数 为 言库.参数表(方言); - -定 姓名位 为 参数.加入(「子衿」); # $1 -定 状态位 为 参数.加入(真); # $2 +定 注册表 为 言库.驱动注册表() + .注册(言库.PostgreSQL驱动(执行器工厂)); -定 SQL 为 「SELECT * FROM 」 - 加 方言.引名(「用户」) - 加 「 WHERE 姓名 = 」加 姓名位 - 加 「 AND 启用 = 」加 状态位; +定 配置 为 言库.数据库配置(「postgresql」) + .主机(「db.internal」) + .端口(5432) + .数据库(「app」) + .用户(「service」) + .TLS(「验证完整」) + .应用名(「api」); -定 结果 为 连接.查询(SQL,参数.参数()); +定 连接 为 言库.数据库(注册表).连接(配置); ``` -| 方言 | 占位符示例 | 标识引号 | -| --- | --- | --- | -| SQLite | `?` | `"name"` | -| PostgreSQL | `$1`、`$2` | `"name"` | -| MySQL | `%s` | `` `name` `` | +这里的 `执行器工厂` 必须由真实驱动提供。应用不应自行拼装认证、TLS 或数据库线协议;测试替身可以使用 `函数连接`。 -`参数表` 只生成占位符并保留独立参数列,不把值拼进 SQL。标识名不能参数化,应先通过模型或白名单选择,再调用 `引名`。 +当前稳定驱动是:[言舟(SQLite)](/ecosystem/libraries/sqlite/)、[言库·象城(PostgreSQL)](/ecosystem/libraries/postgres/)和[言库·海豚(MySQL/MariaDB)](/ecosystem/libraries/mysql/)。上层代码应先检查 `连接.能力().支持(名称)`,不要假定三个后端行为相同。 -## 查询结果 +## 参数化 SQL 与标识符 -驱动响应会归一为 `查询结果`: +SQL 模板和值参数始终分开: ```yanxu -定 结果 为 连接.查询(「SELECT id, name FROM users」,【】); +定 模板 为 言库.SQL模板( + 「SELECT id, name FROM users WHERE status = $1 AND age >= $2」 +); -言 结果.全部(); -言 结果.首行(); -言 结果.标量(); -言 结果.数量(); -言 结果.是否为空(); +定 结果 为 连接.查询模板(模板,【「active」,18】); +定 首行 为 结果.首行对象(); +言 首行.取(「name」); ``` -结果还保留 `列名`、`影响行数`、`末插入号` 和 `元数据`,`转典()` 可用于日志或测试,`映射(变换)` 可把每一行转换为领域值。 +| 方言 | 值占位符 | 标识引用 | +| --- | --- | --- | +| SQLite | `?` | `"name"` | +| PostgreSQL | `$1`、`$2` | `"name"` | +| MySQL/MariaDB | `%s` | `` `name` `` | -## 函数连接与驱动适配 +参数只能代表值,不能代表表名、列名、schema、排序方向或操作符。动态标识符必须先经过业务白名单,再交给所选驱动的 `方言().引名(名称)`,或对应的 `言库.PostgreSQL方言()` 等方言对象;引用只解决 SQL 语法转义,不能代替对象授权。 -最小执行器接收 `{动作, SQL, 参数, 驱动}`,返回 `查询结果` 或响应典: +确实需要执行厂商管理语句时,使用带用途说明的显式边界: ```yanxu -法 执行器(请求:典):典 则 - 归 { - 「成功」:真, - 「列名」:【「问候」】, - 「行」:【{「问候」:「你好」}】, - 「影响行数」:0, - 「末插入号」:空, - 「元数据」:{} - }; -终 - -定 连接 为 言库.函数连接(「示例」,言库.SQLite方言(),执行器); -言 连接.查询(「SELECT ? AS 问候」,【「你好」】).首行(); +连接.执行原始(言库.原始SQL(「VACUUM」,「维护窗口空间整理」)); ``` -为可配置驱动提供“配置到执行器”的工厂,再使用 `SQLite驱动`、`PostgreSQL驱动` 或 `MySQL驱动` 包装: +`原始SQL` 不会自动变安全。正文只能来自受审计源码,不得包含请求文字、密码、令牌或个人数据。 + +## 预编译、结果与资源生命周期 ```yanxu -定 驱动 为 言库.PostgreSQL驱动(执行器工厂); -定 连接 为 驱动.连接({「地址」:「postgres://...」}); +定 语句 为 连接.预编译模板( + 言库.SQL模板(「SELECT id FROM users WHERE email = $1」) +); + +定 已绑定 为 语句.绑定(【「user@example.test」】); +定 结果 为 已绑定.查询(); + +言 结果.全部(); +言 结果.列元数据(); +言 结果.影响行数; +言 结果.末插入号; + +语句.关闭(); ``` -## 事务与保存点 +`全部()` 保留驱动行表示;需要稳定列名和列序号访问时使用 `行对象()`、`首行对象()`与 `列元数据()`。重复列名、缺列或列表行宽度不一致会返回明确错误,不会静默覆盖。 -```yanxu -定 事务 为 连接.开始事务(); -试 则 - 事务.执行(「UPDATE accounts SET balance = balance - ? WHERE id = ?」,【10,1】); +预编译绑定会复制参数。语句关闭后,原语句和已有绑定都拒绝执行;具体原生驱动的语句也不能跨连接或在池租约释放后继续使用。 - 定 内层 为 事务.保存点(); - 内层.执行(「INSERT INTO audit(message) VALUES (?)」,【「扣款」】); - 内层.提交(); +## 事务与保存点 - 事务.提交(); -救 所误 则 - 若 事务.是否活跃() 则 事务.回滚();终 - 抛 所误; +托管事务在回调成功时提交,抛错时回滚: + +```yanxu +法 更新账户(事务) 则 + 定 账户 为 事务.查询(查询SQL,【42】).首行对象(); + 事务.执行(更新SQL,【账户.取(「id」)】); + 事务.提交后(提交后通知); + 归 账户; 终 + +定 账户 为 连接.托管事务( + 更新账户, + 言库.事务选项() + .隔离(「可串行化」) + .只读(假) + .超时(3000) + .标识(「account-42」) +); ``` -嵌套事务映射为保存点,必须后进先出地提交或回滚。连接关闭时如果仍有活动事务,会先回滚。 +嵌套工作必须从当前事务调用 `保存点()` 或 `事务()`,并按后进先出结束。只读事务拒绝写入与言据导入。提交后钩子只在最外层提交完成后运行;钩子失败不代表数据库提交已回滚。 + +测试可用 `事务选项().完成后回滚(真)`:回调结果正常返回,但数据库变化回滚且不执行提交后钩子。手动事务则必须在每条控制路径明确 `提交()` 或 `回滚()`。 ## 连接池 ```yanxu -定 池 为 言库.连接池(连接工厂,8); +定 池配置 为 言库.连接池配置() + .最小(2) + .最大(20) + .取得超时(2000) + .空闲超时(300000) + .最大生命周期(1800000) + .健康间隔(30000) + .等待上限(100) + .关闭超时(10000); + +定 池 为 言库.连接池(新连接,池配置); 定 租约 为 池.取得(); + 试 则 言 租约.查询(「SELECT 1」,【】).标量(); + 租约.释放(); 救 所误 则 租约.释放(); 抛 所误; 终 -租约.释放(); + +池.关闭平缓(10000); +``` + +租约必须在所有路径释放。池会剔除关闭、健康失败、空闲超时或寿命到期的连接,并补足最小连接数。有等待额度时池执行有界等待;队列满、取得超时和优雅关闭超时分别返回稳定错误。池不会自动重放可能已经提交的写操作。 + +测试可以注入言时虚拟时钟或使用 `虚拟连接池`,无需真实等待。 + +## 反射与迁移边界 + +言库没有伪造一个“所有数据库都相同”的反射或迁移 API。厂商目录、锁和 DDL 事务语义由驱动实现: + +| 后端 | 反射入口 | 迁移边界 | +| --- | --- | --- | +| SQLite / 言舟 | `表清单()`、`表结构()`、列/索引/外键信息 | `BEGIN IMMEDIATE` 写锁内校验并原子登记 | +| PostgreSQL / 象城 | schema/表清单、`表结构(模式, 名称)` | advisory transaction lock,DDL 与登记位于事务内 | +| MySQL/MariaDB / 海豚 | 数据库/表清单、`表结构(模式, 名称)` | `GET_LOCK` 命名锁;DDL 隐式提交,不承诺整体回滚 | + +应用可直接使用驱动的版本化迁移器。需要模型反射、开发期安全加法计划、SQL/代码/言据动作迁移和种子时,使用[言映](/ecosystem/libraries/orm/)。无论选择哪一层,生产流程都应先检查、干运行、备份并在精确数据库版本演练。 + +## 超时、取消、日志与指标 + +```yanxu +定 令牌 为 言库.取消令牌(); +定 选项 为 言库.执行选项() + .超时(500) + .取消令牌(令牌) + .请求标识(「query-1」); + +定 结果 为 连接.查询带选项(SQL,参数,选项); +``` + +同步核心只能在驱动调用前后检查令牌与截止时间,不能强制抢占任意同步执行器。只有驱动明确声明 `查询取消` 并安装原生取消器后,`连接.取消(请求标识)` 才能取消正在执行的数据库请求。 + +言录适配默认只记录驱动、动作、SQL 模板、参数数量与类型、耗时、影响行数、请求/事务标识、慢查询和错误代码,不记录参数值。`数据库配置.转安全典()` 用于诊断脱敏;建立真实连接必须使用保留凭据的 `转驱动配置()`。 + +## 言据:规范文本与原生结构 + +这两种策略不能混用: + +| 策略 | 数据库列 | 写入内容 | 适用场景 | +| --- | --- | --- | --- | +| `规范文本` | TEXT/CLOB | 可严格复原的规范 `.yj` 文本 | 跨数据库交换、逐字稳定存储 | +| `原生结构` | PostgreSQL JSONB、MySQL JSON、SQLite JSON1 文本 | 言序普通值,由驱动映射 | 数据库路径查询和原生索引 | + +```yanxu +定 文本 为 言库.言据写入值(资料,「规范文本」,连接.能力()); +定 还原 为 言库.言据读取值(文本,「规范文本」); +``` + +普通 JSON 文本不是规范言据文本。选择 `原生结构` 前必须通过驱动能力门禁,读回时也必须按同一策略恢复言序值。 + +查询结果可导出为带 `yanxu-db/rows` 格式标识的规范言据信封,并严格参数化导入: + +```yanxu +定 正文 为 连接.导出言据( + 言库.言据导出配置(「SELECT id, profile FROM users」).最大行(10000) +); + +定 摘要 为 连接.导入言据( + 正文, + 言库.言据导入配置(「archive.users」).批量(100).要求规范(真) +); ``` -租约释放是幂等的;释放后再次访问会报错。达到最大借出数时,当前同步池明确返回耗尽错误,不隐式阻塞。`状态()` 返回最大、空闲、借出和关闭状态。 +默认导入使用单一事务并严格检查列。核心不申请文件权限;读写 `.yj` 文件、对象存储或流,应通过 `导出言据到` / `导入言据自` 注入由应用授权的回调。 + +## 权限与安全责任 + +言库 1.0.0 自身固定为零权限: + +```toml +[权限] +文件 = [] +网络 = [] +TCP监听 = [] +UDP绑定 = [] +环境 = [] +进程 = false +原生扩展 = false +``` -## 结构化错误 +具体驱动与顶层应用负责网络或文件目标、TLS、凭据和原生扩展权限。言库不验证驱动客户端的密码学或内存安全,也不提供身份认证、行级授权、备份或数据库审计存储。 -执行器抛错或返回 `{成功: 假}` 时,言库生成 `YANKU_` 前缀错误,并保留操作、SQL、参数、驱动和原始原因。`安全执行` 把错误转换成 `{成功, 结果, 错误}`,适合无需异常控制流的应用边界。 +## 兼容性与明确限制 -SQL 参数可能包含敏感信息;记录数据库错误时应使用[言录](/ecosystem/libraries/log/)脱敏,不要直接打印完整参数列。 +- 1.x 保持公开类型、法名、配置键、结果信封、错误代码和默认安全边界;新增可选能力属于兼容变化。 +- 单条 SQL 最多 1 MiB,参数最多 65,536 个;注册驱动最多 64 个。 +- 言据交换最多 100,000 行、1,024 列和 16 MiB 正文;单批导入最多 1,000 行。 +- 连接池最多 10,000 个连接、100,000 个等待者;提交后钩子最多 128 个。 +- 通用层不保证查询抢占、自动重试、厂商迁移、schema 反射或数据库 JSON 与言据文本等价;这些能力必须由具体驱动明确实现并报告。 +- SQLite、PostgreSQL 与 MySQL/MariaDB 的 `RETURNING`、数组、JSON、DDL 事务和取消语义不同,跨库代码必须按能力分支并在目标数据库版本上集成测试。 -仓库:[yanxulang/yanxu-db](https://github.com/yanxulang/yanxu-db)。 +错误以 `YANKU_` 稳定代码分类。仓库与 1.0.0 Release:[yanxulang/yanxu-db](https://github.com/yanxulang/yanxu-db/releases/tag/v1.0.0)。 diff --git a/content/docs/ecosystem/libraries/mysql.mdx b/content/docs/ecosystem/libraries/mysql.mdx new file mode 100644 index 0000000..b31109a --- /dev/null +++ b/content/docs/ecosystem/libraries/mysql.mdx @@ -0,0 +1,282 @@ +--- +title: 言库·海豚:MySQL 与 MariaDB 驱动 +description: 连接 MySQL 8.x 与 MariaDB 10.x/11.x,使用 TLS、参数化查询、事务、连接池、反射、迁移与 JSON 言据路径。 +--- + +言库·海豚(包名 `言库海豚`,仓库 `yanxu-mysql`)是言库 1.x 的 MySQL/MariaDB 原生驱动。它通过言序 ABI v2 和成熟的 Rust MySQL 协议实现直接连接数据库,提供 TLS、预编译、事务、连接池、取消、结构反射、迁移和 JSON/言据集成。 + +| 项目 | 1.0 稳定边界 | +| --- | --- | +| 当前版本 | `1.0.0` | +| 最低言序 | `1.1.12` | +| MySQL | 8.x | +| MariaDB | 10.x、11.x | +| 稳定依赖 | 言库 `v1.0.0` / `^1.0` | +| 原生 ABI | v2 | +| 运行方式 | 字节码 VM、包运行或 YXB;树解释器不能载入 | + +## 安装并固定公开标签 + +```sh +yanbao add mysql --package 言库海豚 --version "^1.0" +``` + +可复现生产清单: + +```toml +[依赖] +言库海豚 = { git = "https://github.com/yanxulang/yanxu-mysql.git", 修订 = "v1.0.0", 版 = "^1.0" } + +[权限] +网络 = ["db.example:3306"] +原生扩展 = true +``` + +依赖不会替顶层应用授权。若配置自定义 CA 文件,还要只对该文件授予读取权限。 + +## 建立安全连接 + +```yanxu +引「包:言库海豚」为 海豚; + +定 配置:典 为 { + 「主机」:「db.example」, + 「端口」:3306, + 「数据库」:「account」, + 「用户」:「account-service」, + 「密码」:安全配置【「数据库密码」】, + 「TLS」:「验证完整」, + 「选项」:{ + 「连接超时毫秒」:5000, + 「读取超时毫秒」:10000, + 「写入超时毫秒」:10000 + } +}; + +定 数据库 为 海豚.打开配置(配置); +``` + +也可传 `mysql://` 地址,再用分离的主机、端口、数据库、用户或密码覆盖。连接建立后驱动执行 `SET NAMES utf8mb4` 和 `SET time_zone = '+00:00'`。 + +| TLS 模式 | 行为 | +| --- | --- | +| `验证完整` | 默认;要求 TLS,验证证书链和连接主机名 | +| `要求` | 与 `验证完整` 使用相同的完整验证 | +| `优先` | 仅在服务端明确不支持 TLS 时允许降级;坏证书、错误 CA 或主机名不匹配不会降级 | +| `禁用` | 明文;仅限隔离本机服务 | + +`根证书路径` 可放在顶层或 `选项` 中。`打开(...)` 默认完整验证;`打开本地(...)` 固定 `127.0.0.1` 与明文,只适合受控测试。1.0 不支持客户端证书/私钥认证或跳过证书验证。 + +## 参数化查询与预编译 + +公共代码优先使用言库 MySQL 方言的 `%s`: + +```yanxu +数据库.执行( + 「CREATE TABLE users(id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(64) NOT NULL, profile JSON NOT NULL)」, + 【】 +); + +定 插入 为 数据库.预编译( + 「INSERT INTO users(name, profile) VALUES(%s, %s)」 +); + +插入.执行(【「子衿」,{「等级」:10}】); +言 插入.参数数(); +插入.关闭(); + +定 用户 为 数据库.查询( + 「SELECT id, name, profile FROM users WHERE name = %s」, + 【「子衿」】 +).首行(); +``` + +原生 `?` 占位符也可使用。驱动只规范化可执行 SQL 上下文中的 `%s`,不会改写引号、反引号标识符或注释内文字;值随后通过服务端预编译协议绑定,不拼入 SQL。 + +参数不能代表表名、列名、数据库名、排序方向或操作符。动态标识符必须先经业务白名单,再使用 `数据库.方言().引名(名称)`。`执行原始` / `查询原始` 只接受受审计迁移或框架 SQL。 + +预编译语句和 `绑定(参数)` 对象都属于创建它们的物理连接。连接关闭或池租约释放后,子资源立即失效。重复结果列名会返回 `MYSQL_COLUMN_DUPLICATE`;联表列应使用唯一别名。 + +## 类型映射与 MariaDB JSON 边界 + +| MySQL/MariaDB 类型 | 言序读取值 | 1.0 写入边界 | +| --- | --- | --- | +| 整数、`YEAR`、`BIT` | 数;超安全整数为文 | 整数数或数值文 | +| `FLOAT/DOUBLE` | 数;非有限值为文 | 有限数 | +| `DECIMAL/NUMERIC` | 保精度十进制文 | 数或十进制文 | +| 字符与文本 | 文 | UTF-8 文 | +| 二进制与 BLOB | 字节 | 字节 | +| MySQL `JSON` | 结构化言序值 | 空、理、数、文、列、典 | +| MariaDB 持久基表 `JSON` | 经约束确认后为结构化值 | 同上 | +| 日期时间 | 稳定 ISO 风格文 | 服务端接受的日期时间文 | +| `TIME` | 含符号与微秒的时段文 | 服务端接受的时间文 | + +`TINYINT(1)` 仍按整数处理,驱动不会根据显示宽度猜测布尔。MariaDB 的 `JSON` 是带 `JSON_VALID` 约束的 `LONGTEXT` 别名;只有结果列可追溯到持久基表且目录确认该约束时才结构化解码。临时表、视图、无源表达式或普通 BLOB 保留线路层文字/字节,即使内容看起来像 JSON,也不做启发式解析。 + +## 事务、保存点、超时与取消 + +```yanxu +引「包:言库」为 言库; + +法 转账(事务)则 + 事务.执行( + 「UPDATE accounts SET balance = balance - %s WHERE id = %s」, + 【10,甲】 + ); + + 定 保存点 为 事务.保存点(); + 保存点.执行( + 「UPDATE accounts SET balance = balance + %s WHERE id = %s」, + 【10,乙】 + ); + 保存点.提交(); + 归 「已提交」; +终 + +定 结果 为 数据库.托管事务( + 转账, + 言库.事务选项() + .隔离(「可重复读」) + .只读(假) + .超时(5000) +); +``` + +支持的隔离名是 `读未提交`、`读已提交`、`可重复读` 和 `串行化`。回调成功提交、抛错回滚;同一连接只允许一个顶层事务,嵌套工作使用保存点。手动事务必须在所有路径结束。 + +MySQL/MariaDB 的许多 DDL 会隐式提交,因此“事务回滚成功”不能作为多条 DDL 整体原子的证明。 + +单次操作可设超时与请求标识: + +```yanxu +定 选项 为 言库.执行选项() + .超时(2000) + .请求标识(「report-20260718」); + +定 结果 为 数据库.查询带选项(SQL,参数,选项); +``` + +`数据库.取消(同一标识)` 通过独立管理连接发送 `KILL QUERY`,只匹配当前活动请求。超时后主连接会重建;取消竞态无法确认时物理连接会被丢弃。取消写请求不证明服务端未提交,应用必须使用幂等键、唯一约束、事务与业务对账。 + +## 连接池 + +```yanxu +定 池配置 为 言库.连接池配置() + .最小(2) + .最大(16) + .取得超时(2000) + .空闲超时(300000) + .最大生命周期(3600000) + .健康间隔(30000) + .等待上限(64); + +定 连接池 为 海豚.创建连接池(配置,池配置); +定 租约 为 连接池.取得(); + +言 租约.查询(「SELECT CURRENT_TIMESTAMP AS current_time」,【】).首行(); +租约.释放(); +连接池.关闭平缓(10000); +``` + +池检查健康、空闲时间和生命周期,并剔除协议状态可疑的连接。它不会自动重放失败写请求;租约释放后不得继续使用其事务、预编译语句或绑定对象。 + +## 结构反射 + +```yanxu +逐 数据库名 于 数据库.模式清单() 则 + 言 数据库名; +终 + +定 结构 为 数据库.当前表结构(「users」); +若 结构【「存在」】 则 + 言 结构【「列」】; + 言 结构【「约束」】; + 言 结构【「索引」】; + 言 结构【「外键」】; +终 +``` + +反射通过 `information_schema` 覆盖数据库、表、视图、列、主键、检查/唯一/外键约束、复合索引与外键动作。数据库名与表名作为参数传入目录查询;缺失对象返回 `存在 = 假` 的稳定空结构。 + +## 版本化迁移 + +```yanxu +定 各迁移 为 【 + 海豚.迁移( + 1, + 「创建用户」, + 「CREATE TABLE users(id BIGINT PRIMARY KEY) ENGINE=InnoDB」, + 「DROP TABLE users」 + ), + 海豚.迁移( + 2, + 「增加姓名」, + 「ALTER TABLE users ADD COLUMN name VARCHAR(64)」, + 「ALTER TABLE users DROP COLUMN name」 + ) +】; + +定 迁移器 为 海豚.默认迁移器(数据库); +言 迁移器.检查(各迁移); +言 迁移器.干运行升级(各迁移); +迁移器.升级(各迁移); +``` + +默认登记表为 `_yanxu_migrations`,迁移器使用连接级 `GET_LOCK` 命名锁,锁内重读严格前缀历史。版本、名称和双向 SQL 进入 SHA-256;缺失定义、名称/内容漂移和不可逆回退会拒绝。 + +MySQL/MariaDB DDL 通常隐式提交,迁移器不声称 DDL 与登记整体原子。失败后,已成功执行并登记的前缀保留,当前失败 DDL 也可能留下结构效果;必须审计实际结构、登记与备份,再决定修复或回退。发布迁移应在 MySQL 和 MariaDB 上分别演练。旧空校验和只能在人工核对后显式采用。 + +## 言据与原生 JSON + +| 策略 | 数据库列 | 语义 | +| --- | --- | --- | +| `规范文本` | TEXT/LONGTEXT | 规范 `.yj` 文本,适合跨方言稳定交换 | +| `原生结构` | MySQL JSON / 经确认的 MariaDB JSON 别名 | 结构化值,适合数据库路径条件 | + +```yanxu +定 写入值 为 数据库.言据写入值(资料,「原生结构」); +数据库.执行(「INSERT INTO profiles(data) VALUES(%s)」,【写入值】); + +定 条件 为 数据库.言据路径(「data」,【「等级」】).至少(10); +定 各行 为 数据库.查询( + (「SELECT data FROM profiles WHERE 」 加 条件.SQL), + 条件.参数() +).全部(); +``` + +路径和比较值参数化,列名由方言引用。`JSON索引建议` 不返回可直接执行的通用 DDL,因为生成列类型、长度和排序规则必须由业务评审;索引应写入受审计迁移。数据库 JSON 不是规范言据文本。 + +## 真实支持矩阵 + +### 数据库服务 + +| 产品与版本 | 支持状态 | 1.0 发布证据边界 | +| --- | --- | --- | +| MySQL 8.0 | 支持 | 发布 CI 真实服务生命周期门禁 | +| MySQL 8.4 | 支持、最高已验证 MySQL 主版本 | 发布 CI 完整明文/TLS/错误证书/源码与 YXB;全生态实测 8.4.10 | +| MariaDB 10.x | 支持 | 10.11 进入发布 CI 真实服务生命周期门禁 | +| MariaDB 11.x | 支持、最高已验证 MariaDB 主版本 | 11.8 进入完整发布门禁;全生态实测 11.8.8 | + +这里的主版本范围是驱动协议承诺。应用仍应在自己的精确补丁、SQL mode、字符集、排序规则、时区、存储引擎和集群配置上执行类型往返、迁移与故障测试。 + +### 原生目标 + +| 操作系统 | x86-64 | ARM64 | 发布门禁 | +| --- | --- | --- | --- | +| macOS | 支持 | 支持 | Mach-O、ABI、安装名、签名与原生消费者 | +| Linux glibc 2.17+ | 支持 | 支持 | ELF、ABI、动态依赖与原生消费者 | +| Windows | 支持 | 支持 | PE、ABI、静态 CRT 与原生消费者 | + +制品路径、大小与 SHA-256 以 `v1.0.0` 清单为准。缺失目标应明确失败,不得回退加载其他架构制品。 + +## 权限、安全边界与明确限制 + +- 顶层应用需授权精确数据库网络目标和原生扩展;自定义 CA 路径还需要最小文件读取权限。 +- 原生层固定拒绝 `LOAD DATA LOCAL INFILE`,避免服务端请求读取客户端本地文件。 +- SQL 最大 1 MiB、参数最多 65,535 个;单次结果最多 100,000 行、1,024 列和 16 MiB,返回前完整解码。 +- 1.0 不提供异步逐行流、自动读写分离、客户端负载均衡、客户端证书认证或自动断线重放。 +- 驱动不声明 PostgreSQL 式 `RETURNING`、数组或只读副本路由。 +- MariaDB 临时表、视图与无源表达式不能可靠识别 JSON 别名;保留文字/字节是明确安全行为。 +- 默认日志不记录参数值、密码、完整 URL 或 CA 内容;自定义观测实现仍须脱敏。 + +厂商错误归一为 `YANKU_*`,海豚边界使用 `MYSQL_*` / `YANHAITUN_*` 稳定代码。仓库与 1.0.0 Release:[yanxulang/yanxu-mysql](https://github.com/yanxulang/yanxu-mysql/releases/tag/v1.0.0)。 diff --git a/content/docs/ecosystem/libraries/orm.mdx b/content/docs/ecosystem/libraries/orm.mdx index a780ed8..97940ed 100644 --- a/content/docs/ecosystem/libraries/orm.mdx +++ b/content/docs/ecosystem/libraries/orm.mdx @@ -1,127 +1,325 @@ --- title: 言映:ORM -description: 显式定义模型与关系,构建参数化查询,追踪实体变更并在事务单元中提交。 +description: 使用显式模型、参数化查询、关系预加载、脏追踪、事务单元、反射、版本化迁移与言据字段。 --- -言映(`yanxu-orm`)以显式模型、字段和连接对象代替魔法全局状态。它使用言库的连接/方言协议生成 SQL,并复用言验规则验证字段。 +言映(包名 `言映`,仓库 `yanxu-orm`)是言序的显式模型映射与数据库工作流库。它使用言库的连接、方言和事务协议执行 SQL,复用言验进行字段验证,并把模型、查询、关系、迁移和种子保留为可组合对象;它不会隐式打开连接,也没有全局事务状态。 -## 安装 +| 项目 | 1.0 稳定边界 | +| --- | --- | +| 当前版本 | `1.0.0` | +| 最低言序 | `1.1.12` | +| 稳定依赖 | 言据 `v1.2.0`、言库/言验/言令 `v1.0.0` | +| 数据库 | SQLite、PostgreSQL、MySQL、MariaDB | +| 核心实现 | 纯言序、零权限;连接与原生能力来自驱动 | +| 许可 | MIT | + +## 安装并固定公开标签 + +先安装 ORM,再显式选择驱动: ```sh -yanbao add orm --package 言映 --version "^0.1" +yanbao add orm --package 言映 --version "^1.0" +yanbao add sqlite --package 言舟 --version "^1.0" ``` -言包会自动安装言库与言验。应用仍需选择具体驱动,例如: +可复现的 SQLite 应用清单: -```sh -yanbao add sqlite --package 言舟 --version "^0.1" +```toml +[依赖] +言映 = { git = "https://github.com/yanxulang/yanxu-orm.git", 修订 = "v1.0.0", 版 = "^1.0" } +言舟 = { git = "https://github.com/yanxulang/yanxu-sqlite.git", 修订 = "v1.0.0", 版 = "^1.0" } +``` + +其他稳定驱动: + +```toml +# PostgreSQL:二选一,不要和下面一项同时作为同一连接使用 +言库象城 = { git = "https://github.com/yanxulang/yanxu-postgres.git", 修订 = "v1.0.0", 版 = "^1.0" } + +# MySQL / MariaDB +言库海豚 = { git = "https://github.com/yanxulang/yanxu-mysql.git", 修订 = "v1.0.0", 版 = "^1.0" } ``` -## 定义模型和字段 +更新依赖后重新生成并提交格式 2 锁文件。连接配置、TLS、连接池、取消和驱动原生制品由[言库](/ecosystem/libraries/db/)及所选驱动管理。 + +## 定义模型与字段 ```yanxu 引「包:言映」为 言映; 引「包:言验」为 言验; -定 用户 为 言映.模型(「用户」,「users」) +定 文章 为 言映.定义模型(「文章」,「posts」) .加字段(言映.自增主键(「编号」).映射(「id」)) - .加字段( - 言映.文字字段(「姓名」) - .映射(「name」) - .用规则(言验.文本().至少(2).至多(50)) - ) - .加字段(言映.文字字段(「邮箱」).映射(「email」).设唯一().设可空()); -``` + .加字段(言映.大整数字段(「作者号」).映射(「author_id」)) + .加字段(言映.文字字段(「标题」).映射(「title」).不可空()); -字段工厂包括 `文字字段`、`整数字段`、`小数字段`、`布尔字段`、`日期字段`、`JSON字段` 和 `自增主键`。链式配置包括: +定 用户 为 言映.定义模型(「用户」,「users」) + .加字段(言映.自增主键(「编号」).映射(「id」)) + .加字段(言映.文字字段(「邮箱」) + .映射(「email」) + .长度(3,254) + .唯一() + .用规则(言验.文本().至少(3))) + .加字段(言映.言据字段(「资料」) + .映射(「profile」) + .大小限制(262144) + .存储为(「原生结构」)) + .加字段(言映.文字字段(「密码摘要」) + .映射(「password_hash」) + .脱敏()) + .启用时间戳(「创建时间」,「更新时间」) + .启用软删除(「删除时间」) + .启用乐观锁(「版本」) + .加关系(言映.一对多(「文章」,文章,「编号」,「作者号」)); +``` -- `映射(列名)`; -- `设主键()`、`设自增()`; -- `设可空()`、`设唯一()`; -- `默认(值)`; -- `用规则(言验规则)`。 +1.0 提供 20 个字段工厂,包括整数、小数、布尔、文字、字节、日期时间、UUID、枚举、JSON、言据、虚拟、自定义字段与自增主键。支持单/复合主键、唯一、可空、默认、数据库默认、检查、外键、复合索引、自定义编解码和映射列名。 -模型提供字段/关系列表与查找、主键读取、行到实体转换、数据验证和按方言生成建表 SQL。 +`数据库默认`、`检查`、`SQL类型为` 和自定义编解码器属于受信任代码边界,只能接收受审计常量,不能直接接收请求文字。 -## 关系 +## 创建、查询与参数安全 ```yanxu -定 文章 为 言映.模型(「文章」,「posts」) - .加字段(言映.自增主键(「编号」).映射(「id」)) - .加字段(言映.整数字段(「作者号」).映射(「user_id」)) - .加字段(言映.文字字段(「标题」).映射(「title」)); +定 新用户 为 用户.创建({ + 「邮箱」:「zijin@example.test」, + 「资料」:{「等级」:2,「标签」:【「新用户」】}, + 「密码摘要」:「已散列值」 +},数据库); + +定 活跃用户 为 言映.查询(用户,数据库) + .条件式(言映.且式( + 言映.字段(「编号」).至少(1), + 言映.字段(「邮箱」).文字结尾(「@example.test」) + )) + .选择(【「编号」,「邮箱」,「资料」】) + .排序(「编号」,「DESC」) + .分页(1,20); -用户.加关系(言映.一对多(「文章」,文章,「编号」,「作者号」)); +言 活跃用户.项目; +言 活跃用户.总数; +言 活跃用户.有下一页(); ``` -支持: +普通条件、IN、区间、文字条件、关系父键与言据路径值都进入参数列。字段先在模型中解析,再由方言安全引用;运算符与排序方向使用固定白名单。参数不能代表表、列、排序或操作符。 -- `一对一(名称, 目标模型, 本地键, 外部键)`; -- `一对多(名称, 目标模型, 本地键, 外部键)`; -- `多对多(名称, 目标模型, 本地键, 目标键, 中间表, 中间本键, 中间目标键)`。 +`查询.SQL()` 与 `查询.参数()` 适合测试生成结果,不应把参数重新拼回 SQL。`原条件(SQL, 参数)` 是显式厂商扩展点:SQL 片段必须来自受信任源码,动态值仍放进参数列。标识引用不能代替行级授权或租户隔离。 -查询的 `预载` 会把父键合并为一次 `IN` 查询;多对多通过一次中间表 JOIN 读取,避免逐实体触发 N+1 查询。 +批量创建使用一个事务单元,逐实体保留验证、钩子、自增键和状态回滚语义;任何项失败会回滚整批,单次最多 1,000 项。它不是 PostgreSQL `COPY` 或数据库专有 bulk loader。 -## 查询构建器 +## 关系与有界预加载 ```yanxu -定 首页 为 言映.查询(用户,连接) - .选择(【「编号」,「姓名」,「邮箱」】) - .等值(「邮箱」,「zijin@example.test」) - .其中(「编号」,【1,2,3】) - .排序(「编号」,「DESC」) - .预载(「文章」) - .分页(1,20); +文章.加关系(言映.属于关系(「作者」,用户,「作者号」,「编号」)); + +定 文章加载 为 言映.关联(「文章」) + .选择(【「编号」,「标题」】) + .排序(「编号」,「ASC」) + .必需(); + +定 各用户 为 言映.查询(用户,数据库) + .包含(文章加载) + .全部(); +``` + +支持 `属于关系`、一对一、一对多和多对多。预加载会合并父键后执行有界 `IN` 查询,多对多使用中间表 JOIN;嵌套 `包含` 也按层批量处理,避免每个父实体一次查询。 + +关系不会因普通字段读取而隐式访问数据库。单实体延迟加载必须显式调用 `实体.加载关联(名称, 连接)`;在循环里反复显式延迟加载仍可能形成 N+1。 -言 首页.项目; -言 首页.总数; -言 首页.有下一页(); +## 实体、脏追踪与并发 + +```yanxu +定 所用户 为 用户.按主键(编号,数据库,假); + +所用户.设(「邮箱」,「new@example.test」); +置 所用户.取(「资料」)【「等级」】 为 4; + +言 所用户.脏字段(); +言 所用户.变更(); +所用户.保存(数据库); + +所用户.删除(数据库); +所用户.恢复(数据库); ``` -可用方法包括 `选择`、`条件`、`等值`、`其中`、`原条件`、`排序`、`限量`、`偏移`、`预载`、`全部`、`首个`、`计数` 和 `分页`。 +实体保存当前值和原始快照。言据字段按规范序列化结果比较,因此嵌套典或列的原地修改也会被识别。持久化成功后刷新快照;事务失败会恢复数据库和内存事务快照。 + +启用软删除后,普通根查询与预加载都排除已删除记录;绕过必须显式使用 `包含已删除`、`只查已删除` 或 `物理删除`。启用乐观锁后,原版本进入 UPDATE 条件且成功时递增;影响零行返回 `YANYING_OPTIMISTIC_LOCK`,不会静默覆盖并发写。 + +`.脱敏()` 只让 `实体.转日志典()` 输出 `***`;`各值()` 和 `转普通对象()` 仍返回业务值,应用不能把它当作字段级加密。 -`SQL()` 与 `参数()` 可在执行前检查生成结果。普通条件始终参数化;`原条件(SQL, 参数)` 适合数据库特有表达式,但 SQL 片段必须来自受信任代码,不能直接接收用户输入。 +## 事务与事务单元 -## 实体与脏追踪 +ORM 的“连接”参数可以是真实连接,也可以是实现同一协议的事务对象: ```yanxu -定 实体 为 言映.新实体(用户,{「姓名」:「子衿」}); -实体.设(「邮箱」,「zijin@example.test」); +法 创建文章(事务)则 + 定 用户项 为 用户.创建(用户数据,事务); + 文章.创建({ + 「作者号」:用户项.取(「编号」), + 「标题」:「新篇」 + },事务); + 归 用户项; +终 + +定 结果 为 数据库.事务(创建文章); +``` + +需要集中协调已有实体时使用事务单元: + +```yanxu +定 单元 为 言映.事务单元(数据库); +单元.新增(待新增) + .跟踪(待更新) + .删除(待删除); -言 实体.状态(); -言 实体.脏字段(); -言 实体.变更(); +定 提交摘要 为 单元.提交(); ``` -实体保存当前值和原始快照。`取/设/赋` 访问字段,`脏字段` 与 `变更` 只报告快照之后的变化;`快照` 在成功持久化后重置基线。关系值与字段值分开保存。 +事务单元在同一事务内验证并执行新增、更新、删除、强制删除或恢复;失败时恢复实体主键、快照和删除状态。保存点、隔离级别、只读、超时和提交后钩子由言库与驱动提供,言映不创建不可见全局事务。 -## 模式反射与迁移 +## 连接池边界 + +言映不重复实现连接池。使用象城/海豚的 `创建连接池(配置, 池配置)`,或用言库连接池组合言舟连接工厂;从租约取得底层连接作为 ORM 参数,并在所有路径释放租约: ```yanxu -定 迁移器 为 言映.模式迁移器(连接); -定 计划 为 迁移器.计划(【用户,文章】); +定 租约 为 连接池.取得(); +试 则 + 定 用户项 为 用户.创建(用户数据,租约.连接()); + 租约.释放(); +救 所误 则 + 租约.释放(); + 抛 所误; +终 +``` + +租约释放后,不能继续使用租约或先前取得的底层连接,也不能保留其事务或预编译语句。池的大小、等待上限、健康检查、空闲/寿命淘汰和优雅关闭属于言库/驱动配置;池不会自动重放不确定状态的写请求。 + +## 开发反射与安全模式计划 +```yanxu +定 反射 为 言映.模式反射器(数据库).读取(用户); +言 反射; + +定 同步器 为 言映.模式迁移器(数据库); +定 计划 为 同步器.计划(【用户,文章】); 言 计划.SQL列; 言 计划.警告列; -迁移器.应用(【用户,文章】); +同步器.应用(【用户,文章】); ``` -反射器统一读取 SQLite PRAGMA 与 PostgreSQL/MySQL `information_schema`。自动迁移只执行安全的加法变化:创建缺失表和添加普通列。删除列、修改类型、补建主键等破坏性变化进入警告列,必须人工审查。 +反射器统一读取 SQLite PRAGMA 与 PostgreSQL/MySQL/MariaDB `information_schema`。开发期模式迁移器只创建缺失表和添加普通列;删除列、改不兼容类型、重建主键等破坏性变化只进入警告,不会自动执行。 -生产环境应先生成并评审计划,再在备份和维护窗口中应用;不要把自动迁移当作无条件模式同步器。 +生产环境不得用模式同步替代版本化迁移。SQLite 在线增加外键/检查约束和一般改列也不受支持,应编写重建表的显式迁移。 -## 事务单元 +## 正式迁移、种子与命令 ```yanxu -定 单元 为 言映.事务单元(连接); -单元.新增(言映.新实体(用户,{「姓名」:「子衿」})) - .跟踪(已有实体) - .删除(待删除实体); +定 各迁移 为 【 + 言映.迁移( + 202607180001, + 「创建用户表」, + 「CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)」, + 「DROP TABLE users」 + ) +】; -定 结果 为 单元.提交(); +定 迁移器 为 言映.默认迁移器(数据库); +言 迁移器.状态(各迁移); +言 迁移器.检查(各迁移); +言 迁移器.干运行升级(各迁移); +迁移器.升级(各迁移); ``` -事务单元在同一事务中验证并处理新增、更新和删除;成功后刷新实体快照,失败时回滚并透传错误。它适合一个业务用例的提交边界,不应跨请求长期持有。 +正式迁移支持三种定义: + +- `迁移`:受信任的升级/降级 SQL; +- `代码迁移`:参数化处理器和调用方提供的稳定校验和; +- `动作迁移` / `言据迁移`:建表、列、索引、外键等结构化动作。 + +版本必须严格递增,SHA-256 漂移会阻止继续。SQLite 使用写事务锁,PostgreSQL 使用事务级 advisory lock,MySQL/MariaDB 使用命名锁。锁只协调使用相同登记表与锁号的迁移器。 + +PostgreSQL 与 SQLite 在其支持范围内可把 DDL 和登记放入事务;MySQL/MariaDB DDL 可能隐式提交,失败后必须审计已完成前缀与真实结构,不能宣称整体回滚。 + +言映还提供 SQL 种子与参数化 `数据种子`,按标识、环境和校验和幂等登记;命令行适配器基于言令提供迁移/种子的状态、检查、干运行、执行和回退。生产发布仍须备份、恢复演练并在精确数据库版本运行迁移。 + +## 言据字段:TEXT 与原生结构 + +```yanxu +定 资料模式:典 为 { + 「类型」:「据」, + 「属性」:{「等级」:{「类型」:「数」,「整数」:真}}, + 「必需」:【「等级」】, + 「允许额外」:真 +}; + +定 资料字段 为 言映.言据字段(「资料」) + .Schema(资料模式) + .大小限制(262144) + .存储为(「规范文本」); +``` + +| 策略 | 后端表示 | 适用场景 | +| --- | --- | --- | +| `规范文本`(默认) | 所有后端的 TEXT/CLOB 中保存规范 `.yj` 文本 | 跨数据库保真、严格解析、稳定比较 | +| `原生结构` | PostgreSQL JSONB、MySQL/MariaDB JSON、SQLite JSON1 文本 | 数据库路径查询与原生索引 | + +数据库 JSON 不是规范言据文本。SQLite 的原生结构实际写入 JSON1 可查询文本;MariaDB 只有驱动确认持久基表 `JSON_VALID` 约束后才结构化解码。驱动未声明能力时,`原生结构` 和路径查询会明确失败,不会全表加载到内存模拟。 + +```yanxu +定 高等级 为 言映.查询(用户,数据库) + .条件式(言映.言据路径(「资料.等级」).至少(10)) + .全部(); +``` + +言据字段可设置 Schema 与字节上限;验证保证结构,不代表调用者有权读写该模型、字段或行。 + +## 四数据库兼容矩阵 + +| 后端 | 稳定驱动 | 支持范围 | 1.0 全生态真实验证 | +| --- | --- | --- | --- | +| SQLite | 言舟 1.x | bundled SQLite 3.53.2、JSON1 | SQLite 3.53.2 内存数据库,源码与 YXB | +| PostgreSQL | 言库象城 1.x | PostgreSQL 14–18 | PostgreSQL 18.4,源码与 YXB | +| MySQL | 言库海豚 1.x | MySQL 8.x | MySQL 8.4.10,源码与 YXB | +| MariaDB | 言库海豚 1.x | MariaDB 10.x、11.x | MariaDB 11.8.8,源码与 YXB | + +| 能力 | SQLite | PostgreSQL | MySQL | MariaDB | +| --- | --- | --- | --- | --- | +| 参数占位符 | `?` | `$1…` | `%s` / `?` | `%s` / `?` | +| 自增主键回填 | 最后插入号 | `RETURNING` | 最后插入号 | 最后插入号 | +| 原生结构 | JSON1 文本 | JSONB | JSON | 经约束确认的 JSON 别名 | +| 结构反射 | PRAGMA | `information_schema` / 系统目录 | `information_schema` | `information_schema` | +| 迁移互斥 | 写事务锁 | advisory transaction lock | 命名锁 | 命名锁 | +| 多 DDL 整体事务 | SQLite 支持范围内 | 是 | 否 | 否 | + +支持范围不等于应用的精确补丁、扩展、字符集、排序规则、时区和数据库参数已经验证。上线前必须在实际环境跑迁移、关系、言据路径、并发和回退测试。 + +## 权限与运行方式 + +言映核心固定不申请文件、网络、监听、环境、进程或原生扩展权限: + +```toml +[权限] +文件 = [] +网络 = [] +TCP监听 = [] +UDP绑定 = [] +环境 = [] +进程 = false +原生扩展 = false +``` + +驱动消费者仍须声明实际权限:言舟文件数据库需要路径文件权限,象城/海豚需要目标网络权限,三套原生驱动都需要 `原生扩展 = true`。真实驱动依赖 `标准:原生`,必须通过字节码 VM、包运行或 YXB;ORM 核心测试替身不代表树解释器可以载入真实驱动。 + +## 明确限制与安全非目标 + +- 模型最多 256 个字段、128 个关系;条件深度最多 32;枚举最多 256 项。 +- 言据字段默认上限 1 MiB,硬上限 16 MiB;批量创建单次最多 1,000 项。 +- 连接池、认证、TLS、查询取消、驱动重试、网络超时、日志与指标属于言库和具体驱动。 +- 言映不提供身份认证、行级授权、租户隔离证明、字段加密、密钥管理、备份恢复或在线模式变更编排。 +- `原条件`、SQL 迁移/种子、数据库默认、检查表达式、自定义 getter/setter/编解码和代码迁移都是受信任扩展点。 +- 生命周期钩子同步执行,不能吞掉数据库错误或在事务外制造与主操作不一致的副作用。 +- 原始错误详情含位置和踪迹,只适合受控诊断;对外响应应映射为最小错误,不公开模式名、文件路径或堆栈。 -言映使用 `YANYING_` 前缀错误。仓库:[yanxulang/yanxu-orm](https://github.com/yanxulang/yanxu-orm)。 +错误以 `YANYING_` 稳定代码分类,底层数据库错误保留言库/驱动分类。仓库与 1.0.0 Release:[yanxulang/yanxu-orm](https://github.com/yanxulang/yanxu-orm/releases/tag/v1.0.0)。 diff --git a/content/docs/ecosystem/libraries/postgres.mdx b/content/docs/ecosystem/libraries/postgres.mdx new file mode 100644 index 0000000..d22c680 --- /dev/null +++ b/content/docs/ecosystem/libraries/postgres.mdx @@ -0,0 +1,280 @@ +--- +title: 言库·象城:PostgreSQL 驱动 +description: 连接 PostgreSQL 14–18,使用 TLS、参数化查询、事务、连接池、反射、迁移与 JSONB 言据路径。 +--- + +言库·象城(包名 `言库象城`,仓库 `yanxu-postgres`)是建立在言库 1.x 协议上的 PostgreSQL 原生驱动。它通过言序 ABI v2 直接连接数据库,覆盖 TLS、预编译、事务、连接池、取消、结构反射、迁移和 JSONB/言据集成。 + +| 项目 | 1.0 稳定边界 | +| --- | --- | +| 当前版本 | `1.0.0` | +| 最低言序 | `1.1.12` | +| PostgreSQL | 14、15、16、17、18 | +| 稳定依赖 | 言库 `v1.0.0` / `^1.0` | +| 原生 ABI | v2 | +| 运行方式 | 字节码 VM、包运行或 YXB;树解释器不能载入 | + +## 安装并固定公开标签 + +```sh +yanbao add postgres --package 言库象城 --version "^1.0" +``` + +生产清单应固定公开附注标签,并把网络授权收紧到实际端点: + +```toml +[依赖] +言库象城 = { git = "https://github.com/yanxulang/yanxu-postgres.git", 修订 = "v1.0.0", 版 = "^1.0" } + +[权限] +网络 = ["db.example:5432"] +原生扩展 = true +``` + +依赖不会扩大顶层应用权限。从文件读取自定义 CA 时,应用还需对该文件授予最小文件权限;驱动配置接收已经读取的 PEM 正文。 + +## 建立安全连接 + +```yanxu +引「包:言库象城」为 象城; + +定 数据库 为 象城.打开配置({ + 「地址」:「postgresql://app@db.example/app」, + 「密码」:安全配置【「数据库密码」】, + 「应用名」:「account-service」, + 「TLS」:「验证完整」, + 「选项」:{ + 「连接超时毫秒」:5000 + } +}); +``` + +也可分别传 `主机`、`端口`、`数据库`、`用户` 和 `密码`;分离字段优先于地址中的同类字段。`打开(主机, 数据库, 用户, 密码)` 默认启用完整 TLS,`打开本地(...)` 固定 `127.0.0.1` 并禁用 TLS,只适合隔离的本机测试服务。 + +| TLS 模式 | 行为 | +| --- | --- | +| `验证完整` | 默认;要求 TLS,验证证书链和连接主机名 | +| `要求` | 与 `验证完整` 使用相同的完整验证 | +| `优先` | 兼容模式;允许按服务端能力选择 TLS | +| `禁用` | 明文;仅限调用方明确接受的隔离环境 | + +`根证书PEM` 可放在顶层或 `选项` 中。1.0 不支持客户端证书/私钥认证,也没有“加密但跳过主机名验证”的模式。连接地址、密码和 CA 正文不进入默认日志或公开错误。 + +## 参数化查询与预编译 + +PostgreSQL 值占位符从 `$1` 开始: + +```yanxu +数据库.执行( + 「CREATE TEMP TABLE users(id BIGSERIAL PRIMARY KEY, name TEXT NOT NULL, profile JSONB NOT NULL)」, + 【】 +); + +定 插入 为 数据库.预编译( + 「INSERT INTO users(name, profile) VALUES($1, $2) RETURNING id」 +); + +定 编号 为 插入.查询(【「子衿」,{「等级」:10}】).标量(); +插入.关闭(); + +定 用户 为 数据库.查询( + 「SELECT id, name, profile FROM users WHERE id = $1」, + 【编号】 +).首行(); +``` + +值参数由 PostgreSQL 协议绑定,不进入 SQL 模板。表名、列名、schema、排序方向和操作符不能参数化;动态标识符必须先经业务白名单,再用 `数据库.方言().引名(名称)` 引用。`执行原始` / `查询原始` 只用于受审计迁移或框架代码。 + +`预编译` 返回持有服务端/原生资源的 `PostgreSQL原生预编译`,支持 `参数数()`、`绑定()`、带选项执行与显式关闭。语句不能跨连接;连接关闭或池租约释放后,不得继续使用语句、绑定对象或事务。 + +查询结果拒绝重复列名,联表查询应给同名列唯一 `AS` 别名。这能避免行典静默覆盖,但也是从宽松客户端迁移时需要检查的兼容变化。 + +## 类型映射 + +| PostgreSQL 类型 | 言序读取值 | 1.0 写入边界 | +| --- | --- | --- | +| `BOOL` | 理 | 理 | +| `INT2/INT4/INT8/OID` | 数;超安全整数为文 | 范围匹配整数;大值按驱动类型要求传十进制文 | +| `FLOAT4/FLOAT8` | 数;非有限值为文 | 有限数 | +| `NUMERIC` | 保精度十进制文 | 数或十进制文 | +| 文字类型 | 文 | 文 | +| `BYTEA` | 字节 | 字节 | +| `UUID` | 规范文 | UUID 文 | +| `JSON/JSONB` | 结构化言序值 | 空、理、数、文、列、典 | +| 日期时间类型 | ISO / RFC 3339 文 | 对应格式文 | +| ENUM | 文 | 文 | +| 支持类型的一维数组 | 列,元素可为空 | 列 | + +多维数组、范围、网络地址、几何、复合类型和扩展自定义二进制类型不属于 1.0 稳定映射。驱动遇到无法安全解码的类型会报结构化类型错误,不做有损猜测。 + +## 事务、保存点、超时与取消 + +```yanxu +引「包:言库」为 言库; + +法 转账(事务)则 + 事务.执行( + 「UPDATE accounts SET balance = balance - $1 WHERE id = $2」, + 【10,甲】 + ); + + 定 保存点 为 事务.保存点(); + 保存点.执行( + 「UPDATE accounts SET balance = balance + $1 WHERE id = $2」, + 【10,乙】 + ); + 保存点.提交(); + 归 「已提交」; +终 + +定 结果 为 数据库.托管事务( + 转账, + 言库.事务选项() + .隔离(「可串行化」) + .只读(假) + .超时(5000) +); +``` + +回调成功提交,抛错回滚;同一连接只允许一个顶层事务,嵌套工作使用保存点。手动事务必须在所有路径提交或回滚。超时覆盖开始、正文和提交阶段,不是自动重试策略。 + +单次操作可绑定请求标识: + +```yanxu +定 选项 为 言库.执行选项() + .超时(2000) + .请求标识(「report-20260718」); + +定 结果 为 数据库.查询带选项(SQL,参数,选项); +``` + +另一执行上下文可调用 `数据库.取消(同一标识)`。驱动使用独立 PostgreSQL cancellation token,只取消当前匹配请求;空闲或不匹配调用不会污染下一次查询。若取消竞态后协议状态无法确认,连接会被丢弃。取消写请求不证明服务端未提交,应用必须使用事务、幂等键、唯一约束和业务对账。 + +## 连接池 + +```yanxu +定 池配置 为 言库.连接池配置() + .最小(2) + .最大(16) + .取得超时(2000) + .空闲超时(300000) + .最大生命周期(3600000) + .健康间隔(30000) + .等待上限(64); + +定 连接池 为 象城.创建连接池(配置,池配置); +定 租约 为 连接池.取得(); + +试 则 + 言 租约.查询(「SELECT now() AS current_time」,【】).首行(); + 租约.释放(); +救 所误 则 + 租约.释放(); + 抛 所误; +终 + +连接池.关闭平缓(10000); +``` + +池会探活并剔除关闭、过期或协议状态不确定的连接,但不会自动重放可能已经提交的写操作。租约释放后不得继续使用其子资源。 + +## 结构反射 + +```yanxu +逐 模式 于 数据库.模式清单() 则 + 言 模式; +终 + +定 结构 为 数据库.公共表结构(「users」); +若 结构【「存在」】 则 + 言 结构【「列」】; + 言 结构【「约束」】; + 言 结构【「索引」】; + 言 结构【「外键」】; +终 +``` + +反射覆盖用户 schema、表、分区表、视图、物化视图、外部表、列、主键、约束、复合索引和外键。schema 与关系名作为目录查询参数传入;缺失关系返回 `存在 = 假` 的稳定空结构。 + +## 版本化迁移 + +```yanxu +定 各迁移 为 【 + 象城.迁移( + 1, + 「创建用户」, + 「CREATE TABLE users(id BIGINT PRIMARY KEY)」, + 「DROP TABLE users」 + ), + 象城.迁移( + 2, + 「增加姓名」, + 「ALTER TABLE users ADD COLUMN name TEXT」, + 「ALTER TABLE users DROP COLUMN name」 + ) +】; + +定 迁移器 为 象城.默认迁移器(数据库); +言 迁移器.检查(各迁移); +言 迁移器.干运行升级(各迁移); +迁移器.升级(各迁移); +``` + +默认登记表是 `public._yanxu_migrations`。迁移器在事务内取得 advisory transaction lock,锁内重读历史,再原子执行 DDL 与登记。版本严格递增,SHA-256 覆盖版本、名称和双向 SQL;缺失定义、名称/内容漂移、非前缀历史与不可逆回退都会拒绝。 + +锁只协调使用同一锁编号的迁移器。扩展函数或数据库外部副作用不一定可随事务回滚;生产迁移仍要先备份、干运行和恢复演练。旧空校验和只能在审计后显式 `采用旧校验和`。 + +## 言据与 JSONB + +| 策略 | PostgreSQL 列 | 语义 | +| --- | --- | --- | +| `规范文本` | TEXT | 规范 `.yj` 文本,适合跨方言保真 | +| `原生结构` | JSONB / JSON | 结构化值,适合路径条件与 GIN 索引 | + +```yanxu +定 写入值 为 数据库.言据写入值(资料,「原生结构」); +数据库.执行(「INSERT INTO profiles(data) VALUES($1)」,【写入值】); + +定 条件 为 数据库.言据路径(「data」,【「等级」】).至少(10); +定 各行 为 数据库.查询( + (「SELECT data FROM profiles WHERE 」 加 条件.SQL), + 条件.参数() +).全部(); +``` + +路径数组、比较值和顶层键全部参数化,列名由方言引用。`JSONB索引建议` 只生成经引用的 DDL 建议,仍应进入受审计迁移。数据库 JSONB 不是规范言据文本;需要逐字稳定交换或跨数据库一致时使用 `规范文本`。 + +## 真实支持矩阵 + +### PostgreSQL 服务 + +| 主版本 | 支持状态 | 1.0 发布证据边界 | +| --- | --- | --- | +| 14 | 最低支持版本 | 发布 CI 真实服务生命周期门禁 | +| 15 | 支持 | 协议与类型兼容范围;本发布未单独列为完整真实服务门禁 | +| 16 | 支持 | 协议与类型兼容范围;本发布未单独列为完整真实服务门禁 | +| 17 | 支持 | 协议与类型兼容范围;本发布未单独列为完整真实服务门禁 | +| 18 | 最高已验证主版本 | 发布 CI 明文/TLS/取消/迁移;全生态在 18.4 完成源码与 YXB 集成 | + +“支持”表示 1.0 公共协议与类型映射的承诺,不等于应用的精确补丁、扩展、排序规则和服务器参数已经替你验证。部署前应在实际补丁版本执行连接、类型往返、迁移、取消与故障恢复测试。 + +### 原生目标 + +| 操作系统 | x86-64 | ARM64 | 发布门禁 | +| --- | --- | --- | --- | +| macOS | 支持 | 支持 | Mach-O、ABI、安装名、签名与原生消费者 | +| Linux glibc 2.17+ | 支持 | 支持 | ELF、ABI、动态依赖与原生消费者 | +| Windows | 支持 | 支持 | PE、ABI、静态 CRT 与原生消费者 | + +目标路径、大小和 SHA-256 以 `v1.0.0` 清单为准。未登记目标必须明确失败,不能加载其他架构或工作目录中的同名库。 + +## 权限、安全边界与明确限制 + +- 顶层应用必须授予精确数据库网络端点与原生扩展权限;象城不需要进程、环境、监听或 UDP 权限。 +- SQL 最大 1 MiB、参数最多 65,535 个;单次结果最多 100,000 行、1,024 列和 16 MiB,返回前完整解码。 +- 1.0 不提供异步逐行流、`COPY`、`LISTEN/NOTIFY`、逻辑复制、流水线模式、客户端证书认证或自动断线重放。 +- 只稳定映射支持类型的一维数组;复杂扩展类型需要上层明确适配。 +- TLS、参数化和标识引用不能代替数据库账号最小权限、行级授权、迁移审核、备份和审计。 +- 标准日志不记录参数值、密码、完整地址或 CA 正文;自定义日志器仍必须继续脱敏。 + +数据库错误归一为 `YANKU_*`,象城边界使用 `POSTGRES_*` / `YANXIANG_*` 稳定代码。仓库与 1.0.0 Release:[yanxulang/yanxu-postgres](https://github.com/yanxulang/yanxu-postgres/releases/tag/v1.0.0)。 diff --git a/content/docs/ecosystem/libraries/sqlite.mdx b/content/docs/ecosystem/libraries/sqlite.mdx index 0a8a6be..c1e898a 100644 --- a/content/docs/ecosystem/libraries/sqlite.mdx +++ b/content/docs/ecosystem/libraries/sqlite.mdx @@ -1,123 +1,251 @@ --- -title: 言舟:SQLite -description: 在言库协议之上连接 SQLite,安全绑定参数,执行事务批、保存点与迁移。 +title: 言舟:SQLite 驱动 +description: 使用 ABI v2 原生 SQLite、安全参数、预编译资源、事务、反射、迁移与 JSON1 言据路径。 --- -言库·轻舟(`yanxu-sqlite`,包名 `言舟`)是言库的 SQLite 实现。默认后端调用 SQLite 官方 `sqlite3` CLI;言序负责安全绑定、结果归一、事务脚本和迁移,宿主进程负责真正打开数据库。 +言库·轻舟(包名 `言舟`,仓库 `yanxu-sqlite`)是建立在言库 1.x 协议上的 SQLite 驱动。1.0 的默认后端通过 ABI v2 原生扩展在进程内使用固定版本 SQLite;系统 `sqlite3` CLI 只作为显式兼容后端保留。 -## 安装与运行条件 +| 项目 | 1.0 稳定边界 | +| --- | --- | +| 当前版本 | `1.0.0` | +| 最低言序 | `1.1.12` | +| 稳定依赖 | 言库 `v1.0.0` / `^1.0` | +| 默认 SQLite | bundled SQLite `3.53.2` | +| CLI 兼容后端 | 系统 SQLite `3.38+` | +| 原生 ABI | v2 | +| 运行方式 | 字节码 VM、包运行或 YXB;树解释器不能载入 | + +## 安装并固定公开标签 ```sh -yanbao add sqlite --package 言舟 --version "^0.1" +yanbao add sqlite --package 言舟 --version "^1.0" ``` -默认后端要求 SQLite 3.38 或更高版本,并确保 `sqlite3` 位于 `PATH`。顶层应用需要允许进程调用,并为数据库路径授予文件访问: +等价的可复现清单: ```toml +[依赖] +言舟 = { git = "https://github.com/yanxulang/yanxu-sqlite.git", 修订 = "v1.0.0", 版 = "^1.0" } + [权限] 文件 = ["data"] -进程 = true +原生扩展 = true ``` -言包会自动安装言库。 +文件权限应只覆盖真实数据库目录;SQLite 还会在同目录创建或访问 WAL/SHM 伴随文件。纯内存数据库可保持文件权限为空。使用 CLI 兼容后端时还要授权 `进程 = true`。 -## 打开与查询 +## 打开数据库 ```yanxu 引「包:言舟」为 言舟; -定 数据库 为 言舟.打开(「data/应用.db」); +定 数据库 为 言舟.打开配置({ + 「路径」:「data/应用.db」, + 「日志模式」:「WAL」, + 「同步模式」:「NORMAL」, + 「外键」:真, + 「忙碌超时毫秒」:5000 +}); 数据库.执行( 「CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY, name TEXT NOT NULL)」, 【】 ); -数据库.预编译(「INSERT INTO users(name) VALUES (?)」) - .执行(【「子衿」】); +定 插入 为 数据库.预编译(「INSERT INTO users(name) VALUES (?)」); +插入.执行(【「子衿」】); +插入.关闭(); -定 用户列 为 数据库 - .查询(「SELECT id, name FROM users WHERE name = ?」,【「子衿」】) - .全部(); +定 用户 为 数据库.查询( + 「SELECT id, name FROM users WHERE name = ?」, + 【「子衿」】 +).首行(); +言 用户; 数据库.关闭(); ``` -`打开为(路径, 程序, 超时毫秒)` 可指定 CLI 路径和超时。`以执行器(法)` 可完全绕过外部进程,用于测试或接入原生 SQLite 驱动。 +便捷入口包括: + +- `打开(路径)`:可创建的原生文件数据库; +- `打开内存()`:独立 `:memory:` 数据库; +- `打开临时()`:SQLite 管理生命周期的临时数据库; +- `打开URI(URI)`:显式 SQLite `file:` URI; +- `打开只读(路径)` / `打开只读URI(URI)`:只读打开; +- `打开CLI(路径)` / `打开为(路径, 程序, 超时毫秒)`:显式 CLI 后端; +- `以执行器(执行器)`:测试或高级适配入口。 + +原生文件默认开启外键、WAL、`NORMAL` 同步和 5 秒 busy timeout。URI 会拒绝 authority、百分号编码、片段、多个查询串和重复 `mode`,避免权限检查路径与 SQLite 实际解码路径不一致。 + +## 参数、标识符与预编译 + +值只能放在参数列: + +```yanxu +定 各行 为 数据库.查询( + 「SELECT id, name FROM users WHERE name = ? AND age >= ?」, + 【姓名,18】 +).全部(); +``` -## 参数绑定 +表名、列名、排序方向与操作符不能作为参数。动态标识符应先通过业务白名单,再用 `数据库.方言().引名(名称)` 引用。 -`绑定(SQL, 参数)` 只替换普通 SQL 上下文中的 `?`,会跳过单引号文字、双引号标识和反引号标识,并严格检查参数数量。 +默认原生后端把 SQL 模板交给 SQLite 编译器,并通过原生 API 绑定值,不把值转换成 SQL 文字。高频模板应复用并显式关闭原生语句: ```yanxu -定 已绑定 为 言舟.绑定( - 「SELECT '?' AS literal, name FROM users WHERE id = ?」, - 【42】 +定 语句 为 数据库.预编译( + 「INSERT INTO events(kind, payload) VALUES (?, ?)」 ); + +逐 事件 于 各事件 则 + 语句.执行(【事件【「种类」】,事件【「正文」】】); +终 + +言 语句.参数数(); +语句.关闭(); ``` -文字使用 SQLite 单引号双写规则,列和典编码为 JSON 文字。应用仍应把动态标识名限制在白名单内;占位符只能代表值,不能代表表名或列名。 +连接关闭会级联失效子语句。CLI 后端的 `SQLite预编译` 只是兼容外观,每次仍启动兼容执行器;`绑定(SQL, 参数)` 也只服务 CLI 与审计展示,不能让不可信 SQL 模板自动安全。 -`SQLite预编译` 保存 SQL 模板,提供 `执行`、`查询` 和用于审计的 `已绑定`。它是可复用语句外观,不声称 CLI 后端持有跨进程原生 statement 句柄。 +## 事务、保存点与批量 -## 批量执行 +原生后端支持可交互事务查询: ```yanxu -定 数量 为 数据库.批量执行(【 - {「SQL」:「INSERT INTO users(name) VALUES (?)」,「参数」:【「甲」】}, - {「SQL」:「INSERT INTO users(name) VALUES (?)」,「参数」:【「乙」】}, - 「DELETE FROM users WHERE name = '过期'」 -】); +定 事务 为 数据库.立即事务(); +试 则 + 事务.执行(「UPDATE accounts SET balance = balance - ? WHERE id = ?」,【10,甲】); + 事务.保存点(「credit」); + 事务.执行(「UPDATE accounts SET balance = balance + ? WHERE id = ?」,【10,乙】); + 事务.释放点(「credit」); + 事务.提交(); +救 所误 则 + 若 事务.是否活跃() 则 + 事务.回滚(); + 终 + 抛 所误; +终 ``` -批量项可以是 SQL 文字或 `{SQL, 参数}`,所有项放在一个立即事务中提交,返回执行项数。 +同一连接一次只能有一个顶层事务。保存点按后进先出释放;`回滚至(名称)` 后保存点仍存在,必须继续使用或释放。 + +`批量执行(各语句)` 把 SQL 文字或 `{SQL, 参数}` 放进一个立即事务。CLI 兼容事务会先安全绑定并缓存脚本,提交时一次执行;它在提交前不能返回事务内查询结果,也不持有真正的原生 statement 句柄。 + +## 连接池 -## 事务批与保存点 +言舟自身不新增池 API;使用言库的通用连接池组合连接工厂: ```yanxu -定 事务 为 数据库.立即事务(); -事务.执行(「INSERT INTO users(name) VALUES (?)」,【「采薇」】) - .保存点(「第二步」) - .执行(「INSERT INTO users(name) VALUES (?)」,【「蒹葭」】) - .释放点(「第二步」) - .提交(); +引「包:言库」为 言库; + +法 新连接() 则 + 归 言舟.打开(「data/应用.db」); +终 + +定 池配置 为 言库.连接池配置() + .最小(1) + .最大(8) + .取得超时(2000) + .健康间隔(30000); + +定 池 为 言库.连接池(新连接,池配置); +定 租约 为 池.取得(); +言 租约.查询(「SELECT 1」,【】).标量(); +租约.释放(); +池.关闭平缓(5000); ``` -标准进程模块没有持久标准输入,因此 CLI 后端先安全绑定并缓存写语句,提交时一次交给同一个 SQLite 进程。这样可以保证原子性与保存点语义。 +共享内存库需要命名 `file:` URI 与 `cache=shared`;多个普通 `打开内存()` 连接互相不可见。池不会替应用选择 WAL、处理长写事务,也不会自动重放失败写入。 + +## 结构反射 -事务批在提交前不返回查询结果;调用 `查询` 会明确报错。提交后通过连接查询。如果需要事务中读取并据此继续写入,应注入支持持久会话的原生执行器。 +```yanxu +逐 表 于 数据库.表清单() 则 + 言 表; +终 + +定 结构 为 数据库.表结构(「users」); +言 结构【「列」】; +言 结构【「索引」】; +言 结构【「外键」】; +``` -保存点必须后进先出释放,提交前不能遗留开放保存点。`回滚()` 丢弃尚未提交的脚本。 +`表结构` 覆盖 main/temp 表与视图、列、复合索引的索引列和外键动作;缺失表返回 `存在 = 假` 的稳定空结构。表名作为反射查询参数传入,不通过错误消息猜测结构。 -## 数据库迁移 +## 版本化迁移 ```yanxu 定 各迁移 为 【 言舟.迁移( 1, - 「创建用户表」, - 「CREATE TABLE users(id INTEGER PRIMARY KEY, name TEXT NOT NULL)」, + 「创建用户」, + 「CREATE TABLE users(id INTEGER PRIMARY KEY)」, 「DROP TABLE users」 ), 言舟.迁移( 2, - 「增加邮箱」, - 「ALTER TABLE users ADD COLUMN email TEXT」, - 「」 + 「增加姓名」, + 「ALTER TABLE users ADD COLUMN name TEXT」, + 「ALTER TABLE users DROP COLUMN name」 ) 】; 定 迁移器 为 言舟.默认迁移器(数据库); -言 迁移器.待升级(各迁移); +言 迁移器.检查(各迁移); +言 迁移器.干运行升级(各迁移); 迁移器.升级(各迁移); ``` -迁移版本必须严格递增。默认迁移表是 `_yanxu_migrations`,记录版本、名称和应用时间。`回退(迁移列, 次数)` 只回退有本地定义且提供降级 SQL 的迁移。 +默认登记表为 `_yanxu_migrations`。版本必须严格递增;SHA-256 覆盖版本、名称、升级和降级 SQL。升级/回退在 `BEGIN IMMEDIATE` 写锁中重读历史,并把 DDL 与登记原子提交。已应用定义缺失、名称或内容漂移、不可逆回退都会被拒绝。 + +旧登记没有校验和时,先审计实际结构和历史,再显式调用 `采用旧校验和(各迁移)`;它只补登记,不会猜测历史 SQL 是否执行过。 + +## 言据与 JSON1 + +| 策略 | SQLite 存储 | 语义 | +| --- | --- | --- | +| `规范文本` | TEXT 中的规范 `.yj` 文本 | 跨数据库保真与稳定交换 | +| `原生结构` | JSON1 可查询的 JSON 文本 | 读回恢复言序值,可做路径查询 | + +```yanxu +定 写入值 为 数据库.言据写入值(资料,「原生结构」); +数据库.执行(「INSERT INTO profiles(data) VALUES (?)」,【写入值】); + +定 条件 为 数据库.言据路径(「data」,【「等级」】).至少(10); +定 表达式 为 条件.转典(); +定 各行 为 数据库.查询( + (「SELECT data FROM profiles WHERE 」 加 表达式【「SQL」】), + 表达式【「参数」】 +).全部(); +``` + +JSON 路径段和比较值也作为参数。JSON1 按运行时探测;不可用时返回能力错误,不会退化为全表内存过滤。SQLite JSON 文本不是规范言据文本,两种策略的磁盘格式不能互相冒充。 + +## 权限与安全边界 + +| 场景 | 顶层应用所需权限 | +| --- | --- | +| 原生文件/URI 数据库 | 对实际路径的 `文件` + `原生扩展 = true` | +| 原生内存/临时数据库 | `原生扩展 = true`;无需应用文件权限 | +| CLI 文件数据库 | 实际路径 `文件` + `进程 = true` + `原生扩展 = true` | + +ABI v2 原生扩展与宿主进程拥有相同操作系统权限,不是文件沙箱。应用不要绕过公开打开入口直接调用 ABI。迁移 SQL、动态标识符和 CLI 程序路径都属于受信任配置边界。 -## 结果与错误 +## 支持矩阵与明确限制 -执行和查询返回言库 `查询结果`。`安全执行` 返回结构化成功/错误典;SQLite 错误使用 `YANZHOU_` 前缀,并保留 SQL、参数、程序和踪迹。 +| 后端/目标 | 1.0 状态 | +| --- | --- | +| bundled SQLite 3.53.2 | 默认原生后端,已固定构建与真实消费者验证 | +| 系统 SQLite 3.38+ | 显式 CLI 兼容后端;需要 `-json` 等能力 | +| macOS x86-64 / ARM64 | 提供固定 ABI v2 制品 | +| Linux glibc x86-64 / ARM64 | 提供固定 ABI v2 制品 | +| Windows x86-64 / ARM64 | 提供固定 ABI v2 制品 | -默认 CLI 方案适合本地工具、测试和中低并发单机应用。需要长连接、高并发或细粒度取消时,应实现持久原生执行器,同时继续复用言库和言舟的公开边界。 +- 原生 SQL 最多 1 MiB、参数最多 65,536 个;单次结果最多 100,000 行、1,024 列和 16 MiB。 +- busy timeout 只控制 SQLite 锁等待,不是总查询截止;1.0 不提供异步查询取消。 +- 1.0 没有言舟专用连接池,使用言库通用池。 +- CLI 每次请求启动进程,性能较低;事务提交前不能查询,也没有真正预编译资源。 +- URI 权限规则刻意拒绝部分 SQLite 合法但难以一致授权的 URI 形式。 +- SQLite 不支持所有在线 DDL;复杂改列、外键和检查约束变更应使用经验证的重建表迁移。 -仓库:[yanxulang/yanxu-sqlite](https://github.com/yanxulang/yanxu-sqlite)。 +错误以 `YANZHOU_`、`YANKU_SQLITE_` 等稳定代码分类。仓库与 1.0.0 Release:[yanxulang/yanxu-sqlite](https://github.com/yanxulang/yanxu-sqlite/releases/tag/v1.0.0)。 From 9fd3987910b4d1f5e600ed2f815a8f2d6cbb4381 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E7=A7=80?= Date: Sat, 18 Jul 2026 19:27:23 +0800 Subject: [PATCH 3/6] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=E8=A8=80?= =?UTF-8?q?=E6=8D=AE=20Web=20=E4=B8=8E=20GUI=20=E7=94=9F=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- content/docs/ecosystem/desktop/choosing.mdx | 9 +- .../docs/ecosystem/desktop/compatibility.mdx | 19 ++- content/docs/ecosystem/desktop/gui.mdx | 121 ++++++++++++++++++ content/docs/ecosystem/desktop/index.mdx | 49 +++---- content/docs/ecosystem/desktop/meta.json | 1 + content/docs/ecosystem/desktop/migration.mdx | 6 +- content/docs/ecosystem/desktop/routes.mdx | 10 +- content/docs/ecosystem/web/framework.mdx | 115 ++++++++++------- content/docs/ecosystem/web/html.mdx | 97 +++++++------- content/docs/ecosystem/web/http.mdx | 100 ++++++++++----- content/docs/ecosystem/web/index.mdx | 102 +++++++-------- content/docs/ecosystem/web/meta.json | 9 +- content/docs/ecosystem/web/migration-1.0.mdx | 44 +++++++ content/docs/ecosystem/web/request.mdx | 96 ++++++++++++++ .../docs/ecosystem/web/security-roadmap.mdx | 81 ++++++++---- content/docs/ecosystem/yanju/format.mdx | 16 ++- content/docs/ecosystem/yanju/index.mdx | 88 ++++++++----- content/docs/ecosystem/yanju/meta.json | 2 +- .../ecosystem/yanju/operations-conversion.mdx | 28 +++- .../docs/ecosystem/yanju/streams-errors.mdx | 20 ++- content/docs/guides/web-application.mdx | 28 +++- 21 files changed, 768 insertions(+), 273 deletions(-) create mode 100644 content/docs/ecosystem/desktop/gui.mdx create mode 100644 content/docs/ecosystem/web/migration-1.0.mdx create mode 100644 content/docs/ecosystem/web/request.mdx diff --git a/content/docs/ecosystem/desktop/choosing.mdx b/content/docs/ecosystem/desktop/choosing.mdx index 70aa1ce..a99f7e0 100644 --- a/content/docs/ecosystem/desktop/choosing.mdx +++ b/content/docs/ecosystem/desktop/choosing.mdx @@ -5,12 +5,13 @@ description: 根据项目阶段、控件需求、稳定性与定制程度选择 ## 直接选择言窗 -以下情况优先使用`yanxu-gui`: +以下情况优先使用稳定的[`yanxu-gui` 1.0](/ecosystem/desktop/gui/): - 现有应用已经上线,当前控件和主题足够; - 希望依赖成熟的 egui 生态与立即模式开发方式; - 需要言界`0.1.0`尚未提供的复杂控件; - 不希望在首版阶段承担新 API 迭代成本。 +- 需要由 CI 汇总并校验的 Windows、macOS、Linux 六目标原生制品。 ## 选择言界 @@ -26,10 +27,12 @@ description: 根据项目阶段、控件需求、稳定性与定制程度选择 | 问题 | 若回答“是” | | --- | --- | -| 已有言窗应用是否稳定上线? | 保持言窗 | +| 已有言窗应用是否稳定上线? | 保持言窗 1.x | | 是否依赖言界首版没有的控件? | 保持言窗或先验证自定义控件 | | 是否必须控制事件传播和焦点顺序? | 选择言界 | | 是否要用言据集中描述主题? | 选择言界 | | 是否要直接调用操作系统句柄? | 两条上层路线都不适合;应贡献言台通用能力 | -建议先复制[完整示例](/ecosystem/desktop/complete-example/)做概念验证,再决定新模块的路线。迁移不要求一次完成;可以保持旧应用使用言窗,让新的独立应用或窗口产品使用言界。 +选择言窗时先运行[言窗 1.0 示例](/ecosystem/desktop/gui/);评估言界时复制 +[完整言界示例](/ecosystem/desktop/complete-example/)。迁移不要求一次完成,可以让不同 +应用分别使用两条路线。 diff --git a/content/docs/ecosystem/desktop/compatibility.mdx b/content/docs/ecosystem/desktop/compatibility.mdx index 634062c..1415e22 100644 --- a/content/docs/ecosystem/desktop/compatibility.mdx +++ b/content/docs/ecosystem/desktop/compatibility.mdx @@ -3,7 +3,19 @@ title: 版本兼容政策 description: 言界、言台、言序、言包、言据和言窗之间的兼容基线与已知限制。 --- -## 0.1.0 基线 +## 言窗 1.0 稳定线 + +| 组件 | 兼容范围 | 已验证版本 | +| --- | --- | --- | +| 言序 | `>=1.1.12` | 1.1.12 | +| 言包 | 格式 2 清单与锁、GUI Bundle | 0.5.x | +| 言窗 | `^1.0`、ABI v2 | 1.0.0 | +| 平台 | Linux GNU、macOS、Windows | x86-64 / ARM64 | + +言窗 Git 标签保存源码和清单模板;完整六目标包以 1.0.0 Release 归档发布。Linux GNU +制品需要 glibc 2.39,macOS 公开制品仅做临时签名。精确限制见[言窗 1.0](/ecosystem/desktop/gui/)。 + +## 言界与言台 0.1 基线 | 组件 | 兼容范围 | 已验证版本 | | --- | --- | --- | @@ -12,11 +24,10 @@ description: 言界、言台、言序、言包、言据和言窗之间的兼容 | 言台 | `^0.1`,ABI v2 | 0.1.0 | | 言界 | `^0.1` | 0.1.0 | | 言据 | `^1.1` | 1.1.2 | -| 言窗 | 独立并行路线 | 未修改 | +| 言窗 | 独立并行路线 | 1.0.0 | 言界要求言序`1.1.8`提供 Windows VM 所有者线程的 8 MiB 栈修复;言台本身仍兼容 -`>=1.1.7`。新路线不要求言包`0.5.1`或新的言据版本,也不会替换、废弃或强制迁移 -`yanxu-gui`。 +`>=1.1.7`。新路线不会替换、废弃或强制迁移`yanxu-gui`。 ## 版本政策 diff --git a/content/docs/ecosystem/desktop/gui.mdx b/content/docs/ecosystem/desktop/gui.mdx new file mode 100644 index 0000000..5a0381a --- /dev/null +++ b/content/docs/ecosystem/desktop/gui.mdx @@ -0,0 +1,121 @@ +--- +title: 言窗 1.0 +description: 使用 yanxu-gui 1.0 构建具备窗口、布局、控件、事件、画布和原生 Bundle 的跨平台应用。 +--- + +言窗(`yanxu-gui`)1.0.0 是稳定桌面 GUI 包。中文对象 API 通过原生 ABI v2 封装 +eframe/egui 与 winit,支持 Windows、macOS、Linux Wayland/X11 的 x86-64 和 ARM64; +应用无需编写 Rust,也不会接触原生指针或框架类型。 + +## 创建项目 + +需要言序 1.1.12 和支持 GUI 模板的言包: + +```sh +yanbao new 我的窗口 --gui +yanbao run --manifest-path 我的窗口 +``` + +言窗的 Git 标签保存源码和清单模板;六目标生成清单及原生库位于 +[1.0.0 Release](https://github.com/yanxulang/yanxu-gui/releases/tag/v1.0.0) 的 +`yanxu-gui-six-targets.tar.gz`。进行离线或审计构建时,应展开该官方归档并通过 +`--gui-path`提供完整包,不要把只有模板的 Git 标签误当成可执行六目标包: + +```sh +yanbao new 我的窗口 --gui --gui-path /已验证/yanxu-gui +``` + +运行时按锁文件的系统、架构、ABI、大小和 SHA-256 精确选择后端,不下载或猜测缺失动态库。 + +## 最小应用 + +```yanxu +引「包:言窗」为 界面; + +定 应用 为 界面.应用(「任务清单」); +定 窗口 为 应用.窗口({「标题」:「任务清单」,「宽」:720,「高」:480}); +定 布局 为 窗口.纵向布局({「间距」:12,「内边距」:16}); +定 提示 为 布局.文字(「尚未保存」); +定 输入 为 布局.输入框({「占位」:「输入任务」}); +定 保存 为 布局.按钮(「保存」); + +法 保存任务(事件):空 则 + 提示.内容(「已保存:」 加 输入.取内容()); + 归 空; +终 + +法 关闭应用(事件):空 则 + 应用.退出(); + 归 空; +终 + +保存.点击(保存任务); +窗口.关闭时(关闭应用); +窗口.显示(); +应用.运行(); +``` + +`应用.运行`阻塞当前线程直至退出。关闭布局会释放整个子树;关闭窗口会释放窗口、布局、 +控件和回调;事件循环返回时清空模型。句柄绑定创建它的线程和事件循环,跨线程使用会 +返回稳定错误。 + +## 布局、控件与事件 + +- 纵向、横向、网格、层叠、双向滚动布局,可递归嵌套。 +- 文字、按钮、单/多行输入、复选、单选、下拉、滑块、进度、图片、列表、标签页、菜单和画布。 +- 点击、变化、焦点、键盘、鼠标、滚轮、窗口、DPI、拖放、自定义事件和有界定时器。 +- 中文与扩展 Unicode 字体回退,文字输入交给平台 IME。 +- 高频鼠标、尺寸和重绘事件可能合并;回调只在创建 VM 的所有者线程运行。 + +## 权限 + +```toml +[权限] +图形界面 = true +剪贴板 = false +文件对话框 = false +``` + +图形权限不隐式授予剪贴板或文件对话框。对话框只返回路径,不读取文件;应用仍需为后续 +文件操作声明路径权限。言窗后端可凭图形权限装载,其他 ABI 扩展仍需单独授权。 + +## 错误与资源预算 + +```yanxu +试 则 + 窗口.标题(「新标题」); +救 所误 则 + 定 详情 为 界面.错误详情(所误); + 若 (详情【「代码」】 等于 「GUI_RESOURCE_CLOSED」)则 + 言「窗口已经关闭」; + 终 +终 +``` + +程序判断`GUI_*`代码,不匹配消息,也不直接依赖底层`NATIVE_V2`来源。图片宽高各不超过 +4,096,解码分配上限 96 MiB;单画布最多 16,384 条命令、估算 64 MiB。资源图、事件队列、 +值树和回调均有硬上限。 + +## Bundle 与正式制品 + +```sh +yanbao build --manifest-path 我的窗口 --release --bundle +``` + +Bundle 包含 standalone 运行时、YXB、当前目标原生库、资源、许可证和逐文件摘要。macOS +输出`.app`,Windows 输出 GUI 应用目录,Linux 输出 AppDir。项目签名、公证或商店上传 +在最后一次未签名摘要验证之后进行。 + +官方 1.0.0 Release 归档为 28,404,376 字节,SHA-256: +`2fddeb3db155f6811ea5a0aab2d7dbd567a62ab44da38db2a6b08cf43e32f3e4`。六个制品均验证 +ABI v2 导出、架构、大小和摘要;macOS 制品是临时签名,不是 Developer ID 签名。 + +## 已知限制 + +- 不支持 musl、WebAssembly、iOS、Android、32 位目标或运行时下载缺失制品。 +- Linux GNU 正式制品需要 glibc 2.39。 +- 非零布局`伸缩`在 1.0 明确失败;图片只支持受限 PNG/JPEG。 +- 渲染像素、字体、剪贴板和对话框外观不保证跨平台完全一致。 +- 原生扩展与主进程同址,不提供进程级隔离。 + +完整 API、架构与安全文档见[yanxulang/yanxu-gui](https://github.com/yanxulang/yanxu-gui)。 diff --git a/content/docs/ecosystem/desktop/index.mdx b/content/docs/ecosystem/desktop/index.mdx index e861c55..464b595 100644 --- a/content/docs/ecosystem/desktop/index.mdx +++ b/content/docs/ecosystem/desktop/index.mdx @@ -1,36 +1,41 @@ --- title: 图形界面概览 -description: 言序原生桌面 GUI 的两条兼容路线、分层边界与首版平台范围。 +description: 在稳定言窗 1.0 与言界、言台保留模式路线之间选择原生桌面 GUI。 --- -言序提供两条并行、都受支持的原生桌面路线。现有的**言窗**适合希望快速使用成熟立即模式控件的应用;新的**言界 + 言台**路线把高级控件放在言序代码中,以保留模式控件树换取更强的组合、主题和演进能力。两条路线都创建真正的桌面窗口,不依赖浏览器、Electron、WebView 或 DOM。 +言序提供两条并行的原生桌面路线。**言窗 1.0**直接以 ABI v2 封装 eframe/egui 与 +winit,适合需要稳定、完整控件和六目标发布的应用;**言界 + 言台**把高级控件保留在 +言序代码中,提供保留模式控件树和更强的主题/组合能力。两条路线都创建真实窗口,不 +依赖浏览器、Electron、WebView 或 DOM。 ![言序原生 GUI 两条路线架构图](/desktop-architecture.svg) -## 新路线的边界 - -`yanxu-platform`(言台)只负责窗口、事件循环、输入、IME、字体、图片、绘制表面、系统服务和资源生命周期。它不会公开按钮、输入框、列表或标签页。`yanxu-ui`(言界)的控件树、布局、状态、事件路由、文本编辑和绘制命令均由言序实现,上层代码不会接触 HWND、NSWindow、Wayland 或 X11 句柄。 + + + + + + -高频事件通过批次跨越 ABI;鼠标移动、尺寸和重绘会合并,滚轮会累积。整帧绘制使用版本化二进制命令缓冲一次提交。言据用于人类可读的主题和配置,JSON 作为兼容输入;两者都不进入逐帧热路径。 +## 平台范围 -## 首版范围 +| 路线 | 版本 | 最低言序 | Windows | macOS | Linux GNU | +| --- | ---: | ---: | --- | --- | --- | +| 言窗 `yanxu-gui` | 1.0.0 | 1.1.12 | x86-64 / ARM64 | x86-64 / ARM64 | x86-64 / ARM64 | +| 言界 + 言台 | 0.1.x | 1.1.8 | x86-64 / ARM64 | x86-64 / ARM64 | x86-64 / ARM64 | -言台`0.1.0`兼容言序`>=1.1.7`;言界`0.1.0`要求言序`>=1.1.8`,以获得 Windows -图形回调路径的运行时栈修复。新路线支持以下六个原生目标: +言窗 1.0 的 Linux GNU 制品最高需要 glibc 2.39;不支持 musl、WebAssembly、移动平台 +或 32 位目标。言界/言台有独立版本和兼容文档,升级一条路线不会隐式升级另一条。 -| 系统 | x86-64 | ARM64 | 窗口后端 | -| --- | --- | --- | --- | -| Windows | 支持 | 支持 | winit / Win32 | -| macOS | 支持 | 支持 | winit / AppKit | -| Linux | 支持 | 支持 | winit / Wayland,X11 回退 | +## 两条路线的边界 -首版提供应用、多窗口、行列/堆叠/网格/滚动布局、文字、按钮、单行与多行输入、列表、标签页、分割面板、菜单、弹出层、画布、图片、主题、事件路由、焦点、快捷键、中文 IME、剪贴板和文件对话框。 +言窗公开应用、窗口、布局、控件、事件、图片、画布、剪贴板和文件对话框对象,控件由 +egui 后端实现。言界中的按钮、输入、列表、布局、状态与事件路由由言序代码实现;言台 +只负责窗口、输入、IME、字体、绘制表面和系统服务。 - - - - - - +两条路线不能在同一个原生窗口混用控件树,也不公开操作系统句柄。可在不同应用中并存, +迁移应按独立产品或窗口渐进完成。 -源码与发布位于[言台仓库](https://github.com/yanxulang/yanxu-platform)和[言界仓库](https://github.com/yanxulang/yanxu-ui)。既有[言窗仓库](https://github.com/yanxulang/yanxu-gui)继续维护兼容性。 +稳定言窗源码与 Release 位于[yanxu-gui](https://github.com/yanxulang/yanxu-gui)。另一条 +路线位于[yanxu-platform](https://github.com/yanxulang/yanxu-platform)和 +[yanxu-ui](https://github.com/yanxulang/yanxu-ui)。 diff --git a/content/docs/ecosystem/desktop/meta.json b/content/docs/ecosystem/desktop/meta.json index 38ea58d..b5bf8db 100644 --- a/content/docs/ecosystem/desktop/meta.json +++ b/content/docs/ecosystem/desktop/meta.json @@ -5,6 +5,7 @@ "index", "routes", "choosing", + "gui", "quick-start", "first-window", "configuration", diff --git a/content/docs/ecosystem/desktop/migration.mdx b/content/docs/ecosystem/desktop/migration.mdx index 554d9a5..c9258fb 100644 --- a/content/docs/ecosystem/desktop/migration.mdx +++ b/content/docs/ecosystem/desktop/migration.mdx @@ -3,7 +3,9 @@ title: 从言窗迁移 description: 保持现有言窗应用可用,并按独立页面或新项目渐进采用言界。 --- -迁移不是升级前置条件。`yanxu-gui`继续维护,新增言台和言界没有修改它的源码、清单或 API。稳定上线的言窗应用可以原样保留。 +迁移不是升级前置条件。`yanxu-gui`已有独立的 1.0 稳定线,新增言台和言界不会替换它。 +现有言窗应用应先按[言窗 1.0](/ecosystem/desktop/gui/)升级并锁定公开 Release 制品,再决定 +是否评估另一条编程模型。 ## 概念映射 @@ -17,7 +19,7 @@ description: 保持现有言窗应用可用,并按独立页面或新项目渐 ## 推荐步骤 -1. 保留原言窗分支和发布版本,不修改已上线窗口。 +1. 保留原言窗发布版本和回归测试,先完成 1.0 标签、清单和原生制品升级。 2. 用[完整示例](/ecosystem/desktop/complete-example/)建立独立言界试验项目。 3. 先迁移数据模型和业务命令,再用行/列/网格重建布局。 4. 把立即模式条件分支改为控件属性、状态绑定和事件回调。 diff --git a/content/docs/ecosystem/desktop/routes.mdx b/content/docs/ecosystem/desktop/routes.mdx index 50c5589..0c814d2 100644 --- a/content/docs/ecosystem/desktop/routes.mdx +++ b/content/docs/ecosystem/desktop/routes.mdx @@ -9,7 +9,7 @@ description: 比较现有立即模式言窗与新的言序保留模式言界路 | --- | --- | --- | | 控件实现 | egui/eframe 后端提供 | 言序代码提供 | | 模型 | 立即模式 | 保留模式控件树 | -| 成熟度 | 现有稳定路线 | 新的`0.1.0`路线 | +| 成熟度 | 稳定`1.0.0`路线 | 独立的`0.1.x`路线 | | 自定义控件 | 围绕 egui API 扩展 | 组合控件、渲染树或画布命令 | | 布局与事件 | 后端框架语义 | 言序统一的布局、捕获/目标/冒泡 | | 原生边界 | 包直接封装 egui/eframe/winit | 言界只调用言台平台原语 | @@ -19,8 +19,8 @@ description: 比较现有立即模式言窗与新的言序保留模式言界路 ## 兼容承诺 -新增言台和言界没有修改言包`0.5.0`、言据`1.1.2`或言窗。六目标验收确认 Windows -原生回调路径需要言序`1.1.8`的运行时栈修复;该补丁保持语言规范、ABI、YXB 和既有源码 -兼容。两个 GUI 包可以在不同应用中并存;首版不支持在同一原生窗口内混合两棵控件树。 +言窗 1.0 要求言序 1.1.12、格式 2 清单和 ABI v2;言界 0.1.x 有自己的兼容线。两个 GUI +包可以在不同应用中并存;不支持在同一原生窗口内混合两棵控件树。 -若已有言窗项目运行稳定,可以继续维护。只有当新页面需要保留状态、深度主题化、可组合控件、确定的事件传播或将控件逻辑留在言序层时,才需要评估言界。 +若已有言窗项目,应先升级到公开 1.0 制品并保持兼容线。只有当新页面需要保留状态、 +深度主题化、可组合控件、确定的事件传播或将控件逻辑留在言序层时,才评估言界。 diff --git a/content/docs/ecosystem/web/framework.mdx b/content/docs/ecosystem/web/framework.mdx index b279495..768ce5b 100644 --- a/content/docs/ecosystem/web/framework.mdx +++ b/content/docs/ecosystem/web/framework.mdx @@ -1,89 +1,116 @@ --- -title: 言枢:应用框架 -description: 使用配置、命名路由、反向 URL、路由组、中间件、模板响应和测试客户端建立言序 Web 应用。 +title: 言枢:Web 应用框架 +description: 使用言枢 1.0 的路由、中间件、言据协商、会话、CSRF、静态文件和无端口测试构建服务。 --- -言枢是`yanxu-web` 0.2 的产品名。仓库与技术包标识保持不变,新源码把包引为`言枢`即可。 +言枢(`yanxu-web`)1.0.0 是言序 Web 应用框架。它组合言讯协议对象、言页安全节点、 +言据数据交换和言访测试传输;所有状态都由显式应用对象持有,不依赖魔法全局变量。 + +## 安装 + +最低言序为 1.1.12: ```sh -yanbao add web --version '^0.2' +yanbao add web --version '^1.0' +yanbao install ``` +言包会固定言据 1.2、言页 1.0、言讯 1.0 和言访 1.0。开发服务器需要回环监听权限; +模板和静态文件另受应用文件权限约束。 + ## 创建应用 ```yanxu 引「包:web」为 言枢; -定 配置项 为 言枢.配置() - .设应用名(「我的站点」) - .设模板根(「templates」) - .设静态根(「static」); -定 应用 为 言枢.创建应用(配置项); - -法 首页(上下文)则 - 归 应用.模板响应(「home.yb.html」,{ - 「标题」:「你好,言枢」, - 「详情地址」:上下文.反向(「文章详情」,{「id」:「first」}) +法 首页(上下文) 则 + 归 言枢.协商响应(上下文,{ + 「服务」:「言枢」, + 「文章」:上下文.反向(「文章详情」,{「id」:「first」}) }); 终 -法 文章(上下文)则 +法 文章(上下文) 则 归 言枢.JSON响应({「id」:上下文.参数值(「id」)}); 终 -应用.命名取(「/」,「首页」,首页); +定 应用 为 言枢.创建应用( + 言枢.配置() + .设应用名(「我的服务」) + .设最大请求正文字节(1048576) + .设最大响应正文字节(2097152) +); + +应用.取(「/」,首页); 应用.命名取(「/posts/:id」,「文章详情」,文章); -应用.挂配置静态(「/static」); -言枢.服务器(应用,「127.0.0.1:8080」).运行(); +定 响应 为 言枢.测试客户端(应用).取(「/posts/first」); ``` -`创建应用`会建立言标环境,并按配置加入 CSP、`X-Content-Type-Options`和同源 referrer policy。 - -## 路由与反向 URL +路径支持静态段、`:名称`和末尾`*名称`。命名路由与路由组生成反向 URL;重复方法/模式 +会失败,不会覆盖旧处理器。中间件按注册顺序进入、逆序退出,404、405 与统一错误边界 +可以分别替换。 -路径支持静态段、`:名称`单段参数和末尾`*名称`通配参数。路径不存在返回 404;路径存在但方法不匹配返回 405 与`Allow`。 +## JSON、言据与协商 ```yanxu -定 接口 为 应用.命名路由组(「/api」,「api」); -接口.命名取(「/posts」,「文章列表」,列表处理); - -定 地址 为 应用.反向(「api:文章列表」,{}); +法 创建言据(上下文) 则 + 定 输入:典 为 上下文.言据正文(); + 归 言枢.言据值响应(输入); +终 ``` -反向 URL 会百分号编码动态参数,页面与处理器无需重复硬编码业务路径。 +`言据正文`只接受言据媒体类型并使用受限解析;`言据值响应`执行规范序列化。 +`协商响应`解析`Accept`质量和特异度,在 JSON 与言据之间选择并写入`Vary: accept`。 -## 请求上下文 +## 会话与 CSRF -`言枢上下文`可读取路径参数、查询首项/全集、首部、Cookie、UTF-8 正文、JSON 正文、AJAX 标记与应用状态。`上下文.反向`使用当前应用的命名路由。 +会话存储是显式协议或三个回调的适配器,不隐式打开数据库: ```yanxu -法 新建(上下文)则 - 定 输入:典 为 上下文.JSON正文(); - 归 言枢.JSON响应({「title」:输入【「title」】}); -终 +定 会话项 为 言枢.会话配置() + .设Cookie名(「__Host-yanxu-session」) + .设最大秒(3600) + .设安全(真) + .设仅HTTP(真) + .设同站(「Lax」); + +应用.使用(言枢.会话中间件(会话项,存储)); ``` -## 中间件与状态处理器 +CSRF 中间件对非安全方法检查`Sec-Fetch-Site`、Origin/Referer 和双提交令牌。它不能替代 +登录、授权或 HTTPS。生产会话存储必须自行提供并发控制、过期清理、容量和可观测性。 -中间件仍采用洋葱模型:按注册顺序进入,调用`下一步`后逆序返回。`设状态处理器`可替换 404 与 405;未捕获错误进入`设错误处理器`指定的 500 边界。所有阶段必须返回`HTTP响应`。 +## 静态文件与 OpenAPI -## 响应 +静态配置支持路径隔离、隐藏文件策略、媒体类型、弱 ETag、Last-Modified、条件请求和 +单一字节范围;多范围不会伪装成完整 multipart 响应。 -应用方法提供`模板响应`与`模板状态响应`。模块还提供 HTML 节点、言标源码、JSON、言据、文字、字节、状态、重定向、永久重定向、空响应和 Cookie 工厂。 +OpenAPI 只收录通过`接口路由`系列显式登记的操作。框架负责稳定操作标识、隔离快照、 +预算和 HTTP 输出;提供器负责生成 OpenAPI 3.1,不反射普通处理器源码。 ## 无端口测试 ```yanxu -定 客户端 为 言枢.测试客户端(应用); -定 首页 为 客户端.取(「/」); -定 新建 为 客户端.发JSON(「/api/posts」,{「title」:「第一篇」}); +引「包:web/言访测试」为 言访测试; + +定 客户端 为 言访测试.客户端(应用); +定 响应 为 客户端.发文(「/profiles」) + .言据正文({「姓名」:「子衿」}) + .发送() + .确保成功(); ``` -客户端直接驱动完整路由、中间件、模板和错误链。真实套接字只需保留少量冒烟测试。 +适配器保留查询、首部、Cookie、字节正文、受控重定向、重复`Set-Cookie`和 HEAD 语义, +但不模拟 DNS、TLS、连接失败或真实网络超时。 + +## 安全与已知限制 -## 当前边界 +言枢提供正文预算、安全首部、路径隔离、CSRF 和错误隐藏边界,应用仍必须实现认证、 +授权、速率限制、审计和敏感数据脱敏。 -开发服务器仍为串行 HTTP/1.1、一连接一请求模型,不承诺 TLS、并发工作池、长连接、流式上传、生产日志或优雅重启。模板与静态路径安全不替代认证、授权、CSRF、领域校验和部署隔离。 +内建服务器串行接受连接、每连接处理一个 HTTP/1.1 请求并关闭,不提供 TLS、HTTP/2/3、 +长连接工作池、流式上传、生产代理信任或优雅重启。生产环境应使用成熟前置服务器。 -继续阅读[言标语法](/ecosystem/web/yanbiao/)、[0.2 迁移](/ecosystem/web/migration-0.2/)和[完整博客](/ecosystem/web/webblog/)。 +仓库与完整 API:[yanxulang/yanxu-web](https://github.com/yanxulang/yanxu-web), +[1.0.0 Release](https://github.com/yanxulang/yanxu-web/releases/tag/v1.0.0)。 diff --git a/content/docs/ecosystem/web/html.mdx b/content/docs/ecosystem/web/html.mdx index ab6c950..18e5b04 100644 --- a/content/docs/ecosystem/web/html.mdx +++ b/content/docs/ecosystem/web/html.mdx @@ -1,70 +1,81 @@ --- -title: yanxu-html:安全 HTML -description: 用节点、元素、属性、组件和文档生成默认转义的 HTML,并显式管理原始内容。 +title: 言页:安全 HTML +description: 使用言页 1.0 的默认转义节点、保守 URL 策略、资源预算和增量渲染生成服务端 HTML。 --- -[`yanxu-html`](https://github.com/yanxulang/yanxu-html)是 Web 栈最底层的输出库,不依赖 HTTP 或框架。它的核心设计是把普通文字和原始 HTML 分成两种节点:普通内容总是转义,绕过转义必须显式调用`原始`。 +言页(`yanxu-html`)1.0.0 是纯言序的安全 HTML 构造与增量渲染库。它用不同节点表示 +普通文字、属性、元素、片段、组件、文档和可信扩展,让自动转义成为默认路径。 + +## 安装 + +最低言序为 1.1.12: ```sh -yanbao add html --version '^0.1' +yanbao add html --package 言页 --version '^1.0' +yanbao install +``` + +```toml +[依赖] +言页 = { git = "https://github.com/yanxulang/yanxu-html.git", 修订 = "v1.0.0", 版 = "^1.0" } ``` -## 第一个页面 +## 构造页面 ```yanxu -引「包:html」为 HTML; - -定 页面 为 HTML.文档(HTML.元素(「html」,【HTML.属性(「lang」,「zh-CN」)】,【 - HTML.元素(「body」,【】,【 - HTML.元素(「h1」,【】,【HTML.文字(「<言序 Web>」)】), - HTML.元素(「a」,【HTML.属性(「href」,「/start」)】,【 - HTML.文字(「开始」) +引「包:言页」为 HTML; + +定 页面 为 HTML.文档( + HTML.元素(「html」,【HTML.属性(「lang」,「zh-CN」)】,【 + HTML.元素(「body」,【】,【 + HTML.元素(「h1」,【】,【HTML.文字(「言序 」)】), + HTML.元素(「a」,【HTML.属性(「href」,「/docs」)】,【 + HTML.文字(「阅读 & 学习」) + 】) 】) 】) -】)); +); 言 HTML.安全渲染(页面); ``` -标题输出为`<言序 Web>`,完整结果以前缀``开始。 - -## 节点模型 +普通文字和文字属性会自动转义。标签和属性名使用保守白名单;默认拒绝事件属性、脚本、 +样式、嵌入、SVG/Math 子语言和重复属性。URL 还要通过字符与协议白名单。 -| 类型 | 作用 | -| --- | --- | -| `HTML节点` | 所有可渲染类型的根。 | -| `HTML文字节点` | 保存普通文字,渲染时自动转义。 | -| `HTML原始内容` | 保存可信 HTML,原样输出。 | -| `HTML属性` | 保存文字、布尔或省略属性。 | -| `HTML元素` | 组合标签、属性和子节点。 | -| `HTML组件` | 延迟调用构建法,并验证返回节点。 | -| `HTML文档` | 添加 HTML5 doctype。 | - -工厂名称与类型一一对应:`文字`、`原始`、`属性`、`元素`、`组件`、`文档`。`安全渲染(节点)`是统一渲染入口。 - -## 属性和 URL - -文字属性值会 HTML 转义;布尔`真`只输出属性名,`假`或`空`不输出。标签名和属性名采用保守 ASCII 白名单,空白、引号、控制字符和标签逃逸字符不能进入名称位置。 - -`href`、`src`、`action`、`formaction`、`poster`与`xlink:href`还会校验 URL。无显式协议的地址可以使用;显式协议只允许`http:`、`https:`、`mailto:`与`tel:`。`javascript:`、`data:`、未知协议及含空白/控制字符的地址会失败。 +## 资源限制与增量输出 ```yanxu -HTML.属性(「href」,「/posts/1」); # 可用 -HTML.属性(「src」,「https://yanxu.dev/a.png」); # 可用 -HTML.属性(「href」,「javascript:alert(1)」); # 失败 +定 正文:文 为 HTML.渲染受限(页面,{ + 「最大深度」:32, + 「最大节点」:5000, + 「最大属性」:64, + 「最大输出字符」:1048576, + 「最大片段」:50000 +}); ``` -## 原始内容是信任升级 +`流式渲染(节点,写入法,限制)`逐片调用同步回调,不先构造完整正文,并返回节点、字符 +和片段统计。它不是事务:后续失败不会撤回已经发送的片段,上层应先完成认证、授权和 +数据读取,再决定何时发送首字节。 + +## 显式可信边界 ```yanxu -HTML.文字(「数据」); -HTML.原始(「可信源码」); +HTML.文字(「不可信文字」); +HTML.原始(「已审计常量」); +HTML.扩展(可信写出器); ``` -第一行输出可见标签文字,第二行输出真实元素。`原始`不做清洗,只用于已审计常量、可信结构化渲染器结果或经过专用清洗器处理的内容。不要把用户、数据库、接口或文件内容传给它。 +`原始`和`扩展`不会清洗内容,只适合源码常量或经过专用安全处理的输出。它们仍受统一 +输出预算约束。不要把用户、数据库、文件或网络内容直接传入。 + +## 错误、权限与限制 -这个库不解析 CSS、脚本或复杂 SVG,也不替代 CSP、HTTPS、Cookie 策略和业务授权。跨层防护见[安全边界](/ecosystem/web/security-roadmap/)。完整 API 与仓内安全文档可在[`yanxu-html/docs`](https://github.com/yanxulang/yanxu-html/tree/main/docs)查阅。 +`HTML.错误详情`和`HTML.尝试渲染`提供稳定`HTML_*`代码。库不申请文件、网络、监听、 +环境、进程或原生权限。 -## 下一步 +1.0 不解析/清洗现有 HTML,不提供 DOM、模板编译、CSS 清洗、CSP 或浏览器运行时;URL +策略不判断主机、重定向、同源和业务授权;流式接口不提供异步背压或取消。 -单独生成 HTML 时已经足够;常规页面优先阅读[言标](/ecosystem/web/yanbiao/),要返回网络响应则继续阅读[yanxu-http](/ecosystem/web/http/)和[言枢](/ecosystem/web/framework/)。 +仓库与完整 API:[yanxulang/yanxu-html](https://github.com/yanxulang/yanxu-html), +[1.0.0 Release](https://github.com/yanxulang/yanxu-html/releases/tag/v1.0.0)。 diff --git a/content/docs/ecosystem/web/http.mdx b/content/docs/ecosystem/web/http.mdx index 157267f..c1677a5 100644 --- a/content/docs/ecosystem/web/http.mdx +++ b/content/docs/ecosystem/web/http.mdx @@ -1,15 +1,26 @@ --- -title: yanxu-http:HTTP/1.1 基础 -description: 解析有预算边界的 HTTP/1.1 请求,并构造状态、首部、Cookie 与多种正文响应。 +title: 言讯:HTTP/1.1 服务端协议 +description: 使用 yanxu-http 1.0 安全解析请求、管理持久连接,并构造 Cookie、分块与升级响应。 --- -[`yanxu-http`](https://github.com/yanxulang/yanxu-http)负责服务端协议对象与 TCP 单请求连接。它不提供路由或 HTML 清洗,因而可以独立用于协议测试、教学或其他框架。 +`yanxu-http` 1.0.0(常用别名`http`,本文称“言讯”)是严格 HTTP/1.1 服务端协议库。 +它负责请求解析、响应编码和连接状态,不负责路由、中间件、TLS 或应用并发。 + +## 安装 + +最低言序为 1.1.6: ```sh -yanbao add http --version '^0.1' +yanbao add http --version '^1.0' +yanbao install +``` + +```toml +[依赖] +http = { 包 = "yanxu-http", git = "https://github.com/yanxulang/yanxu-http.git", 修订 = "v1.0.0", 版 = "^1.0" } ``` -## 解析请求 +## 离线解析与响应 ```yanxu 引「包:http」为 HTTP; @@ -24,42 +35,71 @@ yanbao add http --version '^0.1' 言 请求.路径; 言 请求.查询全部(「q」); 言 请求.Cookie值(「theme」); -言 请求.正文文字(); + +定 响应 为 HTTP.JSON响应({「ok」:真}) + .设首部(「cache-control」,「no-store」) + .添Cookie(HTTP.Cookie(「sid」,「opaque」).设安全(真).设同站(「Lax」)); + +定 线上字节 为 响应.编码(); ``` -`解析请求头`用于没有正文的请求;`解析请求`还会验证实际字节数与`Content-Length`完全一致。查询参数保留重复值,首部查询不区分大小写。 +首部名称按大小写不敏感处理,查询和表单保留重复值。响应编码器独占长度、分块、连接和 +`Set-Cookie`首部,并拒绝 NUL、CR、LF 注入。 -## 严格的 0.1 模型 +## 资源预算 -| 项目 | 行为 | -| --- | --- | -| 版本 | 仅 HTTP/1.1,必须有非空`Host`。 | -| 请求目标 | origin-form路径或`*`。 | -| 首部 | UTF-8、严格 CRLF,不支持折叠。 | -| 正文 | 仅单一合法`Content-Length`。 | -| 默认预算 | 首部 64 KiB、正文 4 MiB、超时 5 秒。 | -| 分块请求 | 出现`Transfer-Encoding`即明确拒绝。 | -| 连接 | 每个连接一个请求,响应固定关闭。 | +```yanxu +定 限制 为 HTTP.限制() + .设读取(3000,4096,32768,1048576) + .设首部项(64,8192,32) + .设分块(512,1024) + .设Multipart为(32,4096) + .设会话(50) + .验证(); +``` -这个子集避免在未实现的协议细节上进行宽松猜测。它仍然只是开发与教学基线,不包含 TLS、并发工作池、持久连接、流式正文或可信代理模型。 +限制覆盖读取超时、请求行、首部区、单首部、正文、分块、尾部、Multipart 和单连接 +请求数。冲突`Content-Length`、同时出现长度与传输编码、模糊编码、非法 Host 和走私形状 +都会在应用处理器之前失败。 -## 构造响应 +## 持久连接 ```yanxu -定 响应 为 HTTP.JSON响应({「ok」:真}); -响应.设首部(「cache-control」,「no-store」); -响应.添Cookie( - HTTP.Cookie(「sid」,「abc123」).设安全(真) -); -定 线上字节 为 响应.编码(); +定 会话 为 HTTP.连接会话为(连接,限制); + +试 则 + 当 真 则 + 定 请求 为 会话.读取下一请求(); + 若 (请求 是 空) 则 断;终 + 会话.发送响应(请求,HTTP.JSON响应({「path」:请求.路径})); + 若 非 会话.可复用() 则 断;终 + 终 +救 所误 则 + 言 HTTP.错误详情(所误)【「代码」】; +终 + +会话.关闭(); ``` -响应工厂包括`字节响应`、`文字响应`、`HTML响应`、`JSON响应`、`言据响应`、`状态响应`、`响应字节`和`重定向响应`。编码器统一管理`Content-Length`、`Connection`和`Set-Cookie`,并拒绝首部值中的 NUL、CR、LF。 +会话顺序处理 keep-alive 和流水线请求,保留超前读取字节,并支持临时 1xx、定长或分块 +响应。达到预算、请求或响应要求关闭、对端结束时不再复用。 + +## 表单、Cookie 与升级 + +- URL 编码表单把名称映射到值列。 +- Multipart 保留项顺序、重复字段、首部与文件字节,不自动落盘。 +- Cookie 支持`__Host-`、`__Secure-`、SameSite、Partitioned 和删除语义。 +- WebSocket 边界验证 RFC 6455 版本 13、Origin 与子协议,并移交套接字和已缓冲字节。 +- 升级通道不解析 WebSocket 帧、扩展、压缩或关闭握手。 -Cookie 默认`Path=/`、`HttpOnly`、`SameSite=Lax`。开发期`Secure`默认关闭,生产 HTTPS 会话必须显式打开;`SameSite=None`没有`Secure`时会失败。 +## 权限、错误与限制 -## 言据的边界 +库清单只声明回环 TCP 监听;部署应用必须在自己的清单授权实际地址。库不申请文件、 +出站网络、UDP、环境、进程或原生扩展权限。 -`言据响应`接收已经序列化的文字。先调用`yanju`的`言据.序列化(值)`,再交给 HTTP 层设置`application/vnd.yanxu.yanju; charset=utf-8`。这样数据格式实现不会复制进协议库。 +稳定错误使用`YANXUN_*`代码,可通过`HTTP.错误详情`读取。1.0 不实现 TLS、HTTP/2、 +HTTP/3、可信代理、并发调度、速率限制或应用背压;Multipart 是有界内存解析器。 -全部公开对象、错误类别与单次 TCP 示例见[`yanxu-http/docs`](https://github.com/yanxulang/yanxu-http/tree/main/docs)。要把请求交给配置、路由、中间件和言标页面,继续阅读[言枢](/ecosystem/web/framework/)。 +仓库与完整协议文档:[yanxulang/yanxu-http](https://github.com/yanxulang/yanxu-http), +[1.0.0 Release](https://github.com/yanxulang/yanxu-http/releases/tag/v1.0.0)。需要路由和 +中间件时继续阅读[言枢](/ecosystem/web/framework/)。 diff --git a/content/docs/ecosystem/web/index.mdx b/content/docs/ecosystem/web/index.mdx index 701974d..189b43e 100644 --- a/content/docs/ecosystem/web/index.mdx +++ b/content/docs/ecosystem/web/index.mdx @@ -1,69 +1,71 @@ --- -title: 言序 Web 开发 -description: 从言枢与言标开始,理解框架、模板、安全 HTML、HTTP 和完整博客的职责边界。 +title: Web 与网络 +description: 用言枢、言访、言讯、言页与言标组成稳定 1.0 的服务端和客户端网络栈。 --- -言序 Web 栈以“言枢”作为应用入口,以内建子项目“言标”表达页面,并继续让`yanxu-html`和`yanxu-http`分别守住底层输出与协议边界。 - -```sh -yanbao add web --version '^0.2' -``` +言序 Web 与网络栈已经进入稳定 1.0:言讯负责严格 HTTP/1.1 服务端协议,言访负责 +HTTP/HTTPS 客户端,言页负责默认安全的 HTML 构造,言枢把路由、中间件、言据、会话、 +CSRF、静态文件和测试传输组合成应用框架。言标继续作为言枢内建的自动转义模板语言。 ```text -yanxu-webblog 言枢配置 · 言标页面 · JSON/言据 · 静态文件 - │ - ▼ -言枢(yanxu-web) 应用 · 命名路由 · 中间件 · 测试客户端 - │ - ├── 言标 自动转义 · 条件/循环 · 包含/继承 - ├── yanxu-html 安全节点 · 属性与 URL 规则 - └── yanxu-http HTTP/1.1 请求 · 响应 · Cookie + ┌─ 言访 1.0:客户端 · 重试 · Mock · 回放 +应用与测试 ─────────┤ + └─ 言枢 1.0:路由 · 中间件 · 会话 · CSRF + │ + ┌────────────┼────────────┐ + ▼ ▼ ▼ + 言讯 1.0 言页 1.0 言据 1.2 + HTTP/1.1 协议 安全 HTML 数据交换 ``` - - - - - + + + + + + -## 如何选择 - -| 目标 | 从哪里开始 | -| --- | --- | -| 建立常规服务端 Web 项目 | 使用[言枢](/ecosystem/web/framework/)和[言标](/ecosystem/web/yanbiao/)。 | -| 生成邮件、静态片段或底层 HTML 节点 | 直接使用[yanxu-html](/ecosystem/web/html/)。 | -| 学习协议解析或自建服务器 | 直接使用[yanxu-http](/ecosystem/web/http/)。 | -| 改造一个可运行项目 | 克隆[言枢博客](/ecosystem/web/webblog/)。 | -| 从 0.1 升级 | 阅读[0.2 迁移指南](/ecosystem/web/migration-0.2/)。 | - -## 0.2 的重点 +## 稳定版本 -言枢 0.2 新增集中配置、命名路由、反向 URL、路由组、自定义 404/405、JSON 正文、言标模板响应和无端口测试客户端。言标支持`{{ 路径 }}`自动转义插值、中文条件与循环、包含、模板继承、过滤器和显式可信 HTML。 +| 仓库 | 包名 | 版本 | 最低言序 | 直接依赖 | +| --- | --- | ---: | ---: | --- | +| `yanxu-http` | `yanxu-http`(常用别名 `http`) | 1.0.0 | 1.1.6 | 无 | +| `yanxu-request` | 言访(常用别名 `request`) | 1.0.0 | 1.1.11 | 言据、言韧、言录 | +| `yanxu-html` | 言页 | 1.0.0 | 1.1.12 | 无 | +| `yanxu-web` | `yanxu-web`(常用别名 `web`) | 1.0.0 | 1.1.12 | 言据、言页、言讯、言访 | -技术包标识和仓库仍为`yanxu-web`,源码建议使用: +安装稳定标签,而不是默认分支: -```yanxu -引「包:web」为 言枢; +```sh +yanbao add http --version '^1.0' +yanbao add request --version '^1.0' +yanbao add html --package 言页 --version '^1.0' +yanbao add web --version '^1.0' +yanbao install ``` -## 推荐学习顺序 +## 如何选择 + +| 目标 | 使用 | +| --- | --- | +| 调用外部 HTTP/HTTPS 服务 | [言访](/ecosystem/web/request/) | +| 编写服务端协议适配器或自建监听器 | [言讯](/ecosystem/web/http/) | +| 构造邮件、静态片段或安全服务端 HTML | [言页](/ecosystem/web/html/) | +| 构建有路由、中间件和应用安全边界的 Web 服务 | [言枢](/ecosystem/web/framework/) | +| 在测试中让言访直接调用言枢而不监听端口 | 言枢的`言访测试`导出 | -1. [言枢框架](/ecosystem/web/framework/):创建配置化应用与命名路由; -2. [言标模板](/ecosystem/web/yanbiao/):理解自动转义、控制流和继承; -3. [安全 HTML](/ecosystem/web/html/):理解更低层的节点与信任升级; -4. [HTTP/1.1](/ecosystem/web/http/):理解请求、响应和连接预算; -5. [完整博客](/ecosystem/web/webblog/):沿真实项目阅读所有层; -6. [安全与路线图](/ecosystem/web/security-roadmap/):确认生产边界。 +言访和言讯没有循环依赖:客户端使用言序标准网络传输,服务端协议类型由言讯独立维护。 +言枢同时依赖两者,只在应用层提供无端口适配。 -## 运行参考项目 +## 生产边界 -```sh -git clone https://github.com/yanxulang/yanxu-web.git -git clone https://github.com/yanxulang/yanxu-webblog.git -yanbao install --manifest-path yanxu-web -yanbao install --manifest-path yanxu-webblog -``` +- 言讯只实现 HTTP/1.1 服务端协议,不终止 TLS,也不提供 HTTP/2、HTTP/3 或并发调度。 +- 言枢内建服务器用于开发与集成;生产环境应由成熟前置服务器终止 TLS 并实施限流。 +- 言访标准传输使用统一总超时和有界缓冲;细分超时、逐跳重定向控制等能力必须由 + 如实声明的自定义传输提供。 +- 言页的`原始`和`扩展`是显式信任升级,不是 HTML 清洗器。 +- 认证、授权、密钥管理、业务 Schema 和隐私留存仍由最终应用负责。 -核心运行时本身仍不内置 Web 框架;这些能力以独立包演进。言包锁文件固定精确 Git 修订和内容校验,依赖包的宿主权限不会自动传递给应用。 +继续按任务阅读各库页面;完整 21 库目录见[第三方库](/ecosystem/libraries/)。 diff --git a/content/docs/ecosystem/web/meta.json b/content/docs/ecosystem/web/meta.json index ea44620..73fa3d1 100644 --- a/content/docs/ecosystem/web/meta.json +++ b/content/docs/ecosystem/web/meta.json @@ -1,17 +1,20 @@ { "title": "Web 与网络", - "description": "使用言枢、言标、安全 HTML 与 HTTP/1.1 构建言序网络应用。", + "description": "使用言枢、言访、言讯、言页与言标构建稳定的言序 Web 和 HTTP 应用。", "pages": [ "index", "---框架与模板---", "framework", "yanbiao", - "migration-0.2", + "migration-1.0", + "request", "---基础类库---", "html", "http", "---完整示例---", "webblog", - "security-roadmap" + "security-roadmap", + "---历史迁移---", + "migration-0.2" ] } diff --git a/content/docs/ecosystem/web/migration-1.0.mdx b/content/docs/ecosystem/web/migration-1.0.mdx new file mode 100644 index 0000000..b26baee --- /dev/null +++ b/content/docs/ecosystem/web/migration-1.0.mdx @@ -0,0 +1,44 @@ +--- +title: 迁移到 Web 1.0 +description: 把言枢、言讯与言页的 0.x 依赖升级到稳定标签,并采用新的预算、安全和测试边界。 +--- + +Web 栈的 1.0 升级不是只改版本号。先固定公开标签、重建锁文件,再逐层处理包名、响应、 +资源预算和测试边界。 + +## 更新依赖 + +```toml +[依赖] +web = { 包 = "yanxu-web", git = "https://github.com/yanxulang/yanxu-web.git", 修订 = "v1.0.0", 版 = "^1.0" } +``` + +言枢会锁定言据 1.2、言页 1.0、言讯 1.0 和言访 1.0。不要继续使用`main`或`^0.2`: + +```sh +yanbao update --dry-run +yanbao update +yanbao check +yanbao test +yanbao build --release +yanbao audit +``` + +## 需要检查的变化 + +1. 言页的稳定包名是`言页`,普通文字必须经过`文字`节点;`原始`仍是可信边界。 +2. 言讯 1.0 支持持久连接、分块、Multipart 和 WebSocket 握手;应用仍负责并发、TLS 和帧层。 +3. 言枢请求/响应、路由、中间件、会话、CSRF、静态文件和 OpenAPI 都受显式预算约束。 +4. JSON 与言据是不同媒体类型;使用`言据值响应`或受限解析,不按正文形状猜测。 +5. 会话存储改为显式协议,生产存储必须实现并发、过期、容量和观测策略。 +6. 无端口测试可使用言枢测试客户端,或把`包:web/言访测试`交给言访。 + +## 发布前回归 + +- 覆盖 404、405、无效正文、超限正文、异常中间件和错误处理器。 +- 覆盖 Session Cookie、CSRF 来源和令牌、静态路径穿越、条件请求与单范围响应。 +- 覆盖 JSON/言据内容协商、不可接受的`Accept`和结构化错误代码。 +- 对生产配置复核文件、监听和出站网络权限;框架依赖不会替应用取得授权。 + +历史 0.1 → 0.2 变化仍可查阅[旧迁移记录](/ecosystem/web/migration-0.2/),但新项目应直接 +以 1.0 页面和公开标签为准。 diff --git a/content/docs/ecosystem/web/request.mdx b/content/docs/ecosystem/web/request.mdx new file mode 100644 index 0000000..2273da4 --- /dev/null +++ b/content/docs/ecosystem/web/request.mdx @@ -0,0 +1,96 @@ +--- +title: 言访:HTTP 客户端 +description: 使用言访 1.0 构造有界 HTTP/HTTPS 请求、发送 JSON/言据、重试并注入测试传输。 +--- + +言访(`yanxu-request`)1.0.0 是现代 HTTP 客户端与请求构建库。它提供真实 HTTP/HTTPS +传输,也允许用 Mock、自定义、录制和严格回放传输替换网络;非 2xx 响应仍是正常响应, +只有显式调用`确保成功`才会转成状态错误。 + +## 安装 + +最低言序为 1.1.11: + +```sh +yanbao add request --version '^1.0' +yanbao install +``` + +等价的格式 2 清单会固定稳定标签: + +```toml +[依赖] +request = { 包 = "言访", git = "https://github.com/yanxulang/yanxu-request.git", 修订 = "v1.0.0", 版 = "^1.0" } +``` + +## 可复用客户端 + +```yanxu +引「包:request」为 言访; + +定 API 为 言访.客户端() + .基础地址(「https://api.example.com/v1/」) + .默认首部({「accept」:「application/json」}) + .总超时(8000) + .最大响应字节(2097152) + .重试状态(【429,502,503,504】); + +定 响应 为 API.取(「users/42」) + .请求编号(「get-user-42」) + .发送() + .确保成功(); + +定 用户:典 为 响应.解析JSON(); +``` + +支持 GET、POST、PUT、PATCH、DELETE、HEAD、OPTIONS 和自定义方法。查询值列会编码为 +重复键;正文支持文字、字节、JSON、规范言据、表单、Multipart 和文件。 + +## 言据与认证 + +```yanxu +定 资料 为 API.发文(「profiles」) + .Bearer认证(令牌) + .言据正文({「姓名」:「子衿」,「等级」:1}) + .发送() + .确保成功() + .解析言据(); +``` + +言据正文使用版本化媒体类型和 UTF-8;普通 JSON 不会被误识别为言据。Basic 用户名、 +Bearer、Cookie 和首部都拒绝控制字符。受控重定向跨协议、主机或有效端口时自动移除 +`authorization`、`cookie`与`proxy-authorization`。 + +## 重试、中间件和传输 + +- 默认只重试幂等方法,并复用言韧的退避、抖动和最大尝试策略。 +- 识别 408、425、429、500、502、503、504 和可重试网络错误,尊重`Retry-After`。 +- 请求前、请求后、错误、重试前和重定向前钩子具有稳定顺序并可移除。 +- 言录集成默认脱敏认证、Cookie、令牌和 API 密钥类首部。 +- Mock 适合单元测试;录制文档可能含原始正文和首部,不应提交生产流量。 + +无端口调用言枢: + +```yanxu +引「包:web/言访测试」为 言访测试; + +定 客户端 为 言访测试.客户端(应用); +定 响应 为 客户端.发文(「/profiles」) + .言据正文({「姓名」:「子衿」}) + .发送() + .确保成功(); +``` + +## 权限与限制 + +库清单不申请网络或文件权限。最终应用必须授权真实目标主机;上传和下载路径另需文件 +权限。Mock、录制解析和回放不需要网络。 + +- 标准传输固定最多自动跟随 5 次重定向,不支持关闭、定制次数、逐跳历史或重复响应首部。 +- 连接、读取和写入细分超时只可在声明对应能力的自定义传输上设置。 +- 网络响应先在`最大响应字节`内完整缓冲;`分块消费`和`下载到`不是套接字级流式下载。 +- 不实现缓存、Cookie 罐、代理配置、客户端证书、HTTP/2/3 或 WebSocket。 + +错误使用`YANFANG_*`或标准网络`NET_*`代码;程序应读取`言访.错误详情`,不要匹配消息。 +仓库与完整 API:[yanxulang/yanxu-request](https://github.com/yanxulang/yanxu-request), +[1.0.0 Release](https://github.com/yanxulang/yanxu-request/releases/tag/v1.0.0)。 diff --git a/content/docs/ecosystem/web/security-roadmap.mdx b/content/docs/ecosystem/web/security-roadmap.mdx index 82770eb..d16eeeb 100644 --- a/content/docs/ecosystem/web/security-roadmap.mdx +++ b/content/docs/ecosystem/web/security-roadmap.mdx @@ -1,39 +1,72 @@ --- -title: Web 栈安全与路线图 -description: 理解言枢、言标、HTML、HTTP 和示例项目的跨层安全责任与生产边界。 +title: Web 栈安全与生产边界 +description: 审计言枢、言访、言讯、言页与言标 1.0 的跨层安全责任、信任升级点和部署边界。 --- -Web 安全不是单一开关。每层只保证自己能验证的边界。 +Web 安全不是单一开关。稳定 1.0 的每一层只承诺自己能够验证的边界;最终应用仍需把 +认证、授权、密钥、数据和部署策略连成完整威胁模型。 -| 层 | 已提供 | 应用仍需负责 | +| 层 | 1.0 已提供 | 应用仍需负责 | | --- | --- | --- | -| 言标 | 插值自动转义、显式可信 HTML、模板路径/大小/深度/循环限制。 | 可信内容来源、CSS/脚本/SVG策略。 | -| `yanxu-html` | 节点/属性转义、名称白名单、危险 URL 协议拒绝。 | CSP、复杂内容清洗与业务 URL 规则。 | -| `yanxu-http` | 严格请求行/首部、长度预算、首部注入防护、安全 Cookie 约束。 | TLS、会话、速率限制、可信代理。 | -| 言枢 | 参数编码、404/405/500、返回类型、静态路径、默认安全首部。 | 认证授权、CSRF、领域校验、日志脱敏。 | -| 言枢博客 | 自动转义、命名路由、404/405 和目录穿越回归。 | 持久存储、账户、备份和生产运维。 | +| 言标 | 普通插值自动转义,模板路径、大小、深度和循环预算。 | 可信内容来源、CSS/脚本/SVG 策略。 | +| 言页 | 节点与属性转义、保守 URL 协议、资源预算和安全渲染。 | CSP、富文本清洗、业务 URL 规则。 | +| 言讯 | 严格 HTTP/1.1、冲突分帧与首部注入防护、keep-alive、分块、Multipart、Cookie 和升级握手边界。 | TLS、并发调度、速率限制、可信代理和 WebSocket 帧。 | +| 言访 | 有界请求与响应、凭据校验、跨来源重定向脱敏、重试约束、Mock/回放和日志脱敏。 | 目标主机授权、密钥轮换、录制数据保管和响应业务校验。 | +| 言枢 | 请求/响应预算、安全首部、路径隔离、会话协议、同源双提交 CSRF、错误隐藏和无端口测试。 | 身份认证、业务授权、生产会话存储、审计、限流和隐私治理。 | -## 可信 HTML +## 显式信任升级 -`言枢.言标.信任HTML`与`HTML.原始`都是显式信任升级,不是清洗器。代码审查应搜索所有使用点,确认输入是源码常量、可信结构化生成结果或经过上下文正确的专用清洗器处理。 +`言枢.言标.信任HTML`、`言页.原始`和言页扩展节点都绕过部分默认防护,不是清洗器。 +代码审查应搜索全部调用点,只允许源码常量、可信结构化生成结果,或经过适合当前 HTML +上下文的专用清洗器处理的内容。不要直接信任用户输入、数据库富文本、Markdown 原始 +HTML 或第三方响应。 -## 当前服务器 +OpenAPI 提供器也是信任边界:言枢限制登记方式、复制和输出大小,但不替完整的 OpenAPI +3.1 语义校验。发布规范前应检查示例、默认值、内部主机和敏感描述。 -0.2 仍只实现串行 HTTP/1.1,每连接一个请求并固定`Connection: close`。它不支持请求流、并发工作池、背压、优雅关闭、长连接或生产代理信任。 +## 服务端边界 -即使放在反向代理后,后端仍有串行容量与慢请求风险。它适合本地开发、教学、协议基线和集成验证,不是通用生产服务器。 +言讯 1.0 的`HTTP连接`能够在单个连接上顺序处理 keep-alive、流水线、分块正文和分块 +响应;它不负责接受循环、跨连接并发、TLS、代理信任或应用背压。 -## Cookie 与会话 +言枢内建服务器的边界更窄:它串行接受连接,每连接处理一个请求后关闭。它适合开发、 +教学和集成验证,不是通用公网服务器。生产部署应使用成熟前置服务器终止 TLS、限制连接 +与速率,再把请求交给受控应用宿主;只有明确配置并验证时才信任转发首部。 -HTTP Cookie 默认`HttpOnly`和`SameSite=Lax`,但回环开发所需的`Secure`默认关闭。HTTPS 部署必须显式打开`Secure`。言枢不自动生成或存储会话;应用须建立安全随机、签名/存储、轮换、过期和撤销策略。 +## 会话、Cookie 与 CSRF -## 演进顺序 +言枢 1.0 已提供显式会话存储协议、编号轮换、空闲过期和安全 Cookie 配置。生产存储仍须 +提供原子写入、并发更新策略、过期清扫、容量监控和静态数据保护。HTTPS 部署应使用 +`Secure`、`HttpOnly`、合适的`SameSite`与尽可能严格的`__Host-`Cookie。 -1. 请求分块传输与严格总预算; -2. Multipart 文件数、单项/总量和临时文件策略; -3. 持久连接、空闲超时和每连接请求预算; -4. WebSocket 帧、掩码、控制帧和背压; -5. HTTP/2 流状态、HPACK 与流控; -6. HTTP/3、QUIC、QPACK、证书与 0-RTT 策略。 +CSRF 中间件检查 Fetch Metadata、Origin/Referer 和双提交令牌。它只保护浏览器发起的 +跨站状态变更,不能替代登录、授权、HTTPS 或防重放业务令牌。多源部署、跨站 API 和 +非浏览器客户端都需要单独配置并审计。 -每项都必须先补威胁模型、协议负例、资源预算和真实客户端互操作。当前可运行基线是[言枢博客](/ecosystem/web/webblog/)。 +## 出站请求 + +言访标准传输会执行运行时网络权限与证书校验,并对跨协议、主机或有效端口的受控重定向 +移除认证和 Cookie 首部。最终应用仍应只授权必要主机,限制响应大小,并在解析 JSON、 +言据或文件前验证媒体类型和业务 Schema。 + +录制回放文件可能含原始正文、首部和个人数据,应按凭据保管,不要把生产流量直接提交到 +仓库。Mock 和无端口言枢传输不会验证 DNS、TLS、证书、代理或真实网络超时,不能替代 +隔离环境中的网络集成测试。 + +## 上线检查 + +- 在顶层项目清单中只授予实际监听地址、目标主机和文件根。 +- 由成熟前置服务器提供 TLS、连接预算、速率限制和可信代理配置。 +- 为请求正文、响应正文、上传项、模板、静态文件和第三方响应设置上限。 +- 对认证、授权、会话、CSRF、重定向和错误路径分别建立负例测试。 +- 默认不记录 Authorization、Cookie、会话编号、CSRF 令牌、数据库密码或个人字段。 +- 用真实客户端验证代理、Cookie、缓存、范围请求和跨来源行为。 + +## 尚未承诺的能力 + +稳定 1.0 不承诺生产级并发服务器、套接字级流式上传或下载、WebSocket 帧协议、HTTP/2、 +HTTP/3、自动代理信任、分布式会话或全自动 OpenAPI Schema 反射。后续能力只有在威胁 +模型、资源预算、协议负例和互操作测试同时完成后,才应进入稳定兼容面。 + +从[言枢](/ecosystem/web/framework/)、[言访](/ecosystem/web/request/)、 +[言讯](/ecosystem/web/http/)和[言页](/ecosystem/web/html/)继续查看各层 API 与限制。 diff --git a/content/docs/ecosystem/yanju/format.mdx b/content/docs/ecosystem/yanju/format.mdx index 403a5f1..4ad2dab 100644 --- a/content/docs/ecosystem/yanju/format.mdx +++ b/content/docs/ecosystem/yanju/format.mdx @@ -1,6 +1,6 @@ --- title: 格式与文件 -description: 言据 v1 的值模型、文法约束、文字转义、格式化与文件读写。 +description: 言据 v1 的值模型、文法、规范序列化、受限解析与 UTF-8 文件读写。 --- 一个`.yj`文档恰好包含一个根值。空白可以出现在结构符号之间,但据键必须是文字,同一据内不得有重复键,列和据都不接受尾随逗号。 @@ -61,12 +61,26 @@ description: 言据 v1 的值模型、文法约束、文字转义、格式化与 `解析据`和`解析列`在根类型不符时抛出`YJ_TYPE`。`序列化`按键的文字顺序输出据,并移除所有非必要结构空白,因此相同值能得到稳定文本。 +解析不可信文字时使用显式预算: + +```yanxu +定 配置 为 言据.解析受限(原文,{ + 「最大字符」:65536, + 「最大深度」:24, + 「最大容器项」:4096 +}); +``` + +`默认限制`返回库默认值,`规范限制`补齐省略项并拒绝未知键、非正整数和超出硬上限的 +配置。`解析`、`校验`与普通文件读取也使用默认限制,不会无界处理输入。 + 处理已有文档时,`格式化(原文,宽)`会先完整解析再重新输出,宽度必须是 0 至 8 的整数;`压缩(原文)`等同以宽度 0 重新格式化。格式错误不会被格式化器掩盖。 ## 文件读写 ```yanxu 定 配置:典 为 言据.读取据(「config/app.yj」); +定 受限配置 为 言据.读取受限(「config/external.yj」,{「最大字符」:65536}); 言据.写入(「build/app.yj」,配置); 言据.美写(「build/app.pretty.yj」,配置); ``` diff --git a/content/docs/ecosystem/yanju/index.mdx b/content/docs/ecosystem/yanju/index.mdx index 1a1395b..b6ced28 100644 --- a/content/docs/ecosystem/yanju/index.mdx +++ b/content/docs/ecosystem/yanju/index.mdx @@ -1,56 +1,42 @@ --- -title: 言据 -description: 言序生态的统一配置与数据交换格式,以及它的纯言序标准库。 +title: 言据 1.2 +description: 使用言序生态的规范数据格式完成受限解析、校验、路径、补丁、流、HTTP 与数据库交换。 --- -言据(YanJu)是言序生态的配置与数据交换格式,文件扩展名为`.yj`。它使用与 JSON 相同的递归值模型,但以`据`、`列`、`真`、`假`、`空`和全角结构符号表达数据。解析、序列化与文件接口由言序自身实现。 +言据(YanJu)1.2.0 是言序生态的配置与数据交换基础,文件扩展名为`.yj`。它使用与 JSON +相同的递归值模型,但以`据`、`列`、`真`、`假`、`空`和全角结构符号表达数据。解析器、 +序列化器、校验和补丁全部由言序实现;JSON 只在显式转换时参与。 ```yanju 据【 「项目」:「言据」, - 「后缀」:「.yj」, 「格式版本」:1, - 「实现语言」:「言序」, + 「库版本」:「1.2.0」, 「稳定」:真, - 「能力」:列【「配置」,「交换」,「流」】, - 「备注」:空 + 「能力」:列【「配置」,「补丁」,「流」,「HTTP」,「数据库」】 】 ``` - - - - + + + + -## 何时使用 +## 安装稳定标签 -- 为言序应用保存可读、可稳定比较的配置; -- 在言序程序、命令行和服务之间交换结构化数据; -- 用声明式规则检查外部配置,而不是在业务代码中反复判断类型; -- 通过逐行帧处理日志、事件或进程管道; -- 需要与 JSON 或配置型 TOML 无损互转的场景。 - -格式版本与库版本分别演进。言据 1.1 标准库仍生成格式 v1 文档,因此 1.0 读取方可以读取它产生的普通`.yj`文件;校验规则和言据流是格式之上的独立协议。 - -## 添加依赖 - -在应用的`言序.toml`中声明 Git 依赖。省略修订时,第一次安装从默认分支解析,并由`言序.lock`固定到精确提交: +最低言序为 1.1.6。格式版本与库版本独立演进:1.2.0 继续读写格式 v1。 ```toml [依赖] -言据 = { git = "https://github.com/yanxulang/yanju.git", 版 = "^1.1" } +言据 = { git = "https://github.com/yanxulang/yanju.git", 修订 = "v1.2.0", 版 = "^1.2" } ``` -使用言包安装并生成锁文件: - ```sh yanbao install ``` -随后从包入口导入: - ```yanxu 引「包:言据」为 言据; @@ -59,8 +45,48 @@ yanbao install 言 言据.美化(配置); ``` -日常升级应显式执行`yanbao update`并审阅锁文件变化。包管理与复现语义见[言包指南](/tooling/package-manager/)。 +## 1.2 稳定能力 + +- 确定性规范序列化、UTF-8 文件读写和格式检查。 +- 类型/联合、枚举、范围、长度、列项目、必需键与额外键 Schema。 +- 路径查询、不可变设定、深复制、稳定比较和递归合并。 +- 稳定差异以及校验旧值的`应用补丁`,冲突返回`YJ_PATCH_CONFLICT`。 +- 字符、容器深度和全树容器项三维解析限制,并覆盖流式帧。 +- JSON 和配置型 TOML 显式互转,不静默丢失无法表示的类型。 +- 版本化 HTTP 媒体类型,不把`application/json`误认成言据。 +- 跨数据库规范 TEXT/CLOB 边界;原生 JSON 仍是另一种表示。 +- 稳定`YJ_*`错误详情,程序不必解析中文消息。 + +## 处理不可信输入 + +默认上限是 1,048,576 个 Unicode 字符、64 层容器和全树 100,000 个容器项。服务端应 +按协议收紧: + +```yanxu +定 值 为 言据.解析受限(正文,{ + 「最大字符」:65536, + 「最大深度」:24, + 「最大容器项」:4096 +}); +``` + +未知限制键或非正整数返回`YJ_LIMIT_CONFIG`;超限分别返回`YJ_SIZE`、`YJ_DEPTH`和 +`YJ_ITEMS`。 + +## HTTP 与数据库 + +推荐媒体类型为`application/vnd.yanxu.yanju; version=1`。发送前使用`序列化`,接收时 +先检查`是言据媒体类型`,再进行受限解析。 + +所有数据库都可以用`数据库文本`写入确定的 TEXT/CLOB,并用`自数据库文本`恢复。 +`自规范数据库文本`用于审计存量内容是否已经规范。PostgreSQL JSONB、MySQL JSON 和 +SQLite JSON1 是结构化存储策略,不等同于`.yj`文本。 + +## 权限与限制 -## 版本与仓库 +内存、HTTP 媒体类型和数据库文本转换不申请权限;文件接口需要最终应用授权实际路径。 +格式 v1 不接受指数、NaN 或无穷值;流协议是一行一帧;TOML 只覆盖无损配置子集;项目 +媒体类型尚未向 IANA 注册。 -当前文档对应言据标准库`1.1.0`和格式 v1。源码、完整规范、API 文档、测试与性能基准位于[言据仓库](https://github.com/yanxulang/yanju)。 +源码、规范、API 与 Release:[yanxulang/yanju](https://github.com/yanxulang/yanju), +[v1.2.0](https://github.com/yanxulang/yanju/releases/tag/v1.2.0)。 diff --git a/content/docs/ecosystem/yanju/meta.json b/content/docs/ecosystem/yanju/meta.json index a9ee69f..0497802 100644 --- a/content/docs/ecosystem/yanju/meta.json +++ b/content/docs/ecosystem/yanju/meta.json @@ -1,6 +1,6 @@ { "title": "言据", - "description": "使用言据保存配置、交换数据并处理结构化数据流。", + "description": "使用言据 1.2 安全解析、校验、补丁、交换并持久化结构化数据。", "pages": [ "index", "---格式与配置---", diff --git a/content/docs/ecosystem/yanju/operations-conversion.mdx b/content/docs/ecosystem/yanju/operations-conversion.mdx index 55edca5..5edce05 100644 --- a/content/docs/ecosystem/yanju/operations-conversion.mdx +++ b/content/docs/ecosystem/yanju/operations-conversion.mdx @@ -1,6 +1,6 @@ --- title: 数据处理与转换 -description: 深层比较、递归合并、结构化差异以及 JSON 和配置型 TOML 互转。 +description: 深层比较、递归合并、可校验补丁以及 JSON、TOML 和数据库文本互转。 --- 言据标准库的数据操作都以言序原生值为输入,不要求先把值重新序列化成文字。 @@ -50,7 +50,20 @@ description: 深层比较、递归合并、结构化差异以及 JSON 和配置 言 言据.美化(各差); ``` -差异适合审计、测试和展示变化,但 1.1 没有定义自动应用差异的补丁协议。 +差异适合审计、测试、展示变化,也可以作为 1.2 补丁入口的稳定输入。 + +## 校验并应用补丁 + +1.2 的`应用差异`与`应用补丁`重放`差异`结果。修改和删除前会比较补丁中的`旧值`, +避免静默覆盖已经变化的基础数据: + +```yanxu +定 各差:列 为 言据.差异(旧配置,新配置); +定 已重放 为 言据.应用补丁(旧配置,各差); +``` + +路径、操作、字段或旧值不合法返回`YJ_PATCH`;旧值冲突返回`YJ_PATCH_CONFLICT`。冲突时 +重新读取基础值并计算新差异,不应移除旧值检查强行覆盖。重放深复制结果,不改写输入。 ## JSON 互转 @@ -79,4 +92,15 @@ TOML 接口面向常见配置,支持单行键值、表、点分键、基础和 日期时间、非十进制数、指数、多行文字、表列以及 TOML 无法表示的`空`会产生`YJ_TOML`,不会静默改写或丢失类型。需要保存言据的完整值模型时,应继续使用`.yj`。 +## 数据库规范文本 + +```yanxu +定 入库文:文 为 言据.数据库文本(值); +定 出库值 为 言据.自数据库文本(入库文); +定 已审计值 为 言据.自规范数据库文本(入库文); +``` + +第一项生成确定的`.yj`文本,适合所有数据库的 TEXT/CLOB。严格入口在文本不是规范形式 +时返回`YJ_CANONICAL`。数据库原生 JSON 是另一种结构化表示,导出`.yj`时仍要规范序列化。 + 持续传输多个值时,不要手工拼接容器,使用[言据流](/ecosystem/yanju/streams-errors/)。 diff --git a/content/docs/ecosystem/yanju/streams-errors.mdx b/content/docs/ecosystem/yanju/streams-errors.mdx index 5847411..83d7dd0 100644 --- a/content/docs/ecosystem/yanju/streams-errors.mdx +++ b/content/docs/ecosystem/yanju/streams-errors.mdx @@ -1,6 +1,6 @@ --- title: 流与结构化错误 -description: 增量处理换行分帧言据,并使用稳定错误代码诊断解析和数据操作失败。 +description: 有界增量处理换行分帧言据,并使用稳定错误代码诊断解析、补丁和资源失败。 --- 言据流 v1 面向日志、进程管道和持续事件传输。它使用换行分帧:每个非空行恰好包含一个完整言据值,空行被忽略。 @@ -25,6 +25,19 @@ description: 增量处理换行分帧言据,并使用稳定错误代码诊断 定 末批:列 为 所流.结束(); ``` +处理外部流时应在送入第一块之前设置每帧限制: + +```yanxu +定 所流 为 言据.流式解析器().限制({ + 「最大字符」:8192, + 「最大深度」:16, + 「最大容器项」:1024 +}); +``` + +开始送入后不能更改限制。未结束帧持续增长到字符上限时立即返回`YJ_SIZE`,不等待换行; +每个完成帧还会独立检查深度和容器项总数。 + `结束`提交无换行的最后一帧并封闭解析器。封闭后再次送入或结束会抛出`YJ_STREAM`。帧解析失败时,`输入位置`是从 1 开始的物理帧行号。 已经持有完整文字时可以使用便捷接口: @@ -73,4 +86,7 @@ description: 增量处理换行分帧言据,并使用稳定错误代码诊断 | `源位置` | 言序运行时记录的源码位置 | | `踪迹` | 调用踪迹 | -常用代码包括`YJ_PARSE`、`YJ_TYPE`、`YJ_SCHEMA`、`YJ_PATH`、`YJ_MERGE`、`YJ_FORMAT`、`YJ_SERIALIZE`、`YJ_JSON`、`YJ_TOML`、`YJ_STREAM`和`YJ_IO`。程序应判断`代码`,不要解析可能随版本改进的`消息`。 +常用代码包括`YJ_PARSE`、`YJ_TYPE`、`YJ_SCHEMA`、`YJ_PATH`、`YJ_MERGE`、 +`YJ_PATCH`、`YJ_PATCH_CONFLICT`、`YJ_LIMIT_CONFIG`、`YJ_SIZE`、`YJ_DEPTH`、 +`YJ_ITEMS`、`YJ_CANONICAL`、`YJ_JSON`、`YJ_TOML`、`YJ_STREAM`和`YJ_IO`。程序应判断 +`代码`,不要解析可能随版本改进的`消息`。 diff --git a/content/docs/guides/web-application.mdx b/content/docs/guides/web-application.mdx index 6cfa84e..9315bff 100644 --- a/content/docs/guides/web-application.mdx +++ b/content/docs/guides/web-application.mdx @@ -1,16 +1,32 @@ --- title: Web 应用 -description: 在明确当前协议范围与安全责任后选择言枢、言标、HTML 或 HTTP 层。 +description: 在稳定 1.0 网络栈中选择言枢、言访、言标、言页或言讯,并明确生产安全责任。 --- -言序核心不内置 Web 框架。当前生态把职责分成四层:言枢组织应用、路由与中间件;言标提供自动转义模板;`yanxu-html`提供安全节点;`yanxu-http`提供受预算约束的 HTTP/1.1 基础。 +言序核心不内置 Web 框架。稳定生态把职责拆成可独立选择的层:言枢组织服务端应用、 +路由与中间件;言访调用外部 HTTP/HTTPS 服务;言标提供自动转义模板;言页构造安全 +HTML;言讯提供受预算约束的 HTTP/1.1 服务端协议对象。 建议顺序: 1. 阅读[Web 与网络总览](/ecosystem/web/),确认目标是否在当前支持范围内。 2. 常规服务端应用从[言枢](/ecosystem/web/framework/)和[言标](/ecosystem/web/yanbiao/)开始。 -3. 只有需要底层节点或协议对象时,再进入[安全 HTML](/ecosystem/web/html/)或[HTTP/1.1](/ecosystem/web/http/)。 -4. 沿[言枢博客](/ecosystem/web/webblog/)运行页面、JSON、言据 API、静态文件和测试。 -5. 发布前逐项检查[安全边界](/ecosystem/web/security-roadmap/)。 +3. 调用外部 API 或编写可注入的客户端测试时使用[言访](/ecosystem/web/request/)。 +4. 只有需要底层节点或协议对象时,再进入[言页](/ecosystem/web/html/)或[言讯](/ecosystem/web/http/)。 +5. 沿[言枢博客](/ecosystem/web/webblog/)运行页面、JSON、言据 API、静态文件和测试。 +6. 发布前逐项检查[安全与生产边界](/ecosystem/web/security-roadmap/)。 -这些库不替代 TLS 终止、CSP、Cookie 策略、身份认证、业务授权、限流和生产级反向代理。网络外连与监听权限必须在顶层项目清单中显式声明。 +安装时锁定稳定发布线,不依赖默认分支: + +```sh +yanbao add web --version '^1.0' +yanbao add request --version '^1.0' +yanbao install +``` + +言枢 1.0 最低需要言序 1.1.12;言访 1.0 最低需要 1.1.11。言枢会传递锁定言据 1.2、 +言页 1.0、言讯 1.0 和言访 1.0,普通服务端项目无需重复声明这些内部依赖。 + +这些库不替代 TLS 终止、最终 CSP、身份认证、业务授权、限流或生产级反向代理。网络 +外连、监听和文件权限必须在顶层项目清单中按实际目标显式声明;Mock 和无端口测试成功 +也不能证明 DNS、TLS、代理或真实超时配置正确。 From 973d141b2606777456bfd0a35b3dab50ac0fede7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E7=A7=80?= Date: Sat, 18 Jul 2026 19:27:36 +0800 Subject: [PATCH 4/6] =?UTF-8?q?docs:=20=E6=96=B0=E5=A2=9E=E7=AC=AC?= =?UTF-8?q?=E4=B8=89=E6=96=B9=E5=BA=93=E7=BC=96=E5=86=99=E4=B8=8E=E5=8F=91?= =?UTF-8?q?=E5=B8=83=E6=8C=87=E5=8D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../libraries/authoring/api-design.mdx | 135 +++++++++++++ .../ecosystem/libraries/authoring/index.mdx | 59 ++++++ .../ecosystem/libraries/authoring/meta.json | 12 ++ .../libraries/authoring/project-structure.mdx | 185 +++++++++++++++++ .../libraries/authoring/publishing.mdx | 153 ++++++++++++++ .../libraries/authoring/requirements.mdx | 123 ++++++++++++ .../libraries/authoring/testing-ci.mdx | 188 ++++++++++++++++++ 7 files changed, 855 insertions(+) create mode 100644 content/docs/ecosystem/libraries/authoring/api-design.mdx create mode 100644 content/docs/ecosystem/libraries/authoring/index.mdx create mode 100644 content/docs/ecosystem/libraries/authoring/meta.json create mode 100644 content/docs/ecosystem/libraries/authoring/project-structure.mdx create mode 100644 content/docs/ecosystem/libraries/authoring/publishing.mdx create mode 100644 content/docs/ecosystem/libraries/authoring/requirements.mdx create mode 100644 content/docs/ecosystem/libraries/authoring/testing-ci.mdx diff --git a/content/docs/ecosystem/libraries/authoring/api-design.mdx b/content/docs/ecosystem/libraries/authoring/api-design.mdx new file mode 100644 index 0000000..aea1bfb --- /dev/null +++ b/content/docs/ecosystem/libraries/authoring/api-design.mdx @@ -0,0 +1,135 @@ +--- +title: API 与错误设计 +description: 设计稳定的中文公开 API、结构化错误、资源预算与兼容边界。 +--- + +## 先写公开契约 + +在实现前列出最小公共面:类型、常量、函数、参数顺序、返回形状、数据字段、错误代码和资源 +上限。默认只导出用户需要组合的能力;解析内核、缓存、平台适配和校验辅助函数保持私有。 + +公开名称使用领域内一致、可搜索的中文词汇。不要同时发布多组含义相同的缩写和别名,也不要 +让一个函数根据隐式全局状态返回不同形状。需要演进时,新增明确入口并保留旧入口至迁移窗口 +结束。 + +```yanxu +公 定 版本:文 为 「1.0.0」; + +公 定 默认限制:典 为 { + 「最大输入字符」:4096, + 「最大项目数」:1024 +}; + +公 法 解析(原文:文):典 则 + # 校验、解析,并返回固定字段的典 +终 + +公 法 尝试解析(原文:文):典 则 + # 返回固定成功或失败信封,不把预期失败变成崩溃 +终 +``` + +文档要说明空值、Unicode、顺序、重复项、可变性、回调时机和并发所有权。若返回典或列, +固定字段名与元素语义;不要让调用方从打印文本中反向解析结果。 + +## 导出与包边界 + +清单中的默认导出决定`引「包:言例」`加载哪个文卷: + +```toml +[导出] +默认 = "src/主.yx" +高级 = "src/高级.yx" +``` + +消费者可使用默认导出或显式导出,但不能依赖包内未声明的文件。用独立消费者验证这一点: + +```yanxu +引「包:言例」为 言例; + +定 结果 为 言例.解析(「示例」); +言 结果; +``` + +不要在示例中直接`引「../src/主.yx」`。这种写法能掩盖漏导出、错误包名、锁文件错误和未 +声明传递依赖。 + +## 稳定结构化错误 + +错误消息面向人,可以改善措辞;程序判断必须使用稳定代码。为库选择唯一前缀,例如 +`YANLI_`,并提供`错误详情`把运行时错误转换为固定结构: + +| 字段 | 含义 | 稳定性 | +| --- | --- | --- | +| `代码` | 如`YANLI_PARSE`、`YANLI_LIMIT` | 同一主版本稳定 | +| `消息` | 面向使用者的中文说明 | 不承诺逐字稳定 | +| `类别` | 参数、格式、资源、状态、外部系统等 | 文档化后稳定 | +| `位置` | 输入位置或源码位置;不可得时为空 | 形状稳定 | +| `原因` | 经脱敏的底层原因;不可得时为空 | 不泄露秘密 | + +对常见的可预期失败,同时提供不抛出的`尝试…`入口。成功与失败信封应有固定键,且失败 +不能携带半成品结果。无法识别的底层错误归一为明确的运行时代码,同时保留安全的诊断字段。 + +这些做法来自稳定库已经验证的模式:言版使用`YANBAN_*`,言章使用`YANZHANG_*`,HTTP +基础库使用`YANXUN_*`。前缀形式可以不同,但调用方绝不能依赖完整中文异常消息。 + +## 安全默认值与资源预算 + +任何接收外部输入的 API 都要回答四个问题: + +1. 最大输入、深度、项目数、输出和迭代次数是多少? +2. 超限是明确失败,还是会产生截断后的伪成功? +3. URL、路径、SQL、HTML、命令参数等信任边界在哪里? +4. 错误、日志和踪迹是否会暴露令牌、密码、连接地址或原文? + +公开默认预算,并为确需调整的调用方提供有上限的配置。未知配置键、负数、非整数或超过 +硬上限的值都应返回配置错误。流式 API 若不能回滚已经交付的片段,要在契约中明确它不是 +事务。 + +安全相关文本和结构必须使用正确的下层能力:SQL 值与 SQL 模板分离,HTML 默认转义, +动态标识符经过白名单,网络重定向剥离跨来源敏感首部。不要把“使用者应小心”当成缺失 +边界检查的替代品。 + +## 可观测性不能泄密 + +日志和指标使用稳定事件名与结构化字段;默认对密码、Authorization、Cookie、令牌、证书 +正文和完整连接地址脱敏。库不应在默认路径打印到标准输出。需要诊断时由调用方注入记录器, +并让关闭、重试、超时和取消事件具有可关联的请求编号。 + +## 生成 API 并阻止漂移 + +言序可以从公开声明生成 Markdown 和机器清单: + +```sh +yanxu 文 src/主.yx docs/API.md +yanxu 文 --json src/主.yx api/api-v1.json +``` + +CI 应重新生成到临时位置并与提交版本逐字节比较。修改公开声明时,在同一功能提交中更新 +API 制品、README、指南和规格;不要手改生成文件来隐藏漂移。 + +## 文档是兼容面的一部分 + +稳定库至少应维护: + +- `README.md`:定位、安装、五分钟示例、错误、权限、兼容性与已知限制; +- `docs/API.md`与机器 API 清单:从源码生成; +- `docs/GUIDE.md`:常用组合与完整工作流; +- `docs/ERRORS.md`:错误代码、恢复策略与不可恢复状态; +- `COMPATIBILITY.md`:最低言序、依赖、平台、协议和明确不支持项; +- `docs/SECURITY_MODEL.md`与`SECURITY.md`:信任边界、安全默认值和私密报告方式; +- `docs/ARCHITECTURE.md`与`docs/PERFORMANCE.md`:所有权、复杂度、资源上限和基准条件。 + +记录已验证能力,也记录未实现能力。一次成功的本机实验不能证明“跨平台”“生产级”或 +“兼容所有版本”;支持声明必须与 CI 和真实消费者的范围完全一致。 + +## 兼容性判断 + +| 变更 | 通常版本 | 例子 | +| --- | --- | --- | +| 修复且公共行为不变 | 补丁 | 修正锁漂移、错误分支或文档错误 | +| 向后兼容新增 | 次版本 | 新函数、新可选配置、新平台且旧平台不变 | +| 破坏公共契约 | 主版本 | 删除或改名、参数重排、返回字段变化、错误码改义 | + +安全收紧可能让过去接受的输入失败。即使 API 签名没变,也要评估是否破坏调用方,并在 +CHANGELOG 与迁移指南中给出受影响输入和替代做法。 diff --git a/content/docs/ecosystem/libraries/authoring/index.mdx b/content/docs/ecosystem/libraries/authoring/index.mdx new file mode 100644 index 0000000..f4f4312 --- /dev/null +++ b/content/docs/ecosystem/libraries/authoring/index.mdx @@ -0,0 +1,59 @@ +--- +title: 第三方库编写指南 +description: 为言序生态设计、实现、验证并发布一个可长期维护的第三方库。 +--- + +一个可发布的言序库不只是若干能运行的文卷。它还要有可复现的依赖图、明确的权限与安全 +边界、稳定的公开 API、跨执行器测试、独立消费者,以及能从公开标签重新得到相同结果的 +发布流程。 + +本指南把现有 21 个稳定库的共同发布门禁整理为社区作者可执行的规范。这些库最终在言序 +1.1.12 上共同完成锁定、检查、源码执行和 Release 构建;当前言包仍通过言序的版本化工程 +协议读写格式 2 清单。你可以声明更低的最低言序版本,但必须用那个版本单独通过同一组门禁。 + +## 开始前确认工具 + +```sh +yanbao 版 +yanxu 版本 --json +yanbao 诊 +``` + +`yanbao --help`列出的当前命令负责工程工作流;`yanxu --help`列出的命令是核心检查、VM、 +包协议和 API 文档入口。发布文档只应使用这两份帮助中真实存在的命令。当前言包没有 +`publish`或`发布`命令:源码包以 Git 标签分发,发布说明与额外制品由 GitHub Release 承载。 + +## 先判断是否值得成为库 + +适合独立发布的选题通常满足至少一项:提供可复用协议或数据结构、封装清晰的外部系统、 +把安全策略固化为 API,或解决多个项目都需要的工程问题。只服务一个应用、没有稳定公共 +边界的代码,先留在应用内部通常更合适。 + +立项前检查已有包,记录你的差异:支持范围、资源限制、错误协议、权限成本和维护承诺。 +不要只换一个名字重复已有能力,也不要使用会让社区误以为官方维护的名称或说明。 + +## 名称分三层 + +| 层级 | 示例 | 约定 | +| --- | --- | --- | +| 仓库 | `yanxu-example` | GitHub 上使用小写英文和连字符,便于发现 | +| 包名 | `言例` | `言序.toml`中的稳定身份,也是默认导入名 | +| 依赖别名 | `言例` | 由消费者清单决定,可与实际包名不同 | + +包名只能包含文字、数字、`_`、`-`和`.`,且不能以`.`或`-`开头。发布后改包名相当于更换 +包身份,应按破坏性迁移处理。仓库名、包名和导出模块的对应关系要在 README 第一段说清楚。 + +## 从想法到稳定版 + +1. 在[项目结构与清单](/ecosystem/libraries/authoring/project-structure/)中创建格式 2 工程, + 收紧依赖和权限。 +2. 按[API 与错误设计](/ecosystem/libraries/authoring/api-design/)固定中文公开面、结构化错误 + 和资源预算。 +3. 用[测试与持续集成](/ecosystem/libraries/authoring/testing-ci/)覆盖规格、示例、基准、 + 最低工具链、独立消费者和声称支持的平台。 +4. 按[版本与发布](/ecosystem/libraries/authoring/publishing/)准备 SemVer、更新记录、普通 + merge PR、附注标签和 GitHub Release。 +5. 发布前逐项满足[硬性要求](/ecosystem/libraries/authoring/requirements/)。 + +先做窄而完整的 0.x,再承诺 1.0 稳定面。版本号不是成熟度装饰:一旦发布 1.0,同一主版本 +内的函数名、参数顺序、返回形状、数据字段和稳定错误代码都成为兼容承诺。 diff --git a/content/docs/ecosystem/libraries/authoring/meta.json b/content/docs/ecosystem/libraries/authoring/meta.json new file mode 100644 index 0000000..a6eb1c5 --- /dev/null +++ b/content/docs/ecosystem/libraries/authoring/meta.json @@ -0,0 +1,12 @@ +{ + "title": "第三方库编写指南", + "description": "从选题、格式 2 清单和稳定 API,到测试、发布与安全验收。", + "pages": [ + "index", + "project-structure", + "api-design", + "testing-ci", + "publishing", + "requirements" + ] +} diff --git a/content/docs/ecosystem/libraries/authoring/project-structure.mdx b/content/docs/ecosystem/libraries/authoring/project-structure.mdx new file mode 100644 index 0000000..4b5ef12 --- /dev/null +++ b/content/docs/ecosystem/libraries/authoring/project-structure.mdx @@ -0,0 +1,185 @@ +--- +title: 项目结构与清单 +description: 创建格式 2 包工程,组织源码与验证资产,并锁定依赖和最小权限。 +--- + +## 创建工程 + +```sh +yanbao 新 yanxu-example --name 言例 +yanbao 查 --manifest-path yanxu-example +``` + +当前言包会创建`src/主.yx`、格式 2 的`言序.toml`和`言序.lock`。如果你从旧模板或手工工程 +开始,应主动把清单迁到格式 2;不要依赖格式 1 的兼容解析继续发布新版本。 + +## 建议目录 + +```text +yanxu-example/ +├── .github/workflows/ci.yml +├── api/api-v1.json +├── benchmarks/典型负载.yx +├── docs/ +│ ├── API.md +│ ├── ARCHITECTURE.md +│ ├── ERRORS.md +│ ├── GUIDE.md +│ ├── MIGRATION.md +│ ├── PERFORMANCE.md +│ └── SECURITY_MODEL.md +├── examples/五分钟示例.yx +├── integration/消费者/ +│ ├── src/主.yx +│ ├── 言序.lock +│ └── 言序.toml +├── src/ +│ ├── 主.yx +│ └── 内核.yx +├── tests/正常与边界.yx +├── CHANGELOG.md +├── COMPATIBILITY.md +├── CONTRIBUTING.md +├── LICENSE +├── README.md +├── SECURITY.md +├── 言序.lock +└── 言序.toml +``` + +小库不必制造空目录,但发布时至少应有源码、规格、可执行示例、README、许可、更新记录、 +兼容策略、安全报告方式和贡献说明。基准要验证结果而不只是打印耗时;独立消费者必须通过 +`包:<名称>`导入,不能用`../src`绕过包边界。 + +## 格式 2 清单 + +下面是面向言序 1.1.12 稳定生态的纯言序库模板。`言序`字段应写实际通过 CI 的最低版本, +而不是开发机上的版本;没有用到的权限保持拒绝。 + +```toml +[包] +格式 = 2 +名称 = "言例" +版本 = "0.1.0" +言序 = ">=1.1.12" +入口 = "src/主.yx" +说明 = "受资源约束的示例能力" +许可 = "MIT" +作者 = ["维护者名称"] + +[依赖] + +[开发依赖] + +[权限] +文件 = [] +网络 = [] +TCP监听 = [] +UDP绑定 = [] +环境 = [] +进程 = false +原生扩展 = false +图形界面 = false +剪贴板 = false +文件对话框 = false +系统通知 = false +托盘 = false +打开外部地址 = false +全局快捷键 = false + +[导出] +默认 = "src/主.yx" + +[构建] +目标 = "字节码" +``` + +这是言序 1.1.12 的完整权限面。较新工具链会在新项目中额外生成`本地网络 = false`;该能力 +从 1.1.15 起可用。若把它设为`true`,必须把最低言序提升到 1.1.15 或更高并增加对应 CI; +面向 1.1.12 的清单应删除该键。 + +入口和导出必须是包内相对`.yx`文卷,不能包含`..`或绝对路径。若库需要随包分发静态资源, +显式增加资源目录: + +```toml +[资源] +目录 = ["assets"] +``` + +资源声明决定包内容,文件权限决定运行时能访问什么;两者不能互相替代。 + +## 依赖来源与锁 + +本地开发可以使用路径依赖: + +```sh +yanbao 加 言版 --manifest-path yanxu-example \ + --path ../yanxu-semver --version '^1.0' +``` + +提交发布候选前,把路径来源换成不可移动的公开标签和兼容范围: + +```sh +yanbao 加 言版 --manifest-path yanxu-example \ + --git https://github.com/yanxulang/yanxu-semver.git \ + --rev v1.0.0 --version '^1.0' +yanbao 装 --manifest-path yanxu-example +yanbao 装 --manifest-path yanxu-example --offline +yanbao 树 --manifest-path yanxu-example --offline +``` + +对应清单是: + +```toml +[依赖] +言版 = { git = "https://github.com/yanxulang/yanxu-semver.git", 修订 = "v1.0.0", 版 = "^1.0" } +``` + +`版`表达允许的 SemVer 范围,`修订`固定本次解析来源;`言序.lock`再记录精确提交、内容 +SHA-256、入口、传递依赖、生成器和目标。三层都要保留。不要以`main`、`HEAD`或本地路径 +作为稳定 Release 的来源,也不要手工修改锁文件来掩盖解析差异。 + +库仓库同样提交锁文件。依赖或清单变化时用`yanbao 更`显式重锁,审查差异后测试;正常 +安装用`yanbao 装`,不会把无意更新伪装成安装。需要最低运行时直接复核时可执行: + +```sh +/path/to/yanxu-1.1.12 包 锁 yanxu-example +/path/to/yanxu-1.1.12 包 锁 --离线 yanxu-example +``` + +不同目标的原生锁可以有目标与制品字段差异,但版本、修订、内容校验和和依赖边必须一致。 + +## 权限最小化 + +第三方依赖不会替顶层应用取得宿主能力。库清单只声明库自身确实需要的能力,消费者还要 +显式授权。常见决策如下: + +| 需要 | 清单写法 | 收紧方法 | +| --- | --- | --- | +| 读取固定目录 | `文件 = ["assets"]` | 不使用`.`或用户主目录 | +| 访问服务 | `网络 = ["api.example.com:443"]` | 固定主机和端口,不用`"*"` | +| 访问回环服务(言序 1.1.15+) | `本地网络 = true` | 提高最低言序,与公网授权分开评审 | +| 读取配置变量 | `环境 = ["EXAMPLE_TOKEN"]` | 只列实际变量名且默认脱敏 | +| 启动子进程 | `进程 = true` | 记录可执行文件来源和参数边界 | +| 加载动态库 | `原生扩展 = true` | 固定 ABI、目标、大小和摘要 | + +纯计算库通常所有权限都应为空或`false`。新增权限属于安全面变化:先更新威胁模型、消费者 +示例和兼容说明,再决定是否需要新的次版本或主版本。 + +## 原生扩展清单 + +原生库应使用当前稳定 ABI v2,并为每个真正发布的目标登记唯一制品: + +```toml +[原生] +ABI = 2 + +[原生.linux.x86_64] +文件 = "dist/x86_64-unknown-linux-gnu/libyanxu_example_native.so" +校验和 = "64位十六进制SHA-256" +大小 = 123456 +``` + +实际清单中的校验和必须是 64 位十六进制值,大小必须与文件逐字节一致。不要复制示例值, +也不要为没有构建和执行验证的目标添加条目。完整的跨平台和 ABI 门禁见 +[测试与持续集成](/ecosystem/libraries/authoring/testing-ci/)。 diff --git a/content/docs/ecosystem/libraries/authoring/publishing.mdx b/content/docs/ecosystem/libraries/authoring/publishing.mdx new file mode 100644 index 0000000..1b9db39 --- /dev/null +++ b/content/docs/ecosystem/libraries/authoring/publishing.mdx @@ -0,0 +1,153 @@ +--- +title: 版本与发布 +description: 采用 SemVer、独立步骤提交、普通 merge PR、附注标签和可复核 Release。 +--- + +## 选择版本 + +言序包使用语义化版本`主.次.修订`: + +- 修订版修复缺陷,不改变已记录公共契约; +- 次版本增加向后兼容能力,可以新增 API 或支持平台; +- 主版本允许破坏性变化,但必须给出迁移路径; +- 预发布写作`1.1.0-rc.1`,不能冒充稳定版。 + +版本判断要同时检查 API、数据格式、错误代码、默认权限、资源上限、最低言序、原生 ABI、 +命令行为和可观察输出。安全策略收紧可能是破坏性变化,不能只看函数签名。 + +## 同步发布文档 + +发布候选至少同步这些位置: + +1. `言序.toml`中的包版本和最低言序; +2. 源码公开版本常量; +3. 原生工程的`Cargo.toml`等版本元数据; +4. `CHANGELOG.md`,按“新增、变更、修复、安全、验证”记录使用者可见变化; +5. `COMPATIBILITY.md`,列出工具链、依赖、平台、外部服务和原生 ABI; +6. `docs/MIGRATION.md`,给出旧写法、新写法、行为变化和回退方案; +7. 生成的 Markdown/JSON API、README 示例和 Release Notes。 + +CHANGELOG 只写已经完成并验证的事实,不复制提交列表。破坏性变化必须可搜索、可操作,说明 +谁受影响、为何改变以及如何迁移。 + +## 分支与独立步骤提交 + +不要直接把发布改动推到默认分支。创建用途清晰的分支: + +```sh +git switch -c release/v1.0.0 +``` + +每个提交只包含一个可独立验证的目的,例如“固定公开错误协议”“补齐独立消费者”“增加 +三平台 CI”“准备 1.0 文档”。先运行对应门禁,再提交该步骤。格式化、大范围重构、依赖更新 +和功能实现不要混在一个提交中;审查后发现问题时增加后续修复提交,不重写已经共享的历史。 + +```sh +git add src tests docs +git commit -m "稳定公开错误协议" +git push -u origin release/v1.0.0 +``` + +提交消息说明结果和边界,不写密钥、内部路径、临时服务地址或与项目无关的元数据。 + +## 普通 merge PR + +通过 PR 审查完整差异和逐项门禁: + +```sh +gh pr create --base main --head release/v1.0.0 --fill +PR="$(gh pr view --json number --jq .number)" +gh pr checks "$PR" --watch +gh pr merge "$PR" --merge --delete-branch +``` + +选择普通 merge,不使用 squash 或 rebase。合并后的主提交应有两个父提交,保留步骤提交与 +审查边界: + +```sh +git switch main +git pull --ff-only origin main +test "$(git rev-list --parents -n 1 HEAD | wc -w | tr -d ' ')" -eq 3 +``` + +在 PR 合并前不得创建稳定标签。主分支 CI 必须在最终合并提交上成功;若工作流监听标签, +标签 CI 也必须执行相同或更严格的门禁。 + +## 创建附注标签 + +稳定标签使用`v`加清单版本,并且必须是附注标签: + +```sh +git fetch origin main --tags +test "$(git rev-parse HEAD)" = "$(git rev-parse origin/main)" +git tag -a v1.0.0 -m "言例 1.0.0" +test "$(git cat-file -t v1.0.0)" = tag +git push origin v1.0.0 +``` + +标签对象指向的提交、`origin/main`和本地最终提交应完全一致: + +```sh +test "$(git rev-parse 'v1.0.0^{}')" = "$(git rev-parse origin/main)" +``` + +不要用轻量标签,也不要让标签指向发布分支的未合并提交。已经公开的标签不可移动、删除后 +重建或强制推送;若发布内容有误,修复后发布新的补丁版本。稳定生态曾用 1.0.1 修复全新 +克隆锁一致性,而没有改写已经公开的 1.0.0,这正是应有做法。 + +## 打包与 GitHub Release + +当前言包可以生成确定性的`.yxp`包,但没有把包直接发布到远端的`publish`命令: + +```sh +yanbao pack --offline -o build/言例-1.0.0.yxp +``` + +纯源码 Git 依赖以附注标签为权威来源,`.yxp`可作为可复核附件。CI 对所有附件计算 SHA-256, +在干净环境重新下载并复算后,再创建非草稿、非预发布的稳定 Release: + +```sh +gh release create v1.0.0 \ + build/言例-1.0.0.yxp \ + build/言例-1.0.0.yxp.sha256 \ + --verify-tag \ + --title "言例 1.0.0" \ + --notes-file docs/RELEASE_NOTES_1.0.0.md +``` + +没有附件的纯源码包可以省略文件参数。候选版使用单独的预发布标签并显式传入 +`--prerelease`,不能先把未验收版本发布为稳定版再原地修改。 + +Release Notes 至少包含: + +- 主要能力和安装清单; +- 最低言序、直接依赖、权限与原生 ABI; +- 已验证平台、架构、外部服务版本和 CI 链接; +- 制品名称、大小、SHA-256、签名或公证的真实状态; +- 破坏性变化与迁移链接; +- 已知限制、安全修复和私密漏洞报告入口。 + +## 原生制品的来源边界 + +Git 依赖解析器读取标签中的仓库内容,不会自动下载 GitHub Release 附件。原生包必须选择 +一种可消费且可审计的方式: + +- 在标签中包含每个支持目标的制品,并在`[原生.*]`固定路径、大小和 SHA-256;或 +- 把完整多目标包发布为 Release 归档,再通过兼容的包索引提供该归档及摘要。 + +不能只在 Release 上传动态库,却让 Git 标签清单指向不存在的文件。也不能把 Release 归档 +误写成 Git 来源。多目标汇总应拒绝缺失、重复、错误架构或摘要不一致的制品,并如实披露 +glibc、Windows CRT、macOS 签名与公证边界。 + +## 发布后核对 + +```sh +gh release view v1.0.0 \ + --json tagName,isDraft,isPrerelease,targetCommitish +git fetch origin main --tags +test "$(git rev-parse 'v1.0.0^{}')" = "$(git rev-parse origin/main)" +``` + +确认 Release 不是草稿或预发布,标签与默认分支一致,主分支及标签 CI 全部成功,公开附件 +能重新下载且摘要一致。最后从公开标签执行[全新克隆和独立消费者验收] +(/ecosystem/libraries/authoring/testing-ci/#全新克隆验收),而不是复用开发工作树或缓存。 diff --git a/content/docs/ecosystem/libraries/authoring/requirements.mdx b/content/docs/ecosystem/libraries/authoring/requirements.mdx new file mode 100644 index 0000000..ef0eda8 --- /dev/null +++ b/content/docs/ecosystem/libraries/authoring/requirements.mdx @@ -0,0 +1,123 @@ +--- +title: 发布硬性要求 +description: 第三方库进入稳定生态前必须满足的工程、API、安全、验证与发布门禁。 +--- + +本页是稳定发布门禁,不是建议评分表。任一适用项缺少直接证据,就不能把版本标为稳定。 +确实不适用的条件要在发布报告中写出原因,不能用“不适用”隐藏未实现或未测试的能力。 + +## 身份与范围 + +| 要求 | 可接受证据 | +| --- | --- | +| 包解决明确且可复用的问题,不冒充官方维护 | README 的定位、维护者和与相近包的差异 | +| 仓库名、包名、依赖别名和导出关系清楚 | README 安装段与格式 2 清单 | +| 包名符合文字、数字、`_`、`-`、`.`规则且身份稳定 | `yanxu 包 .`成功,发布后不在同一兼容线改名 | +| 支持声明有明确边界 | `COMPATIBILITY.md`列出最低/最高已测版本和不支持项 | + +## 工程与依赖 + +1. `言序.toml`必须使用`[包].格式 = 2`,包含名称、SemVer 版本、实际最低言序、包内入口、 + 说明、SPDX 许可、导出和`字节码`构建目标。 +2. 所有权限键必须显式且最小化;纯计算库保持空列和`false`。新增文件、网络、环境、进程、 + 原生或桌面能力必须有威胁模型和拒绝权限测试。 +3. 稳定依赖必须同时给出兼容版本范围和不可移动的标签修订。发布清单、示例、基准和消费者 + 都不得残留路径依赖、`main`或`HEAD`。 +4. 根包及每个独立子项目都提交`言序.lock`。在线安装后离线验证成功;版本、提交、内容摘要 + 和依赖边可复现,不能手改或宽松比较关键字段。 +5. 依赖许可必须兼容;复用或捆绑第三方源码、字体、数据和动态库时记录来源、版本和许可。 +6. 包中不得包含密钥、令牌、私钥、签名证书、真实密码、开发者绝对路径或无关生成文件。 + +## API、错误与安全 + +1. 默认导出只暴露稳定能力,内部实现不成为偶然公共 API;独立消费者只能通过`包:<名称>` + 导入。 +2. 公共函数、类型、常量和字段使用一致的中文命名;参数顺序、返回形状、空值、顺序、所有权 + 和回调行为有文档与规格。 +3. 所有可预期失败都有库级稳定错误代码;完整中文消息不作为程序协议。常见失败提供结构化 + `错误详情`,适合时同时提供不抛出的`尝试…`入口。 +4. 外部输入具有公开且受测的长度、深度、项目、输出、时间或步数预算。超限明确失败,不 + 返回截断伪成功,也不接受无限配置。 +5. SQL、HTML、URL、路径、命令、网络和原生边界使用安全默认值与负面测试;敏感字段在错误、 + 日志、踪迹和制品中默认脱敏。 +6. `yanxu 文`生成的 Markdown 与 JSON API 清单随源码提交,CI 重新生成并逐字节防漂移。 + +## 测试与文档 + +稳定库至少具备并通过: + +- 正常、边界、非法输入、资源上限、错误代码和状态转换规格; +- 可执行五分钟示例; +- 有固定负载、结果断言和环境说明的基准; +- 只用公开包边界的独立消费者; +- 当前稳定言序与清单声明最低言序的清单、锁、格式、静态检查、执行和 release 构建; +- 纯言序路径的树解释器、字节码 VM 与 Release YXB 结果对照; +- 所有声称支持的操作系统、架构和真实外部服务版本。 + +仓库必须提供非占位的`README.md`、`LICENSE`、`CHANGELOG.md`、`COMPATIBILITY.md`、 +`CONTRIBUTING.md`、`SECURITY.md`、API、指南、架构、性能、安全模型和迁移文档。小库可以 +合并相近文档,但上述信息不能缺失。 + +CI 必须在 PR、默认分支和正式标签触发;固定工具链和第三方 Action;默认最小 GitHub 权限; +任何测试、API 漂移、制品缺失、摘要不符或安全门禁失败都阻止发布。 + +## 原生扩展附加要求 + +原生包还必须满足: + +1. 使用已支持的 ABI,稳定生态优先 ABI v2;清单为每个目标固定唯一文件、真实大小和 + SHA-256。 +2. 每个声称支持的操作系统与架构在原生 runner 构建、检查格式与架构、核对 + `yanxu_native_module_v2`导出,并执行源码和 YXB 消费者。 +3. 原生工具链与依赖锁定,格式、单测、严格 lint、release 构建均通过。 +4. macOS 安装名与签名状态、Windows CRT 策略、Linux glibc 与动态依赖下限有机器检查和 + Release 披露。 +5. Git 标签本身包含清单引用的文件,或通过兼容包索引分发完整 Release 归档;不能假设 + Git 依赖解析器会下载 GitHub Release 附件。 +6. 未登记、缺失、错误架构、大小或摘要不符的制品明确失败,不跨目标静默回退。 + +## 版本控制与发布 + +1. 功能、测试、文档、CI 和发布准备按可独立验证的步骤提交;一个提交不混入无关重构或 + 批量格式化。 +2. 变更在独立分支完成,通过 PR 审查和全部检查;不直接推默认分支。 +3. PR 使用普通 merge,最终合并提交恰有两个父提交;不以 squash、rebase 或强推改写步骤 + 历史。 +4. 版本遵循 SemVer。清单、源码版本、原生工程、CHANGELOG、迁移说明、API 和 Release Notes + 同步。 +5. 标签在 PR 合并和最终主分支 CI 成功后创建,命名为`v<版本>`,且必须是附注标签,精确 + 指向最终默认分支提交。 +6. GitHub Release 绑定该标签;稳定版不是草稿、不是预发布;制品大小、SHA-256、签名状态、 + 平台矩阵和已知限制如实披露。 +7. 已公开标签和 Release 不移动、不重写、不用强推替换。发现缺陷后以新的补丁、次版本或 + 主版本修复。 + +## 最终验收 + +发布完成前必须保留一份逐项证据记录,并从外部可见状态重新验证: + +- 默认分支、附注标签和 Release 指向同一最终提交; +- PR 是普通双父合并,主分支与标签 CI 成功; +- 从公开标签、空依赖缓存进行全新克隆,在线安装后离线锁验证成功且工作树干净; +- 独立消费者以公开 Git 标签或兼容索引安装,源码与 release YXB 均完成真实任务; +- 下载公开附件后重新计算大小与 SHA-256,与 CI 产物和 Release 说明一致; +- 权限拒绝、恶意输入、敏感信息扫描、许可证和供应链审计成功; +- 测试或服务创建的文件、端口、进程、数据库对象全部清理; +- 所有已知限制有文档,没有把未实现或未验证能力写成支持。 + +验收必须覆盖发布声明的完整范围。单个平台、单个入口、缓存工作树或仅构建成功的结果,都 +不能证明跨平台、独立消费、离线复现或运行时安全。 + +## 发布清单 + +在创建标签前最后核对: + +- [ ] 格式 2 清单、所有锁文件、依赖标签和权限已审查; +- [ ] 当前与最低言序、全部平台和真实服务门禁成功; +- [ ] API、错误、示例、基准、兼容、安全和迁移文档同步; +- [ ] 独立消费者的源码与 Release 路径成功; +- [ ] 分支步骤提交完整,普通 merge PR 已合并,最终主分支 CI 成功; +- [ ] 附注标签、标签 CI、Release 元数据与制品摘要可复核; +- [ ] 全新克隆、离线锁、安全扫描和清理检查成功。 + +全部勾选并有证据后,版本才具备稳定发布条件。 diff --git a/content/docs/ecosystem/libraries/authoring/testing-ci.mdx b/content/docs/ecosystem/libraries/authoring/testing-ci.mdx new file mode 100644 index 0000000..778ac1c --- /dev/null +++ b/content/docs/ecosystem/libraries/authoring/testing-ci.mdx @@ -0,0 +1,188 @@ +--- +title: 测试与持续集成 +description: 用规格、双执行器、独立消费者、最低工具链和真实平台证明发布声明。 +--- + +测试目标不是“命令退出为零”,而是证明公开契约、失败策略、权限边界和发布制品在使用者 +环境中成立。每一项支持声明都要能指向一条覆盖范围相同的门禁。 + +## 六层验证资产 + +| 层级 | 必须证明什么 | +| --- | --- | +| 规格 | 正常、边界、非法输入、资源上限、状态转换和稳定错误代码 | +| 示例 | README 中的主要工作流可执行,输出和退出状态可断言 | +| 基准 | 典型与最坏负载有固定规模、结果校验和环境说明 | +| API 漂移 | 源码公开声明与提交的 Markdown、JSON 清单一致 | +| 独立消费者 | 只通过标签/锁和`包:<名称>`导入完成真实任务 | +| Release 制品 | release YXB 或原生制品可在声称的平台实际加载和执行 | + +不要用同一份测试重复运行的次数冒充不同层。特别是,库仓库内相对导入成功不能替代独立 +消费者,交叉编译成功不能替代目标平台加载,Mock 不能替代你声称支持的真实服务版本。 + +## 本地质量门禁 + +在项目根目录运行当前言包工作流: + +```sh +yanbao 装 +yanbao 查 +yanbao 试 --json +yanbao 构 --release -o build/言例.yxb +yanbao 审 --deny-level high +yanbao 装 --offline +``` + +再用核心命令检查每份言序文卷、VM 路径与 API 漂移: + +```sh +find src tests examples benchmarks integration -type f -name '*.yx' -print | \ + while IFS= read -r file; do + yanxu 格 "$file" | cmp - "$file" + yanxu 查 "$file" + done + +yanxu 试 tests --json +for file in tests/*.yx; do yanxu 字节 "$file"; done +for file in examples/*.yx; do + yanxu "$file" + yanxu 字节 "$file" +done + +yanxu 文 src/主.yx /tmp/言例-API.md +yanxu 文 --json src/主.yx /tmp/言例-api-v1.json +cmp docs/API.md /tmp/言例-API.md +cmp api/api-v1.json /tmp/言例-api-v1.json + +yanxu 编 . --release -o /tmp/言例-release.yxb +test -s /tmp/言例-release.yxb +``` + +命令中的目录应只包含实际存在的验证资产;若项目没有某一类,先决定为何不需要,并把理由 +写入兼容或性能文档。不要以删除测试目录来让门禁变绿。 + +纯言序库还应让示例和关键消费者分别经树解释器、字节码 VM 与 Release YXB 执行,并比较 +可观察结果。依赖`标准:原生`的库不能由树解释器加载,应逐份使用`yanxu 字节`,再运行同一 +消费者的 YXB;文档要明确这一限制。 + +## 最低版本不是一行清单 + +`言序 = ">=1.1.12"`表示 1.1.12 真能解析、锁定、检查、执行和构建该包。当前言包可能要求 +更新的本机言序,因此最低版本作业直接使用固定的核心可执行文件: + +```sh +YANXU=/opt/yanxu-1.1.12/bin/yanxu + +"$YANXU" 版本 --json +"$YANXU" 包 . +"$YANXU" 包 锁 --离线 . +"$YANXU" 查 src/主.yx +"$YANXU" 试 tests --json +for file in tests/*.yx; do "$YANXU" 字节 "$file"; done +"$YANXU" 编 . --release -o /tmp/言例-1.1.12.yxb +test -s /tmp/言例-1.1.12.yxb +``` + +CI 同时运行“声明的最低版本”和“当前稳定版本”。如果最低版本不能通过,就修复兼容性或 +提高清单下限;不能只在文档中保留较低数字。锁文件由哪一版生成应是明确策略,最终仓库 +必须在该策略下重建收敛。 + +## CI 基线 + +每个 PR、默认分支和正式`v*`标签都应触发质量工作流。至少设置: + +- 仓库只读的默认`permissions`,只在确需上传制品或签署证明的作业单独提权; +- 固定提交的第三方 Action 和固定版本工具链; +- 格式、静态检查、API 漂移、规格、VM、示例、基准、锁和 release 构建; +- Linux、macOS、Windows 中所有声称支持的平台; +- 标签作业复用与主分支相同的门禁,不另设较弱路径; +- 失败即停止发布,制品缺失也视为失败; +- 不把令牌、数据库地址、证书、用户路径或完整不可信输入写入日志和制品。 + +服务型库应在隔离服务中测试已声明的最低与最高版本,覆盖认证失败、TLS、超时、断连、 +资源上限和清理。测试结束后验证临时数据库对象、文件、端口和进程均已释放。 + +## 跨平台与原生 ABI + +发布原生扩展时,先确认运行时的 ABI 能力: + +```sh +yanxu 原生 --json +``` + +稳定生态使用 ABI v2。对清单中每个`[原生.<系统>.<架构>]`条目,CI 必须在对应原生 runner +完成以下验证: + +1. 用固定原生工具链执行格式、单元测试、严格 lint 和 release 构建; +2. 核对制品的操作系统格式、CPU 架构、字节大小与 SHA-256; +3. 核对导出符号`yanxu_native_module_v2`,拒绝错误 ABI 或其他架构; +4. 让言序 VM 从源码消费者实际加载,再让同一消费者从 release YXB 加载; +5. 在真实依赖服务或真实窗口环境运行最小冒烟,而不只检查文件存在; +6. 保存逐目标制品和清单,最后汇总时再次逐项核对。 + +若使用 Rust 后端,常见质量命令是: + +```sh +cargo fmt --all -- --check +cargo test --workspace --locked +cargo clippy --workspace --all-targets --all-features --locked -- -D warnings +cargo build --workspace --release --locked +``` + +macOS 还要检查 dylib 安装名、架构和实际签名状态;Windows 检查 PE 架构与 CRT 分发策略; +Linux 检查 ELF 架构、动态依赖和 glibc 最低符号版本。临时签名不是 Developer ID 签名, +交叉构建不是原生执行,能在某个发行版运行也不能证明更低 glibc。Release 说明必须如实写出 +这些边界。 + +只发布你真正验证的目标。缺少当前目标制品时应明确失败,不能静默加载相近架构或其他操作 +系统的动态库。 + +## 全新克隆验收 + +发布候选通过后,在独立目录和空缓存中从公开标签重新开始: + +```sh +REPO=https://github.com/example/yanxu-example.git +TAG=v1.0.0 +CLONE="$(mktemp -d)/yanxu-example" +CACHE="$(mktemp -d)" + +git clone --branch "$TAG" --depth 1 "$REPO" "$CLONE" +YANXU_CACHE="$CACHE" yanbao 装 --manifest-path "$CLONE" +YANXU_CACHE="$CACHE" yanbao 装 --manifest-path "$CLONE" --offline +YANXU_CACHE="$CACHE" yanbao 查 --manifest-path "$CLONE" +YANXU_CACHE="$CACHE" yanbao 试 --manifest-path "$CLONE" --json +YANXU_CACHE="$CACHE" yanbao 构 --manifest-path "$CLONE" \ + --release -o /tmp/言例-克隆.yxb +test -z "$(git -C "$CLONE" status --short)" +``` + +全新克隆应固定到附注标签指向的提交。在线安装后离线安装必须成功,生成结果不能改写受跟踪 +文件。若锁包含目标特定原生字段,应在各目标分别验证,并只按书面规则比较允许变化的字段。 +版本、修订、内容摘要或依赖边差异永远不能被“规范化”掉。 + +## 独立消费者验收 + +再创建不位于库仓库内的应用,只使用公开来源: + +```sh +CONSUMER="$(mktemp -d)/consumer" +yanbao 新 "$CONSUMER" --name 言例消费者 +yanbao 加 言例 --manifest-path "$CONSUMER" \ + --git https://github.com/example/yanxu-example.git \ + --rev v1.0.0 --version '^1.0' +yanbao 装 --manifest-path "$CONSUMER" --offline +yanbao 行 --manifest-path "$CONSUMER" +yanbao 构 --manifest-path "$CONSUMER" --release -o /tmp/言例消费者.yxb +yanxu 行 /tmp/言例消费者.yxb +``` + +消费者入口要断言公共返回值和稳定错误代码,并显式声明所需权限。原生、数据库、网络、GUI +或文件库要分别覆盖成功和拒绝权限路径。源码与 YXB 的可观察结果必须一致。 + +## 安全验收 + +发布门禁还应证明:依赖来源和 Action 已固定;许可证兼容;仓库历史与制品没有凭据;日志 +已脱敏;路径、URL、SQL、HTML 和命令参数有负面测试;压缩包拒绝路径穿越、特殊文件和异常 +大小;原生文件的摘要在下载后重新计算。`yanbao 审`是其中一层,不替代威胁模型、代码审查 +和真实消费者。 From 378aff3c095af91760d2dd19757df58f125b1091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E7=A7=80?= Date: Sat, 18 Jul 2026 19:27:45 +0800 Subject: [PATCH 5/6] =?UTF-8?q?docs:=20=E9=87=8D=E5=BB=BA=E7=A8=B3?= =?UTF-8?q?=E5=AE=9A=E5=BA=93=E7=9B=AE=E5=BD=95=E4=B8=8E=E5=AE=89=E8=A3=85?= =?UTF-8?q?=E6=8C=87=E5=BC=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- content/docs/ecosystem/index.mdx | 8 +- .../docs/ecosystem/libraries/composition.mdx | 89 +++++------ content/docs/ecosystem/libraries/index.mdx | 91 +++++++----- .../docs/ecosystem/libraries/installation.mdx | 138 ++++++++++-------- content/docs/ecosystem/libraries/meta.json | 9 +- content/docs/projects/dependencies.mdx | 2 +- content/docs/tooling/package-manager.mdx | 8 +- 7 files changed, 189 insertions(+), 156 deletions(-) diff --git a/content/docs/ecosystem/index.mdx b/content/docs/ecosystem/index.mdx index 9f53659..b1cf53c 100644 --- a/content/docs/ecosystem/index.mdx +++ b/content/docs/ecosystem/index.mdx @@ -8,8 +8,10 @@ description: 了解在语言核心之外独立版本化、独立发布的官方 - - + + -选择生态包前先核对其 Release、最低核心版本、锁定来源、权限和测试范围。核心版本号不能代替生态包自己的稳定性声明。 +选择生态包前先核对其 Release、最低核心版本、锁定来源、权限和测试范围。核心版本号不能 +代替生态包自己的稳定性声明。准备发布自己的库时,遵循 +[第三方库编写指南](/ecosystem/libraries/authoring/)。 diff --git a/content/docs/ecosystem/libraries/composition.mdx b/content/docs/ecosystem/libraries/composition.mdx index 93689e2..12ffe71 100644 --- a/content/docs/ecosystem/libraries/composition.mdx +++ b/content/docs/ecosystem/libraries/composition.mdx @@ -1,70 +1,61 @@ --- title: 组合与选型 -description: 按职责组合第三方库,并保持数据库、时间、日志与测试边界可替换。 +description: 按 21 个稳定库的真实依赖关系组合数据、网络、数据库、测试与桌面能力。 --- -这些库刻意保持小而独立。应用可以只安装一项,也可以按层组合完整工程栈。 - -## 依赖关系 +库保持职责单一;箭头只表示包依赖,不表示权限传递。 ```text -应用 -├─ 言章 / 言签 / 言容 / 言令 / 言版 / 言韧 -├─ 言时 -│ └─ 言试(复用虚拟时钟) -├─ 言验 -│ └─ 言映(复用字段验证) +言据 ├─ 言录 -│ └─ 言据(结构化格式) -└─ 言库 - ├─ 言舟(SQLite 驱动) - └─ 言映(ORM) +├─ 言访 ── 言韧、言录 +├─ 言库 ── 言录、言时 +│ ├─ 言舟(SQLite) +│ ├─ 言库象城(PostgreSQL) +│ ├─ 言库海豚(MySQL/MariaDB) +│ └─ 言映 ── 言验、言令 +├─ 言枢 ── 言页、言讯、言访 +└─ 言试 ── 言时、言访、言库 + +言页 ── 言章 + +无直接包依赖:言据、言版、言容、言时、言验、言韧、言令、言讯、言签、言页、言窗 ``` -箭头表示直接依赖,不表示权限传递。比如言映依赖言库,但真实 SQLite 文件和 `sqlite3` 进程仍由顶层应用通过言舟授权。 - ## 常见组合 | 目标 | 建议组合 | 原因 | | --- | --- | --- | -| 命令行工具 | 言令 + 言验 + 言录 | 解析参数、验证配置、输出结构化运行记录 | -| 本地桌面或单机服务 | 言舟 + 言库 + 言验 | SQLite 持久化,同时保留统一连接边界 | -| 领域模型应用 | 言映 + 具体驱动 + 言验 | 模型关系、查询构建和事务单元 | -| HTTP 服务 | 言验 + 言签 + 言录 + 言韧 | 输入边界、会话、观测与上游容错 | -| 可重复测试 | 言试 + 言时 | 注入时钟、Mock、快照、事务回滚 | -| 文档或博客 | 言章 + 言录 | 安全渲染 Markdown,并记录处理错误 | - -## 从边界向内建模 - -推荐把外部世界收敛到少数可注入接口: - -1. 使用言令、言验或言签解析并验证外部输入。 -2. 使用言时的 `时钟协议` 提供“现在”,不要在领域逻辑中直接读取系统时间。 -3. 使用言库的 `连接协议` 提供持久化;业务代码不依赖 SQLite、PostgreSQL 或 MySQL 的具体客户端。 -4. 使用言录记录结构化字段,敏感字段在输出前统一脱敏。 -5. 在测试中用言试替换 HTTP、时间和数据库事务边界。 - -这种分层让生产代码与测试代码使用相同业务入口,只替换最外层适配器。 - -## 数据库层如何选择 - -- 只编写可跨数据库复用的存储代码:依赖言库,并接收 `连接协议`。 -- 使用 SQLite:同时安装言舟,把 `SQLite连接` 传给依赖言库协议的代码。 -- 需要模型、字段映射、关系和脏追踪:安装言映,并继续由应用选择言舟或其他驱动。 -- 需要手工 SQL 的精细控制:可在同一事务边界内直接使用言库/言舟,无须为了统一而强制使用 ORM。 +| 命令行工具 | 言令 + 言验 + 言录 | 参数、输入边界和结构化运行记录 | +| 调用外部服务 | 言访 + 言签 + 言验 | HTTP、令牌和响应 Schema | +| Web 服务 | 言枢 + 言验 + 言录 | 应用路由、安全中间件、输入与观测 | +| 本地数据应用 | 言舟 + 言库 | 原生 SQLite 与统一协议 | +| PostgreSQL 应用 | 言库象城 + 言库 | TLS、连接池、JSONB 和迁移 | +| MySQL/MariaDB 应用 | 言库海豚 + 言库 | 双服务兼容、JSON、连接池和迁移 | +| 领域模型 | 言映 + 一种驱动 | 模型、关联、作用域、迁移与事务单元 | +| 安全内容 | 言章 + 言页 | Markdown 解析与默认转义 HTML | +| 可重复测试 | 言试 + 生产协议对象 | Mock HTTP、虚拟时钟、快照和事务回滚 | +| 桌面应用 | 言窗 + 业务库 | 原生 GUI 与业务层解耦 | -## 时间与超时 +## 数据库层次 -言时把系统时钟和虚拟时钟放在同一协议下。重试库言韧目前负责退避策略和真实等待;需要完全确定的时间测试时,可调用 `立即执行` 跳过等待,或由上层调度器结合言时的虚拟时钟推进。 +业务或仓储代码面向言库协议,应用在组合根选择具体驱动。言映只根据方言能力生成 SQL, +不隐式打开连接。规范言据 TEXT/CLOB 在四个后端通用;JSONB/JSON/JSON1 是独立的原生 +结构化策略,不等同于`.yj`文本。 -言时的 `限时` 是协作式截止:操作接收 `截止` 并主动检查。它不会中断无法取消的同步代码,这一点在数据库驱动和外部进程边界尤其重要。 +只需要手工 SQL 时无需安装 ORM。需要模型、关联、软删除、乐观锁、迁移和言据字段时再 +加入言映;生产破坏性变更继续使用显式迁移,不用自动同步替代。 -## 错误与日志 +## 网络边界 -各库错误使用稳定前缀,例如 `YANZHANG_`、`YANQIAN_`、`YANYAN_`、`YANKU_`。在应用边界捕获错误后,可通过言录的 `记错误` 保留代码、类别、位置和踪迹,同时附加请求号、任务号或数据库操作等上下文字段。 +言讯是服务端协议,言访是客户端,二者不互相依赖。言枢在应用层依赖两者并提供无端口 +测试适配。生产 TLS、并发调度、反向代理信任、速率限制和身份授权不由这些库隐式完成。 -不要把密钥、口令、令牌或完整连接串直接作为消息文字。应放入结构化字段,并配置言录的敏感键集合统一脱敏。 +## 可注入边界 -## 最小化依赖 +1. 把时钟、数据库连接、HTTP 传输和日志器作为参数或构造配置注入。 +2. 在最外层解析与验证外部输入,领域层只接收已经验证的值。 +3. 在测试中替换传输、时钟和事务,不劫持全局函数。 +4. 日志只记录模板、计数、耗时和脱敏字段,不记录口令、令牌、Cookie 或完整连接串。 -优先依赖最靠近需求的库,而不是一次安装全部 13 项。言包的 `tree`、`why` 与 `audit` 可以持续检查传递图、来源、许可证与内容校验。 +使用`yanbao tree`和`yanbao why`验证真实传递图;不要根据文档图猜测自己的锁文件。 diff --git a/content/docs/ecosystem/libraries/index.mdx b/content/docs/ecosystem/libraries/index.mdx index 840db76..e70fc20 100644 --- a/content/docs/ecosystem/libraries/index.mdx +++ b/content/docs/ecosystem/libraries/index.mdx @@ -1,47 +1,60 @@ --- title: 第三方库 -description: 为言序应用补齐验证、日志、数据访问、日期时间、测试与通用工程能力。 +description: 浏览 yanxulang 组织 21 个稳定库,并按版本、依赖、权限和运行边界选择组件。 --- -第三方库模块收录 `yanxulang` 组织维护的 13 个独立言序包。它们都使用 `yanxu-` 仓库前缀、独立中文包名、格式 2 清单和 MIT 许可证,支持言序 1.1.6 或更高版本。 +`yanxulang`组织维护 21 个独立稳定库。每个库都有自己的清单、版本、测试、CI、附注标签 +和 GitHub Release;应用通过`言序.lock`固定提交、来源和内容校验,而不是跟随可变分支。 - - - - + + + + + + -## 库目录 - -| 仓库 | 中文包名 | 适用场景 | 直接依赖 | -| --- | --- | --- | --- | -| [yanxu-markdown](https://github.com/yanxulang/yanxu-markdown) | [言章](/ecosystem/libraries/markdown/) | Markdown 解析、安全 HTML 与目录 | 无 | -| [yanxu-jwt](https://github.com/yanxulang/yanxu-jwt) | [言签](/ecosystem/libraries/jwt/) | HS256 JWT 签发与验证 | 无 | -| [yanxu-collections](https://github.com/yanxulang/yanxu-collections) | [言容](/ecosystem/libraries/collections/) | 容器、缓存与集合算法 | 无 | -| [yanxu-cli](https://github.com/yanxulang/yanxu-cli) | [言令](/ecosystem/libraries/cli/) | 声明式命令行解析与帮助 | 无 | -| [yanxu-semver](https://github.com/yanxulang/yanxu-semver) | [言版](/ecosystem/libraries/semver/) | SemVer 解析、范围与版本选择 | 无 | -| [yanxu-retry](https://github.com/yanxulang/yanxu-retry) | [言韧](/ecosystem/libraries/retry/) | 重试、退避与断路器 | 无 | -| [yanxu-datetime](https://github.com/yanxulang/yanxu-datetime) | [言时](/ecosystem/libraries/datetime/) | 日期时间、时区、时间段、定时器与时钟 | 无 | -| [yanxu-validate](https://github.com/yanxulang/yanxu-validate) | [言验](/ecosystem/libraries/validate/) | 可组合数据验证与转换 | 无 | -| [yanxu-log](https://github.com/yanxulang/yanxu-log) | [言录](/ecosystem/libraries/log/) | 分级结构化日志与文件轮转 | 言据 | -| [yanxu-db](https://github.com/yanxulang/yanxu-db) | [言库](/ecosystem/libraries/db/) | 数据库协议、方言、事务与连接池 | 无 | -| [yanxu-sqlite](https://github.com/yanxulang/yanxu-sqlite) | [言舟](/ecosystem/libraries/sqlite/) | SQLite 驱动、事务批与迁移 | 言库 | -| [yanxu-orm](https://github.com/yanxulang/yanxu-orm) | [言映](/ecosystem/libraries/orm/) | 模型、关系、查询、迁移与事务单元 | 言库、言验 | -| [yanxu-test](https://github.com/yanxulang/yanxu-test) | [言试](/ecosystem/libraries/test/) | 断言、夹具、Mock、快照与测试边界 | 言时 | - -## 如何选择 - -- 处理外部输入时先用言验建立边界,再把已验证的数据交给业务层。 -- 需要可检索、可脱敏的运行记录时使用言录;库代码可通过自定义处理器把日志交给宿主。 -- 只需要 SQL 和统一驱动边界时选择言库;使用 SQLite 时叠加言舟;需要模型关系与事务单元时再加入言映。 -- 业务规则依赖“现在”、等待或截止时间时使用言时并注入时钟;测试中通过言试复用虚拟时钟。 -- 需要轻量算法或命令行入口时,分别选择言容和言令;它们没有权限与传递依赖负担。 - -## 一致的工程约定 - -每个库都独立发布和测试,公开入口统一为 `引「包:中文名」`。纯计算库不申请文件、网络、环境、进程或原生扩展权限;言录、言舟与言试只有在对应能力实际落到顶层应用时才需要宿主授权。 - -库版本当前均为 `0.1.0`。`^0.1` 允许同一小版本线内的兼容更新,精确提交与内容校验由 `言序.lock` 固定。部署或审计时应提交锁文件,并运行 `yanbao audit`。 - -下一步可先阅读[安装与更新](/ecosystem/libraries/installation/),再按[组合与选型](/ecosystem/libraries/composition/)搭建应用栈。 +## 稳定库目录 + +| 仓库 | 包/常用别名 | 版本 | 最低言序 | 文档 | 直接依赖 | +| --- | --- | ---: | ---: | --- | --- | +| `yanju` | 言据 | 1.2.0 | 1.1.6 | [数据格式](/ecosystem/yanju/) | 无 | +| `yanxu-semver` | 言版 | 1.0.0 | 1.1.6 | [语义化版本](/ecosystem/libraries/semver/) | 无 | +| `yanxu-collections` | 言容 | 1.0.0 | 1.1.6 | [容器与缓存](/ecosystem/libraries/collections/) | 无 | +| `yanxu-datetime` | 言时 | 1.0.0 | 1.1.6 | [日期时间](/ecosystem/libraries/datetime/) | 无 | +| `yanxu-validate` | 言验 | 1.0.0 | 1.1.6 | [验证](/ecosystem/libraries/validate/) | 无 | +| `yanxu-log` | 言录 | 1.0.0 | 1.1.6 | [日志](/ecosystem/libraries/log/) | 言据 | +| `yanxu-retry` | 言韧 | 1.0.0 | 1.1.6 | [重试与断路器](/ecosystem/libraries/retry/) | 无 | +| `yanxu-cli` | 言令 | 1.0.0 | 1.1.6 | [命令行](/ecosystem/libraries/cli/) | 无 | +| `yanxu-http` | `http` | 1.0.0 | 1.1.6 | [HTTP/1.1](/ecosystem/web/http/) | 无 | +| `yanxu-request` | `request` / 言访 | 1.0.0 | 1.1.11 | [HTTP 客户端](/ecosystem/web/request/) | 言据、言韧、言录 | +| `yanxu-jwt` | 言签 | 1.0.0 | 1.1.6 | [JWT](/ecosystem/libraries/jwt/) | 无 | +| `yanxu-db` | 言库 | 1.0.0 | 1.1.11 | [数据库协议](/ecosystem/libraries/db/) | 言据、言录、言时 | +| `yanxu-sqlite` | 言舟 | 1.0.0 | 1.1.12 | [SQLite](/ecosystem/libraries/sqlite/) | 言库 | +| `yanxu-postgres` | 言库象城 | 1.0.0 | 1.1.12 | [PostgreSQL](/ecosystem/libraries/postgres/) | 言库 | +| `yanxu-mysql` | 言库海豚 | 1.0.0 | 1.1.12 | [MySQL/MariaDB](/ecosystem/libraries/mysql/) | 言库 | +| `yanxu-orm` | 言映 | 1.0.0 | 1.1.12 | [ORM](/ecosystem/libraries/orm/) | 言据、言库、言验、言令 | +| `yanxu-html` | 言页 | 1.0.0 | 1.1.12 | [安全 HTML](/ecosystem/web/html/) | 无 | +| `yanxu-markdown` | 言章 | 1.0.1 | 1.1.12 | [Markdown](/ecosystem/libraries/markdown/) | 言页 | +| `yanxu-web` | `web` / 言枢 | 1.0.0 | 1.1.12 | [Web 框架](/ecosystem/web/framework/) | 言据、言页、言讯、言访 | +| `yanxu-test` | 言试 | 1.0.0 | 1.1.12 | [测试工具](/ecosystem/libraries/test/) | 言时、言据、言访、言库 | +| `yanxu-gui` | 言窗 | 1.0.0 | 1.1.12 | [桌面 GUI](/ecosystem/desktop/gui/) | 无包依赖;ABI v2 原生制品 | + +## 按任务选择 + +- 数据和配置:言据负责格式,言验负责可组合验证,言录负责结构化观测。 +- 网络:言访调用上游,言讯处理服务端协议,言枢组织应用,言页/言章生成安全 HTML。 +- 数据库:业务协议依赖言库,应用选择言舟/象城/海豚,模型层再叠加言映。 +- 工具:言令处理命令行,言版处理版本范围,言韧处理重试,言时提供可测试时间。 +- 测试:言试提供断言、Mock、快照、虚拟时钟、HTTP 传输和数据库回滚边界。 +- 桌面:言窗提供六目标原生 GUI,完整包来自公开 Release 制品索引。 + +## 稳定工程约定 + +安装时使用版本范围表达兼容线,同时固定公开标签和锁文件。库清单不能替顶层应用取得 +文件、网络、监听、进程、GUI 或原生权限。程序应判断结构化错误代码,不匹配展示消息。 +每个页面都列出真实权限与已知限制;未实现的异步、流式、TLS、数据库或平台能力不会被 +写成“理论支持”。 + +如果要发布自己的包,从[第三方库编写指南](/ecosystem/libraries/authoring/)开始。 diff --git a/content/docs/ecosystem/libraries/installation.mdx b/content/docs/ecosystem/libraries/installation.mdx index 9b40a97..f497a09 100644 --- a/content/docs/ecosystem/libraries/installation.mdx +++ b/content/docs/ecosystem/libraries/installation.mdx @@ -1,110 +1,130 @@ --- title: 安装与更新 -description: 使用言包安装第三方库、声明中文包名、管理锁文件并授权能力。 +description: 使用言包锁定 21 个稳定库的公开标签、审阅依赖图并配置最小宿主权限。 --- -## 准备环境 +## 确认工具链 -这些库要求言序 1.1.6 或更高版本。先确认言序与言包可用: +全部库可在言序 1.1.12 上共同使用;单库最低版本见[库目录](/ecosystem/libraries/)。 ```sh -yanxu --version -yanbao --version -yanbao doctor +yanxu 版本 --json +yanbao 版 +yanbao 诊 ``` -如果尚未安装言包,请先阅读[言包工程工具](/tooling/package-manager/)。 +## 添加稳定依赖 -## 使用短仓库名安装 - -仓库名使用英文短名,清单中的包名使用中文,因此安装时通过 `--package` 明确中文包名: +言包短名默认映射到`yanxulang/yanxu-<短名>`。言据仓库名例外,使用组织/仓库写法。 ```sh -yanbao add markdown --package 言章 --version "^0.1" -yanbao add jwt --package 言签 --version "^0.1" -yanbao add collections --package 言容 --version "^0.1" -yanbao add cli --package 言令 --version "^0.1" -yanbao add semver --package 言版 --version "^0.1" -yanbao add retry --package 言韧 --version "^0.1" -yanbao add datetime --package 言时 --version "^0.1" -yanbao add validate --package 言验 --version "^0.1" -yanbao add log --package 言录 --version "^0.1" -yanbao add db --package 言库 --version "^0.1" -yanbao add sqlite --package 言舟 --version "^0.1" -yanbao add orm --package 言映 --version "^0.1" -yanbao add test --package 言试 --version "^0.1" --dev +yanbao add yanxulang/yanju --package 言据 --version '^1.2' +yanbao add semver --package 言版 --version '^1.0' +yanbao add collections --package 言容 --version '^1.0' +yanbao add datetime --package 言时 --version '^1.0' +yanbao add validate --package 言验 --version '^1.0' +yanbao add log --package 言录 --version '^1.0' +yanbao add retry --package 言韧 --version '^1.0' +yanbao add cli --package 言令 --version '^1.0' +yanbao add http --version '^1.0' +yanbao add request --version '^1.0' +yanbao add jwt --package 言签 --version '^1.0' +yanbao add db --package 言库 --version '^1.0' +yanbao add sqlite --package 言舟 --version '^1.0' +yanbao add postgres --package 言库象城 --version '^1.0' +yanbao add mysql --package 言库海豚 --version '^1.0' +yanbao add orm --package 言映 --version '^1.0' +yanbao add html --package 言页 --version '^1.0' +yanbao add markdown --package 言章 --version '^1.0' +yanbao add web --version '^1.0' +yanbao add test --package 言试 --version '^1.0' --dev ``` -`markdown` 会映射到 `yanxulang/yanxu-markdown`,其他短名同理。言试通常只用于测试,因此示例把它写入 `[开发依赖]`。 +每条命令都会更新格式 2 清单并重解完整图。应用通常只添加需要的顶层库,不应一次复制 +全部命令。 -## 直接编辑清单 +## 手工固定 Git 标签 -也可以手动写入 Git 来源: +审计要求明确来源时直接写清单: ```toml [依赖] -言验 = { git = "https://github.com/yanxulang/yanxu-validate.git", 修订 = "main", 版 = "^0.1" } -言录 = { git = "https://github.com/yanxulang/yanxu-log.git", 修订 = "main", 版 = "^0.1" } +言据 = { git = "https://github.com/yanxulang/yanju.git", 修订 = "v1.2.0", 版 = "^1.2" } +言库 = { git = "https://github.com/yanxulang/yanxu-db.git", 修订 = "v1.0.0", 版 = "^1.0" } +言映 = { git = "https://github.com/yanxulang/yanxu-orm.git", 修订 = "v1.0.0", 版 = "^1.0" } [开发依赖] -言试 = { git = "https://github.com/yanxulang/yanxu-test.git", 修订 = "main", 版 = "^0.1" } +言试 = { git = "https://github.com/yanxulang/yanxu-test.git", 修订 = "v1.0.0", 版 = "^1.0" } ``` -编辑后生成或验证完整锁图: +不要把`main`当成稳定修订。版本范围决定允许升级的兼容线,标签/提交和内容校验由锁文件 +固定。 + +## 生成、解释和验证锁 ```sh yanbao install yanbao tree +yanbao why 言库 yanbao check yanbao test +yanbao audit ``` -格式 2 的 `言序.lock` 会固定版本、Git 提交、内容校验、目标和传递依赖。应用仓库应提交锁文件;库仓库也应保留锁文件用于可重复 CI。 - -## 导入包 - -导入名与清单中文包名一致: - -```yanxu -引「包:言验」为 言验; -引「包:言时」为 言时; -引「包:言录」为 言录; -``` - -不要使用仓库短名作为模块名,例如 `包:validate` 不是言验的公开入口。 +应用和库仓库都应提交`言序.lock`。CI 还应执行离线恢复,证明没有依赖开发缓存或可变网络 +状态。不要手工编辑锁中的提交、校验和、目标或原生制品。 -## 更新、解释与移除 +## 更新 ```sh yanbao outdated yanbao update --dry-run yanbao update -yanbao why 言库 -yanbao remove orm +yanbao check +yanbao test +yanbao build --release yanbao audit ``` -先用 `update --dry-run` 检查将要变化的提交与版本,再执行正式更新并运行项目测试。`why` 可解释言库是直接依赖,还是由言舟或言映传递引入。 +先审阅 dry-run 的版本和来源,再正式更新。主版本变化必须同时阅读 CHANGELOG、迁移、权限 +和兼容说明。 + +## 言窗的发布制品 + +言窗 Git 标签只保存源码与清单模板;可执行的六目标清单和动态库位于 1.0.0 GitHub +Release 归档。普通 GUI 模板使用: -## 权限不会传递 +```sh +yanbao new 我的窗口 --gui +``` -依赖不能替顶层应用取得权限。根据实际用法在应用的 `言序.toml` 中授权: +离线或审计环境应先校验并展开官方六目标归档,再显式提供完整包: -| 能力 | 需要授权的情况 | 建议 | +```sh +yanbao new 我的窗口 --gui --gui-path /已验证/yanxu-gui +``` + +详情见[言窗 1.0](/ecosystem/desktop/gui/)。 + +## 权限由应用决定 + +| 能力 | 典型库 | 应用责任 | | --- | --- | --- | -| 文件 | 言录写日志;言试保存快照或临时文件;言舟打开数据库文件 | 只授权实际目录或文件 | -| 进程 | 言舟使用默认 `sqlite3` CLI 后端 | 设置 `进程 = true`,生产环境固定可执行文件来源 | -| 环境 | 言试的 `隔离系统环境` 读取真实变量 | 只列出测试需要的变量名 | -| 网络 | 应用选择 PostgreSQL/MySQL 网络驱动 | 由驱动和顶层应用声明目标,不由言库申请 | +| 文件 | 言据文件、言录文件、SQLite、言试快照、言访上传下载 | 只授权实际文件根 | +| 出站网络 | 言访、PostgreSQL、MySQL/MariaDB | 只授权目标主机与端口,不记录完整凭据 | +| TCP 监听 | 言讯、言枢开发服务器 | 开发只用回环;生产明确实际地址 | +| 进程 | 言舟可选 CLI 兼容后端 | 原生 SQLite 不需要;CLI 后端显式开启 | +| 原生扩展 | 三个数据库驱动 | 锁定目标、ABI、大小与摘要 | +| GUI/剪贴板/对话框 | 言窗 | 三项独立授权,不互相隐式包含 | -纯计算库本身不需要额外权限。即使依赖清单中展示了开发仓库使用的文件范围,最终能力仍以顶层应用清单为准。 +依赖权限不会传给顶层应用。即使库的测试清单允许某项能力,消费者也必须在自己的清单中 +重新作出最小授权。 ## 本地联调 -修改两个相邻仓库时可以临时使用路径依赖: - ```sh -yanbao add datetime --package 言时 --path ../yanxu-datetime --version "^0.1" +yanbao add datetime --package 言时 --path ../yanxu-datetime --version '^1.0' ``` -提交发布前应换回 Git 来源、更新锁文件并重新运行 `yanbao check`、`yanbao test` 与 `yanbao audit`,避免把开发机路径带入发布清单。 +路径依赖只用于本地开发。发布前换回 Git 标签、更新锁、检查不存在`path:`来源,并从全新 +克隆执行检查、测试、示例、release 构建和消费者验收。 diff --git a/content/docs/ecosystem/libraries/meta.json b/content/docs/ecosystem/libraries/meta.json index acc593f..46c7f2e 100644 --- a/content/docs/ecosystem/libraries/meta.json +++ b/content/docs/ecosystem/libraries/meta.json @@ -1,13 +1,16 @@ { "title": "第三方包", - "description": "言序社区维护的现代工程库、数据栈与测试工具。", + "description": "21 个稳定工程库、数据库与 ORM、网络测试工具和第三方库发布规范。", "defaultOpen": true, "pages": [ "index", "installation", "composition", + "authoring", + "---内容与安全---", "markdown", "jwt", + "---基础工具---", "collections", "cli", "semver", @@ -15,9 +18,13 @@ "datetime", "validate", "log", + "---数据库---", "db", "sqlite", + "postgres", + "mysql", "orm", + "---测试---", "test" ] } diff --git a/content/docs/projects/dependencies.mdx b/content/docs/projects/dependencies.mdx index adad6a3..4c589c2 100644 --- a/content/docs/projects/dependencies.mdx +++ b/content/docs/projects/dependencies.mdx @@ -6,7 +6,7 @@ description: 使用言包管理直接依赖,并理解完整、确定、可离 常用来源都通过同一命令进入清单: ```sh -yanbao add http --version '^0.1' +yanbao add http --version '^1.0' yanbao add acme/yanxu-tools yanbao add 共享工具 --path ../共享工具 --version '^1' yanbao add 测试工具 --dev --path ../测试工具 diff --git a/content/docs/tooling/package-manager.mdx b/content/docs/tooling/package-manager.mdx index 976646f..0368cf2 100644 --- a/content/docs/tooling/package-manager.mdx +++ b/content/docs/tooling/package-manager.mdx @@ -8,9 +8,9 @@ description: 使用言包创建项目,并通过短包名管理 GitHub 依赖 ## 最常用的包命令 ```sh -yanbao add html -yanbao add http -yanbao add web +yanbao add html --version '^1.0' +yanbao add http --version '^1.0' +yanbao add web --version '^1.0' yanbao install ``` @@ -48,7 +48,7 @@ yanbao doctor --manifest-path 我的项目 ```sh yanbao add 工具 --package 共享工具 --path ../共享工具 --version '^1' -yanbao add http --version '^0.1' +yanbao add http --version '^1.0' yanbao add acme/yanxu-tools yanbao add 测试工具 --dev --path ../测试工具 yanbao remove 工具 From 0e7f977a84d633e81e031a2af9af0bd99eb34e47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E7=A7=80?= Date: Sat, 18 Jul 2026 19:27:54 +0800 Subject: [PATCH 6/6] =?UTF-8?q?test:=20=E5=9B=BA=E5=8C=96=E7=A8=B3?= =?UTF-8?q?=E5=AE=9A=E5=BA=93=E6=96=87=E6=A1=A3=E4=B8=8E=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E5=AE=B9=E9=87=8F=E9=97=A8=E7=A6=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/check-content.mjs | 72 +++++++++++++++++++++++++++++++++++++++ scripts/check-site.mjs | 10 ++++-- 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/scripts/check-content.mjs b/scripts/check-content.mjs index 494a50a..9d0435e 100644 --- a/scripts/check-content.mjs +++ b/scripts/check-content.mjs @@ -94,4 +94,76 @@ assert.equal(packageJson.version, '1.1.8'); assert.match(read('content/docs/standard-library/index.mdx'), /25 个标准模块/); assert.match(read('app/layout.tsx'), /metadataBase:\s*new URL\('https:\/\/docs\.yanxu\.dev\/'\)/); +const stableLibraries = [ + ['yanju', '1.2.0', '1.1.6', 'content/docs/ecosystem/yanju/index.mdx', '/ecosystem/yanju/'], + ['yanxu-semver', '1.0.0', '1.1.6', 'content/docs/ecosystem/libraries/semver.mdx', '/ecosystem/libraries/semver/'], + ['yanxu-collections', '1.0.0', '1.1.6', 'content/docs/ecosystem/libraries/collections.mdx', '/ecosystem/libraries/collections/'], + ['yanxu-datetime', '1.0.0', '1.1.6', 'content/docs/ecosystem/libraries/datetime.mdx', '/ecosystem/libraries/datetime/'], + ['yanxu-validate', '1.0.0', '1.1.6', 'content/docs/ecosystem/libraries/validate.mdx', '/ecosystem/libraries/validate/'], + ['yanxu-log', '1.0.0', '1.1.6', 'content/docs/ecosystem/libraries/log.mdx', '/ecosystem/libraries/log/'], + ['yanxu-retry', '1.0.0', '1.1.6', 'content/docs/ecosystem/libraries/retry.mdx', '/ecosystem/libraries/retry/'], + ['yanxu-cli', '1.0.0', '1.1.6', 'content/docs/ecosystem/libraries/cli.mdx', '/ecosystem/libraries/cli/'], + ['yanxu-http', '1.0.0', '1.1.6', 'content/docs/ecosystem/web/http.mdx', '/ecosystem/web/http/'], + ['yanxu-request', '1.0.0', '1.1.11', 'content/docs/ecosystem/web/request.mdx', '/ecosystem/web/request/'], + ['yanxu-jwt', '1.0.0', '1.1.6', 'content/docs/ecosystem/libraries/jwt.mdx', '/ecosystem/libraries/jwt/'], + ['yanxu-db', '1.0.0', '1.1.11', 'content/docs/ecosystem/libraries/db.mdx', '/ecosystem/libraries/db/'], + ['yanxu-sqlite', '1.0.0', '1.1.12', 'content/docs/ecosystem/libraries/sqlite.mdx', '/ecosystem/libraries/sqlite/'], + ['yanxu-postgres', '1.0.0', '1.1.12', 'content/docs/ecosystem/libraries/postgres.mdx', '/ecosystem/libraries/postgres/'], + ['yanxu-mysql', '1.0.0', '1.1.12', 'content/docs/ecosystem/libraries/mysql.mdx', '/ecosystem/libraries/mysql/'], + ['yanxu-orm', '1.0.0', '1.1.12', 'content/docs/ecosystem/libraries/orm.mdx', '/ecosystem/libraries/orm/'], + ['yanxu-html', '1.0.0', '1.1.12', 'content/docs/ecosystem/web/html.mdx', '/ecosystem/web/html/'], + ['yanxu-markdown', '1.0.1', '1.1.12', 'content/docs/ecosystem/libraries/markdown.mdx', '/ecosystem/libraries/markdown/'], + ['yanxu-web', '1.0.0', '1.1.12', 'content/docs/ecosystem/web/framework.mdx', '/ecosystem/web/framework/'], + ['yanxu-test', '1.0.0', '1.1.12', 'content/docs/ecosystem/libraries/test.mdx', '/ecosystem/libraries/test/'], + ['yanxu-gui', '1.0.0', '1.1.12', 'content/docs/ecosystem/desktop/gui.mdx', '/ecosystem/desktop/gui/'], +]; + +const libraryCatalog = read('content/docs/ecosystem/libraries/index.mdx'); +assert.match(libraryCatalog, /组织维护 21 个独立稳定库/); +for (const [repository, version, minimumYanxu, page, route] of stableLibraries) { + const row = libraryCatalog.split('\n').find((line) => line.includes(`\`${repository}\``)); + assert.ok(row, `第三方库目录缺少 ${repository}`); + assert.ok(row.includes(`| ${version} | ${minimumYanxu} |`), + `${repository} 的稳定版本或最低言序不正确`); + assert.ok(row.includes(`](${route})`), `${repository} 缺少稳定文档入口 ${route}`); + + const markdown = read(page); + assert.ok(markdown.includes(version), `${page} 缺少稳定版本 ${version}`); + assert.ok(markdown.includes(minimumYanxu), `${page} 缺少最低言序 ${minimumYanxu}`); + assert.ok(markdown.includes(`https://github.com/yanxulang/${repository}`), `${page} 缺少仓库链接`); + assert.ok(markdown.includes(`/releases/tag/v${version}`), `${page} 缺少稳定 Release 链接`); + assert.ok(!/\^0\.[12]\b/u.test(markdown), `${page} 仍使用 0.x 依赖范围`); + assert.ok(!/(?:修订\s*=\s*["']|--rev\s+)(?:main|HEAD)\b/iu.test(markdown), + `${page} 仍把可变分支写成稳定来源`); +} + +const libraryMeta = JSON.parse(read('content/docs/ecosystem/libraries/meta.json')); +for (const page of ['authoring', 'postgres', 'mysql']) { + assert.ok(libraryMeta.pages.includes(page), `第三方库导航缺少 ${page}`); +} + +const expectedAuthoringPages = [ + 'index', 'project-structure', 'api-design', 'testing-ci', 'publishing', 'requirements', +]; +const authoringMeta = JSON.parse(read('content/docs/ecosystem/libraries/authoring/meta.json')); +assert.deepEqual(authoringMeta.pages, expectedAuthoringPages, '第三方库编写指南导航不完整'); +const authoring = expectedAuthoringPages + .map((page) => read(`content/docs/ecosystem/libraries/authoring/${page}.mdx`)) + .join('\n'); +for (const requirement of [ + '言序.toml', '格式 2', '结构化错误', '资源预算', '测试', 'CI', 'SemVer', + '普通 merge', '附注标签', 'GitHub Release', '权限', 'SECURITY.md', '全新克隆', +]) { + assert.ok(authoring.includes(requirement), `第三方库编写指南缺少 ${requirement}`); +} +assert.match(read('content/docs/ecosystem/libraries/authoring/publishing.mdx'), + /没有把包直接发布到远端的`publish`命令/); +for (const page of [ + 'content/docs/projects/dependencies.mdx', + 'content/docs/tooling/package-manager.mdx', +]) { + assert.ok(!/yanbao add (?:http|html|web).*\^0\.[12]/u.test(read(page)), + `${page} 仍推荐目标库的旧版安装范围`); +} + console.log(`内容检查通过:${files.length} 个文档页面。`); diff --git a/scripts/check-site.mjs b/scripts/check-site.mjs index 3530458..d833348 100644 --- a/scripts/check-site.mjs +++ b/scripts/check-site.mjs @@ -1,6 +1,7 @@ import fs from 'node:fs'; import path from 'node:path'; import process from 'node:process'; +import { gzipSync } from 'node:zlib'; const output = path.resolve(process.argv[2] ?? 'out'); const failures = []; @@ -73,8 +74,13 @@ for (const required of [ if (!fs.existsSync(path.join(output, required))) failures.push(`缺少生产文件:${required}`); } -const searchIndexBytes = fs.statSync(path.join(output, 'api/search')).size; -if (searchIndexBytes > 6_000_000) failures.push(`中文搜索索引超过 6 MB:${searchIndexBytes} B`); +const searchIndex = fs.readFileSync(path.join(output, 'api/search')); +const searchIndexBytes = searchIndex.byteLength; +const compressedSearchIndexBytes = gzipSync(searchIndex, { level: 9 }).byteLength; +if (searchIndexBytes > 8_000_000) failures.push(`中文搜索索引超过 8 MB:${searchIndexBytes} B`); +if (compressedSearchIndexBytes > 2_000_000) { + failures.push(`中文搜索索引 gzip 后超过 2 MB:${compressedSearchIndexBytes} B`); +} for (const file of walk(path.join(output, '_next/static'), '.js')) { const bytes = fs.statSync(file).size;