@itee/client 10.0.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/CHANGELOG.md +251 -0
- package/LICENSE.md +23 -0
- package/README.md +76 -0
- package/builds/itee-client.cjs.js +6517 -0
- package/builds/itee-client.cjs.js.map +1 -0
- package/builds/itee-client.cjs.min.js +125 -0
- package/builds/itee-client.esm.js +6468 -0
- package/builds/itee-client.esm.js.map +1 -0
- package/builds/itee-client.esm.min.js +124 -0
- package/builds/itee-client.iife.js +6524 -0
- package/builds/itee-client.iife.js.map +1 -0
- package/builds/itee-client.iife.min.js +125 -0
- package/package.json +87 -0
- package/sources/client.js +14 -0
- package/sources/cores/TAbstractFactory.js +42 -0
- package/sources/cores/TCloningFactory.js +39 -0
- package/sources/cores/TConstants.js +433 -0
- package/sources/cores/TInstancingFactory.js +41 -0
- package/sources/cores/TStore.js +303 -0
- package/sources/cores/_cores.js +13 -0
- package/sources/input_devices/TKeyboardController.js +158 -0
- package/sources/input_devices/TMouseController.js +31 -0
- package/sources/input_devices/_inputDevices.js +11 -0
- package/sources/loaders/TBinaryConverter.js +35 -0
- package/sources/loaders/TBinaryReader.js +1029 -0
- package/sources/loaders/TBinarySerializer.js +258 -0
- package/sources/loaders/TBinaryWriter.js +429 -0
- package/sources/loaders/_loaders.js +21 -0
- package/sources/loaders/converters/ArrayBinaryConverter.js +33 -0
- package/sources/loaders/converters/BooleanBinaryConverter.js +24 -0
- package/sources/loaders/converters/DateBinaryConverter.js +21 -0
- package/sources/loaders/converters/NullBinaryConverter.js +13 -0
- package/sources/loaders/converters/NumberBinaryConverter.js +15 -0
- package/sources/loaders/converters/ObjectBinaryConverter.js +101 -0
- package/sources/loaders/converters/RegExBinaryConverter.js +11 -0
- package/sources/loaders/converters/StringBinaryConverter.js +26 -0
- package/sources/loaders/converters/UndefinedBinaryConverter.js +13 -0
- package/sources/managers/TDataBaseManager.js +1649 -0
- package/sources/managers/_managers.js +10 -0
- package/sources/utils/TIdFactory.js +84 -0
- package/sources/utils/_utils.js +9 -0
- package/sources/webapis/WebAPI.js +773 -0
- package/sources/webapis/WebAPIOrigin.js +141 -0
- package/sources/webapis/_webapis.js +10 -0
- package/sources/webapis/messages/WebAPIMessage.js +75 -0
- package/sources/webapis/messages/WebAPIMessageData.js +51 -0
- package/sources/webapis/messages/WebAPIMessageError.js +79 -0
- package/sources/webapis/messages/WebAPIMessageEvent.js +58 -0
- package/sources/webapis/messages/WebAPIMessageProgress.js +91 -0
- package/sources/webapis/messages/WebAPIMessageReady.js +66 -0
- package/sources/webapis/messages/WebAPIMessageRequest.js +94 -0
- package/sources/webapis/messages/WebAPIMessageResponse.js +80 -0
- package/sources/webapis/messages/_messages.js +16 -0
- package/sources/workers/AbstractWorker.js +149 -0
- package/sources/workers/_workers.js +10 -0
- package/sources/workers/messages/WorkerMessage.js +33 -0
- package/sources/workers/messages/WorkerMessageData.js +30 -0
- package/sources/workers/messages/WorkerMessageError.js +32 -0
- package/sources/workers/messages/WorkerMessageMethodCall.js +60 -0
- package/sources/workers/messages/WorkerMessageProgress.js +67 -0
- package/sources/workers/messages/_messages.js +14 -0
|
@@ -0,0 +1,773 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author [Tristan Valcke]{@link https://github.com/Itee}
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { DefaultLogger } from '@itee/core'
|
|
6
|
+
import { toArray } from '@itee/utils'
|
|
7
|
+
import {
|
|
8
|
+
isDefined,
|
|
9
|
+
isEmptyArray,
|
|
10
|
+
isNotDefined,
|
|
11
|
+
isNotNumber,
|
|
12
|
+
isNull,
|
|
13
|
+
isNumberNegative,
|
|
14
|
+
isObject,
|
|
15
|
+
isString,
|
|
16
|
+
isUndefined,
|
|
17
|
+
isZero
|
|
18
|
+
} from '@itee/validators'
|
|
19
|
+
import { WebAPIMessageData } from './messages/WebAPIMessageData.js'
|
|
20
|
+
import { WebAPIMessageError } from './messages/WebAPIMessageError.js'
|
|
21
|
+
import { WebAPIMessageEvent } from './messages/WebAPIMessageEvent.js'
|
|
22
|
+
import { WebAPIMessageReady } from './messages/WebAPIMessageReady.js'
|
|
23
|
+
import { WebAPIMessageRequest } from './messages/WebAPIMessageRequest.js'
|
|
24
|
+
import { WebAPIMessageResponse } from './messages/WebAPIMessageResponse.js'
|
|
25
|
+
import { WebAPIOrigin } from './WebAPIOrigin.js'
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* A POJO object containg datas about a distant source to allow
|
|
29
|
+
* @typedef {Object} AllowedOrigin
|
|
30
|
+
* @property {string} id - The id to reference this origin as a human readable string
|
|
31
|
+
* @property {string} uri - The uri of the origin to allow
|
|
32
|
+
* @property {Array<String>} methods - An array of methods names that are allowed for this origins. To allow all methods use '*', in case no methods string were provide the origin won't be able to do
|
|
33
|
+
* anything.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @class
|
|
38
|
+
* @classdesc The abstract class to use standardized webapi.
|
|
39
|
+
* @abstract
|
|
40
|
+
*/
|
|
41
|
+
class WebAPI {
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @constructor
|
|
45
|
+
* @param {Object} parameters - An object containing all parameters to pass through the inheritance chain to initialize this instance
|
|
46
|
+
* @param {Boolean} [parameters.allowAnyOrigins=false] - A boolean to allow or not any origins calls
|
|
47
|
+
* @param {Array<AllowedOrigin>} [parameters.allowedOrigins=[]] - An array containing configured allowed origins
|
|
48
|
+
* @param {Number} [parameters.requestTimeout=2000] - The request timeout before throw an error
|
|
49
|
+
*/
|
|
50
|
+
constructor( parameters = {} ) {
|
|
51
|
+
|
|
52
|
+
const _parameters = {
|
|
53
|
+
...{
|
|
54
|
+
logger: DefaultLogger,
|
|
55
|
+
allowedOrigins: [],
|
|
56
|
+
requestTimeout: 2000,
|
|
57
|
+
methods: this,
|
|
58
|
+
broadcastReadyOnInit: true
|
|
59
|
+
},
|
|
60
|
+
...parameters
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Private stuff
|
|
64
|
+
this._localOriginUri = window.location.origin
|
|
65
|
+
this._awaitingRequest = new Map()
|
|
66
|
+
this._eventListeners = {} //"eventName" : [ callback1, ... ]
|
|
67
|
+
|
|
68
|
+
// Listen message from Window
|
|
69
|
+
window.addEventListener( 'message', this._onMessage.bind( this ), false )
|
|
70
|
+
|
|
71
|
+
// Public stuff
|
|
72
|
+
this.logger = _parameters.logger
|
|
73
|
+
this.allowedOrigins = _parameters.allowedOrigins
|
|
74
|
+
this.requestTimeout = _parameters.requestTimeout
|
|
75
|
+
this.methods = _parameters.methods
|
|
76
|
+
|
|
77
|
+
// Initiate connection to all origins
|
|
78
|
+
if ( _parameters.broadcastReadyOnInit ) {
|
|
79
|
+
this._broadcastReadyMessage()
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
*
|
|
85
|
+
* @returns {TLogger}
|
|
86
|
+
*/
|
|
87
|
+
get logger() {
|
|
88
|
+
return this._logger
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
*
|
|
92
|
+
* @param value {TLogger}
|
|
93
|
+
*/
|
|
94
|
+
set logger( value ) {
|
|
95
|
+
if ( isNull( value ) ) { throw new ReferenceError( `[${ this._localOriginUri }]: The logger cannot be null, expect a TLogger.` )}
|
|
96
|
+
if ( isUndefined( value ) ) { throw new ReferenceError( `[${ this._localOriginUri }]: The logger cannot be undefined, expect a TLogger.` )}
|
|
97
|
+
if ( !value.isLogger ) { throw new ReferenceError( `[${ this._localOriginUri }]: The logger cannot be undefined, expect a TLogger.` )}
|
|
98
|
+
|
|
99
|
+
this._logger = value
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
*
|
|
103
|
+
* @returns {Array<WebAPIOrigin>}
|
|
104
|
+
*/
|
|
105
|
+
get allowedOrigins() {
|
|
106
|
+
return this._allowedOrigins
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
*
|
|
110
|
+
* @param value {Array<WebAPIOrigin>}
|
|
111
|
+
*/
|
|
112
|
+
set allowedOrigins( value ) {
|
|
113
|
+
|
|
114
|
+
this._allowedOrigins = []
|
|
115
|
+
const _allowedOrigins = toArray( value )
|
|
116
|
+
|
|
117
|
+
// Special case for any origin
|
|
118
|
+
// Todo: should log error in production and cancel '*'
|
|
119
|
+
if ( _allowedOrigins.includes( '*' ) ) {
|
|
120
|
+
this.logger.warn( `[${ this._localOriginUri }]: This webApi is allowed for all origin and could lead to security concerne !` )
|
|
121
|
+
this._allowedOrigins.push( '*' )
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Create WebApiOrigin based on provided settings
|
|
126
|
+
for ( let allowedOrigin of _allowedOrigins ) {
|
|
127
|
+
|
|
128
|
+
const origin = new WebAPIOrigin( {
|
|
129
|
+
id: allowedOrigin.id,
|
|
130
|
+
uri: allowedOrigin.uri,
|
|
131
|
+
methods: allowedOrigin.methods,
|
|
132
|
+
window: this._getOriginWindow( allowedOrigin.uri )
|
|
133
|
+
} )
|
|
134
|
+
this._allowedOrigins.push( origin )
|
|
135
|
+
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
*
|
|
141
|
+
* @returns {Number}
|
|
142
|
+
*/
|
|
143
|
+
get requestTimeout() {
|
|
144
|
+
return this._requestTimeout
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
*
|
|
148
|
+
* @param value {Number}
|
|
149
|
+
*/
|
|
150
|
+
set requestTimeout( value ) {
|
|
151
|
+
if ( isNull( value ) ) { throw new ReferenceError( `[${ this._localOriginUri }]: The request timeout cannot be null, expect to be 0 or a positive number.` )}
|
|
152
|
+
if ( isUndefined( value ) ) { throw new ReferenceError( `[${ this._localOriginUri }]: The request timeout cannot be undefined, expect to be 0 or a positive number.` )}
|
|
153
|
+
if ( isNotNumber( value ) ) { throw new ReferenceError( `[${ this._localOriginUri }]: The request timeout expect to be 0 or a positive number.` )}
|
|
154
|
+
if ( isNumberNegative( value ) && !isZero( value ) ) { throw new ReferenceError( `[${ this._localOriginUri }]: The request timeout expect to be 0 or a positive number.` )}
|
|
155
|
+
|
|
156
|
+
this._requestTimeout = value
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
*
|
|
160
|
+
* @returns {Array<Function>}
|
|
161
|
+
*/
|
|
162
|
+
get methods() {
|
|
163
|
+
return this._methods
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
*
|
|
167
|
+
* @param value Array<Function>
|
|
168
|
+
*/
|
|
169
|
+
set methods( value ) {
|
|
170
|
+
if ( isNull( value ) ) { throw new ReferenceError( `[${ this._localOriginUri }]: The methods cannot be null, expect any keyed collection of function.` )}
|
|
171
|
+
if ( isUndefined( value ) ) { throw new ReferenceError( `[${ this._localOriginUri }]: The methods cannot be undefined, expect any keyed collection of function.` )}
|
|
172
|
+
// Todo: isNotObject && isNotMap && isNotSet && isNotApi
|
|
173
|
+
|
|
174
|
+
this._methods = value
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
*
|
|
178
|
+
* @param value {TLogger}
|
|
179
|
+
* @returns {AbstractWebAPI}
|
|
180
|
+
*/
|
|
181
|
+
setLogger( value ) {
|
|
182
|
+
this.logger = value
|
|
183
|
+
return this
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
*
|
|
187
|
+
* @param value {Array<WebAPIOrigin>}
|
|
188
|
+
* @returns {AbstractWebAPI}
|
|
189
|
+
*/
|
|
190
|
+
setAllowedOrigins( value ) {
|
|
191
|
+
this.allowedOrigins = value
|
|
192
|
+
return this
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
*
|
|
196
|
+
* @param value {Number}
|
|
197
|
+
* @returns {AbstractWebAPI}
|
|
198
|
+
*/
|
|
199
|
+
setRequestTimeout( value ) {
|
|
200
|
+
this.requestTimeout = value
|
|
201
|
+
return this
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
*
|
|
205
|
+
* @param value Array<Function>
|
|
206
|
+
* @returns {AbstractWebAPI}
|
|
207
|
+
*/
|
|
208
|
+
setMethods( value ) {
|
|
209
|
+
this.methods = value
|
|
210
|
+
return this
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
addEventListener( eventName, listener ) {
|
|
214
|
+
if ( isNotDefined( this._eventListeners[ eventName ] ) ) {
|
|
215
|
+
this._eventListeners[ eventName ] = []
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
this._eventListeners[ eventName ].push( listener )
|
|
219
|
+
}
|
|
220
|
+
removeListener( eventName, listener ) {
|
|
221
|
+
if ( isNotDefined( this._eventListeners[ eventName ] ) ) {
|
|
222
|
+
return
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const index = this._eventListeners[ eventName ].indexOf( listener )
|
|
226
|
+
if ( index > -1 ) {
|
|
227
|
+
this._eventListeners[ eventName ].splice( index, 1 )
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// Validators
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
*
|
|
234
|
+
* @returns {boolean}
|
|
235
|
+
* @private
|
|
236
|
+
*/
|
|
237
|
+
_isInIframe() {
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
return window.self !== window.top
|
|
241
|
+
} catch ( error ) {
|
|
242
|
+
return true
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
*
|
|
249
|
+
* @returns {boolean}
|
|
250
|
+
* @private
|
|
251
|
+
*/
|
|
252
|
+
_isNotAllowedForAllOrigins() {
|
|
253
|
+
|
|
254
|
+
return !this._allowedOrigins.includes( '*' )
|
|
255
|
+
// return !this.allowAnyOrigins
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
*
|
|
260
|
+
* @param originURI
|
|
261
|
+
* @returns {boolean}
|
|
262
|
+
* @private
|
|
263
|
+
*/
|
|
264
|
+
_isNotAllowedOrigin( originURI ) {
|
|
265
|
+
|
|
266
|
+
return !this._allowedOrigins
|
|
267
|
+
.filter( origin => origin !== '*' )
|
|
268
|
+
.map( origin => origin.uri )
|
|
269
|
+
.includes( originURI )
|
|
270
|
+
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
*
|
|
275
|
+
* @param originURI
|
|
276
|
+
* @returns {boolean}
|
|
277
|
+
* @private
|
|
278
|
+
*/
|
|
279
|
+
_isSameOrigin( originURI ) {
|
|
280
|
+
return this._localOriginUri === originURI
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
*
|
|
285
|
+
* @param origin {WebAPIOrigin}
|
|
286
|
+
* @returns {boolean}
|
|
287
|
+
* @private
|
|
288
|
+
*/
|
|
289
|
+
_isNotAllowedForAllMethods( origin ) {
|
|
290
|
+
return ( origin.allowedMethods.indexOf( '*' ) === -1 )
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
*
|
|
295
|
+
* @param origin {WebAPIOrigin}
|
|
296
|
+
* @param methodName {string}
|
|
297
|
+
* @returns {boolean}
|
|
298
|
+
* @private
|
|
299
|
+
*/
|
|
300
|
+
_isNotAllowedMethod( origin, methodName ) {
|
|
301
|
+
return ( origin.allowedMethods.indexOf( methodName ) === -1 )
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
*
|
|
306
|
+
* @param methodName
|
|
307
|
+
* @returns {boolean}
|
|
308
|
+
* @private
|
|
309
|
+
*/
|
|
310
|
+
_methodNotExist( methodName ) {
|
|
311
|
+
return isNotDefined( this.methods[ methodName ] )
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Utils
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
*
|
|
318
|
+
* @param propertyName
|
|
319
|
+
* @param value
|
|
320
|
+
* @returns {WebAPIOrigin}
|
|
321
|
+
* @private
|
|
322
|
+
*/
|
|
323
|
+
_getAllowedOriginBy( propertyName, value ) {
|
|
324
|
+
return this.allowedOrigins.find( origin => origin[ propertyName ] === value )
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
*
|
|
329
|
+
* @param originURI
|
|
330
|
+
* @returns {Window}
|
|
331
|
+
* @private
|
|
332
|
+
*/
|
|
333
|
+
_getOriginWindow( originURI ) {
|
|
334
|
+
|
|
335
|
+
let originWindow
|
|
336
|
+
|
|
337
|
+
if ( this._isInIframe() ) {
|
|
338
|
+
|
|
339
|
+
originWindow = window.parent
|
|
340
|
+
|
|
341
|
+
} else {
|
|
342
|
+
|
|
343
|
+
const frames = document.getElementsByTagName( 'iframe' )
|
|
344
|
+
const frame = Array.from( frames ).find( iframe => iframe.src.includes( originURI ) )
|
|
345
|
+
if ( isNotDefined( frame ) ) {
|
|
346
|
+
this.logger.warn( `[${ this._localOriginUri }]: Unable to find iframe element for [${ originURI }] URI !` )
|
|
347
|
+
originWindow = null
|
|
348
|
+
} else {
|
|
349
|
+
originWindow = frame.contentWindow
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return originWindow
|
|
355
|
+
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
*
|
|
360
|
+
* @param origin {WebAPIOrigin}
|
|
361
|
+
* @private
|
|
362
|
+
*/
|
|
363
|
+
_processMessageQueueOf( origin ) {
|
|
364
|
+
|
|
365
|
+
const messageQueue = origin.messageQueue
|
|
366
|
+
for ( let messageIndex = messageQueue.length - 1 ; messageIndex >= 0 ; messageIndex-- ) {
|
|
367
|
+
this.postMessageTo( origin.id, messageQueue.shift() )
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
*
|
|
374
|
+
* @private
|
|
375
|
+
*/
|
|
376
|
+
_broadcastReadyMessage() {
|
|
377
|
+
|
|
378
|
+
const ready = new WebAPIMessageReady()
|
|
379
|
+
let checkInterval = 250
|
|
380
|
+
|
|
381
|
+
const broadcast = () => {
|
|
382
|
+
|
|
383
|
+
const unreadyOrigins = this.allowedOrigins.filter( origin => !origin.isReady && origin.isReachable )
|
|
384
|
+
if ( isEmptyArray( unreadyOrigins ) ) {
|
|
385
|
+
return
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
for ( let unreadyOrigin of unreadyOrigins ) {
|
|
389
|
+
this.postReadyTo( unreadyOrigin.id, ready )
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
checkInterval += checkInterval
|
|
393
|
+
setTimeout( broadcast, checkInterval )
|
|
394
|
+
|
|
395
|
+
}
|
|
396
|
+
broadcast()
|
|
397
|
+
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Messaging
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
*
|
|
404
|
+
* @param event
|
|
405
|
+
* @returns {Promise<void>}
|
|
406
|
+
* @private
|
|
407
|
+
*/
|
|
408
|
+
async _onMessage( event ) {
|
|
409
|
+
|
|
410
|
+
// Is allowed origin
|
|
411
|
+
if ( this._isNotAllowedForAllOrigins() && this._isNotAllowedOrigin( event.origin ) ) {
|
|
412
|
+
this.logger.warn( `[${ this._localOriginUri }]: An unallowed origin [${ event.origin }] try to access the web api.` )
|
|
413
|
+
return
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Is self ?
|
|
417
|
+
if ( this._isSameOrigin( event.origin ) ) {
|
|
418
|
+
this.logger.warn( `[${ this._localOriginUri }]: A local origin try to access the web api...
|
|
419
|
+
or... Am i talking to myself ?
|
|
420
|
+
Said i (${ isString( event.data ) ? event.data : JSON.stringify( event.data ) }) ?
|
|
421
|
+
Hummm... Ehhooo ! Who's there ?
|
|
422
|
+
` )
|
|
423
|
+
return
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// In case we are not in embbeded iframe or the origin is not an iframe set the origin window as the source event
|
|
427
|
+
let origin = this._getAllowedOriginBy( 'uri', event.origin )
|
|
428
|
+
if ( isNotDefined( origin ) ) {
|
|
429
|
+
|
|
430
|
+
// If we are here, we are called by an unknown origin but we are allowed for all. So create a new one
|
|
431
|
+
origin = new WebAPIOrigin( {
|
|
432
|
+
uri: event.origin,
|
|
433
|
+
window: event.source
|
|
434
|
+
} )
|
|
435
|
+
this.allowedOrigins.push( origin )
|
|
436
|
+
|
|
437
|
+
} else if ( isNull( origin.window ) ) {
|
|
438
|
+
|
|
439
|
+
origin.window = event.source
|
|
440
|
+
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const eventData = event.data
|
|
444
|
+
const message = isObject( eventData ) ? eventData : JSON.parse( eventData )
|
|
445
|
+
if ( isNotDefined( message ) ) {
|
|
446
|
+
this.logger.error( `[${ this._localOriginUri }]: Recieve null or undefined message from [${ origin.uri }] ! Expect a json object.` )
|
|
447
|
+
return
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
await this._dispatchMessageFrom( origin, message )
|
|
451
|
+
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
*
|
|
456
|
+
* @param origin
|
|
457
|
+
* @param message
|
|
458
|
+
* @private
|
|
459
|
+
*/
|
|
460
|
+
async _dispatchMessageFrom( origin, message ) {
|
|
461
|
+
|
|
462
|
+
this.logger.log( `[${ this._localOriginUri }]: Recieve message of type '${ message.type }' from [${ origin.uri }].` )
|
|
463
|
+
|
|
464
|
+
switch ( message.type ) {
|
|
465
|
+
|
|
466
|
+
case '_ready':
|
|
467
|
+
this._onReadyFrom( origin, message )
|
|
468
|
+
break
|
|
469
|
+
|
|
470
|
+
case '_request':
|
|
471
|
+
await this._onRequestFrom( origin, message )
|
|
472
|
+
break
|
|
473
|
+
|
|
474
|
+
case '_response':
|
|
475
|
+
this._onResponseFrom( origin, message )
|
|
476
|
+
break
|
|
477
|
+
|
|
478
|
+
case '_data':
|
|
479
|
+
this.onDataFrom( origin, message )
|
|
480
|
+
break
|
|
481
|
+
|
|
482
|
+
case '_event':
|
|
483
|
+
this.onEventFrom( origin, message )
|
|
484
|
+
break
|
|
485
|
+
|
|
486
|
+
case '_error':
|
|
487
|
+
this.onErrorFrom( origin, message )
|
|
488
|
+
break
|
|
489
|
+
|
|
490
|
+
default:
|
|
491
|
+
this.onMessageFrom( origin, message )
|
|
492
|
+
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
*
|
|
499
|
+
* @param origin
|
|
500
|
+
* @param message
|
|
501
|
+
*/
|
|
502
|
+
_onReadyFrom( origin, message ) {
|
|
503
|
+
|
|
504
|
+
if ( !origin.isReady ) {
|
|
505
|
+
origin.isReady = true
|
|
506
|
+
|
|
507
|
+
// Avoid some ping-pong ready message
|
|
508
|
+
if ( !message.isBind ) {
|
|
509
|
+
const ready = new WebAPIMessageReady( { isBind: true } )
|
|
510
|
+
this.postMessageTo( origin.id, ready, true )
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
this._processMessageQueueOf( origin )
|
|
515
|
+
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
*
|
|
520
|
+
* @param origin
|
|
521
|
+
* @param request
|
|
522
|
+
*/
|
|
523
|
+
async _onRequestFrom( origin, request ) {
|
|
524
|
+
|
|
525
|
+
let message
|
|
526
|
+
const methodName = request.method
|
|
527
|
+
const parameters = request.parameters
|
|
528
|
+
|
|
529
|
+
if ( this._isNotAllowedForAllMethods( origin ) && this._isNotAllowedMethod( origin, methodName ) ) {
|
|
530
|
+
|
|
531
|
+
this.logger.error( `[${ this._localOriginUri }]: Origin [${ origin.uri }] try to access an unallowed method named '${ methodName }'.` )
|
|
532
|
+
message = new WebAPIMessageError( new RangeError( `Trying to access an unallowed method named '${ methodName }'.` ) )
|
|
533
|
+
|
|
534
|
+
} else if ( this._methodNotExist( methodName ) ) {
|
|
535
|
+
|
|
536
|
+
this.logger.error( `[${ this._localOriginUri }]: Origin [${ origin.uri }] try to access an unexisting method named '${ methodName }'.` )
|
|
537
|
+
message = new WebAPIMessageError( new RangeError( `Trying to access an unexisting method named '${ methodName }'.` ) )
|
|
538
|
+
|
|
539
|
+
} else {
|
|
540
|
+
|
|
541
|
+
try {
|
|
542
|
+
const result = await this.methods[ methodName ]( ...parameters )
|
|
543
|
+
message = new WebAPIMessageData( result )
|
|
544
|
+
} catch ( error ) {
|
|
545
|
+
message = new WebAPIMessageError( error )
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// To avoid unnecessary client timeout we need to respond with error or data in any case
|
|
551
|
+
this.postResponseTo( origin.id, request, message )
|
|
552
|
+
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
*
|
|
557
|
+
* @param origin
|
|
558
|
+
* @param response
|
|
559
|
+
*/
|
|
560
|
+
_onResponseFrom( origin, response ) {
|
|
561
|
+
|
|
562
|
+
const requestId = response.request.id
|
|
563
|
+
if ( !this._awaitingRequest.has( requestId ) ) { return }
|
|
564
|
+
|
|
565
|
+
const request = this._awaitingRequest.get( requestId )
|
|
566
|
+
this._awaitingRequest.delete( requestId )
|
|
567
|
+
|
|
568
|
+
clearTimeout( request.timeoutId )
|
|
569
|
+
|
|
570
|
+
const result = response.result
|
|
571
|
+
if ( isDefined( result ) ) {
|
|
572
|
+
|
|
573
|
+
if ( result.type === '_error' ) {
|
|
574
|
+
request.reject( result.error )
|
|
575
|
+
} else if ( result.type === '_data' ) {
|
|
576
|
+
request.resolve( result.data )
|
|
577
|
+
} else {
|
|
578
|
+
request.resolve( result )
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
} else {
|
|
582
|
+
request.resolve()
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
*
|
|
589
|
+
* @param origin
|
|
590
|
+
* @param message
|
|
591
|
+
* @private
|
|
592
|
+
*/
|
|
593
|
+
|
|
594
|
+
onErrorFrom( origin, message ) {
|
|
595
|
+
// Need to be reimplemented if needed
|
|
596
|
+
this.logger.error( `[${ this._localOriginUri }]: the origin [${ origin.uri }] send error => ${ JSON.stringify( message.error, null, 4 ) }. Need you to reimplement this method ?` )
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
*
|
|
601
|
+
* @param origin
|
|
602
|
+
* @param message
|
|
603
|
+
*/
|
|
604
|
+
|
|
605
|
+
onDataFrom( origin, message ) {
|
|
606
|
+
// Need to be reimplemented if needed
|
|
607
|
+
this.logger.log( `[${ this._localOriginUri }]: the origin [${ origin.uri }] send data => ${ JSON.stringify( message.data, null, 4 ) }. Need you to reimplement this method ?` )
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
*
|
|
612
|
+
* @param origin
|
|
613
|
+
* @param message
|
|
614
|
+
*/
|
|
615
|
+
|
|
616
|
+
onMessageFrom( origin, message ) {
|
|
617
|
+
// Need to be reimplemented if needed
|
|
618
|
+
this.logger.log( `[${ this._localOriginUri }]: the origin [${ origin.uri }] send custom message => ${ JSON.stringify( message, null, 4 ) }. Need you to reimplement this method ?` )
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
onEventFrom( origin, event ) {
|
|
622
|
+
const listeners = this._eventListeners[ event.name ]
|
|
623
|
+
for ( const listener of listeners ) {
|
|
624
|
+
listener( event.data )
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
// Send
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
*
|
|
631
|
+
* @param originId
|
|
632
|
+
* @param ready
|
|
633
|
+
*/
|
|
634
|
+
postReadyTo( originId, ready ) {
|
|
635
|
+
|
|
636
|
+
const _ready = ( ready && ready.constructor.isWebAPIMessageReady ) ? ready : new WebAPIMessageReady()
|
|
637
|
+
this.postMessageTo( originId, _ready, true )
|
|
638
|
+
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
*
|
|
643
|
+
* @param originId
|
|
644
|
+
* @param request
|
|
645
|
+
* @param params
|
|
646
|
+
* @returns {Promise<unknown>}
|
|
647
|
+
*/
|
|
648
|
+
postRequestTo( originId, request, ...params ) {
|
|
649
|
+
|
|
650
|
+
const _request = ( request && request.constructor.isWebAPIMessageRequest ) ? request : new WebAPIMessageRequest( request, params )
|
|
651
|
+
|
|
652
|
+
return new Promise( ( resolve, reject ) => {
|
|
653
|
+
|
|
654
|
+
try {
|
|
655
|
+
|
|
656
|
+
this._awaitingRequest.set( _request.id, {
|
|
657
|
+
request: _request,
|
|
658
|
+
resolve: resolve,
|
|
659
|
+
reject: reject,
|
|
660
|
+
timeoutId: setTimeout( () => {
|
|
661
|
+
this._awaitingRequest.delete( _request.id )
|
|
662
|
+
reject( new Error( `Request timeout for ${ JSON.stringify( _request, null, 4 ) }` ) )
|
|
663
|
+
//Todo send abort to avoid future return that won't be processed
|
|
664
|
+
}, this.requestTimeout )
|
|
665
|
+
} )
|
|
666
|
+
|
|
667
|
+
this.postMessageTo( originId, _request )
|
|
668
|
+
|
|
669
|
+
} catch ( error ) {
|
|
670
|
+
|
|
671
|
+
reject( error )
|
|
672
|
+
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
} )
|
|
676
|
+
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
*
|
|
681
|
+
* @param originId
|
|
682
|
+
* @param request
|
|
683
|
+
* @param reponse
|
|
684
|
+
*/
|
|
685
|
+
postResponseTo( originId, request, reponse ) {
|
|
686
|
+
|
|
687
|
+
const _response = ( reponse && reponse.constructor.isWebAPIMessageResponse ) ? reponse : new WebAPIMessageResponse( request, reponse )
|
|
688
|
+
this.postMessageTo( originId, _response )
|
|
689
|
+
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
*
|
|
694
|
+
* @param originId
|
|
695
|
+
* @param error {WebAPIMessageError|String}
|
|
696
|
+
*/
|
|
697
|
+
postErrorTo( originId, error ) {
|
|
698
|
+
|
|
699
|
+
const _error = ( error && error.constructor.isWebAPIMessageError ) ? error : new WebAPIMessageError( error )
|
|
700
|
+
this.postMessageTo( originId, _error )
|
|
701
|
+
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/**
|
|
705
|
+
*
|
|
706
|
+
* @param originId
|
|
707
|
+
* @param data
|
|
708
|
+
*/
|
|
709
|
+
postDataTo( originId, data ) {
|
|
710
|
+
|
|
711
|
+
const _data = ( data && data.constructor.isWebAPIMessageData ) ? data : new WebAPIMessageData( data )
|
|
712
|
+
this.postMessageTo( originId, _data )
|
|
713
|
+
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
*
|
|
718
|
+
* @param originId
|
|
719
|
+
* @param message
|
|
720
|
+
* @param force
|
|
721
|
+
*/
|
|
722
|
+
postMessageTo( originId, message, force = false ) {
|
|
723
|
+
|
|
724
|
+
if ( isNotDefined( originId ) ) { throw new ReferenceError( `[${ this._localOriginUri }]: Unable to post message to null or undefined origin id !` ) }
|
|
725
|
+
if ( isNotDefined( message ) ) { throw new ReferenceError( `[${ this._localOriginUri }]: Unable to post null or undefined message !` ) }
|
|
726
|
+
|
|
727
|
+
const origin = this._getAllowedOriginBy( 'id', originId )
|
|
728
|
+
if ( isNotDefined( origin ) ) { throw new ReferenceError( `[${ this._localOriginUri }]: Unable to retrieved origin with id: ${ originId }` ) }
|
|
729
|
+
|
|
730
|
+
try {
|
|
731
|
+
|
|
732
|
+
if ( !force && !origin.isReady ) {
|
|
733
|
+
|
|
734
|
+
this.logger.warn( `[${ this._localOriginUri }]: Origin [${ origin.uri }] is not ready yet !` )
|
|
735
|
+
origin.messageQueue.push( message )
|
|
736
|
+
|
|
737
|
+
} else if ( force && !origin.window ) {
|
|
738
|
+
|
|
739
|
+
this.logger.error( `[${ this._localOriginUri }]: Origin [${ origin.uri }] is unreachable !` )
|
|
740
|
+
// origin.isUnreachable = true
|
|
741
|
+
origin.messageQueue.push( message )
|
|
742
|
+
|
|
743
|
+
} else {
|
|
744
|
+
|
|
745
|
+
this.logger.log( `[${ this._localOriginUri }]: Send message of type '${ message.type }' to [${ origin.uri }]` )
|
|
746
|
+
origin.window.postMessage( JSON.stringify( message ), origin.uri )
|
|
747
|
+
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
} catch ( error ) {
|
|
751
|
+
|
|
752
|
+
this.logger.error( error )
|
|
753
|
+
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
postEvent( name = 'DefaultEventName', data ) {
|
|
759
|
+
|
|
760
|
+
const _data = ( data && data.constructor.isWebAPIMessageEvent ) ? data : new WebAPIMessageEvent( name, data )
|
|
761
|
+
|
|
762
|
+
// Broadcast to all potential listener
|
|
763
|
+
const allowedOrigins = this._allowedOrigins.filter( origin => origin !== '*' )
|
|
764
|
+
for ( let i = 0 ; i < allowedOrigins.length ; i++ ) {
|
|
765
|
+
const allowedOrigin = allowedOrigins[ i ]
|
|
766
|
+
const originId = allowedOrigin.id
|
|
767
|
+
this.postMessageTo( originId, _data )
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
export { WebAPI }
|