electron-incremental-update 0.6.1 → 0.6.3

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
@@ -82,14 +82,16 @@ To utilize the electron `net` module for requesting update information, the `che
82
82
 
83
83
  However, you have the option to customize the download function when creating the updater.
84
84
 
85
+ **NOTE: There can only be one function and should be default export in the entry file**
86
+
85
87
  ```ts
86
88
  // electron/main/index.ts
87
- import type { Updater } from 'electron-incremental-update'
89
+ import type { StartupWithUpdater, Updater } from 'electron-incremental-update'
88
90
  import { getEntryVersion, getProductAsarPath, getProductVersion } from 'electron-incremental-update'
89
91
  import { app } from 'electron'
90
92
  import { name } from '../../package.json'
91
93
 
92
- export default function (updater: Updater) {
94
+ const startup: StartupWithUpdater = (updater: Updater) => {
93
95
  await app.whenReady()
94
96
  console.log('\ncurrent:')
95
97
  console.log(`\tasar path: ${getProductAsarPath(name)}`)
@@ -117,6 +119,7 @@ export default function (updater: Updater) {
117
119
  }
118
120
  })
119
121
  }
122
+ export default startup
120
123
  ```
121
124
 
122
125
  ### use native modules
@@ -186,6 +189,15 @@ export default defineConfig(({ command }) => {
186
189
  })
187
190
  ```
188
191
 
192
+ ### modify package.json
193
+
194
+ ```json
195
+ {
196
+ // ...
197
+ "main": "app.js" // <- app entry file
198
+ }
199
+ ```
200
+
189
201
  ### electron-builder config
190
202
 
