iframe.io 1.0.6 → 1.1.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/README.md +260 -324
- package/dist/index.js +9 -5
- package/package.json +1 -1
- package/src/index.ts +13 -6
package/README.md
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
# iframe.io
|
|
2
2
|
|
|
3
|
-
Easy and friendly API to connect and interact between content window and its containing iframe with enhanced
|
|
3
|
+
Easy and friendly API to connect and interact between content window and its containing iframe with enhanced security, reliability, and modern async/await support.
|
|
4
4
|
|
|
5
|
-
## Features
|
|
5
|
+
## New Features & Improvements
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
7
|
+
### 🆕 Version 1.1.0 Updates
|
|
8
|
+
|
|
9
|
+
- **🔒 Enhanced Security**: Origin validation, message sanitization, and payload size limits
|
|
10
|
+
- **🔄 Auto-Reconnection**: Automatic reconnection with exponential backoff strategy
|
|
11
|
+
- **💓 Heartbeat Monitoring**: Connection health monitoring with configurable intervals
|
|
12
|
+
- **📦 Message Queuing**: Queue messages when disconnected and replay on reconnection
|
|
13
|
+
- **⚡ Rate Limiting**: Configurable message rate limiting to prevent spam
|
|
14
|
+
- **🎯 Promise Support**: Modern async/await APIs with timeout handling
|
|
15
|
+
- **📊 Connection Statistics**: Real-time connection and performance metrics
|
|
16
|
+
- **🛡️ Comprehensive Error Handling**: Detailed error types and handling mechanisms
|
|
16
17
|
|
|
17
18
|
## Installation
|
|
18
19
|
|
|
@@ -20,429 +21,364 @@ Easy and friendly API to connect and interact between content window and its con
|
|
|
20
21
|
npm install iframe.io
|
|
21
22
|
```
|
|
22
23
|
|
|
23
|
-
##
|
|
24
|
+
## Basic Usage
|
|
24
25
|
|
|
25
|
-
### Parent Window (WINDOW
|
|
26
|
+
### Parent Window (WINDOW peer)
|
|
26
27
|
|
|
27
28
|
```javascript
|
|
28
29
|
import IOF from 'iframe.io'
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
iof.on('response', (data) => {
|
|
52
|
-
console.log('Received:', data)
|
|
53
|
-
})
|
|
54
|
-
}
|
|
31
|
+
const iframe = document.getElementById('myIframe')
|
|
32
|
+
const iframeIO = new IOF({
|
|
33
|
+
type: 'WINDOW',
|
|
34
|
+
debug: true
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
// Establish connection
|
|
38
|
+
iframeIO.initiate(iframe.contentWindow, 'https://child-domain.com')
|
|
39
|
+
|
|
40
|
+
// Listen for connection
|
|
41
|
+
iframeIO.on('connect', () => {
|
|
42
|
+
console.log('Connected to iframe!')
|
|
43
|
+
|
|
44
|
+
// Send a message
|
|
45
|
+
iframeIO.emit('hello', { message: 'Hello from parent!' })
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
// Listen for messages
|
|
49
|
+
iframeIO.on('response', (data) => {
|
|
50
|
+
console.log('Received:', data)
|
|
51
|
+
})
|
|
55
52
|
```
|
|
56
53
|
|
|
57
|
-
###
|
|
54
|
+
### Child Window (IFRAME peer)
|
|
58
55
|
|
|
59
56
|
```javascript
|
|
60
57
|
import IOF from 'iframe.io'
|
|
61
58
|
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
const iframeIO = new IOF({
|
|
60
|
+
type: 'IFRAME',
|
|
61
|
+
debug: true
|
|
62
|
+
})
|
|
64
63
|
|
|
65
64
|
// Listen for parent connection
|
|
66
|
-
|
|
65
|
+
iframeIO.listen('https://parent-domain.com')
|
|
67
66
|
|
|
68
67
|
// Handle connection
|
|
69
|
-
|
|
68
|
+
iframeIO.on('connect', () => {
|
|
70
69
|
console.log('Connected to parent!')
|
|
71
70
|
})
|
|
72
71
|
|
|
73
72
|
// Listen for messages
|
|
74
|
-
|
|
75
|
-
console.log('Received
|
|
76
|
-
|
|
73
|
+
iframeIO.on('hello', (data) => {
|
|
74
|
+
console.log('Received:', data)
|
|
75
|
+
|
|
77
76
|
// Send response
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
// Acknowledge receipt (optional)
|
|
81
|
-
if (ack) ack(false, 'Message received')
|
|
77
|
+
iframeIO.emit('response', { received: true })
|
|
82
78
|
})
|
|
83
79
|
```
|
|
84
80
|
|
|
85
|
-
##
|
|
86
|
-
|
|
87
|
-
### Constructor
|
|
81
|
+
## Enhanced Configuration Options
|
|
88
82
|
|
|
89
83
|
```javascript
|
|
90
|
-
new IOF(
|
|
84
|
+
const iframeIO = new IOF({
|
|
85
|
+
type: 'WINDOW', // 'WINDOW' or 'IFRAME'
|
|
86
|
+
debug: false, // Enable debug logging
|
|
87
|
+
heartbeatInterval: 30000, // Heartbeat interval in ms (30s)
|
|
88
|
+
connectionTimeout: 10000, // Connection timeout in ms (10s)
|
|
89
|
+
maxMessageSize: 1024 * 1024, // Max message size in bytes (1MB)
|
|
90
|
+
maxMessagesPerSecond: 100, // Rate limit (100 messages/second)
|
|
91
|
+
autoReconnect: true, // Enable automatic reconnection
|
|
92
|
+
messageQueueSize: 50 // Max queued messages when disconnected
|
|
93
|
+
})
|
|
91
94
|
```
|
|
92
95
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
| Option | Type | Default | Description |
|
|
96
|
-
|--------|------|---------|-------------|
|
|
97
|
-
| `type` | `'WINDOW' \| 'IFRAME'` | `'IFRAME'` | Connection type |
|
|
98
|
-
| `debug` | `boolean` | `false` | Enable debug logging |
|
|
99
|
-
| `heartbeatInterval` | `number` | `30000` | Heartbeat interval in ms |
|
|
100
|
-
| `connectionTimeout` | `number` | `10000` | Connection timeout in ms |
|
|
101
|
-
| `maxMessageSize` | `number` | `1048576` | Max message size (1MB) |
|
|
102
|
-
| `maxMessagesPerSecond` | `number` | `100` | Rate limit for messages |
|
|
103
|
-
| `autoReconnect` | `boolean` | `true` | Enable auto reconnection |
|
|
104
|
-
| `messageQueueSize` | `number` | `50` | Max queued messages |
|
|
96
|
+
## New Async/Await Support
|
|
105
97
|
|
|
106
|
-
###
|
|
107
|
-
|
|
108
|
-
#### `initiate(contentWindow, iframeOrigin)`
|
|
109
|
-
|
|
110
|
-
Establish connection with an iframe (WINDOW type only).
|
|
98
|
+
### Send Messages with Acknowledgments
|
|
111
99
|
|
|
112
100
|
```javascript
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
#### `listen(hostOrigin?)`
|
|
121
|
-
|
|
122
|
-
Listen for connection from parent window (IFRAME type).
|
|
101
|
+
// Send message and wait for acknowledgment with timeout
|
|
102
|
+
try {
|
|
103
|
+
const response = await iframeIO.emitAsync('getData', { id: 123 }, 10000) // 10s timeout
|
|
104
|
+
console.log('Response:', response)
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.error('Request failed:', error.message)
|
|
107
|
+
}
|
|
123
108
|
|
|
124
|
-
|
|
125
|
-
|
|
109
|
+
// Listen and acknowledge
|
|
110
|
+
iframeIO.on('getData', async (data, ack) => {
|
|
111
|
+
try {
|
|
112
|
+
const result = await fetchData(data.id)
|
|
113
|
+
ack(false, result) // Success: ack(error, ...response)
|
|
114
|
+
} catch (error) {
|
|
115
|
+
ack(error.message) // Error: ack(errorMessage)
|
|
116
|
+
}
|
|
117
|
+
})
|
|
126
118
|
```
|
|
127
119
|
|
|
128
|
-
|
|
129
|
-
- `hostOrigin` (optional) - Restrict connections to specific origin
|
|
130
|
-
|
|
131
|
-
#### `disconnect(callback?)`
|
|
132
|
-
|
|
133
|
-
Disconnect and clean up all resources.
|
|
120
|
+
### Wait for Connection
|
|
134
121
|
|
|
135
122
|
```javascript
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
123
|
+
// Wait for connection with timeout
|
|
124
|
+
try {
|
|
125
|
+
await iframeIO.connectAsync(5000) // 5 second timeout
|
|
126
|
+
console.log('Connection established!')
|
|
127
|
+
} catch (error) {
|
|
128
|
+
console.error('Connection failed:', error.message)
|
|
129
|
+
}
|
|
140
130
|
|
|
141
|
-
|
|
131
|
+
// Wait for single event
|
|
132
|
+
const userData = await iframeIO.onceAsync('userProfile')
|
|
133
|
+
console.log('User data received:', userData)
|
|
134
|
+
```
|
|
142
135
|
|
|
143
|
-
|
|
136
|
+
## Enhanced Connection Management
|
|
144
137
|
|
|
145
|
-
|
|
138
|
+
### Auto-Reconnection
|
|
146
139
|
|
|
147
140
|
```javascript
|
|
148
|
-
//
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
// With acknowledgment
|
|
152
|
-
iof.emit('myEvent', { data: 'hello' }, (error, response) => {
|
|
153
|
-
if (error) {
|
|
154
|
-
console.error('Error:', error)
|
|
155
|
-
} else {
|
|
156
|
-
console.log('Response:', response)
|
|
157
|
-
}
|
|
141
|
+
// Handle connection events
|
|
142
|
+
iframeIO.on('disconnect', (data) => {
|
|
143
|
+
console.log('Disconnected:', data.reason)
|
|
158
144
|
})
|
|
159
145
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
console.log('Response:', response)
|
|
146
|
+
iframeIO.on('reconnecting', (data) => {
|
|
147
|
+
console.log(`Reconnection attempt ${data.attempt}, delay: ${data.delay}ms`)
|
|
163
148
|
})
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
#### `on(event, listener)`
|
|
167
149
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
```javascript
|
|
171
|
-
iof.on('myEvent', (data, ack) => {
|
|
172
|
-
console.log('Received:', data)
|
|
173
|
-
|
|
174
|
-
// Send acknowledgment (optional)
|
|
175
|
-
if (ack) ack(false, 'Success response')
|
|
150
|
+
iframeIO.on('reconnection_failed', (data) => {
|
|
151
|
+
console.error(`Failed to reconnect after ${data.attempts} attempts`)
|
|
176
152
|
})
|
|
177
153
|
```
|
|
178
154
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
Add one-time event listener.
|
|
155
|
+
### Connection Statistics
|
|
182
156
|
|
|
183
157
|
```javascript
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
158
|
+
const stats = iframeIO.getStats()
|
|
159
|
+
console.log(stats)
|
|
160
|
+
// {
|
|
161
|
+
// connected: true,
|
|
162
|
+
// peerType: 'WINDOW',
|
|
163
|
+
// origin: 'https://example.com',
|
|
164
|
+
// lastHeartbeat: 1609459200000,
|
|
165
|
+
// queuedMessages: 0,
|
|
166
|
+
// reconnectAttempts: 0,
|
|
167
|
+
// activeListeners: 5,
|
|
168
|
+
// messageRate: 2
|
|
169
|
+
// }
|
|
187
170
|
```
|
|
188
171
|
|
|
189
|
-
|
|
172
|
+
## Security Features
|
|
190
173
|
|
|
191
|
-
|
|
174
|
+
### Origin Validation
|
|
192
175
|
|
|
193
176
|
```javascript
|
|
194
|
-
//
|
|
195
|
-
|
|
177
|
+
// Strict origin checking
|
|
178
|
+
iframeIO.listen('https://trusted-domain.com') // Only accept from this origin
|
|
196
179
|
|
|
197
|
-
//
|
|
198
|
-
|
|
180
|
+
// Error handling for invalid origins
|
|
181
|
+
iframeIO.on('error', (error) => {
|
|
182
|
+
if (error.type === 'INVALID_ORIGIN') {
|
|
183
|
+
console.log(`Rejected message from ${error.received}`)
|
|
184
|
+
}
|
|
185
|
+
})
|
|
199
186
|
```
|
|
200
187
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
Remove all event listeners.
|
|
188
|
+
### Message Sanitization
|
|
204
189
|
|
|
205
190
|
```javascript
|
|
206
|
-
|
|
207
|
-
|
|
191
|
+
// Automatic payload sanitization removes functions and undefined values
|
|
192
|
+
iframeIO.emit('data', {
|
|
193
|
+
text: 'Hello',
|
|
194
|
+
func: () => {}, // Functions are automatically removed
|
|
195
|
+
undef: undefined // Undefined values are automatically removed
|
|
208
196
|
})
|
|
209
197
|
```
|
|
210
198
|
|
|
211
|
-
###
|
|
212
|
-
|
|
213
|
-
#### `emitAsync(event, payload?)`
|
|
214
|
-
|
|
215
|
-
Send message and return Promise.
|
|
199
|
+
### Rate Limiting
|
|
216
200
|
|
|
217
201
|
```javascript
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
} catch (error) {
|
|
222
|
-
console.error('Error:', error)
|
|
223
|
-
}
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
#### `onceAsync(event)`
|
|
227
|
-
|
|
228
|
-
Wait for single event occurrence.
|
|
202
|
+
const iframeIO = new IOF({
|
|
203
|
+
maxMessagesPerSecond: 10 // Limit to 10 messages per second
|
|
204
|
+
})
|
|
229
205
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
console.log(
|
|
206
|
+
iframeIO.on('error', (error) => {
|
|
207
|
+
if (error.type === 'RATE_LIMIT_EXCEEDED') {
|
|
208
|
+
console.log(`Rate limited: ${error.current}/${error.limit}`)
|
|
209
|
+
}
|
|
210
|
+
})
|
|
233
211
|
```
|
|
234
212
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
Wait for connection to be established.
|
|
213
|
+
## Comprehensive Error Handling
|
|
238
214
|
|
|
239
215
|
```javascript
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
|
|
216
|
+
iframeIO.on('error', (error) => {
|
|
217
|
+
switch (error.type) {
|
|
218
|
+
case 'INVALID_ORIGIN':
|
|
219
|
+
console.error(`Invalid origin: expected ${error.expected}, got ${error.received}`)
|
|
220
|
+
break
|
|
221
|
+
case 'ORIGIN_MISMATCH':
|
|
222
|
+
console.error(`Origin mismatch: expected ${error.expected}, got ${error.received}`)
|
|
223
|
+
break
|
|
224
|
+
case 'RATE_LIMIT_EXCEEDED':
|
|
225
|
+
console.warn(`Rate limit exceeded: ${error.current}/${error.limit} messages/second`)
|
|
226
|
+
break
|
|
227
|
+
case 'MESSAGE_HANDLING_ERROR':
|
|
228
|
+
console.error(`Error handling event ${error.event}: ${error.error}`)
|
|
229
|
+
break
|
|
230
|
+
case 'EMIT_ERROR':
|
|
231
|
+
console.error(`Error sending event ${error.event}: ${error.error}`)
|
|
232
|
+
break
|
|
233
|
+
case 'LISTENER_ERROR':
|
|
234
|
+
console.error(`Error in listener for ${error.event}: ${error.error}`)
|
|
235
|
+
break
|
|
236
|
+
case 'NO_CONNECTION':
|
|
237
|
+
console.error(`Attempted to send ${error.event} without connection`)
|
|
238
|
+
break
|
|
239
|
+
default:
|
|
240
|
+
console.error('Unknown error:', error)
|
|
241
|
+
}
|
|
242
|
+
})
|
|
246
243
|
```
|
|
247
244
|
|
|
248
|
-
|
|
245
|
+
## Message Queuing
|
|
249
246
|
|
|
250
|
-
|
|
247
|
+
```javascript
|
|
248
|
+
// Messages are automatically queued when disconnected
|
|
249
|
+
iframeIO.emit('important-data', { data: 'This will be queued if disconnected' })
|
|
251
250
|
|
|
252
|
-
|
|
251
|
+
// Clear queue manually if needed
|
|
252
|
+
iframeIO.clearQueue()
|
|
253
253
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
}
|
|
254
|
+
// Check queue status
|
|
255
|
+
const stats = iframeIO.getStats()
|
|
256
|
+
console.log(`${stats.queuedMessages} messages queued`)
|
|
258
257
|
```
|
|
259
258
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
Get connection statistics.
|
|
259
|
+
## API Reference
|
|
263
260
|
|
|
264
|
-
|
|
265
|
-
const stats = iof.getStats()
|
|
266
|
-
console.log('Stats:', stats)
|
|
267
|
-
/*
|
|
268
|
-
{
|
|
269
|
-
connected: true,
|
|
270
|
-
peerType: 'WINDOW',
|
|
271
|
-
origin: 'https://example.com',
|
|
272
|
-
lastHeartbeat: 1640995200000,
|
|
273
|
-
queuedMessages: 0,
|
|
274
|
-
reconnectAttempts: 0,
|
|
275
|
-
activeListeners: 5,
|
|
276
|
-
messageRate: 10
|
|
277
|
-
}
|
|
278
|
-
*/
|
|
279
|
-
```
|
|
261
|
+
### New Methods
|
|
280
262
|
|
|
281
|
-
####
|
|
263
|
+
#### Async Methods
|
|
264
|
+
- **`emitAsync(event, payload?, timeout?)`** - Send message and wait for response (Promise)
|
|
265
|
+
- **`connectAsync(timeout?)`** - Wait for connection with timeout (Promise)
|
|
266
|
+
- **`onceAsync(event)`** - Wait for single event (Promise)
|
|
282
267
|
|
|
283
|
-
|
|
268
|
+
#### Utility Methods
|
|
269
|
+
- **`getStats()`** - Get connection statistics
|
|
270
|
+
- **`clearQueue()`** - Clear queued messages
|
|
284
271
|
|
|
285
|
-
|
|
286
|
-
iof.clearQueue()
|
|
287
|
-
```
|
|
272
|
+
### Connection Methods
|
|
288
273
|
|
|
289
|
-
|
|
274
|
+
- **`initiate(contentWindow, iframeOrigin)`** - Establish connection (WINDOW peer only)
|
|
275
|
+
- **`listen(hostOrigin?)`** - Listen for connection (IFRAME peer only)
|
|
276
|
+
- **`disconnect(callback?)`** - Disconnect and cleanup
|
|
277
|
+
- **`isConnected()`** - Check connection status
|
|
290
278
|
|
|
291
|
-
###
|
|
279
|
+
### Messaging Methods
|
|
292
280
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
| `reconnection_failed` | All reconnection attempts failed | `{ attempts: number }` |
|
|
299
|
-
| `error` | Error occurred | `{ type: string, ...details }` |
|
|
281
|
+
- **`emit(event, payload?, callback?)`** - Send message
|
|
282
|
+
- **`on(event, listener)`** - Add event listener
|
|
283
|
+
- **`once(event, listener)`** - Add one-time event listener
|
|
284
|
+
- **`off(event, listener?)`** - Remove event listener(s)
|
|
285
|
+
- **`removeListeners(callback?)`** - Remove all listeners
|
|
300
286
|
|
|
301
|
-
###
|
|
287
|
+
### Events
|
|
302
288
|
|
|
303
|
-
|
|
304
|
-
-
|
|
305
|
-
-
|
|
306
|
-
-
|
|
307
|
-
-
|
|
308
|
-
- `ORIGIN_MISMATCH` - Origin doesn't match established connection
|
|
309
|
-
- `NO_CONNECTION` - Attempted to send without connection
|
|
289
|
+
#### Connection Events
|
|
290
|
+
- **`connect`** - Connection established
|
|
291
|
+
- **`disconnect`** - Connection lost with reason
|
|
292
|
+
- **`reconnecting`** - Reconnection attempt started
|
|
293
|
+
- **`reconnection_failed`** - All reconnection attempts failed
|
|
310
294
|
|
|
311
|
-
|
|
295
|
+
#### Error Events
|
|
296
|
+
- **`error`** - Various error conditions with detailed error objects
|
|
312
297
|
|
|
313
|
-
|
|
298
|
+
## TypeScript Support
|
|
314
299
|
|
|
315
|
-
|
|
316
|
-
// Sender
|
|
317
|
-
iof.emit('processData', { data: [...] }, (error, result) => {
|
|
318
|
-
if (error) {
|
|
319
|
-
console.error('Processing failed:', error)
|
|
320
|
-
} else {
|
|
321
|
-
console.log('Processing result:', result)
|
|
322
|
-
}
|
|
323
|
-
})
|
|
300
|
+
Full TypeScript support with comprehensive type definitions:
|
|
324
301
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
try {
|
|
328
|
-
const result = await processData(data.data)
|
|
329
|
-
ack(false, result) // Success: ack(error, ...args)
|
|
330
|
-
} catch (error) {
|
|
331
|
-
ack(error.message) // Error: ack(errorMessage)
|
|
332
|
-
}
|
|
333
|
-
})
|
|
334
|
-
```
|
|
302
|
+
```typescript
|
|
303
|
+
import IOF, { Options, Listener, AckFunction } from 'iframe.io'
|
|
335
304
|
|
|
336
|
-
|
|
305
|
+
const options: Options = {
|
|
306
|
+
type: 'WINDOW',
|
|
307
|
+
debug: true,
|
|
308
|
+
heartbeatInterval: 30000,
|
|
309
|
+
maxMessageSize: 512 * 1024
|
|
310
|
+
}
|
|
337
311
|
|
|
338
|
-
|
|
339
|
-
const iof = new IOF({
|
|
340
|
-
heartbeatInterval: 10000, // 10 seconds
|
|
341
|
-
connectionTimeout: 5000, // 5 seconds
|
|
342
|
-
autoReconnect: true
|
|
343
|
-
})
|
|
312
|
+
const iframeIO = new IOF(options)
|
|
344
313
|
|
|
345
|
-
|
|
346
|
-
|
|
314
|
+
// Typed event listeners
|
|
315
|
+
iframeIO.on('userAction', (data: { action: string; userId: number }) => {
|
|
316
|
+
console.log(`User ${data.userId} performed ${data.action}`)
|
|
347
317
|
})
|
|
348
318
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
319
|
+
// Typed async responses
|
|
320
|
+
interface ApiResponse {
|
|
321
|
+
success: boolean
|
|
322
|
+
data: any[]
|
|
323
|
+
}
|
|
352
324
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
}
|
|
325
|
+
const response = await iframeIO.emitAsync<{ query: string }, ApiResponse>(
|
|
326
|
+
'search',
|
|
327
|
+
{ query: 'hello' },
|
|
328
|
+
5000 // 5 second timeout
|
|
329
|
+
)
|
|
356
330
|
```
|
|
357
331
|
|
|
358
|
-
|
|
332
|
+
## Error Types Reference
|
|
359
333
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
334
|
+
| Error Type | Description |
|
|
335
|
+
|------------|-------------|
|
|
336
|
+
| `INVALID_ORIGIN` | Message from unexpected origin |
|
|
337
|
+
| `ORIGIN_MISMATCH` | Origin changed during session |
|
|
338
|
+
| `MESSAGE_HANDLING_ERROR` | Error processing incoming message |
|
|
339
|
+
| `EMIT_ERROR` | Error sending message |
|
|
340
|
+
| `LISTENER_ERROR` | Error in event listener |
|
|
341
|
+
| `RATE_LIMIT_EXCEEDED` | Too many messages sent |
|
|
342
|
+
| `NO_CONNECTION` | Attempted to send without connection |
|
|
365
343
|
|
|
366
|
-
|
|
367
|
-
iof.emit('queuedMessage', { data: 'This will be queued if disconnected' })
|
|
344
|
+
## Browser Compatibility
|
|
368
345
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
346
|
+
- Chrome 60+
|
|
347
|
+
- Firefox 55+
|
|
348
|
+
- Safari 12+
|
|
349
|
+
- Edge 79+
|
|
373
350
|
|
|
374
|
-
|
|
351
|
+
## Migration Guide
|
|
375
352
|
|
|
376
|
-
|
|
377
|
-
const iof = new IOF({
|
|
378
|
-
maxMessagesPerSecond: 50,
|
|
379
|
-
maxMessageSize: 512 * 1024 // 512KB
|
|
380
|
-
})
|
|
353
|
+
### From v1.0.x to v1.1.0
|
|
381
354
|
|
|
382
|
-
|
|
383
|
-
if (type === 'RATE_LIMIT_EXCEEDED') {
|
|
384
|
-
console.log(`Rate limit exceeded: ${current}/${limit} messages per second`)
|
|
385
|
-
}
|
|
386
|
-
})
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
### Security Best Practices
|
|
355
|
+
All existing code continues to work without changes. New features are additive:
|
|
390
356
|
|
|
391
357
|
```javascript
|
|
392
|
-
//
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
console.warn(`Rejected message from ${received}, expected ${expected}`)
|
|
399
|
-
}
|
|
400
|
-
})
|
|
401
|
-
|
|
402
|
-
// Validate message content
|
|
403
|
-
iof.on('userData', (data, ack) => {
|
|
404
|
-
if (!isValidUserData(data)) {
|
|
405
|
-
return ack('Invalid data format')
|
|
358
|
+
// Old way (still works)
|
|
359
|
+
iframeIO.emit('getData', { id: 123 }, (error, result) => {
|
|
360
|
+
if (error) {
|
|
361
|
+
console.error('Error:', error)
|
|
362
|
+
} else {
|
|
363
|
+
console.log('Result:', result)
|
|
406
364
|
}
|
|
407
|
-
// Process valid data...
|
|
408
365
|
})
|
|
409
|
-
```
|
|
410
|
-
|
|
411
|
-
## Browser Support
|
|
412
|
-
|
|
413
|
-
- Modern browsers with postMessage API support
|
|
414
|
-
- Chrome 2+
|
|
415
|
-
- Firefox 3+
|
|
416
|
-
- Safari 4+
|
|
417
|
-
- IE 8+
|
|
418
|
-
- Edge (all versions)
|
|
419
|
-
|
|
420
|
-
## TypeScript Support
|
|
421
|
-
|
|
422
|
-
The library is written in TypeScript and includes full type definitions:
|
|
423
366
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
heartbeatInterval: 15000
|
|
367
|
+
// New async way
|
|
368
|
+
try {
|
|
369
|
+
const result = await iframeIO.emitAsync('getData', { id: 123 })
|
|
370
|
+
console.log('Result:', result)
|
|
371
|
+
} catch (error) {
|
|
372
|
+
console.error('Error:', error.message)
|
|
431
373
|
}
|
|
374
|
+
```
|
|
432
375
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
// Type-safe event handling
|
|
436
|
-
iof.on('myEvent', (data: { message: string }, ack?: AckFunction) => {
|
|
437
|
-
console.log(data.message)
|
|
438
|
-
ack?.(false, 'success')
|
|
439
|
-
})
|
|
376
|
+
## Performance Considerations
|
|
440
377
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
```
|
|
378
|
+
- **Message Size**: Keep messages under the configured `maxMessageSize` (default 1MB)
|
|
379
|
+
- **Rate Limiting**: Respect the `maxMessagesPerSecond` limit (default 100/sec)
|
|
380
|
+
- **Queue Size**: Monitor queued messages to avoid memory issues
|
|
381
|
+
- **Heartbeat**: Adjust `heartbeatInterval` based on your reliability needs
|
|
446
382
|
|
|
447
383
|
## License
|
|
448
384
|
|
|
@@ -456,4 +392,4 @@ Contributions are welcome! Please read our contributing guidelines and submit pu
|
|
|
456
392
|
|
|
457
393
|
- Create an issue on GitHub for bug reports
|
|
458
394
|
- Check existing issues for common problems
|
|
459
|
-
- Review the documentation for usage examples
|
|
395
|
+
- Review the documentation for usage examples
|
package/dist/index.js
CHANGED
|
@@ -242,7 +242,8 @@ var IOF = /** @class */ (function () {
|
|
|
242
242
|
_this.startHeartbeat();
|
|
243
243
|
_this.fire('connect');
|
|
244
244
|
_this.processMessageQueue();
|
|
245
|
-
|
|
245
|
+
_this.debug("[".concat(_this.peer.type, "] connected"));
|
|
246
|
+
return;
|
|
246
247
|
}
|
|
247
248
|
// Fire available event listeners
|
|
248
249
|
_this.fire(_event, payload, cid);
|
|
@@ -326,7 +327,8 @@ var IOF = /** @class */ (function () {
|
|
|
326
327
|
_this.startHeartbeat();
|
|
327
328
|
_this.fire('connect');
|
|
328
329
|
_this.processMessageQueue();
|
|
329
|
-
|
|
330
|
+
_this.debug("[".concat(_this.peer.type, "] connected"));
|
|
331
|
+
return;
|
|
330
332
|
}
|
|
331
333
|
// Fire available event listeners
|
|
332
334
|
_this.fire(_event, payload, cid);
|
|
@@ -346,8 +348,10 @@ var IOF = /** @class */ (function () {
|
|
|
346
348
|
IOF.prototype.fire = function (_event, payload, cid) {
|
|
347
349
|
var _this = this;
|
|
348
350
|
// Volatile event - check if any listeners exist
|
|
349
|
-
if (!this.Events[_event] && !this.Events[_event + '--@once'])
|
|
350
|
-
|
|
351
|
+
if (!this.Events[_event] && !this.Events[_event + '--@once']) {
|
|
352
|
+
this.debug("[".concat(this.peer.type, "] No <").concat(_event, "> listener defined"));
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
351
355
|
var ackFn = cid
|
|
352
356
|
? function (error) {
|
|
353
357
|
var args = [];
|
|
@@ -541,7 +545,7 @@ var IOF = /** @class */ (function () {
|
|
|
541
545
|
}
|
|
542
546
|
};
|
|
543
547
|
IOF.prototype.disconnect = function (fn) {
|
|
544
|
-
//
|
|
548
|
+
// Cleanup on disconnect
|
|
545
549
|
this.cleanup();
|
|
546
550
|
this.peer.connected = false;
|
|
547
551
|
this.peer.source = undefined;
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -312,11 +312,13 @@ export default class IOF {
|
|
|
312
312
|
this.peer.connected = true
|
|
313
313
|
this.reconnectAttempts = 0
|
|
314
314
|
this.peer.lastHeartbeat = Date.now()
|
|
315
|
+
|
|
315
316
|
this.startHeartbeat()
|
|
316
317
|
this.fire('connect')
|
|
317
318
|
this.processMessageQueue()
|
|
319
|
+
this.debug(`[${this.peer.type}] connected`)
|
|
318
320
|
|
|
319
|
-
return
|
|
321
|
+
return
|
|
320
322
|
}
|
|
321
323
|
|
|
322
324
|
// Fire available event listeners
|
|
@@ -347,6 +349,7 @@ export default class IOF {
|
|
|
347
349
|
this.peer.type = 'IFRAME' // iframe.io connection listener is automatically set as IFRAME
|
|
348
350
|
this.peer.connected = false
|
|
349
351
|
this.reconnectAttempts = 0
|
|
352
|
+
|
|
350
353
|
this.debug(`[${this.peer.type}] Listening to connect${hostOrigin ? `: Host <${hostOrigin}>` : ''}`)
|
|
351
354
|
|
|
352
355
|
// Clean up existing listener if any
|
|
@@ -414,7 +417,8 @@ export default class IOF {
|
|
|
414
417
|
this.fire('connect')
|
|
415
418
|
this.processMessageQueue()
|
|
416
419
|
|
|
417
|
-
|
|
420
|
+
this.debug(`[${this.peer.type}] connected`)
|
|
421
|
+
return
|
|
418
422
|
}
|
|
419
423
|
|
|
420
424
|
// Fire available event listeners
|
|
@@ -437,8 +441,10 @@ export default class IOF {
|
|
|
437
441
|
|
|
438
442
|
fire( _event: string, payload?: MessageData['payload'], cid?: string ){
|
|
439
443
|
// Volatile event - check if any listeners exist
|
|
440
|
-
if( !this.Events[_event] && !this.Events[_event + '--@once'] )
|
|
441
|
-
|
|
444
|
+
if( !this.Events[_event] && !this.Events[_event + '--@once'] ){
|
|
445
|
+
this.debug(`[${this.peer.type}] No <${_event}> listener defined`)
|
|
446
|
+
return
|
|
447
|
+
}
|
|
442
448
|
|
|
443
449
|
const ackFn = cid
|
|
444
450
|
? ( error: boolean | string, ...args: any[] ): void => {
|
|
@@ -590,7 +596,7 @@ export default class IOF {
|
|
|
590
596
|
return new Promise(( resolve, reject ) => {
|
|
591
597
|
const timeoutId = setTimeout(() => {
|
|
592
598
|
reject( new Error(`Event '${_event}' acknowledgment timeout after ${timeout}ms`) )
|
|
593
|
-
}, timeout)
|
|
599
|
+
}, timeout )
|
|
594
600
|
|
|
595
601
|
try {
|
|
596
602
|
this.emit( _event, payload, ( error, ...args ) => {
|
|
@@ -646,7 +652,7 @@ export default class IOF {
|
|
|
646
652
|
}
|
|
647
653
|
|
|
648
654
|
disconnect( fn?: () => void ){
|
|
649
|
-
//
|
|
655
|
+
// Cleanup on disconnect
|
|
650
656
|
this.cleanup()
|
|
651
657
|
|
|
652
658
|
this.peer.connected = false
|
|
@@ -656,6 +662,7 @@ export default class IOF {
|
|
|
656
662
|
this.messageQueue = []
|
|
657
663
|
this.messageRateTracker = []
|
|
658
664
|
this.reconnectAttempts = 0
|
|
665
|
+
|
|
659
666
|
this.removeListeners()
|
|
660
667
|
|
|
661
668
|
typeof fn == 'function' && fn()
|