add rpc system to improve ipc

This commit is contained in:
Thomas Nordquist
2022-02-27 18:44:17 +01:00
parent 205ea00c41
commit e1493db7c8
27 changed files with 252 additions and 303 deletions

View File

@@ -11,7 +11,7 @@
"mochatest": "mocha --require ts-node/register src/**/*.spec.ts"
},
"engines": {
"npm": "16"
"node": "16"
},
"author": "",
"license": "CC-BY-ND-4.0",

View File

@@ -9,14 +9,13 @@ import {
import { default as persistentStorage, StorageIdentifier } from '../utils/PersistentStorage'
import { Dispatch } from 'redux'
import { showError } from './Global'
import * as electron from 'electron'
import { promises as fsPromise } from 'fs'
import * as path from 'path'
import { ActionTypes, Action } from '../reducers/ConnectionManager'
import { Subscription } from '../../../backend/src/DataSource/MqttSource'
import { connectionsMigrator } from './migrations/Connection'
import { EventDispatcher, openDialogResponse, OpenDialogResponse, rendererEvents, requestOpenDialog } from '../../../events'
import { v4 } from 'uuid'
import { rendererRpc } from '../../../events'
import { makeOpenDialogRpc } from '../../../events/OpenDialogRequest'
export interface ConnectionDictionary {
[s: string]: ConnectionOptions
@@ -74,28 +73,10 @@ async function openCertificate(): Promise<CertificateParameters> {
certificateSizeDoesNotMatch: 'Certificate size larger/smaller then expected.',
}
let requestId = v4();
const response = new Promise<OpenDialogResponse>((resolve, reject) => {
let callback = (result: OpenDialogResponse) => {
rendererEvents.unsubscribe(openDialogResponse(), callback)
if (result.identifier == requestId) {
resolve(result)
} else {
reject(new Error("Unexpected file select"))
}
}
rendererEvents.subscribe(openDialogResponse(), callback)
})
rendererEvents.emit(requestOpenDialog(), {
identifier: requestId,
options: {
const openDialogReturnValue = await rendererRpc.call(makeOpenDialogRpc(), {
properties: ['openFile'],
securityScopedBookmarks: true,
}
})
const openDialogReturnValue = (await response).result;
const selectedFile = openDialogReturnValue.filePaths && openDialogReturnValue.filePaths[0]
if (!selectedFile) {

View File

@@ -1,7 +1,5 @@
import * as React from 'react'
import Add from '@material-ui/icons/Add'
import ClearAdornment from '../helper/ClearAdornment'
import Delete from '@material-ui/icons/Delete'
import Lock from '@material-ui/icons/Lock'
import { bindActionCreators } from 'redux'
import { Button, Theme, Tooltip, Typography } from '@material-ui/core'

View File

@@ -5,7 +5,7 @@ let heapdump: any
function writeHeapdump(path?: string) {
if (!heapdump) {
//heapdump = require('heapdump')
//<heapdump = require('heapdump')
}
heapdump.writeSnapshot(path || `${Date.now()}.heapsnapshot`)

View File

@@ -13,6 +13,7 @@ import { Theme, withStyles } from '@material-ui/core/styles'
import { updateNotifierActions } from '../actions'
import { Button, IconButton, Modal, Paper, Snackbar, SnackbarContent, Typography } from '@material-ui/core'
import { rendererRpc, getAppVersion } from '../../../events'
interface Props {
showUpdateNotification: boolean
@@ -50,8 +51,12 @@ class UpdateNotifier extends React.PureComponent<Props, State> {
super(props)
this.state = { newerVersions: [] }
const ownVersion = electron.app.getVersion()
this.fetchReleases().then(releases => {
this.checkForUpdates()
}
private async checkForUpdates() {
const ownVersion = await rendererRpc.call(getAppVersion, undefined, 10000);
const releases = await this.fetchReleases();
const newerVersions = releases
.filter(release => this.allowPrereleaseIfOwnVersionIsBeta(release, ownVersion))
.filter(release => compareVersions(release.tag_name, ownVersion) > 0)
@@ -61,7 +66,6 @@ class UpdateNotifier extends React.PureComponent<Props, State> {
this.setState({ newerVersions })
this.props.actions.showUpdateNotification(true)
}
})
}
private allowPrereleaseIfOwnVersionIsBeta(release: GithubRelease, ownVersion: string) {

View File

@@ -1,12 +1,9 @@
import { rendererEvents } from '../../../events'
import { v4 } from 'uuid'
import { rendererRpc } from '../../../events'
import {
storageStoreEvent,
makeStorageResponseEvent,
storageLoadEvent,
storageClearEvent,
makeStorageAcknowledgementEvent,
} from '../../../events/StorageEvents'
export interface StorageIdentifier<Model> {
@@ -20,71 +17,23 @@ export interface PersistentStorage {
}
class RemoteStorage implements PersistentStorage {
private timeoutCallback(event: any, callback: any, reject: any) {
setTimeout(() => {
reject('remote storage timeout')
rendererEvents.unsubscribe(event, callback)
}, 10000)
}
private expectAck(transactionId: string): Promise<void> {
const ack = makeStorageAcknowledgementEvent(transactionId)
return new Promise<void>((resolve, reject) => {
const callback = (msg: any) => {
if (msg && msg.error) {
reject(msg.error)
} else {
resolve()
}
rendererEvents.unsubscribe(ack, callback)
}
rendererEvents.subscribe(ack, callback)
this.timeoutCallback(ack, callback, reject)
})
}
public store<Model>(identifier: StorageIdentifier<Model>, data: Model): Promise<void> {
const transactionId = v4()
const expectation = this.expectAck(transactionId)
rendererEvents.emit(storageStoreEvent, {
return rendererRpc.call(storageStoreEvent, {
data,
transactionId,
store: identifier.id,
})
return expectation
}
public load<Model>(identifier: StorageIdentifier<Model>): Promise<Model | undefined> {
const transactionId = v4()
const responseEvent = makeStorageResponseEvent(transactionId)
const promise = new Promise<Model>((resolve, reject) => {
const callback = (msg: any) => {
if (msg.error) {
reject(msg.error)
} else {
resolve(msg.data)
}
rendererEvents.unsubscribe(responseEvent, callback)
}
rendererEvents.subscribe(responseEvent, callback)
this.timeoutCallback(responseEvent, callback, reject)
})
rendererEvents.emit(storageLoadEvent, {
transactionId,
public async load<Model>(identifier: StorageIdentifier<Model>): Promise<Model | undefined> {
const result = await rendererRpc.call(storageLoadEvent, {
store: identifier.id,
})
}, 10000)
return promise
return (result as any).data
}
public clear(): Promise<void> {
const transactionId = v4()
const expectation = this.expectAck(transactionId)
rendererEvents.emit(storageClearEvent, { transactionId })
return expectation
return rendererRpc.call(storageClearEvent, undefined, 10000)
}
}

View File

@@ -30,6 +30,7 @@ module.exports = {
priority: -10,
},
default: {
name: 'default',
minChunks: 2,
priority: -20,
reuseExistingChunk: true,

View File

@@ -12,7 +12,7 @@
"postinstall": "yarn build"
},
"engines": {
"npm": "16"
"node": "16"
},
"author": "",
"license": "CC-BY-ND-4.0",

View File

@@ -2,10 +2,8 @@ import * as FileAsync from 'lowdb/adapters/FileAsync'
import * as fs from 'fs-extra'
import * as lowdb from 'lowdb'
import * as path from 'path'
import { backendEvents } from '../../events'
import { backendRpc } from '../../events'
import {
makeStorageAcknowledgementEvent,
makeStorageResponseEvent,
storageClearEvent,
storageLoadEvent,
storageStoreEvent,
@@ -32,57 +30,27 @@ export default class ConfigStorage {
}
public async init() {
backendEvents.subscribe(storageStoreEvent, async event => {
const ack = makeStorageAcknowledgementEvent(event.transactionId)
try {
backendRpc.on(storageStoreEvent, async event => {
const db = await this.getDb()
await db.set(event.store, event.data).write()
backendEvents.emit(ack, undefined)
} catch (error) {
backendEvents.emit(ack, {
error,
transactionId: event.transactionId,
store: event.store,
})
throw error
}
return
})
backendEvents.subscribe(storageLoadEvent, async event => {
const responseEvent = makeStorageResponseEvent(event.transactionId)
try {
backendRpc.on(storageLoadEvent, async event => {
const db = await this.getDb()
const data = await db.get(event.store).value()
backendEvents.emit(responseEvent, {
return {
data,
transactionId: event.transactionId,
store: event.store,
})
} catch (error) {
backendEvents.emit(responseEvent, {
error,
transactionId: event.transactionId,
store: event.store,
})
throw error
}
})
backendEvents.subscribe(storageClearEvent, async event => {
try {
backendRpc.on(storageClearEvent, async event => {
const db = await this.getDb()
const keys = await db.keys().value()
for (const key of keys) {
await db.unset(key).write()
}
backendEvents.emit(makeStorageAcknowledgementEvent(event.transactionId), undefined)
} catch (error) {
backendEvents.emit(makeStorageAcknowledgementEvent(event.transactionId), {
error,
transactionId: event.transactionId,
})
throw error
}
})
}
}

View File

@@ -1,6 +1,6 @@
import { ChangeBuffer } from './ChangeBuffer'
import { Destroyable } from './Destroyable'
import { EventBusInterface, EventDispatcher, makeConnectionMessageEvent, MqttMessage } from '../../../events'
import { EventDispatcher, makeConnectionMessageEvent, MqttMessage, EventBusInterface } from '../../../events'
import { TreeNode } from './'
import { TreeNodeFactory } from './TreeNodeFactory'

View File

@@ -1,50 +0,0 @@
import { IpcMain, ipcMain, ipcRenderer } from 'electron'
import { Event } from './Events'
import { IpcRendererEventBus } from './IpcRendererEventBus'
export interface EventBusInterface {
subscribe<MessageType>(event: Event<MessageType>, callback: (msg: MessageType) => void): void
unsubscribeAll<MessageType>(event: Event<MessageType>): void
emit<MessageType>(event: Event<MessageType>, msg: MessageType): void
unsubscribe<MessageType>(event: Event<MessageType>, callback: any): void
}
export interface CallbackStore {
wrappedCallback: any
callback: any
}
class IpcMainEventBus implements EventBusInterface {
private ipc: IpcMain
private client: any
constructor(ipc: IpcMain) {
this.ipc = ipc
}
public subscribe<MessageType>(subscribeEvent: Event<MessageType>, callback: (msg: MessageType) => void) {
console.log('subscribing', subscribeEvent.topic)
this.ipc.on(subscribeEvent.topic, (event: any, arg: any) => {
this.client = event.sender
callback(arg)
})
}
public unsubscribeAll<MessageType>(event: Event<MessageType>) {
console.log('unsubscribeAll', event.topic)
this.ipc.removeAllListeners(event.topic)
}
public unsubscribe<MessageType>(event: Event<MessageType>, callback: any) {
throw new Error('Not implemented') // Todo: implement
}
public emit<MessageType>(event: Event<MessageType>, msg: MessageType) {
if (!this.client.isDestroyed()) {
this.client.send(event.topic, msg)
}
}
}
export const rendererEvents = new IpcRendererEventBus(ipcRenderer)
export const backendEvents = new IpcMainEventBus(ipcMain)

View File

@@ -0,0 +1,4 @@
export interface CallbackStore {
wrappedCallback: any;
callback: any;
}

View File

@@ -0,0 +1,12 @@
import { ipcMain, ipcRenderer } from 'electron'
import { IpcMainEventBus } from './IpcMainEventBus'
import { IpcRendererEventBus } from './IpcRendererEventBus'
import { Rpc } from './Rpc'
export const rendererEvents = new IpcRendererEventBus(ipcRenderer)
export const backendEvents = new IpcMainEventBus(ipcMain)
// Preferred way to communicate typesafe
export const rendererRpc = new Rpc(rendererEvents)
export const backendRpc = new Rpc(backendEvents)

View File

@@ -0,0 +1,8 @@
import { Event } from '../Events';
export interface EventBusInterface {
subscribe<MessageType>(event: Event<MessageType>, callback: (msg: MessageType) => void): void;
unsubscribeAll<MessageType>(event: Event<MessageType>): void;
emit<MessageType>(event: Event<MessageType>, msg: MessageType): void;
unsubscribe<MessageType>(event: Event<MessageType>, callback: any): void;
}

View File

@@ -0,0 +1,34 @@
import { IpcMain } from 'electron';
import { Event } from '../Events';
import { EventBusInterface } from "./EventBusInterface";
export class IpcMainEventBus implements EventBusInterface {
private ipc: IpcMain;
private client: any;
constructor(ipc: IpcMain) {
this.ipc = ipc;
}
public subscribe<MessageType>(subscribeEvent: Event<MessageType>, callback: (msg: MessageType) => void) {
console.log('subscribing', subscribeEvent.topic);
this.ipc.on(subscribeEvent.topic, (event: any, arg: any) => {
this.client = event.sender;
callback(arg);
});
}
public unsubscribeAll<MessageType>(event: Event<MessageType>) {
console.log('unsubscribeAll', event.topic);
this.ipc.removeAllListeners(event.topic);
}
public unsubscribe<MessageType>(event: Event<MessageType>, callback: any) {
throw new Error('Not implemented'); // Todo: implement
}
public emit<MessageType>(event: Event<MessageType>, msg: MessageType) {
if (!this.client.isDestroyed()) {
this.client.send(event.topic, msg);
}
}
}

View File

@@ -1,26 +1,32 @@
import { CallbackStore, EventBusInterface } from './EventBus'
import { Event } from './Events'
import { CallbackStore } from "./CallbackStore"
import { EventBusInterface } from "./EventBusInterface"
import { Event } from '../Events'
import { IpcRenderer } from 'electron'
export class IpcRendererEventBus implements EventBusInterface {
private ipc: IpcRenderer
private callbacks: Array<CallbackStore> = []
constructor(ipc: IpcRenderer) {
this.ipc = ipc
}
public subscribe<MessageType>(event: Event<MessageType>, callback: (msg: MessageType) => void) {
const wrappedCallback = (_: any, arg: any) => {
callback(arg)
}
console.log("subscribing", event.topic)
this.ipc.on(event.topic, wrappedCallback)
this.callbacks.push({
callback,
wrappedCallback,
})
}
public unsubscribeAll<MessageType>(event: Event<MessageType>) {
this.ipc.removeAllListeners(event.topic)
}
public unsubscribe<MessageType>(event: Event<MessageType>, callback: any) {
const item = this.callbacks.find(store => store.callback === callback)
if (!item) {
@@ -29,6 +35,7 @@ export class IpcRendererEventBus implements EventBusInterface {
this.ipc.removeListener(event.topic, item.wrappedCallback)
this.callbacks = this.callbacks.filter(a => a !== item)
}
public emit<MessageType>(event: Event<MessageType>, msg: MessageType) {
this.ipc.send(event.topic, msg)
}

54
events/EventSystem/Rpc.ts Normal file
View File

@@ -0,0 +1,54 @@
import { Event } from '../Events';
import { EventBusInterface } from './EventBusInterface'
import { v4 } from 'uuid';
export type RpcEvent<RequstType, ResponseType> = {
topic: string;
};
export class Rpc {
constructor(private participant: EventBusInterface) { }
async call<RpcRequest, RpcResponse>(event: RpcEvent<RpcRequest, RpcResponse>, request: RpcRequest, timeout: number = 0): Promise<RpcResponse> {
return new Promise((resolve, reject) => {
let id = v4();
let responseEvent: Event<any> = { topic: `${event.topic}/response` };
let requestEvent: Event<any> = { topic: `${event.topic}/request` };
let callback = (result: { id: string; payload: RpcResponse; error: unknown }) => {
this.participant.unsubscribe(responseEvent as any, callback);
if (result.id === id) {
if (result.error) {
reject(result.error)
} else {
resolve(result.payload);
}
}
console.log("received", result)
};
this.participant.subscribe(responseEvent, callback);
this.participant.emit(requestEvent, { id, payload: request });
if (timeout > 0) {
setTimeout(() => {
reject(new Error(`Did not respond to ${event.topic} within ${timeout}ms`))
this.participant.unsubscribe(responseEvent as any, callback);
}, 10000)
}
});
}
async on<RpcRequest, RpcResponse>(event: RpcEvent<RpcRequest, RpcResponse>, handler: (request: RpcRequest) => Promise<RpcResponse>) {
this.participant.subscribe<RpcRequest>({ topic: `${event.topic}/request` } as RpcEvent<any, any>, async (request) => {
let payload;
let error;
try {
payload = await handler((request as any).payload);
} catch (e) {
error = e
}
console.log("Responding with", payload, error)
this.participant.emit({ topic: `${event.topic}/response` }, { id: (request as any).id, payload, error });
});
}
}

View File

@@ -1,7 +1,7 @@
import { Base64Message } from '../backend/src/Model/Base64Message'
import { DataSourceState, MqttOptions } from '../backend/src/DataSource'
import { UpdateInfo } from 'builder-util-runtime'
import { OpenDialogOptions, OpenDialogReturnValue } from 'electron'
import { RpcEvent } from './EventSystem/Rpc';
export type Event<MessageType> = {
topic: string
@@ -51,25 +51,6 @@ export function makeConnectionMessageEvent(connectionId: string): Event<MqttMess
}
}
export interface OpenDialogRequest {
identifier: string
options: OpenDialogOptions
export const getAppVersion: RpcEvent<void, string> = {
topic: `getAppVersion`
}
export function requestOpenDialog(): Event<OpenDialogRequest> {
return {
topic: `requestOpenDialog`,
}
}
export interface OpenDialogResponse {
identifier: string
result: OpenDialogReturnValue
}
export function openDialogResponse(): Event<OpenDialogResponse> {
return {
topic: `openDialogResponse`,
}
}

View File

@@ -0,0 +1,8 @@
import { OpenDialogOptions, OpenDialogReturnValue } from 'electron';
import { RpcEvent } from './EventSystem/Rpc';
export function makeOpenDialogRpc(): RpcEvent<OpenDialogOptions, OpenDialogReturnValue> {
return {
topic: `openDialog`
};
}

View File

View File

@@ -1,39 +1,22 @@
import { Event } from './'
import { RpcEvent } from './EventSystem/Rpc'
interface StorageEvent {
transactionId: string
export interface StoreCommand {
store: string
data: any
}
export interface StoreCommand extends StorageEvent {
store?: string
data?: any
error?: any
}
export interface LoadCommand extends StorageEvent {
export interface LoadCommand {
store: string
}
export const storageStoreEvent: Event<StoreCommand> = {
export const storageStoreEvent: RpcEvent<StoreCommand, void> = {
topic: 'storage/store',
}
export const storageLoadEvent: Event<LoadCommand> = {
export const storageLoadEvent: RpcEvent<LoadCommand, StoreCommand> = {
topic: 'storage/load',
}
export function makeStorageAcknowledgementEvent(transactionId: string): Event<StoreCommand> {
return {
topic: `storage/ack/${transactionId}`,
}
}
export function makeStorageResponseEvent(transactionId: string): Event<StoreCommand> {
return {
topic: `storage/response/${transactionId}`,
}
}
export const storageClearEvent: Event<StorageEvent> = {
export const storageClearEvent: RpcEvent<void, void> = {
topic: 'storage/clear',
}

View File

@@ -1,3 +1,4 @@
export * from './Events'
export * from './EventDispatcher'
export * from './EventBus'
export * from './EventSystem/EventDispatcher'
export * from './EventSystem/EventBus'
export * from './EventSystem/EventBusInterface'

82
package-lock.json generated
View File

@@ -22,6 +22,7 @@
"mime": "^2.4.4",
"mqtt": "^3.0.0",
"sha1": "^1.1.1",
"uuid": "^8.3.2",
"yarn-run-all": "^3.1.1"
},
"devDependencies": {
@@ -35,6 +36,7 @@
"@types/node": "^12.6.8",
"@types/semver": "7",
"@types/sha1": "^1.1.1",
"@types/uuid": "^8.3.4",
"builder-util-runtime": "^9",
"chai": "^4.2.0",
"cspell": "^4.0.28",
@@ -52,6 +54,9 @@
"ts-node": "^10.5.0",
"typescript": "^4.5.5",
"webdriverio": "7.16"
},
"engines": {
"node": "16"
}
},
"node_modules/@ampproject/remapping": {
@@ -1133,6 +1138,12 @@
"integrity": "sha512-N1rW+njavs70y2cApeIw1vLMYXRwfBy+7trgavGuuTfOd7j1Yh7QTRc/yqsPl6ncokt72ZXuxEU0PiCp9bSwNQ==",
"dev": true
},
"node_modules/@types/uuid": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz",
"integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==",
"dev": true
},
"node_modules/@types/verror": {
"version": "1.10.5",
"resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.5.tgz",
@@ -3026,15 +3037,6 @@
"integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==",
"dev": true
},
"node_modules/devtools/node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"dev": true,
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/diff": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
@@ -5113,6 +5115,16 @@
"node": ">=8"
}
},
"node_modules/istanbul-lib-processinfo/node_modules/uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"dev": true,
"bin": {
"uuid": "bin/uuid"
}
},
"node_modules/istanbul-lib-report": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
@@ -8243,15 +8255,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/spectron/node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"dev": true,
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/spectron/node_modules/webdriver": {
"version": "7.16.13",
"resolved": "https://registry.npmjs.org/webdriver/-/webdriver-7.16.13.tgz",
@@ -8979,13 +8982,11 @@
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"node_modules/uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"dev": true,
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"bin": {
"uuid": "bin/uuid"
"uuid": "dist/bin/uuid"
}
},
"node_modules/v8-compile-cache-lib": {
@@ -10899,6 +10900,12 @@
"integrity": "sha512-N1rW+njavs70y2cApeIw1vLMYXRwfBy+7trgavGuuTfOd7j1Yh7QTRc/yqsPl6ncokt72ZXuxEU0PiCp9bSwNQ==",
"dev": true
},
"@types/uuid": {
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz",
"integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==",
"dev": true
},
"@types/verror": {
"version": "1.10.5",
"resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.5.tgz",
@@ -12384,12 +12391,6 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz",
"integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==",
"dev": true
},
"uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"dev": true
}
}
},
@@ -13966,6 +13967,14 @@
"p-map": "^3.0.0",
"rimraf": "^3.0.0",
"uuid": "^3.3.3"
},
"dependencies": {
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"dev": true
}
}
},
"istanbul-lib-report": {
@@ -16435,12 +16444,6 @@
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
"dev": true
},
"uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"dev": true
},
"webdriver": {
"version": "7.16.13",
"resolved": "https://registry.npmjs.org/webdriver/-/webdriver-7.16.13.tgz",
@@ -17019,10 +17022,9 @@
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"dev": true
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
},
"v8-compile-cache-lib": {
"version": "3.0.0",

View File

@@ -4,7 +4,7 @@
"description": "Explore your message queues",
"main": "dist/src/electron.js",
"engines": {
"npm": "16"
"node": "16"
},
"scripts": {
"start": "electron .",
@@ -83,6 +83,7 @@
"@types/node": "^12.6.8",
"@types/semver": "7",
"@types/sha1": "^1.1.1",
"@types/uuid": "^8.3.4",
"builder-util-runtime": "^9",
"chai": "^4.2.0",
"cspell": "^4.0.28",
@@ -114,6 +115,7 @@
"mime": "^2.4.4",
"mqtt": "^3.0.0",
"sha1": "^1.1.1",
"uuid": "^8.3.2",
"yarn-run-all": "^3.1.1"
}
}

View File

@@ -1,7 +1,7 @@
import * as log from 'electron-log'
import * as path from 'path'
import ConfigStorage from '../backend/src/ConfigStorage'
import { ipcMain, app, BrowserWindow, Menu, dialog } from 'electron'
import { app, BrowserWindow, Menu, dialog } from 'electron'
import { autoUpdater } from 'electron-updater'
import { ConnectionManager } from '../backend/src/index'
// import { electronTelemetryFactory } from 'electron-telemetry'
@@ -10,7 +10,8 @@ import buildOptions from './buildOptions'
import { waitForDevServer, isDev, runningUiTestOnCi, loadDevTools } from './development'
import { shouldAutoUpdate, handleAutoUpdate } from './autoUpdater'
import { registerCrashReporter } from './registerCrashReporter'
import { backendEvents, EventDispatcher, OpenDialogRequest, openDialogResponse, OpenDialogResponse, requestOpenDialog } from '../events'
import { makeOpenDialogRpc } from '../events/OpenDialogRequest'
import { backendRpc, getAppVersion } from '../events'
registerCrashReporter()
@@ -20,14 +21,10 @@ registerCrashReporter()
app.commandLine.appendSwitch('--no-sandbox')
app.whenReady().then(() => {
backendEvents.subscribe(requestOpenDialog(), async (request) => {
let result = await dialog.showOpenDialog(BrowserWindow.getFocusedWindow() ?? BrowserWindow.getAllWindows()[0], request.options)
backendEvents.emit(openDialogResponse(), {
identifier: request.identifier,
result: result
})
backendRpc.on(makeOpenDialogRpc(), async (request) => {
return dialog.showOpenDialog(BrowserWindow.getFocusedWindow() ?? BrowserWindow.getAllWindows()[0], request)
})
backendRpc.on(getAppVersion, async () => app.getVersion())
})
autoUpdater.logger = log

View File

@@ -661,6 +661,11 @@
"resolved" "https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.36.tgz"
"version" "0.7.36"
"@types/uuid@^8.3.4":
"integrity" "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw=="
"resolved" "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz"
"version" "8.3.4"
"@types/verror@^1.10.3":
"integrity" "sha512-9UjMCHK5GPgQRoNbqdLIAvAy0EInuiqbW0PBMtVP6B5B2HQJlvoJHM+KodPZMEjOa5VkSc+5LH7xy+cUzQdmHw=="
"resolved" "https://registry.npmjs.org/@types/verror/-/verror-1.10.5.tgz"
@@ -5295,7 +5300,7 @@
"resolved" "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz"
"version" "3.4.0"
"uuid@^8.0.0":
"uuid@^8.0.0", "uuid@^8.3.2":
"integrity" "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
"resolved" "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz"
"version" "8.3.2"