191
203
  ```js
@@ -3,7 +3,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
3
3
  }) : x)(function(x) {
4
4
  if (typeof require !== "undefined")
5
5
  return require.apply(this, arguments);
6
- throw new Error('Dynamic require of "' + x + '" is not supported');
6
+ throw Error('Dynamic require of "' + x + '" is not supported');
7
7
  });
8
8
 
9
9
  // src/crypto.ts
@@ -26,15 +26,15 @@ function key(data, length) {
26
26
  const hash = createHash("SHA256").update(data).digest("binary");
27
27
  return Buffer2.from(hash).subarray(0, length);
28
28
  }
29
- function signature(buffer, privateKey, cert, version) {
29
+ var signature = (buffer, privateKey, cert, version) => {
30
30
  const sig = createSign("RSA-SHA256").update(buffer).sign({
31
31
  key: privateKey,
32
32
  padding: constants.RSA_PKCS1_PADDING,
33
33
  saltLength: constants.RSA_PSS_SALTLEN_DIGEST
34
34
  }, "base64");
35
35
  return encrypt(`${sig}%${version}`, key(cert, 32), key(buffer, 16));
36
- }
37
- function verify(buffer, signature2, cert) {
36
+ };
37
+ var verify = (buffer, signature2, cert) => {
38
38
  try {
39
39
  const [sig, version] = decrypt(signature2, key(cert, 32), key(buffer, 16)).split("%");
40
40
  const result = createVerify("RSA-SHA256").update(buffer).verify(cert, sig, "base64");
@@ -42,7 +42,7 @@ function verify(buffer, signature2, cert) {
42
42
  } catch (error) {
43
43
  return false;
44
44
  }
45
- }
45
+ };
46
46
 
47
47
  export {
48
48
  __require,
package/dist/index.cjs CHANGED
@@ -59,7 +59,7 @@ function key(data, length) {
59
59
  const hash = (0, import_node_crypto.createHash)("SHA256").update(data).digest("binary");
60
60
  return import_node_buffer.Buffer.from(hash).subarray(0, length);
61
61
  }
62
- function verify(buffer, signature, cert) {
62
+ var verify = (buffer, signature, cert) => {
63
63
  try {
64
64
  const [sig, version] = decrypt(signature, key(cert, 32), key(buffer, 16)).split("%");
65
65
  const result = (0, import_node_crypto.createVerify)("RSA-SHA256").update(buffer).verify(cert, sig, "base64");
@@ -67,7 +67,7 @@ function verify(buffer, signature, cert) {
67
67
  } catch (error) {
68
68
  return false;
69
69
  }
70
- }
70
+ };
71
71
 
72
72
  // src/updater/defaultFunctions.ts
73
73
  var import_node_buffer2 = require("buffer");
@@ -200,7 +200,7 @@ async function downloadBufferDefault(url, updater, headers) {
200
200
  request.end();
201
201
  });
202
202
  }
203
- function compareVersionDefault(oldVersion, newVersion) {
203
+ var compareVersionDefault = (oldVersion, newVersion) => {
204
204
  if (!oldVersion || !newVersion) {
205
205
  throw new TypeError("invalid version");
206
206
  }
@@ -221,7 +221,7 @@ function compareVersionDefault(oldVersion, newVersion) {
221
221
  return true;
222
222
  }
223
223
  return false;
224
- }
224
+ };
225
225
 
226
226
  // src/updater/index.ts
227
227
  function createUpdater({
@@ -231,15 +231,19 @@ function createUpdater({
231
231
  releaseAsarURL: _release,
232
232
  updateJsonURL: _update,
233
233
  debug = false,
234
- downloadConfig,
235
- compareVersion
234
+ downloadConfig: { extraHeader, userAgent } = {},
235
+ overrideFunctions: {
236
+ compareVersion,
237
+ verifySignaure,
238
+ downloadBuffer,
239
+ downloadJSON
240
+ } = {}
236
241
  }) {
237
242
  const updater = new import_node_events.EventEmitter();
238
243
  let signature = "";
239
244
  const asarPath = getProductAsarPath(productName);
240
245
  const gzipPath = `${asarPath}.gz`;
241
246
  const tmpFilePath = gzipPath.replace(".asar.gz", ".tmp.asar");
242
- const { downloadBuffer, downloadJSON, extraHeader, userAgent } = downloadConfig || {};
243
247
  function log(msg) {
244
248
  debug && updater.emit("debug", msg);
245
249
  }
@@ -323,6 +327,7 @@ function createUpdater({
323
327
  throw new Error(`invalid type at format '${format}': ${data}`);
324
328
  }
325
329
  }
330
+ updater.setDebug = (isDebug) => debug = isDebug;
326
331
  updater.checkUpdate = async (data) => {
327
332
  try {
328
333
  const { signature: _sig, size, version } = await parseData("json", data);
@@ -348,7 +353,8 @@ function createUpdater({
348
353
  }
349
354
  const buffer = await parseData("buffer", data);
350
355
  log("verify start");
351
- const version = verify(buffer, _sig, SIGNATURE_CERT);
356
+ const _verify = verifySignaure ?? verify;
357
+ const version = _verify(buffer, _sig, SIGNATURE_CERT);
352
358
  if (!version) {
353
359
  throw new Error("verify failed, invalid signature");
354
360
  }
@@ -0,0 +1,237 @@
1
+ import { Buffer } from 'node:buffer';
2
+
3
+ type CheckResultType = Omit<UpdateJSON, 'signature'> | undefined | Error;
4
+ type InstallResult = true | Error;
5
+ type UpdateEvents = {
6
+ downloading: [progress: number];
7
+ debug: [msg: string | Error];
8
+ };
9
+ type UpdateJSON = {
10
+ signature: string;
11
+ version: string;
12
+ size: number;
13
+ };
14
+ declare function isUpdateJSON(json: any): json is UpdateJSON;
15
+ type MaybeArray<T> = T extends undefined | null | never ? [] : T extends any[] ? T['length'] extends 1 ? [data: T[0]] : T : [data: T];
16
+ interface TypedUpdater<T extends Record<string | symbol, MaybeArray<any>>, Event extends Exclude<keyof T, number> = Exclude<keyof T, number>> {
17
+ removeAllListeners<E extends Event>(event?: E): this;
18
+ listeners<E extends Event>(eventName: E): Function[];
19
+ eventNames(): (Event)[];
20
+ on<E extends Event>(eventName: E, listener: (...data: MaybeArray<T[E]>) => void): this;
21
+ once<E extends Event>(eventName: E, listener: (...data: MaybeArray<T[E]>) => void): this;
22
+ emit<E extends Event>(eventName: E, ...args: MaybeArray<T[E]>): boolean;
23
+ off<E extends Event>(eventName: E, listener: (...args: MaybeArray<T[E]>) => void): this;
24
+ /**
25
+ * check update info
26
+ * @param data update json url
27
+ * @returns
28
+ * - `{size: number, version: string}`: available
29
+ * - `false`: unavailable
30
+ * - `Error`: fail
31
+ */
32
+ checkUpdate(data?: string | UpdateJSON): Promise<CheckResultType>;
33
+ /**
34
+ * download update and install
35
+ *
36
+ * if you want to update **offline**, you can set both `src` and `sig` to verify and install
37
+ * @param data asar download url or buffer
38
+ * @param sig signature
39
+ * @returns
40
+ * - `true`: success
41
+ * - `Error`: fail
42
+ */
43
+ downloadAndInstall(data?: string | Buffer, sig?: string): Promise<InstallResult>;
44
+ setDebug(debug: boolean): void;
45
+ }
46
+ type FunctionVerifySignature = (buffer: Buffer, signature: string, cert: string) => string | false;
47
+ type FunctionCompareVersion = (oldVersion: string, newVersion: string) => boolean;
48
+ type Updater = TypedUpdater<UpdateEvents>;
49
+ interface UpdaterOption {
50
+ /**
51
+ * public key of signature
52
+ *
53
+ * it will be auto generated by plugin
54
+ * @example
55
+ * ```ts
56
+ * // auto filled by plugin
57
+ * const SIGNATURE_CERT = ''
58
+ *
59
+ * const updater = createUpdater({
60
+ * SIGNATURE_CERT,
61
+ * ...
62
+ * })
63
+ * ```
64
+ */
65
+ SIGNATURE_CERT: string;
66
+ /**
67
+ * name of your application
68
+ *
69
+ * you can use the `name` in `package.json`
70
+ */
71
+ productName: string;
72
+ /**
73
+ * repository url, e.g. `https://github.com/electron/electron`
74
+ *
75
+ * you can use the `repository` in `package.json`
76
+ *
77
+ * if `updateJsonURL` or `releaseAsarURL` are absent,
78
+ * `repository` will be used to determine the url
79
+ */
80
+ repository?: string;
81
+ /**
82
+ * URL of version info json
83
+ * @default `${repository.replace('github.com', 'raw.githubusercontent.com')}/master/version.json`
84
+ * @throws if `updateJsonURL` and `repository` are all not set
85
+ */
86
+ updateJsonURL?: string;
87
+ /**
88
+ * URL of release asar.gz
89
+ * @default `${repository}/releases/download/latest/${productName}.asar.gz`
90
+ * @throws if `releaseAsarURL` and `repository` are all not set
91
+ */
92
+ releaseAsarURL?: string;
93
+ /**
94
+ * whether to enable debug listener
95
+ */
96
+ debug?: boolean;
97
+ overrideFunctions?: {
98
+ /**
99
+ * custom version compare function {@link FunctionCompareVersion}
100
+ * @param oldVersion old version string
101
+ * @param newVersion new version string
102
+ * @returns whether to update
103
+ */
104
+ compareVersion?: FunctionCompareVersion;
105
+ /**
106
+ * custom verify signature function {@link FunctionVerifySignature}
107
+ * @param buffer file buffer
108
+ * @param signature signature
109
+ * @param cert certificate
110
+ */
111
+ verifySignaure?: FunctionVerifySignature;
112
+ /**
113
+ * custom download JSON function
114
+ * @param url download url
115
+ * @param updater updater, to trigger events
116
+ * @param header download header
117
+ * @returns `UpdateJSON`
118
+ */
119
+ downloadJSON?: (url: string, updater: Updater, headers: Record<string, any>) => Promise<UpdateJSON>;
120
+ /**
121
+ * custom download buffer function
122
+ * @param url download url
123
+ * @param updater updater, to trigger events
124
+ * @param header download header
125
+ * @returns `Buffer`
126
+ */
127
+ downloadBuffer?: (url: string, updater: Updater, headers: Record<string, any>) => Promise<Buffer>;
128
+ };
129
+ downloadConfig?: {
130
+ /**
131
+ * download user agent
132
+ * @default 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36'
133
+ */
134
+ userAgent?: string;
135
+ /**
136
+ * extra download header, `accept` and `user-agent` is set by default
137
+ */
138
+ extraHeader?: Record<string, string>;
139
+ };
140
+ }
141
+
142
+ /**
143
+ * get the application asar absolute path
144
+ * @param name The name of the application
145
+ */
146
+ declare function getProductAsarPath(name: string): string;
147
+ /**
148
+ * get the version of entry (app.asar)
149
+ */
150
+ declare function getEntryVersion(): string;
151
+ /**
152
+ * get the version of application (name.asar)
153
+ * @param name - The name of the application
154
+ */
155
+ declare function getProductVersion(name: string): string;
156
+ /**
157
+ * require native package from app.asar
158
+ * @param packageName native package name
159
+ */
160
+ declare function requireNative<T = any>(packageName: string): T;
161
+ /**
162
+ * get github version.json CDN URL for accelerating the speed of downloading version info
163
+ */
164
+ declare function parseGithubCdnURL(repository: string, cdnPrefix: string, relativeFilePath: string): string;
165
+ /**
166
+ * get group of github release CDN prefix for accelerating the speed of downloading release
167
+ */
168
+ declare function getGithubReleaseCdnGroup(): {
169
+ cdnPrefix: string;
170
+ maintainer: string;
171
+ }[];
172
+ declare function restartApp(): void;
173
+ declare function waitAppReady(duration?: number): Promise<unknown>;
174
+
175
+ declare function createUpdater({ SIGNATURE_CERT, repository, productName, releaseAsarURL: _release, updateJsonURL: _update, debug, downloadConfig: { extraHeader, userAgent }, overrideFunctions: { compareVersion, verifySignaure, downloadBuffer, downloadJSON, }, }: UpdaterOption): Updater;
176
+
177
+ type AppOption = {
178
+ /**
179
+ * name of your application
180
+ *
181
+ * you can use the `name` in `package.json`
182
+ */
183
+ name: string;
184
+ /**
185
+ * path of electron output dist
186
+ * @default 'dist-electron'
187
+ */
188
+ electronDistPath?: string;
189
+ /**
190
+ * relative path of main entry in electron dist
191
+ * @default 'main/index.js'
192
+ */
193
+ mainPath?: string;
194
+ };
195
+ type OptionalProperty<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
196
+ type InitUpdaterOptions = OptionalProperty<UpdaterOption, 'productName'>;
197
+ type StartupWithUpdater = (updater: Updater) => void;
198
+ /**
199
+ * create updater manually
200
+ * @example
201
+ * ```ts
202
+ * import { createUpdater, getGithubReleaseCdnGroup, initApp, parseGithubCdnURL } from 'electron-incremental-update'
203
+ * import { name, repository } from '../package.json'
204
+ *
205
+ * const SIGNATURE_CERT = '' // auto generate
206
+ *
207
+ * const { cdnPrefix } = getGithubReleaseCdnGroup()[0]
208
+ * const updater = createUpdater({
209
+ * SIGNATURE_CERT,
210
+ * productName: name,
211
+ * repository,
212
+ * updateJsonURL: parseGithubCdnURL(repository, 'fastly.jsdelivr.net/gh', 'version.json'),
213
+ * releaseAsarURL: parseGithubCdnURL(repository, cdnPrefix, `download/latest/${name}.asar.gz`),
214
+ * debug: true,
215
+ * })
216
+ * initApp({ name }).setUpdater(updater)
217
+ * ```
218
+ */
219
+ declare function initApp(appOptions: AppOption): {
220
+ setUpdater: (updater: Updater) => void;
221
+ };
222
+ /**
223
+ * create updater when init, no need to set productName
224
+ *
225
+ * @example
226
+ * ```ts
227
+ * import { initApp } from 'electron-incremental-update'
228
+ * import { name, repository } from '../package.json'
229
+ *
230
+ * const SIGNATURE_CERT = '' // auto generate
231
+ *
232
+ * initApp({ name }, { SIGNATURE_CERT, repository })
233
+ * ```
234
+ */
235
+ declare function initApp(appOptions: AppOption, updaterOptions: InitUpdaterOptions): undefined;
236
+
237
+ export { AppOption, CheckResultType, FunctionCompareVersion, FunctionVerifySignature, InitUpdaterOptions, InstallResult, StartupWithUpdater, UpdateJSON, Updater, UpdaterOption, createUpdater, getEntryVersion, getGithubReleaseCdnGroup, getProductAsarPath, getProductVersion, initApp, isUpdateJSON, parseGithubCdnURL, requireNative, restartApp, waitAppReady };
package/dist/index.d.ts CHANGED
@@ -41,7 +41,10 @@ interface TypedUpdater<T extends Record<string | symbol, MaybeArray<any>>, Event
41
41
  * - `Error`: fail
