neo.mjs 3.2.5 → 3.2.8

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.
@@ -0,0 +1,36 @@
1
+ import Neo from '../src/Neo.mjs';
2
+ import * as core from '../src/core/_export.mjs';
3
+ import ServiceBase from '../src/worker/ServiceBase.mjs';
4
+
5
+ /**
6
+ * @class Neo.ServiceWorker
7
+ * @extends Neo.worker.ServiceBase
8
+ * @singleton
9
+ */
10
+ class ServiceWorker extends ServiceBase {
11
+ static getConfig() {return {
12
+ /**
13
+ * @member {String} className='Neo.ServiceWorker'
14
+ * @protected
15
+ */
16
+ className: 'Neo.ServiceWorker',
17
+ /**
18
+ * @member {Boolean} singleton=true
19
+ * @protected
20
+ */
21
+ singleton: true,
22
+ /**
23
+ * @member {String} workerId='service'
24
+ * @protected
25
+ */
26
+ workerId: 'service'
27
+ }}
28
+ }
29
+
30
+ Neo.applyClassConfig(ServiceWorker);
31
+
32
+ let instance = Neo.create(ServiceWorker);
33
+
34
+ Neo.applyToGlobalNs(instance);
35
+
36
+ export default instance;
@@ -61,7 +61,7 @@ if (programOpts.info) {
61
61
  type : 'list',
62
62
  name : 'threads',
63
63
  message: 'Please choose the threads to build:',
64
- choices: ['all', 'app', 'canvas', 'data', 'main', 'vdom'],
64
+ choices: ['all', 'app', 'canvas', 'data', 'main', 'service', 'vdom'],
65
65
  default: 'all'
66
66
  });
67
67
  }
@@ -88,11 +88,12 @@ if (programOpts.info) {
88
88
  }
89
89
 
90
90
  function parseThreads(tPath) {
91
- (threads === 'all' || threads === 'main') && spawnSync(webpack, ['--config', `${tPath}.main.mjs`], cpOpts);
92
- (threads === 'all' || threads === 'app') && spawnSync(webpack, ['--config', `${tPath}.appworker.mjs`, `--env insideNeo=${insideNeo}`], cpOpts);
93
- (threads === 'all' || threads === 'canvas') && spawnSync(webpack, ['--config', `${tPath}.worker.mjs`, `--env insideNeo=${insideNeo} worker=canvas`], cpOpts);
94
- (threads === 'all' || threads === 'data') && spawnSync(webpack, ['--config', `${tPath}.worker.mjs`, `--env insideNeo=${insideNeo} worker=data`], cpOpts);
95
- (threads === 'all' || threads === 'vdom') && spawnSync(webpack, ['--config', `${tPath}.worker.mjs`, `--env insideNeo=${insideNeo} worker=vdom`], cpOpts);
91
+ (threads === 'all' || threads === 'main') && spawnSync(webpack, ['--config', `${tPath}.main.mjs`], cpOpts);
92
+ (threads === 'all' || threads === 'app') && spawnSync(webpack, ['--config', `${tPath}.appworker.mjs`, `--env insideNeo=${insideNeo}`], cpOpts);
93
+ (threads === 'all' || threads === 'canvas') && spawnSync(webpack, ['--config', `${tPath}.worker.mjs`, `--env insideNeo=${insideNeo} worker=canvas`], cpOpts);
94
+ (threads === 'all' || threads === 'data') && spawnSync(webpack, ['--config', `${tPath}.worker.mjs`, `--env insideNeo=${insideNeo} worker=data`], cpOpts);
95
+ (threads === 'all' || threads === 'service') && spawnSync(webpack, ['--config', `${tPath}.worker.mjs`, `--env insideNeo=${insideNeo} worker=service`], cpOpts);
96
+ (threads === 'all' || threads === 'vdom') && spawnSync(webpack, ['--config', `${tPath}.worker.mjs`, `--env insideNeo=${insideNeo} worker=vdom`], cpOpts);
96
97
  }
97
98
 
98
99
  // dist/development
@@ -14,6 +14,10 @@
14
14
  "input": "./src/worker/Data.mjs",
15
15
  "output": "dataworker.js"
16
16
  },
