electron-updater-for-render 1.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/README.zh-CN.md +359 -0
- package/Readme.md +356 -0
- package/dist/builder/index.cjs +112 -0
- package/dist/builder/index.d.ts +2 -0
- package/dist/builder/index.js +77 -0
- package/dist/builder/vite-plugin.cjs +133 -0
- package/dist/builder/vite-plugin.d.ts +5 -0
- package/dist/builder/vite-plugin.js +96 -0
- package/dist/main/index.cjs +372 -0
- package/dist/main/index.d.ts +43 -0
- package/dist/main/index.js +337 -0
- package/dist/types.d.ts +105 -0
- package/package.json +63 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// src/builder/index.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import crypto from "crypto";
|
|
5
|
+
async function createUpdatePackage(options) {
|
|
6
|
+
const asar = await import("asar").catch(() => {
|
|
7
|
+
throw new Error('Please install "asar" as a devDependency to use the builder.');
|
|
8
|
+
});
|
|
9
|
+
const {
|
|
10
|
+
outDir,
|
|
11
|
+
updatesDir = path.resolve(process.cwd(), "dist_updates"),
|
|
12
|
+
asarName = "renderer.asar",
|
|
13
|
+
version: explicitVersion,
|
|
14
|
+
packageJsonPath,
|
|
15
|
+
privateKeyPath,
|
|
16
|
+
releaseNotesPath,
|
|
17
|
+
forceUpdate
|
|
18
|
+
} = options;
|
|
19
|
+
if (!fs.existsSync(outDir)) {
|
|
20
|
+
throw new Error(`Output directory not found at ${outDir}`);
|
|
21
|
+
}
|
|
22
|
+
if (!fs.existsSync(updatesDir)) {
|
|
23
|
+
fs.mkdirSync(updatesDir, { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
let version;
|
|
26
|
+
if (explicitVersion) {
|
|
27
|
+
version = explicitVersion;
|
|
28
|
+
} else {
|
|
29
|
+
const pkgPath = packageJsonPath ?? path.resolve(process.cwd(), "package.json");
|
|
30
|
+
if (!fs.existsSync(pkgPath)) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`Cannot resolve version: package.json not found at ${pkgPath}. Please set "version" or "packageJsonPath" in the plugin options.`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
36
|
+
version = pkg.version || "0.0.0";
|
|
37
|
+
}
|
|
38
|
+
const versionDir = path.join(updatesDir, version);
|
|
39
|
+
if (!fs.existsSync(versionDir)) {
|
|
40
|
+
fs.mkdirSync(versionDir, { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
const asarPath = path.join(versionDir, asarName);
|
|
43
|
+
await asar.createPackage(outDir, asarPath);
|
|
44
|
+
const fileBuffer = fs.readFileSync(asarPath);
|
|
45
|
+
const hashSum = crypto.createHash("sha256");
|
|
46
|
+
hashSum.update(fileBuffer);
|
|
47
|
+
const sha256 = hashSum.digest("hex");
|
|
48
|
+
let signature = "";
|
|
49
|
+
if (privateKeyPath && fs.existsSync(privateKeyPath)) {
|
|
50
|
+
const privateKey = fs.readFileSync(privateKeyPath, "utf8");
|
|
51
|
+
const sign = crypto.createSign("RSA-SHA256");
|
|
52
|
+
sign.update(fileBuffer);
|
|
53
|
+
sign.end();
|
|
54
|
+
signature = sign.sign(privateKey, "base64");
|
|
55
|
+
}
|
|
56
|
+
let releaseNotesContent = "No release notes provided.";
|
|
57
|
+
if (releaseNotesPath && fs.existsSync(releaseNotesPath)) {
|
|
58
|
+
releaseNotesContent = fs.readFileSync(releaseNotesPath, "utf-8");
|
|
59
|
+
}
|
|
60
|
+
const updateInfo = {
|
|
61
|
+
version,
|
|
62
|
+
path: `${version}/${asarName}`,
|
|
63
|
+
sha256,
|
|
64
|
+
...signature ? { signature } : {},
|
|
65
|
+
date: (/* @__PURE__ */ new Date()).toISOString(),
|
|
66
|
+
releaseNotes: releaseNotesContent,
|
|
67
|
+
...forceUpdate ? { forceUpdate } : {}
|
|
68
|
+
};
|
|
69
|
+
const jsonPath = path.join(updatesDir, "latest.json");
|
|
70
|
+
fs.writeFileSync(jsonPath, JSON.stringify(updateInfo, null, 2));
|
|
71
|
+
console.log(`[RenderUpdater Builder] Successfully created package for v${version}`);
|
|
72
|
+
console.log(`[RenderUpdater Builder] ASAR: ${asarPath}`);
|
|
73
|
+
console.log(`[RenderUpdater Builder] Info: ${jsonPath}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// src/builder/vite-plugin.ts
|
|
77
|
+
function electronRenderUpdater(options) {
|
|
78
|
+
return {
|
|
79
|
+
name: "vite-plugin-electron-render-updater",
|
|
80
|
+
apply: "build",
|
|
81
|
+
closeBundle: async () => {
|
|
82
|
+
if (!process.env.ELECTRON_PACK_UPDATE) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
console.log("\n[vite-plugin-electron-render-updater] Build finished, packing ASAR...");
|
|
86
|
+
try {
|
|
87
|
+
await createUpdatePackage(options);
|
|
88
|
+
} catch (err) {
|
|
89
|
+
console.error("[vite-plugin-electron-render-updater] Failed to pack ASAR:", err);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
export {
|
|
95
|
+
electronRenderUpdater
|
|
96
|
+
};
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/main/index.ts
|
|
31
|
+
var main_exports = {};
|
|
32
|
+
__export(main_exports, {
|
|
33
|
+
RenderUpdater: () => RenderUpdater
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(main_exports);
|
|
36
|
+
var import_path = __toESM(require("path"), 1);
|
|
37
|
+
var import_original_fs = __toESM(require("original-fs"), 1);
|
|
38
|
+
var import_crypto = __toESM(require("crypto"), 1);
|
|
39
|
+
var import_electron = require("electron");
|
|
40
|
+
var import_promises = require("stream/promises");
|
|
41
|
+
var import_stream = require("stream");
|
|
42
|
+
var RenderUpdater = class {
|
|
43
|
+
versionsDir;
|
|
44
|
+
currentVersionFile;
|
|
45
|
+
baseUrl;
|
|
46
|
+
activeVersion;
|
|
47
|
+
publicKey;
|
|
48
|
+
isDownloading = false;
|
|
49
|
+
autoDownload;
|
|
50
|
+
autoPrompt;
|
|
51
|
+
maxVersionsToKeep;
|
|
52
|
+
onUpdateAvailable;
|
|
53
|
+
onDownloadProgress;
|
|
54
|
+
onDownloadComplete;
|
|
55
|
+
onError;
|
|
56
|
+
onBeforeRestart;
|
|
57
|
+
constructor(options) {
|
|
58
|
+
this.versionsDir = options.versionsDir;
|
|
59
|
+
this.currentVersionFile = import_path.default.join(this.versionsDir, "current.json");
|
|
60
|
+
this.baseUrl = options.updateUrl;
|
|
61
|
+
this.publicKey = options.publicKey;
|
|
62
|
+
this.autoDownload = options.autoDownload ?? false;
|
|
63
|
+
this.autoPrompt = options.autoPrompt ?? true;
|
|
64
|
+
this.maxVersionsToKeep = options.maxVersionsToKeep ?? 2;
|
|
65
|
+
this.onUpdateAvailable = options.onUpdateAvailable;
|
|
66
|
+
this.onDownloadProgress = options.onDownloadProgress;
|
|
67
|
+
this.onDownloadComplete = options.onDownloadComplete;
|
|
68
|
+
this.onError = options.onError;
|
|
69
|
+
this.onBeforeRestart = options.onBeforeRestart;
|
|
70
|
+
if (!import_original_fs.default.existsSync(this.versionsDir)) {
|
|
71
|
+
import_original_fs.default.mkdirSync(this.versionsDir, { recursive: true });
|
|
72
|
+
}
|
|
73
|
+
this.activeVersion = this.readCurrentVersionFromFile();
|
|
74
|
+
this.cleanOldVersions();
|
|
75
|
+
}
|
|
76
|
+
cleanOldVersions() {
|
|
77
|
+
try {
|
|
78
|
+
if (!import_original_fs.default.existsSync(this.versionsDir)) return;
|
|
79
|
+
const items = import_original_fs.default.readdirSync(this.versionsDir);
|
|
80
|
+
const versionDirs = [];
|
|
81
|
+
for (const item of items) {
|
|
82
|
+
if (item === "current.json") continue;
|
|
83
|
+
const fullPath = import_path.default.join(this.versionsDir, item);
|
|
84
|
+
if (import_original_fs.default.statSync(fullPath).isDirectory()) {
|
|
85
|
+
versionDirs.push(item);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
versionDirs.sort((a, b) => this.compareVersions(b, a));
|
|
89
|
+
const activeIdx = versionDirs.indexOf(this.activeVersion);
|
|
90
|
+
const safeVersions = /* @__PURE__ */ new Set();
|
|
91
|
+
if (activeIdx !== -1) safeVersions.add(this.activeVersion);
|
|
92
|
+
let kept = 0;
|
|
93
|
+
for (const v of versionDirs) {
|
|
94
|
+
if (kept >= this.maxVersionsToKeep) break;
|
|
95
|
+
if (!safeVersions.has(v)) {
|
|
96
|
+
safeVersions.add(v);
|
|
97
|
+
kept++;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
for (const v of versionDirs) {
|
|
101
|
+
if (!safeVersions.has(v)) {
|
|
102
|
+
console.info(`[RenderUpdater] Cleaning up old version: ${v}`);
|
|
103
|
+
import_original_fs.default.rmSync(import_path.default.join(this.versionsDir, v), { recursive: true, force: true });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
} catch (e) {
|
|
107
|
+
console.error("[RenderUpdater] Failed to clean old versions:", e);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
readCurrentVersionFromFile() {
|
|
111
|
+
if (import_original_fs.default.existsSync(this.currentVersionFile)) {
|
|
112
|
+
try {
|
|
113
|
+
const current = JSON.parse(import_original_fs.default.readFileSync(this.currentVersionFile, "utf-8"));
|
|
114
|
+
return current.version;
|
|
115
|
+
} catch {
|
|
116
|
+
return "0.0.0";
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return "0.0.0";
|
|
120
|
+
}
|
|
121
|
+
hasAnyVersion() {
|
|
122
|
+
return import_original_fs.default.existsSync(this.currentVersionFile);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Returns the file:// URL to the latest renderer.asar index.html,
|
|
126
|
+
* or empty string if no update is available.
|
|
127
|
+
*/
|
|
128
|
+
getLoadUrl() {
|
|
129
|
+
try {
|
|
130
|
+
if (import_original_fs.default.existsSync(this.currentVersionFile)) {
|
|
131
|
+
const current = JSON.parse(import_original_fs.default.readFileSync(this.currentVersionFile, "utf-8"));
|
|
132
|
+
const version = current.version;
|
|
133
|
+
const asarPath = import_path.default.join(this.versionsDir, version, "renderer.asar");
|
|
134
|
+
if (import_original_fs.default.existsSync(asarPath)) {
|
|
135
|
+
return `file://${asarPath}/index.html`;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
} catch (e) {
|
|
139
|
+
console.error("[RenderUpdater] Failed to get load URL:", e);
|
|
140
|
+
}
|
|
141
|
+
return "";
|
|
142
|
+
}
|
|
143
|
+
async check() {
|
|
144
|
+
try {
|
|
145
|
+
const response = await fetch(`${this.baseUrl}/latest.json?t=${Date.now()}`);
|
|
146
|
+
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
|
|
147
|
+
const remoteInfo = await response.json();
|
|
148
|
+
const currentVersion = this.activeVersion;
|
|
149
|
+
if (this.compareVersions(remoteInfo.version, currentVersion) > 0) {
|
|
150
|
+
return { updateAvailable: true, version: remoteInfo.version, info: remoteInfo };
|
|
151
|
+
}
|
|
152
|
+
return { updateAvailable: false };
|
|
153
|
+
} catch (e) {
|
|
154
|
+
console.error("[RenderUpdater] Update check failed:", e);
|
|
155
|
+
throw e;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Downloads the update with resume support.
|
|
160
|
+
*/
|
|
161
|
+
async download(onProgress) {
|
|
162
|
+
if (this.isDownloading) throw new Error("[RenderUpdater] Download is already in progress.");
|
|
163
|
+
this.isDownloading = true;
|
|
164
|
+
try {
|
|
165
|
+
const response = await fetch(`${this.baseUrl}/latest.json?t=${Date.now()}`);
|
|
166
|
+
if (!response.ok) throw new Error("[RenderUpdater] Cannot fetch latest.json for download");
|
|
167
|
+
const info = await response.json();
|
|
168
|
+
const versionDir = import_path.default.join(this.versionsDir, info.version);
|
|
169
|
+
const asarPath = import_path.default.join(versionDir, "renderer.asar");
|
|
170
|
+
if (import_original_fs.default.existsSync(asarPath) && this.verifyFile(asarPath, info)) {
|
|
171
|
+
onProgress?.(100);
|
|
172
|
+
this.useVersion(info.version);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (!import_original_fs.default.existsSync(versionDir)) {
|
|
176
|
+
import_original_fs.default.mkdirSync(versionDir, { recursive: true });
|
|
177
|
+
}
|
|
178
|
+
let downloadedBytes = 0;
|
|
179
|
+
if (import_original_fs.default.existsSync(asarPath)) {
|
|
180
|
+
downloadedBytes = import_original_fs.default.statSync(asarPath).size;
|
|
181
|
+
}
|
|
182
|
+
const fetchOptions = {};
|
|
183
|
+
if (downloadedBytes > 0) {
|
|
184
|
+
fetchOptions.headers = {
|
|
185
|
+
Range: `bytes=${downloadedBytes}-`
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
const downloadUrl = info.path ? `${this.baseUrl.replace(/\/$/, "")}/${info.path}` : info.url;
|
|
189
|
+
if (!downloadUrl) throw new Error("[RenderUpdater] Cannot determine download URL: info.path and info.url are both missing.");
|
|
190
|
+
const downloadResponse = await fetch(downloadUrl, fetchOptions);
|
|
191
|
+
if (downloadResponse.status !== 206 && downloadResponse.status !== 200) {
|
|
192
|
+
throw new Error(`Failed to download update: ${downloadResponse.statusText}`);
|
|
193
|
+
}
|
|
194
|
+
if (downloadResponse.status === 200) {
|
|
195
|
+
downloadedBytes = 0;
|
|
196
|
+
}
|
|
197
|
+
const contentLength = downloadResponse.headers.get("content-length");
|
|
198
|
+
const incomingTotal = contentLength ? parseInt(contentLength, 10) : 0;
|
|
199
|
+
const total = downloadedBytes + incomingTotal;
|
|
200
|
+
if (!downloadResponse.body) throw new Error("Response body is empty");
|
|
201
|
+
const fileStream = import_original_fs.default.createWriteStream(asarPath, { flags: downloadResponse.status === 206 ? "a" : "w" });
|
|
202
|
+
const progressTransform = new import_stream.Transform({
|
|
203
|
+
transform(chunk, _encoding, callback) {
|
|
204
|
+
downloadedBytes += chunk.length;
|
|
205
|
+
if (total > 0) {
|
|
206
|
+
onProgress?.(downloadedBytes / total * 100);
|
|
207
|
+
}
|
|
208
|
+
callback(null, chunk);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
await (0, import_promises.pipeline)(
|
|
212
|
+
import_stream.Readable.fromWeb(downloadResponse.body),
|
|
213
|
+
progressTransform,
|
|
214
|
+
fileStream
|
|
215
|
+
);
|
|
216
|
+
if (!this.verifyFile(asarPath, info)) {
|
|
217
|
+
import_original_fs.default.rmSync(versionDir, { recursive: true, force: true });
|
|
218
|
+
throw new Error("[RenderUpdater] Verification failed after download (SHA256 or RSA Mismatch)");
|
|
219
|
+
}
|
|
220
|
+
this.useVersion(info.version);
|
|
221
|
+
this.cleanOldVersions();
|
|
222
|
+
} finally {
|
|
223
|
+
this.isDownloading = false;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
verifyFile(filePath, info) {
|
|
227
|
+
const fileBuffer = import_original_fs.default.readFileSync(filePath);
|
|
228
|
+
const hashSum = import_crypto.default.createHash("sha256");
|
|
229
|
+
hashSum.update(fileBuffer);
|
|
230
|
+
const sha256 = hashSum.digest("hex");
|
|
231
|
+
if (sha256 !== info.sha256) {
|
|
232
|
+
console.error(`[RenderUpdater] SHA256 mismatch! Expected ${info.sha256}, got ${sha256}`);
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
if (this.publicKey && info.signature) {
|
|
236
|
+
try {
|
|
237
|
+
const verify = import_crypto.default.createVerify("RSA-SHA256");
|
|
238
|
+
verify.update(fileBuffer);
|
|
239
|
+
verify.end();
|
|
240
|
+
const isValid = verify.verify(this.publicKey, info.signature, "base64");
|
|
241
|
+
if (!isValid) {
|
|
242
|
+
console.error("[RenderUpdater] RSA Signature verification failed.");
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
} catch (err) {
|
|
246
|
+
console.error("[RenderUpdater] RSA verification error:", err);
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
useVersion(version) {
|
|
253
|
+
import_original_fs.default.writeFileSync(
|
|
254
|
+
this.currentVersionFile,
|
|
255
|
+
JSON.stringify({ version, date: (/* @__PURE__ */ new Date()).toISOString() })
|
|
256
|
+
);
|
|
257
|
+
this.activeVersion = version;
|
|
258
|
+
}
|
|
259
|
+
compareVersions(v1, v2) {
|
|
260
|
+
const parts1 = v1.split(".").map(Number);
|
|
261
|
+
const parts2 = v2.split(".").map(Number);
|
|
262
|
+
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
263
|
+
const n1 = parts1[i] || 0;
|
|
264
|
+
const n2 = parts2[i] || 0;
|
|
265
|
+
if (n1 > n2) return 1;
|
|
266
|
+
if (n1 < n2) return -1;
|
|
267
|
+
}
|
|
268
|
+
return 0;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* One-stop method to check for updates, show dialogs (if autoPrompt=true),
|
|
272
|
+
* download, and restart application.
|
|
273
|
+
*/
|
|
274
|
+
async checkForUpdatesAndNotify() {
|
|
275
|
+
try {
|
|
276
|
+
const checkResult = await this.check();
|
|
277
|
+
if (!checkResult.updateAvailable || !checkResult.info) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
const info = checkResult.info;
|
|
281
|
+
const doDownload = async () => {
|
|
282
|
+
try {
|
|
283
|
+
await this.download(this.onDownloadProgress);
|
|
284
|
+
const doInstall = async () => {
|
|
285
|
+
if (this.onBeforeRestart) {
|
|
286
|
+
await this.onBeforeRestart();
|
|
287
|
+
}
|
|
288
|
+
import_electron.app.relaunch();
|
|
289
|
+
import_electron.app.quit();
|
|
290
|
+
};
|
|
291
|
+
if (this.onDownloadComplete) {
|
|
292
|
+
this.onDownloadComplete(info, () => {
|
|
293
|
+
doInstall().catch(console.error);
|
|
294
|
+
});
|
|
295
|
+
} else {
|
|
296
|
+
if (info.forceUpdate) {
|
|
297
|
+
if (info.forceUpdate === "prompt") {
|
|
298
|
+
import_electron.dialog.showMessageBoxSync({
|
|
299
|
+
type: "warning",
|
|
300
|
+
title: "\u6700\u9AD8\u7EA7\u522B\u5F3A\u5236\u63A5\u7BA1",
|
|
301
|
+
message: "\u7D27\u6025\u707E\u96BE\u4FEE\u590D\u7EC4\u4EF6\u5DF2\u7ECF\u4E0B\u8F7D\u5C31\u7EEA\u3002\u5373\u5C06\u5F3A\u884C\u963B\u65AD\u5E76\u91CD\u542F\u5E94\u7528\u8FDB\u884C\u6302\u8F7D\u3002",
|
|
302
|
+
buttons: ["\u5F3A\u5236\u63A5\u7BA1"]
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
doInstall().catch(console.error);
|
|
306
|
+
} else if (this.autoPrompt) {
|
|
307
|
+
import_electron.dialog.showMessageBoxSync({
|
|
308
|
+
type: "info",
|
|
309
|
+
title: "\u4E0B\u8F7D\u5B8C\u6210",
|
|
310
|
+
message: "\u6700\u65B0\u7248\u672C\u5DF2\u4E0B\u8F7D\u5B8C\u6BD5\u3002\u70B9\u51FB\u786E\u5B9A\u91CD\u542F\u5E94\u7528\u4EE5\u5E94\u7528\u66F4\u65B0\u3002",
|
|
311
|
+
buttons: ["\u786E\u5B9A"]
|
|
312
|
+
});
|
|
313
|
+
doInstall().catch(console.error);
|
|
314
|
+
} else {
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
} catch (e) {
|
|
318
|
+
if (this.onError) {
|
|
319
|
+
this.onError(e);
|
|
320
|
+
} else {
|
|
321
|
+
console.error("[RenderUpdater] Download error:", e);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
if (this.onUpdateAvailable) {
|
|
326
|
+
this.onUpdateAvailable(info, doDownload);
|
|
327
|
+
} else {
|
|
328
|
+
if (info.forceUpdate) {
|
|
329
|
+
if (info.forceUpdate === "prompt") {
|
|
330
|
+
import_electron.dialog.showMessageBoxSync({
|
|
331
|
+
type: "warning",
|
|
332
|
+
title: "\u{1F6A8} \u53D1\u73B0\u751F\u6B7B\u6538\u5173\u7684\u6838\u5FC3\u66F4\u65B0",
|
|
333
|
+
message: `\u63A2\u6D4B\u5230\u7CFB\u7EDF\u81F4\u547D Bug \u7684\u4FEE\u590D\u7248\u672C (v${info.version})\u3002
|
|
334
|
+
\u5E94\u7528\u73AF\u5883\u5373\u523B\u88AB\u9501\u5B9A\uFF0C\u5F00\u59CB\u5F3A\u5236\u6267\u884C\u5168\u91CF\u4E0B\u8F7D\uFF0C\u4EFB\u4F55\u4EBA\u90FD\u65E0\u6CD5\u4E2D\u6B62\uFF01
|
|
335
|
+
|
|
336
|
+
\u66F4\u65B0\u65E5\u5FD7\uFF1A
|
|
337
|
+
${info.releaseNotes || "\u707E\u5907\u9884\u6848\u5F3A\u5236\u6FC0\u6D3B\uFF0C\u65E0\u53EF\u5949\u544A\u3002"}`,
|
|
338
|
+
buttons: ["\u4E56\u4E56\u5347\u7EA7"]
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
await doDownload();
|
|
342
|
+
} else if (this.autoDownload) {
|
|
343
|
+
await doDownload();
|
|
344
|
+
} else if (this.autoPrompt) {
|
|
345
|
+
const { response } = await import_electron.dialog.showMessageBox({
|
|
346
|
+
type: "info",
|
|
347
|
+
title: "\u53D1\u73B0\u65B0\u7248\u672C",
|
|
348
|
+
message: `\u53D1\u73B0\u53EF\u7528\u66F4\u65B0 (v${info.version})\u3002\u662F\u5426\u7ACB\u5373\u4E0B\u8F7D\u66F4\u65B0\uFF1F
|
|
349
|
+
|
|
350
|
+
\u66F4\u65B0\u65E5\u5FD7\uFF1A
|
|
351
|
+
${info.releaseNotes || "\u65E0\u8BE6\u7EC6\u8BB0\u5F55\u3002"}`,
|
|
352
|
+
buttons: ["\u7ACB\u5373\u66F4\u65B0", "\u7A0D\u540E\u518D\u8BF4"],
|
|
353
|
+
cancelId: 1
|
|
354
|
+
});
|
|
355
|
+
if (response === 0) {
|
|
356
|
+
await doDownload();
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
} catch (e) {
|
|
361
|
+
if (this.onError) {
|
|
362
|
+
this.onError(e);
|
|
363
|
+
} else {
|
|
364
|
+
console.error("[RenderUpdater] Error in checkForUpdatesAndNotify:", e);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
370
|
+
0 && (module.exports = {
|
|
371
|
+
RenderUpdater
|
|
372
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { UpdateInfo, UpdaterOptions } from '../types';
|
|
2
|
+
export declare class RenderUpdater {
|
|
3
|
+
private versionsDir;
|
|
4
|
+
private currentVersionFile;
|
|
5
|
+
private baseUrl;
|
|
6
|
+
private activeVersion;
|
|
7
|
+
private publicKey?;
|
|
8
|
+
private isDownloading;
|
|
9
|
+
private autoDownload;
|
|
10
|
+
private autoPrompt;
|
|
11
|
+
private maxVersionsToKeep;
|
|
12
|
+
onUpdateAvailable?: (info: UpdateInfo, doDownload: () => Promise<void>) => void;
|
|
13
|
+
onDownloadProgress?: (percent: number) => void;
|
|
14
|
+
onDownloadComplete?: (info: UpdateInfo, doInstall: () => void) => void;
|
|
15
|
+
onError?: (error: Error) => void;
|
|
16
|
+
onBeforeRestart?: () => void | Promise<void>;
|
|
17
|
+
constructor(options: UpdaterOptions);
|
|
18
|
+
private cleanOldVersions;
|
|
19
|
+
private readCurrentVersionFromFile;
|
|
20
|
+
hasAnyVersion(): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Returns the file:// URL to the latest renderer.asar index.html,
|
|
23
|
+
* or empty string if no update is available.
|
|
24
|
+
*/
|
|
25
|
+
getLoadUrl(): string;
|
|
26
|
+
check(): Promise<{
|
|
27
|
+
updateAvailable: boolean;
|
|
28
|
+
version?: string;
|
|
29
|
+
info?: UpdateInfo;
|
|
30
|
+
}>;
|
|
31
|
+
/**
|
|
32
|
+
* Downloads the update with resume support.
|
|
33
|
+
*/
|
|
34
|
+
download(onProgress?: (percent: number) => void): Promise<void>;
|
|
35
|
+
private verifyFile;
|
|
36
|
+
private useVersion;
|
|
37
|
+
private compareVersions;
|
|
38
|
+
/**
|
|
39
|
+
* One-stop method to check for updates, show dialogs (if autoPrompt=true),
|
|
40
|
+
* download, and restart application.
|
|
41
|
+
*/
|
|
42
|
+
checkForUpdatesAndNotify(): Promise<void>;
|
|
43
|
+
}
|