iframe.io 1.0.4 → 1.0.5
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 +458 -1
- package/package.json +7 -1
package/README.md
CHANGED
|
@@ -1,2 +1,459 @@
|
|
|
1
1
|
# iframe.io
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
Easy and friendly API to connect and interact between content window and its containing iframe with enhanced features like heartbeat monitoring, automatic reconnection, message queuing, and rate limiting.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔄 **Bi-directional Communication** - Seamless messaging between parent window and iframe
|
|
8
|
+
- 💓 **Heartbeat Monitoring** - Automatic connection health checking
|
|
9
|
+
- 🔌 **Auto Reconnection** - Automatic reconnection with exponential backoff
|
|
10
|
+
- 📦 **Message Queuing** - Queue messages when connection is lost
|
|
11
|
+
- 🛡️ **Security** - Origin validation and message sanitization
|
|
12
|
+
- ⚡ **Rate Limiting** - Prevent message flooding
|
|
13
|
+
- 🎯 **Event System** - Robust event-driven architecture
|
|
14
|
+
- ✨ **Promise Support** - Async/await compatible methods
|
|
15
|
+
- 📊 **Connection Stats** - Real-time connection statistics
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install iframe.io
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
### Parent Window (WINDOW type)
|
|
26
|
+
|
|
27
|
+
```javascript
|
|
28
|
+
import IOF from 'iframe.io'
|
|
29
|
+
|
|
30
|
+
// Create iframe and get reference
|
|
31
|
+
const iframe = document.createElement('iframe')
|
|
32
|
+
iframe.src = 'https://example.com/iframe-content'
|
|
33
|
+
document.body.appendChild(iframe)
|
|
34
|
+
|
|
35
|
+
// Initialize connection
|
|
36
|
+
const iof = new IOF({ type: 'WINDOW', debug: true })
|
|
37
|
+
|
|
38
|
+
iframe.onload = () => {
|
|
39
|
+
// Initiate connection with iframe
|
|
40
|
+
iof.initiate(iframe.contentWindow, 'https://example.com')
|
|
41
|
+
|
|
42
|
+
// Listen for connection
|
|
43
|
+
iof.on('connect', () => {
|
|
44
|
+
console.log('Connected to iframe!')
|
|
45
|
+
|
|
46
|
+
// Send message
|
|
47
|
+
iof.emit('hello', { message: 'Hello from parent!' })
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
// Listen for messages
|
|
51
|
+
iof.on('response', (data) => {
|
|
52
|
+
console.log('Received:', data)
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Iframe Content (IFRAME type)
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
import IOF from 'iframe.io'
|
|
61
|
+
|
|
62
|
+
// Create connection listener
|
|
63
|
+
const iof = new IOF({ type: 'IFRAME', debug: true })
|
|
64
|
+
|
|
65
|
+
// Listen for parent connection
|
|
66
|
+
iof.listen('https://parent-domain.com')
|
|
67
|
+
|
|
68
|
+
// Handle connection
|
|
69
|
+
iof.on('connect', () => {
|
|
70
|
+
console.log('Connected to parent!')
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
// Listen for messages
|
|
74
|
+
iof.on('hello', (data, ack) => {
|
|
75
|
+
console.log('Received from parent:', data)
|
|
76
|
+
|
|
77
|
+
// Send response
|
|
78
|
+
iof.emit('response', { message: 'Hello from iframe!' })
|
|
79
|
+
|
|
80
|
+
// Acknowledge receipt (optional)
|
|
81
|
+
if (ack) ack(false, 'Message received')
|
|
82
|
+
})
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## API Reference
|
|
86
|
+
|
|
87
|
+
### Constructor
|
|
88
|
+
|
|
89
|
+
```javascript
|
|
90
|
+
new IOF(options?)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
#### Options
|
|
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 |
|
|
105
|
+
|
|
106
|
+
### Connection Methods
|
|
107
|
+
|
|
108
|
+
#### `initiate(contentWindow, iframeOrigin)`
|
|
109
|
+
|
|
110
|
+
Establish connection with an iframe (WINDOW type only).
|
|
111
|
+
|
|
112
|
+
```javascript
|
|
113
|
+
iof.initiate(iframe.contentWindow, 'https://iframe-origin.com')
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Parameters:**
|
|
117
|
+
- `contentWindow` - The iframe's contentWindow
|
|
118
|
+
- `iframeOrigin` - The iframe's origin URL
|
|
119
|
+
|
|
120
|
+
#### `listen(hostOrigin?)`
|
|
121
|
+
|
|
122
|
+
Listen for connection from parent window (IFRAME type).
|
|
123
|
+
|
|
124
|
+
```javascript
|
|
125
|
+
iof.listen('https://parent-origin.com') // Optional origin restriction
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Parameters:**
|
|
129
|
+
- `hostOrigin` (optional) - Restrict connections to specific origin
|
|
130
|
+
|
|
131
|
+
#### `disconnect(callback?)`
|
|
132
|
+
|
|
133
|
+
Disconnect and clean up all resources.
|
|
134
|
+
|
|
135
|
+
```javascript
|
|
136
|
+
iof.disconnect(() => {
|
|
137
|
+
console.log('Disconnected!')
|
|
138
|
+
})
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Messaging Methods
|
|
142
|
+
|
|
143
|
+
#### `emit(event, payload?, callback?)`
|
|
144
|
+
|
|
145
|
+
Send a message to the peer.
|
|
146
|
+
|
|
147
|
+
```javascript
|
|
148
|
+
// Simple message
|
|
149
|
+
iof.emit('myEvent', { data: 'hello' })
|
|
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
|
+
}
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
// Callback as second parameter
|
|
161
|
+
iof.emit('myEvent', (error, response) => {
|
|
162
|
+
console.log('Response:', response)
|
|
163
|
+
})
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
#### `on(event, listener)`
|
|
167
|
+
|
|
168
|
+
Add event listener.
|
|
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')
|
|
176
|
+
})
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
#### `once(event, listener)`
|
|
180
|
+
|
|
181
|
+
Add one-time event listener.
|
|
182
|
+
|
|
183
|
+
```javascript
|
|
184
|
+
iof.once('myEvent', (data) => {
|
|
185
|
+
console.log('This will only fire once:', data)
|
|
186
|
+
})
|
|
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
|
+
|
|
197
|
+
// Remove all listeners for event
|
|
198
|
+
iof.off('myEvent')
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
#### `removeListeners(callback?)`
|
|
202
|
+
|
|
203
|
+
Remove all event listeners.
|
|
204
|
+
|
|
205
|
+
```javascript
|
|
206
|
+
iof.removeListeners(() => {
|
|
207
|
+
console.log('All listeners removed')
|
|
208
|
+
})
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Async Methods
|
|
212
|
+
|
|
213
|
+
#### `emitAsync(event, payload?)`
|
|
214
|
+
|
|
215
|
+
Send message and return Promise.
|
|
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
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
#### `onceAsync(event)`
|
|
227
|
+
|
|
228
|
+
Wait for single event occurrence.
|
|
229
|
+
|
|
230
|
+
```javascript
|
|
231
|
+
const data = await iof.onceAsync('dataReady')
|
|
232
|
+
console.log('Data received:', data)
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
#### `connectAsync(timeout?)`
|
|
236
|
+
|
|
237
|
+
Wait for connection to be established.
|
|
238
|
+
|
|
239
|
+
```javascript
|
|
240
|
+
try {
|
|
241
|
+
await iof.connectAsync(5000) // 5 second timeout
|
|
242
|
+
console.log('Connected!')
|
|
243
|
+
} catch (error) {
|
|
244
|
+
console.error('Connection timeout')
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Utility Methods
|
|
249
|
+
|
|
250
|
+
#### `isConnected()`
|
|
251
|
+
|
|
252
|
+
Check if connection is active.
|
|
253
|
+
|
|
254
|
+
```javascript
|
|
255
|
+
if (iof.isConnected()) {
|
|
256
|
+
console.log('Connection is active')
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
#### `getStats()`
|
|
261
|
+
|
|
262
|
+
Get connection statistics.
|
|
263
|
+
|
|
264
|
+
```javascript
|
|
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
|
+
```
|
|
280
|
+
|
|
281
|
+
#### `clearQueue()`
|
|
282
|
+
|
|
283
|
+
Clear queued messages.
|
|
284
|
+
|
|
285
|
+
```javascript
|
|
286
|
+
iof.clearQueue()
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Events
|
|
290
|
+
|
|
291
|
+
### Built-in Events
|
|
292
|
+
|
|
293
|
+
| Event | Description | Data |
|
|
294
|
+
|-------|-------------|------|
|
|
295
|
+
| `connect` | Connection established | - |
|
|
296
|
+
| `disconnect` | Connection lost | `{ reason: string }` |
|
|
297
|
+
| `reconnecting` | Reconnection attempt | `{ attempt: number, delay: number }` |
|
|
298
|
+
| `reconnection_failed` | All reconnection attempts failed | `{ attempts: number }` |
|
|
299
|
+
| `error` | Error occurred | `{ type: string, ...details }` |
|
|
300
|
+
|
|
301
|
+
### Error Types
|
|
302
|
+
|
|
303
|
+
- `RATE_LIMIT_EXCEEDED` - Message rate limit exceeded
|
|
304
|
+
- `MESSAGE_HANDLING_ERROR` - Error processing received message
|
|
305
|
+
- `EMIT_ERROR` - Error sending message
|
|
306
|
+
- `LISTENER_ERROR` - Error in event listener
|
|
307
|
+
- `INVALID_ORIGIN` - Message from unauthorized origin
|
|
308
|
+
- `ORIGIN_MISMATCH` - Origin doesn't match established connection
|
|
309
|
+
- `NO_CONNECTION` - Attempted to send without connection
|
|
310
|
+
|
|
311
|
+
## Advanced Usage
|
|
312
|
+
|
|
313
|
+
### Message Acknowledgments
|
|
314
|
+
|
|
315
|
+
```javascript
|
|
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
|
+
})
|
|
324
|
+
|
|
325
|
+
// Receiver
|
|
326
|
+
iof.on('processData', async (data, ack) => {
|
|
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
|
+
```
|
|
335
|
+
|
|
336
|
+
### Connection Monitoring
|
|
337
|
+
|
|
338
|
+
```javascript
|
|
339
|
+
const iof = new IOF({
|
|
340
|
+
heartbeatInterval: 10000, // 10 seconds
|
|
341
|
+
connectionTimeout: 5000, // 5 seconds
|
|
342
|
+
autoReconnect: true
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
iof.on('disconnect', ({ reason }) => {
|
|
346
|
+
console.log('Connection lost:', reason)
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
iof.on('reconnecting', ({ attempt, delay }) => {
|
|
350
|
+
console.log(`Reconnecting (${attempt}/5) in ${delay}ms`)
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
iof.on('reconnection_failed', ({ attempts }) => {
|
|
354
|
+
console.log(`Failed to reconnect after ${attempts} attempts`)
|
|
355
|
+
})
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### Message Queuing
|
|
359
|
+
|
|
360
|
+
```javascript
|
|
361
|
+
const iof = new IOF({
|
|
362
|
+
messageQueueSize: 100,
|
|
363
|
+
autoReconnect: true
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
// Messages sent while disconnected are automatically queued
|
|
367
|
+
iof.emit('queuedMessage', { data: 'This will be queued if disconnected' })
|
|
368
|
+
|
|
369
|
+
// Check queue status
|
|
370
|
+
const stats = iof.getStats()
|
|
371
|
+
console.log(`Queued messages: ${stats.queuedMessages}`)
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Rate Limiting
|
|
375
|
+
|
|
376
|
+
```javascript
|
|
377
|
+
const iof = new IOF({
|
|
378
|
+
maxMessagesPerSecond: 50,
|
|
379
|
+
maxMessageSize: 512 * 1024 // 512KB
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
iof.on('error', ({ type, limit, current }) => {
|
|
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
|
|
390
|
+
|
|
391
|
+
```javascript
|
|
392
|
+
// Always specify allowed origins
|
|
393
|
+
iof.listen('https://trusted-parent.com')
|
|
394
|
+
|
|
395
|
+
// Handle security errors
|
|
396
|
+
iof.on('error', ({ type, expected, received }) => {
|
|
397
|
+
if (type === 'INVALID_ORIGIN') {
|
|
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')
|
|
406
|
+
}
|
|
407
|
+
// Process valid data...
|
|
408
|
+
})
|
|
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
|
+
|
|
424
|
+
```typescript
|
|
425
|
+
import IOF, { Options, PeerType, AckFunction, Listener } from 'iframe.io'
|
|
426
|
+
|
|
427
|
+
const options: Options = {
|
|
428
|
+
type: 'WINDOW',
|
|
429
|
+
debug: true,
|
|
430
|
+
heartbeatInterval: 15000
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const iof = new IOF(options)
|
|
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
|
+
})
|
|
440
|
+
|
|
441
|
+
// Type-safe async emissions
|
|
442
|
+
const response = await iof.emitAsync<{ query: string }, { result: any }>('search', {
|
|
443
|
+
query: 'typescript'
|
|
444
|
+
})
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
## License
|
|
448
|
+
|
|
449
|
+
MIT License - see LICENSE file for details.
|
|
450
|
+
|
|
451
|
+
## Contributing
|
|
452
|
+
|
|
453
|
+
Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository.
|
|
454
|
+
|
|
455
|
+
## Support
|
|
456
|
+
|
|
457
|
+
- Create an issue on GitHub for bug reports
|
|
458
|
+
- Check existing issues for common problems
|
|
459
|
+
- Review the documentation for usage examples
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iframe.io",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Easy and friendly API to connect and interact between content window and its containing iframe",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -34,6 +34,12 @@
|
|
|
34
34
|
},
|
|
35
35
|
"keywords": [
|
|
36
36
|
"realtime",
|
|
37
|
+
"embeddable",
|
|
38
|
+
"widget",
|
|
39
|
+
"components",
|
|
40
|
+
"micro-frontend",
|
|
41
|
+
"Secure",
|
|
42
|
+
"cross-origin",
|
|
37
43
|
"iframe",
|
|
38
44
|
"browser",
|
|
39
45
|
"events",
|