request-iframe 0.0.6 → 0.1.1
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.CN.md +220 -21
- package/README.md +221 -24
- package/esm/api/client.js +80 -0
- package/esm/api/server.js +61 -0
- package/esm/constants/index.js +289 -0
- package/esm/constants/messages.js +157 -0
- package/esm/core/client-server.js +294 -0
- package/esm/core/client.js +873 -0
- package/esm/core/request.js +27 -0
- package/esm/core/response.js +459 -0
- package/esm/core/server.js +776 -0
- package/esm/index.js +21 -0
- package/esm/interceptors/index.js +122 -0
- package/esm/message/channel.js +182 -0
- package/esm/message/dispatcher.js +418 -0
- package/esm/message/index.js +2 -0
- package/esm/stream/file-stream.js +289 -0
- package/esm/stream/index.js +44 -0
- package/esm/stream/readable-stream.js +539 -0
- package/esm/stream/stream-core.js +204 -0
- package/esm/stream/types.js +1 -0
- package/esm/stream/writable-stream.js +836 -0
- package/esm/types/index.js +1 -0
- package/esm/utils/ack.js +36 -0
- package/esm/utils/cache.js +147 -0
- package/esm/utils/cookie.js +352 -0
- package/esm/utils/debug.js +521 -0
- package/esm/utils/error.js +27 -0
- package/esm/utils/index.js +180 -0
- package/esm/utils/origin.js +30 -0
- package/esm/utils/path-match.js +148 -0
- package/esm/utils/protocol.js +157 -0
- package/library/api/client.d.ts.map +1 -1
- package/library/api/client.js +13 -5
- package/library/api/server.d.ts.map +1 -1
- package/library/api/server.js +6 -1
- package/library/constants/index.d.ts +59 -4
- package/library/constants/index.d.ts.map +1 -1
- package/library/constants/index.js +67 -9
- package/library/constants/messages.d.ts +8 -1
- package/library/constants/messages.d.ts.map +1 -1
- package/library/constants/messages.js +8 -1
- package/library/core/client-server.d.ts +7 -15
- package/library/core/client-server.d.ts.map +1 -1
- package/library/core/client-server.js +56 -44
- package/library/core/client.d.ts +4 -1
- package/library/core/client.d.ts.map +1 -1
- package/library/core/client.js +74 -31
- package/library/core/response.d.ts +21 -3
- package/library/core/response.d.ts.map +1 -1
- package/library/core/response.js +55 -7
- package/library/core/server.d.ts +34 -3
- package/library/core/server.d.ts.map +1 -1
- package/library/core/server.js +191 -21
- package/library/message/channel.d.ts +6 -0
- package/library/message/channel.d.ts.map +1 -1
- package/library/message/channel.js +2 -1
- package/library/message/dispatcher.d.ts +32 -0
- package/library/message/dispatcher.d.ts.map +1 -1
- package/library/message/dispatcher.js +131 -1
- package/library/stream/file-stream.d.ts +4 -0
- package/library/stream/file-stream.d.ts.map +1 -1
- package/library/stream/file-stream.js +61 -33
- package/library/stream/index.d.ts.map +1 -1
- package/library/stream/index.js +2 -0
- package/library/stream/readable-stream.d.ts +30 -11
- package/library/stream/readable-stream.d.ts.map +1 -1
- package/library/stream/readable-stream.js +368 -73
- package/library/stream/stream-core.d.ts +65 -0
- package/library/stream/stream-core.d.ts.map +1 -0
- package/library/stream/stream-core.js +211 -0
- package/library/stream/types.d.ts +203 -3
- package/library/stream/types.d.ts.map +1 -1
- package/library/stream/writable-stream.d.ts +59 -13
- package/library/stream/writable-stream.d.ts.map +1 -1
- package/library/stream/writable-stream.js +647 -197
- package/library/types/index.d.ts +70 -4
- package/library/types/index.d.ts.map +1 -1
- package/library/utils/ack.d.ts +2 -0
- package/library/utils/ack.d.ts.map +1 -0
- package/library/utils/ack.js +44 -0
- package/library/utils/debug.js +1 -1
- package/library/utils/index.d.ts +1 -0
- package/library/utils/index.d.ts.map +1 -1
- package/library/utils/index.js +19 -2
- package/library/utils/origin.d.ts +14 -0
- package/library/utils/origin.d.ts.map +1 -0
- package/library/utils/origin.js +35 -0
- package/package.json +30 -7
- package/react/README.md +16 -0
- package/react/esm/index.js +284 -0
- package/react/library/index.d.ts +1 -1
- package/react/library/index.d.ts.map +1 -1
- package/react/library/index.js +3 -3
- package/react/package.json +24 -2
package/library/core/server.js
CHANGED
|
@@ -27,6 +27,7 @@ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/sli
|
|
|
27
27
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
28
28
|
var _utils = require("../utils");
|
|
29
29
|
var _pathMatch = require("../utils/path-match");
|
|
30
|
+
var _origin = require("../utils/origin");
|
|
30
31
|
var _request = require("./request");
|
|
31
32
|
var _response = require("./response");
|
|
32
33
|
var _message = require("../message");
|
|
@@ -58,12 +59,15 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
58
59
|
*/
|
|
59
60
|
class RequestIframeServerImpl {
|
|
60
61
|
constructor(options) {
|
|
61
|
-
var _options$ackTimeout, _options$versionValid;
|
|
62
|
+
var _options$ackTimeout, _options$versionValid, _options$maxConcurren;
|
|
62
63
|
/** Unique instance ID */
|
|
63
64
|
(0, _defineProperty2.default)(this, "handlers", new Map());
|
|
64
65
|
(0, _defineProperty2.default)(this, "middlewares", []);
|
|
66
|
+
(0, _defineProperty2.default)(this, "inFlightByClientKey", new Map());
|
|
65
67
|
/** Responses waiting for client acknowledgment */
|
|
66
68
|
(0, _defineProperty2.default)(this, "pendingAcks", new Map());
|
|
69
|
+
/** Pending pings waiting for client PONG (server -> client heartbeat) */
|
|
70
|
+
(0, _defineProperty2.default)(this, "pendingPongs", new Map());
|
|
67
71
|
/** Pending requests waiting for client stream_start (streamId present) */
|
|
68
72
|
(0, _defineProperty2.default)(this, "pendingStreamRequests", new Map());
|
|
69
73
|
/** Stream message handlers (streamId -> handler) for client→server streams */
|
|
@@ -76,10 +80,23 @@ class RequestIframeServerImpl {
|
|
|
76
80
|
this.id = (options === null || options === void 0 ? void 0 : options.id) || (0, _utils.generateInstanceId)();
|
|
77
81
|
this.ackTimeout = (_options$ackTimeout = options === null || options === void 0 ? void 0 : options.ackTimeout) !== null && _options$ackTimeout !== void 0 ? _options$ackTimeout : _constants.DefaultTimeout.ACK;
|
|
78
82
|
this.versionValidator = (_options$versionValid = options === null || options === void 0 ? void 0 : options.versionValidator) !== null && _options$versionValid !== void 0 ? _options$versionValid : _utils.isCompatibleVersion;
|
|
83
|
+
this.maxConcurrentRequestsPerClient = (_options$maxConcurren = options === null || options === void 0 ? void 0 : options.maxConcurrentRequestsPerClient) !== null && _options$maxConcurren !== void 0 ? _options$maxConcurren : Number.POSITIVE_INFINITY;
|
|
84
|
+
|
|
85
|
+
// Build origin validator (incoming messages)
|
|
86
|
+
if (options !== null && options !== void 0 && options.validateOrigin) {
|
|
87
|
+
this.originValidator = (origin, data, context) => options.validateOrigin(origin, data, context);
|
|
88
|
+
} else if (options !== null && options !== void 0 && options.allowedOrigins) {
|
|
89
|
+
var matcher = options.allowedOrigins;
|
|
90
|
+
this.originValidator = origin => (0, _origin.matchOrigin)(origin, matcher);
|
|
91
|
+
}
|
|
79
92
|
|
|
80
93
|
// Get or create shared channel and create dispatcher
|
|
81
94
|
var channel = (0, _cache.getOrCreateMessageChannel)(options === null || options === void 0 ? void 0 : options.secretKey);
|
|
82
95
|
this.dispatcher = new _message.MessageDispatcher(channel, _constants.MessageRole.SERVER, this.id);
|
|
96
|
+
this.dispatcher.setAutoAckLimits({
|
|
97
|
+
maxMetaLength: options === null || options === void 0 ? void 0 : options.autoAckMaxMetaLength,
|
|
98
|
+
maxIdLength: options === null || options === void 0 ? void 0 : options.autoAckMaxIdLength
|
|
99
|
+
});
|
|
83
100
|
|
|
84
101
|
// Auto-open by default (unless explicitly set to false)
|
|
85
102
|
if ((options === null || options === void 0 ? void 0 : options.autoOpen) !== false) {
|
|
@@ -87,6 +104,39 @@ class RequestIframeServerImpl {
|
|
|
87
104
|
}
|
|
88
105
|
}
|
|
89
106
|
|
|
107
|
+
/**
|
|
108
|
+
* Check whether an incoming message origin is allowed.
|
|
109
|
+
*/
|
|
110
|
+
isOriginAllowed(data, context) {
|
|
111
|
+
if (!this.originValidator) return true;
|
|
112
|
+
try {
|
|
113
|
+
return this.originValidator(context.origin, data, context);
|
|
114
|
+
} catch (_unused) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Build a per-client key used for concurrency limiting.
|
|
121
|
+
* We intentionally include origin to prevent cross-origin collisions.
|
|
122
|
+
*/
|
|
123
|
+
getClientKey(origin, creatorId) {
|
|
124
|
+
return `${origin}::${creatorId || 'unknown'}`;
|
|
125
|
+
}
|
|
126
|
+
incInFlight(clientKey) {
|
|
127
|
+
var current = this.inFlightByClientKey.get(clientKey) || 0;
|
|
128
|
+
this.inFlightByClientKey.set(clientKey, current + 1);
|
|
129
|
+
}
|
|
130
|
+
decInFlight(clientKey) {
|
|
131
|
+
var current = this.inFlightByClientKey.get(clientKey) || 0;
|
|
132
|
+
var next = current - 1;
|
|
133
|
+
if (next <= 0) {
|
|
134
|
+
this.inFlightByClientKey.delete(clientKey);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
this.inFlightByClientKey.set(clientKey, next);
|
|
138
|
+
}
|
|
139
|
+
|
|
90
140
|
/**
|
|
91
141
|
* Open message processing (register message handlers)
|
|
92
142
|
*/
|
|
@@ -130,22 +180,27 @@ class RequestIframeServerImpl {
|
|
|
130
180
|
// Handle PING messages
|
|
131
181
|
this.unregisterFns.push(this.dispatcher.registerHandler(_constants.MessageType.PING, (data, context) => this.handlePing(data, context), handlerOptions));
|
|
132
182
|
|
|
133
|
-
// Handle
|
|
134
|
-
this.unregisterFns.push(this.dispatcher.registerHandler(_constants.MessageType.
|
|
183
|
+
// Handle PONG messages (server -> client heartbeat)
|
|
184
|
+
this.unregisterFns.push(this.dispatcher.registerHandler(_constants.MessageType.PONG, (data, context) => this.handlePong(data, context), handlerOptions));
|
|
185
|
+
|
|
186
|
+
// Handle ACK messages (for confirming response delivery when requireAck === true)
|
|
187
|
+
this.unregisterFns.push(this.dispatcher.registerHandler(_constants.MessageType.ACK, (data, context) => this.handleAck(data, context), handlerOptions));
|
|
135
188
|
|
|
136
189
|
// Handle stream_* messages (client→server stream)
|
|
137
190
|
this.unregisterFns.push(this.dispatcher.registerHandler(_constants.MessageType.STREAM_START, (data, ctx) => this.handleStreamStart(data, ctx), handlerOptions));
|
|
138
|
-
this.unregisterFns.push(this.dispatcher.registerHandler(type => type.startsWith('stream_') && type !== _constants.MessageType.STREAM_START, (data,
|
|
191
|
+
this.unregisterFns.push(this.dispatcher.registerHandler(type => type.startsWith('stream_') && type !== _constants.MessageType.STREAM_START, (data, context) => this.dispatchStreamMessage(data, context), handlerOptions));
|
|
139
192
|
}
|
|
140
193
|
|
|
141
194
|
/** Handle stream_start from client (stream request with streamId) */
|
|
142
|
-
handleStreamStart(data,
|
|
195
|
+
handleStreamStart(data, context) {
|
|
143
196
|
var _body$chunked;
|
|
144
197
|
if (data.role !== _constants.MessageRole.CLIENT) return;
|
|
198
|
+
if (!this.isOriginAllowed(data, context)) return;
|
|
145
199
|
var body = data.body;
|
|
146
200
|
if (!(body !== null && body !== void 0 && body.streamId)) return;
|
|
147
201
|
var pending = this.pendingStreamRequests.get(data.requestId);
|
|
148
202
|
if (!pending || pending.streamId !== body.streamId) return;
|
|
203
|
+
clearTimeout(pending.timeoutId);
|
|
149
204
|
this.pendingStreamRequests.delete(data.requestId);
|
|
150
205
|
var targetWindow = pending.targetWindow,
|
|
151
206
|
targetOrigin = pending.targetOrigin,
|
|
@@ -165,6 +220,7 @@ class RequestIframeServerImpl {
|
|
|
165
220
|
}
|
|
166
221
|
};
|
|
167
222
|
var streamType = body.type || _constants.StreamType.DATA;
|
|
223
|
+
var streamMode = body.mode;
|
|
168
224
|
var streamChunked = (_body$chunked = body.chunked) !== null && _body$chunked !== void 0 ? _body$chunked : true;
|
|
169
225
|
var streamMetadata = body.metadata;
|
|
170
226
|
var req = new _request.ServerRequestImpl(reqData, reqContext, res, pending.params);
|
|
@@ -175,6 +231,8 @@ class RequestIframeServerImpl {
|
|
|
175
231
|
var fileStream = new _stream.IframeFileReadableStream(body.streamId, data.requestId, streamHandler, {
|
|
176
232
|
chunked: streamChunked,
|
|
177
233
|
metadata: streamMetadata,
|
|
234
|
+
secretKey: data.secretKey,
|
|
235
|
+
mode: streamMode,
|
|
178
236
|
filename: streamMetadata === null || streamMetadata === void 0 ? void 0 : streamMetadata.filename,
|
|
179
237
|
mimeType: streamMetadata === null || streamMetadata === void 0 ? void 0 : streamMetadata.mimeType,
|
|
180
238
|
size: streamMetadata === null || streamMetadata === void 0 ? void 0 : streamMetadata.size
|
|
@@ -216,8 +274,10 @@ class RequestIframeServerImpl {
|
|
|
216
274
|
// Non-file stream
|
|
217
275
|
var readableStream = new _stream.IframeReadableStream(body.streamId, data.requestId, streamHandler, {
|
|
218
276
|
type: streamType,
|
|
277
|
+
mode: streamMode,
|
|
219
278
|
chunked: streamChunked,
|
|
220
|
-
metadata: streamMetadata
|
|
279
|
+
metadata: streamMetadata,
|
|
280
|
+
secretKey: data.secretKey
|
|
221
281
|
});
|
|
222
282
|
req.body = undefined;
|
|
223
283
|
req.stream = readableStream;
|
|
@@ -240,11 +300,17 @@ class RequestIframeServerImpl {
|
|
|
240
300
|
}
|
|
241
301
|
});
|
|
242
302
|
}
|
|
243
|
-
dispatchStreamMessage(data) {
|
|
303
|
+
dispatchStreamMessage(data, context) {
|
|
304
|
+
if (!this.isOriginAllowed(data, context)) return;
|
|
244
305
|
var body = data.body;
|
|
245
306
|
if (!(body !== null && body !== void 0 && body.streamId)) return;
|
|
246
307
|
var handler = this.streamHandlers.get(body.streamId);
|
|
247
308
|
if (handler) {
|
|
309
|
+
// Mark as accepted/handled so MessageDispatcher can auto-ack when requireAck === true
|
|
310
|
+
if (!context.handledBy) {
|
|
311
|
+
context.accepted = true;
|
|
312
|
+
context.handledBy = this.id;
|
|
313
|
+
}
|
|
248
314
|
var messageType = data.type.replace('stream_', '');
|
|
249
315
|
handler(_objectSpread(_objectSpread({}, body), {}, {
|
|
250
316
|
type: messageType
|
|
@@ -276,20 +342,67 @@ class RequestIframeServerImpl {
|
|
|
276
342
|
*/
|
|
277
343
|
handlePing(data, context) {
|
|
278
344
|
if (!context.source) return;
|
|
345
|
+
if (!this.isOriginAllowed(data, context)) return;
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Only allow one server instance to respond.
|
|
349
|
+
* This is important when multiple server instances share the same channel.
|
|
350
|
+
*/
|
|
351
|
+
if (!context.handledBy) {
|
|
352
|
+
// Mark as accepted so MessageDispatcher can auto-send ACK when requireAck === true
|
|
353
|
+
context.accepted = true;
|
|
354
|
+
context.handledBy = this.id;
|
|
355
|
+
}
|
|
279
356
|
|
|
280
357
|
// Window check is handled in MessageDispatcher
|
|
281
358
|
this.dispatcher.sendMessage(context.source, context.origin, _constants.MessageType.PONG, data.requestId);
|
|
282
359
|
}
|
|
360
|
+
handlePong(data, context) {
|
|
361
|
+
if (!this.isOriginAllowed(data, context)) return;
|
|
362
|
+
var pending = this.pendingPongs.get(data.requestId);
|
|
363
|
+
if (pending) {
|
|
364
|
+
if (!context.handledBy) {
|
|
365
|
+
context.accepted = true;
|
|
366
|
+
context.handledBy = this.id;
|
|
367
|
+
}
|
|
368
|
+
clearTimeout(pending.timeoutId);
|
|
369
|
+
this.pendingPongs.delete(data.requestId);
|
|
370
|
+
pending.resolve(true);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
pingClient(targetWindow, targetOrigin, targetClientId) {
|
|
374
|
+
var requestId = (0, _utils.generateRequestId)();
|
|
375
|
+
return new Promise(resolve => {
|
|
376
|
+
var timeoutId = setTimeout(() => {
|
|
377
|
+
this.pendingPongs.delete(requestId);
|
|
378
|
+
resolve(false);
|
|
379
|
+
}, this.ackTimeout);
|
|
380
|
+
this.pendingPongs.set(requestId, {
|
|
381
|
+
resolve,
|
|
382
|
+
timeoutId
|
|
383
|
+
});
|
|
384
|
+
// Window check is handled in MessageDispatcher
|
|
385
|
+
this.dispatcher.sendMessage(targetWindow, targetOrigin, _constants.MessageType.PING, requestId, {
|
|
386
|
+
requireAck: true,
|
|
387
|
+
targetId: targetClientId
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
}
|
|
283
391
|
|
|
284
392
|
/**
|
|
285
|
-
* Handle
|
|
393
|
+
* Handle ACK (receipt confirmation for responses when requireAck === true).
|
|
286
394
|
*/
|
|
287
|
-
|
|
395
|
+
handleAck(data, context) {
|
|
396
|
+
if (!this.isOriginAllowed(data, context)) return;
|
|
288
397
|
var pending = this.pendingAcks.get(data.requestId);
|
|
289
398
|
if (pending) {
|
|
399
|
+
// Best-effort: prevent other server instances from also resolving
|
|
400
|
+
if (!context.handledBy) {
|
|
401
|
+
context.handledBy = this.id;
|
|
402
|
+
}
|
|
290
403
|
clearTimeout(pending.timeoutId);
|
|
291
404
|
this.pendingAcks.delete(data.requestId);
|
|
292
|
-
pending.resolve(true);
|
|
405
|
+
pending.resolve(true, data.ack);
|
|
293
406
|
}
|
|
294
407
|
}
|
|
295
408
|
|
|
@@ -304,6 +417,7 @@ class RequestIframeServerImpl {
|
|
|
304
417
|
}
|
|
305
418
|
handleRequestError(res, targetWindow, targetOrigin, data, err) {
|
|
306
419
|
if (!res._sent) {
|
|
420
|
+
res._markSent();
|
|
307
421
|
/**
|
|
308
422
|
* Use INTERNAL_SERVER_ERROR (500) for handler errors unless a different error status code was explicitly set.
|
|
309
423
|
* If statusCode is still the default OK (200), override it to INTERNAL_SERVER_ERROR.
|
|
@@ -328,6 +442,7 @@ class RequestIframeServerImpl {
|
|
|
328
442
|
if (!res._sent && result !== undefined) {
|
|
329
443
|
res.send(result);
|
|
330
444
|
} else if (!res._sent) {
|
|
445
|
+
res._markSent();
|
|
331
446
|
this.dispatcher.sendMessage(targetWindow, targetOrigin, _constants.MessageType.ERROR, data.requestId, {
|
|
332
447
|
path: data.path,
|
|
333
448
|
error: {
|
|
@@ -392,6 +507,7 @@ class RequestIframeServerImpl {
|
|
|
392
507
|
// If targetId is specified, only process if it matches this server's id
|
|
393
508
|
if (!data.path || data.targetId && data.targetId !== this.id) return;
|
|
394
509
|
if (!context.source) return;
|
|
510
|
+
if (!this.isOriginAllowed(data, context)) return;
|
|
395
511
|
|
|
396
512
|
// If message has already been handled by another server instance, skip processing
|
|
397
513
|
if (context.handledBy) {
|
|
@@ -425,25 +541,51 @@ class RequestIframeServerImpl {
|
|
|
425
541
|
}
|
|
426
542
|
var handlerFn = handlerMatch.handler,
|
|
427
543
|
params = handlerMatch.params;
|
|
544
|
+
var clientKey = this.getClientKey(targetOrigin, data.creatorId);
|
|
545
|
+
if (Number.isFinite(this.maxConcurrentRequestsPerClient)) {
|
|
546
|
+
var inFlight = this.inFlightByClientKey.get(clientKey) || 0;
|
|
547
|
+
if (inFlight >= this.maxConcurrentRequestsPerClient) {
|
|
548
|
+
// Prevent other server instances from also responding
|
|
549
|
+
context.handledBy = this.id;
|
|
550
|
+
this.dispatcher.sendMessage(targetWindow, targetOrigin, _constants.MessageType.ERROR, data.requestId, {
|
|
551
|
+
path: data.path,
|
|
552
|
+
error: {
|
|
553
|
+
message: (0, _constants.formatMessage)(_constants.Messages.TOO_MANY_REQUESTS, this.maxConcurrentRequestsPerClient),
|
|
554
|
+
code: _constants.ErrorCode.TOO_MANY_REQUESTS
|
|
555
|
+
},
|
|
556
|
+
status: _constants.HttpStatus.TOO_MANY_REQUESTS,
|
|
557
|
+
statusText: _constants.HttpStatusText[_constants.HttpStatus.TOO_MANY_REQUESTS],
|
|
558
|
+
requireAck: data.requireAck,
|
|
559
|
+
ack: data.ack,
|
|
560
|
+
targetId: data.creatorId
|
|
561
|
+
});
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
this.incInFlight(clientKey);
|
|
566
|
+
|
|
567
|
+
// Mark as accepted so MessageDispatcher can auto-send ACK (delivery confirmation)
|
|
568
|
+
context.accepted = true;
|
|
428
569
|
|
|
429
570
|
// Mark message as handled by this server instance to prevent other server instances from processing it
|
|
430
571
|
context.handledBy = this.id;
|
|
431
572
|
|
|
432
|
-
// Send ACK immediately via dispatcher
|
|
433
|
-
// Use request's creatorId as targetId to route back to the correct client
|
|
434
|
-
// Window check is handled in MessageDispatcher
|
|
435
|
-
this.dispatcher.sendMessage(targetWindow, targetOrigin, _constants.MessageType.ACK, data.requestId, {
|
|
436
|
-
path: data.path,
|
|
437
|
-
targetId: data.creatorId
|
|
438
|
-
});
|
|
439
|
-
|
|
440
573
|
// Create response object with channel reference
|
|
441
574
|
// Pass request's creatorId as targetId so responses are routed back to the correct client
|
|
442
|
-
var res = new _response.ServerResponseImpl(data.requestId, data.path || '', data.secretKey, targetWindow, targetOrigin, this.dispatcher.getChannel(), this.id, data.creatorId
|
|
575
|
+
var res = new _response.ServerResponseImpl(data.requestId, data.path || '', data.secretKey, targetWindow, targetOrigin, this.dispatcher.getChannel(), this.id, data.creatorId, {
|
|
576
|
+
registerStreamHandler: (streamId, handler) => {
|
|
577
|
+
this.streamHandlers.set(streamId, handler);
|
|
578
|
+
},
|
|
579
|
+
unregisterStreamHandler: streamId => {
|
|
580
|
+
this.streamHandlers.delete(streamId);
|
|
581
|
+
},
|
|
582
|
+
heartbeat: () => this.pingClient(targetWindow, targetOrigin, data.creatorId),
|
|
583
|
+
onSent: () => this.decInFlight(clientKey)
|
|
584
|
+
});
|
|
443
585
|
|
|
444
586
|
// Register callback waiting for client acknowledgment
|
|
445
|
-
this.registerPendingAck(data.requestId, received => {
|
|
446
|
-
res._triggerAck(received);
|
|
587
|
+
this.registerPendingAck(data.requestId, (received, ack) => {
|
|
588
|
+
res._triggerAck(received, ack);
|
|
447
589
|
}, () => {
|
|
448
590
|
res._triggerAck(false);
|
|
449
591
|
});
|
|
@@ -452,10 +594,32 @@ class RequestIframeServerImpl {
|
|
|
452
594
|
// If streamId is present, this is a stream request
|
|
453
595
|
var streamId = data.streamId;
|
|
454
596
|
if (streamId) {
|
|
597
|
+
var streamStartTimeout = this.ackTimeout;
|
|
598
|
+
var timeoutId = setTimeout(() => {
|
|
599
|
+
var pending = this.pendingStreamRequests.get(data.requestId);
|
|
600
|
+
if (!pending) return;
|
|
601
|
+
this.pendingStreamRequests.delete(data.requestId);
|
|
602
|
+
if (!pending.res._sent) {
|
|
603
|
+
pending.res._markSent();
|
|
604
|
+
this.dispatcher.sendMessage(pending.targetWindow, pending.targetOrigin, _constants.MessageType.ERROR, pending.requestId, {
|
|
605
|
+
path: pending.path,
|
|
606
|
+
error: {
|
|
607
|
+
message: (0, _constants.formatMessage)(_constants.Messages.STREAM_START_TIMEOUT, streamStartTimeout),
|
|
608
|
+
code: _constants.ErrorCode.STREAM_START_TIMEOUT
|
|
609
|
+
},
|
|
610
|
+
status: _constants.HttpStatus.REQUEST_TIMEOUT,
|
|
611
|
+
statusText: _constants.HttpStatusText[_constants.HttpStatus.REQUEST_TIMEOUT],
|
|
612
|
+
requireAck: pending.data.requireAck,
|
|
613
|
+
ack: pending.data.ack,
|
|
614
|
+
targetId: pending.data.creatorId
|
|
615
|
+
});
|
|
616
|
+
}
|
|
617
|
+
}, streamStartTimeout);
|
|
455
618
|
this.pendingStreamRequests.set(data.requestId, {
|
|
456
619
|
path: data.path || '',
|
|
457
620
|
requestId: data.requestId,
|
|
458
621
|
streamId,
|
|
622
|
+
timeoutId,
|
|
459
623
|
handlerFn,
|
|
460
624
|
targetWindow,
|
|
461
625
|
targetOrigin,
|
|
@@ -598,10 +762,16 @@ class RequestIframeServerImpl {
|
|
|
598
762
|
// Clean up pending
|
|
599
763
|
this.pendingAcks.forEach(pending => clearTimeout(pending.timeoutId));
|
|
600
764
|
this.pendingAcks.clear();
|
|
765
|
+
this.pendingPongs.forEach(pending => clearTimeout(pending.timeoutId));
|
|
766
|
+
this.pendingPongs.clear();
|
|
767
|
+
this.pendingStreamRequests.forEach(pending => clearTimeout(pending.timeoutId));
|
|
768
|
+
this.pendingStreamRequests.clear();
|
|
769
|
+
this.inFlightByClientKey.clear();
|
|
601
770
|
|
|
602
771
|
// Clean up handlers
|
|
603
772
|
this.handlers.clear();
|
|
604
773
|
this.middlewares.length = 0;
|
|
774
|
+
this.streamHandlers.clear();
|
|
605
775
|
|
|
606
776
|
// Destroy dispatcher and release channel reference
|
|
607
777
|
this.dispatcher.destroy();
|
|
@@ -7,6 +7,12 @@ export interface MessageContext {
|
|
|
7
7
|
source?: Window;
|
|
8
8
|
/** Origin of the message */
|
|
9
9
|
origin: string;
|
|
10
|
+
/**
|
|
11
|
+
* Whether the receiver accepted this message for processing.
|
|
12
|
+
* - Used by MessageDispatcher to decide whether it should send an ACK automatically.
|
|
13
|
+
* - Should be set by high-level handlers once they确定会处理该消息(例如:路由匹配到 handler,或找到 pending request)。
|
|
14
|
+
*/
|
|
15
|
+
accepted?: boolean;
|
|
10
16
|
/** ID of the instance that handled this message (if handled) */
|
|
11
17
|
handledBy?: string;
|
|
12
18
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../src/message/channel.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EAChB,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../src/message/channel.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EAChB,MAAM,UAAU,CAAC;AAIlB;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,kDAAkD;IAClD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4BAA4B;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;AAEvF;;GAEG;AACH,eAAO,MAAM,WAAW;IACtB,+BAA+B;;CAEvB,CAAC;AAEX;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,OAAO,WAAW,CAAC,MAAM,OAAO,WAAW,CAAC,CAAC;AAEvE;;;;;;;;;;;;GAYG;AACH,qBAAa,cAAc;IACzB,8CAA8C;IAC9C,SAAgB,IAAI,EAAE,WAAW,CAAC;IAElC,uCAAuC;IACvC,SAAgB,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnC,8DAA8D;IAC9D,OAAO,CAAC,SAAS,CAAmC;IAEpD,gDAAgD;IAChD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAgC;IAE/D,6CAA6C;IAC7C,OAAO,CAAC,QAAQ,CAAK;gBAEF,SAAS,CAAC,EAAE,MAAM,EAAE,IAAI,GAAE,WAAsC;IAOnF;;;OAGG;IACI,WAAW,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI;IAInD;;OAEG;IACI,cAAc,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI;IAItD;;OAEG;IACI,MAAM,IAAI,IAAI;IAIrB;;;OAGG;IACI,OAAO,IAAI,MAAM;IAIxB;;OAEG;IACI,WAAW,IAAI,MAAM;IAI5B;;;OAGG;IACH,OAAO,CAAC,cAAc;IAOtB;;OAEG;IACH,OAAO,CAAC,SAAS;IA4BjB;;;;;OAKG;IACI,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,EAAE,YAAY,GAAE,MAA2B,GAAG,OAAO;IAQzG;;;;;;;OAOG;IACI,WAAW,CAChB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC,EAC7B,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,eAAe,EAAE,mBAAmB,GAAG,MAAM,GAAG,WAAW,GAAG,WAAW,GAAG,MAAM,CAAC,CAAC,GACvG,OAAO;IAQV;;OAEG;IACI,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAIvC;;OAEG;IACI,OAAO,IAAI,IAAI;CAIvB"}
|
|
@@ -13,6 +13,7 @@ require("core-js/modules/web.dom-collections.for-each.js");
|
|
|
13
13
|
require("core-js/modules/web.dom-collections.iterator.js");
|
|
14
14
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
15
15
|
var _utils = require("../utils");
|
|
16
|
+
var _constants = require("../constants");
|
|
16
17
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
17
18
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
18
19
|
/**
|
|
@@ -147,7 +148,7 @@ class MessageChannel {
|
|
|
147
148
|
* @param message message data (already formatted as PostMessageData)
|
|
148
149
|
* @param targetOrigin target origin (defaults to '*')
|
|
149
150
|
*/
|
|
150
|
-
send(target, message, targetOrigin =
|
|
151
|
+
send(target, message, targetOrigin = _constants.OriginConstant.ANY) {
|
|
151
152
|
if (!(0, _utils.isWindowAvailable)(target)) {
|
|
152
153
|
return false;
|
|
153
154
|
}
|
|
@@ -52,6 +52,15 @@ export declare class MessageDispatcher {
|
|
|
52
52
|
private readonly instanceId?;
|
|
53
53
|
/** Underlying message channel */
|
|
54
54
|
private readonly channel;
|
|
55
|
+
private autoAckMaxMetaLength;
|
|
56
|
+
private autoAckMaxIdLength;
|
|
57
|
+
/**
|
|
58
|
+
* Fallback target for sending auto-ack messages when MessageEvent.source is unavailable.
|
|
59
|
+
* - In real browser postMessage events, `source` should normally exist.
|
|
60
|
+
* - This is primarily used for unit tests or edge environments that synthesize MessageEvent without `source`.
|
|
61
|
+
*/
|
|
62
|
+
private fallbackTargetWindow?;
|
|
63
|
+
private fallbackTargetOrigin;
|
|
55
64
|
/** Message handler list */
|
|
56
65
|
private readonly handlers;
|
|
57
66
|
/** Message receiver callback (bound to this) */
|
|
@@ -59,6 +68,10 @@ export declare class MessageDispatcher {
|
|
|
59
68
|
/** Reference count (for determining if can be destroyed when cached) */
|
|
60
69
|
private refCount;
|
|
61
70
|
constructor(channel: MessageChannel, role: MessageRoleValue, instanceId?: string);
|
|
71
|
+
/**
|
|
72
|
+
* Set fallback target for outgoing auto-ack messages.
|
|
73
|
+
*/
|
|
74
|
+
setFallbackTarget(targetWindow: Window, targetOrigin?: string): void;
|
|
62
75
|
/**
|
|
63
76
|
* Increment reference count
|
|
64
77
|
*/
|
|
@@ -88,6 +101,25 @@ export declare class MessageDispatcher {
|
|
|
88
101
|
* Dispatch message to matching handlers
|
|
89
102
|
*/
|
|
90
103
|
private dispatchMessage;
|
|
104
|
+
/**
|
|
105
|
+
* Auto-ack logic (generalized requireAck workflow)
|
|
106
|
+
*
|
|
107
|
+
* Notes:
|
|
108
|
+
* - This is intentionally conservative: it only runs after the message is marked as handled
|
|
109
|
+
* (via `context.handledBy`) to avoid acknowledging messages that no consumer will process.
|
|
110
|
+
* - For backward compatibility:
|
|
111
|
+
* - REQUEST defaults to requiring ACK unless `requireAck === false`
|
|
112
|
+
* - Other message types only ack when `requireAck === true`
|
|
113
|
+
*/
|
|
114
|
+
private tryAutoAck;
|
|
115
|
+
/**
|
|
116
|
+
* Limit echoed ack payload size for auto-ack replies.
|
|
117
|
+
*
|
|
118
|
+
* We only echo a fixed shape `{ id, meta?: string }`:
|
|
119
|
+
* - If meta is too long, we drop meta and only echo `{ id }`.
|
|
120
|
+
* - If id is missing or too long, omit `ack` field.
|
|
121
|
+
*/
|
|
122
|
+
private getAutoAckEchoPayload;
|
|
91
123
|
/**
|
|
92
124
|
* Check if message type matches
|
|
93
125
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/message/dispatcher.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/message/dispatcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -22,6 +22,7 @@ require("core-js/modules/es.regexp.to-string.js");
|
|
|
22
22
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
23
23
|
var _constants = require("../constants");
|
|
24
24
|
var _utils = require("../utils");
|
|
25
|
+
var _ack = require("../utils/ack");
|
|
25
26
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
26
27
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
27
28
|
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
|
|
@@ -71,6 +72,9 @@ class MessageDispatcher {
|
|
|
71
72
|
/** Role of this dispatcher ('client' or 'server') */
|
|
72
73
|
/** Instance ID of the client/server that owns this dispatcher */
|
|
73
74
|
/** Underlying message channel */
|
|
75
|
+
(0, _defineProperty2.default)(this, "autoAckMaxMetaLength", _constants.AutoAckConstant.MAX_META_LENGTH);
|
|
76
|
+
(0, _defineProperty2.default)(this, "autoAckMaxIdLength", _constants.AutoAckConstant.MAX_ID_LENGTH);
|
|
77
|
+
(0, _defineProperty2.default)(this, "fallbackTargetOrigin", _constants.OriginConstant.ANY);
|
|
74
78
|
/** Message handler list */
|
|
75
79
|
(0, _defineProperty2.default)(this, "handlers", []);
|
|
76
80
|
/** Reference count (for determining if can be destroyed when cached) */
|
|
@@ -90,6 +94,29 @@ class MessageDispatcher {
|
|
|
90
94
|
this.channel.addReceiver(this.boundReceiver);
|
|
91
95
|
}
|
|
92
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Set fallback target for outgoing auto-ack messages.
|
|
99
|
+
*/
|
|
100
|
+
setFallbackTarget(targetWindow, targetOrigin = _constants.OriginConstant.ANY) {
|
|
101
|
+
this.fallbackTargetWindow = targetWindow;
|
|
102
|
+
this.fallbackTargetOrigin = targetOrigin;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Configure auto-ack echo limits (advanced/internal).
|
|
107
|
+
*
|
|
108
|
+
* @internal
|
|
109
|
+
*/
|
|
110
|
+
setAutoAckLimits(limits) {
|
|
111
|
+
if (!limits || typeof limits !== 'object') return;
|
|
112
|
+
if (typeof limits.maxMetaLength === 'number' && Number.isFinite(limits.maxMetaLength) && limits.maxMetaLength >= 0) {
|
|
113
|
+
this.autoAckMaxMetaLength = limits.maxMetaLength;
|
|
114
|
+
}
|
|
115
|
+
if (typeof limits.maxIdLength === 'number' && Number.isFinite(limits.maxIdLength) && limits.maxIdLength >= 0) {
|
|
116
|
+
this.autoAckMaxIdLength = limits.maxIdLength;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
93
120
|
// ==================== Reference Counting ====================
|
|
94
121
|
|
|
95
122
|
/**
|
|
@@ -177,6 +204,43 @@ class MessageDispatcher {
|
|
|
177
204
|
}
|
|
178
205
|
var type = data.type;
|
|
179
206
|
var version = (0, _utils.getProtocolVersion)(data);
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Auto-ack state for this incoming message.
|
|
210
|
+
* - We intentionally couple this to `context.handledBy` as the "accepted/handled" signal.
|
|
211
|
+
* - For some message types we only ack if they are truly handled (e.g. response requireAck),
|
|
212
|
+
* so we avoid incorrectly acknowledging messages when there is no pending consumer.
|
|
213
|
+
*
|
|
214
|
+
* Implementation note:
|
|
215
|
+
* We avoid using Proxy for compatibility and instead hook the `handledBy` property
|
|
216
|
+
* with a setter so handlers can trigger the ack immediately when they "accept" a message.
|
|
217
|
+
*/
|
|
218
|
+
var autoAckState = {
|
|
219
|
+
sent: false
|
|
220
|
+
};
|
|
221
|
+
var originalHandledBy = context.handledBy;
|
|
222
|
+
var handledByValue = originalHandledBy;
|
|
223
|
+
try {
|
|
224
|
+
Object.defineProperty(context, 'handledBy', {
|
|
225
|
+
configurable: true,
|
|
226
|
+
enumerable: true,
|
|
227
|
+
get() {
|
|
228
|
+
return handledByValue;
|
|
229
|
+
},
|
|
230
|
+
set: value => {
|
|
231
|
+
handledByValue = value;
|
|
232
|
+
if (value && !autoAckState.sent) {
|
|
233
|
+
autoAckState.sent = true;
|
|
234
|
+
this.tryAutoAck(data, context);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
} catch (_unused) {
|
|
239
|
+
/**
|
|
240
|
+
* In very rare cases `defineProperty` may fail (frozen object).
|
|
241
|
+
* We still proceed without auto-ack; handlers will continue to work as before.
|
|
242
|
+
*/
|
|
243
|
+
}
|
|
180
244
|
var _iterator = _createForOfIteratorHelper(this.handlers),
|
|
181
245
|
_step;
|
|
182
246
|
try {
|
|
@@ -214,6 +278,72 @@ class MessageDispatcher {
|
|
|
214
278
|
}
|
|
215
279
|
}
|
|
216
280
|
|
|
281
|
+
/**
|
|
282
|
+
* Auto-ack logic (generalized requireAck workflow)
|
|
283
|
+
*
|
|
284
|
+
* Notes:
|
|
285
|
+
* - This is intentionally conservative: it only runs after the message is marked as handled
|
|
286
|
+
* (via `context.handledBy`) to avoid acknowledging messages that no consumer will process.
|
|
287
|
+
* - For backward compatibility:
|
|
288
|
+
* - REQUEST defaults to requiring ACK unless `requireAck === false`
|
|
289
|
+
* - Other message types only ack when `requireAck === true`
|
|
290
|
+
*/
|
|
291
|
+
tryAutoAck(data, context) {
|
|
292
|
+
var _context$source;
|
|
293
|
+
var targetWindow = (_context$source = context.source) !== null && _context$source !== void 0 ? _context$source : this.fallbackTargetWindow;
|
|
294
|
+
if (!targetWindow) return;
|
|
295
|
+
var targetOrigin = context.source ? context.origin : this.fallbackTargetOrigin || context.origin;
|
|
296
|
+
var type = data.type;
|
|
297
|
+
|
|
298
|
+
// Don't auto-ack ack messages (avoid loops)
|
|
299
|
+
if (type === _constants.MessageType.ACK) return;
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* ACK-only requireAck workflow (no compatibility guarantees):
|
|
303
|
+
* - For any message with `requireAck === true`, once the message is accepted/handled
|
|
304
|
+
* (via `context.accepted === true` + `context.handledBy` setter hook),
|
|
305
|
+
* we reply with `ack`.
|
|
306
|
+
*
|
|
307
|
+
* This unifies:
|
|
308
|
+
* - request delivery confirmation
|
|
309
|
+
* - response receipt confirmation
|
|
310
|
+
* - stream frame delivery confirmation (e.g. stream_data per-frame ack)
|
|
311
|
+
*/
|
|
312
|
+
if (data.requireAck === true && context.accepted === true) {
|
|
313
|
+
var ack = this.getAutoAckEchoPayload(data.ack);
|
|
314
|
+
this.sendMessage(targetWindow, targetOrigin, _constants.MessageType.ACK, data.requestId, {
|
|
315
|
+
path: data.path,
|
|
316
|
+
targetId: data.creatorId,
|
|
317
|
+
ack
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Limit echoed ack payload size for auto-ack replies.
|
|
324
|
+
*
|
|
325
|
+
* We only echo a fixed shape `{ id, meta?: string }`:
|
|
326
|
+
* - If meta is too long, we drop meta and only echo `{ id }`.
|
|
327
|
+
* - If id is missing or too long, omit `ack` field.
|
|
328
|
+
*/
|
|
329
|
+
getAutoAckEchoPayload(rawAck) {
|
|
330
|
+
if (rawAck === undefined) return undefined;
|
|
331
|
+
var id = (0, _ack.getAckId)(rawAck);
|
|
332
|
+
if (id === undefined) return undefined;
|
|
333
|
+
if (typeof id === 'string' && id.length > this.autoAckMaxIdLength) return undefined;
|
|
334
|
+
var meta = (0, _ack.getAckMeta)(rawAck);
|
|
335
|
+
if (meta === undefined) return {
|
|
336
|
+
id
|
|
337
|
+
};
|
|
338
|
+
if (meta.length > this.autoAckMaxMetaLength) return {
|
|
339
|
+
id
|
|
340
|
+
};
|
|
341
|
+
return {
|
|
342
|
+
id,
|
|
343
|
+
meta
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
217
347
|
/**
|
|
218
348
|
* Check if message type matches
|
|
219
349
|
*/
|
|
@@ -238,7 +368,7 @@ class MessageDispatcher {
|
|
|
238
368
|
* @param message message data (already formatted as PostMessageData)
|
|
239
369
|
* @param targetOrigin target origin (defaults to '*')
|
|
240
370
|
*/
|
|
241
|
-
send(target, message, targetOrigin =
|
|
371
|
+
send(target, message, targetOrigin = _constants.OriginConstant.ANY) {
|
|
242
372
|
// Automatically set role and creatorId if not already set (for backward compatibility)
|
|
243
373
|
if (message.role === undefined) {
|
|
244
374
|
message.role = this.role;
|
|
@@ -32,6 +32,10 @@ export declare class IframeFileReadableStream extends IframeReadableStream<Uint8
|
|
|
32
32
|
* Override merge method to merge all Uint8Array chunks
|
|
33
33
|
*/
|
|
34
34
|
protected mergeChunks(): Uint8Array;
|
|
35
|
+
/**
|
|
36
|
+
* Read all data as a merged Uint8Array (file stream default behavior)
|
|
37
|
+
*/
|
|
38
|
+
read(): Promise<Uint8Array>;
|
|
35
39
|
/**
|
|
36
40
|
* Read as Blob
|
|
37
41
|
*/
|