@dimensional-innovations/electron-background 2.0.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 ADDED
@@ -0,0 +1,109 @@
1
+ # @dimensional-innovations/electron-background
2
+
3
+ electron-background aims to simplify electron apps by providing a simple api to customize the electron application.
4
+
5
+ ## Getting Started
6
+
7
+ ### Install
8
+ Add the package using npm or yarn:
9
+ ```bash
10
+ npm install @dimensional-innovations/electron-background
11
+ ```
12
+
13
+ ```bash
14
+ yarn add @dimensional-innovations/electron-background
15
+ ```
16
+
17
+ ### Setup for Vue CLI / Webpack
18
+
19
+ If you are using the Vue CLI, add the following to your main or background file.
20
+ ```typescript
21
+ import { AutoUpdater, DevTools, VueElectronSettings, init, KioskBrowserWindow, PrivilegedSchemes, StaticFileDir, TouchEvents, VueElectronVersion } from '@dimensional-innovations/electron-background';
22
+ import { config } from '../package';
23
+
24
+ init({
25
+ appUrl: process.env.WEBPACK_DEV_URL ? process.env.WEBPACK_DEV_URL : 'app://index.html',
26
+ config,
27
+ plugins: [
28
+ new AutoUpdater(),
29
+ new DevTools(),
30
+ new KioskBrowserWindow(),
31
+ new PrivilegedSchemes(['app']),
32
+ new StaticFileDir('app', __dirname),
33
+ new TouchEvents(),
34
+ new VueElectronSettings(),
35
+ new VueElectronVersion()
36
+ ]
37
+ });
38
+ ```
39
+
40
+ ### Setup for Vite
41
+
42
+ If you are using Vite, add the following to your main or background file.
43
+ ```typescript
44
+ import { AutoUpdater, DevTools, VueElectronSettings, init, KioskBrowserWindow, PrivilegedSchemes, TouchEvents, VueElectronVersion } from '@dimensional-innovations/electron-background';
45
+ import { app } from 'electron';
46
+ import { config } from '../package';
47
+
48
+ init({
49
+ appUrl: process.env['ELECTRON_RENDERER_URL']
50
+ ? process.env['ELECTRON_RENDERER_URL']
51
+ : `file://${join(__dirname, '../renderer/index.html')}`,
52
+ config,
53
+ plugins: [
54
+ new AutoUpdater(),
55
+ new DevTools(),
56
+ new KioskBrowserWindow(),
57
+ new TouchEvents(),
58
+ new VueElectronSettings(),
59
+ new VueElectronVersion()
60
+ ]
61
+ });
62
+ ```
63
+
64
+ ## Plugins
65
+
66
+ By default, the init method creates a BrowserWindow and loads the specified application into the window. All other features of an application are managed through plugins. Each plugin customizes the application instance during the init process. The built-in plugins are listed below.
67
+
68
+ If a feature you need isn't listed below, you can still add it to the init script by defining the plugin in your application. For example, if we wanted to customize the autoplay policy flag in electron, we would add the following to our init method.
69
+ ```typescript
70
+ import { init } from '@dimensional-innovations/vue-electron-background';
71
+ import { app } from 'electron';
72
+ import { config } from '../package';
73
+
74
+ init({
75
+ appUrl: ...,
76
+ config,
77
+ plugins: [
78
+ ...,
79
+ { beforeReady: () => app.commandLine.appendSwitch('autoplay-policy', 'no-user-gesture-required') }
80
+ ...,
81
+ ]
82
+ });
83
+ ```
84
+
85
+ ### AutoUpdater
86
+ Starts the auto update process, checking for updates every 3 minutes and automatically installing the update once one is found.
87
+
88
+ For more info, see https://www.electron.build/auto-update
89
+
90
+ ### DevTools
91
+ Installs dev tools extensions and opens the devTools panel.
92
+
93
+ ### KioskBrowserWindow
94
+ Enables kiosk mode in the BrowserWindow when the application is packaged.
95
+
96
+ ### BetterUptimeHeartbeat
97
+ Starts a heartbeat, which reports uptime to betteruptime.com. Requires that "heartbeatApiKey" is set in the settings.
98
+
99
+ ### PrivilegedSchemes
100
+ Registers schemes as privileged.
101
+
102
+ ### StaticFileDir
103
+ Registers a custom scheme to serve static files.
104
+
105
+ ### TouchEvents
106
+ Enables touch events in the app.
107
+
108
+ ## API Reference
109
+ For the complete API exported by this package, see API.md
@@ -0,0 +1,18 @@
1
+ import { InitContext, InitPlugin } from './init';
2
+ /**
3
+ * Starts the auto update process, checking for updates every 3 minutes
4
+ * and automatically installing the update once one is found.
5
+ *
6
+ * For more info, see https://www.electron.build/auto-update
7
+ */
8
+ export declare class AutoUpdater implements InitPlugin {
9
+ private readonly enabled;
10
+ /**
11
+ * @constructor
12
+ *
13
+ * @param enabled - Indicates if the plugin is enabled. Used to disable the plugin in development. Defaults to `app.isPackaged`.
14
+ */
15
+ constructor(enabled?: boolean);
16
+ afterLoad(context: InitContext): Promise<void>;
17
+ private startAutoUpdater;
18
+ }
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.AutoUpdater = void 0;
16
+ const electron_updater_1 = require("electron-updater");
17
+ const electron_log_1 = __importDefault(require("electron-log"));
18
+ const electron_1 = require("electron");
19
+ /**
20
+ * Starts the auto update process, checking for updates every 3 minutes
21
+ * and automatically installing the update once one is found.
22
+ *
23
+ * For more info, see https://www.electron.build/auto-update
24
+ */
25
+ class AutoUpdater {
26
+ /**
27
+ * @constructor
28
+ *
29
+ * @param enabled - Indicates if the plugin is enabled. Used to disable the plugin in development. Defaults to `app.isPackaged`.
30
+ */
31
+ constructor(enabled = electron_1.app.isPackaged) {
32
+ this.enabled = enabled;
33
+ }
34
+ afterLoad(context) {
35
+ return __awaiter(this, void 0, void 0, function* () {
36
+ const { autoUpdaterChannel } = context.config;
37
+ if (this.enabled && autoUpdaterChannel) {
38
+ this.startAutoUpdater(autoUpdaterChannel);
39
+ }
40
+ else if (this.enabled) {
41
+ context.log.warn('autoUpdaterChannel was not set in the settings. AutoUpdater was not started.');
42
+ }
43
+ });
44
+ }
45
+ startAutoUpdater(autoUpdaterChannel) {
46
+ const threeMinutes = 180000;
47
+ electron_updater_1.autoUpdater.channel = autoUpdaterChannel;
48
+ electron_updater_1.autoUpdater.autoDownload = true;
49
+ electron_updater_1.autoUpdater.logger = electron_log_1.default;
50
+ electron_updater_1.autoUpdater.on('update-downloaded', () => {
51
+ electron_log_1.default.info('Updated app downloaded... restarting');
52
+ // https://www.electron.build/auto-update#module_electron-updater.AppUpdater+quitAndInstall
53
+ electron_updater_1.autoUpdater.quitAndInstall(true, true);
54
+ });
55
+ electron_updater_1.autoUpdater.on('update-not-available', () => {
56
+ electron_log_1.default.info('No update available. Will try again soon.');
57
+ });
58
+ // check for updates now and then every 3 minutes
59
+ electron_updater_1.autoUpdater.checkForUpdates();
60
+ setInterval(() => {
61
+ electron_updater_1.autoUpdater.checkForUpdates();
62
+ }, threeMinutes);
63
+ }
64
+ }
65
+ exports.AutoUpdater = AutoUpdater;
@@ -0,0 +1,48 @@
1
+ import { BrowserWindowConstructorOptions } from "electron";
2
+ import { InitContext, InitPlugin } from "./init";
3
+ /**
4
+ * Applies default options to the browser window. If `appHeight`, `appWidth`, or `backgroundColor` are included in
5
+ * app config, they will be added to the window options as well.
6
+ */
7
+ export declare class DefaultBrowserWindow implements InitPlugin {
8
+ protected readonly options: BrowserWindowConstructorOptions;
9
+ /**
10
+ * @constructor
11
+ *
12
+ * @param options - Additional options to apply to the browser window.
13
+ */
14
+ constructor(options?: BrowserWindowConstructorOptions);
15
+ afterReady(context: InitContext): Promise<void>;
16
+ protected getDefaultWindowOptions(): BrowserWindowConstructorOptions;
17
+ protected getWindowOptionsFromConfig({ appHeight, appWidth, backgroundColor }: Record<string, string | number | boolean>): BrowserWindowConstructorOptions;
18
+ }
19
+ /**
20
+ * Enables kiosk mode in the BrowserWindow when the application is packaged.
21
+ */
22
+ export declare class KioskBrowserWindow extends DefaultBrowserWindow {
23
+ private readonly enabled;
24
+ /**
25
+ * @constructor
26
+ *
27
+ * @param options - Additional options to apply to the BrowserWindow.
28
+ * @param enableKioskMode - Indicates if the plugin is enabled. Used to disabled kiosk mode in development. Defaults to `app.isPackaged`
29
+ */
30
+ constructor(options?: BrowserWindowConstructorOptions, enabled?: boolean);
31
+ protected getDefaultWindowOptions(): BrowserWindowConstructorOptions;
32
+ }
33
+ /**
34
+ * Ensures the browser window will always be fullscreen. Generally, KioskBrowserWindow is preferred
35
+ * over this plugin, but there are times when app cannot be in kiosk mode.
36
+ */
37
+ export declare class FullScreenBrowserWindow extends DefaultBrowserWindow {
38
+ private readonly enabled;
39
+ /**
40
+ * @constructor
41
+ *
42
+ * @param options - Additional options to apply to the BrowserWindow.
43
+ * @param enabled - Indicates if the plugin is enabled. Used to disable the plugin in development. Defaults to `app.isPackaged`.
44
+ */
45
+ constructor(options?: BrowserWindowConstructorOptions, enabled?: boolean);
46
+ beforeLoad(context: InitContext): Promise<void>;
47
+ protected getDefaultWindowOptions(): BrowserWindowConstructorOptions;
48
+ }
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
+ return new (P || (P = Promise))(function (resolve, reject) {
28
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
33
+ };
34
+ Object.defineProperty(exports, "__esModule", { value: true });
35
+ exports.FullScreenBrowserWindow = exports.KioskBrowserWindow = exports.DefaultBrowserWindow = void 0;
36
+ const electron_1 = require("electron");
37
+ /**
38
+ * Applies default options to the browser window. If `appHeight`, `appWidth`, or `backgroundColor` are included in
39
+ * app config, they will be added to the window options as well.
40
+ */
41
+ class DefaultBrowserWindow {
42
+ /**
43
+ * @constructor
44
+ *
45
+ * @param options - Additional options to apply to the browser window.
46
+ */
47
+ constructor(options = {}) {
48
+ this.options = options;
49
+ }
50
+ afterReady(context) {
51
+ return __awaiter(this, void 0, void 0, function* () {
52
+ context.browserWindowOptions = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, context.browserWindowOptions), this.getDefaultWindowOptions()), this.getWindowOptionsFromConfig(context.config)), this.options), { closable: true });
53
+ });
54
+ }
55
+ getDefaultWindowOptions() {
56
+ return {
57
+ webPreferences: {
58
+ nodeIntegration: true,
59
+ contextIsolation: false,
60
+ webSecurity: false
61
+ }
62
+ };
63
+ }
64
+ getWindowOptionsFromConfig({ appHeight, appWidth, backgroundColor }) {
65
+ const options = {};
66
+ if (appHeight !== undefined && typeof appHeight === 'number') {
67
+ options.height = appHeight;
68
+ }
69
+ if (appWidth !== undefined && typeof appWidth === 'number') {
70
+ options.width = appWidth;
71
+ }
72
+ if (backgroundColor !== undefined && typeof backgroundColor === 'string') {
73
+ options.backgroundColor = backgroundColor;
74
+ }
75
+ return options;
76
+ }
77
+ }
78
+ exports.DefaultBrowserWindow = DefaultBrowserWindow;
79
+ /**
80
+ * Enables kiosk mode in the BrowserWindow when the application is packaged.
81
+ */
82
+ class KioskBrowserWindow extends DefaultBrowserWindow {
83
+ /**
84
+ * @constructor
85
+ *
86
+ * @param options - Additional options to apply to the BrowserWindow.
87
+ * @param enableKioskMode - Indicates if the plugin is enabled. Used to disabled kiosk mode in development. Defaults to `app.isPackaged`
88
+ */
89
+ constructor(options = {}, enabled = electron_1.app.isPackaged) {
90
+ super(options);
91
+ this.enabled = enabled;
92
+ }
93
+ getDefaultWindowOptions() {
94
+ if (!this.enabled) {
95
+ return super.getDefaultWindowOptions();
96
+ }
97
+ return Object.assign(Object.assign({}, super.getDefaultWindowOptions()), { acceptFirstMouse: true, alwaysOnTop: true, autoHideMenuBar: true, fullscreen: true, kiosk: true, minimizable: false, movable: false, x: 0, y: 0 });
98
+ }
99
+ }
100
+ exports.KioskBrowserWindow = KioskBrowserWindow;
101
+ /**
102
+ * Ensures the browser window will always be fullscreen. Generally, KioskBrowserWindow is preferred
103
+ * over this plugin, but there are times when app cannot be in kiosk mode.
104
+ */
105
+ class FullScreenBrowserWindow extends DefaultBrowserWindow {
106
+ /**
107
+ * @constructor
108
+ *
109
+ * @param options - Additional options to apply to the BrowserWindow.
110
+ * @param enabled - Indicates if the plugin is enabled. Used to disable the plugin in development. Defaults to `app.isPackaged`.
111
+ */
112
+ constructor(options = {}, enabled = electron_1.app.isPackaged) {
113
+ super(options);
114
+ this.enabled = enabled;
115
+ }
116
+ beforeLoad(context) {
117
+ return __awaiter(this, void 0, void 0, function* () {
118
+ if (!this.enabled) {
119
+ return;
120
+ }
121
+ const browserWindow = context.browserWindow;
122
+ if (!browserWindow) {
123
+ context.log.error('browserWindow is undefined');
124
+ return;
125
+ }
126
+ const { screen } = yield Promise.resolve().then(() => __importStar(require('electron')));
127
+ const resizeWindow = () => {
128
+ browserWindow.setBounds(screen.getPrimaryDisplay().bounds);
129
+ };
130
+ resizeWindow();
131
+ screen.on('display-added', resizeWindow);
132
+ screen.on('display-metrics-changed', resizeWindow);
133
+ screen.on('display-removed', resizeWindow);
134
+ });
135
+ }
136
+ getDefaultWindowOptions() {
137
+ if (!this.enabled) {
138
+ return super.getDefaultWindowOptions();
139
+ }
140
+ return Object.assign(Object.assign({}, super.getDefaultWindowOptions()), { alwaysOnTop: true, resizable: false, movable: false, frame: false });
141
+ }
142
+ }
143
+ exports.FullScreenBrowserWindow = FullScreenBrowserWindow;
@@ -0,0 +1,18 @@
1
+ import { VUEJS3_DEVTOOLS } from 'electron-devtools-installer';
2
+ import { InitContext, InitPlugin } from './init';
3
+ export type Extension = string | typeof VUEJS3_DEVTOOLS;
4
+ /**
5
+ * Installs dev tools extensions and opens the devTools panel.
6
+ */
7
+ export declare class DevTools implements InitPlugin {
8
+ private readonly devTools;
9
+ private readonly enabled;
10
+ /**
11
+ * @constructor
12
+ *
13
+ * @param devTools - The extensions to install.
14
+ * @param enabled - Indicates if the plugin is enabled. Used to disable the plugin when the app is packaged. Defaults to `!app.isPackaged`.
15
+ */
16
+ constructor(devTools: Array<Extension>, enabled?: boolean);
17
+ beforeLoad(context: InitContext): Promise<void>;
18
+ }
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.DevTools = void 0;
16
+ const electron_1 = require("electron");
17
+ const electron_devtools_installer_1 = __importDefault(require("electron-devtools-installer"));
18
+ /**
19
+ * Installs dev tools extensions and opens the devTools panel.
20
+ */
21
+ class DevTools {
22
+ /**
23
+ * @constructor
24
+ *
25
+ * @param devTools - The extensions to install.
26
+ * @param enabled - Indicates if the plugin is enabled. Used to disable the plugin when the app is packaged. Defaults to `!app.isPackaged`.
27
+ */
28
+ constructor(devTools, enabled = !electron_1.app.isPackaged) {
29
+ this.devTools = devTools;
30
+ this.enabled = enabled;
31
+ }
32
+ beforeLoad(context) {
33
+ return __awaiter(this, void 0, void 0, function* () {
34
+ if (!this.enabled) {
35
+ return;
36
+ }
37
+ try {
38
+ yield (0, electron_devtools_installer_1.default)(this.devTools);
39
+ }
40
+ catch (error) {
41
+ console.error(`Failed to install dev tools: ${this.devTools.join(',')}`);
42
+ console.error(error);
43
+ }
44
+ if (context.browserWindow) {
45
+ const browserWindow = context.browserWindow;
46
+ browserWindow.on('ready-to-show', () => browserWindow.webContents.openDevTools());
47
+ }
48
+ });
49
+ }
50
+ }
51
+ exports.DevTools = DevTools;
@@ -0,0 +1,59 @@
1
+ import { InitContext, InitPlugin } from './init';
2
+ /**
3
+ * Options used to define the request used to monitor the app.
4
+ */
5
+ export interface HeartbeatOptions {
6
+ /**
7
+ * The url to send a request to. If a function is provided, it is called with the current context and a request
8
+ * is sent to the returned value.
9
+ */
10
+ url: string | ((context: InitContext) => string);
11
+ /**
12
+ * The amount of time in milliseconds between requests.
13
+ */
14
+ pollInterval: number;
15
+ }
16
+ /**
17
+ * Starts a "heartbeat", which sends a request to the provided url on an interval.
18
+ */
19
+ export declare class Heartbeat implements InitPlugin {
20
+ private readonly options;
21
+ private readonly enabled;
22
+ /**
23
+ * @constructor
24
+ *
25
+ * @param options - Options that define the url used to monitor the app.
26
+ * @param enabled - Indicates if the plugin is enabled. Used to disable the plugin during development. Defaults to `app.isPackaged`.
27
+ */
28
+ constructor(options: HeartbeatOptions, enabled?: boolean);
29
+ afterLoad(context: InitContext): Promise<void>;
30
+ }
31
+ export interface BetterStackHeartbeatOptions {
32
+ /**
33
+ * The unique key for the app, obtained from betterstack.com
34
+ */
35
+ heartbeatApiKey?: string;
36
+ /**
37
+ * If true sends a request to https://uptime.betterstack.com. Otherwise sends a request to https://betteruptiume.com.
38
+ */
39
+ isBetterStack?: boolean;
40
+ /**
41
+ * The amount of time in milliseconds between requests.
42
+ */
43
+ pollInterval?: number;
44
+ }
45
+ /**
46
+ * Starts a "heartbeat" by sending a request to https://betterstack.com on an interval. Requires the apiKey to be in the app config.
47
+ */
48
+ export declare class BetterStackHeartbeat implements InitPlugin {
49
+ private readonly enabled;
50
+ private readonly options;
51
+ /**
52
+ * @constructor
53
+ *
54
+ * @param enabled - Indicates if the plugin is enabled. Used to disable the plugin during development. Defaults to `app.isPackaged`.
55
+ * @param options - Options that used to start the heartbeat.
56
+ */
57
+ constructor(enabled?: boolean, options?: BetterStackHeartbeatOptions);
58
+ afterLoad(context: InitContext): Promise<void>;
59
+ }
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.BetterStackHeartbeat = exports.Heartbeat = void 0;
16
+ const electron_1 = require("electron");
17
+ const axios_1 = __importDefault(require("axios"));
18
+ /**
19
+ * Starts a "heartbeat", which sends a request to the provided url on an interval.
20
+ */
21
+ class Heartbeat {
22
+ /**
23
+ * @constructor
24
+ *
25
+ * @param options - Options that define the url used to monitor the app.
26
+ * @param enabled - Indicates if the plugin is enabled. Used to disable the plugin during development. Defaults to `app.isPackaged`.
27
+ */
28
+ constructor(options, enabled = electron_1.app.isPackaged) {
29
+ this.options = options;
30
+ this.enabled = enabled;
31
+ }
32
+ afterLoad(context) {
33
+ return __awaiter(this, void 0, void 0, function* () {
34
+ if (!this.enabled)
35
+ return;
36
+ const url = typeof this.options.url === 'string'
37
+ ? this.options.url
38
+ : this.options.url(context);
39
+ new NodeHeartbeat(url, this.options.pollInterval).start();
40
+ });
41
+ }
42
+ }
43
+ exports.Heartbeat = Heartbeat;
44
+ /**
45
+ * Starts a "heartbeat" by sending a request to https://betterstack.com on an interval. Requires the apiKey to be in the app config.
46
+ */
47
+ class BetterStackHeartbeat {
48
+ /**
49
+ * @constructor
50
+ *
51
+ * @param enabled - Indicates if the plugin is enabled. Used to disable the plugin during development. Defaults to `app.isPackaged`.
52
+ * @param options - Options that used to start the heartbeat.
53
+ */
54
+ constructor(enabled = electron_1.app.isPackaged, options = {}) {
55
+ this.enabled = enabled;
56
+ this.options = options;
57
+ }
58
+ afterLoad(context) {
59
+ return __awaiter(this, void 0, void 0, function* () {
60
+ const heartbeatApiKey = context.config.heartbeatApiKey || this.options.heartbeatApiKey;
61
+ if (this.enabled && heartbeatApiKey && typeof heartbeatApiKey === 'string') {
62
+ const url = this.options.isBetterStack
63
+ ? `https://uptime.betterstack.com/api/v1/heartbeat/${heartbeatApiKey}`
64
+ : `https://betteruptime.com/api/v1/heartbeat/${heartbeatApiKey}`;
65
+ new NodeHeartbeat(url, this.options.pollInterval || 30000).start();
66
+ }
67
+ else if (this.enabled) {
68
+ context.log.warn('heartbeatApiKey was not in the settings. Heartbeat was not started.');
69
+ }
70
+ });
71
+ }
72
+ }
73
+ exports.BetterStackHeartbeat = BetterStackHeartbeat;
74
+ class NodeHeartbeat {
75
+ constructor(url, pollInterval) {
76
+ this.url = url;
77
+ this.pollInterval = pollInterval;
78
+ }
79
+ start() {
80
+ this.poll();
81
+ this.interval = setInterval(() => this.poll(), this.pollInterval);
82
+ }
83
+ stop() {
84
+ clearInterval(this.interval);
85
+ }
86
+ poll() {
87
+ return axios_1.default.head(this.url);
88
+ }
89
+ }
@@ -0,0 +1,14 @@
1
+ import { InitPlugin } from './init';
2
+ /**
3
+ * Registers schemes as privileged.
4
+ */
5
+ export declare class PrivilegedSchemes implements InitPlugin {
6
+ private readonly schemes;
7
+ /**
8
+ * @constructor
9
+ *
10
+ * @param schemes - The schemes to register as privileged.
11
+ */
12
+ constructor(schemes: Array<string>);
13
+ beforeReady(): Promise<void>;
14
+ }
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.PrivilegedSchemes = void 0;
13
+ const electron_1 = require("electron");
14
+ /**
15
+ * Registers schemes as privileged.
16
+ */
17
+ class PrivilegedSchemes {
18
+ /**
19
+ * @constructor
20
+ *
21
+ * @param schemes - The schemes to register as privileged.
22
+ */
23
+ constructor(schemes) {
24
+ this.schemes = schemes;
25
+ }
26
+ beforeReady() {
27
+ return __awaiter(this, void 0, void 0, function* () {
28
+ const customSchemes = this.schemes
29
+ .map((scheme) => ({
30
+ scheme,
31
+ privileges: { secure: true, standard: true, supportFetchAPI: true },
32
+ }));
33
+ electron_1.protocol.registerSchemesAsPrivileged(customSchemes);
34
+ });
35
+ }
36
+ }
37
+ exports.PrivilegedSchemes = PrivilegedSchemes;
@@ -0,0 +1,10 @@
1
+ import { InitContext, InitPlugin } from './init';
2
+ /**
3
+ * Enforces that only a single instance of the app can run at the same time.
4
+ * If a second instance of the is opened, the second instance is closed and
5
+ * the first instance is brought back into focus.
6
+ */
7
+ export declare class SingleInstance implements InitPlugin {
8
+ beforeReady(): Promise<void>;
9
+ afterReady(context: InitContext): Promise<void>;
10
+ }
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.SingleInstance = void 0;
13
+ const electron_1 = require("electron");
14
+ /**
15
+ * Enforces that only a single instance of the app can run at the same time.
16
+ * If a second instance of the is opened, the second instance is closed and
17
+ * the first instance is brought back into focus.
18
+ */
19
+ class SingleInstance {
20
+ beforeReady() {
21
+ return __awaiter(this, void 0, void 0, function* () {
22
+ if (electron_1.app.requestSingleInstanceLock())
23
+ return;
24
+ electron_1.app.quit();
25
+ process.exit(0);
26
+ });
27
+ }
28
+ afterReady(context) {
29
+ return __awaiter(this, void 0, void 0, function* () {
30
+ electron_1.app.on('second-instance', () => {
31
+ if (!context.browserWindow)
32
+ return;
33
+ if (context.browserWindow.isMinimized()) {
34
+ context.browserWindow.restore();
35
+ }
36
+ else {
37
+ context.browserWindow.focus();
38
+ }
39
+ });
40
+ });
41
+ }
42
+ }
43
+ exports.SingleInstance = SingleInstance;
@@ -0,0 +1,26 @@
1
+ import { InitPlugin } from './init';
2
+ /**
3
+ * Registers a custom scheme to serve static files.
4
+ */
5
+ export declare class StaticFileDir implements InitPlugin {
6
+ private readonly scheme;
7
+ private readonly dir;
8
+ /**
9
+ * @constructor
10
+ *
11
+ * @param scheme - The scheme to serve the files from.
12
+ * @param dir - The directory where the static files are located.
13
+ */
14
+ constructor(scheme: string, dir: string);
15
+ afterReady(): Promise<void>;
16
+ }
17
+ /**
18
+ * Wrapper around `protocol.registerFileProtocol` that serves files from the given source directory.
19
+ * The handler will convert any url with the custom scheme to a file path in the source directory in
20
+ * order to find the file to serve. For example, with "media" passed in for scheme and `/public` passed
21
+ * in for sourceDirectory "media://videos/demo.mp4" would resolve to "/public/media/videos/demo.mp4".
22
+ *
23
+ * @param scheme - The scheme to register.
24
+ * @param sourceDirectory - The directory where files are served from.
25
+ */
26
+ export declare function createFileProtocol(scheme: string, sourceDirectory: string): void;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.createFileProtocol = exports.StaticFileDir = void 0;
13
+ const electron_1 = require("electron");
14
+ const path_1 = require("path");
15
+ /**
16
+ * Registers a custom scheme to serve static files.
17
+ */
18
+ class StaticFileDir {
19
+ /**
20
+ * @constructor
21
+ *
22
+ * @param scheme - The scheme to serve the files from.
23
+ * @param dir - The directory where the static files are located.
24
+ */
25
+ constructor(scheme, dir) {
26
+ this.scheme = scheme;
27
+ this.dir = dir;
28
+ }
29
+ afterReady() {
30
+ return __awaiter(this, void 0, void 0, function* () {
31
+ createFileProtocol(this.scheme, this.dir);
32
+ });
33
+ }
34
+ }
35
+ exports.StaticFileDir = StaticFileDir;
36
+ /**
37
+ * Wrapper around `protocol.registerFileProtocol` that serves files from the given source directory.
38
+ * The handler will convert any url with the custom scheme to a file path in the source directory in
39
+ * order to find the file to serve. For example, with "media" passed in for scheme and `/public` passed
40
+ * in for sourceDirectory "media://videos/demo.mp4" would resolve to "/public/media/videos/demo.mp4".
41
+ *
42
+ * @param scheme - The scheme to register.
43
+ * @param sourceDirectory - The directory where files are served from.
44
+ */
45
+ function createFileProtocol(scheme, sourceDirectory) {
46
+ electron_1.protocol.registerFileProtocol(scheme, (request, respond) => {
47
+ const requestPath = decodeURI(request.url.replace(`${scheme}://`, ''));
48
+ const requestDir = (0, path_1.dirname)(requestPath);
49
+ const requestFile = (0, path_1.basename)(requestPath);
50
+ const path = (0, path_1.normalize)((0, path_1.join)(sourceDirectory, requestDir, requestFile));
51
+ respond({ path });
52
+ });
53
+ }
54
+ exports.createFileProtocol = createFileProtocol;
@@ -0,0 +1,7 @@
1
+ import { InitPlugin } from './init';
2
+ /**
3
+ * Enables touch events in the app.
4
+ */
5
+ export declare class TouchEvents implements InitPlugin {
6
+ afterReady(): Promise<void>;
7
+ }
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.TouchEvents = void 0;
13
+ const electron_1 = require("electron");
14
+ /**
15
+ * Enables touch events in the app.
16
+ */
17
+ class TouchEvents {
18
+ afterReady() {
19
+ return __awaiter(this, void 0, void 0, function* () {
20
+ electron_1.app.commandLine.appendSwitch('touch-events', 'enabled');
21
+ });
22
+ }
23
+ }
24
+ exports.TouchEvents = TouchEvents;
@@ -0,0 +1,9 @@
1
+ export * from './AutoUpdater';
2
+ export * from './BrowserWindow';
3
+ export * from './DevTools';
4
+ export * from './NodeHeartbeat';
5
+ export * from './PrivilegedSchemes';
6
+ export * from './SingleInstance';
7
+ export * from './StaticFileDir';
8
+ export * from './TouchEvents';
9
+ export * from './init';
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./AutoUpdater"), exports);
18
+ __exportStar(require("./BrowserWindow"), exports);
19
+ __exportStar(require("./DevTools"), exports);
20
+ __exportStar(require("./NodeHeartbeat"), exports);
21
+ __exportStar(require("./PrivilegedSchemes"), exports);
22
+ __exportStar(require("./SingleInstance"), exports);
23
+ __exportStar(require("./StaticFileDir"), exports);
24
+ __exportStar(require("./TouchEvents"), exports);
25
+ __exportStar(require("./init"), exports);
package/dist/init.d.ts ADDED
@@ -0,0 +1,106 @@
1
+ import { BrowserWindow, BrowserWindowConstructorOptions } from 'electron';
2
+ /**
3
+ * The context object passed to each plugin during the init process.
4
+ */
5
+ export declare class InitContext {
6
+ /**
7
+ * The url used to load the application.
8
+ */
9
+ appUrl: string;
10
+ /**
11
+ * Application config used by the app and/or plugins.
12
+ */
13
+ config: Record<string, string | number | boolean>;
14
+ /**
15
+ * Options used to create the BrowserWindow. These can be modified in `beforeReady` or
16
+ * `beforeLoad` methods to change the created BrowserWindow.
17
+ */
18
+ browserWindowOptions: BrowserWindowConstructorOptions;
19
+ /**
20
+ * The main browser window that the app is loaded into. This is available in the context
21
+ * in the `afterLoad` method.
22
+ */
23
+ browserWindow: BrowserWindow | null;
24
+ /**
25
+ * The log instance. This should be used over `console` in plugin implementations.
26
+ */
27
+ log: Pick<Console, 'error' | 'warn' | 'info' | 'debug'>;
28
+ constructor(
29
+ /**
30
+ * The url used to load the application.
31
+ */
32
+ appUrl: string,
33
+ /**
34
+ * Application config used by the app and/or plugins.
35
+ */
36
+ config: Record<string, string | number | boolean>,
37
+ /**
38
+ * Options used to create the BrowserWindow. These can be modified in `beforeReady` or
39
+ * `beforeLoad` methods to change the created BrowserWindow.
40
+ */
41
+ browserWindowOptions: BrowserWindowConstructorOptions,
42
+ /**
43
+ * The main browser window that the app is loaded into. This is available in the context
44
+ * in the `afterLoad` method.
45
+ */
46
+ browserWindow: BrowserWindow | null,
47
+ /**
48
+ * The log instance. This should be used over `console` in plugin implementations.
49
+ */
50
+ log: Pick<Console, 'error' | 'warn' | 'info' | 'debug'>);
51
+ }
52
+ /**
53
+ * A plugin is used to execute logic at various stages during the init process.
54
+ *
55
+ * Implementations can define one or more of the optional methods to customize
56
+ * application instance.
57
+ */
58
+ export interface InitPlugin {
59
+ /**
60
+ * beforeReady is executed before the `app.whenReady()` promise resolves.
61
+ *
62
+ * @param context - The current InitContext instance.
63
+ */
64
+ beforeReady?(context: InitContext): Promise<void>;
65
+ /**
66
+ * afterReady is executed after the `app.whenReady()` promise resolves, but before the
67
+ * BrowserWindow is created.
68
+ *
69
+ * @param context - The current InitContext instance.
70
+ */
71
+ afterReady?(context: InitContext): Promise<void>;
72
+ /**
73
+ * beforeLoad is executed after the browserWindow is created, but before the application
74
+ * has been loaded into the window.
75
+ *
76
+ * @param context - The current InitContext instance.
77
+ */
78
+ beforeLoad?(context: InitContext): Promise<void>;
79
+ /**
80
+ * afterLoad is executed after the application has been loaded into the browserWindow.
81
+ *
82
+ * @param context - The current InitContext instance.
83
+ */
84
+ afterLoad?(context: InitContext): Promise<void>;
85
+ }
86
+ export interface InitOptions {
87
+ /**
88
+ * The url to load once the the app has been created.
89
+ */
90
+ appUrl: string;
91
+ /**
92
+ * The default application settings.
93
+ */
94
+ config?: Record<string, string | number | boolean>;
95
+ /**
96
+ * The list of plugins to load with the application.
97
+ */
98
+ plugins?: Array<InitPlugin>;
99
+ }
100
+ /**
101
+ * Initializes the application, creating a browser window, and loads the provided app url.
102
+ *
103
+ * @param options - Options used to define how the application is initialized.
104
+ * @returns - The final state of the init context, including the created browser window for additional setup.
105
+ */
106
+ export declare function init({ appUrl, config, plugins, }: InitOptions): Promise<InitContext>;
package/dist/init.js ADDED
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.init = exports.InitContext = void 0;
16
+ const electron_1 = require("electron");
17
+ const electron_log_1 = __importDefault(require("electron-log"));
18
+ /**
19
+ * The context object passed to each plugin during the init process.
20
+ */
21
+ class InitContext {
22
+ constructor(
23
+ /**
24
+ * The url used to load the application.
25
+ */
26
+ appUrl,
27
+ /**
28
+ * Application config used by the app and/or plugins.
29
+ */
30
+ config,
31
+ /**
32
+ * Options used to create the BrowserWindow. These can be modified in `beforeReady` or
33
+ * `beforeLoad` methods to change the created BrowserWindow.
34
+ */
35
+ browserWindowOptions,
36
+ /**
37
+ * The main browser window that the app is loaded into. This is available in the context
38
+ * in the `afterLoad` method.
39
+ */
40
+ browserWindow,
41
+ /**
42
+ * The log instance. This should be used over `console` in plugin implementations.
43
+ */
44
+ log) {
45
+ this.appUrl = appUrl;
46
+ this.config = config;
47
+ this.browserWindowOptions = browserWindowOptions;
48
+ this.browserWindow = browserWindow;
49
+ this.log = log;
50
+ }
51
+ }
52
+ exports.InitContext = InitContext;
53
+ /**
54
+ * Initializes the application, creating a browser window, and loads the provided app url.
55
+ *
56
+ * @param options - Options used to define how the application is initialized.
57
+ * @returns - The final state of the init context, including the created browser window for additional setup.
58
+ */
59
+ function init({ appUrl, config = {}, plugins = [], }) {
60
+ return __awaiter(this, void 0, void 0, function* () {
61
+ process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = 'true';
62
+ electron_1.app.on('window-all-closed', electron_1.app.quit);
63
+ process.on('message', (data) => {
64
+ if (data === 'graceful-exit')
65
+ electron_1.app.quit();
66
+ });
67
+ process.on('SIGTERM', electron_1.app.quit);
68
+ const context = new InitContext(appUrl, config, { height: 1920, width: 1080, backgroundColor: '#000' }, null, electron_log_1.default);
69
+ for (const plugin of plugins) {
70
+ if (plugin.beforeReady) {
71
+ yield plugin.beforeReady(context);
72
+ }
73
+ }
74
+ yield electron_1.app.whenReady();
75
+ for (const plugin of plugins) {
76
+ if (plugin.afterReady) {
77
+ yield plugin.afterReady(context);
78
+ }
79
+ }
80
+ context.browserWindow = new electron_1.BrowserWindow(context.browserWindowOptions);
81
+ context.browserWindow.on('closed', () => context.browserWindow = null);
82
+ for (const plugin of plugins) {
83
+ if (plugin.beforeLoad) {
84
+ yield plugin.beforeLoad(context);
85
+ }
86
+ }
87
+ yield context.browserWindow.loadURL(context.appUrl);
88
+ for (const plugin of plugins) {
89
+ if (plugin.afterLoad) {
90
+ plugin.afterLoad(context);
91
+ }
92
+ }
93
+ return context;
94
+ });
95
+ }
96
+ exports.init = init;
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@dimensional-innovations/electron-background",
3
+ "version": "2.0.4",
4
+ "description": "the background script for electron apps",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist/**/*"
9
+ ],
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "lint": "eslint ./src/**/*",
13
+ "lint:fix": "eslint ./src/**/* --fix",
14
+ "generate:docs": "jsdoc2md ./dist/* > API.md"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://gitlab.com/dimensional-innovations/electron-background.git"
19
+ },
20
+ "keywords": [
21
+ "vue",
22
+ "electron",
23
+ "background"
24
+ ],
25
+ "author": "Edgar Esparza",
26
+ "license": "ISC",
27
+ "bugs": {
28
+ "url": "https://gitlab.com/dimensional-innovations/electron-background/issues"
29
+ },
30
+ "homepage": "https://gitlab.com/dimensional-innovations/electron-background#readme",
31
+ "dependencies": {
32
+ "axios": "^1.6.7",
33
+ "electron-devtools-installer": "^3.1.1",
34
+ "electron-log": "^5.1.1",
35
+ "electron-updater": "^6.1.7"
36
+ },
37
+ "devDependencies": {
38
+ "@typescript-eslint/eslint-plugin": "^6.21.0",
39
+ "@typescript-eslint/parser": "^6.21.0",
40
+ "electron": "^28.2.1",
41
+ "eslint": "^8.56.0",
42
+ "eslint-config-airbnb-base": "^15.0.0",
43
+ "eslint-plugin-import": "^2.29.1",
44
+ "jsdoc-to-markdown": "^8.0.1",
45
+ "typescript": "^5.3.3"
46
+ },
47
+ "peerDependencies": {
48
+ "electron": ">=16.0.0"
49
+ }
50
+ }