豫言 / 豫言操作系统命名持久服务 @ 0.3.0

向授权命名持久对象发请求,读取有限或流式回应,并转发入站请求。

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

规范.汉语.md

# 命名持久服务接口规范 0.3.0

`请求命名持久服务文字` 接受授权绑定名、对象名称、内部绝对网址、方法、查询键值列表、请求标头键值列表和文字正文。宿主先按当前程序的 `DO` 许可核对绑定名,再以 `getByName` 定位该绑定内的对象;不得以对象名选择其他绑定或外部网络。对象名称为非空且不超过 128 字符的 ASCII 字母、数字、下划线、连字符或斜线(斜线自 0.3.0 起允许,见文末“0.3.0 变更”)。内部网址只能位于 `https://asset/` 或 `https://internal/`,其路径拒绝反斜线、控制字符、`?` 和 `#`;不得携带自带查询或片段。方法限 GET、HEAD、POST。查询参数最多 16 项,由宿主编码;请求标头仅允许精确名称 `Origin`、`Content-Type`、`Cookie`,最多 8 项且不得重名,值只含可见 ASCII 字符(0x20 至 0x7E)、首尾不得为空格且至多 8192 字节;GET/HEAD 正文必须为空,POST 正文最多 8 MiB(0.1.0 为 2 MiB)。正文按字节发送,不经 JSON 转义,任意字符原样送达。

宿主返回有效 JSON 对象文字:`status` 为上游整数状态,`body` 为完整 UTF-8 文字正文(宽松解码,非法序列成 U+FFFD,字节序标记去掉,同 `Response.text()`),`headers` 只包含上游存在的 `content-type`、`location`、`set-cookie`、`x-yuyan-revision` 四个小写名称和值。正文最多 8 MiB(0.1.0 为 2 MiB),超出则抛豫言异常;先看 `Content-Length`,无声明时流式累计。HTTP 4xx/5xx 仍是成功取得的响应。对象抛错或请求失败抛豫言异常(0.1.0 中此情形会使宿主崩溃,不可捕获)。宿主不得泄露其他响应头、句柄或当前程序之外的对象。当前 `_start` 边界以 JSON 文字传递结果;长期类型化入口可将其改成豫言记录与字节流。

## 0.2.0 新增:流式响应、增量读取与转发入站请求

0.2.0 只新增八个函数,`请求命名持久服务文字` 的签名与语义不变(限额提高与失败可捕获见上)。新增部分服务于“读云端项目对象的 SSE、NDJSON 长响应”与“把入站请求原样转给对象并回传其事件流”。

### 流式请求

`请求命名持久服务流(绑定名, 对象名, 网址, 方法, 查询, 标头, 正文, 超时毫秒)` 返回“(成功,响应柄或错误文)”。绑定名、对象名、网址、方法、查询、标头、正文的校验与 `请求命名持久服务文字` 完全相同(违规抛豫言异常,不触及对象),另加超时毫秒 1 至 900000(越界抛豫言异常),期限用 `AbortSignal.timeout` 覆盖“发请求、等响应头、读响应正文”的整次交换。不跟随重定向。请求失败(对象抛错、传输失败)或响应头到达前期满,返回“阴”和以 `持久服务请求失败或超时:` 或 `持久服务请求超时` 开头的错误文;成功返回“阳”和响应柄。正文按字节发送。

### 响应柄与增量读取

响应柄是不透明字符串,仅在创建它的事件内有效,不可保存、序列化或伪造;`请求命名持久服务文字` 内部也使用同一机制,不返回柄。不是本包签发的字符串一律抛豫言异常。同一柄的读取应当串行,不同柄可交错。

- `读取命名持久服务响应标头(柄, 名)`:返回响应标头值,名不区分大小写、须是 1 至 128 字节的合法标头令牌,缺失返回空字符串。此函数可读到 `x-yuyan-revision` 之外的任意响应标头,因为对象是应用自己的代码。
- `读取命名持久服务响应状态(柄)`:返回 HTTP 状态码。
- `读取命名持久服务响应文字块(柄, 最大字节)`:返回“(状态,文字)”,最大字节 1 至 65536。状态 0 数据块:宿主做流式 UTF-8 解码,跨块拆开的多字节字符正确拼接,非法序列成 U+FFFD,字节序标记去掉,文字绝不含半个字符,UTF-8 字节数不超过最大字节(小于 4 按 4 计),多出部分留待后续调用(上游连续送来 64 个以上空块的极端情形,可返回文字为空的状态 0,应用照常继续读取);状态 1 读尽(文字为空;无正文响应首读即得;流末残缺序列先以一个 U+FFFD 作状态 0 的块返回);状态 2 读取错误(文字为“名称: 消息”);状态 3 期满、已取消或有限读取超限已取消(文字为简短说明)。1、2、3 为终态,再调用返回同样结果。
- `读取命名持久服务响应有限文字(柄, 字节上限)`:一次读完剩余正文,字节上限 1 至 8388608。状态 0 成功;状态 1 超过上限(先看 `Content-Length`,声明超限则一字不读;无声明或不实则流式累计,超过即取消,恰等于上限不算超限);状态 2 错误;状态 3 期满或已取消。
- `取消命名持久服务响应(柄)`:取消读取并对对象响应流执行 cancel,此后读取返回状态 3;幂等。对象能否即时回调其流的 cancel 取决于平台,不作保证。

