electron-incremental-update 0.2.1 → 0.3.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/dist/index.cjs CHANGED
@@ -34,9 +34,11 @@ __export(src_exports, {
34
34
  getAppAsarPath: () => getAppAsarPath,
35
35
  getAppVersion: () => getAppVersion,
36
36
  getEntryVersion: () => getEntryVersion,
37
- getReleaseCdnLink: () => getReleaseCdnLink,
37
+ getGithubReleaseCdnGroup: () => getGithubReleaseCdnGroup,
38
38
  initApp: () => initApp,
39
- requireNative: () => requireNative
39
+ parseGithubCdnURL: () => parseGithubCdnURL,
40
+ requireNative: () => requireNative,
41
+ restartApp: () => restartApp
40
42
  });
41
43
  module.exports = __toCommonJS(src_exports);
42
44
  var import_node_path2 = require("path");
@@ -61,7 +63,6 @@ function downloadJSONDefault(url, updater, headers) {
61
63
  res.headers = headers;
62
64
  res.on("data", (chunk) => data += chunk);
63
65
  res.on("end", () => {
64
- updater.emit("downloadEnd", true);
65
66
  const json = JSON.parse(data);
66
67
  if ("signature" in json && "version" in json && "size" in json) {
67
68
  resolve2(json);
@@ -70,8 +71,6 @@ function downloadJSONDefault(url, updater, headers) {
70
71
  }
71
72
  });
72
73
  }).on("error", (e) => {
73
- e && updater.emit("donwnloadError", e);
74
- updater.emit("downloadEnd", false);
75
74
  reject(e);
76
75
  });
77
76
  });
@@ -86,12 +85,9 @@ function downloadBufferDefault(url, updater, headers) {
86
85
  data.push(chunk);
87
86
  });
88
87
  res.on("end", () => {
89
- updater.emit("downloadEnd", true);
90
88
  resolve2(import_node_buffer.Buffer.concat(data));
91
89
  });
92
90
  }).on("error", (e) => {
93
- e && updater.emit("donwnloadError", e);
94
- updater.emit("downloadEnd", false);
95
91
  reject(e);
96
92
  });
97
93
  });
@@ -130,39 +126,43 @@ function getEntryVersion() {
130
126
  return import_electron.app.getVersion();
131
127
  }
132
128
  function getAppVersion(name) {
133
- return import_electron.app.isPackaged ? (0, import_node_fs.readFileSync)((0, import_node_path.join)(getAppAsarPath(name), "version"), "utf-8").trim() : getEntryVersion();
129
+ return import_electron.app.isPackaged ? (0, import_node_fs.readFileSync)((0, import_node_path.join)(getAppAsarPath(name), "version"), "utf-8") : getEntryVersion();
134
130
  }
135
131
  function requireNative(packageName) {
136
132
  const path = import_electron.app.isPackaged ? (0, import_node_path.join)(import_electron.app.getAppPath(), "node_modules", packageName) : packageName;
137
133
  return require(path);
138
134
  }
