electron-incremental-update 2.0.0-beta.9 → 2.0.1

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.d.ts CHANGED
@@ -1,37 +1,18 @@
1
1
  import { EventEmitter } from 'node:events';
2
- import { U as UpdateJSON, a as UpdateInfo } from './version-DgfjJQUx.js';
3
- import { I as IProvider, D as DownloadingInfo } from './types-D7OK98ln.js';
4
- import '@subframe7536/type-utils';
2
+ import { U as UpdateInfo, a as UpdateJSON } from './version-BYVQ367i.js';
3
+ import { I as IProvider, D as DownloadingInfo } from './types-BLdN9rkY.js';
4
+ import { Promisable } from '@subframe7536/type-utils';
5
5
 
6
6
  declare const ErrorInfo: {
7
- readonly download: "Download failed";
8
- readonly validate: "Validate failed";
9
- readonly param: "Missing params";
10
- readonly network: "Network error";
7
+ readonly download: "Download Failed";
8
+ readonly validate: "Validate Failed";
9
+ readonly param: "Missing Params";
10
+ readonly network: "Network Error";
11
11
  };
12
12
  declare class UpdaterError extends Error {
13
13
  code: keyof typeof ErrorInfo;
14
14
  constructor(msg: keyof typeof ErrorInfo, info: string);
15
15
  }
