electron-incremental-update 0.9.0 → 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.md CHANGED
@@ -1,137 +1,134 @@
1
- ## electron incremental updater
1
+ ## Electron Incremental Updater
2
2
 
3
- This project provide a vite plugin, `Updater` class and some useful functions to generate incremental update.
3
+ This project is based on [vite-plugin-electron](https://github.com/electron-vite/vite-plugin-electron), provide a plugin that build on top of `ElectronSimple`, an `Updater` class and some useful utils for Electron.
4
4
 
5
- There will be two asar in production, `app.asar` and `main.asar` (if "main" is your app's name).
5
+ There will be two asar in production, `app.asar` and `${name}.asar` (`electron.app.name`, also as the `name` field in `package.json`).
6
6
 
7
- The `app.asar` is used to load `main.asar` and initialize the `updater`. Also, all the **native modules**, which are set as `dependencies` in `package.json`, will be packaged into `app.asar` by `electron-builder`, [see usage](#use-native-modules).
7
+ The `app.asar` is used to load `${name}.asar` and initialize the `Updater`.
8
8
 
9
- The new `main.asar` downloaded from remote will be verified by presigned RSA + Signature. When pass the check and restart, the old `main.asar` will be replaced by the new one. Hooks like `beforeDoUpdate` are provided.
9
+ The new `${name}.asar`, which can download from remote or load from buffer, will be verified by `Updater` using presigned RSA + Signature. While passing the check and restart, the old `${name}.asar` will be replaced by the new one. Hooks like `beforeDoUpdate` are provided.
10
10
 
11
- - inspired by Obsidian's update strategy
11
+ All **native modules** should be packaged into `app.asar` to reduce `${name}.asar` file size, [see usage](#use-native-modules)
12
12
 
13
- ### notice
13
+ no `vite-plugin-electron-renderer` config
14
14
 
15
- - this plugin is developed with [vite-plugin-electron](https://github.com/electron-vite/vite-plugin-electron), and may be effect in other electron vite frameworks
16
- - **all options are documented in the jsdoc**
15
+ - inspired by [Obsidian](https://obsidian.md/)'s upgrade strategy
17
16
 
18
- ## install
17
+ ## Install
19
18
 
20
19
  ### npm
21
20
  ```bash
22
- npm install electron-incremental-update
21
+ npm install -D vite-plugin-electron electron-incremental-update
23
22
  ```
24
23
  ### yarn
25
24
  ```bash
26
- yarn add electron-incremental-update
25
+ yarn add -D vite-plugin-electron electron-incremental-update
27
26
  ```
28
27
  ### pnpm
29
28
  ```bash
30
- pnpm add electron-incremental-update
29
+ pnpm add -D vite-plugin-electron electron-incremental-update
31
30
  ```
32
31
 
33
- ## setup
32
+ ## Getting started
33
+
34
+ ### Project structure
34
35
 
35
36
  base on [electron-vite-vue](https://github.com/electron-vite/electron-vite-vue)
36
37
 
37
38
  ```
38
39
  electron
39
- ├── app.ts // <- add app entry file
40
- ├── electron-env.d.ts
40
+ ├── entry.ts // <- entry file
41
41
  ├── main
42
- ├── db.ts
43
- ├── index.ts
44
- └── preload
42
+ └── index.ts
43
+ ├── preload
44
+ └── index.ts
45
+ └── native // possible native modules
45
46
  └── index.ts
46
47
  src
47
48
  └── ...
48
49
  ```
49
50
 
50
- ### setup app
51
+ ### Setup entry
51
52
 
52
53
  ```ts
53
- // electron/app.ts
54
- import { initApp, parseGithubCdnURL } from 'electron-incremental-update'
55
- import { name, repository } from '../package.json'
54
+ // electron/entry.ts
55
+ import { initApp } from 'electron-incremental-update'
56
+ import { parseGithubCdnURL } from 'electron-incremental-update/utils'
57
+ import { repository } from '../package.json'
56
58
 
57
59
  const SIGNATURE_CERT = '' // auto generate certificate when start app
58
60
 
59
61
  initApp({ onStart: console.log })
60
- // can be updater option or function that return updater
61
62
  .setUpdater({
62
63
  SIGNATURE_CERT,
63
- productName: name,
64
- repository,
65
- updateJsonURL: parseGithubCdnURL(repository, '...', 'version.json'),
66
- releaseAsarURL: parseGithubCdnURL(repository, '...', `download/latest/${name}.asar.gz`),
67
- receiveBeta: true
64
+ // repository,
65
+ // updateJsonURL: parseGithubCdnURL(repository, 'https://your.cdn.url/', 'version.json'),
66
+ // releaseAsarURL: parseGithubCdnURL(repository, 'https://your.cdn.url/', `download/latest/${name}.asar.gz`),
67
+ // receiveBeta: true
68
68
  })
69
69
  ```
70
70
 
71
- - [some cdn resources](https://github.com/XIU2/UserScript/blob/master/GithubEnhanced-High-Speed-Download.user.js#L34):
71
+ - [some CDN resources](https://github.com/XIU2/UserScript/blob/master/GithubEnhanced-High-Speed-Download.user.js#L34):
72
72
 
73
- ### setup vite.config.ts
73
+ ### Setup `vite.config.ts`
74
74
 
75
- make sure the plugin is set in the **last** build task
75
+ All options are documented with JSDoc
76
76
 
77
- - for `vite-plugin-electron`, set it to `preload` (the second object in the plugin option array)
78
- - cert is read from `process.env.UPDATER_CERT` first, then read config
79
- - privatekey is read from `process.env.UPDATER_PK` first, then read config
77
+ - cert will read from `process.env.UPDATER_CERT` first, if absend, read config
78
+ - privatekey will read from `process.env.UPDATER_PK` first, if absend, read config
80
79
 
81
80
  ```ts
82
- // vite.config.ts
83
- export default defineConfig(({ command }) => {
84
- const isBuild = command === 'build'
85
- // ...
81
+ // vite.config.mts
82
+ import { defineConfig } from 'vite'
83
+ import { debugStartup, electronWithUpdater } from 'electron-incremental-update/vite'
84
+ import pkg from './package.json'
86
85
 
86
+ export default defineConfig(async ({ command }) => {
87
+ const isBuild = command === 'build'
87
88
  return {
88
89
  plugins: [
89
- electron([
90
- // main
91
- {
92
- // ...
90
+ electronWithUpdater({
91
+ pkg,
92
+ isBuild,
93
+ logParsedOptions: true,
94
+ main: {
95
+ files: ['./electron/main/index.ts', './electron/main/worker.ts'],
96
+ // see https://github.com/electron-vite/electron-vite-vue/blob/85ed267c4851bf59f32888d766c0071661d4b94c/vite.config.ts#L22-L28
97
+ onstart: debugStartup,
93
98
  },
94
- // preload
95
- {
96
- // ...
97
- vite: {
98
- plugins: [
99
- updater({
100
- productName: pkg.name,
101
- version: pkg.version,
102
- isBuild,
103
- }),
104
- ],
105
- // ...
106
- }
99
+ preload: {
100
+ files: './electron/preload/index.ts',
107
101
  },
108
- // when using vite-plugin-electron-renderer
109
- {
110
- // ...
102
+ updater: {
103
+ // options
111
104
  }
112
- ]),
113
- // ...
105
+ }),
114
106
  ],
115
- // ...
107
+ server: process.env.VSCODE_DEBUG && (() => {
108
+ const url = new URL(pkg.debug.env.VITE_DEV_SERVER_URL)
109
+ return {
110
+ host: url.hostname,
111
+ port: +url.port,
112
+ }
113
+ })(),
116
114
  }
117
115
  })
118
116
  ```
119
117
 
120
- ### modify package.json
118
+ ### Modify package.json
121
119
 
122
120
  ```json
123
121
  {
124
- // ...
125
- "main": "app.js" // <- app entry file path
122
+ "main": "dist-entry/entry.js" // <- entry file path
126
123
  }
127
124
  ```
128
125
 
129
- ### config electron-builder
126
+ ### Config electron-builder
130
127
 
131
128
  ```js
132
129
  const { name } = require('./package.json')
133
130
 
134
- const target = `${name}.asar`
131
+ const targetFile = `${name}.asar`
135
132
  /**
136
133
  * @type {import('electron-builder').Configuration}
137
134
  */
@@ -139,26 +136,21 @@ module.exports = {
139
136
  appId: 'YourAppID',
140
137
  productName: name,
141
138
  files: [
142
- 'app.js', // <- app entry file
143
- '!**/{.eslintignore,.eslintrc.cjs,.editorconfig,.prettierignore,.prettierrc.yaml,dev-app-update.yml,LICENSE,.nvmrc,.npmrc}',
144
- '!**/{tsconfig.json,tsconfig.node.json,tsconfig.web.json}',
145
- '!**/*debug*.*',
146
- '!**/*.{md,zip,map}',
147
- '!**/*.{c,cpp,h,hpp,cc,hh,cxx,hxx,gypi,gyp,sh}',
148
- '!**/.{github,vscode}',
149
- '!node_modules/**/better-sqlite3/deps/**',
139
+ // entry files
140
+ 'dist-entry',
150
141
  ],
142
+ npmRebuild: false,
151
143
  asarUnpack: [
152
- '**/*.{node,dll}',
144
+ '**/*.{node,dll,dylib,so}',
153
145
  ],
154
146
  directories: {
155
147
  output: 'release',
156
148
  },
157
149
  extraResources: [
158
- { from: `release/${target}`, to: target }, // <- asar file
150
+ { from: `release/${targetFile}`, to: targetFile }, // <- asar file
159
151
  ],
160
- publish: null, // <- disable publish
161
- // ...
152
+ // disable publish
153
+ publish: null,
162
154
  }
163
155
  ```
164
156
 
@@ -170,21 +162,23 @@ To use electron's `net` module for updating, the `checkUpdate` and `download` fu
170
162
 
171
163
  However, you have the option to customize the download function when creating the updater.
172
164
 
173
- **NOTE: There can only be one function and should be default export in the entry file**
165
+ **NOTE: There should only one function and should be default export in the entry file**
174
166
 
175
167
  ```ts
176
168
  // electron/main/index.ts
177
- import type { StartupWithUpdater, Updater } from 'electron-incremental-update'
178
- import { appInfo, getAppVersion, getElectronVersion, getProductAsarPath } from 'electron-incremental-update/utils'
169
+ import { startupWithUpdater } from 'electron-incremental-update'
170
+ import { getPathFromAppNameAsar, getVersions } from 'electron-incremental-update/utils'
179
171
  import { app } from 'electron'
180
- import { name } from '../../package.json'
181
172
 
182
- const startup: StartupWithUpdater = (updater: Updater) => {
173
+ export default startupWithUpdater((updater) => {
183
174
  await app.whenReady()
184
- console.log('\ncurrent:')
185
- console.log(`\tasar path: ${getProductAsarPath(name)}`)
186
- console.log(`\tapp: ${getAppVersion(name)}`)
187
- console.log(`\telectron: ${getElectronVersion()}`)
175
+
176
+ const { appVersion, electronVersion, entryVersion } = getVersions()
177
+ console.log(`${app.name}.asar path`, getPathFromAppNameAsar())
178
+ console.log('app version:', appVersion)
179
+ console.log('entry (installer) version', entryVersion)
180
+ console.log('electron version', electronVersion)
181
+
188
182
  updater.onDownloading = ({ percent }) => {
189
183
  console.log(percent)
190
184
  }
@@ -201,41 +195,95 @@ const startup: StartupWithUpdater = (updater: Updater) => {
201
195
  buttons: ['Download', 'Later'],
202
196
  message: 'Application update available!',
203
197
  })
204
- response === 0 && console.log(await updater.download())
198
+ if (response !== 0) {
199
+ return
200
+ }
201
+ const downloadResult = await updater.download()
202
+ if (downloadResult) {
203
+ updater.quitAndInstall()
204
+ }
205
205
  }
206
206
  })
207
- }
208
- export default startup
207
+ })
209
208
  ```
210
209
 
211
210
  ### use native modules
212
211
 
213
- the native modules is packed in `app.asar`, so you cannot directly access it when in production
212
+ All the **native modules** should be set as `dependency` in `package.json`. `electron-rebuild` only check dependencies inside `dependency` field.
214
213
 
215
- to use it, you can prebundle native modules, or use `requireNative` to load.
214
+ If you are using `electron-builder` to build distributions, all the native modules with its **large relavent `node_modiles`** will be packaged into `app.asar` by default. You can setup `nativeModuleEntryMap` option to prebundle all the native modules and skip bundled by `electron-builder`
215
+
216
+ in `vite.config.ts`
216
217
 
217
218
  ```ts
218
- // db.ts
219
- import { isNoSuchNativeModuleError, requireNative } from 'electron-incremental-update/utils'
219
+ const plugin = electronWithUpdater({
220
+ // options...
221
+ updater: {
222
+ entry: {
223
+ nativeModuleEntryMap: {
224
+ db: './electron/native/db.ts',
225
+ },
226
+ postBuild: async ({ existsAndCopyToEntryOutputDir }) => {
227
+ // for better-sqlite3
228
+ existsAndCopyToEntryOutputDir({
229
+ from: './node_modules/better-sqlite3/build/Release/better_sqlite3.node',
230
+ skipIfExist: false,
231
+ })
232
+ },
233
+ },
234
+ },
235
+ })
236
+ ```
220
237
 
221
- const Database = requireNative<typeof import('better-sqlite3')>('better-sqlite3')
222
- if (isNoSuchNativeModuleError(Database)) {
223
- // ...
224
- }
225
- const db = new Database(':memory:')
226
- db.exec(
227
- 'DROP TABLE IF EXISTS employees; '
238
+ in `electron/native/db.ts`
239
+
240
+ ```ts
241
+ import Database from 'better-sqlite3'
242
+ import { getPaths } from 'electron-incremental-update/utils'
243
+
244
+ const db = new Database(':memory:', { nativeBinding: getPaths().getPathFromEntryAsar('better_sqlite3.node') })
245
+
246
+ export function test() {
247
+ db.exec(
248
+ 'DROP TABLE IF EXISTS employees; '
228
249
  + 'CREATE TABLE IF NOT EXISTS employees (name TEXT, salary INTEGER)',
229
- )
250
+ )
230
251
 
231
- db.prepare('INSERT INTO employees VALUES (:n, :s)').run({
232
- n: 'James',
233
- s: 50000,
234
- })
252
+ db.prepare('INSERT INTO employees VALUES (:n, :s)').run({
253
+ n: 'James',
254
+ s: 5000,
255
+ })
235
256
 
236
- const r = db.prepare('SELECT * from employees').all()
237
- console.log(r)
238
- // [ { name: 'James', salary: 50000 } ]
257
+ const r = db.prepare('SELECT * from employees').all()
258
+ console.log(r)
259
+ // [ { name: 'James', salary: 50000 } ]
239
260
 
240
- db.close()
261
+ db.close()
262
+ }
241
263
  ```
264
+
265
+ in `electron/main/service.ts`
266
+
267
+ ```ts
268
+ import { loadNativeModuleFromEntry } from 'electron-incremental-update/utils'
269
+
270
+ const requireNative = loadNativeModuleFromEntry()
271
+
272
+ requireNative<typeof import('../native/db')>('db').test()
273
+ ```
274
+
275
+ in `electron-builder.config.js`
276
+
277
+ ```js
278
+ module.exports = {
279
+ files: [
280
+ 'dist-entry',
281
+ // exclude better-sqlite3 from electron-builder
282
+ '!node_modules/better-sqlite3/**',
283
+ ]
284
+ }
285
+ ```
286
+
287
+ ## License
288
+
289
+ MIT
@@ -6,32 +6,6 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
6
6
  throw Error('Dynamic require of "' + x + '" is not supported');
7
7
  });
8
8
 
9
- // src/utils/version.ts
10
- function parseVersion(version) {
11
- const semver = /^(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9\.-]+))?/i;
12
- const match = semver.exec(version);
13
- if (!match) {
14
- throw new TypeError(`invalid version: ${version}`);
15
- }
16
- const [major, minor, patch] = match.slice(1, 4).map(Number);
17
- const ret = {
18
- major,
19
- minor,
20
- patch,
21
- stage: "",
22
- stageVersion: -1
23
- };
24
- if (match[4]) {
25
- let [stage, _v] = match[4].split(".");
26
- ret.stage = stage;
27
- ret.stageVersion = Number(_v) || -1;
28
- }
29
- if (Number.isNaN(major) || Number.isNaN(minor) || Number.isNaN(patch) || Number.isNaN(ret.stageVersion)) {
30
- throw new TypeError(`invalid version: ${version}`);
31
- }
32
- return ret;
33
- }
34
-
35
9
  // src/utils/zip.ts
36
10
  import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
37
11
  import { gunzip, gzip } from "node:zlib";
@@ -67,9 +41,55 @@ async function zipFile(filePath, targetFilePath = `${filePath}.gz`) {
67
41
  });
68
42
  }
69
43
 
44
+ // src/utils/noDep.ts
45
+ function parseGithubCdnURL(originRepoURL, cdnPrefix, relativeFilePath) {
46
+ if (!originRepoURL.startsWith("https://github.com/")) {
47
+ throw new Error("origin url must start with https://github.com/");
48
+ }
49
+ originRepoURL = originRepoURL.trim().replace(/\/?$/, "/").trim();
50
+ relativeFilePath = relativeFilePath.trim().replace(/^\/|\/?$/g, "").trim();
51
+ cdnPrefix = cdnPrefix.trim().replace(/^\/?|\/?$/g, "").trim();
52
+ return originRepoURL.replace("github.com", cdnPrefix) + relativeFilePath;
53
+ }
54
+ function handleUnexpectedErrors(callback) {
55
+ process.on("uncaughtException", callback);
56
+ process.on("unhandledRejection", callback);
57
+ }
58
+ function parseVersion(version) {
59
+ const semver = /^(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9\.-]+))?/i;
60
+ const match = semver.exec(version);
61
+ if (!match) {
62
+ throw new TypeError(`invalid version: ${version}`);
63
+ }
64
+ const [major, minor, patch] = match.slice(1, 4).map(Number);
65
+ const ret = {
66
+ major,
67
+ minor,
68
+ patch,
69
+ stage: "",
70
+ stageVersion: -1
71
+ };
72
+ if (match[4]) {
73
+ let [stage, _v] = match[4].split(".");
74
+ ret.stage = stage;
75
+ ret.stageVersion = Number(_v) || -1;
76
+ }
77
+ if (Number.isNaN(major) || Number.isNaN(minor) || Number.isNaN(patch) || Number.isNaN(ret.stageVersion)) {
78
+ throw new TypeError(`invalid version: ${version}`);
79
+ }
80
+ return ret;
81
+ }
82
+ function isUpdateJSON(json) {
83
+ const is = (j) => !!(j && j.minimumVersion && j.signature && j.size && j.version);
84
+ return is(json) && is(json?.beta);
85
+ }
86
+
70
87
  export {
71
88
  __require,
72
- parseVersion,
73
89
  unzipFile,
74
- zipFile
90
+ zipFile,
91
+ parseGithubCdnURL,
92
+ handleUnexpectedErrors,
93
+ parseVersion,
94
+ isUpdateJSON
75
95
  };
@@ -30,14 +30,7 @@ var verify = (buffer, signature2, cert) => {
30
30
  }
31
31
  };
32
32
 
33
- // src/updateJson.ts
34
- function isUpdateJSON(json) {
35
- const is = (j) => "signature" in j && "version" in j && "size" in j && "minimumVersion" in j;
36
- return is(json) && "beta" in json && is(json.beta);
37
- }
38
-
39
33
  export {
40
34
  signature,
41
- verify,
42
- isUpdateJSON
35
+ verify
43
36
  };
@@ -0,0 +1,148 @@
1
+ import {
2
+ __require
3
+ } from "./chunk-GB6VLKJZ.mjs";
4
+
5
+ // src/utils/electron.ts
6
+ import { existsSync, mkdirSync, readFileSync } from "node:fs";
7
+ import { basename, dirname, join } from "node:path";
8
+ import { release } from "node:os";
9
+ import { app } from "electron";
10
+ var is = {
11
+ dev: !app.isPackaged,
12
+ win: process.platform === "win32",
13
+ mac: process.platform === "darwin",
14
+ linux: process.platform === "linux"
15
+ };
16
+ function getPathFromAppNameAsar(...path) {
17
+ return is.dev ? "DEV.asar" : join(dirname(app.getAppPath()), `${app.name}.asar`, ...path);
18
+ }
19
+ function getVersions() {
20
+ const platform = is.win ? "Windows" : is.mac ? "MacOS" : process.platform.toUpperCase();
21
+ return {
22
+ appVersion: is.dev ? app.getVersion() : readFileSync(getPathFromAppNameAsar("version"), "utf-8"),
23
+ entryVersion: app.getVersion(),
24
+ electronVersion: process.versions.electron,
25
+ nodeVersion: process.versions.node,
26
+ systemVersion: `${platform} ${release()}`
27
+ };
28
+ }
29
+ function loadNativeModuleFromEntry(devEntryDirPath = "../../dist-entry", entryDirPath = join(app.getAppPath(), basename(devEntryDirPath))) {
30
+ const path = is.dev ? devEntryDirPath : entryDirPath;
31
+ return (moduleName) => {
32
+ try {
33
+ return __require(join(path, moduleName));
34
+ } catch (error) {
35
+ console.error("fail to load module", error);
36
+ }
37
+ };
38
+ }
39
+ function restartApp() {
40
+ app.relaunch();
41
+ app.quit();
42
+ }
43
+ function setAppUserModelId(id) {
44
+ app.setAppUserModelId(is.dev ? process.execPath : id ?? `org.${app.name}`);
45
+ }
46
+ function disableHWAccForWin7() {
47
+ if (release().startsWith("6.1")) {
48
+ app.disableHardwareAcceleration();
49
+ }
50
+ }
51
+ function singleInstance(window) {
52
+ const result = app.requestSingleInstanceLock();
53
+ result ? app.on("second-instance", () => {
54
+ if (window) {
55
+ window.show();
56
+ if (window.isMinimized()) {
57
+ window.restore();
58
+ }
59
+ window.focus();
60
+ }
61
+ }) : app.quit();
62
+ return result;
63
+ }
64
+ function setPortableAppDataPath(dirName = "data") {
65
+ const portablePath = join(dirname(app.getPath("exe")), dirName);
66
+ if (!existsSync(portablePath)) {
67
+ mkdirSync(portablePath);
68
+ }
69
+ app.setPath("appData", portablePath);
70
+ }
71
+ function waitAppReady(timeout = 1e3) {
72
+ return app.isReady() ? Promise.resolve() : new Promise((resolve, reject) => {
73
+ const _ = setTimeout(() => {
74
+ reject(new Error("app is not ready"));
75
+ }, timeout);
76
+ app.whenReady().then(() => {
77
+ clearTimeout(_);
78
+ resolve();
79
+ });
80
+ });
81
+ }
82
+ function getPaths(entryDirName = "dist-entry") {
83
+ const root = join(__dirname, "..");
84
+ const mainDirPath = join(root, "main");
85
+ const preloadDirPath = join(root, "preload");
86
+ const rendererDirPath = join(root, "renderer");
87
+ const devServerURL = process.env.VITE_DEV_SERVER_URL;
88
+ const indexHTMLPath = join(rendererDirPath, "index.html");
89
+ const publicDirPath = devServerURL ? join(root, "../public") : rendererDirPath;
90
+ return {
91
+ /**
92
+ * @example
93
+ * ```ts
94
+ * devServerURL && win.loadURL(devServerURL)
95
+ * ```
96
+ */
97
+ devServerURL,
98
+ /**
99
+ * @example
100
+ * ```ts
101
+ * win.loadFile(indexHTMLPath)
102
+ * ```
103
+ */
104
+ indexHTMLPath,
105
+ /**
106
+ * get path inside entry asar
107
+ * @param paths joined path
108
+ */
109
+ getPathFromEntryAsar(...paths) {
110
+ return join(app.getAppPath(), entryDirName, ...paths);
111
+ },
112
+ /**
113
+ * get path inside `${app.name}.asar/main`
114
+ * @param paths joined path
115
+ */
116
+ getPathFromMain(...paths) {
117
+ return join(mainDirPath, ...paths);
118
+ },
119
+ /**
120
+ * get path inside `${app.name}.asar/preload`
121
+ * @param paths joined path
122
+ */
123
+ getPathFromPreload(...paths) {
124
+ return join(preloadDirPath, ...paths);
125
+ },
126
+ /**
127
+ * get path inside public dir
128
+ * @param paths joined path
129
+ */
130
+ getPathFromPublic(...paths) {
131
+ return join(publicDirPath, ...paths);
132
+ }
133
+ };
134
+ }
135
+
136
+ export {
137
+ is,
138
+ getPathFromAppNameAsar,
139
+ getVersions,
140
+ loadNativeModuleFromEntry,
141
+ restartApp,
142
+ setAppUserModelId,
143
+ disableHWAccForWin7,
144
+ singleInstance,
145
+ setPortableAppDataPath,
146
+ waitAppReady,
147
+ getPaths
148
+ };