### 以流式响应作最终响应

`回应入站以命名持久服务原答(柄)` 把 `请求命名持久服务流` 得到的响应(状态、标头、正文流,含 SSE)原样作为当前入站请求的最终响应。用于“先向对象发内部请求,检查状态并做其他处理,再把对象的事件流回传给客户端”。柄须由 `请求命名持久服务流` 签发;已开始增量读取(或已定局)的柄抛豫言异常;一次入站请求只能交付一个最终响应;交付后该柄不可再读,读取返回状态 3 与 `已作终答`。

### 转发入站请求

`转发入站请求至命名持久服务(绑定名, 对象名, 网址, 查询, 保留标头名, 固定标头)` 把当前入站请求转给对象,并以对象的响应作为当前入站请求的最终响应。它需要当前入站请求;无则宿主报错。

- 方法:取入站方法,限 GET、HEAD、POST,否则抛豫言异常。正文:POST 时把入站请求的正文流原样接给对象(不解析、不缓冲,对象可在请求尚未发完时开始读取),GET/HEAD 不带正文;入站正文若已被读取或已上锁,抛豫言异常。
- 网址与对象:对象名、内部网址(`https://asset/` 或 `https://internal/` 开头,路径规则同上)、查询(至多 16 项,由宿主编码)的规则同 `请求命名持久服务文字`。
- 保留标头名:至多 16 项,逐名不区分大小写地只允许 `Accept`、`Authorization`、`Content-Type`、`Cookie`、`CF-Connecting-IP`、`Idempotency-Key`、`If-None-Match`、`Last-Event-ID`、`Origin`、`Range` 与以 `X-Yuyan-` 开头者;不得重复;入站请求没有的略过。宿主只把这些标头的值原样交给对象,其余入站标头(如 `Host`、`Content-Length`、`Transfer-Encoding`、`X-Forwarded-*`)一概不转发,且这些值不经过豫言程序的字符串。
- 固定标头:至多 8 项,名只允许 `Accept`、`Content-Type`、`Origin` 与以 `X-Yuyan-` 开头者,值至多 8192 字节的可见 ASCII,不得与保留名或彼此重复,以固定值写入。
- 响应:对象的 Response(状态、标头、正文流,含 SSE)原样成为最终响应,宿主不缓冲、不改写;一次入站请求只能交付一个最终响应。不跟随重定向。对象抛错或请求失败抛豫言异常,不设响应。

### 失败分类

| 情形 | 类别 |
| --- | --- |
| 对象名、网址、方法、查询、标头、正文、超时、块上限、字节上限等参数违规;无效响应柄;转发时入站正文已被读取;已增量读取后作原答;对象抛错(文字版与转发) | 抛豫言异常,不发请求或不设响应 |
| 绑定未授权或不存在 | 部署错误,宿主报错 |
| 流式请求失败,或响应头到达前期满 | 返回“阴”和原因 |
| 读取中对象出错、期满、取消、超限 | 读取函数的状态 2、3、1 |
| HTTP 4xx、5xx | 成功响应,由应用按状态码处理 |

### 限额

对象名 128 字符;查询 16 项;请求标头 8 项、值 8192 字节;请求与响应正文 8 MiB;流式超时 900000 毫秒;块 65536 字节;有限整读 8 MiB;转发保留名 16 项、固定标头 8 项。

### 跨宿主一致性

不同宿主须满足:对象只经授权绑定内按名定位;先校验后发请求;流式 UTF-8 解码与拼接;期满后读取返回状态 3 并对对象响应流执行 cancel;取消对对象响应流执行 cancel;转发时不缓冲请求与响应正文,响应逐块随对象产生而到达;只转发白名单标头;不跟随重定向。

