neo.mjs 4.0.5 → 4.0.6

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,3 @@
1
+ {
2
+ "success": true
3
+ }
@@ -1,16 +1,17 @@
1
1
  {
2
2
  "namespace": "MyApi",
3
- "url" : "./",
3
+ "url" : "../../examples/remotesApi/basic/remotes-api-response.json",
4
4
 
5
- "services": [{
6
- "name" : "FriendService",
7
- "methods": [{
8
- "name": "getAll"
9
- }]
10
- }, {
11
- "name" : "UserService",
12
- "methods": [{
13
- "name": "getAll"
14
- }]
15
- }]
5
+ "services": {
6
+ "FriendService": {
7
+ "methods": {
8
+ "getAll": {}
9
+ }
10
+ },
11
+ "UserService": {
12
+ "methods": {
13
+ "getAll": {}
14
+ }
15
+ }
16
+ }
16
17
  }
@@ -3,5 +3,5 @@
3
3
  "basePath" : "../../../",
4
4
  "environment": "development",
5
5
  "mainPath" : "./Main.mjs",
6
- "themes" : ["neo-theme-dark"]
6
+ "themes" : ["neo-theme-dark", "neo-theme-light"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "4.0.5",
3
+ "version": "4.0.6",
4
4
  "description": "The webworkers driven UI framework",
5
5
  "type": "module",
6
6
  "repository": {
@@ -40,10 +40,10 @@
40
40
  "autoprefixer": "^10.4.4",
41
41
  "chalk": "^5.0.1",
42
42
  "clean-webpack-plugin": "^4.0.0",
43
- "commander": "^9.1.0",
43
+ "commander": "^9.2.0",
44
44
  "cssnano": "^5.1.7",
45
45
  "envinfo": "^7.8.1",
46
- "fs-extra": "^10.0.1",
46
+ "fs-extra": "^10.1.0",
47
47
  "highlightjs-line-numbers.js": "^2.8.0",
48
48
  "inquirer": "^8.2.2",
49
49
  "neo-jsdoc": "^1.0.1",
@@ -705,7 +705,7 @@ class Base extends CoreBase {
705
705
  /**
706
706
  * Returns all items which match the property and value
707
707
  * @param {Object|String} property
708
- * @param {String|Number} value
708
+ * @param {String|Number} [value] Optional, in case the first param is an object
709
709
  * @returns {Array} Returns an empty Array in case no items are found
710
710
  */
711
711
  find(property, value) {
@@ -95,24 +95,30 @@ class Fetch extends Base {
95
95
  if (!Neo.isString(url)) {
96
96
  config = url;
97
97
  url = config.url;
98
+ } else {
99
+ config.url = config;
98
100
  }
99
101
 
100
- return fetch(url)
101
- .then(resp => {
102
- console.log(resp);
102
+ return fetch(url, {
103
+ body : data,
104
+ method: method || config.method
105
+ }).then(resp => {
106
+ let response = {
107
+ ok : resp.ok,
108
+ redirected: resp.redirected,
109
+ request : config,
110
+ status : resp.status,
111
+ statusText: resp.statusText,
112
+ type : resp.type,
113
+ url : resp.url
114
+ };
103
115
 
104
- let response = {
105
- ok : resp.ok,
106
- redirected: resp.redirected,
107
- request : config,
108
- status : resp.status,
109
- statusText: resp.statusText,
110
- type : resp.type,
111
- url : resp.url
112
- };
113
-
114
- return response
115
- })
116
+ return resp[config.responseType || 'json']()
117
+ .then(data => {
118
+ response.data = data;
119
+ })
120
+ .then(() => (resp.ok ? response : Promise.reject(response)));
121
+ })
116
122
  }
117
123
  }
118
124
 
@@ -1,22 +1,200 @@
1
- import Base from '../../core/Base.mjs';
1
+ import Base from '../../core/Base.mjs';
2
+ import NeoFunction from '../../util/Function.mjs';
3
+ import Observable from '../../core/Observable.mjs';
2
4
 
3
5
  /**
4
- * @class Neo.data.connection.Socket
6
+ * @class Neo.data.connection.WebSocket
5
7
  * @extends Neo.core.Base
6
8
  */
7
9
  class Socket extends Base {
10
+ /**
11
+ * @member {String|null} channel=null
12
+ */
13
+ channel = null
14
+ /**
15
+ * @member {Number} maxReconnectAttempts=5
16
+ */
17
+ maxReconnectAttempts = 5
18
+ /**
19
+ * @member {Number} reconnectAttempts=0
20
+ * @protected
21
+ */
22
+ reconnectAttempts = 0
23
+ /**
24
+ * @member {String|null} serverAddress=null
25
+ */
26
+ serverAddress = null
27
+
28
+ static getStaticConfig() {return {
29
+ /**
30
+ * True automatically applies the core.Observable mixin
31
+ * @member {Boolean} observable=true
32
+ * @static
33
+ */
34
+ observable: true
35
+ }}
36
+
8
37
  static getConfig() {return {
9
38
  /**
10
- * @member {String} className='Neo.data.connection.Socket'
39
+ * @member {String} className='Neo.data.connection.WebSocket'
11
40
  * @protected
12
41
  */
13
- className: 'Neo.data.connection.Socket',
42
+ className: 'Neo.data.connection.WebSocket',
14
43
  /**
15
44
  * @member {String} ntype='socket-connection'
16
45
  * @protected
17
46
  */
18
- ntype: 'socket-connection'
47
+ ntype: 'socket-connection',
48
+ /**
49
+ * @member {WebSocket|null} socket_=null
50
+ * @protected
51
+ */
52
+ socket_: null
19
53
  }}
54
+
55
+ /**
56
+ * @param {Object} config
57
+ */
58
+ construct(config) {
59
+ super.construct(config);
60
+ this.socket = new WebSocket(this.serverAddress);
61
+ }
62
+
63
+ /**
64
+ * @param {Function} callback
65
+ * @param {Object} scope
66
+ */
67
+ attemptReconnect(callback, scope) {
68
+ let me = this;
69
+
70
+ me.reconnectAttempts++;
71
+
72
+ if (me.reconnectAttempts < me.maxReconnectAttempts) {
73
+ me.socket = new WebSocket(me.serverAddress);
74
+
75
+ callback && me.on('open', {
76
+ callback,
77
+ scope : scope || me,
78
+ single: true
79
+ });
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Intercepts the WebSocket send calls
85
+ * @param {Object} data
86
+ * @returns {String}
87
+ */
88
+ beforeSend(data) {
89
+ let me = this,
90
+ channel = me.channel;
91
+
92
+ console.debug('WS: Sending message', (channel ? '\nChannel: ' + channel : ''), '\nData:', data);
93
+
94
+ return JSON.stringify(channel ? {channel, data} : data);
95
+ }
96
+
97
+ /**
98
+ * Triggered before the socket config gets changed.
99
+ * @param {WebSocket|null} value
100
+ * @param {WebSocket|null} oldValue
101
+ * @returns {WebSocket|null}
102
+ * @protected
103
+ */
104
+ beforeSetSocket(value, oldValue) {
105
+ if (value) {
106
+ let me = this;
107
+
108
+ Object.assign(value, {
109
+ onclose : me.onClose .bind(me),
110
+ onerror : me.onError .bind(me),
111
+ onmessage: me.onMessage.bind(me),
112
+ onopen : me.onOpen .bind(me)
113
+ });
114
+
115
+ NeoFunction.intercept(value, 'send', me.beforeSend, me);
116
+ }
117
+
118
+ return value;
119
+ }
120
+
121
+ /**
122
+ * @param {CloseEvent} event The Websocket generated CloseEvent
123
+ * @param {Number} event.code The WebSocket connection close code provided by the server
124
+ *
125
+ * Code Name Description
126
+ * 0-999 Reserved and not used.
127
+ * 1000 CLOSE_NORMAL Normal closure; the connection successfully completed whatever purpose for which it was created.
128
+ * 1001 CLOSE_GOING_AWAY The endpoint is going away, either because of a server failure or because the browser is navigating away from the page that opened the connection.
129
+ * 1002 CLOSE_PROTOCOL_ERROR The endpoint is terminating the connection due to a protocol error.
130
+ * 1003 CLOSE_UNSUPPORTED The connection is being terminated because the endpoint received data of a type it cannot accept (for example, a text-only endpoint received binary data).
131
+ * 1004 CLOSE_TOO_LARGE The endpoint is terminating the connection because a data frame was received that is too large.
132
+ * 1005 CLOSE_NO_STATUS Reserved. Indicates that no status code was provided even though one was expected.
133
+ * 1006 CLOSE_ABNORMAL Reserved. Used to indicate that a connection was closed abnormally (that is, with no close frame being sent) when a status code is expected.
134
+ * 1007-1999 Reserved for future use by the WebSocket standard.
135
+ * 2000-2999 Reserved for use by WebSocket extensions.
136
+ * 3000-3999 Available for use by libraries and frameworks. May not be used by applications.
137
+ * 4000-4999 Available for use by applications.
138
+ *
139
+ * @param {String} reason A string indicating the reason the server closed the connection. This is specific to the particular server and sub-protocol.
140
+ * @param {Boolean} wasClean Indicates whether or not the connection was cleanly closed.
141
+ */
142
+ onClose(event, reason, wasClean) {
143
+ console.log('onClose', event, reason, wasClean);
144
+ }
145
+
146
+ /**
147
+ *
148
+ */
149
+ onError() {
150
+ console.log('onError', arguments);
151
+ }
152
+
153
+ /**
154
+ * @param {Object} event
155
+ */
156
+ onMessage(event) {
157
+ console.log('onMessage', event);
158
+ }
159
+
160
+ /**
161
+ *
162
+ */
163
+ onOpen() {
164
+ this.fire('open', {scope: this});
165
+ }
166
+
167
+ /**
168
+ * @param {Object} data
169
+ */
170
+ send(data) {
171
+ let me = this,
172
+ socket = me.socket,
173
+ d = data;
174
+
175
+ // CONNECTING 0 The connection is not yet open.
176
+ // OPEN 1 The connection is open and ready to communicate.
177
+ // CLOSING 2 The connection is in the process of closing.
178
+ // CLOSED 3 The connection is closed or couldn't be opened.
179
+
180
+ // If socket is not yet ready let's defer to open then resend
181
+ switch (socket.readyState) {
182
+ case WebSocket.CLOSED:
183
+ case WebSocket.CLOSING:
184
+ me.attemptReconnect(function() {
185
+ me.send(d);
186
+ });
187
+ break;
188
+ case WebSocket.CONNECTING:
189
+ me.on('open', function() {
190
+ me.send(d);
191
+ }, me, {single: true});
192
+ break;
193
+ case WebSocket.OPEN:
194
+ socket.send(data);
195
+ break;
196
+ }
197
+ }
20
198
  }
21
199
 
22
200
  Neo.applyClassConfig(Socket);
@@ -0,0 +1,46 @@
1
+ import Base from './Base.mjs';
2
+
3
+ /**
4
+ * @class Neo.manager.RpcApi
5
+ * @extends Neo.manager.Base
6
+ * @singleton
7
+ */
8
+ class RpcApi extends Base {
9
+ static getConfig() {return {
10
+ /**
11
+ * @member {String} className='Neo.manager.RpcApi'
12
+ * @protected
13
+ */
14
+ className: 'Neo.manager.RpcApi',
15
+ /**
16
+ * @member {Boolean} singleton=true
17
+ * @protected
18
+ */
19
+ singleton: true
20
+ }}
21
+
22
+ /**
23
+ * Registers each service & method combination into the collection
24
+ * @param api
25
+ */
26
+ registerApi(api) {
27
+ Object.entries(api.services).forEach(([service, serviceValue]) => {
28
+ Object.entries(serviceValue.methods).forEach(([method, methodValue]) => {
29
+ this.register({
30
+ id : `${service}.${method}`,
31
+ method,
32
+ service,
33
+ url: methodValue.url || serviceValue.url || api.url
34
+ })
35
+ })
36
+ })
37
+ }
38
+ }
39
+
40
+ Neo.applyClassConfig(RpcApi);
41
+
42
+ let instance = Neo.create(RpcApi);
43
+
44
+ Neo.applyToGlobalNs(instance);
45
+
46
+ export default instance;
@@ -0,0 +1,135 @@
1
+ import Base from './Base.mjs';
2
+ import NeoArray from '../util/Array.mjs';
3
+
4
+ /**
5
+ * @class Neo.manager.RpcMessage
6
+ * @extends Neo.manager.Base
7
+ * @singleton
8
+ */
9
+ class RpcMessage extends Base {
10
+ /**
11
+ * Stores the urls of endpoints for which a setTimeout() call is in progress
12
+ * @member {String[]} endPointTimeouts=[]
13
+ */
14
+ endPointTimeouts = []
15
+ /**
16
+ * internal incrementing flag
17
+ * @member {Number} messageId=1
18
+ * @protected
19
+ */
20
+ messageId = 1
21
+ /**
22
+ * Time window in ms for buffering incoming message requests
23
+ * @member {Number} requestBuffer=20
24
+ */
25
+ requestBuffer = 20
26
+ /**
27
+ * internal incrementing flag
28
+ * @member {Number} transactionId=1
29
+ * @protected
30
+ */
31
+ transactionId = 1
32
+
33
+ static getConfig() {return {
34
+ /**
35
+ * @member {String} className='Neo.manager.RpcMessage'
36
+ * @protected
37
+ */
38
+ className: 'Neo.manager.RpcMessage',
39
+ /**
40
+ * @member {Boolean} singleton=true
41
+ * @protected
42
+ */
43
+ singleton: true,
44
+ /**
45
+ * @member {Object[]} sorters
46
+ */
47
+ sorters: [{
48
+ direction: 'ASC',
49
+ property : 'id'
50
+ }]
51
+ }}
52
+
53
+ /**
54
+ *
55
+ * @param {Object} msg
56
+ * @returns {Promise<any>}
57
+ */
58
+ onMessage(msg) {
59
+ return new Promise((resolve, reject) => {
60
+ let me = this,
61
+ method = Neo.manager.RpcApi.get(`${msg.service}.${msg.method}`),
62
+ url = method.url;
63
+
64
+ me.register({
65
+ id : me.messageId,
66
+ method : msg.method,
67
+ params : msg.params,
68
+ reject,
69
+ resolve,
70
+ service : msg.service,
71
+ transactionId: 0,
72
+ url
73
+ });
74
+
75
+ me.messageId++;
76
+
77
+ if (!me.endPointTimeouts.includes(url)) {
78
+ me.endPointTimeouts.push(url);
79
+
80
+ setTimeout(() => {
81
+ me.resolveBufferTimeout(url);
82
+ }, me.requestBuffer)
83
+ }
84
+ });
85
+ }
86
+
87
+ /**
88
+ * @param {String} url
89
+ */
90
+ async resolveBufferTimeout(url) {
91
+ let me = this,
92
+ itemIds = [],
93
+ processItems = me.find({transactionId: 0, url}),
94
+ requests = [],
95
+ transactionId = me.transactionId,
96
+ response;
97
+
98
+ processItems.forEach(item => {
99
+ item.transactionId = transactionId;
100
+
101
+ itemIds.push(item.id);
102
+
103
+ requests.push({
104
+ id : item.id,
105
+ method : item.method,
106
+ params : item.params,
107
+ service: item.service
108
+ });
109
+ });
110
+
111
+ NeoArray.remove(me.endPointTimeouts, url);
112
+
113
+ me.transactionId++;
114
+
115
+ response = await Neo.Fetch.request(url, {}, 'post', JSON.stringify({tid: transactionId, requests}));
116
+
117
+ processItems.forEach(item => {
118
+ // todo: pass the item which is included inside the response object
119
+ // todo: reject the Promise in case the item is missing
120
+
121
+ item.resolve();
122
+ });
123
+
124
+ // todo: remove only the items which are included inside the response
125
+ me.remove(itemIds);
126
+ }
127
+ }
128
+
129
+ Neo.applyClassConfig(RpcMessage);
130
+
131
+ let instance = Neo.create(RpcMessage);
132
+
133
+ Neo.applyToGlobalNs(instance);
134
+
135
+ export default instance;
@@ -51,21 +51,25 @@ class Api extends Base {
51
51
 
52
52
  fetch(path)
53
53
  .then(response => response.json())
54
- .then(data => {this.register(data)})
54
+ .then(data => {
55
+ Neo.currentWorker.sendMessage('data', {action: 'registerApi', data});
56
+ this.register(data)
57
+ })
55
58
  }
56
59
 
57
60
  /**
58
- * @param {Object} data
61
+ * @param {Object} api
59
62
  */
60
- register(data) {
61
- let method, ns, service;
63
+ register(api) {
64
+ let ns;
62
65
 
63
- for (service of data.services) {
64
- for (method of service.methods) {
65
- ns = Neo.ns(`${data.namespace}.${service.name}`, true);
66
- ns[method.name] = this.generateRemote(service.name, method.name);
67
- }
68
- }
66
+ Object.entries(api.services).forEach(([service, serviceValue]) => {
67
+ ns = Neo.ns(`${api.namespace}.${service}`, true);
68
+
69
+ Object.entries(serviceValue.methods).forEach(([method, methodValue]) => {
70
+ ns[method] = this.generateRemote(service, method);
71
+ })
72
+ })
69
73
  }
70
74
  }
71
75
 
@@ -44,20 +44,20 @@ class NeoFunction extends Base {
44
44
  }
45
45
 
46
46
  /**
47
- * @param {Neo.core.Base} target
48
- * @param {String} methodName
49
- * @param {Function} fn
50
- * @param {Object} scope
47
+ * @param {Object} target
48
+ * @param {String} targetMethodName
49
+ * @param {Function} interceptFunction
50
+ * @param {Object} scope=target
51
+ * @param {*} preventedReturnValue=null The value to return in case the interceptFunction returns false
51
52
  * @returns {Function}
52
53
  */
53
- static intercept(target, methodName, fn, scope) {
54
- let method = target[methodName] || Neo.emptyFn;
55
-
56
- return (target[methodName] = function() {
57
- let returnValue = fn.apply(scope || this, arguments);
58
- method.apply(this, arguments);
54
+ static intercept(target, targetMethodName, interceptFunction, scope, preventedReturnValue=null) {
55
+ let targetMethod = target[targetMethodName];
59
56
 
60
- return returnValue;
57
+ return (target[targetMethodName] = function() {
58
+ return (interceptFunction.apply(scope || target, arguments) === false)
59
+ ? preventedReturnValue
60
+ : targetMethod.apply(target, arguments);
61
61
  });
62
62
  }
63
63
  }
@@ -15,10 +15,15 @@ import Xhr from '../Xhr.mjs';
15
15
  */
16
16
  class Data extends Base {
17
17
  /**
18
- * @member {Boolean} remotesManagerLoaded=false
18
+ * @member {Boolean} rpcApiManagerLoaded=false
19
19
  * @protected
20
20
  */
21
- remotesManagerLoaded = false
21
+ rpcApiManagerLoaded = false
22
+ /**
23
+ * @member {Boolean} rpcMessageManagerLoaded=false
24
+ * @protected
25
+ */
26
+ rpcMessageManagerLoaded = false
22
27
 
23
28
  static getConfig() {return {
24
29
  /**
@@ -60,14 +65,25 @@ class Data extends Base {
60
65
  console.log('worker.Data onLoad');
61
66
  }
62
67
 
68
+ /**
69
+ * @param {Object} msg
70
+ * @param {Object} msg.data the API content
71
+ */
72
+ onRegisterApi(msg) {
73
+ import('../manager/RpcApi.mjs').then(module => {
74
+ module.default.registerApi(msg.data);
75
+ this.rpcApiManagerLoaded = true
76
+ })
77
+ }
78
+
63
79
  /**
64
80
  * @param {Object} msg
65
81
  */
66
82
  onRegisterNeoConfig(msg) {
67
83
  super.onRegisterNeoConfig(msg);
68
84
 
69
- Neo.config.remotesApiUrl && import('../manager/RemotesApi.mjs').then(module => {
70
- this.remotesManagerLoaded = true
85
+ Neo.config.remotesApiUrl && import('../manager/RpcMessage.mjs').then(module => {
86
+ this.rpcMessageManagerLoaded = true
71
87
  })
72
88
  }
73
89
 
@@ -80,13 +96,13 @@ class Data extends Base {
80
96
  let me = this,
81
97
  response;
82
98
 
83
- if (!me.remotesManagerLoaded) {
99
+ if (!me.rpcMessageManagerLoaded) {
84
100
  // todo: we could store calls which arrive too early and pass them to the manager once it is ready
85
101
  console.warn('manager.RemotesApi not loaded yet', msg);
86
102
 
87
103
  me.reject(msg);
88
104
  } else {
89
- response = await Neo.manager.RemotesApi.onMessage(msg);
105
+ response = await Neo.manager.RpcMessage.onMessage(msg);
90
106
 
91
107
  me.resolve(msg, response);
92
108
  }
@@ -1,40 +0,0 @@
1
- import Base from './Base.mjs';
2
-
3
- /**
4
- * @class Neo.manager.RemotesApi
5
- * @extends Neo.manager.Base
6
- * @singleton
7
- */
8
- class RemotesApi extends Base {
9
- static getConfig() {return {
10
- /**
11
- * @member {String} className='Neo.manager.RemotesApi'
12
- * @protected
13
- */
14
- className: 'Neo.manager.RemotesApi',
15
- /**
16
- * @member {Boolean} singleton=true
17
- * @protected
18
- */
19
- singleton: true
20
- }}
21
-
22
- /**
23
- *
24
- * @param msg
25
- * @returns {Promise<any>}
26
- */
27
- async onMessage(msg) {
28
- let response = await Neo.Fetch.get(msg);
29
-
30
- return response;
31
- }
32
- }
33
-
34
- Neo.applyClassConfig(RemotesApi);
35
-
36
- let instance = Neo.create(RemotesApi);
37
-
38
- Neo.applyToGlobalNs(instance);
39
-
40
- export default instance;