electron-incremental-update 3.0.0-beta.5 → 3.0.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
@@ -1,12 +1,13 @@
1
- const require_version = require('./version-aPrLuz_-.cjs');
2
- const require_electron = require('./electron-C-qmVhAt.cjs');
3
- let electron = require("electron");
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_crypto = require("./crypto-BSky88mL.cjs");
3
+ const require_local = require("./local-DbXBG1D9.cjs");
4
+ const require_electron = require("./electron-CaS0I3S2.cjs");
4
5
  let node_fs = require("node:fs");
5
- node_fs = require_version.__toESM(node_fs);
6
+ node_fs = require_crypto.__toESM(node_fs, 1);
6
7
  let node_path = require("node:path");
7
- node_path = require_version.__toESM(node_path);
8
+ node_path = require_crypto.__toESM(node_path, 1);
9
+ let electron = require("electron");
8
10
  let node_events = require("node:events");
9
-
10
11
  //#region src/entry/types.ts
11
12
  var UpdaterError = class extends Error {
12
13
  code;
@@ -15,12 +16,12 @@ var UpdaterError = class extends Error {
15
16
  this.code = code;
16
17
  }
17
18
  };
18
-
19
19
  //#endregion
20
20
  //#region src/entry/updater.ts
21
21
  var Updater = class extends node_events.EventEmitter {
22
22
  CERT;
23
23
  controller;
24
+ getCurrentAppVersion;
24
25
  info;
25
26
  tmpFilePath;
26
27
  processing = false;
@@ -47,6 +48,7 @@ var Updater = class extends node_events.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.getCurrentAppVersion = options.getAppVersion ?? require_electron.getAppVersion;
50
52
  this.controller = new AbortController();
51
53
  if (require_electron.isDev && !this.logger) {
52
54
  this.logger = {
@@ -57,10 +59,9 @@ var Updater = class extends node_events.EventEmitter {
57
59
  };
58
60
  this.logger.info("No logger set, enable dev-only logger");
59
61
  }
60
- if (!this.provider) this.logger?.debug("WARN: No update provider");
61
62
  }
62
63
  async fetch(format, data) {
63
- if (typeof data === "object") if (format === "json" && require_version.isUpdateJSON(data) || format === "buffer" && Buffer.isBuffer(data)) return data;
64
+ if (typeof data === "object") if (format === "json" && require_crypto.isUpdateJSON(data) || format === "buffer" && Buffer.isBuffer(data)) return data;
64
65
  else {
65
66
  this.err("Invalid type", "ERR_PARAM", `Invalid type at format '${format}': ${JSON.stringify(data)}`);
66
67
  return;
@@ -90,9 +91,12 @@ var Updater = class extends node_events.EventEmitter {
90
91
  const err = new UpdaterError(code, errorInfo);
91
92
  this.logger?.error(`[${code}] ${msg}`, err);
92
93
  this.cleanup();
93
- this.emit("error", err);
94
+ if (this.listenerCount("error") > 0) this.emit("error", err);
94
95
  }
95
- async checkForUpdates(data) {
96
+ /**
97
+ * Check update info using default options
98
+ */
99
+ async checkForUpdates() {
96
100
  const emitUnavailable = (msg, code, info) => {
97
101
  this.logger?.info(`[${code}] ${msg}`);
98
102
  this.logger?.debug("Check update end");
@@ -100,18 +104,18 @@ var Updater = class extends node_events.EventEmitter {
100
104
  this.emit("update-not-available", code, msg, info);
101
105
  return false;
102
106
  };
107
+ if (!this.provider) {
108
+ const msg = "No update json or provider";
109
+ this.err("Check update failed", "ERR_PARAM", msg);
110
+ return emitUnavailable(msg, "UNAVAILABLE_ERROR");
111
+ }
103
112
  if (this.processing) {
104
113
  this.logger?.info("Updater is already processing, skip check update");
105
114
  return false;
106
115
  }
107
116
  this.processing = true;
108
117
  this.logger?.debug("Check update start");
109
- if (!data && !this.provider) {
110
- const msg = "No update json or provider";
111
- this.err("Check update failed", "ERR_PARAM", msg);
112
- return emitUnavailable(msg, "UNAVAILABLE_ERROR");
113
- }
114
- const _data = await this.fetch("json", data);
118
+ const _data = await this.fetch("json");
115
119
  if (!_data) return emitUnavailable("Failed to get update info", "UNAVAILABLE_ERROR");
116
120
  const { signature, version, minimumVersion, url, ...rest } = this.receiveBeta ? _data.beta : _data;
117
121
  const info = {
@@ -124,12 +128,12 @@ var Updater = class extends node_events.EventEmitter {
124
128
  signature,
125
129
  minimumVersion,
126
130
  version,
127
- appVersion: require_electron.getAppVersion(),
131
+ appVersion: this.getCurrentAppVersion(),
128
132
  entryVersion: require_electron.getEntryVersion(),
129
133
  ...rest
130
134
  };
131
135
  this.logger?.debug(`Checked update, version: ${version}, signature: ${signature}`);
132
- if (require_electron.isDev && !this.forceUpdate && !data) return emitUnavailable("Skip check update in dev mode. To force update, set `updater.forceUpdate` to true or call checkUpdate with UpdateJSON", "UNAVAILABLE_DEV");
136
+ if (require_electron.isDev && !this.forceUpdate) return emitUnavailable("Skip check update in dev mode. To force update, set `updater.forceUpdate` to `true`", "UNAVAILABLE_DEV");
133
137
  const isLowerVersion = this.provider.isLowerVersion;
134
138
  try {
135
139
  if (isLowerVersion(extraVersionInfo.entryVersion, minimumVersion)) return emitUnavailable(`Entry Version (${extraVersionInfo.entryVersion}) < MinimumVersion (${minimumVersion})`, "UNAVAILABLE_VERSION", extraVersionInfo);
@@ -147,32 +151,39 @@ var Updater = class extends node_events.EventEmitter {
147
151
  return emitUnavailable(msg, "UNAVAILABLE_ERROR", extraVersionInfo);
148
152
  }
149
153
  }
150
- async downloadUpdate(data, info) {
154
+ /**
155
+ * Download update using default options
156
+ */
157
+ async downloadUpdate() {
151
158
  const emitError = (code, errorInfo) => {
152
159
  this.err(`Download update failed`, code, errorInfo);
153
160
  this.logger?.debug("Download update end");
154
161
  this.processing = false;
155
162
  return false;
156
163
  };
164
+ if (!this.provider) return emitError("ERR_PARAM", "No update asar buffer and provider");
157
165
  if (this.processing) {
158
166
  this.logger?.info("Updater is already processing, skip download update");
159
167
  return false;
160
168
  }
161
169
  this.processing = true;
162
170
  this.logger?.debug("Download update start");
163
- const _sig = info?.signature ?? this.info?.signature;
164
- const _version = info?.version ?? this.info?.version;
165
- if (!_sig || !_version) return emitError("ERR_PARAM", "No update signature, please call `checkUpdate` first or manually setup params");
166
- if (!data && !this.provider) return emitError("ERR_PARAM", "No update asar buffer and provider");
167
- const buffer = await this.fetch("buffer", data ? Buffer.from(data) : void 0);
168
- if (!buffer) return emitError("ERR_PARAM", "No update asar file buffer");
171
+ const _sig = this.info?.signature;
172
+ const _version = this.info?.version;
173
+ if (!_sig || !_version) return emitError("ERR_PARAM", "No update signature, please call `checkUpdate` first");
174
+ const buffer = await this.fetch("buffer");
175
+ if (!buffer) {
176
+ this.logger?.debug("Download update end");
177
+ this.processing = false;
178
+ return false;
179
+ }
169
180
  this.logger?.debug("Validation start");
170
- if (!await this.provider.verifySignaure(buffer, _version, _sig, this.CERT)) return emitError("ERR_VALIDATE", "Invalid update asar file");
181
+ if (!await this.provider.verifySignature(buffer, _version, _sig, this.CERT)) return emitError("ERR_VALIDATE", "Invalid update asar file");
171
182
  this.logger?.debug("Validation end");
172
183
  try {
173
184
  this.tmpFilePath = `${require_electron.getPathFromAppNameAsar()}.tmp`;
174
185
  this.logger?.debug(`Install to ${this.tmpFilePath}`);
175
- node_fs.default.writeFileSync(this.tmpFilePath, await this.provider.unzipFile(buffer));
186
+ node_fs.default.writeFileSync(this.tmpFilePath, await this.provider.decompressFile(buffer));
176
187
  this.logger?.info(`Download success, version: ${_version}`);
177
188
  this.info = void 0;
178
189
  this.emit("update-downloaded");
@@ -206,7 +217,6 @@ var Updater = class extends node_events.EventEmitter {
206
217
  async function autoUpdate(updater) {
207
218
  if (await updater.checkForUpdates() && await updater.downloadUpdate()) updater.quitAndInstall();
208
219
  }
209
-
210
220
  //#endregion
211
221
  //#region src/entry/core.ts
212
222
  /**
@@ -231,6 +241,25 @@ const defaultOnInstall = (install, _, __, logger) => {
231
241
  install();
232
242
  logger?.info(`update success!`);
233
243
  };
244
+ function readDevAsarVersion() {
245
+ try {
246
+ return node_fs.default.readFileSync(require_electron.getPathFromAppNameAsar("version"), "utf-8").trim();
247
+ } catch {
248
+ return electron.app.getVersion();
249
+ }
250
+ }
251
+ function resolveUpdaterOption(updater) {
252
+ if (!require_electron.isDev || !__EIU_LOCAL_DEV_UPDATE__ || updater?.provider) return updater;
253
+ return {
254
+ ...updater,
255
+ provider: new require_local.LocalDevProvider({
256
+ baseDir: __EIU_LOCAL_DEV_UPDATE_DIR__,
257
+ chunkDelay: __EIU_LOCAL_DEV_UPDATE_CHUNK_DELAY__,
258
+ chunkSize: __EIU_LOCAL_DEV_UPDATE_CHUNK_SIZE__
259
+ }),
260
+ getAppVersion: updater?.getAppVersion ?? readDevAsarVersion
261
+ };
262
+ }
234
263
  /**
235
264
  * Initialize Electron with updater
236
265
  * @example
@@ -249,7 +278,9 @@ const defaultOnInstall = (install, _, __, logger) => {
249
278
  async function createElectronApp(appOptions = {}) {
250
279
  const appNameAsarPath = require_electron.getPathFromAppNameAsar();
251
280
  const { mainPath = require_electron.isDev ? node_path.default.join(electron.app.getAppPath(), __EIU_ELECTRON_DIST_PATH__, "main", __EIU_MAIN_FILE__) : node_path.default.join(node_path.default.dirname(electron.app.getAppPath()), __EIU_ASAR_BASE_NAME__, "main", __EIU_MAIN_FILE__), updater, onInstall = defaultOnInstall, beforeStart, onStartError } = appOptions;
252
- const updaterInstance = typeof updater === "object" || !updater ? new Updater(updater) : await updater();
281
+ const useAutoLocalDevProvider = require_electron.isDev && __EIU_LOCAL_DEV_UPDATE__ && typeof updater !== "function";
282
+ const updaterInstance = typeof updater === "object" || !updater ? new Updater(resolveUpdaterOption(updater)) : await updater();
283
+ if (useAutoLocalDevProvider && updaterInstance.provider?.name === "LocalDevProvider") updaterInstance.forceUpdate = true;
253
284
  const logger = updaterInstance.logger;
254
285
  try {
255
286
  const tempAsarPath = `${appNameAsarPath}.tmp`;
@@ -258,8 +289,10 @@ async function createElectronApp(appOptions = {}) {
258
289
  await onInstall(() => node_fs.default.renameSync(tempAsarPath, appNameAsarPath), tempAsarPath, appNameAsarPath, logger);
259
290
  }
260
291
  await beforeStart?.(mainPath, logger);
261
- if (__EIU_IS_ESM__) runWithDefaultExport(await import(`file://${mainPath}`), updaterInstance);
262
- else runWithDefaultExport(require(mainPath), updaterInstance);
292
+ if (__EIU_IS_ESM__) {
293
+ const { pathToFileURL } = await import("node:url");
294
+ runWithDefaultExport(await import(pathToFileURL(mainPath).href), updaterInstance);
295
+ } else runWithDefaultExport(require(mainPath), updaterInstance);
263
296
  } catch (error) {
264
297
  logger?.error("Fail to startup", error);
265
298
  onStartError?.(error, logger);
@@ -270,11 +303,10 @@ async function createElectronApp(appOptions = {}) {
270
303
  * @deprecated Use {@link createElectronApp} instead
271
304
  */
272
305
  const initApp = createElectronApp;
273
-
274
306
  //#endregion
275
307
  exports.Updater = Updater;
276
308
  exports.UpdaterError = UpdaterError;
277
309
  exports.autoUpdate = autoUpdate;
278
310
  exports.createElectronApp = createElectronApp;
279
311
  exports.initApp = initApp;
280
- exports.startupWithUpdater = startupWithUpdater;
312
+ exports.startupWithUpdater = startupWithUpdater;
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as UpdateJSONWithURL, m as Promisable, n as IProvider, o as UpdateInfo, s as UpdateJSON, t as DownloadingInfo } from "./types-BM9Jfu7q.cjs";
1
+ import { m as Promisable, n as IProvider, o as UpdateInfo, t as DownloadingInfo } from "./types-q78spjKB.cjs";
2
2
  import { EventEmitter } from "node:events";
3
3
 
4
4
  //#region src/entry/types.d.ts
@@ -34,6 +34,13 @@ interface UpdaterOption {
34
34
  * Updater logger
35
35
  */
36
36
  logger?: Logger;
37
+ /**
38
+ * Override current app version source.
39
+ *
40
+ * This is mainly used by dev tooling where the installed asar version differs
41
+ * from Electron's package version.
42
+ */
43
+ getAppVersion?: () => string;
37
44
  }
38
45
  /**
39
46
  * Update info with current app version and entry version
@@ -60,6 +67,7 @@ declare class Updater<T extends UpdateInfoWithExtraVersion = UpdateInfoWithExtra
60
67
  }> {
61
68
  private CERT;
62
69
  private controller;
70
+ private getCurrentAppVersion;
63
71
  private info?;
64
72
  private tmpFilePath?;
65
73
  private processing;
@@ -107,21 +115,10 @@ declare class Updater<T extends UpdateInfoWithExtraVersion = UpdateInfoWithExtra
107
115
  */
108
116
  checkForUpdates(): Promise<boolean>;
109
117
  /**
110
- * Check update info using existing update json
111
- * @param data existing update json
112
- */
113
- checkForUpdates(data: UpdateJSON | UpdateJSONWithURL): Promise<boolean>;
114
- /**
115
118
  * Download update using default options
116
119
  */
117
120
  downloadUpdate(): Promise<boolean>;
118
121
  /**
119
- * Download update using existing `asar.gz` buffer and signature
120
- * @param data existing `asar.gz` buffer
121
- * @param info update info
122
- */
123
- downloadUpdate(data: Uint8Array, info: Omit<UpdateInfo, "minimumVersion">): Promise<boolean>;
124
- /**
125
122
  * quit App and install
126
123
  */
127
124
  quitAndInstall(): void;
@@ -141,7 +138,7 @@ declare function autoUpdate(updater: Updater): Promise<void>;
141
138
  * @param logger logger
142
139
  * @default install(); logger.info('update success!')
143
140
  */
144
- type OnInstallFunction = (install: VoidFunction, tempAsarPath: string, appNameAsarPath: string, logger?: Logger) => Promisable<void>;
141
+ type OnInstallFunction = (install: () => void, tempAsarPath: string, appNameAsarPath: string, logger?: Logger) => Promisable<void>;
145
142
  interface AppOption {
146
143
  /**
147
144
  * Path to index file that make {@link startupWithUpdater} as default export
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as UpdateJSONWithURL, m as Promisable, n as IProvider, o as UpdateInfo, s as UpdateJSON, t as DownloadingInfo } from "./types-DASqEPXE.mjs";
1
+ import { m as Promisable, n as IProvider, o as UpdateInfo, t as DownloadingInfo } from "./types-q78spjKB.mjs";
2
2
  import { EventEmitter } from "node:events";
3
3
 
4
4
  //#region src/entry/types.d.ts
@@ -34,6 +34,13 @@ interface UpdaterOption {
34
34
  * Updater logger
35
35
  */
36
36
  logger?: Logger;
37
+ /**
38
+ * Override current app version source.
39
+ *
40
+ * This is mainly used by dev tooling where the installed asar version differs
41
+ * from Electron's package version.
42
+ */
43
+ getAppVersion?: () => string;
37
44
  }
38
45
  /**
39
46
  * Update info with current app version and entry version
@@ -60,6 +67,7 @@ declare class Updater<T extends UpdateInfoWithExtraVersion = UpdateInfoWithExtra
60
67
  }> {
61
68
  private CERT;
62
69
  private controller;
70
+ private getCurrentAppVersion;
63
71
  private info?;
64
72
  private tmpFilePath?;
65
73
  private processing;
@@ -107,21 +115,10 @@ declare class Updater<T extends UpdateInfoWithExtraVersion = UpdateInfoWithExtra
107
115
  */
108
116
  checkForUpdates(): Promise<boolean>;
109
117
  /**
110
- * Check update info using existing update json
111
- * @param data existing update json
112
- */
113
- checkForUpdates(data: UpdateJSON | UpdateJSONWithURL): Promise<boolean>;
114
- /**
115
118
  * Download update using default options
116
119
  */
117
120
  downloadUpdate(): Promise<boolean>;
118
121
  /**
119
- * Download update using existing `asar.gz` buffer and signature
120
- * @param data existing `asar.gz` buffer
121
- * @param info update info
122
- */
123
- downloadUpdate(data: Uint8Array, info: Omit<UpdateInfo, "minimumVersion">): Promise<boolean>;
124
- /**
125
122
  * quit App and install
126
123
  */
127
124
  quitAndInstall(): void;
@@ -141,7 +138,7 @@ declare function autoUpdate(updater: Updater): Promise<void>;
141
138
  * @param logger logger
142
139
  * @default install(); logger.info('update success!')
143
140
  */
144
- type OnInstallFunction = (install: VoidFunction, tempAsarPath: string, appNameAsarPath: string, logger?: Logger) => Promisable<void>;
141
+ type OnInstallFunction = (install: () => void, tempAsarPath: string, appNameAsarPath: string, logger?: Logger) => Promisable<void>;
145
142
  interface AppOption {
146
143
  /**
147
144
  * Path to index file that make {@link startupWithUpdater} as default export
package/dist/index.mjs CHANGED
@@ -1,10 +1,10 @@
1
- import { a as getPathFromAppNameAsar, f as isDev, i as getEntryVersion, r as getAppVersion, y as restartApp } from "./electron-BJCk7uxG.mjs";
2
- import { r as isUpdateJSON } from "./version--eVB2A7n.mjs";
3
- import { BrowserWindow, app } from "electron";
1
+ import { u as isUpdateJSON } from "./crypto-DZzMmoz2.mjs";
2
+ import { t as LocalDevProvider } from "./local-C5jw-7o5.mjs";
3
+ import { a as getPathFromAppNameAsar, f as isDev, i as getEntryVersion, r as getAppVersion, y as restartApp } from "./electron-BrIF1urZ.mjs";
4
4
  import fs from "node:fs";
5
5
  import path from "node:path";
6
+ import { BrowserWindow, app } from "electron";
6
7
  import { EventEmitter } from "node:events";
7
-
8
8
  //#region src/entry/types.ts
9
9
  var UpdaterError = class extends Error {
10
10
  code;
@@ -13,12 +13,12 @@ var UpdaterError = class extends Error {
13
13
  this.code = code;
14
14
  }
15
15
  };
16
-
17
16
  //#endregion
18
17
  //#region src/entry/updater.ts
19
18
  var Updater = class extends EventEmitter {
20
19
  CERT;
21
20
  controller;
21
+ getCurrentAppVersion;
22
22
  info;
23
23
  tmpFilePath;
24
24
  processing = false;
@@ -45,6 +45,7 @@ var Updater = class extends EventEmitter {
45
45
  this.receiveBeta = options.receiveBeta;
46
46
  this.CERT = options.SIGNATURE_CERT || __EIU_SIGNATURE_CERT__;
47
47
  this.logger = options.logger;
48
+ this.getCurrentAppVersion = options.getAppVersion ?? getAppVersion;
48
49
  this.controller = new AbortController();
49
50
  if (isDev && !this.logger) {
50
51
  this.logger = {
@@ -55,7 +56,6 @@ var Updater = class extends EventEmitter {
55
56
  };
56
57
  this.logger.info("No logger set, enable dev-only logger");
57
58
  }
58
- if (!this.provider) this.logger?.debug("WARN: No update provider");
59
59
  }
60
60
  async fetch(format, data) {
61
61
  if (typeof data === "object") if (format === "json" && isUpdateJSON(data) || format === "buffer" && Buffer.isBuffer(data)) return data;
@@ -88,9 +88,12 @@ var Updater = class extends EventEmitter {
88
88
  const err = new UpdaterError(code, errorInfo);
89
89
  this.logger?.error(`[${code}] ${msg}`, err);
90
90
  this.cleanup();
91
- this.emit("error", err);
91
+ if (this.listenerCount("error") > 0) this.emit("error", err);
92
92
  }
93
- async checkForUpdates(data) {
93
+ /**
94
+ * Check update info using default options
95
+ */
96
+ async checkForUpdates() {
94
97
  const emitUnavailable = (msg, code, info) => {
95
98
  this.logger?.info(`[${code}] ${msg}`);
96
99
  this.logger?.debug("Check update end");
@@ -98,18 +101,18 @@ var Updater = class extends EventEmitter {
98
101
  this.emit("update-not-available", code, msg, info);
99
102
  return false;
100
103
  };
104
+ if (!this.provider) {
105
+ const msg = "No update json or provider";
106
+ this.err("Check update failed", "ERR_PARAM", msg);
107
+ return emitUnavailable(msg, "UNAVAILABLE_ERROR");
108
+ }
101
109
  if (this.processing) {
102
110
  this.logger?.info("Updater is already processing, skip check update");
103
111
  return false;
104
112
  }
105
113
  this.processing = true;
106
114
  this.logger?.debug("Check update start");
107
- if (!data && !this.provider) {
108
- const msg = "No update json or provider";
109
- this.err("Check update failed", "ERR_PARAM", msg);
110
- return emitUnavailable(msg, "UNAVAILABLE_ERROR");
111
- }
112
- const _data = await this.fetch("json", data);
115
+ const _data = await this.fetch("json");
113
116
  if (!_data) return emitUnavailable("Failed to get update info", "UNAVAILABLE_ERROR");
114
117
  const { signature, version, minimumVersion, url, ...rest } = this.receiveBeta ? _data.beta : _data;
115
118
  const info = {
@@ -122,12 +125,12 @@ var Updater = class extends EventEmitter {
122
125
  signature,
123
126
  minimumVersion,
124
127
  version,
125
- appVersion: getAppVersion(),
128
+ appVersion: this.getCurrentAppVersion(),
126
129
  entryVersion: getEntryVersion(),
127
130
  ...rest
128
131
  };
129
132
  this.logger?.debug(`Checked update, version: ${version}, signature: ${signature}`);
130
- if (isDev && !this.forceUpdate && !data) return emitUnavailable("Skip check update in dev mode. To force update, set `updater.forceUpdate` to true or call checkUpdate with UpdateJSON", "UNAVAILABLE_DEV");
133
+ if (isDev && !this.forceUpdate) return emitUnavailable("Skip check update in dev mode. To force update, set `updater.forceUpdate` to `true`", "UNAVAILABLE_DEV");
131
134
  const isLowerVersion = this.provider.isLowerVersion;
132
135
  try {
133
136
  if (isLowerVersion(extraVersionInfo.entryVersion, minimumVersion)) return emitUnavailable(`Entry Version (${extraVersionInfo.entryVersion}) < MinimumVersion (${minimumVersion})`, "UNAVAILABLE_VERSION", extraVersionInfo);
@@ -145,32 +148,39 @@ var Updater = class extends EventEmitter {
145
148
  return emitUnavailable(msg, "UNAVAILABLE_ERROR", extraVersionInfo);
146
149
  }
147
150
  }
148
- async downloadUpdate(data, info) {
151
+ /**
152
+ * Download update using default options
153
+ */
154
+ async downloadUpdate() {
149
155
  const emitError = (code, errorInfo) => {
150
156
  this.err(`Download update failed`, code, errorInfo);
151
157
  this.logger?.debug("Download update end");
152
158
  this.processing = false;
153
159
  return false;
154
160
  };
161
+ if (!this.provider) return emitError("ERR_PARAM", "No update asar buffer and provider");
155
162
  if (this.processing) {
156
163
  this.logger?.info("Updater is already processing, skip download update");
157
164
  return false;
158
165
  }
159
166
  this.processing = true;
160
167
  this.logger?.debug("Download update start");
161
- const _sig = info?.signature ?? this.info?.signature;
162
- const _version = info?.version ?? this.info?.version;
163
- if (!_sig || !_version) return emitError("ERR_PARAM", "No update signature, please call `checkUpdate` first or manually setup params");
164
- if (!data && !this.provider) return emitError("ERR_PARAM", "No update asar buffer and provider");
165
- const buffer = await this.fetch("buffer", data ? Buffer.from(data) : void 0);
166
- if (!buffer) return emitError("ERR_PARAM", "No update asar file buffer");
168
+ const _sig = this.info?.signature;
169
+ const _version = this.info?.version;
170
+ if (!_sig || !_version) return emitError("ERR_PARAM", "No update signature, please call `checkUpdate` first");
171
+ const buffer = await this.fetch("buffer");
172
+ if (!buffer) {
173
+ this.logger?.debug("Download update end");
174
+ this.processing = false;
175
+ return false;
176
+ }
167
177
  this.logger?.debug("Validation start");
168
- if (!await this.provider.verifySignaure(buffer, _version, _sig, this.CERT)) return emitError("ERR_VALIDATE", "Invalid update asar file");
178
+ if (!await this.provider.verifySignature(buffer, _version, _sig, this.CERT)) return emitError("ERR_VALIDATE", "Invalid update asar file");
169
179
  this.logger?.debug("Validation end");
170
180
  try {
171
181
  this.tmpFilePath = `${getPathFromAppNameAsar()}.tmp`;
172
182
  this.logger?.debug(`Install to ${this.tmpFilePath}`);
173
- fs.writeFileSync(this.tmpFilePath, await this.provider.unzipFile(buffer));
183
+ fs.writeFileSync(this.tmpFilePath, await this.provider.decompressFile(buffer));
174
184
  this.logger?.info(`Download success, version: ${_version}`);
175
185
  this.info = void 0;
176
186
  this.emit("update-downloaded");
@@ -204,7 +214,6 @@ var Updater = class extends EventEmitter {
204
214
  async function autoUpdate(updater) {
205
215
  if (await updater.checkForUpdates() && await updater.downloadUpdate()) updater.quitAndInstall();
206
216
  }
207
-
208
217
  //#endregion
209
218
  //#region src/entry/core.ts
210
219
  /**
@@ -229,6 +238,25 @@ const defaultOnInstall = (install, _, __, logger) => {
229
238
  install();
230
239
  logger?.info(`update success!`);
231
240
  };
241
+ function readDevAsarVersion() {
242
+ try {
243
+ return fs.readFileSync(getPathFromAppNameAsar("version"), "utf-8").trim();
244
+ } catch {
245
+ return app.getVersion();
246
+ }
247
+ }
248
+ function resolveUpdaterOption(updater) {
249
+ if (!isDev || !__EIU_LOCAL_DEV_UPDATE__ || updater?.provider) return updater;
250
+ return {
251
+ ...updater,
252
+ provider: new LocalDevProvider({
253
+ baseDir: __EIU_LOCAL_DEV_UPDATE_DIR__,
254
+ chunkDelay: __EIU_LOCAL_DEV_UPDATE_CHUNK_DELAY__,
255
+ chunkSize: __EIU_LOCAL_DEV_UPDATE_CHUNK_SIZE__
256
+ }),
257
+ getAppVersion: updater?.getAppVersion ?? readDevAsarVersion
258
+ };
259
+ }
232
260
  /**
233
261
  * Initialize Electron with updater
234
262
  * @example
@@ -247,7 +275,9 @@ const defaultOnInstall = (install, _, __, logger) => {
247
275
  async function createElectronApp(appOptions = {}) {
248
276
  const appNameAsarPath = getPathFromAppNameAsar();
249
277
  const { mainPath = isDev ? path.join(app.getAppPath(), __EIU_ELECTRON_DIST_PATH__, "main", __EIU_MAIN_FILE__) : path.join(path.dirname(app.getAppPath()), __EIU_ASAR_BASE_NAME__, "main", __EIU_MAIN_FILE__), updater, onInstall = defaultOnInstall, beforeStart, onStartError } = appOptions;
250
- const updaterInstance = typeof updater === "object" || !updater ? new Updater(updater) : await updater();
278
+ const useAutoLocalDevProvider = isDev && __EIU_LOCAL_DEV_UPDATE__ && typeof updater !== "function";
279
+ const updaterInstance = typeof updater === "object" || !updater ? new Updater(resolveUpdaterOption(updater)) : await updater();
280
+ if (useAutoLocalDevProvider && updaterInstance.provider?.name === "LocalDevProvider") updaterInstance.forceUpdate = true;
251
281
  const logger = updaterInstance.logger;
252
282
  try {
253
283
  const tempAsarPath = `${appNameAsarPath}.tmp`;
@@ -256,8 +286,10 @@ async function createElectronApp(appOptions = {}) {
256
286
  await onInstall(() => fs.renameSync(tempAsarPath, appNameAsarPath), tempAsarPath, appNameAsarPath, logger);
257
287
  }
258
288
  await beforeStart?.(mainPath, logger);
259
- if (__EIU_IS_ESM__) runWithDefaultExport(await import(`file://${mainPath}`), updaterInstance);
260
- else runWithDefaultExport(require(mainPath), updaterInstance);
289
+ if (__EIU_IS_ESM__) {
290
+ const { pathToFileURL } = await import("node:url");
291
+ runWithDefaultExport(await import(pathToFileURL(mainPath).href), updaterInstance);
292
+ } else runWithDefaultExport(require(mainPath), updaterInstance);
261
293
  } catch (error) {
262
294
  logger?.error("Fail to startup", error);
263
295
  onStartError?.(error, logger);
@@ -268,6 +300,5 @@ async function createElectronApp(appOptions = {}) {
268
300
  * @deprecated Use {@link createElectronApp} instead
269
301
  */
270
302
  const initApp = createElectronApp;
271
-
272
303
  //#endregion
273
- export { Updater, UpdaterError, autoUpdate, createElectronApp, initApp, startupWithUpdater };
304
+ export { Updater, UpdaterError, autoUpdate, createElectronApp, initApp, startupWithUpdater };
@@ -0,0 +1,105 @@
1
+ import { c as defaultIsLowerVersion, i as defaultVerifySignature, s as defaultDecompressFile, u as isUpdateJSON } from "./crypto-DZzMmoz2.mjs";
2
+ import path from "node:path";
3
+ import fs from "node:fs/promises";
4
+ //#region src/provider/base.ts
5
+ var BaseProvider = class {
6
+ name = "BaseProvider";
7
+ /**
8
+ * @inheritdoc
9
+ */
10
+ isLowerVersion = defaultIsLowerVersion;
11
+ /**
12
+ * @inheritdoc
13
+ */
14
+ verifySignature = defaultVerifySignature;
15
+ /**
16
+ * @inheritdoc
17
+ */
18
+ decompressFile = defaultDecompressFile;
19
+ };
20
+ //#endregion
21
+ //#region src/provider/local.ts
22
+ /**
23
+ * Update Provider for local development
24
+ * - download update json from `{baseDir}/{versionPath}`
25
+ * - download update asar from `{baseDir}/{name}-{version}.asar.br`
26
+ *
27
+ * This provider is useful for testing updates during development without
28
+ * needing to deploy to a remote server.
29
+ * @param options provider options
30
+ */
31
+ var LocalDevProvider = class extends BaseProvider {
32
+ name = "LocalDevProvider";
33
+ verifySignature;
34
+ options;
35
+ constructor(options) {
36
+ super();
37
+ const resolvedOptions = {
38
+ chunkSize: 64 * 1024,
39
+ chunkDelay: 30,
40
+ ...options
41
+ };
42
+ if (resolvedOptions.chunkSize <= 0) throw new Error("localDevUpdate.chunkSize must be greater than 0");
43
+ if (resolvedOptions.chunkDelay < 0) throw new Error("localDevUpdate.chunkDelay must be greater than or equal to 0");
44
+ this.options = resolvedOptions;
45
+ this.verifySignature = async function verifySignature() {
46
+ return true;
47
+ };
48
+ }
49
+ /**
50
+ * @inheritdoc
51
+ */
52
+ async downloadJSON(name, versionPath, signal) {
53
+ signal.throwIfAborted();
54
+ const { beta, version, ...info } = await this.readJSON(versionPath);
55
+ const getURL = (ver) => path.join(this.options.baseDir, `${name}-${ver}.asar.br`);
56
+ return {
57
+ ...info,
58
+ version,
59
+ url: getURL(version),
60
+ beta: {
61
+ ...beta,
62
+ url: getURL(beta.version)
63
+ }
64
+ };
65
+ }
66
+ /**
67
+ * @inheritdoc
68
+ */
69
+ async downloadAsar(info, signal, onDownloading) {
70
+ signal.throwIfAborted();
71
+ const fileBuffer = await fs.readFile(info.url);
72
+ await this.emitProgress(fileBuffer, signal, onDownloading);
73
+ return fileBuffer;
74
+ }
75
+ async readJSON(versionPath) {
76
+ const fullPath = path.join(this.options.baseDir, versionPath);
77
+ const content = await fs.readFile(fullPath, "utf-8");
78
+ const json = JSON.parse(content);
79
+ if (!isUpdateJSON(json)) throw new Error(`Invalid update json: ${content}`);
80
+ return json;
81
+ }
82
+ async emitProgress(fileBuffer, signal, onDownloading) {
83
+ if (!onDownloading) return;
84
+ const total = fileBuffer.length;
85
+ let transferred = 0;
86
+ let lastTime = Date.now();
87
+ while (transferred < total) {
88
+ signal.throwIfAborted();
89
+ const currentTime = Date.now();
90
+ const delta = Math.min(this.options.chunkSize, total - transferred);
91
+ transferred += delta;
92
+ onDownloading({
93
+ delta,
94
+ percent: Math.round(transferred / total * 100),
95
+ total,
96
+ transferred,
97
+ bps: Math.round(delta / Math.max(currentTime - lastTime, 1) * 1e3)
98
+ });
99
+ lastTime = currentTime;
100
+ if (transferred < total && this.options.chunkDelay > 0) await new Promise((resolve) => setTimeout(resolve, this.options.chunkDelay));
101
+ }
102
+ }
103
+ };
104
+ //#endregion
105
+ export { BaseProvider as n, LocalDevProvider as t };