17
+ "service": {
18
+ "input": "./apps/ServiceWorker.mjs",
19
+ "output": "serviceworker.js"
20
+ },
17
21
  "vdom": {
18
22
  "input": "./src/worker/VDom.mjs",
19
23
  "output": "vdomworker.js"
@@ -13,23 +13,5 @@
13
13
  "SharedDialog",
14
14
  "SharedDialog2",
15
15
  "Website"
16
- ],
17
- "workers": {
18
- "app": {
19
- "input": "./src/worker/App.mjs",
20
- "output": "appworker.js"
21
- },
22
- "canvas": {
23
- "input": "./src/worker/Canvas.mjs",
24
- "output": "canvasworker.js"
25
- },
26
- "data": {
27
- "input": "./src/worker/Data.mjs",
28
- "output": "dataworker.js"
29
- },
30
- "vdom": {
31
- "input": "./src/worker/VDom.mjs",
32
- "output": "vdomworker.js"
33
- }
34
- }
16
+ ]
35
17
  }
@@ -0,0 +1,36 @@
1
+ import Neo from '../src/Neo.mjs';
2
+ import * as core from '../src/core/_export.mjs';
3
+ import ServiceBase from '../src/worker/ServiceBase.mjs';
4
+
5
+ /**
6
+ * @class Neo.ServiceWorker
7
+ * @extends Neo.worker.ServiceBase
8
+ * @singleton
9
+ */
10
+ class ServiceWorker extends ServiceBase {
11
+ static getConfig() {return {
12
+ /**
13
+ * @member {String} className='Neo.ServiceWorker'
14
+ * @protected
15
+ */
16
+ className: 'Neo.ServiceWorker',
17
+ /**
18
+ * @member {Boolean} singleton=true
19
+ * @protected
20
+ */
21
+ singleton: true,
22
+ /**
23
+ * @member {String} workerId='service'
24
+ * @protected
25
+ */
26
+ workerId: 'service'
27
+ }}
28
+ }
29
+
30
+ Neo.applyClassConfig(ServiceWorker);
31
+
32
+ let instance = Neo.create(ServiceWorker);
33
+
34
+ Neo.applyToGlobalNs(instance);
35
+
36
+ export default instance;
@@ -4,5 +4,6 @@
4
4
  "environment" : "development",
5
5
  "mainPath" : "./Main.mjs",
6
6
  "renderCountDeltas": true,
7
- "themes" : ["neo-theme-dark"]
7
+ "themes" : ["neo-theme-dark"],
8
+ "useServiceWorker" : true
8
9
  }