139
- function getReleaseCdnLink(url) {
140
- const hub = "https://github.com/";
141
- if (!url.startsWith(hub)) {
142
- throw new Error("URL must start with 'https://github.com/'");
135
+ function parseGithubCdnURL(repository, cdnPrefix, relativeFilePath) {
136
+ if (!repository.startsWith("https://github.com/")) {
137
+ throw new Error("url must start with https://github.com/");
143
138
  }
144
- if (url.endsWith("/")) {
145
- url = url.slice(0, -1);
146
- }
147
- const _url = url.replace(hub, "");
139
+ repository = repository.trim().replace(/\/?$/, "/").trim();
140
+ relativeFilePath = relativeFilePath.trim().replace(/^\/|\/?$/g, "").trim();
141
+ cdnPrefix = cdnPrefix.trim().replace(/^\/?|\/?$/g, "").trim();
142
+ return repository.replace("github.com", cdnPrefix) + relativeFilePath;
143
+ }
144
+ function getGithubReleaseCdnGroup() {
148
145
  return [
149
- { url: `https://gh.gh2233.ml/${url}`, maintainer: "@X.I.U/XIU2" },
150
- { url: `https://ghproxy.com/${url}`, maintainer: "gh-proxy" },
151
- { url: `https://gh.ddlc.top/${url}`, maintainer: "@mtr-static-official" },
152
- { url: `https://ghdl.feizhuqwq.cf/${url}`, maintainer: "feizhuqwq.com" },
153
- { url: `https://slink.ltd/${url}`, maintainer: "\u77E5\u4E86\u5C0F\u7AD9" },
154
- { url: `https://git.xfj0.cn/${url}`, maintainer: "anonymous1" },
155
- { url: `https://gh.con.sh/${url}`, maintainer: "anonymous2" },
156
- { url: `https://ghps.cc/${url}`, maintainer: "anonymous3" },
157
- { url: `https://cors.isteed.cc/github.com/${_url}`, maintainer: "Lufs's" },
158
- { url: `https://hub.gitmirror.com/${url}`, maintainer: "GitMirror" },
159
- { url: `https://js.xxooo.ml/${url}`, maintainer: "\u996D\u592A\u786C" },
160
- { url: `https://proxy.freecdn.ml/?url=${url}`, maintainer: "anonymous4" },
161
- { url: `https://download.njuu.cf/${_url}`, maintainer: "LibraryCloud-njuu" },
162
- { url: `https://download.yzuu.cf/${_url}`, maintainer: "LibraryCloud-yzuu" },
163
- { url: `https://download.nuaa.cf/${_url}`, maintainer: "LibraryCloud-nuaa" }
146
+ { cdnPrefix: "gh.gh2233.ml", maintainer: "@X.I.U/XIU2" },
147
+ { cdnPrefix: "ghproxy.com", maintainer: "gh-proxy" },
148
+ { cdnPrefix: "gh.ddlc.top", maintainer: "@mtr-static-official" },
149
+ { cdnPrefix: "ghdl.feizhuqwq.cf", maintainer: "feizhuqwq.com" },
150
+ { cdnPrefix: "slink.ltd", maintainer: "\u77E5\u4E86\u5C0F\u7AD9" },
151
+ { cdnPrefix: "git.xfj0.cn", maintainer: "anonymous1" },
152
+ { cdnPrefix: "gh.con.sh", maintainer: "anonymous2" },
153
+ { cdnPrefix: "ghps.cc", maintainer: "anonymous3" },
154
+ { cdnPrefix: "cors.isteed.cc/github.com", maintainer: "Lufs's" },
155
+ { cdnPrefix: "hub.gitmirror.com", maintainer: "GitMirror" },
156
+ { cdnPrefix: "js.xxooo.ml", maintainer: "\u996D\u592A\u786C" },
157
+ { cdnPrefix: "download.njuu.cf", maintainer: "LibraryCloud-njuu" },
158
+ { cdnPrefix: "download.yzuu.cf", maintainer: "LibraryCloud-yzuu" },
159
+ { cdnPrefix: "download.nuaa.cf", maintainer: "LibraryCloud-nuaa" }
164
160
  ];
165
161
  }
162
+ function restartApp() {
163
+ import_electron.app.relaunch();
164
+ import_electron.app.quit();
165
+ }
166
166
 
167
167
  // src/updater/index.ts
168
168
  function createUpdater({
@@ -176,9 +176,12 @@ function createUpdater({
176
176
  compareVersion
177
177
  }) {
178
178
  const updater = new import_node_events.EventEmitter();
179
+ let signature = "";
180
+ const gzipPath = `../${productName}.asar.gz`;
181
+ const tmpFile = gzipPath.replace(".asar.gz", ".tmp.gz");
179
182
  const { downloadBuffer, downloadJSON, extraHeader, userAgent } = downloadConfig || {};
180
- function log(...args) {
181
- debug && console.log(...args);
183
+ function log(msg) {
184
+ debug && updater.emit("debug", msg);
182
185
  }
183
186
  async function download(url, format) {
184
187
  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";
@@ -187,7 +190,7 @@ function createUpdater({
187
190
  UserAgent: ua,
188
191
  ...extraHeader
189
192
  };
190
- log("[updater] headers", headers);
193
+ log(`headers: ${headers}`);
191
194
  const downloadFn = format === "json" ? downloadJSON ?? downloadJSONDefault : downloadBuffer ?? downloadBufferDefault;
192
195
  return await downloadFn(url, updater, headers);
193
196
  }
@@ -201,89 +204,105 @@ function createUpdater({
201
204
  const input = (0, import_node_fs2.createReadStream)(gzipFilePath);
202
205
  const outputFilePath = gzipFilePath.replace(".tmp.gz", ".asar");
203
206
  const output = (0, import_node_fs2.createWriteStream)(outputFilePath);
204
- log("[updater] outputFilePath", outputFilePath);
207
+ log(`outputFilePath: ${outputFilePath}`);
205
208
  input.pipe(gunzip).pipe(output).on("finish", async () => {
206
209
  await (0, import_promises.rm)(gzipFilePath);
207
- log("[updater] finish");
210
+ log("finish");
208
211
  resolve2(outputFilePath);
209
212
  }).on("error", async (err) => {
210
213
  await (0, import_promises.rm)(gzipFilePath);
211
- log("[updater] error", err);
214
+ log(`error: ${err}`);
212
215
  output.destroy(err);
213
216
  reject(err);
214
217
  });
215
218
  });
216
219
  }
217
- function verify(buffer, signature) {
218
- log("[updater] signature", signature);
219
- return (0, import_node_crypto.createVerify)("RSA-SHA256").update(buffer).verify(SIGNATURE_PUB, signature, "base64");
220
+ function verify(buffer, signature2) {
221
+ log(`signature: ${signature2}`);
222
+ return (0, import_node_crypto.createVerify)("RSA-SHA256").update(buffer).verify(SIGNATURE_PUB, signature2, "base64");
220
223
  }
221
224
  function needUpdate(version) {
222
225
  if (!version || !import_electron2.app.isPackaged) {
223
226
  return false;
224
227
  }
225
228
  const currentVersion = getEntryVersion();
226
- log("[updater] currentVersion", currentVersion);
227
- log("[updater] newVersion", version);
229
+ log(`currentVersion: ${currentVersion}`);
230
+ log(`newVersion: ${version}`);
228
231
  const _compare = compareVersion ?? compareVersionDefault;
229
232
  return _compare(currentVersion, version);
230
233
  }
231
- async function checkUpdate(option) {
232
- let {
233
- updateJsonURL = _update,
234
- releaseAsarURL = _release
235
- } = option || {};
236
- if (!updateJsonURL || !releaseAsarURL) {
237
- log("[updater] no updateJsonURL or releaseAsarURL, use repository");
234
+ async function checkUpdate(url) {
235
+ url ??= _update;
236
+ if (!url) {
237
+ log("no updateJsonURL, use repository");
238
238
  if (!repository) {
239
- throw new Error("updateJsonURL or releaseAsarURL are not set");
239
+ throw new Error("updateJsonURL or repository are not set");
240
240
  }
241
- updateJsonURL = `${repository.replace("github.com", "raw.githubusercontent.com")}/version.json`;
242
- releaseAsarURL = `${repository}/releases/download/latest/${productName}.asar.gz`;
241
+ url = `${repository.replace("github.com", "raw.githubusercontent.com")}/version.json`;
243
242
  }
244
- log("[updater] updateJsonURL", updateJsonURL);
245
- log("[updater] releaseAsarURL", releaseAsarURL);
246
- const gzipPath = `../${productName}.asar.gz`;
247
- const tmpFile = gzipPath.replace(".asar.gz", ".tmp.gz");
243
+ log(`updateJsonURL: ${url}`);
248
244
  if ((0, import_node_fs2.existsSync)(tmpFile)) {
249
- log("[updater] remove tmp file", tmpFile);
245
+ log(`remove tmp file: ${tmpFile}`);
250
246
  await (0, import_promises.rm)(tmpFile);
251
247
  }
252
- const json = await download(updateJsonURL, "json");
253
- if (!json) {
254
- throw new Error("fetch update json failed");
255
- }
248
+ const json = await download(url, "json");
256
249
  const {
257
- signature,
250
+ signature: _sig,
258
251
  version,
259
252
  size
260
253
  } = json;
261
- log("[updater] UpdateJSON", json);
254
+ log(`UpdateJSON: ${JSON.stringify(json, null, 2)}`);
262
255
  if (!needUpdate(version)) {
263
- return "unavailable";
256
+ return false;
257
+ } else {
258
+ signature = _sig;
259
+ return { size, version };
264
260
  }
265
- updater.emit("downloadStart", size);
266
- const buffer = await download(releaseAsarURL, "buffer");
267
- log("[updater] start verify");
268
- if (!verify(buffer, signature)) {
261
+ }
262
+ async function downloadUpdate(src) {
263
+ if (typeof src !== "object") {
264
+ let _url = src ?? _release;
265
+ if (!_url) {
266
+ log("no releaseAsarURL, use repository");
267
+ if (!repository) {
268
+ throw new Error("releaseAsarURL or repository are not set");
269
+ }
270
+ _url = `${repository}/releases/download/latest/${productName}.asar.gz`;
271
+ }
272
+ log(`releaseAsarURL: ${_url}`);
273
+ src = await download(_url, "buffer");
274
+ }
275
+ log("start verify");
276
+ if (!verify(src, signature)) {
269
277
  throw new Error("file broken, invalid signature!");
270
278
  }
271
- log("[updater] write file", gzipPath);
272
- await (0, import_promises.writeFile)(gzipPath, buffer);
273
- log("[updater] extract file", gzipPath);
279
+ log(`write file: ${gzipPath}`);
280
+ await (0, import_promises.writeFile)(gzipPath, src);
281
+ log(`extract file: ${gzipPath}`);
274
282
  await extractFile(gzipPath);
275
- return "success";
283
+ updater.emit("downloaded");
276
284
  }
277
- const onCheck = async (option) => {
285
+ const onCheck = async (url) => {
278
286
  try {
279
- const result = await checkUpdate(option);
287
+ const result = await checkUpdate(url);
280
288
  updater.emit("checkResult", result);
281
289
  } catch (error) {
282
- updater.emit("checkResult", "fail", error);
290
+ log(error);
291
+ updater.emit("checkResult", error);
283
292
  }
284
293
  };
285
294
  updater.on("check", onCheck);
286
295
  updater.checkUpdate = onCheck;
296
+ const onDownload = async (src) => {
297
+ try {
298
+ await downloadUpdate(src);
299
+ } catch (error) {
300
+ log(error);
301
+ updater.emit("donwnloadError", error);
302
+ }
303
+ };
304
+ updater.on("download", onDownload);
305
+ updater.downloadUpdate = onDownload;
287
306
  return updater;
288
307
  }
289
308
 
@@ -310,7 +329,9 @@ function initApp(productName, updater, option) {
310
329
  getAppAsarPath,
311
330
  getAppVersion,
312
331
  getEntryVersion,
313
- getReleaseCdnLink,
332
+ getGithubReleaseCdnGroup,
314
333
  initApp,
315
- requireNative
334
+ parseGithubCdnURL,
335
+ requireNative,
336
+ restartApp
316
337
  });
package/dist/index.d.ts CHANGED
@@ -1,13 +1,14 @@
1
1
  import { Buffer } from 'node:buffer';
2
2
 
3
- type CheckResultType = 'success' | 'fail' | 'unavailable';
3
+ type CheckResultType = Error | false | Omit<UpdateJSON, 'signature'>;
4
4
  type UpdateEvents = {
5
5
  check: null;
6
- checkResult: [data: CheckResultType, err?: unknown];
7
- downloadStart: [size: number];
6
+ checkResult: [data: CheckResultType];
7
+ download: null;
8
8
  downloading: [current: number];
9
- downloadEnd: [success: boolean];
9
+ downloaded: null;
10
10
  donwnloadError: [error: unknown];
11
+ debug: [msg: string | Error];
11
12
  };
12
13
  type UpdateJSON = {
13
14
  signature: string;
@@ -37,7 +38,13 @@ interface TypedUpdater<T extends Record<string | symbol, MaybeArray<any>>, Event
37
38
  once<E extends Event>(eventName: E, listener: (...args: MaybeArray<T[E]>) => void): this;
38
39
  emit<E extends Event>(eventName: E, ...args: MaybeArray<T[E]>): boolean;
39
40
  off<E extends Event>(eventName: E, listener: (...args: MaybeArray<T[E]>) => void): this;
40
- checkUpdate(options?: BaseOption): Promise<void>;
41
+ /**
42
+ * - `undefined`: errror
43
+ * - `false`: unavailable
44
+ * - `{size: number, version: string}`: success
45
+ */
46
+ checkUpdate(url?: BaseOption['updateJsonURL']): Promise<void>;
47
+ downloadUpdate(url?: BaseOption['releaseAsarURL'] | Buffer): Promise<void>;
41
48
  }
42
49
  type Updater = TypedUpdater<UpdateEvents>;
43
50
  interface UpdaterOption extends BaseOption {
@@ -123,12 +130,17 @@ declare function getAppVersion(name: string): string;
123
130
  */
124
131
  declare function requireNative<T = any>(packageName: string): T;
125
132
  /**
126
- * get group of parsed github release CDN links for accelerating the speed of downloading release
133
+ * get github version.json CDN URL for accelerating the speed of downloading version info
134
+ */
135
+ declare function parseGithubCdnURL(repository: string, cdnPrefix: string, relativeFilePath: string): string;
136
+ /**
137
+ * get group of github release CDN prefix for accelerating the speed of downloading release
127
138
  */
128
- declare function getReleaseCdnLink(url: string): {
129
- url: string;
139
+ declare function getGithubReleaseCdnGroup(): {
140
+ cdnPrefix: string;
130
141
  maintainer: string;
131
142
  }[];
143
+ declare function restartApp(): void;
132
144
 
133
145
  declare function createUpdater({ SIGNATURE_PUB, repository, productName, releaseAsarURL: _release, updateJsonURL: _update, debug, downloadConfig, compareVersion, }: UpdaterOption): Updater;
134
146
 
@@ -189,4 +201,4 @@ declare function initApp(productName: string, updater: Updater | Omit<UpdaterOpt
189
201
  productName?: string;
190
202
  }, option?: AppOption): any;
191
203
 
192
- export { BaseOption, CheckResultType, UpdateJSON, Updater, UpdaterOption, createUpdater, getAppAsarPath, getAppVersion, getEntryVersion, getReleaseCdnLink, initApp, requireNative };
204
+ export { BaseOption, CheckResultType, UpdateJSON, Updater, UpdaterOption, createUpdater, getAppAsarPath, getAppVersion, getEntryVersion, getGithubReleaseCdnGroup, initApp, parseGithubCdnURL, requireNative, restartApp };
package/dist/index.mjs CHANGED
@@ -25,7 +25,6 @@ function downloadJSONDefault(url, updater, headers) {
25
25
  res.headers = headers;
26
26
  res.on("data", (chunk) => data += chunk);
27
27
  res.on("end", () => {
28
- updater.emit("downloadEnd", true);
29
28
  const json = JSON.parse(data);
30
29
  if ("signature" in json && "version" in json && "size" in json) {
31
30
  resolve2(json);
@@ -34,8 +33,6 @@ function downloadJSONDefault(url, updater, headers) {
34
33
  }
35
34
  });
36
35
  }).on("error", (e) => {
37
- e && updater.emit("donwnloadError", e);
38
- updater.emit("downloadEnd", false);
39
36
  reject(e);
40
37
  });
41
38
  });
@@ -50,12 +47,9 @@ function downloadBufferDefault(url, updater, headers) {
50
47
  data.push(chunk);
51
48
  });
52
49
  res.on("end", () => {
53
- updater.emit("downloadEnd", true);
54
50
  resolve2(Buffer.concat(data));
55
51
  });
56
52
  }).on("error", (e) => {
57
- e && updater.emit("donwnloadError", e);
58
- updater.emit("downloadEnd", false);
59
53
  reject(e);
60
54
  });
61
55
  });
@@ -94,39 +88,43 @@ function getEntryVersion() {
94
88
  return app.getVersion();
95
89
  }
96
90
  function getAppVersion(name) {
97
- return app.isPackaged ? readFileSync(join(getAppAsarPath(name), "version"), "utf-8").trim() : getEntryVersion();
91
+ return app.isPackaged ? readFileSync(join(getAppAsarPath(name), "version"), "utf-8") : getEntryVersion();
98
92
  }
99
93
  function requireNative(packageName) {
100
94
  const path = app.isPackaged ? join(app.getAppPath(), "node_modules", packageName) : packageName;
101
95
  return __require(path);
102
96
  }
103
- function getReleaseCdnLink(url) {
104
- const hub = "https://github.com/";
105
- if (!url.startsWith(hub)) {
106
- throw new Error("URL must start with 'https://github.com/'");
97
+ function parseGithubCdnURL(repository, cdnPrefix, relativeFilePath) {
98
+ if (!repository.startsWith("https://github.com/")) {
99
+ throw new Error("url must start with https://github.com/");
107
100
  }
108
- if (url.endsWith("/")) {
109
- url = url.slice(0, -1);
110
- }
111
- const _url = url.replace(hub, "");
101
+ repository = repository.trim().replace(/\/?$/, "/").trim();
102
+ relativeFilePath = relativeFilePath.trim().replace(/^\/|\/?$/g, "").trim();
103
+ cdnPrefix = cdnPrefix.trim().replace(/^\/?|\/?$/g, "").trim();
104
+ return repository.replace("github.com", cdnPrefix) + relativeFilePath;
105
+ }
106
+ function getGithubReleaseCdnGroup() {
112
107
  return [
113
- { url: `https://gh.gh2233.ml/${url}`, maintainer: "@X.I.U/XIU2" },
114
- { url: `https://ghproxy.com/${url}`, maintainer: "gh-proxy" },
115
- { url: `https://gh.ddlc.top/${url}`, maintainer: "@mtr-static-official" },
116
- { url: `https://ghdl.feizhuqwq.cf/${url}`, maintainer: "feizhuqwq.com" },
117
- { url: `https://slink.ltd/${url}`, maintainer: "\u77E5\u4E86\u5C0F\u7AD9" },
118
- { url: `https://git.xfj0.cn/${url}`, maintainer: "anonymous1" },
119
- { url: `https://gh.con.sh/${url}`, maintainer: "anonymous2" },
120
- { url: `https://ghps.cc/${url}`, maintainer: "anonymous3" },
121
- { url: `https://cors.isteed.cc/github.com/${_url}`, maintainer: "Lufs's" },
122
- { url: `https://hub.gitmirror.com/${url}`, maintainer: "GitMirror" },
123
- { url: `https://js.xxooo.ml/${url}`, maintainer: "\u996D\u592A\u786C" },
124
- { url: `https://proxy.freecdn.ml/?url=${url}`, maintainer: "anonymous4" },
125
- { url: `https://download.njuu.cf/${_url}`, maintainer: "LibraryCloud-njuu" },
126
- { url: `https://download.yzuu.cf/${_url}`, maintainer: "LibraryCloud-yzuu" },
127
- { url: `https://download.nuaa.cf/${_url}`, maintainer: "LibraryCloud-nuaa" }
108
+ { cdnPrefix: "gh.gh2233.ml", maintainer: "@X.I.U/XIU2" },
109
+ { cdnPrefix: "ghproxy.com", maintainer: "gh-proxy" },
110
+ { cdnPrefix: "gh.ddlc.top", maintainer: "@mtr-static-official" },
111
+ { cdnPrefix: "ghdl.feizhuqwq.cf", maintainer: "feizhuqwq.com" },
112
+ { cdnPrefix: "slink.ltd", maintainer: "\u77E5\u4E86\u5C0F\u7AD9" },
113
+ { cdnPrefix: "git.xfj0.cn", maintainer: "anonymous1" },
114
+ { cdnPrefix: "gh.con.sh", maintainer: "anonymous2" },
115
+ { cdnPrefix: "ghps.cc", maintainer: "anonymous3" },
116
+ { cdnPrefix: "cors.isteed.cc/github.com", maintainer: "Lufs's" },
117
+ { cdnPrefix: "hub.gitmirror.com", maintainer: "GitMirror" },
118
+ { cdnPrefix: "js.xxooo.ml", maintainer: "\u996D\u592A\u786C" },
119
+ { cdnPrefix: "download.njuu.cf", maintainer: "LibraryCloud-njuu" },
120
+ { cdnPrefix: "download.yzuu.cf", maintainer: "LibraryCloud-yzuu" },
121
+ { cdnPrefix: "download.nuaa.cf", maintainer: "LibraryCloud-nuaa" }
128
122
  ];
129
123
  }
124
+ function restartApp() {
125
+ app.relaunch();
126
+ app.quit();
127
+ }
130
128
 
131
129
  // src/updater/index.ts
132
130
  function createUpdater({
@@ -140,9 +138,12 @@ function createUpdater({
140
138
  compareVersion
141
139
  }) {
142
140
  const updater = new EventEmitter();
141
+ let signature = "";
142
+ const gzipPath = `../${productName}.asar.gz`;
143
+ const tmpFile = gzipPath.replace(".asar.gz", ".tmp.gz");
143
144
  const { downloadBuffer, downloadJSON, extraHeader, userAgent } = downloadConfig || {};
144
- function log(...args) {
145
- debug && console.log(...args);
145
+ function log(msg) {
146
+ debug && updater.emit("debug", msg);
146
147
  }
147
148
  async function download(url, format) {
148
149
  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,7 +152,7 @@ function createUpdater({
151
152
  UserAgent: ua,
152
153
  ...extraHeader
153
154
  };
154
- log("[updater] headers", headers);
155
+ log(`headers: ${headers}`);
155
156
  const downloadFn = format === "json" ? downloadJSON ?? downloadJSONDefault : downloadBuffer ?? downloadBufferDefault;
156
157
  return await downloadFn(url, updater, headers);
157
158
  }
@@ -165,89 +166,105 @@ function createUpdater({
165
166
  const input = createReadStream(gzipFilePath);
166
167
  const outputFilePath = gzipFilePath.replace(".tmp.gz", ".asar");
167
168
  const output = createWriteStream(outputFilePath);
168
- log("[updater] outputFilePath", outputFilePath);
169
+ log(`outputFilePath: ${outputFilePath}`);
169
170
  input.pipe(gunzip).pipe(output).on("finish", async () => {
170
171
  await rm(gzipFilePath);
171
- log("[updater] finish");
172
+ log("finish");
172
173
  resolve2(outputFilePath);
173
174
  }).on("error", async (err) => {
174
175
  await rm(gzipFilePath);
175
- log("[updater] error", err);
176
+ log(`error: ${err}`);
176
177
  output.destroy(err);
177
178
  reject(err);
178
179
  });
179
180
  });
180
181
  }
181
- function verify(buffer, signature) {
182
- log("[updater] signature", signature);
183
- return createVerify("RSA-SHA256").update(buffer).verify(SIGNATURE_PUB, signature, "base64");
182
+ function verify(buffer, signature2) {
183
+ log(`signature: ${signature2}`);
184
+ return createVerify("RSA-SHA256").update(buffer).verify(SIGNATURE_PUB, signature2, "base64");
184
185
  }
185
186
  function needUpdate(version) {
186
187
  if (!version || !app2.isPackaged) {
187
188
  return false;
188
189
  }
189
190
  const currentVersion = getEntryVersion();
190
- log("[updater] currentVersion", currentVersion);
191
- log("[updater] newVersion", version);
191
+ log(`currentVersion: ${currentVersion}`);
192
+ log(`newVersion: ${version}`);
192
193
  const _compare = compareVersion ?? compareVersionDefault;
193
194
  return _compare(currentVersion, version);
194
195
  }
195
- async function checkUpdate(option) {
196
- let {
197
- updateJsonURL = _update,
198
- releaseAsarURL = _release
199
- } = option || {};
200
- if (!updateJsonURL || !releaseAsarURL) {
201
- log("[updater] no updateJsonURL or releaseAsarURL, use repository");
196
+ async function checkUpdate(url) {
197
+ url ??= _update;
198
+ if (!url) {
199
+ log("no updateJsonURL, use repository");
202
200
  if (!repository) {
203
- throw new Error("updateJsonURL or releaseAsarURL are not set");
201
+ throw new Error("updateJsonURL or repository are not set");
204
202
  }
205
- updateJsonURL = `${repository.replace("github.com", "raw.githubusercontent.com")}/version.json`;
206
- releaseAsarURL = `${repository}/releases/download/latest/${productName}.asar.gz`;
203
+ url = `${repository.replace("github.com", "raw.githubusercontent.com")}/version.json`;
207
204
  }
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");
205
+ log(`updateJsonURL: ${url}`);
212
206
  if (existsSync(tmpFile)) {
213
- log("[updater] remove tmp file", tmpFile);
207
+ log(`remove tmp file: ${tmpFile}`);
214
208
  await rm(tmpFile);
215
209
  }
216
- const json = await download(updateJsonURL, "json");
217
- if (!json) {
218
- throw new Error("fetch update json failed");
219
- }
210
+ const json = await download(url, "json");
220
211
  const {
221
- signature,
212
+ signature: _sig,
222
213
  version,
223
214
  size
224
215
  } = json;
225
- log("[updater] UpdateJSON", json);
216
+ log(`UpdateJSON: ${JSON.stringify(json, null, 2)}`);
226
217
  if (!needUpdate(version)) {
227
- return "unavailable";
218
+ return false;
219
+ } else {
220
+ signature = _sig;
221
+ return { size, version };
228
222
  }
229
- updater.emit("downloadStart", size);
230
- const buffer = await download(releaseAsarURL, "buffer");
231
- log("[updater] start verify");
232
- if (!verify(buffer, signature)) {
223
+ }
224
+ async function downloadUpdate(src) {
225
+ if (typeof src !== "object") {
226
+ let _url = src ?? _release;
227
+ if (!_url) {
228
+ log("no releaseAsarURL, use repository");
229
+ if (!repository) {
230
+ throw new Error("releaseAsarURL or repository are not set");
231
+ }
232
+ _url = `${repository}/releases/download/latest/${productName}.asar.gz`;
233
+ }
234
+ log(`releaseAsarURL: ${_url}`);
235
+ src = await download(_url, "buffer");
236
+ }
237
+ log("start verify");
238
+ if (!verify(src, signature)) {
233
239
  throw new Error("file broken, invalid signature!");
234
240
  }
235
- log("[updater] write file", gzipPath);
236
- await writeFile(gzipPath, buffer);
237
- log("[updater] extract file", gzipPath);
241
+ log(`write file: ${gzipPath}`);
242
+ await writeFile(gzipPath, src);
243
+ log(`extract file: ${gzipPath}`);
238
244
  await extractFile(gzipPath);
239
- return "success";
245
+ updater.emit("downloaded");
240
246
  }
241
- const onCheck = async (option) => {
247
+ const onCheck = async (url) => {
242
248
  try {
243
- const result = await checkUpdate(option);
249
+ const result = await checkUpdate(url);
244
250
  updater.emit("checkResult", result);
245
251
  } catch (error) {
246
- updater.emit("checkResult", "fail", error);
252
+ log(error);
253
+ updater.emit("checkResult", error);
247
254
  }
248
255
  };
249
256
  updater.on("check", onCheck);
250
257
  updater.checkUpdate = onCheck;
258
+ const onDownload = async (src) => {
259
+ try {
260
+ await downloadUpdate(src);
261
+ } catch (error) {
262
+ log(error);
263
+ updater.emit("donwnloadError", error);
264
+ }
265
+ };
266
+ updater.on("download", onDownload);
267
+ updater.downloadUpdate = onDownload;
251
268
  return updater;
252
269
  }
253
270
 
@@ -273,7 +290,9 @@ export {
273
290
  getAppAsarPath,
274
291
  getAppVersion,
275
292
  getEntryVersion,
276
- getReleaseCdnLink,
293
+ getGithubReleaseCdnGroup,
277
294
  initApp,
278
- requireNative
295
+ parseGithubCdnURL,
296
+ requireNative,
297
+ restartApp
279
298
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "electron-incremental-update",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "electron incremental update tools, powered by vite",
5
5
  "scripts": {
6
6
  "build": "tsup",