@de./sdk 1.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/LICENSE +21 -0
- package/README.md +4391 -0
- package/dist/allend/Access.d.ts +15 -0
- package/dist/allend/Access.js +164 -0
- package/dist/allend/Access.js.map +1 -0
- package/dist/allend/DAuth/index.d.ts +20 -0
- package/dist/allend/DAuth/index.js +92 -0
- package/dist/allend/DAuth/index.js.map +1 -0
- package/dist/allend/DClient/Client.d.ts +10 -0
- package/dist/allend/DClient/Client.js +144 -0
- package/dist/allend/DClient/Client.js.map +1 -0
- package/dist/allend/DClient/Event.d.ts +18 -0
- package/dist/allend/DClient/Event.js +100 -0
- package/dist/allend/DClient/Event.js.map +1 -0
- package/dist/allend/DClient/Order.d.ts +49 -0
- package/dist/allend/DClient/Order.js +601 -0
- package/dist/allend/DClient/Order.js.map +1 -0
- package/dist/allend/IoTClient.d.ts +29 -0
- package/dist/allend/IoTClient.js +59 -0
- package/dist/allend/IoTClient.js.map +1 -0
- package/dist/allend/MSI/Controls.d.ts +256 -0
- package/dist/allend/MSI/Controls.js +763 -0
- package/dist/allend/MSI/Controls.js.map +1 -0
- package/dist/allend/MSI/Handles.d.ts +88 -0
- package/dist/allend/MSI/Handles.js +445 -0
- package/dist/allend/MSI/Handles.js.map +1 -0
- package/dist/allend/MSI/Plugins.d.ts +22 -0
- package/dist/allend/MSI/Plugins.js +38 -0
- package/dist/allend/MSI/Plugins.js.map +1 -0
- package/dist/allend/MSI/index.d.ts +55 -0
- package/dist/allend/MSI/index.js +155 -0
- package/dist/allend/MSI/index.js.map +1 -0
- package/dist/allend/index.d.ts +12 -0
- package/dist/allend/index.js +18 -0
- package/dist/allend/index.js.map +1 -0
- package/dist/backend/Auth.d.ts +38 -0
- package/dist/backend/Auth.js +286 -0
- package/dist/backend/Auth.js.map +1 -0
- package/dist/baseUrl.d.ts +12 -0
- package/dist/baseUrl.js +16 -0
- package/dist/baseUrl.js.map +1 -0
- package/dist/bundle.js +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/types/access.d.ts +8 -0
- package/dist/types/auth.d.ts +27 -0
- package/dist/types/index.d.ts +281 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/stream.d.ts +20 -0
- package/dist/utils/stream.js +63 -0
- package/dist/utils/stream.js.map +1 -0
- package/package.json +66 -0
- package/src/allend/Access.ts +78 -0
- package/src/allend/DAuth/index.ts +39 -0
- package/src/allend/DClient/Client.ts +76 -0
- package/src/allend/DClient/Event.ts +76 -0
- package/src/allend/DClient/Order.ts +452 -0
- package/src/allend/IoTClient.ts +68 -0
- package/src/allend/MSI/Controls.ts +760 -0
- package/src/allend/MSI/Handles.ts +387 -0
- package/src/allend/MSI/Plugins.ts +52 -0
- package/src/allend/MSI/index.ts +160 -0
- package/src/allend/index.ts +18 -0
- package/src/backend/Auth.ts +201 -0
- package/src/baseUrl.ts +13 -0
- package/src/index.ts +19 -0
- package/src/types/access.d.ts +8 -0
- package/src/types/auth.d.ts +27 -0
- package/src/types/index.d.ts +281 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/stream.ts +68 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Coordinates,
|
|
3
|
+
RTLocation,
|
|
4
|
+
Entity,
|
|
5
|
+
MapOptions,
|
|
6
|
+
Caption,
|
|
7
|
+
Journey,
|
|
8
|
+
ActivePosition,
|
|
9
|
+
PickedLocation,
|
|
10
|
+
RouteOptions
|
|
11
|
+
} from '../../types'
|
|
12
|
+
|
|
13
|
+
import IOF from 'iframe.io'
|
|
14
|
+
import { EventEmitter } from 'events'
|
|
15
|
+
import Controls from './Controls'
|
|
16
|
+
import Stream from '../../utils/stream'
|
|
17
|
+
|
|
18
|
+
export interface ControlEntity {
|
|
19
|
+
add: ( entity: Entity, callback?: () => void ) => void
|
|
20
|
+
remove: ( id: string, callback?: () => void ) => void
|
|
21
|
+
move: ( update: ActivePosition, callback?: () => void ) => void
|
|
22
|
+
}
|
|
23
|
+
export type LRSControlsListener = ( controls: ControlEntity ) => void
|
|
24
|
+
export type LRSErrorListener = ( error?: Error | boolean ) => void
|
|
25
|
+
|
|
26
|
+
export default class Handles extends EventEmitter {
|
|
27
|
+
private chn: IOF
|
|
28
|
+
private controls: Controls
|
|
29
|
+
private options: MapOptions
|
|
30
|
+
|
|
31
|
+
constructor( chn: IOF, controls: Controls, options: MapOptions ){
|
|
32
|
+
super()
|
|
33
|
+
|
|
34
|
+
this.chn = chn
|
|
35
|
+
this.options = options
|
|
36
|
+
this.controls = controls
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Listen when user manually picked a location
|
|
41
|
+
* on the map.
|
|
42
|
+
*
|
|
43
|
+
* @param fn - Event listener function
|
|
44
|
+
* @return - void
|
|
45
|
+
*/
|
|
46
|
+
onPickLocation( fn: ( location: PickedLocation ) => void ){
|
|
47
|
+
this.chn.on('pick:location', fn )
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Create new stream through which user's current location
|
|
52
|
+
* details will be pushed to the API level.
|
|
53
|
+
*
|
|
54
|
+
* @param usertype - (Default: `client`) Type of user at the current location
|
|
55
|
+
* @return - Readable stream
|
|
56
|
+
*/
|
|
57
|
+
myLocation( usertype?: 'client' | 'agent' ){
|
|
58
|
+
if( !this.chn ) return
|
|
59
|
+
this.chn?.emit('pin:current:location', usertype || 'client' )
|
|
60
|
+
|
|
61
|
+
const stream = new Stream()
|
|
62
|
+
|
|
63
|
+
this.chn
|
|
64
|
+
.on('current:location', ( location: RTLocation ) => stream.sync( location ) )
|
|
65
|
+
.on('live:location:start', ( location: RTLocation ) => stream.sync( location ) )
|
|
66
|
+
.on('live:location:update', ( location: RTLocation ) => stream.sync( location ) )
|
|
67
|
+
.on('live:location:end', ( location: RTLocation ) => stream.sync( location ) )
|
|
68
|
+
.on('current:location:error', ( message: string ) => stream.error( new Error( message ) ) )
|
|
69
|
+
|
|
70
|
+
// Listen to stream closed
|
|
71
|
+
stream
|
|
72
|
+
.onerror( error => console.error('[Stream Error] ', error ) )
|
|
73
|
+
.onclose( () => {
|
|
74
|
+
if( !this.chn ) return
|
|
75
|
+
|
|
76
|
+
this.chn
|
|
77
|
+
.off('current:location')
|
|
78
|
+
.off('current:location:live')
|
|
79
|
+
.off('current:location:error')
|
|
80
|
+
} )
|
|
81
|
+
|
|
82
|
+
return stream
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Create new stream through which the current location details
|
|
87
|
+
* of user's peer (Eg. `agent`) will be pushed to the API level.
|
|
88
|
+
*
|
|
89
|
+
* @param position - Peer's current GPS location
|
|
90
|
+
* @param caption - (Optional) Caption information of the peer
|
|
91
|
+
* @return - Readable stream
|
|
92
|
+
*/
|
|
93
|
+
peerLocation( position: RTLocation , caption?: Caption ){
|
|
94
|
+
if( !this.chn ) return
|
|
95
|
+
this.chn?.emit('pin:peer:location', { id: 'peer', position, caption } )
|
|
96
|
+
|
|
97
|
+
const stream = new Stream
|
|
98
|
+
|
|
99
|
+
// Listen to incoming new position data
|
|
100
|
+
stream
|
|
101
|
+
.on('data', ({ position, caption }: any ) => {
|
|
102
|
+
if( !position )
|
|
103
|
+
return stream.error( new Error('Invalid Data') )
|
|
104
|
+
|
|
105
|
+
this.chn?.emit('pin:peer:location', { id: 'peer', position, caption } )
|
|
106
|
+
})
|
|
107
|
+
.onerror( error => console.error('[Stream Error] ', error ) )
|
|
108
|
+
.onclose( () => this.chn?.emit('unpin:peer:location', 'peer') )
|
|
109
|
+
|
|
110
|
+
return stream
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Open a live stream through which the current location details
|
|
115
|
+
* and profile information of all nearby entities around
|
|
116
|
+
* this user (Eg. `bike`, `car`, ...) will be pushed to the
|
|
117
|
+
* API level.
|
|
118
|
+
*
|
|
119
|
+
* @param list - List of detected nearby entities around this user.
|
|
120
|
+
* @return - Live Readable Stream (LRS)
|
|
121
|
+
*/
|
|
122
|
+
nearby( list: Entity[] ){
|
|
123
|
+
if( !this.chn ) return
|
|
124
|
+
|
|
125
|
+
const self = this
|
|
126
|
+
let _CLOSED = false
|
|
127
|
+
|
|
128
|
+
this.chn?.emit('show:nearby', list )
|
|
129
|
+
|
|
130
|
+
const
|
|
131
|
+
stream = new Stream,
|
|
132
|
+
controls: ControlEntity = {
|
|
133
|
+
/**
|
|
134
|
+
* Add new entity to the nearby list
|
|
135
|
+
*
|
|
136
|
+
* @param entity - GPS location and profile of the entity
|
|
137
|
+
*/
|
|
138
|
+
add( entity ): Promise<void> {
|
|
139
|
+
return new Promise( ( resolve, reject ) => {
|
|
140
|
+
if( _CLOSED ) return
|
|
141
|
+
|
|
142
|
+
// Maintain initial list of nearby: Prevent duplicated entity
|
|
143
|
+
for( let x = 0; x < list.length; x++ )
|
|
144
|
+
if( list[x].id === entity.id ){
|
|
145
|
+
list.splice( x, 1 )
|
|
146
|
+
self.chn?.emit('remove:nearby:entity', entity.id )
|
|
147
|
+
break
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Track response timeout
|
|
151
|
+
let TIMEOUT: any
|
|
152
|
+
|
|
153
|
+
// Add new entity
|
|
154
|
+
list.push( entity )
|
|
155
|
+
self.chn?.emit('add:nearby:entity', entity, () => {
|
|
156
|
+
clearTimeout( TIMEOUT )
|
|
157
|
+
resolve()
|
|
158
|
+
|
|
159
|
+
self.emit('nearby--stream', 'add', entity )
|
|
160
|
+
} )
|
|
161
|
+
|
|
162
|
+
setTimeout( () => reject('Add entity timeout'), 8000 )
|
|
163
|
+
})
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Remove an entity (vehicle, premises) from the nearby list
|
|
168
|
+
*
|
|
169
|
+
* @param id - ID of targeted entity
|
|
170
|
+
*/
|
|
171
|
+
remove( id ): Promise<void> {
|
|
172
|
+
return new Promise( ( resolve, reject ) => {
|
|
173
|
+
if( _CLOSED ) return
|
|
174
|
+
|
|
175
|
+
// Track response timeout
|
|
176
|
+
let TIMEOUT: any
|
|
177
|
+
|
|
178
|
+
list = list.filter( each => { return each.id !== id } )
|
|
179
|
+
self.chn?.emit('remove:nearby:entity', id, () => {
|
|
180
|
+
clearTimeout( TIMEOUT )
|
|
181
|
+
resolve()
|
|
182
|
+
|
|
183
|
+
self.emit('nearby--stream', 'remove', id )
|
|
184
|
+
} )
|
|
185
|
+
|
|
186
|
+
setTimeout( () => reject('Remove entity timeout'), 8000 )
|
|
187
|
+
} )
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Change mobile entities position on the map
|
|
192
|
+
*
|
|
193
|
+
* @param location - New GPS location of the entity
|
|
194
|
+
*/
|
|
195
|
+
move( location ): Promise<void> {
|
|
196
|
+
return new Promise( ( resolve, reject ) => {
|
|
197
|
+
if( _CLOSED ) return
|
|
198
|
+
|
|
199
|
+
// Track response timeout
|
|
200
|
+
let TIMEOUT: any
|
|
201
|
+
|
|
202
|
+
self.chn?.emit('move:nearby:entity', location, () => {
|
|
203
|
+
clearTimeout( TIMEOUT )
|
|
204
|
+
resolve()
|
|
205
|
+
|
|
206
|
+
self.emit('nearby--stream', 'move', location )
|
|
207
|
+
} )
|
|
208
|
+
|
|
209
|
+
setTimeout( () => reject('Remove entity timeout'), 8000 )
|
|
210
|
+
} )
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const
|
|
215
|
+
/**
|
|
216
|
+
*
|
|
217
|
+
*/
|
|
218
|
+
live = ( fn: LRSControlsListener ) => {
|
|
219
|
+
fn( controls )
|
|
220
|
+
return stream
|
|
221
|
+
},
|
|
222
|
+
close = ( fn?: LRSErrorListener ) => {
|
|
223
|
+
if( _CLOSED ) return
|
|
224
|
+
|
|
225
|
+
this.chn?.emit('remove:nearby', ( error: string | boolean ) => {
|
|
226
|
+
if( typeof error == 'string' )
|
|
227
|
+
return typeof fn == 'function' && fn( new Error( error ) )
|
|
228
|
+
|
|
229
|
+
_CLOSED = true
|
|
230
|
+
stream.isActive() && stream.close()
|
|
231
|
+
|
|
232
|
+
typeof fn == 'function' && fn()
|
|
233
|
+
})
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
this.on('nearby--stream', ( action, dataset ) => stream.sync({ action, dataset, list }) )
|
|
237
|
+
// Listen to stream closed
|
|
238
|
+
stream
|
|
239
|
+
.onerror( error => console.error('[Stream Error] ', error ) )
|
|
240
|
+
.onclose( ( fn?: () => void ) => {
|
|
241
|
+
this.off('nearby--stream', fn || (() => {}) )
|
|
242
|
+
close()
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
return { live, pipe: stream.pipe, close }
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Set service pickup point.
|
|
250
|
+
*
|
|
251
|
+
* @param location - Location coordinates
|
|
252
|
+
* @param caption - (Optional) Caption information of the pickup point
|
|
253
|
+
*/
|
|
254
|
+
async pickupPoint( location: Coordinates, caption?: Caption ): Promise<void> {
|
|
255
|
+
// Default pickup caption
|
|
256
|
+
const _caption: Caption = {
|
|
257
|
+
label: 'Pickup point',
|
|
258
|
+
...(caption || {})
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
await this.controls?.setRouteOrigin('main', { coords: location, caption: _caption } )
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Set service dropoff point.
|
|
266
|
+
*
|
|
267
|
+
* @param location - Location coordinates
|
|
268
|
+
* @param caption - (Optional) Caption information of the pickup point
|
|
269
|
+
*/
|
|
270
|
+
async dropoffPoint( location: Coordinates, caption?: Caption ): Promise<void> {
|
|
271
|
+
// Default destination caption
|
|
272
|
+
const _caption: Caption = {
|
|
273
|
+
label: 'Destination point',
|
|
274
|
+
...(caption || {})
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
await this.controls?.setRouteDestination('main', { coords: location, caption: _caption } )
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Create new stream through which navigation details of
|
|
282
|
+
* this user's peer will be display on the user's map also
|
|
283
|
+
* pushed to the API level.
|
|
284
|
+
*
|
|
285
|
+
* @return - Readable stream
|
|
286
|
+
*/
|
|
287
|
+
peerDirection( options?: RouteOptions ){
|
|
288
|
+
if( !this.chn ) return
|
|
289
|
+
const stream = new Stream
|
|
290
|
+
|
|
291
|
+
stream
|
|
292
|
+
.on('data', ({ status, direction, position }: any ) => {
|
|
293
|
+
if( !direction || !position )
|
|
294
|
+
return stream.error( new Error('Invalid Data') )
|
|
295
|
+
|
|
296
|
+
this.controls?.casting('peer-direction', direction, position, options )
|
|
297
|
+
|
|
298
|
+
switch( status ){
|
|
299
|
+
case 'STALE':
|
|
300
|
+
case 'STARTED':
|
|
301
|
+
case 'LONG_STOP':
|
|
302
|
+
case 'LOW_TRAFFIC':
|
|
303
|
+
case 'HIGH_TRAFFIC':
|
|
304
|
+
case 'MODERATE_TRAFFIC':
|
|
305
|
+
case 'SPEED_WARNING':
|
|
306
|
+
case 'NEARBY':
|
|
307
|
+
case 'ARRIVED': this.emit(`pe:${status.toLowerCase()}`); break
|
|
308
|
+
case 'UNAVAILABLE': {
|
|
309
|
+
this.emit(`pe:closed`)
|
|
310
|
+
stream.isActive() && stream.close()
|
|
311
|
+
} break
|
|
312
|
+
}
|
|
313
|
+
})
|
|
314
|
+
.onerror( error => console.error('[Stream Error] ', error ) )
|
|
315
|
+
|
|
316
|
+
return stream
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Initiate user (Eg. agent) live navigation on the map
|
|
321
|
+
* and create a new stream through which the navigation details
|
|
322
|
+
* will be pushed to the API level.
|
|
323
|
+
*
|
|
324
|
+
* @param journey - Route origin, waypoints, destination
|
|
325
|
+
* @return - Readable stream
|
|
326
|
+
*/
|
|
327
|
+
navigation( journey: Journey ): Promise<Stream> {
|
|
328
|
+
return new Promise( ( resolve, reject ) => {
|
|
329
|
+
if( !this.chn ) return
|
|
330
|
+
|
|
331
|
+
const initialize = () => {
|
|
332
|
+
const stream = new Stream
|
|
333
|
+
|
|
334
|
+
// Sync with navigation route update
|
|
335
|
+
this.chn?.on('navigation:direction', ({ status, direction, position }) => {
|
|
336
|
+
stream.sync({ status, direction, position })
|
|
337
|
+
|
|
338
|
+
switch( status ){
|
|
339
|
+
case 'STALE':
|
|
340
|
+
case 'STARTED':
|
|
341
|
+
case 'LONG_STOP':
|
|
342
|
+
case 'LOW_TRAFFIC':
|
|
343
|
+
case 'HIGH_TRAFFIC':
|
|
344
|
+
case 'MODERATE_TRAFFIC':
|
|
345
|
+
case 'SPEED_WARNING':
|
|
346
|
+
case 'NEARBY':
|
|
347
|
+
case 'ARRIVED': this.emit(`pe:${status.toLowerCase()}`); break
|
|
348
|
+
case 'UNAVAILABLE': {
|
|
349
|
+
this.emit(`pe:closed`)
|
|
350
|
+
stream.isActive() && stream.close()
|
|
351
|
+
} break
|
|
352
|
+
}
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
stream
|
|
356
|
+
// Listen location/position update
|
|
357
|
+
.on('data', ({ position }: any ) => {
|
|
358
|
+
if( !position )
|
|
359
|
+
return stream.error( new Error('Invalid Data') )
|
|
360
|
+
|
|
361
|
+
this.controls?.navigate( position )
|
|
362
|
+
})
|
|
363
|
+
.onerror( error => console.error('[Stream Error] ', error ) )
|
|
364
|
+
.onclose( () => {
|
|
365
|
+
this.chn?.off('navigation:direction')
|
|
366
|
+
this.controls?.unmountNavigation()
|
|
367
|
+
})
|
|
368
|
+
|
|
369
|
+
resolve( stream )
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Set route
|
|
373
|
+
this.controls?.setRoute( journey )
|
|
374
|
+
.then( async () => {
|
|
375
|
+
// Initialize navigation point to current location
|
|
376
|
+
const position = journey.origin || await this.controls?.getCurrentLocation()
|
|
377
|
+
if( !position ) return reject('Unable to get current location')
|
|
378
|
+
|
|
379
|
+
initialize()
|
|
380
|
+
|
|
381
|
+
await this.controls?.mountNavigation( journey.routeId )
|
|
382
|
+
await this.controls?.setInitialNavigationPosition( position as RTLocation )
|
|
383
|
+
} )
|
|
384
|
+
.catch( reject )
|
|
385
|
+
} )
|
|
386
|
+
}
|
|
387
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { MapOptions } from '../../types'
|
|
2
|
+
|
|
3
|
+
import IOF from 'iframe.io'
|
|
4
|
+
import Handles from './Handles'
|
|
5
|
+
import Controls from './Controls'
|
|
6
|
+
import Utils from '../../utils'
|
|
7
|
+
|
|
8
|
+
export type PluginHook = {
|
|
9
|
+
handles: Handles,
|
|
10
|
+
controls: Controls,
|
|
11
|
+
map: MapOptions,
|
|
12
|
+
utils: typeof Utils
|
|
13
|
+
}
|
|
14
|
+
export type Plugin<PluginAPI, PluginOptions = {}> = ( hooks: PluginHook, options?: PluginOptions ) => PluginAPI
|
|
15
|
+
|
|
16
|
+
export default class Plugins {
|
|
17
|
+
private chn: IOF
|
|
18
|
+
private handles: Handles
|
|
19
|
+
private controls: Controls
|
|
20
|
+
private options: MapOptions
|
|
21
|
+
private ACTIVE_PLUGINS: Record<string, Plugin<any>> = {}
|
|
22
|
+
|
|
23
|
+
constructor( chn: IOF, handles: Handles, controls: Controls, options: MapOptions ){
|
|
24
|
+
this.chn = chn
|
|
25
|
+
this.options = options
|
|
26
|
+
this.handles = handles
|
|
27
|
+
this.controls = controls
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
mount( list: Record<string, Plugin<any>> ){
|
|
31
|
+
Object.entries( list ).forEach( ([name, plugin]) => {
|
|
32
|
+
// TODO: Allow api level permission settings
|
|
33
|
+
|
|
34
|
+
// TODO: Put validation checks in place
|
|
35
|
+
|
|
36
|
+
this.ACTIVE_PLUGINS[ name ] = plugin
|
|
37
|
+
} )
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
use<API, Options>( name: string, options?: Record<string, Options> ){
|
|
41
|
+
if( !(name in this.ACTIVE_PLUGINS) )
|
|
42
|
+
throw new Error(`Undefined <${name}> plugin`)
|
|
43
|
+
|
|
44
|
+
const plugin: Plugin<API> = this.ACTIVE_PLUGINS[ name ]
|
|
45
|
+
return plugin({
|
|
46
|
+
handles: this.handles,
|
|
47
|
+
controls: this.controls,
|
|
48
|
+
map: this.options,
|
|
49
|
+
utils: Utils
|
|
50
|
+
}, options )
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import type { MapOptions } from '../../types'
|
|
2
|
+
|
|
3
|
+
import { EventEmitter } from 'events'
|
|
4
|
+
import IOF from 'iframe.io'
|
|
5
|
+
import Handles from './Handles'
|
|
6
|
+
import Controls from './Controls'
|
|
7
|
+
import Plugins, { type Plugin } from './Plugins'
|
|
8
|
+
|
|
9
|
+
const
|
|
10
|
+
SANDBOX_RULES = ['allow-scripts', 'allow-same-origin'],
|
|
11
|
+
REQUIRED_FEATURES = ['geolocation'],
|
|
12
|
+
REGISTERED_PLUGINS: Record<string, Plugin<any>> = {}
|
|
13
|
+
|
|
14
|
+
export interface MSIInterface {
|
|
15
|
+
controls: Controls,
|
|
16
|
+
handles: Handles,
|
|
17
|
+
plugins: Plugins
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default class MSI extends EventEmitter {
|
|
21
|
+
private isDev: boolean
|
|
22
|
+
private baseURL: string
|
|
23
|
+
private isConnected: boolean
|
|
24
|
+
private options: MapOptions
|
|
25
|
+
private chn?: IOF
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Mapbox control Client
|
|
29
|
+
*
|
|
30
|
+
* @param options - Initial options
|
|
31
|
+
*/
|
|
32
|
+
constructor( options: MapOptions ){
|
|
33
|
+
super()
|
|
34
|
+
|
|
35
|
+
this.options = options
|
|
36
|
+
if( !this.options.accessToken )
|
|
37
|
+
throw new Error('Invalid Access Token')
|
|
38
|
+
|
|
39
|
+
this.isDev = this.options.env == 'dev' || false
|
|
40
|
+
this.baseURL = this.isDev ? 'http://localhost:4800' : 'https://msi.dedot.io'
|
|
41
|
+
|
|
42
|
+
this.isConnected = false
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Listen to embedded iframe load
|
|
47
|
+
*
|
|
48
|
+
* @param e - DOM EventTarget object
|
|
49
|
+
*/
|
|
50
|
+
private onload( e: Event ){
|
|
51
|
+
const iframe = e.target as HTMLIFrameElement
|
|
52
|
+
|
|
53
|
+
// Remove all previous listeners when iframe reloaded
|
|
54
|
+
this.chn && this.chn.removeListeners()
|
|
55
|
+
|
|
56
|
+
this.chn = new IOF({ type: 'WINDOW' })
|
|
57
|
+
this.chn.initiate( iframe.contentWindow as Window, this.baseURL )
|
|
58
|
+
|
|
59
|
+
this.chn
|
|
60
|
+
.once('connect', () => {
|
|
61
|
+
this.chn?.emit('bind', { ...this.options, origin: window.origin }, ( error: string | boolean ) => {
|
|
62
|
+
if( error )
|
|
63
|
+
return this.emit('error', new Error( error as string ) )
|
|
64
|
+
|
|
65
|
+
this.isConnected = true
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
.on('error', ( error: Error | string ) => this.emit('error', typeof error == 'object' ? error : new Error( error ) ) )
|
|
69
|
+
.on('ready', () => {
|
|
70
|
+
console.log('Ready by ready-event')
|
|
71
|
+
this.emit('ready')
|
|
72
|
+
this.emit('loaded', this.chn )
|
|
73
|
+
})
|
|
74
|
+
// .on('event', ( _event: string, ...args: any[] ) => this.emit( _event, ...args ) )
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Embed the gateway into a web UI using
|
|
79
|
+
* an iframe.
|
|
80
|
+
*/
|
|
81
|
+
private render(){
|
|
82
|
+
const container = document.getElementById( this.options.element )
|
|
83
|
+
if( !container ) throw new Error(`HTML Element Container <#${this.options.element}> Not Found`)
|
|
84
|
+
|
|
85
|
+
container.innerHTML = `<iframe id="de-sdk:map"
|
|
86
|
+
src="${this.baseURL}"
|
|
87
|
+
style="width:100%;height:100%;border:none;"
|
|
88
|
+
title="De.MSI Gateway"
|
|
89
|
+
importance="high"
|
|
90
|
+
referrerpolicy="origin"
|
|
91
|
+
allow="${REQUIRED_FEATURES.join()}"
|
|
92
|
+
sandbox="${SANDBOX_RULES.join(' ')}"></iframe>`
|
|
93
|
+
|
|
94
|
+
const element = document.getElementById('de-sdk:map')
|
|
95
|
+
if( !element )
|
|
96
|
+
throw new Error('Unexpected Error Occured. Check HTML Element selector')
|
|
97
|
+
|
|
98
|
+
element.onload = this.onload.bind( this )
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Handle gateway embedding network error
|
|
103
|
+
*/
|
|
104
|
+
private networkError(){
|
|
105
|
+
this.isConnected = false
|
|
106
|
+
this.emit('error', new Error('Internet network problem') )
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Initiate embedding of gateway into current UI by
|
|
111
|
+
* check network and remote gateway availability.
|
|
112
|
+
*/
|
|
113
|
+
load(): Promise<MSIInterface> {
|
|
114
|
+
return new Promise( ( resolve, reject ) => {
|
|
115
|
+
const initializeAPI = ( chn: IOF ) => {
|
|
116
|
+
const
|
|
117
|
+
/**
|
|
118
|
+
* Manual controls of the map remotely
|
|
119
|
+
*/
|
|
120
|
+
controls = new Controls( chn, this.options ),
|
|
121
|
+
/**
|
|
122
|
+
* Initialize handles
|
|
123
|
+
*/
|
|
124
|
+
handles = new Handles( chn, controls, this.options ),
|
|
125
|
+
/**
|
|
126
|
+
* Initialize plugins & mount preloaded ones
|
|
127
|
+
*/
|
|
128
|
+
plugins = new Plugins( chn, handles, controls, this.options )
|
|
129
|
+
plugins.mount( REGISTERED_PLUGINS )
|
|
130
|
+
|
|
131
|
+
resolve({ controls, handles, plugins })
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
this
|
|
135
|
+
.once('loaded', initializeAPI )
|
|
136
|
+
.once('error', reject )
|
|
137
|
+
|
|
138
|
+
window
|
|
139
|
+
.fetch( this.baseURL, { mode: 'no-cors' })
|
|
140
|
+
.then( this.render.bind( this ) )
|
|
141
|
+
.catch( this.networkError.bind( this ) )
|
|
142
|
+
} )
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* @return - Boolean that tells whether the gateway is
|
|
147
|
+
* loaded and ready for interaction
|
|
148
|
+
*/
|
|
149
|
+
isReady(){ return this.chn && this.isConnected }
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Extend the kit's functionalities
|
|
153
|
+
*
|
|
154
|
+
* @param name - Name of the plugin that will be later used to access the plugin object interface
|
|
155
|
+
* @param fn - Function containing the logic of the plugin
|
|
156
|
+
*/
|
|
157
|
+
plugin<T>( name: string, fn: Plugin<T> ){
|
|
158
|
+
REGISTERED_PLUGINS[ name ] = fn
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import MSI, { type MSIInterface } from './MSI'
|
|
2
|
+
import Utils from '../utils'
|
|
3
|
+
|
|
4
|
+
import IoTClient, { type IoTClientOptions } from './IoTClient'
|
|
5
|
+
import Order from './DClient/Order'
|
|
6
|
+
import Event from './DClient/Event'
|
|
7
|
+
import Client from './DClient/Client'
|
|
8
|
+
|
|
9
|
+
const DClient = { Client, Order, Event }
|
|
10
|
+
|
|
11
|
+
export {
|
|
12
|
+
type MSIInterface,
|
|
13
|
+
MSI,
|
|
14
|
+
Utils,
|
|
15
|
+
DClient,
|
|
16
|
+
IoTClient,
|
|
17
|
+
type IoTClientOptions
|
|
18
|
+
}
|