@@ -0,0 +1,6 @@
1
+ import MainContainer from './view/MainContainer.mjs';
2
+
3
+ export const onStart = () => Neo.app({
4
+ mainView: MainContainer,
5
+ name : 'MyApp'
6
+ });
@@ -0,0 +1,11 @@
1
+ <!DOCTYPE HTML>
2
+ <html>
3
+ <head>
4
+ <meta name="viewport" content="width=device-width, initial-scale=1">
5
+ <meta charset="UTF-8">
6
+ <title>Preloading Assets</title>
7
+ </head>
8
+ <body>
9
+ <script src="../../src/MicroLoader.mjs" type="module"></script>
10
+ </body>
11
+ </html>
@@ -0,0 +1,7 @@
1
+ {
2
+ "appPath": "examples/preloadingAssets/app.mjs",
3
+ "basePath": "../../",
4
+ "environment": "development",
5
+ "mainPath": "./Main.mjs",
6
+ "useServiceWorker": true
7
+ }
@@ -0,0 +1,60 @@
1
+ import Component from '../../../src/component/Base.mjs';
2
+ import MainContainerController from './MainContainerController.mjs'
3
+ import TabContainer from '../../../src/tab/Container.mjs';
4
+ import Viewport from '../../../src/container/Viewport.mjs';
5
+
6
+ /**
7
+ * @class Neo.examples.preloadingAssets.view.MainContainer
8
+ * @extends Neo.container.Viewport
9
+ */
10
+ class MainContainer extends Viewport {
11
+ static getConfig() {return {
12
+ className: 'Neo.examples.preloadingAssets.view.MainContainer',
13
+ autoMount: true,
14
+ controller: MainContainerController,
15
+ layout : {ntype: 'fit'},
16
+
17
+ items: [{
18
+ module: TabContainer,
19
+ height: 300,
20
+ width : 500,
21
+ style : {flex: 'none', margin: '20px'},
22
+
23
+ itemDefaults: {
24
+ module: Component,
25
+ style : {flex: 'none', padding: '20px'}
26
+ },
27
+
28
+ items: [{
29
+ tabButtonConfig: {
30
+ iconCls: 'fa fa-user-astronaut',
31
+ text : 'Bob'
32
+ },
33
+
34
+ vdom: {
35
+ tag: 'img',
36
+ src: '../../resources/examples/ai_images/000150.jpg'
37
+ }
38
+ }, {
39
+ tabButtonConfig: {
40
+ iconCls: 'fa fa-user-ninja',
41
+ text : 'Alice'
42
+ },
43
+
44
+ vdom: {
45
+ tag: 'img',
46
+ src: '../../resources/examples/ai_images/000074.jpg'
47
+ }
48
+ }],
49
+
50
+ listeners: {
51
+ activeIndexChange: 'onActiveIndexChange',
52
+ mounted : 'onMounted'
53
+ }
54
+ }]
55
+ }}
56
+ }
57
+
58
+ Neo.applyClassConfig(MainContainer);
59
+
60
+ export default MainContainer;
@@ -0,0 +1,41 @@
1
+ import Component from '../../../src/controller/Component.mjs';
2
+
3
+ /**
4
+ * @class Neo.examples.preloadingAssets.view.MainContainerController
5
+ * @extends Neo.controller.Component
6
+ */
7
+ class MainContainerController extends Component {
8
+ static getConfig() {return {
9
+ /**
10
+ * @member {String} className='Neo.examples.preloadingAssets.view.MainContainerController'
11
+ * @protected
12
+ */
13
+ className: 'Neo.examples.preloadingAssets.view.MainContainerController'
14
+ }}
15
+
16
+ /**
17
+ * @param {Object} data
18
+ */
19
+ onActiveIndexChange(data) {
20
+ console.log('onActiveIndexChange', data);
21
+ }
22
+
23
+ /**
24
+ * @param {String} id
25
+ */
26
+ onMounted(id) {
27
+ console.log('onMounted', id);
28
+
29
+ setTimeout(() => {
30
+ Neo.ServiceWorker.preloadAssets({
31
+ files: ['../resources/examples/ai_images/000074.jpg']
32
+ }).then(data => {
33
+ console.log(data);
34
+ });
35
+ }, 3000)
36
+ }
37
+ }
38
+
39
+ Neo.applyClassConfig(MainContainerController);
40
+
41
+ export default MainContainerController;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "3.2.5",
3
+ "version": "3.2.8",
4
4
  "description": "The webworkers driven UI framework",
5
5
  "type": "module",
