webview.io 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +496 -0
- package/dist/index.d.ts +107 -0
- package/dist/index.js +511 -0
- package/package.json +69 -0
- package/src/index.ts +768 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Fabrice K.M. Ekpetse
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
# webview.io
|
|
2
|
+
|
|
3
|
+
Easy and friendly API to connect and interact between React Native applications and WebView content with enhanced security, reliability, and modern async/await support.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Bidirectional Communication**: Seamless messaging between React Native and WebView
|
|
8
|
+
- **Enhanced Security**: Origin validation, message sanitization, and payload size limits
|
|
9
|
+
- **Auto-Reconnection**: Automatic reconnection with exponential backoff strategy
|
|
10
|
+
- **Heartbeat Monitoring**: Connection health monitoring with configurable intervals
|
|
11
|
+
- **Message Queuing**: Queue messages when disconnected and replay on reconnection
|
|
12
|
+
- **Rate Limiting**: Configurable message rate limiting to prevent spam
|
|
13
|
+
- **Promise Support**: Modern async/await APIs with timeout handling
|
|
14
|
+
- **Connection Statistics**: Real-time connection and performance metrics
|
|
15
|
+
- **Comprehensive Error Handling**: Detailed error types and handling mechanisms
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install webview.io react-native-webview
|
|
21
|
+
# or
|
|
22
|
+
yarn add webview.io react-native-webview
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Peer Dependencies
|
|
26
|
+
|
|
27
|
+
This package requires the following peer dependencies:
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"react": ">=16.8.0",
|
|
32
|
+
"react-native-webview": ">=11.0.0"
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Make sure these are installed in your React Native project.
|
|
37
|
+
|
|
38
|
+
## Basic Usage
|
|
39
|
+
|
|
40
|
+
### React Native Side (WEBVIEW peer)
|
|
41
|
+
|
|
42
|
+
```javascript
|
|
43
|
+
import React, { useRef, useEffect } from 'react'
|
|
44
|
+
import { View } from 'react-native'
|
|
45
|
+
import { WebView } from 'react-native-webview'
|
|
46
|
+
import WIO from 'webview.io'
|
|
47
|
+
|
|
48
|
+
function MapComponent() {
|
|
49
|
+
const webViewRef = useRef(null)
|
|
50
|
+
const wioRef = useRef(null)
|
|
51
|
+
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
// Initialize bridge
|
|
54
|
+
wioRef.current = new WIO({
|
|
55
|
+
type: 'WEBVIEW',
|
|
56
|
+
debug: true
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
// Establish connection
|
|
60
|
+
wioRef.current.initiate(webViewRef, 'https://your-webview-content.com')
|
|
61
|
+
|
|
62
|
+
// Listen for connection
|
|
63
|
+
wioRef.current.on('connect', () => {
|
|
64
|
+
console.log('Connected to WebView!')
|
|
65
|
+
|
|
66
|
+
// Send a message
|
|
67
|
+
wioRef.current.emit('hello', { message: 'Hello from React Native!' })
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
// Listen for messages
|
|
71
|
+
wioRef.current.on('response', (data) => {
|
|
72
|
+
console.log('Received:', data)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
return () => {
|
|
76
|
+
wioRef.current?.disconnect()
|
|
77
|
+
}
|
|
78
|
+
}, [])
|
|
79
|
+
|
|
80
|
+
const handleMessage = (event) => {
|
|
81
|
+
wioRef.current?.handleMessage(event)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<View style={{ flex: 1 }}>
|
|
86
|
+
<WebView
|
|
87
|
+
ref={webViewRef}
|
|
88
|
+
source={{ uri: 'https://your-webview-content.com' }}
|
|
89
|
+
onMessage={handleMessage}
|
|
90
|
+
injectedJavaScript={wioRef.current?.getInjectedJavaScript()}
|
|
91
|
+
javaScriptEnabled={true}
|
|
92
|
+
/>
|
|
93
|
+
</View>
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### WebView Content Side (EMBEDDED peer)
|
|
99
|
+
|
|
100
|
+
```javascript
|
|
101
|
+
// In your HTML/JS loaded in the WebView
|
|
102
|
+
|
|
103
|
+
// The bridge is automatically available as window._wio
|
|
104
|
+
const wio = window._wio
|
|
105
|
+
|
|
106
|
+
// Handle connection
|
|
107
|
+
wio.on('connect', () => {
|
|
108
|
+
console.log('Connected to React Native!')
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
// Listen for messages
|
|
112
|
+
wio.on('hello', (data) => {
|
|
113
|
+
console.log('Received:', data)
|
|
114
|
+
|
|
115
|
+
// Send response
|
|
116
|
+
wio.emit('response', { received: true })
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
// Send messages to React Native
|
|
120
|
+
wio.emit('ready')
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Enhanced Configuration Options
|
|
124
|
+
|
|
125
|
+
```javascript
|
|
126
|
+
const wio = new WIO({
|
|
127
|
+
type: 'WEBVIEW', // 'WEBVIEW' or 'EMBEDDED'
|
|
128
|
+
debug: false, // Enable debug logging
|
|
129
|
+
heartbeatInterval: 30000, // Heartbeat interval in ms (30s)
|
|
130
|
+
connectionTimeout: 10000, // Connection timeout in ms (10s)
|
|
131
|
+
maxMessageSize: 1024 * 1024, // Max message size in bytes (1MB)
|
|
132
|
+
maxMessagesPerSecond: 100, // Rate limit (100 messages/second)
|
|
133
|
+
autoReconnect: true, // Enable automatic reconnection
|
|
134
|
+
messageQueueSize: 50 // Max queued messages when disconnected
|
|
135
|
+
})
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Async/Await Support
|
|
139
|
+
|
|
140
|
+
### Send Messages with Acknowledgments
|
|
141
|
+
|
|
142
|
+
```javascript
|
|
143
|
+
// React Native side
|
|
144
|
+
try {
|
|
145
|
+
const response = await wioRef.current.emitAsync('getData', { id: 123 }, 10000)
|
|
146
|
+
console.log('Response:', response)
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.error('Request failed:', error.message)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// WebView side - Listen and acknowledge
|
|
152
|
+
window._wio.on('getData', async (data, ack) => {
|
|
153
|
+
try {
|
|
154
|
+
const result = await fetchData(data.id)
|
|
155
|
+
ack(false, result) // Success: ack(error, ...response)
|
|
156
|
+
} catch (error) {
|
|
157
|
+
ack(error.message) // Error: ack(errorMessage)
|
|
158
|
+
}
|
|
159
|
+
})
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Wait for Connection
|
|
163
|
+
|
|
164
|
+
```javascript
|
|
165
|
+
// Wait for connection with timeout
|
|
166
|
+
try {
|
|
167
|
+
await wioRef.current.connectAsync(5000) // 5 second timeout
|
|
168
|
+
console.log('Connection established!')
|
|
169
|
+
} catch (error) {
|
|
170
|
+
console.error('Connection failed:', error.message)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Wait for single event
|
|
174
|
+
const userData = await wioRef.current.onceAsync('userProfile')
|
|
175
|
+
console.log('User data received:', userData)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Enhanced Connection Management
|
|
179
|
+
|
|
180
|
+
### Auto-Reconnection
|
|
181
|
+
|
|
182
|
+
```javascript
|
|
183
|
+
// Handle connection events
|
|
184
|
+
wio.on('disconnect', (data) => {
|
|
185
|
+
console.log('Disconnected:', data.reason)
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
wio.on('reconnecting', (data) => {
|
|
189
|
+
console.log(`Reconnection attempt ${data.attempt}, delay: ${data.delay}ms`)
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
wio.on('reconnection_failed', (data) => {
|
|
193
|
+
console.error(`Failed to reconnect after ${data.attempts} attempts`)
|
|
194
|
+
})
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Connection Statistics
|
|
198
|
+
|
|
199
|
+
```javascript
|
|
200
|
+
const stats = wio.getStats()
|
|
201
|
+
console.log(stats)
|
|
202
|
+
// {
|
|
203
|
+
// connected: true,
|
|
204
|
+
// peerType: 'WEBVIEW',
|
|
205
|
+
// origin: 'https://example.com',
|
|
206
|
+
// lastHeartbeat: 1609459200000,
|
|
207
|
+
// queuedMessages: 0,
|
|
208
|
+
// reconnectAttempts: 0,
|
|
209
|
+
// activeListeners: 5,
|
|
210
|
+
// messageRate: 2
|
|
211
|
+
// }
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Security Features
|
|
215
|
+
|
|
216
|
+
### Origin Validation
|
|
217
|
+
|
|
218
|
+
```javascript
|
|
219
|
+
// Strict origin checking (WebView side)
|
|
220
|
+
window._wio.listen('react-native') // Only accept from React Native
|
|
221
|
+
|
|
222
|
+
// Error handling for invalid origins (React Native side)
|
|
223
|
+
wio.on('error', (error) => {
|
|
224
|
+
if (error.type === 'INVALID_ORIGIN') {
|
|
225
|
+
console.log(`Rejected message from ${error.received}`)
|
|
226
|
+
}
|
|
227
|
+
})
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Message Sanitization
|
|
231
|
+
|
|
232
|
+
```javascript
|
|
233
|
+
// Automatic payload sanitization removes functions and undefined values
|
|
234
|
+
wio.emit('data', {
|
|
235
|
+
text: 'Hello',
|
|
236
|
+
func: () => {}, // Functions are automatically removed
|
|
237
|
+
undef: undefined // Undefined values are automatically removed
|
|
238
|
+
})
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Rate Limiting
|
|
242
|
+
|
|
243
|
+
```javascript
|
|
244
|
+
const wio = new WIO({
|
|
245
|
+
maxMessagesPerSecond: 10 // Limit to 10 messages per second
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
wio.on('error', (error) => {
|
|
249
|
+
if (error.type === 'RATE_LIMIT_EXCEEDED') {
|
|
250
|
+
console.log(`Rate limited: ${error.current}/${error.limit}`)
|
|
251
|
+
}
|
|
252
|
+
})
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Comprehensive Error Handling
|
|
256
|
+
|
|
257
|
+
```javascript
|
|
258
|
+
wio.on('error', (error) => {
|
|
259
|
+
switch (error.type) {
|
|
260
|
+
case 'INVALID_ORIGIN':
|
|
261
|
+
console.error(`Invalid origin: expected ${error.expected}, got ${error.received}`)
|
|
262
|
+
break
|
|
263
|
+
case 'ORIGIN_MISMATCH':
|
|
264
|
+
console.error(`Origin mismatch: expected ${error.expected}, got ${error.received}`)
|
|
265
|
+
break
|
|
266
|
+
case 'RATE_LIMIT_EXCEEDED':
|
|
267
|
+
console.warn(`Rate limit exceeded: ${error.current}/${error.limit} messages/second`)
|
|
268
|
+
break
|
|
269
|
+
case 'MESSAGE_HANDLING_ERROR':
|
|
270
|
+
console.error(`Error handling event ${error.event}: ${error.error}`)
|
|
271
|
+
break
|
|
272
|
+
case 'EMIT_ERROR':
|
|
273
|
+
console.error(`Error sending event ${error.event}: ${error.error}`)
|
|
274
|
+
break
|
|
275
|
+
case 'LISTENER_ERROR':
|
|
276
|
+
console.error(`Error in listener for ${error.event}: ${error.error}`)
|
|
277
|
+
break
|
|
278
|
+
case 'NO_CONNECTION':
|
|
279
|
+
console.error(`Attempted to send ${error.event} without connection`)
|
|
280
|
+
break
|
|
281
|
+
default:
|
|
282
|
+
console.error('Unknown error:', error)
|
|
283
|
+
}
|
|
284
|
+
})
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## Message Queuing
|
|
288
|
+
|
|
289
|
+
```javascript
|
|
290
|
+
// Messages are automatically queued when disconnected
|
|
291
|
+
wio.emit('important-data', { data: 'This will be queued if disconnected' })
|
|
292
|
+
|
|
293
|
+
// Clear queue manually if needed
|
|
294
|
+
wio.clearQueue()
|
|
295
|
+
|
|
296
|
+
// Check queue status
|
|
297
|
+
const stats = wio.getStats()
|
|
298
|
+
console.log(`${stats.queuedMessages} messages queued`)
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## API Reference
|
|
302
|
+
|
|
303
|
+
### Methods
|
|
304
|
+
|
|
305
|
+
#### Async Methods
|
|
306
|
+
- **`emitAsync(event, payload?, timeout?)`** - Send message and wait for response (Promise)
|
|
307
|
+
- **`connectAsync(timeout?)`** - Wait for connection with timeout (Promise)
|
|
308
|
+
- **`onceAsync(event)`** - Wait for single event (Promise)
|
|
309
|
+
|
|
310
|
+
#### Utility Methods
|
|
311
|
+
- **`getStats()`** - Get connection statistics
|
|
312
|
+
- **`clearQueue()`** - Clear queued messages
|
|
313
|
+
- **`getInjectedJavaScript()`** - Get JavaScript to inject into WebView
|
|
314
|
+
|
|
315
|
+
#### Connection Methods
|
|
316
|
+
- **`initiate(webViewRef, origin)`** - Establish connection (WEBVIEW peer only)
|
|
317
|
+
- **`listen(hostOrigin?)`** - Listen for connection (EMBEDDED peer only)
|
|
318
|
+
- **`handleMessage(event)`** - Handle incoming message from WebView
|
|
319
|
+
- **`disconnect(callback?)`** - Disconnect and cleanup
|
|
320
|
+
- **`isConnected()`** - Check connection status
|
|
321
|
+
|
|
322
|
+
#### Messaging Methods
|
|
323
|
+
- **`emit(event, payload?, callback?)`** - Send message
|
|
324
|
+
- **`on(event, listener)`** - Add event listener
|
|
325
|
+
- **`once(event, listener)`** - Add one-time event listener
|
|
326
|
+
- **`off(event, listener?)`** - Remove event listener(s)
|
|
327
|
+
- **`removeListeners(callback?)`** - Remove all listeners
|
|
328
|
+
|
|
329
|
+
### Events
|
|
330
|
+
|
|
331
|
+
#### Connection Events
|
|
332
|
+
- **`connect`** - Connection established
|
|
333
|
+
- **`disconnect`** - Connection lost with reason
|
|
334
|
+
- **`reconnecting`** - Reconnection attempt started
|
|
335
|
+
- **`reconnection_failed`** - All reconnection attempts failed
|
|
336
|
+
|
|
337
|
+
#### Error Events
|
|
338
|
+
- **`error`** - Various error conditions with detailed error objects
|
|
339
|
+
|
|
340
|
+
## Complete Example
|
|
341
|
+
|
|
342
|
+
```javascript
|
|
343
|
+
import React, { useRef, useEffect, useState } from 'react'
|
|
344
|
+
import { View, TouchableOpacity, Text, StyleSheet } from 'react-native'
|
|
345
|
+
import { WebView } from 'react-native-webview'
|
|
346
|
+
import WIO from 'webview.io'
|
|
347
|
+
|
|
348
|
+
function App() {
|
|
349
|
+
const webViewRef = useRef(null)
|
|
350
|
+
const wioRef = useRef(null)
|
|
351
|
+
const [isConnected, setIsConnected] = useState(false)
|
|
352
|
+
|
|
353
|
+
useEffect(() => {
|
|
354
|
+
wioRef.current = new WIO({
|
|
355
|
+
type: 'WEBVIEW',
|
|
356
|
+
debug: true
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
wioRef.current.initiate(webViewRef, 'https://your-app.com')
|
|
360
|
+
|
|
361
|
+
wioRef.current
|
|
362
|
+
.on('connect', () => {
|
|
363
|
+
console.log('Connected!')
|
|
364
|
+
setIsConnected(true)
|
|
365
|
+
})
|
|
366
|
+
.on('disconnect', () => {
|
|
367
|
+
console.log('Disconnected!')
|
|
368
|
+
setIsConnected(false)
|
|
369
|
+
})
|
|
370
|
+
.on('location:picked', (location) => {
|
|
371
|
+
console.log('User picked location:', location)
|
|
372
|
+
})
|
|
373
|
+
|
|
374
|
+
return () => {
|
|
375
|
+
wioRef.current?.disconnect()
|
|
376
|
+
}
|
|
377
|
+
}, [])
|
|
378
|
+
|
|
379
|
+
const getLocation = async () => {
|
|
380
|
+
try {
|
|
381
|
+
const location = await wioRef.current.emitAsync('get:location')
|
|
382
|
+
console.log('Got location:', location)
|
|
383
|
+
} catch (error) {
|
|
384
|
+
console.error('Failed to get location:', error)
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return (
|
|
389
|
+
<View style={styles.container}>
|
|
390
|
+
<WebView
|
|
391
|
+
ref={webViewRef}
|
|
392
|
+
source={{ uri: 'https://your-app.com' }}
|
|
393
|
+
onMessage={(event) => wioRef.current?.handleMessage(event)}
|
|
394
|
+
injectedJavaScript={wioRef.current?.getInjectedJavaScript()}
|
|
395
|
+
javaScriptEnabled={true}
|
|
396
|
+
/>
|
|
397
|
+
|
|
398
|
+
<View style={styles.controls}>
|
|
399
|
+
<Text>Status: {isConnected ? 'Connected' : 'Disconnected'}</Text>
|
|
400
|
+
<TouchableOpacity onPress={getLocation}>
|
|
401
|
+
<Text>Get Location</Text>
|
|
402
|
+
</TouchableOpacity>
|
|
403
|
+
</View>
|
|
404
|
+
</View>
|
|
405
|
+
)
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const styles = StyleSheet.create({
|
|
409
|
+
container: { flex: 1 },
|
|
410
|
+
controls: { padding: 16 }
|
|
411
|
+
})
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
## TypeScript Support
|
|
415
|
+
|
|
416
|
+
Full TypeScript support with comprehensive type definitions:
|
|
417
|
+
|
|
418
|
+
```typescript
|
|
419
|
+
import WIO, { Options, Listener, AckFunction } from 'webview.io'
|
|
420
|
+
|
|
421
|
+
const options: Options = {
|
|
422
|
+
type: 'WEBVIEW',
|
|
423
|
+
debug: true,
|
|
424
|
+
heartbeatInterval: 30000,
|
|
425
|
+
maxMessageSize: 512 * 1024
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const wio = new WIO(options)
|
|
429
|
+
|
|
430
|
+
// Typed event listeners
|
|
431
|
+
wio.on('userAction', (data: { action: string; userId: number }) => {
|
|
432
|
+
console.log(`User ${data.userId} performed ${data.action}`)
|
|
433
|
+
})
|
|
434
|
+
|
|
435
|
+
// Typed async responses
|
|
436
|
+
interface ApiResponse {
|
|
437
|
+
success: boolean
|
|
438
|
+
data: any[]
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const response = await wio.emitAsync<{ query: string }, ApiResponse>(
|
|
442
|
+
'search',
|
|
443
|
+
{ query: 'hello' },
|
|
444
|
+
5000
|
|
445
|
+
)
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
## Error Types Reference
|
|
449
|
+
|
|
450
|
+
| Error Type | Description |
|
|
451
|
+
|------------|-------------|
|
|
452
|
+
| `INVALID_ORIGIN` | Message from unexpected origin |
|
|
453
|
+
| `ORIGIN_MISMATCH` | Origin changed during session |
|
|
454
|
+
| `MESSAGE_HANDLING_ERROR` | Error processing incoming message |
|
|
455
|
+
| `EMIT_ERROR` | Error sending message |
|
|
456
|
+
| `LISTENER_ERROR` | Error in event listener |
|
|
457
|
+
| `RATE_LIMIT_EXCEEDED` | Too many messages sent |
|
|
458
|
+
| `NO_CONNECTION` | Attempted to send without connection |
|
|
459
|
+
|
|
460
|
+
## React Native Compatibility
|
|
461
|
+
|
|
462
|
+
- React Native 0.60+
|
|
463
|
+
- React 16.8+ (Hooks support)
|
|
464
|
+
- react-native-webview 11.0+
|
|
465
|
+
|
|
466
|
+
## Performance Considerations
|
|
467
|
+
|
|
468
|
+
- **Message Size**: Keep messages under the configured `maxMessageSize` (default 1MB)
|
|
469
|
+
- **Rate Limiting**: Respect the `maxMessagesPerSecond` limit (default 100/sec)
|
|
470
|
+
- **Queue Size**: Monitor queued messages to avoid memory issues
|
|
471
|
+
- **Heartbeat**: Adjust `heartbeatInterval` based on your reliability needs
|
|
472
|
+
- **Battery Impact**: Consider disabling heartbeat or increasing interval for battery-sensitive applications
|
|
473
|
+
|
|
474
|
+
## Differences from iframe.io
|
|
475
|
+
|
|
476
|
+
`webview.io` is adapted specifically for React Native and differs from `iframe.io` in the following ways:
|
|
477
|
+
|
|
478
|
+
- **Peer Types**: `WEBVIEW` (React Native) and `EMBEDDED` (WebView content) instead of `WINDOW` and `IFRAME`
|
|
479
|
+
- **Initialization**: Uses `RefObject<WebView>` instead of `Window` object
|
|
480
|
+
- **Message Handling**: Requires explicit `handleMessage()` call in `onMessage` prop
|
|
481
|
+
- **Injected Script**: Uses `getInjectedJavaScript()` to setup bridge in WebView
|
|
482
|
+
- **No DOM Dependencies**: Works in React Native environment without DOM APIs
|
|
483
|
+
|
|
484
|
+
## License
|
|
485
|
+
|
|
486
|
+
MIT License - see LICENSE file for details.
|
|
487
|
+
|
|
488
|
+
## Contributing
|
|
489
|
+
|
|
490
|
+
Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository.
|
|
491
|
+
|
|
492
|
+
## Support
|
|
493
|
+
|
|
494
|
+
- Create an issue on GitHub for bug reports
|
|
495
|
+
- Check existing issues for common problems
|
|
496
|
+
- Review the documentation for usage examples
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type { RefObject } from 'react';
|
|
2
|
+
import type { WebView } from 'react-native-webview';
|
|
3
|
+
export type PeerType = 'WEBVIEW' | 'EMBEDDED';
|
|
4
|
+
export type AckFunction = (error: boolean | string, ...args: any[]) => void;
|
|
5
|
+
export type Listener = (payload?: any, ack?: AckFunction) => void;
|
|
6
|
+
export type Options = {
|
|
7
|
+
type?: PeerType;
|
|
8
|
+
debug?: boolean;
|
|
9
|
+
heartbeatInterval?: number;
|
|
10
|
+
connectionTimeout?: number;
|
|
11
|
+
maxMessageSize?: number;
|
|
12
|
+
maxMessagesPerSecond?: number;
|
|
13
|
+
autoReconnect?: boolean;
|
|
14
|
+
messageQueueSize?: number;
|
|
15
|
+
};
|
|
16
|
+
export interface RegisteredEvents {
|
|
17
|
+
[index: string]: Listener[];
|
|
18
|
+
}
|
|
19
|
+
export type Peer = {
|
|
20
|
+
type: PeerType;
|
|
21
|
+
webViewRef?: RefObject<WebView>;
|
|
22
|
+
origin?: string;
|
|
23
|
+
connected?: boolean;
|
|
24
|
+
lastHeartbeat?: number;
|
|
25
|
+
};
|
|
26
|
+
export type MessageData = {
|
|
27
|
+
_event: string;
|
|
28
|
+
payload: any;
|
|
29
|
+
cid: string | undefined;
|
|
30
|
+
timestamp?: number;
|
|
31
|
+
size?: number;
|
|
32
|
+
};
|
|
33
|
+
export type Message = {
|
|
34
|
+
data: MessageData;
|
|
35
|
+
};
|
|
36
|
+
export type QueuedMessage = {
|
|
37
|
+
_event: string;
|
|
38
|
+
payload: any;
|
|
39
|
+
fn?: AckFunction;
|
|
40
|
+
timestamp: number;
|
|
41
|
+
};
|
|
42
|
+
export default class WIO {
|
|
43
|
+
Events: RegisteredEvents;
|
|
44
|
+
peer: Peer;
|
|
45
|
+
options: Options;
|
|
46
|
+
private heartbeatTimer?;
|
|
47
|
+
private reconnectTimer?;
|
|
48
|
+
private messageQueue;
|
|
49
|
+
private messageRateTracker;
|
|
50
|
+
private reconnectAttempts;
|
|
51
|
+
private maxReconnectAttempts;
|
|
52
|
+
constructor(options?: Options);
|
|
53
|
+
debug(...args: any[]): void;
|
|
54
|
+
isConnected(): boolean;
|
|
55
|
+
private startHeartbeat;
|
|
56
|
+
private stopHeartbeat;
|
|
57
|
+
private handleConnectionLoss;
|
|
58
|
+
private attemptReconnection;
|
|
59
|
+
private checkRateLimit;
|
|
60
|
+
private queueMessage;
|
|
61
|
+
private processMessageQueue;
|
|
62
|
+
/**
|
|
63
|
+
* Establish a connection with WebView
|
|
64
|
+
*/
|
|
65
|
+
initiate(webViewRef: RefObject<WebView>, origin: string): this;
|
|
66
|
+
/**
|
|
67
|
+
* Listening to connection from the WebView host
|
|
68
|
+
* Note: In React Native context, this is handled by injected JavaScript
|
|
69
|
+
*/
|
|
70
|
+
listen(hostOrigin?: string): this;
|
|
71
|
+
/**
|
|
72
|
+
* Handle incoming message from WebView
|
|
73
|
+
* Called by React Native component via onMessage prop
|
|
74
|
+
*/
|
|
75
|
+
handleMessage(event: {
|
|
76
|
+
nativeEvent: {
|
|
77
|
+
data: string;
|
|
78
|
+
};
|
|
79
|
+
}): void;
|
|
80
|
+
fire(_event: string, payload?: MessageData['payload'], cid?: string): void;
|
|
81
|
+
emit<T = any>(_event: string, payload?: T | AckFunction, fn?: AckFunction): this;
|
|
82
|
+
on(_event: string, fn: Listener): this;
|
|
83
|
+
once(_event: string, fn: Listener): this;
|
|
84
|
+
off(_event: string, fn?: Listener): this;
|
|
85
|
+
removeListeners(fn?: Listener): this;
|
|
86
|
+
emitAsync<T = any, R = any>(_event: string, payload?: T, timeout?: number): Promise<R>;
|
|
87
|
+
onceAsync<T = any>(_event: string): Promise<T>;
|
|
88
|
+
connectAsync(timeout?: number): Promise<void>;
|
|
89
|
+
private cleanup;
|
|
90
|
+
disconnect(fn?: () => void): this;
|
|
91
|
+
getStats(): {
|
|
92
|
+
connected: boolean;
|
|
93
|
+
peerType: PeerType;
|
|
94
|
+
origin: string | undefined;
|
|
95
|
+
lastHeartbeat: number | undefined;
|
|
96
|
+
queuedMessages: number;
|
|
97
|
+
reconnectAttempts: number;
|
|
98
|
+
activeListeners: number;
|
|
99
|
+
messageRate: number;
|
|
100
|
+
};
|
|
101
|
+
clearQueue(): this;
|
|
102
|
+
/**
|
|
103
|
+
* Get injected JavaScript for WebView
|
|
104
|
+
* Sets up the EMBEDDED side of the bridge
|
|
105
|
+
*/
|
|
106
|
+
getInjectedJavaScript(): string;
|
|
107
|
+
}
|