request-iframe 0.2.0 → 0.2.2

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 (93) hide show
  1. package/QUICKSTART.CN.md +33 -11
  2. package/QUICKSTART.md +33 -11
  3. package/README.CN.md +157 -44
  4. package/README.md +159 -41
  5. package/cdn/request-iframe.umd.js +4814 -4026
  6. package/cdn/request-iframe.umd.js.map +1 -1
  7. package/cdn/request-iframe.umd.min.js +2 -2
  8. package/cdn/request-iframe.umd.min.js.map +1 -1
  9. package/esm/api/client.js +45 -22
  10. package/esm/api/endpoint.js +30 -13
  11. package/esm/api/server.js +22 -13
  12. package/esm/constants/warn-once.js +7 -1
  13. package/esm/endpoint/index.js +1 -2
  14. package/esm/endpoint/infra/inbox.js +5 -4
  15. package/esm/endpoint/infra/outbox.js +8 -8
  16. package/esm/endpoint/stream/file-auto-resolve.js +9 -8
  17. package/esm/impl/client.js +3 -2
  18. package/esm/impl/response.js +4 -2
  19. package/esm/impl/server.js +8 -6
  20. package/esm/message/channel.js +15 -3
  21. package/esm/message/dispatcher.js +27 -0
  22. package/esm/stream/file-stream.js +311 -72
  23. package/esm/stream/writable-stream.js +21 -4
  24. package/esm/utils/blob.js +17 -0
  25. package/esm/utils/debug-lazy.js +76 -0
  26. package/esm/utils/logger.js +33 -1
  27. package/esm/utils/strict-mode.js +85 -0
  28. package/esm/utils/warn-once.js +30 -0
  29. package/esm/utils/warnings.js +47 -0
  30. package/library/api/client.d.ts.map +1 -1
  31. package/library/api/client.js +45 -22
  32. package/library/api/endpoint.d.ts.map +1 -1
  33. package/library/api/endpoint.js +30 -13
  34. package/library/api/server.d.ts.map +1 -1
  35. package/library/api/server.js +22 -13
  36. package/library/constants/warn-once.d.ts +6 -0
  37. package/library/constants/warn-once.d.ts.map +1 -1
  38. package/library/constants/warn-once.js +7 -1
  39. package/library/endpoint/index.d.ts +0 -1
  40. package/library/endpoint/index.d.ts.map +1 -1
  41. package/library/endpoint/index.js +1 -8
  42. package/library/endpoint/infra/inbox.d.ts.map +1 -1
  43. package/library/endpoint/infra/inbox.js +4 -3
  44. package/library/endpoint/infra/outbox.d.ts +2 -0
  45. package/library/endpoint/infra/outbox.d.ts.map +1 -1
  46. package/library/endpoint/infra/outbox.js +7 -7
  47. package/library/endpoint/stream/file-auto-resolve.d.ts +1 -1
  48. package/library/endpoint/stream/file-auto-resolve.d.ts.map +1 -1
  49. package/library/endpoint/stream/file-auto-resolve.js +8 -8
  50. package/library/impl/client.d.ts +2 -0
  51. package/library/impl/client.d.ts.map +1 -1
  52. package/library/impl/client.js +3 -2
  53. package/library/impl/response.d.ts.map +1 -1
  54. package/library/impl/response.js +4 -2
  55. package/library/impl/server.d.ts.map +1 -1
  56. package/library/impl/server.js +7 -5
  57. package/library/message/channel.d.ts +2 -2
  58. package/library/message/channel.d.ts.map +1 -1
  59. package/library/message/channel.js +15 -3
  60. package/library/message/dispatcher.d.ts.map +1 -1
  61. package/library/message/dispatcher.js +27 -0
  62. package/library/stream/file-stream.d.ts +70 -5
  63. package/library/stream/file-stream.d.ts.map +1 -1
  64. package/library/stream/file-stream.js +310 -70
  65. package/library/stream/types.d.ts +2 -0
  66. package/library/stream/types.d.ts.map +1 -1
  67. package/library/stream/writable-stream.d.ts.map +1 -1
  68. package/library/stream/writable-stream.js +21 -4
  69. package/library/types/index.d.ts +38 -0
  70. package/library/types/index.d.ts.map +1 -1
  71. package/library/utils/blob.d.ts +7 -0
  72. package/library/utils/blob.d.ts.map +1 -1
  73. package/library/utils/blob.js +18 -0
  74. package/library/utils/debug-lazy.d.ts +26 -0
  75. package/library/utils/debug-lazy.d.ts.map +1 -0
  76. package/library/utils/debug-lazy.js +85 -0
  77. package/library/utils/logger.d.ts +20 -0
  78. package/library/utils/logger.d.ts.map +1 -1
  79. package/library/utils/logger.js +34 -1
  80. package/library/utils/strict-mode.d.ts +37 -0
  81. package/library/utils/strict-mode.d.ts.map +1 -0
  82. package/library/utils/strict-mode.js +94 -0
  83. package/library/utils/warn-once.d.ts +9 -0
  84. package/library/utils/warn-once.d.ts.map +1 -0
  85. package/library/utils/warn-once.js +36 -0
  86. package/library/utils/warnings.d.ts +48 -0
  87. package/library/utils/warnings.d.ts.map +1 -0
  88. package/library/utils/warnings.js +54 -0
  89. package/package.json +1 -1
  90. package/esm/endpoint/stream/file-writable.js +0 -105
  91. package/library/endpoint/stream/file-writable.d.ts +0 -33
  92. package/library/endpoint/stream/file-writable.d.ts.map +0 -1
  93. package/library/endpoint/stream/file-writable.js +0 -115