### 一致性样例

- 对象返回 3 个 SSE 事件,按 11 字节切块,增量读取得到的文字拼起来等于原文。
- 查询 `a=1 2`、`b=中文&=`:对象收到 `?a=1+2&b=%E4%B8%AD%E6%96%87%26%3D`;`Origin`、`Content-Type`、`Cookie` 原样到达;POST 正文(含引号、反斜线、换行、制表、表情)逐字节相同。
- 对象长久不出块,超时 300 毫秒:得已到的块,再得状态 3,宿主对对象响应流执行 cancel(原因 `TimeoutError`;能回调 cancel 的宿主可观察到);对象迟迟不回响应头:得“阴”和原因。
- 文字版:响应 8388608 字节成功,8388609 字节抛异常;请求正文 8388608 字节可发,多一字节抛异常;对象响应头 `x-secret` 不出现在结果里。
- 先请求后作原答:向对象 GET `/events`,读得状态 207 后 `回应入站以命名持久服务原答`,客户端收到同样的状态、头与逐事件到达的正文;若先读过一块再作原答,抛异常。
- 转发:对象每 80 毫秒发一个事件,共 4 个,入站请求方逐个收到(首个与末个到达时刻相差不小于 150 毫秒);状态 207 与 `x-yuyan-revision` 保留;入站 POST 请求体先发首块,对象读到首块后入站方才发第二块,仍能完成(缓冲整个请求体会死锁);只转发所列标头,`Host`、`X-Cfg` 之类不转;保留名含 `Host` 抛异常。

### 0.3.0 变更:对象名称允许斜线

0.3.0 不新增函数、不改任何签名。对象名称的字符集由“ASCII 字母、数字、下划线、连字符”放宽为再加斜线 `/`,长度仍为 1 至 128 字符。原因:云工作台的项目对象自旧实现起就以 `账户编号/项目编号` 命名(Durable Object 的标识由名称派生,改名即丢失既有数据),接口必须能按原名定位它们。斜线可以出现在名称的任何位置,宿主只把名称交给 `getByName`,不把它当作路径、网址或文件名解释;其他字符(空格、点、冒号、反斜线、非 ASCII 等)仍一律拒绝,抛“持久服务对象名无效”。三个函数(文字版、流式版、转发)共用这一规则。

一致性样例(0.3.0 新增):对象名 `7/3f2a9c1e-4b5d-4e6f-8a7b-9c0d1e2f3a4b`(账户编号加斜线加项目编号)可用,宿主以该原名 `getByName`;`a b`、`a.b`、`a\b`、`中文`、空串、129 个字符的名称仍抛出可捕获的豫言异常。

### 边界

宿主不保证对象观察到取消,只保证读取一方立即得到状态 3、不再拉取对象。本包不提供:选择多个流中先到者的原语;对象之间的调用与广播;对象的创建与唯一标识(只按名定位);WebSocket;跨事件保存响应柄;自动重试。宿主执行事件的总时限由宿主配置,本包只保证按给定期限令读取失败。

规范.文言.md

# 命名持久服务之约 0.3.0

`请求命名持久服务文字` 受获授绑定名、对象名、内部绝对址、法、查询键值列、请头列与正文。宿主先循当前程序 `DO` 许可核绑定,乃以 `getByName` 定其内对象;不得因对象名越至异绑定或外网。对象名非空,至多一百二十八字,惟 ASCII 字母、数字、下划线、连字符、斜线(斜线自〇点三始许,见文末“〇点三之变”)。内部址惟许 `https://asset/` 或 `https://internal/`,其径拒反斜线、控字、`?`、`#`,毋自带查询片段。法限 GET、HEAD、POST。查询至多十六项,由宿主编码;请头惟许精确 `Origin`、`Content-Type`、`Cookie`,至多八项且毋重名,值惟可见 ASCII 字(自空格至 `~`),首尾不得为空格,至多八一九二字节;GET/HEAD 正文必空,POST 正文不逾八 MiB(〇点一为二 MiB)。正文以字节发之,不经 JSON 转义,凡字皆原样而达。

宿主返有效 JSON 物文:`status` 为上游整数号,`body` 为全 UTF-8 文(宽解,非法序为 U+FFFD,去字节序记,同 `Response.text()`),`headers` 惟载上游现有之 `content-type`、`location`、`set-cookie`、`x-yuyan-revision` 四小写名及值。正文不逾八 MiB(〇点一为二 MiB),逾则告豫言异常;先观 `Content-Length`,无所称则流式累计。HTTP 错号仍属得答。对象抛错或请败则告豫言异常(〇点一此情使宿主崩,不可捕)。不得露异头、句柄或异程序之对象。今 `_start` 以 JSON 文传果,后可易豫言记录与字节流。

