electron-incremental-update 0.2.0 → 0.2.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.cjs CHANGED
@@ -33,7 +33,7 @@ __export(src_exports, {
33
33
  createUpdater: () => createUpdater,
34
34
  getAppAsarPath: () => getAppAsarPath,
35
35
  getAppVersion: () => getAppVersion,
36
- getElectronVersion: () => getElectronVersion,
36
+ getEntryVersion: () => getEntryVersion,
37
37
  getReleaseCdnLink: () => getReleaseCdnLink,
38
38
  initApp: () => initApp,
39
39
  requireNative: () => requireNative
@@ -50,7 +50,7 @@ var import_node_fs2 = require("fs");
50
50
  var import_promises = require("fs/promises");
51
51
  var import_electron2 = require("electron");
52
52
 
53
- // src/updater/download.ts
53
+ // src/updater/defaultFunctions.ts
54
54
  var import_node_buffer = require("buffer");
55
55
  var import_node_https = __toESM(require("https"), 1);
56
56
  function downloadJSONDefault(url, updater, headers) {
@@ -96,6 +96,28 @@ function downloadBufferDefault(url, updater, headers) {
96
96
  });
97
97
  });
98
98
  }
99
+ function compareVersionDefault(oldVersion, newVersion) {
100
+ if (!oldVersion || !newVersion) {
101
+ throw new TypeError("invalid version");
102
+ }
103
+ const parseVersion = (version) => {
104
+ const [versionNumber, stage] = version.split("-");
105
+ const [major, minor, patch] = versionNumber.split(".").map(Number);
106
+ if (isNaN(major) || isNaN(minor) || isNaN(patch)) {
107
+ throw new TypeError("invalid version");
108
+ }
109
+ return { major, minor, patch, stage };
110
+ };
111
+ const oldV = parseVersion(oldVersion);
112
+ const newV = parseVersion(newVersion);
113
+ if (oldV.major < newV.major || oldV.major === newV.major && oldV.minor < newV.minor || oldV.major === newV.major && oldV.minor === newV.minor && oldV.patch < newV.patch) {
114
+ return true;
115
+ }
116
+ if (oldV.stage < newV.stage || !newV.stage && oldV.stage) {
117
+ return true;
118
+ }
119
+ return false;
120
+ }
99
121
 
100
122
  // src/updater/utils.ts
101
123
  var import_node_fs = require("fs");
@@ -104,11 +126,11 @@ var import_electron = require("electron");
104
126
  function getAppAsarPath(name) {
105
127
  return import_electron.app.isPackaged ? (0, import_node_path.join)((0, import_node_path.dirname)(import_electron.app.getAppPath()), `${name}.asar`) : "dev";
106
128
  }
