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