electron-incremental-update 2.0.0-beta.1 → 2.0.0-beta.10

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.js CHANGED
@@ -1,185 +1,167 @@
1
- import {
2
- __require,
3
- getAppVersion,
4
- getEntryVersion,
5
- getPathFromAppNameAsar,
6
- isDev,
7
- isUpdateJSON,
8
- restartApp,
9
- unzipFile
10
- } from "./chunk-RSLOPAIZ.js";
1
+ import { isDev, getEntryVersion, getAppVersion, getPathFromAppNameAsar, restartApp } from './chunk-4MH6ZXCY.js';
2
+ import { isUpdateJSON, __require } from './chunk-72ZAJ7AF.js';
3
+ import fs2 from 'node:fs';
4
+ import { EventEmitter } from 'node:events';
5
+ import { app } from 'electron';
6
+ import path from 'node:path';
11
7
 
12
- // src/entry.ts
13
- import { join } from "node:path";
14
- import { existsSync as existsSync2, renameSync } from "node:fs";
15
- import { app as app2 } from "electron";
16
-
17
- // src/updater/core.ts
18
- import { existsSync, rmSync, writeFileSync } from "node:fs";
19
- import { app } from "electron";
20
-
21
- // src/updater/types.ts
8
+ // src/entry/types.ts
22
9
  var ErrorInfo = {
23
- download: "Download failed",
24
- validate: "Validate failed",
25
- param: "Missing params",
26
- version: "Unsatisfied version"
10
+ download: "Download Failed",
11
+ validate: "Validate Failed",
12
+ param: "Missing Params",
13
+ network: "Network Error"
27
14
  };
28
15
  var UpdaterError = class extends Error {
16
+ code;
29
17
  constructor(msg, info) {
30
- super(msg + ": " + info);
18
+ super("[" + ErrorInfo[msg] + "] " + info);
19
+ this.code = msg;
31
20
  }
32
21
  };
33
22
 
