@ capawesome/ electron- live- update
超空(OTA)网络捆绑更新的电源 SDK 云雾。在秒内更新您的 Electron app 的网络代码 — 不重建、 重新签名或重新发布二进制 .
此 SDK 使用相同的协议和词汇 @capawesome/capacitor-live-update 插件:一个产品,一个词汇,横跨iOS,Android和Electron.
特征
- ⚡ OTA 更新: 立即通过 : 云雾 或任何自我托管的服务器 说相同的协议.
- 🛟 杀人安全回滚: 磁盘上坚持一个待启动标记 在此之前 一个新的捆绑负载。 如果应用程序在启动时崩溃,被挂起或被杀死——甚至因为失去动力——下一个启动会自动恢复到默认的捆绑并可选地阻挡被损坏的.
- 🔒 签字核查: 每个下载捆绑的RSA签名验证(
publicKey),外加激活时安装的捆绑的检查和再核查——下载后进行篡改也被发现. - 🌐 服务于稳定来源: 特权定制方案为源码下的活包服务, 所以
localStorage, IndexedDB 和服务人员通过捆绑开关生存. 一个简单的基于路径的模式可以作为一种替代. - 🚦 频道: 向不同的用户群体(生产、β、分级推出)交付不同的捆绑,并在运行时与它们一起发现
fetchChannels()。 。 。 . - 📂 多个捆绑: 程序下载、管理和交换捆绑.
- 🔁 背景更新: App启动时可选择自动同步,焦点和恢复时可选择自动同步.
- 🔐 默认安全: HTTPS-只下载(localhost豁免开发),zip-slip保护,原子捆绑安装.
- 🧪 测试过的核心:捆绑店,回滚状态机和核查管经过单位测试,一个被包-应用的端到端套件在每次CI跑道(macOS,Windows,Linux)上都模拟出靴子时的杀法.
- 🤝 兼容性: 镜像 API 中的 电容Live 更新插件 并暴露其更新引擎作为独立的切入点(
@capawesome/electron-live-update/engine) (中文(简体) ).
安装
npm install @capawesome/electron-live-update
该包有四个切入点:
| 入境点 | 运行在 | 目的 |
|---|---|---|
. | 主要进程 | createLiveUpdate() - SDK车 |
./preload | 预装 | exposeLiveUpdateApi() ——上下文桥 |
./renderer | 渲染器 | LiveUpdate ——一个输入客户端映射插件 API |
./engine | 节点.js | LiveUpdateEngine ——纯更新引擎(无电能) |
开始
主要进程
import { createLiveUpdate } from '@capawesome/electron-live-update';
import { BrowserWindow, app } from 'electron';
import { join } from 'node:path';
const liveUpdate = createLiveUpdate({
appId: '6e351b4f-69a7-415e-a057-4567df7ffe94', // Your Capawesome Cloud app ID
defaultBundlePath: join(__dirname, '..', 'renderer'), // Your packaged web assets
readyTimeout: 10000, // STRONGLY recommended: enables rollback protection
autoBlockRolledBackBundles: true,
});
liveUpdate.serve(); // BEFORE app.whenReady()
app.whenReady().then(async () => {
const window = new BrowserWindow({
webPreferences: { preload: join(__dirname, 'preload.js') },
});
liveUpdate.attach(window);
await window.loadURL(liveUpdate.getServeUrl());
});
预装脚本
import { exposeLiveUpdateApi } from '@capawesome/electron-live-update/preload';
exposeLiveUpdateApi();
渲染器
import { LiveUpdate } from '@capawesome/electron-live-update/renderer';
// As soon as your app is up and running:
await LiveUpdate.ready();
// Check for and apply updates:
const result = await LiveUpdate.sync();
if (result.nextBundleId) {
await LiveUpdate.reload();
}
就是这样。 渲染器代码是行换行,与iOS和Android上的Capacitor Live更新插件相同.
[注] 所有 SDK 方法在返回对象上的主要过程中也可以使用
createLiveUpdate(),这样您就可以从主进程驱动全部更新.
服务模式
自定义方案( 建议) : serve()
serve() 注册特权自定义方案( 默认 : capawesome-live-update),并服务于稳定来源下的活集的文件 capawesome-live-update://bundle。因为来源从未改变:
localStorage,索引数据库,以及其他原产地范围存储 幸存的捆绑开关, (中文(简体) ),fetch()服务人员从事正常安全出身的工作,- 您捆绑中相对的 URL 自然解析, 而 SPA 风格的路径返回到
index.html。 。 。 .
serve() 必须叫 在此之前 应用程序已备妥(优先方案只能在该点之前注册)并要求 defaultBundlePath 选项,这样当没有活的更新活动时就可以服务内置的捆绑.
liveUpdate.serve(); // or liveUpdate.serve({ scheme: 'my-app' })
await window.loadURL(liveUpdate.getServeUrl()); // 'capawesome-live-update://bundle/'
简单模式 : getCurrentBundlePath()
如果你喜欢自己装货,跳过 serve() 从磁盘上装入活动捆绑 :
const bundlePath = await liveUpdate.getCurrentBundlePath();
await window.loadFile(join(bundlePath, 'index.html'));
getCurrentBundlePath() 返回活动捆绑的目录,或 defaultBundlePath 当默认捆绑活动时。 注意与 loadFile,来源来自文件路径,所以来源范围存储是 没有 可靠地存活了捆绑开关。 使用 serve() 除非你有理由不这么做.
在两种模式中, reload() 应用下一个捆绑并重新装入所有附加窗口.
后退保护
实时更新 SDK 绝不能让用户被中断更新所困. 此 SDK 继续其回滚状态机器为磁盘 :
- 当实时更新捆绑即将装入时, a 待启动标记 被写入州档案.. 在此之前 捆绑要跑.
- 你的应用电话
ready()一旦它开始运行。 这能清出标记. - 若为
ready()内部不调用readyTimeout毫秒后,SDK会回滚到默认的捆绑并重新装入. - 如果进程在任何一种情况发生之前即已死亡——崩溃、强制退出、断电——则检测到未清理标记 在下一个开始 后退发生。 没有计时器需要生存;状态在磁盘上.
与 autoBlockRolledBackBundles: true,导致回滚的捆绑也会被阻断 在未来再次安装 sync() 调用(最多100个捆绑;最老的条目在达到限制时解锁).
[大 回滚保护是 默认禁用 (单位:千美元)
readyTimeout: 0)与Capacitor Live更新插件的行为相匹配. 强烈建议设立readyTimeout(例如:.10000)和打电话ready()在你的制作器早期。 在桌面上,没有应用商店可以重新安装: 取消安装和重新安装一个Electron应用通常不会清除它的用户数据,所以一个破碎的捆绑会持续到您的用户手动清除应用数据或者发送一个固定的更新.
作为额外的安全网,每个已安装的捆绑文件的校验和在安装时间被记录,并在捆绑被激活时被重新验证. 在(已验证)下载后在磁盘上被修改的捆绑被丢弃而不是被激活.
代码签名
通过签名验证您的捆绑的真实性并配置相应的公钥 :
const liveUpdate = createLiveUpdate({
// ...
publicKey: '-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----',
});
配置公钥时, 每个已下载的捆绑 必须 提供有效签名(通过 X-Signature 响应头或 signature 选项)——拒绝未签名或被篡改的捆绑。 方案与Capacitor Live Update插件相同:一个RSA PKCS#1 v1.5签名捆绑文件的SHA-256散列,编码为base64. 见 卡帕韦斯克云文档 用于生成密钥和签名捆绑.
没有公用钥匙,廉洁检查和(X-Checksum 页眉或 checksum 选项,SHA-256以六进制格式进行验证).
Web 捆绑更新 vs 二进制更新
这个 SDK 更新 网络捆绑 您的应用程序: HTML, CSS, JavaScript – 所有您的渲染器负载 。 故意不动 二进制图层: Electron 运行时间,本地模块,安装器通过 Electron 的 Squirrel等工具更新 autoUpdater 或者说 electron-updater。 。 。 .
这两个层面是互补的,而不是相互竞争的:
| 层 | 目录 | 更新于 | 低调 |
|---|---|---|---|
| 网络捆绑 | 渲染器 HTML/CSS/JS | 这个SDK (OTA,秒数) | 随时随地 |
| 二进制 | 电子运行时间, 本地模块 | 松鼠 / 松鼠 / 松鼠 electron-updater | 有时 |
一个实用的设置同时使用: 船网代码在空气中不断改变, 当你碰到Electron或者改变本土依赖性时, 滚动一个二进制释放. 二进制更新后,该应用程序继续使用已下载的捆绑——呼叫 reset() 如果您的新二进制需要匹配的网络捆绑基线,则在主要版本更改上.
一个引擎,两个适配器
此套件是带有子路径导出的一个 npm 套件 。 这个 ./engine 入站点包含整个更新逻辑 — Cloud 协议客户端, 捆绑商店, 状态机器, 下载/ 摘录/ 验证管道 – with 电源和电源的零进口数字 :
@capawesome/electron-live-update (ONE npm package)
├── "." → the standalone SDK ← for plain Electron apps (this README)
├── "./preload" → context bridge helper
├── "./renderer" → typed renderer client
└── "./engine" → the shared engine ← pure update logic, zero Electron imports
▲
│ consumed as a dependency by…
@capawesome/capacitor-live-update (the Capacitor plugin)
└── electron platform implementation ← thin adapter: plugin JS API → "./engine"
- 普通电子软件 只安装此软件包.
- 电容应用程序 (包括Electron平台,一旦可用)仅安装
@capawesome/capacitor-live-update-和iOS和Android上一样.
API 稳定
发动机的公用表面(LiveUpdateEngine, (中文(简体) ), LiveUpdateEngineConfig,词汇类型,以及 ErrorCode是一个 稳定 API:未来的Capacitor Electron适配器依赖于它. 主机注入所有特定平台 :
import { LiveUpdateEngine } from '@capawesome/electron-live-update/engine';
const engine = new LiveUpdateEngine({
appId: '...',
dataDirectory: '/path/to/writable/storage',
platform: '2',
osVersion: '...',
versionCode: '1',
versionName: '1.0.0',
pluginVersion: '0.1.0',
readyTimeout: 10000,
});
const { currentBundleId } = await engine.initialize(); // BEFORE loading web content
// engine.sync(), engine.ready(), engine.setNextBundle(), engine.applyNextBundle(), ...
与电容插件的比较
API 镜像 @capawesome/capacitor-live-update存在的差异是蓄意的,并在此列出:
| 侧面 | 电容插件 | 这个SDK |
|---|---|---|
readyTimeout 默认 | 0 (残疾) | 0 (残疾)——相同的缺省,相同的设置建议 10000 |
| 击杀安全后退 | —— 说 | 磁盘上待启动标记, 在每个进程启动时检查 |
| 激活时间核查 | —— 说 | 根据安装时间校验和重新验证已安装的捆绑 |
| 回滚屏蔽 | 打开 ready() | 在回滚时间( 在死亡之前存活) ready()(中文(简体) ) |
| 配置 | 电容配置文件 | createLiveUpdate() 选项 |
versionCode 编号 : versionName | 原生应用程序版本 | app.getVersion() 除非已配置 |
| 设备标识 | 随机UUID(Android)/供应商ID(iOS) | 随机UUID, 持续每个应用程序ID |
| 服务 | 电容网络视频 | serve() 自定义方案或 getCurrentBundlePath() |
setConfig() | 可用 | 仅限发动机LiveUpdateEngine(中文(简体) ) |
fetchChannels() | 可用 | 可用 |
manifest 文物类型 | 可用(delta更新) | 尚未提供( E)zip 仅限) |
API 密码
创建Live Update( 配置)
创建 SDK 。 调用一次,在您的主要进程早期(在 app.whenReady() 使用时 serve()) (中文(简体) ).
配置
| 选项 | 类型 | 默认 | 说明 |
|---|---|---|---|
appId | string | – 说吧 – | Capawesome Cloud应用识别码. 所需经费 sync()编号 :fetchLatestBundle()。 。 。 . |
autoBlockRolledBackBundles | boolean | false | 导致回滚的块包 如果 readyTimeout 实值 0。 。 。 . |
autoDeleteBundles | boolean | false | 删除未使用的捆绑后 ready()。 。 。 . |
autoUpdateStrategy | 'none' | 'background' | 'none' | background:在开始,焦点和恢复时自动同步(最多每15分钟). |
dataDirectory | string | join(app.getPath('userData'), 'capawesome-live-update') | 捆绑和状态存放的地方. |
defaultChannel | string | – 说吧 – | 默认更新通道 . |
defaultBundlePath | string | – 说吧 – | 被打包的网络资产目录 。 所需经费 serve()。 。 。 . |
httpTimeout | number | 60000 | HTTP超时以毫秒计. |
logger | LiveUpdateLogger | console | 自定义记录器 . |
publicKey | string | – 说吧 – | PEM-编码的RSA公钥用于签名验证. |
readyTimeout | number | 0 | 倒转保护暂停 以毫秒. 0 关闭它。 建议: 10000。 。 。 . |
serverDomain | string | 'api.cloud.capawesome.io' | API域名,没有方案或路径. localhost域使用plain HTTP进行开发. |
versionCode | string | app.getVersion() | 版本代码向更新服务器报告. |
versionName | string | app.getVersion() | 向更新服务器报告版本名称 . |
方法
回来的 LiveUpdateHost 对象执行共享词汇——您从 Capacitor 插件中知道的相同方法 :
clearBlockedBundles(), (中文(简体) ), deleteBundle(options), (中文(简体) ), downloadBundle(options), (中文(简体) ), fetchChannels(options?), (中文(简体) ), fetchLatestBundle(options?), (中文(简体) ), getBlockedBundles(), (中文(简体) ), getChannel(), (中文(简体) ), getCurrentBundle(), (中文(简体) ), getCustomId(), (中文(简体) ), getDeviceId(), (中文(简体) ), getDownloadedBundles(), (中文(简体) ), getNextBundle(), (中文(简体) ), getVersionCode(), (中文(简体) ), getVersionName(), (中文(简体) ), isSyncing(), (中文(简体) ), ready(), (中文(简体) ), reload(), (中文(简体) ), reset(), (中文(简体) ), setChannel(options), (中文(简体) ), setCustomId(options), (中文(简体) ), setNextBundle(options), (中文(简体) ), sync(options?), (中文(简体) ), addListener(eventName, listener), (中文(简体) ), removeAllListeners()
加电子专用服务集成:
serve(options?)- 通过特权定制计划为主动捆绑提供服务(见 服务模式) (中文(简体) ).getServeUrl()在服务模式下装入窗口的 URL .attach(window)登记aBrowserWindow页:1reload()和渲染器 IPC访问.getCurrentBundlePath()——活动捆绑的上盘路径(简单模式).
所有选项和结果都使用与Capacitor插件完全相同的形状(SyncResult.nextBundleId, (中文(简体) ), ReadyResult.rollback,捆绑ID作为用户意指的名字,例如 '1.0.0', (中文(简体) ), null 表示默认捆绑等). 见JSDoc全文的TypeScript定义.
活动
| 活动 | 有效载荷 | 何时出院 |
|---|---|---|
downloadBundleProgress | { bundleId, downloadedBytes, progress, totalBytes } | 捆绑下载取得进展 |
nextBundleSet | { bundleId } | 捆绑被设定为下一个捆绑 |
reloaded | – 说吧 – | 应用程序通过 reload() |
rolledBack | { currentBundleId, previousBundleId } | 应用程序在未及时发出信号后被卷回默认捆绑 |
主要进程可提供各种活动(liveUpdate.addListener(...)并转交所附的制造器(LiveUpdate.addListener(...)) (中文(简体) ).
错误
所有错误都是 LiveUpdateError 实例 code 从 ErrorCode enum( 例如 ErrorCode.ChecksumMismatch, (中文(简体) ), ErrorCode.SignatureVerificationFailed, (中文(简体) ), ErrorCode.SyncInProgress) (中文(简体) ). 这些代码在IPC边界后幸存下来,因此渲染器代码可以可靠地处理:
import {
ErrorCode,
LiveUpdateError,
} from '@capawesome/electron-live-update/renderer';
try {
await LiveUpdate.sync();
} catch (error) {
if (
error instanceof LiveUpdateError &&
error.code === ErrorCode.SyncInProgress
) {
// ...
}
}
示例
一个完整的示例应用程序( Vite 渲染器, 模拟更新服务器, 两种服务模式, 签名捆绑) 都住在 example/。在 e2e/ 包并运行对被包二进制的 kill-during-boot回滚钻.
npm install
npm run build
npm run build --workspace example
npm run test # unit tests
npm run test:e2e # packaged-app e2e incl. the rollback drill
更改日志
见 变动情况。 。 。 .
许可证
见 自由主义。 。 。 .