package/esm/api/client.js CHANGED
@@ -1,10 +1,12 @@
1
1
  import { getIframeTargetOrigin } from '../utils/iframe';
2
2
  import { generateInstanceId } from '../utils/id';
3
3
  import { RequestIframeClientImpl } from '../impl/client';
4
- import { setupClientDebugInterceptors } from '../utils/debug';
5
4
  import { setRequestIframeLogLevel } from '../utils/logger';
6
5
  import { Messages, ErrorCode, OriginConstant, LogLevel } from '../constants';
7
6
  import { clearMessageChannelCache } from '../message/channel-cache';
7
+ import { warnUnsafeTargetOriginForWindow } from '../utils/warnings';
8
+ import { ensureClientDebugInterceptors, loadDebugModule, wrapClientMethodsForDebug } from '../utils/debug-lazy';
9
+ import { applyStrictClientSecurityDefaults } from '../utils/strict-mode';
8
10
 
9
11
  /**
10
12
  * Create a client (for sending requests)
@@ -15,12 +17,13 @@ import { clearMessageChannelCache } from '../message/channel-cache';
15
17
  * - This allows different versions of the library to coexist
16
18
  */
17
19
  export function requestIframeClient(target, options) {
20
+ var _resolved$options;
18
21
  var targetWindow = null;
19
- var targetOrigin = OriginConstant.ANY;
22
+ var defaultTargetOrigin = OriginConstant.ANY;
20
23
  if (target.tagName === 'IFRAME') {
21
24
  var iframe = target;
22
25
  targetWindow = iframe.contentWindow;
23
- targetOrigin = getIframeTargetOrigin(iframe);
26
+ defaultTargetOrigin = getIframeTargetOrigin(iframe);
24
27
  if (!targetWindow) {
25
28
  throw {
26
29
  message: Messages.IFRAME_NOT_READY,
@@ -29,16 +32,26 @@ export function requestIframeClient(target, options) {
29
32
  }
30
33
  } else {
31
34
  targetWindow = target;
32
- targetOrigin = OriginConstant.ANY;
35
+ defaultTargetOrigin = OriginConstant.ANY;
33
36
  }
37
+ var resolved = applyStrictClientSecurityDefaults(defaultTargetOrigin, options);
38
+ var targetOrigin = resolved.targetOrigin;
39
+ var resolvedOptions = (_resolved$options = resolved.options) !== null && _resolved$options !== void 0 ? _resolved$options : options;
34
40
 
35
- // Allow user to override targetOrigin explicitly
36
- if (options !== null && options !== void 0 && options.targetOrigin) {
37
- targetOrigin = options.targetOrigin;
38
- }
41
+ /**
42
+ * P1: warn on unsafe default targetOrigin for Window targets.
43
+ * - If targetOrigin is '*' and user did not configure allowedOrigins/validateOrigin,
44
+ * incoming message origin validation is effectively disabled.
45
+ */
46
+ warnUnsafeTargetOriginForWindow({
47
+ isIframeTarget: target.tagName === 'IFRAME',
48
+ targetOrigin,
49
+ allowedOrigins: resolvedOptions === null || resolvedOptions === void 0 ? void 0 : resolvedOptions.allowedOrigins,
50
+ validateOrigin: resolvedOptions === null || resolvedOptions === void 0 ? void 0 : resolvedOptions.validateOrigin
51
+ });
39
52
 
40
53
  // Determine secretKey
41
- var secretKey = options === null || options === void 0 ? void 0 : options.secretKey;
54
+ var secretKey = resolvedOptions === null || resolvedOptions === void 0 ? void 0 : resolvedOptions.secretKey;
42
55
 
43
56
  // Generate instance ID first (will be used by both client and server)
44
57
  var instanceId = generateInstanceId();
@@ -46,16 +59,16 @@ export function requestIframeClient(target, options) {
46
59
  // Create client instance (internally creates its core message server)
47
60
  var client = new RequestIframeClientImpl(targetWindow, targetOrigin, {
48
61
  secretKey,
49
- ackTimeout: options === null || options === void 0 ? void 0 : options.ackTimeout,
50
- timeout: options === null || options === void 0 ? void 0 : options.timeout,
51
- asyncTimeout: options === null || options === void 0 ? void 0 : options.asyncTimeout,
52
- returnData: options === null || options === void 0 ? void 0 : options.returnData,
53
- headers: options === null || options === void 0 ? void 0 : options.headers,
54
- allowedOrigins: options === null || options === void 0 ? void 0 : options.allowedOrigins,
55
- validateOrigin: options === null || options === void 0 ? void 0 : options.validateOrigin,
56
- autoOpen: options === null || options === void 0 ? void 0 : options.autoOpen,
57
- autoAckMaxMetaLength: options === null || options === void 0 ? void 0 : options.autoAckMaxMetaLength,
58
- autoAckMaxIdLength: options === null || options === void 0 ? void 0 : options.autoAckMaxIdLength
62
+ ackTimeout: resolvedOptions === null || resolvedOptions === void 0 ? void 0 : resolvedOptions.ackTimeout,
63
+ timeout: resolvedOptions === null || resolvedOptions === void 0 ? void 0 : resolvedOptions.timeout,
64
+ asyncTimeout: resolvedOptions === null || resolvedOptions === void 0 ? void 0 : resolvedOptions.asyncTimeout,
65
+ returnData: resolvedOptions === null || resolvedOptions === void 0 ? void 0 : resolvedOptions.returnData,
66
+ headers: resolvedOptions === null || resolvedOptions === void 0 ? void 0 : resolvedOptions.headers,
67
+ allowedOrigins: resolvedOptions === null || resolvedOptions === void 0 ? void 0 : resolvedOptions.allowedOrigins,
68
+ validateOrigin: resolvedOptions === null || resolvedOptions === void 0 ? void 0 : resolvedOptions.validateOrigin,
69
+ autoOpen: resolvedOptions === null || resolvedOptions === void 0 ? void 0 : resolvedOptions.autoOpen,
70
+ autoAckMaxMetaLength: resolvedOptions === null || resolvedOptions === void 0 ? void 0 : resolvedOptions.autoAckMaxMetaLength,
71
+ autoAckMaxIdLength: resolvedOptions === null || resolvedOptions === void 0 ? void 0 : resolvedOptions.autoAckMaxIdLength
59
72
  }, instanceId);
60
73
 
61
74
  /**
@@ -63,11 +76,21 @@ export function requestIframeClient(target, options) {
63
76
  * - default: only warn/error will be printed (logger default)
64
77
  * - if trace enabled: raise log level and (optionally) enable detailed debug interceptors
65
78
  */
66
- if (options !== null && options !== void 0 && options.trace) {
67
- var level = options.trace === true ? LogLevel.TRACE : options.trace;
79
+ if (resolvedOptions !== null && resolvedOptions !== void 0 && resolvedOptions.trace) {
80
+ var level = resolvedOptions.trace === true ? LogLevel.TRACE : resolvedOptions.trace;
68
81
  setRequestIframeLogLevel(level);
69
82
  if (level === LogLevel.TRACE || level === LogLevel.INFO) {
70
- setupClientDebugInterceptors(client);
83
+ /**
84
+ * Lazy-load debug hooks to keep main bundle smaller, but still ensure
85
+ * the first request in trace mode won't miss debug interceptors.
86
+ */
87
+ wrapClientMethodsForDebug(client);
88
+ // Preheat import early (best-effort)
89
+ void loadDebugModule().catch(() => {
90
+ /** ignore */
91
+ });
92
+ // Attach ASAP (best-effort)
93
+ void ensureClientDebugInterceptors(client);
71
94
  }
72
95
  }
73
96
  return client;
@@ -5,9 +5,11 @@ import { generateInstanceId } from '../utils/id';
5
5
  import { isWindowAvailable } from '../utils/window';
6
6
  import { RequestIframeClientImpl } from '../impl/client';
7
7
  import { RequestIframeServerImpl } from '../impl/server';
8
- import { setupClientDebugInterceptors, setupServerDebugListeners } from '../utils/debug';
9
8
  import { setRequestIframeLogLevel } from '../utils/logger';
10
9
  import { Messages, ErrorCode, OriginConstant, LogLevel } from '../constants';
10
+ import { warnUnsafeTargetOriginForWindow } from '../utils/warnings';
11
+ import { ensureClientDebugInterceptors, loadDebugModule, wrapClientMethodsForDebug } from '../utils/debug-lazy';
12
+ import { applyStrictClientSecurityDefaults, applyStrictServerSecurityDefaults } from '../utils/strict-mode';
11
13
 
12
14
  /**
13
15
  * Endpoint facade type (client + server).
@@ -71,7 +73,11 @@ class RequestIframeEndpointApiFacade {
71
73
  var level = options.trace === true ? LogLevel.TRACE : options.trace;
72
74
  setRequestIframeLogLevel(level);
73
75
  if (level === LogLevel.TRACE || level === LogLevel.INFO) {
74
- setupClientDebugInterceptors(client);
76
+ wrapClientMethodsForDebug(client);
77
+ void loadDebugModule().catch(() => {
78
+ /** ignore */
79
+ });
80
+ void ensureClientDebugInterceptors(client);
75
81
  }
76
82
  }
77
83
  this.client_ = client;
@@ -106,7 +112,9 @@ class RequestIframeEndpointApiFacade {
106
112
  var level = options.trace === true ? LogLevel.TRACE : options.trace;
107
113
  setRequestIframeLogLevel(level);
108
114
  if (level === LogLevel.TRACE || level === LogLevel.INFO) {
109
- setupServerDebugListeners(server);
115
+ void loadDebugModule().then(m => m.setupServerDebugListeners(server)).catch(() => {
116
+ /** ignore */
117
+ });
110
118
  }
111
119
  }
112
120
  this.server_ = server;
@@ -192,13 +200,13 @@ class RequestIframeEndpointApiFacade {
192
200
  * - handle requests from the peer (server)
193
201
  */
194
202
  export function requestIframeEndpoint(target, options) {
195
- var _options$id;
203
+ var _applyStrictServerSec, _resolvedClient$optio, _resolvedClient$optio2, _resolvedOptions$id;
196
204
  var targetWindow = null;
197
- var targetOrigin = OriginConstant.ANY;
205
+ var defaultTargetOrigin = OriginConstant.ANY;
198
206
  if (target.tagName === 'IFRAME') {
199
207
  var iframe = target;
200
208
  targetWindow = iframe.contentWindow;
201
- targetOrigin = getIframeTargetOrigin(iframe);
209
+ defaultTargetOrigin = getIframeTargetOrigin(iframe);
202
210
  if (!targetWindow) {
203
211
  throw {
204
212
  message: Messages.IFRAME_NOT_READY,
@@ -207,23 +215,32 @@ export function requestIframeEndpoint(target, options) {
207
215
  }
208
216
  } else {
209
217
  targetWindow = target;
210
- targetOrigin = OriginConstant.ANY;
218
+ defaultTargetOrigin = OriginConstant.ANY;
211
219
  }
220
+ var resolvedClient = applyStrictClientSecurityDefaults(defaultTargetOrigin, options);
221
+ var targetOrigin = resolvedClient.targetOrigin;
222
+ var resolvedOptions = (_applyStrictServerSec = applyStrictServerSecurityDefaults((_resolvedClient$optio = resolvedClient.options) !== null && _resolvedClient$optio !== void 0 ? _resolvedClient$optio : options)) !== null && _applyStrictServerSec !== void 0 ? _applyStrictServerSec : (_resolvedClient$optio2 = resolvedClient.options) !== null && _resolvedClient$optio2 !== void 0 ? _resolvedClient$optio2 : options;
212
223
 
213
- /** Allow user to override targetOrigin explicitly */
214
- if (options !== null && options !== void 0 && options.targetOrigin) {
215
- targetOrigin = options.targetOrigin;
216
- }
224
+ /**
225
+ * P1: warn on unsafe default targetOrigin for Window targets.
226
+ * Endpoint facade is lazy (client/server created later), so warn here.
227
+ */
228
+ warnUnsafeTargetOriginForWindow({
229
+ isIframeTarget: target.tagName === 'IFRAME',
230
+ targetOrigin,
231
+ allowedOrigins: resolvedOptions === null || resolvedOptions === void 0 ? void 0 : resolvedOptions.allowedOrigins,
232
+ validateOrigin: resolvedOptions === null || resolvedOptions === void 0 ? void 0 : resolvedOptions.validateOrigin
233
+ });
217
234
 
218
235
  /**
219
236
  * Endpoint uses ONE shared id by default, so it behaves like a single endpoint.
220
237
  * If options.id is provided, it becomes the shared id for both client+server.
221
238
  */
222
- var endpointId = (_options$id = options === null || options === void 0 ? void 0 : options.id) !== null && _options$id !== void 0 ? _options$id : generateInstanceId();
239
+ var endpointId = (_resolvedOptions$id = resolvedOptions === null || resolvedOptions === void 0 ? void 0 : resolvedOptions.id) !== null && _resolvedOptions$id !== void 0 ? _resolvedOptions$id : generateInstanceId();
223
240
  return new RequestIframeEndpointApiFacade({
224
241
  targetWindow,
225
242
  targetOrigin,
226
- options,
243
+ options: resolvedOptions,
227
244
  endpointId
228
245
  });
229
246
  }
package/esm/api/server.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import { RequestIframeServerImpl } from '../impl/server';
2
- import { setupServerDebugListeners } from '../utils/debug';
3
2
  import { setRequestIframeLogLevel } from '../utils/logger';
4
3
  import { getCachedServer, cacheServer, clearServerCache } from '../utils/cache';
5
4
  import { LogLevel } from '../constants';
5
+ import { loadDebugModule } from '../utils/debug-lazy';
6
+ import { applyStrictServerSecurityDefaults } from '../utils/strict-mode';
6
7
 
7
8
  /**
8
9
  * Create a server (for receiving and handling requests)
@@ -14,9 +15,11 @@ import { LogLevel } from '../constants';
14
15
  * - This allows different versions of the library to coexist
15
16
  */
16
17
  export function requestIframeServer(options) {
18
+ var _applyStrictServerSec;
19
+ var resolvedOptions = (_applyStrictServerSec = applyStrictServerSecurityDefaults(options)) !== null && _applyStrictServerSec !== void 0 ? _applyStrictServerSec : options;
17
20
  // Determine secretKey and id
18
- var secretKey = options === null || options === void 0 ? void 0 : options.secretKey;
19
- var id = options === null || options === void 0 ? void 0 : options.id;
21
+ var secretKey = resolvedOptions === null || resolvedOptions === void 0 ? void 0 : resolvedOptions.secretKey;
22
+ var id = resolvedOptions === null || resolvedOptions === void 0 ? void 0 : resolvedOptions.id;
20
23
 
21
24
  // If id is specified, check cache first
22
25
  if (id) {
@@ -30,13 +33,13 @@ export function requestIframeServer(options) {
30
33
  var server = new RequestIframeServerImpl({
31
34
  secretKey,
32
35
  id,
33
- ackTimeout: options === null || options === void 0 ? void 0 : options.ackTimeout,
34
- autoOpen: options === null || options === void 0 ? void 0 : options.autoOpen,
35
- allowedOrigins: options === null || options === void 0 ? void 0 : options.allowedOrigins,
36
- validateOrigin: options === null || options === void 0 ? void 0 : options.validateOrigin,
37
- maxConcurrentRequestsPerClient: options === null || options === void 0 ? void 0 : options.maxConcurrentRequestsPerClient,
38
- autoAckMaxMetaLength: options === null || options === void 0 ? void 0 : options.autoAckMaxMetaLength,
39
- autoAckMaxIdLength: options === null || options === void 0 ? void 0 : options.autoAckMaxIdLength
36
+ ackTimeout: resolvedOptions === null || resolvedOptions === void 0 ? void 0 : resolvedOptions.ackTimeout,
37
+ autoOpen: resolvedOptions === null || resolvedOptions === void 0 ? void 0 : resolvedOptions.autoOpen,
38
+ allowedOrigins: resolvedOptions === null || resolvedOptions === void 0 ? void 0 : resolvedOptions.allowedOrigins,
39
+ validateOrigin: resolvedOptions === null || resolvedOptions === void 0 ? void 0 : resolvedOptions.validateOrigin,
40
+ maxConcurrentRequestsPerClient: resolvedOptions === null || resolvedOptions === void 0 ? void 0 : resolvedOptions.maxConcurrentRequestsPerClient,
41
+ autoAckMaxMetaLength: resolvedOptions === null || resolvedOptions === void 0 ? void 0 : resolvedOptions.autoAckMaxMetaLength,
42
+ autoAckMaxIdLength: resolvedOptions === null || resolvedOptions === void 0 ? void 0 : resolvedOptions.autoAckMaxIdLength
40
43
  });
41
44
 
42
45
  /**
@@ -44,11 +47,17 @@ export function requestIframeServer(options) {
44
47
  * - default: only warn/error will be printed (logger default)
45
48
  * - if trace enabled: raise log level and (optionally) enable detailed debug listeners
46
49
  */
47
- if (options !== null && options !== void 0 && options.trace) {
48
- var level = options.trace === true ? LogLevel.TRACE : options.trace;
50
+ if (resolvedOptions !== null && resolvedOptions !== void 0 && resolvedOptions.trace) {
51
+ var level = resolvedOptions.trace === true ? LogLevel.TRACE : resolvedOptions.trace;
49
52
  setRequestIframeLogLevel(level);
50
53
  if (level === LogLevel.TRACE || level === LogLevel.INFO) {
51
- setupServerDebugListeners(server);
54
+ /**
55
+ * Lazy-load debug hooks to keep main bundle smaller.
56
+ * Best-effort: ignore dynamic import errors.
57
+ */
58
+ void loadDebugModule().then(m => m.setupServerDebugListeners(server)).catch(() => {
59
+ /** ignore */
60
+ });
52
61
  }
53
62
  }
54
63
 
@@ -7,7 +7,13 @@ import "core-js/modules/es.array.map.js";
7
7
 
8
8
  export var WarnOnceKey = {
9
9
  INBOX_MISSING_PENDING_WHEN_CLOSED: 'inbox:missingPendingWhenClosed',
10
- SERVER_MISSING_PENDING_WHEN_CLOSED: 'server:missingPendingWhenClosed'
10
+ SERVER_MISSING_PENDING_WHEN_CLOSED: 'server:missingPendingWhenClosed',
11
+ /**
12
+ * Security warning:
13
+ * - targetOrigin is '*'
14
+ * - and no allowedOrigins/validateOrigin is configured
15
+ */
16
+ TARGET_ORIGIN_ANY_WITHOUT_ORIGIN_VALIDATION: 'targetOrigin:anyWithoutOriginValidation'
11
17
  };
12
18
  export function buildWarnOnceKey(prefix, ...parts) {
13
19
  if (!parts.length) return prefix;
@@ -9,5 +9,4 @@ export { parseStreamStart, createReadableStreamFromStart } from './stream/factor
9
9
  export { RequestIframeEndpointFacade } from './facade';
10
10
  export { createStreamMessageHandler } from './stream/handler';
11
11
  export { buildStreamStartTimeoutErrorPayload } from './stream/errors';
12
- export { autoResolveIframeFileReadableStream, parseFilenameFromContentDisposition } from './stream/file-auto-resolve';
13
- export { createIframeFileWritableStreamFromContent } from './stream/file-writable';
12
+ export { autoResolveIframeFileReadableStream, parseFilenameFromContentDisposition } from './stream/file-auto-resolve';
@@ -2,10 +2,10 @@ import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
2
2
  import "core-js/modules/es.array.slice.js";
3
3
  import "core-js/modules/es.promise.js";
4
4
  import "core-js/modules/es.regexp.to-string.js";
5
- import { MessageType, ProtocolVersion, Messages, formatMessage, MessageRole, WarnOnceKey, buildWarnOnceKey } from '../../constants';
5
+ import { MessageType, ProtocolVersion, Messages, formatMessage, MessageRole } from '../../constants';
6
6
  import { createPingResponder } from '../heartbeat/ping';
7
7
  import { SyncHook } from '../../utils/hooks';
8
- import { requestIframeLog } from '../../utils/logger';
8
+ import { warnClientServerIgnoredMessageWhenClosedOnce } from '../../utils/warnings';
9
9
 
10
10
  /**
11
11
  * Pending request awaiting response
@@ -162,8 +162,9 @@ export class RequestIframeEndpointInbox {
162
162
  */
163
163
  if (!this.hub.isOpen) {
164
164
  this.hooks.missingPending.call(data, context);
165
- this.hub.warnOnce(buildWarnOnceKey(WarnOnceKey.INBOX_MISSING_PENDING_WHEN_CLOSED, data.requestId), () => {
166
- requestIframeLog('warn', formatMessage(Messages.CLIENT_SERVER_IGNORED_MESSAGE_WHEN_CLOSED, data.type, data.requestId));
165
+ warnClientServerIgnoredMessageWhenClosedOnce(this.hub, {
166
+ type: data.type,
167
+ requestId: data.requestId
167
168
  });
168
169
  }
169
170
  return;
@@ -8,8 +8,7 @@ import "core-js/modules/es.object.get-own-property-descriptors.js";
8
8
  import "core-js/modules/es.promise.js";
9
9
  import "core-js/modules/es.promise.finally.js";
10
10
  import "core-js/modules/web.dom-collections.for-each.js";
11
- import { isIframeWritableStream } from '../../stream';
12
- import { createIframeFileWritableStreamFromContent } from '../stream/file-writable';
11
+ import { IframeFileWritableStream, isIframeWritableStream } from '../../stream';
13
12
  import { SyncHook } from '../../utils/hooks';
14
13
 
15
14
  /**
@@ -330,31 +329,32 @@ export class RequestIframeEndpointOutbox {
330
329
  case 0:
331
330
  return _context9.abrupt("return", _this5.runWithHooks(params, /*#__PURE__*/_asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee8() {
332
331
  var _params$onFileInfo;
333
- var created;
332
+ var stream;
334
333
  return _regeneratorRuntime.wrap(function (_context8) {
335
334
  while (1) switch (_context8.prev = _context8.next) {
336
335
  case 0:
337
336
  _context8.next = 1;
338
- return createIframeFileWritableStreamFromContent({
337
+ return IframeFileWritableStream.from({
339
338
  content: params.content,
340
339
  fileName: params.fileName,
341
340
  mimeType: params.mimeType,
342
341
  chunked: params.chunked,
342
+ chunkSize: params.chunkSize,
343
343
  autoResolve: params.autoResolve,
344
344
  defaultFileName: params.defaultFileName,
345
345
  defaultMimeType: params.defaultMimeType
346
346
  });
347
347
  case 1:
348
- created = _context8.sent;
348
+ stream = _context8.sent;
349
349
  _context8.next = 2;
350
350
  return (_params$onFileInfo = params.onFileInfo) === null || _params$onFileInfo === void 0 ? void 0 : _params$onFileInfo.call(params, {
351
- fileName: created.fileName,
352
- mimeType: created.mimeType
351
+ fileName: stream.filename,
352
+ mimeType: stream.mimeType
353
353
  });
354
354
  case 2:
355
355
  _context8.next = 3;
356
356
  return _this5.runStreamSend(_objectSpread(_objectSpread({}, params.stream), {}, {
357
- stream: created.stream
357
+ stream: stream
358
358
  }));
359
359
  case 3:
360
360
  return _context8.abrupt("return", _context8.sent);
@@ -1,6 +1,6 @@
1
- import "core-js/modules/es.regexp.exec.js";
2
- import "core-js/modules/es.string.match.js";
3
1
  import { HttpHeader } from '../../constants';
2
+ import { IframeFileReadableStream } from '../../stream';
3
+
4
4
  /**
5
5
  * Endpoint Stream integration layer (`src/endpoint/stream`)
6
6
  *
@@ -11,11 +11,7 @@ import { HttpHeader } from '../../constants';
11
11
  * Parse filename from Content-Disposition header.
12
12
  */
13
13
  export function parseFilenameFromContentDisposition(value) {
14
- if (!value) return undefined;
15
- var disposition = typeof value === 'string' ? value : value[0];
16
- if (!disposition) return undefined;
17
- var match = disposition.match(/filename="?([^"]+)"?/i);
18
- return match ? match[1] : undefined;
14
+ return IframeFileReadableStream.parseFilenameFromContentDisposition(value);
19
15
  }
20
16
 
21
17
  /**
@@ -30,5 +26,10 @@ export function autoResolveIframeFileReadableStream(params) {
30
26
  var _params$headers, _params$info;
31
27
  var headerFilename = parseFilenameFromContentDisposition((_params$headers = params.headers) === null || _params$headers === void 0 ? void 0 : _params$headers[HttpHeader.CONTENT_DISPOSITION]);
32
28
  var fileName = headerFilename || ((_params$info = params.info) === null || _params$info === void 0 || (_params$info = _params$info.metadata) === null || _params$info === void 0 ? void 0 : _params$info.filename) || params.fileStream.filename;
33
- return fileName ? params.fileStream.readAsFile(fileName) : params.fileStream.readAsBlob();
29
+ var anyStream = params.fileStream;
30
+ if (typeof anyStream.readAsFileOrBlob === 'function') {
31
+ return anyStream.readAsFileOrBlob(fileName);
32
+ }
33
+ // Backward-compatible fallback for mocks/older stream objects
34
+ return fileName ? anyStream.readAsFile(fileName) : anyStream.readAsBlob();
34
35
  }
@@ -296,7 +296,7 @@ export class RequestIframeClientImpl {
296
296
  sendFile(path, content, options) {
297
297
  var _this2 = this;
298
298
  return _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
299
- var _processedConfig$requ2, _processedConfig$targ, _options$autoResolve;
299
+ var _processedConfig$requ2, _processedConfig$targ, _options$chunked, _options$autoResolve;
300
300
  var config, processedConfig, requestId, targetId, processedPath, mergedHeaders, pathMatchedCookies, mergedCookies, streamConfig;
301
301
  return _regeneratorRuntime.wrap(function (_context2) {
302
302
  while (1) switch (_context2.prev = _context2.next) {
@@ -322,7 +322,8 @@ export class RequestIframeClientImpl {
322
322
  content,
323
323
  fileName: options === null || options === void 0 ? void 0 : options.fileName,
324
324
  mimeType: options === null || options === void 0 ? void 0 : options.mimeType,
325
- chunked: false,
325
+ chunked: (_options$chunked = options === null || options === void 0 ? void 0 : options.chunked) !== null && _options$chunked !== void 0 ? _options$chunked : false,
326
+ chunkSize: options === null || options === void 0 ? void 0 : options.chunkSize,
326
327
  autoResolve: (_options$autoResolve = options === null || options === void 0 ? void 0 : options.autoResolve) !== null && _options$autoResolve !== void 0 ? _options$autoResolve : true,
327
328
  defaultFileName: typeof File !== 'undefined' && content instanceof File ? content.name : 'file',
328
329
  defaultMimeType: 'application/octet-stream',
@@ -250,6 +250,7 @@ export class ServerResponseImpl {
250
250
  sendFile(content, options) {
251
251
  var _this2 = this;
252
252
  return _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee3() {
253
+ var _options$chunked, _options$autoResolve;
253
254
  return _regeneratorRuntime.wrap(function (_context3) {
254
255
  while (1) switch (_context3.prev = _context3.next) {
255
256
  case 0:
@@ -264,8 +265,9 @@ export class ServerResponseImpl {
264
265
  content,
265
266
  fileName: options === null || options === void 0 ? void 0 : options.fileName,
266
267
  mimeType: options === null || options === void 0 ? void 0 : options.mimeType,
267
- chunked: false,
268
- autoResolve: true,
268
+ chunked: (_options$chunked = options === null || options === void 0 ? void 0 : options.chunked) !== null && _options$chunked !== void 0 ? _options$chunked : false,
269
+ chunkSize: options === null || options === void 0 ? void 0 : options.chunkSize,
270
+ autoResolve: (_options$autoResolve = options === null || options === void 0 ? void 0 : options.autoResolve) !== null && _options$autoResolve !== void 0 ? _options$autoResolve : true,
269
271
  defaultFileName: 'file',
270
272
  defaultMimeType: 'application/octet-stream',
271
273
  onFileInfo: ({
@@ -22,10 +22,10 @@ import { ServerRequestImpl } from './request';
22
22
  import { ServerResponseImpl } from './response';
23
23
  import { generateInstanceId } from '../utils/id';
24
24
  import { RequestIframeEndpointFacade, buildStreamStartTimeoutErrorPayload, autoResolveIframeFileReadableStream } from '../endpoint';
25
- import { MessageType, ErrorCode, HttpStatus, HttpStatusText, Messages, DefaultTimeout, ProtocolVersion, formatMessage, MessageRole, StreamType as StreamTypeConstant, WarnOnceKey, buildWarnOnceKey } from '../constants';
25
+ import { MessageType, ErrorCode, HttpStatus, HttpStatusText, Messages, formatMessage, DefaultTimeout, ProtocolVersion, MessageRole, StreamType as StreamTypeConstant } from '../constants';
26
26
  import { isPromise } from '../utils/promise';
27
27
  import { isFunction } from '../utils/is';
28
- import { requestIframeLog } from '../utils/logger';
28
+ import { warnServerIgnoredMessageWhenClosedOnce } from '../utils/warnings';
29
29
 
30
30
  /**
31
31
  * Middleware item (contains path matcher and middleware function)
@@ -61,8 +61,9 @@ export class RequestIframeServerImpl {
61
61
  handledBy: this.id,
62
62
  isOriginAllowed: (d, ctx) => this.isOriginAllowed(d, ctx),
63
63
  warnMissingPendingWhenClosed: d => {
64
- this.hub.warnOnce(buildWarnOnceKey(WarnOnceKey.SERVER_MISSING_PENDING_WHEN_CLOSED, d.type, d.requestId), () => {
65
- requestIframeLog('warn', formatMessage(Messages.SERVER_IGNORED_MESSAGE_WHEN_CLOSED, d.type, d.requestId));
64
+ warnServerIgnoredMessageWhenClosedOnce(this.hub, {
65
+ type: d.type,
66
+ requestId: d.requestId
66
67
  });
67
68
  }
68
69
  },
@@ -77,8 +78,9 @@ export class RequestIframeServerImpl {
77
78
  this.ackTimeout = (_options$ackTimeout = options === null || options === void 0 ? void 0 : options.ackTimeout) !== null && _options$ackTimeout !== void 0 ? _options$ackTimeout : DefaultTimeout.ACK;
78
79
  this.maxConcurrentRequestsPerClient = (_options$maxConcurren = options === null || options === void 0 ? void 0 : options.maxConcurrentRequestsPerClient) !== null && _options$maxConcurren !== void 0 ? _options$maxConcurren : Number.POSITIVE_INFINITY;
79
80
  var warnMissingPendingWhenClosed = d => {
80
- this.hub.warnOnce(buildWarnOnceKey(WarnOnceKey.SERVER_MISSING_PENDING_WHEN_CLOSED, d.type, d.requestId), () => {
81
- requestIframeLog('warn', formatMessage(Messages.SERVER_IGNORED_MESSAGE_WHEN_CLOSED, d.type, d.requestId));
81
+ warnServerIgnoredMessageWhenClosedOnce(this.hub, {
82
+ type: d.type,
83
+ requestId: d.requestId
82
84
  });
83
85
  };
84
86
  var handlerOptions = this.hub.createHandlerOptions(this.handleVersionError.bind(this));
@@ -228,10 +228,22 @@ export class MessageChannel {
228
228
  * @param message message data (already formatted as PostMessageData)
229
229
  * @param targetOrigin target origin (defaults to '*')
230
230
  */
231
- send(target, message, targetOrigin = OriginConstant.ANY) {
231
+ send(target, message, targetOrigin = OriginConstant.ANY, transfer) {
232
232
  if (!isWindowAvailable(target)) {
233
233
  return false;
234
234
  }
235
+ /**
236
+ * Prefer transferable objects when provided.
237
+ * Use a try/catch fallback for environments that don't support the 3rd arg signature.
238
+ */
239
+ if (transfer && transfer.length) {
240
+ try {
241
+ target.postMessage(message, targetOrigin, transfer);
242
+ return true;
243
+ } catch (_unused) {
244
+ /** fall through to 2-arg postMessage */
245
+ }
246
+ }
235
247
  target.postMessage(message, targetOrigin);
236
248
  return true;
237
249
  }
@@ -244,11 +256,11 @@ export class MessageChannel {
244
256
  * @param requestId request ID
245
257
  * @param data additional data
246
258
  */
247
- sendMessage(target, targetOrigin, type, requestId, data) {
259
+ sendMessage(target, targetOrigin, type, requestId, data, transfer) {
248
260
  var message = createPostMessage(type, requestId, _objectSpread(_objectSpread({}, data), {}, {
249
261
  secretKey: this.secretKey
250
262
  }));
251
- return this.send(target, message, targetOrigin);
263
+ return this.send(target, message, targetOrigin, transfer);
252
264
  }
253
265
 
254
266
  /**
@@ -24,6 +24,7 @@ import { isFunction } from '../utils/is';
24
24
  import { getAckId, getAckMeta } from '../utils/ack';
25
25
  import { SyncHook } from '../utils/hooks';
26
26
  import { requestIframeLog } from '../utils/logger';
27
+ import { MessageContextStage } from './channel';
27
28
 
28
29
  /**
29
30
  * Message handler function type
@@ -200,6 +201,7 @@ export class MessageDispatcher {
200
201
  return;
201
202
  }
202
203
  }
204
+ var offEarlyAutoAck;
203
205
  try {
204
206
  this.hooks.inbound.call(data, context);
205
207
  var _type = data.type;
@@ -223,6 +225,25 @@ export class MessageDispatcher {
223
225
  autoAckState.sent = true;
224
226
  this.tryAutoAck(data, context);
225
227
  };
228
+
229
+ /**
230
+ * Early auto-ack: send ACK as soon as the handler "accepts" the message.
231
+ *
232
+ * Why:
233
+ * - For request delivery confirmation, ACK should represent "accepted/claimed by server"
234
+ * instead of "handler finished".
235
+ * - This keeps client-side ackTimeout meaningful and avoids being delayed by business logic.
236
+ *
237
+ * Notes:
238
+ * - Only contexts created by MessageChannel have onStateChange; tests may provide mocks.
239
+ * - We keep the original end-of-dispatch maybeAutoAck() as a fallback for older/edge contexts.
240
+ */
241
+ var onStateChangeMaybe = context.onStateChange;
242
+ offEarlyAutoAck = typeof onStateChangeMaybe === 'function' ? onStateChangeMaybe('autoAck:early', (_prev, next) => {
243
+ if (next === MessageContextStage.ACCEPTED) {
244
+ maybeAutoAck();
245
+ }
246
+ }) : undefined;
226
247
  var _iterator = _createForOfIteratorHelper(this.handlers),
227
248
  _step;
228
249
  try {
@@ -266,6 +287,12 @@ export class MessageDispatcher {
266
287
  }
267
288
  maybeAutoAck();
268
289
  } finally {
290
+ try {
291
+ var _offEarlyAutoAck;
292
+ (_offEarlyAutoAck = offEarlyAutoAck) === null || _offEarlyAutoAck === void 0 || _offEarlyAutoAck();
293
+ } catch (_unused) {
294
+ // ignore
295
+ }
269
296
  /**
270
297
  * Mark as "done" only when this dispatcher actually claimed/handled this message.
271
298
  * - If the message was never claimed (handledBy not set), we keep `doneBy` empty so another