electron-incremental-update 0.5.0 → 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-VADH6AZA.mjs +51 -0
- package/dist/index.cjs +129 -104
- package/dist/index.d.ts +26 -15
- package/dist/index.mjs +118 -94
- package/dist/vite.cjs +72 -52
- package/dist/vite.d.ts +24 -2
- package/dist/vite.mjs +63 -34
- package/package.json +3 -2
- package/dist/chunk-SSJ6PDMK.mjs +0 -61
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
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined")
|
|
5
|
+
return require.apply(this, arguments);
|
|
6
|
+
throw new Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
// src/crypto.ts
|
|
10
|
+
import { constants, createCipheriv, createDecipheriv, createHash, createSign, createVerify } from "node:crypto";
|
|
11
|
+
import { Buffer as Buffer2 } from "node:buffer";
|
|
12
|
+
var aesEncode = "base64url";
|
|
13
|
+
function encrypt(plainText, key2, iv) {
|
|
14
|
+
const cipher = createCipheriv("aes-256-cbc", key2, iv);
|
|
15
|
+
let encrypted = cipher.update(plainText, "utf8", aesEncode);
|
|
16
|
+
encrypted += cipher.final(aesEncode);
|
|
17
|
+
return encrypted;
|
|
18
|
+
}
|
|
19
|
+
function decrypt(encryptedText, key2, iv) {
|
|
20
|
+
const decipher = createDecipheriv("aes-256-cbc", key2, iv);
|
|
21
|
+
let decrypted = decipher.update(encryptedText, aesEncode, "utf8");
|
|
22
|
+
decrypted += decipher.final("utf8");
|
|
23
|
+
return decrypted;
|
|
24
|
+
}
|
|
25
|
+
function key(data, length) {
|
|
26
|
+
const hash = createHash("SHA256").update(data).digest("binary");
|
|
27
|
+
return Buffer2.from(hash).subarray(0, length);
|
|
28
|
+
}
|
|
29
|
+
function signature(buffer, privateKey, cert, version) {
|
|
30
|
+
const sig = createSign("RSA-SHA256").update(buffer).sign({
|
|
31
|
+
key: privateKey,
|
|
32
|
+
padding: constants.RSA_PKCS1_PADDING,
|
|
33
|
+
saltLength: constants.RSA_PSS_SALTLEN_DIGEST
|
|
34
|
+
}, "base64");
|
|
35
|
+
return encrypt(`${sig}%${version}`, key(cert, 32), key(buffer, 16));
|
|
36
|
+
}
|
|
37
|
+
function verify(buffer, signature2, cert) {
|
|
38
|
+
try {
|
|
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;
|
|
42
|
+
} catch (error) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
__require,
|
|
49
|
+
signature,
|
|
50
|
+
verify
|
|
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,45 +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
|
|
65
|
-
|
|
66
|
-
const hash = (0, import_node_crypto.createHash)("SHA256").update(str).digest("binary");
|
|
67
|
+
function key(data, length) {
|
|
68
|
+
const hash = (0, import_node_crypto.createHash)("SHA256").update(data).digest("binary");
|
|
67
69
|
return import_node_buffer.Buffer.from(hash).subarray(0, length);
|
|
68
70
|
}
|
|
69
|
-
function verify(buffer, signature,
|
|
71
|
+
function verify(buffer, signature, cert) {
|
|
70
72
|
try {
|
|
71
|
-
const sig = decrypt(signature,
|
|
72
|
-
|
|
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;
|
|
73
76
|
} catch (error) {
|
|
74
77
|
return false;
|
|
75
78
|
}
|
|
@@ -78,8 +81,15 @@ function verify(buffer, signature, publicKey, name) {
|
|
|
78
81
|
// src/updater/defaultFunctions.ts
|
|
79
82
|
var import_node_buffer2 = require("buffer");
|
|
80
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
|
|
81
91
|
function downloadJSONDefault(url, updater, headers) {
|
|
82
|
-
return new Promise((
|
|
92
|
+
return new Promise((resolve3, reject) => {
|
|
83
93
|
import_node_https.default.get(url, (res) => {
|
|
84
94
|
let data = "";
|
|
85
95
|
res.setEncoding("utf8");
|
|
@@ -88,8 +98,8 @@ function downloadJSONDefault(url, updater, headers) {
|
|
|
88
98
|
res.on("end", () => {
|
|
89
99
|
try {
|
|
90
100
|
const json = JSON.parse(data);
|
|
91
|
-
if (
|
|
92
|
-
|
|
101
|
+
if (isUpdateJSON(json)) {
|
|
102
|
+
resolve3(json);
|
|
93
103
|
} else {
|
|
94
104
|
throw Error;
|
|
95
105
|
}
|
|
@@ -104,7 +114,7 @@ function downloadJSONDefault(url, updater, headers) {
|
|
|
104
114
|
}
|
|
105
115
|
function downloadBufferDefault(url, updater, headers) {
|
|
106
116
|
let progress = 0;
|
|
107
|
-
return new Promise((
|
|
117
|
+
return new Promise((resolve3, reject) => {
|
|
108
118
|
import_node_https.default.get(url, (res) => {
|
|
109
119
|
let data = [];
|
|
110
120
|
res.headers = headers;
|
|
@@ -114,7 +124,7 @@ function downloadBufferDefault(url, updater, headers) {
|
|
|
114
124
|
data.push(chunk);
|
|
115
125
|
});
|
|
116
126
|
res.on("end", () => {
|
|
117
|
-
|
|
127
|
+
resolve3(import_node_buffer2.Buffer.concat(data));
|
|
118
128
|
});
|
|
119
129
|
}).on("error", (e) => {
|
|
120
130
|
reject(e);
|
|
@@ -148,14 +158,14 @@ function compareVersionDefault(oldVersion, newVersion) {
|
|
|
148
158
|
var import_node_fs = require("fs");
|
|
149
159
|
var import_node_path = require("path");
|
|
150
160
|
var import_electron = require("electron");
|
|
151
|
-
function
|
|
161
|
+
function getProductAsarPath(name) {
|
|
152
162
|
return import_electron.app.isPackaged ? (0, import_node_path.join)((0, import_node_path.dirname)(import_electron.app.getAppPath()), `${name}.asar`) : "dev";
|
|
153
163
|
}
|
|
154
164
|
function getEntryVersion() {
|
|
155
165
|
return import_electron.app.getVersion();
|
|
156
166
|
}
|
|
157
|
-
function
|
|
158
|
-
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();
|
|
159
169
|
}
|
|
160
170
|
function requireNative(packageName) {
|
|
161
171
|
const path = import_electron.app.isPackaged ? (0, import_node_path.join)(import_electron.app.getAppPath(), "node_modules", packageName) : packageName;
|
|
@@ -195,7 +205,7 @@ function restartApp() {
|
|
|
195
205
|
|
|
196
206
|
// src/updater/index.ts
|
|
197
207
|
function createUpdater({
|
|
198
|
-
|
|
208
|
+
SIGNATURE_CERT,
|
|
199
209
|
repository,
|
|
200
210
|
productName,
|
|
201
211
|
releaseAsarURL: _release,
|
|
@@ -206,94 +216,103 @@ function createUpdater({
|
|
|
206
216
|
}) {
|
|
207
217
|
const updater = new import_node_events.EventEmitter();
|
|
208
218
|
let signature = "";
|
|
209
|
-
|
|
210
|
-
const gzipPath =
|
|
211
|
-
const
|
|
219
|
+
const asarPath = getProductAsarPath(productName);
|
|
220
|
+
const gzipPath = `${asarPath}.gz`;
|
|
221
|
+
const tmpFilePath = gzipPath.replace(".asar.gz", ".tmp.asar");
|
|
212
222
|
const { downloadBuffer, downloadJSON, extraHeader, userAgent } = downloadConfig || {};
|
|
213
223
|
function log(msg) {
|
|
214
224
|
debug && updater.emit("debug", msg);
|
|
215
225
|
}
|
|
216
|
-
async function
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
Accept: `application/${format === "json" ? "json" : "octet-stream"}`,
|
|
220
|
-
UserAgent: ua,
|
|
221
|
-
...extraHeader
|
|
222
|
-
};
|
|
223
|
-
log(`download headers: ${JSON.stringify(headers, null, 2)}`);
|
|
224
|
-
const downloadFn = format === "json" ? downloadJSON ?? downloadJSONDefault : downloadBuffer ?? downloadBufferDefault;
|
|
225
|
-
log(`download ${format} from ${url}`);
|
|
226
|
-
const ret = await downloadFn(url, updater, headers);
|
|
227
|
-
log(`download ${format} success`);
|
|
228
|
-
return ret;
|
|
229
|
-
}
|
|
230
|
-
async function extractFile(gzipFilePath) {
|
|
231
|
-
if (!gzipFilePath.endsWith(".asar.gz") || !(0, import_node_fs2.existsSync)(gzipFilePath)) {
|
|
232
|
-
log("update .asar.gz file not exist");
|
|
233
|
-
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");
|
|
234
229
|
}
|
|
235
|
-
|
|
236
|
-
return new Promise((resolve2, reject) => {
|
|
230
|
+
return new Promise((resolve3, reject) => {
|
|
237
231
|
const gunzip = (0, import_node_zlib.createGunzip)();
|
|
238
|
-
const input = (0, import_node_fs2.createReadStream)(
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
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}`);
|
|
242
235
|
input.pipe(gunzip).pipe(output).on("finish", async () => {
|
|
243
|
-
await (0, import_promises.rm)(
|
|
244
|
-
log(`${
|
|
245
|
-
|
|
236
|
+
await (0, import_promises.rm)(gzipPath);
|
|
237
|
+
log(`${gzipPath} unzipped`);
|
|
238
|
+
resolve3(null);
|
|
246
239
|
}).on("error", async (err) => {
|
|
247
|
-
await (0, import_promises.rm)(
|
|
240
|
+
await (0, import_promises.rm)(gzipPath);
|
|
248
241
|
output.destroy(err);
|
|
249
242
|
reject(err);
|
|
250
243
|
});
|
|
251
244
|
});
|
|
252
245
|
}
|
|
253
|
-
function needUpdate(
|
|
246
|
+
function needUpdate(version) {
|
|
254
247
|
if (!import_electron2.app.isPackaged) {
|
|
255
248
|
log("in dev mode, no need to update");
|
|
256
249
|
return false;
|
|
257
250
|
}
|
|
258
251
|
const currentVersion = getEntryVersion();
|
|
259
|
-
log(`check update:
|
|
260
|
-
current version is ${currentVersion},
|
|
261
|
-
new version is ${version2}`);
|
|
252
|
+
log(`check update: current version is ${currentVersion}, new version is ${version}`);
|
|
262
253
|
const _compare = compareVersion ?? compareVersionDefault;
|
|
263
|
-
return _compare(currentVersion,
|
|
254
|
+
return _compare(currentVersion, version);
|
|
264
255
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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`);
|
|
270
293
|
if (!repository) {
|
|
271
|
-
throw new Error(
|
|
294
|
+
throw new Error(`${info.name} or repository are not set`);
|
|
272
295
|
}
|
|
273
|
-
|
|
296
|
+
data = info.repoFallback;
|
|
274
297
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
log(`update info: ${JSON.stringify(json, null, 2)}`);
|
|
290
|
-
if (!await needUpdate(_v)) {
|
|
291
|
-
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}`);
|
|
292
312
|
return void 0;
|
|
293
313
|
} else {
|
|
294
|
-
log(`update available: ${
|
|
314
|
+
log(`update available: ${version}`);
|
|
295
315
|
signature = _sig;
|
|
296
|
-
version = _v;
|
|
297
316
|
return { size, version };
|
|
298
317
|
}
|
|
299
318
|
} catch (error) {
|
|
@@ -301,30 +320,35 @@ function createUpdater({
|
|
|
301
320
|
return error;
|
|
302
321
|
}
|
|
303
322
|
};
|
|
304
|
-
updater.
|
|
323
|
+
updater.downloadAndInstall = async (data, sig) => {
|
|
305
324
|
try {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
log("no releaseAsarURL, fallback to use repository");
|
|
310
|
-
if (!repository) {
|
|
311
|
-
throw new Error("releaseAsarURL or repository are not set");
|
|
312
|
-
}
|
|
313
|
-
_url = `${repository}/releases/download/latest/${productName}.asar.gz`;
|
|
314
|
-
}
|
|
315
|
-
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");
|
|
316
328
|
}
|
|
329
|
+
const buffer = await parseData("buffer", data);
|
|
317
330
|
log("verify start");
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
throw new Error("invalid signature");
|
|
331
|
+
const version = verify(buffer, _sig, SIGNATURE_CERT);
|
|
332
|
+
if (!version) {
|
|
333
|
+
throw new Error("verify failed, invalid signature");
|
|
321
334
|
}
|
|
322
335
|
log("verify success");
|
|
336
|
+
if (!await needUpdate(version)) {
|
|
337
|
+
throw new Error(`update unavailable: ${version}`);
|
|
338
|
+
}
|
|
323
339
|
log(`write file: ${gzipPath}`);
|
|
324
|
-
await (0, import_promises.writeFile)(gzipPath,
|
|
340
|
+
await (0, import_promises.writeFile)(gzipPath, buffer);
|
|
325
341
|
log(`extract file: ${gzipPath}`);
|
|
326
|
-
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
|
+
}
|
|
327
350
|
log(`update success, version: ${version}`);
|
|
351
|
+
signature = "";
|
|
328
352
|
return true;
|
|
329
353
|
} catch (error) {
|
|
330
354
|
log(error);
|
|
@@ -342,7 +366,7 @@ function initApp(appOptions, updaterOptions) {
|
|
|
342
366
|
mainPath = "main/index.js"
|
|
343
367
|
} = appOptions ?? {};
|
|
344
368
|
const mainDir = import_electron3.app.isPackaged ? `../${productName}.asar` : electronDistPath;
|
|
345
|
-
const entry = (0,
|
|
369
|
+
const entry = (0, import_node_path3.resolve)(__dirname, mainDir, mainPath);
|
|
346
370
|
if (updaterOptions) {
|
|
347
371
|
require(entry)(
|
|
348
372
|
createUpdater({ ...updaterOptions, productName })
|
|
@@ -358,11 +382,12 @@ function initApp(appOptions, updaterOptions) {
|
|
|
358
382
|
// Annotate the CommonJS export names for ESM import in node:
|
|
359
383
|
0 && (module.exports = {
|
|
360
384
|
createUpdater,
|
|
361
|
-
getAppAsarPath,
|
|
362
|
-
getAppVersion,
|
|
363
385
|
getEntryVersion,
|
|
364
386
|
getGithubReleaseCdnGroup,
|
|
387
|
+
getProductAsarPath,
|
|
388
|
+
getProductVersion,
|
|
365
389
|
initApp,
|
|
390
|
+
isUpdateJSON,
|
|
366
391
|
parseGithubCdnURL,
|
|
367
392
|
requireNative,
|
|
368
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 };
|