42
42
  */
43
43
  downloadAndInstall(data?: string | Buffer, sig?: string): Promise<InstallResult>;
44
+ setDebug(debug: boolean): void;
44
45
  }
46
+ type FunctionVerifySignature = (buffer: Buffer, signature: string, cert: string) => string | false;
47
+ type FunctionCompareVersion = (oldVersion: string, newVersion: string) => boolean;
45
48
  type Updater = TypedUpdater<UpdateEvents>;
46
49
  interface UpdaterOption {
47
50
  /**
@@ -91,25 +94,23 @@ interface UpdaterOption {
91
94
  * whether to enable debug listener
92
95
  */
93
96
  debug?: boolean;
94
- /**
95
- * custom version compare function
96
- * @param oldVersion old version string
97
- * @param newVersion new version string
98
- * @returns whether to update
99
- */
100
- compareVersion?: (oldVersion: string, newVersion: string) => boolean;
101
- downloadConfig?: {
97
+ overrideFunctions?: {
102
98
  /**
103
- * download user agent
104
- * @default 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36'
105
- */
106
- userAgent?: string;
99
+ * custom version compare function {@link FunctionCompareVersion}
100
+ * @param oldVersion old version string
101
+ * @param newVersion new version string
102
+ * @returns whether to update
103
+ */
104
+ compareVersion?: FunctionCompareVersion;
107
105
  /**
108
- * extra download header, `accept` and `user-agent` is set by default
109
- */
110
- extraHeader?: Record<string, string>;
106
+ * custom verify signature function {@link FunctionVerifySignature}
107
+ * @param buffer file buffer
108
+ * @param signature signature
109
+ * @param cert certificate
110
+ */
111
+ verifySignaure?: FunctionVerifySignature;
111
112
  /**
112
- * download JSON function
113
+ * custom download JSON function
113
114
  * @param url download url
114
115
  * @param updater updater, to trigger events
115
116
  * @param header download header
@@ -117,7 +118,7 @@ interface UpdaterOption {
117
118
  */
118
119
  downloadJSON?: (url: string, updater: Updater, headers: Record<string, any>) => Promise<UpdateJSON>;
119
120
  /**
120
- * download buffer function
121
+ * custom download buffer function
121
122
  * @param url download url
122
123
  * @param updater updater, to trigger events
123
124
  * @param header download header
@@ -125,6 +126,17 @@ interface UpdaterOption {
125
126
  */
126
127
  downloadBuffer?: (url: string, updater: Updater, headers: Record<string, any>) => Promise<Buffer>;
127
128
  };
129
+ downloadConfig?: {
130
+ /**
131
+ * download user agent
132
+ * @default 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36'
133
+ */
134
+ userAgent?: string;
135
+ /**
136
+ * extra download header, `accept` and `user-agent` is set by default
137
+ */
138
+ extraHeader?: Record<string, string>;
139
+ };
128
140
  }
129
141
 
130
142
  /**
@@ -160,7 +172,7 @@ declare function getGithubReleaseCdnGroup(): {
160
172
  declare function restartApp(): void;
161
173
  declare function waitAppReady(duration?: number): Promise<unknown>;
162
174
 
163
- declare function createUpdater({ SIGNATURE_CERT, repository, productName, releaseAsarURL: _release, updateJsonURL: _update, debug, downloadConfig, compareVersion, }: UpdaterOption): Updater;
175
+ declare function createUpdater({ SIGNATURE_CERT, repository, productName, releaseAsarURL: _release, updateJsonURL: _update, debug, downloadConfig: { extraHeader, userAgent }, overrideFunctions: { compareVersion, verifySignaure, downloadBuffer, downloadJSON, }, }: UpdaterOption): Updater;
164
176
 
165
177
  type AppOption = {
166
178
  /**
@@ -182,6 +194,7 @@ type AppOption = {
182
194
  };
183
195
  type OptionalProperty<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
184
196
  type InitUpdaterOptions = OptionalProperty<UpdaterOption, 'productName'>;
197
+ type StartupWithUpdater = (updater: Updater) => void;
185
198
  /**
186
199
  * create updater manually
187
200
  * @example
@@ -221,4 +234,4 @@ declare function initApp(appOptions: AppOption): {
221
234
  */
222
235
  declare function initApp(appOptions: AppOption, updaterOptions: InitUpdaterOptions): undefined;
223
236
 
224
- export { AppOption, CheckResultType, InitUpdaterOptions, InstallResult, UpdateJSON, Updater, UpdaterOption, createUpdater, getEntryVersion, getGithubReleaseCdnGroup, getProductAsarPath, getProductVersion, initApp, isUpdateJSON, parseGithubCdnURL, requireNative, restartApp, waitAppReady };
237
+ export { AppOption, CheckResultType, FunctionCompareVersion, FunctionVerifySignature, InitUpdaterOptions, InstallResult, StartupWithUpdater, UpdateJSON, Updater, UpdaterOption, createUpdater, getEntryVersion, getGithubReleaseCdnGroup, getProductAsarPath, getProductVersion, initApp, isUpdateJSON, parseGithubCdnURL, requireNative, restartApp, waitAppReady };
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  __require,
3
3
  verify
4
- } from "./chunk-VADH6AZA.mjs";
4
+ } from "./chunk-XQ4Z2OVN.mjs";
5
5
 
