v0.1首个版本化设计基线查看版本范围
Developer

store

store 提供应用作用域、版本化的持久化键值存储。宿主负责持久化、并发冲突和账户同步;guest 决定值的格式、索引和领域语义。

基本示例

const encoder = new TextEncoder();
const decoder = new TextDecoder();

const current = await host.store.get('settings/theme');
const settings = current
  ? JSON.parse(decoder.decode(current.value))
  : { theme: 'system' };

await host.store.put(
  'settings/theme',
  encoder.encode(JSON.stringify({ ...settings, theme: 'dark' })),
  current?.version,
);

传入 expectedVersion 可以防止两个窗口静默覆盖彼此。

适用场景

  • 设置、草稿、进度和小型结构化数据。
  • guest 数据库的页、日志或索引块。
  • 可通过应用账户在设备间同步的密文值。

SQL、查询规划、全文索引、CRDT 和业务 schema 属于 L2。大媒体文件或无限追加日志需要后续专门的对象/流能力,不能挤进 KV。

能力声明

{
  "capabilities": [
    "host:store.get",
    "host:store.put",
    "host:store.delete",
    "host:store.list",
    "host:store.deletePrefix"
  ]
}

读、写、列举和批量删除逐方法声明。

Reference

数据模型

StoreEntry {
  key: bounded-string
  value: Bytes
  version: u64
  updatedAt?: coarse-time
}

键是规范化 UTF-8 字符串。/ 只是普通字符的约定分隔符,不对应宿主路径。值是无类型字节;getTextputJSON 等便利方法应由 SDK 提供。

分页与并发示例

let cursor: string | undefined;
do {
  const page = await host.store.list('documents/', cursor);
  for (const item of page.items) renderDocumentRow(item.key, item.version);
  cursor = page.cursor;
} while (cursor);

解决乐观并发冲突:

async function increment(key: string) {
  for (let attempt = 0; attempt < 3; attempt++) {
    const old = await host.store.get(key);
    const next = encodeNumber((old ? decodeNumber(old.value) : 0) + 1);
    try {
      return await host.store.put(key, next, old?.version);
    } catch (error) {
      if (error.code !== 'conflict') throw error;
    }
  }
  throw new Error('too much contention');
}

原生 WASM 示例

uint8_t buffer[4096];
StoreGetResult r = host_store_get("settings/theme", byte_sink(buffer, sizeof(buffer)));
if (r.found) decode_settings(buffer, r.written, r.version);

超过当前 sink 的值返回 required 或可继续读取的明确结果,不能截断后伪装成功。

一致性与同步

  • put 和单键 delete 对一个键线性化;相同 expectedVersion 只能有一个成功。
  • list 的 cursor 是不透明、有限期值;guest 不解析也不持久化。
  • 多设备同步可以延迟,但冲突不能靠最后写入静默吞掉版本条件。
  • 平台服务只持有端到端加密后的值;键名和大小等元数据泄露必须在隐私说明中披露。

安全与预算

  • 信任档位:green。
  • 数据按 appId 隔离;任何调用都不能覆盖 namespace。
  • 预算覆盖键长、值大小、键数、总容量、分页大小、在途调用和时间窗写入量。
  • deletePrefix 必须显示实际删除数量,并受单次和时间窗预算限制。
  • 错误、日志和遥测不得记录值内容。

错误与测试

特有错误包括 conflictquota-exceededinvalid-keycursor-expiredsink-too-small。一致性测试至少覆盖首次写入、条件更新、并发写、删除竞态、分页稳定性、空前缀、配额、跨应用隔离、同步重放和密文不可读性。

Host API 版本与分层概览Host API v0.1版本非正式提议提议canvas API呈现canvas.getInfo()方法canvas.draw()方法canvas.present()方法canvas.measureText()方法canvas.frame event事件canvas.resize event事件asset API呈现asset.read()方法asset.loadFont()方法playback API呈现playback.open()方法playback.write()方法playback.stop()方法playback.ready event事件playback.ended event事件input API输入input.focus()方法input.pointer event事件input.key event事件input.wheel event事件ime API输入ime.openSession()方法ime.updateState()方法ime.updateGeometry()方法ime.closeSession()方法ime.textUpdate event事件ime.composition event事件ime.formatUpdate event事件ime.characterBoundsRequest event事件capture API输入capture.requestCamera()方法capture.requestMic()方法capture.frame()方法capture.readAudio()方法capture.stop()方法capture.ended event事件store API数据store.get()方法store.put()方法store.delete()方法store.list()方法store.deletePrefix()方法clipboard API数据clipboard.readText()方法clipboard.writeText()方法clipboard.readImage()方法clipboard.writeImage()方法net API网络net.fetch()方法identity API身份identity.status()方法identity.login()方法identity.logout()方法env API平台集成env.snapshot()方法env.change event事件print API平台集成print.open()方法开始调用 Host API指南构建交互式绘图应用指南构建自绘文本编辑器指南管理媒体采集与播放指南组织本地数据与同步指南