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.
Files changed (95) hide show
  1. package/README.CN.md +220 -21
  2. package/README.md +221 -24
  3. package/esm/api/client.js +80 -0
  4. package/esm/api/server.js +61 -0
  5. package/esm/constants/index.js +289 -0
  6. package/esm/constants/messages.js +157 -0
  7. package/esm/core/client-server.js +294 -0
  8. package/esm/core/client.js +873 -0
  9. package/esm/core/request.js +27 -0
  10. package/esm/core/response.js +459 -0
  11. package/esm/core/server.js +776 -0
  12. package/esm/index.js +21 -0
  13. package/esm/interceptors/index.js +122 -0
  14. package/esm/message/channel.js +182 -0
  15. package/esm/message/dispatcher.js +418 -0
  16. package/esm/message/index.js +2 -0
  17. package/esm/stream/file-stream.js +289 -0
  18. package/esm/stream/index.js +44 -0
  19. package/esm/stream/readable-stream.js +539 -0
  20. package/esm/stream/stream-core.js +204 -0
  21. package/esm/stream/types.js +1 -0
  22. package/esm/stream/writable-stream.js +836 -0
  23. package/esm/types/index.js +1 -0
  24. package/esm/utils/ack.js +36 -0
  25. package/esm/utils/cache.js +147 -0
  26. package/esm/utils/cookie.js +352 -0
  27. package/esm/utils/debug.js +521 -0
  28. package/esm/utils/error.js +27 -0
  29. package/esm/utils/index.js +180 -0
  30. package/esm/utils/origin.js +30 -0
  31. package/esm/utils/path-match.js +148 -0
  32. package/esm/utils/protocol.js +157 -0
  33. package/library/api/client.d.ts.map +1 -1
  34. package/library/api/client.js +13 -5
  35. package/library/api/server.d.ts.map +1 -1
  36. package/library/api/server.js +6 -1
  37. package/library/constants/index.d.ts +59 -4
  38. package/library/constants/index.d.ts.map +1 -1
  39. package/library/constants/index.js +67 -9
  40. package/library/constants/messages.d.ts +8 -1
  41. package/library/constants/messages.d.ts.map +1 -1
  42. package/library/constants/messages.js +8 -1
  43. package/library/core/client-server.d.ts +7 -15
  44. package/library/core/client-server.d.ts.map +1 -1
  45. package/library/core/client-server.js +56 -44
  46. package/library/core/client.d.ts +4 -1
  47. package/library/core/client.d.ts.map +1 -1
  48. package/library/core/client.js +74 -31
  49. package/library/core/response.d.ts +21 -3
  50. package/library/core/response.d.ts.map +1 -1
  51. package/library/core/response.js +55 -7
  52. package/library/core/server.d.ts +34 -3
  53. package/library/core/server.d.ts.map +1 -1
  54. package/library/core/server.js +191 -21
  55. package/library/message/channel.d.ts +6 -0
  56. package/library/message/channel.d.ts.map +1 -1
  57. package/library/message/channel.js +2 -1
  58. package/library/message/dispatcher.d.ts +32 -0
  59. package/library/message/dispatcher.d.ts.map +1 -1
  60. package/library/message/dispatcher.js +131 -1
  61. package/library/stream/file-stream.d.ts +4 -0
  62. package/library/stream/file-stream.d.ts.map +1 -1
  63. package/library/stream/file-stream.js +61 -33
  64. package/library/stream/index.d.ts.map +1 -1
  65. package/library/stream/index.js +2 -0
  66. package/library/stream/readable-stream.d.ts +30 -11
  67. package/library/stream/readable-stream.d.ts.map +1 -1
  68. package/library/stream/readable-stream.js +368 -73
  69. package/library/stream/stream-core.d.ts +65 -0
  70. package/library/stream/stream-core.d.ts.map +1 -0
  71. package/library/stream/stream-core.js +211 -0
  72. package/library/stream/types.d.ts +203 -3
  73. package/library/stream/types.d.ts.map +1 -1
  74. package/library/stream/writable-stream.d.ts +59 -13
  75. package/library/stream/writable-stream.d.ts.map +1 -1
  76. package/library/stream/writable-stream.js +647 -197
  77. package/library/types/index.d.ts +70 -4
  78. package/library/types/index.d.ts.map +1 -1
  79. package/library/utils/ack.d.ts +2 -0
  80. package/library/utils/ack.d.ts.map +1 -0
  81. package/library/utils/ack.js +44 -0
  82. package/library/utils/debug.js +1 -1
  83. package/library/utils/index.d.ts +1 -0
  84. package/library/utils/index.d.ts.map +1 -1
  85. package/library/utils/index.js +19 -2
  86. package/library/utils/origin.d.ts +14 -0
  87. package/library/utils/origin.d.ts.map +1 -0
  88. package/library/utils/origin.js +35 -0
  89. package/package.json +30 -7
  90. package/react/README.md +16 -0
  91. package/react/esm/index.js +284 -0
  92. package/react/library/index.d.ts +1 -1
  93. package/react/library/index.d.ts.map +1 -1
  94. package/react/library/index.js +3 -3
  95. package/react/package.json +24 -2