34
- // src/updater/core.ts
35
- var Updater = class {
23
+ // src/entry/updater.ts
24
+ var Updater = class extends EventEmitter {
36
25
  CERT = __EIU_SIGNATURE_CERT__;
37
26
  info;
38
- options;
39
- asarPath;
40
- gzipPath;
41
- tmpFilePath;
42
27
  provider;
43
28
  /**
44
- * updater logger
29
+ * Updater logger
45
30
  */
46
31
  logger;
47
32
  /**
48
- * downloading progress hook
49
- * @param progress download progress
50
- * @example
51
- * updater.onDownloading = ({ percent, total, current }) => {
52
- * console.log(`download progress: ${percent}, total: ${total}, current: ${current}`)
53
- * }
54
- */
55
- onDownloading;
56
- /**
57
- * URL handler hook
58
- *
59
- * for Github, there are some {@link https://github.com/XIU2/UserScript/blob/master/GithubEnhanced-High-Speed-Download.user.js#L34 public CDN links}
60
- * @param url source url
61
- * @param isDownloadAsar whether is download asar
33
+ * Whether to receive beta update
62
34
  */
63
- handleURL;
35
+ receiveBeta;
64
36
  /**
65
- * whether receive beta version
37
+ * Whether force update in DEV
66
38
  */
67
- get receiveBeta() {
68
- return !!this.options.receiveBeta;
69
- }
70
- set receiveBeta(receiveBeta) {
71
- this.options.receiveBeta = receiveBeta;
72
- }
39
+ forceUpdate;
73
40
  /**
74
- * initialize incremental updater
75
- * @param provider update provider
76
- * @param option UpdaterOption
41
+ * Initialize incremental updater
42
+ * @param options UpdaterOption
77
43
  */
78
- constructor(provider, option = {}) {
79
- this.provider = provider;
80
- this.options = option;
81
- if (option.SIGNATURE_CERT) {
82
- this.CERT = option.SIGNATURE_CERT;
44
+ constructor(options = {}) {
45
+ super();
46
+ this.provider = options.provider;
47
+ this.receiveBeta = options.receiveBeta;
48
+ this.CERT = options.SIGNATURE_CERT || __EIU_SIGNATURE_CERT__;
49
+ this.logger = options.logger;
50
+ if (isDev && !this.logger) {
51
+ this.logger = {
52
+ info: (...args) => console.log("[EIU-INFO ]", ...args),
53
+ debug: (...args) => console.log("[EIU-DEBUG]", ...args),
54
+ warn: (...args) => console.log("[EIU-WARN ]", ...args),
55
+ error: (...args) => console.error("[EIU-ERROR]", ...args)
56
+ };
57
+ this.logger.info("no logger set, enable dev-only logger");
83
58
  }
84
- if (option.logger) {
85
- this.logger = option.logger;
59
+ if (!this.provider) {
60
+ this.logger?.debug("No update provider, please setup provider before checking update");
86
61
  }
87
- this.asarPath = getPathFromAppNameAsar();
88
- this.gzipPath = `${this.asarPath}.gz`;
89
- this.tmpFilePath = `${this.asarPath}.tmp`;
90
62
  }
91
- async needUpdate(version, minVersion) {
92
- if (isDev) {
93
- this.logger?.warn(`in dev mode, skip check update`);
94
- return false;
95
- }
96
- const isLowerVersion = this.provider.isLowerVersion;
97
- const entryVersion = getEntryVersion();
98
- const appVersion = getAppVersion();
99
- if (await isLowerVersion(entryVersion, minVersion)) {
100
- throw new UpdaterError(ErrorInfo.version, `entry version (${entryVersion}) < minimumVersion (${minVersion})`);
63
+ checkProvider() {
64
+ if (!this.provider) {
65
+ throw new UpdaterError("param", "missing update provider");
101
66
  }
102
- this.logger?.info(`check update: current version is ${appVersion}, new version is ${version}`);
103
- return await isLowerVersion(appVersion, version);
104
67
  }
105
- async parseData(format, data) {
106
- if (existsSync(this.tmpFilePath)) {
107
- this.logger?.warn(`remove tmp file: ${this.tmpFilePath}`);
108
- rmSync(this.tmpFilePath);
109
- }
110
- if (existsSync(this.gzipPath)) {
111
- this.logger?.warn(`remove .gz file: ${this.gzipPath}`);
112
- rmSync(this.gzipPath);
113
- }
68
+ async fetch(format, data) {
114
69
  if (typeof data === "object") {
115
70
  if (format === "json" && isUpdateJSON(data) || format === "buffer" && Buffer.isBuffer(data)) {
116
71
  return data;
117
72
  } else {
118
- throw new UpdaterError(ErrorInfo.param, `invalid type at format '${format}': ${JSON.stringify(data)}`);
73
+ this.err("invalid type", "param", `invalid type at format '${format}': ${JSON.stringify(data)}`);
74
+ return;
119
75
  }
120
76
  }
121
77
  this.logger?.debug(`download from ${this.provider.name}`);
122
78
  try {
123
- const result = format === "json" ? await this.provider.downloadJSON(data ?? __EIU_VERSION_PATH__) : await this.provider.downloadBuffer(app.name, this.info, this.onDownloading);
79
+ const result = format === "json" ? await this.provider.downloadJSON(data ?? __EIU_VERSION_PATH__) : await this.provider.downloadAsar(app.name, this.info, (data2) => this.emit("download-progress", data2));
124
80
  this.logger?.debug(`download ${format} success${format === "buffer" ? `, file size: ${result.length}` : ""}`);
125
81
  return result;
126
82
  } catch (e) {
127
- this.logger?.warn(`download ${format} failed: ${e}`);
128
- throw new UpdaterError(ErrorInfo.download, `download ${format} failed: ${e}`);
83
+ this.err(`fetch ${format} failed`, "network", `download ${format} failed: ${e}`);
129
84
  }
130
85
  }
86
+ /**
87
+ * Handle error message and emit error event
88
+ */
89
+ err(msg, code, errorInfo) {
90
+ const err = new UpdaterError(code, errorInfo);
91
+ this.logger?.error(msg, err);
92
+ this.emit("error", err);
93
+ }
131
94
  async checkUpdate(data) {
95
+ this.checkProvider();
96
+ const emitUnavailable = (msg) => {
97
+ this.logger?.info(msg);
98
+ this.emit("update-unavailable", msg);
99
+ return false;
100
+ };
101
+ const _data = await this.fetch("json", data);
102
+ if (!_data) {
103
+ return emitUnavailable("failed to get update info");
104
+ }
105
+ let { signature, version, minimumVersion, beta } = _data;
106
+ if (this.receiveBeta) {
107
+ version = beta.version;
108
+ signature = beta.signature;
109
+ minimumVersion = beta.minimumVersion;
110
+ }
111
+ this.logger?.debug(`checked update, version: ${version}, signature: ${signature}`);
112
+ if (isDev && !this.forceUpdate && !data) {
113
+ return emitUnavailable("skip check update in dev mode, to force update, set `updater.forceUpdate` to true or call checkUpdate with UpdateJSON");
114
+ }
115
+ const isLowerVersion = this.provider.isLowerVersion;
116
+ const entryVersion = getEntryVersion();
117
+ const appVersion = getAppVersion();
132
118
  try {
133
- let { signature, size, version, minimumVersion, beta } = await this.parseData("json", data);
134
- if (this.receiveBeta) {
135
- version = beta.version;
136
- signature = beta.signature;
137
- minimumVersion = beta.minimumVersion;
138
- size = beta.size;
119
+ if (isLowerVersion(entryVersion, minimumVersion)) {
120
+ return emitUnavailable(`entry version (${entryVersion}) < minimumVersion (${minimumVersion})`);
139
121
  }
140
- this.logger?.debug(`checked update, version: ${version}, size: ${size}, signature: ${signature}`);
141
- if (!await this.needUpdate(version, minimumVersion)) {
142
- this.logger?.info(`update unavailable: ${version} is the latest version`);
143
- return { success: false, data: version };
144
- } else {
145
- this.logger?.info(`update available: ${version}`);
146
- this.info = { signature, minimumVersion, version, size };
147
- return { success: true, data: this.info };
122
+ this.logger?.info(`check update: current version is ${appVersion}, new version is ${version}`);
123
+ if (!isLowerVersion(appVersion, version)) {
124
+ return emitUnavailable(`current version (${appVersion}) < new version (${version})`);
148
125
  }
149
- } catch (error) {
150
- this.logger?.error("check update failed", error);
151
- return {
152
- success: false,
153
- data: error instanceof UpdaterError ? error : new UpdaterError(ErrorInfo.download, error.toString())
154
- };
126
+ this.logger?.info(`update available: ${version}`);
127
+ this.info = { signature, minimumVersion, version };
128
+ this.emit("update-available", this.info);
129
+ return true;
130
+ } catch {
131
+ this.err("Fail to parse version", "validate", "fail to parse version string");
132
+ return false;
155
133
  }
156
134
  }
157
- async download(data, sig) {
135
+ async downloadUpdate(data, info) {
136
+ this.checkProvider();
137
+ const _sig = info?.signature ?? this.info?.signature;
138
+ const _version = info?.version ?? this.info?.version;
139
+ if (!_sig || !_version) {
140
+ this.err("download failed", "param", "no update signature, please call `checkUpdate` first or manually setup params");
141
+ return false;
142
+ }
143
+ const buffer = await this.fetch("buffer", data ? Buffer.from(data) : void 0);
144
+ if (!buffer) {
145
+ this.err("download failed", "param", "no update asar file buffer");
146
+ return false;
147
+ }
148
+ this.logger?.debug("verify start");
149
+ if (!await this.provider.verifySignaure(buffer, _version, _sig, this.CERT)) {
150
+ this.err("download failed", "validate", "invalid update asar file");
151
+ return false;
152
+ }
153
+ this.logger?.debug("verify success");
158
154
  try {
159
- if (!this.info) {
160
- throw new UpdaterError(ErrorInfo.param, "no update info");
161
- }
162
- const _sig = sig ?? this.info.signature;
163
- const buffer = await this.parseData("buffer", data);
164
- this.logger?.debug("verify start");
165
- const _ver = await this.provider.verifySignaure(buffer, _sig, this.CERT);
166
- if (!_ver) {
167
- throw new UpdaterError(ErrorInfo.validate, "invalid signature or certificate");
168
- }
169
- this.logger?.debug("verify success");
170
- this.logger?.debug(`write to ${this.gzipPath}`);
171
- writeFileSync(this.gzipPath, buffer);
172
- this.logger?.debug(`extract to ${this.tmpFilePath}`);
173
- await unzipFile(this.gzipPath, this.tmpFilePath);
174
- this.logger?.info(`download success, version: ${_ver}`);
155
+ const tmpFilePath = getPathFromAppNameAsar() + ".tmp";
156
+ this.logger?.debug(`install to ${tmpFilePath}`);
157
+ fs2.writeFileSync(tmpFilePath, await this.provider.unzipFile(buffer));
158
+ this.logger?.info(`download success, version: ${_version}`);
175
159
  this.info = void 0;
176
- return { success: true };
160
+ this.emit("update-downloaded");
161
+ return true;
177
162
  } catch (error) {
178
- this.logger?.error("download asar failed", error);
179
- return {
180
- success: false,
181
- data: error instanceof UpdaterError ? error : new UpdaterError(ErrorInfo.download, error.toString())
182
- };
163
+ this.err("download failed", "download", `fail to unwrap asar file, ${error}`);
164
+ return false;
183
165
  }
184
166
  }
185
167
  /**
@@ -190,18 +172,20 @@ var Updater = class {
190
172
  restartApp();
191
173
  }
192
174
  };
193
-
194
- // src/entry.ts
175
+ async function autoUpdate(updater) {
176
+ if (await updater.checkUpdate() && await updater.downloadUpdate()) {
177
+ updater.quitAndInstall();
178
+ }
179
+ }
195
180
  function startupWithUpdater(fn) {
196
181
  return fn;
197
182
  }
198
183
  var defaultOnInstall = (install, _, __, logger) => {
199
184
  install();
200
- logger.info(`update success!`);
185
+ logger?.info(`update success!`);
201
186
  };
202
- async function initApp(appOptions) {
187
+ async function initApp(appOptions = {}) {
203
188
  const {
204
- provider,
205
189
  updater,
206
190
  onInstall = defaultOnInstall,
207
191
  beforeStart,
@@ -209,35 +193,30 @@ async function initApp(appOptions) {
209
193
  } = appOptions;
210
194
  let updaterInstance;
211
195
  if (typeof updater === "object" || !updater) {
212
- updaterInstance = new Updater(provider, updater);
196
+ updaterInstance = new Updater(updater);
213
197
  } else {
214
198
  updaterInstance = await updater();
215
199
  }
216
- const logger = updaterInstance.logger || console;
200
+ const logger = updaterInstance.logger;
217
201
  try {
218
202
  const appNameAsarPath = getPathFromAppNameAsar();
219
203
  const tempAsarPath = `${appNameAsarPath}.tmp`;
220
- if (existsSync2(tempAsarPath)) {
221
- logger.info(`installing new asar: ${tempAsarPath}`);
222
- await onInstall(() => renameSync(tempAsarPath, appNameAsarPath), tempAsarPath, appNameAsarPath, logger);
204
+ if (fs2.existsSync(tempAsarPath)) {
205
+ logger?.info(`installing new asar: ${tempAsarPath}`);
206
+ await onInstall(() => fs2.renameSync(tempAsarPath, appNameAsarPath), tempAsarPath, appNameAsarPath, logger);
223
207
  }
224
- const mainFilePath = join(
225
- isDev ? join(app2.getAppPath(), __EIU_MAIN_DEV_DIR__) : appNameAsarPath,
208
+ const mainFilePath = path.join(
209
+ isDev ? path.join(app.getAppPath(), __EIU_MAIN_DEV_DIR__) : appNameAsarPath,
226
210
  "main",
227
211
  __EIU_MAIN_FILE__
228
212
  );
229
213
  await beforeStart?.(mainFilePath, logger);
230
214
  __require(mainFilePath)(updaterInstance);
231
215
  } catch (error) {
232
- logger.error("startup error", error);
216
+ logger?.error("startup error", error);
233
217
  onStartError?.(error, logger);
234
- app2.quit();
218
+ app.quit();
235
219
  }
236
220
  }
237
- export {
238
- ErrorInfo,
239
- Updater,
240
- UpdaterError,
241
- initApp,
242
- startupWithUpdater
243
- };
221
+
222
+ export { ErrorInfo, Updater, UpdaterError, autoUpdate, initApp, startupWithUpdater };