electron-incremental-update 3.0.0-beta.5 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +338 -373
- package/dist/crypto-BSky88mL.cjs +259 -0
- package/dist/crypto-DZzMmoz2.mjs +164 -0
- package/dist/{download-BN4uMS4_.d.mts → download-BVmLiAvB.d.cts} +2 -2
- package/dist/download-BdX4fZYM.mjs +130 -0
- package/dist/{download-DO7iuxEJ.d.cts → download-GONr15zK.d.mts} +2 -2
- package/dist/download-KySXUyWC.cjs +165 -0
- package/dist/{electron-BJCk7uxG.mjs → electron-BrIF1urZ.mjs} +38 -13
- package/dist/electron-CaS0I3S2.cjs +346 -0
- package/dist/index.cjs +66 -34
- package/dist/index.d.cts +10 -13
- package/dist/index.d.mts +10 -13
- package/dist/index.mjs +62 -31
- package/dist/local-C5jw-7o5.mjs +105 -0
- package/dist/local-DbXBG1D9.cjs +118 -0
- package/dist/provider.cjs +24 -43
- package/dist/provider.d.cts +58 -20
- package/dist/provider.d.mts +58 -20
- package/dist/provider.mjs +12 -33
- package/dist/{types-BM9Jfu7q.d.cts → types-q78spjKB.d.cts} +22 -7
- package/dist/{types-DASqEPXE.d.mts → types-q78spjKB.d.mts} +22 -7
- package/dist/utils.cjs +22 -22
- package/dist/utils.d.cts +6 -6
- package/dist/utils.d.mts +6 -6
- package/dist/utils.mjs +4 -5
- package/dist/vite.d.mts +99 -110
- package/dist/vite.mjs +656 -732
- package/package.json +30 -35
- package/dist/electron-C-qmVhAt.cjs +0 -321
- package/dist/version--eVB2A7n.mjs +0 -72
- package/dist/version-aPrLuz_-.cjs +0 -129
- package/dist/zip-BCC7FAQ_.cjs +0 -264
- package/dist/zip-Dwm7s1C9.mjs +0 -185
package/README.md
CHANGED
|
@@ -1,107 +1,100 @@
|
|
|
1
1
|
# Electron Incremental Update
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Lightweight incremental update tools for Electron applications built with Vite. The package provides a Vite plugin, an Electron startup wrapper, update providers, bytecode protection, and utilities for common Electron app paths.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
4
6
|
|
|
5
7
|
- [Electron Incremental Update](#electron-incremental-update)
|
|
6
|
-
- [
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
- [
|
|
11
|
-
- [
|
|
12
|
-
- [
|
|
13
|
-
|
|
14
|
-
- [
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
- [
|
|
18
|
-
- [
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
- [Benefits](#benefits)
|
|
27
|
-
- [Limitation](#limitation)
|
|
28
|
-
- [Utils](#utils)
|
|
29
|
-
- [Electron Utilities](#electron-utilities)
|
|
30
|
-
- [Crypto Utilities](#crypto-utilities)
|
|
31
|
-
- [Zip Utilities](#zip-utilities)
|
|
8
|
+
- [Contents](#contents)
|
|
9
|
+
- [Why](#why)
|
|
10
|
+
- [How It Works](#how-it-works)
|
|
11
|
+
- [Installation](#installation)
|
|
12
|
+
- [Quick Start](#quick-start)
|
|
13
|
+
- [Entry Process](#entry-process)
|
|
14
|
+
- [Main Process](#main-process)
|
|
15
|
+
- [Vite Configuration](#vite-configuration)
|
|
16
|
+
- [Plugin Options](#plugin-options)
|
|
17
|
+
- [Electron Builder](#electron-builder)
|
|
18
|
+
- [Runtime Usage](#runtime-usage)
|
|
19
|
+
- [Providers](#providers)
|
|
20
|
+
- [Testing The Local Flow](#testing-the-local-flow)
|
|
21
|
+
- [Update Artifacts](#update-artifacts)
|
|
22
|
+
- [Native Modules](#native-modules)
|
|
23
|
+
- [Result in app.asar](#result-in-appasar)
|
|
24
|
+
- [Bytecode Protection](#bytecode-protection)
|
|
25
|
+
- [Development Bundling](#development-bundling)
|
|
26
|
+
- [Utilities](#utilities)
|
|
27
|
+
- [API Reference](#api-reference)
|
|
32
28
|
- [Credits](#credits)
|
|
33
29
|
- [License](#license)
|
|
34
30
|
|
|
35
|
-
##
|
|
36
|
-
|
|
37
|
-
This solution provides a comprehensive update system for Electron applications, including:
|
|
38
|
-
|
|
39
|
-
- **Vite Plugin** - Seamlessly integrates with your existing Vite build process
|
|
40
|
-
- **Startup Entry Function** - Handles application initialization and update checking
|
|
41
|
-
- **Updater Class** - Manages the complete update workflow with event-driven API
|
|
42
|
-
- **Utility Functions** - Helper functions for file paths, version management, and more
|
|
31
|
+
## Why
|
|
43
32
|
|
|
44
|
-
|
|
33
|
+
Electron applications are commonly shipped as full installers. This package keeps the installer stable and updates only the application asar file, which makes update packages smaller and keeps the runtime workflow explicit.
|
|
45
34
|
|
|
46
|
-
|
|
35
|
+
Key features:
|
|
47
36
|
|
|
48
|
-
-
|
|
49
|
-
-
|
|
37
|
+
- Vite plugin based on `vite-plugin-electron/multi-env`
|
|
38
|
+
- Dual asar runtime layout
|
|
39
|
+
- Signed update metadata and asar verification
|
|
40
|
+
- GitHub and local development providers
|
|
41
|
+
- Optional V8 bytecode generation
|
|
42
|
+
- Utilities for app paths, native modules, renderer loading, and Electron startup
|
|
50
43
|
|
|
51
|
-
|
|
44
|
+
## How It Works
|
|
52
45
|
|
|
53
|
-
The
|
|
46
|
+
The packaged app uses two asar files:
|
|
54
47
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
3. **Prepare for Update** - The application quits to prepare for the update
|
|
58
|
-
4. **Apply Update** - On next launch, replace the old `${electron.app.name}.asar` with the new version and load it
|
|
48
|
+
- `app.asar`: the stable entry asar generated from `entry.files`
|
|
49
|
+
- `${app.name}.asar`: the replaceable application asar generated from the Electron main, preload, and renderer build outputs
|
|
59
50
|
|
|
60
|
-
|
|
51
|
+
The update flow is:
|
|
61
52
|
|
|
62
|
-
|
|
63
|
-
|
|
53
|
+
1. `createElectronApp()` starts from `app.asar`.
|
|
54
|
+
2. If `${app.name}.asar.tmp` exists, it is renamed to `${app.name}.asar`.
|
|
55
|
+
3. The configured main file is loaded from `${app.name}.asar`.
|
|
56
|
+
4. Your main process calls `updater.checkForUpdates()`.
|
|
57
|
+
5. The provider downloads `version.json`.
|
|
58
|
+
6. The updater compares versions and emits `update-available` when a newer update exists.
|
|
59
|
+
7. `updater.downloadUpdate()` downloads, decompresses, verifies, and writes `${app.name}.asar.tmp`.
|
|
60
|
+
8. `updater.quitAndInstall()` restarts the app so the new asar can be installed on next launch.
|
|
64
61
|
|
|
65
|
-
##
|
|
66
|
-
|
|
67
|
-
### Install
|
|
62
|
+
## Installation
|
|
68
63
|
|
|
69
64
|
```sh
|
|
70
|
-
npm install -D electron-incremental-update
|
|
65
|
+
npm install -D electron-incremental-update @electron/asar @babel/core
|
|
71
66
|
```
|
|
72
67
|
|
|
73
68
|
```sh
|
|
74
|
-
|
|
69
|
+
pnpm add -D electron-incremental-update @electron/asar @babel/core
|
|
75
70
|
```
|
|
76
71
|
|
|
77
72
|
```sh
|
|
78
|
-
|
|
73
|
+
yarn add -D electron-incremental-update @electron/asar @babel/core
|
|
79
74
|
```
|
|
80
75
|
|
|
81
|
-
|
|
76
|
+
> Upgrading from `3.0.0-beta.x`? See [MIGRATION.md](./MIGRATION.md).
|
|
82
77
|
|
|
83
|
-
|
|
78
|
+
## Quick Start
|
|
84
79
|
|
|
85
|
-
|
|
80
|
+
Recommended project layout:
|
|
81
|
+
|
|
82
|
+
```txt
|
|
86
83
|
electron
|
|
87
|
-
├── entry.ts
|
|
84
|
+
├── entry.ts
|
|
88
85
|
├── main
|
|
89
86
|
│ └── index.ts
|
|
90
87
|
├── preload
|
|
91
88
|
│ └── index.ts
|
|
92
|
-
└── native
|
|
93
|
-
└──
|
|
89
|
+
└── native
|
|
90
|
+
└── db.ts
|
|
94
91
|
src
|
|
95
92
|
└── ...
|
|
96
93
|
```
|
|
97
94
|
|
|
98
|
-
###
|
|
99
|
-
|
|
100
|
-
The entry is used to load the application and initialize the `Updater`
|
|
95
|
+
### Entry Process
|
|
101
96
|
|
|
102
|
-
`
|
|
103
|
-
|
|
104
|
-
in `electron/entry.ts`
|
|
97
|
+
`electron/entry.ts` is the stable startup file. It creates the updater and loads the real main process from `${app.name}.asar`.
|
|
105
98
|
|
|
106
99
|
```ts
|
|
107
100
|
import { createElectronApp } from 'electron-incremental-update'
|
|
@@ -109,123 +102,177 @@ import { GitHubProvider } from 'electron-incremental-update/provider'
|
|
|
109
102
|
|
|
110
103
|
createElectronApp({
|
|
111
104
|
updater: {
|
|
112
|
-
// optional, you can setup later
|
|
113
105
|
provider: new GitHubProvider({
|
|
114
|
-
|
|
115
|
-
repo: '
|
|
106
|
+
user: 'your-github-user',
|
|
107
|
+
repo: 'your-repo',
|
|
116
108
|
}),
|
|
117
109
|
},
|
|
118
110
|
beforeStart(mainFilePath, logger) {
|
|
119
|
-
logger?.debug(mainFilePath)
|
|
111
|
+
logger?.debug(`Starting app from ${mainFilePath}`)
|
|
120
112
|
},
|
|
121
113
|
})
|
|
122
114
|
```
|
|
123
115
|
|
|
124
|
-
|
|
116
|
+
### Main Process
|
|
125
117
|
|
|
126
|
-
|
|
118
|
+
The main entry must default-export one function wrapped with `startupWithUpdater()`.
|
|
127
119
|
|
|
128
|
-
|
|
120
|
+
```ts
|
|
121
|
+
import { app, BrowserWindow, dialog } from 'electron'
|
|
122
|
+
import { startupWithUpdater } from 'electron-incremental-update'
|
|
123
|
+
import { getAppVersion, getPathFromPreload, loadPage } from 'electron-incremental-update/utils'
|
|
129
124
|
|
|
130
|
-
|
|
131
|
-
|
|
125
|
+
export default startupWithUpdater(async (updater) => {
|
|
126
|
+
await app.whenReady()
|
|
132
127
|
|
|
133
|
-
|
|
128
|
+
const win = new BrowserWindow({
|
|
129
|
+
webPreferences: {
|
|
130
|
+
preload: getPathFromPreload('index.js'),
|
|
131
|
+
},
|
|
132
|
+
})
|
|
134
133
|
|
|
135
|
-
|
|
134
|
+
loadPage(win)
|
|
136
135
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
136
|
+
updater.on('update-available', async ({ version }) => {
|
|
137
|
+
const { response } = await dialog.showMessageBox({
|
|
138
|
+
type: 'info',
|
|
139
|
+
message: `Version ${version} is available. Current version is ${getAppVersion()}.`,
|
|
140
|
+
buttons: ['Download', 'Later'],
|
|
141
|
+
})
|
|
140
142
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
})(),
|
|
172
|
-
}
|
|
143
|
+
if (response === 0) {
|
|
144
|
+
await updater.downloadUpdate()
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
updater.on('download-progress', (info) => {
|
|
149
|
+
win.webContents.send('update-progress', info)
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
updater.on('update-downloaded', async () => {
|
|
153
|
+
const { response } = await dialog.showMessageBox({
|
|
154
|
+
type: 'info',
|
|
155
|
+
message: 'Update downloaded.',
|
|
156
|
+
buttons: ['Restart Now', 'Later'],
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
if (response === 0) {
|
|
160
|
+
updater.quitAndInstall()
|
|
161
|
+
}
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
updater.on('update-not-available', (code, message) => {
|
|
165
|
+
console.log(`[${code}] ${message}`)
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
updater.on('error', (error) => {
|
|
169
|
+
console.error(error)
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
await updater.checkForUpdates()
|
|
173
173
|
})
|
|
174
174
|
```
|
|
175
175
|
|
|
176
|
-
|
|
176
|
+
## Vite Configuration
|
|
177
|
+
|
|
178
|
+
Use `defineElectronConfig()` when the same Vite config owns the renderer and Electron processes.
|
|
177
179
|
|
|
178
180
|
```ts
|
|
179
181
|
import { defineElectronConfig } from 'electron-incremental-update/vite'
|
|
180
182
|
|
|
181
183
|
export default defineElectronConfig({
|
|
182
184
|
entry: {
|
|
183
|
-
files:
|
|
185
|
+
files: './electron/entry.ts',
|
|
184
186
|
},
|
|
185
187
|
main: {
|
|
186
|
-
files:
|
|
187
|
-
// see https://github.com/electron-vite/electron-vite-vue/blob/85ed267c4851bf59f32888d766c0071661d4b94c/vite.config.ts#L22-L28
|
|
188
|
-
onstart: debugStartup,
|
|
188
|
+
files: './electron/main/index.ts',
|
|
189
189
|
},
|
|
190
190
|
preload: {
|
|
191
191
|
files: './electron/preload/index.ts',
|
|
192
192
|
},
|
|
193
193
|
updater: {
|
|
194
|
-
|
|
194
|
+
minimumVersion: '0.0.0',
|
|
195
|
+
paths: {
|
|
196
|
+
asarOutputPath: 'release/my-app.asar',
|
|
197
|
+
compressedPath: 'release/my-app-1.0.0.asar.br',
|
|
198
|
+
versionPath: 'release/version.json',
|
|
199
|
+
},
|
|
195
200
|
},
|
|
196
201
|
renderer: {
|
|
197
|
-
server:
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
return {
|
|
202
|
-
host: url.hostname,
|
|
203
|
-
port: +url.port,
|
|
202
|
+
server: process.env.VSCODE_DEBUG
|
|
203
|
+
? {
|
|
204
|
+
host: '127.0.0.1',
|
|
205
|
+
port: 5173,
|
|
204
206
|
}
|
|
205
|
-
|
|
207
|
+
: undefined,
|
|
206
208
|
},
|
|
207
209
|
})
|
|
208
210
|
```
|
|
209
211
|
|
|
210
|
-
|
|
212
|
+
Use `electronWithUpdater()` directly when you want to manage the renderer config yourself.
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
import { electronWithUpdater } from 'electron-incremental-update/vite'
|
|
216
|
+
import { defineConfig } from 'vite'
|
|
217
|
+
|
|
218
|
+
export default defineConfig({
|
|
219
|
+
plugins: [
|
|
220
|
+
electronWithUpdater({
|
|
221
|
+
entry: {
|
|
222
|
+
files: './electron/entry.ts',
|
|
223
|
+
},
|
|
224
|
+
main: {
|
|
225
|
+
files: './electron/main/index.ts',
|
|
226
|
+
},
|
|
227
|
+
preload: {
|
|
228
|
+
files: './electron/preload/index.ts',
|
|
229
|
+
},
|
|
230
|
+
}),
|
|
231
|
+
],
|
|
232
|
+
})
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Plugin Options
|
|
236
|
+
|
|
237
|
+
Common options overview:
|
|
238
|
+
|
|
239
|
+
- `entry.files`: entry process input. Required.
|
|
240
|
+
- `main.files`: main process input. Required.
|
|
241
|
+
- `preload.files`: preload process input. Optional.
|
|
242
|
+
- `sourcemap`: defaults to development or `VSCODE_DEBUG`.
|
|
243
|
+
- `minify`: defaults to production builds.
|
|
244
|
+
- `bytecode`: `true` or bytecode options.
|
|
245
|
+
- `notBundle`: externalizes Node modules in development. Defaults to `true`.
|
|
246
|
+
- `external`: additional Vite/Rolldown externals. Use `true` to externalize `dependencies`.
|
|
247
|
+
- `buildVersionJson`: generates update JSON. Defaults to CI only.
|
|
248
|
+
- `localDevUpdate`: generates and serves a local update package during dev startup. Use `true`
|
|
249
|
+
for defaults, or pass `{ baseDir, packageJsonPath, chunkSize, chunkDelay }`. See [Testing The Local Flow](#testing-the-local-flow) for details.
|
|
250
|
+
- `updater.minimumVersion`: minimum supported entry asar version. Defaults to `0.0.0`.
|
|
251
|
+
|
|
252
|
+
> 📖 See [API.md → Plugin Options](./API.md#plugin-options) for the complete reference including all types, defaults, paths, keys, and generator overrides.
|
|
253
|
+
|
|
254
|
+
## Electron Builder
|
|
255
|
+
|
|
256
|
+
Set `package.json#main` to the entry output file:
|
|
211
257
|
|
|
212
258
|
```json
|
|
213
259
|
{
|
|
214
|
-
"main": "dist-entry/entry.js"
|
|
260
|
+
"main": "dist-entry/entry.js"
|
|
215
261
|
}
|
|
216
262
|
```
|
|
217
263
|
|
|
218
|
-
|
|
264
|
+
Minimal `electron-builder.config.cjs`:
|
|
219
265
|
|
|
220
266
|
```js
|
|
221
267
|
const { name } = require('./package.json')
|
|
222
268
|
|
|
223
269
|
const targetFile = `${name}.asar`
|
|
270
|
+
|
|
224
271
|
/**
|
|
225
272
|
* @type {import('electron-builder').Configuration}
|
|
226
273
|
*/
|
|
227
274
|
module.exports = {
|
|
228
|
-
appId:
|
|
275
|
+
appId: `org.${name}`,
|
|
229
276
|
productName: name,
|
|
230
277
|
files: [
|
|
231
278
|
// entry files
|
|
@@ -243,168 +290,149 @@ module.exports = {
|
|
|
243
290
|
}
|
|
244
291
|
```
|
|
245
292
|
|
|
246
|
-
## Usage
|
|
247
|
-
|
|
248
|
-
### Use In Main Process
|
|
293
|
+
## Runtime Usage
|
|
249
294
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
295
|
+
```ts
|
|
296
|
+
import { createElectronApp } from 'electron-incremental-update'
|
|
297
|
+
import { GitHubProvider } from 'electron-incremental-update/provider'
|
|
253
298
|
|
|
254
|
-
|
|
299
|
+
createElectronApp({
|
|
300
|
+
updater: {
|
|
301
|
+
provider: new GitHubProvider({
|
|
302
|
+
user: 'your-github-user',
|
|
303
|
+
repo: 'your-repo',
|
|
304
|
+
}),
|
|
305
|
+
},
|
|
306
|
+
beforeStart(mainFilePath, logger) {
|
|
307
|
+
logger?.debug(`Starting app from ${mainFilePath}`)
|
|
308
|
+
},
|
|
309
|
+
})
|
|
310
|
+
```
|
|
255
311
|
|
|
256
|
-
|
|
257
|
-
import { app, dialog } from 'electron'
|
|
258
|
-
import { startupWithUpdater } from 'electron-incremental-update'
|
|
259
|
-
import {
|
|
260
|
-
getPathFromAppNameAsar,
|
|
261
|
-
getAppVersion,
|
|
262
|
-
getEntryVersion,
|
|
263
|
-
} from 'electron-incremental-update/utils'
|
|
312
|
+
The full `Updater` API, `createElectronApp` options, `AppOption`, events, and error codes are documented in the API reference.
|
|
264
313
|
|
|
265
|
-
|
|
266
|
-
await app.whenReady()
|
|
314
|
+
> 📖 See [API.md → Entry API](./API.md#entry-api) for the complete reference.
|
|
267
315
|
|
|
268
|
-
|
|
269
|
-
console.table({
|
|
270
|
-
[`${app.name}.asar path:`]: getPathFromAppNameAsar(),
|
|
271
|
-
'app version:': getAppVersion(),
|
|
272
|
-
'entry (installer) version:': getEntryVersion(),
|
|
273
|
-
'electron version:': process.versions.electron,
|
|
274
|
-
})
|
|
316
|
+
## Providers
|
|
275
317
|
|
|
276
|
-
|
|
277
|
-
updater.onDownloading = ({ percent }) => {
|
|
278
|
-
console.log(`Download progress: ${percent}%`)
|
|
279
|
-
}
|
|
318
|
+
The package includes GitHub-based providers (file, atom, API) and a local development provider.
|
|
280
319
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
buttons: ['Download', 'Later'],
|
|
286
|
-
message: `Version ${version} update available!`,
|
|
287
|
-
})
|
|
288
|
-
if (response === 0) {
|
|
289
|
-
// 0 = Download button
|
|
290
|
-
await updater.downloadUpdate()
|
|
291
|
-
}
|
|
292
|
-
})
|
|
320
|
+
- **GitHubProvider** — reads `version.json` from a repository branch, downloads asar from Releases.
|
|
321
|
+
- **GitHubAtomProvider** — reads the latest release from `releases.atom`.
|
|
322
|
+
- **GitHubApiProvider** — uses the GitHub Releases API (supports tokens for private repos).
|
|
323
|
+
- **LocalDevProvider** — reads update artifacts from the local filesystem for dev testing.
|
|
293
324
|
|
|
294
|
-
|
|
295
|
-
updater.on('update-not-available', (code, reason, info) => {
|
|
296
|
-
console.log('No update available:', reason)
|
|
297
|
-
})
|
|
325
|
+
All GitHub providers support a `urlHandler` for mirrors and custom gateways.
|
|
298
326
|
|
|
299
|
-
|
|
300
|
-
updater.on('download-progress', (data) => {
|
|
301
|
-
console.log('Download progress:', data)
|
|
302
|
-
// Send progress to renderer if needed
|
|
303
|
-
const [mainWindow] = BrowserWindow.getAllWindows()
|
|
304
|
-
if (mainWindow) {
|
|
305
|
-
mainWindow.webContents.send('update-progress', data)
|
|
306
|
-
}
|
|
307
|
-
})
|
|
327
|
+
See the [API reference](./API.md#providers) for the complete provider constructor options and method signatures.
|
|
308
328
|
|
|
309
|
-
|
|
310
|
-
updater.on('update-downloaded', () => {
|
|
311
|
-
dialog
|
|
312
|
-
.showMessageBox({
|
|
313
|
-
type: 'info',
|
|
314
|
-
message: 'Update downloaded successfully!',
|
|
315
|
-
buttons: ['Restart Now', 'Later'],
|
|
316
|
-
})
|
|
317
|
-
.then(({ response }) => {
|
|
318
|
-
if (response === 0) {
|
|
319
|
-
updater.quitAndInstall()
|
|
320
|
-
}
|
|
321
|
-
})
|
|
322
|
-
})
|
|
329
|
+
### Testing The Local Flow
|
|
323
330
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
console.error('Update error:', error)
|
|
327
|
-
dialog.showErrorBox('Update Error', error.message || 'Failed to check for updates')
|
|
328
|
-
})
|
|
331
|
+
Prefer `localDevUpdate: true` in the Vite plugin over constructing `LocalDevProvider` manually.
|
|
332
|
+
See [playground](./playground) as a complete local update test:
|
|
329
333
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
})
|
|
334
|
+
```sh
|
|
335
|
+
bun run play
|
|
333
336
|
```
|
|
334
337
|
|
|
335
|
-
|
|
338
|
+
`bun run play` builds the package first, then starts the Vite dev server and Electron playground.
|
|
336
339
|
|
|
337
|
-
|
|
340
|
+
Expected flow:
|
|
338
341
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
url.pathname = `https://github.com${url.pathname}`
|
|
348
|
-
return url
|
|
349
|
-
},
|
|
350
|
-
})
|
|
351
|
-
```
|
|
342
|
+
1. The plugin builds the Electron main, preload, and entry outputs.
|
|
343
|
+
2. The plugin creates `DEV.asar` and a local update archive.
|
|
344
|
+
3. `createElectronApp()` installs any existing `DEV.asar.tmp`.
|
|
345
|
+
4. The app starts from `DEV.asar`.
|
|
346
|
+
5. The updater checks the generated `version.json`.
|
|
347
|
+
6. The playground shows an available local update.
|
|
348
|
+
7. Clicking download emits simulated progress events.
|
|
349
|
+
8. Clicking restart installs `DEV.asar.tmp` and restarts Electron.
|
|
352
350
|
|
|
353
|
-
|
|
351
|
+
Explicit `updater.provider` values still take priority. If you set a provider manually, automatic
|
|
352
|
+
local provider setup is skipped.
|
|
354
353
|
|
|
355
|
-
|
|
356
|
-
updater.logger = console
|
|
357
|
-
```
|
|
354
|
+
## Update Artifacts
|
|
358
355
|
|
|
359
|
-
|
|
356
|
+
The Vite plugin can generate:
|
|
360
357
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
358
|
+
- `${app.name}.asar`
|
|
359
|
+
- `${app.name}-${version}.asar.br`
|
|
360
|
+
- `version.json`
|
|
364
361
|
|
|
365
|
-
|
|
362
|
+
Default `version.json` shape:
|
|
366
363
|
|
|
367
|
-
|
|
364
|
+
```json
|
|
365
|
+
{
|
|
366
|
+
"version": "1.0.0",
|
|
367
|
+
"minimumVersion": "0.0.0",
|
|
368
|
+
"signature": "...",
|
|
369
|
+
"beta": {
|
|
370
|
+
"version": "1.0.1-beta.1",
|
|
371
|
+
"minimumVersion": "0.0.0",
|
|
372
|
+
"signature": "..."
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
```
|
|
368
376
|
|
|
369
|
-
|
|
377
|
+
Stable releases update both the top-level fields and `beta`. Prerelease versions update only `beta`.
|
|
370
378
|
|
|
371
|
-
|
|
379
|
+
Set `buildVersionJson: true` if you need metadata during non-CI builds.
|
|
372
380
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
381
|
+
> [!NOTE]
|
|
382
|
+
> The **default** version parser supports `major.minor.patch[-prerelease[.number]]` (e.g. `1.0.0-beta.1`).
|
|
383
|
+
> Build metadata (`+build`) and complex semver prerelease identifiers (e.g. `1.0.0-beta.1.2`)
|
|
384
|
+
> are not supported by default.
|
|
385
|
+
>
|
|
386
|
+
> To use full semver or a custom version scheme, override
|
|
387
|
+
> [`provider.isLowerVersion`](#providers) with your own comparator
|
|
388
|
+
> (e.g. `semver.lt` from the `semver` package).
|
|
377
389
|
|
|
378
|
-
|
|
390
|
+
## Native Modules
|
|
379
391
|
|
|
380
|
-
in `
|
|
392
|
+
To keep update packages small, put native modules and their native binaries in `app.asar`, then load them from the main process with `requireNative()` or `importNative()`.
|
|
381
393
|
|
|
382
394
|
```ts
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
395
|
+
import { readdirSync } from 'node:fs'
|
|
396
|
+
|
|
397
|
+
import { electronWithUpdater } from 'electron-incremental-update/vite'
|
|
398
|
+
|
|
399
|
+
export default {
|
|
400
|
+
plugins: [
|
|
401
|
+
electronWithUpdater({
|
|
402
|
+
external: false,
|
|
403
|
+
entry: {
|
|
404
|
+
files: ['./electron/entry.ts', './electron/native/db.ts'],
|
|
405
|
+
postBuild({ isBuild, copyToEntryOutputDir }) {
|
|
406
|
+
if (!isBuild) {
|
|
407
|
+
return
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
copyToEntryOutputDir({
|
|
411
|
+
from: './node_modules/better-sqlite3/build/Release/better_sqlite3.node',
|
|
412
|
+
skipIfExist: false,
|
|
413
|
+
})
|
|
414
|
+
|
|
415
|
+
const packageName = readdirSync('./node_modules/.pnpm').find((name) =>
|
|
416
|
+
name.startsWith('@napi-rs+image-'),
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
if (packageName) {
|
|
420
|
+
const archName = packageName.substring('@napi-rs+image-'.length).split('@')[0]
|
|
421
|
+
copyToEntryOutputDir({
|
|
422
|
+
from: `./node_modules/.pnpm/${packageName}/node_modules/@napi-rs/image-${archName}/image.${archName}.node`,
|
|
423
|
+
})
|
|
424
|
+
}
|
|
425
|
+
},
|
|
426
|
+
},
|
|
427
|
+
main: {
|
|
428
|
+
files: './electron/main/index.ts',
|
|
429
|
+
},
|
|
430
|
+
}),
|
|
431
|
+
],
|
|
432
|
+
}
|
|
405
433
|
```
|
|
406
434
|
|
|
407
|
-
|
|
435
|
+
Use the copied native binding from entry asar:
|
|
408
436
|
|
|
409
437
|
```ts
|
|
410
438
|
import Database from 'better-sqlite3'
|
|
@@ -414,38 +442,27 @@ const db = new Database(':memory:', {
|
|
|
414
442
|
nativeBinding: getPathFromEntryAsar('./better_sqlite3.node'),
|
|
415
443
|
})
|
|
416
444
|
|
|
417
|
-
export function
|
|
418
|
-
db.exec(
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
db.prepare('INSERT INTO employees VALUES (:n, :s)').run({
|
|
424
|
-
n: 'James',
|
|
425
|
-
s: 5000,
|
|
445
|
+
export function testDatabase(): void {
|
|
446
|
+
db.exec('CREATE TABLE IF NOT EXISTS employees (name TEXT, salary INTEGER)')
|
|
447
|
+
db.prepare('INSERT INTO employees VALUES (:name, :salary)').run({
|
|
448
|
+
name: 'James',
|
|
449
|
+
salary: 5000,
|
|
426
450
|
})
|
|
427
|
-
|
|
428
|
-
const r = db.prepare('SELECT * from employees').all()
|
|
429
|
-
console.log(r)
|
|
430
|
-
// [ { name: 'James', salary: 50000 } ]
|
|
431
|
-
|
|
432
|
-
db.close()
|
|
433
451
|
}
|
|
434
452
|
```
|
|
435
453
|
|
|
436
|
-
|
|
454
|
+
Load native helper modules from main:
|
|
437
455
|
|
|
438
456
|
```ts
|
|
439
457
|
import { importNative, requireNative } from 'electron-incremental-update/utils'
|
|
440
458
|
|
|
441
|
-
|
|
442
|
-
requireNative<typeof import('../native/db')>('db').test()
|
|
459
|
+
requireNative<typeof import('../native/db')>('db').testDatabase()
|
|
443
460
|
|
|
444
|
-
|
|
445
|
-
|
|
461
|
+
const nativeDb = await importNative<typeof import('../native/db')>('db')
|
|
462
|
+
nativeDb.testDatabase()
|
|
446
463
|
```
|
|
447
464
|
|
|
448
|
-
|
|
465
|
+
For `electron-builder`, exclude `node_modules` when you have bundled dependencies manually:
|
|
449
466
|
|
|
450
467
|
```js
|
|
451
468
|
module.exports = {
|
|
@@ -457,7 +474,7 @@ module.exports = {
|
|
|
457
474
|
}
|
|
458
475
|
```
|
|
459
476
|
|
|
460
|
-
|
|
477
|
+
### Result in app.asar
|
|
461
478
|
|
|
462
479
|
Before: Redundant 🤮
|
|
463
480
|
|
|
@@ -525,114 +542,62 @@ After: Clean 😍
|
|
|
525
542
|
└── package.json
|
|
526
543
|
```
|
|
527
544
|
|
|
528
|
-
|
|
545
|
+
## Bytecode Protection
|
|
529
546
|
|
|
530
|
-
|
|
547
|
+
Bytecode protection compiles JavaScript into V8 bytecode.
|
|
531
548
|
|
|
532
549
|
```ts
|
|
533
550
|
electronWithUpdater({
|
|
534
|
-
|
|
535
|
-
|
|
551
|
+
bytecode: true,
|
|
552
|
+
entry: {
|
|
553
|
+
files: './electron/entry.ts',
|
|
554
|
+
},
|
|
555
|
+
main: {
|
|
556
|
+
files: './electron/main/index.ts',
|
|
557
|
+
},
|
|
536
558
|
})
|
|
537
559
|
```
|
|
538
560
|
|
|
539
|
-
|
|
561
|
+
Notes:
|
|
540
562
|
|
|
541
|
-
|
|
563
|
+
- CommonJS only. Remove `"type": "module"` from `package.json` when enabling bytecode.
|
|
564
|
+
- Main process bytecode is enabled by default.
|
|
565
|
+
- To include preload scripts, use `bytecode: { enablePreload: true }`.
|
|
566
|
+
- If preload bytecode is enabled, create the `BrowserWindow` with `sandbox: false`.
|
|
542
567
|
|
|
543
|
-
|
|
544
|
-
- Protect all strings by default
|
|
545
|
-
- Minification is allowed
|
|
568
|
+
## Development Bundling
|
|
546
569
|
|
|
547
|
-
|
|
570
|
+
`notBundle` is enabled by default in development. It externalizes Node modules in entry and main builds to improve startup speed.
|
|
548
571
|
|
|
549
|
-
|
|
550
|
-
|
|
572
|
+
```ts
|
|
573
|
+
electronWithUpdater({
|
|
574
|
+
notBundle: false,
|
|
575
|
+
entry: {
|
|
576
|
+
files: './electron/entry.ts',
|
|
577
|
+
},
|
|
578
|
+
main: {
|
|
579
|
+
files: './electron/main/index.ts',
|
|
580
|
+
},
|
|
581
|
+
})
|
|
582
|
+
```
|
|
551
583
|
|
|
552
|
-
|
|
584
|
+
## Utilities
|
|
553
585
|
|
|
554
|
-
|
|
586
|
+
Import from `electron-incremental-update/utils` for path helpers, platform checks, native module loading, crypto, compression, download, and version parsing utilities.
|
|
555
587
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
isMac,
|
|
562
|
-
isLinux,
|
|
563
|
-
getPathFromAppNameAsar,
|
|
564
|
-
getPathFromEntryAsar,
|
|
565
|
-
getPathFromMain,
|
|
566
|
-
getPathFromPreload,
|
|
567
|
-
getPathFromPublic,
|
|
568
|
-
getAppVersion,
|
|
569
|
-
getEntryVersion,
|
|
570
|
-
requireNative,
|
|
571
|
-
importNative,
|
|
572
|
-
restartApp,
|
|
573
|
-
setAppUserModelId,
|
|
574
|
-
disableHWAccForWin7,
|
|
575
|
-
singleInstance,
|
|
576
|
-
setPortableDataPath,
|
|
577
|
-
loadPage,
|
|
578
|
-
beautifyDevTools,
|
|
579
|
-
handleUnexpectedErrors,
|
|
580
|
-
|
|
581
|
-
// Crypto utilities
|
|
582
|
-
hashBuffer,
|
|
583
|
-
aesEncrypt,
|
|
584
|
-
aesDecrypt,
|
|
585
|
-
defaultSignature,
|
|
586
|
-
defaultVerifySignature,
|
|
587
|
-
|
|
588
|
-
// Zip utilities
|
|
589
|
-
defaultZipFile,
|
|
590
|
-
defaultUnzipFile,
|
|
591
|
-
} from 'electron-incremental-update/utils'
|
|
592
|
-
```
|
|
588
|
+
> 📖 See [API.md → Utilities API](./API.md#utilities-api) for the complete function list with signatures and descriptions.
|
|
589
|
+
|
|
590
|
+
## API Reference
|
|
591
|
+
|
|
592
|
+
For the full API documentation including all types, options tables, provider constructors, and plugin configuration, see the standalone reference:
|
|
593
593
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
- **isDev** - Compile-time dev check
|
|
597
|
-
- **isWin** - Check if running on Windows
|
|
598
|
-
- **isMac** - Check if running on macOS
|
|
599
|
-
- **isLinux** - Check if running on Linux
|
|
600
|
-
- **getPathFromAppNameAsar(...paths)** - Get joined path of `${electron.app.name}.asar`
|
|
601
|
-
- **getPathFromEntryAsar(...paths)** - Get joined path from entry asar
|
|
602
|
-
- **getPathFromMain(...paths)** - Get joined path from main dir
|
|
603
|
-
- **getPathFromPreload(...paths)** - Get joined path from preload dir
|
|
604
|
-
- **getPathFromPublic(...paths)** - Get joined path from public dir
|
|
605
|
-
- **getAppVersion()** - Get app version (returns entry version in dev)
|
|
606
|
-
- **getEntryVersion()** - Get entry version
|
|
607
|
-
- **requireNative(moduleName)** - Load native module using require from entry asar
|
|
608
|
-
- **importNative(moduleName)** - Load native module using import from entry asar
|
|
609
|
-
- **restartApp()** - Restart the Electron app
|
|
610
|
-
- **setAppUserModelId(id)** - Fix app model ID (Windows only)
|
|
611
|
-
- **disableHWAccForWin7()** - Disable hardware acceleration for Windows 7
|
|
612
|
-
- **singleInstance(window)** - Keep single instance and restore window
|
|
613
|
-
- **setPortableDataPath(dirName, create)** - Set userData dir to exe dir for portable apps
|
|
614
|
-
- **loadPage(win, htmlFilePath)** - Load dev server URL in dev or HTML file otherwise
|
|
615
|
-
- **beautifyDevTools(win, options)** - Beautify devtools font and scrollbar
|
|
616
|
-
- **handleUnexpectedErrors(callback)** - Handle all unhandled errors
|
|
617
|
-
|
|
618
|
-
#### Crypto Utilities
|
|
619
|
-
|
|
620
|
-
- **hashBuffer(data, length)** - Hash data using SHA-256
|
|
621
|
-
- **aesEncrypt(plainText, key, iv)** - Encrypt text using AES
|
|
622
|
-
- **aesDecrypt(encryptedText, key, iv)** - Decrypt text using AES
|
|
623
|
-
- **defaultSignature(buffer, privateKey, cert, version)** - Generate RSA signature for asar file
|
|
624
|
-
- **defaultVerifySignature(buffer, version, signature, cert)** - Verify RSA signature of asar file
|
|
625
|
-
|
|
626
|
-
#### Zip Utilities
|
|
627
|
-
|
|
628
|
-
- **defaultZipFile(buffer)** - Compress file using brotli
|
|
629
|
-
- **defaultUnzipFile(buffer)** - Decompress file using brotli
|
|
594
|
+
> 📖 **[API.md](./API.md)** — Auto-generated JSDoc extraction + manual reference sections.
|
|
630
595
|
|
|
631
596
|
## Credits
|
|
632
597
|
|
|
633
|
-
- [Obsidian](https://obsidian.md/) for
|
|
634
|
-
- [vite-plugin-electron](https://github.com/electron-vite/vite-plugin-electron) for
|
|
635
|
-
- [electron-builder](https://github.com/electron-userland/electron-builder) for
|
|
598
|
+
- [Obsidian](https://obsidian.md/) for the dual asar update strategy
|
|
599
|
+
- [vite-plugin-electron](https://github.com/electron-vite/vite-plugin-electron) for the Vite Electron plugin foundation
|
|
600
|
+
- [electron-builder](https://github.com/electron-userland/electron-builder) for Electron packaging
|
|
636
601
|
- [electron-vite](https://github.com/alex8088/electron-vite) for bytecode plugin inspiration
|
|
637
602
|
|
|
638
603
|
## License
|