request-iframe 0.1.0 → 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 +174 -22
- package/README.md +166 -22
- package/esm/api/client.js +6 -5
- package/esm/api/server.js +3 -1
- package/esm/constants/index.js +38 -6
- package/esm/constants/messages.js +3 -1
- package/esm/core/client-server.js +8 -43
- package/esm/core/client.js +7 -7
- package/esm/core/response.js +16 -8
- package/esm/core/server.js +18 -9
- package/esm/message/channel.js +2 -1
- package/esm/message/dispatcher.js +63 -25
- package/esm/stream/readable-stream.js +57 -18
- package/esm/stream/stream-core.js +115 -2
- package/esm/stream/writable-stream.js +281 -27
- package/esm/utils/ack.js +36 -0
- package/esm/utils/debug.js +1 -1
- package/esm/utils/index.js +5 -3
- package/esm/utils/origin.js +3 -1
- package/library/api/client.d.ts.map +1 -1
- package/library/api/client.js +5 -4
- package/library/api/server.d.ts.map +1 -1
- package/library/api/server.js +3 -1
- package/library/constants/index.d.ts +36 -5
- package/library/constants/index.d.ts.map +1 -1
- package/library/constants/index.js +41 -8
- package/library/constants/messages.d.ts +3 -1
- package/library/constants/messages.d.ts.map +1 -1
- package/library/constants/messages.js +3 -1
- package/library/core/client-server.d.ts +4 -13
- package/library/core/client-server.d.ts.map +1 -1
- package/library/core/client-server.js +7 -42
- package/library/core/client.d.ts.map +1 -1
- package/library/core/client.js +6 -6
- package/library/core/response.d.ts +2 -2
- package/library/core/response.d.ts.map +1 -1
- package/library/core/response.js +15 -7
- package/library/core/server.d.ts +6 -2
- package/library/core/server.d.ts.map +1 -1
- package/library/core/server.js +18 -9
- package/library/message/channel.d.ts +1 -1
- package/library/message/channel.d.ts.map +1 -1
- package/library/message/channel.js +2 -1
- package/library/message/dispatcher.d.ts +10 -0
- package/library/message/dispatcher.d.ts.map +1 -1
- package/library/message/dispatcher.js +62 -24
- package/library/stream/readable-stream.d.ts.map +1 -1
- package/library/stream/readable-stream.js +56 -17
- package/library/stream/stream-core.d.ts +22 -1
- package/library/stream/stream-core.d.ts.map +1 -1
- package/library/stream/stream-core.js +114 -1
- package/library/stream/types.d.ts +115 -2
- package/library/stream/types.d.ts.map +1 -1
- package/library/stream/writable-stream.d.ts +20 -2
- package/library/stream/writable-stream.d.ts.map +1 -1
- package/library/stream/writable-stream.js +277 -23
- package/library/types/index.d.ts +3 -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.map +1 -1
- package/library/utils/index.js +4 -3
- package/library/utils/origin.d.ts.map +1 -1
- package/library/utils/origin.js +2 -1
- package/package.json +1 -1
- package/esm/utils/ack-meta.js +0 -53
- package/library/utils/ack-meta.d.ts +0 -2
- package/library/utils/ack-meta.d.ts.map +0 -1
- package/library/utils/ack-meta.js +0 -59
package/esm/api/server.js
CHANGED
|
@@ -32,7 +32,9 @@ export function requestIframeServer(options) {
|
|
|
32
32
|
autoOpen: options === null || options === void 0 ? void 0 : options.autoOpen,
|
|
33
33
|
allowedOrigins: options === null || options === void 0 ? void 0 : options.allowedOrigins,
|
|
34
34
|
validateOrigin: options === null || options === void 0 ? void 0 : options.validateOrigin,
|
|
35
|
-
maxConcurrentRequestsPerClient: options === null || options === void 0 ? void 0 : options.maxConcurrentRequestsPerClient
|
|
35
|
+
maxConcurrentRequestsPerClient: options === null || options === void 0 ? void 0 : options.maxConcurrentRequestsPerClient,
|
|
36
|
+
autoAckMaxMetaLength: options === null || options === void 0 ? void 0 : options.autoAckMaxMetaLength,
|
|
37
|
+
autoAckMaxIdLength: options === null || options === void 0 ? void 0 : options.autoAckMaxIdLength
|
|
36
38
|
});
|
|
37
39
|
|
|
38
40
|
// If trace mode is enabled, register debug listeners
|
package/esm/constants/index.js
CHANGED
|
@@ -14,6 +14,24 @@ export var ProtocolVersion = {
|
|
|
14
14
|
MIN_SUPPORTED: 1
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Special origin values
|
|
19
|
+
*/
|
|
20
|
+
export var OriginConstant = {
|
|
21
|
+
/** Wildcard origin (postMessage targetOrigin = '*') */
|
|
22
|
+
ANY: '*'
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Auto-ack (ACK-only) limits.
|
|
27
|
+
*/
|
|
28
|
+
export var AutoAckConstant = {
|
|
29
|
+
/** Max length of ack.id to be echoed back in auto-ack */
|
|
30
|
+
MAX_ID_LENGTH: 1024,
|
|
31
|
+
/** Max length of ack.meta to be echoed back in auto-ack */
|
|
32
|
+
MAX_META_LENGTH: 5120
|
|
33
|
+
};
|
|
34
|
+
|
|
17
35
|
/**
|
|
18
36
|
* Protocol version type
|
|
19
37
|
*/
|
|
@@ -113,8 +131,6 @@ export var MessageType = {
|
|
|
113
131
|
RESPONSE: 'response',
|
|
114
132
|
/** Error message */
|
|
115
133
|
ERROR: 'error',
|
|
116
|
-
/** Client confirms response received */
|
|
117
|
-
RECEIVED: 'received',
|
|
118
134
|
/** Ping message (for connection detection) */
|
|
119
135
|
PING: 'ping',
|
|
120
136
|
/** Pong message (for connection detection) */
|
|
@@ -146,7 +162,7 @@ export var MessageRole = {
|
|
|
146
162
|
export var DefaultTimeout = {
|
|
147
163
|
/**
|
|
148
164
|
* ACK confirmation timeout: 1000ms (1s)
|
|
149
|
-
* Used for
|
|
165
|
+
* Used for requireAck confirmation (ACK-only).
|
|
150
166
|
* Increased from 500ms to accommodate slower environments or busy browsers where postMessage
|
|
151
167
|
* serialization/deserialization may take longer.
|
|
152
168
|
*/
|
|
@@ -218,9 +234,7 @@ export var StreamInternalMessageType = {
|
|
|
218
234
|
/** Cancel message */
|
|
219
235
|
CANCEL: 'cancel',
|
|
220
236
|
/** Pull message */
|
|
221
|
-
PULL: 'pull'
|
|
222
|
-
/** Ack message */
|
|
223
|
-
ACK: 'ack'
|
|
237
|
+
PULL: 'pull'
|
|
224
238
|
};
|
|
225
239
|
|
|
226
240
|
/**
|
|
@@ -251,6 +265,24 @@ export var StreamState = {
|
|
|
251
265
|
* Stream state value type
|
|
252
266
|
*/
|
|
253
267
|
|
|
268
|
+
/**
|
|
269
|
+
* Stream event name constants (for stream.on / observability)
|
|
270
|
+
*/
|
|
271
|
+
export var StreamEvent = {
|
|
272
|
+
START: 'start',
|
|
273
|
+
DATA: 'data',
|
|
274
|
+
READ: 'read',
|
|
275
|
+
WRITE: 'write',
|
|
276
|
+
SEND: 'send',
|
|
277
|
+
PULL: 'pull',
|
|
278
|
+
ACK: 'ack',
|
|
279
|
+
END: 'end',
|
|
280
|
+
CANCEL: 'cancel',
|
|
281
|
+
ERROR: 'error',
|
|
282
|
+
TIMEOUT: 'timeout',
|
|
283
|
+
EXPIRED: 'expired',
|
|
284
|
+
STATE: 'state'
|
|
285
|
+
};
|
|
254
286
|
/**
|
|
255
287
|
* Message constants (for multi-language support)
|
|
256
288
|
*/
|
|
@@ -57,6 +57,8 @@ var defaultMessages = {
|
|
|
57
57
|
STREAM_ENDED: 'Stream has ended',
|
|
58
58
|
STREAM_READ_ERROR: 'Failed to read stream data',
|
|
59
59
|
STREAM_WRITE_ONLY_IN_PUSH_MODE: 'Stream write() is only available when mode is "push"',
|
|
60
|
+
STREAM_PENDING_QUEUE_OVERFLOW: 'Stream pending queue overflow (limit: {0})',
|
|
61
|
+
STREAM_PENDING_BYTES_OVERFLOW: 'Stream pending bytes overflow (limit: {0})',
|
|
60
62
|
/** Debug messages - Client */
|
|
61
63
|
DEBUG_CLIENT_REQUEST_START: '📤 [Client] Request Start',
|
|
62
64
|
DEBUG_CLIENT_REQUEST_SUCCESS: '✅ [Client] Request Success',
|
|
@@ -73,7 +75,7 @@ var defaultMessages = {
|
|
|
73
75
|
DEBUG_CLIENT_REQUEST_TIMEOUT: '⏱️ [Client] Request Timeout',
|
|
74
76
|
DEBUG_CLIENT_SENDING_REQUEST: '📤 [Client] Sending Request',
|
|
75
77
|
DEBUG_CLIENT_SENDING_PING: '📤 [Client] Sending Ping',
|
|
76
|
-
DEBUG_CLIENT_SENDING_RECEIVED_ACK: '📤 [Client] Sending
|
|
78
|
+
DEBUG_CLIENT_SENDING_RECEIVED_ACK: '📤 [Client] Sending ACK',
|
|
77
79
|
/** Debug messages - Server */
|
|
78
80
|
DEBUG_SERVER_RECEIVED_REQUEST: '📥 [Server] Received Request',
|
|
79
81
|
DEBUG_SERVER_SETTING_STATUS_CODE: '📝 [Server] Setting Status Code',
|
|
@@ -8,16 +8,12 @@ import "core-js/modules/web.dom-collections.iterator.js";
|
|
|
8
8
|
import { MessageDispatcher } from '../message';
|
|
9
9
|
import { getOrCreateMessageChannel, releaseMessageChannel } from '../utils/cache';
|
|
10
10
|
import { isCompatibleVersion } from '../utils';
|
|
11
|
-
import { MessageType,
|
|
11
|
+
import { MessageType, ProtocolVersion, Messages, formatMessage, MessageRole, OriginConstant } from '../constants';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Stream message handler callback
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
/**
|
|
18
|
-
* Pending acknowledgment response
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
17
|
/**
|
|
22
18
|
* Pending request awaiting response
|
|
23
19
|
*/
|
|
@@ -33,9 +29,7 @@ import { MessageType, DefaultTimeout, ProtocolVersion, Messages, formatMessage,
|
|
|
33
29
|
*/
|
|
34
30
|
export class RequestIframeClientServer {
|
|
35
31
|
constructor(options, instanceId) {
|
|
36
|
-
var _options$
|
|
37
|
-
/** Pending responses awaiting client acknowledgment */
|
|
38
|
-
_defineProperty(this, "pendingAcks", new Map());
|
|
32
|
+
var _options$versionValid;
|
|
39
33
|
/** Pending requests awaiting response */
|
|
40
34
|
_defineProperty(this, "pendingRequests", new Map());
|
|
41
35
|
/**
|
|
@@ -46,12 +40,15 @@ export class RequestIframeClientServer {
|
|
|
46
40
|
_defineProperty(this, "unregisterFns", []);
|
|
47
41
|
/** Whether opened */
|
|
48
42
|
_defineProperty(this, "_isOpen", false);
|
|
49
|
-
this.ackTimeout = (_options$ackTimeout = options === null || options === void 0 ? void 0 : options.ackTimeout) !== null && _options$ackTimeout !== void 0 ? _options$ackTimeout : DefaultTimeout.ACK;
|
|
50
43
|
this.versionValidator = (_options$versionValid = options === null || options === void 0 ? void 0 : options.versionValidator) !== null && _options$versionValid !== void 0 ? _options$versionValid : isCompatibleVersion;
|
|
51
44
|
|
|
52
45
|
// Get or create shared channel and create dispatcher
|
|
53
46
|
var channel = getOrCreateMessageChannel(options === null || options === void 0 ? void 0 : options.secretKey);
|
|
54
47
|
this.dispatcher = new MessageDispatcher(channel, MessageRole.CLIENT, instanceId);
|
|
48
|
+
this.dispatcher.setAutoAckLimits({
|
|
49
|
+
maxMetaLength: options === null || options === void 0 ? void 0 : options.autoAckMaxMetaLength,
|
|
50
|
+
maxIdLength: options === null || options === void 0 ? void 0 : options.autoAckMaxIdLength
|
|
51
|
+
});
|
|
55
52
|
|
|
56
53
|
// Auto-open by default (unless explicitly set to false)
|
|
57
54
|
if ((options === null || options === void 0 ? void 0 : options.autoOpen) !== false) {
|
|
@@ -111,9 +108,6 @@ export class RequestIframeClientServer {
|
|
|
111
108
|
// Handle ERROR messages
|
|
112
109
|
this.unregisterFns.push(this.dispatcher.registerHandler(MessageType.ERROR, boundHandleClientResponse, handlerOptions));
|
|
113
110
|
|
|
114
|
-
// Handle RECEIVED messages
|
|
115
|
-
this.unregisterFns.push(this.dispatcher.registerHandler(MessageType.RECEIVED, this.handleReceived.bind(this), handlerOptions));
|
|
116
|
-
|
|
117
111
|
// Handle PONG messages
|
|
118
112
|
this.unregisterFns.push(this.dispatcher.registerHandler(MessageType.PONG, this.handlePong.bind(this), handlerOptions));
|
|
119
113
|
|
|
@@ -188,7 +182,7 @@ export class RequestIframeClientServer {
|
|
|
188
182
|
// If validator throws, treat as disallowed
|
|
189
183
|
return;
|
|
190
184
|
}
|
|
191
|
-
} else if (pending.origin && pending.origin !==
|
|
185
|
+
} else if (pending.origin && pending.origin !== OriginConstant.ANY && context.origin !== pending.origin) {
|
|
192
186
|
return;
|
|
193
187
|
}
|
|
194
188
|
|
|
@@ -213,18 +207,6 @@ export class RequestIframeClientServer {
|
|
|
213
207
|
pending.resolve(data);
|
|
214
208
|
}
|
|
215
209
|
|
|
216
|
-
/**
|
|
217
|
-
* Handle received acknowledgment
|
|
218
|
-
*/
|
|
219
|
-
handleReceived(data) {
|
|
220
|
-
var pending = this.pendingAcks.get(data.requestId);
|
|
221
|
-
if (pending) {
|
|
222
|
-
clearTimeout(pending.timeoutId);
|
|
223
|
-
this.pendingAcks.delete(data.requestId);
|
|
224
|
-
pending.resolve(true);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
210
|
/**
|
|
229
211
|
* Handle pong
|
|
230
212
|
*/
|
|
@@ -239,7 +221,7 @@ export class RequestIframeClientServer {
|
|
|
239
221
|
} catch (_unused2) {
|
|
240
222
|
return;
|
|
241
223
|
}
|
|
242
|
-
} else if (pending.origin && pending.origin !==
|
|
224
|
+
} else if (pending.origin && pending.origin !== OriginConstant.ANY && context.origin !== pending.origin) {
|
|
243
225
|
return;
|
|
244
226
|
}
|
|
245
227
|
if (!context.handledBy) {
|
|
@@ -268,21 +250,6 @@ export class RequestIframeClientServer {
|
|
|
268
250
|
return this.dispatcher;
|
|
269
251
|
}
|
|
270
252
|
|
|
271
|
-
/**
|
|
272
|
-
* Register pending acknowledgment response
|
|
273
|
-
*/
|
|
274
|
-
_registerPendingAck(requestId, resolve, reject) {
|
|
275
|
-
var timeoutId = setTimeout(() => {
|
|
276
|
-
this.pendingAcks.delete(requestId);
|
|
277
|
-
resolve(false);
|
|
278
|
-
}, this.ackTimeout);
|
|
279
|
-
this.pendingAcks.set(requestId, {
|
|
280
|
-
resolve,
|
|
281
|
-
reject,
|
|
282
|
-
timeoutId
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
|
|
286
253
|
/**
|
|
287
254
|
* Register pending request awaiting response
|
|
288
255
|
*/
|
|
@@ -318,8 +285,6 @@ export class RequestIframeClientServer {
|
|
|
318
285
|
|
|
319
286
|
// Clear pending
|
|
320
287
|
this.pendingRequests.clear();
|
|
321
|
-
this.pendingAcks.forEach(pending => clearTimeout(pending.timeoutId));
|
|
322
|
-
this.pendingAcks.clear();
|
|
323
288
|
this.warnedMissingPendingWhenClosed.clear();
|
|
324
289
|
|
|
325
290
|
// Destroy dispatcher and release channel reference
|
package/esm/core/client.js
CHANGED
|
@@ -24,11 +24,11 @@ import "core-js/modules/es.string.starts-with.js";
|
|
|
24
24
|
import "core-js/modules/web.dom-collections.for-each.js";
|
|
25
25
|
import "core-js/modules/web.dom-collections.iterator.js";
|
|
26
26
|
import { RequestIframeError } from '../utils';
|
|
27
|
-
import {
|
|
27
|
+
import { isAckMatch } from '../utils/ack';
|
|
28
28
|
import { detectContentType, blobToBase64, isWindowAvailable, matchOrigin } from '../utils';
|
|
29
29
|
import { generateRequestId, generateInstanceId, CookieStore } from '../utils';
|
|
30
30
|
import { RequestInterceptorManager, ResponseInterceptorManager, runRequestInterceptors, runResponseInterceptors } from '../interceptors';
|
|
31
|
-
import { DefaultTimeout, ErrorCode, MessageType, HttpStatus, HttpStatusText, HttpHeader, Messages, formatMessage, StreamType as StreamTypeConstant } from '../constants';
|
|
31
|
+
import { DefaultTimeout, ErrorCode, MessageType, OriginConstant, HttpStatus, HttpStatusText, HttpHeader, Messages, formatMessage, StreamType as StreamTypeConstant } from '../constants';
|
|
32
32
|
import { IframeReadableStream, IframeFileReadableStream, isIframeWritableStream } from '../stream';
|
|
33
33
|
|
|
34
34
|
/**
|
|
@@ -133,7 +133,7 @@ export class RequestIframeClientImpl {
|
|
|
133
133
|
} catch (_unused) {
|
|
134
134
|
return;
|
|
135
135
|
}
|
|
136
|
-
} else if (this.targetOrigin !==
|
|
136
|
+
} else if (this.targetOrigin !== OriginConstant.ANY && context.origin !== this.targetOrigin) {
|
|
137
137
|
return;
|
|
138
138
|
}
|
|
139
139
|
}
|
|
@@ -450,7 +450,7 @@ export class RequestIframeClientImpl {
|
|
|
450
450
|
_processedConfig$requ3 = processedConfig.requireAck,
|
|
451
451
|
requireAck = _processedConfig$requ3 === void 0 ? true : _processedConfig$requ3,
|
|
452
452
|
streamTimeout = processedConfig.streamTimeout,
|
|
453
|
-
|
|
453
|
+
ack = processedConfig.ack,
|
|
454
454
|
_processedConfig$retu = processedConfig.returnData,
|
|
455
455
|
returnData = _processedConfig$retu === void 0 ? this.defaultReturnData : _processedConfig$retu;
|
|
456
456
|
return new Promise((resolve, reject) => {
|
|
@@ -524,8 +524,8 @@ export class RequestIframeClientImpl {
|
|
|
524
524
|
|
|
525
525
|
// Received ACK: server has received request
|
|
526
526
|
if (data.type === MessageType.ACK) {
|
|
527
|
-
// Optional
|
|
528
|
-
if (
|
|
527
|
+
// Optional ack match (ignore mismatched ACK)
|
|
528
|
+
if (ack !== undefined && !isAckMatch(ack, data.ack)) {
|
|
529
529
|
return;
|
|
530
530
|
}
|
|
531
531
|
// Remember server's creatorId as target server ID for future requests
|
|
@@ -740,7 +740,7 @@ export class RequestIframeClientImpl {
|
|
|
740
740
|
cookies: mergedCookies,
|
|
741
741
|
targetId,
|
|
742
742
|
requireAck,
|
|
743
|
-
|
|
743
|
+
ack
|
|
744
744
|
};
|
|
745
745
|
if (extraPayload !== null && extraPayload !== void 0 && extraPayload.streamId) {
|
|
746
746
|
payload.streamId = extraPayload.streamId;
|
package/esm/core/response.js
CHANGED
|
@@ -9,10 +9,10 @@ import "core-js/modules/es.object.get-own-property-descriptors.js";
|
|
|
9
9
|
import "core-js/modules/es.promise.js";
|
|
10
10
|
import "core-js/modules/web.dom-collections.for-each.js";
|
|
11
11
|
import "core-js/modules/web.dom-collections.iterator.js";
|
|
12
|
-
import { createPostMessage, createSetCookie, createClearCookie, detectContentType, blobToBase64 } from '../utils';
|
|
12
|
+
import { createPostMessage, createSetCookie, createClearCookie, detectContentType, blobToBase64, generateRequestId } from '../utils';
|
|
13
13
|
import { MessageType, HttpStatus, HttpHeader, getStatusText, MessageRole, ErrorCode } from '../constants';
|
|
14
14
|
import { IframeFileWritableStream, isIframeWritableStream } from '../stream';
|
|
15
|
-
import {
|
|
15
|
+
import { isAckMatch } from '../utils/ack';
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Callback waiting for client acknowledgment
|
|
@@ -88,9 +88,9 @@ export class ServerResponseImpl {
|
|
|
88
88
|
/**
|
|
89
89
|
* Trigger client acknowledgment callback
|
|
90
90
|
*/
|
|
91
|
-
_triggerAck(received,
|
|
91
|
+
_triggerAck(received, ack) {
|
|
92
92
|
if (this.onAckCallback) {
|
|
93
|
-
this.onAckCallback(received,
|
|
93
|
+
this.onAckCallback(received, ack);
|
|
94
94
|
this.onAckCallback = undefined;
|
|
95
95
|
}
|
|
96
96
|
}
|
|
@@ -119,11 +119,19 @@ export class ServerResponseImpl {
|
|
|
119
119
|
* Internal method: send raw data (used by send after type detection)
|
|
120
120
|
*/
|
|
121
121
|
_sendRaw(data, options) {
|
|
122
|
-
var _options$requireAck;
|
|
122
|
+
var _options$requireAck, _ack;
|
|
123
123
|
if (this._sent) return Promise.resolve(false);
|
|
124
124
|
this.markSent();
|
|
125
125
|
var requireAck = (_options$requireAck = options === null || options === void 0 ? void 0 : options.requireAck) !== null && _options$requireAck !== void 0 ? _options$requireAck : false;
|
|
126
|
-
|
|
126
|
+
/**
|
|
127
|
+
* When requireAck is enabled, attach a unique ack payload by default so ACK can be
|
|
128
|
+
* unambiguously associated with this send.
|
|
129
|
+
*
|
|
130
|
+
* NOTE: ack is an internal reserved field (not part of public API).
|
|
131
|
+
*/
|
|
132
|
+
var expectedAck = (_ack = options === null || options === void 0 ? void 0 : options.ack) !== null && _ack !== void 0 ? _ack : requireAck ? {
|
|
133
|
+
id: generateRequestId()
|
|
134
|
+
} : undefined;
|
|
127
135
|
try {
|
|
128
136
|
// If acknowledgment not required, send directly and return true
|
|
129
137
|
if (!requireAck) {
|
|
@@ -150,7 +158,7 @@ export class ServerResponseImpl {
|
|
|
150
158
|
resolve(false);
|
|
151
159
|
return;
|
|
152
160
|
}
|
|
153
|
-
if (
|
|
161
|
+
if (expectedAck !== undefined && !isAckMatch(expectedAck, receivedAckMeta)) {
|
|
154
162
|
resolve(false);
|
|
155
163
|
return;
|
|
156
164
|
}
|
|
@@ -164,7 +172,7 @@ export class ServerResponseImpl {
|
|
|
164
172
|
statusText: getStatusText(this.statusCode),
|
|
165
173
|
headers: this.headers,
|
|
166
174
|
requireAck: true,
|
|
167
|
-
|
|
175
|
+
ack: expectedAck,
|
|
168
176
|
role: MessageRole.SERVER,
|
|
169
177
|
creatorId: this.serverId,
|
|
170
178
|
targetId: this.targetId
|
package/esm/core/server.js
CHANGED
|
@@ -89,6 +89,10 @@ export class RequestIframeServerImpl {
|
|
|
89
89
|
// Get or create shared channel and create dispatcher
|
|
90
90
|
var channel = getOrCreateMessageChannel(options === null || options === void 0 ? void 0 : options.secretKey);
|
|
91
91
|
this.dispatcher = new MessageDispatcher(channel, MessageRole.SERVER, this.id);
|
|
92
|
+
this.dispatcher.setAutoAckLimits({
|
|
93
|
+
maxMetaLength: options === null || options === void 0 ? void 0 : options.autoAckMaxMetaLength,
|
|
94
|
+
maxIdLength: options === null || options === void 0 ? void 0 : options.autoAckMaxIdLength
|
|
95
|
+
});
|
|
92
96
|
|
|
93
97
|
// Auto-open by default (unless explicitly set to false)
|
|
94
98
|
if ((options === null || options === void 0 ? void 0 : options.autoOpen) !== false) {
|
|
@@ -175,8 +179,8 @@ export class RequestIframeServerImpl {
|
|
|
175
179
|
// Handle PONG messages (server -> client heartbeat)
|
|
176
180
|
this.unregisterFns.push(this.dispatcher.registerHandler(MessageType.PONG, (data, context) => this.handlePong(data, context), handlerOptions));
|
|
177
181
|
|
|
178
|
-
// Handle
|
|
179
|
-
this.unregisterFns.push(this.dispatcher.registerHandler(MessageType.
|
|
182
|
+
// Handle ACK messages (for confirming response delivery when requireAck === true)
|
|
183
|
+
this.unregisterFns.push(this.dispatcher.registerHandler(MessageType.ACK, (data, context) => this.handleAck(data, context), handlerOptions));
|
|
180
184
|
|
|
181
185
|
// Handle stream_* messages (client→server stream)
|
|
182
186
|
this.unregisterFns.push(this.dispatcher.registerHandler(MessageType.STREAM_START, (data, ctx) => this.handleStreamStart(data, ctx), handlerOptions));
|
|
@@ -298,6 +302,11 @@ export class RequestIframeServerImpl {
|
|
|
298
302
|
if (!(body !== null && body !== void 0 && body.streamId)) return;
|
|
299
303
|
var handler = this.streamHandlers.get(body.streamId);
|
|
300
304
|
if (handler) {
|
|
305
|
+
// Mark as accepted/handled so MessageDispatcher can auto-ack when requireAck === true
|
|
306
|
+
if (!context.handledBy) {
|
|
307
|
+
context.accepted = true;
|
|
308
|
+
context.handledBy = this.id;
|
|
309
|
+
}
|
|
301
310
|
var messageType = data.type.replace('stream_', '');
|
|
302
311
|
handler(_objectSpread(_objectSpread({}, body), {}, {
|
|
303
312
|
type: messageType
|
|
@@ -377,9 +386,9 @@ export class RequestIframeServerImpl {
|
|
|
377
386
|
}
|
|
378
387
|
|
|
379
388
|
/**
|
|
380
|
-
* Handle
|
|
389
|
+
* Handle ACK (receipt confirmation for responses when requireAck === true).
|
|
381
390
|
*/
|
|
382
|
-
|
|
391
|
+
handleAck(data, context) {
|
|
383
392
|
if (!this.isOriginAllowed(data, context)) return;
|
|
384
393
|
var pending = this.pendingAcks.get(data.requestId);
|
|
385
394
|
if (pending) {
|
|
@@ -389,7 +398,7 @@ export class RequestIframeServerImpl {
|
|
|
389
398
|
}
|
|
390
399
|
clearTimeout(pending.timeoutId);
|
|
391
400
|
this.pendingAcks.delete(data.requestId);
|
|
392
|
-
pending.resolve(true, data.
|
|
401
|
+
pending.resolve(true, data.ack);
|
|
393
402
|
}
|
|
394
403
|
}
|
|
395
404
|
|
|
@@ -543,7 +552,7 @@ export class RequestIframeServerImpl {
|
|
|
543
552
|
status: HttpStatus.TOO_MANY_REQUESTS,
|
|
544
553
|
statusText: HttpStatusText[HttpStatus.TOO_MANY_REQUESTS],
|
|
545
554
|
requireAck: data.requireAck,
|
|
546
|
-
|
|
555
|
+
ack: data.ack,
|
|
547
556
|
targetId: data.creatorId
|
|
548
557
|
});
|
|
549
558
|
return;
|
|
@@ -571,8 +580,8 @@ export class RequestIframeServerImpl {
|
|
|
571
580
|
});
|
|
572
581
|
|
|
573
582
|
// Register callback waiting for client acknowledgment
|
|
574
|
-
this.registerPendingAck(data.requestId, (received,
|
|
575
|
-
res._triggerAck(received,
|
|
583
|
+
this.registerPendingAck(data.requestId, (received, ack) => {
|
|
584
|
+
res._triggerAck(received, ack);
|
|
576
585
|
}, () => {
|
|
577
586
|
res._triggerAck(false);
|
|
578
587
|
});
|
|
@@ -597,7 +606,7 @@ export class RequestIframeServerImpl {
|
|
|
597
606
|
status: HttpStatus.REQUEST_TIMEOUT,
|
|
598
607
|
statusText: HttpStatusText[HttpStatus.REQUEST_TIMEOUT],
|
|
599
608
|
requireAck: pending.data.requireAck,
|
|
600
|
-
|
|
609
|
+
ack: pending.data.ack,
|
|
601
610
|
targetId: pending.data.creatorId
|
|
602
611
|
});
|
|
603
612
|
}
|
package/esm/message/channel.js
CHANGED
|
@@ -8,6 +8,7 @@ import "core-js/modules/es.set.js";
|
|
|
8
8
|
import "core-js/modules/web.dom-collections.for-each.js";
|
|
9
9
|
import "core-js/modules/web.dom-collections.iterator.js";
|
|
10
10
|
import { isValidPostMessage, createPostMessage, isWindowAvailable } from '../utils';
|
|
11
|
+
import { OriginConstant } from '../constants';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Message context (extracted from MessageEvent, transport-agnostic)
|
|
@@ -141,7 +142,7 @@ export class MessageChannel {
|
|
|
141
142
|
* @param message message data (already formatted as PostMessageData)
|
|
142
143
|
* @param targetOrigin target origin (defaults to '*')
|
|
143
144
|
*/
|
|
144
|
-
send(target, message, targetOrigin =
|
|
145
|
+
send(target, message, targetOrigin = OriginConstant.ANY) {
|
|
145
146
|
if (!isWindowAvailable(target)) {
|
|
146
147
|
return false;
|
|
147
148
|
}
|
|
@@ -18,8 +18,9 @@ import "core-js/modules/es.object.get-own-property-descriptors.js";
|
|
|
18
18
|
import "core-js/modules/es.regexp.constructor.js";
|
|
19
19
|
import "core-js/modules/es.regexp.exec.js";
|
|
20
20
|
import "core-js/modules/es.regexp.to-string.js";
|
|
21
|
-
import { MessageRole, MessageType } from '../constants';
|
|
21
|
+
import { AutoAckConstant, MessageRole, MessageType, OriginConstant } from '../constants';
|
|
22
22
|
import { getProtocolVersion, createPostMessage } from '../utils';
|
|
23
|
+
import { getAckId, getAckMeta } from '../utils/ack';
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* Message handler function type
|
|
@@ -65,12 +66,9 @@ export class MessageDispatcher {
|
|
|
65
66
|
/** Role of this dispatcher ('client' or 'server') */
|
|
66
67
|
/** Instance ID of the client/server that owns this dispatcher */
|
|
67
68
|
/** Underlying message channel */
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
* - This is primarily used for unit tests or edge environments that synthesize MessageEvent without `source`.
|
|
72
|
-
*/
|
|
73
|
-
_defineProperty(this, "fallbackTargetOrigin", '*');
|
|
69
|
+
_defineProperty(this, "autoAckMaxMetaLength", AutoAckConstant.MAX_META_LENGTH);
|
|
70
|
+
_defineProperty(this, "autoAckMaxIdLength", AutoAckConstant.MAX_ID_LENGTH);
|
|
71
|
+
_defineProperty(this, "fallbackTargetOrigin", OriginConstant.ANY);
|
|
74
72
|
/** Message handler list */
|
|
75
73
|
_defineProperty(this, "handlers", []);
|
|
76
74
|
/** Reference count (for determining if can be destroyed when cached) */
|
|
@@ -93,11 +91,26 @@ export class MessageDispatcher {
|
|
|
93
91
|
/**
|
|
94
92
|
* Set fallback target for outgoing auto-ack messages.
|
|
95
93
|
*/
|
|
96
|
-
setFallbackTarget(targetWindow, targetOrigin =
|
|
94
|
+
setFallbackTarget(targetWindow, targetOrigin = OriginConstant.ANY) {
|
|
97
95
|
this.fallbackTargetWindow = targetWindow;
|
|
98
96
|
this.fallbackTargetOrigin = targetOrigin;
|
|
99
97
|
}
|
|
100
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Configure auto-ack echo limits (advanced/internal).
|
|
101
|
+
*
|
|
102
|
+
* @internal
|
|
103
|
+
*/
|
|
104
|
+
setAutoAckLimits(limits) {
|
|
105
|
+
if (!limits || typeof limits !== 'object') return;
|
|
106
|
+
if (typeof limits.maxMetaLength === 'number' && Number.isFinite(limits.maxMetaLength) && limits.maxMetaLength >= 0) {
|
|
107
|
+
this.autoAckMaxMetaLength = limits.maxMetaLength;
|
|
108
|
+
}
|
|
109
|
+
if (typeof limits.maxIdLength === 'number' && Number.isFinite(limits.maxIdLength) && limits.maxIdLength >= 0) {
|
|
110
|
+
this.autoAckMaxIdLength = limits.maxIdLength;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
101
114
|
// ==================== Reference Counting ====================
|
|
102
115
|
|
|
103
116
|
/**
|
|
@@ -277,29 +290,54 @@ export class MessageDispatcher {
|
|
|
277
290
|
var type = data.type;
|
|
278
291
|
|
|
279
292
|
// Don't auto-ack ack messages (avoid loops)
|
|
280
|
-
if (type === MessageType.ACK
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
293
|
+
if (type === MessageType.ACK) return;
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* ACK-only requireAck workflow (no compatibility guarantees):
|
|
297
|
+
* - For any message with `requireAck === true`, once the message is accepted/handled
|
|
298
|
+
* (via `context.accepted === true` + `context.handledBy` setter hook),
|
|
299
|
+
* we reply with `ack`.
|
|
300
|
+
*
|
|
301
|
+
* This unifies:
|
|
302
|
+
* - request delivery confirmation
|
|
303
|
+
* - response receipt confirmation
|
|
304
|
+
* - stream frame delivery confirmation (e.g. stream_data per-frame ack)
|
|
305
|
+
*/
|
|
306
|
+
if (data.requireAck === true && context.accepted === true) {
|
|
307
|
+
var ack = this.getAutoAckEchoPayload(data.ack);
|
|
286
308
|
this.sendMessage(targetWindow, targetOrigin, MessageType.ACK, data.requestId, {
|
|
287
309
|
path: data.path,
|
|
288
310
|
targetId: data.creatorId,
|
|
289
|
-
|
|
290
|
-
});
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
|
-
if (shouldAckResponse && context.accepted === true) {
|
|
294
|
-
// Receipt acknowledgment for response/error
|
|
295
|
-
this.sendMessage(targetWindow, targetOrigin, MessageType.RECEIVED, data.requestId, {
|
|
296
|
-
path: data.path,
|
|
297
|
-
targetId: data.creatorId,
|
|
298
|
-
ackMeta: data.ackMeta
|
|
311
|
+
ack
|
|
299
312
|
});
|
|
300
313
|
}
|
|
301
314
|
}
|
|
302
315
|
|
|
316
|
+
/**
|
|
317
|
+
* Limit echoed ack payload size for auto-ack replies.
|
|
318
|
+
*
|
|
319
|
+
* We only echo a fixed shape `{ id, meta?: string }`:
|
|
320
|
+
* - If meta is too long, we drop meta and only echo `{ id }`.
|
|
321
|
+
* - If id is missing or too long, omit `ack` field.
|
|
322
|
+
*/
|
|
323
|
+
getAutoAckEchoPayload(rawAck) {
|
|
324
|
+
if (rawAck === undefined) return undefined;
|
|
325
|
+
var id = getAckId(rawAck);
|
|
326
|
+
if (id === undefined) return undefined;
|
|
327
|
+
if (typeof id === 'string' && id.length > this.autoAckMaxIdLength) return undefined;
|
|
328
|
+
var meta = getAckMeta(rawAck);
|
|
329
|
+
if (meta === undefined) return {
|
|
330
|
+
id
|
|
331
|
+
};
|
|
332
|
+
if (meta.length > this.autoAckMaxMetaLength) return {
|
|
333
|
+
id
|
|
334
|
+
};
|
|
335
|
+
return {
|
|
336
|
+
id,
|
|
337
|
+
meta
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
303
341
|
/**
|
|
304
342
|
* Check if message type matches
|
|
305
343
|
*/
|
|
@@ -324,7 +362,7 @@ export class MessageDispatcher {
|
|
|
324
362
|
* @param message message data (already formatted as PostMessageData)
|
|
325
363
|
* @param targetOrigin target origin (defaults to '*')
|
|
326
364
|
*/
|
|
327
|
-
send(target, message, targetOrigin =
|
|
365
|
+
send(target, message, targetOrigin = OriginConstant.ANY) {
|
|
328
366
|
// Automatically set role and creatorId if not already set (for backward compatibility)
|
|
329
367
|
if (message.role === undefined) {
|
|
330
368
|
message.role = this.role;
|