@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.
Files changed (74) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +4391 -0
  3. package/dist/allend/Access.d.ts +15 -0
  4. package/dist/allend/Access.js +164 -0
  5. package/dist/allend/Access.js.map +1 -0
  6. package/dist/allend/DAuth/index.d.ts +20 -0
  7. package/dist/allend/DAuth/index.js +92 -0
  8. package/dist/allend/DAuth/index.js.map +1 -0
  9. package/dist/allend/DClient/Client.d.ts +10 -0
  10. package/dist/allend/DClient/Client.js +144 -0
  11. package/dist/allend/DClient/Client.js.map +1 -0
  12. package/dist/allend/DClient/Event.d.ts +18 -0
  13. package/dist/allend/DClient/Event.js +100 -0
  14. package/dist/allend/DClient/Event.js.map +1 -0
  15. package/dist/allend/DClient/Order.d.ts +49 -0
  16. package/dist/allend/DClient/Order.js +601 -0
  17. package/dist/allend/DClient/Order.js.map +1 -0
  18. package/dist/allend/IoTClient.d.ts +29 -0
  19. package/dist/allend/IoTClient.js +59 -0
  20. package/dist/allend/IoTClient.js.map +1 -0
  21. package/dist/allend/MSI/Controls.d.ts +256 -0
  22. package/dist/allend/MSI/Controls.js +763 -0
  23. package/dist/allend/MSI/Controls.js.map +1 -0
  24. package/dist/allend/MSI/Handles.d.ts +88 -0
  25. package/dist/allend/MSI/Handles.js +445 -0
  26. package/dist/allend/MSI/Handles.js.map +1 -0
  27. package/dist/allend/MSI/Plugins.d.ts +22 -0
  28. package/dist/allend/MSI/Plugins.js +38 -0
  29. package/dist/allend/MSI/Plugins.js.map +1 -0
  30. package/dist/allend/MSI/index.d.ts +55 -0
  31. package/dist/allend/MSI/index.js +155 -0
  32. package/dist/allend/MSI/index.js.map +1 -0
  33. package/dist/allend/index.d.ts +12 -0
  34. package/dist/allend/index.js +18 -0
  35. package/dist/allend/index.js.map +1 -0
  36. package/dist/backend/Auth.d.ts +38 -0
  37. package/dist/backend/Auth.js +286 -0
  38. package/dist/backend/Auth.js.map +1 -0
  39. package/dist/baseUrl.d.ts +12 -0
  40. package/dist/baseUrl.js +16 -0
  41. package/dist/baseUrl.js.map +1 -0
  42. package/dist/bundle.js +1 -0
  43. package/dist/index.d.ts +13 -0
  44. package/dist/index.js +20 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/types/access.d.ts +8 -0
  47. package/dist/types/auth.d.ts +27 -0
  48. package/dist/types/index.d.ts +281 -0
  49. package/dist/utils/index.d.ts +5 -0
  50. package/dist/utils/index.js +8 -0
  51. package/dist/utils/index.js.map +1 -0
  52. package/dist/utils/stream.d.ts +20 -0
  53. package/dist/utils/stream.js +63 -0
  54. package/dist/utils/stream.js.map +1 -0
  55. package/package.json +66 -0
  56. package/src/allend/Access.ts +78 -0
  57. package/src/allend/DAuth/index.ts +39 -0
  58. package/src/allend/DClient/Client.ts +76 -0
  59. package/src/allend/DClient/Event.ts +76 -0
  60. package/src/allend/DClient/Order.ts +452 -0
  61. package/src/allend/IoTClient.ts +68 -0
  62. package/src/allend/MSI/Controls.ts +760 -0
  63. package/src/allend/MSI/Handles.ts +387 -0
  64. package/src/allend/MSI/Plugins.ts +52 -0
  65. package/src/allend/MSI/index.ts +160 -0
  66. package/src/allend/index.ts +18 -0
  67. package/src/backend/Auth.ts +201 -0
  68. package/src/baseUrl.ts +13 -0
  69. package/src/index.ts +19 -0
  70. package/src/types/access.d.ts +8 -0
  71. package/src/types/auth.d.ts +27 -0
  72. package/src/types/index.d.ts +281 -0
  73. package/src/utils/index.ts +4 -0
  74. 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
+ }