@schukai/monster 3.2.0 → 3.4.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.
@@ -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