6
6
  "repository": {
@@ -41,16 +41,16 @@
41
41
  "chalk": "^5.0.0",
42
42
  "clean-webpack-plugin": "^4.0.0",
43
43
  "commander": "^9.0.0",
44
- "cssnano": "^5.0.17",
44
+ "cssnano": "^5.1.0",
45
45
  "envinfo": "^7.8.1",
46
- "fs-extra": "^10.0.0",
46
+ "fs-extra": "^10.0.1",
47
47
  "highlightjs-line-numbers.js": "^2.8.0",
48
48
  "inquirer": "^8.1.5",
49
49
  "neo-jsdoc": "^1.0.1",
50
50
  "neo-jsdoc-x": "^1.0.4",
51
- "postcss": "^8.4.6",
52
- "sass": "^1.49.8",
53
- "webpack": "^5.69.1",
51
+ "postcss": "^8.4.7",
52
+ "sass": "^1.49.9",
53
+ "webpack": "^5.70.0",
54
54
  "webpack-cli": "^4.9.2",
55
55
  "webpack-dev-server": "4.7.4",
56
56
  "webpack-hook-plugin": "^1.0.7",
@@ -134,7 +134,7 @@ const DefaultConfig = {
134
134
  * Experimental flag if an offscreen canvas worker should get created.
135
135
  * @default false
136
136
  * @memberOf! module:Neo
137
- * @name config.useCssVars
137
+ * @name config.useCanvasWorker
138
138
  * @type Boolean
139
139
  */
140
140
  useCanvasWorker: false,
@@ -163,6 +163,15 @@ const DefaultConfig = {
163
163
  * @type Boolean
164
164
  */
165
165
  useGoogleAnalytics: false,
166
+ /**
167
+ * True will add the ServiceWorker main thread addon to support caching of assets (PWA)
168
+ * See: https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API
169
+ * @default false
170
+ * @memberOf! module:Neo
171
+ * @name config.useServiceWorker
172
+ * @type Boolean
173
+ */
174
+ useServiceWorker: false,
166
175
  /**
167
176
  * Creates App, Data & VDom as SharedWorkers.
168
177
  * Set this one to true in case you want to connect multiple main threads.
package/src/Main.mjs CHANGED
@@ -188,9 +188,11 @@ class Main extends core.Base {
188
188
  *
189
189
  */
190
190
  async onDomContentLoaded() {
191
- let me = this,
192
- config = Neo.config,
193
- imports = [];
191
+ let me = this,
192
+ config = Neo.config,
193
+ mainThreadAddons = config.mainThreadAddons,
194
+ imports = [],
195
+ modules;
194
196
 
195
197
  DomAccess.onDomContentLoaded();
196
198
 
@@ -203,18 +205,20 @@ class Main extends core.Base {
203
205
  __webpack_require__.p = config.basePath.substring(6);
204
206
  }
205
207
 
206
- config.mainThreadAddons.forEach(addon => {
207
- if (addon !== 'AnalyticsByGoogle') {
208
- imports.push(import(`./main/addon/${addon}.mjs`));
209
- }
210
- });
211
-
212
208
  // intended for the online examples where we need an easy way to add GA to every generated app
213
- if (config.useGoogleAnalytics || config.mainThreadAddons.includes('AnalyticsByGoogle')) {
214
- imports.push(import('./main/addon/AnalyticsByGoogle.mjs'));
209
+ if (config.useGoogleAnalytics && !mainThreadAddons.includes('AnalyticsByGoogle')) {
210
+ mainThreadAddons.push('AnalyticsByGoogle');
215
211
  }
216
212
 
217
- const modules = await Promise.all(imports);
213
+ if (config.useServiceWorker && !mainThreadAddons.includes('ServiceWorker')) {
214
+ mainThreadAddons.push('ServiceWorker');
215
+ }
216
+
217
+ mainThreadAddons.forEach(addon => {
218
+ imports.push(import(`./main/addon/${addon}.mjs`));
219
+ });
220
+
221
+ modules = await Promise.all(imports);
218
222
 
219
223
  me.addon = {};
220
224
 
@@ -1,2 +1,2 @@
1
1
  # Development in progress
2
- Currently, scheduled for the v2.4 release
2
+ Currently, scheduled for the v4.x release
@@ -92,10 +92,7 @@ class EditEventContainer extends FormContainer {
92
92
  */
93
93
  afterSetMounted(value, oldValue) {
94
94
  super.afterSetMounted(value, oldValue);
95
-
96
- if (value) {
97
- this.getField('title').focus();
98
- }
95
+ value && this.getField('title').focus();
99
96
  }
100
97
 
101
98
  /**
@@ -310,7 +310,7 @@ class MainContainer extends Container {
310
310
  style : {marginLeft: '10px'}
311
311
  });
312
312
  }, 10);
313
- } else if (value === false && oldValue) {
313
+ } else if (!value && oldValue) {
314
314
  // we only need this logic in case we dynamically change the config from true to false
315
315
  me.items[1] .removeLast();
316
316
  me.items[0].items[1].removeLast();
@@ -652,9 +652,7 @@ class MainContainer extends Container {
652
652
 
653
653
  map[me.activeView]();
654
654
 
655
- me.model.setData({
656
- currentDate: currentDate
657
- });
655
+ me.model.setData({currentDate});
658
656
  }
659
657
  }
660
658
 
@@ -29,9 +29,7 @@ class HBox extends Flexbox {
29
29
  */
30
30
  applyChildAttributes(item) {
31
31
  // Do not apply flex if fixed width
32
- if (!item.width) {
33
- super.applyChildAttributes(item);
34
- }
32
+ !item.width && super.applyChildAttributes(item);
35
33
  }
36
34
  }
37
35
 
@@ -29,9 +29,7 @@ class VBox extends Flexbox {
29
29
  */
30
30
  applyChildAttributes(item) {
31
31
  // Do not apply flex if fixed height
32
- if (!item.height) {
33
- super.applyChildAttributes(item);
34
- }
32
+ !item.height && super.applyChildAttributes(item);
35
33
  }
36
34
  }
37
35
 
@@ -0,0 +1,71 @@
1
+ import Base from '../../core/Base.mjs';
2
+ import RemoteMethodAccess from '../../worker/mixin/RemoteMethodAccess.mjs';
3
+ import WorkerManager from '../../worker/Manager.mjs';
4
+
5
+ /**
6
+ * Creates a ServiceWorker instance, in case Neo.config.useServiceWorker is set to true
7
+ * @class Neo.main.addon.ServiceWorker
8
+ * @extends Neo.core.Base
9
+ * @singleton
10
+ */
11
+ class ServiceWorker extends Base {
12
+ static getConfig() {return {
13
+ /**
14
+ * @member {String} className='Neo.main.addon.ServiceWorker'
15
+ * @protected
16
+ */
17
+ 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
+ /**
28
+ * @member {Boolean} singleton=true
29
+ * @protected
30
+ */
31
+ singleton: true
32
+ }}
33
+
34
+ /**
35
+ * @param {Object} config
36
+ */
37
+ construct(config) {
38
+ if ('serviceWorker' in navigator) {
39
+ let me = this,
40
+ config = Neo.config,
41
+ devMode = config.environment === 'development',
42
+ fileName = devMode ? 'ServiceWorker.mjs' : 'serviceworker.js',
43
+ folder = window.location.pathname.includes('/examples/') ? 'examples/' : 'apps/',
44
+ opts = devMode ? {type: 'module'} : {},
45
+ serviceWorker = navigator.serviceWorker;
46
+
47
+ serviceWorker.register(config.basePath + folder + fileName, opts)
48
+ .then(registration => {
49
+ me.registration = registration;
50
+
51
+ serviceWorker.ready.then(() => {
52
+ serviceWorker.onmessage = WorkerManager.onWorkerMessage.bind(WorkerManager);
53
+
54
+ WorkerManager.sendMessage('service', {
55
+ action : 'registerNeoConfig',
56
+ channelPort: registration.active,
57
+ data : config
58
+ });
59
+ });
60
+ })
61
+ }
62
+ }
63
+ }
64
+
65
+ Neo.applyClassConfig(ServiceWorker);
66
+
67
+ let instance = Neo.create(ServiceWorker);
68
+
69
+ Neo.applyToGlobalNs(instance);
70
+
71
+ export default instance;
@@ -53,7 +53,6 @@ class Stylesheet extends Base {
53
53
 
54
54
  if (Neo.config.themes.length > 0 && Neo.config.themes[0] !== '') {
55
55
  this.addGlobalCss();
56
- // this.insertTheme();
57
56
  }
58
57
  }
59
58
 
@@ -139,9 +139,9 @@ class Container extends BaseContainer {
139
139
  me.updateTabButtons();
140
140
 
141
141
  me.fire('activeIndexChange', {
142
- item : me.getActiveCard(),
143
- oldValue: oldValue,
144
- value : value
142
+ item: me.getActiveCard(),
143
+ oldValue,
144
+ value
145
145
  });
146
146
  }
147
147
  }
@@ -245,7 +245,7 @@ class Container extends BaseContainer {
245
245
  tabButtons.push(me.getTabButtonConfig(item.tabButtonConfig, index));
246
246
 
247
247
  if (!(item instanceof Neo.component.Base)) {
248
- item = {...me.itemDefaults, flex: 1, isTab:true, ...item};
248
+ item = {flex: 1, ...me.itemDefaults, isTab: true, ...item};
249
249
  }
250
250
 
251
251
  tabComponents.push(item);
@@ -72,7 +72,7 @@ class App extends Base {
72
72
  }
73
73
 
74
74
  /**
75
- * @param {JSON} data
75
+ * @param {Object} data
76
76
  */
77
77
  createThemeMap(data) {
78
78
  Neo.ns('Neo.cssMap.fileInfo', true);
@@ -265,7 +265,7 @@ class App extends Base {
265
265
  this.onRegisterApp({ appName });
266
266
 
267
267
  this.sendMessage('main', {
268
- action:'registerAppName',
268
+ action: 'registerAppName',
269
269
  appName
270
270
  });
271
271
  }
@@ -218,6 +218,10 @@ class Manager extends Base {
218
218
  * @returns {Worker}
219
219
  */
220
220
  getWorker(name) {
221
+ if (name === 'service') {
222
+ return navigator.serviceWorker?.controller;
223
+ }
224
+
221
225
  return name instanceof Worker ? name : this.workers[name].worker;
222
226
  }
223
227
 
@@ -386,7 +390,12 @@ class Manager extends Base {
386
390
  message, worker;
387
391
 
388
392
  if (!me.stopCommunication) {
389
- worker = me.getWorker(dest);
393
+ if (opts.channelPort) {
394
+ worker = opts.channelPort;
395
+ delete opts.channelPort;
396
+ } else {
397
+ worker = me.getWorker(dest);
398
+ }
390
399
 
391
400
  if (!worker) {
392
401
  throw new Error('Called sendMessage for a worker that does not exist: ' + dest);
@@ -396,7 +405,7 @@ class Manager extends Base {
396
405
 
397
406
  message = new Message(opts);
398
407
 
399
- (me.sharedWorkersEnabled && NeoConfig.useSharedWorkers ? worker.port : worker).postMessage(message, transfer);
408
+ (worker.port ? worker.port : worker).postMessage(message, transfer);
400
409
  return message;
401
410
  }
402
411
  }
@@ -0,0 +1,336 @@
1
+ import Base from '../core/Base.mjs';
2
+ import Message from './Message.mjs';
3
+ import RemoteMethodAccess from './mixin/RemoteMethodAccess.mjs';
4
+
5
+ /**
6
+ * @class Neo.worker.ServiceBase
7
+ * @extends Neo.core.Base
8
+ * @abstract
9
+ */
10
+ class ServiceBase extends Base {
11
+ /**
12
+ * @member {Object[]|null} channelPorts=null
13
+ * @protected
14
+ */
15
+ channelPorts = null
16
+ /**
17
+ * @member {Client|null} lastClient=null
18
+ * @protected
19
+ */
20
+ lastClient = null
21
+ /**
22
+ * @member {Object[]} promises=[]
23
+ * @protected
24
+ */
25
+ promises = []
26
+ /**
27
+ * @member {String[]} remotes=[]
28
+ * @protected
29
+ */
30
+ remotes = []
31
+
32
+ static getConfig() {return {
33
+ /**
34
+ * @member {String} className='Neo.worker.ServiceBase'
35
+ * @protected
36
+ */
37
+ 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
+ /**
52
+ * @member {String[]|Neo.core.Base[]|null} mixins=[RemoteMethodAccess]
53
+ */
54
+ mixins: [RemoteMethodAccess],
55
+ /**
56
+ * Remote method access for other workers
57
+ * @member {Object} remote={app: [//...]}
58
+ * @protected
59
+ */
60
+ remote: {
61
+ app: [
62
+ 'clearCache',
63
+ 'clearCaches',
64
+ 'preloadAssets'
65
+ ]
66
+ },
67
+ /**
68
+ * @member {String|null} workerId=null
69
+ * @protected
70
+ */
71
+ workerId: null
72
+ }}
73
+
74
+ /**
75
+ * @param {Object} config
76
+ */
77
+ construct(config) {
78
+ super.construct(config);
79
+
80
+ let me = this,
81
+ bind = name => me[name].bind(me);
82
+
83
+ me.channelPorts = [];
84
+
85
+ Object.assign(globalThis, {
86
+ onactivate: bind('onActivate'),
87
+ onfetch : bind('onFetch'),
88
+ oninstall : bind('onInstall'),
89
+ onmessage : bind('onMessage')
90
+ });
91
+
92
+ Neo.currentWorker = me;
93
+ Neo.workerId = me.workerId;
94
+ }
95
+
96
+ /**
97
+ * @param {String} name=this.cacheName
98
+ */
99
+ clearCache(name=this.cacheName) {
100
+ caches.keys()
101
+ .then(cacheNames => cacheNames.filter(cacheName => cacheName === name))
102
+ .then(cachesToDelete => Promise.all(cachesToDelete.map(cacheToDelete => caches.delete(cacheToDelete))))
103
+ }
104
+
105
+ /**
106
+ *
107
+ */
108
+ clearCaches() {
109
+ caches.keys()
110
+ .then(cachesToDelete => Promise.all(cachesToDelete.map(cacheToDelete => caches.delete(cacheToDelete))))
111
+ }
112
+
113
+ /**
114
+ * @param {Client} client
115
+ */
116
+ createMessageChannel(client) {
117
+ let me = this,
118
+ channel = new MessageChannel(),
119
+ port = channel.port2;
120
+
121
+ channel.port1.onmessage = me.onMessage.bind(me);
122
+
123
+ me.sendMessage('app', {action: 'registerPort', transfer: port}, [port]);
124
+
125
+ me.channelPorts.push({
126
+ clientId : client.id,
127
+ destination: 'app',
128
+ port : channel.port1
129
+ });
130
+ }
131
+
132
+ /**
133
+ *
134
+ * @param {String} destination
135
+ * @param {String} clientId=this.lastClient.id
136
+ * @returns {MessagePort|null}
137
+ */
138
+ getPort(destination, clientId=this.lastClient?.id) {
139
+ for (let port of this.channelPorts) {
140
+ if (clientId === port.clientId && destination === port.destination) {
141
+ return port.port;
142
+ }
143
+ }
144
+
145
+ return null;
146
+ }
147
+
148
+ /**
149
+ * Ignore the call in case there is no connected client in place yet
150
+ */
151
+ initRemote() {
152
+ let me = this,
153
+ lastClientId = me.lastClient?.id;
154
+
155
+ if (lastClientId && !me.remotes.includes(lastClientId)) {
156
+ me.remotes.push(lastClientId);
157
+ super.initRemote();
158
+ }
159
+ }
160
+
161
+ /**
162
+ * @param {ExtendableMessageEvent} event
163
+ */
164
+ onActivate(event) {
165
+ console.log('onActivate', event);
166
+ }
167
+
168
+ /**
169
+ * @param {Client} source
170
+ */
171
+ onConnect(source) {
172
+ console.log('onConnect', source);
173
+
174
+ this.createMessageChannel(source);
175
+ this.initRemote();
176
+ }
177
+
178
+ /**
179
+ * @param {ExtendableMessageEvent} event
180
+ */
181
+ onFetch(event) {
182
+ let hasMatch = false,
183
+ request = event.request,
184
+ key;
185
+
186
+ for (key of this.cachePaths) {
187
+ if (request.url.includes(key)) {
188
+ hasMatch = true;
189
+ break;
190
+ }
191
+ }
192
+
193
+ hasMatch && event.respondWith(
194
+ caches.match(request)
195
+ .then(cachedResponse => cachedResponse || caches.open(this.cacheName)
196
+ .then(cache => fetch(request)
197
+ .then(response => cache.put(request, response.clone())
198
+ .then(() => response)
199
+ )))
200
+ );
201
+ }
202
+
203
+ /**
204
+ * @param {ExtendableMessageEvent} event
205
+ */
206
+ onInstall(event) {
207
+ console.log('onInstall', event);
208
+ globalThis.skipWaiting();
209
+ }
210
+
211
+ /**
212
+ * For a client based message we receive an ExtendableMessageEvent,
213
+ * for a MessageChannel based message a MessageEvent
214
+ * @param {ExtendableMessageEvent|MessageEvent} event
215
+ */
216
+ onMessage(event) {
217
+ let me = this,
218
+ data = event.data,
219
+ action = data.action,
220
+ replyId = data.replyId,
221
+ promise;
222
+
223
+ if (event.source) { // ExtendableMessageEvent
224
+ me.lastClient = event.source;
225
+ }
226
+
227
+ if (!action) {
228
+ throw new Error('Message action is missing: ' + data.id);
229
+ }
230
+
231
+ if (action !== 'reply') {
232
+ me['on' + Neo.capitalize(action)](data, event);
233
+ } else if (promise = action === 'reply' && me.promises[replyId]) {
234
+ promise[data.reject ? 'reject' : 'resolve'](data.data);
235
+ delete me.promises[replyId];
236
+ }
237
+ }
238
+
239
+ /**
240
+ * @param {Object} msg
241
+ * @param {ExtendableMessageEvent} event
242
+ */
243
+ onPing(msg, event) {
244
+ this.resolve(msg, {originMsg: msg});
245
+ }
246
+
247
+ /**
248
+ * @param {Object} msg
249
+ * @param {ExtendableMessageEvent} event
250
+ */
251
+ onRegisterNeoConfig(msg, event) {
252
+ Neo.config = Neo.config || {};
253
+ Object.assign(Neo.config, msg.data);
254
+
255
+ this.onConnect(event.source);
256
+ }
257
+
258
+ /**
259
+ * @param {Object} data
260
+ * @param {String} [data.cacheName=this.cacheName]
261
+ * @param {String[]|String} data.files
262
+ * @param {Boolean} [data.foreReload=false]
263
+ */
264
+ async preloadAssets(data) {
265
+ let cacheName = data.cacheName || this.cacheName,
266
+ cache = await caches.open(cacheName),
267
+ files = data.files,
268
+ items = [],
269
+ asset, hasMatch, item;
270
+
271
+ if (!Array.isArray(files)) {
272
+ files = [files];
273
+ }
274
+
275
+ for (item of files) {
276
+ hasMatch = false;
277
+
278
+ if (!data.forceReload) {
279
+ asset = await cache.match(item);
280
+ hasMatch = !!asset;
281
+ }
282
+
283
+ if (!hasMatch) {
284
+ items.push(item);
285
+ }
286
+ }
287
+
288
+ if (items.length > 0) {
289
+ await cache.addAll(items);
290
+ }
291
+
292
+ return {success: true};
293
+ }
294
+
295
+ /**
296
+ * @param {String} dest app, data, main or vdom (excluding the current worker)
297
+ * @param {Object} opts configs for Neo.worker.Message
298
+ * @param {Array} [transfer] An optional array of Transferable objects to transfer ownership of.
299
+ * If the ownership of an object is transferred, it becomes unusable (neutered) in the context it was sent from
300
+ * and becomes available only to the worker it was sent to.
301
+ * @returns {Promise<any>}
302
+ */
303
+ promiseMessage(dest, opts, transfer) {
304
+ let me = this;
305
+
306
+ return new Promise(function(resolve, reject) {
307
+ let message = me.sendMessage(dest, opts, transfer),
308
+ msgId = message.id;
309
+
310
+ me.promises[msgId] = {reject, resolve};
311
+ });
312
+ }
313
+
314
+ /**
315
+ * @param {String} dest app, data, main or vdom (excluding the current worker)
316
+ * @param {Object} opts configs for Neo.worker.Message
317
+ * @param {Array} [transfer] An optional array of Transferable objects to transfer ownership of.
318
+ * If the ownership of an object is transferred, it becomes unusable (neutered) in the context it was sent from
319
+ * and becomes available only to the worker it was sent to.
320
+ * @returns {Neo.worker.Message}
321
+ * @protected
322
+ */
323
+ sendMessage(dest, opts, transfer) {
324
+ opts.destination = dest;
325
+
326
+ let message = new Message(opts),
327
+ port = this.getPort(dest) || this.lastClient;
328
+
329
+ port.postMessage(message, transfer);
330
+ return message;
331
+ }
332
+ }
333
+
334
+ Neo.applyClassConfig(ServiceBase);
335
+
336
+ export default ServiceBase;
@@ -56,7 +56,7 @@ class RemoteMethodAccess extends Base {
56
56
  methods = remote.methods,
57
57
  pkg = Neo.ns(className, true);
58
58
 
59
- methods.forEach(function(method) {
59
+ methods.forEach(method => {
60
60
  if (remote.origin !== 'main' && pkg[method]) {
61
61
  throw new Error('Duplicate remote method definition ' + className + '.' + method);
62
62
  }