@@ -0,0 +1,873 @@
1
+ import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator";
2
+ import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
3
+ import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
4
+ 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; } } }; }
5
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
6
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
7
+ import _regeneratorRuntime from "@babel/runtime/regenerator";
8
+ 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; }
9
+ 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) { _defineProperty(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; }
10
+ import "core-js/modules/es.symbol.description.js";
11
+ import "core-js/modules/es.array.filter.js";
12
+ import "core-js/modules/es.array.from.js";
13
+ import "core-js/modules/es.array.iterator.js";
14
+ import "core-js/modules/es.array.slice.js";
15
+ import "core-js/modules/es.map.js";
16
+ import "core-js/modules/es.object.entries.js";
17
+ import "core-js/modules/es.promise.js";
18
+ import "core-js/modules/es.object.get-own-property-descriptors.js";
19
+ import "core-js/modules/es.regexp.exec.js";
20
+ import "core-js/modules/es.regexp.to-string.js";
21
+ import "core-js/modules/es.string.match.js";
22
+ import "core-js/modules/es.string.replace.js";
23
+ import "core-js/modules/es.string.starts-with.js";
24
+ import "core-js/modules/web.dom-collections.for-each.js";
25
+ import "core-js/modules/web.dom-collections.iterator.js";
26
+ import { RequestIframeError } from '../utils';
27
+ import { isAckMatch } from '../utils/ack';
28
+ import { detectContentType, blobToBase64, isWindowAvailable, matchOrigin } from '../utils';
29
+ import { generateRequestId, generateInstanceId, CookieStore } from '../utils';
30
+ import { RequestInterceptorManager, ResponseInterceptorManager, runRequestInterceptors, runResponseInterceptors } from '../interceptors';
31
+ import { DefaultTimeout, ErrorCode, MessageType, OriginConstant, HttpStatus, HttpStatusText, HttpHeader, Messages, formatMessage, StreamType as StreamTypeConstant } from '../constants';
32
+ import { IframeReadableStream, IframeFileReadableStream, isIframeWritableStream } from '../stream';
33
+
34
+ /**
35
+ * Client configuration options
36
+ */
37
+
38
+ /**
39
+ * RequestIframeClient implementation (only responsible for initiating requests, reuses server's listener)
40
+ */
41
+ export class RequestIframeClientImpl {
42
+ /**
43
+ * Target server ID (remembered from responses)
44
+ * When a response is received, we remember the server's creatorId as the targetId for future requests
45
+ */
46
+
47
+ constructor(targetWindow, targetOrigin, server, options, instanceId) {
48
+ var _options$ackTimeout, _options$timeout, _options$asyncTimeout, _options$returnData;
49
+ /** Unique instance ID */
50
+ _defineProperty(this, "interceptors", {
51
+ request: new RequestInterceptorManager(),
52
+ response: new ResponseInterceptorManager()
53
+ });
54
+ /**
55
+ * Internal cookies storage
56
+ * - Automatically includes cookies matching the path when sending requests
57
+ * - Automatically parses Set-Cookie and saves when receiving response
58
+ */
59
+ _defineProperty(this, "_cookieStore", new CookieStore());
60
+ /**
61
+ * Stream message handler map
62
+ * key: streamId
63
+ * value: stream message handler function
64
+ */
65
+ _defineProperty(this, "streamHandlers", new Map());
66
+ this.id = instanceId || generateInstanceId();
67
+ this.targetWindow = targetWindow;
68
+ this.targetOrigin = targetOrigin;
69
+ this.server = server;
70
+ this.secretKey = options === null || options === void 0 ? void 0 : options.secretKey;
71
+
72
+ // Provide fallback target for auto-ack (useful when MessageEvent.source is missing in tests)
73
+ this.server.messageDispatcher.setFallbackTarget(this.targetWindow, this.targetOrigin);
74
+
75
+ // Build origin validator (incoming messages)
76
+ if (options !== null && options !== void 0 && options.validateOrigin) {
77
+ this.originValidator = (origin, data, context) => options.validateOrigin(origin, data, context);
78
+ } else if (options !== null && options !== void 0 && options.allowedOrigins) {
79
+ var matcher = options.allowedOrigins;
80
+ this.originValidator = origin => matchOrigin(origin, matcher);
81
+ }
82
+
83
+ // Set default timeout configuration
84
+ this.defaultAckTimeout = (_options$ackTimeout = options === null || options === void 0 ? void 0 : options.ackTimeout) !== null && _options$ackTimeout !== void 0 ? _options$ackTimeout : DefaultTimeout.ACK;
85
+ this.defaultTimeout = (_options$timeout = options === null || options === void 0 ? void 0 : options.timeout) !== null && _options$timeout !== void 0 ? _options$timeout : DefaultTimeout.REQUEST;
86
+ this.defaultAsyncTimeout = (_options$asyncTimeout = options === null || options === void 0 ? void 0 : options.asyncTimeout) !== null && _options$asyncTimeout !== void 0 ? _options$asyncTimeout : DefaultTimeout.ASYNC;
87
+
88
+ // Set default returnData configuration
89
+ this.defaultReturnData = (_options$returnData = options === null || options === void 0 ? void 0 : options.returnData) !== null && _options$returnData !== void 0 ? _options$returnData : false;
90
+
91
+ // Save initial headers configuration
92
+ this.initialHeaders = options === null || options === void 0 ? void 0 : options.headers;
93
+
94
+ // Register stream message processing callback
95
+ this.server.setStreamCallback((data, context) => {
96
+ this.dispatchStreamMessage(data, context);
97
+ });
98
+ }
99
+
100
+ /**
101
+ * Register stream message handler (StreamMessageHandler interface implementation)
102
+ */
103
+ registerStreamHandler(streamId, handler) {
104
+ this.streamHandlers.set(streamId, handler);
105
+ }
106
+
107
+ /**
108
+ * Unregister stream message handler (StreamMessageHandler interface implementation)
109
+ */
110
+ unregisterStreamHandler(streamId) {
111
+ this.streamHandlers.delete(streamId);
112
+ }
113
+
114
+ /*
115
+ Send message (StreamMessageHandler interface implementation)
116
+ */
117
+ postMessage(message) {
118
+ // Window check is handled in MessageDispatcher
119
+ this.server.messageDispatcher.send(this.targetWindow, message, this.targetOrigin);
120
+ }
121
+
122
+ /**
123
+ * Dispatch stream message to corresponding handler
124
+ */
125
+ dispatchStreamMessage(data, context) {
126
+ // Validate origin for stream messages (stream_data/stream_end/stream_error/stream_cancel)
127
+ if (context) {
128
+ if (this.originValidator) {
129
+ try {
130
+ if (!this.originValidator(context.origin, data, context)) {
131
+ return;
132
+ }
133
+ } catch (_unused) {
134
+ return;
135
+ }
136
+ } else if (this.targetOrigin !== OriginConstant.ANY && context.origin !== this.targetOrigin) {
137
+ return;
138
+ }
139
+ }
140
+ var body = data.body;
141
+ if (!body || !body.streamId) return;
142
+ var handler = this.streamHandlers.get(body.streamId);
143
+ if (handler) {
144
+ if (context && !context.handledBy) {
145
+ context.accepted = true;
146
+ context.handledBy = this.id;
147
+ }
148
+ // Extract message type (remove stream_ prefix)
149
+ var messageType = data.type.replace('stream_', '');
150
+ handler(_objectSpread(_objectSpread({}, body), {}, {
151
+ type: messageType
152
+ }));
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Resolve header value (handle function type headers)
158
+ */
159
+ resolveHeaderValue(value, config) {
160
+ if (typeof value === 'function') {
161
+ return value(config);
162
+ }
163
+ return value;
164
+ }
165
+
166
+ /**
167
+ * Detect Content-Type for request body
168
+ */
169
+ detectContentTypeForBody(body) {
170
+ return detectContentType(body, {
171
+ checkStream: false
172
+ });
173
+ }
174
+
175
+ /**
176
+ * Check if header exists (case-insensitive)
177
+ */
178
+ hasHeader(headers, name) {
179
+ var lower = name.toLowerCase();
180
+ return Object.keys(headers).some(k => k.toLowerCase() === lower);
181
+ }
182
+
183
+ /**
184
+ * Merge and resolve headers (initial headers + request headers)
185
+ * Request headers take precedence over initial headers
186
+ * Also auto-detects and sets Content-Type if not already set
187
+ */
188
+ mergeHeaders(config, body) {
189
+ var resolvedHeaders = {};
190
+
191
+ // First, resolve initial headers
192
+ if (this.initialHeaders) {
193
+ for (var _i = 0, _Object$entries = Object.entries(this.initialHeaders); _i < _Object$entries.length; _i++) {
194
+ var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
195
+ key = _Object$entries$_i[0],
196
+ value = _Object$entries$_i[1];
197
+ resolvedHeaders[key] = this.resolveHeaderValue(value, config);
198
+ }
199
+ }
200
+
201
+ // Then, merge request headers (request headers take precedence)
202
+ if (config.headers) {
203
+ for (var _i2 = 0, _Object$entries2 = Object.entries(config.headers); _i2 < _Object$entries2.length; _i2++) {
204
+ var _Object$entries2$_i = _slicedToArray(_Object$entries2[_i2], 2),
205
+ _key = _Object$entries2$_i[0],
206
+ _value = _Object$entries2$_i[1];
207
+ resolvedHeaders[_key] = this.resolveHeaderValue(_value, config);
208
+ }
209
+ }
210
+
211
+ // Auto-detect and set Content-Type if not already set and body is provided
212
+ if (body !== undefined && !this.hasHeader(resolvedHeaders, HttpHeader.CONTENT_TYPE)) {
213
+ var contentType = this.detectContentTypeForBody(body);
214
+ if (contentType) {
215
+ resolvedHeaders[HttpHeader.CONTENT_TYPE] = contentType;
216
+ }
217
+ }
218
+ return resolvedHeaders;
219
+ }
220
+
221
+ /**
222
+ * Check if server is reachable
223
+ */
224
+ isConnect() {
225
+ return new Promise((resolve, reject) => {
226
+ var requestId = generateRequestId();
227
+ var done = false;
228
+ var timeoutId = null;
229
+ var cleanup = () => {
230
+ if (timeoutId) clearTimeout(timeoutId);
231
+ this.server._unregisterPendingRequest(requestId);
232
+ };
233
+
234
+ // Check if target window is still available before sending ping
235
+ if (!isWindowAvailable(this.targetWindow)) {
236
+ reject(new RequestIframeError({
237
+ message: Messages.TARGET_WINDOW_CLOSED,
238
+ code: ErrorCode.TARGET_WINDOW_CLOSED,
239
+ config: undefined,
240
+ requestId
241
+ }));
242
+ return;
243
+ }
244
+ this.server._registerPendingRequest(requestId, data => {
245
+ if (done) return;
246
+ if (data.type === MessageType.ACK || data.type === MessageType.PONG) {
247
+ // Remember server's creatorId as target server ID for future requests
248
+ if (data.creatorId && !this._targetServerId) {
249
+ this._targetServerId = data.creatorId;
250
+ }
251
+ done = true;
252
+ cleanup();
253
+ resolve(true);
254
+ }
255
+ }, () => {
256
+ if (done) return;
257
+ done = true;
258
+ cleanup();
259
+ resolve(false);
260
+ }, this.targetOrigin, this.originValidator);
261
+ timeoutId = setTimeout(() => {
262
+ if (done) return;
263
+ done = true;
264
+ cleanup();
265
+ resolve(false);
266
+ }, this.defaultAckTimeout);
267
+
268
+ // Send ping via MessageDispatcher
269
+ this.server.messageDispatcher.sendMessage(this.targetWindow, this.targetOrigin, MessageType.PING, requestId, {
270
+ requireAck: true,
271
+ targetId: this._targetServerId
272
+ });
273
+ });
274
+ }
275
+ send(path, body, options) {
276
+ var _this = this;
277
+ return _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
278
+ var config, processedConfig, processedBody, mergedHeaders, processedPath, processedCookies, userTargetId, _processedConfig$requ, requestId, targetId;
279
+ return _regeneratorRuntime.wrap(function (_context) {
280
+ while (1) switch (_context.prev = _context.next) {
281
+ case 0:
282
+ config = _objectSpread({
283
+ path,
284
+ body
285
+ }, options);
286
+ _context.next = 1;
287
+ return runRequestInterceptors(_this.interceptors.request, config);
288
+ case 1:
289
+ processedConfig = _context.sent;
290
+ processedBody = processedConfig.body; // Universal send: dispatch by type (like response.send)
291
+ if (!(typeof File !== 'undefined' && processedBody instanceof File || typeof Blob !== 'undefined' && processedBody instanceof Blob)) {
292
+ _context.next = 2;
293
+ break;
294
+ }
295
+ return _context.abrupt("return", _this.sendFile(path, processedBody, options));
296
+ case 2:
297
+ if (!isIframeWritableStream(processedBody)) {
298
+ _context.next = 3;
299
+ break;
300
+ }
301
+ return _context.abrupt("return", _this.sendStream(path, processedBody, options));
302
+ case 3:
303
+ // Merge and resolve headers (initial headers + request headers)
304
+ mergedHeaders = _this.mergeHeaders(processedConfig, processedBody);
305
+ processedPath = processedConfig.path, processedCookies = processedConfig.cookies, userTargetId = processedConfig.targetId, _processedConfig$requ = processedConfig.requestId, requestId = _processedConfig$requ === void 0 ? generateRequestId() : _processedConfig$requ;
306
+ targetId = userTargetId || _this._targetServerId;
307
+ return _context.abrupt("return", _this._sendRequest(processedPath, processedBody, mergedHeaders, processedCookies, processedConfig, requestId, targetId));
308
+ case 4:
309
+ case "end":
310
+ return _context.stop();
311
+ }
312
+ }, _callee);
313
+ }))();
314
+ }
315
+
316
+ /**
317
+ * Send file as request body (stream only; server receives stream or auto-resolved File/Blob via autoResolve).
318
+ */
319
+ sendFile(path, content, options) {
320
+ var _this2 = this;
321
+ return _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee3() {
322
+ var _options$autoResolve;
323
+ var streamAutoResolve, mimeType, fileName, _yield$import, IframeFileWritableStream, fileStream;
324
+ return _regeneratorRuntime.wrap(function (_context3) {
325
+ while (1) switch (_context3.prev = _context3.next) {
326
+ case 0:
327
+ streamAutoResolve = (_options$autoResolve = options === null || options === void 0 ? void 0 : options.autoResolve) !== null && _options$autoResolve !== void 0 ? _options$autoResolve : true;
328
+ mimeType = options === null || options === void 0 ? void 0 : options.mimeType;
329
+ fileName = options === null || options === void 0 ? void 0 : options.fileName;
330
+ _context3.next = 1;
331
+ return import('../stream');
332
+ case 1:
333
+ _yield$import = _context3.sent;
334
+ IframeFileWritableStream = _yield$import.IframeFileWritableStream;
335
+ fileStream = new IframeFileWritableStream({
336
+ filename: fileName || (typeof File !== 'undefined' && content instanceof File ? content.name : 'file'),
337
+ mimeType: mimeType || (typeof File !== 'undefined' && content instanceof File ? content.type : content === null || content === void 0 ? void 0 : content.type) || 'application/octet-stream',
338
+ chunked: false,
339
+ autoResolve: streamAutoResolve,
340
+ next: function () {
341
+ var _next = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
342
+ var data, _t;
343
+ return _regeneratorRuntime.wrap(function (_context2) {
344
+ while (1) switch (_context2.prev = _context2.next) {
345
+ case 0:
346
+ if (!(typeof content === 'string')) {
347
+ _context2.next = 1;
348
+ break;
349
+ }
350
+ _t = btoa(unescape(encodeURIComponent(content)));
351
+ _context2.next = 3;
352
+ break;
353
+ case 1:
354
+ _context2.next = 2;
355
+ return blobToBase64(content);
356
+ case 2:
357
+ _t = _context2.sent;
358
+ case 3:
359
+ data = _t;
360
+ return _context2.abrupt("return", {
361
+ data,
362
+ done: true
363
+ });
364
+ case 4:
365
+ case "end":
366
+ return _context2.stop();
367
+ }
368
+ }, _callee2);
369
+ }));
370
+ function next() {
371
+ return _next.apply(this, arguments);
372
+ }
373
+ return next;
374
+ }()
375
+ });
376
+ return _context3.abrupt("return", _this2.sendStream(path, fileStream, options));
377
+ case 2:
378
+ case "end":
379
+ return _context3.stop();
380
+ }
381
+ }, _callee3);
382
+ }))();
383
+ }
384
+
385
+ /**
386
+ * Send stream as request body (server receives readable stream).
387
+ * Sends REQUEST with streamId and stream: true, then starts the writable stream.
388
+ */
389
+ sendStream(path, stream, options) {
390
+ var _this3 = this;
391
+ return _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee4() {
392
+ var _processedConfig$requ2, _processedConfig$targ;
393
+ var config, processedConfig, requestId, targetId, processedPath, mergedHeaders, pathMatchedCookies, mergedCookies, streamConfig, promise;
394
+ return _regeneratorRuntime.wrap(function (_context4) {
395
+ while (1) switch (_context4.prev = _context4.next) {
396
+ case 0:
397
+ config = _objectSpread({
398
+ path,
399
+ body: undefined
400
+ }, options);
401
+ _context4.next = 1;
402
+ return runRequestInterceptors(_this3.interceptors.request, config);
403
+ case 1:
404
+ processedConfig = _context4.sent;
405
+ requestId = (_processedConfig$requ2 = processedConfig.requestId) !== null && _processedConfig$requ2 !== void 0 ? _processedConfig$requ2 : generateRequestId();
406
+ targetId = (_processedConfig$targ = processedConfig.targetId) !== null && _processedConfig$targ !== void 0 ? _processedConfig$targ : _this3._targetServerId;
407
+ processedPath = processedConfig.path;
408
+ stream._bind({
409
+ requestId,
410
+ targetWindow: _this3.targetWindow,
411
+ targetOrigin: _this3.targetOrigin,
412
+ secretKey: _this3.secretKey,
413
+ channel: _this3.server.messageDispatcher.getChannel(),
414
+ registerStreamHandler: _this3.registerStreamHandler.bind(_this3),
415
+ unregisterStreamHandler: _this3.unregisterStreamHandler.bind(_this3),
416
+ heartbeat: () => _this3.isConnect(),
417
+ clientId: _this3.id,
418
+ targetId
419
+ });
420
+ mergedHeaders = _this3.mergeHeaders(processedConfig, undefined);
421
+ pathMatchedCookies = _this3._cookieStore.getForPath(processedPath);
422
+ mergedCookies = _objectSpread(_objectSpread({}, pathMatchedCookies), processedConfig.cookies);
423
+ streamConfig = _objectSpread(_objectSpread({}, processedConfig), {}, {
424
+ requestId
425
+ });
426
+ promise = _this3._sendRequest(processedPath, undefined, mergedHeaders, mergedCookies, streamConfig, requestId, targetId, {
427
+ streamId: stream.streamId
428
+ });
429
+ /** Start stream after REQUEST is sent (_sendRequest sends synchronously in executor) */
430
+ void stream.start();
431
+ return _context4.abrupt("return", promise);
432
+ case 2:
433
+ case "end":
434
+ return _context4.stop();
435
+ }
436
+ }, _callee4);
437
+ }))();
438
+ }
439
+
440
+ /**
441
+ * Internal: send REQUEST and wait for response (used by send, sendFile, sendStream).
442
+ */
443
+ _sendRequest(requestPath, body, mergedHeaders, processedCookies, processedConfig, requestId, targetId, extraPayload) {
444
+ var _processedConfig$ackT = processedConfig.ackTimeout,
445
+ ackTimeout = _processedConfig$ackT === void 0 ? this.defaultAckTimeout : _processedConfig$ackT,
446
+ _processedConfig$time = processedConfig.timeout,
447
+ timeout = _processedConfig$time === void 0 ? this.defaultTimeout : _processedConfig$time,
448
+ _processedConfig$asyn = processedConfig.asyncTimeout,
449
+ asyncTimeout = _processedConfig$asyn === void 0 ? this.defaultAsyncTimeout : _processedConfig$asyn,
450
+ _processedConfig$requ3 = processedConfig.requireAck,
451
+ requireAck = _processedConfig$requ3 === void 0 ? true : _processedConfig$requ3,
452
+ streamTimeout = processedConfig.streamTimeout,
453
+ ack = processedConfig.ack,
454
+ _processedConfig$retu = processedConfig.returnData,
455
+ returnData = _processedConfig$retu === void 0 ? this.defaultReturnData : _processedConfig$retu;
456
+ return new Promise((resolve, reject) => {
457
+ var done = false;
458
+ var timeoutId = null;
459
+ var cleanup = () => {
460
+ if (timeoutId) clearTimeout(timeoutId);
461
+ this.server._unregisterPendingRequest(requestId);
462
+ };
463
+ var fail = error => {
464
+ if (done) return;
465
+ done = true;
466
+ cleanup();
467
+ // Convert to RequestIframeError instance
468
+ var errorInstance = error instanceof RequestIframeError ? error : new RequestIframeError(error);
469
+ // Run response interceptors to allow error logging
470
+ Promise.reject(errorInstance).catch(err => {
471
+ // Run through response interceptors' rejected callbacks
472
+ var promise = Promise.reject(err);
473
+ this.interceptors.response.forEach(interceptor => {
474
+ promise = promise.catch(e => {
475
+ if (interceptor.rejected) {
476
+ return interceptor.rejected(e);
477
+ }
478
+ return Promise.reject(e);
479
+ });
480
+ });
481
+ return promise;
482
+ }).catch(() => {
483
+ // After interceptors, reject with original error
484
+ reject(errorInstance);
485
+ });
486
+ };
487
+ var setAckTimeout = () => {
488
+ if (timeoutId) clearTimeout(timeoutId);
489
+ timeoutId = setTimeout(() => {
490
+ fail(new RequestIframeError({
491
+ message: formatMessage(Messages.ACK_TIMEOUT, ackTimeout),
492
+ code: ErrorCode.ACK_TIMEOUT,
493
+ config: processedConfig,
494
+ requestId
495
+ }));
496
+ }, ackTimeout);
497
+ };
498
+ var setRequestTimeout = () => {
499
+ if (timeoutId) clearTimeout(timeoutId);
500
+ timeoutId = setTimeout(() => {
501
+ fail(new RequestIframeError({
502
+ message: formatMessage(Messages.REQUEST_TIMEOUT, timeout),
503
+ code: ErrorCode.TIMEOUT,
504
+ config: processedConfig,
505
+ requestId
506
+ }));
507
+ }, timeout);
508
+ };
509
+ var setAsyncTimeout = () => {
510
+ if (timeoutId) clearTimeout(timeoutId);
511
+ timeoutId = setTimeout(() => {
512
+ fail(new RequestIframeError({
513
+ message: formatMessage(Messages.ASYNC_REQUEST_TIMEOUT, asyncTimeout),
514
+ code: ErrorCode.ASYNC_TIMEOUT,
515
+ config: processedConfig,
516
+ requestId
517
+ }));
518
+ }, asyncTimeout);
519
+ };
520
+
521
+ // Register to server's pending requests
522
+ this.server._registerPendingRequest(requestId, data => {
523
+ if (done) return;
524
+
525
+ // Received ACK: server has received request
526
+ if (data.type === MessageType.ACK) {
527
+ // Optional ack match (ignore mismatched ACK)
528
+ if (ack !== undefined && !isAckMatch(ack, data.ack)) {
529
+ return;
530
+ }
531
+ // Remember server's creatorId as target server ID for future requests
532
+ if (data.creatorId && !this._targetServerId) {
533
+ this._targetServerId = data.creatorId;
534
+ }
535
+ // Switch to request timeout
536
+ setRequestTimeout();
537
+ return;
538
+ }
539
+
540
+ // Received ASYNC notification: this is an async task
541
+ if (data.type === MessageType.ASYNC) {
542
+ // Remember server's creatorId as target server ID for future requests
543
+ if (data.creatorId && !this._targetServerId) {
544
+ this._targetServerId = data.creatorId;
545
+ }
546
+ // Switch to async timeout
547
+ setAsyncTimeout();
548
+ return;
549
+ }
550
+
551
+ // Received stream start message
552
+ if (data.type === MessageType.STREAM_START) {
553
+ var _streamBody$chunked, _streamBody$autoResol;
554
+ done = true;
555
+ cleanup();
556
+ var streamBody = data.body;
557
+ var streamId = streamBody.streamId;
558
+ var streamType = streamBody.type || StreamTypeConstant.DATA;
559
+ var streamMode = streamBody.mode;
560
+ var streamChunked = (_streamBody$chunked = streamBody.chunked) !== null && _streamBody$chunked !== void 0 ? _streamBody$chunked : true;
561
+ var streamMetadata = streamBody.metadata;
562
+ var autoResolve = (_streamBody$autoResol = streamBody.autoResolve) !== null && _streamBody$autoResol !== void 0 ? _streamBody$autoResol : false;
563
+
564
+ // Create corresponding readable stream based on stream type
565
+ if (streamType === StreamTypeConstant.FILE) {
566
+ var _readableStream = new IframeFileReadableStream(streamId, requestId, this, {
567
+ chunked: streamChunked,
568
+ metadata: streamMetadata,
569
+ secretKey: this.secretKey,
570
+ idleTimeout: streamTimeout,
571
+ heartbeat: () => this.isConnect(),
572
+ mode: streamMode,
573
+ filename: streamMetadata === null || streamMetadata === void 0 ? void 0 : streamMetadata.filename,
574
+ mimeType: streamMetadata === null || streamMetadata === void 0 ? void 0 : streamMetadata.mimeType,
575
+ size: streamMetadata === null || streamMetadata === void 0 ? void 0 : streamMetadata.size
576
+ });
577
+
578
+ // If autoResolve is enabled, automatically read and convert to File/Blob
579
+ if (autoResolve) {
580
+ var _data$headers;
581
+ // Extract fileName from headers if available
582
+ var contentDisposition = (_data$headers = data.headers) === null || _data$headers === void 0 ? void 0 : _data$headers[HttpHeader.CONTENT_DISPOSITION];
583
+ var fileName;
584
+ if (contentDisposition) {
585
+ var disposition = typeof contentDisposition === 'string' ? contentDisposition : contentDisposition[0];
586
+ var filenameMatch = disposition.match(/filename="?([^"]+)"?/i);
587
+ if (filenameMatch) {
588
+ fileName = filenameMatch[1];
589
+ }
590
+ }
591
+ // Fallback to stream metadata if not found in headers
592
+ fileName = fileName || (streamMetadata === null || streamMetadata === void 0 ? void 0 : streamMetadata.filename) || _readableStream.filename;
593
+
594
+ // Use stream's readAsFile or readAsBlob method
595
+ var fileDataPromise = fileName ? _readableStream.readAsFile(fileName) : _readableStream.readAsBlob();
596
+ fileDataPromise.then(fileData => {
597
+ var resp = {
598
+ data: fileData,
599
+ status: data.status || HttpStatus.OK,
600
+ statusText: data.statusText || HttpStatusText[HttpStatus.OK],
601
+ requestId,
602
+ headers: data.headers
603
+ };
604
+ return runResponseInterceptors(this.interceptors.response, resp);
605
+ }).then(response => {
606
+ resolve(returnData ? response.data : response);
607
+ }).catch(reject);
608
+ return;
609
+ }
610
+
611
+ // Non-autoResolve: return file stream directly
612
+ var _resp = {
613
+ data: undefined,
614
+ status: data.status || HttpStatus.OK,
615
+ statusText: data.statusText || HttpStatusText[HttpStatus.OK],
616
+ requestId,
617
+ headers: data.headers,
618
+ stream: _readableStream
619
+ };
620
+ runResponseInterceptors(this.interceptors.response, _resp).then(response => {
621
+ resolve(returnData ? response.data : response);
622
+ }).catch(reject);
623
+ return;
624
+ }
625
+
626
+ // Non-file stream: create regular readable stream
627
+ var readableStream = new IframeReadableStream(streamId, requestId, this, {
628
+ type: streamType,
629
+ mode: streamMode,
630
+ chunked: streamChunked,
631
+ metadata: streamMetadata,
632
+ secretKey: this.secretKey,
633
+ idleTimeout: streamTimeout,
634
+ heartbeat: () => this.isConnect()
635
+ });
636
+ var resp = {
637
+ data: undefined,
638
+ status: data.status || HttpStatus.OK,
639
+ statusText: data.statusText || HttpStatusText[HttpStatus.OK],
640
+ requestId,
641
+ headers: data.headers,
642
+ stream: readableStream
643
+ };
644
+ runResponseInterceptors(this.interceptors.response, resp).then(response => {
645
+ resolve(returnData ? response.data : response);
646
+ }).catch(reject);
647
+ return;
648
+ }
649
+
650
+ // Received stream data/end/error/cancel message - dispatch to stream handler
651
+ if (data.type.startsWith('stream_')) {
652
+ this.dispatchStreamMessage(data);
653
+ return;
654
+ }
655
+
656
+ // Received response
657
+ if (data.type === MessageType.RESPONSE) {
658
+ done = true;
659
+ cleanup();
660
+
661
+ // Remember server's creatorId as target server ID for future requests
662
+ if (data.creatorId && !this._targetServerId) {
663
+ this._targetServerId = data.creatorId;
664
+ }
665
+
666
+ // Parse and save server-set cookies (from Set-Cookie header)
667
+ if (data.headers && data.headers[HttpHeader.SET_COOKIE]) {
668
+ var setCookies = data.headers[HttpHeader.SET_COOKIE];
669
+ var setCookieArray = Array.isArray(setCookies) ? setCookies : [setCookies];
670
+ var _iterator = _createForOfIteratorHelper(setCookieArray),
671
+ _step;
672
+ try {
673
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
674
+ var setCookieStr = _step.value;
675
+ this._cookieStore.setFromSetCookie(setCookieStr);
676
+ }
677
+ } catch (err) {
678
+ _iterator.e(err);
679
+ } finally {
680
+ _iterator.f();
681
+ }
682
+ }
683
+ var _resp2 = {
684
+ data: data.data,
685
+ status: data.status || HttpStatus.OK,
686
+ statusText: data.statusText || HttpStatusText[HttpStatus.OK],
687
+ requestId,
688
+ headers: data.headers
689
+ };
690
+ runResponseInterceptors(this.interceptors.response, _resp2).then(response => {
691
+ resolve(returnData ? response.data : response);
692
+ }).catch(reject);
693
+ return;
694
+ }
695
+
696
+ // Received error
697
+ if (data.type === MessageType.ERROR) {
698
+ var _data$error, _data$error2;
699
+ // Remember server's creatorId as target server ID for future requests
700
+ if (data.creatorId && !this._targetServerId) {
701
+ this._targetServerId = data.creatorId;
702
+ }
703
+ fail(new RequestIframeError({
704
+ message: ((_data$error = data.error) === null || _data$error === void 0 ? void 0 : _data$error.message) || Messages.REQUEST_FAILED,
705
+ code: ((_data$error2 = data.error) === null || _data$error2 === void 0 ? void 0 : _data$error2.code) || ErrorCode.REQUEST_ERROR,
706
+ config: processedConfig,
707
+ response: data.status ? {
708
+ data: data.data,
709
+ status: data.status,
710
+ statusText: data.statusText || Messages.ERROR
711
+ } : undefined,
712
+ requestId
713
+ }));
714
+ }
715
+ }, error => {
716
+ fail(new RequestIframeError({
717
+ message: error.message || Messages.REQUEST_FAILED,
718
+ code: ErrorCode.REQUEST_ERROR,
719
+ config: processedConfig,
720
+ requestId
721
+ }));
722
+ }, this.targetOrigin, this.originValidator);
723
+
724
+ // Set ACK timeout (delivery stage). If disabled, start request timeout immediately.
725
+ if (requireAck === false) {
726
+ setRequestTimeout();
727
+ } else {
728
+ setAckTimeout();
729
+ }
730
+
731
+ // Get cookies matching request path and merge with user-provided cookies (user-provided takes precedence)
732
+ var pathMatchedCookies = this._cookieStore.getForPath(requestPath);
733
+ var mergedCookies = _objectSpread(_objectSpread({}, pathMatchedCookies), processedCookies);
734
+
735
+ // Send request via MessageDispatcher
736
+ var payload = {
737
+ path: requestPath,
738
+ body,
739
+ headers: mergedHeaders,
740
+ cookies: mergedCookies,
741
+ targetId,
742
+ requireAck,
743
+ ack
744
+ };
745
+ if (extraPayload !== null && extraPayload !== void 0 && extraPayload.streamId) {
746
+ payload.streamId = extraPayload.streamId;
747
+ }
748
+
749
+ // Check if target window is still available before sending
750
+ if (!isWindowAvailable(this.targetWindow)) {
751
+ fail(new RequestIframeError({
752
+ message: Messages.TARGET_WINDOW_CLOSED,
753
+ code: ErrorCode.TARGET_WINDOW_CLOSED,
754
+ config: processedConfig,
755
+ requestId
756
+ }));
757
+ return;
758
+ }
759
+ this.server.messageDispatcher.sendMessage(this.targetWindow, this.targetOrigin, MessageType.REQUEST, requestId, payload);
760
+ });
761
+ }
762
+ /**
763
+ * Get internal server instance (for debugging)
764
+ */
765
+ getServer() {
766
+ return this.server;
767
+ }
768
+
769
+ /**
770
+ * Whether message handling is enabled
771
+ */
772
+ get isOpen() {
773
+ return this.server.isOpen;
774
+ }
775
+
776
+ /**
777
+ * Check if target window is still available (not closed/removed)
778
+ * @returns true if target window is available, false otherwise
779
+ */
780
+ isAvailable() {
781
+ return isWindowAvailable(this.targetWindow);
782
+ }
783
+
784
+ /**
785
+ * Enable message handling (register message handlers)
786
+ */
787
+ open() {
788
+ this.server.open();
789
+ }
790
+
791
+ /**
792
+ * Disable message handling (unregister message handlers, but don't release resources)
793
+ */
794
+ close() {
795
+ this.server.close();
796
+ }
797
+
798
+ /**
799
+ * Destroy client (close and release all resources)
800
+ */
801
+ destroy() {
802
+ // Clear cookies
803
+ this._cookieStore.clear();
804
+
805
+ // Clear stream handlers
806
+ this.streamHandlers.clear();
807
+
808
+ // Clear interceptors
809
+ this.interceptors.request.clear();
810
+ this.interceptors.response.clear();
811
+
812
+ // Destroy server (this will also release the message channel)
813
+ this.server.destroy();
814
+ }
815
+
816
+ /**
817
+ * Get all cookies matching specified path
818
+ * @param path Request path, returns all cookies if not provided
819
+ */
820
+ getCookies(path) {
821
+ if (path) {
822
+ return this._cookieStore.getForPath(path);
823
+ }
824
+ return this._cookieStore.getAllSimple();
825
+ }
826
+
827
+ /**
828
+ * Get specified cookie
829
+ * @param name Cookie name
830
+ * @param path Path (optional)
831
+ */
832
+ getCookie(name, path) {
833
+ return this._cookieStore.get(name, path);
834
+ }
835
+
836
+ /**
837
+ * Set cookie
838
+ * @param name Cookie name
839
+ * @param value Cookie value
840
+ * @param options Cookie options (path, etc.)
841
+ */
842
+ setCookie(name, value, options) {
843
+ var _options$path;
844
+ var expires;
845
+ if (options !== null && options !== void 0 && options.expires) {
846
+ expires = options.expires.getTime();
847
+ } else if ((options === null || options === void 0 ? void 0 : options.maxAge) !== undefined) {
848
+ expires = Date.now() + options.maxAge * 1000;
849
+ }
850
+ this._cookieStore.set({
851
+ name,
852
+ value,
853
+ path: (_options$path = options === null || options === void 0 ? void 0 : options.path) !== null && _options$path !== void 0 ? _options$path : '/',
854
+ expires
855
+ });
856
+ }
857
+
858
+ /**
859
+ * Remove specified cookie
860
+ * @param name Cookie name
861
+ * @param path Path (optional, defaults to '/')
862
+ */
863
+ removeCookie(name, path) {
864
+ this._cookieStore.remove(name, path !== null && path !== void 0 ? path : '/');
865
+ }
866
+
867
+ /**
868
+ * Clear all cookies
869
+ */
870
+ clearCookies() {
871
+ this._cookieStore.clear();
872
+ }
873
+ }