@schukai/monster 3.0.0 → 3.1.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/package.json +1 -1
- package/source/data/datasource/restapi.mjs +21 -13
- package/source/data/datasource/websocket.mjs +337 -0
- package/source/data/datasource.mjs +12 -0
- package/source/dom/customelement.mjs +1 -2
- package/source/types/queue.mjs +9 -2
- package/source/types/version.mjs +1 -1
- package/test/cases/data/datasource/websocket.mjs +96 -0
- package/test/cases/monster.mjs +1 -1
- package/test/cases/util/deadmansswitch.mjs +3 -3
- package/test/util/websocket.mjs +22 -0
- package/test/web/import.js +1 -0
- package/test/web/test.html +2 -2
- package/test/web/tests.js +300 -16
package/package.json
CHANGED
@@ -22,7 +22,7 @@ export {RestAPI}
|
|
22
22
|
* @since 1.22.0
|
23
23
|
* @copyright schukai GmbH
|
24
24
|
* @memberOf Monster.Data.Datasource
|
25
|
-
* @summary The
|
25
|
+
* @summary The RestAPI is a class that binds a REST API server.
|
26
26
|
*/
|
27
27
|
class RestAPI extends Datasource {
|
28
28
|
|
@@ -30,7 +30,6 @@ class RestAPI extends Datasource {
|
|
30
30
|
*
|
31
31
|
* @param {Object} [readDefinition] An options object containing any custom settings that you want to apply to the read request.
|
32
32
|
* @param {Object} [writeDefinition] An options object containing any custom settings that you want to apply to the write request.
|
33
|
-
* @throws {TypeError} value is not a string
|
34
33
|
*/
|
35
34
|
constructor(readDefinition, writeDefinition) {
|
36
35
|
super();
|
@@ -54,17 +53,21 @@ class RestAPI extends Datasource {
|
|
54
53
|
}
|
55
54
|
|
56
55
|
/**
|
57
|
-
* @property {string} url=undefined Defines the resource that you wish to fetch.
|
58
56
|
* @property {Object} write={} Options
|
59
57
|
* @property {Object} write.init={} An options object containing any custom settings that you want to apply to the request. The parameters are identical to those of the {@link https://developer.mozilla.org/en-US/docs/Web/API/Request/Request|Request constructor}
|
60
58
|
* @property {string} write.init.method=POST
|
59
|
+
* @property {Object} write.init.headers Object containing any custom headers that you want to apply to the request.
|
61
60
|
* @property {string} write.acceptedStatus=[200,201]
|
62
61
|
* @property {string} write.url URL
|
63
62
|
* @property {Object} write.mapping the mapping is applied before writing.
|
64
63
|
* @property {String} write.mapping.transformer Transformer to select the appropriate entries
|
64
|
+
* @property {Monster.Data.Datasource~exampleCallback[]} write.mapping.callback with the help of the callback, the structures can be adjusted before writing.
|
65
65
|
* @property {Object} write.report
|
66
66
|
* @property {String} write.report.path Path to validations
|
67
|
-
* @property {
|
67
|
+
* @property {Object} write.sheathing
|
68
|
+
* @property {Object} write.sheathing.object Object to be wrapped
|
69
|
+
* @property {string} write.sheathing.path Path to the data
|
70
|
+
* @property {Object} read={} Options
|
68
71
|
* @property {Object} read.init={} An options object containing any custom settings that you want to apply to the request. The parameters are identical to those of the {@link https://developer.mozilla.org/en-US/docs/Web/API/Request/Request|Request constructor}
|
69
72
|
* @property {string} read.init.method=GET
|
70
73
|
* @property {string} read.acceptedStatus=[200]
|
@@ -85,6 +88,10 @@ class RestAPI extends Datasource {
|
|
85
88
|
transformer: undefined,
|
86
89
|
callbacks: []
|
87
90
|
},
|
91
|
+
sheathing: {
|
92
|
+
object: undefined,
|
93
|
+
path: undefined,
|
94
|
+
},
|
88
95
|
report: {
|
89
96
|
path: undefined
|
90
97
|
}
|
@@ -146,6 +153,11 @@ class RestAPI extends Datasource {
|
|
146
153
|
let transformation = self.getOption('read.mapping.transformer');
|
147
154
|
if (transformation !== undefined) {
|
148
155
|
const pipe = new Pipe(transformation);
|
156
|
+
|
157
|
+
for (const callback of self.getOption('read.mapping.callbacks')) {
|
158
|
+
pipe.setCallback(callback.constructor.name, callback);
|
159
|
+
}
|
160
|
+
|
149
161
|
obj = pipe.run(obj);
|
150
162
|
}
|
151
163
|
|
@@ -174,6 +186,11 @@ class RestAPI extends Datasource {
|
|
174
186
|
let transformation = self.getOption('write.mapping.transformer');
|
175
187
|
if (transformation !== undefined) {
|
176
188
|
const pipe = new Pipe(transformation);
|
189
|
+
|
190
|
+
for (const callback of self.getOption('write.mapping.callbacks')) {
|
191
|
+
pipe.setCallback(callback.constructor.name, callback);
|
192
|
+
}
|
193
|
+
|
177
194
|
obj = pipe.run(obj);
|
178
195
|
}
|
179
196
|
|
@@ -234,12 +251,3 @@ class RestAPI extends Datasource {
|
|
234
251
|
}
|
235
252
|
|
236
253
|
|
237
|
-
/**
|
238
|
-
* This callback can be passed to a datasource and is used to adapt data structures.
|
239
|
-
*
|
240
|
-
* @callback Monster.Data.Datasource~exampleCallback
|
241
|
-
* @param {*} value Value
|
242
|
-
* @param {string} key Key
|
243
|
-
* @memberOf Monster.Data
|
244
|
-
* @see Monster.Data.Datasource
|
245
|
-
*/
|
@@ -0,0 +1,337 @@
|
|
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 {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";
|
14
|
+
|
15
|
+
export {WebSocketDatasource}
|
16
|
+
|
17
|
+
/**
|
18
|
+
* @private
|
19
|
+
* @type {symbol}
|
20
|
+
*/
|
21
|
+
const receiveQueueSymbol = Symbol("queue");
|
22
|
+
|
23
|
+
|
24
|
+
/**
|
25
|
+
* @private
|
26
|
+
* @type {symbol}
|
27
|
+
*
|
28
|
+
* hint: this name is used in the tests. if you want to change it, please change it in the tests as well.
|
29
|
+
*/
|
30
|
+
const connectionSymbol = Symbol("connection");
|
31
|
+
|
32
|
+
/**
|
33
|
+
* @private
|
34
|
+
* @type {symbol}
|
35
|
+
*/
|
36
|
+
const manualCloseSymbol = Symbol("manualClose");
|
37
|
+
|
38
|
+
/**
|
39
|
+
* @see https://www.rfc-editor.org/rfc/rfc6455.html#section-7.4.1
|
40
|
+
* @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
|
+
*/
|
42
|
+
const connectionStatusCode = {
|
43
|
+
1000: "Normal closure",
|
44
|
+
1001: "Going away",
|
45
|
+
1002: "Protocol error",
|
46
|
+
1003: "Unsupported data",
|
47
|
+
1004: "Reserved",
|
48
|
+
1005: "No status code",
|
49
|
+
1006: "Connection closed abnormally",
|
50
|
+
1007: "Invalid frame payload data",
|
51
|
+
1008: "Policy violation",
|
52
|
+
1009: "Message too big",
|
53
|
+
1010: "Mandatory extension",
|
54
|
+
1011: "Internal server error",
|
55
|
+
1015: "TLS handshake"
|
56
|
+
};
|
57
|
+
|
58
|
+
/**
|
59
|
+
* The RestAPI is a class that enables a REST API server.
|
60
|
+
*
|
61
|
+
* @externalExample ../../../example/data/storage/restapi.mjs
|
62
|
+
* @license AGPLv3
|
63
|
+
* @since 3.1.0
|
64
|
+
* @copyright schukai GmbH
|
65
|
+
* @memberOf Monster.Data.Datasource
|
66
|
+
* @summary The LocalStorage class encapsulates the access to data objects.
|
67
|
+
*/
|
68
|
+
class WebSocketDatasource extends Datasource {
|
69
|
+
|
70
|
+
/**
|
71
|
+
*
|
72
|
+
* @param {Object} [options] options contains definitions for the datasource.
|
73
|
+
*/
|
74
|
+
constructor(options) {
|
75
|
+
super();
|
76
|
+
|
77
|
+
if (isString(options)) {
|
78
|
+
options = {url: options};
|
79
|
+
}
|
80
|
+
|
81
|
+
if (!isObject(options)) options = {};
|
82
|
+
this.setOptions(options);
|
83
|
+
this[receiveQueueSymbol] = new Queue();
|
84
|
+
|
85
|
+
this[connectionSymbol] = {};
|
86
|
+
this[connectionSymbol].socket = null;
|
87
|
+
this[connectionSymbol].reconnectCounter = 0;
|
88
|
+
this[manualCloseSymbol]=false;
|
89
|
+
}
|
90
|
+
|
91
|
+
/**
|
92
|
+
*
|
93
|
+
* @returns {Websocketdatasource}
|
94
|
+
* @throws {Error} No url defined for websocket datasource.
|
95
|
+
*/
|
96
|
+
connect() {
|
97
|
+
const self = this;
|
98
|
+
|
99
|
+
let connected = false;
|
100
|
+
let reconnectTimeout = self.getOption('reconnect.timeout');
|
101
|
+
if (!isInteger(reconnectTimeout) || reconnectTimeout < 1000) reconnectTimeout = 1000;
|
102
|
+
let reconnectAttempts = self.getOption('reconnect.attempts');
|
103
|
+
if (!isInteger(reconnectAttempts) || reconnectAttempts < 1) reconnectAttempts = 1;
|
104
|
+
let reconnectEnabled = self.getOption('reconnect.enabled');
|
105
|
+
if (reconnectEnabled !== true) reconnectEnabled = false;
|
106
|
+
|
107
|
+
self[manualCloseSymbol] = false;
|
108
|
+
self[connectionSymbol].reconnectCounter++;
|
109
|
+
|
110
|
+
if (self[connectionSymbol].socket && self[connectionSymbol].socket.readyState < 2) {
|
111
|
+
self[connectionSymbol].socket.close();
|
112
|
+
}
|
113
|
+
self[connectionSymbol].socket = null;
|
114
|
+
|
115
|
+
const url = self.getOption('url');
|
116
|
+
if (!url) throw new Error('No url defined for websocket datasource.');
|
117
|
+
|
118
|
+
self[connectionSymbol].socket = new WebSocket(url);
|
119
|
+
|
120
|
+
self[connectionSymbol].socket.onmessage = function (event) {
|
121
|
+
self[receiveQueueSymbol].add(event);
|
122
|
+
setTimeout(function () {
|
123
|
+
self.read();
|
124
|
+
}, 0);
|
125
|
+
};
|
126
|
+
|
127
|
+
self[connectionSymbol].socket.onopen = function () {
|
128
|
+
connected = true;
|
129
|
+
self[connectionSymbol].reconnectCounter = 0;
|
130
|
+
};
|
131
|
+
|
132
|
+
self[connectionSymbol].socket.close = function (event) {
|
133
|
+
|
134
|
+
if (self[manualCloseSymbol]) {
|
135
|
+
self[manualCloseSymbol] = false;
|
136
|
+
return;
|
137
|
+
}
|
138
|
+
|
139
|
+
if (reconnectEnabled && this[connectionSymbol].reconnectCounter < reconnectAttempts) {
|
140
|
+
setTimeout(() => {
|
141
|
+
self.connect();
|
142
|
+
}, reconnectTimeout * this[connectionSymbol].reconnectCounter);
|
143
|
+
}
|
144
|
+
|
145
|
+
};
|
146
|
+
|
147
|
+
self[connectionSymbol].socket.onerror = (error) => {
|
148
|
+
|
149
|
+
if (reconnectEnabled && self[connectionSymbol].reconnectCounter < reconnectAttempts) {
|
150
|
+
setTimeout(() => {
|
151
|
+
self.connect();
|
152
|
+
}, reconnectTimeout * this[connectionSymbol].reconnectCounter);
|
153
|
+
}
|
154
|
+
|
155
|
+
};
|
156
|
+
}
|
157
|
+
|
158
|
+
isConnected() {
|
159
|
+
return this[connectionSymbol].socket && this[connectionSymbol].socket.readyState === 1;
|
160
|
+
}
|
161
|
+
|
162
|
+
/**
|
163
|
+
* This method is called by the `instanceof` operator.
|
164
|
+
* @returns {symbol}
|
165
|
+
*/
|
166
|
+
static get [instanceSymbol]() {
|
167
|
+
return Symbol.for("@schukai/monster/data/datasource/websocket");
|
168
|
+
}
|
169
|
+
|
170
|
+
/**
|
171
|
+
* @property {string} url=undefined Defines the resource that you wish to fetch.
|
172
|
+
* @property {Number} reconnect.timeout The timeout in milliseconds for the reconnect.
|
173
|
+
* @property {Number} reconnect.attempts The maximum number of reconnects.
|
174
|
+
* @property {Bool} reconnect.enabled If the reconnect is enabled.
|
175
|
+
* @property {Object} write={} Options
|
176
|
+
* @property {Object} write.mapping the mapping is applied before writing.
|
177
|
+
* @property {String} write.mapping.transformer Transformer to select the appropriate entries
|
178
|
+
* @property {Monster.Data.Datasource~exampleCallback[]} write.mapping.callback with the help of the callback, the structures can be adjusted before writing.
|
179
|
+
* @property {Object} write.report
|
180
|
+
* @property {String} write.report.path Path to validations
|
181
|
+
* @property {Object} write.sheathing
|
182
|
+
* @property {Object} write.sheathing.object Object to be wrapped
|
183
|
+
* @property {string} write.sheathing.path Path to the data
|
184
|
+
* @property {Object} read={} Options
|
185
|
+
* @property {Object} read.mapping the mapping is applied after reading.
|
186
|
+
* @property {String} read.mapping.transformer Transformer to select the appropriate entries
|
187
|
+
* @property {Monster.Data.Datasource~exampleCallback[]} read.mapping.callback with the help of the callback, the structures can be adjusted after reading.
|
188
|
+
*/
|
189
|
+
get defaults() {
|
190
|
+
return Object.assign({}, super.defaults, {
|
191
|
+
url: undefined,
|
192
|
+
write: {
|
193
|
+
mapping: {
|
194
|
+
transformer: undefined,
|
195
|
+
callbacks: []
|
196
|
+
},
|
197
|
+
report: {
|
198
|
+
path: undefined
|
199
|
+
},
|
200
|
+
sheathing: {
|
201
|
+
object: undefined,
|
202
|
+
path: undefined,
|
203
|
+
},
|
204
|
+
},
|
205
|
+
read: {
|
206
|
+
mapping: {
|
207
|
+
transformer: undefined,
|
208
|
+
callbacks: []
|
209
|
+
},
|
210
|
+
},
|
211
|
+
reconnect: {
|
212
|
+
timeout: 1000,
|
213
|
+
attempts: 10,
|
214
|
+
enabled: true
|
215
|
+
}
|
216
|
+
});
|
217
|
+
}
|
218
|
+
|
219
|
+
/**
|
220
|
+
* This method closes the connection.
|
221
|
+
*
|
222
|
+
* @returns {Promise}
|
223
|
+
*/
|
224
|
+
close() {
|
225
|
+
this[manualCloseSymbol]=true;
|
226
|
+
if (this[connectionSymbol].socket) {
|
227
|
+
this[connectionSymbol].socket.close();
|
228
|
+
}
|
229
|
+
return this;
|
230
|
+
}
|
231
|
+
|
232
|
+
/**
|
233
|
+
* @return {Promise}
|
234
|
+
* @throws {Error} the options does not contain a valid json definition
|
235
|
+
* @throws {Error} the data cannot be read
|
236
|
+
* @throws {TypeError} value is not an object
|
237
|
+
*/
|
238
|
+
read() {
|
239
|
+
const self = this;
|
240
|
+
let response;
|
241
|
+
|
242
|
+
return new Promise((resolve, reject) => {
|
243
|
+
if (self[receiveQueueSymbol].isEmpty()) {
|
244
|
+
resolve();
|
245
|
+
}
|
246
|
+
|
247
|
+
while (!self[receiveQueueSymbol].isEmpty()) {
|
248
|
+
|
249
|
+
const event = self[receiveQueueSymbol].poll();
|
250
|
+
const body = event?.data;
|
251
|
+
if (!body) continue;
|
252
|
+
|
253
|
+
let obj;
|
254
|
+
try {
|
255
|
+
obj = JSON.parse(body);
|
256
|
+
} catch (e) {
|
257
|
+
|
258
|
+
let msg = 'the response does not contain a valid json (actual: ';
|
259
|
+
|
260
|
+
if (body.length > 100) {
|
261
|
+
msg += body.substring(0, 97) + '...';
|
262
|
+
} else {
|
263
|
+
msg += body;
|
264
|
+
}
|
265
|
+
|
266
|
+
msg += "; " + e.message + ')';
|
267
|
+
|
268
|
+
reject(msg);
|
269
|
+
}
|
270
|
+
|
271
|
+
let transformation = self.getOption('read.mapping.transformer');
|
272
|
+
if (transformation !== undefined) {
|
273
|
+
const pipe = new Pipe(transformation);
|
274
|
+
|
275
|
+
for (const callback of self.getOption('read.mapping.callbacks')) {
|
276
|
+
pipe.setCallback(callback.constructor.name, callback);
|
277
|
+
}
|
278
|
+
|
279
|
+
obj = pipe.run(obj);
|
280
|
+
}
|
281
|
+
|
282
|
+
self.set(obj);
|
283
|
+
return response;
|
284
|
+
}
|
285
|
+
})
|
286
|
+
}
|
287
|
+
|
288
|
+
/**
|
289
|
+
* @return {Promise}
|
290
|
+
*/
|
291
|
+
write() {
|
292
|
+
const self = this;
|
293
|
+
|
294
|
+
let obj = self.get();
|
295
|
+
let transformation = self.getOption('write.mapping.transformer');
|
296
|
+
if (transformation !== undefined) {
|
297
|
+
const pipe = new Pipe(transformation);
|
298
|
+
|
299
|
+
for (const callback of self.getOption('write.mapping.callbacks')) {
|
300
|
+
pipe.setCallback(callback.constructor.name, callback);
|
301
|
+
}
|
302
|
+
|
303
|
+
obj = pipe.run(obj);
|
304
|
+
}
|
305
|
+
|
306
|
+
let sheathingObject = self.getOption('write.sheathing.object');
|
307
|
+
let sheathingPath = self.getOption('write.sheathing.path');
|
308
|
+
let reportPath = self.getOption('write.report.path');
|
309
|
+
|
310
|
+
if (sheathingObject && sheathingPath) {
|
311
|
+
const sub = obj;
|
312
|
+
obj = sheathingObject;
|
313
|
+
(new Pathfinder(obj)).setVia(sheathingPath, sub);
|
314
|
+
}
|
315
|
+
|
316
|
+
return new Promise((resolve, reject) => {
|
317
|
+
|
318
|
+
if (self[connectionSymbol].socket.readyState !== 1) {
|
319
|
+
reject('the socket is not ready');
|
320
|
+
}
|
321
|
+
|
322
|
+
self[connectionSymbol].socket.send(JSON.stringify(obj))
|
323
|
+
resolve();
|
324
|
+
});
|
325
|
+
}
|
326
|
+
|
327
|
+
|
328
|
+
/**
|
329
|
+
* @return {RestAPI}
|
330
|
+
*/
|
331
|
+
getClone() {
|
332
|
+
const self = this;
|
333
|
+
return new Websocketdatasource(self[internalSymbol].getRealSubject()['options']);
|
334
|
+
}
|
335
|
+
|
336
|
+
}
|
337
|
+
|
@@ -17,6 +17,18 @@ import {Pathfinder} from "./pathfinder.mjs";
|
|
17
17
|
|
18
18
|
export {Datasource}
|
19
19
|
|
20
|
+
|
21
|
+
/**
|
22
|
+
* This callback can be passed to a datasource and is used to adapt data structures.
|
23
|
+
*
|
24
|
+
* @callback Monster.Data.Datasource~exampleCallback
|
25
|
+
* @param {*} value Value
|
26
|
+
* @param {string} key Key
|
27
|
+
* @memberOf Monster.Data
|
28
|
+
* @see Monster.Data.Datasource
|
29
|
+
*/
|
30
|
+
|
31
|
+
|
20
32
|
/**
|
21
33
|
* @private
|
22
34
|
* @type {symbol}
|
@@ -16,7 +16,7 @@ import {Observer} from "../types/observer.mjs";
|
|
16
16
|
import {ProxyObserver} from "../types/proxyobserver.mjs";
|
17
17
|
import {validateFunction, validateInstance, validateObject, validateString} from "../types/validate.mjs";
|
18
18
|
import {clone} from "../util/clone.mjs";
|
19
|
-
import {addAttributeToken,
|
19
|
+
import {addAttributeToken, getLinkedObjects, hasObjectLink} from "./attributes.mjs";
|
20
20
|
import {
|
21
21
|
ATTRIBUTE_DISABLED,
|
22
22
|
ATTRIBUTE_ERRORMESSAGE,
|
@@ -468,7 +468,6 @@ class CustomElement extends HTMLElement {
|
|
468
468
|
}
|
469
469
|
|
470
470
|
addObjectWithUpdaterToElement.call(self, nodeList, customElementUpdaterLinkSymbol, clone(self[internalSymbol].getRealSubject()['options']))
|
471
|
-
//assignUpdaterToElement.call(self, nodeList, clone(self[internalSymbol].getRealSubject()['options']));
|
472
471
|
return self;
|
473
472
|
}
|
474
473
|
|
package/source/types/queue.mjs
CHANGED
@@ -7,11 +7,18 @@
|
|
7
7
|
|
8
8
|
import {Base} from './base.mjs';
|
9
9
|
import {instanceSymbol} from '../constants.mjs';
|
10
|
+
|
10
11
|
export {Queue}
|
11
12
|
|
12
13
|
/**
|
13
|
-
*
|
14
|
-
*
|
14
|
+
* A queue is a list of items that are processed one after another (first in, first out).
|
15
|
+
*
|
16
|
+
* With a queue you can add items to the end of the list `Queue.add()` and remove items from the beginning of the list `Queue.pop()`.
|
17
|
+
*
|
18
|
+
* With `Queue.peek()` you can get the first item without removing it from the list.
|
19
|
+
*
|
20
|
+
* You can create the instance via `new Queue()`.
|
21
|
+
*
|
15
22
|
* @externalExample ../../example/types/queue.mjs
|
16
23
|
* @license AGPLv3
|
17
24
|
* @since 1.4.0
|
package/source/types/version.mjs
CHANGED
@@ -0,0 +1,96 @@
|
|
1
|
+
import {expect} from "chai"
|
2
|
+
import {WebSocketDatasource} from "../../../../../application/source/data/datasource/websocket.mjs";
|
3
|
+
import {initWebSocket} from "../../../util/websocket.mjs";
|
4
|
+
|
5
|
+
const testUrl = "wss://ws.postman-echo.com/raw"
|
6
|
+
|
7
|
+
// const g = getGlobal();
|
8
|
+
// g['WebSocket'] = WS;
|
9
|
+
|
10
|
+
|
11
|
+
describe('Websocket', function () {
|
12
|
+
|
13
|
+
let ds = undefined
|
14
|
+
|
15
|
+
before(function (done) {
|
16
|
+
initWebSocket().then(() => {
|
17
|
+
done()
|
18
|
+
}).catch((e) => {
|
19
|
+
done(e)
|
20
|
+
})
|
21
|
+
});
|
22
|
+
|
23
|
+
afterEach(function (done) {
|
24
|
+
if (ds) {
|
25
|
+
ds.close()
|
26
|
+
}
|
27
|
+
|
28
|
+
// without this, the node test will hang
|
29
|
+
for (const sym of Object.getOwnPropertySymbols(ds)) {
|
30
|
+
if (sym.toString() ==='Symbol(connection)') {
|
31
|
+
if(ds[sym]?.socket?.['terminate']) {
|
32
|
+
ds[sym]?.socket?.['terminate']()
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
done()
|
38
|
+
});
|
39
|
+
|
40
|
+
it('should connect', function (done) {
|
41
|
+
ds = new WebSocketDatasource({
|
42
|
+
url: testUrl,
|
43
|
+
reconnect: {
|
44
|
+
enabled: false
|
45
|
+
}
|
46
|
+
});
|
47
|
+
ds.connect()
|
48
|
+
setTimeout(() => {
|
49
|
+
expect(ds.isConnected()).to.be.true;
|
50
|
+
done();
|
51
|
+
}, 500);
|
52
|
+
|
53
|
+
|
54
|
+
})
|
55
|
+
|
56
|
+
it('should send message', function (done) {
|
57
|
+
ds = new WebSocketDatasource({
|
58
|
+
url: testUrl,
|
59
|
+
reconnect: {
|
60
|
+
enabled: false
|
61
|
+
}
|
62
|
+
});
|
63
|
+
ds.connect()
|
64
|
+
|
65
|
+
ds.set({
|
66
|
+
data: {
|
67
|
+
message: "Hello World"
|
68
|
+
}
|
69
|
+
})
|
70
|
+
|
71
|
+
setTimeout(() => {
|
72
|
+
|
73
|
+
ds.write().then(() => {
|
74
|
+
|
75
|
+
ds.set({})
|
76
|
+
expect(ds.get()).to.be.deep.equal({});
|
77
|
+
|
78
|
+
|
79
|
+
setTimeout(() => {
|
80
|
+
|
81
|
+
expect(ds.get()).to.be.deep.equal({
|
82
|
+
data: {
|
83
|
+
message: "Hello World"
|
84
|
+
}
|
85
|
+
});
|
86
|
+
done();
|
87
|
+
}, 1000);
|
88
|
+
}).catch((err) => {
|
89
|
+
done(new Error(err));
|
90
|
+
})
|
91
|
+
}, 1000)
|
92
|
+
|
93
|
+
|
94
|
+
}).timeout(10000);
|
95
|
+
|
96
|
+
});
|
package/test/cases/monster.mjs
CHANGED
@@ -9,7 +9,7 @@ describe('DeadMansSwitch', function () {
|
|
9
9
|
|
10
10
|
const ms1 = Date.now();
|
11
11
|
|
12
|
-
|
12
|
+
new DeadMansSwitch(100, () => {
|
13
13
|
|
14
14
|
const ms2 = Date.now();
|
15
15
|
|
@@ -38,12 +38,12 @@ describe('DeadMansSwitch', function () {
|
|
38
38
|
const diff = ms2 - ms1;
|
39
39
|
|
40
40
|
if (ms1 > ms2) {
|
41
|
-
done('timing error');
|
41
|
+
done(new Error('timing error'));
|
42
42
|
return;
|
43
43
|
}
|
44
44
|
|
45
45
|
if (diff < 600) {
|
46
|
-
done('to short ' + diff);
|
46
|
+
done(new Error('to short ' + diff));
|
47
47
|
return;
|
48
48
|
}
|
49
49
|
done();
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import {getGlobal} from "../../../application/source/types/global.mjs";
|
2
|
+
|
3
|
+
function initWebSocket() {
|
4
|
+
if (typeof window === "object" && window['WebSocket']) return Promise.resolve();
|
5
|
+
|
6
|
+
return import("ws").then((ws) => {
|
7
|
+
getGlobal().WebSocket = class extends ws['WebSocket'] {
|
8
|
+
constructor(url, protocols) {
|
9
|
+
super(url, protocols, {
|
10
|
+
handshakeTimeout: 1000,
|
11
|
+
maxPayload: 1024 * 1024 * 1024,
|
12
|
+
});
|
13
|
+
|
14
|
+
}
|
15
|
+
};
|
16
|
+
|
17
|
+
});
|
18
|
+
|
19
|
+
|
20
|
+
}
|
21
|
+
|
22
|
+
export {initWebSocket}
|
package/test/web/import.js
CHANGED
@@ -78,4 +78,5 @@ import "../cases/data/diff.mjs";
|
|
78
78
|
import "../cases/data/datasource/restapi.mjs";
|
79
79
|
import "../cases/data/datasource/storage/sessionstorage.mjs";
|
80
80
|
import "../cases/data/datasource/storage/localstorage.mjs";
|
81
|
+
import "../cases/data/datasource/webservice.mjs";
|
81
82
|
import "../cases/math/random.mjs";
|
package/test/web/test.html
CHANGED
@@ -14,8 +14,8 @@
|
|
14
14
|
</head>
|
15
15
|
<body>
|
16
16
|
<div id="headline" style="display: flex;align-items: center;justify-content: center;flex-direction: column;">
|
17
|
-
<h1 style='margin-bottom: 0.1em;'>Monster
|
18
|
-
<div id="lastupdate" style='font-size:0.7em'>last update
|
17
|
+
<h1 style='margin-bottom: 0.1em;'>Monster 3.0.0</h1>
|
18
|
+
<div id="lastupdate" style='font-size:0.7em'>last update Fr 6. Jan 12:54:47 CET 2023</div>
|
19
19
|
</div>
|
20
20
|
<div id="mocks"></div>
|
21
21
|
<div id="mocha"></div>
|