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/dist/index.js
CHANGED
|
@@ -1,24 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __assign = (this && this.__assign) || function () {
|
|
3
|
-
__assign = Object.assign || function(t) {
|
|
4
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
-
s = arguments[i];
|
|
6
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
-
t[p] = s[p];
|
|
8
|
-
}
|
|
9
|
-
return t;
|
|
10
|
-
};
|
|
11
|
-
return __assign.apply(this, arguments);
|
|
12
|
-
};
|
|
13
|
-
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
14
|
-
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
15
|
-
if (ar || !(i in from)) {
|
|
16
|
-
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
17
|
-
ar[i] = from[i];
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
return to.concat(ar || Array.prototype.slice.call(from));
|
|
21
|
-
};
|
|
22
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
3
|
function newObject(data) {
|
|
24
4
|
return JSON.parse(JSON.stringify(data));
|
|
@@ -27,88 +7,107 @@ function getMessageSize(data) {
|
|
|
27
7
|
try {
|
|
28
8
|
return JSON.stringify(data).length;
|
|
29
9
|
}
|
|
30
|
-
catch
|
|
10
|
+
catch {
|
|
31
11
|
return 0;
|
|
32
12
|
}
|
|
33
13
|
}
|
|
34
14
|
function sanitizePayload(payload, maxSize) {
|
|
35
15
|
if (!payload)
|
|
36
16
|
return payload;
|
|
37
|
-
|
|
17
|
+
const size = getMessageSize(payload);
|
|
38
18
|
if (size > maxSize)
|
|
39
|
-
throw new Error(
|
|
19
|
+
throw new Error(`Message size ${size} exceeds limit ${maxSize}`);
|
|
40
20
|
// Basic sanitization - remove functions and undefined values
|
|
41
21
|
return JSON.parse(JSON.stringify(payload));
|
|
42
22
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
23
|
+
const ackId = () => {
|
|
24
|
+
// Prefer cryptographically strong randomness when available
|
|
25
|
+
try {
|
|
26
|
+
const globalCrypto = (typeof crypto !== 'undefined'
|
|
27
|
+
? crypto
|
|
28
|
+
: (typeof window !== 'undefined' && window.crypto)
|
|
29
|
+
|| (typeof globalThis !== 'undefined' && globalThis.crypto));
|
|
30
|
+
if (globalCrypto && typeof globalCrypto.getRandomValues === 'function') {
|
|
31
|
+
const buffer = new Uint32Array(4);
|
|
32
|
+
globalCrypto.getRandomValues(buffer);
|
|
33
|
+
const randomPart = Array.from(buffer).map(n => n.toString(16)).join('');
|
|
34
|
+
return `${Date.now()}_${randomPart}`;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// Fall back to Math.random-based implementation below
|
|
39
|
+
}
|
|
40
|
+
const rmin = 100000, rmax = 999999, timestampFallback = Date.now(), randomFallback = Math.floor(Math.random() * (rmax - rmin + 1) + rmin);
|
|
41
|
+
return `${timestampFallback}_${randomFallback}`;
|
|
46
42
|
};
|
|
47
|
-
|
|
43
|
+
const RESERVED_EVENTS = [
|
|
48
44
|
'ping',
|
|
49
45
|
'pong',
|
|
50
46
|
'__heartbeat',
|
|
51
47
|
'__heartbeat_response'
|
|
52
48
|
];
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (options === void 0) { options = {}; }
|
|
49
|
+
class IOF {
|
|
50
|
+
constructor(options = {}) {
|
|
56
51
|
this.messageQueue = [];
|
|
57
52
|
this.messageRateTracker = [];
|
|
58
53
|
this.reconnectAttempts = 0;
|
|
59
54
|
this.maxReconnectAttempts = 5;
|
|
60
55
|
if (options && typeof options !== 'object')
|
|
61
56
|
throw new Error('Invalid Options');
|
|
62
|
-
this.options =
|
|
57
|
+
this.options = {
|
|
58
|
+
debug: false,
|
|
59
|
+
heartbeatInterval: 30000,
|
|
60
|
+
connectionTimeout: 10000,
|
|
61
|
+
maxMessageSize: 1024 * 1024,
|
|
62
|
+
maxMessagesPerSecond: 100,
|
|
63
|
+
autoReconnect: true,
|
|
64
|
+
messageQueueSize: 50,
|
|
65
|
+
...options
|
|
66
|
+
};
|
|
63
67
|
this.Events = {};
|
|
64
68
|
this.peer = { type: 'IFRAME', connected: false };
|
|
65
69
|
if (options.type)
|
|
66
70
|
this.peer.type = options.type.toUpperCase();
|
|
67
71
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
this.options.debug && console.debug.apply(console, args);
|
|
74
|
-
};
|
|
75
|
-
IOF.prototype.isConnected = function () {
|
|
72
|
+
debug(...args) {
|
|
73
|
+
this.options.debug && console.debug(...args);
|
|
74
|
+
}
|
|
75
|
+
isConnected() {
|
|
76
76
|
return !!this.peer.connected && !!this.peer.source;
|
|
77
|
-
}
|
|
77
|
+
}
|
|
78
78
|
// Enhanced connection health monitoring
|
|
79
|
-
|
|
80
|
-
var _this = this;
|
|
79
|
+
startHeartbeat() {
|
|
81
80
|
if (!this.options.heartbeatInterval)
|
|
82
81
|
return;
|
|
83
|
-
this.heartbeatTimer = setInterval(
|
|
84
|
-
if (
|
|
85
|
-
|
|
82
|
+
this.heartbeatTimer = setInterval(() => {
|
|
83
|
+
if (this.isConnected()) {
|
|
84
|
+
const now = Date.now();
|
|
86
85
|
// Check if peer is still responsive
|
|
87
|
-
if (
|
|
88
|
-
&& (now -
|
|
89
|
-
|
|
90
|
-
|
|
86
|
+
if (this.peer.lastHeartbeat
|
|
87
|
+
&& (now - this.peer.lastHeartbeat) > (this.options.heartbeatInterval * 2)) {
|
|
88
|
+
this.debug(`[${this.peer.type}] Heartbeat timeout detected`);
|
|
89
|
+
this.handleConnectionLoss();
|
|
91
90
|
return;
|
|
92
91
|
}
|
|
93
92
|
// Send heartbeat
|
|
94
93
|
try {
|
|
95
|
-
|
|
94
|
+
this.emit('__heartbeat', { timestamp: now });
|
|
96
95
|
}
|
|
97
96
|
catch (error) {
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
this.debug(`[${this.peer.type}] Heartbeat send failed:`, error);
|
|
98
|
+
this.handleConnectionLoss();
|
|
100
99
|
}
|
|
101
100
|
}
|
|
102
101
|
}, this.options.heartbeatInterval);
|
|
103
|
-
}
|
|
104
|
-
|
|
102
|
+
}
|
|
103
|
+
stopHeartbeat() {
|
|
105
104
|
if (!this.heartbeatTimer)
|
|
106
105
|
return;
|
|
107
106
|
clearInterval(this.heartbeatTimer);
|
|
108
107
|
this.heartbeatTimer = undefined;
|
|
109
|
-
}
|
|
108
|
+
}
|
|
110
109
|
// Handle connection loss and potential reconnection
|
|
111
|
-
|
|
110
|
+
handleConnectionLoss() {
|
|
112
111
|
if (!this.peer.connected)
|
|
113
112
|
return;
|
|
114
113
|
this.peer.connected = false;
|
|
@@ -117,40 +116,39 @@ var IOF = /** @class */ (function () {
|
|
|
117
116
|
this.options.autoReconnect
|
|
118
117
|
&& this.reconnectAttempts < this.maxReconnectAttempts
|
|
119
118
|
&& this.attemptReconnection();
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
var _this = this;
|
|
119
|
+
}
|
|
120
|
+
attemptReconnection() {
|
|
123
121
|
if (this.reconnectTimer)
|
|
124
122
|
return;
|
|
125
123
|
this.reconnectAttempts++;
|
|
126
|
-
|
|
127
|
-
this.debug(
|
|
128
|
-
this.fire('reconnecting', { attempt: this.reconnectAttempts, delay
|
|
129
|
-
this.reconnectTimer = setTimeout(
|
|
130
|
-
|
|
124
|
+
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts - 1), 30000); // Exponential backoff, max 30s
|
|
125
|
+
this.debug(`[${this.peer.type}] Attempting reconnection ${this.reconnectAttempts}/${this.maxReconnectAttempts} in ${delay}ms`);
|
|
126
|
+
this.fire('reconnecting', { attempt: this.reconnectAttempts, delay });
|
|
127
|
+
this.reconnectTimer = setTimeout(() => {
|
|
128
|
+
this.reconnectTimer = undefined;
|
|
131
129
|
// Re-initiate connection for WINDOW type
|
|
132
|
-
|
|
133
|
-
&&
|
|
134
|
-
&&
|
|
135
|
-
&&
|
|
130
|
+
this.peer.type === 'WINDOW'
|
|
131
|
+
&& this.peer.source
|
|
132
|
+
&& this.peer.origin
|
|
133
|
+
&& this.emit('ping');
|
|
136
134
|
// For IFRAME type, just wait for incoming connection
|
|
137
135
|
// Set timeout for this reconnection attempt
|
|
138
|
-
setTimeout(
|
|
139
|
-
if (
|
|
136
|
+
setTimeout(() => {
|
|
137
|
+
if (this.peer.connected)
|
|
140
138
|
return;
|
|
141
|
-
|
|
142
|
-
?
|
|
143
|
-
:
|
|
144
|
-
},
|
|
139
|
+
this.reconnectAttempts < this.maxReconnectAttempts
|
|
140
|
+
? this.attemptReconnection()
|
|
141
|
+
: this.fire('reconnection_failed', { attempts: this.reconnectAttempts });
|
|
142
|
+
}, this.options.connectionTimeout);
|
|
145
143
|
}, delay);
|
|
146
|
-
}
|
|
144
|
+
}
|
|
147
145
|
// Message rate limiting
|
|
148
|
-
|
|
146
|
+
checkRateLimit() {
|
|
149
147
|
if (!this.options.maxMessagesPerSecond)
|
|
150
148
|
return true;
|
|
151
|
-
|
|
149
|
+
const now = Date.now(), aSecondAgo = now - 1000;
|
|
152
150
|
// Clean old entries
|
|
153
|
-
this.messageRateTracker = this.messageRateTracker.filter(
|
|
151
|
+
this.messageRateTracker = this.messageRateTracker.filter(timestamp => timestamp > aSecondAgo);
|
|
154
152
|
// Check if limit exceeded
|
|
155
153
|
if (this.messageRateTracker.length >= this.options.maxMessagesPerSecond) {
|
|
156
154
|
this.fire('error', {
|
|
@@ -162,45 +160,43 @@ var IOF = /** @class */ (function () {
|
|
|
162
160
|
}
|
|
163
161
|
this.messageRateTracker.push(now);
|
|
164
162
|
return true;
|
|
165
|
-
}
|
|
163
|
+
}
|
|
166
164
|
// Queue messages when not connected
|
|
167
|
-
|
|
165
|
+
queueMessage(_event, payload, fn) {
|
|
168
166
|
if (this.messageQueue.length >= this.options.messageQueueSize) {
|
|
169
167
|
// Remove oldest message
|
|
170
|
-
|
|
171
|
-
this.debug(
|
|
168
|
+
const removed = this.messageQueue.shift();
|
|
169
|
+
this.debug(`[${this.peer.type}] Message queue full, removed oldest message:`, removed?._event);
|
|
172
170
|
}
|
|
173
171
|
this.messageQueue.push({
|
|
174
|
-
_event
|
|
175
|
-
payload
|
|
176
|
-
fn
|
|
172
|
+
_event,
|
|
173
|
+
payload,
|
|
174
|
+
fn,
|
|
177
175
|
timestamp: Date.now()
|
|
178
176
|
});
|
|
179
|
-
this.debug(
|
|
180
|
-
}
|
|
177
|
+
this.debug(`[${this.peer.type}] Queued message: ${_event} (queue size: ${this.messageQueue.length})`);
|
|
178
|
+
}
|
|
181
179
|
// Process queued messages when connection is established
|
|
182
|
-
|
|
183
|
-
var _this = this;
|
|
180
|
+
processMessageQueue() {
|
|
184
181
|
if (!this.isConnected() || this.messageQueue.length === 0)
|
|
185
182
|
return;
|
|
186
|
-
this.debug(
|
|
187
|
-
|
|
183
|
+
this.debug(`[${this.peer.type}] Processing ${this.messageQueue.length} queued messages`);
|
|
184
|
+
const queue = [...this.messageQueue];
|
|
188
185
|
this.messageQueue = [];
|
|
189
|
-
queue.forEach(
|
|
186
|
+
queue.forEach(message => {
|
|
190
187
|
try {
|
|
191
|
-
|
|
188
|
+
this.emit(message._event, message.payload, message.fn);
|
|
192
189
|
}
|
|
193
190
|
catch (error) {
|
|
194
|
-
|
|
191
|
+
this.debug(`[${this.peer.type}] Failed to send queued message:`, error);
|
|
195
192
|
}
|
|
196
193
|
});
|
|
197
|
-
}
|
|
194
|
+
}
|
|
198
195
|
/**
|
|
199
196
|
* Establish a connection with an iframe containing
|
|
200
197
|
* in the current window
|
|
201
198
|
*/
|
|
202
|
-
|
|
203
|
-
var _this = this;
|
|
199
|
+
initiate(contentWindow, iframeOrigin) {
|
|
204
200
|
if (!contentWindow || !iframeOrigin)
|
|
205
201
|
throw new Error('Invalid Connection initiation arguments');
|
|
206
202
|
if (this.peer.type === 'IFRAME')
|
|
@@ -211,73 +207,94 @@ var IOF = /** @class */ (function () {
|
|
|
211
207
|
this.peer.origin = iframeOrigin;
|
|
212
208
|
this.peer.connected = false;
|
|
213
209
|
this.reconnectAttempts = 0;
|
|
214
|
-
this.messageListener =
|
|
215
|
-
var origin = _a.origin, data = _a.data, source = _a.source;
|
|
210
|
+
this.messageListener = ({ origin, data, source }) => {
|
|
216
211
|
try {
|
|
217
212
|
// Enhanced security: check valid message structure
|
|
218
|
-
if (origin !==
|
|
213
|
+
if (origin !== this.peer.origin
|
|
219
214
|
|| !source
|
|
220
215
|
|| typeof data !== 'object'
|
|
221
216
|
|| !data.hasOwnProperty('_event'))
|
|
222
217
|
return;
|
|
223
|
-
|
|
218
|
+
const { _event, payload, cid, timestamp } = data;
|
|
224
219
|
// Handle heartbeat responses
|
|
225
220
|
if (_event === '__heartbeat_response') {
|
|
226
|
-
|
|
221
|
+
this.peer.lastHeartbeat = Date.now();
|
|
227
222
|
return;
|
|
228
223
|
}
|
|
229
224
|
// Handle heartbeat requests
|
|
230
225
|
if (_event === '__heartbeat') {
|
|
231
|
-
|
|
232
|
-
|
|
226
|
+
this.emit('__heartbeat_response', { timestamp: Date.now() });
|
|
227
|
+
this.peer.lastHeartbeat = Date.now();
|
|
233
228
|
return;
|
|
234
229
|
}
|
|
235
|
-
|
|
230
|
+
this.debug(`[${this.peer.type}] Message: ${_event}`, payload || '');
|
|
236
231
|
// Handshake or availability check events
|
|
237
232
|
if (_event == 'pong') {
|
|
238
233
|
// Content Window is connected to iframe
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
234
|
+
this.peer.connected = true;
|
|
235
|
+
this.reconnectAttempts = 0;
|
|
236
|
+
this.peer.lastHeartbeat = Date.now();
|
|
237
|
+
this.startHeartbeat();
|
|
238
|
+
this.fire('connect');
|
|
239
|
+
this.processMessageQueue();
|
|
240
|
+
this.debug(`[${this.peer.type}] connected`);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
// Optional application-level incoming validation (non-reserved events only)
|
|
244
|
+
if (!RESERVED_EVENTS.includes(_event)) {
|
|
245
|
+
if (this.options.allowedIncomingEvents
|
|
246
|
+
&& !this.options.allowedIncomingEvents.includes(_event)) {
|
|
247
|
+
this.fire('error', {
|
|
248
|
+
type: 'DISALLOWED_EVENT',
|
|
249
|
+
direction: 'incoming',
|
|
250
|
+
event: _event,
|
|
251
|
+
origin
|
|
252
|
+
});
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (this.options.validateIncoming
|
|
256
|
+
&& !this.options.validateIncoming(_event, payload, origin)) {
|
|
257
|
+
this.fire('error', {
|
|
258
|
+
type: 'INVALID_MESSAGE',
|
|
259
|
+
direction: 'incoming',
|
|
260
|
+
event: _event,
|
|
261
|
+
origin
|
|
262
|
+
});
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
246
265
|
}
|
|
247
266
|
// Fire available event listeners
|
|
248
|
-
|
|
267
|
+
this.fire(_event, payload, cid);
|
|
249
268
|
}
|
|
250
269
|
catch (error) {
|
|
251
|
-
|
|
252
|
-
|
|
270
|
+
this.debug(`[${this.peer.type}] Message handling error:`, error);
|
|
271
|
+
this.fire('error', {
|
|
253
272
|
type: 'MESSAGE_HANDLING_ERROR',
|
|
254
273
|
error: error instanceof Error ? error.message : String(error),
|
|
255
|
-
origin
|
|
274
|
+
origin
|
|
256
275
|
});
|
|
257
276
|
}
|
|
258
277
|
};
|
|
259
278
|
window.addEventListener('message', this.messageListener, false);
|
|
260
|
-
this.debug(
|
|
279
|
+
this.debug(`[${this.peer.type}] Initiate connection: IFrame origin <${iframeOrigin}>`);
|
|
261
280
|
this.emit('ping');
|
|
262
281
|
return this;
|
|
263
|
-
}
|
|
282
|
+
}
|
|
264
283
|
/**
|
|
265
284
|
* Listening to connection from the content window
|
|
266
285
|
*/
|
|
267
|
-
|
|
268
|
-
var _this = this;
|
|
286
|
+
listen(hostOrigin) {
|
|
269
287
|
this.peer.type = 'IFRAME'; // iframe.io connection listener is automatically set as IFRAME
|
|
270
288
|
this.peer.connected = false;
|
|
271
289
|
this.reconnectAttempts = 0;
|
|
272
|
-
this.debug(
|
|
290
|
+
this.debug(`[${this.peer.type}] Listening to connect${hostOrigin ? `: Host <${hostOrigin}>` : ''}`);
|
|
273
291
|
// Clean up existing listener if any
|
|
274
292
|
this.cleanup();
|
|
275
|
-
this.messageListener =
|
|
276
|
-
var origin = _a.origin, data = _a.data, source = _a.source;
|
|
293
|
+
this.messageListener = ({ origin, data, source }) => {
|
|
277
294
|
try {
|
|
278
295
|
// Enhanced security: check host origin where event must only come from
|
|
279
296
|
if (hostOrigin && hostOrigin !== origin) {
|
|
280
|
-
|
|
297
|
+
this.fire('error', {
|
|
281
298
|
type: 'INVALID_ORIGIN',
|
|
282
299
|
expected: hostOrigin,
|
|
283
300
|
received: origin
|
|
@@ -290,75 +307,73 @@ var IOF = /** @class */ (function () {
|
|
|
290
307
|
|| !data.hasOwnProperty('_event'))
|
|
291
308
|
return;
|
|
292
309
|
// Define peer source window and origin
|
|
293
|
-
if (!
|
|
294
|
-
|
|
295
|
-
|
|
310
|
+
if (!this.peer.source) {
|
|
311
|
+
this.peer = { ...this.peer, source: source, origin };
|
|
312
|
+
this.debug(`[${this.peer.type}] Connect to ${origin}`);
|
|
296
313
|
}
|
|
297
314
|
// Origin different from handshaked source origin
|
|
298
|
-
else if (origin !==
|
|
299
|
-
|
|
315
|
+
else if (origin !== this.peer.origin) {
|
|
316
|
+
this.fire('error', {
|
|
300
317
|
type: 'ORIGIN_MISMATCH',
|
|
301
|
-
expected:
|
|
318
|
+
expected: this.peer.origin,
|
|
302
319
|
received: origin
|
|
303
320
|
});
|
|
304
321
|
return;
|
|
305
322
|
}
|
|
306
|
-
|
|
323
|
+
const { _event, payload, cid, timestamp } = data;
|
|
307
324
|
// Handle heartbeat responses
|
|
308
325
|
if (_event === '__heartbeat_response') {
|
|
309
|
-
|
|
326
|
+
this.peer.lastHeartbeat = Date.now();
|
|
310
327
|
return;
|
|
311
328
|
}
|
|
312
329
|
// Handle heartbeat requests
|
|
313
330
|
if (_event === '__heartbeat') {
|
|
314
|
-
|
|
315
|
-
|
|
331
|
+
this.emit('__heartbeat_response', { timestamp: Date.now() });
|
|
332
|
+
this.peer.lastHeartbeat = Date.now();
|
|
316
333
|
return;
|
|
317
334
|
}
|
|
318
|
-
|
|
335
|
+
this.debug(`[${this.peer.type}] Message: ${_event}`, payload || '');
|
|
319
336
|
// Handshake or availability check events
|
|
320
337
|
if (_event == 'ping') {
|
|
321
|
-
|
|
338
|
+
this.emit('pong');
|
|
322
339
|
// Iframe is connected to content window
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
340
|
+
this.peer.connected = true;
|
|
341
|
+
this.reconnectAttempts = 0;
|
|
342
|
+
this.peer.lastHeartbeat = Date.now();
|
|
343
|
+
this.startHeartbeat();
|
|
344
|
+
this.fire('connect');
|
|
345
|
+
this.processMessageQueue();
|
|
346
|
+
this.debug(`[${this.peer.type}] connected`);
|
|
347
|
+
return;
|
|
330
348
|
}
|
|
331
349
|
// Fire available event listeners
|
|
332
|
-
|
|
350
|
+
this.fire(_event, payload, cid);
|
|
333
351
|
}
|
|
334
352
|
catch (error) {
|
|
335
|
-
|
|
336
|
-
|
|
353
|
+
this.debug(`[${this.peer.type}] Message handling error:`, error);
|
|
354
|
+
this.fire('error', {
|
|
337
355
|
type: 'MESSAGE_HANDLING_ERROR',
|
|
338
356
|
error: error instanceof Error ? error.message : String(error),
|
|
339
|
-
origin
|
|
357
|
+
origin
|
|
340
358
|
});
|
|
341
359
|
}
|
|
342
360
|
};
|
|
343
361
|
window.addEventListener('message', this.messageListener, false);
|
|
344
362
|
return this;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
var _this = this;
|
|
363
|
+
}
|
|
364
|
+
fire(_event, payload, cid) {
|
|
348
365
|
// Volatile event - check if any listeners exist
|
|
349
|
-
if (!this.Events[_event] && !this.Events[_event + '--@once'])
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
}
|
|
357
|
-
_this.emit("".concat(_event, "--").concat(cid, "--@ack"), { error: error || false, args: args });
|
|
366
|
+
if (!this.Events[_event] && !this.Events[_event + '--@once']) {
|
|
367
|
+
this.debug(`[${this.peer.type}] No <${_event}> listener defined`);
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
const ackFn = cid
|
|
371
|
+
? (error, ...args) => {
|
|
372
|
+
this.emit(`${_event}--${cid}--@ack`, { error: error || false, args });
|
|
358
373
|
return;
|
|
359
374
|
}
|
|
360
375
|
: undefined;
|
|
361
|
-
|
|
376
|
+
let listeners = [];
|
|
362
377
|
if (this.Events[_event + '--@once']) {
|
|
363
378
|
// Once triggable event
|
|
364
379
|
_event += '--@once';
|
|
@@ -369,21 +384,21 @@ var IOF = /** @class */ (function () {
|
|
|
369
384
|
else
|
|
370
385
|
listeners = this.Events[_event];
|
|
371
386
|
// Fire listeners with error handling
|
|
372
|
-
listeners.forEach(
|
|
387
|
+
listeners.forEach(fn => {
|
|
373
388
|
try {
|
|
374
389
|
payload !== undefined ? fn(payload, ackFn) : fn(ackFn);
|
|
375
390
|
}
|
|
376
391
|
catch (error) {
|
|
377
|
-
|
|
378
|
-
|
|
392
|
+
this.debug(`[${this.peer.type}] Listener error for ${_event}:`, error);
|
|
393
|
+
this.fire('error', {
|
|
379
394
|
type: 'LISTENER_ERROR',
|
|
380
395
|
event: _event,
|
|
381
396
|
error: error instanceof Error ? error.message : String(error)
|
|
382
397
|
});
|
|
383
398
|
}
|
|
384
399
|
});
|
|
385
|
-
}
|
|
386
|
-
|
|
400
|
+
}
|
|
401
|
+
emit(_event, payload, fn) {
|
|
387
402
|
// Check rate limiting
|
|
388
403
|
if (!this.checkRateLimit())
|
|
389
404
|
return this;
|
|
@@ -405,30 +420,27 @@ var IOF = /** @class */ (function () {
|
|
|
405
420
|
}
|
|
406
421
|
try {
|
|
407
422
|
// Enhanced security: sanitize and validate payload
|
|
408
|
-
|
|
423
|
+
const sanitizedPayload = payload
|
|
409
424
|
? sanitizePayload(payload, this.options.maxMessageSize)
|
|
410
425
|
: payload;
|
|
411
426
|
// Acknowledge event listener
|
|
412
|
-
|
|
427
|
+
let cid;
|
|
413
428
|
if (typeof fn === 'function') {
|
|
414
|
-
|
|
429
|
+
const ackFunction = fn;
|
|
415
430
|
cid = ackId();
|
|
416
|
-
this.once(
|
|
417
|
-
var error = _a.error, args = _a.args;
|
|
418
|
-
return ackFunction_1.apply(void 0, __spreadArray([error], args, false));
|
|
419
|
-
});
|
|
431
|
+
this.once(`${_event}--${cid}--@ack`, ({ error, args }) => ackFunction(error, ...args));
|
|
420
432
|
}
|
|
421
|
-
|
|
422
|
-
_event
|
|
433
|
+
const messageData = {
|
|
434
|
+
_event,
|
|
423
435
|
payload: sanitizedPayload,
|
|
424
|
-
cid
|
|
436
|
+
cid,
|
|
425
437
|
timestamp: Date.now(),
|
|
426
438
|
size: getMessageSize(sanitizedPayload)
|
|
427
439
|
};
|
|
428
440
|
this.peer.source.postMessage(newObject(messageData), this.peer.origin);
|
|
429
441
|
}
|
|
430
442
|
catch (error) {
|
|
431
|
-
this.debug(
|
|
443
|
+
this.debug(`[${this.peer.type}] Emit error:`, error);
|
|
432
444
|
this.fire('error', {
|
|
433
445
|
type: 'EMIT_ERROR',
|
|
434
446
|
event: _event,
|
|
@@ -439,29 +451,29 @@ var IOF = /** @class */ (function () {
|
|
|
439
451
|
&& fn(error instanceof Error ? error.message : String(error));
|
|
440
452
|
}
|
|
441
453
|
return this;
|
|
442
|
-
}
|
|
443
|
-
|
|
454
|
+
}
|
|
455
|
+
on(_event, fn) {
|
|
444
456
|
// Add Event listener
|
|
445
457
|
if (!this.Events[_event])
|
|
446
458
|
this.Events[_event] = [];
|
|
447
459
|
this.Events[_event].push(fn);
|
|
448
|
-
this.debug(
|
|
460
|
+
this.debug(`[${this.peer.type}] New <${_event}> listener on`);
|
|
449
461
|
return this;
|
|
450
|
-
}
|
|
451
|
-
|
|
462
|
+
}
|
|
463
|
+
once(_event, fn) {
|
|
452
464
|
// Add Once Event listener
|
|
453
465
|
_event += '--@once';
|
|
454
466
|
if (!this.Events[_event])
|
|
455
467
|
this.Events[_event] = [];
|
|
456
468
|
this.Events[_event].push(fn);
|
|
457
|
-
this.debug(
|
|
469
|
+
this.debug(`[${this.peer.type}] New <${_event} once> listener on`);
|
|
458
470
|
return this;
|
|
459
|
-
}
|
|
460
|
-
|
|
471
|
+
}
|
|
472
|
+
off(_event, fn) {
|
|
461
473
|
// Remove Event listener
|
|
462
474
|
if (fn && this.Events[_event]) {
|
|
463
475
|
// Remove specific listener if provided
|
|
464
|
-
|
|
476
|
+
const index = this.Events[_event].indexOf(fn);
|
|
465
477
|
if (index > -1) {
|
|
466
478
|
this.Events[_event].splice(index, 1);
|
|
467
479
|
// Remove event array if empty
|
|
@@ -473,29 +485,23 @@ var IOF = /** @class */ (function () {
|
|
|
473
485
|
else
|
|
474
486
|
delete this.Events[_event];
|
|
475
487
|
typeof fn == 'function' && fn();
|
|
476
|
-
this.debug(
|
|
488
|
+
this.debug(`[${this.peer.type}] <${_event}> listener off`);
|
|
477
489
|
return this;
|
|
478
|
-
}
|
|
479
|
-
|
|
490
|
+
}
|
|
491
|
+
removeListeners(fn) {
|
|
480
492
|
// Clear all event listeners
|
|
481
493
|
this.Events = {};
|
|
482
494
|
typeof fn == 'function' && fn();
|
|
483
|
-
this.debug(
|
|
495
|
+
this.debug(`[${this.peer.type}] All listeners removed`);
|
|
484
496
|
return this;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
var timeoutId = setTimeout(function () {
|
|
491
|
-
reject(new Error("Event '".concat(_event, "' acknowledgment timeout after ").concat(timeout, "ms")));
|
|
497
|
+
}
|
|
498
|
+
emitAsync(_event, payload, timeout = 5000) {
|
|
499
|
+
return new Promise((resolve, reject) => {
|
|
500
|
+
const timeoutId = setTimeout(() => {
|
|
501
|
+
reject(new Error(`Event '${_event}' acknowledgment timeout after ${timeout}ms`));
|
|
492
502
|
}, timeout);
|
|
493
503
|
try {
|
|
494
|
-
|
|
495
|
-
var args = [];
|
|
496
|
-
for (var _i = 1; _i < arguments.length; _i++) {
|
|
497
|
-
args[_i - 1] = arguments[_i];
|
|
498
|
-
}
|
|
504
|
+
this.emit(_event, payload, (error, ...args) => {
|
|
499
505
|
clearTimeout(timeoutId);
|
|
500
506
|
error
|
|
501
507
|
? reject(new Error(typeof error === 'string' ? error : 'Ack error'))
|
|
@@ -507,29 +513,27 @@ var IOF = /** @class */ (function () {
|
|
|
507
513
|
reject(error);
|
|
508
514
|
}
|
|
509
515
|
});
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
return new Promise(function (resolve, reject) {
|
|
518
|
-
if (_this.isConnected())
|
|
516
|
+
}
|
|
517
|
+
onceAsync(_event) {
|
|
518
|
+
return new Promise(resolve => this.once(_event, resolve));
|
|
519
|
+
}
|
|
520
|
+
connectAsync(timeout) {
|
|
521
|
+
return new Promise((resolve, reject) => {
|
|
522
|
+
if (this.isConnected())
|
|
519
523
|
return resolve();
|
|
520
|
-
|
|
521
|
-
|
|
524
|
+
const timeoutId = setTimeout(() => {
|
|
525
|
+
this.off('connect', connectHandler);
|
|
522
526
|
reject(new Error('Connection timeout'));
|
|
523
|
-
}, timeout ||
|
|
524
|
-
|
|
527
|
+
}, timeout || this.options.connectionTimeout);
|
|
528
|
+
const connectHandler = () => {
|
|
525
529
|
clearTimeout(timeoutId);
|
|
526
530
|
resolve();
|
|
527
531
|
};
|
|
528
|
-
|
|
532
|
+
this.once('connect', connectHandler);
|
|
529
533
|
});
|
|
530
|
-
}
|
|
534
|
+
}
|
|
531
535
|
// Clean up all resources
|
|
532
|
-
|
|
536
|
+
cleanup() {
|
|
533
537
|
if (this.messageListener) {
|
|
534
538
|
window.removeEventListener('message', this.messageListener);
|
|
535
539
|
this.messageListener = undefined;
|
|
@@ -539,9 +543,9 @@ var IOF = /** @class */ (function () {
|
|
|
539
543
|
clearTimeout(this.reconnectTimer);
|
|
540
544
|
this.reconnectTimer = undefined;
|
|
541
545
|
}
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
//
|
|
546
|
+
}
|
|
547
|
+
disconnect(fn) {
|
|
548
|
+
// Cleanup on disconnect
|
|
545
549
|
this.cleanup();
|
|
546
550
|
this.peer.connected = false;
|
|
547
551
|
this.peer.source = undefined;
|
|
@@ -552,11 +556,11 @@ var IOF = /** @class */ (function () {
|
|
|
552
556
|
this.reconnectAttempts = 0;
|
|
553
557
|
this.removeListeners();
|
|
554
558
|
typeof fn == 'function' && fn();
|
|
555
|
-
this.debug(
|
|
559
|
+
this.debug(`[${this.peer.type}] Disconnected`);
|
|
556
560
|
return this;
|
|
557
|
-
}
|
|
561
|
+
}
|
|
558
562
|
// Get connection statistics
|
|
559
|
-
|
|
563
|
+
getStats() {
|
|
560
564
|
return {
|
|
561
565
|
connected: this.isConnected(),
|
|
562
566
|
peerType: this.peer.type,
|
|
@@ -567,14 +571,13 @@ var IOF = /** @class */ (function () {
|
|
|
567
571
|
activeListeners: Object.keys(this.Events).length,
|
|
568
572
|
messageRate: this.messageRateTracker.length
|
|
569
573
|
};
|
|
570
|
-
}
|
|
574
|
+
}
|
|
571
575
|
// Clear message queue manually
|
|
572
|
-
|
|
573
|
-
|
|
576
|
+
clearQueue() {
|
|
577
|
+
const queueSize = this.messageQueue.length;
|
|
574
578
|
this.messageQueue = [];
|
|
575
|
-
this.debug(
|
|
579
|
+
this.debug(`[${this.peer.type}] Cleared ${queueSize} queued messages`);
|
|
576
580
|
return this;
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
}());
|
|
581
|
+
}
|
|
582
|
+
}
|
|
580
583
|
exports.default = IOF;
|