Skip to content

Commit

Permalink
Misc
Browse files Browse the repository at this point in the history
  • Loading branch information
cmdcolin committed Oct 13, 2022
1 parent dbab27b commit f188304
Show file tree
Hide file tree
Showing 14 changed files with 294 additions and 239 deletions.
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ webpack.config.js
craco.config.js
packages/core/util/QuickLRU.js
packages/core/util/QuickLRU.d.ts
test_data/volvox/umd_plugin.js
products/jbrowse-web/public/umd_plugin.js
4 changes: 4 additions & 0 deletions packages/core/PluginManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,10 @@ export default class PluginManager {
return this.getElementTypeRecord(groupName).all()
}

getRpcElements() {
return this.getElementTypesInGroup('rpc method') as RpcMethodType[]
}

/** get a MST type for the union of all specified pluggable MST types */
pluggableMstType(
groupName: PluggableElementTypeGroup,
Expand Down
25 changes: 16 additions & 9 deletions packages/core/rpc/WebWorkerRpcDriver.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import Rpc from 'librpc-web-mod'
import shortid from 'shortid'
import { deserializeError } from 'serialize-error'

// locals
import BaseRpcDriver, { RpcDriverConstructorArgs } from './BaseRpcDriver'
import { PluginDefinition } from '../PluginLoader'

Expand Down Expand Up @@ -61,6 +64,7 @@ export default class WebWorkerRpcDriver extends BaseRpcDriver {
const worker = new WebWorkerHandle({ workers: [instance] })
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)
if (isSafari) {
// xref https://github.com/GMOD/jbrowse-components/issues/3245
// eslint-disable-next-line no-console
console.log(
'console logging the webworker handle avoids the track going into an infinite loading state, this is a hacky workaround for safari',
Expand All @@ -69,18 +73,21 @@ export default class WebWorkerRpcDriver extends BaseRpcDriver {
}

// send the worker its boot configuration using info from the pluginManager
const p = new Promise((resolve: (w: WebWorkerHandle) => void, reject) => {
worker.workers[0].onmessage = e => {
if (e.data === 'ready') {
return new Promise((resolve: (w: WebWorkerHandle) => void, reject) => {
const listener = (e: MessageEvent) => {
if (e.data.message === 'ready') {
resolve(worker)
} else if (e.data === 'readyForConfig') {
worker.workers[0].postMessage(this.workerBootConfiguration)
} else {
reject()
worker.workers[0].removeEventListener('message', listener)
} else if (e.data.message === 'readyForConfig') {
worker.workers[0].postMessage({
message: 'config',
config: this.workerBootConfiguration,
})
} else if (e.data.message === 'error') {
reject(deserializeError(e.data.error))
}
}
worker.workers[0].addEventListener('message', listener)
})

return p
}
}
1 change: 1 addition & 0 deletions products/jbrowse-desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
"react-error-boundary": "^3.0.0",
"rxjs": "^6.5.2",
"sanitize-filename": "^1.6.3",
"serialize-error": "^8.0.0",
"timeago.js": "^4.0.2",
"tss-react": "^3.7.0",
"use-query-params": "^2.0.0",
Expand Down
59 changes: 29 additions & 30 deletions products/jbrowse-desktop/src/rpc.worker.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,44 @@
/* eslint-disable no-restricted-globals */
import './workerPolyfill'

// @ts-ignore
import RpcServer from 'librpc-web-mod'
import { enableStaticRendering } from 'mobx-react'

import PluginManager from '@jbrowse/core/PluginManager'
import RpcMethodType from '@jbrowse/core/pluggableElementTypes/RpcMethodType'
import { remoteAbortRpcHandler } from '@jbrowse/core/rpc/remoteAbortSignals'
import PluginLoader, { PluginDefinition } from '@jbrowse/core/PluginLoader'
import { serializeError } from 'serialize-error'

// locals
import corePlugins from './corePlugins'

// prevent mobx-react from doing funny things when we render in the worker.
// but only if we are running in the browser. in node tests, leave it alone.
if (typeof __webpack_require__ === 'function') {
enableStaticRendering(true)
}
// static rendering is used for "SSR" style rendering which is done on the
// worker
enableStaticRendering(true)

interface WorkerConfiguration {
plugins: PluginDefinition[]
windowHref: string
}

let jbPluginManager: PluginManager | undefined

// waits for a message from the main thread containing our configuration, which
// must be sent on boot
function receiveConfiguration(): Promise<WorkerConfiguration> {
function receiveConfiguration() {
const configurationP: Promise<WorkerConfiguration> = new Promise(resolve => {
// listen for the configuration
self.onmessage = (event: MessageEvent) => {
resolve(event.data as WorkerConfiguration)
self.onmessage = () => {}
function listener(event: MessageEvent) {
if (event.data.message === 'config') {
resolve(event.data.config as WorkerConfiguration)
removeEventListener('message', listener)
}
}
self.addEventListener('message', listener)
})
postMessage('readyForConfig')
postMessage({ message: 'readyForConfig' })
return configurationP
}

async function getPluginManager() {
if (jbPluginManager) {
return jbPluginManager
}
// Load runtime plugins
const config = await receiveConfiguration()
const pluginLoader = new PluginLoader(config.plugins, {
Expand All @@ -52,7 +50,7 @@ async function getPluginManager() {
const pluginManager = new PluginManager(plugins.map(P => new P.plugin()))
pluginManager.createPluggableElements()
pluginManager.configure()
jbPluginManager = pluginManager

return pluginManager
}

Expand Down Expand Up @@ -83,25 +81,26 @@ function wrapForRpc(func: RpcFunc) {
getPluginManager()
.then(pluginManager => {
const rpcConfig = Object.fromEntries(
pluginManager.getElementTypesInGroup('rpc method').map(entry => {
const { execute, name } = entry as RpcMethodType
return [name, wrapForRpc((execute as RpcFunc).bind(entry))]
}),
pluginManager
.getRpcElements()
.map(entry => [entry.name, wrapForRpc(entry.execute.bind(entry))]),
)

// @ts-ignore
self.rpcServer = new RpcServer.Server({
...rpcConfig,
...remoteAbortRpcHandler(),
ping: () => {}, // < the ping method is required by the worker driver for checking the health of the worker
})
postMessage('ready')
})
.catch(error => {
// @ts-ignore
self.rpcServer = new RpcServer.Server({
ping: () => {
throw error
// the ping method is required by the worker driver for checking the
// health of the worker
},
})
postMessage({ message: 'ready' })
})
.catch(error => {
postMessage({ message: 'error', error: serializeError(error) })
})

export default () => {
/* do nothing */
}
1 change: 1 addition & 0 deletions products/jbrowse-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"react-error-boundary": "^3.0.0",
"requestidlecallback-polyfill": "^1.0.2",
"rxjs": "^6.5.2",
"serialize-error": "^8.0.0",
"shortid": "^2.2.15",
"tss-react": "^3.7.0",
"use-query-params": "^2.0.0",
Expand Down
30 changes: 17 additions & 13 deletions products/jbrowse-web/public/umd_plugin.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
function MyComponent() {
return 'Hello world'
}
/* eslint-disable no-restricted-globals */
// we put the code in a function to avoid variable name collisions with the
// global scope
;(function () {
class Plugin {
name = 'UMDUrlPlugin'
version = '1.0'

class MyClass2 {
name = 'UMDUrlPlugin'
install(pluginManager) {}
configure() {}
}
install(/* pluginManager */) {}

// the plugin will be included in both the main thread and web worker, so
// install plugin to either window or self (webworker global scope)
;(typeof self !== 'undefined' ? self : window).JBrowsePluginUMDUrlPlugin = {
default: MyClass2,
}
configure(/* pluginManager */) {}
}

// the plugin will be included in both the main thread and web worker, so
// install plugin to either window or self (webworker global scope)
;(typeof self !== 'undefined' ? self : window).JBrowsePluginUMDUrlPlugin = {
default: Plugin,
}
})()
12 changes: 6 additions & 6 deletions products/jbrowse-web/src/SessionLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,25 +53,25 @@ async function checkPlugins(pluginsToCheck: PluginDefinition[]) {
if (isUMDPluginDefinition(p)) {
return Boolean(
storePlugins.plugins.find(
p =>
pp =>
isUMDPluginDefinition(p) &&
(('url' in p && 'url' in p && p.url === p.url) ||
('umdUrl' in p && 'umdUrl' in p && p.umdUrl === p.umdUrl)),
(('url' in pp && 'url' in p && p.url === pp.url) ||
('umdUrl' in pp && 'umdUrl' in p && p.umdUrl === pp.umdUrl)),
),
)
}
if (isESMPluginDefinition(p)) {
return Boolean(
storePlugins.plugins.find(
p =>
isESMPluginDefinition(p) && 'esmUrl' in p && p.esmUrl === p.esmUrl,
pp =>
isESMPluginDefinition(p) && 'esmUrl' in p && p.esmUrl === pp.esmUrl,
),
)
}
if (isCJSPluginDefinition(p)) {
return Boolean(
storePlugins.plugins.find(
p => isCJSPluginDefinition(p) && p.cjsUrl === p.cjsUrl,
pp => isCJSPluginDefinition(p) && p.cjsUrl === pp.cjsUrl,
),
)
}
Expand Down
67 changes: 35 additions & 32 deletions products/jbrowse-web/src/rpc.worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,42 +6,39 @@ import RpcServer from 'librpc-web-mod'
import { enableStaticRendering } from 'mobx-react'

import PluginManager from '@jbrowse/core/PluginManager'
import RpcMethodType from '@jbrowse/core/pluggableElementTypes/RpcMethodType'
import { remoteAbortRpcHandler } from '@jbrowse/core/rpc/remoteAbortSignals'
import PluginLoader, { PluginDefinition } from '@jbrowse/core/PluginLoader'
import { serializeError } from 'serialize-error'

// locals
import corePlugins from './corePlugins'

// prevent mobx-react from doing funny things when we render in the worker.
// but only if we are running in the browser. in node tests, leave it alone.
if (typeof __webpack_require__ === 'function') {
enableStaticRendering(true)
}
// static rendering is used for "SSR" style rendering which is done on the
// worker
enableStaticRendering(true)

interface WorkerConfiguration {
plugins: PluginDefinition[]
windowHref: string
}

let jbPluginManager: PluginManager | undefined

// waits for a message from the main thread containing our configuration, which
// must be sent on boot
function receiveConfiguration(): Promise<WorkerConfiguration> {
function receiveConfiguration() {
const configurationP: Promise<WorkerConfiguration> = new Promise(resolve => {
// listen for the configuration
self.onmessage = (event: MessageEvent) => {
resolve(event.data as WorkerConfiguration)
self.onmessage = () => {}
function listener(event: MessageEvent) {
if (event.data.message === 'config') {
resolve(event.data.config as WorkerConfiguration)
removeEventListener('message', listener)
}
}
self.addEventListener('message', listener)
})
postMessage('readyForConfig')
postMessage({ message: 'readyForConfig' })
return configurationP
}

async function getPluginManager() {
if (jbPluginManager) {
return jbPluginManager
}
// Load runtime plugins
const config = await receiveConfiguration()
const pluginLoader = new PluginLoader(config.plugins, {
Expand All @@ -53,7 +50,7 @@ async function getPluginManager() {
const pluginManager = new PluginManager(plugins.map(P => new P.plugin()))
pluginManager.createPluggableElements()
pluginManager.configure()
jbPluginManager = pluginManager

return pluginManager
}

Expand Down Expand Up @@ -81,22 +78,28 @@ function wrapForRpc(func: RpcFunc) {
}
}

getPluginManager().then(pluginManager => {
const rpcConfig = Object.fromEntries(
pluginManager.getElementTypesInGroup('rpc method').map(entry => {
const { execute, name } = entry as RpcMethodType
return [name, wrapForRpc((execute as RpcFunc).bind(entry))]
}),
)
getPluginManager()
.then(pluginManager => {
const rpcConfig = Object.fromEntries(
pluginManager
.getRpcElements()
.map(entry => [entry.name, wrapForRpc(entry.execute.bind(entry))]),
)

// @ts-ignore
self.rpcServer = new RpcServer.Server({
...rpcConfig,
...remoteAbortRpcHandler(),
ping: () => {}, // < the ping method is required by the worker driver for checking the health of the worker
// @ts-ignore
self.rpcServer = new RpcServer.Server({
...rpcConfig,
...remoteAbortRpcHandler(),
ping: () => {
// the ping method is required by the worker driver for checking the
// health of the worker
},
})
postMessage({ message: 'ready' })
})
.catch(error => {
postMessage({ message: 'error', error: serializeError(error) })
})
postMessage('ready')
})

export default () => {
/* do nothing */
Expand Down
28 changes: 18 additions & 10 deletions test_data/volvox/umd_plugin.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
class MyClass {
name = 'UMDLocPlugin'
install(pluginManager) {}
configure() {}
}
/* eslint-disable no-restricted-globals */
// we put the code in a function to avoid variable name collisions with the
// global scope
;(function () {
class Plugin {
name = 'UMDLocPlugin'
version = '1.0'

// the plugin will be included in both the main thread and web worker, so
// install plugin to either window or self (webworker global scope)
;(typeof self !== 'undefined' ? self : window).JBrowsePluginUMDLocPlugin = {
default: MyClass,
}
install(/* pluginManager */) {}

configure(/* pluginManager */) {}
}

// the plugin will be included in both the main thread and web worker, so
// install plugin to either window or self (webworker global scope)
;(typeof self !== 'undefined' ? self : window).JBrowsePluginUMDLocPlugin = {
default: Plugin,
}
})()
Loading

0 comments on commit f188304

Please sign in to comment.