@schukai/monster 3.2.0 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Copyright schukai GmbH and contributors 2022. All Rights Reserved.
3
+ * Node module: @schukai/monster
4
+ * This file is licensed under the AGPLv3 License.
5
+ * License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
6
+ */
7
+
8
+ import {Base} from "../../types/base.mjs";
9
+ import {validateObject, validateString} from "../../types/validate.mjs";
10
+
11
+ export {Message}
12
+
13
+ const dataSymbol = Symbol("@@data");
14
+
15
+ /**
16
+ * This class represents a WebSocket message.
17
+ */
18
+ class Message extends Base {
19
+
20
+ /**
21
+ * @param {Object} data
22
+ * @throws {TypeError} value is not a object
23
+ */
24
+ constructor(data) {
25
+ super();
26
+ this[dataSymbol] = validateObject(data);
27
+ }
28
+
29
+ /**
30
+ * Returns the raw message.
31
+ *
32
+ * @returns {object}
33
+ */
34
+ getData() {
35
+ return this[dataSymbol];
36
+ }
37
+
38
+ /**
39
+ * @returns {*}
40
+ */
41
+ toJSON() {
42
+ return this[dataSymbol];
43
+ }
44
+
45
+ /**
46
+ * @param {string} json
47
+ * @returns {Message}
48
+ * @throws {TypeError} value is not a string
49
+ */
50
+ static fromJSON(json) {
51
+ validateString(json);
52
+ return new Message(JSON.parse(json));
53
+ }
54
+
55
+ }
@@ -5,21 +5,25 @@
5
5
  * License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
6
6
  */
7
7
 
8
- import {internalSymbol, instanceSymbol} from "../../constants.mjs";
9
- import {isInteger, isString, isObject} from "../../types/is.mjs";
10
- import {Queue} from "../../types/queue.mjs";
11
- import {Datasource} from "../datasource.mjs";
12
- import {Pathfinder} from "../pathfinder.mjs";
13
- import {Pipe} from "../pipe.mjs";
8
+ import {instanceSymbol} from "../constants.mjs";
9
+ import {isInteger, isString, isObject} from "../types/is.mjs";
10
+ import {BaseWithOptions} from "../types/basewithoptions.mjs";
11
+ import {ObservableQueue} from "../types/observablequeue.mjs";
12
+ import {Message} from "./webconnect/message.mjs";
14
13
 
15
- export {WebSocketDatasource}
14
+
15
+ export {WebConnect}
16
16
 
17
17
  /**
18
18
  * @private
19
19
  * @type {Symbol}
20
20
  */
21
- const receiveQueueSymbol = Symbol("queue");
22
-
21
+ const receiveQueueSymbol = Symbol("receiveQueue");
22
+ /**
23
+ * @private
24
+ * @type {Symbol}
25
+ */
26
+ const sendQueueSymbol = Symbol("sendQueue");
23
27
 
24
28
  /**
25
29
  * @private
@@ -36,6 +40,7 @@ const connectionSymbol = Symbol("connection");
36
40
  const manualCloseSymbol = Symbol("manualClose");
37
41
 
38
42
  /**
43
+ * @private
39
44
  * @see https://www.rfc-editor.org/rfc/rfc6455.html#section-7.4.1
40
45
  * @type {{"1000": string, "1011": string, "1010": string, "1008": string, "1007": string, "1006": string, "1005": string, "1004": string, "1015": string, "1003": string, "1002": string, "1001": string, "1009": string}}
41
46
  */