6
6
  // src/index.ts
7
7
  import { resolve as resolve2 } from "node:path";
@@ -147,7 +147,7 @@ async function downloadBufferDefault(url, updater, headers) {
147
147
  request.end();
148
148
  });
149
149
  }
150
- function compareVersionDefault(oldVersion, newVersion) {
150
+ var compareVersionDefault = (oldVersion, newVersion) => {
151
151
  if (!oldVersion || !newVersion) {
152
152
  throw new TypeError("invalid version");
153
153
  }
@@ -168,7 +168,7 @@ function compareVersionDefault(oldVersion, newVersion) {
168
168
  return true;
169
169
  }
170
170
  return false;
171
- }
171
+ };
172
172
 
173
173
  // src/updater/index.ts
174
174
  function createUpdater({
@@ -178,15 +178,19 @@ function createUpdater({
178
178
  releaseAsarURL: _release,
179
179
  updateJsonURL: _update,
180
180
  debug = false,
181
- downloadConfig,
182
- compareVersion
181
+ downloadConfig: { extraHeader, userAgent } = {},
182
+ overrideFunctions: {
183
+ compareVersion,
184
+ verifySignaure,
185
+ downloadBuffer,
186
+ downloadJSON
187
+ } = {}
183
188
  }) {
184
189
  const updater = new EventEmitter();
185
190
  let signature = "";
186
191
  const asarPath = getProductAsarPath(productName);
187
192
  const gzipPath = `${asarPath}.gz`;
188
193
  const tmpFilePath = gzipPath.replace(".asar.gz", ".tmp.asar");
189
- const { downloadBuffer, downloadJSON, extraHeader, userAgent } = downloadConfig || {};
190
194
  function log(msg) {
191
195
  debug && updater.emit("debug", msg);
192
196
  }
@@ -270,6 +274,7 @@ function createUpdater({
270
274
  throw new Error(`invalid type at format '${format}': ${data}`);
271
275
  }
272
276
  }
277
+ updater.setDebug = (isDebug) => debug = isDebug;
273
278
  updater.checkUpdate = async (data) => {
274
279
  try {
275
280
  const { signature: _sig, size, version } = await parseData("json", data);
@@ -295,7 +300,8 @@ function createUpdater({
295
300
  }
296
301
  const buffer = await parseData("buffer", data);
297
302
  log("verify start");
298
- const version = verify(buffer, _sig, SIGNATURE_CERT);
303
+ const _verify = verifySignaure ?? verify;
304
+ const version = _verify(buffer, _sig, SIGNATURE_CERT);
299
305
  if (!version) {
300
306
  throw new Error("verify failed, invalid signature");
301
307
  }
package/dist/vite.cjs CHANGED
@@ -55,14 +55,14 @@ function key(data, length) {
55
55
  const hash = (0, import_node_crypto.createHash)("SHA256").update(data).digest("binary");
56
56
  return import_node_buffer.Buffer.from(hash).subarray(0, length);
57
57
  }
58
- function signature(buffer, privateKey, cert, version) {
58
+ var signature = (buffer, privateKey, cert, version) => {
59
59
  const sig = (0, import_node_crypto.createSign)("RSA-SHA256").update(buffer).sign({
60
60
  key: privateKey,
61
61
  padding: import_node_crypto.constants.RSA_PKCS1_PADDING,
62
62
  saltLength: import_node_crypto.constants.RSA_PSS_SALTLEN_DIGEST
63
63
  }, "base64");
64
64
  return encrypt(`${sig}%${version}`, key(cert, 32), key(buffer, 16));
65
- }
65
+ };
66
66
 
67
67
  // src/build-plugins/build.ts
68
68
  function gzipFile(filePath) {
@@ -106,11 +106,13 @@ async function buildVersion({
106
106
  versionPath,
107
107
  privateKey,
108
108
  cert,
109
- version
109
+ version,
110
+ generateSignature
110
111
  }) {
111
112
  const buffer = await (0, import_promises.readFile)(`${asarOutputPath}.gz`);
113
+ const _func = generateSignature ?? signature;
112
114
  await (0, import_promises.writeFile)(versionPath, JSON.stringify({
113
- signature: signature(buffer, privateKey, cert, version),
115
+ signature: _func(buffer, privateKey, cert, version),
114
116
  version,
115
117
  size: buffer.length
116
118
  }, null, 2));
@@ -136,23 +138,13 @@ var import_node_path = require("path");
136
138
  var import_node_os = require("os");
137
139
  var import_node_crypto2 = require("crypto");
138
140
  var import_jscert = require("@cyyynthia/jscert");
139
- function generateCert(privateKey) {
140
- const dn = {
141
- country: "zh-CN",
142
- state: "zj",
143
- locality: "hz",
144
- organization: "test",
145
- organizationalUnit: "test unit",
146
- commonName: "test.test",
147
- emailAddress: "test@example.com"
148
- };
141
+ function generateCert(privateKey, dn, expires) {
149
142
  const csr = new import_jscert.CertificateSigningRequest(dn, privateKey, { digest: "sha256" });
150
- const expiry = new Date(Date.now() + 365 * 864e5);
151
- return csr.createSelfSignedCertificate(expiry).toPem();
143
+ return csr.createSelfSignedCertificate(expires).toPem();
152
144
  }
153
- function generateKeys(length = 2048) {
145
+ function generateKeyPairDefault(length, subjects, expires) {
154
146
  const { privateKey: _key } = (0, import_node_crypto2.generateKeyPairSync)("rsa", { modulusLength: length });
155
- const cert = generateCert(_key);
147
+ const cert = generateCert(_key, subjects, expires);
156
148
  const privateKey = _key.export({ type: "pkcs1", format: "pem" });
157
149
  return {
158
150
  privateKey,
@@ -188,13 +180,17 @@ function getKeys({
188
180
  keyLength,
189
181
  privateKeyPath,
190
182
  certPath,
191
- entryPath
183
+ entryPath,
184
+ subject,
185
+ expires,
186
+ generateKeyPair
192
187
  }) {
193
188
  const keysDir = (0, import_node_path.dirname)(privateKeyPath);
194
189
  !(0, import_node_fs2.existsSync)(keysDir) && (0, import_node_fs2.mkdirSync)(keysDir);
195
190
  let privateKey, cert;
196
191
  if (!(0, import_node_fs2.existsSync)(privateKeyPath) || !(0, import_node_fs2.existsSync)(certPath)) {
197
- const keys = generateKeys(keyLength);
192
+ const _func = generateKeyPair ?? generateKeyPairDefault;
193
+ const keys = _func(keyLength, subject, expires);
198
194
  privateKey = keys.privateKey;
199
195
  cert = keys.cert;
200
196
  (0, import_node_fs2.writeFileSync)(privateKeyPath, privateKey);
@@ -213,28 +209,38 @@ function getKeys({
213
209
  // src/build-plugins/option.ts
214
210
  var import_ci_info = require("ci-info");
215
211
  function parseOptions(options) {
216
- const { isBuild, productName, version, minify = false, paths = {}, keys = {} } = options;
217
212
  const {
218
- entryPath = "electron/app.ts",
219
- entryOutputPath = "app.js",
220
- asarOutputPath = `release/${productName}-${version}.asar`,
221
- electronDistPath = "dist-electron",
222
- rendererDistPath = "dist",
223
- versionPath = "version.json"
224
- } = paths;
213
+ isBuild,
214
+ productName,
215
+ version,
216
+ minify = false,
217
+ paths: {
218
+ entryPath = "electron/app.ts",
219
+ entryOutputPath = "app.js",
220
+ asarOutputPath = `release/${productName}-${version}.asar`,
221
+ electronDistPath = "dist-electron",
222
+ rendererDistPath = "dist",
223
+ versionPath = "version.json"
224
+ } = {},
225
+ keys: {
226
+ privateKeyPath = "keys/private.pem",
227
+ certPath = "keys/cert.pem",
228
+ keyLength = 2048,
229
+ certInfo = {},
230
+ overrideFunctions = {}
231
+ } = {}
232
+ } = options;
225
233
  const {
226
- privateKeyPath = "keys/private.pem",
227
- certPath = "keys/cert.pem",
228
- keyLength = 2048,
229
- certInfo
230
- } = keys;
234
+ generateKeyPair,
235
+ generateSignature
236
+ } = overrideFunctions;
231
237
  let {
232
238
  subject = {
233
239
  commonName: productName,
234
240
  organization: `org.${productName}`
235
241
  },
236
242
  expires = Date.now() + 365 * 864e5
237
- } = certInfo || {};
243
+ } = certInfo;
238
244
  const buildAsarOption = {
239
245
  version,
240
246
  asarOutputPath,
@@ -257,14 +263,16 @@ function parseOptions(options) {
257
263
  certPath,
258
264
  entryPath,
259
265
  subject,
260
- expires
266
+ expires,
267
+ generateKeyPair
261
268
  });
262
269
  buildVersionOption = {
263
270
  version,
264
271
  asarOutputPath,
265
272
  privateKey,
266
273
  cert,
267
- versionPath
274
+ versionPath,
275
+ generateSignature
268
276
  };
269
277
  }
270
278
  return { isBuild, buildAsarOption, buildEntryOption, buildVersionOption };
@@ -0,0 +1,129 @@
1
+ import { Plugin } from 'vite';
2
+ import { Buffer } from 'node:buffer';
3
+ import { DistinguishedName } from '@cyyynthia/jscert';
4
+
5
+ type FunctionGenerateKeyPair = (keyLength: number, subject: DistinguishedName, expires: Date) => {
6
+ privateKey: string;
7
+ cert: string;
8
+ };
9
+ type FunctionGenerateSignature = (buffer: Buffer, privateKey: string, cert: string, version: string) => string;
10
+ type Options = {
11
+ /**
12
+ * whether is in build mode
13
+ */
14
+ isBuild: boolean;
15
+ /**
16
+ * the name of you application
17
+ *
18
+ * you can set as 'name' in `package.json`
19
+ */
20
+ productName: string;
21
+ /**
22
+ * the version of you application
23
+ *
24
+ * you can set as 'version' in `package.json`
25
+ */
26
+ version: string;
27
+ /**
28
+ * Whether to minify entry file
29
+ */
30
+ minify?: boolean;
31
+ /**
32
+ * paths config
33
+ */
34
+ paths?: {
35
+ /**
36
+ * Path to app entry file
37
+ * @default 'electron/app.ts'
38
+ */
39
+ entryPath?: string;
40
+ /**
41
+ * Path to app entry output file
42
+ * @default 'app.js'
43
+ */
44
+ entryOutputPath?: string;
45
+ /**
46
+ * Path to asar file
47
+ * @default `release/${productName}.asar`
48
+ */
49
+ asarOutputPath?: string;
50
+ /**
51
+ * Path to electron build output
52
+ * @default `dist-electron`
53
+ */
54
+ electronDistPath?: string;
55
+ /**
56
+ * Path to renderer build output
57
+ * @default `dist`
58
+ */
59
+ rendererDistPath?: string;
60
+ /**
61
+ * Path to version info output
62
+ * @default `version.json`
63
+ */
64
+ versionPath?: string;
65
+ };
66
+ /**
67
+ * signature config
68
+ */
69
+ keys?: {
70
+ /**
71
+ * Path to the pem file that contains private key
72
+ * if not ended with .pem, it will be appended
73
+ * @default 'keys/private.pem'
74
+ */
75
+ privateKeyPath?: string;
76
+ /**
77
+ * Path to the pem file that contains public key
78
+ * if not ended with .pem, it will be appended
79
+ * @default 'keys/cert.pem'
80
+ */
81
+ certPath?: string;
82
+ /**
83
+ * Length of the key
84
+ * @default 2048
85
+ */
86
+ keyLength?: number;
87
+ /**
88
+ * X509 certificate info
89
+ *
90
+ * only generate simple **self-signed** certificate **without extensions**
91
+ */
92
+ certInfo?: {
93
+ /**
94
+ * the subject of the certificate
95
+ *
96
+ * @default { commonName: productName, organization: `org.${productName}` }
97
+ */
98
+ subject?: DistinguishedName;
99
+ /**
100
+ * expires of the certificate
101
+ * - `Date`: expire date
102
+ * - `number`: expire duration in seconds
103
+ *
104
+ * @default Date.now() + 365 * 864e5 (1 year)
105
+ */
106
+ expires?: Date | number;
107
+ };
108
+ overrideFunctions?: {
109
+ /**
110
+ * custom key pair generate function {@link FunctionGenerateKeyPair}
111
+ * @param keyLength key length
112
+ * @param subject subject info
113
+ * @param expires expire date
114
+ */
115
+ generateKeyPair?: FunctionGenerateKeyPair;
116
+ /**
117
+ * custom signature generate function {@link FunctionGenerateSignature}
118
+ * @param buffer file buffer
119
+ * @param privateKey private key
120
+ * @param cert certificate
121
+ */
122
+ generateSignature?: FunctionGenerateSignature;
123
+ };
124
+ };
125
+ };
126
+
127
+ declare function export_default(options: Options): Plugin;
128
+
129
+ export { export_default as default };
package/dist/vite.d.ts CHANGED
@@ -1,6 +1,12 @@
1
1
  import { Plugin } from 'vite';
2
+ import { Buffer } from 'node:buffer';
2
3
  import { DistinguishedName } from '@cyyynthia/jscert';
3
4
 
5
+ type FunctionGenerateKeyPair = (keyLength: number, subject: DistinguishedName, expires: Date) => {
6
+ privateKey: string;
7
+ cert: string;
8
+ };
9
+ type FunctionGenerateSignature = (buffer: Buffer, privateKey: string, cert: string, version: string) => string;
4
10
  type Options = {
5
11
  /**
6
12
  * whether is in build mode
@@ -99,6 +105,22 @@ type Options = {
99
105
  */
100
106
  expires?: Date | number;
101
107
  };
108
+ overrideFunctions?: {
109
+ /**
110
+ * custom key pair generate function {@link FunctionGenerateKeyPair}
111
+ * @param keyLength key length
112
+ * @param subject subject info
113
+ * @param expires expire date
114
+ */
115
+ generateKeyPair?: FunctionGenerateKeyPair;
116
+ /**
117
+ * custom signature generate function {@link FunctionGenerateSignature}
118
+ * @param buffer file buffer
119
+ * @param privateKey private key
120
+ * @param cert certificate
121
+ */
122
+ generateSignature?: FunctionGenerateSignature;
123
+ };
102
124
  };
103
125
  };
104
126
 
package/dist/vite.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  signature
3
- } from "./chunk-VADH6AZA.mjs";
3
+ } from "./chunk-XQ4Z2OVN.mjs";
4
4
 
5
5
  // src/vite.ts
6
6
  import { createLogger } from "vite";
@@ -51,11 +51,13 @@ async function buildVersion({
51
51
  versionPath,
52
52
  privateKey,
53
53
  cert,
54
- version
54
+ version,
55
+ generateSignature
55
56
  }) {
56
57
  const buffer = await readFile(`${asarOutputPath}.gz`);
58
+ const _func = generateSignature ?? signature;
57
59
  await writeFile(versionPath, JSON.stringify({
58
- signature: signature(buffer, privateKey, cert, version),
60
+ signature: _func(buffer, privateKey, cert, version),
59
61
  version,
60
62
  size: buffer.length
61
63
  }, null, 2));
@@ -81,23 +83,13 @@ import { dirname } from "node:path";
81
83
  import { EOL } from "node:os";
82
84
  import { generateKeyPairSync } from "node:crypto";
83
85
  import { CertificateSigningRequest } from "@cyyynthia/jscert";
84
- function generateCert(privateKey) {
85
- const dn = {
86
- country: "zh-CN",
87
- state: "zj",
88
- locality: "hz",
89
- organization: "test",
90
- organizationalUnit: "test unit",
91
- commonName: "test.test",
92
- emailAddress: "test@example.com"
93
- };
86
+ function generateCert(privateKey, dn, expires) {
94
87
  const csr = new CertificateSigningRequest(dn, privateKey, { digest: "sha256" });
95
- const expiry = new Date(Date.now() + 365 * 864e5);
96
- return csr.createSelfSignedCertificate(expiry).toPem();
88
+ return csr.createSelfSignedCertificate(expires).toPem();
97
89
  }
98
- function generateKeys(length = 2048) {
90
+ function generateKeyPairDefault(length, subjects, expires) {
99
91
  const { privateKey: _key } = generateKeyPairSync("rsa", { modulusLength: length });
100
- const cert = generateCert(_key);
92
+ const cert = generateCert(_key, subjects, expires);
101
93
  const privateKey = _key.export({ type: "pkcs1", format: "pem" });
102
94
  return {
103
95
  privateKey,
@@ -133,13 +125,17 @@ function getKeys({
133
125
  keyLength,
134
126
  privateKeyPath,
135
127
  certPath,
136
- entryPath
128
+ entryPath,
129
+ subject,
130
+ expires,
131
+ generateKeyPair
137
132
  }) {
138
133
  const keysDir = dirname(privateKeyPath);
139
134
  !existsSync(keysDir) && mkdirSync(keysDir);
140
135
  let privateKey, cert;
141
136
  if (!existsSync(privateKeyPath) || !existsSync(certPath)) {
142
- const keys = generateKeys(keyLength);
137
+ const _func = generateKeyPair ?? generateKeyPairDefault;
138
+ const keys = _func(keyLength, subject, expires);
143
139
  privateKey = keys.privateKey;
144
140
  cert = keys.cert;
145
141
  writeFileSync(privateKeyPath, privateKey);
@@ -158,28 +154,38 @@ function getKeys({
158
154
  // src/build-plugins/option.ts
159
155
  import { isCI } from "ci-info";
160
156
  function parseOptions(options) {
161
- const { isBuild, productName, version, minify = false, paths = {}, keys = {} } = options;
162
157
  const {
163
- entryPath = "electron/app.ts",
164
- entryOutputPath = "app.js",
165
- asarOutputPath = `release/${productName}-${version}.asar`,
166
- electronDistPath = "dist-electron",
167
- rendererDistPath = "dist",
168
- versionPath = "version.json"
169
- } = paths;
158
+ isBuild,
159
+ productName,
160
+ version,
161
+ minify = false,
162
+ paths: {
163
+ entryPath = "electron/app.ts",
164
+ entryOutputPath = "app.js",
165
+ asarOutputPath = `release/${productName}-${version}.asar`,
166
+ electronDistPath = "dist-electron",
167
+ rendererDistPath = "dist",
168
+ versionPath = "version.json"
169
+ } = {},
170
+ keys: {
171
+ privateKeyPath = "keys/private.pem",
172
+ certPath = "keys/cert.pem",
173
+ keyLength = 2048,
174
+ certInfo = {},
175
+ overrideFunctions = {}
176
+ } = {}
177
+ } = options;
170
178
  const {
171
- privateKeyPath = "keys/private.pem",
172
- certPath = "keys/cert.pem",
173
- keyLength = 2048,
174
- certInfo
175
- } = keys;
179
+ generateKeyPair,
180
+ generateSignature
181
+ } = overrideFunctions;
176
182
  let {
177
183
  subject = {
178
184
  commonName: productName,
179
185
  organization: `org.${productName}`
180
186
  },
181
187
  expires = Date.now() + 365 * 864e5
182
- } = certInfo || {};
188
+ } = certInfo;
183
189
  const buildAsarOption = {
184
190
  version,
185
191
  asarOutputPath,
@@ -202,14 +208,16 @@ function parseOptions(options) {
202
208
  certPath,
203
209
  entryPath,
204
210
  subject,
205
- expires
211
+ expires,
212
+ generateKeyPair
206
213
  });
207
214
  buildVersionOption = {
208
215
  version,
209
216
  asarOutputPath,
210
217
  privateKey,
211
218
  cert,
212
- versionPath
219
+ versionPath,
220
+ generateSignature
213
221
  };
214
222
  }
215
223
  return { isBuild, buildAsarOption, buildEntryOption, buildVersionOption };
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "electron-incremental-update",
3
- "version": "0.6.1",
3
+ "author": "subframe7536",
4
+ "version": "0.6.3",
4
5
  "description": "electron incremental update tools, powered by vite",
5
6
  "scripts": {
6
7
  "build": "tsup",
7
- "release": "pnpm test && pnpm run build && bumpp && npm publish",
8
+ "release": "pnpm test && pnpm run build && bumpp --all && npm publish",
8
9
  "test": "vitest --run"
9
10
  },
10
11
  "publishConfig": {
@@ -22,22 +23,24 @@
22
23
  "types": "dist/index.d.ts",
23
24
  "exports": {
24
25
  ".": {
25
- "require": "./dist/index.cjs",
26
- "import": "./dist/index.mjs"
26
+ "import": {
27
+ "default": "./dist/index.mjs",
28
+ "types": "./dist/index.d.mts"
29
+ },
30
+ "require": {
31
+ "default": "./dist/index.cjs",
32
+ "types": "./dist/index.d.ts"
33
+ }
27
34
  },
28
35
  "./vite": {
29
- "require": "./dist/vite.cjs",
30
- "import": "./dist/vite.mjs"
31
- }
32
- },
33
- "typesVersions": {
34
- "*": {
35
- ".": [
36
- "dist/updater.d.ts"
37
- ],
38
- "vite": [
39
- "dist/vite.d.ts"
40
- ]
36
+ "import": {
37
+ "default": "./dist/index.mjs",
38
+ "types": "./dist/index.d.mts"
39
+ },
40
+ "require": {
41
+ "default": "./dist/index.cjs",
42
+ "types": "./dist/index.d.ts"
43
+ }
41
44
  }
42
45
  },
43
46
  "keywords": [
@@ -45,18 +48,17 @@
45
48
  "incremental update",
46
49
  "updater"
47
50
  ],
48
- "author": "",
49
51
  "devDependencies": {
50
52
  "@electron/asar": "^3.2.4",
51
53
  "@subframe7536/eslint-config": "^0.1.9",
52
- "@types/node": "^20.2.5",
54
+ "@types/node": "^20.3.2",
53
55
  "asar": "^3.2.0",
54
56
  "bumpp": "^9.1.1",
55
- "electron": "^25.0.1",
56
- "eslint": "^8.42.0",
57
+ "electron": "^25.2.0",
58
+ "eslint": "^8.43.0",
57
59
  "fs-jetpack": "^5.1.0",
58
- "tsup": "^6.7.0",
59
- "typescript": "^5.1.3",
60
+ "tsup": "^7.1.0",
61
+ "typescript": "^5.1.5",
60
62
  "vite": "^4.3.9",
61
63
  "vitest": "^0.32.2"
62
64
  },