webview.io 1.0.1 → 1.0.2
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 +208 -37
- package/dist/index.d.ts +15 -2
- package/dist/index.js +198 -23
- package/package.json +1 -1
- package/src/index.ts +485 -162
package/dist/index.js
CHANGED
|
@@ -44,11 +44,17 @@ var ackId = function () {
|
|
|
44
44
|
var rmin = 100000, rmax = 999999, timestamp = Date.now(), random = Math.floor(Math.random() * (rmax - rmin + 1) + rmin);
|
|
45
45
|
return "".concat(timestamp, "_").concat(random);
|
|
46
46
|
};
|
|
47
|
+
var generateToken = function () {
|
|
48
|
+
return "".concat(Date.now(), "_").concat(Math.random().toString(36).substring(2, 15));
|
|
49
|
+
};
|
|
47
50
|
var RESERVED_EVENTS = [
|
|
48
51
|
'ping',
|
|
49
52
|
'pong',
|
|
50
53
|
'__heartbeat',
|
|
51
|
-
'__heartbeat_response'
|
|
54
|
+
'__heartbeat_response',
|
|
55
|
+
'__embedded_ready',
|
|
56
|
+
'__connection_ack',
|
|
57
|
+
'__webview_ready'
|
|
52
58
|
];
|
|
53
59
|
var WIO = /** @class */ (function () {
|
|
54
60
|
function WIO(options) {
|
|
@@ -57,11 +63,12 @@ var WIO = /** @class */ (function () {
|
|
|
57
63
|
this.messageRateTracker = [];
|
|
58
64
|
this.reconnectAttempts = 0;
|
|
59
65
|
this.maxReconnectAttempts = 5;
|
|
66
|
+
this.connectionAttempts = 0;
|
|
60
67
|
if (options && typeof options !== 'object')
|
|
61
68
|
throw new Error('Invalid Options');
|
|
62
|
-
this.options = __assign({ debug: false, heartbeatInterval: 30000, connectionTimeout: 10000, maxMessageSize: 1024 * 1024, maxMessagesPerSecond: 100, autoReconnect: true, messageQueueSize: 50 }, options);
|
|
69
|
+
this.options = __assign({ debug: false, heartbeatInterval: 30000, connectionTimeout: 10000, maxMessageSize: 1024 * 1024, maxMessagesPerSecond: 100, autoReconnect: true, messageQueueSize: 50, connectionPingInterval: 2000, maxConnectionAttempts: 5 }, options);
|
|
63
70
|
this.Events = {};
|
|
64
|
-
this.peer = { type: 'WEBVIEW', connected: false };
|
|
71
|
+
this.peer = { type: 'WEBVIEW', connected: false, embeddedReady: false };
|
|
65
72
|
if (options.type)
|
|
66
73
|
this.peer.type = options.type;
|
|
67
74
|
}
|
|
@@ -112,7 +119,9 @@ var WIO = /** @class */ (function () {
|
|
|
112
119
|
if (!this.peer.connected)
|
|
113
120
|
return;
|
|
114
121
|
this.peer.connected = false;
|
|
122
|
+
this.peer.embeddedReady = false;
|
|
115
123
|
this.stopHeartbeat();
|
|
124
|
+
this.stopConnectionAttempt();
|
|
116
125
|
this.fire('disconnect', { reason: 'CONNECTION_LOST' });
|
|
117
126
|
this.options.autoReconnect
|
|
118
127
|
&& this.reconnectAttempts < this.maxReconnectAttempts
|
|
@@ -128,10 +137,19 @@ var WIO = /** @class */ (function () {
|
|
|
128
137
|
this.fire('reconnecting', { attempt: this.reconnectAttempts, delay: delay });
|
|
129
138
|
this.reconnectTimer = setTimeout(function () {
|
|
130
139
|
_this.reconnectTimer = undefined;
|
|
140
|
+
// Reset connection state
|
|
141
|
+
_this.peer.connected = false;
|
|
142
|
+
_this.peer.embeddedReady = false;
|
|
143
|
+
_this.connectionAttempts = 0;
|
|
144
|
+
_this.connectionToken = generateToken();
|
|
131
145
|
// Re-initiate connection for WEBVIEW type
|
|
132
|
-
_this.peer.type === 'WEBVIEW'
|
|
133
|
-
|
|
134
|
-
|
|
146
|
+
if (_this.peer.type === 'WEBVIEW') {
|
|
147
|
+
_this.startConnectionAttempt();
|
|
148
|
+
}
|
|
149
|
+
// For EMBEDDED type, announce readiness
|
|
150
|
+
if (_this.peer.type === 'EMBEDDED') {
|
|
151
|
+
_this.announceEmbeddedReady();
|
|
152
|
+
}
|
|
135
153
|
// Set timeout for this reconnection attempt
|
|
136
154
|
setTimeout(function () {
|
|
137
155
|
if (_this.peer.connected)
|
|
@@ -142,6 +160,82 @@ var WIO = /** @class */ (function () {
|
|
|
142
160
|
}, _this.options.connectionTimeout);
|
|
143
161
|
}, delay);
|
|
144
162
|
};
|
|
163
|
+
// Start connection attempt with timeout and retries
|
|
164
|
+
WIO.prototype.startConnectionAttempt = function () {
|
|
165
|
+
var _this = this;
|
|
166
|
+
this.stopConnectionAttempt();
|
|
167
|
+
this.debug("[".concat(this.peer.type, "] Starting connection attempt"));
|
|
168
|
+
// Send initial ping
|
|
169
|
+
this.emit('ping', { token: this.connectionToken });
|
|
170
|
+
// Set up periodic ping until connected
|
|
171
|
+
this.connectionPingInterval = setInterval(function () {
|
|
172
|
+
if (!_this.peer.connected) {
|
|
173
|
+
_this.connectionAttempts++;
|
|
174
|
+
if (_this.connectionAttempts >= _this.options.maxConnectionAttempts) {
|
|
175
|
+
_this.debug("[".concat(_this.peer.type, "] Max connection attempts reached"));
|
|
176
|
+
_this.stopConnectionAttempt();
|
|
177
|
+
_this.fire('connect_timeout', { attempts: _this.connectionAttempts });
|
|
178
|
+
_this.options.autoReconnect && _this.attemptReconnection();
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
_this.debug("[".concat(_this.peer.type, "] Connection attempt ").concat(_this.connectionAttempts, "/").concat(_this.options.maxConnectionAttempts));
|
|
182
|
+
_this.emit('ping', { token: _this.connectionToken });
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
_this.stopConnectionAttempt();
|
|
186
|
+
}
|
|
187
|
+
}, this.options.connectionPingInterval);
|
|
188
|
+
// Set overall timeout
|
|
189
|
+
this.connectionAttemptTimer = setTimeout(function () {
|
|
190
|
+
if (!_this.peer.connected) {
|
|
191
|
+
_this.debug("[".concat(_this.peer.type, "] Connection timeout after ").concat(_this.options.connectionTimeout, "ms"));
|
|
192
|
+
_this.stopConnectionAttempt();
|
|
193
|
+
_this.fire('connect_timeout', { attempts: _this.connectionAttempts });
|
|
194
|
+
_this.options.autoReconnect && _this.attemptReconnection();
|
|
195
|
+
}
|
|
196
|
+
}, this.options.connectionTimeout);
|
|
197
|
+
};
|
|
198
|
+
WIO.prototype.stopConnectionAttempt = function () {
|
|
199
|
+
if (this.connectionPingInterval) {
|
|
200
|
+
clearInterval(this.connectionPingInterval);
|
|
201
|
+
this.connectionPingInterval = undefined;
|
|
202
|
+
}
|
|
203
|
+
if (this.connectionAttemptTimer) {
|
|
204
|
+
clearTimeout(this.connectionAttemptTimer);
|
|
205
|
+
this.connectionAttemptTimer = undefined;
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
// For EMBEDDED side to announce readiness
|
|
209
|
+
WIO.prototype.announceEmbeddedReady = function () {
|
|
210
|
+
var _this = this;
|
|
211
|
+
this.stopEmbeddedReadyAnnouncement();
|
|
212
|
+
var attempts = 0;
|
|
213
|
+
var maxAttempts = this.options.maxConnectionAttempts || 5;
|
|
214
|
+
this.debug("[".concat(this.peer.type, "] Announcing embedded ready"));
|
|
215
|
+
this.emit('__embedded_ready');
|
|
216
|
+
this.embeddedReadyCheckInterval = setInterval(function () {
|
|
217
|
+
if (!_this.peer.connected) {
|
|
218
|
+
attempts++;
|
|
219
|
+
if (attempts >= maxAttempts) {
|
|
220
|
+
_this.debug("[".concat(_this.peer.type, "] Max ready announcement attempts reached"));
|
|
221
|
+
_this.stopEmbeddedReadyAnnouncement();
|
|
222
|
+
_this.fire('connect_timeout', { attempts: attempts });
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
_this.debug("[".concat(_this.peer.type, "] Ready announcement attempt ").concat(attempts, "/").concat(maxAttempts));
|
|
226
|
+
_this.emit('__embedded_ready');
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
_this.stopEmbeddedReadyAnnouncement();
|
|
230
|
+
}
|
|
231
|
+
}, this.options.connectionPingInterval);
|
|
232
|
+
};
|
|
233
|
+
WIO.prototype.stopEmbeddedReadyAnnouncement = function () {
|
|
234
|
+
if (this.embeddedReadyCheckInterval) {
|
|
235
|
+
clearInterval(this.embeddedReadyCheckInterval);
|
|
236
|
+
this.embeddedReadyCheckInterval = undefined;
|
|
237
|
+
}
|
|
238
|
+
};
|
|
145
239
|
// Message rate limiting
|
|
146
240
|
WIO.prototype.checkRateLimit = function () {
|
|
147
241
|
if (!this.options.maxMessagesPerSecond)
|
|
@@ -206,25 +300,33 @@ var WIO = /** @class */ (function () {
|
|
|
206
300
|
this.peer.webViewRef = webViewRef;
|
|
207
301
|
this.peer.origin = origin;
|
|
208
302
|
this.peer.connected = false;
|
|
303
|
+
this.peer.embeddedReady = false;
|
|
209
304
|
this.reconnectAttempts = 0;
|
|
305
|
+
this.connectionAttempts = 0;
|
|
306
|
+
this.connectionToken = generateToken();
|
|
210
307
|
this.debug("[".concat(this.peer.type, "] Initiate connection: WebView origin <").concat(origin, ">"));
|
|
211
|
-
|
|
308
|
+
// Start connection attempt with timeout and retries
|
|
309
|
+
this.startConnectionAttempt();
|
|
212
310
|
return this;
|
|
213
311
|
};
|
|
214
312
|
/**
|
|
215
313
|
* Listening to connection from the WebView host
|
|
216
|
-
* Note: In React Native context, this is handled by injected JavaScript
|
|
217
314
|
*/
|
|
218
315
|
WIO.prototype.listen = function (hostOrigin) {
|
|
219
|
-
|
|
316
|
+
var _this = this;
|
|
317
|
+
this.peer.type = 'EMBEDDED';
|
|
220
318
|
this.peer.connected = false;
|
|
319
|
+
this.peer.embeddedReady = false;
|
|
221
320
|
this.reconnectAttempts = 0;
|
|
222
321
|
this.debug("[".concat(this.peer.type, "] Listening to connect").concat(hostOrigin ? ": Host <".concat(hostOrigin, ">") : ''));
|
|
322
|
+
// Start announcing readiness
|
|
323
|
+
setTimeout(function () {
|
|
324
|
+
_this.announceEmbeddedReady();
|
|
325
|
+
}, 100);
|
|
223
326
|
return this;
|
|
224
327
|
};
|
|
225
328
|
/**
|
|
226
329
|
* Handle incoming message from WebView
|
|
227
|
-
* Called by React Native component via onMessage prop
|
|
228
330
|
*/
|
|
229
331
|
WIO.prototype.handleMessage = function (event) {
|
|
230
332
|
try {
|
|
@@ -232,7 +334,15 @@ var WIO = /** @class */ (function () {
|
|
|
232
334
|
// Enhanced security: check valid message structure
|
|
233
335
|
if (typeof data !== 'object' || !data.hasOwnProperty('_event'))
|
|
234
336
|
return;
|
|
235
|
-
var _a = data, _event = _a._event, payload = _a.payload, cid = _a.cid, timestamp = _a.timestamp;
|
|
337
|
+
var _a = data, _event = _a._event, payload = _a.payload, cid = _a.cid, timestamp = _a.timestamp, token = _a.token;
|
|
338
|
+
// Validate origin if specified
|
|
339
|
+
if (this.peer.origin && event.nativeEvent && 'origin' in event.nativeEvent) {
|
|
340
|
+
var messageOrigin = event.nativeEvent.origin;
|
|
341
|
+
if (messageOrigin && messageOrigin !== this.peer.origin) {
|
|
342
|
+
this.debug("[".concat(this.peer.type, "] Message from unauthorized origin: ").concat(messageOrigin));
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
236
346
|
// Handle heartbeat responses
|
|
237
347
|
if (_event === '__heartbeat_response') {
|
|
238
348
|
this.peer.lastHeartbeat = Date.now();
|
|
@@ -244,17 +354,74 @@ var WIO = /** @class */ (function () {
|
|
|
244
354
|
this.peer.lastHeartbeat = Date.now();
|
|
245
355
|
return;
|
|
246
356
|
}
|
|
357
|
+
// Handle embedded ready announcement
|
|
358
|
+
if (_event === '__embedded_ready') {
|
|
359
|
+
this.peer.embeddedReady = true;
|
|
360
|
+
this.debug("[".concat(this.peer.type, "] Embedded peer ready"));
|
|
361
|
+
// If we're WEBVIEW and not connected, send ping
|
|
362
|
+
if (this.peer.type === 'WEBVIEW' && !this.peer.connected) {
|
|
363
|
+
this.emit('ping', { token: this.connectionToken });
|
|
364
|
+
}
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
// Handle webview ready signal
|
|
368
|
+
if (_event === '__webview_ready') {
|
|
369
|
+
this.debug("[".concat(this.peer.type, "] WebView peer ready"));
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
247
372
|
this.debug("[".concat(this.peer.type, "] Message: ").concat(_event), payload || '');
|
|
248
|
-
// Handshake
|
|
249
|
-
if (_event
|
|
250
|
-
//
|
|
251
|
-
this.peer.
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
373
|
+
// Handshake: ping event
|
|
374
|
+
if (_event === 'ping') {
|
|
375
|
+
// EMBEDDED receives ping from WEBVIEW
|
|
376
|
+
if (this.peer.type === 'EMBEDDED') {
|
|
377
|
+
this.connectionToken = token;
|
|
378
|
+
this.emit('pong', { token: this.connectionToken });
|
|
379
|
+
// Don't set fully connected yet - wait for ack
|
|
380
|
+
this.debug("[".concat(this.peer.type, "] Received ping, sent pong"));
|
|
381
|
+
}
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
// Handshake: pong event
|
|
385
|
+
if (_event === 'pong') {
|
|
386
|
+
// WEBVIEW receives pong from EMBEDDED
|
|
387
|
+
if (this.peer.type === 'WEBVIEW') {
|
|
388
|
+
// Validate token if provided
|
|
389
|
+
if (token && token !== this.connectionToken) {
|
|
390
|
+
this.debug("[".concat(this.peer.type, "] Invalid connection token in pong"));
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
this.peer.connected = true;
|
|
394
|
+
this.reconnectAttempts = 0;
|
|
395
|
+
this.connectionAttempts = 0;
|
|
396
|
+
this.peer.lastHeartbeat = Date.now();
|
|
397
|
+
// Send connection acknowledgment to complete 3-way handshake
|
|
398
|
+
this.emit('__connection_ack', { token: this.connectionToken });
|
|
399
|
+
this.stopConnectionAttempt();
|
|
400
|
+
this.startHeartbeat();
|
|
401
|
+
this.fire('connect');
|
|
402
|
+
this.processMessageQueue();
|
|
403
|
+
this.debug("[".concat(this.peer.type, "] Connected (3-way handshake complete)"));
|
|
404
|
+
}
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
// Handshake: connection ack
|
|
408
|
+
if (_event === '__connection_ack') {
|
|
409
|
+
// EMBEDDED receives ack from WEBVIEW
|
|
410
|
+
if (this.peer.type === 'EMBEDDED') {
|
|
411
|
+
// Validate token if provided
|
|
412
|
+
if (token && token !== this.connectionToken) {
|
|
413
|
+
this.debug("[".concat(this.peer.type, "] Invalid connection token in ack"));
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
this.peer.connected = true;
|
|
417
|
+
this.reconnectAttempts = 0;
|
|
418
|
+
this.peer.lastHeartbeat = Date.now();
|
|
419
|
+
this.stopEmbeddedReadyAnnouncement();
|
|
420
|
+
this.startHeartbeat();
|
|
421
|
+
this.fire('connect');
|
|
422
|
+
this.processMessageQueue();
|
|
423
|
+
this.debug("[".concat(this.peer.type, "] Connected (received ack)"));
|
|
424
|
+
}
|
|
258
425
|
return;
|
|
259
426
|
}
|
|
260
427
|
// Fire available event listeners
|
|
@@ -351,7 +518,8 @@ var WIO = /** @class */ (function () {
|
|
|
351
518
|
payload: sanitizedPayload,
|
|
352
519
|
cid: cid,
|
|
353
520
|
timestamp: Date.now(),
|
|
354
|
-
size: getMessageSize(sanitizedPayload)
|
|
521
|
+
size: getMessageSize(sanitizedPayload),
|
|
522
|
+
token: RESERVED_EVENTS.includes(_event) ? this.connectionToken : undefined
|
|
355
523
|
};
|
|
356
524
|
(_a = this.peer.webViewRef.current) === null || _a === void 0 ? void 0 : _a.postMessage(JSON.stringify(newObject(messageData)));
|
|
357
525
|
}
|
|
@@ -459,6 +627,8 @@ var WIO = /** @class */ (function () {
|
|
|
459
627
|
// Clean up all resources
|
|
460
628
|
WIO.prototype.cleanup = function () {
|
|
461
629
|
this.stopHeartbeat();
|
|
630
|
+
this.stopConnectionAttempt();
|
|
631
|
+
this.stopEmbeddedReadyAnnouncement();
|
|
462
632
|
if (this.reconnectTimer) {
|
|
463
633
|
clearTimeout(this.reconnectTimer);
|
|
464
634
|
this.reconnectTimer = undefined;
|
|
@@ -468,12 +638,15 @@ var WIO = /** @class */ (function () {
|
|
|
468
638
|
// Cleanup on disconnect
|
|
469
639
|
this.cleanup();
|
|
470
640
|
this.peer.connected = false;
|
|
641
|
+
this.peer.embeddedReady = false;
|
|
471
642
|
this.peer.webViewRef = undefined;
|
|
472
643
|
this.peer.origin = undefined;
|
|
473
644
|
this.peer.lastHeartbeat = undefined;
|
|
474
645
|
this.messageQueue = [];
|
|
475
646
|
this.messageRateTracker = [];
|
|
476
647
|
this.reconnectAttempts = 0;
|
|
648
|
+
this.connectionAttempts = 0;
|
|
649
|
+
this.connectionToken = undefined;
|
|
477
650
|
this.removeListeners();
|
|
478
651
|
typeof fn == 'function' && fn();
|
|
479
652
|
this.debug("[".concat(this.peer.type, "] Disconnected"));
|
|
@@ -483,11 +656,13 @@ var WIO = /** @class */ (function () {
|
|
|
483
656
|
WIO.prototype.getStats = function () {
|
|
484
657
|
return {
|
|
485
658
|
connected: this.isConnected(),
|
|
659
|
+
embeddedReady: this.peer.embeddedReady,
|
|
486
660
|
peerType: this.peer.type,
|
|
487
661
|
origin: this.peer.origin,
|
|
488
662
|
lastHeartbeat: this.peer.lastHeartbeat,
|
|
489
663
|
queuedMessages: this.messageQueue.length,
|
|
490
664
|
reconnectAttempts: this.reconnectAttempts,
|
|
665
|
+
connectionAttempts: this.connectionAttempts,
|
|
491
666
|
activeListeners: Object.keys(this.Events).length,
|
|
492
667
|
messageRate: this.messageRateTracker.length
|
|
493
668
|
};
|
|
@@ -504,7 +679,7 @@ var WIO = /** @class */ (function () {
|
|
|
504
679
|
* Sets up the EMBEDDED side of the bridge
|
|
505
680
|
*/
|
|
506
681
|
WIO.prototype.getInjectedJavaScript = function () {
|
|
507
|
-
return "\n (function() {\n alert('[EMBEDDED] Injected JavaScript')\n const RESERVED_EVENTS = ['ping', 'pong', '__heartbeat', '__heartbeat_response'];\n \n window._wio = {\n type: 'EMBEDDED',\n connected: false,\n Events: {},\n messageQueue: [],\n\n listen: function(){\n console.log('[EMBEDDED] Listening for messages...')\n\n // Listen to messages from React Native\n window.addEventListener('message', function( event ){\n try {\n console.log('[EMBEDDED] Received message:', event.data);\n const message = JSON.parse( event.data );\n window._wio.handleMessage( message );\n }\n catch( error ){ console.error('[EMBEDDED] Parse error:', error); }\n });\n \n // Android support\n if( typeof document !== 'undefined' ){\n document.addEventListener('message', function( event ){\n try {\n console.log('[EMBEDDED] Received message:', event.data);\n const message = JSON.parse( event.data );\n window._wio.handleMessage( message );\n }\n catch( error ){ console.error('[EMBEDDED] Parse error:', error); }\n });\n }\n },\n \n ackId: function(){\n const\n rmin = 100000,\n rmax = 999999,\n timestamp = Date.now(),\n random = Math.floor( Math.random() * ( rmax - rmin + 1 ) + rmin );\n\n return timestamp + '_' + random;\n },\n \n fire: function( _event, payload, cid ){\n if( !this.Events[_event] && !this.Events[_event + '--@once'] ) return;\n \n const ackFn = cid\n ? ( error, ...args ) => {\n this.emit( _event + '--' + cid + '--@ack', { error: error || false, args } );\n }\n : undefined;\n \n let listeners = [];\n if( this.Events[_event + '--@once'] ){\n _event += '--@once';\n listeners = this.Events[_event];\n delete this.Events[_event];\n }\n else listeners = this.Events[_event] || [];\n \n listeners.forEach( fn => {\n try { payload !== undefined ? fn( payload, ackFn ) : fn( ackFn ); }\n catch( error ){ console.error('[EMBEDDED] Listener error:', error); }\n });\n },\n \n emit: function( _event, payload, fn ){\n if( typeof payload === 'function' ){\n fn = payload;\n payload = undefined;\n }\n \n if( !this.connected && !RESERVED_EVENTS.includes(_event) ){\n this.messageQueue.push({ _event, payload, fn, timestamp: Date.now() });\n return;\n }\n \n try {\n let cid;\n if( typeof fn === 'function' ){\n cid = this.ackId();\n this.once( _event + '--' + cid + '--@ack', ({ error, args }) => fn( error, ...args ) );\n }\n \n const messageData = {\n _event,\n payload,\n cid,\n timestamp: Date.now()\n };\n \n window.ReactNativeWebView.postMessage( JSON.stringify( messageData ) );\n }\n catch( error ){\n console.error('[EMBEDDED] Emit error:', error);\n typeof fn === 'function' && fn( String(error) );\n }\n },\n \n on: function( _event, fn ){\n if( !this.Events[_event] ) this.Events[_event] = [];\n this.Events[_event].push( fn );\n },\n \n once: function( _event, fn ){\n _event += '--@once';\n if( !this.Events[_event] ) this.Events[_event] = [];\n this.Events[_event].push( fn );\n },\n \n off: function( _event, fn ){\n if( fn && this.Events[_event] ){\n const index = this.Events[_event].indexOf( fn );\n if( index > -1 ){\n this.Events[_event].splice( index, 1 );\n if( this.Events[_event].length === 0 ) delete this.Events[_event];\n }\n }\n else delete this.Events[_event];\n },\n \n processMessageQueue: function(){\n if( !this.connected || this.messageQueue.length === 0 ) return;\n \n const queue = [...this.messageQueue];\n this.messageQueue = [];\n \n queue.forEach( msg => {\n try { this.emit( msg._event, msg.payload, msg.fn ); }\n catch( error ){ console.error('[EMBEDDED] Queue process error:', error); }\n });\n },\n \n handleMessage: function( data ){\n if( !data || !data._event ) return;\n \n const { _event, payload, cid } = data;\n \n if( _event === '__heartbeat_response' ) return;\n \n if( _event === '__heartbeat' ){\n this.emit('__heartbeat_response', { timestamp: Date.now() });\n return;\n }\n \n if( _event === 'ping' ){\n this.emit('pong');\n this.connected = true;\n this.processMessageQueue();\n return;\n }\n \n this.fire( _event, payload, cid );\n }\n };\n\n true;\n })();\n ";
|
|
682
|
+
return "\n (function() {\n try {\n console.log('[EMBEDDED] Initializing WIO bridge...');\n \n const RESERVED_EVENTS = ['ping', 'pong', '__heartbeat', '__heartbeat_response', '__embedded_ready', '__connection_ack', '__webview_ready'];\n \n window._wio = {\n type: 'EMBEDDED',\n connected: false,\n Events: {},\n messageQueue: [],\n connectionToken: null,\n setupComplete: false,\n\n listen: function(){\n console.log('[EMBEDDED] Setting up message listeners...');\n\n // Listen to messages from React Native\n window.addEventListener('message', function( event ){\n try {\n const message = typeof event.data === 'string' ? JSON.parse( event.data ) : event.data;\n window._wio.handleMessage( message );\n }\n catch( error ){ \n console.error('[EMBEDDED] Parse error:', error); \n }\n });\n \n // Android support\n if( typeof document !== 'undefined' ){\n document.addEventListener('message', function( event ){\n try {\n const message = typeof event.data === 'string' ? JSON.parse( event.data ) : event.data;\n window._wio.handleMessage( message );\n }\n catch( error ){ \n console.error('[EMBEDDED] Parse error:', error); \n }\n });\n }\n\n this.setupComplete = true;\n console.log('[EMBEDDED] Setup complete');\n },\n \n ackId: function(){\n const\n rmin = 100000,\n rmax = 999999,\n timestamp = Date.now(),\n random = Math.floor( Math.random() * ( rmax - rmin + 1 ) + rmin );\n\n return timestamp + '_' + random;\n },\n \n fire: function( _event, payload, cid ){\n if( !this.Events[_event] && !this.Events[_event + '--@once'] ){\n console.log('[EMBEDDED] No listener for:', _event);\n return;\n }\n \n const ackFn = cid\n ? ( error, ...args ) => {\n this.emit( _event + '--' + cid + '--@ack', { error: error || false, args } );\n }\n : undefined;\n \n let listeners = [];\n if( this.Events[_event + '--@once'] ){\n _event += '--@once';\n listeners = this.Events[_event];\n delete this.Events[_event];\n }\n else listeners = this.Events[_event] || [];\n \n listeners.forEach( fn => {\n try { \n payload !== undefined ? fn( payload, ackFn ) : fn( ackFn ); \n }\n catch( error ){ \n console.error('[EMBEDDED] Listener error:', error); \n }\n });\n },\n \n emit: function( _event, payload, fn ){\n if( typeof payload === 'function' ){\n fn = payload;\n payload = undefined;\n }\n \n if( !this.connected && !RESERVED_EVENTS.includes(_event) ){\n this.messageQueue.push({ _event, payload, fn, timestamp: Date.now() });\n console.log('[EMBEDDED] Queued message:', _event);\n return;\n }\n \n try {\n let cid;\n if( typeof fn === 'function' ){\n cid = this.ackId();\n this.once( _event + '--' + cid + '--@ack', ({ error, args }) => fn( error, ...args ) );\n }\n \n const messageData = {\n _event,\n payload,\n cid,\n timestamp: Date.now(),\n token: RESERVED_EVENTS.includes(_event) ? this.connectionToken : undefined\n };\n \n if( typeof window.ReactNativeWebView !== 'undefined' ){\n window.ReactNativeWebView.postMessage( JSON.stringify( messageData ) );\n }\n else {\n console.error('[EMBEDDED] ReactNativeWebView not available');\n }\n }\n catch( error ){\n console.error('[EMBEDDED] Emit error:', error);\n typeof fn === 'function' && fn( String(error) );\n }\n },\n \n on: function( _event, fn ){\n if( !this.Events[_event] ) this.Events[_event] = [];\n this.Events[_event].push( fn );\n },\n \n once: function( _event, fn ){\n _event += '--@once';\n if( !this.Events[_event] ) this.Events[_event] = [];\n this.Events[_event].push( fn );\n },\n \n off: function( _event, fn ){\n if( fn && this.Events[_event] ){\n const index = this.Events[_event].indexOf( fn );\n if( index > -1 ){\n this.Events[_event].splice( index, 1 );\n if( this.Events[_event].length === 0 ) delete this.Events[_event];\n }\n }\n else delete this.Events[_event];\n },\n \n processMessageQueue: function(){\n if( !this.connected || this.messageQueue.length === 0 ) return;\n \n console.log('[EMBEDDED] Processing', this.messageQueue.length, 'queued messages');\n const queue = [...this.messageQueue];\n this.messageQueue = [];\n \n queue.forEach( msg => {\n try { \n this.emit( msg._event, msg.payload, msg.fn ); \n }\n catch( error ){ \n console.error('[EMBEDDED] Queue process error:', error); \n }\n });\n },\n \n handleMessage: function( data ){\n if( !data || !data._event ) return;\n \n const { _event, payload, cid, token } = data;\n \n console.log('[EMBEDDED] Received:', _event);\n \n // Handle heartbeat response\n if( _event === '__heartbeat_response' ){\n return;\n }\n \n // Handle heartbeat request\n if( _event === '__heartbeat' ){\n this.emit('__heartbeat_response', { timestamp: Date.now() });\n return;\n }\n \n // Handle webview ready signal\n if( _event === '__webview_ready' ){\n console.log('[EMBEDDED] WebView ready signal received');\n return;\n }\n \n // Handle ping from WEBVIEW\n if( _event === 'ping' ){\n console.log('[EMBEDDED] Received ping, sending pong');\n this.connectionToken = token;\n this.emit('pong', { token: this.connectionToken });\n return;\n }\n \n // Handle connection acknowledgment\n if( _event === '__connection_ack' ){\n if( token && token !== this.connectionToken ){\n console.error('[EMBEDDED] Invalid connection token in ack');\n return;\n }\n \n console.log('[EMBEDDED] Connection established (received ack)');\n this.connected = true;\n this.processMessageQueue();\n this.fire('connect');\n return;\n }\n \n // Fire event listeners\n this.fire( _event, payload, cid );\n },\n \n announceReady: function(){\n let attempts = 0;\n const maxAttempts = 5;\n const interval = 2000;\n \n console.log('[EMBEDDED] Starting ready announcements');\n \n const announce = () => {\n if( this.connected ){\n console.log('[EMBEDDED] Connected, stopping announcements');\n return;\n }\n \n attempts++;\n if( attempts > maxAttempts ){\n console.log('[EMBEDDED] Max announcement attempts reached');\n return;\n }\n \n console.log('[EMBEDDED] Announcing ready (attempt', attempts + '/' + maxAttempts + ')');\n this.emit('__embedded_ready');\n \n setTimeout( announce, interval );\n };\n \n announce();\n }\n };\n\n // Initialize\n window._wio.listen();\n \n // Start announcing readiness after a short delay\n setTimeout(() => {\n window._wio.announceReady();\n }, 100);\n \n console.log('[EMBEDDED] WIO bridge initialized successfully');\n }\n catch( error ){\n console.error('[EMBEDDED] Setup failed:', error);\n \n // Create minimal fallback\n window._wio = {\n error: error.toString(),\n emit: function(){ console.error('[EMBEDDED] WIO failed to initialize'); },\n on: function(){},\n once: function(){},\n off: function(){}\n };\n }\n\n true;\n })();\n ";
|
|
508
683
|
};
|
|
509
684
|
return WIO;
|
|
510
685
|
}());
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "webview.io",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Easy and friendly API to connect and interact between React Native and WebView with enhanced security, reliability, and modern async/await support.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|