豫言 / 豫言操作系统网址查询 @ 0.1.0

按表单编码规则生成与解析查询串,并改写网址的查询参数。

源码浏览已生成;不能解析的文件以纯文本展示,不表示通过类型检查。

规范.汉语.md

# 网址查询接口规范 0.1.0

本包提供三个只做字符串运算的函数,按 WHATWG `application/x-www-form-urlencoded` 规则生成、解析查询串,并改写网址的一个查询参数。三者无权限要求、无外部副作用;当前只有云工适配(用宿主的 `URLSearchParams` 与 `URL`),其他宿主若实现须给出相同结果。

`编码查询串 项列` 接受有序的(名称,值)列表,返回表单编码的查询串(即 `new URLSearchParams(项列).toString()`):空格写作 `+`;除字母、数字与 `*-._` 外一律按 UTF-8 百分号编码,因此 `!'()~` 也被编码;同名项按原序保留;空列表得空字符串;名称、值可含任意 UTF-8 与控制字符。限额:最多 1024 项,全部名称与值的总字节数不超过 1 MiB,超限为违规(“查询项超过 1024 项”“查询项总长度超过 1 MiB”)。

`解析表单键值 文字` 按 WHATWG `application/x-www-form-urlencoded` 规则解析:以 `&` 分项,忽略空项,首个 `=` 前为名称,其后为值,`+` 视为空格,百分号解码,非法 UTF-8 序列以 U+FFFD 替换;输入首字符若是 `?`,先去掉这一个(与 `URLSearchParams` 构造器一致,故可直接传入含开头问号的原查询串)。返回保持原序的 `[["名","值"],…]` JSON 数组文字,同名项各占一项,空输入得 `[]`;返回文字是宿主序列化的紧凑 JSON,应用应把它当 JSON 解析,不依赖空白或转义细节。输入不超过 1 MiB,超限为违规(“表单文字超过 1 MiB”)。

`设置网址查询参数 网址 键 值` 按 `URL` 与 `searchParams.set` 的语义设置一个查询参数:同名旧项全部替换为一项,位置在第一个同名项处,其余参数保留,并按表单规则重新序列化整个查询串(因此其余参数的百分号写法可能被规范化);片段原样保留。
- 绝对网址:必须是 `http:` 或 `https:`(其他方案,如 `javascript:`、`data:`、`ftp:`,失败,“绝对网址不是 HTTP(S)”),返回完整的 `href`。
- 相对网址:以内部固定基址 `https://yuyan-lang.org` 解析,只返回 `pathname + search + hash`;解析结果的来源若不是该基址(例如 `//evil.example/x`、`\\evil.example`)则失败(“相对网址不得改变来源”),无法解析时失败(“网址无效”)。
- 限额:网址、键、值各不超过 64 KiB(“网址、键或值超过 64 KiB”)。键与值可含任意 UTF-8 与控制字符,由宿主自行编码,不会破坏参数。

## 失败类别

违规(可捕获的豫言异常):项数或总长度超限、输入超限、网址无效、方案不是 HTTP(S)、相对网址改变来源。本包没有业务情形分支:结果总是字符串。

## 一致性样例

| 调用 | 必须观察到 |
| --- | --- |
| `编码查询串` 传 [("a b","1+2"),("a b","!'()~*-._")] | `a+b=1%2B2&a+b=%21%27%28%29%7E*-._` |
| `编码查询串` 传 [("名","\u0001\n")] | `%E5%90%8D=%01%0A` |
| `解析表单键值` 传 `a=1&b=x+y%20z&a=%E4%BD%A0&c&d=%ff&=e` | `[["a","1"],["b","x y z"],["a","你"],["c",""],["d","\ufffd"],["","e"]]`(JSON 中的 U+FFFD 可以字面或转义写出) |
| `解析表单键值` 传 `?a=1` | `[["a","1"]]` |
| `设置网址查询参数` 传 `/p?a=1&b=2&a=3#h`,键 `a`,值 `新 值` | `/p?a=%E6%96%B0+%E5%80%BC&b=2#h` |
| `设置网址查询参数` 传 `https://h.example/p?a=1`,键 `b`,值 `2` | `https://h.example/p?a=1&b=2` |
| `设置网址查询参数` 传 `//evil.example/x` 或 `javascript:alert(1)` | 违规 |

## 不做什么

不发起网络请求,不解析或校验主机名,不做路径规范化以外的网址改写,不支持数组式(`a[]=`)或嵌套的查询参数约定,不提供多值删除,也不排序参数。

## 补充:表单项数上限与首 BOM(独立审查后修订)

