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 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 SIGNATURE_PUB = '' // auto generate RSA public key when start app
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 }, { SIGNATURE_PUB, repository })
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
- SIGNATURE_PUB,
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 { getAppAsarPath, getAppVersion, getEntryVersion } from 'electron-incremental-update'
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: ${getAppAsarPath(name)}`)
90
+ console.log(`\tasar path: ${getProductAsarPath(name)}`)
91
91
  console.log(`\tentry: ${getEntryVersion()}`)
92
- console.log(`\tapp: ${getAppVersion(name)}`)
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.downloadUpdate())
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 key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
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 import_node_path2 = require("path");
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, key, iv) {
59
- const decipher = (0, import_node_crypto.createDecipheriv)("aes-256-cbc", key, iv);
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 generateKey(buffer, str, length) {
65
- str += (0, import_node_crypto.createHash)("md5").update(buffer.map((v, i) => i & length / 4 && v)).digest("hex");
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, publicKey, name) {
71
+ function verify(buffer, signature, cert) {
70
72
  try {
71
- const sig = decrypt(signature, generateKey(buffer, publicKey, 32), generateKey(buffer, name, 16));
72
- return (0, import_node_crypto.createVerify)("RSA-SHA256").update(buffer).verify(publicKey, sig, "base64");
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((resolve2, reject) => {
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 ("signature" in json && "version" in json && "size" in json) {
92
- resolve2(json);
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((resolve2, reject) => {
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
- resolve2(import_node_buffer2.Buffer.concat(data));
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 getAppAsarPath(name) {
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 getAppVersion(name) {
158
- return import_electron.app.isPackaged ? (0, import_node_fs.readFileSync)((0, import_node_path.join)(getAppAsarPath(name), "version"), "utf-8") : getEntryVersion();
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
- SIGNATURE_PUB,
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
- let version = "";
210
- const gzipPath = `../${productName}.asar.gz`;
211
- const tmpFile = gzipPath.replace(".asar.gz", ".tmp.gz");
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 download(url, format) {
217
- 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";
218
- const headers = {
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
- gzipFilePath = gzipFilePath.replace(".asar.gz", ".tmp.gz");
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)(gzipFilePath);
239
- const outputFilePath = gzipFilePath.replace(".tmp.gz", ".asar");
240
- const output = (0, import_node_fs2.createWriteStream)(outputFilePath);
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)(gzipFilePath);
244
- log(`${gzipFilePath} unzipped`);
245
- resolve2(outputFilePath);
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)(gzipFilePath);
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(version2) {
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, version2);
254
+ return _compare(currentVersion, version);
264
255
  }
265
- updater.checkUpdate = async (url) => {
266
- try {
267
- url ??= _update;
268
- if (!url) {
269
- log("no updateJsonURL, fallback to use repository");
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("updateJsonURL or repository are not set");
294
+ throw new Error(`${info.name} or repository are not set`);
272
295
  }
273
- url = `${repository.replace("github.com", "raw.githubusercontent.com")}/master/version.json`;
296
+ data = info.repoFallback;
274
297
  }
275
- if ((0, import_node_fs2.existsSync)(tmpFile)) {
276
- log(`remove tmp file: ${tmpFile}`);
277
- await (0, import_promises.rm)(tmpFile);
278
- }
279
- if ((0, import_node_fs2.existsSync)(gzipPath)) {
280
- log(`remove .gz file: ${gzipPath}`);
281
- await (0, import_promises.rm)(gzipPath);
282
- }
283
- const json = await download(url, "json");
284
- const {
285
- signature: _sig,
286
- version: _v,
287
- size
288
- } = json;
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: ${_v}`);
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.downloadUpdate = async (src) => {
323
+ updater.downloadAndInstall = async (data, sig) => {
305
324
  try {
306
- if (typeof src !== "object") {
307
- let _url = src ?? _release;
308
- if (!_url) {
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
- if (!verify(src, signature, SIGNATURE_PUB, productName)) {
319
- log("verify failed");
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, src);
340
+ await (0, import_promises.writeFile)(gzipPath, buffer);
325
341
  log(`extract file: ${gzipPath}`);
326
- await extractFile(gzipPath);
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, import_node_path2.resolve)(__dirname, mainDir, mainPath);
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 DownloadResult = true | Error;
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(url?: string): Promise<CheckResultType>;
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
- downloadUpdate(url?: string | Buffer): Promise<DownloadResult>;
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 SIGNATURE_PUB = ''
54
+ * const SIGNATURE_CERT = ''
44
55
  *
45
56
  * const updater = createUpdater({
46
- * SIGNATURE_PUB,
57
+ * SIGNATURE_CERT,
47
58
  * ...
48
59
  * })
49
60
  * ```
50
61
  */
51
- SIGNATURE_PUB: string;
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 | Promise<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 getAppAsarPath(name: string): string;
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 getAppVersion(name: string): string;
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({ SIGNATURE_PUB, repository, productName, releaseAsarURL: _release, updateJsonURL: _update, debug, downloadConfig, compareVersion, }: UpdaterOption): Updater;
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 SIGNATURE_PUB = '' // auto generate
191
+ * const SIGNATURE_CERT = '' // auto generate
181
192
  *
182
193
  * const { cdnPrefix } = getGithubReleaseCdnGroup()[0]
183
194
  * const updater = createUpdater({
184
- * SIGNATURE_PUB,
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 SIGNATURE_PUB = '' // auto generate
216
+ * const SIGNATURE_CERT = '' // auto generate
206
217
  *
207
- * initApp({ name }, { SIGNATURE_PUB, repository })
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, DownloadResult, InitUpdaterOptions, UpdateJSON, Updater, UpdaterOption, createUpdater, getAppAsarPath, getAppVersion, getEntryVersion, getGithubReleaseCdnGroup, initApp, parseGithubCdnURL, requireNative, restartApp };
223
+ export { AppOption, CheckResultType, InitUpdaterOptions, InstallResult, UpdateJSON, Updater, UpdaterOption, createUpdater, getEntryVersion, getGithubReleaseCdnGroup, getProductAsarPath, getProductVersion, initApp, isUpdateJSON, parseGithubCdnURL, requireNative, restartApp };