electron-incremental-update 0.2.2 → 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
@@ -37,7 +37,8 @@ __export(src_exports, {
37
37
  getGithubReleaseCdnGroup: () => getGithubReleaseCdnGroup,
38
38
  initApp: () => initApp,
39
39
  parseGithubCdnURL: () => parseGithubCdnURL,
40
- requireNative: () => requireNative
40
+ requireNative: () => requireNative,
41
+ restartApp: () => restartApp
41
42
  });
42
43
  module.exports = __toCommonJS(src_exports);
43
44
  var import_node_path2 = require("path");
@@ -62,7 +63,6 @@ function downloadJSONDefault(url, updater, headers) {
62
63
  res.headers = headers;
63
64
  res.on("data", (chunk) => data += chunk);
64
65
  res.on("end", () => {
65
- updater.emit("downloadEnd", true);
66
66
  const json = JSON.parse(data);
67
67
  if ("signature" in json && "version" in json && "size" in json) {
68
68
  resolve2(json);
@@ -71,8 +71,6 @@ function downloadJSONDefault(url, updater, headers) {
71
71
  }
72
72
  });
73
73
  }).on("error", (e) => {
74
- e && updater.emit("donwnloadError", e);
75
- updater.emit("downloadEnd", false);
76
74
  reject(e);
77
75
  });
78
76
  });
@@ -87,12 +85,9 @@ function downloadBufferDefault(url, updater, headers) {
87
85
  data.push(chunk);
88
86
  });
89
87
  res.on("end", () => {
90
- updater.emit("downloadEnd", true);
91
88
  resolve2(import_node_buffer.Buffer.concat(data));
92
89
  });
93
90
  }).on("error", (e) => {
94
- e && updater.emit("donwnloadError", e);
95
- updater.emit("downloadEnd", false);
96
91
  reject(e);
97
92
  });
98
93
  });
@@ -164,6 +159,10 @@ function getGithubReleaseCdnGroup() {
164
159
  { cdnPrefix: "download.nuaa.cf", maintainer: "LibraryCloud-nuaa" }
165
160
  ];
166
161
  }
162
+ function restartApp() {
163
+ import_electron.app.relaunch();
164
+ import_electron.app.quit();
165
+ }
167
166
 
168
167
  // src/updater/index.ts
