electron-incremental-update 0.2.2 → 0.4.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 +46 -116
- package/dist/index.cjs +111 -71
- package/dist/index.d.ts +74 -57
- package/dist/index.mjs +109 -70
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -16,6 +16,8 @@ using RSA + Signature to sign the new `main.asar` downloaded from remote and rep
|
|
|
16
16
|
|
|
17
17
|
develop with [vite-plugin-electron](https://github.com/electron-vite/vite-plugin-electron), and may be effect in other electron vite frameworks
|
|
18
18
|
|
|
19
|
+
**all options are documented in the jsdoc**
|
|
20
|
+
|
|
19
21
|
## install
|
|
20
22
|
|
|
21
23
|
### npm
|
|
@@ -50,21 +52,28 @@ src
|
|
|
50
52
|
|
|
51
53
|
### setup app
|
|
52
54
|
|
|
53
|
-
more example see comment on `initApp()`
|
|
54
55
|
|
|
55
56
|
```ts
|
|
56
57
|
// electron/app.ts
|
|
57
|
-
import { createUpdater, initApp } from 'electron-incremental-update'
|
|
58
|
+
import { createUpdater, getGithubReleaseCdnGroup, initApp, parseGithubCdnURL } from 'electron-incremental-update'
|
|
58
59
|
import { name, repository } from '../package.json'
|
|
59
60
|
|
|
60
61
|
const SIGNATURE_PUB = '' // auto generate RSA public key when start app
|
|
61
62
|
|
|
63
|
+
// create updater when init, no need to set productName
|
|
64
|
+
initApp({ name }, { SIGNATURE_PUB, repository })
|
|
65
|
+
|
|
66
|
+
// or create updater manually
|
|
67
|
+
const { cdnPrefix } = getGithubReleaseCdnGroup()[0]
|
|
62
68
|
const updater = createUpdater({
|
|
63
69
|
SIGNATURE_PUB,
|
|
64
|
-
repository,
|
|
65
70
|
productName: name,
|
|
71
|
+
repository,
|
|
72
|
+
updateJsonURL: parseGithubCdnURL(repository, 'fastly.jsdelivr.net/gh', 'version.json'),
|
|
73
|
+
releaseAsarURL: parseGithubCdnURL(repository, cdnPrefix, `download/latest/${name}.asar.gz`),
|
|
74
|
+
debug: true,
|
|
66
75
|
})
|
|
67
|
-
initApp(name
|
|
76
|
+
initApp({ name }).setUpdater(updater)
|
|
68
77
|
```
|
|
69
78
|
|
|
70
79
|
### setup main
|
|
@@ -72,43 +81,43 @@ initApp(name, updater)
|
|
|
72
81
|
```ts
|
|
73
82
|
// electron/main/index.ts
|
|
74
83
|
import type { Updater } from 'electron-incremental-update'
|
|
75
|
-
import { getAppAsarPath, getAppVersion,
|
|
84
|
+
import { getAppAsarPath, getAppVersion, getEntryVersion } from 'electron-incremental-update'
|
|
76
85
|
import { app } from 'electron'
|
|
77
86
|
import { name } from '../../package.json'
|
|
78
87
|
|
|
79
88
|
export default function (updater: Updater) {
|
|
80
89
|
console.log('\ncurrent:')
|
|
81
|
-
console.log(`\telectron: ${getElectronVersion()}`)
|
|
82
90
|
console.log(`\tasar path: ${getAppAsarPath(name)}`)
|
|
91
|
+
console.log(`\tentry: ${getEntryVersion()}`)
|
|
83
92
|
console.log(`\tapp: ${getAppVersion(name)}`)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
updater.on('checkResult', async (result
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
case 'unavailable':
|
|
101
|
-
console.log('Update Unavailable')
|
|
102
|
-
break
|
|
103
|
-
case 'fail':
|
|
104
|
-
console.error(err)
|
|
105
|
-
break
|
|
93
|
+
let size = 0
|
|
94
|
+
let currentSize = 0
|
|
95
|
+
updater.on('checkResult', async (result) => {
|
|
96
|
+
if (result === false) {
|
|
97
|
+
console.log('Update Unavailable')
|
|
98
|
+
} else if (result instanceof Error) {
|
|
99
|
+
console.error(result)
|
|
100
|
+
} else {
|
|
101
|
+
size = result.size
|
|
102
|
+
console.log('new version: ', result.version)
|
|
103
|
+
const { response } = await dialog.showMessageBox({
|
|
104
|
+
type: 'info',
|
|
105
|
+
buttons: ['Download', 'Later'],
|
|
106
|
+
message: 'Application update available!',
|
|
107
|
+
})
|
|
108
|
+
response === 0 && await updater.downloadUpdate()
|
|
106
109
|
}
|
|
107
110
|
})
|
|
108
|
-
updater.on('
|
|
109
|
-
updater.on('downloading',
|
|
110
|
-
|
|
111
|
+
updater.on('download', () => console.log('download start'))
|
|
112
|
+
updater.on('downloading', (len) => {
|
|
113
|
+
currentSize += len
|
|
114
|
+
console.log(`${(currentSize / size).toFixed(2)}%`)
|
|
115
|
+
})
|
|
116
|
+
updater.on('downloaded', () => console.log('download end'))
|
|
111
117
|
updater.on('donwnloadError', console.error)
|
|
118
|
+
// to debug, it need to set debug to true in updater options
|
|
119
|
+
updater.on('debug', data => console.log('[updater]:', data))
|
|
120
|
+
updater.checkUpdate()
|
|
112
121
|
|
|
113
122
|
// app logics
|
|
114
123
|
app.whenReady().then(() => {
|
|
@@ -144,7 +153,12 @@ db.close()
|
|
|
144
153
|
|
|
145
154
|
### setup vite.config.ts
|
|
146
155
|
|
|
156
|
+
make sure the plugin is set in the **last** build task plugin option
|
|
157
|
+
|
|
158
|
+
- set it to preload task plugin, as the end of build task
|
|
159
|
+
|
|
147
160
|
```ts
|
|
161
|
+
// vite.config.ts
|
|
148
162
|
export default defineConfig(({ command }) => {
|
|
149
163
|
|
|
150
164
|
const isBuild = command === 'build'
|
|
@@ -162,7 +176,7 @@ export default defineConfig(({ command }) => {
|
|
|
162
176
|
// ...
|
|
163
177
|
vite: {
|
|
164
178
|
plugins: [
|
|
165
|
-
updater({
|
|
179
|
+
updater({
|
|
166
180
|
productName: pkg.name,
|
|
167
181
|
version: pkg.version,
|
|
168
182
|
isBuild,
|
|
@@ -179,90 +193,6 @@ export default defineConfig(({ command }) => {
|
|
|
179
193
|
})
|
|
180
194
|
```
|
|
181
195
|
|
|
182
|
-
#### plugin options
|
|
183
|
-
|
|
184
|
-
```ts
|
|
185
|
-
type Options = {
|
|
186
|
-
/**
|
|
187
|
-
* whether is in build mode
|
|
188
|
-
*/
|
|
189
|
-
isBuild: boolean
|
|
190
|
-
/**
|
|
191
|
-
* the name of you application
|
|
192
|
-
*
|
|
193
|
-
* you can set as 'name' in package.json
|
|
194
|
-
*/
|
|
195
|
-
productName: string
|
|
196
|
-
/**
|
|
197
|
-
* the version of you application
|
|
198
|
-
*
|
|
199
|
-
* you can set as 'version' in package.json
|
|
200
|
-
*/
|
|
201
|
-
version: string
|
|
202
|
-
/**
|
|
203
|
-
* Whether to minify
|
|
204
|
-
*/
|
|
205
|
-
minify?: boolean
|
|
206
|
-
/**
|
|
207
|
-
* path config
|
|
208
|
-
*/
|
|
209
|
-
paths?: {
|
|
210
|
-
/**
|
|
211
|
-
* Path to app entry file
|
|
212
|
-
* @default 'electron/app.ts'
|
|
213
|
-
*/
|
|
214
|
-
entryPath?: string
|
|
215
|
-
/**
|
|
216
|
-
* Path to app entry output file
|
|
217
|
-
* @default 'app.js'
|
|
218
|
-
*/
|
|
219
|
-
entryOutputPath?: string
|
|
220
|
-
/**
|
|
221
|
-
* Path to asar file
|
|
222
|
-
* @default `release/${ProductName}.asar`
|
|
223
|
-
*/
|
|
224
|
-
asarOutputPath?: string
|
|
225
|
-
/**
|
|
226
|
-
* Path to electron build output
|
|
227
|
-
* @default `dist-electron`
|
|
228
|
-
*/
|
|
229
|
-
electronDistPath?: string
|
|
230
|
-
/**
|
|
231
|
-
* Path to renderer build output
|
|
232
|
-
* @default `dist`
|
|
233
|
-
*/
|
|
234
|
-
rendererDistPath?: string
|
|
235
|
-
/**
|
|
236
|
-
* Path to version info output
|
|
237
|
-
* @default `version.json`
|
|
238
|
-
*/
|
|
239
|
-
versionPath?: string
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* signature config
|
|
243
|
-
*/
|
|
244
|
-
keys?: {
|
|
245
|
-
/**
|
|
246
|
-
* Path to the pem file that contains private key
|
|
247
|
-
* if not ended with .pem, it will be appended
|
|
248
|
-
* @default 'public/private.pem'
|
|
249
|
-
*/
|
|
250
|
-
privateKeyPath?: string
|
|
251
|
-
/**
|
|
252
|
-
* Path to the pem file that contains public key
|
|
253
|
-
* if not ended with .pem, it will be appended
|
|
254
|
-
* @default 'public/public.pem'
|
|
255
|
-
*/
|
|
256
|
-
publicKeyPath?: string
|
|
257
|
-
/**
|
|
258
|
-
* Length of the key
|
|
259
|
-
* @default 2048
|
|
260
|
-
*/
|
|
261
|
-
keyLength?: number
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
```
|
|
265
|
-
|
|
266
196
|
### electron-builder config
|
|
267
197
|
|
|
268
198
|
```js
|
package/dist/index.cjs
CHANGED
|
@@ -37,7 +37,8 @@ __export(src_exports, {
|
|
|
37
37
|
getGithubReleaseCdnGroup: () => getGithubReleaseCdnGroup,
|
|
38
38
|
initApp: () => initApp,
|
|
39
39
|
parseGithubCdnURL: () => parseGithubCdnURL,
|
|
40
|
-
requireNative: () => requireNative
|
|
40
|
+
requireNative: () => requireNative,
|
|
41
|
+
restartApp: () => restartApp
|
|
41
42
|
});
|
|
42
43
|
module.exports = __toCommonJS(src_exports);
|
|
43
44
|
var import_node_path2 = require("path");
|
|
@@ -62,17 +63,18 @@ function downloadJSONDefault(url, updater, headers) {
|
|
|
62
63
|
res.headers = headers;
|
|
63
64
|
res.on("data", (chunk) => data += chunk);
|
|
64
65
|
res.on("end", () => {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
try {
|
|
67
|
+
const json = JSON.parse(data);
|
|
68
|
+
if ("signature" in json && "version" in json && "size" in json) {
|
|
69
|
+
resolve2(json);
|
|
70
|
+
} else {
|
|
71
|
+
throw Error;
|
|
72
|
+
}
|
|
73
|
+
} catch (e) {
|
|
74
|
+
reject(new Error("invalid json"));
|
|
71
75
|
}
|
|
72
76
|
});
|
|
73
77
|
}).on("error", (e) => {
|
|
74
|
-
e && updater.emit("donwnloadError", e);
|
|
75
|
-
updater.emit("downloadEnd", false);
|
|
76
78
|
reject(e);
|
|
77
79
|
});
|
|
78
80
|
});
|
|
@@ -87,12 +89,9 @@ function downloadBufferDefault(url, updater, headers) {
|
|
|
87
89
|
data.push(chunk);
|
|
88
90
|
});
|
|
89
91
|
res.on("end", () => {
|
|
90
|
-
updater.emit("downloadEnd", true);
|
|
91
92
|
resolve2(import_node_buffer.Buffer.concat(data));
|
|
92
93
|
});
|
|
93
94
|
}).on("error", (e) => {
|
|
94
|
-
e && updater.emit("donwnloadError", e);
|
|
95
|
-
updater.emit("downloadEnd", false);
|
|
96
95
|
reject(e);
|
|
97
96
|
});
|
|
98
97
|
});
|
|
@@ -164,6 +163,10 @@ function getGithubReleaseCdnGroup() {
|
|
|
164
163
|
{ cdnPrefix: "download.nuaa.cf", maintainer: "LibraryCloud-nuaa" }
|
|
165
164
|
];
|
|
166
165
|
}
|
|
166
|
+
function restartApp() {
|
|
167
|
+
import_electron.app.relaunch();
|
|
168
|
+
import_electron.app.quit();
|
|
169
|
+
}
|
|
167
170
|
|
|
168
171
|
// src/updater/index.ts
|
|
169
172
|
function createUpdater({
|
|
@@ -177,9 +180,13 @@ function createUpdater({
|
|
|
177
180
|
compareVersion
|
|
178
181
|
}) {
|
|
179
182
|
const updater = new import_node_events.EventEmitter();
|
|
183
|
+
let signature = "";
|
|
184
|
+
let version = "";
|
|
185
|
+
const gzipPath = `../${productName}.asar.gz`;
|
|
186
|
+
const tmpFile = gzipPath.replace(".asar.gz", ".tmp.gz");
|
|
180
187
|
const { downloadBuffer, downloadJSON, extraHeader, userAgent } = downloadConfig || {};
|
|
181
|
-
function log(
|
|
182
|
-
debug &&
|
|
188
|
+
function log(msg) {
|
|
189
|
+
debug && updater.emit("debug", msg);
|
|
183
190
|
}
|
|
184
191
|
async function download(url, format) {
|
|
185
192
|
const ua = userAgent || "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36";
|
|
@@ -188,12 +195,16 @@ function createUpdater({
|
|
|
188
195
|
UserAgent: ua,
|
|
189
196
|
...extraHeader
|
|
190
197
|
};
|
|
191
|
-
log(
|
|
198
|
+
log(`download headers: ${JSON.stringify(headers, null, 2)}`);
|
|
192
199
|
const downloadFn = format === "json" ? downloadJSON ?? downloadJSONDefault : downloadBuffer ?? downloadBufferDefault;
|
|
193
|
-
|
|
200
|
+
log(`download ${format} from ${url}`);
|
|
201
|
+
const ret = await downloadFn(url, updater, headers);
|
|
202
|
+
log(`download ${format} success`);
|
|
203
|
+
return ret;
|
|
194
204
|
}
|
|
195
205
|
async function extractFile(gzipFilePath) {
|
|
196
206
|
if (!gzipFilePath.endsWith(".asar.gz") || !(0, import_node_fs2.existsSync)(gzipFilePath)) {
|
|
207
|
+
log("update .asar.gz file not exist");
|
|
197
208
|
return;
|
|
198
209
|
}
|
|
199
210
|
gzipFilePath = gzipFilePath.replace(".asar.gz", ".tmp.gz");
|
|
@@ -202,108 +213,136 @@ function createUpdater({
|
|
|
202
213
|
const input = (0, import_node_fs2.createReadStream)(gzipFilePath);
|
|
203
214
|
const outputFilePath = gzipFilePath.replace(".tmp.gz", ".asar");
|
|
204
215
|
const output = (0, import_node_fs2.createWriteStream)(outputFilePath);
|
|
205
|
-
log(
|
|
216
|
+
log(`outputFilePath: ${outputFilePath}`);
|
|
206
217
|
input.pipe(gunzip).pipe(output).on("finish", async () => {
|
|
207
218
|
await (0, import_promises.rm)(gzipFilePath);
|
|
208
|
-
log(
|
|
219
|
+
log(`${gzipFilePath} unzipped`);
|
|
209
220
|
resolve2(outputFilePath);
|
|
210
221
|
}).on("error", async (err) => {
|
|
211
222
|
await (0, import_promises.rm)(gzipFilePath);
|
|
212
|
-
log("[updater] error", err);
|
|
213
223
|
output.destroy(err);
|
|
214
224
|
reject(err);
|
|
215
225
|
});
|
|
216
226
|
});
|
|
217
227
|
}
|
|
218
|
-
function verify(buffer,
|
|
219
|
-
|
|
220
|
-
return (0, import_node_crypto.createVerify)("RSA-SHA256").update(buffer).verify(SIGNATURE_PUB, signature, "base64");
|
|
228
|
+
function verify(buffer, signature2) {
|
|
229
|
+
return (0, import_node_crypto.createVerify)("RSA-SHA256").update(buffer).verify(SIGNATURE_PUB, signature2, "base64");
|
|
221
230
|
}
|
|
222
|
-
function needUpdate(
|
|
223
|
-
if (!
|
|
231
|
+
function needUpdate(version2) {
|
|
232
|
+
if (!import_electron2.app.isPackaged) {
|
|
233
|
+
log("in dev mode, no need to update");
|
|
224
234
|
return false;
|
|
225
235
|
}
|
|
226
236
|
const currentVersion = getEntryVersion();
|
|
227
|
-
log(
|
|
228
|
-
|
|
237
|
+
log(`check update:
|
|
238
|
+
current version is ${currentVersion},
|
|
239
|
+
new version is ${version2}`);
|
|
229
240
|
const _compare = compareVersion ?? compareVersionDefault;
|
|
230
|
-
return _compare(currentVersion,
|
|
241
|
+
return _compare(currentVersion, version2);
|
|
231
242
|
}
|
|
232
|
-
async function checkUpdate(
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
} = option || {};
|
|
237
|
-
if (!updateJsonURL || !releaseAsarURL) {
|
|
238
|
-
log("[updater] no updateJsonURL or releaseAsarURL, use repository");
|
|
243
|
+
async function checkUpdate(url) {
|
|
244
|
+
url ??= _update;
|
|
245
|
+
if (!url) {
|
|
246
|
+
log("no updateJsonURL, fallback to use repository");
|
|
239
247
|
if (!repository) {
|
|
240
|
-
throw new Error("updateJsonURL or
|
|
248
|
+
throw new Error("updateJsonURL or repository are not set");
|
|
241
249
|
}
|
|
242
|
-
|
|
243
|
-
releaseAsarURL = `${repository}/releases/download/latest/${productName}.asar.gz`;
|
|
250
|
+
url = `${repository.replace("github.com", "raw.githubusercontent.com")}/master/version.json`;
|
|
244
251
|
}
|
|
245
|
-
log("[updater] updateJsonURL", updateJsonURL);
|
|
246
|
-
log("[updater] releaseAsarURL", releaseAsarURL);
|
|
247
|
-
const gzipPath = `../${productName}.asar.gz`;
|
|
248
|
-
const tmpFile = gzipPath.replace(".asar.gz", ".tmp.gz");
|
|
249
252
|
if ((0, import_node_fs2.existsSync)(tmpFile)) {
|
|
250
|
-
log(
|
|
253
|
+
log(`remove tmp file: ${tmpFile}`);
|
|
251
254
|
await (0, import_promises.rm)(tmpFile);
|
|
252
255
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
+
if ((0, import_node_fs2.existsSync)(gzipPath)) {
|
|
257
|
+
log(`remove .gz file: ${gzipPath}`);
|
|
258
|
+
await (0, import_promises.rm)(gzipPath);
|
|
256
259
|
}
|
|
260
|
+
const json = await download(url, "json");
|
|
257
261
|
const {
|
|
258
|
-
signature,
|
|
259
|
-
version,
|
|
262
|
+
signature: _sig,
|
|
263
|
+
version: _v,
|
|
260
264
|
size
|
|
261
265
|
} = json;
|
|
262
|
-
log(
|
|
263
|
-
if (!needUpdate(
|
|
264
|
-
|
|
266
|
+
log(`update info: ${JSON.stringify(json, null, 2)}`);
|
|
267
|
+
if (!await needUpdate(_v)) {
|
|
268
|
+
log(`update unavailable: ${_v}`);
|
|
269
|
+
return false;
|
|
270
|
+
} else {
|
|
271
|
+
log(`update available: ${_v}`);
|
|
272
|
+
signature = _sig;
|
|
273
|
+
version = _v;
|
|
274
|
+
return { size, version };
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
async function downloadUpdate(src) {
|
|
278
|
+
if (typeof src !== "object") {
|
|
279
|
+
let _url = src ?? _release;
|
|
280
|
+
if (!_url) {
|
|
281
|
+
log("no releaseAsarURL, fallback to use repository");
|
|
282
|
+
if (!repository) {
|
|
283
|
+
throw new Error("releaseAsarURL or repository are not set");
|
|
284
|
+
}
|
|
285
|
+
_url = `${repository}/releases/download/latest/${productName}.asar.gz`;
|
|
286
|
+
}
|
|
287
|
+
src = await download(_url, "buffer");
|
|
265
288
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
throw new Error("file broken, invalid signature!");
|
|
289
|
+
log("verify start");
|
|
290
|
+
if (!verify(src, signature)) {
|
|
291
|
+
log("verify failed");
|
|
292
|
+
throw new Error("invalid signature");
|
|
271
293
|
}
|
|
272
|
-
log("
|
|
273
|
-
|
|
274
|
-
|
|
294
|
+
log("verify success");
|
|
295
|
+
log(`write file: ${gzipPath}`);
|
|
296
|
+
await (0, import_promises.writeFile)(gzipPath, src);
|
|
297
|
+
log(`extract file: ${gzipPath}`);
|
|
275
298
|
await extractFile(gzipPath);
|
|
276
|
-
|
|
299
|
+
log(`update success, version: ${version}`);
|
|
300
|
+
updater.emit("downloaded");
|
|
277
301
|
}
|
|
278
|
-
const onCheck = async (
|
|
302
|
+
const onCheck = async (url) => {
|
|
279
303
|
try {
|
|
280
|
-
const result = await checkUpdate(
|
|
304
|
+
const result = await checkUpdate(url);
|
|
281
305
|
updater.emit("checkResult", result);
|
|
282
306
|
} catch (error) {
|
|
283
|
-
|
|
307
|
+
log(error);
|
|
308
|
+
updater.emit("checkResult", error);
|
|
284
309
|
}
|
|
285
310
|
};
|
|
286
311
|
updater.on("check", onCheck);
|
|
287
312
|
updater.checkUpdate = onCheck;
|
|
313
|
+
const onDownload = async (src) => {
|
|
314
|
+
try {
|
|
315
|
+
await downloadUpdate(src);
|
|
316
|
+
} catch (error) {
|
|
317
|
+
log(error);
|
|
318
|
+
updater.emit("donwnloadError", error);
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
updater.on("download", onDownload);
|
|
322
|
+
updater.downloadUpdate = onDownload;
|
|
288
323
|
return updater;
|
|
289
324
|
}
|
|
290
325
|
|
|
291
326
|
// src/index.ts
|
|
292
|
-
function initApp(
|
|
327
|
+
function initApp(appOptions, updaterOptions) {
|
|
293
328
|
const {
|
|
329
|
+
name: productName,
|
|
294
330
|
electronDistPath = "dist-electron",
|
|
295
331
|
mainPath = "main/index.js"
|
|
296
|
-
} =
|
|
332
|
+
} = appOptions ?? {};
|
|
297
333
|
const mainDir = import_electron3.app.isPackaged ? `../${productName}.asar` : electronDistPath;
|
|
298
334
|
const entry = (0, import_node_path2.resolve)(__dirname, mainDir, mainPath);
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
335
|
+
if (updaterOptions) {
|
|
336
|
+
require(entry)(
|
|
337
|
+
createUpdater({ ...updaterOptions, productName })
|
|
338
|
+
);
|
|
303
339
|
} else {
|
|
304
|
-
|
|
340
|
+
return {
|
|
341
|
+
setUpdater(updater) {
|
|
342
|
+
require(entry)(updater);
|
|
343
|
+
}
|
|
344
|
+
};
|
|
305
345
|
}
|
|
306
|
-
return require(entry)(_updater);
|
|
307
346
|
}
|
|
308
347
|
// Annotate the CommonJS export names for ESM import in node:
|
|
309
348
|
0 && (module.exports = {
|
|
@@ -314,5 +353,6 @@ function initApp(productName, updater, option) {
|
|
|
314
353
|
getGithubReleaseCdnGroup,
|
|
315
354
|
initApp,
|
|
316
355
|
parseGithubCdnURL,
|
|
317
|
-
requireNative
|
|
356
|
+
requireNative,
|
|
357
|
+
restartApp
|
|
318
358
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
2
|
|
|
3
|
-
type CheckResultType =
|
|
3
|
+
type CheckResultType = Error | false | Omit<UpdateJSON, 'signature'>;
|
|
4
4
|
type UpdateEvents = {
|
|
5
|
-
check:
|
|
6
|
-
checkResult: [data: CheckResultType
|
|
7
|
-
|
|
5
|
+
check: [url?: string];
|
|
6
|
+
checkResult: [data: CheckResultType];
|
|
7
|
+
download: [src?: string | Buffer];
|
|
8
8
|
downloading: [current: number];
|
|
9
|
-
|
|
9
|
+
downloaded: null;
|
|
10
10
|
donwnloadError: [error: unknown];
|
|
11
|
+
debug: [msg: string | Error];
|
|
11
12
|
};
|
|
12
13
|
type UpdateJSON = {
|
|
13
14
|
signature: string;
|
|
@@ -15,20 +16,6 @@ type UpdateJSON = {
|
|
|
15
16
|
size: number;
|
|
16
17
|
};
|
|
17
18
|
type MaybeArray<T> = T extends undefined | null | never ? [] : T extends any[] ? T['length'] extends 1 ? [data: T[0]] : T : [data: T];
|
|
18
|
-
interface BaseOption {
|
|
19
|
-
/**
|
|
20
|
-
* URL of version info json
|
|
21
|
-
* @default `${repository.replace('github.com', 'raw.githubusercontent.com')}/version.json`
|
|
22
|
-
* @throws if `updateJsonURL` and `repository` are all not set
|
|
23
|
-
*/
|
|
24
|
-
updateJsonURL?: string;
|
|
25
|
-
/**
|
|
26
|
-
* URL of release asar.gz
|
|
27
|
-
* @default `${repository}/releases/download/latest/${productName}.asar.gz`
|
|
28
|
-
* @throws if `releaseAsarURL` and `repository` are all not set
|
|
29
|
-
*/
|
|
30
|
-
releaseAsarURL?: string;
|
|
31
|
-
}
|
|
32
19
|
interface TypedUpdater<T extends Record<string | symbol, MaybeArray<any>>, Event extends Exclude<keyof T, number> = Exclude<keyof T, number>> {
|
|
33
20
|
removeAllListeners<E extends Event>(event?: E): this;
|
|
34
21
|
listeners<E extends Event>(eventName: E): Function[];
|
|
@@ -37,10 +24,16 @@ interface TypedUpdater<T extends Record<string | symbol, MaybeArray<any>>, Event
|
|
|
37
24
|
once<E extends Event>(eventName: E, listener: (...args: MaybeArray<T[E]>) => void): this;
|
|
38
25
|
emit<E extends Event>(eventName: E, ...args: MaybeArray<T[E]>): boolean;
|
|
39
26
|
off<E extends Event>(eventName: E, listener: (...args: MaybeArray<T[E]>) => void): this;
|
|
40
|
-
|
|
27
|
+
/**
|
|
28
|
+
* - `undefined`: errror
|
|
29
|
+
* - `false`: unavailable
|
|
30
|
+
* - `{size: number, version: string}`: success
|
|
31
|
+
*/
|
|
32
|
+
checkUpdate(url?: string): Promise<void>;
|
|
33
|
+
downloadUpdate(url?: string | Buffer): Promise<void>;
|
|
41
34
|
}
|
|
42
35
|
type Updater = TypedUpdater<UpdateEvents>;
|
|
43
|
-
interface UpdaterOption
|
|
36
|
+
interface UpdaterOption {
|
|
44
37
|
/**
|
|
45
38
|
* public key of signature
|
|
46
39
|
*
|
|
@@ -58,7 +51,7 @@ interface UpdaterOption extends BaseOption {
|
|
|
58
51
|
*/
|
|
59
52
|
SIGNATURE_PUB: string;
|
|
60
53
|
/**
|
|
61
|
-
*
|
|
54
|
+
* name of your application
|
|
62
55
|
*
|
|
63
56
|
* you can use the `name` in `package.json`
|
|
64
57
|
*/
|
|
@@ -72,8 +65,29 @@ interface UpdaterOption extends BaseOption {
|
|
|
72
65
|
* `repository` will be used to determine the url
|
|
73
66
|
*/
|
|
74
67
|
repository?: string;
|
|
68
|
+
/**
|
|
69
|
+
* URL of version info json
|
|
70
|
+
* @default `${repository.replace('github.com', 'raw.githubusercontent.com')}/master/version.json`
|
|
71
|
+
* @throws if `updateJsonURL` and `repository` are all not set
|
|
72
|
+
*/
|
|
73
|
+
updateJsonURL?: string;
|
|
74
|
+
/**
|
|
75
|
+
* URL of release asar.gz
|
|
76
|
+
* @default `${repository}/releases/download/latest/${productName}.asar.gz`
|
|
77
|
+
* @throws if `releaseAsarURL` and `repository` are all not set
|
|
78
|
+
*/
|
|
79
|
+
releaseAsarURL?: string;
|
|
80
|
+
/**
|
|
81
|
+
* whether to enable debug listener
|
|
82
|
+
*/
|
|
75
83
|
debug?: boolean;
|
|
76
|
-
|
|
84
|
+
/**
|
|
85
|
+
* custom version compare function
|
|
86
|
+
* @param oldVersion old version string
|
|
87
|
+
* @param newVersion new version string
|
|
88
|
+
* @returns whether to update
|
|
89
|
+
*/
|
|
90
|
+
compareVersion?: (oldVersion: string, newVersion: string) => boolean | Promise<boolean>;
|
|
77
91
|
downloadConfig?: {
|
|
78
92
|
/**
|
|
79
93
|
* download user agent
|
|
@@ -87,7 +101,7 @@ interface UpdaterOption extends BaseOption {
|
|
|
87
101
|
/**
|
|
88
102
|
* download JSON function
|
|
89
103
|
* @param url download url
|
|
90
|
-
* @param updater updater,
|
|
104
|
+
* @param updater updater, to trigger events
|
|
91
105
|
* @param header download header
|
|
92
106
|
* @returns `UpdateJSON`
|
|
93
107
|
*/
|
|
@@ -95,7 +109,7 @@ interface UpdaterOption extends BaseOption {
|
|
|
95
109
|
/**
|
|
96
110
|
* download buffer function
|
|
97
111
|
* @param url download url
|
|
98
|
-
* @param updater updater,
|
|
112
|
+
* @param updater updater, to trigger events
|
|
99
113
|
* @param header download header
|
|
100
114
|
* @returns `Buffer`
|
|
101
115
|
*/
|
|
@@ -133,64 +147,67 @@ declare function getGithubReleaseCdnGroup(): {
|
|
|
133
147
|
cdnPrefix: string;
|
|
134
148
|
maintainer: string;
|
|
135
149
|
}[];
|
|
150
|
+
declare function restartApp(): void;
|
|
136
151
|
|
|
137
152
|
declare function createUpdater({ SIGNATURE_PUB, repository, productName, releaseAsarURL: _release, updateJsonURL: _update, debug, downloadConfig, compareVersion, }: UpdaterOption): Updater;
|
|
138
153
|
|
|
139
|
-
|
|
154
|
+
type AppOption = {
|
|
155
|
+
/**
|
|
156
|
+
* name of your application
|
|
157
|
+
*
|
|
158
|
+
* you can use the `name` in `package.json`
|
|
159
|
+
*/
|
|
160
|
+
name: string;
|
|
140
161
|
/**
|
|
141
162
|
* path of electron output dist
|
|
142
163
|
* @default 'dist-electron'
|
|
143
|
-
|
|
164
|
+
*/
|
|
144
165
|
electronDistPath?: string;
|
|
145
166
|
/**
|
|
146
167
|
* relative path of main entry in electron dist
|
|
147
168
|
* @default 'main/index.js'
|
|
148
|
-
|
|
169
|
+
*/
|
|
149
170
|
mainPath?: string;
|
|
150
|
-
}
|
|
171
|
+
};
|
|
172
|
+
type OptionalProperty<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
|
173
|
+
type InitUpdaterOptions = OptionalProperty<UpdaterOption, 'productName'>;
|
|
151
174
|
/**
|
|
152
|
-
*
|
|
153
|
-
* @param productName name of your application
|
|
154
|
-
* @param updater updater instance or updater options
|
|
155
|
-
* @param option options for entry, will be used to generate electron main path, default target path: `dist-electron/main/index.js`
|
|
156
|
-
* @returns a function to init your application with a updater
|
|
157
|
-
*
|
|
175
|
+
* create updater manually
|
|
158
176
|
* @example
|
|
159
|
-
* **manual** generate updater
|
|
160
177
|
* ```ts
|
|
161
|
-
* import { initApp } from 'electron-incremental-
|
|
178
|
+
* import { createUpdater, getGithubReleaseCdnGroup, initApp, parseGithubCdnURL } from 'electron-incremental-update'
|
|
162
179
|
* import { name, repository } from '../package.json'
|
|
163
180
|
*
|
|
164
|
-
* const SIGNATURE_PUB = '' // auto generate
|
|
181
|
+
* const SIGNATURE_PUB = '' // auto generate
|
|
182
|
+
*
|
|
183
|
+
* const { cdnPrefix } = getGithubReleaseCdnGroup()[0]
|
|
165
184
|
* const updater = createUpdater({
|
|
166
185
|
* SIGNATURE_PUB,
|
|
167
186
|
* productName: name,
|
|
168
187
|
* repository,
|
|
188
|
+
* updateJsonURL: parseGithubCdnURL(repository, 'fastly.jsdelivr.net/gh', 'version.json'),
|
|
189
|
+
* releaseAsarURL: parseGithubCdnURL(repository, cdnPrefix, `download/latest/${name}.asar.gz`),
|
|
190
|
+
* debug: true,
|
|
169
191
|
* })
|
|
170
|
-
* initApp(name
|
|
192
|
+
* initApp({ name }).setUpdater(updater)
|
|
171
193
|
* ```
|
|
172
|
-
|
|
173
|
-
|
|
194
|
+
*/
|
|
195
|
+
declare function initApp(appOptions: AppOption): {
|
|
196
|
+
setUpdater: (updater: Updater) => void;
|
|
197
|
+
};
|
|
198
|
+
/**
|
|
199
|
+
* create updater when init, no need to set productName
|
|
174
200
|
*
|
|
201
|
+
* @example
|
|
175
202
|
* ```ts
|
|
176
|
-
* import {
|
|
203
|
+
* import { initApp } from 'electron-incremental-update'
|
|
177
204
|
* import { name, repository } from '../package.json'
|
|
178
205
|
*
|
|
179
|
-
* const SIGNATURE_PUB = '' // auto generate
|
|
206
|
+
* const SIGNATURE_PUB = '' // auto generate
|
|
180
207
|
*
|
|
181
|
-
*
|
|
182
|
-
* initApp(name, {
|
|
183
|
-
* SIGNATURE_PUB,
|
|
184
|
-
* repository,
|
|
185
|
-
* updateJsonURL: `https://cdn.jsdelivr.net/gh/${repository.replace('https://github.com', '')}/version.json`,
|
|
186
|
-
* releaseAsarURL: `${urlPrefix}/download/latest/${name}.asar.gz`,
|
|
187
|
-
* }, {
|
|
188
|
-
* // options for main entry
|
|
189
|
-
* })
|
|
208
|
+
* initApp({ name }, { SIGNATURE_PUB, repository })
|
|
190
209
|
* ```
|
|
191
|
-
*/
|
|
192
|
-
declare function initApp(
|
|
193
|
-
productName?: string;
|
|
194
|
-
}, option?: AppOption): any;
|
|
210
|
+
*/
|
|
211
|
+
declare function initApp(appOptions: AppOption, updaterOptions: InitUpdaterOptions): undefined;
|
|
195
212
|
|
|
196
|
-
export {
|
|
213
|
+
export { AppOption, CheckResultType, InitUpdaterOptions, UpdateJSON, Updater, UpdaterOption, createUpdater, getAppAsarPath, getAppVersion, getEntryVersion, getGithubReleaseCdnGroup, initApp, parseGithubCdnURL, requireNative, restartApp };
|
package/dist/index.mjs
CHANGED
|
@@ -25,17 +25,18 @@ function downloadJSONDefault(url, updater, headers) {
|
|
|
25
25
|
res.headers = headers;
|
|
26
26
|
res.on("data", (chunk) => data += chunk);
|
|
27
27
|
res.on("end", () => {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
try {
|
|
29
|
+
const json = JSON.parse(data);
|
|
30
|
+
if ("signature" in json && "version" in json && "size" in json) {
|
|
31
|
+
resolve2(json);
|
|
32
|
+
} else {
|
|
33
|
+
throw Error;
|
|
34
|
+
}
|
|
35
|
+
} catch (e) {
|
|
36
|
+
reject(new Error("invalid json"));
|
|
34
37
|
}
|
|
35
38
|
});
|
|
36
39
|
}).on("error", (e) => {
|
|
37
|
-
e && updater.emit("donwnloadError", e);
|
|
38
|
-
updater.emit("downloadEnd", false);
|
|
39
40
|
reject(e);
|
|
40
41
|
});
|
|
41
42
|
});
|
|
@@ -50,12 +51,9 @@ function downloadBufferDefault(url, updater, headers) {
|
|
|
50
51
|
data.push(chunk);
|
|
51
52
|
});
|
|
52
53
|
res.on("end", () => {
|
|
53
|
-
updater.emit("downloadEnd", true);
|
|
54
54
|
resolve2(Buffer.concat(data));
|
|
55
55
|
});
|
|
56
56
|
}).on("error", (e) => {
|
|
57
|
-
e && updater.emit("donwnloadError", e);
|
|
58
|
-
updater.emit("downloadEnd", false);
|
|
59
57
|
reject(e);
|
|
60
58
|
});
|
|
61
59
|
});
|
|
@@ -127,6 +125,10 @@ function getGithubReleaseCdnGroup() {
|
|
|
127
125
|
{ cdnPrefix: "download.nuaa.cf", maintainer: "LibraryCloud-nuaa" }
|
|
128
126
|
];
|
|
129
127
|
}
|
|
128
|
+
function restartApp() {
|
|
129
|
+
app.relaunch();
|
|
130
|
+
app.quit();
|
|
131
|
+
}
|
|
130
132
|
|
|
131
133
|
// src/updater/index.ts
|
|
132
134
|
function createUpdater({
|
|
@@ -140,9 +142,13 @@ function createUpdater({
|
|
|
140
142
|
compareVersion
|
|
141
143
|
}) {
|
|
142
144
|
const updater = new EventEmitter();
|
|
145
|
+
let signature = "";
|
|
146
|
+
let version = "";
|
|
147
|
+
const gzipPath = `../${productName}.asar.gz`;
|
|
148
|
+
const tmpFile = gzipPath.replace(".asar.gz", ".tmp.gz");
|
|
143
149
|
const { downloadBuffer, downloadJSON, extraHeader, userAgent } = downloadConfig || {};
|
|
144
|
-
function log(
|
|
145
|
-
debug &&
|
|
150
|
+
function log(msg) {
|
|
151
|
+
debug && updater.emit("debug", msg);
|
|
146
152
|
}
|
|
147
153
|
async function download(url, format) {
|
|
148
154
|
const ua = userAgent || "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36";
|
|
@@ -151,12 +157,16 @@ function createUpdater({
|
|
|
151
157
|
UserAgent: ua,
|
|
152
158
|
...extraHeader
|
|
153
159
|
};
|
|
154
|
-
log(
|
|
160
|
+
log(`download headers: ${JSON.stringify(headers, null, 2)}`);
|
|
155
161
|
const downloadFn = format === "json" ? downloadJSON ?? downloadJSONDefault : downloadBuffer ?? downloadBufferDefault;
|
|
156
|
-
|
|
162
|
+
log(`download ${format} from ${url}`);
|
|
163
|
+
const ret = await downloadFn(url, updater, headers);
|
|
164
|
+
log(`download ${format} success`);
|
|
165
|
+
return ret;
|
|
157
166
|
}
|
|
158
167
|
async function extractFile(gzipFilePath) {
|
|
159
168
|
if (!gzipFilePath.endsWith(".asar.gz") || !existsSync(gzipFilePath)) {
|
|
169
|
+
log("update .asar.gz file not exist");
|
|
160
170
|
return;
|
|
161
171
|
}
|
|
162
172
|
gzipFilePath = gzipFilePath.replace(".asar.gz", ".tmp.gz");
|
|
@@ -165,108 +175,136 @@ function createUpdater({
|
|
|
165
175
|
const input = createReadStream(gzipFilePath);
|
|
166
176
|
const outputFilePath = gzipFilePath.replace(".tmp.gz", ".asar");
|
|
167
177
|
const output = createWriteStream(outputFilePath);
|
|
168
|
-
log(
|
|
178
|
+
log(`outputFilePath: ${outputFilePath}`);
|
|
169
179
|
input.pipe(gunzip).pipe(output).on("finish", async () => {
|
|
170
180
|
await rm(gzipFilePath);
|
|
171
|
-
log(
|
|
181
|
+
log(`${gzipFilePath} unzipped`);
|
|
172
182
|
resolve2(outputFilePath);
|
|
173
183
|
}).on("error", async (err) => {
|
|
174
184
|
await rm(gzipFilePath);
|
|
175
|
-
log("[updater] error", err);
|
|
176
185
|
output.destroy(err);
|
|
177
186
|
reject(err);
|
|
178
187
|
});
|
|
179
188
|
});
|
|
180
189
|
}
|
|
181
|
-
function verify(buffer,
|
|
182
|
-
|
|
183
|
-
return createVerify("RSA-SHA256").update(buffer).verify(SIGNATURE_PUB, signature, "base64");
|
|
190
|
+
function verify(buffer, signature2) {
|
|
191
|
+
return createVerify("RSA-SHA256").update(buffer).verify(SIGNATURE_PUB, signature2, "base64");
|
|
184
192
|
}
|
|
185
|
-
function needUpdate(
|
|
186
|
-
if (!
|
|
193
|
+
function needUpdate(version2) {
|
|
194
|
+
if (!app2.isPackaged) {
|
|
195
|
+
log("in dev mode, no need to update");
|
|
187
196
|
return false;
|
|
188
197
|
}
|
|
189
198
|
const currentVersion = getEntryVersion();
|
|
190
|
-
log(
|
|
191
|
-
|
|
199
|
+
log(`check update:
|
|
200
|
+
current version is ${currentVersion},
|
|
201
|
+
new version is ${version2}`);
|
|
192
202
|
const _compare = compareVersion ?? compareVersionDefault;
|
|
193
|
-
return _compare(currentVersion,
|
|
203
|
+
return _compare(currentVersion, version2);
|
|
194
204
|
}
|
|
195
|
-
async function checkUpdate(
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
} = option || {};
|
|
200
|
-
if (!updateJsonURL || !releaseAsarURL) {
|
|
201
|
-
log("[updater] no updateJsonURL or releaseAsarURL, use repository");
|
|
205
|
+
async function checkUpdate(url) {
|
|
206
|
+
url ??= _update;
|
|
207
|
+
if (!url) {
|
|
208
|
+
log("no updateJsonURL, fallback to use repository");
|
|
202
209
|
if (!repository) {
|
|
203
|
-
throw new Error("updateJsonURL or
|
|
210
|
+
throw new Error("updateJsonURL or repository are not set");
|
|
204
211
|
}
|
|
205
|
-
|
|
206
|
-
releaseAsarURL = `${repository}/releases/download/latest/${productName}.asar.gz`;
|
|
212
|
+
url = `${repository.replace("github.com", "raw.githubusercontent.com")}/master/version.json`;
|
|
207
213
|
}
|
|
208
|
-
log("[updater] updateJsonURL", updateJsonURL);
|
|
209
|
-
log("[updater] releaseAsarURL", releaseAsarURL);
|
|
210
|
-
const gzipPath = `../${productName}.asar.gz`;
|
|
211
|
-
const tmpFile = gzipPath.replace(".asar.gz", ".tmp.gz");
|
|
212
214
|
if (existsSync(tmpFile)) {
|
|
213
|
-
log(
|
|
215
|
+
log(`remove tmp file: ${tmpFile}`);
|
|
214
216
|
await rm(tmpFile);
|
|
215
217
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
218
|
+
if (existsSync(gzipPath)) {
|
|
219
|
+
log(`remove .gz file: ${gzipPath}`);
|
|
220
|
+
await rm(gzipPath);
|
|
219
221
|
}
|
|
222
|
+
const json = await download(url, "json");
|
|
220
223
|
const {
|
|
221
|
-
signature,
|
|
222
|
-
version,
|
|
224
|
+
signature: _sig,
|
|
225
|
+
version: _v,
|
|
223
226
|
size
|
|
224
227
|
} = json;
|
|
225
|
-
log(
|
|
226
|
-
if (!needUpdate(
|
|
227
|
-
|
|
228
|
+
log(`update info: ${JSON.stringify(json, null, 2)}`);
|
|
229
|
+
if (!await needUpdate(_v)) {
|
|
230
|
+
log(`update unavailable: ${_v}`);
|
|
231
|
+
return false;
|
|
232
|
+
} else {
|
|
233
|
+
log(`update available: ${_v}`);
|
|
234
|
+
signature = _sig;
|
|
235
|
+
version = _v;
|
|
236
|
+
return { size, version };
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
async function downloadUpdate(src) {
|
|
240
|
+
if (typeof src !== "object") {
|
|
241
|
+
let _url = src ?? _release;
|
|
242
|
+
if (!_url) {
|
|
243
|
+
log("no releaseAsarURL, fallback to use repository");
|
|
244
|
+
if (!repository) {
|
|
245
|
+
throw new Error("releaseAsarURL or repository are not set");
|
|
246
|
+
}
|
|
247
|
+
_url = `${repository}/releases/download/latest/${productName}.asar.gz`;
|
|
248
|
+
}
|
|
249
|
+
src = await download(_url, "buffer");
|
|
228
250
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
throw new Error("file broken, invalid signature!");
|
|
251
|
+
log("verify start");
|
|
252
|
+
if (!verify(src, signature)) {
|
|
253
|
+
log("verify failed");
|
|
254
|
+
throw new Error("invalid signature");
|
|
234
255
|
}
|
|
235
|
-
log("
|
|
236
|
-
|
|
237
|
-
|
|
256
|
+
log("verify success");
|
|
257
|
+
log(`write file: ${gzipPath}`);
|
|
258
|
+
await writeFile(gzipPath, src);
|
|
259
|
+
log(`extract file: ${gzipPath}`);
|
|
238
260
|
await extractFile(gzipPath);
|
|
239
|
-
|
|
261
|
+
log(`update success, version: ${version}`);
|
|
262
|
+
updater.emit("downloaded");
|
|
240
263
|
}
|
|
241
|
-
const onCheck = async (
|
|
264
|
+
const onCheck = async (url) => {
|
|
242
265
|
try {
|
|
243
|
-
const result = await checkUpdate(
|
|
266
|
+
const result = await checkUpdate(url);
|
|
244
267
|
updater.emit("checkResult", result);
|
|
245
268
|
} catch (error) {
|
|
246
|
-
|
|
269
|
+
log(error);
|
|
270
|
+
updater.emit("checkResult", error);
|
|
247
271
|
}
|
|
248
272
|
};
|
|
249
273
|
updater.on("check", onCheck);
|
|
250
274
|
updater.checkUpdate = onCheck;
|
|
275
|
+
const onDownload = async (src) => {
|
|
276
|
+
try {
|
|
277
|
+
await downloadUpdate(src);
|
|
278
|
+
} catch (error) {
|
|
279
|
+
log(error);
|
|
280
|
+
updater.emit("donwnloadError", error);
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
updater.on("download", onDownload);
|
|
284
|
+
updater.downloadUpdate = onDownload;
|
|
251
285
|
return updater;
|
|
252
286
|
}
|
|
253
287
|
|
|
254
288
|
// src/index.ts
|
|
255
|
-
function initApp(
|
|
289
|
+
function initApp(appOptions, updaterOptions) {
|
|
256
290
|
const {
|
|
291
|
+
name: productName,
|
|
257
292
|
electronDistPath = "dist-electron",
|
|
258
293
|
mainPath = "main/index.js"
|
|
259
|
-
} =
|
|
294
|
+
} = appOptions ?? {};
|
|
260
295
|
const mainDir = app3.isPackaged ? `../${productName}.asar` : electronDistPath;
|
|
261
296
|
const entry = resolve(__dirname, mainDir, mainPath);
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
297
|
+
if (updaterOptions) {
|
|
298
|
+
__require(entry)(
|
|
299
|
+
createUpdater({ ...updaterOptions, productName })
|
|
300
|
+
);
|
|
266
301
|
} else {
|
|
267
|
-
|
|
302
|
+
return {
|
|
303
|
+
setUpdater(updater) {
|
|
304
|
+
__require(entry)(updater);
|
|
305
|
+
}
|
|
306
|
+
};
|
|
268
307
|
}
|
|
269
|
-
return __require(entry)(_updater);
|
|
270
308
|
}
|
|
271
309
|
export {
|
|
272
310
|
createUpdater,
|
|
@@ -276,5 +314,6 @@ export {
|
|
|
276
314
|
getGithubReleaseCdnGroup,
|
|
277
315
|
initApp,
|
|
278
316
|
parseGithubCdnURL,
|
|
279
|
-
requireNative
|
|
317
|
+
requireNative,
|
|
318
|
+
restartApp
|
|
280
319
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "electron-incremental-update",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "electron incremental update tools, powered by vite",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "tsup",
|
|
@@ -63,4 +63,4 @@
|
|
|
63
63
|
"dependencies": {
|
|
64
64
|
"ci-info": "^3.8.0"
|
|
65
65
|
}
|
|
66
|
-
}
|
|
66
|
+
}
|