- `解析表单键值` 最多 1024 项(以 `URLSearchParams.size` 计),超过抛可捕获的「表单项数超过 1024」,免得 1 MiB 的表单产出五十多万项而拖垮癸象解析。
- 文字交宿主 TextDecoder 时使用 `ignoreBOM: true`:文首的 U+FEFF 保留。

规范.文言.md

# 网址查询之约 0.1.0

此包具三术,惟作文字之运算,依 WHATWG `application/x-www-form-urlencoded` 之规,成查询串、析查询串,并改网址之一查询参。三术无权限之求、无外效;今惟云工有适配(借宿主之 `URLSearchParams` 与 `URL`),他宿主若承之,须得同果。

`编码查询串 项列` 受有序(名,值)之列,返表单编码之查询串(即 `new URLSearchParams(项列).toString()`):空格作 `+`;除字母、数及 `*-._` 外皆依 UTF-8 作百分号编码,故 `!'()~` 亦被编码;同名之项守其原序;空列得空文;名与值可含任意 UTF-8 及控字符。限额:至多一零二四项,诸名与值之总字节不逾 1 MiB,逾则违约(「查询项超过 1024 项」「查询项总长度超过 1 MiB」)。

`解析表单键值 文` 依 WHATWG `application/x-www-form-urlencoded` 之规而析:以 `&` 分项,空项不理,首个 `=` 之前为名,其后为值,`+` 视为空格,百分号解之,坏 UTF-8 之序以 U+FFFD 代之;所入之文首字若为 `?`,先去此一字(同 `URLSearchParams` 构造器,故可径授含首问号之原查询串)。返守原序之 `[["名","值"],…]` JSON 数组文,同名之项各占其一,空入得 `[]`;所返之文乃宿主所书之紧凑 JSON,客当以 JSON 析之,不恃空白与转义之细节。所入不逾 1 MiB,逾则违约(「表单文字超过 1 MiB」)。

`设置网址查询参数 网址 键 值` 依 `URL` 与 `searchParams.set` 之义设一查询参:同名之旧项尽易为一项,居首个同名项之处,余参皆留,并依表单之规重书全查询串(故余参之百分号写法或被规整);片段原样存之。
- 绝对之址:必为 `http:` 或 `https:`(他方案如 `javascript:`、`data:`、`ftp:` 则败,「绝对网址不是 HTTP(S)」),返完整之 `href`。
- 相对之址:以内部固定基址 `https://yuyan-lang.org` 析之,惟返 `pathname + search + hash`;析后之源若非此基址(如 `//evil.example/x`、`\\evil.example`)则败(「相对网址不得改变来源」),不可析则败(「网址无效」)。
- 限额:网址、键、值各不逾 64 KiB(「网址、键或值超过 64 KiB」)。键与值可含任意 UTF-8 及控字符,宿主自为编码,不伤参数。

## 败之类

违约(可捕之事故):项数或总长逾限、所入逾限、网址无效、方案非 HTTP(S)、相对之址易源。此包无业务之情分支:果恒为文。

## 一致之例

| 调用 | 必见 |
| --- | --- |
| `编码查询串` 授 [("a b","1+2"),("a b","!'()~*-._")] | `a+b=1%2B2&a+b=%21%27%28%29%7E*-._` |
| `编码查询串` 授 [("名","\u0001\n")] | `%E5%90%8D=%01%0A` |
| `解析表单键值` 授 `a=1&b=x+y%20z&a=%E4%BD%A0&c&d=%ff&=e` | `[["a","1"],["b","x y z"],["a","你"],["c",""],["d","\ufffd"],["","e"]]`(JSON 中之 U+FFFD 可以字面或转义书之) |
| `解析表单键值` 授 `?a=1` | `[["a","1"]]` |
| `设置网址查询参数` 授 `/p?a=1&b=2&a=3#h`,键 `a`,值 `新 值` | `/p?a=%E6%96%B0+%E5%80%BC&b=2#h` |
| `设置网址查询参数` 授 `https://h.example/p?a=1`,键 `b`,值 `2` | `https://h.example/p?a=1&b=2` |
| `设置网址查询参数` 授 `//evil.example/x` 或 `javascript:alert(1)` | 违约 |

## 所不为

不发网请,不析不验主机名,不作路径规整以外之网址改写,不支持数组式(`a[]=`)或嵌套之查询约定,不供多值之删,亦不排序诸参。

## 补:表单项数之限与首 BOM(独立审后修订)

- `解析表单键值` 至多一千二十四项(以 `URLSearchParams.size` 计),逾者抛可捕之「表单项数超过 1024」,免一 MiB 之表单产五十余万项而拖累癸象之解析。
- 文授宿主 TextDecoder 用 `ignoreBOM: true`:文首之 U+FEFF 存之。