Skip to content

快速开始 ​

新项目可先运行 hb-sdk create my-miniapp 获得 Vanilla JavaScript + Vite HelloWorld;模板只演示 Toast,业务代码可以直接替换。

如果页面不需要小黑盒开放能力,可以不接入 SDK。SDK 导入时会自动启动握手,能力调用会自动等待;使用受保护能力前需在 package.json#heybox.permissions 声明对应权限,详见权限声明。

交互控件与握手状态 ​

auth.login() 等可能展示授权 UI 的能力必须保留原始可信用户手势。不要在点击回调中等待握手;页面加载时读取并订阅持久握手状态,只在 status === 'ready' 时启用按钮:

ts
import hbSDK, { type MiniProgramSDKHandshakeState } from '@heybox/hb-sdk'
import { computed, onUnmounted, ref } from 'vue'

const handshakeState = ref<MiniProgramSDKHandshakeState>(hbSDK.getHandshakeState())
const stopHandshakeState = hbSDK.onHandshakeStateChange(state => {
  handshakeState.value = state
})
const sdkReady = computed(() => handshakeState.value.status === 'ready')

onUnmounted(stopHandshakeState)

把 sdkReady 用作按钮的 disabled 条件。订阅会立即回放当前状态,因此晚挂载组件也能得到 ready 或 failed 终态。ready 生命周期事件只在握手成功瞬间派发且不会重放,不能作为当前状态来源。

读取当前用户 ​

声明 userInfo 后,可以读取按小程序隔离的 app_user_id 和已授权资料。该能力不依赖公开 network 权限:

ts
import hbSDK from '@heybox/hb-sdk'

async function getCurrentUser() {
  const result = await hbSDK.user.getInfo()
  return result.userInfo
}

user.getInfo() 不展示授权弹窗,也不要求可信用户手势。未登录时返回未登录状态;已登录时隐式授予当前本地用户 scopes(identity、profile),静默返回 userInfo.app_user_id、profile.nickname 和 profile.avatar。平台为当前版本启用 Steam 账号能力时,还会授予 steam_account,已绑定用户返回可选 userInfo.steam_id。这条读取不返回授权码或 OpenAPI token;需要建立开发者服务端会话时使用 auth.login()。

获取服务端授权码 ​

声明 userInfo 后,可通过 auth.login() 获取短期授权码。登录可能展示 UI,因此应绑定到按钮点击等明确的用户操作。平台为当前版本启用 Steam 账号能力后,联网小程序可在 scopes 中按需加入 steam_account,并由开发者服务端通过 OpenAPI 读取 steam_id。该调用内部使用 Host 受信请求;平台为当前版本启用 Steam 账号能力后,无网络的 local 模式调用会返回 SERVER_API_REQUIRED,兼容 legacy 模式保持既有行为;示例中的 network.request() 另需声明并获得 network 平台批准。

SDK 会自动完成握手。登录按钮应按上面的持久握手状态启用;点击回调直接调用 auth.login(),不要先等待其他异步任务,以免首次操作丢失可信手势。

ts
import hbSDK, { HbMiniProgramSDKError } from '@heybox/hb-sdk'

// 这是你自己的后端接口,不是黑盒 OpenAPI 地址。

async function loginFromUserAction() {
  try {
    const result = await hbSDK.auth.login({ scopes: ['profile'] })

    await hbSDK.network.request({
      url: 'https://api.example.com/session/heybox',
      method: 'POST',
      data: { code: result.code },
    })
  } catch (error) {
    if (error instanceof HbMiniProgramSDKError && error.code === 'AUTHORIZATION_CANCELLED') {
      return
    }
    throw error
  }
}

返回值与边界以用户身份与登录为准。页面只把 code 提交给自己的服务端。不要在页面初始化阶段自动调用 auth.login()。

文件与下载 ​

文件能力的完整用法、沙箱模型、路径规则、配额与错误码见文件能力与沙箱;本节只给最短上手路径。

files.sandbox 是当前小程序隔离的逻辑根。file() / directory() 只创建 lazy proxy,不执行 I/O;第一次真实 I/O 仍由 Runtime 和 Host 校验当前 session、授权、mode、kind 和路径。

ts
import hbSDK, { HbMiniProgramSDKError } from '@heybox/hb-sdk'

const exportDirectory = hbSDK.files.sandbox.directory('exports')
await exportDirectory.create({ recursive: true })

const controller = new AbortController()

try {
  const file = await hbSDK.network.download({
    url: 'https://cdn.example.com/report.json',
    to: exportDirectory,
    suggestedName: 'report.json',
    overwrite: true,
    maxBytes: 8 * 1024 * 1024,
    signal: controller.signal,
    onProgress({ loaded, total, lengthComputable }) {
      console.log(lengthComputable ? `${loaded}/${total}` : loaded)
    },
  })

  console.log(await file.readText())
} catch (error) {
  if (error instanceof HbMiniProgramSDKError && error.code === 'METHOD_FORBIDDEN') {
    // 当前 Host 不支持 V1 文件能力。
    return
  }
  throw error
}

上传已有文件时使用 network.upload(),由 Host 直接传输可读 FileHandle;需要 network 与 filesystem 权限,并单独协商上传能力。raw/multipart 示例见上传受控文件。

当前平台状态如下:

Hostfiles.sandbox外部 pickerfiles.saveFile()network.download()
新 PC支持支持支持支持
Android支持支持已适配客户端支持支持
iOS支持支持支持支持
OHOS按宿主协商按宿主协商按宿主协商按宿主协商
Web不支持不支持不支持不支持
Browser Dev Host不支持不支持不支持不支持

当前 Runtime 认识但 Host 未实现时返回 METHOD_FORBIDDEN;旧 Runtime 收到新 method 时返回 METHOD_NOT_FOUND。新 PC 注入真实文件与流式下载 primitive;Android/iOS 1.3.396+ 还需在 启动时通过 system.describe 宣告对应 Native operations。缺失 handler、协商超时或 operation 不完整时对应能力保持关闭,且不提供 throwing stub、Blob 或内存假下载。

Mobile 的可用性完全由 Host 宣告的 operation 集决定,Runtime/H5 不按操作系统分支判断能力: Android 已适配客户端支持 files.saveFile() 和精确 SAF 文件的 readwrite 选择,仍需系统授予写权限;较早客户端可能拒绝。精确 SAF 文件的写入不保证原子替换,也不支持作为 network.download() 的原子替换目标,详见保存到用户指定位置。

路径与创建 ​

  • 逻辑相对路径统一使用 /。拒绝空字符串、绝对路径、空 segment、.、..、反斜杠、尾分隔符、NUL 和控制字符。
  • create() 的 recursive 与 exclusive 默认均为 false。默认行为是不截断内容的 ensure-exists;exclusive: true 在同名实体已存在时失败。
  • writeText() / writeBytes() 可以在父目录存在时隐式创建目标,但不会自动创建父目录。
  • exists() 在祖先/目标缺失或 kind mismatch 时返回 false;授权撤销、quota 与 I/O 错误仍抛出。
  • file.remove() 与 directory.remove() 只删除 sandbox 对象;外部 picker/saveFile 授权调用删除时抛出 FILE_ACCESS_DENIED。
  • 非空目录默认不能删除并抛出 DIRECTORY_NOT_EMPTY;确认删除整个目录树后显式调用 directory.remove({ recursive: true })。
  • directory.list() 默认最多返回 1000 项,显式 limit 也不能超过 1000;超限抛出 DIRECTORY_LIST_LIMIT_EXCEEDED。
  • Host 文件错误只暴露稳定 code 与 canonical 安全文案;原始 message、data、native path 和 credential 不会进入小程序。
  • FileStat.size 是字节数;modifiedAt / createdAt 若存在,单位为 Unix epoch milliseconds。

外部授权与保存 ​

pickFiles()、pickDirectory() 和 saveFile() 必须直接从按钮点击等可信用户操作调用。picker 取消会抛 FILE_PICKER_CANCELLED,不会返回空数组或 undefined。

ts
async function saveFromUserAction(text: string) {
  const file = await hbSDK.files.saveFile({ suggestedName: 'report.json' })
  await file.writeText(text)
}

saveFile() 请求 Host 返回精确 File 授权,不会同时创建或保留 Directory 授权。新 PC 当前先让 用户选择保存目录,再以 suggestedName 独占创建空文件;同名文件抛出 FILE_ALREADY_EXISTS, 不会静默覆盖。V1 不接受 accept;扩展名过滤只属于 pickFiles({ accept: ['.json'] })。外部授权 仅在当前 Runtime session 有效。

pickFiles() / pickDirectory() 默认返回 mode: 'read' 的授权;只有显式传入 multiple: true 时,pickFiles() 才允许 Host 返回多个文件。需要修改文件、在所选目录中创建 文件,或把外部授权用作下载目标时,必须在 picker options 中显式请求 mode: 'readwrite'。

单个 Runtime session 最多保留 1024 个 picker/saveFile direct handle;整批选择超过剩余额度时抛出 FILE_QUOTA_EXCEEDED,不会保留部分授权。Sandbox 派生项与目录枚举结果使用逻辑引用,不持续占用该额度。

下载边界 ​

  • 下载固定使用 GET,不接受 method、body、上传或 Range 字段。
  • File target 必须已存在且为 readwrite,成功后返回同一个 File proxy;Directory target 必须存在且为 readwrite,成功后返回新的子 File proxy。
  • Directory 默认不覆盖同名文件;只有显式 overwrite: true 才允许原子替换。
  • Directory target 的子文件名按安全的 Content-Disposition filename* / filename、suggestedName、URL basename、download.bin 依次选择。
  • timeout 是网络空闲超时,不是整个下载总时长。取消或失败不会改动原目标。
  • AbortSignal 只发出取消意图并等待 Host 终态;如果 Host 已先完成原子提交,Promise 仍以成功结果 resolve,否则抛出 DOWNLOAD_CANCELLED。
  • 公开 request 与 download 都不跟随重定向;3xx 或 partial representation 会失败。
  • 进度从 loaded: 0 开始并单调不减;原子提交后、Promise resolve 前会再发送一次 final progress,因此空文件或 Host 已报告最终值时允许相邻值相同。只有无歧义、identity-encoded 的 Content-Length 才使 lengthComputable=true;压缩、chunked 或重复长度都视为未知 total,可信长度必须在提交前与实际字节数一致。
  • onProgress 在本地同步执行;回调异常不会中断下载。成功时最后一次回调发生在文件提交后、Promise resolve 前。

默认单例 ​

大多数小程序页面都应该使用默认实例。0.6 起不再对业务代码提供独立实例工厂。