neo.mjs 3.2.8 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/apps/ServiceWorker.mjs +7 -6
- package/buildScripts/createApp.mjs +16 -0
- package/buildScripts/webpack/development/webpack.config.worker.mjs +3 -1
- package/buildScripts/webpack/production/webpack.config.worker.mjs +3 -1
- package/examples/ServiceWorker.mjs +7 -6
- package/examples/preloadingAssets/view/MainContainer.mjs +22 -5
- package/examples/preloadingAssets/view/MainContainerController.mjs +1 -1
- package/package.json +8 -8
- package/src/main/addon/ServiceWorker.mjs +17 -18
- package/src/worker/ServiceBase.mjs +82 -31
package/README.md
CHANGED
|
@@ -54,7 +54,7 @@ neo.mjs offers two different setups which follow the exact same API.
|
|
|
54
54
|
You can switch between <a href="https://developer.mozilla.org/en-US/docs/Web/API/Worker">dedicated</a> and
|
|
55
55
|
<a href="https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker">shared</a> workers at any point.
|
|
56
56
|
|
|
57
|
-
<img src="https://raw.githubusercontent.com/neomjs/pages/master/resources/images/workers-setup-
|
|
57
|
+
<img src="https://raw.githubusercontent.com/neomjs/pages/master/resources/images/workers-setup-v4.png">
|
|
58
58
|
|
|
59
59
|
The dedicated workers setup uses 4 threads (CPUs).
|
|
60
60
|
Most parts of the frameworks as well as your apps and components live within the app worker.
|
package/apps/ServiceWorker.mjs
CHANGED
|
@@ -8,6 +8,12 @@ import ServiceBase from '../src/worker/ServiceBase.mjs';
|
|
|
8
8
|
* @singleton
|
|
9
9
|
*/
|
|
10
10
|
class ServiceWorker extends ServiceBase {
|
|
11
|
+
/**
|
|
12
|
+
* @member {String} workerId='service'
|
|
13
|
+
* @protected
|
|
14
|
+
*/
|
|
15
|
+
workerId = 'service'
|
|
16
|
+
|
|
11
17
|
static getConfig() {return {
|
|
12
18
|
/**
|
|
13
19
|
* @member {String} className='Neo.ServiceWorker'
|
|
@@ -18,12 +24,7 @@ class ServiceWorker extends ServiceBase {
|
|
|
18
24
|
* @member {Boolean} singleton=true
|
|
19
25
|
* @protected
|
|
20
26
|
*/
|
|
21
|
-
singleton: true
|
|
22
|
-
/**
|
|
23
|
-
* @member {String} workerId='service'
|
|
24
|
-
* @protected
|
|
25
|
-
*/
|
|
26
|
-
workerId: 'service'
|
|
27
|
+
singleton: true
|
|
27
28
|
}}
|
|
28
29
|
}
|
|
29
30
|
|
|
@@ -32,6 +32,7 @@ program
|
|
|
32
32
|
.option('-i, --info', 'print environment debug info')
|
|
33
33
|
.option('-a, --appName <value>')
|
|
34
34
|
.option('-m, --mainThreadAddons <value>', `Comma separated list of:\n${addonChoices.join(', ')}\nDefaults to DragDrop, Stylesheet`)
|
|
35
|
+
.option('-s, --useServiceWorker <value>', '"yes", "no"')
|
|
35
36
|
.option('-t, --themes <value>', ['all', ...themeFolders, 'none'].join(", "))
|
|
36
37
|
.option('-u, --useSharedWorkers <value>', '"yes", "no"')
|
|
37
38
|
.allowUnknownOption()
|
|
@@ -105,11 +106,22 @@ if (programOpts.info) {
|
|
|
105
106
|
});
|
|
106
107
|
}
|
|
107
108
|
|
|
109
|
+
if (!programOpts.useServiceWorker) {
|
|
110
|
+
questions.push({
|
|
111
|
+
type : 'list',
|
|
112
|
+
name : 'useServiceWorker',
|
|
113
|
+
message: 'Do you want to use a ServiceWorker for caching assets?',
|
|
114
|
+
choices: ['yes', 'no'],
|
|
115
|
+
default: 'no'
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
108
119
|
inquirer.prompt(questions).then(answers => {
|
|
109
120
|
let appName = programOpts.appName || answers.appName,
|
|
110
121
|
mainThreadAddons = programOpts.mainThreadAddons || answers.mainThreadAddons,
|
|
111
122
|
themes = programOpts.themes || answers.themes,
|
|
112
123
|
useSharedWorkers = programOpts.useSharedWorkers || answers.useSharedWorkers,
|
|
124
|
+
useServiceWorker = programOpts.useServiceWorker || answers.useServiceWorker,
|
|
113
125
|
lAppName = appName.toLowerCase(),
|
|
114
126
|
appPath = 'apps/' + lAppName + '/',
|
|
115
127
|
dir = 'apps/' + lAppName,
|
|
@@ -181,6 +193,10 @@ if (programOpts.info) {
|
|
|
181
193
|
neoConfig.useSharedWorkers = true;
|
|
182
194
|
}
|
|
183
195
|
|
|
196
|
+
if (useServiceWorker !== 'no') {
|
|
197
|
+
neoConfig.useServiceWorker = true;
|
|
198
|
+
}
|
|
199
|
+
|
|
184
200
|
if (!insideNeo) {
|
|
185
201
|
neoConfig.workerBasePath = '../../node_modules/neo.mjs/src/worker/';
|
|
186
202
|
}
|
|
@@ -15,8 +15,10 @@ export default env => {
|
|
|
15
15
|
|
|
16
16
|
if (filenameConfig.workers) {
|
|
17
17
|
Object.entries(filenameConfig.workers).forEach(([key, value]) => {
|
|
18
|
+
// Inside a neo workspace, we want to use the SW inside the top level apps folder,
|
|
19
|
+
// to allow overriding its logic
|
|
18
20
|
if (key === env.worker) {
|
|
19
|
-
entry[key] = path.resolve(neoPath, value.input);
|
|
21
|
+
entry[key] = path.resolve(key === 'service' && !insideNeo ? cwd : neoPath, value.input);
|
|
20
22
|
}
|
|
21
23
|
});
|
|
22
24
|
}
|
|
@@ -15,8 +15,10 @@ export default env => {
|
|
|
15
15
|
|
|
16
16
|
if (filenameConfig.workers) {
|
|
17
17
|
Object.entries(filenameConfig.workers).forEach(([key, value]) => {
|
|
18
|
+
// Inside a neo workspace, we want to use the SW inside the top level apps folder,
|
|
19
|
+
// to allow overriding its logic
|
|
18
20
|
if (key === env.worker) {
|
|
19
|
-
entry[key] = path.resolve(neoPath, value.input);
|
|
21
|
+
entry[key] = path.resolve(key === 'service' && !insideNeo ? cwd : neoPath, value.input);
|
|
20
22
|
}
|
|
21
23
|
});
|
|
22
24
|
}
|
|
@@ -8,6 +8,12 @@ import ServiceBase from '../src/worker/ServiceBase.mjs';
|
|
|
8
8
|
* @singleton
|
|
9
9
|
*/
|
|
10
10
|
class ServiceWorker extends ServiceBase {
|
|
11
|
+
/**
|
|
12
|
+
* @member {String} workerId='service'
|
|
13
|
+
* @protected
|
|
14
|
+
*/
|
|
15
|
+
workerId = 'service'
|
|
16
|
+
|
|
11
17
|
static getConfig() {return {
|
|
12
18
|
/**
|
|
13
19
|
* @member {String} className='Neo.ServiceWorker'
|
|
@@ -18,12 +24,7 @@ class ServiceWorker extends ServiceBase {
|
|
|
18
24
|
* @member {Boolean} singleton=true
|
|
19
25
|
* @protected
|
|
20
26
|
*/
|
|
21
|
-
singleton: true
|
|
22
|
-
/**
|
|
23
|
-
* @member {String} workerId='service'
|
|
24
|
-
* @protected
|
|
25
|
-
*/
|
|
26
|
-
workerId: 'service'
|
|
27
|
+
singleton: true
|
|
27
28
|
}}
|
|
28
29
|
}
|
|
29
30
|
|
|
@@ -9,11 +9,26 @@ import Viewport from '../../../src/container/Viewport.mjs';
|
|
|
9
9
|
*/
|
|
10
10
|
class MainContainer extends Viewport {
|
|
11
11
|
static getConfig() {return {
|
|
12
|
+
/**
|
|
13
|
+
* @member {String} className='Neo.examples.preloadingAssets.view.MainContainer'
|
|
14
|
+
* @protected
|
|
15
|
+
*/
|
|
12
16
|
className: 'Neo.examples.preloadingAssets.view.MainContainer',
|
|
17
|
+
/**
|
|
18
|
+
* @member {Boolean} autoMount=true
|
|
19
|
+
*/
|
|
13
20
|
autoMount: true,
|
|
21
|
+
/**
|
|
22
|
+
* @member {Neo.controller.Component} controller=MainContainerController
|
|
23
|
+
*/
|
|
14
24
|
controller: MainContainerController,
|
|
15
|
-
|
|
16
|
-
|
|
25
|
+
/**
|
|
26
|
+
* @member {Object} layout={ntype:'fit'}
|
|
27
|
+
*/
|
|
28
|
+
layout: {ntype: 'fit'},
|
|
29
|
+
/**
|
|
30
|
+
* @member {Object[]} items
|
|
31
|
+
*/
|
|
17
32
|
items: [{
|
|
18
33
|
module: TabContainer,
|
|
19
34
|
height: 300,
|
|
@@ -33,7 +48,7 @@ class MainContainer extends Viewport {
|
|
|
33
48
|
|
|
34
49
|
vdom: {
|
|
35
50
|
tag: 'img',
|
|
36
|
-
src: '
|
|
51
|
+
src: 'https://raw.githubusercontent.com/neomjs/pages/master/resources/examples/ai_images/000150.jpg'
|
|
37
52
|
}
|
|
38
53
|
}, {
|
|
39
54
|
tabButtonConfig: {
|
|
@@ -43,10 +58,12 @@ class MainContainer extends Viewport {
|
|
|
43
58
|
|
|
44
59
|
vdom: {
|
|
45
60
|
tag: 'img',
|
|
46
|
-
src: '
|
|
61
|
+
src: 'https://raw.githubusercontent.com/neomjs/pages/master/resources/examples/ai_images/000074.jpg'
|
|
47
62
|
}
|
|
48
63
|
}],
|
|
49
|
-
|
|
64
|
+
/**
|
|
65
|
+
* @member {Object} listeners
|
|
66
|
+
*/
|
|
50
67
|
listeners: {
|
|
51
68
|
activeIndexChange: 'onActiveIndexChange',
|
|
52
69
|
mounted : 'onMounted'
|
|
@@ -28,7 +28,7 @@ class MainContainerController extends Component {
|
|
|
28
28
|
|
|
29
29
|
setTimeout(() => {
|
|
30
30
|
Neo.ServiceWorker.preloadAssets({
|
|
31
|
-
files: ['
|
|
31
|
+
files: ['https://raw.githubusercontent.com/neomjs/pages/master/resources/examples/ai_images/000074.jpg']
|
|
32
32
|
}).then(data => {
|
|
33
33
|
console.log(data);
|
|
34
34
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neo.mjs",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "The webworkers driven UI framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -34,21 +34,21 @@
|
|
|
34
34
|
},
|
|
35
35
|
"homepage": "https://neomjs.github.io/pages/",
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@fortawesome/fontawesome-free": "^6.
|
|
37
|
+
"@fortawesome/fontawesome-free": "^6.1.1",
|
|
38
38
|
"@material/mwc-button": "^0.25.3",
|
|
39
39
|
"@material/mwc-textfield": "^0.25.3",
|
|
40
|
-
"autoprefixer": "^10.4.
|
|
41
|
-
"chalk": "^5.0.
|
|
40
|
+
"autoprefixer": "^10.4.4",
|
|
41
|
+
"chalk": "^5.0.1",
|
|
42
42
|
"clean-webpack-plugin": "^4.0.0",
|
|
43
|
-
"commander": "^9.
|
|
44
|
-
"cssnano": "^5.1.
|
|
43
|
+
"commander": "^9.1.0",
|
|
44
|
+
"cssnano": "^5.1.5",
|
|
45
45
|
"envinfo": "^7.8.1",
|
|
46
46
|
"fs-extra": "^10.0.1",
|
|
47
47
|
"highlightjs-line-numbers.js": "^2.8.0",
|
|
48
|
-
"inquirer": "^8.
|
|
48
|
+
"inquirer": "^8.2.2",
|
|
49
49
|
"neo-jsdoc": "^1.0.1",
|
|
50
50
|
"neo-jsdoc-x": "^1.0.4",
|
|
51
|
-
"postcss": "^8.4.
|
|
51
|
+
"postcss": "^8.4.12",
|
|
52
52
|
"sass": "^1.49.9",
|
|
53
53
|
"webpack": "^5.70.0",
|
|
54
54
|
"webpack-cli": "^4.9.2",
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import Base
|
|
2
|
-
import
|
|
3
|
-
import WorkerManager from '../../worker/Manager.mjs';
|
|
1
|
+
import Base from '../../core/Base.mjs';
|
|
2
|
+
import WorkerManager from '../../worker/Manager.mjs';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* Creates a ServiceWorker instance, in case Neo.config.useServiceWorker is set to true
|
|
@@ -15,15 +14,6 @@ class ServiceWorker extends Base {
|
|
|
15
14
|
* @protected
|
|
16
15
|
*/
|
|
17
16
|
className: 'Neo.main.addon.ServiceWorker',
|
|
18
|
-
/**
|
|
19
|
-
* @member {String[]|Neo.core.Base[]|null} mixins=[RemoteMethodAccess]
|
|
20
|
-
*/
|
|
21
|
-
mixins: [RemoteMethodAccess],
|
|
22
|
-
/**
|
|
23
|
-
* @member {ServiceWorkerRegistration|null} registration=null
|
|
24
|
-
* @protected
|
|
25
|
-
*/
|
|
26
|
-
registration: null,
|
|
27
17
|
/**
|
|
28
18
|
* @member {Boolean} singleton=true
|
|
29
19
|
* @protected
|
|
@@ -42,24 +32,33 @@ class ServiceWorker extends Base {
|
|
|
42
32
|
fileName = devMode ? 'ServiceWorker.mjs' : 'serviceworker.js',
|
|
43
33
|
folder = window.location.pathname.includes('/examples/') ? 'examples/' : 'apps/',
|
|
44
34
|
opts = devMode ? {type: 'module'} : {},
|
|
35
|
+
path = (devMode ? config.basePath : config.workerBasePath) + (devMode ? folder : '') + fileName,
|
|
45
36
|
serviceWorker = navigator.serviceWorker;
|
|
46
37
|
|
|
47
|
-
|
|
48
|
-
.then(registration => {
|
|
49
|
-
me.registration = registration;
|
|
38
|
+
window.addEventListener('beforeunload', me.onBeforeUnload.bind(me));
|
|
50
39
|
|
|
40
|
+
serviceWorker.register(path, opts)
|
|
41
|
+
.then(registration => {
|
|
51
42
|
serviceWorker.ready.then(() => {
|
|
52
43
|
serviceWorker.onmessage = WorkerManager.onWorkerMessage.bind(WorkerManager);
|
|
53
44
|
|
|
54
45
|
WorkerManager.sendMessage('service', {
|
|
55
|
-
action
|
|
56
|
-
|
|
57
|
-
data : config
|
|
46
|
+
action: 'registerNeoConfig',
|
|
47
|
+
data : config
|
|
58
48
|
});
|
|
59
49
|
});
|
|
60
50
|
})
|
|
61
51
|
}
|
|
62
52
|
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
*
|
|
56
|
+
*/
|
|
57
|
+
onBeforeUnload() {
|
|
58
|
+
WorkerManager.sendMessage('service', {
|
|
59
|
+
action: 'unregisterPort'
|
|
60
|
+
});
|
|
61
|
+
}
|
|
63
62
|
}
|
|
64
63
|
|
|
65
64
|
Neo.applyClassConfig(ServiceWorker);
|
|
@@ -8,6 +8,19 @@ import RemoteMethodAccess from './mixin/RemoteMethodAccess.mjs';
|
|
|
8
8
|
* @abstract
|
|
9
9
|
*/
|
|
10
10
|
class ServiceBase extends Base {
|
|
11
|
+
/**
|
|
12
|
+
* @member {String} cacheName='neo-runtime'
|
|
13
|
+
*/
|
|
14
|
+
cacheName = 'neo-runtime'
|
|
15
|
+
/**
|
|
16
|
+
* @member {String[]} cachePaths
|
|
17
|
+
*/
|
|
18
|
+
cachePaths = [
|
|
19
|
+
'raw.githubusercontent.com/',
|
|
20
|
+
'/dist/production/',
|
|
21
|
+
'/fontawesome',
|
|
22
|
+
'/resources/'
|
|
23
|
+
]
|
|
11
24
|
/**
|
|
12
25
|
* @member {Object[]|null} channelPorts=null
|
|
13
26
|
* @protected
|
|
@@ -28,6 +41,11 @@ class ServiceBase extends Base {
|
|
|
28
41
|
* @protected
|
|
29
42
|
*/
|
|
30
43
|
remotes = []
|
|
44
|
+
/**
|
|
45
|
+
* @member {String|null} workerId=null
|
|
46
|
+
* @protected
|
|
47
|
+
*/
|
|
48
|
+
workerId = null
|
|
31
49
|
|
|
32
50
|
static getConfig() {return {
|
|
33
51
|
/**
|
|
@@ -35,19 +53,6 @@ class ServiceBase extends Base {
|
|
|
35
53
|
* @protected
|
|
36
54
|
*/
|
|
37
55
|
className: 'Neo.worker.ServiceBase',
|
|
38
|
-
/**
|
|
39
|
-
* @member {String} cacheName='neo-runtime'
|
|
40
|
-
*/
|
|
41
|
-
cacheName: 'neo-runtime',
|
|
42
|
-
/**
|
|
43
|
-
* @member {String[]|null} cachePaths
|
|
44
|
-
*/
|
|
45
|
-
cachePaths: [
|
|
46
|
-
'raw.githubusercontent.com/',
|
|
47
|
-
'/dist/production/',
|
|
48
|
-
'/fontawesome',
|
|
49
|
-
'/resources/'
|
|
50
|
-
],
|
|
51
56
|
/**
|
|
52
57
|
* @member {String[]|Neo.core.Base[]|null} mixins=[RemoteMethodAccess]
|
|
53
58
|
*/
|
|
@@ -61,14 +66,10 @@ class ServiceBase extends Base {
|
|
|
61
66
|
app: [
|
|
62
67
|
'clearCache',
|
|
63
68
|
'clearCaches',
|
|
64
|
-
'preloadAssets'
|
|
69
|
+
'preloadAssets',
|
|
70
|
+
'removeAssets'
|
|
65
71
|
]
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* @member {String|null} workerId=null
|
|
69
|
-
* @protected
|
|
70
|
-
*/
|
|
71
|
-
workerId: null
|
|
72
|
+
}
|
|
72
73
|
}}
|
|
73
74
|
|
|
74
75
|
/**
|
|
@@ -95,19 +96,20 @@ class ServiceBase extends Base {
|
|
|
95
96
|
|
|
96
97
|
/**
|
|
97
98
|
* @param {String} name=this.cacheName
|
|
99
|
+
* @returns {Object}
|
|
98
100
|
*/
|
|
99
|
-
clearCache(name=this.cacheName) {
|
|
100
|
-
caches.
|
|
101
|
-
|
|
102
|
-
.then(cachesToDelete => Promise.all(cachesToDelete.map(cacheToDelete => caches.delete(cacheToDelete))))
|
|
101
|
+
async clearCache(name=this.cacheName) {
|
|
102
|
+
await caches.delete(name);
|
|
103
|
+
return {success: true}
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
/**
|
|
106
|
-
*
|
|
107
|
+
* @returns {Object}
|
|
107
108
|
*/
|
|
108
|
-
clearCaches() {
|
|
109
|
-
caches.keys()
|
|
110
|
-
|
|
109
|
+
async clearCaches() {
|
|
110
|
+
let keys = await caches.keys();
|
|
111
|
+
await Promise.all(keys.map(name => caches.delete(name)));
|
|
112
|
+
return {success: true}
|
|
111
113
|
}
|
|
112
114
|
|
|
113
115
|
/**
|
|
@@ -255,6 +257,19 @@ class ServiceBase extends Base {
|
|
|
255
257
|
this.onConnect(event.source);
|
|
256
258
|
}
|
|
257
259
|
|
|
260
|
+
/**
|
|
261
|
+
* @param {Object} msg
|
|
262
|
+
* @param {ExtendableMessageEvent} event
|
|
263
|
+
*/
|
|
264
|
+
onUnregisterPort(msg, event) {
|
|
265
|
+
for (let [index, value] of this.channelPorts.entries()) {
|
|
266
|
+
if (value.clientId === event.source.id) {
|
|
267
|
+
this.channelPorts.splice(index, 1);
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
258
273
|
/**
|
|
259
274
|
* @param {Object} data
|
|
260
275
|
* @param {String} [data.cacheName=this.cacheName]
|
|
@@ -280,9 +295,7 @@ class ServiceBase extends Base {
|
|
|
280
295
|
hasMatch = !!asset;
|
|
281
296
|
}
|
|
282
297
|
|
|
283
|
-
|
|
284
|
-
items.push(item);
|
|
285
|
-
}
|
|
298
|
+
!hasMatch && items.push(item);
|
|
286
299
|
}
|
|
287
300
|
|
|
288
301
|
if (items.length > 0) {
|
|
@@ -311,6 +324,44 @@ class ServiceBase extends Base {
|
|
|
311
324
|
});
|
|
312
325
|
}
|
|
313
326
|
|
|
327
|
+
/**
|
|
328
|
+
* You can either pass an url, an array of urls or an object with additional options
|
|
329
|
+
* See: https://developer.mozilla.org/en-US/docs/Web/API/Cache/delete
|
|
330
|
+
* @param {String|String[]|Object} data
|
|
331
|
+
* @param {String|String[]} data.assets
|
|
332
|
+
* @param {String} data.cacheName=this.cacheName
|
|
333
|
+
* @param {Object} data.options
|
|
334
|
+
* @param {Boolean} data.options.ignoreMethod=false
|
|
335
|
+
* @param {Boolean} data.options.ignoreSearch=false
|
|
336
|
+
* @param {Boolean} data.options.ignoreVary=false
|
|
337
|
+
* @returns {Object}
|
|
338
|
+
*/
|
|
339
|
+
async removeAssets(data) {
|
|
340
|
+
if (!Neo.isObject(data)) {
|
|
341
|
+
data = {
|
|
342
|
+
assets: data
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
let assets = data.assets,
|
|
347
|
+
cacheName = data.cacheName || this.cacheName,
|
|
348
|
+
options = data.options || {},
|
|
349
|
+
cache = await caches.open(cacheName),
|
|
350
|
+
promises = [];
|
|
351
|
+
|
|
352
|
+
if (!Array.isArray(assets)) {
|
|
353
|
+
assets = [assets];
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
assets.forEach(asset => {
|
|
357
|
+
promises.push(cache.delete(asset, options));
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
await Promise.all(promises);
|
|
361
|
+
|
|
362
|
+
return {success: true};
|
|
363
|
+
}
|
|
364
|
+
|
|
314
365
|
/**
|
|
315
366
|
* @param {String} dest app, data, main or vdom (excluding the current worker)
|
|
316
367
|
* @param {Object} opts configs for Neo.worker.Message
|