107
- function getElectronVersion() {
129
+ function getEntryVersion() {
108
130
  return import_electron.app.getVersion();
109
131
  }
110
132
  function getAppVersion(name) {
111
- return import_electron.app.isPackaged ? (0, import_node_fs.readFileSync)((0, import_node_path.join)(getAppAsarPath(name), "version"), "utf-8").trim() : getElectronVersion();
133
+ return import_electron.app.isPackaged ? (0, import_node_fs.readFileSync)((0, import_node_path.join)(getAppAsarPath(name), "version"), "utf-8").trim() : getEntryVersion();
112
134
  }
113
135
  function requireNative(packageName) {
114
136
  const path = import_electron.app.isPackaged ? (0, import_node_path.join)(import_electron.app.getAppPath(), "node_modules", packageName) : packageName;
@@ -149,10 +171,15 @@ function createUpdater({
149
171
  productName,
150
172
  releaseAsarURL: _release,
151
173
  updateJsonURL: _update,
152
- downloadConfig
174
+ debug = false,
175
+ downloadConfig,
176
+ compareVersion
153
177
  }) {
154
178
  const updater = new import_node_events.EventEmitter();
155
179
  const { downloadBuffer, downloadJSON, extraHeader, userAgent } = downloadConfig || {};
180
+ function log(...args) {
181
+ debug && console.log(...args);
182
+ }
156
183
  async function download(url, format) {
157
184
  const ua = userAgent || "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36";
158
185
  const headers = {
@@ -160,6 +187,7 @@ function createUpdater({
160
187
  UserAgent: ua,
161
188
  ...extraHeader
162
189
  };
190
+ log("[updater] headers", headers);
163
191
  const downloadFn = format === "json" ? downloadJSON ?? downloadJSONDefault : downloadBuffer ?? downloadBufferDefault;
164
192
  return await downloadFn(url, updater, headers);
165
193
  }
@@ -173,25 +201,32 @@ function createUpdater({
173
201
  const input = (0, import_node_fs2.createReadStream)(gzipFilePath);
174
202
  const outputFilePath = gzipFilePath.replace(".tmp.gz", ".asar");
175
203
  const output = (0, import_node_fs2.createWriteStream)(outputFilePath);
204
+ log("[updater] outputFilePath", outputFilePath);
176
205
  input.pipe(gunzip).pipe(output).on("finish", async () => {
177
206
  await (0, import_promises.rm)(gzipFilePath);
207
+ log("[updater] finish");
178
208
  resolve2(outputFilePath);
179
209
  }).on("error", async (err) => {
180
210
  await (0, import_promises.rm)(gzipFilePath);
211
+ log("[updater] error", err);
181
212
  output.destroy(err);
182
213
  reject(err);
183
214
  });
184
215
  });
185
216
  }
186
217
  function verify(buffer, signature) {
218
+ log("[updater] signature", signature);
187
219
  return (0, import_node_crypto.createVerify)("RSA-SHA256").update(buffer).verify(SIGNATURE_PUB, signature, "base64");
188
220
  }
189
221
  function needUpdate(version) {
190
- const parseVersion = (version2) => {
191
- const [major, minor, patch] = version2.split(".");
192
- return ~~major * 100 + ~~minor * 10 + ~~patch;
193
- };
194
- return import_electron2.app.isPackaged && parseVersion(import_electron2.app.getVersion()) < parseVersion(version);
222
+ if (!version || !import_electron2.app.isPackaged) {
223
+ return false;
224
+ }
225
+ const currentVersion = getEntryVersion();
226
+ log("[updater] currentVersion", currentVersion);
227
+ log("[updater] newVersion", version);
228
+ const _compare = compareVersion ?? compareVersionDefault;
229
+ return _compare(currentVersion, version);
195
230
  }
196
231
  async function checkUpdate(option) {
197
232
  let {
@@ -199,15 +234,19 @@ function createUpdater({
199
234
  releaseAsarURL = _release
200
235
  } = option || {};
201
236
  if (!updateJsonURL || !releaseAsarURL) {
237
+ log("[updater] no updateJsonURL or releaseAsarURL, use repository");
202
238
  if (!repository) {
203
239
  throw new Error("updateJsonURL or releaseAsarURL are not set");
204
240
  }
205
241
  updateJsonURL = `${repository.replace("github.com", "raw.githubusercontent.com")}/version.json`;
206
242
  releaseAsarURL = `${repository}/releases/download/latest/${productName}.asar.gz`;
207
243
  }
244
+ log("[updater] updateJsonURL", updateJsonURL);
245
+ log("[updater] releaseAsarURL", releaseAsarURL);
208
246
  const gzipPath = `../${productName}.asar.gz`;
209
247
  const tmpFile = gzipPath.replace(".asar.gz", ".tmp.gz");
210
248
  if ((0, import_node_fs2.existsSync)(tmpFile)) {
249
+ log("[updater] remove tmp file", tmpFile);
211
250
  await (0, import_promises.rm)(tmpFile);
212
251
  }
213
252
  const json = await download(updateJsonURL, "json");
@@ -219,15 +258,19 @@ function createUpdater({
219
258
  version,
220
259
  size
221
260
  } = json;
261
+ log("[updater] UpdateJSON", json);
222
262
  if (!needUpdate(version)) {
223
263
  return "unavailable";
224
264
  }
225
265
  updater.emit("downloadStart", size);
226
266
  const buffer = await download(releaseAsarURL, "buffer");
267
+ log("[updater] start verify");
227
268
  if (!verify(buffer, signature)) {
228
269
  throw new Error("file broken, invalid signature!");
229
270
  }
271
+ log("[updater] write file", gzipPath);
230
272
  await (0, import_promises.writeFile)(gzipPath, buffer);
273
+ log("[updater] extract file", gzipPath);
231
274
  await extractFile(gzipPath);
232
275
  return "success";
233
276
  }
@@ -266,7 +309,7 @@ function initApp(productName, updater, option) {
266
309
  createUpdater,
267
310
  getAppAsarPath,
268
311
  getAppVersion,
269
- getElectronVersion,
312
+ getEntryVersion,
270
313
  getReleaseCdnLink,
271
314
  initApp,
272
315
  requireNative
package/dist/index.d.ts CHANGED
@@ -72,6 +72,8 @@ interface UpdaterOption extends BaseOption {
72
72
  * `repository` will be used to determine the url
73
73
  */
74
74
  repository?: string;
75
+ debug?: boolean;
76
+ compareVersion?: (oldVersion: string, newVersion: string) => boolean;
75
77
  downloadConfig?: {
76
78
  /**
77
79
  * download user agent
@@ -107,11 +109,11 @@ interface UpdaterOption extends BaseOption {
107
109
  */
108
110
  declare function getAppAsarPath(name: string): string;
109
111
  /**
110
- * get the version of electron
112
+ * get the version of entry (app.asar)
111
113
  */
112
- declare function getElectronVersion(): string;
114
+ declare function getEntryVersion(): string;
113
115
  /**
114
- * get the version of application
116
+ * get the version of application (name.asar)
115
117
  * @param name - The name of the application
116
118
  */
117
119
  declare function getAppVersion(name: string): string;
@@ -128,7 +130,7 @@ declare function getReleaseCdnLink(url: string): {
128
130
  maintainer: string;
129
131
  }[];
130
132
 
131
- declare function createUpdater({ SIGNATURE_PUB, repository, productName, releaseAsarURL: _release, updateJsonURL: _update, downloadConfig, }: UpdaterOption): Updater;
133
+ declare function createUpdater({ SIGNATURE_PUB, repository, productName, releaseAsarURL: _release, updateJsonURL: _update, debug, downloadConfig, compareVersion, }: UpdaterOption): Updater;
132
134
 
133
135
  interface AppOption {
134
136
  /**
@@ -187,4 +189,4 @@ declare function initApp(productName: string, updater: Updater | Omit<UpdaterOpt
187
189
  productName?: string;
188
190
  }, option?: AppOption): any;
189
191
 
190
- export { BaseOption, CheckResultType, UpdateJSON, Updater, UpdaterOption, createUpdater, getAppAsarPath, getAppVersion, getElectronVersion, getReleaseCdnLink, initApp, requireNative };
192
+ export { BaseOption, CheckResultType, UpdateJSON, Updater, UpdaterOption, createUpdater, getAppAsarPath, getAppVersion, getEntryVersion, getReleaseCdnLink, initApp, requireNative };
package/dist/index.mjs CHANGED
@@ -14,7 +14,7 @@ import { createReadStream, createWriteStream, existsSync } from "node:fs";
14
14
  import { rm, writeFile } from "node:fs/promises";
15
15
  import { app as app2 } from "electron";
16
16
 
17
- // src/updater/download.ts
17
+ // src/updater/defaultFunctions.ts
18
18
  import { Buffer } from "node:buffer";
19
19
  import https from "node:https";
20
20
  function downloadJSONDefault(url, updater, headers) {
@@ -60,6 +60,28 @@ function downloadBufferDefault(url, updater, headers) {
60
60
  });
61
61
  });
62
62
  }
63
+ function compareVersionDefault(oldVersion, newVersion) {
64
+ if (!oldVersion || !newVersion) {
65
+ throw new TypeError("invalid version");
66
+ }
67
+ const parseVersion = (version) => {
68
+ const [versionNumber, stage] = version.split("-");
69
+ const [major, minor, patch] = versionNumber.split(".").map(Number);
70
+ if (isNaN(major) || isNaN(minor) || isNaN(patch)) {
71
+ throw new TypeError("invalid version");
72
+ }
73
+ return { major, minor, patch, stage };
74
+ };
75
+ const oldV = parseVersion(oldVersion);
76
+ const newV = parseVersion(newVersion);
77
+ if (oldV.major < newV.major || oldV.major === newV.major && oldV.minor < newV.minor || oldV.major === newV.major && oldV.minor === newV.minor && oldV.patch < newV.patch) {
78
+ return true;
79
+ }
80
+ if (oldV.stage < newV.stage || !newV.stage && oldV.stage) {
81
+ return true;
82
+ }
83
+ return false;
84
+ }
63
85
 
64
86
  // src/updater/utils.ts
65
87
  import { readFileSync } from "node:fs";
@@ -68,11 +90,11 @@ import { app } from "electron";
68
90
  function getAppAsarPath(name) {
69
91
  return app.isPackaged ? join(dirname(app.getAppPath()), `${name}.asar`) : "dev";
70
92
  }
71
- function getElectronVersion() {
93
+ function getEntryVersion() {
72
94
  return app.getVersion();
73
95
  }
74
96
  function getAppVersion(name) {
75
- return app.isPackaged ? readFileSync(join(getAppAsarPath(name), "version"), "utf-8").trim() : getElectronVersion();
97
+ return app.isPackaged ? readFileSync(join(getAppAsarPath(name), "version"), "utf-8").trim() : getEntryVersion();
76
98
  }
77
99
  function requireNative(packageName) {
78
100
  const path = app.isPackaged ? join(app.getAppPath(), "node_modules", packageName) : packageName;
@@ -113,10 +135,15 @@ function createUpdater({
113
135
  productName,
114
136
  releaseAsarURL: _release,
115
137
  updateJsonURL: _update,
116
- downloadConfig
138
+ debug = false,
139
+ downloadConfig,
140
+ compareVersion
117
141
  }) {
118
142
  const updater = new EventEmitter();
119
143
  const { downloadBuffer, downloadJSON, extraHeader, userAgent } = downloadConfig || {};
144
+ function log(...args) {
145
+ debug && console.log(...args);
146
+ }
120
147
  async function download(url, format) {
121
148
  const ua = userAgent || "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36";
122
149
  const headers = {
@@ -124,6 +151,7 @@ function createUpdater({
124
151
  UserAgent: ua,
125
152
  ...extraHeader
126
153
  };
154
+ log("[updater] headers", headers);
127
155
  const downloadFn = format === "json" ? downloadJSON ?? downloadJSONDefault : downloadBuffer ?? downloadBufferDefault;
128
156
  return await downloadFn(url, updater, headers);
129
157
  }
@@ -137,25 +165,32 @@ function createUpdater({
137
165
  const input = createReadStream(gzipFilePath);
138
166
  const outputFilePath = gzipFilePath.replace(".tmp.gz", ".asar");
139
167
  const output = createWriteStream(outputFilePath);
168
+ log("[updater] outputFilePath", outputFilePath);
140
169
  input.pipe(gunzip).pipe(output).on("finish", async () => {
141
170
  await rm(gzipFilePath);
171
+ log("[updater] finish");
142
172
  resolve2(outputFilePath);
143
173
  }).on("error", async (err) => {
144
174
  await rm(gzipFilePath);
175
+ log("[updater] error", err);
145
176
  output.destroy(err);
146
177
  reject(err);
147
178
  });
148
179
  });
149
180
  }
150
181
  function verify(buffer, signature) {
182
+ log("[updater] signature", signature);
151
183
  return createVerify("RSA-SHA256").update(buffer).verify(SIGNATURE_PUB, signature, "base64");
152
184
  }
153
185
  function needUpdate(version) {
154
- const parseVersion = (version2) => {
155
- const [major, minor, patch] = version2.split(".");
156
- return ~~major * 100 + ~~minor * 10 + ~~patch;
157
- };
158
- return app2.isPackaged && parseVersion(app2.getVersion()) < parseVersion(version);
186
+ if (!version || !app2.isPackaged) {
187
+ return false;
188
+ }
189
+ const currentVersion = getEntryVersion();
190
+ log("[updater] currentVersion", currentVersion);
191
+ log("[updater] newVersion", version);
192
+ const _compare = compareVersion ?? compareVersionDefault;
193
+ return _compare(currentVersion, version);
159
194
  }
160
195
  async function checkUpdate(option) {
161
196
  let {
@@ -163,15 +198,19 @@ function createUpdater({
163
198
  releaseAsarURL = _release
164
199
  } = option || {};
165
200
  if (!updateJsonURL || !releaseAsarURL) {
201
+ log("[updater] no updateJsonURL or releaseAsarURL, use repository");
166
202
  if (!repository) {
167
203
  throw new Error("updateJsonURL or releaseAsarURL are not set");
168
204
  }
169
205
  updateJsonURL = `${repository.replace("github.com", "raw.githubusercontent.com")}/version.json`;
170
206
  releaseAsarURL = `${repository}/releases/download/latest/${productName}.asar.gz`;
171
207
  }
208
+ log("[updater] updateJsonURL", updateJsonURL);
209
+ log("[updater] releaseAsarURL", releaseAsarURL);
172
210
  const gzipPath = `../${productName}.asar.gz`;
173
211
  const tmpFile = gzipPath.replace(".asar.gz", ".tmp.gz");
174
212
  if (existsSync(tmpFile)) {
213
+ log("[updater] remove tmp file", tmpFile);
175
214
  await rm(tmpFile);
176
215
  }
177
216
  const json = await download(updateJsonURL, "json");
@@ -183,15 +222,19 @@ function createUpdater({
183
222
  version,
184
223
  size
185
224
  } = json;
225
+ log("[updater] UpdateJSON", json);
186
226
  if (!needUpdate(version)) {
187
227
  return "unavailable";
188
228
  }
189
229
  updater.emit("downloadStart", size);
190
230
  const buffer = await download(releaseAsarURL, "buffer");
231
+ log("[updater] start verify");
191
232
  if (!verify(buffer, signature)) {
192
233
  throw new Error("file broken, invalid signature!");
193
234
  }
235
+ log("[updater] write file", gzipPath);
194
236
  await writeFile(gzipPath, buffer);
237
+ log("[updater] extract file", gzipPath);
195
238
  await extractFile(gzipPath);
196
239
  return "success";
197
240
  }
@@ -229,7 +272,7 @@ export {
229
272
  createUpdater,
230
273
  getAppAsarPath,
231
274
  getAppVersion,
232
- getElectronVersion,
275
+ getEntryVersion,
233
276
  getReleaseCdnLink,
234
277
  initApp,
235
278
  requireNative
package/package.json CHANGED
@@ -1,10 +1,15 @@
1
1
  {
2
2
  "name": "electron-incremental-update",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "electron incremental update tools, powered by vite",
5
5
  "scripts": {
6
6
  "build": "tsup",
7
- "release": "tsup && bumpp"
7
+ "release": "pnpm test && pnpm run build && bumpp && npm publish",
8
+ "test": "vitest --run"
9
+ },
10
+ "publishConfig": {
11
+ "access": "public",
12
+ "registry": "https://registry.npmjs.org/"
8
13
  },
9
14
  "repository": "https://github.com/subframe7536/electron-incremental-update",
10
15
  "type": "module",
@@ -52,13 +57,10 @@
52
57
  "fs-jetpack": "^5.1.0",
53
58
  "tsup": "^6.7.0",
54
59
  "typescript": "^5.1.3",
55
- "vite": "^4.3.9"
60
+ "vite": "^4.3.9",
61
+ "vitest": "^0.32.2"
56
62
  },
57
63
  "dependencies": {
58
64
  "ci-info": "^3.8.0"
59
- },
60
- "peerDependencies": {
61
- "@electron/asar": "*",
62
- "asar": "*"
63
65
  }
64
66
  }