electron-incremental-update 0.1.2 → 0.1.4

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.md CHANGED
@@ -1,25 +1,37 @@
1
1
  ## electron incremental updater
2
2
 
3
- inspired by Obsidian's update strategy, using RSA + Signature to sign the update asar and replace the old one when verified
3
+ provider a vite plugin and useful functions to generate updater and split entry file and real app
4
+
5
+ ### principle
6
+
7
+ using two asar, `app.asar` and `main.asar` (if "main" is your app's name)
8
+
9
+ the `app.asar` is used to load `main.asar` and initialize the updater
10
+
11
+ using RSA + Signature to sign the new `main.asar` downloaded from remote and replace the old one when verified
12
+
13
+ - inspired by Obsidian's update strategy
14
+
15
+ ### notice
4
16
 
5
17
  develop with [vite-plugin-electron](https://github.com/electron-vite/vite-plugin-electron), and may be effect in other electron vite frameworks
6
18
 
7
- ### install
19
+ ## install
8
20
 
9
- #### npm
21
+ ### npm
10
22
  ```bash
11
23
  npm install electron-incremental-update
12
24
  ```
13
- #### yarn
25
+ ### yarn
14
26
  ```bash
15
27
  yarn add electron-incremental-update
16
28
  ```
17
- #### pnpm
29
+ ### pnpm
18
30
  ```bash
19
31
  pnpm add electron-incremental-update
20
32
  ```
21
33
 
22
- ### usage
34
+ ## usage
23
35
 
24
36
  base on [electron-vite-vue](https://github.com/electron-vite/electron-vite-vue)
25
37
 
@@ -36,7 +48,7 @@ src
36
48
  └── ...
37
49
  ```
38
50
 
39
- #### setup app
51
+ ### setup app
40
52
 
41
53
  ```ts
42
54
  // electron/app.ts
@@ -53,7 +65,7 @@ const updater = createUpdater({
53
65
  initApp(name, updater)
54
66
  ```
55
67
 
56
- #### setup main
68
+ ### setup main
57
69
 
58
70
  ```ts
59
71
  // electron/main/index.ts
@@ -103,7 +115,7 @@ export default function (updater: Updater) {
103
115
  }
104
116
  ```
105
117
 
106
- #### use native modules
118
+ ### use native modules
107
119
 
108
120
  ```ts
109
121
  // db.ts
@@ -128,7 +140,7 @@ console.log(r)
128
140
  db.close()
129
141
  ```
130
142
 
131
- #### setup vite.config.ts
143
+ ### setup vite.config.ts
132
144
 
133
145
  ```ts
134
146
  import { rmSync } from 'node:fs'
@@ -189,7 +201,7 @@ export default defineConfig(({ command }) => {
189
201
  })
190
202
  ```
191
203
 
192
- ##### option
204
+ #### option
193
205
 
194
206
  ```ts
195
207
  type Options = {
@@ -213,6 +225,9 @@ type Options = {
213
225
  * Whether to minify
214
226
  */
215
227
  minify?: boolean
228
+ /**
229
+ * path config
230
+ */
216
231
  paths?: {
217
232
  /**
218
233
  * Path to app entry file
@@ -225,7 +240,7 @@ type Options = {
225
240
  */
226
241
  entryOutputPath?: string
227
242
  /**
228
- * Path to app entry file
243
+ * Path to asar file
229
244
  * @default `release/${ProductName}.asar`
230
245
  */
231
246
  asarOutputPath?: string
@@ -239,7 +254,15 @@ type Options = {
239
254
  * @default `dist`
240
255
  */
241
256
  rendererDistPath?: string
257
+ /**
258
+ * Path to version info output
259
+ * @default `version.json`
260
+ */
261
+ versionPath?: string
242
262
  }
263
+ /**
264
+ * signature config
265
+ */
243
266
  keys?: {
244
267
  /**
245
268
  * Path to the pem file that contains private key
@@ -262,7 +285,7 @@ type Options = {
262
285
  }
263
286
  ```
264
287
 
265
- #### electron-builder config
288
+ ### electron-builder config
266
289
 
267
290
  ```js
268
291
  const { name } = require('./package.json')
package/dist/index.cjs CHANGED
@@ -42,7 +42,62 @@ module.exports = __toCommonJS(src_exports);
42
42
  var import_node_path2 = require("path");
43
43
  var import_electron3 = require("electron");
44
44
 
45
- // src/utils.ts
45
+ // src/updater/index.ts
46
+ var import_node_events = require("events");
47
+ var import_node_crypto = require("crypto");
48
+ var import_node_zlib = require("zlib");
49
+ var import_node_fs2 = require("fs");
50
+ var import_promises = require("fs/promises");
51
+ var import_electron2 = require("electron");
52
+
53
+ // src/updater/download.ts
54
+ var import_node_buffer = require("buffer");
55
+ var import_node_https = __toESM(require("https"), 1);
56
+ function downloadJSONDefault(url, updater, headers) {
57
+ return new Promise((resolve2, reject) => {
58
+ import_node_https.default.get(url, (res) => {
59
+ let data = "";
60
+ res.setEncoding("utf8");
61
+ res.headers = headers;
62
+ res.on("data", (chunk) => data += chunk);
63
+ res.on("end", () => {
64
+ updater.emit("downloadEnd", true);
65
+ const json = JSON.parse(data);
66
+ if ("signature" in json && "version" in json && "size" in json) {
67
+ resolve2(json);
68
+ } else {
69
+ throw new Error("invalid update json");
70
+ }
71
+ });
72
+ }).on("error", (e) => {
73
+ e && updater.emit("donwnloadError", e);
74
+ updater.emit("downloadEnd", false);
75
+ reject(e);
76
+ });
77
+ });
78
+ }
79
+ function downloadBufferDefault(url, updater, headers) {
80
+ return new Promise((resolve2, reject) => {
81
+ import_node_https.default.get(url, (res) => {
82
+ let data = [];
83
+ res.headers = headers;
84
+ res.on("data", (chunk) => {
85
+ updater.emit("downloading", chunk.length);
86
+ data.push(chunk);
87
+ });
88
+ res.on("end", () => {
89
+ updater.emit("downloadEnd", true);
90
+ resolve2(import_node_buffer.Buffer.concat(data));
91
+ });
92
+ }).on("error", (e) => {
93
+ e && updater.emit("donwnloadError", e);
94
+ updater.emit("downloadEnd", false);
95
+ reject(e);
96
+ });
97
+ });
98
+ }
99
+
100
+ // src/updater/utils.ts
46
101
  var import_node_fs = require("fs");
47
102
  var import_node_path = require("path");
48
103
  var import_electron = require("electron");
@@ -80,64 +135,26 @@ function getReleaseDnsPrefix() {
80
135
  ];
81
136
  }
82
137
 
83
- // src/updater.ts
84
- var import_node_events = require("events");
85
- var import_node_buffer = require("buffer");
86
- var import_node_crypto = require("crypto");
87
- var import_node_zlib = require("zlib");
88
- var import_node_fs2 = require("fs");
89
- var import_promises = require("fs/promises");
90
- var import_node_https = __toESM(require("https"), 1);
91
- var import_electron2 = require("electron");
138
+ // src/updater/index.ts
92
139
  function createUpdater({
93
140
  SIGNATURE_PUB,
94
141
  repository,
95
142
  productName,
96
143
  releaseAsarURL: _release,
97
144
  updateJsonURL: _update,
98
- userAgent,
99
- extraHeader
145
+ downloadConfig
100
146
  }) {
101
147
  const updater = new import_node_events.EventEmitter();
148
+ const { downloadBuffer, downloadJSON, extraHeader, userAgent } = downloadConfig || {};
102
149
  async function download(url, format) {
103
150
  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";
104
- const commonHeader = {
151
+ const headers = {
152
+ Accept: `application/${format === "json" ? "json" : "octet-stream"}`,
105
153
  UserAgent: ua,
106
154
  ...extraHeader
107
155
  };
108
- return await new Promise((resolve2, reject) => {
109
- import_node_https.default.get(url, (res) => {
110
- if (format === "json") {
111
- let data = "";
112
- res.setEncoding("utf8");
113
- res.headers = {
114
- Accept: "application/json",
115
- ...commonHeader
116
- };
117
- res.on("data", (chunk) => data += chunk);
118
- res.on("end", () => {
119
- resolve2(JSON.parse(data));
120
- });
121
- } else if (format === "buffer") {
122
- let data = [];
123
- res.headers = {
124
- Accept: "application/octet-stream",
125
- ...commonHeader
126
- };
127
- res.on("data", (chunk) => {
128
- updater.emit("downloading", chunk.length);
129
- data.push(chunk);
130
- });
131
- res.on("end", () => {
132
- updater.emit("downloadEnd", true);
133
- resolve2(import_node_buffer.Buffer.concat(data));
134
- });
135
- }
136
- }).on("error", (e) => {
137
- e && updater.emit("donwnloadError", e);
138
- reject(e);
139
- });
140
- });
156
+ const downloadFn = format === "json" ? downloadJSON ?? downloadJSONDefault : downloadBuffer ?? downloadBufferDefault;
157
+ return await downloadFn(url, updater, headers);
141
158
  }
142
159
  async function extractFile(gzipFilePath) {
143
160
  if (!gzipFilePath.endsWith(".asar.gz") || !(0, import_node_fs2.existsSync)(gzipFilePath)) {
@@ -174,11 +191,13 @@ function createUpdater({
174
191
  updateJsonURL = _update,
175
192
  releaseAsarURL = _release
176
193
  } = option || {};
177
- if ((!updateJsonURL || !releaseAsarURL) && !repository) {
178
- throw new Error("updateJsonURL or releaseAsarURL are not set");
194
+ if (!updateJsonURL || !releaseAsarURL) {
195
+ if (!repository) {
196
+ throw new Error("updateJsonURL or releaseAsarURL are not set");
197
+ }
198
+ updateJsonURL = `${repository.replace("github.com", "raw.githubusercontent.com")}/version.json`;
199
+ releaseAsarURL = `${repository}/releases/download/latest/${productName}.asar.gz`;
179
200
  }
180
- updateJsonURL ??= `${repository}/version.json`;
181
- releaseAsarURL ??= `${repository}/releases/download/latest/${productName}.asar.gz`;
182
201
  const gzipPath = `../${productName}.asar.gz`;
183
202
  const tmpFile = gzipPath.replace(".asar.gz", ".tmp.gz");
184
203
  if ((0, import_node_fs2.existsSync)(tmpFile)) {
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { Buffer } from 'node:buffer';
2
+
1
3
  type CheckResultType = 'success' | 'fail' | 'unavailable';
2
4
  type UpdateEvents = {
3
5
  check: null;
@@ -7,11 +9,16 @@ type UpdateEvents = {
7
9
  downloadEnd: [success: boolean];
8
10
  donwnloadError: [error: unknown];
9
11
  };
12
+ type UpdateJSON = {
13
+ signature: string;
14
+ version: string;
15
+ size: number;
16
+ };
10
17
  type MaybeArray<T> = T extends undefined | null | never ? [] : T extends any[] ? T['length'] extends 1 ? [data: T[0]] : T : [data: T];
11
18
  interface UpdateOption {
12
19
  /**
13
20
  * URL of version info json
14
- * @default `${repository}/version.json`
21
+ * @default `${repository.replace('github.com', 'raw.githubusercontent.com')}/version.json`
15
22
  * @throws if `updateJsonURL` and `repository` are all not set
16
23
  */
17
24
  updateJsonURL?: string;
@@ -53,29 +60,46 @@ interface Options extends UpdateOption {
53
60
  /**
54
61
  * product name
55
62
  *
56
- * you can use the `name` in package.json
63
+ * you can use the `name` in `package.json`
57
64
  */
58
65
  productName: string;
59
66
  /**
60
67
  * repository url, e.g. `https://github.com/electron/electron`
61
68
  *
62
- * you can use the `repository` in package.json
69
+ * you can use the `repository` in `package.json`
63
70
  *
64
71
  * if `updateJsonURL` or `releaseAsarURL` are absent,
65
72
  * `repository` will be used to determine the url
66
73
  */
67
74
  repository?: string;
68
- /**
69
- * download user agent
70
- * @default 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36'
71
- */
72
- userAgent?: string;
73
- /**
74
- * extra download header, `accept` and `user-agent` is set by default
75
- */
76
- extraHeader?: Record<string, string>;
75
+ downloadConfig?: {
76
+ /**
77
+ * download user agent
78
+ * @default 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36'
79
+ */
80
+ userAgent?: string;
81
+ /**
82
+ * extra download header, `accept` and `user-agent` is set by default
83
+ */
84
+ extraHeader?: Record<string, string>;
85
+ /**
86
+ * download JSON function
87
+ * @param url download url
88
+ * @param updater updater, emit events
89
+ * @param header download header
90
+ * @returns `UpdateJSON`
91
+ */
92
+ downloadJSON?: (url: string, updater: Updater, headers: Record<string, any>) => Promise<UpdateJSON>;
93
+ /**
94
+ * download buffer function
95
+ * @param url download url
96
+ * @param updater updater, emit events
97
+ * @param header download header
98
+ * @returns `Buffer`
99
+ */
100
+ downloadBuffer?: (url: string, updater: Updater, headers: Record<string, any>) => Promise<Buffer>;
101
+ };
77
102
  }
78
- declare function createUpdater({ SIGNATURE_PUB, repository, productName, releaseAsarURL: _release, updateJsonURL: _update, userAgent, extraHeader, }: Options): Updater;
79
103
 
80
104
  declare function getAppAsarPath(name: string): string;
81
105
  declare function getElectronVersion(): string;
@@ -86,6 +110,8 @@ declare function getReleaseDnsPrefix(): {
86
110
  maintainer: string;
87
111
  }[];
88
112
 
113
+ declare function createUpdater({ SIGNATURE_PUB, repository, productName, releaseAsarURL: _release, updateJsonURL: _update, downloadConfig, }: Options): Updater;
114
+
89
115
  interface PathConfig {
90
116
  /**
91
117
  * path of electron output dist
@@ -107,4 +133,4 @@ interface PathConfig {
107
133
  */
108
134
  declare function initApp(productName: string, updater: Updater, option?: PathConfig): any;
109
135
 
110
- export { Options, Updater, createUpdater, getAppAsarPath, getAppVersion, getElectronVersion, getReleaseDnsPrefix, initApp, requireNative };
136
+ export { CheckResultType, Options, UpdateJSON, UpdateOption, Updater, createUpdater, getAppAsarPath, getAppVersion, getElectronVersion, getReleaseDnsPrefix, initApp, requireNative };
package/dist/index.mjs CHANGED
@@ -6,7 +6,62 @@ import {
6
6
  import { resolve } from "node:path";
7
7
  import { app as app3 } from "electron";
8
8
 
9
- // src/utils.ts
9
+ // src/updater/index.ts
10
+ import { EventEmitter } from "node:events";
11
+ import { createVerify } from "node:crypto";
12
+ import { createGunzip } from "node:zlib";
13
+ import { createReadStream, createWriteStream, existsSync } from "node:fs";
14
+ import { rm, writeFile } from "node:fs/promises";
15
+ import { app as app2 } from "electron";
16
+
17
+ // src/updater/download.ts
18
+ import { Buffer } from "node:buffer";
19
+ import https from "node:https";
20
+ function downloadJSONDefault(url, updater, headers) {
21
+ return new Promise((resolve2, reject) => {
22
+ https.get(url, (res) => {
23
+ let data = "";
24
+ res.setEncoding("utf8");
25
+ res.headers = headers;
26
+ res.on("data", (chunk) => data += chunk);
27
+ res.on("end", () => {
28
+ updater.emit("downloadEnd", true);
29
+ const json = JSON.parse(data);
30
+ if ("signature" in json && "version" in json && "size" in json) {
31
+ resolve2(json);
32
+ } else {
33
+ throw new Error("invalid update json");
34
+ }
35
+ });
36
+ }).on("error", (e) => {
37
+ e && updater.emit("donwnloadError", e);
38
+ updater.emit("downloadEnd", false);
39
+ reject(e);
40
+ });
41
+ });
42
+ }
43
+ function downloadBufferDefault(url, updater, headers) {
44
+ return new Promise((resolve2, reject) => {
45
+ https.get(url, (res) => {
46
+ let data = [];
47
+ res.headers = headers;
48
+ res.on("data", (chunk) => {
49
+ updater.emit("downloading", chunk.length);
50
+ data.push(chunk);
51
+ });
52
+ res.on("end", () => {
53
+ updater.emit("downloadEnd", true);
54
+ resolve2(Buffer.concat(data));
55
+ });
56
+ }).on("error", (e) => {
57
+ e && updater.emit("donwnloadError", e);
58
+ updater.emit("downloadEnd", false);
59
+ reject(e);
60
+ });
61
+ });
62
+ }
63
+
64
+ // src/updater/utils.ts
10
65
  import { readFileSync } from "node:fs";
11
66
  import { dirname, join } from "node:path";
12
67
  import { app } from "electron";
@@ -44,64 +99,26 @@ function getReleaseDnsPrefix() {
44
99
  ];
45
100
  }
46
101
 
47
- // src/updater.ts
48
- import { EventEmitter } from "node:events";
49
- import { Buffer } from "node:buffer";
50
- import { createVerify } from "node:crypto";
51
- import { createGunzip } from "node:zlib";
52
- import { createReadStream, createWriteStream, existsSync } from "node:fs";
53
- import { rm, writeFile } from "node:fs/promises";
54
- import https from "node:https";
55
- import { app as app2 } from "electron";
102
+ // src/updater/index.ts
56
103
  function createUpdater({
57
104
  SIGNATURE_PUB,
58
105
  repository,
59
106
  productName,
60
107
  releaseAsarURL: _release,
61
108
  updateJsonURL: _update,
62
- userAgent,
63
- extraHeader
109
+ downloadConfig
64
110
  }) {
65
111
  const updater = new EventEmitter();
112
+ const { downloadBuffer, downloadJSON, extraHeader, userAgent } = downloadConfig || {};
66
113
  async function download(url, format) {
67
114
  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";
68
- const commonHeader = {
115
+ const headers = {
116
+ Accept: `application/${format === "json" ? "json" : "octet-stream"}`,
69
117
  UserAgent: ua,
70
118
  ...extraHeader
71
119
  };
72
- return await new Promise((resolve2, reject) => {
73
- https.get(url, (res) => {
74
- if (format === "json") {
75
- let data = "";
76
- res.setEncoding("utf8");
77
- res.headers = {
78
- Accept: "application/json",
79
- ...commonHeader
80
- };
81
- res.on("data", (chunk) => data += chunk);
82
- res.on("end", () => {
83
- resolve2(JSON.parse(data));
84
- });
85
- } else if (format === "buffer") {
86
- let data = [];
87
- res.headers = {
88
- Accept: "application/octet-stream",
89
- ...commonHeader
90
- };
91
- res.on("data", (chunk) => {
92
- updater.emit("downloading", chunk.length);
93
- data.push(chunk);
94
- });
95
- res.on("end", () => {
96
- updater.emit("downloadEnd", true);
97
- resolve2(Buffer.concat(data));
98
- });
99
- }
100
- }).on("error", (e) => {
101
- e && updater.emit("donwnloadError", e);
102
- reject(e);
103
- });
104
- });
120
+ const downloadFn = format === "json" ? downloadJSON ?? downloadJSONDefault : downloadBuffer ?? downloadBufferDefault;
121
+ return await downloadFn(url, updater, headers);
105
122
  }
106
123
  async function extractFile(gzipFilePath) {
107
124
  if (!gzipFilePath.endsWith(".asar.gz") || !existsSync(gzipFilePath)) {
@@ -138,11 +155,13 @@ function createUpdater({
138
155
  updateJsonURL = _update,
139
156
  releaseAsarURL = _release
140
157
  } = option || {};
141
- if ((!updateJsonURL || !releaseAsarURL) && !repository) {
142
- throw new Error("updateJsonURL or releaseAsarURL are not set");
158
+ if (!updateJsonURL || !releaseAsarURL) {
159
+ if (!repository) {
160
+ throw new Error("updateJsonURL or releaseAsarURL are not set");
161
+ }
162
+ updateJsonURL = `${repository.replace("github.com", "raw.githubusercontent.com")}/version.json`;
163
+ releaseAsarURL = `${repository}/releases/download/latest/${productName}.asar.gz`;
143
164
  }
144
- updateJsonURL ??= `${repository}/version.json`;
145
- releaseAsarURL ??= `${repository}/releases/download/latest/${productName}.asar.gz`;
146
165
  const gzipPath = `../${productName}.asar.gz`;
147
166
  const tmpFile = gzipPath.replace(".asar.gz", ".tmp.gz");
148
167
  if (existsSync(tmpFile)) {
package/dist/vite.cjs CHANGED
@@ -35,23 +35,85 @@ __export(vite_exports, {
35
35
  module.exports = __toCommonJS(vite_exports);
36
36
  var import_vite = require("vite");
37
37
 
38
- // src/build-entry.ts
38
+ // src/build-plugins/asar.ts
39
+ var import_node_crypto = require("crypto");
39
40
  var import_node_fs = require("fs");
40
41
  var import_promises = require("fs/promises");
41
- var import_node_crypto = require("crypto");
42
- var import_node_os = require("os");
42
+ var import_node_zlib = __toESM(require("zlib"), 1);
43
43
  var import_ci_info = require("ci-info");
44
+ function gzipFile(filePath) {
45
+ return new Promise((resolve, reject) => {
46
+ const gzip = import_node_zlib.default.createGzip();
47
+ const input = (0, import_node_fs.createReadStream)(filePath);
48
+ const output = (0, import_node_fs.createWriteStream)(`${filePath}.gz`);
49
+ input.pipe(gzip).pipe(output).on("finish", () => resolve(null)).on("error", (err) => reject(err));
50
+ });
51
+ }
52
+ function generateSignature(buffer, privateKey) {
53
+ return (0, import_node_crypto.createSign)("RSA-SHA256").update(buffer).sign({
54
+ key: privateKey,
55
+ padding: import_node_crypto.constants.RSA_PKCS1_PADDING,
56
+ saltLength: import_node_crypto.constants.RSA_PSS_SALTLEN_DIGEST
57
+ }, "base64");
58
+ }
59
+ async function pack(dir, target) {
60
+ let asar = null;
61
+ try {
62
+ asar = await import("asar");
63
+ } catch (ignore) {
64
+ }
65
+ if (!asar) {
66
+ try {
67
+ asar = await import("@electron/asar");
68
+ } catch (ignore) {
69
+ }
70
+ }
71
+ if (!asar) {
72
+ throw new Error("no asar, please install @electron/asar");
73
+ }
74
+ await asar.createPackage(dir, target);
75
+ }
76
+ async function buildAsar({
77
+ version,
78
+ asarOutputPath,
79
+ privateKeyPath,
80
+ electronDistPath,
81
+ rendererDistPath,
82
+ versionPath
83
+ }) {
84
+ await (0, import_promises.rename)(rendererDistPath, `${electronDistPath}/renderer`);
85
+ await (0, import_promises.writeFile)(`${electronDistPath}/version`, version);
86
+ await pack(electronDistPath, asarOutputPath);
87
+ if (import_ci_info.isCI) {
88
+ return;
89
+ }
90
+ await gzipFile(asarOutputPath);
91
+ const buffer = await (0, import_promises.readFile)(`${asarOutputPath}.gz`);
92
+ const signature = generateSignature(buffer, await (0, import_promises.readFile)(privateKeyPath, "utf-8"));
93
+ await (0, import_promises.writeFile)(versionPath, JSON.stringify({
94
+ signature,
95
+ version,
96
+ size: buffer.length
97
+ }, null, 2));
98
+ }
99
+
100
+ // src/build-plugins/entry.ts
101
+ var import_node_fs2 = require("fs");
102
+ var import_promises2 = require("fs/promises");
103
+ var import_node_crypto2 = require("crypto");
104
+ var import_node_os = require("os");
105
+ var import_ci_info2 = require("ci-info");
44
106
  var import_esbuild = require("esbuild");
45
107
  async function generateKey(privateKeyPath, publicKeyPath, length) {
46
- const pair = (0, import_node_crypto.generateKeyPairSync)("rsa", { modulusLength: length });
108
+ const pair = (0, import_node_crypto2.generateKeyPairSync)("rsa", { modulusLength: length });
47
109
  const privateKey = pair.privateKey.export({ type: "pkcs1", format: "pem" });
48
110
  const publicKey = pair.publicKey.export({ type: "pkcs1", format: "pem" });
49
- await (0, import_promises.writeFile)(privateKeyPath, privateKey);
50
- await (0, import_promises.writeFile)(publicKeyPath, publicKey);
111
+ await (0, import_promises2.writeFile)(privateKeyPath, privateKey);
112
+ await (0, import_promises2.writeFile)(publicKeyPath, publicKey);
51
113
  }
52
114
  async function writePublicKeyToMain(updatePath, publicKeyPath) {
53
- const file = await (0, import_promises.readFile)(updatePath, "utf-8");
54
- const key = await (0, import_promises.readFile)(publicKeyPath, "utf-8");
115
+ const file = await (0, import_promises2.readFile)(updatePath, "utf-8");
116
+ const key = await (0, import_promises2.readFile)(publicKeyPath, "utf-8");
55
117
  const regex = /const SIGNATURE_PUB = ['`][\s\S]*?['`]/;
56
118
  const replacement = `const SIGNATURE_PUB = \`${key}\``;
57
119
  let replaced = file;
@@ -73,7 +135,7 @@ async function writePublicKeyToMain(updatePath, publicKeyPath) {
73
135
  !isMatched && lines.push(r);
74
136
  replaced = lines.join(import_node_os.EOL);
75
137
  }
76
- await (0, import_promises.writeFile)(updatePath, replaced);
138
+ await (0, import_promises2.writeFile)(updatePath, replaced);
77
139
  }
78
140
  async function buildEntry({
79
141
  privateKeyPath,
@@ -83,8 +145,8 @@ async function buildEntry({
83
145
  minify,
84
146
  keyLength
85
147
  }) {
86
- if (!import_ci_info.isCI) {
87
- !(0, import_node_fs.existsSync)(privateKeyPath) && await generateKey(privateKeyPath, publicKeyPath, keyLength);
148
+ if (!import_ci_info2.isCI) {
149
+ !(0, import_node_fs2.existsSync)(privateKeyPath) && await generateKey(privateKeyPath, publicKeyPath, keyLength);
88
150
  await writePublicKeyToMain(entryPath, publicKeyPath);
89
151
  }
90
152
  await (0, import_esbuild.build)({
@@ -97,69 +159,7 @@ async function buildEntry({
97
159
  });
98
160
  }
99
161
 
100
- // src/build-asar.ts
101
- var import_node_crypto2 = require("crypto");
102
- var import_node_fs2 = require("fs");
103
- var import_promises2 = require("fs/promises");
104
- var import_node_zlib = __toESM(require("zlib"), 1);
105
- var import_ci_info2 = require("ci-info");
106
- function gzipFile(filePath) {
107
- return new Promise((resolve, reject) => {
108
- const gzip = import_node_zlib.default.createGzip();
109
- const input = (0, import_node_fs2.createReadStream)(filePath);
110
- const output = (0, import_node_fs2.createWriteStream)(`${filePath}.gz`);
111
- input.pipe(gzip).pipe(output).on("finish", () => resolve(null)).on("error", (err) => reject(err));
112
- });
113
- }
114
- function generateSignature(buffer, privateKey) {
115
- return (0, import_node_crypto2.createSign)("RSA-SHA256").update(buffer).sign({
116
- key: privateKey,
117
- padding: import_node_crypto2.constants.RSA_PKCS1_PADDING,
118
- saltLength: import_node_crypto2.constants.RSA_PSS_SALTLEN_DIGEST
119
- }, "base64");
120
- }
121
- async function pack(dir, target) {
122
- let asar = null;
123
- try {
124
- asar = await import("asar");
125
- } catch (ignore) {
126
- }
127
- if (!asar) {
128
- try {
129
- asar = await import("@electron/asar");
130
- } catch (ignore) {
131
- }
132
- }
133
- if (!asar) {
134
- throw new Error("no asar, please install @electron/asar");
135
- }
136
- await asar.createPackage(dir, target);
137
- }
138
- async function buildAsar({
139
- version,
140
- asarOutputPath,
141
- privateKeyPath,
142
- electronDistPath,
143
- rendererDistPath,
144
- versionPath
145
- }) {
146
- await (0, import_promises2.rename)(rendererDistPath, `${electronDistPath}/renderer`);
147
- await (0, import_promises2.writeFile)(`${electronDistPath}/version`, version);
148
- await pack(electronDistPath, asarOutputPath);
149
- if (import_ci_info2.isCI) {
150
- return;
151
- }
152
- await gzipFile(asarOutputPath);
153
- const buffer = await (0, import_promises2.readFile)(`${asarOutputPath}.gz`);
154
- const signature = generateSignature(buffer, await (0, import_promises2.readFile)(privateKeyPath, "utf-8"));
155
- await (0, import_promises2.writeFile)(versionPath, JSON.stringify({
156
- signature,
157
- version,
158
- size: buffer.length
159
- }, null, 2));
160
- }
161
-
162
- // src/option.ts
162
+ // src/build-plugins/option.ts
163
163
  function parseOptions(options) {
164
164
  const { isBuild, productName, version, minify = false, paths = {}, keys = {} } = options;
165
165
  const {
package/dist/vite.d.ts CHANGED
@@ -8,19 +8,22 @@ type Options = {
8
8
  /**
9
9
  * the name of you application
10
10
  *
11
- * you can set as 'name' in package.json
11
+ * you can set as 'name' in `package.json`
12
12
  */
13
13
  productName: string;
14
14
  /**
15
15
  * the version of you application
16
16
  *
17
- * you can set as 'version' in package.json
17
+ * you can set as 'version' in `package.json`
18
18
  */
19
19
  version: string;
20
20
  /**
21
- * Whether to minify
21
+ * Whether to minify entry file
22
22
  */
23
23
  minify?: boolean;
24
+ /**
25
+ * paths config
26
+ */
24
27
  paths?: {
25
28
  /**
26
29
  * Path to app entry file
@@ -33,7 +36,7 @@ type Options = {
33
36
  */
34
37
  entryOutputPath?: string;
35
38
  /**
36
- * Path to app entry file
39
+ * Path to asar file
37
40
  * @default `release/${ProductName}.asar`
38
41
  */
39
42
  asarOutputPath?: string;
@@ -53,6 +56,9 @@ type Options = {
53
56
  */
54
57
  versionPath?: string;
55
58
  };
59
+ /**
60
+ * signature config
61
+ */
56
62
  keys?: {
57
63
  /**
58
64
  * Path to the pem file that contains private key
package/dist/vite.mjs CHANGED
@@ -3,74 +3,12 @@ import "./chunk-AKU6F3WT.mjs";
3
3
  // src/vite.ts
4
4
  import { createLogger } from "vite";
5
5
 
6
- // src/build-entry.ts
7
- import { existsSync } from "node:fs";
8
- import { readFile, writeFile } from "node:fs/promises";
9
- import { generateKeyPairSync } from "node:crypto";
10
- import { EOL } from "node:os";
11
- import { isCI } from "ci-info";
12
- import { build } from "esbuild";
13
- async function generateKey(privateKeyPath, publicKeyPath, length) {
14
- const pair = generateKeyPairSync("rsa", { modulusLength: length });
15
- const privateKey = pair.privateKey.export({ type: "pkcs1", format: "pem" });
16
- const publicKey = pair.publicKey.export({ type: "pkcs1", format: "pem" });
17
- await writeFile(privateKeyPath, privateKey);
18
- await writeFile(publicKeyPath, publicKey);
19
- }
20
- async function writePublicKeyToMain(updatePath, publicKeyPath) {
21
- const file = await readFile(updatePath, "utf-8");
22
- const key = await readFile(publicKeyPath, "utf-8");
23
- const regex = /const SIGNATURE_PUB = ['`][\s\S]*?['`]/;
24
- const replacement = `const SIGNATURE_PUB = \`${key}\``;
25
- let replaced = file;
26
- const signaturePubExists = regex.test(file);
27
- if (signaturePubExists) {
28
- replaced = file.replace(regex, replacement);
29
- } else {
30
- const lines = file.split(EOL);
31
- const r = `${EOL}${replacement}${EOL}`;
32
- let isMatched = false;
33
- for (let i = 0; i < lines.length; i++) {
34
- const line = lines[i];
35
- if (!line.startsWith("import") && !line.startsWith("/")) {
36
- lines.splice(i, 0, r);
37
- isMatched = true;
38
- break;
39
- }
40
- }
41
- !isMatched && lines.push(r);
42
- replaced = lines.join(EOL);
43
- }
44
- await writeFile(updatePath, replaced);
45
- }
46
- async function buildEntry({
47
- privateKeyPath,
48
- publicKeyPath,
49
- entryPath,
50
- entryOutputPath: outfile,
51
- minify,
52
- keyLength
53
- }) {
54
- if (!isCI) {
55
- !existsSync(privateKeyPath) && await generateKey(privateKeyPath, publicKeyPath, keyLength);
56
- await writePublicKeyToMain(entryPath, publicKeyPath);
57
- }
58
- await build({
59
- entryPoints: [entryPath],
60
- bundle: true,
61
- platform: "node",
62
- outfile,
63
- minify,
64
- external: ["electron"]
65
- });
66
- }
67
-
68
- // src/build-asar.ts
6
+ // src/build-plugins/asar.ts
69
7
  import { constants, createSign } from "node:crypto";
70
8
  import { createReadStream, createWriteStream } from "node:fs";
71
- import { readFile as readFile2, rename, writeFile as writeFile2 } from "node:fs/promises";
9
+ import { readFile, rename, writeFile } from "node:fs/promises";
72
10
  import zlib from "node:zlib";
73
- import { isCI as isCI2 } from "ci-info";
11
+ import { isCI } from "ci-info";
74
12
  function gzipFile(filePath) {
75
13
  return new Promise((resolve, reject) => {
76
14
  const gzip = zlib.createGzip();
@@ -112,22 +50,84 @@ async function buildAsar({
112
50
  versionPath
113
51
  }) {
114
52
  await rename(rendererDistPath, `${electronDistPath}/renderer`);
115
- await writeFile2(`${electronDistPath}/version`, version);
53
+ await writeFile(`${electronDistPath}/version`, version);
116
54
  await pack(electronDistPath, asarOutputPath);
117
- if (isCI2) {
55
+ if (isCI) {
118
56
  return;
119
57
  }
120
58
  await gzipFile(asarOutputPath);
121
- const buffer = await readFile2(`${asarOutputPath}.gz`);
122
- const signature = generateSignature(buffer, await readFile2(privateKeyPath, "utf-8"));
123
- await writeFile2(versionPath, JSON.stringify({
59
+ const buffer = await readFile(`${asarOutputPath}.gz`);
60
+ const signature = generateSignature(buffer, await readFile(privateKeyPath, "utf-8"));
61
+ await writeFile(versionPath, JSON.stringify({
124
62
  signature,
125
63
  version,
126
64
  size: buffer.length
127
65
  }, null, 2));
128
66
  }
129
67
 
130
- // src/option.ts
68
+ // src/build-plugins/entry.ts
69
+ import { existsSync } from "node:fs";
70
+ import { readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
71
+ import { generateKeyPairSync } from "node:crypto";
72
+ import { EOL } from "node:os";
73
+ import { isCI as isCI2 } from "ci-info";
74
+ import { build } from "esbuild";
75
+ async function generateKey(privateKeyPath, publicKeyPath, length) {
76
+ const pair = generateKeyPairSync("rsa", { modulusLength: length });
77
+ const privateKey = pair.privateKey.export({ type: "pkcs1", format: "pem" });
78
+ const publicKey = pair.publicKey.export({ type: "pkcs1", format: "pem" });
79
+ await writeFile2(privateKeyPath, privateKey);
80
+ await writeFile2(publicKeyPath, publicKey);
81
+ }
82
+ async function writePublicKeyToMain(updatePath, publicKeyPath) {
83
+ const file = await readFile2(updatePath, "utf-8");
84
+ const key = await readFile2(publicKeyPath, "utf-8");
85
+ const regex = /const SIGNATURE_PUB = ['`][\s\S]*?['`]/;
86
+ const replacement = `const SIGNATURE_PUB = \`${key}\``;
87
+ let replaced = file;
88
+ const signaturePubExists = regex.test(file);
89
+ if (signaturePubExists) {
90
+ replaced = file.replace(regex, replacement);
91
+ } else {
92
+ const lines = file.split(EOL);
93
+ const r = `${EOL}${replacement}${EOL}`;
94
+ let isMatched = false;
95
+ for (let i = 0; i < lines.length; i++) {
96
+ const line = lines[i];
97
+ if (!line.startsWith("import") && !line.startsWith("/")) {
98
+ lines.splice(i, 0, r);
99
+ isMatched = true;
100
+ break;
101
+ }
102
+ }
103
+ !isMatched && lines.push(r);
104
+ replaced = lines.join(EOL);
105
+ }
106
+ await writeFile2(updatePath, replaced);
107
+ }
108
+ async function buildEntry({
109
+ privateKeyPath,
110
+ publicKeyPath,
111
+ entryPath,
112
+ entryOutputPath: outfile,
113
+ minify,
114
+ keyLength
115
+ }) {
116
+ if (!isCI2) {
117
+ !existsSync(privateKeyPath) && await generateKey(privateKeyPath, publicKeyPath, keyLength);
118
+ await writePublicKeyToMain(entryPath, publicKeyPath);
119
+ }
120
+ await build({
121
+ entryPoints: [entryPath],
122
+ bundle: true,
123
+ platform: "node",
124
+ outfile,
125
+ minify,
126
+ external: ["electron"]
127
+ });
128
+ }
129
+
130
+ // src/build-plugins/option.ts
131
131
  function parseOptions(options) {
132
132
  const { isBuild, productName, version, minify = false, paths = {}, keys = {} } = options;
133
133
  const {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "electron-incremental-update",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "electron incremental update tools, powered by vite",
5
5
  "scripts": {
6
6
  "build": "tsup",