@@ -57,13 +62,20 @@ const connectionStatusCode = {
57
62
 
58
63
  /**
59
64
  * @private
60
- * @this {WebSocketDatasource}
65
+ * @this {WebConnect}
61
66
  * @throws {Error} No url defined for websocket datasource.
62
67
  */
63
68
  function connectServer(resolve, reject) {
64
69
  const self = this;
65
70
 
71
+ const url = self.getOption('url');
72
+ if (!url) {
73
+ reject('No url defined for webconnect.');
74
+ return;
75
+ }
76
+
66
77
  let promiseAllredyResolved = false;
78
+
67
79
  let connectionTimeout = self.getOption('connection.timeout');
68
80
  if (!isInteger(connectionTimeout) || connectionTimeout < 100) {
69
81
  connectionTimeout = 5000;
@@ -90,20 +102,18 @@ function connectServer(resolve, reject) {
90
102
  self[connectionSymbol].socket.close();
91
103
  }
92
104
  self[connectionSymbol].socket = null;
93
-
94
- const url = self.getOption('url');
95
- if (!url) {
96
- reject('No url defined for websocket datasource.');
97
- return;
98
- }
99
-
100
105
  self[connectionSymbol].socket = new WebSocket(url);
101
106
 
102
107
  self[connectionSymbol].socket.onmessage = function (event) {
103
- self[receiveQueueSymbol].add(event);
104
- setTimeout(function () {
105
- self.read();
106
- }, 1);
108
+ if (event.data instanceof Blob) {
109
+ const reader = new FileReader();
110
+ reader.addEventListener("loadend", function () {
111
+ self[receiveQueueSymbol].add(new Message(reader.result))
112
+ });
113
+ reader.readAsText(new Message(event.data));
114
+ } else {
115
+ self[receiveQueueSymbol].add(Message.fromJSON(event.data));
116
+ }
107
117
  };
108
118
 
109
119
  self[connectionSymbol].socket.onopen = function () {
@@ -155,22 +165,22 @@ function connectServer(resolve, reject) {
155
165
  * @memberOf Monster.Data.Datasource
156
166
  * @summary The LocalStorage class encapsulates the access to data objects.
157
167
  */
158
- class WebSocketDatasource extends Datasource {
168
+ class WebConnect extends BaseWithOptions {
159
169
 
160
170
  /**
161
171
  *
162
172
  * @param {Object} [options] options contains definitions for the datasource.
163
173
  */
164
174
  constructor(options) {
165
- super();
166
175
 
167
176
  if (isString(options)) {
168
177
  options = {url: options};
169
178
  }
179
+
180
+ super(options);
170
181
 
171
- if (!isObject(options)) options = {};
172
- this.setOptions(options);
173
- this[receiveQueueSymbol] = new Queue();
182
+ this[receiveQueueSymbol] = new ObservableQueue();
183
+ this[sendQueueSymbol] = new ObservableQueue();
174
184
 
175
185
  this[connectionSymbol] = {};
176
186
  this[connectionSymbol].socket = null;
@@ -194,7 +204,7 @@ class WebSocketDatasource extends Datasource {
194
204
  * @returns {boolean}
195
205
  */
196
206
  isConnected() {
197
- return this[connectionSymbol]?.socket?.readyState === 1;
207
+ return this[connectionSymbol]?.socket?.readyState === 1;
198
208
  }
199
209
 
200
210
  /**
@@ -202,7 +212,7 @@ class WebSocketDatasource extends Datasource {
202
212
  * @returns {symbol}
203
213
  */
204
214
  static get [instanceSymbol]() {
205
- return Symbol.for("@schukai/monster/data/datasource/websocket");
215
+ return Symbol.for("@schukai/monster/net/webconnect");
206
216
  }
207
217
 
208
218
  /**
@@ -212,42 +222,10 @@ class WebSocketDatasource extends Datasource {
212
222
  * @property {Number} connection.reconnect.timeout The timeout in milliseconds for the reconnect.
213
223
  * @property {Number} connection.reconnect.attempts The maximum number of reconnects.
214
224
  * @property {Bool} connection.reconnect.enabled If the reconnect is enabled.
215
- * @property {Object} write={} Options
216
- * @property {Object} write.mapping the mapping is applied before writing.
217
- * @property {String} write.mapping.transformer Transformer to select the appropriate entries
218
- * @property {Monster.Data.Datasource~exampleCallback[]} write.mapping.callback with the help of the callback, the structures can be adjusted before writing.
219
- * @property {Object} write.report
220
- * @property {String} write.report.path Path to validations
221
- * @property {Object} write.sheathing
222
- * @property {Object} write.sheathing.object Object to be wrapped
223
- * @property {string} write.sheathing.path Path to the data
224
- * @property {Object} read={} Options
225
- * @property {Object} read.mapping the mapping is applied after reading.
226
- * @property {String} read.mapping.transformer Transformer to select the appropriate entries
227
- * @property {Monster.Data.Datasource~exampleCallback[]} read.mapping.callback with the help of the callback, the structures can be adjusted after reading.
228
225
  */
229
226
  get defaults() {
230
227
  return Object.assign({}, super.defaults, {
231
228
  url: undefined,
232
- write: {
233
- mapping: {
234
- transformer: undefined,
235
- callbacks: []
236
- },
237
- report: {
238
- path: undefined
239
- },
240
- sheathing: {
241
- object: undefined,
242
- path: undefined,
243
- },
244
- },
245
- read: {
246
- mapping: {
247
- transformer: undefined,
248
- callbacks: []
249
- },
250
- },
251
229
  connection: {
252
230
  timeout: 5000,
253
231
  reconnect: {
@@ -262,124 +240,107 @@ class WebSocketDatasource extends Datasource {
262
240
  /**
263
241
  * This method closes the connection.
264
242
  *
243
+ * @param {Number} [code=1000] The close code.
244
+ * @param {String} [reason=""] The close reason.
265
245
  * @returns {Promise}
246
+ * @see https://www.rfc-editor.org/rfc/rfc6455.html#section-7.4.1
266
247
  */
267
- close() {
268
- this[manualCloseSymbol] = true;
269
- if (this[connectionSymbol].socket) {
270
- this[connectionSymbol].socket.close();
248
+ close(statusCode, reason ) {
249
+ if (!isInteger(statusCode) || statusCode < 1000 || statusCode > 4999) {
250
+ statusCode = 1000;
271
251
  }
272
- return this;
273
- }
274
-
275
- /**
276
- * @return {Promise}
277
- */
278
- read() {
279
- const self = this;
280
- let response;
281
-
282
- if (self[connectionSymbol]?.socket?.readyState !== 1) {
283
- return Promise.reject('The connection is not established.');
252
+ if (!isString(reason)) {
253
+ reason = '';
284
254
  }
285
255
 
286
256
  return new Promise((resolve, reject) => {
287
- if (self[receiveQueueSymbol].isEmpty()) {
288
- resolve();
257
+ try {
258
+ this[manualCloseSymbol] = true;
259
+ if (this[connectionSymbol].socket) {
260
+ this[connectionSymbol].socket.close(statusCode, reason);
261
+ }
262
+ } catch (error) {
263
+ reject(error);
289
264
  }
265
+ resolve();
266
+ });
290
267
 
291
- while (!self[receiveQueueSymbol].isEmpty()) {
292
-
293
- const event = self[receiveQueueSymbol].poll();
294
- const body = event?.data;
295
- if (!body) continue;
296
-
297
- let obj;
298
- try {
299
- obj = JSON.parse(body);
300
- } catch (e) {
301
-
302
- let msg = 'the response does not contain a valid json (actual: ';
303
-
304
- if (body.length > 100) {
305
- msg += body.substring(0, 97) + '...';
306
- } else {
307
- msg += body;
308
- }
268
+ }
309
269
 
310
- msg += "; " + e.message + ')';
270
+ /**
271
+ * Polls the receive queue for new messages.
272
+ *
273
+ * @returns {Message}
274
+ */
275
+ poll() {
276
+ return this[receiveQueueSymbol].poll();
277
+ }
311
278
 
312
- reject(msg);
313
- }
279
+ /**
280
+ * Are there any messages in the receive queue?
281
+ *
282
+ * @returns {boolean}
283
+ */
284
+ dataReceived() {
285
+ return !this[receiveQueueSymbol].isEmpty();
286
+ }
314
287
 
315
- let transformation = self.getOption('read.mapping.transformer');
316
- if (transformation !== undefined) {
317
- const pipe = new Pipe(transformation);
288
+ /**
289
+ * Get Message from the receive queue, but do not remove it.
290
+ *
291
+ * @returns {Object}
292
+ */
293
+ peek() {
294
+ return this[receiveQueueSymbol].peek();
295
+ }
318
296
 
319
- for (const callback of self.getOption('read.mapping.callbacks')) {
320
- pipe.setCallback(callback.constructor.name, callback);
321
- }
297
+ /**
298
+ * Attach a new observer
299
+ *
300
+ * @param {Observer} observer
301
+ * @returns {ProxyObserver}
302
+ */
303
+ attachObserver(observer) {
304
+ this[receiveQueueSymbol].attachObserver(observer);
305
+ return this;
306
+ }
322
307
 
323
- obj = pipe.run(obj);
324
- }
308
+ /**
309
+ * Detach a observer
310
+ *
311
+ * @param {Observer} observer
312
+ * @returns {ProxyObserver}
313
+ */
314
+ detachObserver(observer) {
315
+ this[receiveQueueSymbol].detachObserver(observer);
316
+ return this;
317
+ }
325
318
 
326
- self.set(obj);
327
- return response;
328
- }
329
- })
319
+ /**
320
+ * @param {Observer} observer
321
+ * @returns {boolean}
322
+ */
323
+ containsObserver(observer) {
324
+ return this[receiveQueueSymbol].containsObserver(observer);
330
325
  }
331
326
 
332
327
  /**
328
+ * @param {Message|Object} message
333
329
  * @return {Promise}
334
330
  */
335
- write() {
331
+ send(message) {
336
332
  const self = this;
337
333
 
338
- if (self[connectionSymbol]?.socket?.readyState !== 1) {
339
- return Promise.reject('The connection is not established.');
340
- }
341
-
342
- let obj = self.get();
343
- let transformation = self.getOption('write.mapping.transformer');
344
- if (transformation !== undefined) {
345
- const pipe = new Pipe(transformation);
346
-
347
- for (const callback of self.getOption('write.mapping.callbacks')) {
348
- pipe.setCallback(callback.constructor.name, callback);
349
- }
350
-
351
- obj = pipe.run(obj);
352
- }
353
-
354
- let sheathingObject = self.getOption('write.sheathing.object');
355
- let sheathingPath = self.getOption('write.sheathing.path');
356
- let reportPath = self.getOption('write.report.path');
357
-
358
- if (sheathingObject && sheathingPath) {
359
- const sub = obj;
360
- obj = sheathingObject;
361
- (new Pathfinder(obj)).setVia(sheathingPath, sub);
362
- }
363
-
364
334
  return new Promise((resolve, reject) => {
365
335
 
366
336
  if (self[connectionSymbol].socket.readyState !== 1) {
367
337
  reject('the socket is not ready');
368
338
  }
369
339
 
370
- self[connectionSymbol].socket.send(JSON.stringify(obj))
340
+ self[connectionSymbol].socket.send(JSON.stringify(message))
371
341
  resolve();
372
342
  });
373
343
  }
374
344
 
375
-
376
- /**
377
- * @return {RestAPI}
378
- */
379
- getClone() {
380
- const self = this;
381
- return new WebSocketDatasource(self[internalSymbol].getRealSubject()['options']);
382
- }
383
-
384
345
  }
385
346
 
@@ -84,7 +84,7 @@ function getGlobal() {
84
84
  *
85
85
  * ```
86
86
  * <script type="module">
87
- * import {Monster} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@1.30.0/dist/monster.mjs';
87
+ * import {Monster} from '@schukai/monster/source/monster.mjs';
88
88
  * Monster.Types.getGlobalObject('document')
89
89
  * // ↦ { }
90
90
  * </script>
@@ -124,7 +124,7 @@ function getGlobalObject(name) {
124
124
  *
125
125
  * ```
126
126
  * <script type="module">
127
- * import {Monster} from 'https://cdn.jsdelivr.net/npm/@schukai/monster@1.30.0/dist/monster.mjs';
127
+ * import {Monster} from '@schukai/monster/source/monster.mjs';
128
128
  * console.log(Monster.Types.getGlobalFunction('parseInt')) // ↦ f parseInt() { }
129
129
  * </script>
130
130
  * ```
@@ -134,7 +134,7 @@ class MediaType extends Base {
134
134
  *
135
135
  * ```
136
136
  * <script type="module">
137
- * import {Monster} from '@schukai/monster/source//monster.mjs';
137
+ * import {Monster} from '@schukai/monster/source/monster.mjs';
138
138
  * console.log(Monster.Types.parseMediaType())
139
139
  * </script>
140
140
  * ```
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Copyright schukai GmbH and contributors 2022. All Rights Reserved.
3
+ * Node module: @schukai/monster
4
+ * This file is licensed under the AGPLv3 License.
5
+ * License text available at https://www.gnu.org/licenses/agpl-3.0.en.html
6
+ */
7
+
8
+ import {Queue} from './queue.mjs';
9
+ import {instanceSymbol, internalSymbol} from '../constants.mjs';
10
+ import {ObserverList} from "./observerlist.mjs";
11
+
12
+ export {ObservableQueue};
13
+
14
+ /**
15
+ * An observable queue is a list of items that are processed one after another (first in, first out).
16
+ *
17
+ * `Queue.add()` and `Queue.clear()` notify all observers.
18
+ *
19
+ * @externalExample ../../example/types/queue.mjs
20
+ * @license AGPLv3
21
+ * @since 3.3.0
22
+ * @copyright schukai GmbH
23
+ * @memberOf Monster.Types
24
+ * @summary An observable Queue (Fifo)
25
+ */
26
+ class ObservableQueue extends Queue {
27
+
28
+ /**
29
+ *
30
+ */
31
+ constructor() {
32
+ super();
33
+ this[internalSymbol]= {
34
+ observers: new ObserverList()
35
+ };
36
+ }
37
+
38
+ /**
39
+ * This method is called by the `instanceof` operator.
40
+ * @returns {symbol}
41
+ * @since 2.1.0
42
+ */
43
+ static get [instanceSymbol]() {
44
+ return Symbol.for("@schukai/monster/types/observablequeue");
45
+ }
46
+
47
+ /**
48
+ * Add a new element to the end of the queue.
49
+ *
50
+ * @param {*} value
51
+ * @returns {Queue}
52
+ */
53
+ add(value) {
54
+ super.add(value);
55
+ this.notifyObservers();
56
+ return this;
57
+ }
58
+
59
+ /**
60
+ * remove all entries
61
+ *
62
+ * @returns {Queue}
63
+ */
64
+ clear() {
65
+ super.clear();
66
+ this.notifyObservers();
67
+ return this;
68
+ }
69
+
70
+ /**
71
+ * Attach a new observer
72
+ *
73
+ * @param {Observer} observer
74
+ * @returns {ProxyObserver}
75
+ */
76
+ attachObserver(observer) {
77
+ this[internalSymbol].observers.attach(observer)
78
+ return this;
79
+ }
80
+
81
+ /**
82
+ * Detach a observer
83
+ *
84
+ * @param {Observer} observer
85
+ * @returns {ProxyObserver}
86
+ */
87
+ detachObserver(observer) {
88
+ this[internalSymbol].observers.detach(observer)
89
+ return this;
90
+ }
91
+
92
+ /**
93
+ * Notify all observer
94
+ *
95
+ * @returns {Promise}
96
+ */
97
+ notifyObservers() {
98
+ return this[internalSymbol].observers.notify(this);
99
+ }
100
+
101
+ /**
102
+ * @param {Observer} observer
103
+ * @returns {boolean}
104
+ */
105
+ containsObserver(observer) {
106
+ return this[internalSymbol].observers.contains(observer)
107
+ }
108
+
109
+
110
+ }
@@ -61,9 +61,9 @@ export {ProxyObserver}
61
61
 
62
62
 
63
63
  /**
64
- * get the real object
64
+ * Get the real object
65
65
  *
66
- * changes to this object are not noticed by the observers, so you can make a large number of changes and inform the observers later.
66
+ * Changes to this object are not noticed by the observers, so you can make a large number of changes and inform the observers later.
67
67
  *
68
68
  * @returns {object}
69
69
  */
@@ -6,12 +6,13 @@
6
6
  */
7
7
 
8
8
  import {Queue} from "./queue.mjs";
9
+ import {internalSymbol} from "../constants.mjs";
9
10
  import {validateObject} from "./validate.mjs";
10
11
 
11
12
  export {UniqueQueue}
12
13
 
13
14
  /**
14
- * A UniqueQueue is a queue that contains items only once.
15
+ * An UniqueQueue is a queue that contains items only once.
15
16
  *
16
17
  * @license AGPLv3
17
18
  * @since 1.4.0
@@ -26,7 +27,9 @@ export {UniqueQueue}
26
27
  */
27
28
  constructor() {
28
29
  super();
29
- this.unique = new WeakSet();
30
+ this[internalSymbol]={
31
+ unique : new WeakSet()
32
+ };
30
33
  }
31
34
 
32
35
  /**
@@ -40,8 +43,8 @@ export {UniqueQueue}
40
43
 
41
44
  validateObject(value);
42
45
 
43
- if (!this.unique.has(value)) {
44
- this.unique.add(value);
46
+ if (!this[internalSymbol].unique.has(value)) {
47
+ this[internalSymbol].unique.add(value);
45
48
  super.add(value);
46
49
  }
47
50
 
@@ -55,7 +58,7 @@ export {UniqueQueue}
55
58
  */
56
59
  clear() {
57
60
  super.clear();
58
- this.unique = new WeakSet;
61
+ this[internalSymbol].unique = new WeakSet;
59
62
  return this;
60
63
  }
61
64
 
@@ -71,7 +74,7 @@ export {UniqueQueue}
71
74
  return undefined;
72
75
  }
73
76
  let value = this.data.shift();
74
- this.unique.delete(value);
77
+ this[internalSymbol].unique.delete(value);
75
78
  return value;
76
79
  }
77
80
 
@@ -149,7 +149,7 @@ function getMonsterVersion() {
149
149
  }
150
150
 
151
151
  /** don't touch, replaced by make with package.json version */
152
- monsterVersion = new Version('3.2.0')
152
+ monsterVersion = new Version('3.4.0')
153
153
 
154
154
  return monsterVersion;
155
155