asset
asset 只访问应用包中由 manifest 声明的只读资源。它解决“guest 如何取得自己的静态文件”和“宿主渲染器如何使用包内字体”,不是通用文件系统或编解码服务。
适用场景
- 读取应用随包发布的关卡、词典、模板或二进制模型。
- 将包内字体注册给
canvas的宿主文本渲染器。 - 由 guest 自己解码包内图片、音频或压缩数据。
用户选择的文件、拖放内容、网络下载、任意 URL、目录遍历和持久写回都不属于 asset。
manifest 声明
资源先在包清单中获得稳定的 assetId:
{
"assets": {
"levels/main": "assets/level-main.bin",
"fonts/ui": "assets/ui.woff2"
},
"capabilities": [
"host:asset.read",
"host:asset.loadFont"
]
}
assetId 是清单键,不是包内路径。guest 不能把运行时字符串当路径拼接。
Reference
方法
asset.read
asset.read(assetId, output: ByteSink, offset? = 0) -> {
written: u32,
required?: u64,
eof: boolean
}
从 offset 开始把资源字节写入 ByteSink。required 在宿主知道完整资源大小时给出;调用方不能假定它永远存在。eof: false 表示输出空间不足,调用方应以新 offset 继续。
asset.loadFont
asset.loadFont(assetId) -> Handle<font, use, app-context, runtime>
验证、解码并注册包内字体,返回只能用于宿主文本渲染接口的句柄。字体句柄不能被 guest 当字节读取,也不能跨应用传递。
JS 工作流示例
读取一个大小未知的包内二进制资源:
async function readAsset(assetId: string): Promise<Uint8Array> {
const chunks: Uint8Array[] = [];
let offset = 0;
for (;;) {
const buffer = new Uint8Array(64 * 1024);
const result = await host.asset.read(assetId, buffer, offset);
chunks.push(buffer.subarray(0, result.written));
offset += result.written;
if (result.eof) return concat(chunks, offset);
}
}
const levelBytes = await readAsset('levels/main');
const level = decodeLevel(levelBytes); // guest 库负责格式语义
注册字体并交给 canvas:
const uiFont = await host.asset.loadFont('fonts/ui');
const [metrics] = await host.canvas.measureText('main', ['开始'], uiFont);
原生 WASM 绑定示例
原生 guest 可以用固定缓冲循环读取,无需一次分配完整资源:
uint8_t chunk[65536];
uint64_t offset = 0;
for (;;) {
AssetReadResult r = host_asset_read(
"levels/main",
byte_sink(chunk, sizeof(chunk)),
offset
);
level_decoder_push(chunk, r.written);
offset += r.written;
if (r.eof) break;
}
绑定层只暴露实际写入的字节;未写区域不得被当作宿主返回值。
生命周期
- 包内资源在应用版本生命周期内不可变。
- 更新应用版本可以替换同名
assetId的内容,因此 guest 不得跨版本缓存裸 offset 或内容哈希之外的推断。 - 字体句柄只在当前运行时有效;应用卸载、热重载或宿主字体后端重置都会使其失效。
- 读取可以取消;取消不影响资源本身。
错误
| code | 含义 | retryable |
|---|---|---|
not-found |
assetId 未在本版本 manifest 声明 |
false |
invalid-offset |
offset 超出资源或不受支持 | false |
invalid-format |
字体格式、魔数或结构校验失败 | false |
sink-too-small |
该绑定要求的最小输出空间不足 | true |
limit-exceeded |
读取速率、解码内存或字体数超预算 | true |
安全与预算
- 信任档位:green;provenance 为
app-context。 - 宿主必须按 manifest 查表,不能把
assetId转成未规范化路径后直接打开。 - 字体先经过大小、格式和结构校验,再交给平台字体后端。
- 预算覆盖单次输出、时间窗总字节、并发读取、活跃字体数和字体解码内存。
- 资源内容不得包含宿主密钥、平台配置或其他应用的数据。
不属于本 API
图片/音视频解码、压缩、数据库、网络缓存和场景资源管理属于 L2。若以后基准证明某种解码必须宿主加速,应设计只消费明确 ByteSource 的独立能力,不能重新引入能接受任意 file token 的旁路。
一致性测试
宿主至少覆盖:未知 ID、路径穿越字符串、零长度资源、分块边界、取消、超大 offset、包升级、损坏字体、字体炸弹、句柄跨应用使用,以及 JS/原生 guest 的字节完全一致性。