request-iframe 0.0.3 → 0.0.5

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 (69) hide show
  1. package/QUICKSTART.CN.md +35 -8
  2. package/QUICKSTART.md +35 -8
  3. package/README.CN.md +177 -24
  4. package/README.md +237 -19
  5. package/library/__tests__/channel.test.ts +16 -4
  6. package/library/__tests__/coverage-branches.test.ts +356 -0
  7. package/library/__tests__/debug.test.ts +22 -0
  8. package/library/__tests__/dispatcher.test.ts +8 -4
  9. package/library/__tests__/requestIframe.test.ts +1243 -87
  10. package/library/__tests__/stream.test.ts +92 -16
  11. package/library/__tests__/utils.test.ts +41 -1
  12. package/library/api/client.d.ts.map +1 -1
  13. package/library/api/client.js +1 -0
  14. package/library/constants/index.d.ts +2 -0
  15. package/library/constants/index.d.ts.map +1 -1
  16. package/library/constants/index.js +3 -1
  17. package/library/constants/messages.d.ts +3 -0
  18. package/library/constants/messages.d.ts.map +1 -1
  19. package/library/constants/messages.js +3 -0
  20. package/library/core/client-server.d.ts +4 -0
  21. package/library/core/client-server.d.ts.map +1 -1
  22. package/library/core/client-server.js +45 -22
  23. package/library/core/client.d.ts +36 -4
  24. package/library/core/client.d.ts.map +1 -1
  25. package/library/core/client.js +508 -285
  26. package/library/core/request.d.ts +3 -1
  27. package/library/core/request.d.ts.map +1 -1
  28. package/library/core/request.js +2 -1
  29. package/library/core/response.d.ts +26 -4
  30. package/library/core/response.d.ts.map +1 -1
  31. package/library/core/response.js +192 -112
  32. package/library/core/server.d.ts +13 -0
  33. package/library/core/server.d.ts.map +1 -1
  34. package/library/core/server.js +221 -6
  35. package/library/index.d.ts +2 -1
  36. package/library/index.d.ts.map +1 -1
  37. package/library/index.js +39 -3
  38. package/library/message/channel.d.ts +2 -2
  39. package/library/message/channel.d.ts.map +1 -1
  40. package/library/message/channel.js +5 -1
  41. package/library/message/dispatcher.d.ts +2 -2
  42. package/library/message/dispatcher.d.ts.map +1 -1
  43. package/library/message/dispatcher.js +6 -5
  44. package/library/stream/index.d.ts +11 -1
  45. package/library/stream/index.d.ts.map +1 -1
  46. package/library/stream/index.js +21 -3
  47. package/library/stream/types.d.ts +2 -2
  48. package/library/stream/types.d.ts.map +1 -1
  49. package/library/stream/writable-stream.d.ts +1 -1
  50. package/library/stream/writable-stream.d.ts.map +1 -1
  51. package/library/stream/writable-stream.js +87 -47
  52. package/library/types/index.d.ts +29 -5
  53. package/library/types/index.d.ts.map +1 -1
  54. package/library/utils/debug.d.ts.map +1 -1
  55. package/library/utils/debug.js +6 -2
  56. package/library/utils/error.d.ts +21 -0
  57. package/library/utils/error.d.ts.map +1 -0
  58. package/library/utils/error.js +34 -0
  59. package/library/utils/index.d.ts +21 -0
  60. package/library/utils/index.d.ts.map +1 -1
  61. package/library/utils/index.js +141 -2
  62. package/library/utils/path-match.d.ts +16 -0
  63. package/library/utils/path-match.d.ts.map +1 -1
  64. package/library/utils/path-match.js +65 -0
  65. package/package.json +2 -1
  66. package/react/library/__tests__/index.test.tsx +44 -22
  67. package/react/library/index.d.ts.map +1 -1
  68. package/react/library/index.js +81 -23
  69. package/react/package.json +7 -0
@@ -6,6 +6,7 @@ require("core-js/modules/es.array.from.js");
6
6
  require("core-js/modules/es.array.slice.js");
7
7
  require("core-js/modules/es.object.get-own-property-descriptors.js");
8
8
  require("core-js/modules/es.regexp.to-string.js");
9
+ require("core-js/modules/es.weak-map.js");
9
10
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
10
11
  Object.defineProperty(exports, "__esModule", {
11
12
  value: true
@@ -32,6 +33,7 @@ var _stream = require("../stream");
32
33
  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; } } }; }
33
34
  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; } }
34
35
  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; }
36
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t2 in e) "default" !== _t2 && {}.hasOwnProperty.call(e, _t2) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t2)) && (i.get || i.set) ? o(f, _t2, i) : f[_t2] = e[_t2]); return f; })(e, t); }
35
37
  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; }
36
38
  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; }
37
39
  /**
@@ -48,7 +50,7 @@ class RequestIframeClientImpl {
48
50
  */
49
51
 
