@superleapai/flow-ui 1.0.2 → 2.2.1
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/README.md +161 -16
- package/core/bridge.js +524 -0
- package/core/crm.js +336 -0
- package/dist/output.css +1 -1
- package/dist/superleap-flow.js +14205 -0
- package/dist/superleap-flow.js.map +1 -0
- package/dist/superleap-flow.min.js +2 -0
- package/index.d.ts +190 -11
- package/index.js +182 -177
- package/package.json +14 -7
package/core/bridge.js
ADDED
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Superleap-Flow Bridge Transport Layer
|
|
3
|
+
*
|
|
4
|
+
* Handles postMessage / MessageChannel communication between an iframe
|
|
5
|
+
* (running this library) and the host CRM window. Also supports React
|
|
6
|
+
* Native WebView via window.ReactNativeWebView.
|
|
7
|
+
*
|
|
8
|
+
* This module is transport-only — it knows nothing about CRM concepts,
|
|
9
|
+
* the SuperLeap SDK, or FlowUI state. The higher-level core/crm.js
|
|
10
|
+
* builds on top of it.
|
|
11
|
+
*
|
|
12
|
+
* Exposed temporarily as window.SuperleapBridge, then captured into
|
|
13
|
+
* _components and removed from the global scope by index.js.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
(function (global) {
|
|
17
|
+
"use strict";
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Constants
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
var MESSAGE_TYPE = "superleap-bridge";
|
|
24
|
+
var HANDSHAKE_TIMEOUT = 5000; // ms
|
|
25
|
+
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Closure state (reset on each connect / disconnect cycle)
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
var _instanceId = null;
|
|
31
|
+
var _nonce = null;
|
|
32
|
+
var _port = null; // MessagePort (iframe transport, post-handshake)
|
|
33
|
+
var _connected = false;
|
|
34
|
+
var _transport = null; // 'iframe' | 'react-native'
|
|
35
|
+
var _crmOrigin = null; // validated origin (iframe only)
|
|
36
|
+
var _messageListener = null; // window 'message' handler ref
|
|
37
|
+
var _eventHandlers = {}; // { action: [cb, …] }
|
|
38
|
+
var _pendingResolve = null;
|
|
39
|
+
var _pendingReject = null;
|
|
40
|
+
var _handshakeTimer = null;
|
|
41
|
+
var _useMessageChannel = true; // false when MessageChannel unavailable
|
|
42
|
+
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// Helpers – crypto
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
function generateUUID() {
|
|
48
|
+
// crypto.randomUUID where available (modern browsers)
|
|
49
|
+
if (
|
|
50
|
+
typeof crypto !== "undefined" &&
|
|
51
|
+
typeof crypto.randomUUID === "function"
|
|
52
|
+
) {
|
|
53
|
+
return crypto.randomUUID();
|
|
54
|
+
}
|
|
55
|
+
// Fallback: manual v4 UUID via getRandomValues or Math.random
|
|
56
|
+
var bytes;
|
|
57
|
+
if (
|
|
58
|
+
typeof crypto !== "undefined" &&
|
|
59
|
+
typeof crypto.getRandomValues === "function"
|
|
60
|
+
) {
|
|
61
|
+
bytes = new Uint8Array(16);
|
|
62
|
+
crypto.getRandomValues(bytes);
|
|
63
|
+
} else {
|
|
64
|
+
bytes = new Uint8Array(16);
|
|
65
|
+
for (var i = 0; i < 16; i++) {
|
|
66
|
+
bytes[i] = (Math.random() * 256) | 0;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40; // version 4
|
|
70
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 1
|
|
71
|
+
var hex = [];
|
|
72
|
+
for (var j = 0; j < 16; j++) {
|
|
73
|
+
hex.push(("0" + bytes[j].toString(16)).slice(-2));
|
|
74
|
+
}
|
|
75
|
+
return (
|
|
76
|
+
hex.slice(0, 4).join("") +
|
|
77
|
+
"-" +
|
|
78
|
+
hex.slice(4, 6).join("") +
|
|
79
|
+
"-" +
|
|
80
|
+
hex.slice(6, 8).join("") +
|
|
81
|
+
"-" +
|
|
82
|
+
hex.slice(8, 10).join("") +
|
|
83
|
+
"-" +
|
|
84
|
+
hex.slice(10).join("")
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function generateNonce() {
|
|
89
|
+
var bytes;
|
|
90
|
+
if (
|
|
91
|
+
typeof crypto !== "undefined" &&
|
|
92
|
+
typeof crypto.getRandomValues === "function"
|
|
93
|
+
) {
|
|
94
|
+
bytes = new Uint8Array(16);
|
|
95
|
+
crypto.getRandomValues(bytes);
|
|
96
|
+
} else {
|
|
97
|
+
bytes = new Uint8Array(16);
|
|
98
|
+
for (var i = 0; i < 16; i++) {
|
|
99
|
+
bytes[i] = (Math.random() * 256) | 0;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
var hex = "";
|
|
103
|
+
for (var j = 0; j < bytes.length; j++) {
|
|
104
|
+
hex += ("0" + bytes[j].toString(16)).slice(-2);
|
|
105
|
+
}
|
|
106
|
+
return hex;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
// Helpers – transport
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Detect which transport to use.
|
|
115
|
+
* @returns {'react-native'|'iframe'}
|
|
116
|
+
*/
|
|
117
|
+
function detectTransport() {
|
|
118
|
+
if (
|
|
119
|
+
global.ReactNativeWebView &&
|
|
120
|
+
typeof global.ReactNativeWebView.postMessage === "function"
|
|
121
|
+
) {
|
|
122
|
+
return "react-native";
|
|
123
|
+
}
|
|
124
|
+
if (global.parent && global.parent !== global) {
|
|
125
|
+
return "iframe";
|
|
126
|
+
}
|
|
127
|
+
throw new Error(
|
|
128
|
+
"SuperleapBridge: Not running in an embedded context (no parent window or ReactNativeWebView)",
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Wrap action + payload in the standard message envelope.
|
|
134
|
+
*/
|
|
135
|
+
function createEnvelope(action, payload) {
|
|
136
|
+
return {
|
|
137
|
+
type: MESSAGE_TYPE,
|
|
138
|
+
instanceId: _instanceId,
|
|
139
|
+
action: action,
|
|
140
|
+
payload: payload || {},
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Low-level send. Before handshake-complete the message goes via
|
|
146
|
+
* window.parent.postMessage (iframe) or ReactNativeWebView.postMessage (RN).
|
|
147
|
+
* After handshake-complete it goes via the dedicated MessagePort (iframe).
|
|
148
|
+
* RN always uses ReactNativeWebView.postMessage (no MessageChannel).
|
|
149
|
+
*
|
|
150
|
+
* @param {Object} message – envelope object
|
|
151
|
+
* @param {Array} [transfer] – Transferable objects (e.g. [port2])
|
|
152
|
+
*/
|
|
153
|
+
function sendRaw(message, transfer) {
|
|
154
|
+
if (_transport === "react-native") {
|
|
155
|
+
global.ReactNativeWebView.postMessage(JSON.stringify(message));
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// iframe transport
|
|
160
|
+
if (_port) {
|
|
161
|
+
// Post-handshake — private channel
|
|
162
|
+
_port.postMessage(message);
|
|
163
|
+
} else {
|
|
164
|
+
// Pre-handshake or no MessageChannel — broadcast via parent
|
|
165
|
+
var targetOrigin = _crmOrigin || "*";
|
|
166
|
+
if (transfer && transfer.length) {
|
|
167
|
+
global.parent.postMessage(message, targetOrigin, transfer);
|
|
168
|
+
} else {
|
|
169
|
+
global.parent.postMessage(message, targetOrigin);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
// Incoming message handling
|
|
176
|
+
// ---------------------------------------------------------------------------
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Central dispatcher for all incoming messages (both during handshake and
|
|
180
|
+
* after).
|
|
181
|
+
*
|
|
182
|
+
* @param {Object} data – parsed message data
|
|
183
|
+
* @param {string} origin – event.origin (empty string for RN / port msgs)
|
|
184
|
+
*/
|
|
185
|
+
function handleIncomingMessage(data, origin) {
|
|
186
|
+
// Ignore messages that aren't ours
|
|
187
|
+
if (!data || data.type !== MESSAGE_TYPE) return;
|
|
188
|
+
// Ignore messages for a different instance
|
|
189
|
+
if (data.instanceId && data.instanceId !== _instanceId) return;
|
|
190
|
+
|
|
191
|
+
var action = data.action;
|
|
192
|
+
|
|
193
|
+
// ----- Handshake responses -----
|
|
194
|
+
if (action === "handshake-ack" && _pendingResolve) {
|
|
195
|
+
// Validate nonce
|
|
196
|
+
if (!data.payload || data.payload.nonce !== _nonce) {
|
|
197
|
+
if (_pendingReject) {
|
|
198
|
+
_pendingReject(
|
|
199
|
+
new Error(
|
|
200
|
+
"SuperleapBridge: Nonce validation failed (possible replay)",
|
|
201
|
+
),
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
cleanup();
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Validate origin (iframe only)
|
|
209
|
+
if (_transport === "iframe" && _crmOrigin && origin !== _crmOrigin) {
|
|
210
|
+
if (_pendingReject) {
|
|
211
|
+
_pendingReject(
|
|
212
|
+
new Error("SuperleapBridge: Origin validation failed"),
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
cleanup();
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Store validated origin
|
|
220
|
+
if (_transport === "iframe" && origin) {
|
|
221
|
+
_crmOrigin = origin;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
clearTimeout(_handshakeTimer);
|
|
225
|
+
_handshakeTimer = null;
|
|
226
|
+
|
|
227
|
+
// Attempt MessageChannel upgrade (iframe only)
|
|
228
|
+
if (
|
|
229
|
+
_transport === "iframe" &&
|
|
230
|
+
_useMessageChannel &&
|
|
231
|
+
typeof MessageChannel !== "undefined"
|
|
232
|
+
) {
|
|
233
|
+
var channel = new MessageChannel();
|
|
234
|
+
_port = channel.port1;
|
|
235
|
+
_port.onmessage = function (evt) {
|
|
236
|
+
handleIncomingMessage(evt.data, "");
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// Send handshake-complete with port2 transferred
|
|
240
|
+
var completeMsg = createEnvelope("handshake-complete", {});
|
|
241
|
+
global.parent.postMessage(completeMsg, _crmOrigin, [channel.port2]);
|
|
242
|
+
} else {
|
|
243
|
+
// No channel upgrade — send plain handshake-complete
|
|
244
|
+
sendRaw(createEnvelope("handshake-complete", {}));
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
_connected = true;
|
|
248
|
+
_nonce = null; // one-time use
|
|
249
|
+
|
|
250
|
+
// Remove the broad window listener if we have a port now
|
|
251
|
+
if (_port && _messageListener) {
|
|
252
|
+
global.removeEventListener("message", _messageListener);
|
|
253
|
+
_messageListener = null;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
var resolve = _pendingResolve;
|
|
257
|
+
_pendingResolve = null;
|
|
258
|
+
_pendingReject = null;
|
|
259
|
+
resolve(data.payload);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// ----- Post-handshake messages -----
|
|
264
|
+
if (_connected) {
|
|
265
|
+
// Origin check for non-port iframe messages
|
|
266
|
+
if (
|
|
267
|
+
_transport === "iframe" &&
|
|
268
|
+
!_port &&
|
|
269
|
+
_crmOrigin &&
|
|
270
|
+
origin &&
|
|
271
|
+
origin !== _crmOrigin
|
|
272
|
+
) {
|
|
273
|
+
return; // ignore messages from unexpected origins
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
emitEvent(action, data.payload);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Fire callbacks registered for a given action.
|
|
282
|
+
*/
|
|
283
|
+
function emitEvent(action, payload) {
|
|
284
|
+
var handlers = _eventHandlers[action];
|
|
285
|
+
if (!handlers || !handlers.length) return;
|
|
286
|
+
// Iterate over a copy in case a handler removes itself
|
|
287
|
+
var copy = handlers.slice();
|
|
288
|
+
for (var i = 0; i < copy.length; i++) {
|
|
289
|
+
try {
|
|
290
|
+
copy[i](payload);
|
|
291
|
+
} catch (e) {
|
|
292
|
+
console.error("SuperleapBridge: handler error for " + action, e);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// ---------------------------------------------------------------------------
|
|
298
|
+
// Lifecycle helpers
|
|
299
|
+
// ---------------------------------------------------------------------------
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Reset all state to initial values.
|
|
303
|
+
*/
|
|
304
|
+
function cleanup() {
|
|
305
|
+
if (_handshakeTimer) {
|
|
306
|
+
clearTimeout(_handshakeTimer);
|
|
307
|
+
_handshakeTimer = null;
|
|
308
|
+
}
|
|
309
|
+
if (_port) {
|
|
310
|
+
try {
|
|
311
|
+
_port.close();
|
|
312
|
+
} catch (_) {}
|
|
313
|
+
_port = null;
|
|
314
|
+
}
|
|
315
|
+
if (_messageListener) {
|
|
316
|
+
global.removeEventListener("message", _messageListener);
|
|
317
|
+
_messageListener = null;
|
|
318
|
+
}
|
|
319
|
+
if (_transport === "react-native" && global.__superleapBridge) {
|
|
320
|
+
delete global.__superleapBridge;
|
|
321
|
+
}
|
|
322
|
+
_instanceId = null;
|
|
323
|
+
_nonce = null;
|
|
324
|
+
_connected = false;
|
|
325
|
+
_transport = null;
|
|
326
|
+
_crmOrigin = null;
|
|
327
|
+
_pendingResolve = null;
|
|
328
|
+
_pendingReject = null;
|
|
329
|
+
// Event handlers are intentionally preserved across reconnects
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// ---------------------------------------------------------------------------
|
|
333
|
+
// Public API
|
|
334
|
+
// ---------------------------------------------------------------------------
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Initiate a handshake with the host CRM.
|
|
338
|
+
*
|
|
339
|
+
* @param {Object} options
|
|
340
|
+
* @param {string} options.flowId – identifies this flow to the CRM
|
|
341
|
+
* @param {string} [options.version] – library version (informational)
|
|
342
|
+
* @param {string} [options.crmOrigin] – expected CRM origin for validation
|
|
343
|
+
* @param {number} [options.timeout] – handshake timeout in ms (default 5000)
|
|
344
|
+
* @returns {Promise<Object>} resolves with the CRM's handshake-ack payload
|
|
345
|
+
*/
|
|
346
|
+
function connect(options) {
|
|
347
|
+
if (_connected) {
|
|
348
|
+
return Promise.reject(
|
|
349
|
+
new Error(
|
|
350
|
+
"SuperleapBridge: Already connected. Call disconnect() first.",
|
|
351
|
+
),
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
var opts = options || {};
|
|
356
|
+
|
|
357
|
+
return new Promise(function (resolve, reject) {
|
|
358
|
+
try {
|
|
359
|
+
_transport = detectTransport();
|
|
360
|
+
} catch (err) {
|
|
361
|
+
reject(err);
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
_instanceId = generateUUID();
|
|
366
|
+
_nonce = generateNonce();
|
|
367
|
+
_crmOrigin = opts.crmOrigin || null;
|
|
368
|
+
_pendingResolve = resolve;
|
|
369
|
+
_pendingReject = reject;
|
|
370
|
+
_useMessageChannel = typeof MessageChannel !== "undefined";
|
|
371
|
+
|
|
372
|
+
// --- Set up listeners ---
|
|
373
|
+
if (_transport === "iframe") {
|
|
374
|
+
_messageListener = function (event) {
|
|
375
|
+
handleIncomingMessage(event.data, event.origin);
|
|
376
|
+
};
|
|
377
|
+
global.addEventListener("message", _messageListener);
|
|
378
|
+
} else {
|
|
379
|
+
// React Native — register global receiver
|
|
380
|
+
global.__superleapBridge = {
|
|
381
|
+
receive: function (raw) {
|
|
382
|
+
var data;
|
|
383
|
+
if (typeof raw === "string") {
|
|
384
|
+
try {
|
|
385
|
+
data = JSON.parse(raw);
|
|
386
|
+
} catch (_) {
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
} else {
|
|
390
|
+
data = raw;
|
|
391
|
+
}
|
|
392
|
+
handleIncomingMessage(data, "");
|
|
393
|
+
},
|
|
394
|
+
};
|
|
395
|
+
// Also listen on window.message for RN webViewRef.postMessage
|
|
396
|
+
_messageListener = function (event) {
|
|
397
|
+
var data = event.data;
|
|
398
|
+
if (typeof data === "string") {
|
|
399
|
+
try {
|
|
400
|
+
data = JSON.parse(data);
|
|
401
|
+
} catch (_) {
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
handleIncomingMessage(data, "");
|
|
406
|
+
};
|
|
407
|
+
global.addEventListener("message", _messageListener);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// --- Send handshake-request ---
|
|
411
|
+
var envelope = createEnvelope("handshake-request", {
|
|
412
|
+
flowId: opts.flowId || "",
|
|
413
|
+
version: opts.version || "",
|
|
414
|
+
nonce: _nonce,
|
|
415
|
+
});
|
|
416
|
+
sendRaw(envelope);
|
|
417
|
+
|
|
418
|
+
// --- Timeout ---
|
|
419
|
+
var timeoutMs = opts.timeout || HANDSHAKE_TIMEOUT;
|
|
420
|
+
_handshakeTimer = setTimeout(function () {
|
|
421
|
+
if (!_connected) {
|
|
422
|
+
var err = new Error(
|
|
423
|
+
"SuperleapBridge: Handshake timed out after " +
|
|
424
|
+
timeoutMs +
|
|
425
|
+
"ms. Is this page embedded in the SuperLeap CRM?",
|
|
426
|
+
);
|
|
427
|
+
if (_pendingReject) _pendingReject(err);
|
|
428
|
+
cleanup();
|
|
429
|
+
}
|
|
430
|
+
}, timeoutMs);
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Tear down the connection and clean up all listeners.
|
|
436
|
+
*/
|
|
437
|
+
function disconnect() {
|
|
438
|
+
if (_connected) {
|
|
439
|
+
try {
|
|
440
|
+
sendRaw(createEnvelope("disconnect", {}));
|
|
441
|
+
} catch (_) {
|
|
442
|
+
// best-effort
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
cleanup();
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Send a message to the CRM over the established connection.
|
|
450
|
+
*
|
|
451
|
+
* @param {string} action – message action / event name
|
|
452
|
+
* @param {Object} [payload]
|
|
453
|
+
*/
|
|
454
|
+
function send(action, payload) {
|
|
455
|
+
if (!_connected) {
|
|
456
|
+
throw new Error(
|
|
457
|
+
"SuperleapBridge: Not connected. Call connect() first.",
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
sendRaw(createEnvelope(action, payload));
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Register a handler for messages with a given action.
|
|
465
|
+
*
|
|
466
|
+
* @param {string} action
|
|
467
|
+
* @param {Function} callback – receives (payload)
|
|
468
|
+
* @returns {Function} unsubscribe function
|
|
469
|
+
*/
|
|
470
|
+
function onMessage(action, callback) {
|
|
471
|
+
if (!_eventHandlers[action]) {
|
|
472
|
+
_eventHandlers[action] = [];
|
|
473
|
+
}
|
|
474
|
+
_eventHandlers[action].push(callback);
|
|
475
|
+
|
|
476
|
+
return function unsubscribe() {
|
|
477
|
+
offMessage(action, callback);
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Remove a specific handler for an action.
|
|
483
|
+
*/
|
|
484
|
+
function offMessage(action, callback) {
|
|
485
|
+
var handlers = _eventHandlers[action];
|
|
486
|
+
if (!handlers) return;
|
|
487
|
+
var idx = handlers.indexOf(callback);
|
|
488
|
+
if (idx !== -1) {
|
|
489
|
+
handlers.splice(idx, 1);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* @returns {boolean}
|
|
495
|
+
*/
|
|
496
|
+
function isConnected() {
|
|
497
|
+
return _connected;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* @returns {string|null}
|
|
502
|
+
*/
|
|
503
|
+
function getInstanceId() {
|
|
504
|
+
return _instanceId;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// ---------------------------------------------------------------------------
|
|
508
|
+
// Expose
|
|
509
|
+
// ---------------------------------------------------------------------------
|
|
510
|
+
|
|
511
|
+
var SuperleapBridge = {
|
|
512
|
+
connect: connect,
|
|
513
|
+
disconnect: disconnect,
|
|
514
|
+
send: send,
|
|
515
|
+
onMessage: onMessage,
|
|
516
|
+
offMessage: offMessage,
|
|
517
|
+
isConnected: isConnected,
|
|
518
|
+
getInstanceId: getInstanceId,
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
if (global) {
|
|
522
|
+
global.SuperleapBridge = SuperleapBridge;
|
|
523
|
+
}
|
|
524
|
+
})(typeof window !== "undefined" ? window : this);
|