@firebolt-js/core-client 1.0.0-next.5
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 +0 -0
- package/CONTRIBUTING.md +4 -0
- package/LICENSE +202 -0
- package/NOTICE +24 -0
- package/README.md +26 -0
- package/dist/docs/Accessibility/index.md +384 -0
- package/dist/docs/Accessibility/schemas/index.md +52 -0
- package/dist/docs/Advertising/index.md +120 -0
- package/dist/docs/Device/index.md +297 -0
- package/dist/docs/Discovery/index.md +128 -0
- package/dist/docs/Display/index.md +87 -0
- package/dist/docs/Internal/index.md +68 -0
- package/dist/docs/Lifecycle2/index.md +160 -0
- package/dist/docs/Localization/index.md +314 -0
- package/dist/docs/Localization/schemas/index.md +41 -0
- package/dist/docs/Metrics/index.md +987 -0
- package/dist/docs/Network/index.md +128 -0
- package/dist/docs/Policies/schemas/index.md +25 -0
- package/dist/docs/Presentation/index.md +53 -0
- package/dist/docs/Stats/index.md +63 -0
- package/dist/docs/TextToSpeech/index.md +524 -0
- package/dist/docs/Types/schemas/index.md +37 -0
- package/dist/firebolt-core-app-open-rpc.json +1082 -0
- package/dist/firebolt-core-open-rpc.json +3773 -0
- package/dist/lib/Accessibility/defaults.mjs +61 -0
- package/dist/lib/Accessibility/index.mjs +148 -0
- package/dist/lib/Advertising/defaults.mjs +26 -0
- package/dist/lib/Advertising/index.mjs +31 -0
- package/dist/lib/Device/defaults.mjs +34 -0
- package/dist/lib/Device/index.mjs +84 -0
- package/dist/lib/Discovery/defaults.mjs +22 -0
- package/dist/lib/Discovery/index.mjs +34 -0
- package/dist/lib/Events/index.mjs +345 -0
- package/dist/lib/Gateway/AppApi.mjs +125 -0
- package/dist/lib/Gateway/Bidirectional.mjs +113 -0
- package/dist/lib/Gateway/PlatformApi.mjs +130 -0
- package/dist/lib/Gateway/index.mjs +48 -0
- package/dist/lib/Localization/defaults.mjs +44 -0
- package/dist/lib/Localization/index.mjs +123 -0
- package/dist/lib/Log/index.mjs +57 -0
- package/dist/lib/Metrics/defaults.mjs +40 -0
- package/dist/lib/Metrics/index.mjs +206 -0
- package/dist/lib/Network/defaults.mjs +24 -0
- package/dist/lib/Network/index.mjs +70 -0
- package/dist/lib/Platform/defaults.mjs +27 -0
- package/dist/lib/Platform/index.mjs +28 -0
- package/dist/lib/Prop/MockProps.mjs +28 -0
- package/dist/lib/Prop/Router.mjs +25 -0
- package/dist/lib/Prop/index.mjs +60 -0
- package/dist/lib/Settings/index.mjs +86 -0
- package/dist/lib/Transport/MockTransport.mjs +191 -0
- package/dist/lib/Transport/WebsocketTransport.mjs +55 -0
- package/dist/lib/Transport/index.mjs +81 -0
- package/dist/lib/firebolt.d.ts +795 -0
- package/dist/lib/firebolt.mjs +51 -0
- package/firebolt-js-core-client-1.0.0-next.5.tgz +0 -0
- package/package.json +54 -0
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2021 Comcast Cable Communications Management, LLC
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*
|
|
16
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import Gateway from '../Gateway/index.mjs'
|
|
20
|
+
|
|
21
|
+
let listenerId = 0
|
|
22
|
+
|
|
23
|
+
// holds two maps of ${module}.${event} => listenerId, e.g. callback method id
|
|
24
|
+
// note that one callback can listen to multiple events, e.g. 'discovery.*'
|
|
25
|
+
// internal is only available via a private export that we use to ensure our modules know about
|
|
26
|
+
// events before the apps using the SDK (otherwise state errors can happen)
|
|
27
|
+
const listeners = {
|
|
28
|
+
internal: {},
|
|
29
|
+
external: {},
|
|
30
|
+
|
|
31
|
+
// Several convenience functions below for checking both internal & external lists w/ one operation
|
|
32
|
+
|
|
33
|
+
// gets a merge list of ids for a single event key
|
|
34
|
+
get: (key) => {
|
|
35
|
+
return Object.assign(
|
|
36
|
+
Object.assign({}, listeners.internal[key]),
|
|
37
|
+
listeners.external[key],
|
|
38
|
+
)
|
|
39
|
+
},
|
|
40
|
+
// adds a callback/id to a key on the external list only
|
|
41
|
+
set: (key, id, value) => {
|
|
42
|
+
listeners.external[key] = listeners.external[key] || {}
|
|
43
|
+
listeners.external[key][id] = value
|
|
44
|
+
},
|
|
45
|
+
// adds a callback/id to a key on the internal list only
|
|
46
|
+
setInternal: (key, id, value) => {
|
|
47
|
+
listeners.internal[key] = listeners.internal[key] || {}
|
|
48
|
+
listeners.internal[key][id] = value
|
|
49
|
+
},
|
|
50
|
+
// finds the key for an id in either list (it can only be in one)
|
|
51
|
+
find: (id) => {
|
|
52
|
+
let key
|
|
53
|
+
;[listeners.internal, listeners.external].find((group) => {
|
|
54
|
+
key = Object.keys(group).find((key) => group[key][id])
|
|
55
|
+
if (key) return true
|
|
56
|
+
})
|
|
57
|
+
return key
|
|
58
|
+
},
|
|
59
|
+
// removes an id from either list
|
|
60
|
+
remove: (id) => {
|
|
61
|
+
;[listeners.internal, listeners.external].forEach((group) => {
|
|
62
|
+
Object.keys(group).forEach((key) => {
|
|
63
|
+
if (group[key] && group[key][id]) {
|
|
64
|
+
delete group[key][id]
|
|
65
|
+
if (Object.values(group[key]).length === 0) {
|
|
66
|
+
delete group[key]
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
},
|
|
72
|
+
// removes a key from both lists if _internal is true, otherwise only the external list
|
|
73
|
+
removeKey: (key, _internal = false) => {
|
|
74
|
+
_internal && listeners.internal[key] && delete listeners.internal[key]
|
|
75
|
+
listeners.external[key] && delete listeners.external[key]
|
|
76
|
+
},
|
|
77
|
+
// gives a list of all keys
|
|
78
|
+
keys: () => {
|
|
79
|
+
return Array.from(
|
|
80
|
+
new Set(
|
|
81
|
+
Object.keys(listeners.internal).concat(Object.keys(listeners.external)),
|
|
82
|
+
),
|
|
83
|
+
)
|
|
84
|
+
},
|
|
85
|
+
// counts how many listeners are in a key across both lists
|
|
86
|
+
count: (key) => {
|
|
87
|
+
return Object.values(listeners.get(key)).length
|
|
88
|
+
},
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// holds a map of ${module}.${event} => Transport.send calls (only called once per event)
|
|
92
|
+
// note that the keys here MUST NOT contain wild cards
|
|
93
|
+
const oncers = []
|
|
94
|
+
const validEvents = {}
|
|
95
|
+
const validContext = {}
|
|
96
|
+
|
|
97
|
+
export const registerEvents = (module, events) => {
|
|
98
|
+
validEvents[module] = events.concat()
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export const registerEventContext = (module, event, context) => {
|
|
102
|
+
validContext[module] = validContext[module] || {}
|
|
103
|
+
validContext[module][event] = context.concat()
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const callCallbacks = (key, params) => {
|
|
107
|
+
const args = Object.values(params)
|
|
108
|
+
const callbacks = Object.entries(listeners.internal[key] || {}).concat(
|
|
109
|
+
Object.entries(listeners.external[key] || {}),
|
|
110
|
+
)
|
|
111
|
+
callbacks.forEach(([listenerId, callback]) => {
|
|
112
|
+
if (oncers.indexOf(parseInt(listenerId)) >= 0) {
|
|
113
|
+
oncers.splice(oncers.indexOf(parseInt(listenerId)), 1)
|
|
114
|
+
delete listeners.external[key][listenerId]
|
|
115
|
+
}
|
|
116
|
+
if (args.length <= callback.length) {
|
|
117
|
+
callback.apply(null, args)
|
|
118
|
+
} else if (
|
|
119
|
+
args.length > 0 &&
|
|
120
|
+
callback.length > 0 &&
|
|
121
|
+
callback.length < args.length
|
|
122
|
+
) {
|
|
123
|
+
// read the params the callback takes and if they are less then the size of args remove the extra params from the front
|
|
124
|
+
const callbackArgs = args.slice(-callback.length, args.length)
|
|
125
|
+
callback.apply(null, callbackArgs)
|
|
126
|
+
} else {
|
|
127
|
+
callback.call(null)
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const doListen = function (
|
|
133
|
+
module,
|
|
134
|
+
event,
|
|
135
|
+
callback,
|
|
136
|
+
context,
|
|
137
|
+
once,
|
|
138
|
+
internal = false,
|
|
139
|
+
) {
|
|
140
|
+
init()
|
|
141
|
+
|
|
142
|
+
if (typeof callback !== 'function') {
|
|
143
|
+
return Promise.reject('No valid callback function provided.')
|
|
144
|
+
} else {
|
|
145
|
+
if (module === '*') {
|
|
146
|
+
return Promise.reject('No valid module name provided')
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
//TODO: This is a patch and should be removed! Adds "on" if the event does not start with "on".
|
|
150
|
+
// Added temporarily for backward compatibility with older event namings
|
|
151
|
+
if (event != '*' && !event.startsWith('on')) {
|
|
152
|
+
event = 'on' + event[0].toUpperCase() + event.substring(1)
|
|
153
|
+
console.warn(
|
|
154
|
+
`Event names should begin with 'on'. Using '${module + '.' + event}' instead.`,
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const wildcard = event === '*'
|
|
159
|
+
const events = wildcard ? validEvents[module] : [event] // explodes wildcards into an array
|
|
160
|
+
const promises = []
|
|
161
|
+
const hasContext = Object.values(context).length > 0
|
|
162
|
+
const contextKey = Object.keys(context)
|
|
163
|
+
.sort()
|
|
164
|
+
.map((key) => key + '=' + JSON.stringify(context[key]))
|
|
165
|
+
.join('&')
|
|
166
|
+
|
|
167
|
+
listenerId++
|
|
168
|
+
|
|
169
|
+
if (once) {
|
|
170
|
+
oncers.push(listenerId)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
events.forEach((event) => {
|
|
174
|
+
const key = module + '.' + event + (hasContext ? `.${contextKey}` : '')
|
|
175
|
+
|
|
176
|
+
if (Object.values(listeners.get(key)).length === 0) {
|
|
177
|
+
const args = Object.assign({ listen: true }, context)
|
|
178
|
+
|
|
179
|
+
const subscriber = module + '.' + event
|
|
180
|
+
const notifier = module + '.' + event
|
|
181
|
+
|
|
182
|
+
Gateway.subscribe(notifier, (params) => {
|
|
183
|
+
callCallbacks(key, params)
|
|
184
|
+
})
|
|
185
|
+
const promise = Gateway.request(subscriber, args)
|
|
186
|
+
promises.push(promise)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const setter = internal ? listeners.setInternal : listeners.set
|
|
190
|
+
|
|
191
|
+
if (wildcard) {
|
|
192
|
+
setter(key, '' + listenerId, (value) => callback(event, value))
|
|
193
|
+
} else {
|
|
194
|
+
setter(key, '' + listenerId, callback)
|
|
195
|
+
}
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
let resolve, reject
|
|
199
|
+
let p = new Promise((res, rej) => {
|
|
200
|
+
resolve = res
|
|
201
|
+
reject = rej
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
// Iterate and resolve/reject through the list of promises sequentially
|
|
205
|
+
const templistenerId = listenerId
|
|
206
|
+
if (promises.length) {
|
|
207
|
+
promises.reduce((prevPromise, currentPromise) => {
|
|
208
|
+
return prevPromise
|
|
209
|
+
.then(() => currentPromise)
|
|
210
|
+
.then((responses) => {
|
|
211
|
+
resolve(templistenerId)
|
|
212
|
+
})
|
|
213
|
+
.catch((error) => {
|
|
214
|
+
if (event === '*') {
|
|
215
|
+
resolve(templistenerId)
|
|
216
|
+
} else {
|
|
217
|
+
// Remove the failed listener
|
|
218
|
+
doClear(templistenerId, event, context)
|
|
219
|
+
reject(error)
|
|
220
|
+
}
|
|
221
|
+
})
|
|
222
|
+
}, Promise.resolve())
|
|
223
|
+
} else {
|
|
224
|
+
resolve(listenerId)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return p
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const getListenArgs = function (...args) {
|
|
232
|
+
const callback = args.pop()
|
|
233
|
+
const [module, event, context] = getClearArgs(...args)
|
|
234
|
+
|
|
235
|
+
return [module, event, callback, context]
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const getClearArgs = function (...args) {
|
|
239
|
+
const module = args.shift() || '*'
|
|
240
|
+
const event = args.shift() || '*'
|
|
241
|
+
const context = {}
|
|
242
|
+
|
|
243
|
+
// populate context based on registered context for this module/event
|
|
244
|
+
// if an argument is not registered in validContext, that is ok meaning it is not a context argument
|
|
245
|
+
// Needed after introducing x-contextual-params
|
|
246
|
+
for (let i = 0; args.length; i++) {
|
|
247
|
+
let currentArg = args.shift()
|
|
248
|
+
if (
|
|
249
|
+
validContext[module] &&
|
|
250
|
+
validContext[module][event] &&
|
|
251
|
+
validContext[module][event][i]
|
|
252
|
+
) {
|
|
253
|
+
context[validContext[module][event][i]] = currentArg
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return [module, event, context]
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const once = function (...args) {
|
|
260
|
+
const [module, event, callback, context] = getListenArgs(...args)
|
|
261
|
+
return doListen(module, event, callback, context, true)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const listen = function (...args) {
|
|
265
|
+
const [module, event, callback, context] = getListenArgs(...args)
|
|
266
|
+
return doListen(module, event, callback, context, false)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const clear = function (...args) {
|
|
270
|
+
if (args && args.length && typeof args[0] === 'number') {
|
|
271
|
+
return doClear(args[0])
|
|
272
|
+
} else if (args && args.length && typeof args[1] === 'number') {
|
|
273
|
+
return doClear(args[1])
|
|
274
|
+
} else {
|
|
275
|
+
const [moduleOrId, event, context] = getClearArgs(...args)
|
|
276
|
+
return doClear(moduleOrId, event, context)
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// calls doListen with a priority flag for internal listeners to get priority
|
|
281
|
+
export const prioritize = function (...args) {
|
|
282
|
+
const [module, event, callback, context] = getListenArgs(...args)
|
|
283
|
+
return doListen(module, event, callback, context, false, true)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const unsubscribe = (key, context) => {
|
|
287
|
+
const [module, event] = key.split('.').slice(0, 2)
|
|
288
|
+
const args = Object.assign({ listen: false }, context)
|
|
289
|
+
Gateway.request(module + '.' + event, args)
|
|
290
|
+
Gateway.unsubscribe(`${module}.${event}`)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// TODO: clear needs to go through Transport Layer
|
|
294
|
+
const doClear = function (moduleOrId = false, event = false, context) {
|
|
295
|
+
if (event === '*') {
|
|
296
|
+
event = false
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (typeof moduleOrId === 'number') {
|
|
300
|
+
const searchId = moduleOrId.toString()
|
|
301
|
+
const key = listeners.find(searchId)
|
|
302
|
+
|
|
303
|
+
if (key) {
|
|
304
|
+
listeners.remove(searchId)
|
|
305
|
+
if (listeners.count(key) === 0) {
|
|
306
|
+
unsubscribe(key)
|
|
307
|
+
}
|
|
308
|
+
return true
|
|
309
|
+
}
|
|
310
|
+
return false
|
|
311
|
+
} else {
|
|
312
|
+
if (!moduleOrId && !event) {
|
|
313
|
+
listeners.keys().forEach((key) => {
|
|
314
|
+
listeners.removeKey(key)
|
|
315
|
+
unsubscribe(key)
|
|
316
|
+
})
|
|
317
|
+
} else if (!event) {
|
|
318
|
+
listeners.keys().forEach((key) => {
|
|
319
|
+
if (key.indexOf(moduleOrId) === 0) {
|
|
320
|
+
listeners.removeKey(key)
|
|
321
|
+
unsubscribe(key)
|
|
322
|
+
}
|
|
323
|
+
})
|
|
324
|
+
} else {
|
|
325
|
+
const hasContext = Object.values(context).length > 0
|
|
326
|
+
const contextKey = Object.keys(context)
|
|
327
|
+
.sort()
|
|
328
|
+
.map((key) => key + '=' + JSON.stringify(context[key]))
|
|
329
|
+
.join('&')
|
|
330
|
+
const key =
|
|
331
|
+
moduleOrId + '.' + event + (hasContext ? `.${contextKey}` : '')
|
|
332
|
+
|
|
333
|
+
listeners.removeKey(key)
|
|
334
|
+
unsubscribe(key, context)
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const init = () => {}
|
|
340
|
+
|
|
341
|
+
export default {
|
|
342
|
+
listen: listen,
|
|
343
|
+
once: once,
|
|
344
|
+
clear: clear,
|
|
345
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2021 Comcast Cable Communications Management, LLC
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*
|
|
16
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import Transport from '../Transport/index.mjs'
|
|
20
|
+
|
|
21
|
+
const win = typeof window !== 'undefined' ? window : {}
|
|
22
|
+
win.__firebolt = win.__firebolt || {}
|
|
23
|
+
|
|
24
|
+
// JSON RPC id generator, to be shared across all SDKs
|
|
25
|
+
class JsonRpcIdIterator {
|
|
26
|
+
constructor() {
|
|
27
|
+
this._id = 1
|
|
28
|
+
}
|
|
29
|
+
getJsonRpcId() {
|
|
30
|
+
return this._id++
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let idGenerator = win.__firebolt.idGenerator || new JsonRpcIdIterator()
|
|
35
|
+
win.__firebolt.idGenerator = idGenerator
|
|
36
|
+
|
|
37
|
+
const promises = {}
|
|
38
|
+
const deprecated = {}
|
|
39
|
+
|
|
40
|
+
// request = { method: string, params: object, id: boolean }[]
|
|
41
|
+
// request with no `id` property are assumed to NOT be notifications, i.e. id must be set to false explicitly
|
|
42
|
+
export async function batch(requests) {
|
|
43
|
+
if (Array.isArray(requests)) {
|
|
44
|
+
const processed = requests.map((req) =>
|
|
45
|
+
processRequest(req.method, req.params, req.id, req.id === false),
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
// filter requests exclude notifications, as they don't need promises
|
|
49
|
+
const promises = processed
|
|
50
|
+
.filter((req) => req.id)
|
|
51
|
+
.map((request) => addPromiseToQueue(request.id))
|
|
52
|
+
|
|
53
|
+
Transport.send(processed)
|
|
54
|
+
|
|
55
|
+
// Using Promise.all get's us batch blocking for free
|
|
56
|
+
return Promise.all(promises)
|
|
57
|
+
}
|
|
58
|
+
throw `Bulk requests must be in an array`
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Request that the server provide fulfillment of an method
|
|
62
|
+
export async function request(method, params) {
|
|
63
|
+
const json = processRequest(method, params)
|
|
64
|
+
const promise = addPromiseToQueue(json.id)
|
|
65
|
+
Transport.send(json)
|
|
66
|
+
return promise
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function notify(method, params) {
|
|
70
|
+
Transport.send(processRequest(method, params, true))
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function response(id, result, error) {
|
|
74
|
+
const promise = promises[id]
|
|
75
|
+
|
|
76
|
+
if (promise) {
|
|
77
|
+
if (error !== undefined) {
|
|
78
|
+
promises[id].reject(error)
|
|
79
|
+
} else {
|
|
80
|
+
promises[id].resolve(result)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// TODO make sure this works
|
|
84
|
+
delete promises[id]
|
|
85
|
+
} else {
|
|
86
|
+
// All SDKs use a global id generator and a global transport where they register their callbacks for the responses.
|
|
87
|
+
// When the global transport receives a message, it calls all registered callbacks with that same message.
|
|
88
|
+
// That's why we can't generate an error here, as the response could be for a request made by another SDK.
|
|
89
|
+
// console.log(`Received a response for an unidentified request ${id}, result: ${result}, error: ${error}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function deprecate(method, alternative) {
|
|
94
|
+
deprecated[method] = alternative
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function addPromiseToQueue(id) {
|
|
98
|
+
return new Promise((resolve, reject) => {
|
|
99
|
+
promises[id] = {}
|
|
100
|
+
promises[id].promise = this
|
|
101
|
+
promises[id].resolve = resolve
|
|
102
|
+
promises[id].reject = reject
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function processRequest(method, params, notification = false) {
|
|
107
|
+
if (deprecated[method]) {
|
|
108
|
+
console.warn(`WARNING: ${method}() is deprecated. ` + deprecated[method])
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const id = !notification && idGenerator.getJsonRpcId()
|
|
112
|
+
const jsonrpc = '2.0'
|
|
113
|
+
const json = { jsonrpc, method, params }
|
|
114
|
+
|
|
115
|
+
!notification && (json.id = id)
|
|
116
|
+
|
|
117
|
+
return json
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export default {
|
|
121
|
+
request,
|
|
122
|
+
batch,
|
|
123
|
+
response,
|
|
124
|
+
deprecate,
|
|
125
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2021 Comcast Cable Communications Management, LLC
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*
|
|
16
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import PlatformApi from './PlatformApi.mjs'
|
|
20
|
+
import AppApi from './AppApi.mjs'
|
|
21
|
+
import Transport from '../Transport/index.mjs'
|
|
22
|
+
import Settings from '../Settings/index.mjs'
|
|
23
|
+
|
|
24
|
+
Transport.receive(async (message) => {
|
|
25
|
+
const json = JSON.parse(message)
|
|
26
|
+
if (Array.isArray(json)) {
|
|
27
|
+
json.forEach((message) => processMessage(message))
|
|
28
|
+
} else {
|
|
29
|
+
processMessage(json)
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
function processMessage(json) {
|
|
34
|
+
if (Settings.getLogLevel() === 'DEBUG') {
|
|
35
|
+
console.debug(
|
|
36
|
+
'Receiving message from transport: \n' +
|
|
37
|
+
JSON.stringify(json, { indent: '\t' }),
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (json.method !== undefined) {
|
|
42
|
+
if (json.id !== undefined) {
|
|
43
|
+
PlatformApi.request(json.id, json.method, json.params)
|
|
44
|
+
} else {
|
|
45
|
+
let params = json.params
|
|
46
|
+
// TODO: Check if json.params is an array and if so, convert to a key-value object
|
|
47
|
+
// This is necessary because for Ripple, if params is an array this means that
|
|
48
|
+
// the callback function is expected to have one argument - the array itself.
|
|
49
|
+
// This is not compliant with the JSON-RPC specification, i.e. it should be removed.
|
|
50
|
+
if (Array.isArray(params)) {
|
|
51
|
+
params = { value: json.params }
|
|
52
|
+
}
|
|
53
|
+
PlatformApi.notify(json.method, params)
|
|
54
|
+
}
|
|
55
|
+
} else if (json.id !== undefined) {
|
|
56
|
+
AppApi.response(json.id, json.result, json.error)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function batch(requests) {
|
|
61
|
+
if (Array.isArray(requests)) {
|
|
62
|
+
return await AppApi.batch(requests)
|
|
63
|
+
} else {
|
|
64
|
+
throw 'Gateway.batch() requires an array of requests: { method: String, params: Object, id: Boolean }'
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function request(method, params) {
|
|
69
|
+
if (Array.isArray(method)) {
|
|
70
|
+
throw 'Use Gateway.batch() for batch requests.'
|
|
71
|
+
} else {
|
|
72
|
+
return await AppApi.request(method, params)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function notify(method, params) {
|
|
77
|
+
if (Array.isArray(method)) {
|
|
78
|
+
throw 'Use Gateway.batch() for batch requests.'
|
|
79
|
+
} else {
|
|
80
|
+
return await AppApi.notify(method, params)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function subscribe(event, callback) {
|
|
85
|
+
PlatformApi.subscribe(event, callback)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function unsubscribe(event) {
|
|
89
|
+
PlatformApi.unsubscribe(event)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function simulate(event, value) {
|
|
93
|
+
PlatformApi.simulate(event, value)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function provide(interfaceName, provider) {
|
|
97
|
+
PlatformApi.provide(interfaceName, provider)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function deprecate(method, alternative) {
|
|
101
|
+
AppApi.deprecate(method, alternative)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export default {
|
|
105
|
+
request,
|
|
106
|
+
notify,
|
|
107
|
+
batch,
|
|
108
|
+
subscribe,
|
|
109
|
+
unsubscribe,
|
|
110
|
+
simulate,
|
|
111
|
+
provide,
|
|
112
|
+
deprecate,
|
|
113
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2021 Comcast Cable Communications Management, LLC
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*
|
|
16
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import Transport from '../Transport/index.mjs'
|
|
20
|
+
|
|
21
|
+
const providers = {}
|
|
22
|
+
const interfaces = {}
|
|
23
|
+
const listeners = {}
|
|
24
|
+
|
|
25
|
+
// Request that the app provide fulfillment of an method
|
|
26
|
+
export async function request(id, method, params, transforms) {
|
|
27
|
+
let result, error
|
|
28
|
+
try {
|
|
29
|
+
result = await getProviderResult(method, params)
|
|
30
|
+
} catch (e) {
|
|
31
|
+
error = e
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const response = {
|
|
35
|
+
jsonrpc: '2.0',
|
|
36
|
+
id: id,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (error) {
|
|
40
|
+
// todo: make sure this conforms to JSON-RPC schema for errors
|
|
41
|
+
response.error = error
|
|
42
|
+
} else {
|
|
43
|
+
response.result = result
|
|
44
|
+
if (result === undefined) {
|
|
45
|
+
response.result = null
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
Transport.send(response)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function notify(method, params) {
|
|
53
|
+
if (listeners[method]) {
|
|
54
|
+
listeners[method](params)
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
// All SDKs use a global transport where they register their callbacks for the responses.
|
|
58
|
+
// When the global transport receives a message, it calls all registered callbacks with that same message.
|
|
59
|
+
// That's why we can't generate an error here, as the response could be for an event registered by another SDK.
|
|
60
|
+
//console.log( `Notification not implemented: ${method}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Register a provider implementation with an interface name
|
|
64
|
+
export function provide(interfaceName, provider) {
|
|
65
|
+
providers[interfaceName] = provider
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Register a notification listener with an event name
|
|
69
|
+
export function subscribe(event, callback) {
|
|
70
|
+
listeners[event] = callback
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function unsubscribe(event) {
|
|
74
|
+
delete listeners[event]
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function simulate(event, value) {
|
|
78
|
+
listeners[event](value)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// TODO: consider renaming
|
|
82
|
+
export function registerProviderInterface(
|
|
83
|
+
capability,
|
|
84
|
+
_interface,
|
|
85
|
+
method,
|
|
86
|
+
parameters,
|
|
87
|
+
response,
|
|
88
|
+
focusable,
|
|
89
|
+
) {
|
|
90
|
+
interfaces[_interface] = interfaces[_interface] || {
|
|
91
|
+
capability,
|
|
92
|
+
name: _interface,
|
|
93
|
+
methods: [],
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
interfaces[_interface].methods.push({
|
|
97
|
+
name: method,
|
|
98
|
+
parameters,
|
|
99
|
+
response,
|
|
100
|
+
focusable,
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function getProviderResult(method, params = {}) {
|
|
105
|
+
const split = method.split('.')
|
|
106
|
+
method = split.pop()
|
|
107
|
+
const interfaceName = split.join('.')
|
|
108
|
+
|
|
109
|
+
if (providers[interfaceName]) {
|
|
110
|
+
if (providers[interfaceName][method]) {
|
|
111
|
+
// sort the params into an array based on the interface parameter order
|
|
112
|
+
const parameters = interfaces[interfaceName].methods
|
|
113
|
+
.find((m) => m.name === method)
|
|
114
|
+
.parameters.map((p) => params[p])
|
|
115
|
+
.filter((p) => p !== undefined)
|
|
116
|
+
return await providers[interfaceName][method](...parameters)
|
|
117
|
+
}
|
|
118
|
+
throw `Method not implemented: ${method}`
|
|
119
|
+
}
|
|
120
|
+
throw `Interface not provided: ${interfaceName}`
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export default {
|
|
124
|
+
request,
|
|
125
|
+
notify,
|
|
126
|
+
provide,
|
|
127
|
+
subscribe,
|
|
128
|
+
unsubscribe,
|
|
129
|
+
simulate,
|
|
130
|
+
}
|