16
- type CheckResult<T extends UpdateJSON> = {
17
- success: true;
18
- data: Omit<T, 'beta'>;
19
- } | {
20
- success: false;
21
- /**
22
- * minimal version that can update
23
- */
24
- data: string;
25
- } | {
26
- success: false;
27
- data: UpdaterError;
28
- };
29
- type DownloadResult = {
30
- success: true;
31
- } | {
32
- success: false;
33
- data: UpdaterError;
34
- };
35
16
  interface Logger {
36
17
  info: (msg: string) => void;
37
18
  debug: (msg: string) => void;
@@ -40,81 +21,82 @@ interface Logger {
40
21
  }
41
22
  interface UpdaterOption {
42
23
  /**
43
- * update provider
24
+ * Update provider
25
+ *
26
+ * If you will not setup `UpdateJSON` or `Buffer` in params when checking update or download, this option is **required**
44
27
  */
45
28
  provider?: IProvider;
46
29
  /**
47
- * public key of signature, which will be auto generated by plugin,
30
+ * Certifaction key of signature, which will be auto generated by plugin,
48
31
  * generate by `selfsigned` if not set
49
32
  */
50
33
  SIGNATURE_CERT?: string;
51
34
  /**
52
- * whether to receive beta update
35
+ * Whether to receive beta update
53
36
  */
54
37
  receiveBeta?: boolean;
38
+ /**
39
+ * Updater logger
40
+ */
55
41
  logger?: Logger;
56
42
  }
57
43
 
58
44
  declare class Updater extends EventEmitter<{
59
45
  'checking': any;
60
46
  'update-available': [data: UpdateInfo];
61
- 'update-unavailable': [reason: string];
47
+ 'update-not-available': [reason: string, data?: UpdateInfo];
62
48
  'error': [error: UpdaterError];
63
49
  'download-progress': [info: DownloadingInfo];
64
50
  'update-downloaded': any;
51
+ 'update-cancelled': any;
65
52
  }> {
66
53
  private CERT;
54
+ private controller;
67
55
  private info?;
68
56
  provider?: IProvider;
69
57
  /**
70
- * updater logger
58
+ * Updater logger
71
59
  */
72
60
  logger?: Logger;
73
61
  /**
74
- * whether to receive beta update
62
+ * Whether to receive beta update
75
63
  */
76
64
  receiveBeta?: boolean;
77
65
  /**
78
- * whether force update in DEV
66
+ * Whether force update in DEV
79
67
  */
80
68
  forceUpdate?: boolean;
81
69
  /**
82
- * initialize incremental updater
70
+ * Initialize incremental updater
83
71
  * @param options UpdaterOption
84
72
  */
85
73
  constructor(options?: UpdaterOption);
86
- private checkProvider;
87
- /**
88
- * this function is used to parse download data.
89
- * - if format is `'json'`
90
- * - if data is `UpdateJSON`, return it
91
- * - if data is string or absent, download URL data and return it
92
- * - if format is `'buffer'`
93
- * - if data is `Buffer`, return it
94
- * - if data is string or absent, download URL data and return it
95
- * @param format 'json' or 'buffer'
96
- * @param data download URL or update json or buffer
74
+ /**
75
+ * This function is used to parse download data.
76
+ *
77
+ * if data is absent, download URL from provider and return it,
78
+ * else if data is `UpdateJSON`, return it
97
79
  */
98
80
  private fetch;
99
81
  /**
100
- * handle error message and emit error event
82
+ * Handle error message and emit error event
101
83
  */
102
84
  private err;
103
85
  /**
104
- * check update info using default options
86
+ * Check update info using default options
105
87
  */
106
- checkUpdate(): Promise<boolean>;
88
+ checkForUpdates(): Promise<boolean>;
107
89
  /**
108
- * check update info using existing update json
90
+ * Check update info using existing update json
109
91
  * @param data existing update json
110
92
  */
111
- checkUpdate(data: UpdateJSON): Promise<boolean>;
93
+ checkForUpdates(data: UpdateJSON): Promise<boolean>;
112
94
  /**
113
- * download update using default options
95
+ * Download update using default options
114
96
  */
115
97
  downloadUpdate(): Promise<boolean>;
116
98
  /**
117
- * download update using existing `asar.gz` buffer and signature
99
+ * Download update using existing `asar.gz` buffer and signature
118
100
  * @param data existing `asar.gz` buffer
119
101
  * @param info update info
120
102
  */
@@ -123,46 +105,52 @@ declare class Updater extends EventEmitter<{
123
105
  * quit App and install
124
106
  */
125
107
  quitAndInstall(): void;
108
+ cancel(): void;
126
109
  }
127
110
  /**
128
- * auto check update, download and install
111
+ * Auto check update, download and install
129
112
  */
130
113
  declare function autoUpdate(updater: Updater): Promise<void>;
131
114
 
132
- type Promisable<T> = T | Promise<T>;
133
115
  /**
134
- * hooks on rename temp asar path to `${app.name}.asar`
116
+ * Hooks on rename temp asar path to `${app.name}.asar`
135
117
  * @param install `() => renameSync(tempAsarPath, appNameAsarPath)`
136
118
  * @param tempAsarPath temp(updated) asar path
137
119
  * @param appNameAsarPath `${app.name}.asar` path
138
120
  * @param logger logger
139
- * @default install(); logger.info(`update success!`)
121
+ * @default install(); logger.info('update success!')
140
122
  */
141
123
  type OnInstallFunction = (install: VoidFunction, tempAsarPath: string, appNameAsarPath: string, logger?: Logger) => Promisable<void>;
142
124
  interface AppOption {
143
125
  /**
144
- * updater options
126
+ * Path to index file that make {@link startupWithUpdater} as default export
127
+ *
128
+ * Generate from plugin configuration by default
129
+ */
130
+ mainPath?: string;
131
+ /**
132
+ * Updater options
145
133
  */
146
134
  updater?: (() => Promisable<Updater>) | UpdaterOption;
147
135
  /**
148
- * hooks on rename temp asar path to `${app.name}.asar`
136
+ * Hooks on rename temp asar path to `${app.name}.asar`
149
137
  */
150
138
  onInstall?: OnInstallFunction;
151
139
  /**
152
- * hooks before app start up
140
+ * Hooks before app startup
153
141
  * @param mainFilePath main file path of `${app.name}.asar`
154
142
  * @param logger logger
155
143
  */
156
144
  beforeStart?: (mainFilePath: string, logger?: Logger) => Promisable<void>;
157
145
  /**
158
- * hooks on app start up error
146
+ * Hooks on app startup error
159
147
  * @param err installing or startup error
160
148
  * @param logger logger
161
149
  */
162
150
  onStartError?: (err: unknown, logger?: Logger) => void;
163
151
  }
164
152
  /**
165
- * utils for startuping with updater
153
+ * Utils to startup with updater
166
154
  * @param fn startup function
167
155
  * @example
168
156
  * // in electron/main/index.ts
@@ -172,28 +160,24 @@ interface AppOption {
172
160
  */
173
161
  declare function startupWithUpdater(fn: (updater: Updater) => Promisable<void>): (updater: Updater) => Promisable<void>;
174
162
  /**
175
- * initialize app
163
+ * Initialize Electron with updater
176
164
  * @example
177
- * ```ts
178
- * import { getGithubReleaseCdnGroup, initApp, parseGithubCdnURL } from 'electron-incremental-update'
179
- * import { repository } from '../package.json'
180
- *
181
- * const { cdnPrefix: asarPrefix } = getGithubReleaseCdnGroup()[0]
182
- * const { cdnPrefix: jsonPrefix } = getGithubFileCdnGroup()[0]
183
- *
184
- * initApp({
185
- * // can be updater option or function that return updater
165
+ * createElectronApp({
186
166
  * updater: {
187
- * SIGNATURE_CERT: 'custom certificate',
188
- * repository,
189
- * updateJsonURL: parseGithubCdnURL(repository, jsonPrefix, 'version.json'),
190
- * releaseAsarURL: parseGithubCdnURL(repository, asarPrefix, `download/latest/${app.name}.asar.gz`),
191
- * receiveBeta: true,
167
+ * provider: new GitHubProvider({
168
+ * username: 'yourname',
169
+ * repo: 'electron',
170
+ * }),
171
+ * },
172
+ * beforeStart(mainFilePath, logger) {
173
+ * logger?.debug(mainFilePath)
192
174
  * },
193
- * onStart: console.log
194
175
  * })
195
- * ```
196
176
  */
197
- declare function initApp(appOptions: AppOption): Promise<void>;
177
+ declare function createElectronApp(appOptions?: AppOption): Promise<void>;
178
+ /**
179
+ * @alias {@link createElectronApp}
180
+ */
181
+ declare const initApp: typeof createElectronApp;
198
182
 
199
- export { type AppOption, type CheckResult, type DownloadResult, ErrorInfo, type Logger, Updater, UpdaterError, type UpdaterOption, autoUpdate, initApp, startupWithUpdater };
183
+ export { type AppOption, ErrorInfo, type Logger, Updater, UpdaterError, type UpdaterOption, autoUpdate, createElectronApp, initApp, startupWithUpdater };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { isDev, getEntryVersion, getAppVersion, getPathFromAppNameAsar, restartApp } from './chunk-4MH6ZXCY.js';
2
- import { isUpdateJSON, __require } from './chunk-72ZAJ7AF.js';
1
+ import { isDev, getEntryVersion, getAppVersion, getPathFromAppNameAsar, restartApp } from './chunk-GTDMND3I.js';
2
+ import { isUpdateJSON, __require } from './chunk-RCRKUKFX.js';
3
3
  import fs2 from 'node:fs';
4
4
  import { EventEmitter } from 'node:events';
5
5
  import { app } from 'electron';
@@ -7,38 +7,39 @@ import path from 'node:path';
7
7
 
8
8
  // src/entry/types.ts
9
9
  var ErrorInfo = {
10
- download: "Download failed",
11
- validate: "Validate failed",
12
- param: "Missing params",
13
- network: "Network error"
10
+ download: "Download Failed",
11
+ validate: "Validate Failed",
12
+ param: "Missing Params",
13
+ network: "Network Error"
14
14
  };
15
15
  var UpdaterError = class extends Error {
16
16
  code;
17
17
  constructor(msg, info) {
18
- super(ErrorInfo[msg] + ": " + info);
18
+ super(`[${ErrorInfo[msg]}] ${info}`);
19
19
  this.code = msg;
20
20
  }
21
21
  };
22
22
 
23
23
  // src/entry/updater.ts
24
24
  var Updater = class extends EventEmitter {
25
- CERT = __EIU_SIGNATURE_CERT__;
25
+ CERT;
26
+ controller;
26
27
  info;
27
28
  provider;
28
29
  /**
29
- * updater logger
30
+ * Updater logger
30
31
  */
31
32
  logger;
32
33
  /**
33
- * whether to receive beta update
34
+ * Whether to receive beta update
34
35
  */
35
36
  receiveBeta;
36
37
  /**
37
- * whether force update in DEV
38
+ * Whether force update in DEV
38
39
  */
39
40
  forceUpdate;
40
41
  /**
41
- * initialize incremental updater
42
+ * Initialize incremental updater
42
43
  * @param options UpdaterOption
43
44
  */
44
45
  constructor(options = {}) {
@@ -47,6 +48,7 @@ var Updater = class extends EventEmitter {
47
48
  this.receiveBeta = options.receiveBeta;
48
49
  this.CERT = options.SIGNATURE_CERT || __EIU_SIGNATURE_CERT__;
49
50
  this.logger = options.logger;
51
+ this.controller = new AbortController();
50
52
  if (isDev && !this.logger) {
51
53
  this.logger = {
52
54
  info: (...args) => console.log("[EIU-INFO ]", ...args),
@@ -54,15 +56,10 @@ var Updater = class extends EventEmitter {
54
56
  warn: (...args) => console.log("[EIU-WARN ]", ...args),
55
57
  error: (...args) => console.error("[EIU-ERROR]", ...args)
56
58
  };
57
- this.logger.info("no logger set, enable dev-only logger");
59
+ this.logger.info("No logger set, enable dev-only logger");
58
60
  }
59
61
  if (!this.provider) {
60
- this.logger?.debug("No update provider, please setup provider before checking update");
61
- }
62
- }
63
- checkProvider() {
64
- if (!this.provider) {
65
- throw new UpdaterError("param", "missing update provider");
62
+ this.logger?.debug("WARN: No update provider");
66
63
  }
67
64
  }
68
65
  async fetch(format, data) {
@@ -70,92 +67,99 @@ var Updater = class extends EventEmitter {
70
67
  if (format === "json" && isUpdateJSON(data) || format === "buffer" && Buffer.isBuffer(data)) {
71
68
  return data;
72
69
  } else {
73
- this.err("invalid type", "param", `invalid type at format '${format}': ${JSON.stringify(data)}`);
70
+ this.err("Invalid type", "param", `Invalid type at format '${format}': ${JSON.stringify(data)}`);
74
71
  return;
75
72
  }
76
73
  }
77
- this.logger?.debug(`download from ${this.provider.name}`);
74
+ this.logger?.debug(`Download from \`${this.provider.name}\``);
78
75
  try {
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));
80
- this.logger?.debug(`download ${format} success${format === "buffer" ? `, file size: ${result.length}` : ""}`);
76
+ const result = format === "json" ? await this.provider.downloadJSON(__EIU_VERSION_PATH__, this.controller.signal) : await this.provider.downloadAsar(app.name, this.info, this.controller.signal, (info) => this.emit("download-progress", info));
77
+ this.logger?.debug(`Download ${format} success${format === "buffer" ? `, file size: ${result.length}` : ""}`);
81
78
  return result;
82
79
  } catch (e) {
83
- this.err(`fetch ${format} failed`, "network", `download ${format} failed: ${e}`);
80
+ this.err(`Fetch ${format} failed`, "network", e instanceof Error ? e.message : e.toString());
84
81
  }
85
82
  }
86
83
  /**
87
- * handle error message and emit error event
84
+ * Handle error message and emit error event
88
85
  */
89
86
  err(msg, code, errorInfo) {
90
87
  const err = new UpdaterError(code, errorInfo);
91
88
  this.logger?.error(msg, err);
92
89
  this.emit("error", err);
93
90
  }
94
- async checkUpdate(data) {
95
- this.checkProvider();
96
- const emitUnavailable = (msg) => {
91
+ async checkForUpdates(data) {
92
+ const emitUnavailable = (msg, info2) => {
97
93
  this.logger?.info(msg);
98
- this.emit("update-unavailable", msg);
94
+ this.emit("update-not-available", msg, info2);
99
95
  return false;
100
96
  };
97
+ if (!data && !this.provider) {
98
+ this.err("Check update failed", "param", "No update json or provider");
99
+ return false;
100
+ }
101
101
  const _data = await this.fetch("json", data);
102
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;
103
+ return emitUnavailable("Failed to get update info");
110
104
  }
111
- this.logger?.debug(`checked update, version: ${version}, signature: ${signature}`);
105
+ const { signature, version, minimumVersion } = this.receiveBeta ? _data.beta : _data;
106
+ const info = { signature, minimumVersion, version };
107
+ this.logger?.debug(`Checked update, version: ${version}, signature: ${signature}`);
112
108
  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");
109
+ return emitUnavailable("Skip check update in dev mode. To force update, set `updater.forceUpdate` to true or call checkUpdate with UpdateJSON", info);
114
110
  }
115
111
  const isLowerVersion = this.provider.isLowerVersion;
116
112
  const entryVersion = getEntryVersion();
117
113
  const appVersion = getAppVersion();
118
- if (isLowerVersion(entryVersion, minimumVersion)) {
119
- return emitUnavailable(`entry version (${entryVersion}) < minimumVersion (${minimumVersion})`);
120
- }
121
- this.logger?.info(`check update: current version is ${appVersion}, new version is ${version}`);
122
- if (!isLowerVersion(appVersion, version)) {
123
- return emitUnavailable(`current version (${appVersion}) < new version (${version})`);
114
+ try {
115
+ if (isLowerVersion(entryVersion, minimumVersion)) {
116
+ return emitUnavailable(`Entry Version (${entryVersion}) < MinimumVersion (${minimumVersion})`, info);
117
+ }
118
+ this.logger?.info(`Check update: current version is ${appVersion}, new version is ${version}`);
119
+ if (!isLowerVersion(appVersion, version)) {
120
+ return emitUnavailable(`Current version (${appVersion}) < New version (${version})`, info);
121
+ }
122
+ this.logger?.info(`Update available: ${version}`);
123
+ this.emit("update-available", info);
124
+ this.info = info;
125
+ return true;
126
+ } catch {
127
+ this.err("Fail to parse version", "validate", "Fail to parse version string");
128
+ return false;
124
129
  }
125
- this.logger?.info(`update available: ${version}`);
126
- this.info = { signature, minimumVersion, version };
127
- this.emit("update-available", this.info);
128
- return true;
129
130
  }
130
131
  async downloadUpdate(data, info) {
131
- this.checkProvider();
132
132
  const _sig = info?.signature ?? this.info?.signature;
133
133
  const _version = info?.version ?? this.info?.version;
134
134
  if (!_sig || !_version) {
135
- this.err("download failed", "param", "no update signature, please call `checkUpdate` first or manually setup params");
135
+ this.err("Download failed", "param", "No update signature, please call `checkUpdate` first or manually setup params");
136
+ return false;
137
+ }
138
+ if (!data && !this.provider) {
139
+ this.err("Download failed", "param", "No update asar buffer and provider");
136
140
  return false;
137
141
  }
138
142
  const buffer = await this.fetch("buffer", data ? Buffer.from(data) : void 0);
139
143
  if (!buffer) {
140
- this.err("download failed", "param", "no update asar file buffer");
144
+ this.err("Download failed", "param", "No update asar file buffer");
141
145
  return false;
142
146
  }
143
147
  this.logger?.debug("verify start");
144
148
  if (!await this.provider.verifySignaure(buffer, _version, _sig, this.CERT)) {
145
- this.err("download failed", "validate", "invalid update asar file");
149
+ this.err("Download failed", "validate", "Invalid update asar file");
146
150
  return false;
147
151
  }
148
- this.logger?.debug("verify success");
152
+ this.logger?.debug("Verify success");
149
153
  try {
150
- const tmpFilePath = getPathFromAppNameAsar() + ".tmp";
151
- this.logger?.debug(`install to ${tmpFilePath}`);
154
+ const tmpFilePath = `${getPathFromAppNameAsar()}.tmp`;
155
+ this.logger?.debug(`Install to ${tmpFilePath}`);
152
156
  fs2.writeFileSync(tmpFilePath, await this.provider.unzipFile(buffer));
153
- this.logger?.info(`download success, version: ${_version}`);
157
+ this.logger?.info(`Download success, version: ${_version}`);
154
158
  this.info = void 0;
155
159
  this.emit("update-downloaded");
156
160
  return true;
157
161
  } catch (error) {
158
- this.err("download failed", "download", `fail to unwrap asar file, ${error}`);
162
+ this.err("Download failed", "download", `Fail to unwrap asar file, ${error}`);
159
163
  return false;
160
164
  }
161
165
  }
@@ -163,12 +167,21 @@ var Updater = class extends EventEmitter {
163
167
  * quit App and install
164
168
  */
165
169
  quitAndInstall() {
166
- this.logger?.info("quit and install");
170
+ this.logger?.info("Quit and install");
167
171
  restartApp();
168
172
  }
173
+ cancel() {
174
+ if (this.controller.signal.aborted) {
175
+ return;
176
+ }
177
+ this.controller.abort();
178
+ this.logger?.info("Cancel update");
179
+ this.emit("update-cancelled");
180
+ this.controller = new AbortController();
181
+ }
169
182
  };
170
183
  async function autoUpdate(updater) {
171
- if (await updater.checkUpdate() && await updater.downloadUpdate()) {
184
+ if (await updater.checkForUpdates() && await updater.downloadUpdate()) {
172
185
  updater.quitAndInstall();
173
186
  }
174
187
  }
@@ -179,8 +192,14 @@ var defaultOnInstall = (install, _, __, logger) => {
179
192
  install();
180
193
  logger?.info(`update success!`);
181
194
  };
182
- async function initApp(appOptions) {
195
+ async function createElectronApp(appOptions = {}) {
196
+ const appNameAsarPath = getPathFromAppNameAsar();
183
197
  const {
198
+ mainPath = path.join(
199
+ isDev ? path.join(app.getAppPath(), __EIU_MAIN_DEV_DIR__) : appNameAsarPath,
200
+ "main",
201
+ __EIU_MAIN_FILE__
202
+ ),
184
203
  updater,
185
204
  onInstall = defaultOnInstall,
186
205
  beforeStart,
@@ -194,24 +213,23 @@ async function initApp(appOptions) {
194
213
  }
195
214
  const logger = updaterInstance.logger;
196
215
  try {
197
- const appNameAsarPath = getPathFromAppNameAsar();
198
216
  const tempAsarPath = `${appNameAsarPath}.tmp`;
199
217
  if (fs2.existsSync(tempAsarPath)) {
200
- logger?.info(`installing new asar: ${tempAsarPath}`);
218
+ logger?.info(`Installing new asar from ${tempAsarPath}`);
201
219
  await onInstall(() => fs2.renameSync(tempAsarPath, appNameAsarPath), tempAsarPath, appNameAsarPath, logger);
202
220
  }
203
- const mainFilePath = path.join(
204
- isDev ? path.join(app.getAppPath(), __EIU_MAIN_DEV_DIR__) : appNameAsarPath,
205
- "main",
206
- __EIU_MAIN_FILE__
207
- );
208
- await beforeStart?.(mainFilePath, logger);
209
- __require(mainFilePath)(updaterInstance);
221
+ await beforeStart?.(mainPath, logger);
222
+ if (__EIU_IS_ESM__) {
223
+ (await import(`file://${mainPath}`)).default(updaterInstance);
224
+ } else {
225
+ __require(mainPath)(updaterInstance);
226
+ }
210
227
  } catch (error) {
211
228
  logger?.error("startup error", error);
212
229
  onStartError?.(error, logger);
213
230
  app.quit();
214
231
  }
215
232
  }
233
+ var initApp = createElectronApp;
216
234
 
217
- export { ErrorInfo, Updater, UpdaterError, autoUpdate, initApp, startupWithUpdater };
235
+ export { ErrorInfo, Updater, UpdaterError, autoUpdate, createElectronApp, initApp, startupWithUpdater };