## 〇点二新增:流答、渐读与转入站请

〇点二惟增八术,`请求命名持久服务文字` 之型与义不变(限额之增与败之可捕见上)。所增者,为“读云端项目对象之 SSE、NDJSON 长答”与“以入站请原样转于对象而回传其事件流”而设。

### 流请

`请求命名持久服务流(绑定名, 对象名, 网址, 方法, 查询, 标头, 正文, 超时毫秒)` 返「(成否,答柄或误文)」。绑定名、对象名、网址、法、查询、头、正文之验,与 `请求命名持久服务文字` 全同(违则告豫言异常,不及对象),另加超时毫秒一至九〇〇〇〇〇(越界告豫言异常),期限以 `AbortSignal.timeout` 贯“发请、候答头、读答文”之全程。不随改向。请败(对象抛错、传输败)或答头未至而期满,返“阴”与误文,误文以 `持久服务请求失败或超时:` 或 `持久服务请求超时` 始;成则返“阳”与答柄。正文以字节发之。

### 答柄与渐读

答柄为不透明之文,惟创之之事有效,不可存、不可序、不可伪;`请求命名持久服务文字` 内部亦用此机,然不返柄。非本包所发之文皆告豫言异常。同柄之读当有序,异柄可错综。

- `读取命名持久服务响应标头(柄, 名)`:返答头之值,名不分大小写,须为一至一二八字节之合法头令牌,无则空文。此术可读 `x-yuyan-revision` 以外之任一答头,以对象乃客自己之码也。
- `读取命名持久服务响应状态(柄)`:返 HTTP 状态号。
- `读取命名持久服务响应文字块(柄, 最大字节)`:返「(状态,文)」,最大字节一至六五五三六。状态零得一块:宿主流式解 UTF-8,跨块而裂之多字节字必正相衔,非法序为 U+FFFD,去字节序记,文绝无半字,其 UTF-8 字节数不逾最大字节(不足四者以四计),溢出者留待后调(上游连送六十四空块以上之极端,可返文空之状态零,客照常续读);状态一读尽(文空;无正文之答首读即得;流末残缺之序先以一 U+FFFD 为状态零之块返之);状态二读败(文为“名称: 消息”);状态三期满、已止,或有限整读因逾限已止(文为简说)。一、二、三为终态,再调返同果。
- `读取命名持久服务响应有限文字(柄, 字节上限)`:一次读毕余文,字节上限一至八三八八六〇八。状态零成;状态一逾上限(先观 `Content-Length`,所称已逾则一字不读;无所称或不实则流式累计,逾则止,恰等于上限不为逾);状态二败;状态三期满或已止。
- `取消命名持久服务响应(柄)`:止读而对对象答流行 cancel,此后之读返状态三;幂等。对象能否即回调其流之 cancel,视平台而定,不作保证。

### 以流答为终答

`回应入站以命名持久服务原答(柄)` 以 `请求命名持久服务流` 所得之答(状态、头、正文流,含 SSE)原样为当前入站请之终答。所以处“先向对象发内请,验状态而行他事,乃以对象之事件流回传于客”者。柄须由 `请求命名持久服务流` 所发;已始渐读(或已定局)之柄告豫言异常;一入站请惟一终答;既交则此柄不可复读,读则返状态三与 `已作终答`。

### 转入站请

`转发入站请求至命名持久服务(绑定名, 对象名, 网址, 查询, 保留标头名, 固定标头)` 以当前入站请转于对象,并以对象之答为当前入站请之终答。须有当前入站请;无则宿主报。

- 法:取入站之法,限 GET、HEAD、POST,否则告豫言异常。正文:POST 则以入站请之正文流原样接于对象(不析、不缓冲,对象可于请未发尽时始读),GET/HEAD 不带正文;入站正文若已读或已锁,告豫言异常。
- 址与对象:对象名、内部址(`https://asset/` 或 `https://internal/` 起,径则同上)、查询(至多十六项,由宿主编码)之则同 `请求命名持久服务文字`。
- 保留头名:至多十六项,逐名不分大小写,惟许 `Accept`、`Authorization`、`Content-Type`、`Cookie`、`CF-Connecting-IP`、`Idempotency-Key`、`If-None-Match`、`Last-Event-ID`、`Origin`、`Range`,及以 `X-Yuyan-` 起者;不得重出;入站无者略之。宿主惟以此诸头之值原样交于对象,余入站头(如 `Host`、`Content-Length`、`Transfer-Encoding`、`X-Forwarded-*`)一概不转,且其值不经豫言程序之字符串。
- 定头:至多八项,名惟许 `Accept`、`Content-Type`、`Origin`,及以 `X-Yuyan-` 起者,值至多八一九二字节之可见 ASCII,不得与保留名或彼此重出,以定值书之。
- 答:对象之 Response(状态、头、正文流,含 SSE)原样为终答,宿主不缓冲、不改写;一入站请惟一终答。不随改向。对象抛错或请败告豫言异常,不设答。