169
168
  function createUpdater({
@@ -177,9 +176,12 @@ function createUpdater({
177
176
  compareVersion
178
177
  }) {
179
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");
180
182
  const { downloadBuffer, downloadJSON, extraHeader, userAgent } = downloadConfig || {};
181
- function log(...args) {
182
- debug && console.log(...args);
183
+ function log(msg) {
184
+ debug && updater.emit("debug", msg);
183
185
  }
184
186
  async function download(url, format) {
185
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";
@@ -188,7 +190,7 @@ function createUpdater({
188
190
  UserAgent: ua,
189
191
  ...extraHeader
190
192
  };
191
- log("[updater] headers", headers);
193
+ log(`headers: ${headers}`);
192
194
  const downloadFn = format === "json" ? downloadJSON ?? downloadJSONDefault : downloadBuffer ?? downloadBufferDefault;
193
195
  return await downloadFn(url, updater, headers);
194
196
  }
@@ -202,89 +204,105 @@ function createUpdater({
202
204
  const input = (0, import_node_fs2.createReadStream)(gzipFilePath);
203
205
  const outputFilePath = gzipFilePath.replace(".tmp.gz", ".asar");
204
206
  const output = (0, import_node_fs2.createWriteStream)(outputFilePath);
205
- log("[updater] outputFilePath", outputFilePath);
207
+ log(`outputFilePath: ${outputFilePath}`);
206
208
  input.pipe(gunzip).pipe(output).on("finish", async () => {
207
209
  await (0, import_promises.rm)(gzipFilePath);
208
- log("[updater] finish");
210
+ log("finish");
209
211
  resolve2(outputFilePath);
210
212
  }).on("error", async (err) => {
211
213
  await (0, import_promises.rm)(gzipFilePath);
212
- log("[updater] error", err);
214
+ log(`error: ${err}`);
213
215
  output.destroy(err);
214
216
  reject(err);
215
217
  });
216
218
  });
217
219
  }
218
- function verify(buffer, signature) {
219
- log("[updater] signature", signature);
220
- 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");
221
223
  }
222
224
  function needUpdate(version) {
223
225
  if (!version || !import_electron2.app.isPackaged) {
224
226
  return false;
225
227
  }
226
228
  const currentVersion = getEntryVersion();
227
- log("[updater] currentVersion", currentVersion);
228
- log("[updater] newVersion", version);
229
+ log(`currentVersion: ${currentVersion}`);
230
+ log(`newVersion: ${version}`);
229
231
  const _compare = compareVersion ?? compareVersionDefault;
230
232
  return _compare(currentVersion, version);
231
233
  }
232
- async function checkUpdate(option) {
233
- let {
234
- updateJsonURL = _update,
235
- releaseAsarURL = _release
236
- } = option || {};
237
- if (!updateJsonURL || !releaseAsarURL) {
238
- 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");
239
238
  if (!repository) {
240
- throw new Error("updateJsonURL or releaseAsarURL are not set");
239
+ throw new Error("updateJsonURL or repository are not set");
241
240
  }
242
- updateJsonURL = `${repository.replace("github.com", "raw.githubusercontent.com")}/version.json`;
243
- releaseAsarURL = `${repository}/releases/download/latest/${productName}.asar.gz`;
241
+ url = `${repository.replace("github.com", "raw.githubusercontent.com")}/version.json`;
244
242
  }
245
- log("[updater] updateJsonURL", updateJsonURL);
246
- log("[updater] releaseAsarURL", releaseAsarURL);
247
- const gzipPath = `../${productName}.asar.gz`;
248
- const tmpFile = gzipPath.replace(".asar.gz", ".tmp.gz");
243
+ log(`updateJsonURL: ${url}`);
249
244
  if ((0, import_node_fs2.existsSync)(tmpFile)) {
250
- log("[updater] remove tmp file", tmpFile);
245
+ log(`remove tmp file: ${tmpFile}`);
251
246
  await (0, import_promises.rm)(tmpFile);
252
247
  }
253
- const json = await download(updateJsonURL, "json");
254
- if (!json) {
255
- throw new Error("fetch update json failed");
256
- }
248
+ const json = await download(url, "json");
257
249
  const {
258
- signature,
250
+ signature: _sig,
259
251
  version,
260
252
  size
261
253
  } = json;
262
- log("[updater] UpdateJSON", json);
254
+ log(`UpdateJSON: ${JSON.stringify(json, null, 2)}`);
263
255
  if (!needUpdate(version)) {
264
- return "unavailable";
256
+ return false;
257
+ } else {
258
+ signature = _sig;
259
+ return { size, version };
265
260
  }
266
- updater.emit("downloadStart", size);
267
- const buffer = await download(releaseAsarURL, "buffer");
268
- log("[updater] start verify");
269
- 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)) {
270
277
  throw new Error("file broken, invalid signature!");
271
278
  }
272
- log("[updater] write file", gzipPath);
273
- await (0, import_promises.writeFile)(gzipPath, buffer);
274
- log("[updater] extract file", gzipPath);
279
+ log(`write file: ${gzipPath}`);
280
+ await (0, import_promises.writeFile)(gzipPath, src);
281
+ log(`extract file: ${gzipPath}`);
275
282
  await extractFile(gzipPath);
276
- return "success";
283
+ updater.emit("downloaded");
277
284
  }
278
- const onCheck = async (option) => {
285
+ const onCheck = async (url) => {
279
286
  try {
280
- const result = await checkUpdate(option);
287
+ const result = await checkUpdate(url);
281
288
  updater.emit("checkResult", result);
282
289
  } catch (error) {
283
- updater.emit("checkResult", "fail", error);
290
+ log(error);
291
+ updater.emit("checkResult", error);
284
292
  }
285
293
  };
286
294
  updater.on("check", onCheck);
287
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;
288
306
  return updater;
289
307
  }
290
308
 
@@ -314,5 +332,6 @@ function initApp(productName, updater, option) {
314
332
  getGithubReleaseCdnGroup,
315
333
  initApp,
316
334
  parseGithubCdnURL,
317
- requireNative
335
+ requireNative,
336
+ restartApp
318
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 {
@@ -133,6 +140,7 @@ declare function getGithubReleaseCdnGroup(): {
133
140
  cdnPrefix: string;
134
141
  maintainer: string;
135
142
  }[];
143
+ declare function restartApp(): void;
136
144
 
137
145
  declare function createUpdater({ SIGNATURE_PUB, repository, productName, releaseAsarURL: _release, updateJsonURL: _update, debug, downloadConfig, compareVersion, }: UpdaterOption): Updater;
138
146
 
@@ -193,4 +201,4 @@ declare function initApp(productName: string, updater: Updater | Omit<UpdaterOpt
193
201
  productName?: string;
194
202
  }, option?: AppOption): any;
195
203
 
196
- export { BaseOption, CheckResultType, UpdateJSON, Updater, UpdaterOption, createUpdater, getAppAsarPath, getAppVersion, getEntryVersion, getGithubReleaseCdnGroup, initApp, parseGithubCdnURL, 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
  });
@@ -127,6 +121,10 @@ function getGithubReleaseCdnGroup() {
127
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
 
@@ -276,5 +293,6 @@ export {
276
293
  getGithubReleaseCdnGroup,
277
294
  initApp,
278
295
  parseGithubCdnURL,
279
- requireNative
296
+ requireNative,
297
+ restartApp
280
298
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "electron-incremental-update",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "electron incremental update tools, powered by vite",
5
5
  "scripts": {
6
6
  "build": "tsup",