electron-incremental-update 0.5.1 → 0.6.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 +7 -7
- package/dist/{chunk-OBERMV66.mjs → chunk-VADH6AZA.mjs} +12 -21
- package/dist/index.cjs +128 -102
- package/dist/index.d.ts +26 -15
- package/dist/index.mjs +118 -94
- package/dist/vite.cjs +71 -48
- package/dist/vite.d.ts +24 -2
- package/dist/vite.mjs +63 -32
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -58,15 +58,15 @@ src
|
|
|
58
58
|
import { createUpdater, getGithubReleaseCdnGroup, initApp, parseGithubCdnURL } from 'electron-incremental-update'
|
|
59
59
|
import { name, repository } from '../package.json'
|
|
60
60
|
|
|
61
|
-
const
|
|
61
|
+
const SIGNATURE_CERT = '' // auto generate certificate when start app
|
|
62
62
|
|
|
63
63
|
// create updater when init, no need to set productName
|
|
64
|
-
initApp({ name }, {
|
|
64
|
+
initApp({ name }, { SIGNATURE_CERT, repository })
|
|
65
65
|
|
|
66
66
|
// or create updater manually
|
|
67
67
|
const { cdnPrefix } = getGithubReleaseCdnGroup()[0]
|
|
68
68
|
const updater = createUpdater({
|
|
69
|
-
|
|
69
|
+
SIGNATURE_CERT,
|
|
70
70
|
productName: name,
|
|
71
71
|
repository,
|
|
72
72
|
updateJsonURL: parseGithubCdnURL(repository, 'fastly.jsdelivr.net/gh', 'version.json'),
|
|
@@ -81,15 +81,15 @@ initApp({ name }).setUpdater(updater)
|
|
|
81
81
|
```ts
|
|
82
82
|
// electron/main/index.ts
|
|
83
83
|
import type { Updater } from 'electron-incremental-update'
|
|
84
|
-
import {
|
|
84
|
+
import { getEntryVersion, getProductAsarPath, getProductVersion } from 'electron-incremental-update'
|
|
85
85
|
import { app } from 'electron'
|
|
86
86
|
import { name } from '../../package.json'
|
|
87
87
|
|
|
88
88
|
export default function (updater: Updater) {
|
|
89
89
|
console.log('\ncurrent:')
|
|
90
|
-
console.log(`\tasar path: ${
|
|
90
|
+
console.log(`\tasar path: ${getProductAsarPath(name)}`)
|
|
91
91
|
console.log(`\tentry: ${getEntryVersion()}`)
|
|
92
|
-
console.log(`\tapp: ${
|
|
92
|
+
console.log(`\tapp: ${getProductVersion(name)}`)
|
|
93
93
|
let size = 0
|
|
94
94
|
updater.on('downloading', (progress) => {
|
|
95
95
|
console.log(`${(progress / size).toFixed(2)}%`)
|
|
@@ -108,7 +108,7 @@ export default function (updater: Updater) {
|
|
|
108
108
|
buttons: ['Download', 'Later'],
|
|
109
109
|
message: 'Application update available!',
|
|
110
110
|
})
|
|
111
|
-
response === 0 && console.log(await updater.
|
|
111
|
+
response === 0 && console.log(await updater.downloadAndInstall())
|
|
112
112
|
}
|
|
113
113
|
})
|
|
114
114
|
// app logics
|
|
@@ -7,46 +7,38 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
7
7
|
});
|
|
8
8
|
|
|
9
9
|
// src/crypto.ts
|
|
10
|
-
import { constants, createCipheriv, createDecipheriv, createHash, createSign, createVerify
|
|
10
|
+
import { constants, createCipheriv, createDecipheriv, createHash, createSign, createVerify } from "node:crypto";
|
|
11
11
|
import { Buffer as Buffer2 } from "node:buffer";
|
|
12
12
|
var aesEncode = "base64url";
|
|
13
|
-
function
|
|
14
|
-
const
|
|
15
|
-
const privateKey = pair.privateKey.export({ type: "pkcs1", format: "pem" });
|
|
16
|
-
const publicKey = pair.publicKey.export({ type: "pkcs1", format: "pem" });
|
|
17
|
-
return {
|
|
18
|
-
privateKey,
|
|
19
|
-
publicKey
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
function encrypt(plainText, key, iv) {
|
|
23
|
-
const cipher = createCipheriv("aes-256-cbc", key, iv);
|
|
13
|
+
function encrypt(plainText, key2, iv) {
|
|
14
|
+
const cipher = createCipheriv("aes-256-cbc", key2, iv);
|
|
24
15
|
let encrypted = cipher.update(plainText, "utf8", aesEncode);
|
|
25
16
|
encrypted += cipher.final(aesEncode);
|
|
26
17
|
return encrypted;
|
|
27
18
|
}
|
|
28
|
-
function decrypt(encryptedText,
|
|
29
|
-
const decipher = createDecipheriv("aes-256-cbc",
|
|
19
|
+
function decrypt(encryptedText, key2, iv) {
|
|
20
|
+
const decipher = createDecipheriv("aes-256-cbc", key2, iv);
|
|
30
21
|
let decrypted = decipher.update(encryptedText, aesEncode, "utf8");
|
|
31
22
|
decrypted += decipher.final("utf8");
|
|
32
23
|
return decrypted;
|
|
33
24
|
}
|
|
34
|
-
function
|
|
25
|
+
function key(data, length) {
|
|
35
26
|
const hash = createHash("SHA256").update(data).digest("binary");
|
|
36
27
|
return Buffer2.from(hash).subarray(0, length);
|
|
37
28
|
}
|
|
38
|
-
function signature(buffer, privateKey,
|
|
29
|
+
function signature(buffer, privateKey, cert, version) {
|
|
39
30
|
const sig = createSign("RSA-SHA256").update(buffer).sign({
|
|
40
31
|
key: privateKey,
|
|
41
32
|
padding: constants.RSA_PKCS1_PADDING,
|
|
42
33
|
saltLength: constants.RSA_PSS_SALTLEN_DIGEST
|
|
43
34
|
}, "base64");
|
|
44
|
-
return encrypt(sig
|
|
35
|
+
return encrypt(`${sig}%${version}`, key(cert, 32), key(buffer, 16));
|
|
45
36
|
}
|
|
46
|
-
function verify(buffer, signature2,
|
|
37
|
+
function verify(buffer, signature2, cert) {
|
|
47
38
|
try {
|
|
48
|
-
const sig = decrypt(signature2,
|
|
49
|
-
|
|
39
|
+
const [sig, version] = decrypt(signature2, key(cert, 32), key(buffer, 16)).split("%");
|
|
40
|
+
const result = createVerify("RSA-SHA256").update(buffer).verify(cert, sig, "base64");
|
|
41
|
+
return result ? version : false;
|
|
50
42
|
} catch (error) {
|
|
51
43
|
return false;
|
|
52
44
|
}
|
|
@@ -54,7 +46,6 @@ function verify(buffer, signature2, publicKey) {
|
|
|
54
46
|
|
|
55
47
|
export {
|
|
56
48
|
__require,
|
|
57
|
-
generateRSA,
|
|
58
49
|
signature,
|
|
59
50
|
verify
|
|
60
51
|
};
|
package/dist/index.cjs
CHANGED
|
@@ -11,9 +11,9 @@ var __export = (target, all) => {
|
|
|
11
11
|
};
|
|
12
12
|
var __copyProps = (to, from, except, desc) => {
|
|
13
13
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
-
for (let
|
|
15
|
-
if (!__hasOwnProp.call(to,
|
|
16
|
-
__defProp(to,
|
|
14
|
+
for (let key2 of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key2) && key2 !== except)
|
|
16
|
+
__defProp(to, key2, { get: () => from[key2], enumerable: !(desc = __getOwnPropDesc(from, key2)) || desc.enumerable });
|
|
17
17
|
}
|
|
18
18
|
return to;
|
|
19
19
|
};
|
|
@@ -31,44 +31,48 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var src_exports = {};
|
|
32
32
|
__export(src_exports, {
|
|
33
33
|
createUpdater: () => createUpdater,
|
|
34
|
-
getAppAsarPath: () => getAppAsarPath,
|
|
35
|
-
getAppVersion: () => getAppVersion,
|
|
36
34
|
getEntryVersion: () => getEntryVersion,
|
|
37
35
|
getGithubReleaseCdnGroup: () => getGithubReleaseCdnGroup,
|
|
36
|
+
getProductAsarPath: () => getProductAsarPath,
|
|
37
|
+
getProductVersion: () => getProductVersion,
|
|
38
38
|
initApp: () => initApp,
|
|
39
|
+
isUpdateJSON: () => isUpdateJSON,
|
|
39
40
|
parseGithubCdnURL: () => parseGithubCdnURL,
|
|
40
41
|
requireNative: () => requireNative,
|
|
41
42
|
restartApp: () => restartApp
|
|
42
43
|
});
|
|
43
44
|
module.exports = __toCommonJS(src_exports);
|
|
44
|
-
var
|
|
45
|
+
var import_node_path3 = require("path");
|
|
45
46
|
var import_electron3 = require("electron");
|
|
46
47
|
|
|
47
48
|
// src/updater/index.ts
|
|
48
49
|
var import_node_events = require("events");
|
|
50
|
+
var import_node_buffer3 = require("buffer");
|
|
49
51
|
var import_node_zlib = require("zlib");
|
|
50
52
|
var import_node_fs2 = require("fs");
|
|
51
53
|
var import_promises = require("fs/promises");
|
|
54
|
+
var import_node_path2 = require("path");
|
|
52
55
|
var import_electron2 = require("electron");
|
|
53
56
|
|
|
54
57
|
// src/crypto.ts
|
|
55
58
|
var import_node_crypto = require("crypto");
|
|
56
59
|
var import_node_buffer = require("buffer");
|
|
57
60
|
var aesEncode = "base64url";
|
|
58
|
-
function decrypt(encryptedText,
|
|
59
|
-
const decipher = (0, import_node_crypto.createDecipheriv)("aes-256-cbc",
|
|
61
|
+
function decrypt(encryptedText, key2, iv) {
|
|
62
|
+
const decipher = (0, import_node_crypto.createDecipheriv)("aes-256-cbc", key2, iv);
|
|
60
63
|
let decrypted = decipher.update(encryptedText, aesEncode, "utf8");
|
|
61
64
|
decrypted += decipher.final("utf8");
|
|
62
65
|
return decrypted;
|
|
63
66
|
}
|
|
64
|
-
function
|
|
67
|
+
function key(data, length) {
|
|
65
68
|
const hash = (0, import_node_crypto.createHash)("SHA256").update(data).digest("binary");
|
|
66
69
|
return import_node_buffer.Buffer.from(hash).subarray(0, length);
|
|
67
70
|
}
|
|
68
|
-
function verify(buffer, signature,
|
|
71
|
+
function verify(buffer, signature, cert) {
|
|
69
72
|
try {
|
|
70
|
-
const sig = decrypt(signature,
|
|
71
|
-
|
|
73
|
+
const [sig, version] = decrypt(signature, key(cert, 32), key(buffer, 16)).split("%");
|
|
74
|
+
const result = (0, import_node_crypto.createVerify)("RSA-SHA256").update(buffer).verify(cert, sig, "base64");
|
|
75
|
+
return result ? version : false;
|
|
72
76
|
} catch (error) {
|
|
73
77
|
return false;
|
|
74
78
|
}
|
|
@@ -77,8 +81,15 @@ function verify(buffer, signature, publicKey) {
|
|
|
77
81
|
// src/updater/defaultFunctions.ts
|
|
78
82
|
var import_node_buffer2 = require("buffer");
|
|
79
83
|
var import_node_https = __toESM(require("https"), 1);
|
|
84
|
+
|
|
85
|
+
// src/updater/types.ts
|
|
86
|
+
function isUpdateJSON(json) {
|
|
87
|
+
return "signature" in json && "version" in json && "size" in json;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/updater/defaultFunctions.ts
|
|
80
91
|
function downloadJSONDefault(url, updater, headers) {
|
|
81
|
-
return new Promise((
|
|
92
|
+
return new Promise((resolve3, reject) => {
|
|
82
93
|
import_node_https.default.get(url, (res) => {
|
|
83
94
|
let data = "";
|
|
84
95
|
res.setEncoding("utf8");
|
|
@@ -87,8 +98,8 @@ function downloadJSONDefault(url, updater, headers) {
|
|
|
87
98
|
res.on("end", () => {
|
|
88
99
|
try {
|
|
89
100
|
const json = JSON.parse(data);
|
|
90
|
-
if (
|
|
91
|
-
|
|
101
|
+
if (isUpdateJSON(json)) {
|
|
102
|
+
resolve3(json);
|
|
92
103
|
} else {
|
|
93
104
|
throw Error;
|
|
94
105
|
}
|
|
@@ -103,7 +114,7 @@ function downloadJSONDefault(url, updater, headers) {
|
|
|
103
114
|
}
|
|
104
115
|
function downloadBufferDefault(url, updater, headers) {
|
|
105
116
|
let progress = 0;
|
|
106
|
-
return new Promise((
|
|
117
|
+
return new Promise((resolve3, reject) => {
|
|
107
118
|
import_node_https.default.get(url, (res) => {
|
|
108
119
|
let data = [];
|
|
109
120
|
res.headers = headers;
|
|
@@ -113,7 +124,7 @@ function downloadBufferDefault(url, updater, headers) {
|
|
|
113
124
|
data.push(chunk);
|
|
114
125
|
});
|
|
115
126
|
res.on("end", () => {
|
|
116
|
-
|
|
127
|
+
resolve3(import_node_buffer2.Buffer.concat(data));
|
|
117
128
|
});
|
|
118
129
|
}).on("error", (e) => {
|
|
119
130
|
reject(e);
|
|
@@ -147,14 +158,14 @@ function compareVersionDefault(oldVersion, newVersion) {
|
|
|
147
158
|
var import_node_fs = require("fs");
|
|
148
159
|
var import_node_path = require("path");
|
|
149
160
|
var import_electron = require("electron");
|
|
150
|
-
function
|
|
161
|
+
function getProductAsarPath(name) {
|
|
151
162
|
return import_electron.app.isPackaged ? (0, import_node_path.join)((0, import_node_path.dirname)(import_electron.app.getAppPath()), `${name}.asar`) : "dev";
|
|
152
163
|
}
|
|
153
164
|
function getEntryVersion() {
|
|
154
165
|
return import_electron.app.getVersion();
|
|
155
166
|
}
|
|
156
|
-
function
|
|
157
|
-
return import_electron.app.isPackaged ? (0, import_node_fs.readFileSync)((0, import_node_path.join)(
|
|
167
|
+
function getProductVersion(name) {
|
|
168
|
+
return import_electron.app.isPackaged ? (0, import_node_fs.readFileSync)((0, import_node_path.join)(getProductAsarPath(name), "version"), "utf-8") : getEntryVersion();
|
|
158
169
|
}
|
|
159
170
|
function requireNative(packageName) {
|
|
160
171
|
const path = import_electron.app.isPackaged ? (0, import_node_path.join)(import_electron.app.getAppPath(), "node_modules", packageName) : packageName;
|
|
@@ -194,7 +205,7 @@ function restartApp() {
|
|
|
194
205
|
|
|
195
206
|
// src/updater/index.ts
|
|
196
207
|
function createUpdater({
|
|
197
|
-
|
|
208
|
+
SIGNATURE_CERT,
|
|
198
209
|
repository,
|
|
199
210
|
productName,
|
|
200
211
|
releaseAsarURL: _release,
|
|
@@ -205,94 +216,103 @@ function createUpdater({
|
|
|
205
216
|
}) {
|
|
206
217
|
const updater = new import_node_events.EventEmitter();
|
|
207
218
|
let signature = "";
|
|
208
|
-
|
|
209
|
-
const gzipPath =
|
|
210
|
-
const
|
|
219
|
+
const asarPath = getProductAsarPath(productName);
|
|
220
|
+
const gzipPath = `${asarPath}.gz`;
|
|
221
|
+
const tmpFilePath = gzipPath.replace(".asar.gz", ".tmp.asar");
|
|
211
222
|
const { downloadBuffer, downloadJSON, extraHeader, userAgent } = downloadConfig || {};
|
|
212
223
|
function log(msg) {
|
|
213
224
|
debug && updater.emit("debug", msg);
|
|
214
225
|
}
|
|
215
|
-
async function
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
Accept: `application/${format === "json" ? "json" : "octet-stream"}`,
|
|
219
|
-
UserAgent: ua,
|
|
220
|
-
...extraHeader
|
|
221
|
-
};
|
|
222
|
-
log(`download headers: ${JSON.stringify(headers, null, 2)}`);
|
|
223
|
-
const downloadFn = format === "json" ? downloadJSON ?? downloadJSONDefault : downloadBuffer ?? downloadBufferDefault;
|
|
224
|
-
log(`download ${format} from ${url}`);
|
|
225
|
-
const ret = await downloadFn(url, updater, headers);
|
|
226
|
-
log(`download ${format} success`);
|
|
227
|
-
return ret;
|
|
228
|
-
}
|
|
229
|
-
async function extractFile(gzipFilePath) {
|
|
230
|
-
if (!gzipFilePath.endsWith(".asar.gz") || !(0, import_node_fs2.existsSync)(gzipFilePath)) {
|
|
231
|
-
log("update .asar.gz file not exist");
|
|
232
|
-
return;
|
|
226
|
+
async function extractFile() {
|
|
227
|
+
if (!gzipPath.endsWith(".asar.gz") || !(0, import_node_fs2.existsSync)(gzipPath)) {
|
|
228
|
+
throw new Error(".asar.gz file not exist");
|
|
233
229
|
}
|
|
234
|
-
|
|
235
|
-
return new Promise((resolve2, reject) => {
|
|
230
|
+
return new Promise((resolve3, reject) => {
|
|
236
231
|
const gunzip = (0, import_node_zlib.createGunzip)();
|
|
237
|
-
const input = (0, import_node_fs2.createReadStream)(
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
log(`outputFilePath: ${outputFilePath}`);
|
|
232
|
+
const input = (0, import_node_fs2.createReadStream)(gzipPath);
|
|
233
|
+
const output = (0, import_node_fs2.createWriteStream)(tmpFilePath);
|
|
234
|
+
log(`outputFilePath: ${tmpFilePath}`);
|
|
241
235
|
input.pipe(gunzip).pipe(output).on("finish", async () => {
|
|
242
|
-
await (0, import_promises.rm)(
|
|
243
|
-
log(`${
|
|
244
|
-
|
|
236
|
+
await (0, import_promises.rm)(gzipPath);
|
|
237
|
+
log(`${gzipPath} unzipped`);
|
|
238
|
+
resolve3(null);
|
|
245
239
|
}).on("error", async (err) => {
|
|
246
|
-
await (0, import_promises.rm)(
|
|
240
|
+
await (0, import_promises.rm)(gzipPath);
|
|
247
241
|
output.destroy(err);
|
|
248
242
|
reject(err);
|
|
249
243
|
});
|
|
250
244
|
});
|
|
251
245
|
}
|
|
252
|
-
function needUpdate(
|
|
246
|
+
function needUpdate(version) {
|
|
253
247
|
if (!import_electron2.app.isPackaged) {
|
|
254
248
|
log("in dev mode, no need to update");
|
|
255
249
|
return false;
|
|
256
250
|
}
|
|
257
251
|
const currentVersion = getEntryVersion();
|
|
258
|
-
log(`check update:
|
|
259
|
-
current version is ${currentVersion},
|
|
260
|
-
new version is ${version2}`);
|
|
252
|
+
log(`check update: current version is ${currentVersion}, new version is ${version}`);
|
|
261
253
|
const _compare = compareVersion ?? compareVersionDefault;
|
|
262
|
-
return _compare(currentVersion,
|
|
254
|
+
return _compare(currentVersion, version);
|
|
263
255
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
256
|
+
async function parseData(format, data) {
|
|
257
|
+
if ((0, import_node_fs2.existsSync)(tmpFilePath)) {
|
|
258
|
+
log(`remove tmp file: ${tmpFilePath}`);
|
|
259
|
+
await (0, import_promises.rm)(tmpFilePath);
|
|
260
|
+
}
|
|
261
|
+
if ((0, import_node_fs2.existsSync)(gzipPath)) {
|
|
262
|
+
log(`remove .gz file: ${gzipPath}`);
|
|
263
|
+
await (0, import_promises.rm)(gzipPath);
|
|
264
|
+
}
|
|
265
|
+
if (typeof data === "object") {
|
|
266
|
+
if (format === "json" && isUpdateJSON(data) || format === "buffer" && import_node_buffer3.Buffer.isBuffer(data)) {
|
|
267
|
+
return data;
|
|
268
|
+
} else {
|
|
269
|
+
throw new Error(`invalid type at format '${format}': ${data}`);
|
|
270
|
+
}
|
|
271
|
+
} else if (["string", "undefined"].includes(typeof data)) {
|
|
272
|
+
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";
|
|
273
|
+
const headers = {
|
|
274
|
+
Accept: `application/${format === "json" ? "json" : "octet-stream"}`,
|
|
275
|
+
UserAgent: ua,
|
|
276
|
+
...extraHeader
|
|
277
|
+
};
|
|
278
|
+
log(`download headers: ${JSON.stringify(headers, null, 2)}`);
|
|
279
|
+
const info = format === "json" ? {
|
|
280
|
+
name: "updateJsonURL",
|
|
281
|
+
url: _update,
|
|
282
|
+
repoFallback: `${repository.replace("github.com", "raw.githubusercontent.com")}/master/version.json`,
|
|
283
|
+
fn: downloadJSON ?? downloadJSONDefault
|
|
284
|
+
} : {
|
|
285
|
+
name: "releaseAsarURL",
|
|
286
|
+
url: _release,
|
|
287
|
+
repoFallback: `${repository}/releases/download/latest/${productName}.asar.gz`,
|
|
288
|
+
fn: downloadBuffer ?? downloadBufferDefault
|
|
289
|
+
};
|
|
290
|
+
data ??= info.url;
|
|
291
|
+
if (!data) {
|
|
292
|
+
log(`no ${info.name}, fallback to use repository`);
|
|
269
293
|
if (!repository) {
|
|
270
|
-
throw new Error(
|
|
294
|
+
throw new Error(`${info.name} or repository are not set`);
|
|
271
295
|
}
|
|
272
|
-
|
|
296
|
+
data = info.repoFallback;
|
|
273
297
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
log(`update info: ${JSON.stringify(json, null, 2)}`);
|
|
289
|
-
if (!await needUpdate(_v)) {
|
|
290
|
-
log(`update unavailable: ${_v}`);
|
|
298
|
+
log(`download ${format} from ${data}`);
|
|
299
|
+
const ret = await info.fn(data, updater, headers);
|
|
300
|
+
log(`download ${format} success`);
|
|
301
|
+
return ret;
|
|
302
|
+
} else {
|
|
303
|
+
throw new Error(`invalid type at format '${format}': ${data}`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
updater.checkUpdate = async (data) => {
|
|
307
|
+
try {
|
|
308
|
+
const { signature: _sig, size, version } = await parseData("json", data);
|
|
309
|
+
log(`checked version: ${version}, size: ${size}`);
|
|
310
|
+
if (!await needUpdate(version)) {
|
|
311
|
+
log(`update unavailable: ${version}`);
|
|
291
312
|
return void 0;
|
|
292
313
|
} else {
|
|
293
|
-
log(`update available: ${
|
|
314
|
+
log(`update available: ${version}`);
|
|
294
315
|
signature = _sig;
|
|
295
|
-
version = _v;
|
|
296
316
|
return { size, version };
|
|
297
317
|
}
|
|
298
318
|
} catch (error) {
|
|
@@ -300,30 +320,35 @@ function createUpdater({
|
|
|
300
320
|
return error;
|
|
301
321
|
}
|
|
302
322
|
};
|
|
303
|
-
updater.
|
|
323
|
+
updater.downloadAndInstall = async (data, sig) => {
|
|
304
324
|
try {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
log("no releaseAsarURL, fallback to use repository");
|
|
309
|
-
if (!repository) {
|
|
310
|
-
throw new Error("releaseAsarURL or repository are not set");
|
|
311
|
-
}
|
|
312
|
-
_url = `${repository}/releases/download/latest/${productName}.asar.gz`;
|
|
313
|
-
}
|
|
314
|
-
src = await download(_url, "buffer");
|
|
325
|
+
const _sig = sig ?? signature;
|
|
326
|
+
if (!_sig) {
|
|
327
|
+
throw new Error("signature are not set, please checkUpdate first or set the second parameter");
|
|
315
328
|
}
|
|
329
|
+
const buffer = await parseData("buffer", data);
|
|
316
330
|
log("verify start");
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
throw new Error("invalid signature");
|
|
331
|
+
const version = verify(buffer, _sig, SIGNATURE_CERT);
|
|
332
|
+
if (!version) {
|
|
333
|
+
throw new Error("verify failed, invalid signature");
|
|
320
334
|
}
|
|
321
335
|
log("verify success");
|
|
336
|
+
if (!await needUpdate(version)) {
|
|
337
|
+
throw new Error(`update unavailable: ${version}`);
|
|
338
|
+
}
|
|
322
339
|
log(`write file: ${gzipPath}`);
|
|
323
|
-
await (0, import_promises.writeFile)(gzipPath,
|
|
340
|
+
await (0, import_promises.writeFile)(gzipPath, buffer);
|
|
324
341
|
log(`extract file: ${gzipPath}`);
|
|
325
|
-
await extractFile(
|
|
342
|
+
await extractFile();
|
|
343
|
+
const asarVersion = await (0, import_promises.readFile)((0, import_node_path2.resolve)(tmpFilePath, "version"), "utf8");
|
|
344
|
+
if (asarVersion !== version) {
|
|
345
|
+
(0, import_node_fs2.rmSync)(tmpFilePath);
|
|
346
|
+
throw new Error(`update failed: asar version is ${asarVersion}, but it should be ${version}`);
|
|
347
|
+
} else {
|
|
348
|
+
await (0, import_promises.rename)(tmpFilePath, asarPath);
|
|
349
|
+
}
|
|
326
350
|
log(`update success, version: ${version}`);
|
|
351
|
+
signature = "";
|
|
327
352
|
return true;
|
|
328
353
|
} catch (error) {
|
|
329
354
|
log(error);
|
|
@@ -341,7 +366,7 @@ function initApp(appOptions, updaterOptions) {
|
|
|
341
366
|
mainPath = "main/index.js"
|
|
342
367
|
} = appOptions ?? {};
|
|
343
368
|
const mainDir = import_electron3.app.isPackaged ? `../${productName}.asar` : electronDistPath;
|
|
344
|
-
const entry = (0,
|
|
369
|
+
const entry = (0, import_node_path3.resolve)(__dirname, mainDir, mainPath);
|
|
345
370
|
if (updaterOptions) {
|
|
346
371
|
require(entry)(
|
|
347
372
|
createUpdater({ ...updaterOptions, productName })
|
|
@@ -357,11 +382,12 @@ function initApp(appOptions, updaterOptions) {
|
|
|
357
382
|
// Annotate the CommonJS export names for ESM import in node:
|
|
358
383
|
0 && (module.exports = {
|
|
359
384
|
createUpdater,
|
|
360
|
-
getAppAsarPath,
|
|
361
|
-
getAppVersion,
|
|
362
385
|
getEntryVersion,
|
|
363
386
|
getGithubReleaseCdnGroup,
|
|
387
|
+
getProductAsarPath,
|
|
388
|
+
getProductVersion,
|
|
364
389
|
initApp,
|
|
390
|
+
isUpdateJSON,
|
|
365
391
|
parseGithubCdnURL,
|
|
366
392
|
requireNative,
|
|
367
393
|
restartApp
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Buffer } from 'node:buffer';
|
|
2
2
|
|
|
3
3
|
type CheckResultType = Omit<UpdateJSON, 'signature'> | undefined | Error;
|
|
4
|
-
type
|
|
4
|
+
type InstallResult = true | Error;
|
|
5
5
|
type UpdateEvents = {
|
|
6
6
|
downloading: [progress: number];
|
|
7
7
|
debug: [msg: string | Error];
|
|
@@ -11,25 +11,36 @@ type UpdateJSON = {
|
|
|
11
11
|
version: string;
|
|
12
12
|
size: number;
|
|
13
13
|
};
|
|
14
|
+
declare function isUpdateJSON(json: any): json is UpdateJSON;
|
|
14
15
|
type MaybeArray<T> = T extends undefined | null | never ? [] : T extends any[] ? T['length'] extends 1 ? [data: T[0]] : T : [data: T];
|
|
15
16
|
interface TypedUpdater<T extends Record<string | symbol, MaybeArray<any>>, Event extends Exclude<keyof T, number> = Exclude<keyof T, number>> {
|
|
16
17
|
removeAllListeners<E extends Event>(event?: E): this;
|
|
17
18
|
listeners<E extends Event>(eventName: E): Function[];
|
|
18
19
|
eventNames(): (Event)[];
|
|
19
20
|
on<E extends Event>(eventName: E, listener: (...data: MaybeArray<T[E]>) => void): this;
|
|
21
|
+
once<E extends Event>(eventName: E, listener: (...data: MaybeArray<T[E]>) => void): this;
|
|
20
22
|
emit<E extends Event>(eventName: E, ...args: MaybeArray<T[E]>): boolean;
|
|
21
23
|
off<E extends Event>(eventName: E, listener: (...args: MaybeArray<T[E]>) => void): this;
|
|
22
24
|
/**
|
|
25
|
+
* check update info
|
|
26
|
+
* @param data update json url
|
|
27
|
+
* @returns
|
|
23
28
|
* - `{size: number, version: string}`: available
|
|
24
29
|
* - `false`: unavailable
|
|
25
30
|
* - `Error`: fail
|
|
26
31
|
*/
|
|
27
|
-
checkUpdate(
|
|
32
|
+
checkUpdate(data?: string | UpdateJSON): Promise<CheckResultType>;
|
|
28
33
|
/**
|
|
34
|
+
* download update and install
|
|
35
|
+
*
|
|
36
|
+
* if you want to update **offline**, you can set both `src` and `sig` to verify and install
|
|
37
|
+
* @param data asar download url or buffer
|
|
38
|
+
* @param sig signature
|
|
39
|
+
* @returns
|
|
29
40
|
* - `true`: success
|
|
30
41
|
* - `Error`: fail
|
|
31
42
|
*/
|
|
32
|
-
|
|
43
|
+
downloadAndInstall(data?: string | Buffer, sig?: string): Promise<InstallResult>;
|
|
33
44
|
}
|
|
34
45
|
type Updater = TypedUpdater<UpdateEvents>;
|
|
35
46
|
interface UpdaterOption {
|
|
@@ -40,15 +51,15 @@ interface UpdaterOption {
|
|
|
40
51
|
* @example
|
|
41
52
|
* ```ts
|
|
42
53
|
* // auto filled by plugin
|
|
43
|
-
* const
|
|
54
|
+
* const SIGNATURE_CERT = ''
|
|
44
55
|
*
|
|
45
56
|
* const updater = createUpdater({
|
|
46
|
-
*
|
|
57
|
+
* SIGNATURE_CERT,
|
|
47
58
|
* ...
|
|
48
59
|
* })
|
|
49
60
|
* ```
|
|
50
61
|
*/
|
|
51
|
-
|
|
62
|
+
SIGNATURE_CERT: string;
|
|
52
63
|
/**
|
|
53
64
|
* name of your application
|
|
54
65
|
*
|
|
@@ -86,7 +97,7 @@ interface UpdaterOption {
|
|
|
86
97
|
* @param newVersion new version string
|
|
87
98
|
* @returns whether to update
|
|
88
99
|
*/
|
|
89
|
-
compareVersion?: (oldVersion: string, newVersion: string) => boolean
|
|
100
|
+
compareVersion?: (oldVersion: string, newVersion: string) => boolean;
|
|
90
101
|
downloadConfig?: {
|
|
91
102
|
/**
|
|
92
103
|
* download user agent
|
|
@@ -120,7 +131,7 @@ interface UpdaterOption {
|
|
|
120
131
|
* get the application asar absolute path
|
|
121
132
|
* @param name The name of the application
|
|
122
133
|
*/
|
|
123
|
-
declare function
|
|
134
|
+
declare function getProductAsarPath(name: string): string;
|
|
124
135
|
/**
|
|
125
136
|
* get the version of entry (app.asar)
|
|
126
137
|
*/
|
|
@@ -129,7 +140,7 @@ declare function getEntryVersion(): string;
|
|
|
129
140
|
* get the version of application (name.asar)
|
|
130
141
|
* @param name - The name of the application
|
|
131
142
|
*/
|
|
132
|
-
declare function
|
|
143
|
+
declare function getProductVersion(name: string): string;
|
|
133
144
|
/**
|
|
134
145
|
* require native package from app.asar
|
|
135
146
|
* @param packageName native package name
|
|
@@ -148,7 +159,7 @@ declare function getGithubReleaseCdnGroup(): {
|
|
|
148
159
|
}[];
|
|
149
160
|
declare function restartApp(): void;
|
|
150
161
|
|
|
151
|
-
declare function createUpdater({
|
|
162
|
+
declare function createUpdater({ SIGNATURE_CERT, repository, productName, releaseAsarURL: _release, updateJsonURL: _update, debug, downloadConfig, compareVersion, }: UpdaterOption): Updater;
|
|
152
163
|
|
|
153
164
|
type AppOption = {
|
|
154
165
|
/**
|
|
@@ -177,11 +188,11 @@ type InitUpdaterOptions = OptionalProperty<UpdaterOption, 'productName'>;
|
|
|
177
188
|
* import { createUpdater, getGithubReleaseCdnGroup, initApp, parseGithubCdnURL } from 'electron-incremental-update'
|
|
178
189
|
* import { name, repository } from '../package.json'
|
|
179
190
|
*
|
|
180
|
-
* const
|
|
191
|
+
* const SIGNATURE_CERT = '' // auto generate
|
|
181
192
|
*
|
|
182
193
|
* const { cdnPrefix } = getGithubReleaseCdnGroup()[0]
|
|
183
194
|
* const updater = createUpdater({
|
|
184
|
-
*
|
|
195
|
+
* SIGNATURE_CERT,
|
|
185
196
|
* productName: name,
|
|
186
197
|
* repository,
|
|
187
198
|
* updateJsonURL: parseGithubCdnURL(repository, 'fastly.jsdelivr.net/gh', 'version.json'),
|
|
@@ -202,11 +213,11 @@ declare function initApp(appOptions: AppOption): {
|
|
|
202
213
|
* import { initApp } from 'electron-incremental-update'
|
|
203
214
|
* import { name, repository } from '../package.json'
|
|
204
215
|
*
|
|
205
|
-
* const
|
|
216
|
+
* const SIGNATURE_CERT = '' // auto generate
|
|
206
217
|
*
|
|
207
|
-
* initApp({ name }, {
|
|
218
|
+
* initApp({ name }, { SIGNATURE_CERT, repository })
|
|
208
219
|
* ```
|
|
209
220
|
*/
|
|
210
221
|
declare function initApp(appOptions: AppOption, updaterOptions: InitUpdaterOptions): undefined;
|
|
211
222
|
|
|
212
|
-
export { AppOption, CheckResultType,
|
|
223
|
+
export { AppOption, CheckResultType, InitUpdaterOptions, InstallResult, UpdateJSON, Updater, UpdaterOption, createUpdater, getEntryVersion, getGithubReleaseCdnGroup, getProductAsarPath, getProductVersion, initApp, isUpdateJSON, parseGithubCdnURL, requireNative, restartApp };
|