electron-updater-for-render 1.1.2-beta.7 → 2.0.0-beta.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/bin/cli.cjs CHANGED
@@ -50,7 +50,8 @@ async function createUpdatePackage(options) {
50
50
  packageJsonPath,
51
51
  privateKeyPath,
52
52
  releaseNotesPath,
53
- forceUpdate
53
+ forceUpdate,
54
+ rolloutRule
54
55
  } = options;
55
56
  if (!import_fs.default.existsSync(outDir)) {
56
57
  throw new Error(`Output directory not found at ${outDir}`);
@@ -100,7 +101,8 @@ async function createUpdatePackage(options) {
100
101
  ...signature ? { signature } : {},
101
102
  date: (/* @__PURE__ */ new Date()).toISOString(),
102
103
  releaseNotes: releaseNotesContent,
103
- ...forceUpdate ? { forceUpdate } : {}
104
+ ...forceUpdate ? { forceUpdate } : {},
105
+ ...rolloutRule ? { rolloutRule } : {}
104
106
  };
105
107
  const jsonPath = import_path.default.join(updatesDir, "latest.json");
106
108
  import_fs.default.writeFileSync(jsonPath, JSON.stringify(updateInfo, null, 2));
package/dist/bin/cli.js CHANGED
@@ -23,7 +23,8 @@ async function createUpdatePackage(options) {
23
23
  packageJsonPath,
24
24
  privateKeyPath,
25
25
  releaseNotesPath,
26
- forceUpdate
26
+ forceUpdate,
27
+ rolloutRule
27
28
  } = options;
28
29
  if (!fs.existsSync(outDir)) {
29
30
  throw new Error(`Output directory not found at ${outDir}`);
@@ -73,7 +74,8 @@ async function createUpdatePackage(options) {
73
74
  ...signature ? { signature } : {},
74
75
  date: (/* @__PURE__ */ new Date()).toISOString(),
75
76
  releaseNotes: releaseNotesContent,
76
- ...forceUpdate ? { forceUpdate } : {}
77
+ ...forceUpdate ? { forceUpdate } : {},
78
+ ...rolloutRule ? { rolloutRule } : {}
77
79
  };
78
80
  const jsonPath = path.join(updatesDir, "latest.json");
79
81
  fs.writeFileSync(jsonPath, JSON.stringify(updateInfo, null, 2));
@@ -52,7 +52,8 @@ async function createUpdatePackage(options) {
52
52
  packageJsonPath,
53
53
  privateKeyPath,
54
54
  releaseNotesPath,
55
- forceUpdate
55
+ forceUpdate,
56
+ rolloutRule
56
57
  } = options;
57
58
  if (!import_fs.default.existsSync(outDir)) {
58
59
  throw new Error(`Output directory not found at ${outDir}`);
@@ -102,7 +103,8 @@ async function createUpdatePackage(options) {
102
103
  ...signature ? { signature } : {},
103
104
  date: (/* @__PURE__ */ new Date()).toISOString(),
104
105
  releaseNotes: releaseNotesContent,
105
- ...forceUpdate ? { forceUpdate } : {}
106
+ ...forceUpdate ? { forceUpdate } : {},
107
+ ...rolloutRule ? { rolloutRule } : {}
106
108
  };
107
109
  const jsonPath = import_path.default.join(updatesDir, "latest.json");
108
110
  import_fs.default.writeFileSync(jsonPath, JSON.stringify(updateInfo, null, 2));
@@ -17,7 +17,8 @@ async function createUpdatePackage(options) {
17
17
  packageJsonPath,
18
18
  privateKeyPath,
19
19
  releaseNotesPath,
20
- forceUpdate
20
+ forceUpdate,
21
+ rolloutRule
21
22
  } = options;
22
23
  if (!fs.existsSync(outDir)) {
23
24
  throw new Error(`Output directory not found at ${outDir}`);
@@ -67,7 +68,8 @@ async function createUpdatePackage(options) {
67
68
  ...signature ? { signature } : {},
68
69
  date: (/* @__PURE__ */ new Date()).toISOString(),
69
70
  releaseNotes: releaseNotesContent,
70
- ...forceUpdate ? { forceUpdate } : {}
71
+ ...forceUpdate ? { forceUpdate } : {},
72
+ ...rolloutRule ? { rolloutRule } : {}
71
73
  };
72
74
  const jsonPath = path.join(updatesDir, "latest.json");
73
75
  fs.writeFileSync(jsonPath, JSON.stringify(updateInfo, null, 2));
@@ -30,12 +30,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/main/index.ts
31
31
  var main_exports = {};
32
32
  __export(main_exports, {
33
- RenderUpdater: () => RenderUpdater
33
+ RenderUpdater: () => RenderUpdater,
34
+ setupUpdaterIPC: () => setupUpdaterIPC
34
35
  });
35
36
  module.exports = __toCommonJS(main_exports);
36
37
  var import_path2 = __toESM(require("path"), 1);
37
38
  var import_original_fs = __toESM(require("original-fs"), 1);
38
- var import_crypto = __toESM(require("crypto"), 1);
39
39
  var import_semver = __toESM(require("semver"), 1);
40
40
  var import_electron2 = require("electron");
41
41
  var import_promises = require("stream/promises");
@@ -158,7 +158,46 @@ var RouterHandler = class _RouterHandler {
158
158
  }
159
159
  };
160
160
 
161
+ // src/main/utils.ts
162
+ var import_fs2 = require("fs");
163
+ var import_crypto = __toESM(require("crypto"), 1);
164
+ function verifyFileStream(filePath, info, publicKey) {
165
+ return new Promise((resolve, reject) => {
166
+ const hash = import_crypto.default.createHash("sha256");
167
+ const verify = publicKey && info.signature ? import_crypto.default.createVerify("RSA-SHA256") : null;
168
+ const stream = (0, import_fs2.createReadStream)(filePath);
169
+ stream.on("data", (chunk) => {
170
+ hash.update(chunk);
171
+ if (verify) {
172
+ verify.update(chunk);
173
+ }
174
+ });
175
+ stream.on("end", () => {
176
+ const sha256 = hash.digest("hex");
177
+ if (info.sha256 && sha256 !== info.sha256) {
178
+ console.error(`[RenderUpdater] SHA256 mismatch! Expected ${info.sha256}, got ${sha256}`);
179
+ return resolve(false);
180
+ }
181
+ if (verify && publicKey && info.signature) {
182
+ verify.end();
183
+ const isValid = verify.verify(publicKey, info.signature, "base64");
184
+ if (!isValid) {
185
+ console.error("[RenderUpdater] RSA Signature verification failed.");
186
+ return resolve(false);
187
+ }
188
+ }
189
+ resolve(true);
190
+ });
191
+ stream.on("error", (err) => {
192
+ console.error("[RenderUpdater] Stream error during file verification:", err);
193
+ reject(err);
194
+ });
195
+ });
196
+ }
197
+
161
198
  // src/main/index.ts
199
+ var import_electron3 = require("electron");
200
+ var fsPromises = import_original_fs.default.promises;
162
201
  var RenderUpdater = class {
163
202
  versionsDir;
164
203
  currentVersionFile;
@@ -180,6 +219,9 @@ var RenderUpdater = class {
180
219
  onStatusChanged;
181
220
  onError;
182
221
  onBeforeRestart;
222
+ identity;
223
+ allowPrerelease;
224
+ requestOptions;
183
225
  constructor(options) {
184
226
  console.log("[RenderUpdater] Initializing with versionsDir:", options.versionsDir);
185
227
  this.versionsDir = options.versionsDir;
@@ -197,6 +239,9 @@ var RenderUpdater = class {
197
239
  this.onBeforeRestart = options.onBeforeRestart;
198
240
  this.routerMode = options.routerMode ?? "hash";
199
241
  this.protocolName = options.protocol ?? "app";
242
+ this.identity = options.identity;
243
+ this.allowPrerelease = options.allowPrerelease ?? false;
244
+ this.requestOptions = options.requestOptions;
200
245
  this.download = this.download.bind(this);
201
246
  this.check = this.check.bind(this);
202
247
  this.checkForUpdatesAndNotify = this.checkForUpdatesAndNotify.bind(this);
@@ -206,7 +251,7 @@ var RenderUpdater = class {
206
251
  if (!import_original_fs.default.existsSync(this.versionsDir)) {
207
252
  import_original_fs.default.mkdirSync(this.versionsDir, { recursive: true });
208
253
  }
209
- const savedVersion = this.readCurrentVersionFromFile();
254
+ const savedVersion = this.readCurrentVersionFromFileSync();
210
255
  this._activeVersion = savedVersion || "0.0.0";
211
256
  this._diskVersion = savedVersion || "0.0.0";
212
257
  console.log("[RenderUpdater] Initialized. Active version:", this._activeVersion);
@@ -221,7 +266,8 @@ var RenderUpdater = class {
221
266
  });
222
267
  }
223
268
  }
224
- readCurrentVersionFromFile() {
269
+ // 初始化使用一次的同步方法(可接受)
270
+ readCurrentVersionFromFileSync() {
225
271
  if (import_original_fs.default.existsSync(this.currentVersionFile)) {
226
272
  try {
227
273
  const data = JSON.parse(import_original_fs.default.readFileSync(this.currentVersionFile, "utf-8"));
@@ -234,35 +280,33 @@ var RenderUpdater = class {
234
280
  }
235
281
  return "0.0.0";
236
282
  }
237
- cleanOldVersions() {
283
+ // 全面异步替换原同步清理操作
284
+ async cleanOldVersions() {
238
285
  try {
239
286
  if (!this.versionsDir) return;
240
- const versionDirs = import_original_fs.default.readdirSync(this.versionsDir).filter((v) => {
241
- return import_original_fs.default.statSync(import_path2.default.join(this.versionsDir, v)).isDirectory() && import_semver.default.valid(v);
242
- });
287
+ const files = await fsPromises.readdir(this.versionsDir, { withFileTypes: true });
288
+ const versionDirs = files.filter((dirent) => dirent.isDirectory() && import_semver.default.valid(dirent.name)).map((dirent) => dirent.name);
243
289
  versionDirs.sort((a, b) => import_semver.default.compare(import_semver.default.coerce(b) ?? b, import_semver.default.coerce(a) ?? a));
244
- const activeIdx = versionDirs.indexOf(this._activeVersion);
245
- const diskIdx = versionDirs.indexOf(this._diskVersion);
246
290
  const safeVersions = /* @__PURE__ */ new Set();
247
- if (activeIdx !== -1) safeVersions.add(this._activeVersion);
248
- if (diskIdx !== -1) safeVersions.add(this._diskVersion);
291
+ safeVersions.add(this._activeVersion);
292
+ safeVersions.add(this._diskVersion);
249
293
  let kept = 0;
250
294
  for (const v of versionDirs) {
251
295
  if (!safeVersions.has(v)) {
252
- console.info(`[RenderUpdater] Cleaning up old version: ${v}`);
253
- try {
254
- import_original_fs.default.rmSync(import_path2.default.join(this.versionsDir, v), { recursive: true, force: true });
255
- } catch (e) {
256
- if (e.code === "EBUSY") {
257
- console.warn(`[RenderUpdater] Version ${v} is locked, skipping.`);
258
- } else {
259
- console.error(`[RenderUpdater] Failed to remove version ${v}:`, e);
296
+ if (kept >= this.maxVersionsToKeep) {
297
+ console.info(`[RenderUpdater] Cleaning up old version: ${v}`);
298
+ try {
299
+ await fsPromises.rm(import_path2.default.join(this.versionsDir, v), { recursive: true, force: true });
300
+ } catch (e) {
301
+ if (e.code === "EBUSY") {
302
+ console.warn(`[RenderUpdater] Version ${v} is locked, skipping.`);
303
+ } else {
304
+ console.error(`[RenderUpdater] Failed to remove version ${v}:`, e);
305
+ }
260
306
  }
307
+ } else {
308
+ kept++;
261
309
  }
262
- } else {
263
- kept++;
264
- }
265
- if (kept >= this.maxVersionsToKeep && !safeVersions.has(v)) {
266
310
  }
267
311
  }
268
312
  } catch (e) {
@@ -297,7 +341,18 @@ var RenderUpdater = class {
297
341
  async check() {
298
342
  let remoteInfo = null;
299
343
  try {
300
- const response = await fetch(`${this.baseUrl}/latest.json?t=${Date.now()}`);
344
+ const fetchUrl = new URL(`${this.baseUrl}/latest.json`);
345
+ fetchUrl.searchParams.append("t", Date.now().toString());
346
+ if (this.requestOptions?.query) {
347
+ for (const [k, v] of Object.entries(this.requestOptions.query)) {
348
+ fetchUrl.searchParams.append(k, v);
349
+ }
350
+ }
351
+ const fetchOpts = {};
352
+ if (this.requestOptions?.headers) {
353
+ fetchOpts.headers = this.requestOptions.headers;
354
+ }
355
+ const response = await fetch(fetchUrl.toString(), fetchOpts);
301
356
  if (response.ok) {
302
357
  remoteInfo = await response.json();
303
358
  }
@@ -307,7 +362,16 @@ var RenderUpdater = class {
307
362
  const runningVersion = this._activeVersion || "0.0.0";
308
363
  const pendingVersion = this._diskVersion || "0.0.0";
309
364
  if (remoteInfo && remoteInfo.version && import_semver.default.valid(remoteInfo.version)) {
310
- if (import_semver.default.gt(remoteInfo.version, pendingVersion)) {
365
+ let allowUpdate = true;
366
+ if (remoteInfo.rolloutRule?.deviceIds) {
367
+ if (!this.identity?.deviceId || !remoteInfo.rolloutRule.deviceIds.includes(this.identity.deviceId)) {
368
+ allowUpdate = false;
369
+ }
370
+ }
371
+ if (allowUpdate && !this.allowPrerelease && import_semver.default.prerelease(remoteInfo.version)) {
372
+ allowUpdate = false;
373
+ }
374
+ if (allowUpdate && import_semver.default.gt(remoteInfo.version, pendingVersion, { includePrerelease: this.allowPrerelease })) {
311
375
  return { updateAvailable: true, version: remoteInfo.version, info: remoteInfo, status: "available" };
312
376
  }
313
377
  }
@@ -332,32 +396,46 @@ var RenderUpdater = class {
332
396
  this.isDownloading = true;
333
397
  try {
334
398
  console.log("[RenderUpdater] download() entry. current this.versionsDir:", this.versionsDir);
335
- const infoToUse = providedInfo || await (await fetch(`${this.baseUrl}/latest.json?t=${Date.now()}`)).json();
336
- if (!this.versionsDir) {
337
- throw new Error("[RenderUpdater] CRITICAL: this.versionsDir lost in download.");
338
- }
339
- if (!infoToUse || !infoToUse.version) {
340
- throw new Error(`[RenderUpdater] CRITICAL: infoToUse or version missing. info: ${JSON.stringify(infoToUse)}`);
399
+ let infoToUse;
400
+ if (providedInfo) {
401
+ infoToUse = providedInfo;
402
+ } else {
403
+ const fetchUrl = new URL(`${this.baseUrl}/latest.json`);
404
+ fetchUrl.searchParams.append("t", Date.now().toString());
405
+ if (this.requestOptions?.query) {
406
+ for (const [k, v] of Object.entries(this.requestOptions.query)) {
407
+ fetchUrl.searchParams.append(k, v);
408
+ }
409
+ }
410
+ const fetchOpts = {};
411
+ if (this.requestOptions?.headers) {
412
+ fetchOpts.headers = this.requestOptions.headers;
413
+ }
414
+ const res = await fetch(fetchUrl.toString(), fetchOpts);
415
+ infoToUse = await res.json();
341
416
  }
417
+ if (!this.versionsDir) throw new Error("[RenderUpdater] CRITICAL: this.versionsDir lost in download.");
418
+ if (!infoToUse || !infoToUse.version) throw new Error(`[RenderUpdater] CRITICAL: infoToUse or version missing. info: ${JSON.stringify(infoToUse)}`);
342
419
  const versionDir = import_path2.default.join(this.versionsDir, infoToUse.version);
343
- const asarPath = import_path2.default.join(versionDir, "renderer.asar");
344
- if (import_original_fs.default.existsSync(asarPath) && this.verifyFile(asarPath, infoToUse)) {
420
+ const finalAsarPath = import_path2.default.join(versionDir, "renderer.asar");
421
+ const tmpAsarPath = import_path2.default.join(versionDir, "renderer.asar.tmp");
422
+ if (import_original_fs.default.existsSync(finalAsarPath) && await verifyFileStream(finalAsarPath, infoToUse, this.publicKey)) {
345
423
  console.log("[RenderUpdater] File already exists and verified. Skipping download.");
346
424
  onProgress?.(100);
347
425
  return;
348
426
  }
349
- if (!import_original_fs.default.existsSync(versionDir)) {
350
- import_original_fs.default.mkdirSync(versionDir, { recursive: true });
351
- }
427
+ await fsPromises.mkdir(versionDir, { recursive: true });
352
428
  let downloadedBytes = 0;
353
- if (import_original_fs.default.existsSync(asarPath)) {
354
- downloadedBytes = import_original_fs.default.statSync(asarPath).size;
429
+ if (import_original_fs.default.existsSync(tmpAsarPath)) {
430
+ const stats = await fsPromises.stat(tmpAsarPath);
431
+ downloadedBytes = stats.size;
432
+ }
433
+ const fetchOptions = { headers: {} };
434
+ if (this.requestOptions?.headers) {
435
+ Object.assign(fetchOptions.headers, this.requestOptions.headers);
355
436
  }
356
- const fetchOptions = {};
357
437
  if (downloadedBytes > 0) {
358
- fetchOptions.headers = {
359
- Range: `bytes=${downloadedBytes}-`
360
- };
438
+ fetchOptions.headers["Range"] = `bytes=${downloadedBytes}-`;
361
439
  }
362
440
  const downloadUrl = infoToUse.path ? `${this.baseUrl.replace(/\/$/, "")}/${infoToUse.path}` : infoToUse.url;
363
441
  if (!downloadUrl) throw new Error("[RenderUpdater] Cannot determine download URL: info.path and info.url are both missing.");
@@ -372,7 +450,7 @@ var RenderUpdater = class {
372
450
  const incomingTotal = contentLength ? parseInt(contentLength, 10) : 0;
373
451
  const total = downloadedBytes + incomingTotal;
374
452
  if (!downloadResponse.body) throw new Error("Response body is empty");
375
- const fileStream = import_original_fs.default.createWriteStream(asarPath, { flags: downloadResponse.status === 206 ? "a" : "w" });
453
+ const fileStream = import_original_fs.default.createWriteStream(tmpAsarPath, { flags: downloadResponse.status === 206 ? "a" : "w" });
376
454
  const progressTransform = new import_stream.Transform({
377
455
  transform(chunk, _encoding, callback) {
378
456
  downloadedBytes += chunk.length;
@@ -387,62 +465,34 @@ var RenderUpdater = class {
387
465
  progressTransform,
388
466
  fileStream
389
467
  );
390
- if (!this.verifyFile(asarPath, infoToUse)) {
391
- import_original_fs.default.rmSync(versionDir, { recursive: true, force: true });
468
+ if (!await verifyFileStream(tmpAsarPath, infoToUse, this.publicKey)) {
469
+ await fsPromises.rm(tmpAsarPath, { force: true });
392
470
  throw new Error("[RenderUpdater] Verification failed after download (SHA256 or RSA Mismatch)");
393
471
  }
394
- this.setUpdatePending(infoToUse.version);
395
- this.cleanOldVersions();
472
+ await fsPromises.rename(tmpAsarPath, finalAsarPath);
473
+ await this.setUpdatePending(infoToUse.version);
474
+ await this.cleanOldVersions();
396
475
  } finally {
397
476
  this.isDownloading = false;
398
477
  }
399
478
  }
400
- verifyFile(filePath, info) {
401
- const fileBuffer = import_original_fs.default.readFileSync(filePath);
402
- const hashSum = import_crypto.default.createHash("sha256");
403
- hashSum.update(fileBuffer);
404
- const sha256 = hashSum.digest("hex");
405
- if (sha256 !== info.sha256 && info.sha256 !== "") {
406
- console.error(`[RenderUpdater] SHA256 mismatch! Expected ${info.sha256}, got ${sha256}`);
407
- return false;
408
- }
409
- if (this.publicKey && info.signature) {
410
- try {
411
- const verify = import_crypto.default.createVerify("RSA-SHA256");
412
- verify.update(fileBuffer);
413
- verify.end();
414
- const isValid = verify.verify(this.publicKey, info.signature, "base64");
415
- if (!isValid) {
416
- console.error("[RenderUpdater] RSA Signature verification failed.");
417
- return false;
418
- }
419
- } catch (err) {
420
- console.error("[RenderUpdater] RSA verification error:", err);
421
- return false;
422
- }
423
- }
424
- return true;
425
- }
426
- useVersion(version) {
427
- this.updateDiskVersion(version);
428
- this.updateMemoryVersion(version);
479
+ async useVersion(version) {
480
+ await this.updateDiskVersion(version);
481
+ this._activeVersion = version;
429
482
  }
430
- setUpdatePending(version) {
431
- this.updateDiskVersion(version);
483
+ async setUpdatePending(version) {
484
+ await this.updateDiskVersion(version);
432
485
  if (this.onStatusChanged) {
433
486
  this.onStatusChanged({ status: "ready", version });
434
487
  }
435
488
  }
436
- updateDiskVersion(version) {
437
- import_original_fs.default.writeFileSync(
489
+ async updateDiskVersion(version) {
490
+ await fsPromises.writeFile(
438
491
  this.currentVersionFile,
439
492
  JSON.stringify({ version, date: (/* @__PURE__ */ new Date()).toISOString() })
440
493
  );
441
494
  this._diskVersion = version;
442
495
  }
443
- updateMemoryVersion(version) {
444
- this._activeVersion = version;
445
- }
446
496
  async installAndRestart() {
447
497
  if (this.onBeforeRestart) {
448
498
  await this.onBeforeRestart();
@@ -450,107 +500,158 @@ var RenderUpdater = class {
450
500
  import_electron2.app.relaunch();
451
501
  import_electron2.app.quit();
452
502
  }
503
+ // --- 改版后切分的上帝函数 ---
453
504
  async checkForUpdatesAndNotify() {
454
505
  try {
455
506
  const checkRes = await this.check();
456
- if (checkRes.updateAvailable && checkRes.info) {
457
- const info = checkRes.info;
458
- const isReady = checkRes.status === "ready";
459
- const doDownload = async () => {
460
- try {
461
- if (!isReady) {
462
- await this.download(info, this.onDownloadProgress);
463
- }
464
- const performInstall = async (shouldRestart) => {
465
- if (shouldRestart) {
466
- this.useVersion(info.version);
467
- await this.installAndRestart();
468
- } else {
469
- this.setUpdatePending(info.version);
470
- console.info(`[RenderUpdater] Update v${info.version} ready, will apply on next launch.`);
471
- }
472
- };
473
- if (this.onDownloadComplete) {
474
- this.onDownloadComplete(info, () => {
475
- performInstall(true).catch(console.error);
476
- });
477
- } else {
478
- if (info.forceUpdate || isReady) {
479
- if (isReady) {
480
- const { response } = await import_electron2.dialog.showMessageBox({
481
- type: "info",
482
- title: "\u66F4\u65B0\u5C31\u7EEA",
483
- message: `\u65B0\u7248\u672C v${info.version} \u5DF2\u51C6\u5907\u5C31\u7EEA\u3002\u662F\u5426\u7ACB\u5373\u91CD\u542F\u5E94\u7528\uFF1F`,
484
- buttons: ["\u7ACB\u5373\u91CD\u542F", "\u7A0D\u540E\u518D\u8BF4"],
485
- defaultId: 0,
486
- cancelId: 1
487
- });
488
- await performInstall(response === 0);
489
- } else {
490
- if (info.forceUpdate === "prompt") {
491
- import_electron2.dialog.showMessageBoxSync({
492
- type: "warning",
493
- title: "\u4E0B\u8F7D\u5B8C\u6210",
494
- message: `\u6700\u65B0\u7248\u672C v${info.version} \u5DF2\u4E0B\u8F7D\u5B8C\u6BD5\u3002\u5373\u5C06\u91CD\u542F\u7A0B\u5E8F\u4EE5\u5E94\u7528\u66F4\u65B0\u3002`,
495
- buttons: ["\u6211\u77E5\u9053\u4E86"]
496
- });
497
- }
498
- await performInstall(true);
499
- }
500
- } else if (this.autoPrompt) {
501
- const { response } = await import_electron2.dialog.showMessageBox({
502
- type: "info",
503
- title: "\u4E0B\u8F7D\u5B8C\u6210",
504
- message: `\u6700\u65B0\u7248\u672C v${info.version} \u5DF2\u4E0B\u8F7D\u5B8C\u6BD5\u3002\u662F\u5426\u7ACB\u5373\u91CD\u542F\u4EE5\u5E94\u7528\u66F4\u65B0\uFF1F`,
505
- buttons: ["\u7ACB\u5373\u91CD\u542F", "\u7A0D\u540E\u518D\u8BF4"],
506
- defaultId: 0,
507
- cancelId: 1
508
- });
509
- await performInstall(response === 0);
510
- }
511
- }
512
- } catch (e) {
513
- if (this.onError) {
514
- this.onError(e);
515
- } else {
516
- console.error("[RenderUpdater] Download/Install error:", e);
517
- }
518
- }
519
- };
520
- if (this.onUpdateAvailable) {
521
- this.onUpdateAvailable(info, doDownload);
507
+ if (!checkRes.updateAvailable || !checkRes.info) return;
508
+ const info = checkRes.info;
509
+ if (checkRes.status === "ready") {
510
+ await this.handleReadyUpdate(info);
511
+ } else {
512
+ await this.handleNewUpdate(info);
513
+ }
514
+ } catch (e) {
515
+ this.handleError(e);
516
+ }
517
+ }
518
+ async handleReadyUpdate(info) {
519
+ if (this.onUpdateAvailable) {
520
+ this.onUpdateAvailable(info, async () => {
521
+ if (this.onDownloadComplete) {
522
+ this.onDownloadComplete(info, () => this.performInstall(info, true).catch(this.handleError.bind(this)));
522
523
  } else {
523
- if (info.forceUpdate || isReady) {
524
- await doDownload();
525
- } else if (this.autoDownload) {
526
- await doDownload();
527
- } else if (this.autoPrompt) {
528
- const { response } = await import_electron2.dialog.showMessageBox({
529
- type: "info",
530
- title: "\u53D1\u73B0\u65B0\u7248\u672C",
531
- message: `\u53D1\u73B0\u53EF\u7528\u66F4\u65B0 (v${info.version})\u3002\u662F\u5426\u7ACB\u5373\u4E0B\u8F7D\u66F4\u65B0\uFF1F
532
-
533
- \u66F4\u65B0\u65E5\u5FD7\uFF1A
534
- ${info.releaseNotes || "\u65E0\u8BE6\u7EC6\u8BB0\u5F55\u3002"}`,
535
- buttons: ["\u7ACB\u5373\u66F4\u65B0", "\u7A0D\u540E\u518D\u8BF4"],
536
- cancelId: 1
537
- });
538
- if (response === 0) {
539
- await doDownload();
540
- }
541
- }
524
+ await this.performInstall(info, true);
542
525
  }
526
+ });
527
+ return;
528
+ }
529
+ if (info.forceUpdate || !this.autoPrompt) {
530
+ await this.performInstall(info, true);
531
+ return;
532
+ }
533
+ const response = await this.showReadyDialog(info.version);
534
+ await this.performInstall(info, response === 0);
535
+ }
536
+ async handleNewUpdate(info) {
537
+ if (this.onUpdateAvailable) {
538
+ this.onUpdateAvailable(info, () => this.executeAutoDownload(info));
539
+ return;
540
+ }
541
+ if (info.forceUpdate || this.autoDownload) {
542
+ await this.executeAutoDownload(info);
543
+ } else if (this.autoPrompt) {
544
+ const response = await this.showNewUpdateDialog(info);
545
+ if (response === 0) {
546
+ await this.executeAutoDownload(info);
543
547
  }
544
- } catch (e) {
545
- if (this.onError) {
546
- this.onError(e);
548
+ }
549
+ }
550
+ async executeAutoDownload(info) {
551
+ try {
552
+ await this.download(info, this.onDownloadProgress);
553
+ const doInstall = (shouldRestart) => this.performInstall(info, shouldRestart).catch(this.handleError.bind(this));
554
+ if (this.onDownloadComplete) {
555
+ this.onDownloadComplete(info, () => doInstall(true));
547
556
  } else {
548
- console.error("[RenderUpdater] Error in checkForUpdatesAndNotify:", e);
557
+ const isSilent = info.forceUpdate === "silent";
558
+ const shouldRestart = isSilent || await this.showDownloadCompleteDialog(info.version, info.forceUpdate === "prompt") === 0;
559
+ await doInstall(shouldRestart);
549
560
  }
561
+ } catch (e) {
562
+ this.handleError(e);
563
+ }
564
+ }
565
+ async performInstall(info, shouldRestart) {
566
+ if (shouldRestart) {
567
+ await this.useVersion(info.version);
568
+ await this.installAndRestart();
569
+ } else {
570
+ await this.setUpdatePending(info.version);
571
+ console.info(`[RenderUpdater] Update v${info.version} ready, will apply on next launch.`);
572
+ }
573
+ }
574
+ handleError(error) {
575
+ if (this.onError) {
576
+ this.onError(error);
577
+ } else {
578
+ console.error("[RenderUpdater] Error:", error);
579
+ }
580
+ }
581
+ // UI 分发抽离
582
+ async showReadyDialog(version) {
583
+ const { response } = await import_electron2.dialog.showMessageBox({
584
+ type: "info",
585
+ title: "\u66F4\u65B0\u5C31\u7EEA",
586
+ message: `\u65B0\u7248\u672C v${version} \u5DF2\u51C6\u5907\u5C31\u7EEA\u3002\u662F\u5426\u7ACB\u5373\u91CD\u542F\u5E94\u7528\uFF1F`,
587
+ buttons: ["\u7ACB\u5373\u91CD\u542F", "\u7A0D\u540E\u518D\u8BF4"],
588
+ defaultId: 0,
589
+ cancelId: 1
590
+ });
591
+ return response;
592
+ }
593
+ async showNewUpdateDialog(info) {
594
+ const { response } = await import_electron2.dialog.showMessageBox({
595
+ type: "info",
596
+ title: "\u53D1\u73B0\u65B0\u7248\u672C",
597
+ message: `\u53D1\u73B0\u53EF\u7528\u66F4\u65B0 (v${info.version})\u3002\u662F\u5426\u7ACB\u5373\u4E0B\u8F7D\u66F4\u65B0\uFF1F
598
+
599
+ \u66F4\u65B0\u65E5\u5FD7\uFF1A
600
+ ${info.releaseNotes || "\u65E0\u8BE6\u7EC6\u8BB0\u5F55\u3002"}`,
601
+ buttons: ["\u7ACB\u5373\u66F4\u65B0", "\u7A0D\u540E\u518D\u8BF4"],
602
+ cancelId: 1
603
+ });
604
+ return response;
605
+ }
606
+ async showDownloadCompleteDialog(version, forcePrompt) {
607
+ if (forcePrompt) {
608
+ import_electron2.dialog.showMessageBoxSync({
609
+ type: "warning",
610
+ title: "\u4E0B\u8F7D\u5B8C\u6210",
611
+ message: `\u6700\u65B0\u7248\u672C v${version} \u5DF2\u4E0B\u8F7D\u5B8C\u6BD5\u3002\u5373\u5C06\u91CD\u542F\u7A0B\u5E8F\u4EE5\u5E94\u7528\u66F4\u65B0\u3002`,
612
+ buttons: ["\u6211\u77E5\u9053\u4E86"]
613
+ });
614
+ return 0;
615
+ } else if (this.autoPrompt) {
616
+ const { response } = await import_electron2.dialog.showMessageBox({
617
+ type: "info",
618
+ title: "\u4E0B\u8F7D\u5B8C\u6210",
619
+ message: `\u6700\u65B0\u7248\u672C v${version} \u5DF2\u4E0B\u8F7D\u5B8C\u6BD5\u3002\u662F\u5426\u7ACB\u5373\u91CD\u542F\u4EE5\u5E94\u7528\u66F4\u65B0\uFF1F`,
620
+ buttons: ["\u7ACB\u5373\u91CD\u542F", "\u7A0D\u540E\u518D\u8BF4"],
621
+ defaultId: 0,
622
+ cancelId: 1
623
+ });
624
+ return response;
550
625
  }
626
+ return 0;
551
627
  }
552
628
  };
629
+ function setupUpdaterIPC(updater) {
630
+ import_electron3.ipcMain.handle("updater:check", () => updater.check());
631
+ import_electron3.ipcMain.handle("updater:download", () => updater.download());
632
+ import_electron3.ipcMain.handle("updater:installAndRestart", () => updater.installAndRestart());
633
+ import_electron3.ipcMain.on("updater:useVersion", (_, version) => updater.useVersion(version));
634
+ updater.onDownloadProgress = (percent) => {
635
+ const { webContents } = require("electron");
636
+ webContents.getAllWebContents().forEach((wc) => {
637
+ wc.send("updater:onDownloadProgress", percent);
638
+ });
639
+ };
640
+ updater.onStatusChanged = (status) => {
641
+ const { webContents } = require("electron");
642
+ webContents.getAllWebContents().forEach((wc) => {
643
+ wc.send("updater:onStatusChanged", status);
644
+ });
645
+ };
646
+ updater.onBeforeRestart = () => {
647
+ const { webContents } = require("electron");
648
+ webContents.getAllWebContents().forEach((wc) => {
649
+ wc.send("updater:onBeforeRestart");
650
+ });
651
+ };
652
+ }
553
653
  // Annotate the CommonJS export names for ESM import in node:
554
654
  0 && (module.exports = {
555
- RenderUpdater
655
+ RenderUpdater,
656
+ setupUpdaterIPC
556
657
  });