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/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
- && _this.emit('ping');
134
- // For EMBEDDED type, just wait for incoming connection
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
- this.emit('ping');
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
- this.peer.type = 'EMBEDDED'; // iframe.io-rn connection listener is automatically set as EMBEDDED
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 or availability check events
249
- if (_event == 'pong') {
250
- // WebView is connected
251
- this.peer.connected = true;
252
- this.reconnectAttempts = 0;
253
- this.peer.lastHeartbeat = Date.now();
254
- this.startHeartbeat();
255
- this.fire('connect');
256
- this.processMessageQueue();
257
- this.debug("[".concat(this.peer.type, "] connected"));
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.1",
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",