50
52
  constructor(targetWindow, targetOrigin, server, options, instanceId) {
51
- var _options$ackTimeout, _options$timeout, _options$asyncTimeout;
53
+ var _options$ackTimeout, _options$timeout, _options$asyncTimeout, _options$returnData;
52
54
  /** Unique instance ID */
53
55
  (0, _defineProperty2.default)(this, "interceptors", {
54
56
  request: new _interceptors.RequestInterceptorManager(),
@@ -77,6 +79,9 @@ class RequestIframeClientImpl {
77
79
  this.defaultTimeout = (_options$timeout = options === null || options === void 0 ? void 0 : options.timeout) !== null && _options$timeout !== void 0 ? _options$timeout : _constants.DefaultTimeout.REQUEST;
78
80
  this.defaultAsyncTimeout = (_options$asyncTimeout = options === null || options === void 0 ? void 0 : options.asyncTimeout) !== null && _options$asyncTimeout !== void 0 ? _options$asyncTimeout : _constants.DefaultTimeout.ASYNC;
79
81
 
82
+ // Set default returnData configuration
83
+ this.defaultReturnData = (_options$returnData = options === null || options === void 0 ? void 0 : options.returnData) !== null && _options$returnData !== void 0 ? _options$returnData : false;
84
+
80
85
  // Save initial headers configuration
81
86
  this.initialHeaders = options === null || options === void 0 ? void 0 : options.headers;
82
87
 
@@ -104,6 +109,7 @@ class RequestIframeClientImpl {
104
109
  Send message (StreamMessageHandler interface implementation)
105
110
  */
106
111
  postMessage(message) {
112
+ // Window check is handled in MessageDispatcher
107
113
  this.server.messageDispatcher.send(this.targetWindow, message, this.targetOrigin);
108
114
  }
109
115
 
@@ -133,11 +139,29 @@ class RequestIframeClientImpl {
133
139
  return value;
134
140
  }
135
141
 
142
+ /**
143
+ * Detect Content-Type for request body
144
+ */
145
+ detectContentTypeForBody(body) {
146
+ return (0, _utils.detectContentType)(body, {
147
+ checkStream: false
148
+ });
149
+ }
150
+
151
+ /**
152
+ * Check if header exists (case-insensitive)
153
+ */
154
+ hasHeader(headers, name) {
155
+ var lower = name.toLowerCase();
156
+ return Object.keys(headers).some(k => k.toLowerCase() === lower);
157
+ }
158
+
136
159
  /**
137
160
  * Merge and resolve headers (initial headers + request headers)
138
161
  * Request headers take precedence over initial headers
162
+ * Also auto-detects and sets Content-Type if not already set
139
163
  */
140
- mergeHeaders(config) {
164
+ mergeHeaders(config, body) {
141
165
  var resolvedHeaders = {};
142
166
 
143
167
  // First, resolve initial headers
@@ -159,6 +183,14 @@ class RequestIframeClientImpl {
159
183
  resolvedHeaders[_key] = this.resolveHeaderValue(_value, config);
160
184
  }
161
185
  }
186
+
187
+ // Auto-detect and set Content-Type if not already set and body is provided
188
+ if (body !== undefined && !this.hasHeader(resolvedHeaders, _constants.HttpHeader.CONTENT_TYPE)) {
189
+ var contentType = this.detectContentTypeForBody(body);
190
+ if (contentType) {
191
+ resolvedHeaders[_constants.HttpHeader.CONTENT_TYPE] = contentType;
192
+ }
193
+ }
162
194
  return resolvedHeaders;
163
195
  }
164
196
 
@@ -166,7 +198,7 @@ class RequestIframeClientImpl {
166
198
  * Check if server is reachable
167
199
  */
168
200
  isConnect() {
169
- return new Promise(resolve => {
201
+ return new Promise((resolve, reject) => {
170
202
  var requestId = (0, _utils.generateRequestId)();
171
203
  var done = false;
172
204
  var timeoutId = null;
@@ -174,6 +206,17 @@ class RequestIframeClientImpl {
174
206
  if (timeoutId) clearTimeout(timeoutId);
175
207
  this.server._unregisterPendingRequest(requestId);
176
208
  };
209
+
210
+ // Check if target window is still available before sending ping
211
+ if (!(0, _utils.isWindowAvailable)(this.targetWindow)) {
212
+ reject(new _utils.RequestIframeError({
213
+ message: _constants.Messages.TARGET_WINDOW_CLOSED,
214
+ code: _constants.ErrorCode.TARGET_WINDOW_CLOSED,
215
+ config: undefined,
216
+ requestId
217
+ }));
218
+ return;
219
+ }
177
220
  this.server._registerPendingRequest(requestId, data => {
178
221
  if (done) return;
179
222
  if (data.type === _constants.MessageType.PONG) {
@@ -201,7 +244,7 @@ class RequestIframeClientImpl {
201
244
  send(path, body, options) {
202
245
  var _this = this;
203
246
  return (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee() {
204
- var config, processedConfig, mergedHeaders, processedPath, processedBody, processedCookies, userTargetId, _processedConfig$ackT, ackTimeout, _processedConfig$time, timeout, _processedConfig$asyn, asyncTimeout, _processedConfig$requ, requestId, targetId;
247
+ var config, processedConfig, processedBody, mergedHeaders, processedPath, processedCookies, userTargetId, _processedConfig$requ, requestId, targetId;
205
248
  return _regenerator.default.wrap(function (_context) {
206
249
  while (1) switch (_context.prev = _context.next) {
207
250
  case 0:
@@ -213,300 +256,472 @@ class RequestIframeClientImpl {
213
256
  return (0, _interceptors.runRequestInterceptors)(_this.interceptors.request, config);
214
257
  case 1:
215
258
  processedConfig = _context.sent;
259
+ processedBody = processedConfig.body; // Universal send: dispatch by type (like response.send)
260
+ if (!(typeof File !== 'undefined' && processedBody instanceof File || typeof Blob !== 'undefined' && processedBody instanceof Blob)) {
261
+ _context.next = 2;
262
+ break;
263
+ }
264
+ return _context.abrupt("return", _this.sendFile(path, processedBody, options));
265
+ case 2:
266
+ if (!(0, _stream.isIframeWritableStream)(processedBody)) {
267
+ _context.next = 3;
268
+ break;
269
+ }
270
+ return _context.abrupt("return", _this.sendStream(path, processedBody, options));
271
+ case 3:
216
272
  // Merge and resolve headers (initial headers + request headers)
217
- mergedHeaders = _this.mergeHeaders(processedConfig);
218
- processedPath = processedConfig.path, processedBody = processedConfig.body, processedCookies = processedConfig.cookies, userTargetId = processedConfig.targetId, _processedConfig$ackT = processedConfig.ackTimeout, ackTimeout = _processedConfig$ackT === void 0 ? _this.defaultAckTimeout : _processedConfig$ackT, _processedConfig$time = processedConfig.timeout, timeout = _processedConfig$time === void 0 ? _this.defaultTimeout : _processedConfig$time, _processedConfig$asyn = processedConfig.asyncTimeout, asyncTimeout = _processedConfig$asyn === void 0 ? _this.defaultAsyncTimeout : _processedConfig$asyn, _processedConfig$requ = processedConfig.requestId, requestId = _processedConfig$requ === void 0 ? (0, _utils.generateRequestId)() : _processedConfig$requ; // Use user-specified targetId, or remembered target server ID, or undefined
273
+ mergedHeaders = _this.mergeHeaders(processedConfig, processedBody);
274
+ processedPath = processedConfig.path, processedCookies = processedConfig.cookies, userTargetId = processedConfig.targetId, _processedConfig$requ = processedConfig.requestId, requestId = _processedConfig$requ === void 0 ? (0, _utils.generateRequestId)() : _processedConfig$requ;
219
275
  targetId = userTargetId || _this._targetServerId;
220
- return _context.abrupt("return", new Promise((resolve, reject) => {
221
- var prefixedPath = _this.prefixPath(processedPath);
222
- var done = false;
223
- var timeoutId = null;
224
- var cleanup = () => {
225
- if (timeoutId) clearTimeout(timeoutId);
226
- _this.server._unregisterPendingRequest(requestId);
227
- };
228
- var fail = error => {
229
- if (done) return;
230
- done = true;
231
- cleanup();
232
- // Run response interceptors to allow error logging
233
- Promise.reject(error).catch(err => {
234
- // Run through response interceptors' rejected callbacks
235
- var promise = Promise.reject(err);
236
- _this.interceptors.response.forEach(interceptor => {
237
- promise = promise.catch(e => {
238
- if (interceptor.rejected) {
239
- return interceptor.rejected(e);
240
- }
241
- return Promise.reject(e);
242
- });
243
- });
244
- return promise;
245
- }).catch(() => {
246
- // After interceptors, reject with original error
247
- reject(error);
248
- });
249
- };
250
- var setAckTimeout = () => {
251
- if (timeoutId) clearTimeout(timeoutId);
252
- timeoutId = setTimeout(() => {
253
- fail({
254
- message: (0, _constants.formatMessage)(_constants.Messages.ACK_TIMEOUT, ackTimeout),
255
- code: _constants.ErrorCode.ACK_TIMEOUT,
256
- config: processedConfig,
257
- requestId
258
- });
259
- }, ackTimeout);
260
- };
261
- var setRequestTimeout = () => {
262
- if (timeoutId) clearTimeout(timeoutId);
263
- timeoutId = setTimeout(() => {
264
- fail({
265
- message: (0, _constants.formatMessage)(_constants.Messages.REQUEST_TIMEOUT, timeout),
266
- code: _constants.ErrorCode.TIMEOUT,
267
- config: processedConfig,
268
- requestId
269
- });
270
- }, timeout);
271
- };
272
- var setAsyncTimeout = () => {
273
- if (timeoutId) clearTimeout(timeoutId);
274
- timeoutId = setTimeout(() => {
275
- fail({
276
- message: (0, _constants.formatMessage)(_constants.Messages.ASYNC_REQUEST_TIMEOUT, asyncTimeout),
277
- code: _constants.ErrorCode.ASYNC_TIMEOUT,
278
- config: processedConfig,
279
- requestId
280
- });
281
- }, asyncTimeout);
282
- };
283
-
284
- // Register to server's pending requests
285
- _this.server._registerPendingRequest(requestId, data => {
286
- if (done) return;
287
-
288
- // Received ACK: server has received request
289
- if (data.type === _constants.MessageType.ACK) {
290
- // Remember server's creatorId as target server ID for future requests
291
- if (data.creatorId && !_this._targetServerId) {
292
- _this._targetServerId = data.creatorId;
293
- }
294
- // Switch to request timeout
295
- setRequestTimeout();
296
- return;
297
- }
298
-
299
- // Received ASYNC notification: this is an async task
300
- if (data.type === _constants.MessageType.ASYNC) {
301
- // Remember server's creatorId as target server ID for future requests
302
- if (data.creatorId && !_this._targetServerId) {
303
- _this._targetServerId = data.creatorId;
304
- }
305
- // Switch to async timeout
306
- setAsyncTimeout();
307
- return;
308
- }
276
+ return _context.abrupt("return", _this._sendRequest(processedPath, processedBody, mergedHeaders, processedCookies, processedConfig, requestId, targetId));
277
+ case 4:
278
+ case "end":
279
+ return _context.stop();
280
+ }
281
+ }, _callee);
282
+ }))();
283
+ }
309
284
 
310
- // Received stream start message
311
- if (data.type === _constants.MessageType.STREAM_START) {
312
- var _streamBody$chunked, _streamBody$autoResol;
313
- done = true;
314
- cleanup();
315
- var streamBody = data.body;
316
- var streamId = streamBody.streamId;
317
- var streamType = streamBody.type || _constants.StreamType.DATA;
318
- var streamChunked = (_streamBody$chunked = streamBody.chunked) !== null && _streamBody$chunked !== void 0 ? _streamBody$chunked : true;
319
- var streamMetadata = streamBody.metadata;
320
- var autoResolve = (_streamBody$autoResol = streamBody.autoResolve) !== null && _streamBody$autoResol !== void 0 ? _streamBody$autoResol : false;
321
-
322
- // Create corresponding readable stream based on stream type
323
- if (streamType === _constants.StreamType.FILE) {
324
- var _readableStream = new _stream.IframeFileReadableStream(streamId, requestId, _this, {
325
- chunked: streamChunked,
326
- metadata: streamMetadata,
327
- filename: streamMetadata === null || streamMetadata === void 0 ? void 0 : streamMetadata.filename,
328
- mimeType: streamMetadata === null || streamMetadata === void 0 ? void 0 : streamMetadata.mimeType,
329
- size: streamMetadata === null || streamMetadata === void 0 ? void 0 : streamMetadata.size
330
- });
331
-
332
- // If autoResolve is enabled, automatically read and convert to File/Blob
333
- if (autoResolve) {
334
- var _data$headers;
335
- // Extract fileName from headers if available
336
- var contentDisposition = (_data$headers = data.headers) === null || _data$headers === void 0 ? void 0 : _data$headers[_constants.HttpHeader.CONTENT_DISPOSITION];
337
- var fileName;
338
- if (contentDisposition) {
339
- var disposition = typeof contentDisposition === 'string' ? contentDisposition : contentDisposition[0];
340
- var filenameMatch = disposition.match(/filename="?([^"]+)"?/i);
341
- if (filenameMatch) {
342
- fileName = filenameMatch[1];
285
+ /**
286
+ * Send file as request body (stream only; server receives stream or auto-resolved File/Blob via autoResolve).
287
+ */
288
+ sendFile(path, content, options) {
289
+ var _this2 = this;
290
+ return (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee3() {
291
+ var _options$autoResolve;
292
+ var streamAutoResolve, mimeType, fileName, _yield$import, IframeFileWritableStream, fileStream;
293
+ return _regenerator.default.wrap(function (_context3) {
294
+ while (1) switch (_context3.prev = _context3.next) {
295
+ case 0:
296
+ streamAutoResolve = (_options$autoResolve = options === null || options === void 0 ? void 0 : options.autoResolve) !== null && _options$autoResolve !== void 0 ? _options$autoResolve : true;
297
+ mimeType = options === null || options === void 0 ? void 0 : options.mimeType;
298
+ fileName = options === null || options === void 0 ? void 0 : options.fileName;
299
+ _context3.next = 1;
300
+ return Promise.resolve().then(() => _interopRequireWildcard(require('../stream')));
301
+ case 1:
302
+ _yield$import = _context3.sent;
303
+ IframeFileWritableStream = _yield$import.IframeFileWritableStream;
304
+ fileStream = new IframeFileWritableStream({
305
+ filename: fileName || (typeof File !== 'undefined' && content instanceof File ? content.name : 'file'),
306
+ mimeType: mimeType || (typeof File !== 'undefined' && content instanceof File ? content.type : content === null || content === void 0 ? void 0 : content.type) || 'application/octet-stream',
307
+ chunked: false,
308
+ autoResolve: streamAutoResolve,
309
+ next: function () {
310
+ var _next = (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee2() {
311
+ var data, _t;
312
+ return _regenerator.default.wrap(function (_context2) {
313
+ while (1) switch (_context2.prev = _context2.next) {
314
+ case 0:
315
+ if (!(typeof content === 'string')) {
316
+ _context2.next = 1;
317
+ break;
343
318
  }
344
- }
345
- // Fallback to stream metadata if not found in headers
346
- fileName = fileName || (streamMetadata === null || streamMetadata === void 0 ? void 0 : streamMetadata.filename) || _readableStream.filename;
347
-
348
- // Use stream's readAsFile or readAsBlob method
349
- var fileDataPromise = fileName ? _readableStream.readAsFile(fileName) : _readableStream.readAsBlob();
350
- fileDataPromise.then(fileData => {
351
- var resp = {
352
- data: fileData,
353
- status: data.status || _constants.HttpStatus.OK,
354
- statusText: data.statusText || _constants.HttpStatusText[_constants.HttpStatus.OK],
355
- requestId,
356
- headers: data.headers
357
- };
358
- return (0, _interceptors.runResponseInterceptors)(_this.interceptors.response, resp);
359
- }).then(resolve).catch(reject);
360
- return;
319
+ _t = btoa(unescape(encodeURIComponent(content)));
320
+ _context2.next = 3;
321
+ break;
322
+ case 1:
323
+ _context2.next = 2;
324
+ return (0, _utils.blobToBase64)(content);
325
+ case 2:
326
+ _t = _context2.sent;
327
+ case 3:
328
+ data = _t;
329
+ return _context2.abrupt("return", {
330
+ data,
331
+ done: true
332
+ });
333
+ case 4:
334
+ case "end":
335
+ return _context2.stop();
361
336
  }
362
-
363
- // Non-autoResolve: return file stream directly
364
- var _resp = {
365
- data: undefined,
366
- status: data.status || _constants.HttpStatus.OK,
367
- statusText: data.statusText || _constants.HttpStatusText[_constants.HttpStatus.OK],
368
- requestId,
369
- headers: data.headers,
370
- stream: _readableStream
371
- };
372
- (0, _interceptors.runResponseInterceptors)(_this.interceptors.response, _resp).then(resolve).catch(reject);
373
- return;
374
- }
375
-
376
- // Non-file stream: create regular readable stream
377
- var readableStream = new _stream.IframeReadableStream(streamId, requestId, _this, {
378
- type: streamType,
379
- chunked: streamChunked,
380
- metadata: streamMetadata
381
- });
382
- var resp = {
383
- data: undefined,
384
- status: data.status || _constants.HttpStatus.OK,
385
- statusText: data.statusText || _constants.HttpStatusText[_constants.HttpStatus.OK],
386
- requestId,
387
- headers: data.headers,
388
- stream: readableStream
389
- };
390
- (0, _interceptors.runResponseInterceptors)(_this.interceptors.response, resp).then(resolve).catch(reject);
391
- return;
337
+ }, _callee2);
338
+ }));
339
+ function next() {
340
+ return _next.apply(this, arguments);
392
341
  }
393
-
394
- // Received stream data/end/error/cancel message - dispatch to stream handler
395
- if (data.type.startsWith('stream_')) {
396
- _this.dispatchStreamMessage(data);
397
- return;
398
- }
399
-
400
- // Received response
401
- if (data.type === _constants.MessageType.RESPONSE) {
402
- done = true;
403
- cleanup();
404
-
405
- // Remember server's creatorId as target server ID for future requests
406
- if (data.creatorId && !_this._targetServerId) {
407
- _this._targetServerId = data.creatorId;
408
- }
409
-
410
- // If server requires acknowledgment, send received message
411
- if (data.requireAck) {
412
- _this.server.messageDispatcher.sendMessage(_this.targetWindow, _this.targetOrigin, _constants.MessageType.RECEIVED, requestId, {
413
- path: prefixedPath,
414
- targetId: data.creatorId
415
- });
416
- }
417
-
418
- // Parse and save server-set cookies (from Set-Cookie header)
419
- if (data.headers && data.headers[_constants.HttpHeader.SET_COOKIE]) {
420
- var setCookies = data.headers[_constants.HttpHeader.SET_COOKIE];
421
- var setCookieArray = Array.isArray(setCookies) ? setCookies : [setCookies];
422
- var _iterator = _createForOfIteratorHelper(setCookieArray),
423
- _step;
424
- try {
425
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
426
- var setCookieStr = _step.value;
427
- _this._cookieStore.setFromSetCookie(setCookieStr);
428
- }
429
- } catch (err) {
430
- _iterator.e(err);
431
- } finally {
432
- _iterator.f();
433
- }
434
- }
435
- var _resp2 = {
436
- data: data.data,
437
- status: data.status || _constants.HttpStatus.OK,
438
- statusText: data.statusText || _constants.HttpStatusText[_constants.HttpStatus.OK],
439
- requestId,
440
- headers: data.headers
441
- };
442
- (0, _interceptors.runResponseInterceptors)(_this.interceptors.response, _resp2).then(resolve).catch(reject);
443
- return;
444
- }
445
-
446
- // Received error
447
- if (data.type === _constants.MessageType.ERROR) {
448
- var _data$error, _data$error2;
449
- // Remember server's creatorId as target server ID for future requests
450
- if (data.creatorId && !_this._targetServerId) {
451
- _this._targetServerId = data.creatorId;
452
- }
453
-
454
- // If server requires acknowledgment, send received message
455
- if (data.requireAck) {
456
- _this.server.messageDispatcher.sendMessage(_this.targetWindow, _this.targetOrigin, _constants.MessageType.RECEIVED, requestId, {
457
- path: prefixedPath,
458
- targetId: data.creatorId
459
- });
460
- }
461
- var err = {
462
- message: ((_data$error = data.error) === null || _data$error === void 0 ? void 0 : _data$error.message) || _constants.Messages.REQUEST_FAILED,
463
- code: ((_data$error2 = data.error) === null || _data$error2 === void 0 ? void 0 : _data$error2.code) || _constants.ErrorCode.REQUEST_ERROR,
464
- config: processedConfig,
465
- response: data.status ? {
466
- data: data.data,
467
- status: data.status,
468
- statusText: data.statusText || _constants.Messages.ERROR
469
- } : undefined,
470
- requestId
471
- };
472
- fail(err);
473
- }
474
- }, error => {
475
- fail({
476
- message: error.message || _constants.Messages.REQUEST_FAILED,
477
- code: _constants.ErrorCode.REQUEST_ERROR,
478
- config: processedConfig,
479
- requestId
480
- });
481
- }, _this.targetOrigin);
482
-
483
- // Set ACK timeout
484
- setAckTimeout();
485
-
486
- // Get cookies matching request path and merge with user-provided cookies (user-provided takes precedence)
487
- var pathMatchedCookies = _this._cookieStore.getForPath(processedPath);
488
- var mergedCookies = _objectSpread(_objectSpread({}, pathMatchedCookies), processedCookies);
489
-
490
- // Send request via MessageDispatcher
491
- _this.server.messageDispatcher.sendMessage(_this.targetWindow, _this.targetOrigin, _constants.MessageType.REQUEST, requestId, {
492
- path: prefixedPath,
493
- body: processedBody,
494
- headers: mergedHeaders,
495
- cookies: mergedCookies,
496
- targetId
497
- });
498
- }));
342
+ return next;
343
+ }()
344
+ });
345
+ return _context3.abrupt("return", _this2.sendStream(path, fileStream, options));
499
346
  case 2:
500
347
  case "end":
501
- return _context.stop();
348
+ return _context3.stop();
502
349
  }
503
- }, _callee);
350
+ }, _callee3);
504
351
  }))();
505
352
  }
506
- prefixPath(path) {
507
- return this.secretKey ? `${this.secretKey}:${path}` : path;
353
+
354
+ /**
355
+ * Send stream as request body (server receives readable stream).
356
+ * Sends REQUEST with streamId and stream: true, then starts the writable stream.
357
+ */
358
+ sendStream(path, stream, options) {
359
+ var _this3 = this;
360
+ return (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee4() {
361
+ var _processedConfig$requ2, _processedConfig$targ;
362
+ var config, processedConfig, requestId, targetId, processedPath, mergedHeaders, pathMatchedCookies, mergedCookies, streamConfig, promise;
363
+ return _regenerator.default.wrap(function (_context4) {
364
+ while (1) switch (_context4.prev = _context4.next) {
365
+ case 0:
366
+ config = _objectSpread({
367
+ path,
368
+ body: undefined
369
+ }, options);
370
+ _context4.next = 1;
371
+ return (0, _interceptors.runRequestInterceptors)(_this3.interceptors.request, config);
372
+ case 1:
373
+ processedConfig = _context4.sent;
374
+ requestId = (_processedConfig$requ2 = processedConfig.requestId) !== null && _processedConfig$requ2 !== void 0 ? _processedConfig$requ2 : (0, _utils.generateRequestId)();
375
+ targetId = (_processedConfig$targ = processedConfig.targetId) !== null && _processedConfig$targ !== void 0 ? _processedConfig$targ : _this3._targetServerId;
376
+ processedPath = processedConfig.path;
377
+ stream._bind({
378
+ requestId,
379
+ targetWindow: _this3.targetWindow,
380
+ targetOrigin: _this3.targetOrigin,
381
+ secretKey: _this3.secretKey,
382
+ channel: _this3.server.messageDispatcher.getChannel(),
383
+ clientId: _this3.id,
384
+ targetId
385
+ });
386
+ mergedHeaders = _this3.mergeHeaders(processedConfig, undefined);
387
+ pathMatchedCookies = _this3._cookieStore.getForPath(processedPath);
388
+ mergedCookies = _objectSpread(_objectSpread({}, pathMatchedCookies), processedConfig.cookies);
389
+ streamConfig = _objectSpread(_objectSpread({}, processedConfig), {}, {
390
+ requestId
391
+ });
392
+ promise = _this3._sendRequest(processedPath, undefined, mergedHeaders, mergedCookies, streamConfig, requestId, targetId, {
393
+ streamId: stream.streamId
394
+ });
395
+ /** Start stream after REQUEST is sent (_sendRequest sends synchronously in executor) */
396
+ void stream.start();
397
+ return _context4.abrupt("return", promise);
398
+ case 2:
399
+ case "end":
400
+ return _context4.stop();
401
+ }
402
+ }, _callee4);
403
+ }))();
508
404
  }
509
405
 
406
+ /**
407
+ * Internal: send REQUEST and wait for response (used by send, sendFile, sendStream).
408
+ */
409
+ _sendRequest(requestPath, body, mergedHeaders, processedCookies, processedConfig, requestId, targetId, extraPayload) {
410
+ var _processedConfig$ackT = processedConfig.ackTimeout,
411
+ ackTimeout = _processedConfig$ackT === void 0 ? this.defaultAckTimeout : _processedConfig$ackT,
412
+ _processedConfig$time = processedConfig.timeout,
413
+ timeout = _processedConfig$time === void 0 ? this.defaultTimeout : _processedConfig$time,
414
+ _processedConfig$asyn = processedConfig.asyncTimeout,
415
+ asyncTimeout = _processedConfig$asyn === void 0 ? this.defaultAsyncTimeout : _processedConfig$asyn,
416
+ _processedConfig$retu = processedConfig.returnData,
417
+ returnData = _processedConfig$retu === void 0 ? this.defaultReturnData : _processedConfig$retu;
418
+ return new Promise((resolve, reject) => {
419
+ var done = false;
420
+ var timeoutId = null;
421
+ var cleanup = () => {
422
+ if (timeoutId) clearTimeout(timeoutId);
423
+ this.server._unregisterPendingRequest(requestId);
424
+ };
425
+ var fail = error => {
426
+ if (done) return;
427
+ done = true;
428
+ cleanup();
429
+ // Convert to RequestIframeError instance
430
+ var errorInstance = error instanceof _utils.RequestIframeError ? error : new _utils.RequestIframeError(error);
431
+ // Run response interceptors to allow error logging
432
+ Promise.reject(errorInstance).catch(err => {
433
+ // Run through response interceptors' rejected callbacks
434
+ var promise = Promise.reject(err);
435
+ this.interceptors.response.forEach(interceptor => {
436
+ promise = promise.catch(e => {
437
+ if (interceptor.rejected) {
438
+ return interceptor.rejected(e);
439
+ }
440
+ return Promise.reject(e);
441
+ });
442
+ });
443
+ return promise;
444
+ }).catch(() => {
445
+ // After interceptors, reject with original error
446
+ reject(errorInstance);
447
+ });
448
+ };
449
+ var setAckTimeout = () => {
450
+ if (timeoutId) clearTimeout(timeoutId);
451
+ timeoutId = setTimeout(() => {
452
+ fail(new _utils.RequestIframeError({
453
+ message: (0, _constants.formatMessage)(_constants.Messages.ACK_TIMEOUT, ackTimeout),
454
+ code: _constants.ErrorCode.ACK_TIMEOUT,
455
+ config: processedConfig,
456
+ requestId
457
+ }));
458
+ }, ackTimeout);
459
+ };
460
+ var setRequestTimeout = () => {
461
+ if (timeoutId) clearTimeout(timeoutId);
462
+ timeoutId = setTimeout(() => {
463
+ fail(new _utils.RequestIframeError({
464
+ message: (0, _constants.formatMessage)(_constants.Messages.REQUEST_TIMEOUT, timeout),
465
+ code: _constants.ErrorCode.TIMEOUT,
466
+ config: processedConfig,
467
+ requestId
468
+ }));
469
+ }, timeout);
470
+ };
471
+ var setAsyncTimeout = () => {
472
+ if (timeoutId) clearTimeout(timeoutId);
473
+ timeoutId = setTimeout(() => {
474
+ fail(new _utils.RequestIframeError({
475
+ message: (0, _constants.formatMessage)(_constants.Messages.ASYNC_REQUEST_TIMEOUT, asyncTimeout),
476
+ code: _constants.ErrorCode.ASYNC_TIMEOUT,
477
+ config: processedConfig,
478
+ requestId
479
+ }));
480
+ }, asyncTimeout);
481
+ };
482
+
483
+ // Register to server's pending requests
484
+ this.server._registerPendingRequest(requestId, data => {
485
+ if (done) return;
486
+
487
+ // Received ACK: server has received request
488
+ if (data.type === _constants.MessageType.ACK) {
489
+ // Remember server's creatorId as target server ID for future requests
490
+ if (data.creatorId && !this._targetServerId) {
491
+ this._targetServerId = data.creatorId;
492
+ }
493
+ // Switch to request timeout
494
+ setRequestTimeout();
495
+ return;
496
+ }
497
+
498
+ // Received ASYNC notification: this is an async task
499
+ if (data.type === _constants.MessageType.ASYNC) {
500
+ // Remember server's creatorId as target server ID for future requests
501
+ if (data.creatorId && !this._targetServerId) {
502
+ this._targetServerId = data.creatorId;
503
+ }
504
+ // Switch to async timeout
505
+ setAsyncTimeout();
506
+ return;
507
+ }
508
+
509
+ // Received stream start message
510
+ if (data.type === _constants.MessageType.STREAM_START) {
511
+ var _streamBody$chunked, _streamBody$autoResol;
512
+ done = true;
513
+ cleanup();
514
+ var streamBody = data.body;
515
+ var streamId = streamBody.streamId;
516
+ var streamType = streamBody.type || _constants.StreamType.DATA;
517
+ var streamChunked = (_streamBody$chunked = streamBody.chunked) !== null && _streamBody$chunked !== void 0 ? _streamBody$chunked : true;
518
+ var streamMetadata = streamBody.metadata;
519
+ var autoResolve = (_streamBody$autoResol = streamBody.autoResolve) !== null && _streamBody$autoResol !== void 0 ? _streamBody$autoResol : false;
520
+
521
+ // Create corresponding readable stream based on stream type
522
+ if (streamType === _constants.StreamType.FILE) {
523
+ var _readableStream = new _stream.IframeFileReadableStream(streamId, requestId, this, {
524
+ chunked: streamChunked,
525
+ metadata: streamMetadata,
526
+ filename: streamMetadata === null || streamMetadata === void 0 ? void 0 : streamMetadata.filename,
527
+ mimeType: streamMetadata === null || streamMetadata === void 0 ? void 0 : streamMetadata.mimeType,
528
+ size: streamMetadata === null || streamMetadata === void 0 ? void 0 : streamMetadata.size
529
+ });
530
+
531
+ // If autoResolve is enabled, automatically read and convert to File/Blob
532
+ if (autoResolve) {
533
+ var _data$headers;
534
+ // Extract fileName from headers if available
535
+ var contentDisposition = (_data$headers = data.headers) === null || _data$headers === void 0 ? void 0 : _data$headers[_constants.HttpHeader.CONTENT_DISPOSITION];
536
+ var fileName;
537
+ if (contentDisposition) {
538
+ var disposition = typeof contentDisposition === 'string' ? contentDisposition : contentDisposition[0];
539
+ var filenameMatch = disposition.match(/filename="?([^"]+)"?/i);
540
+ if (filenameMatch) {
541
+ fileName = filenameMatch[1];
542
+ }
543
+ }
544
+ // Fallback to stream metadata if not found in headers
545
+ fileName = fileName || (streamMetadata === null || streamMetadata === void 0 ? void 0 : streamMetadata.filename) || _readableStream.filename;
546
+
547
+ // Use stream's readAsFile or readAsBlob method
548
+ var fileDataPromise = fileName ? _readableStream.readAsFile(fileName) : _readableStream.readAsBlob();
549
+ fileDataPromise.then(fileData => {
550
+ var resp = {
551
+ data: fileData,
552
+ status: data.status || _constants.HttpStatus.OK,
553
+ statusText: data.statusText || _constants.HttpStatusText[_constants.HttpStatus.OK],
554
+ requestId,
555
+ headers: data.headers
556
+ };
557
+ return (0, _interceptors.runResponseInterceptors)(this.interceptors.response, resp);
558
+ }).then(response => {
559
+ resolve(returnData ? response.data : response);
560
+ }).catch(reject);
561
+ return;
562
+ }
563
+
564
+ // Non-autoResolve: return file stream directly
565
+ var _resp = {
566
+ data: undefined,
567
+ status: data.status || _constants.HttpStatus.OK,
568
+ statusText: data.statusText || _constants.HttpStatusText[_constants.HttpStatus.OK],
569
+ requestId,
570
+ headers: data.headers,
571
+ stream: _readableStream
572
+ };
573
+ (0, _interceptors.runResponseInterceptors)(this.interceptors.response, _resp).then(response => {
574
+ resolve(returnData ? response.data : response);
575
+ }).catch(reject);
576
+ return;
577
+ }
578
+
579
+ // Non-file stream: create regular readable stream
580
+ var readableStream = new _stream.IframeReadableStream(streamId, requestId, this, {
581
+ type: streamType,
582
+ chunked: streamChunked,
583
+ metadata: streamMetadata
584
+ });
585
+ var resp = {
586
+ data: undefined,
587
+ status: data.status || _constants.HttpStatus.OK,
588
+ statusText: data.statusText || _constants.HttpStatusText[_constants.HttpStatus.OK],
589
+ requestId,
590
+ headers: data.headers,
591
+ stream: readableStream
592
+ };
593
+ (0, _interceptors.runResponseInterceptors)(this.interceptors.response, resp).then(response => {
594
+ resolve(returnData ? response.data : response);
595
+ }).catch(reject);
596
+ return;
597
+ }
598
+
599
+ // Received stream data/end/error/cancel message - dispatch to stream handler
600
+ if (data.type.startsWith('stream_')) {
601
+ this.dispatchStreamMessage(data);
602
+ return;
603
+ }
604
+
605
+ // Received response
606
+ if (data.type === _constants.MessageType.RESPONSE) {
607
+ done = true;
608
+ cleanup();
609
+
610
+ // Remember server's creatorId as target server ID for future requests
611
+ if (data.creatorId && !this._targetServerId) {
612
+ this._targetServerId = data.creatorId;
613
+ }
614
+
615
+ // If server requires acknowledgment, send received message
616
+ if (data.requireAck) {
617
+ // Check if target window is still available before sending
618
+ if ((0, _utils.isWindowAvailable)(this.targetWindow)) {
619
+ this.server.messageDispatcher.sendMessage(this.targetWindow, this.targetOrigin, _constants.MessageType.RECEIVED, requestId, {
620
+ path: requestPath,
621
+ targetId: data.creatorId
622
+ });
623
+ }
624
+ }
625
+
626
+ // Parse and save server-set cookies (from Set-Cookie header)
627
+ if (data.headers && data.headers[_constants.HttpHeader.SET_COOKIE]) {
628
+ var setCookies = data.headers[_constants.HttpHeader.SET_COOKIE];
629
+ var setCookieArray = Array.isArray(setCookies) ? setCookies : [setCookies];
630
+ var _iterator = _createForOfIteratorHelper(setCookieArray),
631
+ _step;
632
+ try {
633
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
634
+ var setCookieStr = _step.value;
635
+ this._cookieStore.setFromSetCookie(setCookieStr);
636
+ }
637
+ } catch (err) {
638
+ _iterator.e(err);
639
+ } finally {
640
+ _iterator.f();
641
+ }
642
+ }
643
+ var _resp2 = {
644
+ data: data.data,
645
+ status: data.status || _constants.HttpStatus.OK,
646
+ statusText: data.statusText || _constants.HttpStatusText[_constants.HttpStatus.OK],
647
+ requestId,
648
+ headers: data.headers
649
+ };
650
+ (0, _interceptors.runResponseInterceptors)(this.interceptors.response, _resp2).then(response => {
651
+ resolve(returnData ? response.data : response);
652
+ }).catch(reject);
653
+ return;
654
+ }
655
+
656
+ // Received error
657
+ if (data.type === _constants.MessageType.ERROR) {
658
+ var _data$error, _data$error2;
659
+ // Remember server's creatorId as target server ID for future requests
660
+ if (data.creatorId && !this._targetServerId) {
661
+ this._targetServerId = data.creatorId;
662
+ }
663
+
664
+ // If server requires acknowledgment, send received message
665
+ if (data.requireAck) {
666
+ // Window check is handled in MessageDispatcher
667
+ this.server.messageDispatcher.sendMessage(this.targetWindow, this.targetOrigin, _constants.MessageType.RECEIVED, requestId, {
668
+ path: requestPath,
669
+ targetId: data.creatorId
670
+ });
671
+ }
672
+ fail(new _utils.RequestIframeError({
673
+ message: ((_data$error = data.error) === null || _data$error === void 0 ? void 0 : _data$error.message) || _constants.Messages.REQUEST_FAILED,
674
+ code: ((_data$error2 = data.error) === null || _data$error2 === void 0 ? void 0 : _data$error2.code) || _constants.ErrorCode.REQUEST_ERROR,
675
+ config: processedConfig,
676
+ response: data.status ? {
677
+ data: data.data,
678
+ status: data.status,
679
+ statusText: data.statusText || _constants.Messages.ERROR
680
+ } : undefined,
681
+ requestId
682
+ }));
683
+ }
684
+ }, error => {
685
+ fail(new _utils.RequestIframeError({
686
+ message: error.message || _constants.Messages.REQUEST_FAILED,
687
+ code: _constants.ErrorCode.REQUEST_ERROR,
688
+ config: processedConfig,
689
+ requestId
690
+ }));
691
+ }, this.targetOrigin);
692
+
693
+ // Set ACK timeout
694
+ setAckTimeout();
695
+
696
+ // Get cookies matching request path and merge with user-provided cookies (user-provided takes precedence)
697
+ var pathMatchedCookies = this._cookieStore.getForPath(requestPath);
698
+ var mergedCookies = _objectSpread(_objectSpread({}, pathMatchedCookies), processedCookies);
699
+
700
+ // Send request via MessageDispatcher
701
+ var payload = {
702
+ path: requestPath,
703
+ body,
704
+ headers: mergedHeaders,
705
+ cookies: mergedCookies,
706
+ targetId
707
+ };
708
+ if (extraPayload !== null && extraPayload !== void 0 && extraPayload.streamId) {
709
+ payload.streamId = extraPayload.streamId;
710
+ }
711
+
712
+ // Check if target window is still available before sending
713
+ if (!(0, _utils.isWindowAvailable)(this.targetWindow)) {
714
+ fail(new _utils.RequestIframeError({
715
+ message: _constants.Messages.TARGET_WINDOW_CLOSED,
716
+ code: _constants.ErrorCode.TARGET_WINDOW_CLOSED,
717
+ config: processedConfig,
718
+ requestId
719
+ }));
720
+ return;
721
+ }
722
+ this.server.messageDispatcher.sendMessage(this.targetWindow, this.targetOrigin, _constants.MessageType.REQUEST, requestId, payload);
723
+ });
724
+ }
510
725
  /**
511
726
  * Get internal server instance (for debugging)
512
727
  */
@@ -521,6 +736,14 @@ class RequestIframeClientImpl {
521
736
  return this.server.isOpen;
522
737
  }
523
738
 
739
+ /**
740
+ * Check if target window is still available (not closed/removed)
741
+ * @returns true if target window is available, false otherwise
742
+ */
743
+ isAvailable() {
744
+ return (0, _utils.isWindowAvailable)(this.targetWindow);
745
+ }
746
+
524
747
  /**
525
748
  * Enable message handling (register message handlers)
526
749
  */