An easy-to-use platform for EEG experimentation in the classroom
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Massive overhaul

Managed to migrate BrainWaves from the old electron with babel and webpack setup from four years ago to electron-vite

+1294 -2881
+6 -3
.eslintignore
··· 31 31 # OSX 32 32 .DS_Store 33 33 34 - # App packaged 34 + # Build outputs 35 35 release 36 + out 37 + dist 38 + dll 39 + 40 + # Old webpack build artifacts 36 41 app/*.main.prod.js 37 42 app/main.prod.js 38 43 app/main.prod.js.map ··· 40 45 app/renderer.prod.js.map 41 46 app/style.css 42 47 app/style.css.map 43 - dist 44 - dll 45 48 main.js 46 49 main.js.map 47 50
+5 -1
.gitignore
··· 32 32 33 33 # App packaged 34 34 release 35 + out 36 + dist 37 + 38 + # Old webpack build artifacts 35 39 app/main.prod.js 36 40 app/main.prod.js.map 37 41 app/renderer.prod.js 38 42 app/renderer.prod.js.map 39 43 app/style.css 40 44 app/style.css.map 41 - dist 42 45 dll 43 46 main.js 44 47 main.js.map ··· 51 54 keys.js 52 55 53 56 app/utils/pyodide/src 57 + src/renderer/utils/pyodide/src
app/actions/deviceActions.ts src/renderer/actions/deviceActions.ts
app/actions/experimentActions.ts src/renderer/actions/experimentActions.ts
app/actions/index.tsx src/renderer/actions/index.tsx
app/actions/pyodideActions.ts src/renderer/actions/pyodideActions.ts
+4 -8
app/app.global.css src/renderer/app.global.css
··· 1 - /* 2 - * @NOTE: Prepend a `~` to css file paths that are in your node_modules 3 - * See https://github.com/webpack-contrib/sass-loader#imports 4 - */ 5 - @import '~semantic-ui-css/semantic.min.css'; 6 - @import '~rc-slider/assets/index.css'; 7 - @import '~react-toastify/dist/ReactToastify.css'; 8 - @import '~lab.js/dist/lab.css'; 1 + @import 'semantic-ui-css/semantic.min.css'; 2 + @import 'rc-slider/assets/index.css'; 3 + @import 'react-toastify/dist/ReactToastify.css'; 4 + @import 'lab.js/dist/lab.css'; 9 5 10 6 body { 11 7 position: relative;
-50
app/app.html
··· 1 - <!DOCTYPE html> 2 - <html> 3 - <head> 4 - <meta charset="utf-8" /> 5 - <title>BrainWaves</title> 6 - <script> 7 - (() => { 8 - if ( 9 - typeof process !== 'object' || 10 - (typeof process === 'object' && !process.env.START_HOT) 11 - ) { 12 - const link = document.createElement('link'); 13 - link.rel = 'stylesheet'; 14 - link.href = './dist/style.css'; 15 - // HACK: Writing the script path should be done with webpack 16 - document.getElementsByTagName('head')[0].appendChild(link); 17 - } 18 - })(); 19 - </script> 20 - </head> 21 - <body> 22 - <div id="root"></div> 23 - <script> 24 - if (typeof process === 'object') { 25 - const scripts = []; 26 - 27 - if (process.env.NODE_ENV === 'development') { 28 - // Dynamically insert the DLL script in development env in the 29 - // renderer process 30 - scripts.push('../dll/renderer.dev.dll.js'); 31 - } 32 - if (process.env.START_HOT) { 33 - // Dynamically insert the bundled app script in the renderer process 34 - const port = process.env.PORT || 1212; 35 - scripts.push(`http://localhost:${port}/dist/renderer.dev.js`); 36 - } else { 37 - scripts.push('./dist/renderer.prod.js'); 38 - } 39 - 40 - if (scripts.length) { 41 - document.write( 42 - scripts 43 - .map((script) => `<script defer src="${script}"><\/script>`) 44 - .join('') 45 - ); 46 - } 47 - } 48 - </script> 49 - </body> 50 - </html>
app/app.icns

This is a binary file and will not be displayed.

app/assets/common/Brainwaves_Icon.png src/renderer/assets/common/Brainwaves_Icon.png
app/assets/common/Brainwaves_Icon_big.png src/renderer/assets/common/Brainwaves_Icon_big.png
app/assets/common/Custom.png src/renderer/assets/common/Custom.png
app/assets/common/EEG.png src/renderer/assets/common/EEG.png
app/assets/common/Hypothesis2.png src/renderer/assets/common/Hypothesis2.png
app/assets/common/Methods2.png src/renderer/assets/common/Methods2.png
app/assets/common/ResearchQuestion2.png src/renderer/assets/common/ResearchQuestion2.png
app/assets/common/app_logo.png src/renderer/assets/common/app_logo.png
app/assets/common/divingMan.svg src/renderer/assets/common/divingMan.svg
app/assets/common/fixationcross.png src/renderer/assets/common/fixationcross.png
app/assets/common/hypothesis.png src/renderer/assets/common/hypothesis.png
app/assets/common/methods.png src/renderer/assets/common/methods.png
app/assets/common/research_question.png src/renderer/assets/common/research_question.png
-14
app/assets/image.d.ts
··· 1 - declare module '*.png' { 2 - const value: any; 3 - export = value; 4 - } 5 - 6 - declare module '*.jpg' { 7 - const value: any; 8 - export = value; 9 - } 10 - 11 - declare module '*.svg' { 12 - const value: any; 13 - export = value; 14 - }
app/components/AnalyzeComponent.tsx src/renderer/components/AnalyzeComponent.tsx
app/components/CleanComponent/CleanSidebar.tsx src/renderer/components/CleanComponent/CleanSidebar.tsx
app/components/CleanComponent/index.tsx src/renderer/components/CleanComponent/index.tsx
app/components/CollectComponent/ConnectModal.tsx src/renderer/components/CollectComponent/ConnectModal.tsx
app/components/CollectComponent/HelpSidebar.tsx src/renderer/components/CollectComponent/HelpSidebar.tsx
app/components/CollectComponent/PreTestComponent.tsx src/renderer/components/CollectComponent/PreTestComponent.tsx
+4 -7
app/components/CollectComponent/RunComponent.tsx src/renderer/components/CollectComponent/RunComponent.tsx
··· 1 - import { remote } from 'electron'; 2 - import React, { Component, useCallback, useState } from 'react'; 1 + import React, { useCallback, useState } from 'react'; 3 2 import { Grid, Button, Segment, Header, Divider } from 'semantic-ui-react'; 4 3 import { Link } from 'react-router-dom'; 5 - import styles from '../styles/common.css'; 4 + import styles from '../styles/common.module.css'; 6 5 import InputCollect from '../InputCollect'; 7 6 import { injectEmotivMarker } from '../../utils/eeg/emotiv'; 8 7 import { injectMuseMarker } from '../../utils/eeg/muse'; ··· 11 10 import { checkFileExists, getImages } from '../../utils/filesystem/storage'; 12 11 import { ExperimentParameters } from '../../constants/interfaces'; 13 12 import { ExperimentActions as globalExperimentActions } from '../../actions'; 14 - 15 - const { dialog } = remote; 16 13 17 14 interface Props { 18 15 type: EXPERIMENTS; ··· 51 48 52 49 const handleStartExperiment = useCallback(async () => { 53 50 const filename = `${subject}-${group}-${session}-behavior.csv`; 54 - const fileExists = checkFileExists(title, subject, filename); 51 + const fileExists = await checkFileExists(title, subject, filename); 55 52 if (fileExists) { 56 53 const options = { 57 54 buttons: ['No', 'Yes'], 58 55 message: 59 56 'You already have a file with the same name. If you continue the experiment, the current file will be deleted. Do you really want to overwrite the data?', 60 57 }; 61 - const response = await dialog.showMessageBox(options); 58 + const response = await window.electronAPI.showMessageBox(options); 62 59 if (response.response === 1) { 63 60 ExperimentActions.Start(); 64 61 }
app/components/CollectComponent/index.tsx src/renderer/components/CollectComponent/index.tsx
app/components/DesignComponent/CustomDesignComponent.tsx src/renderer/components/DesignComponent/CustomDesignComponent.tsx
app/components/DesignComponent/ParamSlider.tsx src/renderer/components/DesignComponent/ParamSlider.tsx
app/components/DesignComponent/StimuliDesignColumn.tsx src/renderer/components/DesignComponent/StimuliDesignColumn.tsx
app/components/DesignComponent/StimuliRow.tsx src/renderer/components/DesignComponent/StimuliRow.tsx
+2 -3
app/components/DesignComponent/index.tsx src/renderer/components/DesignComponent/index.tsx
··· 11 11 } from 'semantic-ui-react'; 12 12 import { isNil } from 'lodash'; 13 13 import { toast } from 'react-toastify'; 14 - import { shell } from 'electron'; 15 - import styles from '../styles/common.css'; 14 + import styles from '../styles/common.module.css'; 16 15 import { EXPERIMENTS, SCREENS } from '../../constants/constants'; 17 16 import { readWorkspaces } from '../../utils/filesystem/storage'; 18 17 import { ··· 253 252 key={link.address} 254 253 secondary 255 254 onClick={() => { 256 - shell.openExternal(link.address); 255 + window.open(link.address, '_blank'); 257 256 }} 258 257 > 259 258 {link.name}
app/components/EEGExplorationComponent.tsx src/renderer/components/EEGExplorationComponent.tsx
app/components/ExperimentWindow.tsx src/renderer/components/ExperimentWindow.tsx
app/components/HomeComponent/ExperimentCard.tsx src/renderer/components/HomeComponent/ExperimentCard.tsx
app/components/HomeComponent/OverviewComponent.tsx src/renderer/components/HomeComponent/OverviewComponent.tsx
+2 -5
app/components/HomeComponent/index.tsx src/renderer/components/HomeComponent/index.tsx
··· 4 4 import { toast } from 'react-toastify'; 5 5 import * as moment from 'moment'; 6 6 import { History } from 'history'; 7 - import { remote } from 'electron'; 8 7 import { Observable } from 'rxjs'; 9 - import styles from '../styles/common.css'; 8 + import styles from '../styles/common.module.css'; 10 9 import { 11 10 EXPERIMENTS, 12 11 SCREENS, ··· 40 39 import { SignalQualityData } from '../../constants/interfaces'; 41 40 import { getExperimentFromType } from '../../utils/labjs/functions'; 42 41 import PyodidePlotWidget from '../PyodidePlotWidget'; 43 - 44 - const { dialog } = remote; 45 42 46 43 const HOME_STEPS = { 47 44 // TODO: maybe change the recent and new labels, but not necessary right now ··· 180 177 buttons: ['No', 'Yes'], 181 178 message: 'Do you really want to delete the experiment?', 182 179 }; 183 - const response = await dialog.showMessageBox(options); 180 + const response = await window.electronAPI.showMessageBox(options); 184 181 if (response.response === 1) { 185 182 deleteWorkspaceDir(dir); 186 183 this.setState({ recentWorkspaces: readWorkspaces() });
app/components/InputCollect.tsx src/renderer/components/InputCollect.tsx
app/components/InputModal.tsx src/renderer/components/InputModal.tsx
app/components/PreviewButtonComponent.tsx src/renderer/components/PreviewButtonComponent.tsx
app/components/PreviewExperimentComponent.tsx src/renderer/components/PreviewExperimentComponent.tsx
app/components/PyodidePlotWidget.tsx src/renderer/components/PyodidePlotWidget.tsx
app/components/SecondaryNavComponent/SecondaryNavSegment.tsx src/renderer/components/SecondaryNavComponent/SecondaryNavSegment.tsx
app/components/SecondaryNavComponent/index.tsx src/renderer/components/SecondaryNavComponent/index.tsx
app/components/SignalQualityIndicatorComponent.tsx src/renderer/components/SignalQualityIndicatorComponent.tsx
app/components/TopNavComponent/PrimaryNavSegment.tsx src/renderer/components/TopNavComponent/PrimaryNavSegment.tsx
app/components/TopNavComponent/index.tsx src/renderer/components/TopNavComponent/index.tsx
+15 -6
app/components/ViewerComponent.tsx src/renderer/components/ViewerComponent.tsx
··· 1 1 import React, { Component } from 'react'; 2 2 import { Subscription, Observable } from 'rxjs'; 3 3 import { isNil } from 'lodash'; 4 - import { WebviewTag } from 'electron'; 5 4 import { 6 5 MUSE_CHANNELS, 7 6 EMOTIV_CHANNELS, 8 7 DEVICES, 9 8 VIEWER_DEFAULTS, 10 - RESOURCE_PATH, 11 9 } from '../constants/constants'; 10 + 11 + type WebviewTag = HTMLElement & { 12 + send: (channel: string, ...args: unknown[]) => void; 13 + addEventListener: (event: string, handler: () => void) => void; 14 + }; 12 15 import { PipesEpoch, SignalQualityData } from '../constants/interfaces'; 13 16 14 - const Mousetrap = require('mousetrap'); 17 + import Mousetrap from 'mousetrap'; 15 18 16 19 interface Props { 17 20 signalQualityObservable: Observable<SignalQualityData>; ··· 23 26 channels: Array<string>; 24 27 domain: number; 25 28 autoScale: boolean; 29 + viewerUrl: string; 26 30 } 27 31 28 32 class ViewerComponent extends Component<Props, State> { ··· 36 40 ...VIEWER_DEFAULTS, 37 41 channels: 38 42 props.deviceType === DEVICES.EMOTIV ? EMOTIV_CHANNELS : MUSE_CHANNELS, 43 + viewerUrl: '', 39 44 }; 40 45 this.graphView = null; 41 46 this.signalQualitySubscription = null; 42 47 } 43 48 44 - componentDidMount() { 49 + async componentDidMount() { 50 + const viewerUrl = await window.electronAPI.getViewerUrl(); 51 + this.setState({ viewerUrl }); 45 52 this.graphView = document.querySelector('webview'); 46 53 this.graphView?.addEventListener('dom-ready', () => { 47 54 this.graphView?.send('initGraph', { ··· 115 122 116 123 render() { 117 124 const trueAsString = 'true' as any; 125 + if (!this.state.viewerUrl) { 126 + return null; 127 + } 118 128 return ( 119 129 <webview 120 130 id="eegView" 121 - src={`file://${RESOURCE_PATH}/app/viewer.html`} 131 + src={this.state.viewerUrl} 122 132 autosize={trueAsString} 123 - nodeintegration={trueAsString} 124 133 plugins={trueAsString} 125 134 /> 126 135 );
+4 -4
app/components/d3Classes/EEGViewer.js src/renderer/components/d3Classes/EEGViewer.js
··· 1 1 /* eslint prefer-template: 0 */ 2 - const d3 = require('d3'); 3 - const throttle = require('lodash/throttle'); 4 - const simplify = require('simplify-js'); 2 + import * as d3 from 'd3'; 3 + import throttle from 'lodash/throttle'; 4 + import simplify from 'simplify-js'; 5 5 6 6 class EEGViewer { 7 7 constructor(svg, parameters) { ··· 271 271 } 272 272 } 273 273 274 - module.exports = EEGViewer; 274 + export default EEGViewer;
app/components/styles/collect.css src/renderer/components/styles/collect.module.css
app/components/styles/common.css src/renderer/components/styles/common.module.css
app/components/styles/secondarynav.css src/renderer/components/styles/secondarynav.module.css
app/components/styles/topnavbar.css src/renderer/components/styles/topnavbar.module.css
app/components/svgs/ClickableHeadDiagramSVG.tsx src/renderer/components/svgs/ClickableHeadDiagramSVG.tsx
app/components/svgs/SignalQualityIndicatorSVG.tsx src/renderer/components/svgs/SignalQualityIndicatorSVG.tsx
+4 -4
app/constants/constants.ts src/renderer/constants/constants.ts
··· 133 133 TIMELINE = 'TIMELINE', 134 134 } 135 135 136 - export const RESOURCE_PATH = 137 - !process.env.NODE_ENV || process.env.NODE_ENV === 'production' 138 - ? process.resourcesPath // Live Mode 139 - : __dirname; // Dev Mode 136 + // Injected synchronously by the preload script via additionalArguments. 137 + // In dev: points to src/renderer/. In prod: points to process.resourcesPath. 138 + // eslint-disable-next-line @typescript-eslint/no-explicit-any 139 + export const RESOURCE_PATH: string = (window as any).__ELECTRON_RESOURCE_PATH__ || '';
app/constants/interfaces.ts src/renderer/constants/interfaces.ts
app/containers/AnalyzeContainer.ts src/renderer/containers/AnalyzeContainer.ts
app/containers/App.tsx src/renderer/containers/App.tsx
app/containers/CleanContainer.ts src/renderer/containers/CleanContainer.ts
app/containers/CollectContainer.ts src/renderer/containers/CollectContainer.ts
app/containers/ExperimentDesignContainer.ts src/renderer/containers/ExperimentDesignContainer.ts
app/containers/HomeContainer.ts src/renderer/containers/HomeContainer.ts
+1 -2
app/containers/Root.tsx src/renderer/containers/Root.tsx
··· 1 1 import React from 'react'; 2 2 import { Provider } from 'react-redux'; 3 3 import { ConnectedRouter } from 'connected-react-router'; 4 - import { hot } from 'react-hot-loader/root'; 5 4 import { History } from 'history'; 6 5 import { Store } from '../reducers/types'; 7 6 import Routes from '../routes'; ··· 20 19 </Provider> 21 20 ); 22 21 23 - export default hot(Root); 22 + export default Root;
app/containers/TopNavBarContainer.ts src/renderer/containers/TopNavBarContainer.ts
app/css.d.ts src/renderer/css.d.ts
app/epics/deviceEpics.ts src/renderer/epics/deviceEpics.ts
+28 -21
app/epics/experimentEpics.ts src/renderer/epics/experimentEpics.ts
··· 1 - import { combineEpics, Epic } from 'redux-observable'; 2 - import { from, of, ObservableInput } from 'rxjs'; 1 + import { combineEpics, Epic, ofType } from 'redux-observable'; 2 + import { from, of } from 'rxjs'; 3 3 import { 4 4 map, 5 - mapTo, 6 5 mergeMap, 7 6 pluck, 8 7 filter, 9 8 takeUntil, 10 9 throttleTime, 11 - ignoreElements, 12 10 tap, 13 11 } from 'rxjs/operators'; 14 12 import { isActionOf } from '../utils/redux'; ··· 48 46 action$.pipe( 49 47 filter(isActionOf(ExperimentActions.CreateNewWorkspace)), 50 48 pluck<'payload', WorkSpaceInfo>('payload'), 51 - tap((workspaceInfo) => createWorkspaceDir(workspaceInfo.title)), 49 + mergeMap(async (workspaceInfo) => { 50 + await createWorkspaceDir(workspaceInfo.title); 51 + return workspaceInfo; 52 + }), 52 53 mergeMap((workspaceInfo) => { 53 54 const experiment = getExperimentFromType(workspaceInfo.type); 54 55 return of( ··· 64 65 action$.pipe( 65 66 filter(isActionOf(ExperimentActions.Start)), 66 67 filter(() => !state$.value.experiment.isRunning), 67 - map(() => { 68 + mergeMap(async () => { 68 69 if ( 69 70 state$.value.device.connectionStatus === CONNECTION_STATUS.CONNECTED 70 71 ) { 71 - const writeStream = createEEGWriteStream( 72 + const streamId = await createEEGWriteStream( 72 73 state$.value.experiment.title, 73 74 state$.value.experiment.subject, 74 75 state$.value.experiment.group, 75 76 state$.value.experiment.session 76 77 ); 77 78 78 - if (!writeStream) { 79 - return; 79 + if (!streamId) { 80 + return true; 80 81 } 81 82 writeHeader( 82 - writeStream, 83 + streamId, 83 84 state$.value.device.deviceType === DEVICES.EMOTIV 84 85 ? EMOTIV_CHANNELS 85 86 : MUSE_CHANNELS ··· 95 96 state$.value.device.rawObservable 96 97 .pipe( 97 98 takeUntil( 98 - action$.ofType( 99 - ExperimentActions.Stop.type, 100 - ExperimentActions.ExperimentCleanup.type 99 + action$.pipe( 100 + ofType( 101 + ExperimentActions.Stop.type, 102 + ExperimentActions.ExperimentCleanup.type 103 + ) 101 104 ) 102 105 ) 103 106 ) 104 - .subscribe((eegData) => writeEEGData(writeStream, eegData)); 107 + .subscribe((eegData) => writeEEGData(streamId, eegData)); 105 108 } 109 + return true; 106 110 }), 107 - mapTo(true), 108 111 map(ExperimentActions.SetIsRunning) 109 112 ); 110 113 ··· 161 164 ); 162 165 163 166 const autoSaveEpic: Epic<any, ExperimentActionType, RootState> = (action$) => 164 - action$.ofType('@@router/LOCATION_CHANGE').pipe( 167 + action$.pipe(ofType('@@router/LOCATION_CHANGE'), 165 168 pluck('payload', 'pathname'), 166 169 filter((pathname) => pathname !== '/' && pathname !== '/home'), 167 170 map(ExperimentActions.SaveWorkspace) ··· 180 183 ? state$.value.experiment.title.length > 1 181 184 : false 182 185 ), 183 - map(() => Date.now()), 184 - tap((now) => 185 - storeExperimentState({ ...state$.value.experiment, dateModified: now }) 186 - ), 186 + mergeMap(async () => { 187 + const now = Date.now(); 188 + await storeExperimentState({ 189 + ...state$.value.experiment, 190 + dateModified: now, 191 + }); 192 + return now; 193 + }), 187 194 map(ExperimentActions.SetDateModified) 188 195 ); 189 196 ··· 191 198 action$, 192 199 state$ 193 200 ) => 194 - action$.ofType('@@router/LOCATION_CHANGE').pipe( 201 + action$.pipe(ofType('@@router/LOCATION_CHANGE'), 195 202 tap((pathname) => console.log('navigation', pathname)), 196 203 pluck('payload', 'location', 'pathname'), 197 204 tap((pathname) => console.log('navigation', pathname)),
app/epics/index.ts src/renderer/epics/index.ts
+1 -1
app/epics/pyodideEpics.ts src/renderer/epics/pyodideEpics.ts
··· 1 1 import { combineEpics, Epic } from 'redux-observable'; 2 - import Rx, { fromEvent, Observable, ObservableInput, of } from 'rxjs'; 2 + import { fromEvent, Observable, ObservableInput, of } from 'rxjs'; 3 3 import { map, mergeMap, tap, pluck, filter } from 'rxjs/operators'; 4 4 import { toast } from 'react-toastify'; 5 5 import { isActionOf } from '../utils/redux';
app/experiments/cat_dog/content_background.js src/renderer/experiments/cat_dog/content_background.js
app/experiments/cat_dog/content_overview.js src/renderer/experiments/cat_dog/content_overview.js
app/experiments/cat_dog/stimuli/cats/target-1.jpg src/renderer/experiments/cat_dog/stimuli/cats/target-1.jpg
app/experiments/cat_dog/stimuli/cats/target-2.jpg src/renderer/experiments/cat_dog/stimuli/cats/target-2.jpg
app/experiments/cat_dog/stimuli/cats/target-3.jpg src/renderer/experiments/cat_dog/stimuli/cats/target-3.jpg
app/experiments/cat_dog/stimuli/cats/target-4.jpg src/renderer/experiments/cat_dog/stimuli/cats/target-4.jpg
app/experiments/cat_dog/stimuli/dogs/nontarget-1.jpg src/renderer/experiments/cat_dog/stimuli/dogs/nontarget-1.jpg
app/experiments/cat_dog/stimuli/dogs/nontarget-2.jpg src/renderer/experiments/cat_dog/stimuli/dogs/nontarget-2.jpg
app/experiments/cat_dog/stimuli/dogs/nontarget-3.jpg src/renderer/experiments/cat_dog/stimuli/dogs/nontarget-3.jpg
app/experiments/cat_dog/stimuli/dogs/nontarget-4.jpg src/renderer/experiments/cat_dog/stimuli/dogs/nontarget-4.jpg
app/experiments/custom/content_background.js src/renderer/experiments/custom/content_background.js
app/experiments/custom/content_overview.js src/renderer/experiments/custom/content_overview.js
app/experiments/custom/content_protocol.js src/renderer/experiments/custom/content_protocol.js
app/experiments/custom/experiment.js src/renderer/experiments/custom/experiment.js
app/experiments/custom/icon.png src/renderer/experiments/custom/icon.png
app/experiments/custom/index.ts src/renderer/experiments/custom/index.ts
app/experiments/faces_houses/content_background.js src/renderer/experiments/faces_houses/content_background.js
app/experiments/faces_houses/content_overview.js src/renderer/experiments/faces_houses/content_overview.js
app/experiments/faces_houses/content_protocol.js src/renderer/experiments/faces_houses/content_protocol.js
app/experiments/faces_houses/experiment.ts src/renderer/experiments/faces_houses/experiment.ts
app/experiments/faces_houses/icon.png src/renderer/experiments/faces_houses/icon.png
app/experiments/faces_houses/index.ts src/renderer/experiments/faces_houses/index.ts
app/experiments/faces_houses/n170_example.png src/renderer/experiments/faces_houses/n170_example.png
+1 -1
app/experiments/faces_houses/params.ts src/renderer/experiments/faces_houses/params.ts
··· 1 - import * as path from 'path'; 1 + import path from 'pathe'; 2 2 import { EVENTS, RESOURCE_PATH } from '../../constants/constants'; 3 3 4 4 const parentDir = path.join(
app/experiments/faces_houses/stimuli/LICENSE.txt src/renderer/experiments/faces_houses/stimuli/LICENSE.txt
app/experiments/faces_houses/stimuli/faces/Face1.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face1.jpg
app/experiments/faces_houses/stimuli/faces/Face10.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face10.jpg
app/experiments/faces_houses/stimuli/faces/Face11.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face11.jpg
app/experiments/faces_houses/stimuli/faces/Face12.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face12.jpg
app/experiments/faces_houses/stimuli/faces/Face13.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face13.jpg
app/experiments/faces_houses/stimuli/faces/Face14.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face14.jpg
app/experiments/faces_houses/stimuli/faces/Face15.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face15.jpg
app/experiments/faces_houses/stimuli/faces/Face16.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face16.jpg
app/experiments/faces_houses/stimuli/faces/Face17.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face17.jpg
app/experiments/faces_houses/stimuli/faces/Face18.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face18.jpg
app/experiments/faces_houses/stimuli/faces/Face19.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face19.jpg
app/experiments/faces_houses/stimuli/faces/Face2.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face2.jpg
app/experiments/faces_houses/stimuli/faces/Face20.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face20.jpg
app/experiments/faces_houses/stimuli/faces/Face21.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face21.jpg
app/experiments/faces_houses/stimuli/faces/Face22.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face22.jpg
app/experiments/faces_houses/stimuli/faces/Face23.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face23.jpg
app/experiments/faces_houses/stimuli/faces/Face24.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face24.jpg
app/experiments/faces_houses/stimuli/faces/Face25.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face25.jpg
app/experiments/faces_houses/stimuli/faces/Face26.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face26.jpg
app/experiments/faces_houses/stimuli/faces/Face27.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face27.jpg
app/experiments/faces_houses/stimuli/faces/Face28.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face28.jpg
app/experiments/faces_houses/stimuli/faces/Face29.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face29.jpg
app/experiments/faces_houses/stimuli/faces/Face3.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face3.jpg
app/experiments/faces_houses/stimuli/faces/Face30.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face30.jpg
app/experiments/faces_houses/stimuli/faces/Face4.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face4.jpg
app/experiments/faces_houses/stimuli/faces/Face5.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face5.jpg
app/experiments/faces_houses/stimuli/faces/Face6.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face6.jpg
app/experiments/faces_houses/stimuli/faces/Face7.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face7.jpg
app/experiments/faces_houses/stimuli/faces/Face8.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face8.jpg
app/experiments/faces_houses/stimuli/faces/Face9.jpg src/renderer/experiments/faces_houses/stimuli/faces/Face9.jpg
app/experiments/faces_houses/stimuli/houses/House1.jpg src/renderer/experiments/faces_houses/stimuli/houses/House1.jpg
app/experiments/faces_houses/stimuli/houses/House10.jpg src/renderer/experiments/faces_houses/stimuli/houses/House10.jpg
app/experiments/faces_houses/stimuli/houses/House11.jpg src/renderer/experiments/faces_houses/stimuli/houses/House11.jpg
app/experiments/faces_houses/stimuli/houses/House12.jpg src/renderer/experiments/faces_houses/stimuli/houses/House12.jpg
app/experiments/faces_houses/stimuli/houses/House13.jpg src/renderer/experiments/faces_houses/stimuli/houses/House13.jpg
app/experiments/faces_houses/stimuli/houses/House14.jpg src/renderer/experiments/faces_houses/stimuli/houses/House14.jpg
app/experiments/faces_houses/stimuli/houses/House15.jpg src/renderer/experiments/faces_houses/stimuli/houses/House15.jpg
app/experiments/faces_houses/stimuli/houses/House16.jpg src/renderer/experiments/faces_houses/stimuli/houses/House16.jpg
app/experiments/faces_houses/stimuli/houses/House17.jpg src/renderer/experiments/faces_houses/stimuli/houses/House17.jpg
app/experiments/faces_houses/stimuli/houses/House18.jpg src/renderer/experiments/faces_houses/stimuli/houses/House18.jpg
app/experiments/faces_houses/stimuli/houses/House19.jpg src/renderer/experiments/faces_houses/stimuli/houses/House19.jpg
app/experiments/faces_houses/stimuli/houses/House2.jpg src/renderer/experiments/faces_houses/stimuli/houses/House2.jpg
app/experiments/faces_houses/stimuli/houses/House20.jpg src/renderer/experiments/faces_houses/stimuli/houses/House20.jpg
app/experiments/faces_houses/stimuli/houses/House21.jpg src/renderer/experiments/faces_houses/stimuli/houses/House21.jpg
app/experiments/faces_houses/stimuli/houses/House22.jpg src/renderer/experiments/faces_houses/stimuli/houses/House22.jpg
app/experiments/faces_houses/stimuli/houses/House23.jpg src/renderer/experiments/faces_houses/stimuli/houses/House23.jpg
app/experiments/faces_houses/stimuli/houses/House24.jpg src/renderer/experiments/faces_houses/stimuli/houses/House24.jpg
app/experiments/faces_houses/stimuli/houses/House25.jpg src/renderer/experiments/faces_houses/stimuli/houses/House25.jpg
app/experiments/faces_houses/stimuli/houses/House26.jpg src/renderer/experiments/faces_houses/stimuli/houses/House26.jpg
app/experiments/faces_houses/stimuli/houses/House27.jpg src/renderer/experiments/faces_houses/stimuli/houses/House27.jpg
app/experiments/faces_houses/stimuli/houses/House28.jpg src/renderer/experiments/faces_houses/stimuli/houses/House28.jpg
app/experiments/faces_houses/stimuli/houses/House29.jpg src/renderer/experiments/faces_houses/stimuli/houses/House29.jpg
app/experiments/faces_houses/stimuli/houses/House3.jpg src/renderer/experiments/faces_houses/stimuli/houses/House3.jpg
app/experiments/faces_houses/stimuli/houses/House30.jpg src/renderer/experiments/faces_houses/stimuli/houses/House30.jpg
app/experiments/faces_houses/stimuli/houses/House4.jpg src/renderer/experiments/faces_houses/stimuli/houses/House4.jpg
app/experiments/faces_houses/stimuli/houses/House5.jpg src/renderer/experiments/faces_houses/stimuli/houses/House5.jpg
app/experiments/faces_houses/stimuli/houses/House6.jpg src/renderer/experiments/faces_houses/stimuli/houses/House6.jpg
app/experiments/faces_houses/stimuli/houses/House7.jpg src/renderer/experiments/faces_houses/stimuli/houses/House7.jpg
app/experiments/faces_houses/stimuli/houses/House8.jpg src/renderer/experiments/faces_houses/stimuli/houses/House8.jpg
app/experiments/faces_houses/stimuli/houses/House9.jpg src/renderer/experiments/faces_houses/stimuli/houses/House9.jpg
app/experiments/multitasking/content_background.js src/renderer/experiments/multitasking/content_background.js
app/experiments/multitasking/content_overview.js src/renderer/experiments/multitasking/content_overview.js
app/experiments/multitasking/content_protocol.js src/renderer/experiments/multitasking/content_protocol.js
app/experiments/multitasking/experiment.ts src/renderer/experiments/multitasking/experiment.ts
app/experiments/multitasking/icon.png src/renderer/experiments/multitasking/icon.png
app/experiments/multitasking/index.ts src/renderer/experiments/multitasking/index.ts
app/experiments/multitasking/params.ts src/renderer/experiments/multitasking/params.ts
app/experiments/multitasking/stimuli/all_conditions.png src/renderer/experiments/multitasking/stimuli/all_conditions.png
app/experiments/multitasking/stimuli/diamond_2.png src/renderer/experiments/multitasking/stimuli/diamond_2.png
app/experiments/multitasking/stimuli/diamond_3.png src/renderer/experiments/multitasking/stimuli/diamond_3.png
app/experiments/multitasking/stimuli/example_1.png src/renderer/experiments/multitasking/stimuli/example_1.png
app/experiments/multitasking/stimuli/example_2.png src/renderer/experiments/multitasking/stimuli/example_2.png
app/experiments/multitasking/stimuli/example_3.png src/renderer/experiments/multitasking/stimuli/example_3.png
app/experiments/multitasking/stimuli/example_4.png src/renderer/experiments/multitasking/stimuli/example_4.png
app/experiments/multitasking/stimuli/filling.png src/renderer/experiments/multitasking/stimuli/filling.png
app/experiments/multitasking/stimuli/multiConditionDots.png src/renderer/experiments/multitasking/stimuli/multiConditionDots.png
app/experiments/multitasking/stimuli/multiConditionShape.png src/renderer/experiments/multitasking/stimuli/multiConditionShape.png
app/experiments/multitasking/stimuli/rectangle_2.png src/renderer/experiments/multitasking/stimuli/rectangle_2.png
app/experiments/multitasking/stimuli/rectangle_3.png src/renderer/experiments/multitasking/stimuli/rectangle_3.png
app/experiments/multitasking/stimuli/shape.png src/renderer/experiments/multitasking/stimuli/shape.png
app/experiments/multitasking/utils.ts src/renderer/experiments/multitasking/utils.ts
app/experiments/search/content_background.js src/renderer/experiments/search/content_background.js
app/experiments/search/content_overview.js src/renderer/experiments/search/content_overview.js
app/experiments/search/content_protocol.js src/renderer/experiments/search/content_protocol.js
app/experiments/search/experiment.ts src/renderer/experiments/search/experiment.ts
app/experiments/search/icon.png src/renderer/experiments/search/icon.png
app/experiments/search/index.ts src/renderer/experiments/search/index.ts
app/experiments/search/params.ts src/renderer/experiments/search/params.ts
app/experiments/search/stimuli/conditionNoOrangeT.png src/renderer/experiments/search/stimuli/conditionNoOrangeT.png
app/experiments/search/stimuli/conditionOrangeT.png src/renderer/experiments/search/stimuli/conditionOrangeT.png
app/experiments/search/utils.ts src/renderer/experiments/search/utils.ts
app/experiments/ssvep/content_background.js src/renderer/experiments/ssvep/content_background.js
app/experiments/ssvep/content_overview.js src/renderer/experiments/ssvep/content_overview.js
app/experiments/ssvep/content_protocol.js src/renderer/experiments/ssvep/content_protocol.js
app/experiments/ssvep/index.ts src/renderer/experiments/ssvep/index.ts
app/experiments/ssvep/params.js src/renderer/experiments/ssvep/params.js
app/experiments/ssvep/stimuli/Checkerboard_pattern.svg src/renderer/experiments/ssvep/stimuli/Checkerboard_pattern.svg
app/experiments/ssvep/stimuli/Checkerboard_pattern_neg.svg src/renderer/experiments/ssvep/stimuli/Checkerboard_pattern_neg.svg
app/experiments/stroop/content_background.js src/renderer/experiments/stroop/content_background.js
app/experiments/stroop/content_overview.js src/renderer/experiments/stroop/content_overview.js
app/experiments/stroop/content_protocol.js src/renderer/experiments/stroop/content_protocol.js
app/experiments/stroop/experiment.ts src/renderer/experiments/stroop/experiment.ts
app/experiments/stroop/icon.png src/renderer/experiments/stroop/icon.png
app/experiments/stroop/index.ts src/renderer/experiments/stroop/index.ts
app/experiments/stroop/params.ts src/renderer/experiments/stroop/params.ts
app/experiments/stroop/stimuli/match_b.png src/renderer/experiments/stroop/stimuli/match_b.png
app/experiments/stroop/stimuli/match_g.png src/renderer/experiments/stroop/stimuli/match_g.png
app/experiments/stroop/stimuli/match_p.png src/renderer/experiments/stroop/stimuli/match_p.png
app/experiments/stroop/stimuli/match_r.png src/renderer/experiments/stroop/stimuli/match_r.png
app/experiments/stroop/stimuli/mismatch10_r.png src/renderer/experiments/stroop/stimuli/mismatch10_r.png
app/experiments/stroop/stimuli/mismatch11_g.png src/renderer/experiments/stroop/stimuli/mismatch11_g.png
app/experiments/stroop/stimuli/mismatch12_b.png src/renderer/experiments/stroop/stimuli/mismatch12_b.png
app/experiments/stroop/stimuli/mismatch1_b.png src/renderer/experiments/stroop/stimuli/mismatch1_b.png
app/experiments/stroop/stimuli/mismatch2_g.png src/renderer/experiments/stroop/stimuli/mismatch2_g.png
app/experiments/stroop/stimuli/mismatch3_p.png src/renderer/experiments/stroop/stimuli/mismatch3_p.png
app/experiments/stroop/stimuli/mismatch4_b.png src/renderer/experiments/stroop/stimuli/mismatch4_b.png
app/experiments/stroop/stimuli/mismatch5_p.png src/renderer/experiments/stroop/stimuli/mismatch5_p.png
app/experiments/stroop/stimuli/mismatch6_r.png src/renderer/experiments/stroop/stimuli/mismatch6_r.png
app/experiments/stroop/stimuli/mismatch7_p.png src/renderer/experiments/stroop/stimuli/mismatch7_p.png
app/experiments/stroop/stimuli/mismatch8_r.png src/renderer/experiments/stroop/stimuli/mismatch8_r.png
app/experiments/stroop/stimuli/mismatch9_g.png src/renderer/experiments/stroop/stimuli/mismatch9_g.png
-20
app/index.tsx
··· 1 - import React, { Fragment } from 'react'; 2 - import { render } from 'react-dom'; 3 - import { AppContainer as ReactHotAppContainer } from 'react-hot-loader'; 4 - import { configuredStore, history } from './store'; 5 - import './app.global.css'; 6 - 7 - const store = configuredStore(); 8 - 9 - const AppContainer = process.env.PLAIN_HMR ? Fragment : ReactHotAppContainer; 10 - 11 - document.addEventListener('DOMContentLoaded', () => { 12 - // eslint-disable-next-line global-require 13 - const Root = require('./containers/Root').default; 14 - render( 15 - <AppContainer> 16 - <Root store={store} history={history} /> 17 - </AppContainer>, 18 - document.getElementById('root') 19 - ); 20 - });
-143
app/main.dev.ts
··· 1 - /* eslint global-require: off, no-console: off */ 2 - 3 - /** 4 - * This module executes inside of electron's main process. You can start 5 - * electron renderer process from here and communicate with the other processes 6 - * through IPC. 7 - * 8 - * When running `yarn build` or `yarn build-main`, this file is compiled to 9 - * `./app/main.prod.js` using webpack. This gives us some performance wins. 10 - */ 11 - import 'core-js/stable'; 12 - import 'regenerator-runtime/runtime'; 13 - import { app, BrowserWindow, ipcMain } from 'electron'; 14 - import path from 'path'; 15 - import { autoUpdater } from 'electron-updater'; 16 - import log from 'electron-log'; 17 - import { loadDialog } from './utils/filesystem/dialog'; 18 - import MenuBuilder from './menu'; 19 - 20 - app.commandLine.appendSwitch( 21 - 'enable-experimental-web-platform-features', 22 - 'true' 23 - ); 24 - app.commandLine.appendSwitch('user-activation-v2', 'true'); 25 - 26 - app.allowRendererProcessReuse = false; 27 - 28 - export default class AppUpdater { 29 - constructor() { 30 - log.transports.file.level = 'info'; 31 - autoUpdater.logger = log; 32 - autoUpdater.checkForUpdatesAndNotify(); 33 - } 34 - } 35 - 36 - let mainWindow: BrowserWindow | null = null; 37 - 38 - if (process.env.NODE_ENV === 'production') { 39 - const sourceMapSupport = require('source-map-support'); 40 - sourceMapSupport.install(); 41 - } 42 - 43 - if ( 44 - process.env.NODE_ENV === 'development' || 45 - process.env.DEBUG_PROD === 'true' 46 - ) { 47 - require('electron-debug')(); 48 - // TODO: test whether we need this. Was this added to solve native dep issues? 49 - const p = path.join(__dirname, '..', 'app', 'node_modules'); 50 - require('module').globalPaths.push(p); 51 - } 52 - 53 - const installExtensions = async () => { 54 - const installer = require('electron-devtools-installer'); 55 - const forceDownload = !!process.env.UPGRADE_EXTENSIONS; 56 - const extensions = ['REACT_DEVELOPER_TOOLS', 'REDUX_DEVTOOLS']; 57 - 58 - return Promise.all( 59 - extensions.map((name) => installer.default(installer[name], forceDownload)) 60 - ).catch(console.log); 61 - }; 62 - 63 - const createWindow = async () => { 64 - if ( 65 - process.env.NODE_ENV === 'development' || 66 - process.env.DEBUG_PROD === 'true' 67 - ) { 68 - await installExtensions(); 69 - } 70 - 71 - mainWindow = new BrowserWindow({ 72 - show: false, 73 - width: 1280, 74 - height: 800, 75 - webPreferences: 76 - (process.env.NODE_ENV === 'development' || 77 - process.env.E2E_BUILD === 'true') && 78 - process.env.ERB_SECURE !== 'true' 79 - ? { 80 - nodeIntegration: true, 81 - webviewTag: true, 82 - } 83 - : { 84 - preload: path.join(__dirname, 'dist/renderer.prod.js'), 85 - webviewTag: true, 86 - }, 87 - }); 88 - 89 - mainWindow.setMinimumSize(1075, 708); 90 - 91 - // IPC Listener for file dialog events 92 - ipcMain.on('loadDialog', loadDialog); 93 - 94 - mainWindow.loadURL(`file://${__dirname}/app.html`); 95 - 96 - // @TODO: Use 'ready-to-show' event 97 - // https://github.com/electron/electron/blob/master/docs/api/browser-window.md#using-ready-to-show-event 98 - mainWindow.webContents.on('did-finish-load', () => { 99 - if (!mainWindow) { 100 - throw new Error('"mainWindow" is not defined'); 101 - } 102 - if (process.env.START_MINIMIZED) { 103 - mainWindow.minimize(); 104 - } else { 105 - mainWindow.show(); 106 - mainWindow.focus(); 107 - } 108 - }); 109 - 110 - mainWindow.on('closed', () => { 111 - mainWindow = null; 112 - }); 113 - 114 - const menuBuilder = new MenuBuilder(mainWindow); 115 - menuBuilder.buildMenu(); 116 - // Remove this if your app does not use auto updates 117 - // eslint-disable-next-line 118 - new AppUpdater(); 119 - }; 120 - 121 - /** 122 - * Add event listeners... 123 - */ 124 - app.on('window-all-closed', () => { 125 - // Respect the OSX convention of having the application in memory even 126 - // after all windows have been closed 127 - if (process.platform !== 'darwin') { 128 - app.quit(); 129 - } 130 - }); 131 - 132 - if (process.env.E2E_BUILD === 'true') { 133 - // eslint-disable-next-line promise/catch-or-return 134 - app.whenReady().then(createWindow); 135 - } else { 136 - app.on('ready', createWindow); 137 - } 138 - 139 - app.on('activate', () => { 140 - // On macOS it's common to re-create a window in the app when the 141 - // dock icon is clicked and there are no other windows open. 142 - if (mainWindow === null) createWindow(); 143 - });
-1
app/main.prod.js.LICENSE.txt
··· 1 - /*! http://mths.be/fromcodepoint v0.1.0 by @mathias */
app/menu.ts src/main/menu.ts
-1179
app/package-lock.json
··· 1 - { 2 - "name": "BrainWaves", 3 - "version": "0.15.1", 4 - "lockfileVersion": 1, 5 - "requires": true, 6 - "dependencies": { 7 - "@babel/register": { 8 - "version": "7.10.1", 9 - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.10.1.tgz", 10 - "integrity": "sha512-sl96+kB3IA2B9EzpwwBmYadOT14vw3KaXOknGDbJaZCOj52GDA4Tivudq9doCJcB+bEIKCEARZYwRgBBsCGXyg==", 11 - "dev": true, 12 - "requires": { 13 - "find-cache-dir": "^2.0.0", 14 - "lodash": "^4.17.13", 15 - "make-dir": "^2.1.0", 16 - "pirates": "^4.0.0", 17 - "source-map-support": "^0.5.16" 18 - } 19 - }, 20 - "@babel/runtime": { 21 - "version": "7.10.2", 22 - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.2.tgz", 23 - "integrity": "sha512-6sF3uQw2ivImfVIl62RZ7MXhO2tap69WeWK57vAaimT6AZbE4FbqjdEJIN1UqoD6wI6B+1n9UiagafH1sxjOtg==", 24 - "requires": { 25 - "regenerator-runtime": "^0.13.4" 26 - } 27 - }, 28 - "@babel/runtime-corejs2": { 29 - "version": "7.10.2", 30 - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.10.2.tgz", 31 - "integrity": "sha512-ZLwsFnNm3WpIARU1aLFtufjMHsmEnc8TjtrfAjmbgMbeoyR+LuQoyESoNdTfeDhL6IdY12SpeycXMgSgl8XGXA==", 32 - "requires": { 33 - "core-js": "^2.6.5", 34 - "regenerator-runtime": "^0.13.4" 35 - } 36 - }, 37 - "@neurosity/pipes": { 38 - "version": "3.2.3", 39 - "resolved": "https://registry.npmjs.org/@neurosity/pipes/-/pipes-3.2.3.tgz", 40 - "integrity": "sha512-aDa/iTe7OUbwgFFnRgGcB/hTgBdkr21E5vQbpOXZpByQijB4czGDgXJfFtI3zuLKST3bZuiQ7/ZPBA3+P8GENA==", 41 - "requires": { 42 - "dsp.js": "^1.0.1", 43 - "fili": "^2.0.1", 44 - "rxjs": "^6.3.1" 45 - } 46 - }, 47 - "@nteract/commutable": { 48 - "version": "7.2.12", 49 - "resolved": "https://registry.npmjs.org/@nteract/commutable/-/commutable-7.2.12.tgz", 50 - "integrity": "sha512-6cgjLkH5/xButb1VQEGwHxBa93GBAShyJFyG2jMYONZqy6vPU2MwlMRQYA1aaa16LsOcei1FFf7DKOBWYkue3g==", 51 - "requires": { 52 - "immutable": "^4.0.0-rc.12", 53 - "uuid": "^8.0.0" 54 - }, 55 - "dependencies": { 56 - "uuid": { 57 - "version": "8.1.0", 58 - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.1.0.tgz", 59 - "integrity": "sha512-CI18flHDznR0lq54xBycOVmphdCYnQLKn8abKn7PXUiKUGdEd+/l9LWNJmugXel4hXq7S+RMNl34ecyC9TntWg==" 60 - } 61 - } 62 - }, 63 - "@nteract/messaging": { 64 - "version": "7.0.7", 65 - "resolved": "https://registry.npmjs.org/@nteract/messaging/-/messaging-7.0.7.tgz", 66 - "integrity": "sha512-ucfiyQf48ecYOKrIUrIwb20LfPhx2YdM0iSejVycM7QR3CP8V3p4CccdCjcJHbv7aI9RgfgGp4FUp8Exbh3weA==", 67 - "requires": { 68 - "@nteract/types": "^6.0.7", 69 - "@types/uuid": "^8.0.0", 70 - "lodash.clonedeep": "^4.5.0", 71 - "rxjs": "^6.3.3", 72 - "uuid": "^8.0.0" 73 - }, 74 - "dependencies": { 75 - "uuid": { 76 - "version": "8.1.0", 77 - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.1.0.tgz", 78 - "integrity": "sha512-CI18flHDznR0lq54xBycOVmphdCYnQLKn8abKn7PXUiKUGdEd+/l9LWNJmugXel4hXq7S+RMNl34ecyC9TntWg==" 79 - } 80 - } 81 - }, 82 - "@nteract/types": { 83 - "version": "6.0.7", 84 - "resolved": "https://registry.npmjs.org/@nteract/types/-/types-6.0.7.tgz", 85 - "integrity": "sha512-sv4X97iXVHXVoPBl2m/xxO396GwujUSfm+7Cx6W5ziGUxSBjs4ydGTn2Uy6zv1MRa7Gwv9uJ/Sl8hgm7vY80Tw==", 86 - "requires": { 87 - "@nteract/commutable": "^7.2.12", 88 - "immutable": "^4.0.0-rc.12", 89 - "rxjs": "^6.3.3", 90 - "uuid": "^8.0.0" 91 - }, 92 - "dependencies": { 93 - "uuid": { 94 - "version": "8.1.0", 95 - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.1.0.tgz", 96 - "integrity": "sha512-CI18flHDznR0lq54xBycOVmphdCYnQLKn8abKn7PXUiKUGdEd+/l9LWNJmugXel4hXq7S+RMNl34ecyC9TntWg==" 97 - } 98 - } 99 - }, 100 - "@types/uuid": { 101 - "version": "8.0.0", 102 - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.0.0.tgz", 103 - "integrity": "sha512-xSQfNcvOiE5f9dyd4Kzxbof1aTrLobL278pGLKOZI6esGfZ7ts9Ka16CzIN6Y8hFHE1C7jIBZokULhK1bOgjRw==" 104 - }, 105 - "abbrev": { 106 - "version": "1.1.1", 107 - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", 108 - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" 109 - }, 110 - "ansi-regex": { 111 - "version": "2.1.1", 112 - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 113 - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" 114 - }, 115 - "aproba": { 116 - "version": "1.2.0", 117 - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", 118 - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" 119 - }, 120 - "are-we-there-yet": { 121 - "version": "1.1.5", 122 - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", 123 - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", 124 - "requires": { 125 - "delegates": "^1.0.0", 126 - "readable-stream": "^2.0.6" 127 - } 128 - }, 129 - "async": { 130 - "version": "2.6.4", 131 - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", 132 - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", 133 - "requires": { 134 - "lodash": "^4.17.14" 135 - } 136 - }, 137 - "balanced-match": { 138 - "version": "1.0.0", 139 - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 140 - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 141 - }, 142 - "base64-js": { 143 - "version": "1.3.1", 144 - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", 145 - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" 146 - }, 147 - "bl": { 148 - "version": "4.0.3", 149 - "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz", 150 - "integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==", 151 - "requires": { 152 - "buffer": "^5.5.0", 153 - "inherits": "^2.0.4", 154 - "readable-stream": "^3.4.0" 155 - }, 156 - "dependencies": { 157 - "readable-stream": { 158 - "version": "3.6.0", 159 - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 160 - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 161 - "requires": { 162 - "inherits": "^2.0.3", 163 - "string_decoder": "^1.1.1", 164 - "util-deprecate": "^1.0.1" 165 - } 166 - } 167 - } 168 - }, 169 - "brace-expansion": { 170 - "version": "1.1.11", 171 - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 172 - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 173 - "requires": { 174 - "balanced-match": "^1.0.0", 175 - "concat-map": "0.0.1" 176 - } 177 - }, 178 - "buffer": { 179 - "version": "5.6.0", 180 - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", 181 - "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", 182 - "requires": { 183 - "base64-js": "^1.0.2", 184 - "ieee754": "^1.1.4" 185 - } 186 - }, 187 - "buffer-from": { 188 - "version": "1.1.1", 189 - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 190 - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", 191 - "dev": true 192 - }, 193 - "chownr": { 194 - "version": "1.1.4", 195 - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", 196 - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" 197 - }, 198 - "code-point-at": { 199 - "version": "1.1.0", 200 - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", 201 - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" 202 - }, 203 - "commondir": { 204 - "version": "1.0.1", 205 - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", 206 - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", 207 - "dev": true 208 - }, 209 - "concat-map": { 210 - "version": "0.0.1", 211 - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 212 - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 213 - }, 214 - "console-control-strings": { 215 - "version": "1.1.0", 216 - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", 217 - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" 218 - }, 219 - "core-js": { 220 - "version": "2.6.11", 221 - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", 222 - "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" 223 - }, 224 - "core-util-is": { 225 - "version": "1.0.2", 226 - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 227 - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 228 - }, 229 - "cross-spawn": { 230 - "version": "6.0.5", 231 - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", 232 - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", 233 - "requires": { 234 - "nice-try": "^1.0.4", 235 - "path-key": "^2.0.1", 236 - "semver": "^5.5.0", 237 - "shebang-command": "^1.2.0", 238 - "which": "^1.2.9" 239 - } 240 - }, 241 - "debug": { 242 - "version": "3.2.6", 243 - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 244 - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 245 - "requires": { 246 - "ms": "^2.1.1" 247 - }, 248 - "dependencies": { 249 - "ms": { 250 - "version": "2.1.2", 251 - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 252 - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 253 - } 254 - } 255 - }, 256 - "decompress-response": { 257 - "version": "4.2.1", 258 - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", 259 - "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", 260 - "requires": { 261 - "mimic-response": "^2.0.0" 262 - } 263 - }, 264 - "deep-extend": { 265 - "version": "0.6.0", 266 - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", 267 - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" 268 - }, 269 - "delegates": { 270 - "version": "1.0.0", 271 - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", 272 - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" 273 - }, 274 - "detect-libc": { 275 - "version": "1.0.3", 276 - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", 277 - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" 278 - }, 279 - "dsp.js": { 280 - "version": "1.0.1", 281 - "resolved": "https://registry.npmjs.org/dsp.js/-/dsp.js-1.0.1.tgz", 282 - "integrity": "sha1-AlVKIj7lFM0OImy2+4jW0LPpEwg=" 283 - }, 284 - "enchannel-zmq-backend": { 285 - "version": "9.1.22", 286 - "resolved": "https://registry.npmjs.org/enchannel-zmq-backend/-/enchannel-zmq-backend-9.1.22.tgz", 287 - "integrity": "sha512-/hda4CYmFtSqBUm77nUDIe46eeMIR39BLO1qBvYezmHm8yrHEdQ40SYAJlhy1iG5GNwvQSaxQS2pRowzTzFb3g==", 288 - "requires": { 289 - "@nteract/messaging": "^7.0.6", 290 - "jmp": "^2.0.0", 291 - "rxjs": "^6.3.3", 292 - "uuid": "^7.0.0" 293 - } 294 - }, 295 - "end-of-stream": { 296 - "version": "1.4.4", 297 - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", 298 - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", 299 - "requires": { 300 - "once": "^1.4.0" 301 - } 302 - }, 303 - "execa": { 304 - "version": "0.10.0", 305 - "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", 306 - "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", 307 - "requires": { 308 - "cross-spawn": "^6.0.0", 309 - "get-stream": "^3.0.0", 310 - "is-stream": "^1.1.0", 311 - "npm-run-path": "^2.0.0", 312 - "p-finally": "^1.0.0", 313 - "signal-exit": "^3.0.0", 314 - "strip-eof": "^1.0.0" 315 - } 316 - }, 317 - "expand-template": { 318 - "version": "2.0.3", 319 - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", 320 - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==" 321 - }, 322 - "fili": { 323 - "version": "2.0.3", 324 - "resolved": "https://registry.npmjs.org/fili/-/fili-2.0.3.tgz", 325 - "integrity": "sha512-NW/EY++EMP9f4mSpVCjg5PJnAkkxzh170My+R18sh4CSXu3Puxw3Cs4aQmXoUtNg1EonZmvmR5TLcKEJtX5hQg==" 326 - }, 327 - "find-cache-dir": { 328 - "version": "2.1.0", 329 - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", 330 - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", 331 - "dev": true, 332 - "requires": { 333 - "commondir": "^1.0.1", 334 - "make-dir": "^2.0.0", 335 - "pkg-dir": "^3.0.0" 336 - } 337 - }, 338 - "find-up": { 339 - "version": "3.0.0", 340 - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", 341 - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", 342 - "dev": true, 343 - "requires": { 344 - "locate-path": "^3.0.0" 345 - } 346 - }, 347 - "fs-constants": { 348 - "version": "1.0.0", 349 - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", 350 - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" 351 - }, 352 - "fs-minipass": { 353 - "version": "1.2.7", 354 - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", 355 - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", 356 - "requires": { 357 - "minipass": "^2.6.0" 358 - } 359 - }, 360 - "fs.realpath": { 361 - "version": "1.0.0", 362 - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 363 - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 364 - }, 365 - "gauge": { 366 - "version": "2.7.4", 367 - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", 368 - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", 369 - "requires": { 370 - "aproba": "^1.0.3", 371 - "console-control-strings": "^1.0.0", 372 - "has-unicode": "^2.0.0", 373 - "object-assign": "^4.1.0", 374 - "signal-exit": "^3.0.0", 375 - "string-width": "^1.0.1", 376 - "strip-ansi": "^3.0.1", 377 - "wide-align": "^1.1.0" 378 - } 379 - }, 380 - "get-stream": { 381 - "version": "3.0.0", 382 - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", 383 - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" 384 - }, 385 - "github-from-package": { 386 - "version": "0.0.0", 387 - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", 388 - "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=" 389 - }, 390 - "glob": { 391 - "version": "7.1.6", 392 - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", 393 - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", 394 - "requires": { 395 - "fs.realpath": "^1.0.0", 396 - "inflight": "^1.0.4", 397 - "inherits": "2", 398 - "minimatch": "^3.0.4", 399 - "once": "^1.3.0", 400 - "path-is-absolute": "^1.0.0" 401 - } 402 - }, 403 - "graceful-fs": { 404 - "version": "4.2.4", 405 - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", 406 - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" 407 - }, 408 - "has-unicode": { 409 - "version": "2.0.1", 410 - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", 411 - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" 412 - }, 413 - "home-dir": { 414 - "version": "1.0.0", 415 - "resolved": "https://registry.npmjs.org/home-dir/-/home-dir-1.0.0.tgz", 416 - "integrity": "sha1-KRfrRL3JByztqUJXlUOEfjAX/k4=" 417 - }, 418 - "iconv-lite": { 419 - "version": "0.4.24", 420 - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 421 - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 422 - "requires": { 423 - "safer-buffer": ">= 2.1.2 < 3" 424 - } 425 - }, 426 - "ieee754": { 427 - "version": "1.1.13", 428 - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", 429 - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" 430 - }, 431 - "ignore-walk": { 432 - "version": "3.0.3", 433 - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", 434 - "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", 435 - "requires": { 436 - "minimatch": "^3.0.4" 437 - } 438 - }, 439 - "immutable": { 440 - "version": "4.0.0-rc.12", 441 - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0-rc.12.tgz", 442 - "integrity": "sha512-0M2XxkZLx/mi3t8NVwIm1g8nHoEmM9p9UBl/G9k4+hm0kBgOVdMV/B3CY5dQ8qG8qc80NN4gDV4HQv6FTJ5q7A==" 443 - }, 444 - "inflight": { 445 - "version": "1.0.6", 446 - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 447 - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 448 - "requires": { 449 - "once": "^1.3.0", 450 - "wrappy": "1" 451 - } 452 - }, 453 - "inherits": { 454 - "version": "2.0.4", 455 - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 456 - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 457 - }, 458 - "ini": { 459 - "version": "1.3.8", 460 - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", 461 - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" 462 - }, 463 - "is-fullwidth-code-point": { 464 - "version": "1.0.0", 465 - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 466 - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", 467 - "requires": { 468 - "number-is-nan": "^1.0.0" 469 - } 470 - }, 471 - "is-stream": { 472 - "version": "1.1.0", 473 - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 474 - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" 475 - }, 476 - "isarray": { 477 - "version": "1.0.0", 478 - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 479 - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 480 - }, 481 - "isexe": { 482 - "version": "2.0.0", 483 - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 484 - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" 485 - }, 486 - "jmp": { 487 - "version": "2.0.0", 488 - "resolved": "https://registry.npmjs.org/jmp/-/jmp-2.0.0.tgz", 489 - "integrity": "sha512-VATfWVHErQJA2XMtmQjJQHHyQ/hxjHMmsy+egmwRk/RzFchQB4xjrR1iX496VZr+Hyhcr4zvL+IkkSlIYKx6Yw==", 490 - "requires": { 491 - "uuid": "3", 492 - "zeromq": "5" 493 - }, 494 - "dependencies": { 495 - "uuid": { 496 - "version": "3.4.0", 497 - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", 498 - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" 499 - } 500 - } 501 - }, 502 - "jsonfile": { 503 - "version": "3.0.1", 504 - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", 505 - "integrity": "sha1-pezG9l9T9mLEQVx2daAzHQmS7GY=", 506 - "requires": { 507 - "graceful-fs": "^4.1.6" 508 - } 509 - }, 510 - "jupyter-paths": { 511 - "version": "2.0.3", 512 - "resolved": "https://registry.npmjs.org/jupyter-paths/-/jupyter-paths-2.0.3.tgz", 513 - "integrity": "sha512-cQuCfHtKINnwiVTu1Ljm7Pk+tRZiV2wJkZLn0fUmZIJ76v9cIw/nu3PXgUYZ3T120eYxg6oELRxGkXianCowZQ==", 514 - "requires": { 515 - "home-dir": "^1.0.0" 516 - } 517 - }, 518 - "kernelspecs": { 519 - "version": "2.0.0", 520 - "resolved": "https://registry.npmjs.org/kernelspecs/-/kernelspecs-2.0.0.tgz", 521 - "integrity": "sha512-lce4pPDrs4VdxKYTEBnGLT81A3yNP8syyMAq5AejE+CKAkiXQXrHZaHO1F4c/RmgkKKF1Otis1XrpBxOOQsdnw==", 522 - "requires": { 523 - "jupyter-paths": "^2.0.0" 524 - } 525 - }, 526 - "locate-path": { 527 - "version": "3.0.0", 528 - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", 529 - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", 530 - "dev": true, 531 - "requires": { 532 - "p-locate": "^3.0.0", 533 - "path-exists": "^3.0.0" 534 - } 535 - }, 536 - "lodash": { 537 - "version": "4.17.21", 538 - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 539 - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 540 - }, 541 - "lodash.clonedeep": { 542 - "version": "4.5.0", 543 - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", 544 - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" 545 - }, 546 - "make-dir": { 547 - "version": "2.1.0", 548 - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", 549 - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", 550 - "dev": true, 551 - "requires": { 552 - "pify": "^4.0.1", 553 - "semver": "^5.6.0" 554 - } 555 - }, 556 - "mimic-response": { 557 - "version": "2.1.0", 558 - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", 559 - "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==" 560 - }, 561 - "minimatch": { 562 - "version": "3.0.4", 563 - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 564 - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 565 - "requires": { 566 - "brace-expansion": "^1.1.7" 567 - } 568 - }, 569 - "minimist": { 570 - "version": "1.2.6", 571 - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", 572 - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" 573 - }, 574 - "minipass": { 575 - "version": "2.9.0", 576 - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", 577 - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", 578 - "requires": { 579 - "safe-buffer": "^5.1.2", 580 - "yallist": "^3.0.0" 581 - } 582 - }, 583 - "minizlib": { 584 - "version": "1.3.3", 585 - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", 586 - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", 587 - "requires": { 588 - "minipass": "^2.9.0" 589 - } 590 - }, 591 - "mkdirp": { 592 - "version": "0.5.5", 593 - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", 594 - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", 595 - "requires": { 596 - "minimist": "^1.2.5" 597 - } 598 - }, 599 - "mkdirp-classic": { 600 - "version": "0.5.3", 601 - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", 602 - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" 603 - }, 604 - "nan": { 605 - "version": "2.14.1", 606 - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", 607 - "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==" 608 - }, 609 - "napi-build-utils": { 610 - "version": "1.0.2", 611 - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", 612 - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" 613 - }, 614 - "needle": { 615 - "version": "2.5.0", 616 - "resolved": "https://registry.npmjs.org/needle/-/needle-2.5.0.tgz", 617 - "integrity": "sha512-o/qITSDR0JCyCKEQ1/1bnUXMmznxabbwi/Y4WwJElf+evwJNFNwIDMCCt5IigFVxgeGBJESLohGtIS9gEzo1fA==", 618 - "requires": { 619 - "debug": "^3.2.6", 620 - "iconv-lite": "^0.4.4", 621 - "sax": "^1.2.4" 622 - }, 623 - "dependencies": { 624 - "ms": { 625 - "version": "2.1.2", 626 - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 627 - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 628 - } 629 - } 630 - }, 631 - "nice-try": { 632 - "version": "1.0.5", 633 - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", 634 - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" 635 - }, 636 - "node-abi": { 637 - "version": "2.18.0", 638 - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.18.0.tgz", 639 - "integrity": "sha512-yi05ZoiuNNEbyT/xXfSySZE+yVnQW6fxPZuFbLyS1s6b5Kw3HzV2PHOM4XR+nsjzkHxByK+2Wg+yCQbe35l8dw==", 640 - "requires": { 641 - "semver": "^5.4.1" 642 - } 643 - }, 644 - "node-modules-regexp": { 645 - "version": "1.0.0", 646 - "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", 647 - "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", 648 - "dev": true 649 - }, 650 - "node-pre-gyp": { 651 - "version": "0.15.0", 652 - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.15.0.tgz", 653 - "integrity": "sha512-7QcZa8/fpaU/BKenjcaeFF9hLz2+7S9AqyXFhlH/rilsQ/hPZKK32RtR5EQHJElgu+q5RfbJ34KriI79UWaorA==", 654 - "requires": { 655 - "detect-libc": "^1.0.2", 656 - "mkdirp": "^0.5.3", 657 - "needle": "^2.5.0", 658 - "nopt": "^4.0.1", 659 - "npm-packlist": "^1.1.6", 660 - "npmlog": "^4.0.2", 661 - "rc": "^1.2.7", 662 - "rimraf": "^2.6.1", 663 - "semver": "^5.3.0", 664 - "tar": "^4.4.2" 665 - } 666 - }, 667 - "noop-logger": { 668 - "version": "0.1.1", 669 - "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", 670 - "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=" 671 - }, 672 - "nopt": { 673 - "version": "4.0.3", 674 - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", 675 - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", 676 - "requires": { 677 - "abbrev": "1", 678 - "osenv": "^0.1.4" 679 - } 680 - }, 681 - "npm-bundled": { 682 - "version": "1.1.1", 683 - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz", 684 - "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", 685 - "requires": { 686 - "npm-normalize-package-bin": "^1.0.1" 687 - } 688 - }, 689 - "npm-normalize-package-bin": { 690 - "version": "1.0.1", 691 - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", 692 - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" 693 - }, 694 - "npm-packlist": { 695 - "version": "1.4.8", 696 - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz", 697 - "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", 698 - "requires": { 699 - "ignore-walk": "^3.0.1", 700 - "npm-bundled": "^1.0.1", 701 - "npm-normalize-package-bin": "^1.0.1" 702 - } 703 - }, 704 - "npm-run-path": { 705 - "version": "2.0.2", 706 - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", 707 - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", 708 - "requires": { 709 - "path-key": "^2.0.0" 710 - } 711 - }, 712 - "npmlog": { 713 - "version": "4.1.2", 714 - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", 715 - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", 716 - "requires": { 717 - "are-we-there-yet": "~1.1.2", 718 - "console-control-strings": "~1.1.0", 719 - "gauge": "~2.7.3", 720 - "set-blocking": "~2.0.0" 721 - } 722 - }, 723 - "number-is-nan": { 724 - "version": "1.0.1", 725 - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 726 - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" 727 - }, 728 - "object-assign": { 729 - "version": "4.1.1", 730 - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 731 - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 732 - }, 733 - "once": { 734 - "version": "1.4.0", 735 - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 736 - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 737 - "requires": { 738 - "wrappy": "1" 739 - } 740 - }, 741 - "os-homedir": { 742 - "version": "1.0.2", 743 - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", 744 - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" 745 - }, 746 - "os-tmpdir": { 747 - "version": "1.0.2", 748 - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", 749 - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" 750 - }, 751 - "osenv": { 752 - "version": "0.1.5", 753 - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", 754 - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", 755 - "requires": { 756 - "os-homedir": "^1.0.0", 757 - "os-tmpdir": "^1.0.0" 758 - } 759 - }, 760 - "p-finally": { 761 - "version": "1.0.0", 762 - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", 763 - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" 764 - }, 765 - "p-limit": { 766 - "version": "2.3.0", 767 - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", 768 - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", 769 - "dev": true, 770 - "requires": { 771 - "p-try": "^2.0.0" 772 - } 773 - }, 774 - "p-locate": { 775 - "version": "3.0.0", 776 - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", 777 - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", 778 - "dev": true, 779 - "requires": { 780 - "p-limit": "^2.0.0" 781 - } 782 - }, 783 - "p-try": { 784 - "version": "2.2.0", 785 - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", 786 - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", 787 - "dev": true 788 - }, 789 - "path-exists": { 790 - "version": "3.0.0", 791 - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", 792 - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", 793 - "dev": true 794 - }, 795 - "path-is-absolute": { 796 - "version": "1.0.1", 797 - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 798 - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 799 - }, 800 - "path-key": { 801 - "version": "2.0.1", 802 - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", 803 - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" 804 - }, 805 - "pify": { 806 - "version": "4.0.1", 807 - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", 808 - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", 809 - "dev": true 810 - }, 811 - "pirates": { 812 - "version": "4.0.1", 813 - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", 814 - "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", 815 - "dev": true, 816 - "requires": { 817 - "node-modules-regexp": "^1.0.0" 818 - } 819 - }, 820 - "pkg-dir": { 821 - "version": "3.0.0", 822 - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", 823 - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", 824 - "dev": true, 825 - "requires": { 826 - "find-up": "^3.0.0" 827 - } 828 - }, 829 - "portfinder": { 830 - "version": "1.0.26", 831 - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.26.tgz", 832 - "integrity": "sha512-Xi7mKxJHHMI3rIUrnm/jjUgwhbYMkp/XKEcZX3aG4BrumLpq3nmoQMX+ClYnDZnZ/New7IatC1no5RX0zo1vXQ==", 833 - "requires": { 834 - "async": "^2.6.2", 835 - "debug": "^3.1.1", 836 - "mkdirp": "^0.5.1" 837 - }, 838 - "dependencies": { 839 - "ms": { 840 - "version": "2.1.2", 841 - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 842 - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 843 - } 844 - } 845 - }, 846 - "prebuild-install": { 847 - "version": "5.3.4", 848 - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.4.tgz", 849 - "integrity": "sha512-AkKN+pf4fSEihjapLEEj8n85YIw/tN6BQqkhzbDc0RvEZGdkpJBGMUYx66AAMcPG2KzmPQS7Cm16an4HVBRRMA==", 850 - "requires": { 851 - "detect-libc": "^1.0.3", 852 - "expand-template": "^2.0.3", 853 - "github-from-package": "0.0.0", 854 - "minimist": "^1.2.3", 855 - "mkdirp": "^0.5.1", 856 - "napi-build-utils": "^1.0.1", 857 - "node-abi": "^2.7.0", 858 - "noop-logger": "^0.1.1", 859 - "npmlog": "^4.0.1", 860 - "pump": "^3.0.0", 861 - "rc": "^1.2.7", 862 - "simple-get": "^3.0.3", 863 - "tar-fs": "^2.0.0", 864 - "tunnel-agent": "^0.6.0", 865 - "which-pm-runs": "^1.0.0" 866 - } 867 - }, 868 - "process-nextick-args": { 869 - "version": "2.0.1", 870 - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 871 - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" 872 - }, 873 - "pump": { 874 - "version": "3.0.0", 875 - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", 876 - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", 877 - "requires": { 878 - "end-of-stream": "^1.1.0", 879 - "once": "^1.3.1" 880 - } 881 - }, 882 - "rc": { 883 - "version": "1.2.8", 884 - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", 885 - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", 886 - "requires": { 887 - "deep-extend": "^0.6.0", 888 - "ini": "~1.3.0", 889 - "minimist": "^1.2.0", 890 - "strip-json-comments": "~2.0.1" 891 - } 892 - }, 893 - "readable-stream": { 894 - "version": "2.3.7", 895 - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", 896 - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", 897 - "requires": { 898 - "core-util-is": "~1.0.0", 899 - "inherits": "~2.0.3", 900 - "isarray": "~1.0.0", 901 - "process-nextick-args": "~2.0.0", 902 - "safe-buffer": "~5.1.1", 903 - "string_decoder": "~1.1.1", 904 - "util-deprecate": "~1.0.1" 905 - } 906 - }, 907 - "regenerator-runtime": { 908 - "version": "0.13.5", 909 - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", 910 - "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" 911 - }, 912 - "rimraf": { 913 - "version": "2.7.1", 914 - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", 915 - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", 916 - "requires": { 917 - "glob": "^7.1.3" 918 - } 919 - }, 920 - "rxjs": { 921 - "version": "6.5.5", 922 - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", 923 - "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==", 924 - "requires": { 925 - "tslib": "^1.9.0" 926 - } 927 - }, 928 - "safe-buffer": { 929 - "version": "5.1.2", 930 - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 931 - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 932 - }, 933 - "safer-buffer": { 934 - "version": "2.1.2", 935 - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 936 - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 937 - }, 938 - "sax": { 939 - "version": "1.2.4", 940 - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", 941 - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" 942 - }, 943 - "semver": { 944 - "version": "5.7.1", 945 - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 946 - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" 947 - }, 948 - "set-blocking": { 949 - "version": "2.0.0", 950 - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 951 - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" 952 - }, 953 - "shebang-command": { 954 - "version": "1.2.0", 955 - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", 956 - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", 957 - "requires": { 958 - "shebang-regex": "^1.0.0" 959 - } 960 - }, 961 - "shebang-regex": { 962 - "version": "1.0.0", 963 - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", 964 - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" 965 - }, 966 - "signal-exit": { 967 - "version": "3.0.3", 968 - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", 969 - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" 970 - }, 971 - "simple-concat": { 972 - "version": "1.0.0", 973 - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", 974 - "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=" 975 - }, 976 - "simple-get": { 977 - "version": "3.1.1", 978 - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", 979 - "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", 980 - "requires": { 981 - "decompress-response": "^4.2.0", 982 - "once": "^1.3.1", 983 - "simple-concat": "^1.0.0" 984 - } 985 - }, 986 - "source-map": { 987 - "version": "0.6.1", 988 - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 989 - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 990 - "dev": true 991 - }, 992 - "source-map-support": { 993 - "version": "0.5.19", 994 - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", 995 - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", 996 - "dev": true, 997 - "requires": { 998 - "buffer-from": "^1.0.0", 999 - "source-map": "^0.6.0" 1000 - } 1001 - }, 1002 - "spawnteract": { 1003 - "version": "5.0.1", 1004 - "resolved": "https://registry.npmjs.org/spawnteract/-/spawnteract-5.0.1.tgz", 1005 - "integrity": "sha512-7R+unoZfdInm/fAqLeCirqoF8clth3N5SQBohAdv/LhYNJ0I6tnL0AN2catX8T+KedwsgujaeTTuyukB6Jc+Ew==", 1006 - "requires": { 1007 - "execa": "^0.10.0", 1008 - "jsonfile": "^3.0.0", 1009 - "jupyter-paths": "^2.0.0", 1010 - "kernelspecs": "^2.0.0", 1011 - "mkdirp": "^0.5.1", 1012 - "portfinder": "^1.0.13", 1013 - "uuid": "^3.0.1" 1014 - }, 1015 - "dependencies": { 1016 - "uuid": { 1017 - "version": "3.4.0", 1018 - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", 1019 - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" 1020 - } 1021 - } 1022 - }, 1023 - "string-width": { 1024 - "version": "1.0.2", 1025 - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 1026 - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", 1027 - "requires": { 1028 - "code-point-at": "^1.0.0", 1029 - "is-fullwidth-code-point": "^1.0.0", 1030 - "strip-ansi": "^3.0.0" 1031 - } 1032 - }, 1033 - "string_decoder": { 1034 - "version": "1.1.1", 1035 - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 1036 - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 1037 - "requires": { 1038 - "safe-buffer": "~5.1.0" 1039 - } 1040 - }, 1041 - "strip-ansi": { 1042 - "version": "3.0.1", 1043 - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 1044 - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 1045 - "requires": { 1046 - "ansi-regex": "^2.0.0" 1047 - } 1048 - }, 1049 - "strip-eof": { 1050 - "version": "1.0.0", 1051 - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", 1052 - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" 1053 - }, 1054 - "strip-json-comments": { 1055 - "version": "2.0.1", 1056 - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 1057 - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" 1058 - }, 1059 - "tar": { 1060 - "version": "4.4.19", 1061 - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", 1062 - "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", 1063 - "requires": { 1064 - "chownr": "^1.1.4", 1065 - "fs-minipass": "^1.2.7", 1066 - "minipass": "^2.9.0", 1067 - "minizlib": "^1.3.3", 1068 - "mkdirp": "^0.5.5", 1069 - "safe-buffer": "^5.2.1", 1070 - "yallist": "^3.1.1" 1071 - }, 1072 - "dependencies": { 1073 - "safe-buffer": { 1074 - "version": "5.2.1", 1075 - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1076 - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 1077 - } 1078 - } 1079 - }, 1080 - "tar-fs": { 1081 - "version": "2.1.0", 1082 - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.0.tgz", 1083 - "integrity": "sha512-9uW5iDvrIMCVpvasdFHW0wJPez0K4JnMZtsuIeDI7HyMGJNxmDZDOCQROr7lXyS+iL/QMpj07qcjGYTSdRFXUg==", 1084 - "requires": { 1085 - "chownr": "^1.1.1", 1086 - "mkdirp-classic": "^0.5.2", 1087 - "pump": "^3.0.0", 1088 - "tar-stream": "^2.0.0" 1089 - } 1090 - }, 1091 - "tar-stream": { 1092 - "version": "2.1.2", 1093 - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.2.tgz", 1094 - "integrity": "sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q==", 1095 - "requires": { 1096 - "bl": "^4.0.1", 1097 - "end-of-stream": "^1.4.1", 1098 - "fs-constants": "^1.0.0", 1099 - "inherits": "^2.0.3", 1100 - "readable-stream": "^3.1.1" 1101 - }, 1102 - "dependencies": { 1103 - "readable-stream": { 1104 - "version": "3.6.0", 1105 - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", 1106 - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", 1107 - "requires": { 1108 - "inherits": "^2.0.3", 1109 - "string_decoder": "^1.1.1", 1110 - "util-deprecate": "^1.0.1" 1111 - } 1112 - } 1113 - } 1114 - }, 1115 - "tslib": { 1116 - "version": "1.13.0", 1117 - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", 1118 - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" 1119 - }, 1120 - "tunnel-agent": { 1121 - "version": "0.6.0", 1122 - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 1123 - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 1124 - "requires": { 1125 - "safe-buffer": "^5.0.1" 1126 - } 1127 - }, 1128 - "util-deprecate": { 1129 - "version": "1.0.2", 1130 - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1131 - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 1132 - }, 1133 - "uuid": { 1134 - "version": "7.0.3", 1135 - "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", 1136 - "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==" 1137 - }, 1138 - "which": { 1139 - "version": "1.3.1", 1140 - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 1141 - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 1142 - "requires": { 1143 - "isexe": "^2.0.0" 1144 - } 1145 - }, 1146 - "which-pm-runs": { 1147 - "version": "1.0.0", 1148 - "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", 1149 - "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=" 1150 - }, 1151 - "wide-align": { 1152 - "version": "1.1.3", 1153 - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", 1154 - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", 1155 - "requires": { 1156 - "string-width": "^1.0.2 || 2" 1157 - } 1158 - }, 1159 - "wrappy": { 1160 - "version": "1.0.2", 1161 - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1162 - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 1163 - }, 1164 - "yallist": { 1165 - "version": "3.1.1", 1166 - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", 1167 - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" 1168 - }, 1169 - "zeromq": { 1170 - "version": "5.2.0", 1171 - "resolved": "https://registry.npmjs.org/zeromq/-/zeromq-5.2.0.tgz", 1172 - "integrity": "sha512-qsckhCmrg6et6zrAJytC971SSN/4iLxKgkXK1Wqn2Gij5KXMY+TA+3cy/iFwehaWdU5usg5HNOOgaBdjSqtCVw==", 1173 - "requires": { 1174 - "nan": "^2.14.0", 1175 - "prebuild-install": "^5.3.2" 1176 - } 1177 - } 1178 - } 1179 - }
-50
app/package.json
··· 1 - { 2 - "name": "brainwaves", 3 - "productName": "BrainWaves", 4 - "version": "0.15.1", 5 - "description": "EEG Experiment Desktop Application", 6 - "main": "./main.prod.js", 7 - "type": "module", 8 - "author": { 9 - "name": "Dano Morrison", 10 - "email": "dano@neurotechx.com", 11 - "url": "https://github.com/makebrainwaves" 12 - }, 13 - "contributors": [ 14 - { 15 - "name": "Teon L Brooks", 16 - "url": "https://teonbrooks.com" 17 - }, 18 - { 19 - "name": "Yury Shevchenko", 20 - "url": "https://yuryshevchenko.com/" 21 - } 22 - ], 23 - "scripts": { 24 - "electron-rebuild": "node -r ../internals/scripts/BabelRegister.js ../internals/scripts/ElectronRebuild.js", 25 - "postinstall": "yarn electron-rebuild" 26 - }, 27 - "license": "MIT", 28 - "keywords": [ 29 - "eeg", 30 - "electron", 31 - "react", 32 - "redux", 33 - "redux-observable", 34 - "muse", 35 - "emotiv", 36 - "pyodide", 37 - "wasm", 38 - "lab.js" 39 - ], 40 - "homepage": "https://github.com/makebrainwaves/BrainWaves/", 41 - "dependencies": { 42 - "@neurosity/pipes": "^3.2.3", 43 - "@babel/runtime": "7.10.2", 44 - "@babel/runtime-corejs2": "^7.10.2", 45 - "node-pre-gyp": "^0.15.0" 46 - }, 47 - "devDependencies": { 48 - "@babel/register": "^7.10.1" 49 - } 50 - }
app/reducers/deviceReducer.ts src/renderer/reducers/deviceReducer.ts
app/reducers/experimentReducer.ts src/renderer/reducers/experimentReducer.ts
app/reducers/index.ts src/renderer/reducers/index.ts
app/reducers/pyodideReducer.ts src/renderer/reducers/pyodideReducer.ts
app/reducers/types.ts src/renderer/reducers/types.ts
app/routes.tsx src/renderer/routes.tsx
+1 -10
app/store.ts src/renderer/store.ts
··· 24 24 25 25 const excludeLoggerEnvs = ['test', 'production']; 26 26 const shouldIncludeLogger = !excludeLoggerEnvs.includes( 27 - process.env.NODE_ENV || '' 27 + import.meta.env.MODE || '' 28 28 ); 29 29 30 30 if (shouldIncludeLogger) { ··· 36 36 } 37 37 38 38 export const configuredStore = (initialState?: RootState) => { 39 - // Create Store 40 39 const store = configureStore({ 41 40 reducer: rootReducer, 42 41 middleware, 43 42 preloadedState: initialState, 44 43 }); 45 - 46 - if (process.env.NODE_ENV === 'development' && module.hot) { 47 - module.hot.accept( 48 - './reducers', 49 - // eslint-disable-next-line global-require 50 - () => store.replaceReducer(require('./reducers').default) 51 - ); 52 - } 53 44 54 45 epicMiddleware.run(rootEpic); 55 46 return store;
+5 -6
app/store/configureStore.dev.js src/renderer/store/configureStore.dev.js
··· 30 30 }); 31 31 32 32 // Skip redux logs in console during the tests 33 - if (process.env.NODE_ENV !== 'test') { 33 + if (import.meta.env.MODE !== 'test') { 34 34 middleware.push(logger); 35 35 } 36 36 ··· 61 61 // Create Store 62 62 const store = createStore(rootReducer, initialState, enhancer); 63 63 64 - if (module.hot) { 65 - module.hot.accept( 66 - '../reducers', 67 - () => store.replaceReducer(require('../reducers')) // eslint-disable-line global-require 68 - ); 64 + if (import.meta.hot) { 65 + import.meta.hot.accept('../reducers', (newModule) => { 66 + store.replaceReducer(newModule?.default ?? newModule); 67 + }); 69 68 } 70 69 71 70 // Redux Observable
app/utils/behavior/compute.js src/renderer/utils/behavior/compute.js
+2 -2
app/utils/eeg/cortex.js src/renderer/utils/eeg/cortex.js
··· 19 19 * exception of the login/auth flow, which we expose as the init() method. 20 20 */ 21 21 // const WebSocket = require('ws'); 22 - const EventEmitter = require('events'); 22 + import { EventEmitter } from 'events'; 23 23 24 24 const CORTEX_URL = 'wss://localhost:6868'; 25 25 ··· 31 31 } 32 32 }; 33 33 34 - if (global.process) { 34 + if (typeof process !== 'undefined' && process.env) { 35 35 process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; 36 36 } 37 37
+4 -2
app/utils/eeg/emotiv.ts src/renderer/utils/eeg/emotiv.ts
··· 8 8 import { addInfo, epoch, bandpassFilter } from '@neurosity/pipes'; 9 9 import { toast } from 'react-toastify'; 10 10 import { parseEmotivSignalQuality } from './pipes'; 11 - import { CLIENT_ID, CLIENT_SECRET, LICENSE_ID } from '../../../keys'; 11 + const CLIENT_ID = import.meta.env.VITE_CLIENT_ID ?? ''; 12 + const CLIENT_SECRET = import.meta.env.VITE_CLIENT_SECRET ?? ''; 13 + const LICENSE_ID = import.meta.env.VITE_LICENSE_ID ?? ''; 12 14 import { EMOTIV_CHANNELS, PLOTTING_INTERVAL } from '../../constants/constants'; 13 15 import Cortex from './cortex'; 14 16 import { Device, DeviceInfo } from '../../constants/interfaces'; ··· 26 28 } 27 29 28 30 // Creates the Cortex object from SDK 29 - const verbose = process.env.LOG_LEVEL || 1; 31 + const verbose = import.meta.env.VITE_LOG_LEVEL || 1; 30 32 const options = { verbose }; 31 33 32 34 // This global client is used in every Cortex API call
+1 -8
app/utils/eeg/muse.ts src/renderer/utils/eeg/muse.ts
··· 1 - import 'hazardous'; 2 1 import { 3 2 withLatestFrom, 4 3 share, ··· 13 12 bandpassFilter, 14 13 addSignalQuality, 15 14 } from '@neurosity/pipes'; 16 - import { release } from 'os'; 17 15 import { MUSE_SERVICE, MuseClient, zipSamples, EEGSample } from 'muse-js'; 18 16 import { from, Observable } from 'rxjs'; 19 17 import { isNaN } from 'lodash'; ··· 27 25 28 26 const INTER_SAMPLE_INTERVAL = -(1 / 256) * 1000; 29 27 30 - if ( 31 - process.platform === 'win32' && 32 - parseInt(release().split('.')[0], 10) < 10 33 - ) { 34 - console.error('Muse EEG not available in Windows 7'); 35 - } 28 + // Windows 7 check removed — process.platform and os.release are not available in renderer context 36 29 37 30 const client = new MuseClient(); 38 31 client.enableAux = false;
app/utils/eeg/pipes.ts src/renderer/utils/eeg/pipes.ts
-40
app/utils/filesystem/dialog.ts
··· 1 - /** 2 - * Functions involved handling system dialogs 3 - * These functions are all executed in the main process 4 - */ 5 - 6 - import { dialog } from 'electron'; 7 - import { FILE_TYPES } from '../../constants/constants'; 8 - 9 - export const loadDialog = (event, arg) => { 10 - switch (arg) { 11 - case FILE_TYPES.STIMULUS_DIR: 12 - return selectStimulusFolder(event); 13 - 14 - case FILE_TYPES.TIMELINE: 15 - default: 16 - return selectTimeline(event); 17 - } 18 - }; 19 - 20 - const selectTimeline = (event) => { 21 - const filePaths = dialog.showOpenDialog({ 22 - title: 'Select a jsPsych timeline file', 23 - properties: ['openFile', 'promptToCreate'], 24 - }); 25 - if (filePaths) { 26 - event.sender.send('loadDialogReply', filePaths[0]); 27 - } 28 - }; 29 - 30 - const selectStimulusFolder = (event) => { 31 - const dirs = dialog.showOpenDialog({ 32 - title: 'Select a folder of images', 33 - properties: ['openDirectory'], 34 - }); 35 - if (dirs) { 36 - event.sender.send('loadDialogReply', dirs[0]); 37 - } else { 38 - event.sender.send('loadDialogReply', ''); 39 - } 40 - };
app/utils/filesystem/dir.ts src/renderer/utils/filesystem/dir.ts
-10
app/utils/filesystem/read.js
··· 1 - const fs = require('fs'); 2 - 3 - export const readFiles = (filePathsArray) => { 4 - return filePathsArray.map((path) => { 5 - console.log('about to read file'); 6 - const file = fs.readFileSync(path, 'utf8'); 7 - console.log('read file'); 8 - return file; 9 - }); 10 - };
-14
app/utils/filesystem/select.ts
··· 1 - /** 2 - * Functions for selecting files and directories from disk 3 - */ 4 - 5 - import { ipcRenderer } from 'electron'; 6 - import { FILE_TYPES } from '../../constants/constants'; 7 - 8 - export const loadFromSystemDialog = (fileType: FILE_TYPES) => 9 - new Promise((resolve) => { 10 - ipcRenderer.send('loadDialog', fileType); 11 - ipcRenderer.on('loadDialogReply', (_, result) => { 12 - resolve(result); 13 - }); 14 - });
-285
app/utils/filesystem/storage.ts
··· 1 - /** 2 - * Functions for managing user data stored on disk 3 - */ 4 - import * as fs from 'fs'; 5 - import * as os from 'os'; 6 - import * as path from 'path'; 7 - import recursive from 'recursive-readdir'; 8 - import { shell, remote } from 'electron'; 9 - import Papa from 'papaparse'; 10 - import mkdirp from 'mkdirp'; 11 - import { ExperimentStateType } from '../../reducers/experimentReducer'; 12 - import { ExperimentParameters } from '../../constants/interfaces'; 13 - 14 - const workspaces = path.join(os.homedir(), 'BrainWaves_Workspaces'); 15 - const { dialog } = remote; 16 - 17 - // ----------------------------------------------------------------------------------------------- 18 - // Creating and Getting 19 - 20 - // Gets the absolute path for a workspace from a given title 21 - export const getWorkspaceDir = (title: string) => path.join(workspaces, title); 22 - 23 - // Creates a new directory for a given workspace with the passed title if it doesn't already exist 24 - export const createWorkspaceDir = (title: string) => 25 - mkdirPathSync(getWorkspaceDir(title)); 26 - 27 - // Opens a workspace folder in explorer (or other native OS filesystem browser) 28 - export const openWorkspaceDir = (title: string) => 29 - shell.showItemInFolder(path.join(workspaces, title)); 30 - 31 - // ----------------------------------------------------------------------------------------------- 32 - // Storing 33 - 34 - // Writes experiment tree state to file as a JSON object 35 - export const storeExperimentState = (state: ExperimentStateType) => { 36 - fs.writeFileSync( 37 - path.join(getWorkspaceDir(state.title), 'appState.json'), 38 - JSON.stringify(state) 39 - ); 40 - }; 41 - 42 - export const restoreExperimentState = (state: ExperimentStateType) => { 43 - if (state.type !== 'NONE') { 44 - const timestampedState: ExperimentStateType = { 45 - ...state, 46 - subject: '', 47 - group: '', 48 - session: 1, 49 - }; 50 - if (!timestampedState.title) { 51 - return; 52 - } 53 - fs.writeFileSync( 54 - path.join(getWorkspaceDir(timestampedState.title), 'appState.json'), 55 - JSON.stringify(timestampedState) 56 - ); 57 - } 58 - }; 59 - 60 - export const storeBehavioralData = ( 61 - csv: string, 62 - title: string, 63 - subject: string, 64 - group: string, 65 - session: number 66 - ) => { 67 - const dir = path.join(getWorkspaceDir(title), 'Data', subject, 'Behavior'); 68 - const filename = `${subject}-${group}-${session}-behavior.csv`; 69 - mkdirPathSync(dir); 70 - fs.writeFile(path.join(dir, filename), csv, (err) => { 71 - if (err) { 72 - console.error(err); 73 - } 74 - }); 75 - }; 76 - 77 - // Stores an image to workspace dir 78 - export const storePyodideImage = ( 79 - title: string, 80 - imageTitle: string, 81 - rawData: Buffer 82 - ) => { 83 - const dir = path.join(getWorkspaceDir(title), 'Results', 'Images'); 84 - const filename = `${imageTitle}.png`; 85 - mkdirPathSync(dir); 86 - fs.writeFile(path.join(dir, filename), rawData, (err) => { 87 - if (err) { 88 - console.error(err); 89 - } 90 - }); 91 - }; 92 - 93 - // ----------------------------------------------------------------------------------------------- 94 - // Reading 95 - 96 - // Returns a list of workspaces in the workspaces directory. Will make the workspaces dir if it doesn't exist yet 97 - export const readWorkspaces = () => { 98 - try { 99 - return fs 100 - .readdirSync(workspaces) 101 - .filter((workspace) => workspace !== '.DS_Store'); 102 - } catch (e) { 103 - if (e.code === 'ENOENT') { 104 - mkdirPathSync(workspaces); 105 - } 106 - console.log(e); 107 - return []; 108 - } 109 - }; 110 - 111 - // Returns a list of the raw EEG files in a workspace 112 - export const readWorkspaceRawEEGData = async (title: string) => { 113 - try { 114 - const files = await recursive(getWorkspaceDir(title)); 115 - const rawFiles = files 116 - .filter((filepath) => filepath.slice(-7).includes('raw.csv')) 117 - .map((filepath) => ({ 118 - name: path.basename(filepath), 119 - path: filepath, 120 - })); 121 - return rawFiles; 122 - } catch (e) { 123 - if (e.code === 'ENOENT') { 124 - console.log(e); 125 - return []; 126 - } 127 - } 128 - }; 129 - 130 - // Returns a list of the cleaned EEG files in a workspace 131 - export const readWorkspaceCleanedEEGData = async (title: string) => { 132 - try { 133 - const files = await recursive(getWorkspaceDir(title)); 134 - return files 135 - .filter((filepath) => filepath.slice(-7).includes('epo.fif')) 136 - .map((filepath) => ({ 137 - name: path.basename(filepath), 138 - path: filepath, 139 - })); 140 - } catch (e) { 141 - console.log(e); 142 - return []; 143 - } 144 - }; 145 - 146 - // Returns a list of the behavioral CSV files in a workspace 147 - export const readWorkspaceBehaviorData = async ( 148 - title: string 149 - ): Promise<{ name: string; path: string }[]> => { 150 - try { 151 - const files: string[] = await recursive(getWorkspaceDir(title)); 152 - const behaviorFiles = files 153 - .filter((filepath) => filepath.slice(-12).includes('behavior.csv')) 154 - .map((filepath) => ({ 155 - name: path.basename(filepath), 156 - path: filepath, 157 - })); 158 - return behaviorFiles; 159 - } catch (e) { 160 - if (e.code === 'ENOENT') { 161 - console.log(e); 162 - } 163 - return []; 164 - } 165 - }; 166 - 167 - // Reads an experiment state tree from disk and parses it from JSON 168 - export const readAndParseState = (dir: string): ExperimentStateType | null => { 169 - try { 170 - const state = JSON.parse( 171 - fs.readFileSync(path.join(workspaces, dir, 'appState.json'), { 172 - encoding: 'utf8', 173 - }) 174 - ); 175 - return state; 176 - } catch (e) { 177 - if (e.code === 'ENOENT') { 178 - console.log('appState does not exist for recent workspace'); 179 - } 180 - return null; 181 - } 182 - }; 183 - 184 - // Reads a list of images that are in a directory 185 - export const readImages = (dir: string) => 186 - fs.readdirSync(dir).filter((filename) => { 187 - const extension = filename.slice(-3).toLowerCase(); 188 - return ( 189 - extension === 'png' || 190 - extension === 'jpg' || 191 - extension === 'gif' || 192 - extension === 'peg' // support .jpeg? 193 - ); 194 - }); 195 - 196 - // Returns an array of images that are used in a timeline for use in preloading 197 - export const getImages = (params: ExperimentParameters) => { 198 - if (!params.stimuli) { 199 - return []; 200 - } 201 - const images: string[] = []; 202 - 203 - for (const stimuli of params.stimuli) { 204 - const { dir } = stimuli; 205 - if (dir) { 206 - const files = fs.readdirSync(dir); 207 - for (const file of files) { 208 - images.push(path.join(dir, file)); 209 - } 210 - } 211 - } 212 - return images; 213 - }; 214 - 215 - // ----------------------------------------------------------------------------------------------- 216 - // Util 217 - 218 - // Creates a directory path if it doesn't exist 219 - export const mkdirPathSync = (dirPath) => { 220 - mkdirp.sync(dirPath); 221 - }; 222 - 223 - export const getSubjectNamesFromFiles = (filePaths: Array<string>) => 224 - filePaths 225 - .map((filePath) => path.basename(filePath)) 226 - .map((fileName) => fileName.substring(0, fileName.indexOf('-'))); 227 - 228 - // Read CSV files with behavioral data and return an object 229 - export const readBehaviorData = (files: Array<string>) => { 230 - try { 231 - return files.map((file) => { 232 - const csv = fs.readFileSync(file, 'utf-8'); 233 - const obj = convertCSVToObject(csv); 234 - obj.meta.datafile = file; 235 - return obj; 236 - }); 237 - } catch (e) { 238 - console.log('error', e); 239 - return null; 240 - } 241 - }; 242 - 243 - export const storeAggregatedBehaviorData = (data, title) => { 244 - const csv = convertObjectToSCV(data); 245 - saveFileOnDisk(csv, title); 246 - }; 247 - 248 - const saveFileOnDisk = (data, title) => { 249 - dialog.showSaveDialog({ 250 - title: 'Select a folder to save the data', 251 - defaultPath: path.join(getWorkspaceDir(title), 'Data', `aggregated.csv`), 252 - }); 253 - }; 254 - 255 - // convert a csv file to an object with Papaparse 256 - const convertCSVToObject = (csv) => { 257 - const data = Papa.parse(csv, { 258 - header: true, 259 - }); 260 - return data; 261 - }; 262 - 263 - // convert an object to a csv file with Papaparse 264 - const convertObjectToSCV = (data) => { 265 - const csv = Papa.unparse(data); 266 - return csv; 267 - }; 268 - 269 - // Deletes a workspace folder 270 - export const deleteWorkspaceDir = (title: string) => { 271 - shell.moveItemToTrash(path.join(workspaces, title)); 272 - }; 273 - 274 - // Check whether the file with the given name already exists in the filesystem 275 - export const checkFileExists = (title, subject, filename) => { 276 - const file = path.join( 277 - getWorkspaceDir(title), 278 - 'Data', 279 - subject, 280 - 'Behavior', 281 - filename 282 - ); 283 - const fileExists = fs.existsSync(file); 284 - return fileExists; 285 - };
-54
app/utils/filesystem/write.ts
··· 1 - /** 2 - * Functions for writing EEG data to disk 3 - */ 4 - 5 - import * as fs from 'fs'; 6 - import * as path from 'path'; 7 - import { has } from 'lodash'; 8 - import { getWorkspaceDir, mkdirPathSync } from './storage'; 9 - import { EEGData } from '../../constants/interfaces'; 10 - 11 - // Creates an appropriate filename and returns a writestream that will write to that file 12 - export const createEEGWriteStream = ( 13 - title: string, 14 - subject: string, 15 - group: string, 16 - session: number 17 - ) => { 18 - try { 19 - const dir = path.join(getWorkspaceDir(title), 'Data', subject, 'EEG'); 20 - const filename = `${subject}-${group}-${session}-raw.csv`; 21 - mkdirPathSync(dir); 22 - return fs.createWriteStream(path.join(dir, filename)); 23 - } catch (e) { 24 - console.log('createEEGWriteStream: ', e); 25 - } 26 - }; 27 - 28 - // Writes the header for a simple CSV EEG file format. 29 - // timestamp followed by channels, followed by markers 30 - export const writeHeader = ( 31 - writeStream: fs.WriteStream, 32 - channels: Array<string> 33 - ) => { 34 - try { 35 - const headerLabels = `Timestamp,${channels.join(',')},Marker\n`; 36 - writeStream.write(headerLabels); 37 - } catch (e) { 38 - console.log('writeHeader: ', e); 39 - } 40 - }; 41 - 42 - // Writes an array of EEG data to a CSV through an active WriteStream 43 - export const writeEEGData = (writeStream: fs.WriteStream, eegData: EEGData) => { 44 - writeStream.write(`${eegData.timestamp},`); 45 - const len = eegData.data.length; 46 - for (let i = 0; i < len; i++) { 47 - writeStream.write(`${eegData.data[i].toString()},`); // Round data 48 - } 49 - if (has(eegData, 'marker')) { 50 - writeStream.write(`${eegData.marker}\n`); 51 - } else { 52 - writeStream.write(`0\n`); 53 - } 54 - };
app/utils/labjs/functions.ts src/renderer/utils/labjs/functions.ts
+1 -1
app/utils/labjs/protocols/custom.ts src/renderer/utils/labjs/protocols/custom.ts
··· 1 - import * as path from 'path'; 1 + import path from 'pathe'; 2 2 import { EVENTS, RESOURCE_PATH } from '../../../constants/constants'; 3 3 4 4 // Default directories containing stimuli
app/utils/labjs/scripts/custom.js src/renderer/utils/labjs/scripts/custom.js
app/utils/pyodide/functions.ts src/renderer/utils/pyodide/functions.ts
+9 -10
app/utils/pyodide/index.ts src/renderer/utils/pyodide/index.ts
··· 1 - import * as path from 'path'; 2 - import { readFileSync } from 'fs'; 3 1 import { formatFilePath } from './functions'; 2 + import path from 'pathe'; 3 + import patchesPy from './patches.py?raw'; 4 + import utilsPy from './utils.py?raw'; 4 5 5 6 // --------------------------------- 6 7 // This file contains the JS functions that allow the app to access python-wasm through pyodide 7 8 // These functions wrap the python strings defined in the 9 + // Python source files (loaded as raw strings via Vite's ?raw import) 8 10 9 11 // ----------------------------- 10 12 // Imports and Utility functions 11 13 12 14 export const loadPyodide = async () => { 13 - const freshWorker = await new Worker('./utils/pyodide/webworker.js'); 15 + // Classic worker (importScripts used inside cannot run in module workers) 16 + const freshWorker = new Worker(new URL('./webworker.js', import.meta.url)); 14 17 return freshWorker; 15 18 }; 16 19 17 20 export const loadPatches = async (worker: Worker) => 18 21 worker.postMessage({ 19 - data: readFileSync(path.join(__dirname, '/utils/pyodide/patches.py'), 'utf8'), 22 + data: patchesPy, 20 23 }); 21 24 22 25 export const applyPatches = async (worker: Worker) => ··· 26 29 27 30 export const loadUtils = async (worker: Worker) => 28 31 worker.postMessage({ 29 - data: readFileSync(path.join(__dirname, '/utils/pyodide/utils.py'), 'utf8'), 32 + data: utilsPy, 30 33 }); 31 34 32 - export const loadCSV = async (worker: Worker, csvArray: Array<any>) => { 35 + export const loadCSV = async (worker: Worker, csvArray: Array<unknown>) => { 33 36 // TODO: Pass attached variable name as parameter to load_data 34 37 // @ts-expect-error 35 38 window.csvArray = csvArray; ··· 103 106 // Plot functions 104 107 105 108 export const cleanEpochsPlot = async (worker: Worker) => { 106 - // TODO: Figure out how to get image results from pyodide 107 109 await worker.postMessage({ 108 110 data: `raw_epochs.plot(scalings='auto', n_epochs=6, title="Clean Data", events=None)`, 109 111 }); 110 112 }; 111 113 112 114 export const plotPSD = async (worker: Worker) => { 113 - // TODO: Figure out how to get image results from pyodide 114 115 return worker.postMessage({ data: `raw.plot_psd(fmin=1, fmax=30)` }); 115 116 }; 116 117 117 118 export const plotTopoMap = async (worker: Worker) => { 118 - // TODO: Figure out how to get image results from pyodide 119 119 return worker.postMessage({ 120 120 data: `plot_topo(clean_epochs, conditions)`, 121 121 }); ··· 125 125 if (!worker) { 126 126 return; 127 127 } 128 - // TODO: Figure out how to get image results from pyodide 129 128 return worker.postMessage({ 130 129 data: `import matplotlib.pyplot as plt; fig= plt.plot([1,2,3,4])`, 131 130 });
app/utils/pyodide/patches.py src/renderer/utils/pyodide/patches.py
app/utils/pyodide/utils.py src/renderer/utils/pyodide/utils.py
+5 -3
app/utils/pyodide/webworker.js src/renderer/utils/pyodide/webworker.js
··· 3 3 * pyodide to be used in a web worker within this 4 4 */ 5 5 6 - // self.languagePluginUrl = './src'; 7 - importScripts('./src/pyodide/pyodide.js'); 6 + // pyodide is served as a static asset at /pyodide/ (via Vite publicDir). 7 + // An absolute path is required so importScripts resolves correctly regardless 8 + // of where the worker script itself is served from. 9 + importScripts('/pyodide/pyodide.js'); 8 10 9 11 async function loadPyodideAndPackages() { 10 - self.pyodide = await loadPyodide({ indexURL: './src/pyodide/' }); 12 + self.pyodide = await loadPyodide({ indexURL: '/pyodide/' }); 11 13 await self.pyodide.loadPackage(['matplotlib', 'mne', 'pandas']); 12 14 } 13 15 let pyodideReadyPromise = loadPyodideAndPackages();
app/utils/redux/index.ts src/renderer/utils/redux/index.ts
app/utils/ui/index.tsx src/renderer/utils/ui/index.tsx
+1 -19
app/viewer.html src/renderer/viewer.html
··· 41 41 } 42 42 </style> 43 43 </head> 44 - 45 44 <body> 46 45 <svg id="graph" /> 46 + <script type="module" src="./viewer.ts"></script> 47 47 </body> 48 - <script> 49 - { 50 - const scripts = []; 51 - 52 - // Insert the app script in the viewer process 53 - if (process.env.NODE_ENV === 'development') { 54 - scripts.push('./viewer.js'); 55 - } else { 56 - scripts.push('./dist/viewer.entry.js'); 57 - } 58 - 59 - document.write( 60 - scripts 61 - .map((script) => '<script defer src="' + script + '"><\/script>') 62 - .join('') 63 - ); 64 - } 65 - </script> 66 48 </html>
-29
app/viewer.js
··· 1 - // Webpack entry point for the second Viewer renderer process 2 - const ipcChannel = require('electron').ipcRenderer; 3 - const EEGGraph = require('./components/d3Classes/EEGViewer'); 4 - 5 - let graph = {}; 6 - ipcChannel.on('initGraph', (event, message) => { 7 - graph = new EEGGraph(document.getElementById('graph'), message); 8 - }); 9 - ipcChannel.on('newData', (event, message) => { 10 - graph.updateData(message); 11 - }); 12 - ipcChannel.on('zoomIn', () => { 13 - graph.zoomOut(); 14 - }); 15 - ipcChannel.on('zoomOut', () => { 16 - graph.zoomIn(); 17 - }); 18 - ipcChannel.on('updateChannels', (event, message) => { 19 - graph.updateChannels(message); 20 - }); 21 - ipcChannel.on('updateDomain', (event, message) => { 22 - graph.updateDomain(message); 23 - }); 24 - ipcChannel.on('updateDownsampling', (event, message) => { 25 - graph.updateDownsampling(message); 26 - }); 27 - ipcChannel.on('autoScale', () => { 28 - graph.autoScale(); 29 - });
-688
app/yarn.lock
··· 1 - # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 - # yarn lockfile v1 3 - 4 - 5 - "@babel/register@^7.10.1": 6 - version "7.10.3" 7 - resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.10.3.tgz#b49b6603fc8d214cd2f77a6ed2256bd198b5994b" 8 - integrity sha512-s1il0vdd02HCGwV1iocGJEzcbTNouZqMolSXKXFAiTNJSudPas9jdLQwyPPyAJxdNL6KGJ8pwWIOpKmgO/JWqg== 9 - dependencies: 10 - find-cache-dir "^2.0.0" 11 - lodash "^4.17.13" 12 - make-dir "^2.1.0" 13 - pirates "^4.0.0" 14 - source-map-support "^0.5.16" 15 - 16 - "@babel/runtime-corejs2@^7.10.2": 17 - version "7.10.3" 18 - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs2/-/runtime-corejs2-7.10.3.tgz#81bc99a96bfcb6db3f0dcf73fdc577cc554d341b" 19 - integrity sha512-enKvnR/kKFbZFgXYo165wtSA5OeiTlgsnU4jV3vpKRhfWUJjLS6dfVcjIPeRcgJbgEgdgu0I+UyBWqu6c0GumQ== 20 - dependencies: 21 - core-js "^2.6.5" 22 - regenerator-runtime "^0.13.4" 23 - 24 - "@babel/runtime@7.10.2": 25 - version "7.10.2" 26 - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.2.tgz#d103f21f2602497d38348a32e008637d506db839" 27 - integrity sha512-6sF3uQw2ivImfVIl62RZ7MXhO2tap69WeWK57vAaimT6AZbE4FbqjdEJIN1UqoD6wI6B+1n9UiagafH1sxjOtg== 28 - dependencies: 29 - regenerator-runtime "^0.13.4" 30 - 31 - "@neurosity/pipes@^3.2.3": 32 - version "3.2.3" 33 - resolved "https://registry.yarnpkg.com/@neurosity/pipes/-/pipes-3.2.3.tgz#acc71f9f965c5ba96dd1ba9d1ca9785924217b5e" 34 - integrity sha512-aDa/iTe7OUbwgFFnRgGcB/hTgBdkr21E5vQbpOXZpByQijB4czGDgXJfFtI3zuLKST3bZuiQ7/ZPBA3+P8GENA== 35 - dependencies: 36 - dsp.js "^1.0.1" 37 - fili "^2.0.1" 38 - rxjs "^6.3.1" 39 - 40 - abbrev@1: 41 - version "1.1.1" 42 - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" 43 - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== 44 - 45 - ansi-regex@^2.0.0: 46 - version "2.1.1" 47 - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" 48 - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= 49 - 50 - ansi-regex@^3.0.0: 51 - version "3.0.0" 52 - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" 53 - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= 54 - 55 - aproba@^1.0.3: 56 - version "1.2.0" 57 - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" 58 - integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== 59 - 60 - are-we-there-yet@~1.1.2: 61 - version "1.1.5" 62 - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" 63 - integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== 64 - dependencies: 65 - delegates "^1.0.0" 66 - readable-stream "^2.0.6" 67 - 68 - async@^2.6.2: 69 - version "2.6.4" 70 - resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" 71 - integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== 72 - dependencies: 73 - lodash "^4.17.14" 74 - 75 - balanced-match@^1.0.0: 76 - version "1.0.0" 77 - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 78 - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= 79 - 80 - brace-expansion@^1.1.7: 81 - version "1.1.11" 82 - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 83 - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 84 - dependencies: 85 - balanced-match "^1.0.0" 86 - concat-map "0.0.1" 87 - 88 - buffer-from@^1.0.0: 89 - version "1.1.1" 90 - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" 91 - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== 92 - 93 - buffer@^5.5.0: 94 - version "5.6.0" 95 - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" 96 - integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== 97 - dependencies: 98 - base64-js "^1.0.2" 99 - ieee754 "^1.1.4" 100 - 101 - chownr@^1.1.1, chownr@^1.1.4: 102 - version "1.1.4" 103 - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" 104 - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== 105 - 106 - code-point-at@^1.0.0: 107 - version "1.1.0" 108 - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" 109 - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= 110 - 111 - commondir@^1.0.1: 112 - version "1.0.1" 113 - resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" 114 - integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= 115 - 116 - concat-map@0.0.1: 117 - version "0.0.1" 118 - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 119 - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 120 - 121 - console-control-strings@^1.0.0, console-control-strings@~1.1.0: 122 - version "1.1.0" 123 - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" 124 - integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= 125 - 126 - core-js@^2.6.5: 127 - version "2.6.11" 128 - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" 129 - integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== 130 - 131 - core-util-is@~1.0.0: 132 - version "1.0.2" 133 - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 134 - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= 135 - 136 - debug@^3.2.6: 137 - version "3.2.6" 138 - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" 139 - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== 140 - dependencies: 141 - ms "^2.1.1" 142 - 143 - deep-extend@^0.6.0: 144 - version "0.6.0" 145 - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" 146 - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== 147 - 148 - delegates@^1.0.0: 149 - version "1.0.0" 150 - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" 151 - integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= 152 - 153 - detect-libc@^1.0.2: 154 - version "1.0.3" 155 - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" 156 - integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= 157 - 158 - dsp.js@^1.0.1: 159 - version "1.0.1" 160 - resolved "https://registry.yarnpkg.com/dsp.js/-/dsp.js-1.0.1.tgz#02554a223ee514cd0e226cb6fb88d6d0b3e91308" 161 - integrity sha1-AlVKIj7lFM0OImy2+4jW0LPpEwg= 162 - 163 - fili@^2.0.1: 164 - version "2.0.3" 165 - resolved "https://registry.yarnpkg.com/fili/-/fili-2.0.3.tgz#a2a62dd77f1f2b98e9afff173d24167f4221a83d" 166 - integrity sha512-NW/EY++EMP9f4mSpVCjg5PJnAkkxzh170My+R18sh4CSXu3Puxw3Cs4aQmXoUtNg1EonZmvmR5TLcKEJtX5hQg== 167 - 168 - find-cache-dir@^2.0.0: 169 - version "2.1.0" 170 - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" 171 - integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== 172 - dependencies: 173 - commondir "^1.0.1" 174 - make-dir "^2.0.0" 175 - pkg-dir "^3.0.0" 176 - 177 - find-up@^3.0.0: 178 - version "3.0.0" 179 - resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" 180 - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== 181 - dependencies: 182 - locate-path "^3.0.0" 183 - 184 - fs-constants@^1.0.0: 185 - version "1.0.0" 186 - resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" 187 - integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== 188 - 189 - fs-minipass@^1.2.7: 190 - version "1.2.7" 191 - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" 192 - integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== 193 - dependencies: 194 - minipass "^2.6.0" 195 - 196 - fs.realpath@^1.0.0: 197 - version "1.0.0" 198 - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 199 - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 200 - 201 - gauge@~2.7.3: 202 - version "2.7.4" 203 - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" 204 - integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= 205 - dependencies: 206 - aproba "^1.0.3" 207 - console-control-strings "^1.0.0" 208 - has-unicode "^2.0.0" 209 - object-assign "^4.1.0" 210 - signal-exit "^3.0.0" 211 - string-width "^1.0.1" 212 - strip-ansi "^3.0.1" 213 - wide-align "^1.1.0" 214 - 215 - glob@^7.1.3: 216 - version "7.1.6" 217 - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" 218 - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== 219 - dependencies: 220 - fs.realpath "^1.0.0" 221 - inflight "^1.0.4" 222 - inherits "2" 223 - minimatch "^3.0.4" 224 - once "^1.3.0" 225 - path-is-absolute "^1.0.0" 226 - 227 - has-unicode@^2.0.0: 228 - version "2.0.1" 229 - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" 230 - integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= 231 - 232 - iconv-lite@^0.4.4: 233 - version "0.4.24" 234 - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" 235 - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== 236 - dependencies: 237 - safer-buffer ">= 2.1.2 < 3" 238 - 239 - ignore-walk@^3.0.1: 240 - version "3.0.3" 241 - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" 242 - integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw== 243 - dependencies: 244 - minimatch "^3.0.4" 245 - 246 - inflight@^1.0.4: 247 - version "1.0.6" 248 - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 249 - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 250 - dependencies: 251 - once "^1.3.0" 252 - wrappy "1" 253 - 254 - inherits@2, inherits@~2.0.3: 255 - version "2.0.4" 256 - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 257 - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 258 - 259 - ini@~1.3.0: 260 - version "1.3.8" 261 - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" 262 - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== 263 - 264 - is-fullwidth-code-point@^1.0.0: 265 - version "1.0.0" 266 - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" 267 - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= 268 - dependencies: 269 - number-is-nan "^1.0.0" 270 - 271 - is-fullwidth-code-point@^2.0.0: 272 - version "2.0.0" 273 - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" 274 - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= 275 - 276 - isarray@~1.0.0: 277 - version "1.0.0" 278 - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 279 - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= 280 - 281 - locate-path@^3.0.0: 282 - version "3.0.0" 283 - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" 284 - integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== 285 - dependencies: 286 - p-locate "^3.0.0" 287 - path-exists "^3.0.0" 288 - 289 - lodash@^4.17.13: 290 - version "4.17.20" 291 - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" 292 - integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== 293 - 294 - make-dir@^2.0.0, make-dir@^2.1.0: 295 - version "2.1.0" 296 - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" 297 - integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== 298 - dependencies: 299 - pify "^4.0.1" 300 - semver "^5.6.0" 301 - 302 - minimatch@^3.0.4: 303 - version "3.0.4" 304 - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 305 - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== 306 - dependencies: 307 - brace-expansion "^1.1.7" 308 - 309 - minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5: 310 - version "1.2.6" 311 - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" 312 - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== 313 - 314 - minipass@^2.6.0, minipass@^2.9.0: 315 - version "2.9.0" 316 - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" 317 - integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== 318 - dependencies: 319 - safe-buffer "^5.1.2" 320 - yallist "^3.0.0" 321 - 322 - minizlib@^1.3.3: 323 - version "1.3.3" 324 - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" 325 - integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== 326 - dependencies: 327 - minipass "^2.9.0" 328 - 329 - mkdirp-classic@^0.5.2: 330 - version "0.5.3" 331 - resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" 332 - integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== 333 - 334 - mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5: 335 - version "0.5.5" 336 - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" 337 - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== 338 - dependencies: 339 - minimist "^1.2.5" 340 - 341 - ms@^2.1.1: 342 - version "2.1.2" 343 - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 344 - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 345 - 346 - needle@^2.5.0: 347 - version "2.5.0" 348 - resolved "https://registry.yarnpkg.com/needle/-/needle-2.5.0.tgz#e6fc4b3cc6c25caed7554bd613a5cf0bac8c31c0" 349 - integrity sha512-o/qITSDR0JCyCKEQ1/1bnUXMmznxabbwi/Y4WwJElf+evwJNFNwIDMCCt5IigFVxgeGBJESLohGtIS9gEzo1fA== 350 - dependencies: 351 - debug "^3.2.6" 352 - iconv-lite "^0.4.4" 353 - sax "^1.2.4" 354 - 355 - node-modules-regexp@^1.0.0: 356 - version "1.0.0" 357 - resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" 358 - integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= 359 - 360 - node-pre-gyp@^0.15.0: 361 - version "0.15.0" 362 - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.15.0.tgz#c2fc383276b74c7ffa842925241553e8b40f1087" 363 - integrity sha512-7QcZa8/fpaU/BKenjcaeFF9hLz2+7S9AqyXFhlH/rilsQ/hPZKK32RtR5EQHJElgu+q5RfbJ34KriI79UWaorA== 364 - dependencies: 365 - detect-libc "^1.0.2" 366 - mkdirp "^0.5.3" 367 - needle "^2.5.0" 368 - nopt "^4.0.1" 369 - npm-packlist "^1.1.6" 370 - npmlog "^4.0.2" 371 - rc "^1.2.7" 372 - rimraf "^2.6.1" 373 - semver "^5.3.0" 374 - tar "^4.4.2" 375 - 376 - nopt@^4.0.1: 377 - version "4.0.3" 378 - resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" 379 - integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== 380 - dependencies: 381 - abbrev "1" 382 - osenv "^0.1.4" 383 - 384 - npm-bundled@^1.0.1: 385 - version "1.1.1" 386 - resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b" 387 - integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA== 388 - dependencies: 389 - npm-normalize-package-bin "^1.0.1" 390 - 391 - npm-normalize-package-bin@^1.0.1: 392 - version "1.0.1" 393 - resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" 394 - integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== 395 - 396 - npm-packlist@^1.1.6: 397 - version "1.4.8" 398 - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" 399 - integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== 400 - dependencies: 401 - ignore-walk "^3.0.1" 402 - npm-bundled "^1.0.1" 403 - npm-normalize-package-bin "^1.0.1" 404 - 405 - npmlog@^4.0.2: 406 - version "4.1.2" 407 - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" 408 - integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== 409 - dependencies: 410 - are-we-there-yet "~1.1.2" 411 - console-control-strings "~1.1.0" 412 - gauge "~2.7.3" 413 - set-blocking "~2.0.0" 414 - 415 - number-is-nan@^1.0.0: 416 - version "1.0.1" 417 - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" 418 - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= 419 - 420 - object-assign@^4.1.0: 421 - version "4.1.1" 422 - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 423 - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= 424 - 425 - once@^1.3.0: 426 - version "1.4.0" 427 - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 428 - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== 429 - dependencies: 430 - wrappy "1" 431 - 432 - os-homedir@^1.0.0: 433 - version "1.0.2" 434 - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" 435 - integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= 436 - 437 - os-tmpdir@^1.0.0: 438 - version "1.0.2" 439 - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" 440 - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= 441 - 442 - osenv@^0.1.4: 443 - version "0.1.5" 444 - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" 445 - integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== 446 - dependencies: 447 - os-homedir "^1.0.0" 448 - os-tmpdir "^1.0.0" 449 - 450 - p-limit@^2.0.0: 451 - version "2.3.0" 452 - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" 453 - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== 454 - dependencies: 455 - p-try "^2.0.0" 456 - 457 - p-locate@^3.0.0: 458 - version "3.0.0" 459 - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" 460 - integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== 461 - dependencies: 462 - p-limit "^2.0.0" 463 - 464 - p-try@^2.0.0: 465 - version "2.2.0" 466 - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" 467 - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== 468 - 469 - path-exists@^3.0.0: 470 - version "3.0.0" 471 - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" 472 - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= 473 - 474 - path-is-absolute@^1.0.0: 475 - version "1.0.1" 476 - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 477 - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 478 - 479 - pify@^4.0.1: 480 - version "4.0.1" 481 - resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" 482 - integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== 483 - 484 - pirates@^4.0.0: 485 - version "4.0.1" 486 - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" 487 - integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== 488 - dependencies: 489 - node-modules-regexp "^1.0.0" 490 - 491 - pkg-dir@^3.0.0: 492 - version "3.0.0" 493 - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" 494 - integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== 495 - dependencies: 496 - find-up "^3.0.0" 497 - 498 - process-nextick-args@~2.0.0: 499 - version "2.0.1" 500 - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" 501 - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== 502 - 503 - rc@^1.2.7: 504 - version "1.2.8" 505 - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" 506 - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== 507 - dependencies: 508 - deep-extend "^0.6.0" 509 - ini "~1.3.0" 510 - minimist "^1.2.0" 511 - strip-json-comments "~2.0.1" 512 - 513 - readable-stream@^2.0.6: 514 - version "2.3.7" 515 - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" 516 - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== 517 - dependencies: 518 - core-util-is "~1.0.0" 519 - inherits "~2.0.3" 520 - isarray "~1.0.0" 521 - process-nextick-args "~2.0.0" 522 - safe-buffer "~5.1.1" 523 - string_decoder "~1.1.1" 524 - util-deprecate "~1.0.1" 525 - 526 - regenerator-runtime@^0.13.4: 527 - version "0.13.5" 528 - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" 529 - integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== 530 - 531 - rimraf@^2.6.1: 532 - version "2.7.1" 533 - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" 534 - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== 535 - dependencies: 536 - glob "^7.1.3" 537 - 538 - rxjs@^6.3.1: 539 - version "6.5.5" 540 - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec" 541 - integrity sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ== 542 - dependencies: 543 - tslib "^1.9.0" 544 - 545 - safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@^5.2.1, safe-buffer@~5.2.0: 546 - version "5.2.1" 547 - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" 548 - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== 549 - 550 - safe-buffer@~5.1.0, safe-buffer@~5.1.1: 551 - version "5.1.2" 552 - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 553 - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== 554 - 555 - "safer-buffer@>= 2.1.2 < 3": 556 - version "2.1.2" 557 - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" 558 - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== 559 - 560 - sax@^1.2.4: 561 - version "1.2.4" 562 - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" 563 - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== 564 - 565 - semver@^5.3.0, semver@^5.6.0: 566 - version "5.7.1" 567 - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" 568 - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== 569 - 570 - set-blocking@~2.0.0: 571 - version "2.0.0" 572 - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" 573 - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= 574 - 575 - signal-exit@^3.0.0: 576 - version "3.0.3" 577 - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" 578 - integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== 579 - 580 - simple-concat@^1.0.0: 581 - version "1.0.1" 582 - resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" 583 - integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== 584 - 585 - simple-get@^3.0.3: 586 - version "3.1.1" 587 - resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.1.1.tgz#cc7ba77cfbe761036fbfce3d021af25fc5584d55" 588 - integrity sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA== 589 - dependencies: 590 - decompress-response "^4.2.0" 591 - once "^1.3.1" 592 - simple-concat "^1.0.0" 593 - 594 - source-map-support@^0.5.16: 595 - version "0.5.19" 596 - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" 597 - integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== 598 - dependencies: 599 - buffer-from "^1.0.0" 600 - source-map "^0.6.0" 601 - 602 - source-map@^0.6.0: 603 - version "0.6.1" 604 - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" 605 - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== 606 - 607 - string-width@^1.0.1: 608 - version "1.0.2" 609 - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" 610 - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= 611 - dependencies: 612 - code-point-at "^1.0.0" 613 - is-fullwidth-code-point "^1.0.0" 614 - strip-ansi "^3.0.0" 615 - 616 - "string-width@^1.0.2 || 2": 617 - version "2.1.1" 618 - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" 619 - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== 620 - dependencies: 621 - is-fullwidth-code-point "^2.0.0" 622 - strip-ansi "^4.0.0" 623 - 624 - string_decoder@~1.1.1: 625 - version "1.1.1" 626 - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" 627 - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== 628 - dependencies: 629 - safe-buffer "~5.1.0" 630 - 631 - strip-ansi@^3.0.0, strip-ansi@^3.0.1: 632 - version "3.0.1" 633 - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" 634 - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= 635 - dependencies: 636 - ansi-regex "^2.0.0" 637 - 638 - strip-ansi@^4.0.0: 639 - version "4.0.0" 640 - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" 641 - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= 642 - dependencies: 643 - ansi-regex "^3.0.0" 644 - 645 - strip-json-comments@~2.0.1: 646 - version "2.0.1" 647 - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" 648 - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= 649 - 650 - tar@^4.4.2: 651 - version "4.4.19" 652 - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" 653 - integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA== 654 - dependencies: 655 - chownr "^1.1.4" 656 - fs-minipass "^1.2.7" 657 - minipass "^2.9.0" 658 - minizlib "^1.3.3" 659 - mkdirp "^0.5.5" 660 - safe-buffer "^5.2.1" 661 - yallist "^3.1.1" 662 - 663 - tslib@^1.9.0: 664 - version "1.13.0" 665 - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" 666 - integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== 667 - 668 - util-deprecate@~1.0.1: 669 - version "1.0.2" 670 - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 671 - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= 672 - 673 - wide-align@^1.1.0: 674 - version "1.1.3" 675 - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" 676 - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== 677 - dependencies: 678 - string-width "^1.0.2 || 2" 679 - 680 - wrappy@1: 681 - version "1.0.2" 682 - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 683 - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== 684 - 685 - yallist@^3.0.0, yallist@^3.1.1: 686 - version "3.1.1" 687 - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" 688 - integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
+51
internals/scripts/BuildViewers.mjs
··· 1 + /** 2 + * Post-build script for the EEG viewer assets. 3 + * 4 + * electron-vite's preload and renderer builds only support a single entry 5 + * point each. This script uses esbuild directly to compile the viewer 6 + * preload and the viewer renderer page after the main electron-vite build. 7 + * 8 + * Run automatically via the "postbuild" npm lifecycle hook. 9 + */ 10 + import { build } from 'esbuild'; 11 + import { readFileSync, writeFileSync, mkdirSync } from 'fs'; 12 + import { resolve, join } from 'path'; 13 + import { fileURLToPath } from 'url'; 14 + 15 + const __dirname = fileURLToPath(new URL('.', import.meta.url)); 16 + const root = resolve(__dirname, '../..'); 17 + 18 + // ---------------------------------------------------------------- 19 + // 1. Viewer preload: src/preload/viewer.ts → out/preload/viewer.js 20 + // ---------------------------------------------------------------- 21 + await build({ 22 + entryPoints: [resolve(root, 'src/preload/viewer.ts')], 23 + bundle: true, 24 + platform: 'node', 25 + external: ['electron'], 26 + outfile: resolve(root, 'out/preload/viewer.js'), 27 + format: 'cjs', 28 + }); 29 + 30 + // ---------------------------------------------------------------- 31 + // 2. Viewer renderer: src/renderer/viewer.ts → out/renderer/viewer.js 32 + // ---------------------------------------------------------------- 33 + const rendererOut = resolve(root, 'out/renderer'); 34 + mkdirSync(rendererOut, { recursive: true }); 35 + 36 + await build({ 37 + entryPoints: [resolve(root, 'src/renderer/viewer.ts')], 38 + bundle: true, 39 + platform: 'browser', 40 + outfile: join(rendererOut, 'viewer.js'), 41 + format: 'esm', 42 + }); 43 + 44 + // ---------------------------------------------------------------- 45 + // 3. viewer.html: copy src → out, update script src .ts → .js 46 + // ---------------------------------------------------------------- 47 + let html = readFileSync(resolve(root, 'src/renderer/viewer.html'), 'utf-8'); 48 + html = html.replace('src="./viewer.ts"', 'src="./viewer.js"'); 49 + writeFileSync(join(rendererOut, 'viewer.html'), html); 50 + 51 + console.log('Viewer builds complete: out/preload/viewer.js, out/renderer/viewer.{js,html}');
-5
internals/scripts/CheckYarn.js
··· 1 - if (!/yarn\.js$/.test(process.env.npm_execpath || '')) { 2 - console.warn( 3 - "\u001b[33mYou don't seem to be using yarn. This could produce unexpected results.\u001b[39m" 4 - ); 5 - }
+2 -2
internals/scripts/InstallPyodide.js
··· 9 9 const PYODIDE_VERSION = '0.21.0'; 10 10 const TAR_NAME = `pyodide-build-${PYODIDE_VERSION}.tar.bz2`; 11 11 const TAR_URL = `https://github.com/pyodide/pyodide/releases/download/${PYODIDE_VERSION}/pyodide-build-${PYODIDE_VERSION}.tar.bz2`; 12 - const PYODIDE_DIR = 'app/utils/pyodide/src/'; 12 + const PYODIDE_DIR = 'src/renderer/utils/pyodide/src/'; 13 13 14 14 const writeAndUnzipFile = (response) => { 15 15 const filePath = `${PYODIDE_DIR}${TAR_NAME}`; ··· 61 61 console.log( 62 62 `${chalk.green.bold(`Downloading pyodide ${PYODIDE_VERSION}...`)}` 63 63 ); 64 - mkdirp.sync(`app/utils/pyodide/src`); 64 + mkdirp.sync(`src/renderer/utils/pyodide/src`); 65 65 https.get(TAR_URL, downloadFile); 66 66 })();
+71 -157
package.json
··· 1 1 { 2 2 "name": "brainwaves", 3 3 "productName": "BrainWaves", 4 + "version": "1.0.0", 4 5 "description": "EEG Experiment Desktop Application", 6 + "main": "./out/main/index.js", 5 7 "scripts": { 6 - "build": "concurrently \"yarn build-main\" \"yarn build-renderer\"", 7 - "build-dll": "cross-env NODE_ENV=development webpack --config ./configs/webpack.config.renderer.dev.dll.babel.js --colors", 8 - "build-e2e": "cross-env E2E_BUILD=true yarn build", 9 - "build-main": "cross-env NODE_ENV=production webpack --config ./configs/webpack.config.main.prod.babel.js --colors", 10 - "build-renderer": "cross-env NODE_ENV=production webpack --config ./configs/webpack.config.renderer.prod.babel.js --colors", 11 - "dev": "cross-env START_HOT=1 node -r @babel/register ./internals/scripts/CheckPortInUse.js && cross-env START_HOT=1 yarn start-renderer-dev", 12 - "electron-rebuild": "electron-rebuild --parallel --force --types prod,dev,optional --module-dir app", 8 + "dev": "node internals/scripts/patchDeps.mjs && electron-vite dev", 9 + "build": "electron-vite build", 10 + "postbuild": "node internals/scripts/BuildViewers.mjs", 11 + "preview": "electron-vite preview", 12 + "package": "npm run build && electron-builder build --publish never", 13 + "package-all": "npm run build && electron-builder build -mwl", 14 + "package-ci": "npm run build && electron-builder --publish always", 15 + "package-mac": "npm run build && electron-builder build --mac", 16 + "package-linux": "npm run build && electron-builder build --linux", 17 + "package-win": "npm run build && electron-builder build --win --x64", 18 + "postinstall": "electron-builder install-app-deps && node internals/scripts/InstallPyodide.js && node internals/scripts/patchDeps.mjs", 13 19 "lint": "cross-env NODE_ENV=development eslint . --cache --ext .js,.jsx,.ts,.tsx", 14 - "lint-fix": "yarn --silent lint --fix", 20 + "lint-fix": "npm run lint -- --fix", 15 21 "lint-styles": "stylelint --ignore-path .eslintignore '**/*.*(css|scss)' --syntax scss", 16 - "lint-styles-fix": "yarn --silent lint-styles --fix; exit 0", 17 - "package": "yarn build && electron-builder build --publish never", 18 - "package-all": "yarn build && electron-builder build -mwl", 19 - "package-ci": "yarn postinstall && yarn build && electron-builder --publish always", 20 - "package-mac": "yarn build && electron-builder build --mac", 21 - "package-linux": "yarn build && electron-builder build --linux", 22 - "package-win": "yarn build && electron-builder build --win --x64", 23 - "postinstall": "node -r @babel/register internals/scripts/CheckNativeDep.js && electron-builder install-app-deps && yarn build-dll && opencollective-postinstall && node -r @babel/register internals/scripts/InstallPyodide.js", 22 + "lint-styles-fix": "npm run lint-styles -- --fix; exit 0", 24 23 "postlint-fix": "prettier --ignore-path .eslintignore --single-quote --write '**/*.{js,jsx,json,html,css,less,scss,yml}'", 25 24 "postlint-styles-fix": "prettier --ignore-path .eslintignore --single-quote --write '**/*.{css,scss}'", 26 - "preinstall": "node ./internals/scripts/CheckYarn.js", 27 - "prestart": "yarn build", 28 - "start": "cross-env NODE_ENV=production electron ./app/main.prod.js", 29 - "start-main-debug": "yarn start-main-dev --inspect=5858 --remote-debugging-port=9223", 30 - "start-main-dev": "cross-env START_HOT=1 NODE_ENV=development electron -r ./internals/scripts/BabelRegister ./app/main.dev.ts", 31 - "start-renderer-dev": "cross-env NODE_ENV=development webpack-dev-server --config configs/webpack.config.renderer.dev.babel.js", 32 - "test": "cross-env BABEL_DISABLE_CACHE=1 jest --passWithNoTests", 33 - "test-all": "yarn lint && yarn tsc && yarn build && yarn test", 34 - "test-e2e": "node -r @babel/register ./internals/scripts/CheckBuildsExist.js && cross-env NODE_ENV=test testcafe electron:./app ./test/e2e/HomePage.e2e.ts", 35 - "test-e2e-live": "node -r @babel/register ./internals/scripts/CheckBuildsExist.js && cross-env NODE_ENV=test testcafe --live electron:./app ./test/e2e/HomePage.e2e.ts", 36 - "test-watch": "yarn test --watch" 25 + "test": "cross-env jest --passWithNoTests", 26 + "test-all": "npm run lint && npm run tsc && npm run build && npm test", 27 + "tsc": "tsc --noEmit" 37 28 }, 38 29 "lint-staged": { 39 30 "*.{js,jsx,ts,tsx}": [ 40 31 "cross-env NODE_ENV=development eslint --cache" 41 32 ], 42 - "{*.json,.{babelrc,eslintrc,prettierrc,stylelintrc}}": [ 33 + "{*.json,.{eslintrc,prettierrc,stylelintrc}}": [ 43 34 "prettier --ignore-path .eslintignore --parser json --write" 44 35 ], 45 36 "*.{css,scss}": [ ··· 53 44 "build": { 54 45 "productName": "BrainWaves", 55 46 "appId": "com.electron.brainwaves", 47 + "asar": true, 56 48 "files": [ 57 - "dist/", 58 - "node_modules/", 59 - "assets/", 60 - "utils/pyodide/utils.py", 61 - "utils/pyodide/pyimport.py", 62 - "app.html", 63 - "main.prod.js", 64 - "main.prod.js.map", 49 + "out/**/*", 50 + "node_modules/**/*", 65 51 "package.json" 66 52 ], 67 53 "dmg": { ··· 104 90 }, 105 91 "extraResources": [ 106 92 { 107 - "from": "./app/experiments/", 93 + "from": "./src/renderer/experiments/", 108 94 "to": "experiments", 95 + "filter": "**/*" 96 + }, 97 + { 98 + "from": "./src/renderer/utils/pyodide/src/", 99 + "to": "pyodide", 109 100 "filter": "**/*" 110 101 } 111 102 ] ··· 157 148 "json" 158 149 ], 159 150 "moduleDirectories": [ 160 - "node_modules", 161 - "app/node_modules" 151 + "node_modules" 162 152 ], 163 - "setupFiles": [ 164 - "./internals/scripts/CheckBuildsExist.js" 165 - ] 153 + "setupFiles": [] 166 154 }, 167 155 "devDependencies": { 168 - "@amilajack/testcafe-browser-provider-electron": "^0.0.15-alpha.1", 169 - "@babel/core": "^7.11.1", 170 156 "@babel/plugin-proposal-class-properties": "^7.10.4", 171 157 "@babel/plugin-proposal-decorators": "^7.10.5", 172 - "@babel/plugin-proposal-do-expressions": "^7.10.4", 173 - "@babel/plugin-proposal-export-default-from": "^7.10.4", 174 - "@babel/plugin-proposal-export-namespace-from": "^7.10.4", 175 - "@babel/plugin-proposal-function-bind": "^7.10.5", 176 - "@babel/plugin-proposal-function-sent": "^7.10.4", 177 - "@babel/plugin-proposal-json-strings": "^7.10.4", 178 - "@babel/plugin-proposal-logical-assignment-operators": "^7.11.0", 179 - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.4", 180 - "@babel/plugin-proposal-numeric-separator": "^7.10.4", 181 - "@babel/plugin-proposal-optional-chaining": "^7.11.0", 182 - "@babel/plugin-proposal-pipeline-operator": "^7.10.5", 183 - "@babel/plugin-proposal-throw-expressions": "^7.10.4", 184 - "@babel/plugin-syntax-dynamic-import": "^7.8.3", 185 - "@babel/plugin-syntax-import-meta": "^7.10.4", 186 - "@babel/plugin-transform-react-constant-elements": "^7.10.4", 187 - "@babel/plugin-transform-react-inline-elements": "^7.10.4", 188 - "@babel/preset-env": "^7.11.0", 189 - "@babel/preset-react": "^7.10.4", 190 - "@babel/preset-typescript": "^7.10.4", 191 - "@babel/register": "^7.10.5", 192 - "@types/enzyme": "^3.10.5", 193 - "@types/enzyme-adapter-react-16": "^1.0.6", 194 158 "@types/history": "^4.7.6", 195 159 "@types/jest": "^26.0.5", 196 - "@types/node": "12", 197 - "@types/react": "16.9.38", 160 + "@types/node": "^18.0.0", 161 + "@types/react": "^16.9.38", 198 162 "@types/react-dom": "^16.9.8", 199 163 "@types/react-redux": "^7.1.9", 200 164 "@types/react-router": "^5.1.8", 201 165 "@types/react-router-dom": "^5.1.5", 202 166 "@types/react-test-renderer": "^16.9.2", 203 167 "@types/redux-logger": "^3.0.8", 204 - "@types/webpack": "^4.41.21", 205 - "@types/webpack-env": "^1.15.2", 206 - "@typescript-eslint/eslint-plugin": "^3.6.1", 207 - "@typescript-eslint/parser": "^3.6.1", 208 - "babel-eslint": "^10.1.0", 209 - "babel-jest": "^26.1.0", 210 - "babel-loader": "^8.1.0", 211 - "babel-plugin-dev-expression": "^0.2.2", 212 - "babel-plugin-transform-react-remove-prop-types": "^0.4.24", 213 - "browserslist-config-erb": "^0.0.1", 168 + "@typescript-eslint/eslint-plugin": "^5.0.0", 169 + "@typescript-eslint/parser": "^5.0.0", 170 + "@vitejs/plugin-react": "^4.0.0", 214 171 "chalk": "^4.1.0", 215 - "concurrently": "^5.2.0", 216 - "core-js": "^3.6.5", 172 + "concurrently": "^7.0.0", 217 173 "cross-env": "^7.0.0", 218 - "css-loader": "^3.6.0", 219 - "detect-port": "^1.3.0", 220 - "electron": "^9", 221 - "electron-builder": "^22.3.6", 222 - "electron-devtools-installer": "^2.2.4", 223 - "electron-rebuild": "^1.10.0", 224 - "enzyme": "^3.11.0", 225 - "enzyme-adapter-react-16": "^1.15.2", 226 - "enzyme-to-json": "^3.5.0", 227 - "eslint": "^7.5.0", 228 - "eslint-config-airbnb": "^18.2.0", 229 - "eslint-config-airbnb-typescript": "^9.0.0", 230 - "eslint-config-erb": "^1.0.0", 231 - "eslint-config-prettier": "^6.11.0", 232 - "eslint-import-resolver-webpack": "^0.12.2", 233 - "eslint-plugin-compat": "^3.8.0", 234 - "eslint-plugin-import": "^2.22.0", 235 - "eslint-plugin-jest": "^23.18.0", 236 - "eslint-plugin-jsx-a11y": "6.3.1", 237 - "eslint-plugin-prettier": "^3.1.4", 238 - "eslint-plugin-promise": "^4.2.1", 239 - "eslint-plugin-react": "^7.20.3", 240 - "eslint-plugin-react-hooks": "^4.0.8", 241 - "eslint-plugin-testcafe": "^0.2.1", 242 - "file-loader": "^6.0.0", 243 - "husky": "^4.2.5", 174 + "electron": "^39.6.1", 175 + "electron-builder": "^24.0.0", 176 + "electron-vite": "^5.0.0", 177 + "eslint": "^8.0.0", 178 + "eslint-config-prettier": "^8.0.0", 179 + "eslint-plugin-import": "^2.27.0", 180 + "eslint-plugin-jsx-a11y": "^6.6.1", 181 + "eslint-plugin-prettier": "^4.2.0", 182 + "eslint-plugin-react": "^7.33.0", 183 + "eslint-plugin-react-hooks": "^4.6.0", 184 + "husky": "^8.0.0", 244 185 "identity-obj-proxy": "^3.0.0", 245 - "jest": "^26.1.0", 246 - "lint-staged": "^10.2.11", 247 - "mini-css-extract-plugin": "^0.9.0", 248 - "opencollective-postinstall": "^2.0.3", 249 - "optimize-css-assets-webpack-plugin": "^5.0.3", 250 - "patch-package": "6.2.2", 186 + "jest": "^27.0.0", 187 + "lint-staged": "^13.0.0", 188 + "mkdirp": "^1.0.4", 251 189 "prettier": "^2.0.5", 252 190 "react-test-renderer": "^16.12.0", 253 191 "redux-logger": "^3.0.6", 254 192 "rimraf": "^3.0.0", 255 - "sass-loader": "^9.0.2", 256 - "style-loader": "^1.2.1", 257 - "stylelint": "^13.6.1", 258 - "stylelint-config-prettier": "^8.0.2", 259 - "stylelint-config-standard": "^20.0.0", 193 + "sass": "^1.50.0", 194 + "stylelint": "^14.0.0", 195 + "stylelint-config-prettier": "^9.0.0", 196 + "stylelint-config-standard": "^26.0.0", 260 197 "tar-fs": "2.0.1", 261 - "terser-webpack-plugin": "^3.0.7", 262 - "testcafe": "^1.8.8", 263 - "testcafe-browser-provider-electron": "^0.0.15", 264 - "testcafe-react-selectors": "^4.0.0", 265 - "typescript": "^3.9.7", 266 - "typings-for-css-modules-loader": "^1.7.0", 198 + "typescript": "^5.0.0", 267 199 "unbzip2-stream": "1.4.2", 268 - "url-loader": "^4.1.0", 269 - "webpack": "^4.43.0", 270 - "webpack-bundle-analyzer": "^3.8.0", 271 - "webpack-cli": "^3.3.12", 272 - "webpack-dev-server": "^3.11.0", 273 - "webpack-merge": "^5.0.9", 274 - "yarn": "^1.21.1" 200 + "vite": "^5.0.0" 275 201 }, 276 202 "dependencies": { 277 - "@babel/runtime": "7.10.2", 278 - "@babel/runtime-corejs2": "^7.10.2", 203 + "@electron-toolkit/preload": "^3.0.2", 204 + "@electron-toolkit/utils": "^4.0.0", 279 205 "@fortawesome/fontawesome-free": "^5.13.0", 280 - "@hot-loader/react-dom": "^16.13.0", 206 + "@neurosity/pipes": "^5.2.1", 281 207 "@nteract/messaging": "^7.0.7", 282 208 "@nteract/transforms": "^4.4.7", 283 209 "@reduxjs/toolkit": "^1.4.0", 210 + "@types/react-plotly.js": "^2.6.4", 284 211 "ajv": "^6.12.2", 285 212 "caniuse-lite": "^1.0.30001241", 286 213 "connected-react-router": "^6.6.1", 287 - "core-js": "^3.6.5", 288 - "d3": "^5.16.0", 289 - "devtron": "^1.4.0", 290 - "electron-debug": "^3.1.0", 214 + "d3": "^7.9.0", 291 215 "electron-log": "^4.2.2", 292 - "electron-updater": "^4.3.1", 216 + "electron-updater": "^5.0.0", 217 + "events": "^3.3.0", 293 218 "hazardous": "^0.3.0", 294 219 "history": "^4.7.2", 295 220 "lab.js": "^21.0.0-rc3", ··· 300 225 "mousetrap": "^1.6.5", 301 226 "muse-js": "^3.1.0", 302 227 "papaparse": "^5.2.0", 303 - "plotly.js": "^1.54.2", 228 + "pathe": "^2.0.3", 229 + "plotly.js": "^2.35.3", 304 230 "rc-slider": "9.2.4", 305 231 "react": "^16.13.1", 306 232 "react-dom": "^16.12.0", 307 - "react-hot-loader": "^4.12.21", 308 - "react-plotly.js": "^2.4.0", 233 + "react-plotly.js": "^2.6.0", 309 234 "react-redux": "^7.2.0", 310 235 "react-router": "^5.2.0", 311 236 "react-router-dom": "^5.2.0", 312 - "react-router-redux": "^5.0.0-alpha.9", 313 237 "react-toastify": "^6.0.5", 314 238 "recursive-readdir": "^2.2.2", 315 239 "redux": "^4.0.5", 316 - "redux-observable": "^1.2.0", 240 + "redux-observable": "^2.0.0-rc.2", 317 241 "redux-thunk": "^2.3.0", 318 242 "regenerator-runtime": "^0.13.5", 319 - "rxjs": "^6.5.5", 243 + "rxjs": "^7.8.2", 320 244 "rxjs-compat": "^6.5.5", 321 245 "semantic-ui-css": "^2.4.1", 322 246 "semantic-ui-react": "^0.88.2", 323 247 "simple-statistics": "^7.1.0", 324 248 "simplify-js": "^1.2.4", 325 - "source-map-support": "^0.5.19", 326 - "spawnteract": "^5.0.1", 327 249 "typesafe-actions": "^5.1.0", 328 250 "ws": "^7.3.0" 329 251 }, 330 - "devEngines": { 331 - "node": ">=7.x", 332 - "npm": ">=4.x", 333 - "yarn": ">=0.21.3" 252 + "engines": { 253 + "node": ">=18.x", 254 + "npm": ">=8.x" 334 255 }, 335 - "browserslist": [], 336 256 "prettier": { 337 257 "overrides": [ 338 258 { 339 259 "files": [ 340 260 ".prettierrc", 341 - ".babelrc", 342 261 ".eslintrc", 343 262 ".stylelintrc" 344 263 ], ··· 353 272 "extends": [ 354 273 "stylelint-config-standard", 355 274 "stylelint-config-prettier" 356 - ] 357 - }, 358 - "renovate": { 359 - "extends": [ 360 - "bliss" 361 275 ] 362 276 }, 363 277 "husky": {
+483
src/main/index.ts
··· 1 + /* eslint no-console: off */ 2 + 3 + /** 4 + * Electron main process entry point. 5 + * All Node.js / filesystem / shell operations the renderer needs 6 + * are handled here via ipcMain handlers and exposed via the preload. 7 + */ 8 + import { app, BrowserWindow, ipcMain, dialog, shell, session } from 'electron'; 9 + import path from 'path'; 10 + import fs from 'fs'; 11 + import os from 'os'; 12 + import recursive from 'recursive-readdir'; 13 + import Papa from 'papaparse'; 14 + import mkdirp from 'mkdirp'; 15 + import { autoUpdater } from 'electron-updater'; 16 + import log from 'electron-log'; 17 + import { is, optimizer } from '@electron-toolkit/utils'; 18 + import MenuBuilder from './menu'; 19 + import { FILE_TYPES } from '../renderer/constants/constants'; 20 + 21 + // Chrome extension IDs for React and Redux DevTools 22 + const REACT_DEVTOOLS_ID = 'fmkadmapgofadopljbjfkapdkoienihi'; 23 + const REDUX_DEVTOOLS_ID = 'lmhkpmbekcpmknklioeibfkpmmfibljd'; 24 + 25 + // Needed for WASM/SharedArrayBuffer support (pyodide) 26 + app.commandLine.appendSwitch( 27 + 'enable-experimental-web-platform-features', 28 + 'true' 29 + ); 30 + 31 + export default class AppUpdater { 32 + constructor() { 33 + log.transports.file.level = 'info'; 34 + autoUpdater.logger = log; 35 + autoUpdater.checkForUpdatesAndNotify(); 36 + } 37 + } 38 + 39 + let mainWindow: BrowserWindow | null = null; 40 + 41 + 42 + 43 + const installExtensions = async () => { 44 + const forceDownload = !!process.env.UPGRADE_EXTENSIONS; 45 + const sess = session.defaultSession; 46 + const extDir = path.join(app.getPath('userData'), 'extensions'); 47 + 48 + const loadExt = async (id: string) => { 49 + const extPath = path.join(extDir, id); 50 + if (!fs.existsSync(extPath)) return; 51 + const existing = sess.extensions.getAllExtensions().find((e) => e.id === id); 52 + if (existing && !forceDownload) return; 53 + if (existing) { 54 + sess.removeExtension(id); 55 + } 56 + await sess.extensions.loadExtension(extPath); 57 + }; 58 + 59 + await Promise.all([REACT_DEVTOOLS_ID, REDUX_DEVTOOLS_ID].map(loadExt)).catch( 60 + console.log 61 + ); 62 + }; 63 + 64 + // ------------------------------------------------------------------ 65 + // Filesystem helpers (mirroring renderer's storage.ts / write.ts) 66 + // ------------------------------------------------------------------ 67 + 68 + const workspaces = path.join(os.homedir(), 'BrainWaves_Workspaces'); 69 + 70 + const getWorkspaceDir = (title: string) => path.join(workspaces, title); 71 + 72 + const mkdirPathSync = (dirPath: string) => mkdirp.sync(dirPath); 73 + 74 + // Active EEG write streams keyed by a UUID-like id 75 + const activeStreams = new Map<string, fs.WriteStream>(); 76 + 77 + // ------------------------------------------------------------------ 78 + // IPC handlers 79 + // ------------------------------------------------------------------ 80 + 81 + // Dialogs 82 + ipcMain.handle('dialog:showOpen', (_event, options) => 83 + dialog.showOpenDialog(mainWindow!, options) 84 + ); 85 + 86 + ipcMain.handle('dialog:showMessage', (_event, options) => 87 + dialog.showMessageBox(mainWindow!, options) 88 + ); 89 + 90 + ipcMain.handle('dialog:showSave', (_event, options) => 91 + dialog.showSaveDialog(mainWindow!, options) 92 + ); 93 + 94 + ipcMain.handle('loadDialog', async (_event, fileType) => { 95 + if (fileType === FILE_TYPES.STIMULUS_DIR) { 96 + const result = await dialog.showOpenDialog(mainWindow!, { 97 + title: 'Select a folder of images', 98 + properties: ['openDirectory'], 99 + }); 100 + return result.canceled ? '' : result.filePaths[0]; 101 + } 102 + const result = await dialog.showOpenDialog(mainWindow!, { 103 + title: 'Select a jsPsych timeline file', 104 + properties: ['openFile', 'promptToCreate'], 105 + }); 106 + return result.canceled ? null : result.filePaths[0]; 107 + }); 108 + 109 + // Shell 110 + ipcMain.handle('shell:showItemInFolder', (_event, fullPath) => 111 + shell.showItemInFolder(fullPath) 112 + ); 113 + 114 + ipcMain.handle('shell:moveItemToTrash', (_event, fullPath) => 115 + shell.trashItem(fullPath) 116 + ); 117 + 118 + // Workspace management 119 + ipcMain.handle('fs:getWorkspaceDir', (_event, title) => 120 + getWorkspaceDir(title) 121 + ); 122 + 123 + ipcMain.handle('fs:createWorkspaceDir', (_event, title) => { 124 + mkdirPathSync(getWorkspaceDir(title)); 125 + }); 126 + 127 + ipcMain.handle('fs:readWorkspaces', () => { 128 + try { 129 + return fs 130 + .readdirSync(workspaces) 131 + .filter((workspace) => workspace !== '.DS_Store'); 132 + } catch (e: any) { 133 + if (e.code === 'ENOENT') { 134 + mkdirPathSync(workspaces); 135 + } 136 + console.log(e); 137 + return []; 138 + } 139 + }); 140 + 141 + ipcMain.handle('fs:readAndParseState', (_event, dir) => { 142 + try { 143 + const state = JSON.parse( 144 + fs.readFileSync(path.join(workspaces, dir, 'appState.json'), { 145 + encoding: 'utf8', 146 + }) 147 + ); 148 + return state; 149 + } catch (e: any) { 150 + if (e.code === 'ENOENT') { 151 + console.log('appState does not exist for recent workspace'); 152 + } 153 + return null; 154 + } 155 + }); 156 + 157 + ipcMain.handle('fs:storeExperimentState', (_event, state: any) => { 158 + fs.writeFileSync( 159 + path.join(getWorkspaceDir(state.title), 'appState.json'), 160 + JSON.stringify(state) 161 + ); 162 + }); 163 + 164 + ipcMain.handle('fs:restoreExperimentState', (_event, state: any) => { 165 + if (state.type !== 'NONE') { 166 + const timestampedState = { ...state, subject: '', group: '', session: 1 }; 167 + if (!timestampedState.title) return; 168 + fs.writeFileSync( 169 + path.join(getWorkspaceDir(timestampedState.title), 'appState.json'), 170 + JSON.stringify(timestampedState) 171 + ); 172 + } 173 + }); 174 + 175 + ipcMain.handle('fs:readWorkspaceRawEEGData', async (_event, title) => { 176 + try { 177 + const files = await recursive(getWorkspaceDir(title)); 178 + return files 179 + .filter((filepath) => filepath.slice(-7).includes('raw.csv')) 180 + .map((filepath) => ({ name: path.basename(filepath), path: filepath })); 181 + } catch (e: any) { 182 + if (e.code === 'ENOENT') console.log(e); 183 + return []; 184 + } 185 + }); 186 + 187 + ipcMain.handle('fs:readWorkspaceCleanedEEGData', async (_event, title) => { 188 + try { 189 + const files = await recursive(getWorkspaceDir(title)); 190 + return files 191 + .filter((filepath) => filepath.slice(-7).includes('epo.fif')) 192 + .map((filepath) => ({ name: path.basename(filepath), path: filepath })); 193 + } catch (e: any) { 194 + console.log(e); 195 + return []; 196 + } 197 + }); 198 + 199 + ipcMain.handle('fs:readWorkspaceBehaviorData', async (_event, title) => { 200 + try { 201 + const files: string[] = await recursive(getWorkspaceDir(title)); 202 + return files 203 + .filter((filepath) => filepath.slice(-12).includes('behavior.csv')) 204 + .map((filepath) => ({ name: path.basename(filepath), path: filepath })); 205 + } catch (e: any) { 206 + if (e.code === 'ENOENT') console.log(e); 207 + return []; 208 + } 209 + }); 210 + 211 + ipcMain.handle( 212 + 'fs:storeBehavioralData', 213 + (_event, csv, title, subject, group, session) => { 214 + const dir = path.join( 215 + getWorkspaceDir(title), 216 + 'Data', 217 + subject, 218 + 'Behavior' 219 + ); 220 + const filename = `${subject}-${group}-${session}-behavior.csv`; 221 + mkdirPathSync(dir); 222 + return new Promise<void>((resolve, reject) => { 223 + fs.writeFile(path.join(dir, filename), csv, (err) => { 224 + if (err) reject(err); 225 + else resolve(); 226 + }); 227 + }); 228 + } 229 + ); 230 + 231 + ipcMain.handle( 232 + 'fs:storePyodideImage', 233 + (_event, title, imageTitle, rawData: ArrayBuffer) => { 234 + const dir = path.join(getWorkspaceDir(title), 'Results', 'Images'); 235 + const filename = `${imageTitle}.png`; 236 + mkdirPathSync(dir); 237 + const buffer = Buffer.from(rawData); 238 + return new Promise<void>((resolve, reject) => { 239 + fs.writeFile(path.join(dir, filename), buffer, (err) => { 240 + if (err) reject(err); 241 + else resolve(); 242 + }); 243 + }); 244 + } 245 + ); 246 + 247 + ipcMain.handle('fs:deleteWorkspaceDir', (_event, title) => 248 + shell.trashItem(path.join(workspaces, title)) 249 + ); 250 + 251 + ipcMain.handle('fs:readImages', (_event, dir) => { 252 + return fs.readdirSync(dir).filter((filename) => { 253 + const ext = filename.slice(-3).toLowerCase(); 254 + return ext === 'png' || ext === 'jpg' || ext === 'gif' || ext === 'peg'; 255 + }); 256 + }); 257 + 258 + ipcMain.handle('fs:getImages', (_event, params: any) => { 259 + if (!params.stimuli) return []; 260 + const images: string[] = []; 261 + for (const stimuli of params.stimuli) { 262 + const { dir } = stimuli; 263 + if (dir) { 264 + const files = fs.readdirSync(dir); 265 + for (const file of files) { 266 + images.push(path.join(dir, file)); 267 + } 268 + } 269 + } 270 + return images; 271 + }); 272 + 273 + ipcMain.handle('fs:readBehaviorData', (_event, files: string[]) => { 274 + try { 275 + return files.map((file) => { 276 + const csv = fs.readFileSync(file, 'utf-8'); 277 + const obj = Papa.parse(csv, { header: true }); 278 + (obj as any).meta.datafile = file; 279 + return obj; 280 + }); 281 + } catch (e) { 282 + console.log('error', e); 283 + return null; 284 + } 285 + }); 286 + 287 + ipcMain.handle( 288 + 'fs:storeAggregatedBehaviorData', 289 + async (_event, data, title) => { 290 + const csv = Papa.unparse(data as any); 291 + await dialog.showSaveDialog(mainWindow!, { 292 + title: 'Select a folder to save the data', 293 + defaultPath: path.join(getWorkspaceDir(title), 'Data', 'aggregated.csv'), 294 + }); 295 + // If user picks a path, write the file 296 + const result = await dialog.showSaveDialog(mainWindow!, { 297 + defaultPath: path.join(getWorkspaceDir(title), 'Data', 'aggregated.csv'), 298 + }); 299 + if (!result.canceled && result.filePath) { 300 + fs.writeFileSync(result.filePath, csv); 301 + } 302 + } 303 + ); 304 + 305 + ipcMain.handle( 306 + 'fs:checkFileExists', 307 + (_event, title, subject, filename) => { 308 + const file = path.join( 309 + getWorkspaceDir(title), 310 + 'Data', 311 + subject, 312 + 'Behavior', 313 + filename 314 + ); 315 + return fs.existsSync(file); 316 + } 317 + ); 318 + 319 + ipcMain.handle('fs:readFiles', (_event, filePathsArray: string[]) => { 320 + return filePathsArray.map((filePath) => { 321 + console.log('reading file', filePath); 322 + return fs.readFileSync(filePath, 'utf8'); 323 + }); 324 + }); 325 + 326 + // EEG streaming — main process holds write streams for performance 327 + ipcMain.handle( 328 + 'eeg:createWriteStream', 329 + (_event, title, subject, group, session) => { 330 + const dir = path.join(getWorkspaceDir(title), 'Data', subject, 'EEG'); 331 + const filename = `${subject}-${group}-${session}-raw.csv`; 332 + mkdirPathSync(dir); 333 + const stream = fs.createWriteStream(path.join(dir, filename)); 334 + const streamId = `${Date.now()}-${Math.random()}`; 335 + activeStreams.set(streamId, stream); 336 + return streamId; 337 + } 338 + ); 339 + 340 + ipcMain.on('eeg:writeHeader', (_event, streamId, channels: string[]) => { 341 + const stream = activeStreams.get(streamId); 342 + if (stream) { 343 + stream.write(`Timestamp,${channels.join(',')},Marker\n`); 344 + } 345 + }); 346 + 347 + ipcMain.on('eeg:writeData', (_event, streamId, eegData: any) => { 348 + const stream = activeStreams.get(streamId); 349 + if (!stream) return; 350 + stream.write(`${eegData.timestamp},`); 351 + const len = eegData.data.length; 352 + for (let i = 0; i < len; i++) { 353 + stream.write(`${eegData.data[i].toString()},`); 354 + } 355 + if (eegData.marker !== undefined) { 356 + stream.write(`${eegData.marker}\n`); 357 + } else { 358 + stream.write(`0\n`); 359 + } 360 + }); 361 + 362 + ipcMain.handle('eeg:closeStream', (_event, streamId) => { 363 + return new Promise<void>((resolve) => { 364 + const stream = activeStreams.get(streamId); 365 + if (stream) { 366 + stream.end(() => { 367 + activeStreams.delete(streamId); 368 + resolve(); 369 + }); 370 + } else { 371 + resolve(); 372 + } 373 + }); 374 + }); 375 + 376 + // Resource path (for experiment file loading) 377 + ipcMain.handle('getResourcePath', () => { 378 + return is.dev 379 + ? path.join(__dirname, '../../src/renderer') 380 + : process.resourcesPath; 381 + }); 382 + 383 + // Viewer URL — used by ViewerComponent to load the EEG viewer in a webview 384 + ipcMain.handle('getViewerUrl', () => { 385 + if (is.dev && process.env['ELECTRON_RENDERER_URL']) { 386 + return `${process.env['ELECTRON_RENDERER_URL']}/viewer.html`; 387 + } 388 + return `file://${path.join(__dirname, '../renderer/viewer.html')}`; 389 + }); 390 + 391 + // ------------------------------------------------------------------ 392 + // Window creation 393 + // ------------------------------------------------------------------ 394 + 395 + const createWindow = async () => { 396 + if (is.dev || process.env.DEBUG_PROD === 'true') { 397 + await installExtensions(); 398 + } 399 + 400 + mainWindow = new BrowserWindow({ 401 + show: false, 402 + width: 1280, 403 + height: 800, 404 + webPreferences: { 405 + preload: path.join(__dirname, '../preload/index.js'), 406 + nodeIntegration: false, 407 + contextIsolation: true, 408 + webviewTag: true, 409 + additionalArguments: [ 410 + // Pass resource path so preload can inject it synchronously 411 + `--resource-path=${ 412 + is.dev 413 + ? path.join(__dirname, '../../src/renderer') 414 + : process.resourcesPath 415 + }`, 416 + ], 417 + }, 418 + }); 419 + 420 + mainWindow.setMinimumSize(1075, 708); 421 + 422 + if (is.dev && process.env['ELECTRON_RENDERER_URL']) { 423 + mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL']); 424 + } else { 425 + mainWindow.loadFile(path.join(__dirname, '../renderer/index.html')); 426 + } 427 + 428 + mainWindow.webContents.on('did-finish-load', () => { 429 + if (!mainWindow) { 430 + throw new Error('"mainWindow" is not defined'); 431 + } 432 + if (process.env.START_MINIMIZED) { 433 + mainWindow.minimize(); 434 + } else { 435 + mainWindow.show(); 436 + mainWindow.focus(); 437 + } 438 + if (is.dev || process.env.DEBUG_PROD === 'true') { 439 + mainWindow.webContents.openDevTools(); 440 + } 441 + }); 442 + 443 + mainWindow.on('closed', () => { 444 + mainWindow = null; 445 + }); 446 + 447 + const menuBuilder = new MenuBuilder(mainWindow); 448 + menuBuilder.buildMenu(); 449 + 450 + // eslint-disable-next-line 451 + new AppUpdater(); 452 + }; 453 + 454 + // ------------------------------------------------------------------ 455 + 456 + app.on('window-all-closed', () => { 457 + if (process.platform !== 'darwin') { 458 + app.quit(); 459 + } 460 + }); 461 + 462 + app.whenReady().then(async () => { 463 + // Enable F12 devtools shortcut and Ctrl+R reload in dev, disable in prod 464 + app.on('browser-window-created', (_, window) => { 465 + optimizer.watchWindowShortcuts(window); 466 + }); 467 + 468 + // In dev, Vite sets Cache-Control: immutable on pre-bundled deps. The 469 + // Electron session cache persists across restarts, so patched dep files 470 + // may not be picked up. Clearing the HTTP cache here ensures the renderer 471 + // always fetches fresh files from the Vite dev server on each launch. 472 + if (is.dev) { 473 + await session.defaultSession.clearCache(); 474 + } 475 + 476 + createWindow(); 477 + }); 478 + 479 + app.on('activate', () => { 480 + if (mainWindow === null) { 481 + createWindow(); 482 + } 483 + });
+151
src/preload/index.ts
··· 1 + /** 2 + * Preload script for the main BrainWaves window. 3 + * 4 + * Exposes a safe `window.electronAPI` object to the renderer via contextBridge. 5 + * All Node.js / Electron API access that the renderer needs must go through here. 6 + */ 7 + import { contextBridge, ipcRenderer } from 'electron'; 8 + 9 + // Inject the resource path synchronously so renderer module-level code can use it 10 + // (The main process passes it as --resource-path in additionalArguments) 11 + const resourcePathArg = process.argv.find((arg) => 12 + arg.startsWith('--resource-path=') 13 + ); 14 + const resourcePath = resourcePathArg 15 + ? resourcePathArg.replace('--resource-path=', '') 16 + : ''; 17 + 18 + contextBridge.exposeInMainWorld('__ELECTRON_RESOURCE_PATH__', resourcePath); 19 + 20 + contextBridge.exposeInMainWorld('electronAPI', { 21 + // ------------------------------------------------------------------ 22 + // Dialogs 23 + // ------------------------------------------------------------------ 24 + showOpenDialog: (options: Electron.OpenDialogOptions) => 25 + ipcRenderer.invoke('dialog:showOpen', options), 26 + 27 + showMessageBox: (options: Electron.MessageBoxOptions) => 28 + ipcRenderer.invoke('dialog:showMessage', options), 29 + 30 + showSaveDialog: (options: Electron.SaveDialogOptions) => 31 + ipcRenderer.invoke('dialog:showSave', options), 32 + 33 + loadDialog: (fileType: string): Promise<string | null> => 34 + ipcRenderer.invoke('loadDialog', fileType), 35 + 36 + // ------------------------------------------------------------------ 37 + // Shell 38 + // ------------------------------------------------------------------ 39 + showItemInFolder: (fullPath: string) => 40 + ipcRenderer.invoke('shell:showItemInFolder', fullPath), 41 + 42 + moveItemToTrash: (fullPath: string) => 43 + ipcRenderer.invoke('shell:moveItemToTrash', fullPath), 44 + 45 + // ------------------------------------------------------------------ 46 + // Filesystem — workspace management 47 + // ------------------------------------------------------------------ 48 + getWorkspaceDir: (title: string): Promise<string> => 49 + ipcRenderer.invoke('fs:getWorkspaceDir', title), 50 + 51 + createWorkspaceDir: (title: string): Promise<void> => 52 + ipcRenderer.invoke('fs:createWorkspaceDir', title), 53 + 54 + readWorkspaces: (): Promise<string[]> => 55 + ipcRenderer.invoke('fs:readWorkspaces'), 56 + 57 + readAndParseState: (dir: string) => 58 + ipcRenderer.invoke('fs:readAndParseState', dir), 59 + 60 + storeExperimentState: (state: unknown): Promise<void> => 61 + ipcRenderer.invoke('fs:storeExperimentState', state), 62 + 63 + restoreExperimentState: (state: unknown): Promise<void> => 64 + ipcRenderer.invoke('fs:restoreExperimentState', state), 65 + 66 + readWorkspaceRawEEGData: (title: string) => 67 + ipcRenderer.invoke('fs:readWorkspaceRawEEGData', title), 68 + 69 + readWorkspaceCleanedEEGData: (title: string) => 70 + ipcRenderer.invoke('fs:readWorkspaceCleanedEEGData', title), 71 + 72 + readWorkspaceBehaviorData: (title: string) => 73 + ipcRenderer.invoke('fs:readWorkspaceBehaviorData', title), 74 + 75 + storeBehavioralData: ( 76 + csv: string, 77 + title: string, 78 + subject: string, 79 + group: string, 80 + session: number 81 + ): Promise<void> => 82 + ipcRenderer.invoke( 83 + 'fs:storeBehavioralData', 84 + csv, 85 + title, 86 + subject, 87 + group, 88 + session 89 + ), 90 + 91 + storePyodideImage: ( 92 + title: string, 93 + imageTitle: string, 94 + rawData: ArrayBuffer 95 + ): Promise<void> => 96 + ipcRenderer.invoke('fs:storePyodideImage', title, imageTitle, rawData), 97 + 98 + deleteWorkspaceDir: (title: string): Promise<void> => 99 + ipcRenderer.invoke('fs:deleteWorkspaceDir', title), 100 + 101 + readImages: (dir: string): Promise<string[]> => 102 + ipcRenderer.invoke('fs:readImages', dir), 103 + 104 + getImages: (params: unknown): Promise<string[]> => 105 + ipcRenderer.invoke('fs:getImages', params), 106 + 107 + readBehaviorData: (files: string[]) => 108 + ipcRenderer.invoke('fs:readBehaviorData', files), 109 + 110 + storeAggregatedBehaviorData: (data: unknown, title: string): Promise<void> => 111 + ipcRenderer.invoke('fs:storeAggregatedBehaviorData', data, title), 112 + 113 + checkFileExists: ( 114 + title: string, 115 + subject: string, 116 + filename: string 117 + ): Promise<boolean> => 118 + ipcRenderer.invoke('fs:checkFileExists', title, subject, filename), 119 + 120 + readFiles: (filePathsArray: string[]): Promise<string[]> => 121 + ipcRenderer.invoke('fs:readFiles', filePathsArray), 122 + 123 + // ------------------------------------------------------------------ 124 + // EEG streaming — main process holds the write stream for performance 125 + // ------------------------------------------------------------------ 126 + createEEGWriteStream: ( 127 + title: string, 128 + subject: string, 129 + group: string, 130 + session: number 131 + ): Promise<string> => 132 + ipcRenderer.invoke('eeg:createWriteStream', title, subject, group, session), 133 + 134 + writeEEGHeader: (streamId: string, channels: string[]): void => 135 + ipcRenderer.send('eeg:writeHeader', streamId, channels), 136 + 137 + writeEEGData: (streamId: string, data: unknown): void => 138 + ipcRenderer.send('eeg:writeData', streamId, data), 139 + 140 + closeEEGStream: (streamId: string): Promise<void> => 141 + ipcRenderer.invoke('eeg:closeStream', streamId), 142 + 143 + // ------------------------------------------------------------------ 144 + // Misc 145 + // ------------------------------------------------------------------ 146 + getResourcePath: (): Promise<string> => 147 + ipcRenderer.invoke('getResourcePath'), 148 + 149 + getViewerUrl: (): Promise<string> => 150 + ipcRenderer.invoke('getViewerUrl'), 151 + });
+33
src/preload/viewer.ts
··· 1 + /** 2 + * Preload script for the EEG Viewer window. 3 + * 4 + * The viewer uses ipcRenderer directly to receive graph data from the main 5 + * process. We expose a minimal API for the D3-based viewer renderer. 6 + */ 7 + import { contextBridge, ipcRenderer } from 'electron'; 8 + 9 + contextBridge.exposeInMainWorld('viewerAPI', { 10 + onInitGraph: (callback: (message: unknown) => void) => 11 + ipcRenderer.on('initGraph', (_event, message) => callback(message)), 12 + 13 + onNewData: (callback: (message: unknown) => void) => 14 + ipcRenderer.on('newData', (_event, message) => callback(message)), 15 + 16 + onZoomIn: (callback: () => void) => 17 + ipcRenderer.on('zoomIn', () => callback()), 18 + 19 + onZoomOut: (callback: () => void) => 20 + ipcRenderer.on('zoomOut', () => callback()), 21 + 22 + onUpdateChannels: (callback: (message: unknown) => void) => 23 + ipcRenderer.on('updateChannels', (_event, message) => callback(message)), 24 + 25 + onUpdateDomain: (callback: (message: unknown) => void) => 26 + ipcRenderer.on('updateDomain', (_event, message) => callback(message)), 27 + 28 + onUpdateDownsampling: (callback: (message: unknown) => void) => 29 + ipcRenderer.on('updateDownsampling', (_event, message) => callback(message)), 30 + 31 + onAutoScale: (callback: () => void) => 32 + ipcRenderer.on('autoScale', () => callback()), 33 + });
+29
src/renderer/assets/image.d.ts
··· 1 + declare module '*.png' { 2 + const content: string; 3 + export default content; 4 + } 5 + 6 + declare module '*.jpg' { 7 + const content: string; 8 + export default content; 9 + } 10 + 11 + declare module '*.jpeg' { 12 + const content: string; 13 + export default content; 14 + } 15 + 16 + declare module '*.gif' { 17 + const content: string; 18 + export default content; 19 + } 20 + 21 + declare module '*.svg' { 22 + const content: string; 23 + export default content; 24 + } 25 + 26 + declare module '*.py?raw' { 27 + const content: string; 28 + export default content; 29 + }
+15
src/renderer/index.html
··· 1 + <!DOCTYPE html> 2 + <html> 3 + <head> 4 + <meta charset="utf-8" /> 5 + <meta 6 + http-equiv="Content-Security-Policy" 7 + content="default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: blob:; connect-src 'self' ws: wss: webpack:; font-src 'self' data: https://fonts.gstatic.com; worker-src blob: 'self';" 8 + /> 9 + <title>BrainWaves</title> 10 + </head> 11 + <body> 12 + <div id="root"></div> 13 + <script type="module" src="./index.tsx"></script> 14 + </body> 15 + </html>
+12
src/renderer/index.tsx
··· 1 + import React from 'react'; 2 + import { render } from 'react-dom'; 3 + import Root from './containers/Root'; 4 + import { configuredStore, history } from './store'; 5 + import './app.global.css'; 6 + 7 + const store = configuredStore(); 8 + 9 + render( 10 + <Root store={store} history={history} />, 11 + document.getElementById('root') 12 + );
+9
src/renderer/utils/filesystem/dialog.ts
··· 1 + /** 2 + * Dialog helpers — proxied to the main process via window.electronAPI. 3 + * The actual electron.dialog calls live in src/main/index.ts IPC handlers. 4 + */ 5 + 6 + export const showOpenDialog = ( 7 + options: Record<string, unknown> 8 + ): Promise<{ canceled: boolean; filePaths: string[] }> => 9 + window.electronAPI.showOpenDialog(options);
+2
src/renderer/utils/filesystem/read.js
··· 1 + export const readFiles = (filePathsArray) => 2 + window.electronAPI.readFiles(filePathsArray);
+19
src/renderer/utils/filesystem/select.ts
··· 1 + /** 2 + * Functions for selecting files and directories from disk. 3 + * Uses the electronAPI bridge exposed by the preload script. 4 + */ 5 + import { FILE_TYPES } from '../../constants/constants'; 6 + 7 + declare global { 8 + interface Window { 9 + electronAPI: { 10 + loadDialog: (fileType: string) => Promise<string | null>; 11 + showOpenDialog: ( 12 + options: Record<string, unknown> 13 + ) => Promise<{ canceled: boolean; filePaths: string[] }>; 14 + }; 15 + } 16 + } 17 + 18 + export const loadFromSystemDialog = (fileType: FILE_TYPES): Promise<string | null> => 19 + window.electronAPI.loadDialog(fileType);
+108
src/renderer/utils/filesystem/storage.ts
··· 1 + /** 2 + * Functions for managing user data stored on disk. 3 + * All filesystem / shell operations are proxied to the main process 4 + * via window.electronAPI (defined in src/preload/index.ts). 5 + */ 6 + import path from 'pathe'; 7 + import Papa from 'papaparse'; 8 + import { ExperimentStateType } from '../../reducers/experimentReducer'; 9 + import { ExperimentParameters } from '../../constants/interfaces'; 10 + 11 + // electronAPI is injected by the preload script 12 + declare global { 13 + interface Window { 14 + // eslint-disable-next-line @typescript-eslint/no-explicit-any 15 + electronAPI: any; 16 + } 17 + } 18 + 19 + const api = () => window.electronAPI; 20 + 21 + // ----------------------------------------------------------------------------------------------- 22 + // Creating and Getting 23 + 24 + export const getWorkspaceDir = (title: string): Promise<string> => 25 + api().getWorkspaceDir(title); 26 + 27 + export const createWorkspaceDir = (title: string): Promise<void> => 28 + api().createWorkspaceDir(title); 29 + 30 + export const openWorkspaceDir = (title: string): Promise<void> => 31 + api().showItemInFolder(path.join('BrainWaves_Workspaces', title)); 32 + 33 + // ----------------------------------------------------------------------------------------------- 34 + // Storing 35 + 36 + export const storeExperimentState = (state: ExperimentStateType): Promise<void> => 37 + api().storeExperimentState(state); 38 + 39 + export const restoreExperimentState = (state: ExperimentStateType): Promise<void> => 40 + api().restoreExperimentState(state); 41 + 42 + export const storeBehavioralData = ( 43 + csv: string, 44 + title: string, 45 + subject: string, 46 + group: string, 47 + session: number 48 + ): Promise<void> => api().storeBehavioralData(csv, title, subject, group, session); 49 + 50 + export const storePyodideImage = ( 51 + title: string, 52 + imageTitle: string, 53 + rawData: ArrayBuffer 54 + ): Promise<void> => api().storePyodideImage(title, imageTitle, rawData); 55 + 56 + // ----------------------------------------------------------------------------------------------- 57 + // Reading 58 + 59 + export const readWorkspaces = (): Promise<string[]> => api().readWorkspaces(); 60 + 61 + export const readWorkspaceRawEEGData = (title: string) => 62 + api().readWorkspaceRawEEGData(title); 63 + 64 + export const readWorkspaceCleanedEEGData = (title: string) => 65 + api().readWorkspaceCleanedEEGData(title); 66 + 67 + export const readWorkspaceBehaviorData = (title: string) => 68 + api().readWorkspaceBehaviorData(title); 69 + 70 + export const readAndParseState = (dir: string): Promise<ExperimentStateType | null> => 71 + api().readAndParseState(dir); 72 + 73 + export const readImages = (dir: string): Promise<string[]> => 74 + api().readImages(dir); 75 + 76 + export const getImages = (params: ExperimentParameters): Promise<string[]> => 77 + api().getImages(params); 78 + 79 + export const readBehaviorData = (files: string[]) => 80 + api().readBehaviorData(files); 81 + 82 + export const storeAggregatedBehaviorData = (data: unknown, title: string): Promise<void> => 83 + api().storeAggregatedBehaviorData(data, title); 84 + 85 + // ----------------------------------------------------------------------------------------------- 86 + // Util 87 + 88 + export const mkdirPathSync = (_dirPath: string): void => { 89 + // Directory creation is handled in the main process; this is a no-op in the renderer 90 + }; 91 + 92 + export const getSubjectNamesFromFiles = (filePaths: string[]): string[] => 93 + filePaths 94 + .map((filePath) => path.basename(filePath)) 95 + .map((fileName) => fileName.substring(0, fileName.indexOf('-'))); 96 + 97 + export const deleteWorkspaceDir = (title: string): Promise<void> => 98 + api().deleteWorkspaceDir(title); 99 + 100 + export const checkFileExists = ( 101 + title: string, 102 + subject: string, 103 + filename: string 104 + ): Promise<boolean> => api().checkFileExists(title, subject, filename); 105 + 106 + // Kept for backward compatibility — converts CSV string to parsed object using PapaParse 107 + export const convertCSVToObject = (csv: string) => 108 + Papa.parse(csv, { header: true });
+28
src/renderer/utils/filesystem/write.ts
··· 1 + /** 2 + * Functions for writing EEG data to disk. 3 + * The actual write stream lives in the main process; the renderer 4 + * communicates via the IPC bridge in window.electronAPI. 5 + */ 6 + 7 + import { EEGData } from '../../constants/interfaces'; 8 + 9 + // Creates an EEG write stream in the main process and returns a stream ID 10 + export const createEEGWriteStream = ( 11 + title: string, 12 + subject: string, 13 + group: string, 14 + session: number 15 + ): Promise<string> => 16 + window.electronAPI.createEEGWriteStream(title, subject, group, session); 17 + 18 + // Writes the CSV header row to the stream identified by streamId 19 + export const writeHeader = (streamId: string, channels: string[]): void => 20 + window.electronAPI.writeEEGHeader(streamId, channels); 21 + 22 + // Writes a single EEG data row (fire-and-forget for performance) 23 + export const writeEEGData = (streamId: string, eegData: EEGData): void => 24 + window.electronAPI.writeEEGData(streamId, eegData); 25 + 26 + // Closes the write stream 27 + export const closeEEGStream = (streamId: string): Promise<void> => 28 + window.electronAPI.closeEEGStream(streamId);
+47
src/renderer/viewer.ts
··· 1 + /** 2 + * EEG Viewer renderer — uses the viewerAPI exposed by src/preload/viewer.ts 3 + * to receive graph data from the main process via IPC. 4 + */ 5 + import EEGGraph from './components/d3Classes/EEGViewer'; 6 + 7 + // eslint-disable-next-line @typescript-eslint/no-explicit-any 8 + let graph: any = {}; 9 + 10 + declare global { 11 + interface Window { 12 + // eslint-disable-next-line @typescript-eslint/no-explicit-any 13 + viewerAPI: any; 14 + } 15 + } 16 + 17 + window.viewerAPI.onInitGraph((message: unknown) => { 18 + graph = new EEGGraph(document.getElementById('graph'), message); 19 + }); 20 + 21 + window.viewerAPI.onNewData((message: unknown) => { 22 + graph.updateData(message); 23 + }); 24 + 25 + window.viewerAPI.onZoomIn(() => { 26 + graph.zoomOut(); 27 + }); 28 + 29 + window.viewerAPI.onZoomOut(() => { 30 + graph.zoomIn(); 31 + }); 32 + 33 + window.viewerAPI.onUpdateChannels((message: unknown) => { 34 + graph.updateChannels(message); 35 + }); 36 + 37 + window.viewerAPI.onUpdateDomain((message: unknown) => { 38 + graph.updateDomain(message); 39 + }); 40 + 41 + window.viewerAPI.onUpdateDownsampling((message: unknown) => { 42 + graph.updateDownsampling(message); 43 + }); 44 + 45 + window.viewerAPI.onAutoScale(() => { 46 + graph.autoScale(); 47 + });
+22
src/renderer/vite-env.d.ts
··· 1 + /// <reference types="vite/client" /> 2 + 3 + interface ImportMetaEnv { 4 + readonly VITE_CLIENT_ID: string; 5 + readonly VITE_CLIENT_SECRET: string; 6 + readonly VITE_LICENSE_ID: string; 7 + readonly VITE_LOG_LEVEL: string; 8 + } 9 + 10 + interface ImportMeta { 11 + readonly env: ImportMetaEnv; 12 + } 13 + 14 + declare module '*.py?raw' { 15 + const content: string; 16 + export default content; 17 + } 18 + 19 + declare module '*.py' { 20 + const content: string; 21 + export default content; 22 + }
+19 -13
tsconfig.json
··· 1 1 { 2 2 "compilerOptions": { 3 - "target": "ES2018", 4 - "module": "CommonJS", 5 - "lib": ["dom", "esnext"], 6 - "declaration": true, 7 - "declarationMap": true, 3 + "target": "ES2020", 4 + "module": "ESNext", 5 + "lib": ["dom", "dom.iterable", "esnext"], 8 6 "noEmit": true, 9 7 "jsx": "react", 10 8 "strict": true, 11 9 "pretty": true, 12 10 "sourceMap": true, 13 11 "noImplicitAny": false, 14 - /* Module Resolution Options */ 15 - "moduleResolution": "node", 12 + "moduleResolution": "bundler", 16 13 "esModuleInterop": true, 17 14 "allowSyntheticDefaultImports": true, 18 15 "resolveJsonModule": true, 19 - "allowJs": true 16 + "allowJs": true, 17 + "skipLibCheck": true, 18 + "baseUrl": ".", 19 + "paths": { 20 + "@renderer/*": ["src/renderer/*"], 21 + "@main/*": ["src/main/*"], 22 + "@preload/*": ["src/preload/*"] 23 + } 20 24 }, 25 + "include": [ 26 + "src/**/*", 27 + "electron.d.ts" 28 + ], 21 29 "exclude": [ 22 - "test", 30 + "node_modules", 31 + "out", 23 32 "release", 24 - "app/main.prod.js", 25 - "app/renderer.prod.js", 26 - "app/dist", 27 - "dll" 33 + "dist" 28 34 ] 29 35 }
+87
vite.config.ts
··· 1 + import { defineConfig } from 'electron-vite'; 2 + import react from '@vitejs/plugin-react'; 3 + import path from 'path'; 4 + import { createRequire } from 'module'; 5 + const _require = createRequire(import.meta.url); 6 + 7 + // Load Emotiv SDK keys from keys.js and expose them as VITE_* env vars so that 8 + // import.meta.env.VITE_* works in the renderer in both dev and production. 9 + // Environment variables always take precedence over keys.js values. 10 + try { 11 + const keys: { CLIENT_ID?: string; CLIENT_SECRET?: string; LICENSE_ID?: string } = 12 + _require('./keys.js'); 13 + if (keys.CLIENT_ID) process.env.VITE_CLIENT_ID ??= keys.CLIENT_ID; 14 + if (keys.CLIENT_SECRET) process.env.VITE_CLIENT_SECRET ??= keys.CLIENT_SECRET; 15 + if (keys.LICENSE_ID) process.env.VITE_LICENSE_ID ??= keys.LICENSE_ID; 16 + } catch { 17 + // keys.js may not exist in CI environments 18 + } 19 + 20 + export default defineConfig({ 21 + // ------------------------------------------------------------------ 22 + // Main process — electron-vite defaults to src/main/index.ts 23 + // ------------------------------------------------------------------ 24 + main: { 25 + resolve: { 26 + alias: { 27 + '@main': path.resolve(__dirname, 'src/main'), 28 + '@renderer': path.resolve(__dirname, 'src/renderer'), 29 + }, 30 + }, 31 + }, 32 + 33 + // ------------------------------------------------------------------ 34 + // Preload scripts — main window only; viewer preload is built by 35 + // internals/scripts/BuildViewers.js (postbuild npm hook) 36 + // ------------------------------------------------------------------ 37 + preload: {}, 38 + 39 + // ------------------------------------------------------------------ 40 + // Renderer (React + Vite dev server) 41 + // ------------------------------------------------------------------ 42 + renderer: { 43 + // Serve the pyodide runtime files as static assets so Vite does NOT 44 + // transform them. importScripts() in a classic worker cannot load 45 + // ES modules; Vite's HMR injection turns .js files into ESM, breaking 46 + // the worker. Files in publicDir are served verbatim at the root URL: 47 + // /pyodide/pyodide.js, /pyodide/pyodide.asm.js, etc. 48 + publicDir: path.resolve(__dirname, 'src/renderer/utils/pyodide/src'), 49 + plugins: [ 50 + react({ 51 + jsxRuntime: 'classic', // React 16 does not ship react/jsx-runtime 52 + babel: { 53 + plugins: [ 54 + // Legacy decorator support (used throughout the codebase) 55 + ['@babel/plugin-proposal-decorators', { legacy: true }], 56 + ['@babel/plugin-proposal-class-properties', { loose: true }], 57 + ], 58 + }, 59 + }), 60 + ], 61 + css: { 62 + modules: { 63 + localsConvention: 'camelCase', 64 + generateScopedName: '[name]__[local]___[hash:base64:5]', 65 + }, 66 + }, 67 + resolve: { 68 + alias: { 69 + '@renderer': path.resolve(__dirname, 'src/renderer'), 70 + // Browser-compatible path utilities (pathe = modern drop-in for Node's path) 71 + path: 'pathe', 72 + events: 'events', 73 + }, 74 + }, 75 + optimizeDeps: { 76 + include: ['@neurosity/pipes'], 77 + }, 78 + build: { 79 + rollupOptions: { 80 + // viewer.html + viewer.ts are handled by viewerRendererPlugin above 81 + input: { 82 + index: path.resolve(__dirname, 'src/renderer/index.html'), 83 + }, 84 + }, 85 + }, 86 + }, 87 + });