在宿主的隔离编译环境里把多文件豫言项目编译为 WasmGC 程序:拉取式逐个取回阶段提示与编译器输出,再取产物字节或 Base64。
源码浏览已生成;不能解析的文件以纯文本展示,不表示通过类型检查。
# 编译运行接口规范 0.1.0
本包让应用在宿主的隔离编译环境里,把一个**多文件豫言项目**编译成 WasmGC 程序,并在编译进行中逐个取回阶段提示与编译器输出。它是“云端编译服务”的核心:请求的校验、限额、响应形状与流式回报都由应用用豫言决定;宿主只做“运行豫言编译器”这一件事——内存文件系统、标准库资料、编译器 Wasm 与 Binaryen 组装。编译产物是文件式 WasmGC 程序,可直接交给[隔离运行接口](../隔离运行/规范.汉语.md)运行:本包的 `读取编译产物Base64` 给出的正是它的程序参数。这是云工专用能力:宿主用外壳在进程里固定的编译资料运行编译器,应用须在许可里授权一个 `COMPILE` 绑定名。
## 运行模型
拉取式,一次运行的生命周期是:`启动编译运行` 得到运行号 → 循环 `读取编译运行事件` 阻塞取下一个事件,直到取到终止事件 → 按需 `读取编译产物` 或 `读取编译产物Base64` → `结束编译运行` 释放。运行号是宿主生成的、不可猜测的字符串;应用不能自造。**运行号只在启动它的事件内有效**:另一个事件里拿到的运行号(哪怕字符串相同)一律使调用抛出可捕获的豫言异常,不会误操作别的事件的运行(同 网页事件流 的流柄规则;也因为 Worker 一类宿主不允许一个事件的异步工作在别的事件里继续)。所以一次编译的启动、事件读取、产物读取与结束必须发生在同一个事件里。事件按发生顺序取出,宿主可以边编译边产出,也可以编译结束后一次产出,接口都不承诺产出时机(应用不得依赖“先见到过程事件、后编译结束”的时序)。
## 函数
`启动编译运行 编译器名 请求`,返回运行号。
| 参数 | 含义 |
| --- | --- |
| 编译器名 | 应用许可里 `COMPILE` 列表授权的绑定名,1 至 128 字节,不含控制字符;未授权或绑定不存在使本次事件失败 |
| 请求 | JSON 文字,至多 2097152 字节:`{"files":{"路径":"文字",…},"entry":"路径"}`,其他字段忽略 |
请求规则:`files` 是路径到文字的对象,含 1 至 256 个文件,值都是字符串,全部内容合计至多 262144 个 UTF-8 字节;每个路径至多 256 个 UTF-16 码元,不含反斜线与 U+0000 至 U+001F 的控制字符,不以 `/` 起首,且以 `/` 分段后每段非空、不是 `.` 或 `..`。`entry` 缺省或为 `null` 时取 `入口。豫`,必须是合规路径且在 `files` 里。违反任何一条,或尚未终止的运行已有 8 个(宿主忙),都抛出可捕获的豫言异常(消息中文,说明原因),不产生运行;绑定未授权则使本事件失败。成功时立即返回,编译在宿主里进行。
`读取编译运行事件 编译器名 运行号 等待毫秒`,返回(状态,事件 JSON 文字)。
- 阻塞等待这次运行的下一个事件。等待毫秒须在 1 至 60000,越界抛异常。
- 状态 `0`:得到一个事件,文字是它的 JSON(每个事件是一个 JSON 对象,见下节)。
- 状态 `1`:等待毫秒到期而没有新事件,运行仍在进行,事件文字为空串。应用可据此写心跳、检查自己的期限或继续等待。
- 状态 `2`:运行已终止且全部事件已取尽(终止事件已经在前面某次以状态 0 交付过),或者读取者在等待期间运行被 `结束编译运行` 结束;事件文字为空串。
- 同一运行同一时刻至多一个等待者;运行号无效(空、过长、含控制字符)、不属于当前事件、未知或已结束,抛出可捕获的豫言异常。
`读取编译产物 编译器名 运行号`,返回字节串:编译成功(终止事件是带 `artifact` 的 `finished`)的 WasmGC 程序原始字节,至多 8388608 字节,可含零字节。运行尚未终止、编译失败、宿主故障或运行已结束,抛出可捕获的豫言异常。`读取编译产物Base64` 返回同一份产物的 Base64 文字(标准字母表,带 `=` 填充,无换行),前提与失败相同。两个函数在 `结束编译运行` 之前可重复调用,结果相同。
`结束编译运行 编译器名 运行号` 结束运行并释放宿主资源(事件队列、产物字节),返回单位值。幂等:运行号未知或已结束不报错。尚未开始编译的运行不再编译;已经在编译的运行无法被打断,宿主丢弃它的后续结果。结束后再用该运行号读取事件或产物抛出可捕获的豫言异常;正在等待事件的读取者立即得到状态 2。运行号不属于当前事件同样抛出异常(幂等只对本事件里已结束或未知的运行号)。应用应当在任何路径(含出错)上都结束自己启动的运行,否则运行留在宿主里直到 5 分钟的存活期满被宿主回收;事件异常退出遗留的运行既不会永久占用并发额度(并发额度只算尚未终止的运行),也不会无限堆积(见限额)。
## 事件
事件文字是一个 JSON 对象,`type` 区分种类。宿主可以增加新的过程事件种类,应用遇到不认识的种类须忽略而不是报错。
| type | 字段 | 含义 |
| --- | --- | --- |
| `stage` | `label`:中文阶段提示;可带 `phase`(`compile`) | 编译进行到的阶段,依据实际执行的步骤发出。至少有“正在加载标准库与项目文件”与“正在执行豫言编译器”,其后编译器组装 Wasm 时还有“正在组装 Wasm(N 字符)” |
| `output` | `phase`(`compile`)、`stream`(`stdout` 或 `stderr`)、`text` | 编译器写出的一段输出,原样、不截断、不拆行合并 |
| `diagnostic` | `text` | 编译器异常终止时的诊断(最近读取的文件与错误栈),只在失败时出现 |
| `finished` | `result`:`{"ok":爻,"stdout":文字,"stderr":文字,"exitCode":整数,"error"?:文字}`;`artifact`(仅成功):`{"format":"wasmgc","bytes":整数,"sha256":64 位小写十六进制}` | **终止事件**:编译器已运行完毕。`ok` 为真当且仅当退出码为 0;`stdout` 与 `stderr` 是全部输出,等于依序拼接全部同名 `output` 事件的 `text`。编译失败(语法错误、类型错误、输出超限、超时)是 `finished` 且 `ok` 为假,没有 `artifact`,`error` 与 `stderr` 说明原因 |
| `failed` | `error`:中文原因 | **终止事件**:宿主故障,编译器没有给出结果(标准库资源不可用、文件系统超限、产物超过 8 MiB 等) |
每次运行恰有一个终止事件,它总是最后一个事件。`artifact.sha256` 是产物字节的 SHA-256;`bytes` 是字节数。
## 编译环境
- **标准环境**:每次运行都在**全新的内存文件系统**里进行,预置宿主固定版本的标准库资料;包上下文由宿主固定:包 `豫言/标准库`,以及所有者 `访客`、名 `云项目`、依赖 `标准库` 的项目包。项目文件放在项目包目录(宿主内部路径 `/用户程序/`)下,`entry` 相对它;项目若含 `云项目。包。豫` 则采用它,否则宿主放一个空描述。编译目标固定为 WasmGC。应用不能选择编译器版本、目标、包上下文或标准库。
- **无外部能力**:编译过程没有网络、原生进程、环境变量与宿主文件;运行之间不遗留任何用户文件或产物,任何一次运行也读不到前次运行写入的东西。
- **限额**:编译器输出(`stdout` 与 `stderr` 合计)至多 2097152 字节,超过则编译失败;一次编译的墙钟期限由宿主定(云工宿主是 90 秒,在编译器调用宿主原语时检查);产物至多 8388608 字节;事件文字合计至多 16777216 字节(超出的过程事件被宿主丢弃,终止事件不丢)。同一宿主进程里尚未终止(排队或编译中)的运行至多 8 个,超过则 `启动编译运行` 抛异常;已终止而等待读取的运行另留至多 32 个,更多时丢弃最旧的;任何运行自创建起至多存活 5 分钟。编译阶段串行进行(同一时刻只有一份编译文件系统副本),排在后面的运行先等待。
- 同一版本的编译器与标准库对同一份项目应给出相同的 `ok`、`exitCode` 与产物字节;编译器日志行带墙钟时间戳,`stderr` 的文字因此不逐字节可比。
## 失败分类
- **违规(可捕获的豫言异常)**:请求不合规、超限、并发运行过多、编译器名不合规、等待毫秒越界、运行号无效或不属于当前事件、运行号未知或已结束、同一运行已有等待者、产物不可用。异常消息是中文,说明原因。
- **绑定未授权或不存在**:使本次事件失败(宿主错误,应用不能捕获),与其他云工接口一致;这是部署配置问题,不是运行时业务情形。
- **编译失败**:是**返回值**(`finished` 且 `ok` 为假),不是异常。
- **宿主故障**:是 `failed` 事件,不是异常。
## 一致性样例
1. 入口 `寻观「标准库」之书。寻观「云项目」之「问候」之书。「打印行」于「问候」。`、`问候。豫`(定义 `问候` 为『你好,多文件』)与 `云项目。包。豫` 三个文件:事件序列以 `stage`(正在加载标准库与项目文件)、`stage`(正在执行豫言编译器)起头,若干 `output` 之后恰有一个 `finished`(`ok` 为真、`exitCode` 为 0)与 `artifact`;再读事件得状态 2;`读取编译产物` 的字节以 Wasm 魔数起头且长度等于 `artifact.bytes`,SHA-256 等于 `artifact.sha256`;`读取编译产物Base64` 解码后与字节一致;`stdout` 与 `stderr` 等于对应 `output` 的 `text` 依序拼接。
2. 入口内容 `此语不成立。`:`finished`、`ok` 为假、无 `artifact`;`读取编译产物` 抛出异常;`stderr` 或 `error` 提到入口文件。
3. 请求 `{"files":{"../逃逸。豫":""}}`、`{"files":{}}`、`{"files":{"入口。豫":1}}`、入口内容 262145 字节、文件名含反斜线或以 `/` 起首、`entry` 不在 `files` 里:`启动编译运行` 都抛出异常,宿主里没有产生运行。
4. 先后启动两次内容不同的运行:各自的产物与事件互不串扰;第二次读不到第一次写入的任何文件(同一路径的第二个项目不会看到第一个项目的其他文件)。
5. 事件流取尽后再读:状态 2;`结束编译运行` 之后再读事件、再读产物:抛异常;重复结束:不报错。
6. 阻塞等待时另一个并发读取者:抛异常,不影响原等待者;等待毫秒为 0 或 60001:抛异常。
7. 在另一个事件里使用启动事件返回的运行号(读取事件、读取产物、结束):抛异常,原事件里的运行不受影响。
## 边界
本包只做编译:不运行产物(用隔离运行接口)、不解析或托管 HTTP、不保存任何状态、不提供网络、不做包解析(只有标准库和项目自己的文件)、不做编译缓存的对外承诺。宿主可以用不同的技术实现(云工用 Worker 内的 WasmGC 编译器与静态 Binaryen,别的宿主可用进程沙箱或容器),只要满足上述环境、限额与事件语义。`COMPILE` 是云工的许可种类名;别的宿主用各自的授权方式,接口只要求“编译器名由应用给出、由宿主核对”。
# 编译运行公约 0.1.0
此包使客于宿主之隔绝编室,编**多文之豫言项目**为 WasmGC 程序,并于编时逐一取其阶段之告与编器之出。此乃“云端编译之务”之核:请求之验、限额、应答之形与流式之报,皆客以豫言决之;宿主惟司一事,曰“行豫言之编器”——内存文件系、标准库之资、编器 Wasm 与 Binaryen 之组装。所成之物乃文件式 WasmGC 程序,可径授[隔离运行公约](../隔离运行/规范.文言.md)运之:本包 `读取编译产物Base64` 所返,正其程序之参。此乃云工专用之能:宿主以外壳于进程中固定之编资运编器,客之许须授一 `COMPILE` 绑定之名。
## 运之制
拉取之式。一运之始终:`启动编译运行` 得运行号 → 循环 `读取编译运行事件` 阻候下一事,至得终止之事 → 按需 `读取编译产物` 或 `读取编译产物Base64` → `结束编译运行` 释之。运行号乃宿主所生、不可猜之字符串,客不得自造。**运行号惟启之之事有效**:别事所得之号(纵字同)一律使调用抛可捕之豫言异,不误操他事之运(同网页事件流之流柄;亦以 Worker 之类宿主不许一事之异步工作续于他事也)。故一编之启、读事、取产物与终,须皆在同一事内。事依发生之序而出;宿主可边编边出,亦可编毕一并出,公约不承诺出之时,客不得恃“先见过程之事、后见编毕”之序。
## 术
`启动编译运行 编译器名 请求`,返运行号。
| 参 | 义 |
| --- | --- |
| 编译器名 | 客之许 `COMPILE` 列中所授之绑定名,一至一百二十八字节,不含控制之字;未授或绑定不存则本事失败 |
| 请求 | JSON 文,至多二百零九万七千一百五十二字节:`{"files":{"路径":"文",…},"entry":"路径"}`,余字段不问 |
请求之则:`files` 乃路径至文之对象,含一至二百五十六件,值皆字符串,诸内容合至多二十六万二千一百四十四 UTF-8 字节;每路径至多二百五十六 UTF-16 码元,不含反斜线与 U+0000 至 U+001F 之控制字,不以 `/` 起,依 `/` 分段后每段非空且非 `.`、`..`。`entry` 缺或为 `null` 则取 `入口。豫`,须为合规之路径而在 `files` 中。违一则,或未终之运已有八(宿主忙),皆抛可捕之豫言异,辞为中文而明其故,不生运;绑定未授则使本事失败。成则即返,编于宿主中行。
`读取编译运行事件 编译器名 运行号 等待毫秒`,返(状态,事 JSON 文)。
- 阻候此运之下一事。等待毫秒须在一至六万,越则抛异。
- 状态 `0`:得一事,文为其 JSON(每事乃一 JSON 对象,见下节)。
- 状态 `1`:等待毫秒已满而无新事,运仍行,事文为空。客可因而写心跳、察己之限或续候。
- 状态 `2`:运已终而诸事已尽(终止之事已于前某次以状态零交付),或读者候时运被 `结束编译运行` 所终;事文为空。
- 同一运同一时至多一候者;运行号无效(空、过长、含控制字)、不属当前事、未知或已结束,抛可捕之豫言异。
`读取编译产物 编译器名 运行号`,返字节串:编成(终止之事乃带 `artifact` 之 `finished`)之 WasmGC 程序原字节,至多八百三十八万八千六百零八字节,可含零字节。运未终、编败、宿主故或运已结束,抛可捕之豫言异。`读取编译产物Base64` 返同一产物之 Base64 文(标准字母表,带 `=` 之填充,无换行),前提与败同上。二术于 `结束编译运行` 之前可重复用,其果同。
`结束编译运行 编译器名 运行号` 终运而释宿主之资(事之队、产物之字节),返单位值。幂等:运行号未知或已结束不为错。未始编之运不复编;已在编之运不可断,宿主弃其后果。既结束,再以此号读事或产物则抛可捕之豫言异;正候事之读者立得状态二。运行号不属当前事者亦抛异(幂等惟对本事内已结束或未知之号)。客当于一切路径(含出错)终己所启之运,否则运留于宿主至五分钟存活之期满而为宿主所收;事异退所遗之运,既不永占并发之额(并发之额惟计未终之运),亦不无限而积(见限额)。
## 事
事文乃一 JSON 对象,以 `type` 别其类。宿主可增新之过程事类,客遇不识之类当置之而不为错。
| type | 字段 | 义 |
| --- | --- | --- |
| `stage` | `label`:中文阶段之告;可带 `phase`(`compile`) | 编所至之阶,依所实行之步而发。至少有“正在加载标准库与项目文件”“正在执行豫言编译器”,其后编器组装 Wasm 时又有“正在组装 Wasm(N 字符)” |
| `output` | `phase`(`compile`)、`stream`(`stdout` 或 `stderr`)、`text` | 编器所写出之一段,原样,不截,不并行 |
| `diagnostic` | `text` | 编器异终时之诊(最近所读之文件与错误之栈),惟败时有之 |
| `finished` | `result`:`{"ok":爻,"stdout":文,"stderr":文,"exitCode":整数,"error"?:文}`;`artifact`(惟成时):`{"format":"wasmgc","bytes":整数,"sha256":六十四位小写十六进制}` | **终止之事**:编器已行毕。`ok` 为真,当且仅当退出码为零;`stdout`、`stderr` 乃诸出之全,等于依序拼诸同名 `output` 事之 `text`。编败(语法误、类型误、出逾限、逾时)乃 `finished` 而 `ok` 为假,无 `artifact`,`error` 与 `stderr` 明其故 |
| `failed` | `error`:中文之因 | **终止之事**:宿主之故,编器未给其果(标准库之资不可用、文件系逾限、产物逾八 MiB 等) |
每运恰有一终止之事,恒为最后一事。`artifact.sha256` 乃产物字节之 SHA-256;`bytes` 乃其字节数。
## 编室
- **标准之境**:每运皆于**全新之内存文件系**中行,预置宿主固定版之标准库资;包上下文由宿主固定:包 `豫言/标准库`,及所有者 `访客`、名 `云项目`、依 `标准库` 之项目包。项目之文置于项目包之目录(宿主内部之径 `/用户程序/`)下,`entry` 相对之;项目若含 `云项目。包。豫` 则用之,否则宿主置一空描述。编之目标固定为 WasmGC。客不得择编器之版、目标、包上下文或标准库。
- **无外能**:编时无网、无原生进程、无环境变量、无宿主之文件;运与运之间不遗任何用户之文与产物,任一运亦读不到前运所写。
- **限额**:编器之出(`stdout` 与 `stderr` 合)至多二百零九万七千一百五十二字节,逾则编败;一编之墙钟期限由宿主定(云工宿主为九十秒,编器调宿主原语时察之);产物至多八百三十八万八千六百零八字节;事文合至多一千六百七十七万七千二百一十六字节(逾者过程之事为宿主所弃,终止之事不弃)。同一宿主进程中未终(候或编中)之运至多八,逾则 `启动编译运行` 抛异;既终而待读之运另留至多三十二,逾则弃其最旧;诸运自创至多存活五分钟。编阶段串行(同一时唯一份编文件系副本),后者先候。
- 同版之编器与标准库,于同一项目,`ok`、`exitCode` 与产物字节当同;编器之志行带墙钟之时戳,故 `stderr` 之文不可逐字节而较。
## 败之分
- **违规(可捕之豫言异)**:请求不合规、逾限、并运过多、编译器名不合规、等待毫秒越界、运行号无效或不属当前事、运行号未知或已结束、同一运已有候者、产物不可用。异之辞为中文,明其故。
- **绑定未授或不存**:使本事失败(宿主之误,客不可捕),与他云工公约同;此乃部署配置之事,非运时之业务情形。
- **编败**:乃**返值**(`finished` 而 `ok` 为假),非异。
- **宿主之故**:乃 `failed` 之事,非异。
## 一致之例
1. 入口 `寻观「标准库」之书。寻观「云项目」之「问候」之书。「打印行」于「问候」。`、`问候。豫`(以『你好,多文件』为 `问候`)与 `云项目。包。豫` 三文:事序以 `stage`(正在加载标准库与项目文件)、`stage`(正在执行豫言编译器)始,若干 `output` 之后恰有一 `finished`(`ok` 为真、`exitCode` 为零)与 `artifact`;再读事得状态二;`读取编译产物` 之字节以 Wasm 魔数始而长等于 `artifact.bytes`,SHA-256 等于 `artifact.sha256`;`读取编译产物Base64` 解码后与字节同;`stdout`、`stderr` 等于对应 `output` 之 `text` 依序所拼。
2. 入口之文为 `此语不成立。`:`finished`、`ok` 为假、无 `artifact`;`读取编译产物` 抛异;`stderr` 或 `error` 言及入口之文。
3. 请求 `{"files":{"../逃逸。豫":""}}`、`{"files":{}}`、`{"files":{"入口。豫":1}}`、入口之文二十六万二千一百四十五字节、文名含反斜线或以 `/` 起、`entry` 不在 `files` 中:`启动编译运行` 皆抛异,宿主中不生运。
4. 先后启两运而其文不同:各自之产物与事不相扰;后者读不到前者所写之任何文(同一径之次项目不见前项目之他文)。
5. 事尽后再读:状态二;`结束编译运行` 后再读事、再读产物:抛异;重复结束:不为错。
6. 阻候时别有并发之读者:抛异,不扰原候者;等待毫秒为零或六万零一:抛异。
7. 于他事中用启之之事所返之运行号(读事、取产物、终):抛异,原事中之运不受扰。
## 界
此包惟司编:不运产物(用隔离运行公约)、不解不托 HTTP、不存任何状、不供网、不解包(惟标准库与项目自己之文)、不对外承诺编译之缓存。宿主可以异术实现(云工以 Worker 内之 WasmGC 编器与静态 Binaryen,他宿主可用进程沙箱或容器),惟须合上述之境、限与事之义。`COMPILE` 乃云工许可之种名;他宿主各以其授权之法,公约惟求“编译器名由客给,由宿主核”。