iframe.io 1.0.6 → 1.2.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 +282 -319
- package/dist/index.d.ts +10 -0
- package/dist/index.js +233 -230
- package/package.json +2 -2
- package/src/index.ts +72 -11
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,391 @@ 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
|
+
allowedIncomingEvents: ['hello', 'response'], // Optional incoming event allowlist (non-reserved events)
|
|
94
|
+
validateIncoming: (event, payload, origin) => true // Optional custom incoming validator
|
|
95
|
+
})
|
|
91
96
|
```
|
|
92
97
|
|
|
93
|
-
|
|
98
|
+
## New Async/Await Support
|
|
94
99
|
|
|
95
|
-
|
|
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 |
|
|
100
|
+
### Send Messages with Acknowledgments
|
|
105
101
|
|
|
106
|
-
|
|
102
|
+
```javascript
|
|
103
|
+
// Send message and wait for acknowledgment with timeout
|
|
104
|
+
try {
|
|
105
|
+
const response = await iframeIO.emitAsync('getData', { id: 123 }, 10000) // 10s timeout
|
|
106
|
+
console.log('Response:', response)
|
|
107
|
+
} catch (error) {
|
|
108
|
+
console.error('Request failed:', error.message)
|
|
109
|
+
}
|
|
107
110
|
|
|
108
|
-
|
|
111
|
+
// Listen and acknowledge
|
|
112
|
+
iframeIO.on('getData', async (data, ack) => {
|
|
113
|
+
try {
|
|
114
|
+
const result = await fetchData(data.id)
|
|
115
|
+
ack(false, result) // Success: ack(error, ...response)
|
|
116
|
+
} catch (error) {
|
|
117
|
+
ack(error.message) // Error: ack(errorMessage)
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
```
|
|
109
121
|
|
|
110
|
-
|
|
122
|
+
### Wait for Connection
|
|
111
123
|
|
|
112
124
|
```javascript
|
|
113
|
-
|
|
114
|
-
|
|
125
|
+
// Wait for connection with timeout
|
|
126
|
+
try {
|
|
127
|
+
await iframeIO.connectAsync(5000) // 5 second timeout
|
|
128
|
+
console.log('Connection established!')
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.error('Connection failed:', error.message)
|
|
131
|
+
}
|
|
115
132
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
133
|
+
// Wait for single event
|
|
134
|
+
const userData = await iframeIO.onceAsync('userProfile')
|
|
135
|
+
console.log('User data received:', userData)
|
|
136
|
+
```
|
|
119
137
|
|
|
120
|
-
|
|
138
|
+
## Enhanced Connection Management
|
|
121
139
|
|
|
122
|
-
|
|
140
|
+
### Auto-Reconnection
|
|
123
141
|
|
|
124
142
|
```javascript
|
|
125
|
-
|
|
126
|
-
|
|
143
|
+
// Handle connection events
|
|
144
|
+
iframeIO.on('disconnect', (data) => {
|
|
145
|
+
console.log('Disconnected:', data.reason)
|
|
146
|
+
})
|
|
127
147
|
|
|
128
|
-
|
|
129
|
-
|
|
148
|
+
iframeIO.on('reconnecting', (data) => {
|
|
149
|
+
console.log(`Reconnection attempt ${data.attempt}, delay: ${data.delay}ms`)
|
|
150
|
+
})
|
|
130
151
|
|
|
131
|
-
|
|
152
|
+
iframeIO.on('reconnection_failed', (data) => {
|
|
153
|
+
console.error(`Failed to reconnect after ${data.attempts} attempts`)
|
|
154
|
+
})
|
|
155
|
+
```
|
|
132
156
|
|
|
133
|
-
|
|
157
|
+
### Connection Statistics
|
|
134
158
|
|
|
135
159
|
```javascript
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
160
|
+
const stats = iframeIO.getStats()
|
|
161
|
+
console.log(stats)
|
|
162
|
+
// {
|
|
163
|
+
// connected: true,
|
|
164
|
+
// peerType: 'WINDOW',
|
|
165
|
+
// origin: 'https://example.com',
|
|
166
|
+
// lastHeartbeat: 1609459200000,
|
|
167
|
+
// queuedMessages: 0,
|
|
168
|
+
// reconnectAttempts: 0,
|
|
169
|
+
// activeListeners: 5,
|
|
170
|
+
// messageRate: 2
|
|
171
|
+
// }
|
|
139
172
|
```
|
|
140
173
|
|
|
141
|
-
|
|
174
|
+
## Security Features
|
|
142
175
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
Send a message to the peer.
|
|
176
|
+
### Origin Validation
|
|
146
177
|
|
|
147
178
|
```javascript
|
|
148
|
-
//
|
|
149
|
-
|
|
179
|
+
// Strict origin checking
|
|
180
|
+
iframeIO.listen('https://trusted-domain.com') // Only accept from this origin
|
|
150
181
|
|
|
151
|
-
//
|
|
152
|
-
|
|
153
|
-
if (error) {
|
|
154
|
-
console.
|
|
155
|
-
} else {
|
|
156
|
-
console.log('Response:', response)
|
|
182
|
+
// Error handling for invalid origins
|
|
183
|
+
iframeIO.on('error', (error) => {
|
|
184
|
+
if (error.type === 'INVALID_ORIGIN') {
|
|
185
|
+
console.log(`Rejected message from ${error.received}`)
|
|
157
186
|
}
|
|
158
187
|
})
|
|
159
|
-
|
|
160
|
-
// Callback as second parameter
|
|
161
|
-
iof.emit('myEvent', (error, response) => {
|
|
162
|
-
console.log('Response:', response)
|
|
163
|
-
})
|
|
164
188
|
```
|
|
165
189
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
Add event listener.
|
|
190
|
+
### Message Sanitization
|
|
169
191
|
|
|
170
192
|
```javascript
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
//
|
|
175
|
-
|
|
193
|
+
// Automatic payload sanitization removes functions and undefined values
|
|
194
|
+
iframeIO.emit('data', {
|
|
195
|
+
text: 'Hello',
|
|
196
|
+
func: () => {}, // Functions are automatically removed
|
|
197
|
+
undef: undefined // Undefined values are automatically removed
|
|
176
198
|
})
|
|
177
199
|
```
|
|
178
200
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
Add one-time event listener.
|
|
201
|
+
### Rate Limiting
|
|
182
202
|
|
|
183
203
|
```javascript
|
|
184
|
-
|
|
185
|
-
|
|
204
|
+
const iframeIO = new IOF({
|
|
205
|
+
maxMessagesPerSecond: 10 // Limit to 10 messages per second
|
|
186
206
|
})
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
#### `off(event, listener?)`
|
|
190
|
-
|
|
191
|
-
Remove event listener(s).
|
|
192
|
-
|
|
193
|
-
```javascript
|
|
194
|
-
// Remove specific listener
|
|
195
|
-
iof.off('myEvent', myListener)
|
|
196
207
|
|
|
197
|
-
|
|
198
|
-
|
|
208
|
+
iframeIO.on('error', (error) => {
|
|
209
|
+
if (error.type === 'RATE_LIMIT_EXCEEDED') {
|
|
210
|
+
console.log(`Rate limited: ${error.current}/${error.limit}`)
|
|
211
|
+
}
|
|
212
|
+
})
|
|
199
213
|
```
|
|
200
214
|
|
|
201
|
-
|
|
215
|
+
### Incoming Event Allowlist & Validation
|
|
202
216
|
|
|
203
|
-
|
|
217
|
+
For defense-in-depth, you can restrict which **application-level** events are accepted and/or validate incoming payloads. Reserved internal events (`ping`, `pong`, `__heartbeat`, `__heartbeat_response`) are always allowed.
|
|
204
218
|
|
|
205
219
|
```javascript
|
|
206
|
-
|
|
207
|
-
|
|
220
|
+
const iframeIO = new IOF({
|
|
221
|
+
type: 'IFRAME',
|
|
222
|
+
debug: true,
|
|
223
|
+
allowedIncomingEvents: ['getData', 'hello'],
|
|
224
|
+
validateIncoming: (event, payload, origin) => {
|
|
225
|
+
// Example: basic shape checks
|
|
226
|
+
if (event === 'getData') return payload && typeof payload.id === 'number'
|
|
227
|
+
return true
|
|
228
|
+
}
|
|
208
229
|
})
|
|
209
|
-
```
|
|
210
230
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
```javascript
|
|
218
|
-
try {
|
|
219
|
-
const response = await iof.emitAsync('getData', { id: 123 })
|
|
220
|
-
console.log('Response:', response)
|
|
221
|
-
} catch (error) {
|
|
222
|
-
console.error('Error:', error)
|
|
223
|
-
}
|
|
231
|
+
iframeIO.on('error', (error) => {
|
|
232
|
+
if (error.type === 'DISALLOWED_EVENT' || error.type === 'INVALID_MESSAGE') {
|
|
233
|
+
console.warn('Dropped incoming message:', error)
|
|
234
|
+
}
|
|
235
|
+
})
|
|
224
236
|
```
|
|
225
237
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
Wait for single event occurrence.
|
|
238
|
+
## Comprehensive Error Handling
|
|
229
239
|
|
|
230
240
|
```javascript
|
|
231
|
-
|
|
232
|
-
|
|
241
|
+
iframeIO.on('error', (error) => {
|
|
242
|
+
switch (error.type) {
|
|
243
|
+
case 'INVALID_ORIGIN':
|
|
244
|
+
console.error(`Invalid origin: expected ${error.expected}, got ${error.received}`)
|
|
245
|
+
break
|
|
246
|
+
case 'ORIGIN_MISMATCH':
|
|
247
|
+
console.error(`Origin mismatch: expected ${error.expected}, got ${error.received}`)
|
|
248
|
+
break
|
|
249
|
+
case 'RATE_LIMIT_EXCEEDED':
|
|
250
|
+
console.warn(`Rate limit exceeded: ${error.current}/${error.limit} messages/second`)
|
|
251
|
+
break
|
|
252
|
+
case 'MESSAGE_HANDLING_ERROR':
|
|
253
|
+
console.error(`Error handling event ${error.event}: ${error.error}`)
|
|
254
|
+
break
|
|
255
|
+
case 'EMIT_ERROR':
|
|
256
|
+
console.error(`Error sending event ${error.event}: ${error.error}`)
|
|
257
|
+
break
|
|
258
|
+
case 'LISTENER_ERROR':
|
|
259
|
+
console.error(`Error in listener for ${error.event}: ${error.error}`)
|
|
260
|
+
break
|
|
261
|
+
case 'NO_CONNECTION':
|
|
262
|
+
console.error(`Attempted to send ${error.event} without connection`)
|
|
263
|
+
break
|
|
264
|
+
default:
|
|
265
|
+
console.error('Unknown error:', error)
|
|
266
|
+
}
|
|
267
|
+
})
|
|
233
268
|
```
|
|
234
269
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
Wait for connection to be established.
|
|
270
|
+
## Message Queuing
|
|
238
271
|
|
|
239
272
|
```javascript
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
console.log('Connected!')
|
|
243
|
-
} catch (error) {
|
|
244
|
-
console.error('Connection timeout')
|
|
245
|
-
}
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
### Utility Methods
|
|
273
|
+
// Messages are automatically queued when disconnected
|
|
274
|
+
iframeIO.emit('important-data', { data: 'This will be queued if disconnected' })
|
|
249
275
|
|
|
250
|
-
|
|
276
|
+
// Clear queue manually if needed
|
|
277
|
+
iframeIO.clearQueue()
|
|
251
278
|
|
|
252
|
-
Check
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
if (iof.isConnected()) {
|
|
256
|
-
console.log('Connection is active')
|
|
257
|
-
}
|
|
279
|
+
// Check queue status
|
|
280
|
+
const stats = iframeIO.getStats()
|
|
281
|
+
console.log(`${stats.queuedMessages} messages queued`)
|
|
258
282
|
```
|
|
259
283
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
Get connection statistics.
|
|
284
|
+
## API Reference
|
|
263
285
|
|
|
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
|
-
```
|
|
286
|
+
### New Methods
|
|
280
287
|
|
|
281
|
-
####
|
|
288
|
+
#### Async Methods
|
|
289
|
+
- **`emitAsync(event, payload?, timeout?)`** - Send message and wait for response (Promise)
|
|
290
|
+
- **`connectAsync(timeout?)`** - Wait for connection with timeout (Promise)
|
|
291
|
+
- **`onceAsync(event)`** - Wait for single event (Promise)
|
|
282
292
|
|
|
283
|
-
|
|
293
|
+
#### Utility Methods
|
|
294
|
+
- **`getStats()`** - Get connection statistics
|
|
295
|
+
- **`clearQueue()`** - Clear queued messages
|
|
284
296
|
|
|
285
|
-
|
|
286
|
-
iof.clearQueue()
|
|
287
|
-
```
|
|
297
|
+
### Connection Methods
|
|
288
298
|
|
|
289
|
-
|
|
299
|
+
- **`initiate(contentWindow, iframeOrigin)`** - Establish connection (WINDOW peer only)
|
|
300
|
+
- **`listen(hostOrigin?)`** - Listen for connection (IFRAME peer only)
|
|
301
|
+
- **`disconnect(callback?)`** - Disconnect and cleanup
|
|
302
|
+
- **`isConnected()`** - Check connection status
|
|
290
303
|
|
|
291
|
-
###
|
|
304
|
+
### Messaging Methods
|
|
292
305
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
| `reconnection_failed` | All reconnection attempts failed | `{ attempts: number }` |
|
|
299
|
-
| `error` | Error occurred | `{ type: string, ...details }` |
|
|
306
|
+
- **`emit(event, payload?, callback?)`** - Send message
|
|
307
|
+
- **`on(event, listener)`** - Add event listener
|
|
308
|
+
- **`once(event, listener)`** - Add one-time event listener
|
|
309
|
+
- **`off(event, listener?)`** - Remove event listener(s)
|
|
310
|
+
- **`removeListeners(callback?)`** - Remove all listeners
|
|
300
311
|
|
|
301
|
-
###
|
|
312
|
+
### Events
|
|
302
313
|
|
|
303
|
-
|
|
304
|
-
-
|
|
305
|
-
-
|
|
306
|
-
-
|
|
307
|
-
-
|
|
308
|
-
- `ORIGIN_MISMATCH` - Origin doesn't match established connection
|
|
309
|
-
- `NO_CONNECTION` - Attempted to send without connection
|
|
314
|
+
#### Connection Events
|
|
315
|
+
- **`connect`** - Connection established
|
|
316
|
+
- **`disconnect`** - Connection lost with reason
|
|
317
|
+
- **`reconnecting`** - Reconnection attempt started
|
|
318
|
+
- **`reconnection_failed`** - All reconnection attempts failed
|
|
310
319
|
|
|
311
|
-
|
|
320
|
+
#### Error Events
|
|
321
|
+
- **`error`** - Various error conditions with detailed error objects
|
|
312
322
|
|
|
313
|
-
|
|
323
|
+
## TypeScript Support
|
|
314
324
|
|
|
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
|
-
})
|
|
325
|
+
Full TypeScript support with comprehensive type definitions:
|
|
324
326
|
|
|
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
|
-
```
|
|
327
|
+
```typescript
|
|
328
|
+
import IOF, { Options, Listener, AckFunction } from 'iframe.io'
|
|
335
329
|
|
|
336
|
-
|
|
330
|
+
const options: Options = {
|
|
331
|
+
type: 'WINDOW',
|
|
332
|
+
debug: true,
|
|
333
|
+
heartbeatInterval: 30000,
|
|
334
|
+
maxMessageSize: 512 * 1024
|
|
335
|
+
}
|
|
337
336
|
|
|
338
|
-
|
|
339
|
-
const iof = new IOF({
|
|
340
|
-
heartbeatInterval: 10000, // 10 seconds
|
|
341
|
-
connectionTimeout: 5000, // 5 seconds
|
|
342
|
-
autoReconnect: true
|
|
343
|
-
})
|
|
337
|
+
const iframeIO = new IOF(options)
|
|
344
338
|
|
|
345
|
-
|
|
346
|
-
|
|
339
|
+
// Typed event listeners
|
|
340
|
+
iframeIO.on('userAction', (data: { action: string; userId: number }) => {
|
|
341
|
+
console.log(`User ${data.userId} performed ${data.action}`)
|
|
347
342
|
})
|
|
348
343
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
344
|
+
// Typed async responses
|
|
345
|
+
interface ApiResponse {
|
|
346
|
+
success: boolean
|
|
347
|
+
data: any[]
|
|
348
|
+
}
|
|
352
349
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
}
|
|
350
|
+
const response = await iframeIO.emitAsync<{ query: string }, ApiResponse>(
|
|
351
|
+
'search',
|
|
352
|
+
{ query: 'hello' },
|
|
353
|
+
5000 // 5 second timeout
|
|
354
|
+
)
|
|
356
355
|
```
|
|
357
356
|
|
|
358
|
-
|
|
357
|
+
## Error Types Reference
|
|
359
358
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
359
|
+
| Error Type | Description |
|
|
360
|
+
|------------|-------------|
|
|
361
|
+
| `INVALID_ORIGIN` | Message from unexpected origin |
|
|
362
|
+
| `ORIGIN_MISMATCH` | Origin changed during session |
|
|
363
|
+
| `MESSAGE_HANDLING_ERROR` | Error processing incoming message |
|
|
364
|
+
| `EMIT_ERROR` | Error sending message |
|
|
365
|
+
| `LISTENER_ERROR` | Error in event listener |
|
|
366
|
+
| `DISALLOWED_EVENT` | Incoming event rejected by `allowedIncomingEvents` |
|
|
367
|
+
| `INVALID_MESSAGE` | Incoming message rejected by `validateIncoming` |
|
|
368
|
+
| `RATE_LIMIT_EXCEEDED` | Too many messages sent |
|
|
369
|
+
| `NO_CONNECTION` | Attempted to send without connection |
|
|
365
370
|
|
|
366
|
-
|
|
367
|
-
iof.emit('queuedMessage', { data: 'This will be queued if disconnected' })
|
|
371
|
+
## Browser Compatibility
|
|
368
372
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
+
- Chrome 60+
|
|
374
|
+
- Firefox 55+
|
|
375
|
+
- Safari 12+
|
|
376
|
+
- Edge 79+
|
|
373
377
|
|
|
374
|
-
|
|
378
|
+
## Migration Guide
|
|
375
379
|
|
|
376
|
-
|
|
377
|
-
const iof = new IOF({
|
|
378
|
-
maxMessagesPerSecond: 50,
|
|
379
|
-
maxMessageSize: 512 * 1024 // 512KB
|
|
380
|
-
})
|
|
380
|
+
### From v1.0.x to v1.1.0
|
|
381
381
|
|
|
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
|
|
382
|
+
All existing code continues to work without changes. New features are additive:
|
|
390
383
|
|
|
391
384
|
```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')
|
|
385
|
+
// Old way (still works)
|
|
386
|
+
iframeIO.emit('getData', { id: 123 }, (error, result) => {
|
|
387
|
+
if (error) {
|
|
388
|
+
console.error('Error:', error)
|
|
389
|
+
} else {
|
|
390
|
+
console.log('Result:', result)
|
|
406
391
|
}
|
|
407
|
-
// Process valid data...
|
|
408
392
|
})
|
|
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
393
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
heartbeatInterval: 15000
|
|
394
|
+
// New async way
|
|
395
|
+
try {
|
|
396
|
+
const result = await iframeIO.emitAsync('getData', { id: 123 })
|
|
397
|
+
console.log('Result:', result)
|
|
398
|
+
} catch (error) {
|
|
399
|
+
console.error('Error:', error.message)
|
|
431
400
|
}
|
|
401
|
+
```
|
|
432
402
|
|
|
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
|
-
})
|
|
403
|
+
## Performance Considerations
|
|
440
404
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
```
|
|
405
|
+
- **Message Size**: Keep messages under the configured `maxMessageSize` (default 1MB)
|
|
406
|
+
- **Rate Limiting**: Respect the `maxMessagesPerSecond` limit (default 100/sec)
|
|
407
|
+
- **Queue Size**: Monitor queued messages to avoid memory issues
|
|
408
|
+
- **Heartbeat**: Adjust `heartbeatInterval` based on your reliability needs
|
|
446
409
|
|
|
447
410
|
## License
|
|
448
411
|
|
|
@@ -456,4 +419,4 @@ Contributions are welcome! Please read our contributing guidelines and submit pu
|
|
|
456
419
|
|
|
457
420
|
- Create an issue on GitHub for bug reports
|
|
458
421
|
- Check existing issues for common problems
|
|
459
|
-
- Review the documentation for usage examples
|
|
422
|
+
- Review the documentation for usage examples
|