### 败之分类

| 情形 | 类 |
| --- | --- |
| 对象名、址、法、查询、头、正文、时限、块限、字节上限诸参违规;柄无效;转发时入站正文已读;已渐读而作原答;对象抛错(文字术与转发) | 告豫言异常,不发请或不设答 |
| 绑定未授或无此绑定 | 部署之误,宿主报之 |
| 流请败,或答头未至而期满 | 返“阴”与因 |
| 读时对象出错、期满、已止、逾限 | 读术之状态二、三、一 |
| HTTP 四百、五百号 | 得答之成,由客按状态号处之 |

### 限额

对象名一二八字;查询十六项;请头八项、值八一九二字节;请与答之正文八 MiB;流时限九〇〇〇〇〇毫秒;块六五五三六字节;有限整读八 MiB;转发保留名十六项、定头八项。

### 异宿主之一致

异宿主必守:对象惟由授权绑定内按名而定;先验而后发请;流式解 UTF-8 而相衔;期满则读返状态三并对对象答流行 cancel;止则对对象答流行 cancel;转发不缓冲请与答之正文,答逐块随对象所产而至;惟转白名之头;不随改向。

### 一致之例

- 对象返三事件之 SSE,以十一字节切块渐读,文拼之与原文同。
- 查询 `a=1 2`、`b=中文&=`:对象所收为 `?a=1+2&b=%E4%B8%AD%E6%96%87%26%3D`;`Origin`、`Content-Type`、`Cookie` 原样而达;POST 正文(含引号、反斜线、换行、制表、表情)逐字节同。
- 对象久不出块,时限三〇〇毫秒:得已至之块,次得状态三,宿主对对象答流行 cancel(因 `TimeoutError`;能回调 cancel 之宿主可察之);对象久不回答头:得“阴”与因。
- 文字术:答八三八八六〇八字节成,八三八八六〇九字节告异常;请正文八三八八六〇八字节可发,多一字节告异常;对象答头 `x-secret` 不现于果。
- 先请后作原答:向对象 GET `/events`,读得状态二〇七后 `回应入站以命名持久服务原答`,客所收之状态、头同,正文逐事而至;若先读一块而后作原答,告异常。
- 转发:对象每八十毫秒发一事,共四事,入站方逐事而收(首末到时相差不小于一百五十毫秒);状态二〇七与 `x-yuyan-revision` 存之;入站 POST 请体先发首块,对象读得首块后入站方乃发次块,仍得成(缓冲全请体则死锁);惟转所列之头,`Host`、`X-Cfg` 之类不转;保留名含 `Host` 告异常。

### 〇点三之变:对象名许斜线

〇点三不增术、不改型。对象名之字集,由“ASCII 字母、数字、下划线、连字符”宽为兼许斜线 `/`,长仍一至一二八字。所以然者:云工作台之项目对象,自旧制即以“户号/项目号”为名(久物之识由名而出,易名则失旧籍),接口须能依原名而定之。斜线可居名中任何位;宿主惟以名授 `getByName`,不以为径、址或文件名;余字(空格、点、冒号、反斜线、非 ASCII 等)仍一概拒之,告“持久服务对象名无效”。文字术、流术、转发术三者同守此则。

一致之例(〇点三所增):对象名 `7/3f2a9c1e-4b5d-4e6f-8a7b-9c0d1e2f3a4b`(户号斜线项目号)可用,宿主以原名 `getByName`;`a b`、`a.b`、`a\b`、`中文`、空串、一二九字之名仍告可捕之豫言异常。

### 界

宿主不保证对象得见其止,惟保证读者一方立得状态三、不复拉取对象。此包不予:于诸流择先至者之术;对象间之呼与广播;对象之创与唯一之识(惟按名而定);WebSocket;跨事而存答柄;自动重试。宿主事之总限由宿主配之,此包惟保证依所给期限令读败。