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
@@ -2,7 +2,6 @@
2
2
 
3
3
  require("core-js/modules/es.array.filter.js");
4
4
  require("core-js/modules/es.object.get-own-property-descriptors.js");
5
- require("core-js/modules/web.dom-collections.for-each.js");
6
5
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
7
6
  Object.defineProperty(exports, "__esModule", {
8
7
  value: true
@@ -11,19 +10,19 @@ exports.IframeWritableStream = void 0;
11
10
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
12
11
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
13
12
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
14
- require("core-js/modules/es.symbol.description.js");
15
- require("core-js/modules/es.symbol.async-iterator.js");
16
13
  require("core-js/modules/es.array.iterator.js");
17
14
  require("core-js/modules/es.array.slice.js");
15
+ require("core-js/modules/es.map.js");
18
16
  require("core-js/modules/es.promise.js");
17
+ require("core-js/modules/es.promise.finally.js");
19
18
  require("core-js/modules/es.regexp.to-string.js");
19
+ require("core-js/modules/web.dom-collections.for-each.js");
20
20
  require("core-js/modules/web.dom-collections.iterator.js");
21
21
  var _utils = require("../utils");
22
22
  var _constants = require("../constants");
23
+ var _streamCore = require("./stream-core");
23
24
  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; }
24
25
  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; }
25
- function _asyncIterator(r) { var n, t, o, e = 2; for ("undefined" != typeof Symbol && (t = Symbol.asyncIterator, o = Symbol.iterator); e--;) { if (t && null != (n = r[t])) return n.call(r); if (o && null != (n = r[o])) return new AsyncFromSyncIterator(n.call(r)); t = "@@asyncIterator", o = "@@iterator"; } throw new TypeError("Object is not async iterable"); }
26
- function AsyncFromSyncIterator(r) { function AsyncFromSyncIteratorContinuation(r) { if (Object(r) !== r) return Promise.reject(new TypeError(r + " is not an object.")); var n = r.done; return Promise.resolve(r.value).then(function (r) { return { value: r, done: n }; }); } return AsyncFromSyncIterator = function AsyncFromSyncIterator(r) { this.s = r, this.n = r.next; }, AsyncFromSyncIterator.prototype = { s: null, n: null, next: function next() { return AsyncFromSyncIteratorContinuation(this.n.apply(this.s, arguments)); }, return: function _return(r) { var n = this.s.return; return void 0 === n ? Promise.resolve({ value: r, done: !0 }) : AsyncFromSyncIteratorContinuation(n.apply(this.s, arguments)); }, throw: function _throw(r) { var n = this.s.return; return void 0 === n ? Promise.reject(r) : AsyncFromSyncIteratorContinuation(n.apply(this.s, arguments)); } }, new AsyncFromSyncIterator(r); }
27
26
  /**
28
27
  * Generate a unique stream ID
29
28
  */
@@ -33,25 +32,113 @@ function generateStreamId() {
33
32
 
34
33
  /**
35
34
  * IframeWritableStream - Server-side writable stream
36
- * Used to send stream data to the client
35
+ *
36
+ * Writer/producer stream.
37
+ * It can be created on either side:
38
+ * - Server → Client: used as response stream (via `res.sendStream(stream)`)
39
+ * - Client → Server: used as request body stream (via `client.sendStream(path, stream)`)
37
40
  */
38
- class IframeWritableStream {
41
+ class IframeWritableStream extends _streamCore.IframeStreamCore {
39
42
  constructor(options = {}) {
40
- var _options$type, _options$chunked;
41
- (0, _defineProperty2.default)(this, "_state", _constants.StreamState.PENDING);
43
+ var _options$type, _options$chunked, _options$mode, _options$expireTimeou;
44
+ var streamId = generateStreamId();
45
+ var streamType = (_options$type = options.type) !== null && _options$type !== void 0 ? _options$type : _constants.StreamType.DATA;
46
+ var chunked = (_options$chunked = options.chunked) !== null && _options$chunked !== void 0 ? _options$chunked : true;
47
+ super(streamId, streamType, chunked, options.metadata, false, options.mode);
42
48
  (0, _defineProperty2.default)(this, "context", null);
43
- this.streamId = generateStreamId();
44
- this.type = (_options$type = options.type) !== null && _options$type !== void 0 ? _options$type : _constants.StreamType.DATA;
45
- this.chunked = (_options$chunked = options.chunked) !== null && _options$chunked !== void 0 ? _options$chunked : true;
49
+ (0, _defineProperty2.default)(this, "expireTimer", null);
50
+ (0, _defineProperty2.default)(this, "idleTimer", null);
51
+ (0, _defineProperty2.default)(this, "lastRemoteActivityAt", Date.now());
52
+ (0, _defineProperty2.default)(this, "heartbeatInFlight", null);
53
+ /** pull/ack protocol */
54
+ (0, _defineProperty2.default)(this, "pullCredit", 0);
55
+ (0, _defineProperty2.default)(this, "seq", 0);
56
+ (0, _defineProperty2.default)(this, "pendingBytes", 0);
57
+ (0, _defineProperty2.default)(this, "pendingQueue", []);
58
+ (0, _defineProperty2.default)(this, "pumping", false);
59
+ (0, _defineProperty2.default)(this, "completionPromise", null);
60
+ (0, _defineProperty2.default)(this, "resolveCompletion", null);
61
+ (0, _defineProperty2.default)(this, "rejectCompletion", null);
62
+ (0, _defineProperty2.default)(this, "ackWaiters", new Map());
63
+ (0, _defineProperty2.default)(this, "ackReceiverRegistered", false);
64
+ this.mode = (_options$mode = options.mode) !== null && _options$mode !== void 0 ? _options$mode : _constants.StreamMode.PULL;
46
65
  this.iterator = options.iterator;
47
66
  this.nextFn = options.next;
48
- this.metadata = options.metadata;
49
67
  this.autoResolve = options.autoResolve;
68
+ // Default to async-timeout length to avoid leaking long-lived streams
69
+ this.expireTimeout = (_options$expireTimeou = options.expireTimeout) !== null && _options$expireTimeou !== void 0 ? _options$expireTimeou : _constants.DefaultTimeout.ASYNC;
70
+ this.streamTimeout = options.streamTimeout;
71
+ this.maxPendingChunks = options.maxPendingChunks;
72
+ this.maxPendingBytes = options.maxPendingBytes;
73
+ }
74
+ enqueue(item) {
75
+ var max = this.maxPendingChunks;
76
+ if (typeof max === 'number' && max > 0 && this.pendingQueue.length >= max) {
77
+ throw new Error((0, _constants.formatMessage)(_constants.Messages.STREAM_PENDING_QUEUE_OVERFLOW, max));
78
+ }
79
+ var bytes = this.estimateChunkBytes(item.data);
80
+ var maxBytes = this.maxPendingBytes;
81
+ if (typeof maxBytes === 'number' && maxBytes > 0) {
82
+ var next = this.pendingBytes + bytes;
83
+ if (!Number.isFinite(next) || next > maxBytes) {
84
+ throw new Error((0, _constants.formatMessage)(_constants.Messages.STREAM_PENDING_BYTES_OVERFLOW, maxBytes));
85
+ }
86
+ }
87
+ this.pendingQueue.push(_objectSpread(_objectSpread({}, item), {}, {
88
+ bytes
89
+ }));
90
+ this.pendingBytes += bytes;
91
+ }
92
+ estimateChunkBytes(data) {
93
+ if (data === null || data === undefined) return 0;
94
+ if (typeof data === 'string') return this.utf8ByteLength(data);
95
+ try {
96
+ // ArrayBuffer
97
+ if (typeof ArrayBuffer !== 'undefined' && data instanceof ArrayBuffer) {
98
+ return data.byteLength;
99
+ }
100
+ // TypedArray / DataView
101
+ if (typeof ArrayBuffer !== 'undefined' && ArrayBuffer.isView && ArrayBuffer.isView(data)) {
102
+ return data.byteLength;
103
+ }
104
+ } catch (_unused) {
105
+ /** ignore */
106
+ }
107
+ try {
108
+ // Blob / File
109
+ if (typeof Blob !== 'undefined' && data instanceof Blob) {
110
+ return data.size;
111
+ }
112
+ } catch (_unused2) {
113
+ /** ignore */
114
+ }
115
+
116
+ // Strategy C: only count well-defined types; other values are not counted.
117
+ return 0;
118
+ }
119
+ utf8ByteLength(text) {
120
+ try {
121
+ if (typeof TextEncoder !== 'undefined') {
122
+ return new TextEncoder().encode(text).length;
123
+ }
124
+ } catch (_unused3) {
125
+ /** ignore */
126
+ }
127
+ try {
128
+ // eslint-disable-next-line no-undef
129
+ if (typeof Buffer !== 'undefined') {
130
+ // eslint-disable-next-line no-undef
131
+ return Buffer.byteLength(text, 'utf8');
132
+ }
133
+ } catch (_unused4) {
134
+ /** ignore */
135
+ }
136
+ return text.length;
50
137
  }
51
138
 
52
139
  /** Get stream state */
53
140
  get state() {
54
- return this._state;
141
+ return super.state;
55
142
  }
56
143
 
57
144
  /**
@@ -61,6 +148,125 @@ class IframeWritableStream {
61
148
  _bind(context) {
62
149
  this.context = context;
63
150
  }
151
+ registerControlHandler() {
152
+ var _this$context;
153
+ if (!((_this$context = this.context) !== null && _this$context !== void 0 && _this$context.registerStreamHandler)) return;
154
+ this.context.registerStreamHandler(this.streamId, this.handleControlMessage.bind(this));
155
+ }
156
+ unregisterControlHandler() {
157
+ var _this$context2;
158
+ if (!((_this$context2 = this.context) !== null && _this$context2 !== void 0 && _this$context2.unregisterStreamHandler)) return;
159
+ this.context.unregisterStreamHandler(this.streamId);
160
+ }
161
+ handleControlMessage(data) {
162
+ // Update remote activity timestamp on any control message
163
+ this.lastRemoteActivityAt = Date.now();
164
+ switch (data.type) {
165
+ case _constants.StreamInternalMessageType.PULL:
166
+ {
167
+ var credit = typeof data.credit === 'number' && data.credit > 0 ? data.credit : 1;
168
+ this.pullCredit += credit;
169
+ this.emit(_constants.StreamEvent.PULL, {
170
+ credit,
171
+ totalCredit: this.pullCredit
172
+ });
173
+ // Try flushing buffered chunks or pumping generator
174
+ this.flush();
175
+ break;
176
+ }
177
+ case _constants.StreamInternalMessageType.CANCEL:
178
+ this.emit(_constants.StreamEvent.CANCEL, {
179
+ reason: data.reason,
180
+ remote: true
181
+ });
182
+ this.cancel(data.reason);
183
+ break;
184
+ default:
185
+ break;
186
+ }
187
+ }
188
+ clearIdleTimer() {
189
+ if (this.idleTimer) {
190
+ clearTimeout(this.idleTimer);
191
+ this.idleTimer = null;
192
+ }
193
+ }
194
+ performHeartbeat() {
195
+ var _this = this;
196
+ return (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee() {
197
+ var _this$context3;
198
+ return _regenerator.default.wrap(function (_context) {
199
+ while (1) switch (_context.prev = _context.next) {
200
+ case 0:
201
+ if ((_this$context3 = _this.context) !== null && _this$context3 !== void 0 && _this$context3.heartbeat) {
202
+ _context.next = 1;
203
+ break;
204
+ }
205
+ return _context.abrupt("return", false);
206
+ case 1:
207
+ if (!_this.heartbeatInFlight) {
208
+ _this.heartbeatInFlight = Promise.resolve().then(() => _this.context.heartbeat()).catch(() => false).finally(() => {
209
+ _this.heartbeatInFlight = null;
210
+ });
211
+ }
212
+ return _context.abrupt("return", _this.heartbeatInFlight);
213
+ case 2:
214
+ case "end":
215
+ return _context.stop();
216
+ }
217
+ }, _callee);
218
+ }))();
219
+ }
220
+ startIdleTimer() {
221
+ var _this2 = this;
222
+ if (!this.streamTimeout || this.streamTimeout <= 0) return;
223
+ this.clearIdleTimer();
224
+ var timeout = this.streamTimeout;
225
+ this.idleTimer = setTimeout(/*#__PURE__*/(0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee2() {
226
+ var ok;
227
+ return _regenerator.default.wrap(function (_context2) {
228
+ while (1) switch (_context2.prev = _context2.next) {
229
+ case 0:
230
+ if (!(_this2._state !== _constants.StreamState.STREAMING)) {
231
+ _context2.next = 1;
232
+ break;
233
+ }
234
+ return _context2.abrupt("return");
235
+ case 1:
236
+ if (!(Date.now() - _this2.lastRemoteActivityAt < timeout)) {
237
+ _context2.next = 2;
238
+ break;
239
+ }
240
+ _this2.startIdleTimer();
241
+ return _context2.abrupt("return");
242
+ case 2:
243
+ _context2.next = 3;
244
+ return _this2.performHeartbeat();
245
+ case 3:
246
+ ok = _context2.sent;
247
+ if (!ok) {
248
+ _context2.next = 4;
249
+ break;
250
+ }
251
+ _this2.lastRemoteActivityAt = Date.now();
252
+ _this2.startIdleTimer();
253
+ return _context2.abrupt("return");
254
+ case 4:
255
+ try {
256
+ _this2.emit(_constants.StreamEvent.TIMEOUT, {
257
+ timeout
258
+ });
259
+ _this2.error((0, _constants.formatMessage)(_constants.Messages.STREAM_TIMEOUT, timeout));
260
+ } catch (_unused5) {
261
+ /** ignore */
262
+ }
263
+ case 5:
264
+ case "end":
265
+ return _context2.stop();
266
+ }
267
+ }, _callee2);
268
+ })), timeout);
269
+ }
64
270
 
65
271
  /**
66
272
  * Send message (to client when server-side stream, to server when client-side stream)
@@ -85,266 +291,459 @@ class IframeWritableStream {
85
291
  var ok = this.context.channel.send(this.context.targetWindow, message, this.context.targetOrigin);
86
292
  if (!ok) {
87
293
  this._state = _constants.StreamState.CANCELLED;
294
+ this.clearExpireTimer();
88
295
  // For most stream messages, if we cannot send, treat as a hard cancellation signal
89
296
  // so callers can stop further processing immediately.
90
297
  throw new Error((0, _constants.formatMessage)(_constants.Messages.STREAM_CANCELLED, 'Target window closed'));
91
298
  }
92
299
  return true;
93
300
  }
301
+ ensureAckReceiver() {
302
+ if (this.ackReceiverRegistered) return;
303
+ if (!this.context) return;
304
+ var ch = this.context.channel;
305
+ if (typeof ch.addReceiver !== 'function' || typeof ch.removeReceiver !== 'function') return;
306
+ this.ackReceiver = (data, context) => {
307
+ if (!data || data.type !== _constants.MessageType.ACK) return;
308
+ var pending = this.ackWaiters.get(data.requestId);
309
+ if (!pending) return;
310
+ if (context && !context.handledBy) {
311
+ context.handledBy = `stream:${this.streamId}`;
312
+ }
313
+ clearTimeout(pending.timeoutId);
314
+ this.ackWaiters.delete(data.requestId);
315
+ pending.resolve(true);
316
+ };
317
+ ch.addReceiver(this.ackReceiver);
318
+ this.ackReceiverRegistered = true;
319
+ }
320
+ cleanupAckWaiters() {
321
+ this.ackWaiters.forEach(p => {
322
+ clearTimeout(p.timeoutId);
323
+ p.resolve(false);
324
+ });
325
+ this.ackWaiters.clear();
326
+ if (this.ackReceiverRegistered && this.ackReceiver && this.context) {
327
+ var ch = this.context.channel;
328
+ if (typeof ch.removeReceiver === 'function') {
329
+ try {
330
+ ch.removeReceiver(this.ackReceiver);
331
+ } catch (_unused6) {
332
+ /** ignore */
333
+ }
334
+ }
335
+ }
336
+ this.ackReceiverRegistered = false;
337
+ this.ackReceiver = undefined;
338
+ }
339
+ registerAckWaiter(requestId, timeoutMs, resolve) {
340
+ var timeoutId = setTimeout(() => {
341
+ this.ackWaiters.delete(requestId);
342
+ resolve(false);
343
+ }, timeoutMs);
344
+ this.ackWaiters.set(requestId, {
345
+ resolve,
346
+ timeoutId
347
+ });
348
+ }
349
+ clearExpireTimer() {
350
+ if (this.expireTimer) {
351
+ clearTimeout(this.expireTimer);
352
+ this.expireTimer = null;
353
+ }
354
+ }
355
+ startExpireTimer() {
356
+ if (!this.expireTimeout || this.expireTimeout <= 0) return;
357
+ var expireTimeout = this.expireTimeout;
358
+ this.clearExpireTimer();
359
+ this.expireTimer = setTimeout(() => {
360
+ if (this._state !== _constants.StreamState.STREAMING) return;
361
+ try {
362
+ this.emit(_constants.StreamEvent.EXPIRED, {
363
+ timeout: expireTimeout
364
+ });
365
+ this.error((0, _constants.formatMessage)(_constants.Messages.STREAM_EXPIRED, expireTimeout));
366
+ } catch (_unused7) {
367
+ /** ignore timer-triggered send failures */
368
+ }
369
+ }, expireTimeout);
370
+ }
94
371
 
95
372
  /**
96
373
  * Start stream transfer
97
374
  */
98
375
  start() {
99
- var _this = this;
100
- return (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee() {
376
+ var _this3 = this;
377
+ return (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee3() {
101
378
  var _t;
102
- return _regenerator.default.wrap(function (_context) {
103
- while (1) switch (_context.prev = _context.next) {
379
+ return _regenerator.default.wrap(function (_context3) {
380
+ while (1) switch (_context3.prev = _context3.next) {
104
381
  case 0:
105
- if (_this.context) {
106
- _context.next = 1;
382
+ if (_this3.context) {
383
+ _context3.next = 1;
107
384
  break;
108
385
  }
109
386
  throw new Error(_constants.Messages.STREAM_NOT_BOUND);
110
387
  case 1:
111
- if (!(_this._state !== _constants.StreamState.PENDING)) {
112
- _context.next = 2;
388
+ if (!(_this3._state !== _constants.StreamState.PENDING)) {
389
+ _context3.next = 2;
113
390
  break;
114
391
  }
115
392
  throw new Error(_constants.Messages.STREAM_ALREADY_STARTED);
116
393
  case 2:
117
- _this._state = _constants.StreamState.STREAMING;
394
+ _this3.completionPromise = new Promise((resolve, reject) => {
395
+ _this3.resolveCompletion = resolve;
396
+ _this3.rejectCompletion = reject;
397
+ });
398
+ _this3._state = _constants.StreamState.STREAMING;
399
+ _this3.startExpireTimer();
400
+ _this3.startIdleTimer();
401
+ _this3.lastRemoteActivityAt = Date.now();
402
+ _this3.registerControlHandler();
118
403
 
119
404
  // Send stream start message
120
- _this.sendMessage(_constants.MessageType.STREAM_START, {
121
- type: _this.type,
122
- chunked: _this.chunked,
123
- metadata: _this.metadata,
124
- autoResolve: _this.autoResolve
405
+ _this3.sendMessage(_constants.MessageType.STREAM_START, {
406
+ type: _this3.type,
407
+ mode: _this3.mode,
408
+ chunked: _this3.chunked,
409
+ metadata: _this3.metadata,
410
+ autoResolve: _this3.autoResolve
125
411
  });
126
- _context.prev = 3;
127
- if (!_this.iterator) {
128
- _context.next = 5;
412
+ _this3.emit(_constants.StreamEvent.START, {
413
+ streamId: _this3.streamId,
414
+ type: _this3.type,
415
+ chunked: _this3.chunked,
416
+ mode: _this3.mode,
417
+ metadata: _this3.metadata
418
+ });
419
+ _context3.prev = 3;
420
+ if (!(_this3.mode === _constants.StreamMode.PUSH)) {
421
+ _context3.next = 5;
129
422
  break;
130
423
  }
131
- _context.next = 4;
132
- return _this.streamFromIterator();
424
+ _context3.next = 4;
425
+ return _this3.completionPromise;
133
426
  case 4:
134
- _context.next = 8;
135
- break;
427
+ return _context3.abrupt("return", _context3.sent);
136
428
  case 5:
137
- if (!_this.nextFn) {
138
- _context.next = 7;
139
- break;
140
- }
141
- _context.next = 6;
142
- return _this.streamFromNext();
429
+ // pull protocol: produce only when receiver grants credit
430
+ _this3.flush();
431
+ _context3.next = 6;
432
+ return _this3.completionPromise;
143
433
  case 6:
144
- _context.next = 8;
145
- break;
434
+ return _context3.abrupt("return", _context3.sent);
146
435
  case 7:
147
- // No data source, end directly
148
- _this.end();
149
- case 8:
150
- _context.next = 11;
151
- break;
152
- case 9:
153
- _context.prev = 9;
154
- _t = _context["catch"](3);
155
- if (!(_this._state === _constants.StreamState.CANCELLED)) {
156
- _context.next = 10;
436
+ _context3.prev = 7;
437
+ _t = _context3["catch"](3);
438
+ if (!(_this3._state === _constants.StreamState.CANCELLED)) {
439
+ _context3.next = 8;
157
440
  break;
158
441
  }
442
+ _this3.clearExpireTimer();
159
443
  throw _t;
444
+ case 8:
445
+ _this3.error(_t.message || String(_t));
446
+ _context3.next = 9;
447
+ return _this3.completionPromise;
448
+ case 9:
449
+ return _context3.abrupt("return", _context3.sent);
160
450
  case 10:
161
- _this.error(_t.message || String(_t));
162
- case 11:
163
451
  case "end":
164
- return _context.stop();
452
+ return _context3.stop();
165
453
  }
166
- }, _callee, null, [[3, 9]]);
454
+ }, _callee3, null, [[3, 7]]);
167
455
  }))();
168
456
  }
169
457
 
458
+ /**
459
+ * Push a chunk manually (mode === 'push').
460
+ */
461
+
462
+ write(data, doneOrOptions = false, options) {
463
+ var _opts$ackTimeout;
464
+ if (this.mode !== _constants.StreamMode.PUSH) {
465
+ throw new Error(_constants.Messages.STREAM_WRITE_ONLY_IN_PUSH_MODE);
466
+ }
467
+ if (this._state === _constants.StreamState.PENDING) {
468
+ /**
469
+ * In push mode, users must call start() first so STREAM_START is sent and binding is complete.
470
+ */
471
+ throw new Error(_constants.Messages.STREAM_NOT_BOUND);
472
+ }
473
+ if (this._state !== _constants.StreamState.STREAMING) {
474
+ throw new Error(_constants.Messages.STREAM_ENDED);
475
+ }
476
+ var done = typeof doneOrOptions === 'boolean' ? doneOrOptions : false;
477
+ var opts = typeof doneOrOptions === 'object' ? doneOrOptions : options;
478
+ var requireAck = (opts === null || opts === void 0 ? void 0 : opts.requireAck) === true;
479
+ var ackTimeout = (_opts$ackTimeout = opts === null || opts === void 0 ? void 0 : opts.ackTimeout) !== null && _opts$ackTimeout !== void 0 ? _opts$ackTimeout : _constants.DefaultTimeout.ACK;
480
+ if (!requireAck) {
481
+ // push mode now buffers and sends based on pull credit
482
+ this.enqueue({
483
+ data,
484
+ done
485
+ });
486
+ this.emit(_constants.StreamEvent.WRITE, {
487
+ data,
488
+ done
489
+ });
490
+ this.flush();
491
+ return;
492
+ }
493
+ return new Promise(resolve => {
494
+ var ackRequestId = (0, _utils.generateRequestId)();
495
+ this.enqueue({
496
+ data,
497
+ done,
498
+ requireAck: true,
499
+ ackRequestId,
500
+ ackTimeout,
501
+ resolveAck: resolve
502
+ });
503
+ this.emit(_constants.StreamEvent.WRITE, {
504
+ data,
505
+ done
506
+ });
507
+ this.flush();
508
+ });
509
+ }
510
+
511
+ /**
512
+ * End the stream (mode === 'push').
513
+ */
514
+ end() {
515
+ if (this.mode !== _constants.StreamMode.PUSH) {
516
+ // For pull mode, end is controlled internally
517
+ this.endInternal();
518
+ return;
519
+ }
520
+ // In push mode, end means enqueue a terminal marker if nothing queued
521
+ if (this.mode === _constants.StreamMode.PUSH) {
522
+ if (this.pendingQueue.length === 0) {
523
+ this.enqueue({
524
+ data: undefined,
525
+ done: true
526
+ });
527
+ } else {
528
+ // Ensure the last queued chunk marks done
529
+ this.pendingQueue[this.pendingQueue.length - 1].done = true;
530
+ }
531
+ this.flush();
532
+ return;
533
+ }
534
+ this.endInternal();
535
+ }
536
+
170
537
  /**
171
538
  * Generate data from iterator
172
539
  */
173
- streamFromIterator() {
174
- var _this2 = this;
175
- return (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee2() {
176
- var gen, _iteratorAbruptCompletion, _didIteratorError, _iteratorError, _iterator, _step, chunk, _t2, _t3, _t4;
177
- return _regenerator.default.wrap(function (_context2) {
178
- while (1) switch (_context2.prev = _context2.next) {
540
+ pumpFromGenerator() {
541
+ var _this4 = this;
542
+ return (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee4() {
543
+ var gen, r, result, _t2;
544
+ return _regenerator.default.wrap(function (_context4) {
545
+ while (1) switch (_context4.prev = _context4.next) {
179
546
  case 0:
180
- if (_this2.iterator) {
181
- _context2.next = 1;
547
+ if (!_this4.pumping) {
548
+ _context4.next = 1;
182
549
  break;
183
550
  }
184
- return _context2.abrupt("return");
551
+ return _context4.abrupt("return");
185
552
  case 1:
186
- gen = _this2.iterator();
187
- _context2.prev = 2;
188
- _iteratorAbruptCompletion = false;
189
- _didIteratorError = false;
190
- _context2.prev = 3;
191
- _iterator = _asyncIterator(gen);
192
- case 4:
193
- _context2.next = 5;
194
- return _iterator.next();
195
- case 5:
196
- if (!(_iteratorAbruptCompletion = !(_step = _context2.sent).done)) {
197
- _context2.next = 10;
553
+ _this4.pumping = true;
554
+ _context4.prev = 2;
555
+ if (!_this4.iterator) {
556
+ _context4.next = 8;
198
557
  break;
199
558
  }
200
- chunk = _step.value;
201
- if (!(_this2._state !== _constants.StreamState.STREAMING)) {
202
- _context2.next = 6;
559
+ gen = _this4.iterator();
560
+ case 3:
561
+ if (!(_this4._state === _constants.StreamState.STREAMING)) {
562
+ _context4.next = 7;
203
563
  break;
204
564
  }
205
- return _context2.abrupt("continue", 10);
206
- case 6:
207
- _context2.prev = 6;
208
- _this2.sendData(chunk);
209
- _context2.next = 9;
210
- break;
211
- case 7:
212
- _context2.prev = 7;
213
- _t2 = _context2["catch"](6);
214
- if (!(_this2._state === _constants.StreamState.CANCELLED)) {
215
- _context2.next = 8;
565
+ if (!(_this4.pullCredit <= 0)) {
566
+ _context4.next = 4;
216
567
  break;
217
568
  }
218
- throw _t2;
219
- case 8:
220
- throw _t2;
221
- case 9:
222
- _iteratorAbruptCompletion = false;
223
- _context2.next = 4;
224
- break;
225
- case 10:
226
- _context2.next = 12;
227
- break;
228
- case 11:
229
- _context2.prev = 11;
230
- _t3 = _context2["catch"](3);
231
- _didIteratorError = true;
232
- _iteratorError = _t3;
233
- case 12:
234
- _context2.prev = 12;
235
- _context2.prev = 13;
236
- if (!(_iteratorAbruptCompletion && _iterator.return != null)) {
237
- _context2.next = 14;
238
- break;
239
- }
240
- _context2.next = 14;
241
- return _iterator.return();
242
- case 14:
243
- _context2.prev = 14;
244
- if (!_didIteratorError) {
245
- _context2.next = 15;
569
+ return _context4.abrupt("continue", 7);
570
+ case 4:
571
+ _context4.next = 5;
572
+ return gen.next();
573
+ case 5:
574
+ r = _context4.sent;
575
+ if (!r.done) {
576
+ _context4.next = 6;
246
577
  break;
247
578
  }
248
- throw _iteratorError;
249
- case 15:
250
- return _context2.finish(14);
251
- case 16:
252
- return _context2.finish(12);
253
- case 17:
254
- if (_this2._state === _constants.StreamState.STREAMING) {
255
- _this2.end();
256
- }
257
- _context2.next = 20;
579
+ // no more data: send end
580
+ _this4.endInternal();
581
+ return _context4.abrupt("continue", 7);
582
+ case 6:
583
+ _this4.enqueue({
584
+ data: r.value,
585
+ done: false
586
+ });
587
+ _this4.flush();
588
+ _context4.next = 3;
258
589
  break;
259
- case 18:
260
- _context2.prev = 18;
261
- _t4 = _context2["catch"](2);
262
- if (!(_this2._state === _constants.StreamState.CANCELLED)) {
263
- _context2.next = 19;
590
+ case 7:
591
+ _context4.next = 15;
592
+ break;
593
+ case 8:
594
+ if (!_this4.nextFn) {
595
+ _context4.next = 14;
264
596
  break;
265
597
  }
266
- throw _t4;
267
- case 19:
268
- if (_this2._state === _constants.StreamState.STREAMING) {
269
- _this2.error(_t4.message || String(_t4));
270
- }
271
- case 20:
272
- case "end":
273
- return _context2.stop();
274
- }
275
- }, _callee2, null, [[2, 18], [3, 11, 12, 17], [6, 7], [13,, 14, 16]]);
276
- }))();
277
- }
278
-
279
- /**
280
- * Generate data from next function
281
- */
282
- streamFromNext() {
283
- var _this3 = this;
284
- return (0, _asyncToGenerator2.default)(/*#__PURE__*/_regenerator.default.mark(function _callee3() {
285
- var result, _t5;
286
- return _regenerator.default.wrap(function (_context3) {
287
- while (1) switch (_context3.prev = _context3.next) {
288
- case 0:
289
- if (_this3.nextFn) {
290
- _context3.next = 1;
598
+ case 9:
599
+ if (!(_this4._state === _constants.StreamState.STREAMING)) {
600
+ _context4.next = 13;
291
601
  break;
292
602
  }
293
- return _context3.abrupt("return");
294
- case 1:
295
- _context3.prev = 1;
296
- case 2:
297
- if (!(_this3._state === _constants.StreamState.STREAMING)) {
298
- _context3.next = 5;
603
+ if (!(_this4.pullCredit <= 0)) {
604
+ _context4.next = 10;
299
605
  break;
300
606
  }
301
- _context3.next = 3;
302
- return Promise.resolve(_this3.nextFn());
303
- case 3:
304
- result = _context3.sent;
607
+ return _context4.abrupt("continue", 13);
608
+ case 10:
609
+ _context4.next = 11;
610
+ return Promise.resolve(_this4.nextFn());
611
+ case 11:
612
+ result = _context4.sent;
613
+ _this4.enqueue({
614
+ data: result.data,
615
+ done: result.done
616
+ });
617
+ _this4.flush();
305
618
  if (!result.done) {
306
- _context3.next = 4;
619
+ _context4.next = 12;
307
620
  break;
308
621
  }
309
- _this3.sendData(result.data, true);
310
- _this3.end();
311
- return _context3.abrupt("continue", 5);
312
- case 4:
313
- _this3.sendData(result.data);
314
- _context3.next = 2;
622
+ return _context4.abrupt("continue", 13);
623
+ case 12:
624
+ _context4.next = 9;
315
625
  break;
316
- case 5:
317
- _context3.next = 8;
626
+ case 13:
627
+ _context4.next = 15;
318
628
  break;
319
- case 6:
320
- _context3.prev = 6;
321
- _t5 = _context3["catch"](1);
322
- if (!(_this3._state === _constants.StreamState.CANCELLED)) {
323
- _context3.next = 7;
324
- break;
629
+ case 14:
630
+ // No producer, just end when pulled
631
+ if (_this4.pullCredit > 0) {
632
+ _this4.endInternal();
325
633
  }
326
- throw _t5;
327
- case 7:
328
- if (_this3._state === _constants.StreamState.STREAMING) {
329
- _this3.error(_t5.message || String(_t5));
634
+ case 15:
635
+ _context4.next = 17;
636
+ break;
637
+ case 16:
638
+ _context4.prev = 16;
639
+ _t2 = _context4["catch"](2);
640
+ if (_this4._state === _constants.StreamState.STREAMING) {
641
+ _this4.error((_t2 === null || _t2 === void 0 ? void 0 : _t2.message) || String(_t2));
330
642
  }
331
- case 8:
643
+ case 17:
644
+ _context4.prev = 17;
645
+ _this4.pumping = false;
646
+ return _context4.finish(17);
647
+ case 18:
332
648
  case "end":
333
- return _context3.stop();
649
+ return _context4.stop();
334
650
  }
335
- }, _callee3, null, [[1, 6]]);
651
+ }, _callee4, null, [[2, 16, 17, 18]]);
336
652
  }))();
337
653
  }
338
654
 
339
655
  /**
340
656
  * Send data chunk
341
657
  */
342
- sendData(data, done = false) {
343
- this.sendMessage(_constants.MessageType.STREAM_DATA, {
344
- data: this.encodeData(data),
658
+ sendData(data, done = false, options) {
659
+ var _this$context$serverI2, _options$requestId;
660
+ if (!this.context) {
661
+ throw new Error(_constants.Messages.STREAM_NOT_BOUND);
662
+ }
663
+ var seq = this.seq++;
664
+ var isClientStream = this.context.clientId !== undefined && this.context.serverId === undefined;
665
+ var role = isClientStream ? _constants.MessageRole.CLIENT : _constants.MessageRole.SERVER;
666
+ var creatorId = (_this$context$serverI2 = this.context.serverId) !== null && _this$context$serverI2 !== void 0 ? _this$context$serverI2 : this.context.clientId;
667
+ var message = (0, _utils.createPostMessage)(_constants.MessageType.STREAM_DATA, (_options$requestId = options === null || options === void 0 ? void 0 : options.requestId) !== null && _options$requestId !== void 0 ? _options$requestId : this.context.requestId, {
668
+ secretKey: this.context.secretKey,
669
+ requireAck: options === null || options === void 0 ? void 0 : options.requireAck,
670
+ /**
671
+ * When per-frame requireAck is enabled, include a unique identifier in ack.
672
+ * - seq is the stream frame sequence number.
673
+ *
674
+ * NOTE: ack is an internal reserved field (not part of public API).
675
+ */
676
+ ack: options !== null && options !== void 0 && options.requireAck ? {
677
+ id: `${this.streamId}:${seq}`
678
+ } : undefined,
679
+ body: {
680
+ streamId: this.streamId,
681
+ data: this.encodeData(data),
682
+ done,
683
+ seq
684
+ },
685
+ role,
686
+ creatorId,
687
+ targetId: this.context.targetId
688
+ });
689
+ var ok = this.context.channel.send(this.context.targetWindow, message, this.context.targetOrigin);
690
+ if (!ok) {
691
+ this._state = _constants.StreamState.CANCELLED;
692
+ this.clearExpireTimer();
693
+ throw new Error((0, _constants.formatMessage)(_constants.Messages.STREAM_CANCELLED, 'Target window closed'));
694
+ }
695
+ this.emit(_constants.StreamEvent.SEND, {
696
+ seq,
345
697
  done
346
698
  });
347
699
  }
700
+ flush() {
701
+ if (this._state !== _constants.StreamState.STREAMING) return;
702
+
703
+ // First try to pump from generator if needed
704
+ if (this.mode === _constants.StreamMode.PULL && this.pendingQueue.length === 0) {
705
+ void this.pumpFromGenerator();
706
+ }
707
+ while (this.pullCredit > 0 && this.pendingQueue.length > 0 && this._state === _constants.StreamState.STREAMING) {
708
+ var item = this.pendingQueue.shift();
709
+ this.pendingBytes -= item.bytes;
710
+ this.pullCredit--;
711
+ try {
712
+ if (item.requireAck && item.ackRequestId && item.resolveAck) {
713
+ this.ensureAckReceiver();
714
+ if (this.ackReceiverRegistered) {
715
+ var _item$ackTimeout;
716
+ this.registerAckWaiter(item.ackRequestId, (_item$ackTimeout = item.ackTimeout) !== null && _item$ackTimeout !== void 0 ? _item$ackTimeout : _constants.DefaultTimeout.ACK, item.resolveAck);
717
+ this.sendData(item.data, item.done, {
718
+ requestId: item.ackRequestId,
719
+ requireAck: true
720
+ });
721
+ } else {
722
+ item.resolveAck(false);
723
+ this.sendData(item.data, item.done);
724
+ }
725
+ } else {
726
+ this.sendData(item.data, item.done);
727
+ }
728
+ } catch (e) {
729
+ var _this$rejectCompletio;
730
+ // send failure treated as cancellation
731
+ this._state = _constants.StreamState.CANCELLED;
732
+ this.clearExpireTimer();
733
+ this.clearIdleTimer();
734
+ this.unregisterControlHandler();
735
+ this.pendingQueue.length = 0;
736
+ this.pendingBytes = 0;
737
+ this.cleanupAckWaiters();
738
+ (_this$rejectCompletio = this.rejectCompletion) === null || _this$rejectCompletio === void 0 || _this$rejectCompletio.call(this, e instanceof Error ? e : new Error(String(e)));
739
+ throw e;
740
+ }
741
+ if (item.done) {
742
+ this.endInternal();
743
+ break;
744
+ }
745
+ }
746
+ }
348
747
 
349
748
  /**
350
749
  * Encode data (subclasses can override, e.g., FileStream needs Base64 encoding)
@@ -356,38 +755,89 @@ class IframeWritableStream {
356
755
  /**
357
756
  * End stream
358
757
  */
359
- end() {
758
+ endInternal() {
759
+ var _this$resolveCompleti;
360
760
  if (this._state !== _constants.StreamState.STREAMING) return;
361
761
  this._state = _constants.StreamState.ENDED;
762
+ this.clearExpireTimer();
763
+ this.clearIdleTimer();
764
+ this.unregisterControlHandler();
765
+ this.pendingQueue.length = 0;
766
+ this.pendingBytes = 0;
362
767
  this.sendMessage(_constants.MessageType.STREAM_END);
768
+ this.emit(_constants.StreamEvent.END);
769
+ this.emit(_constants.StreamEvent.STATE, {
770
+ state: this._state
771
+ });
772
+ this.cleanupAckWaiters();
773
+ this.clearAllListeners();
774
+ (_this$resolveCompleti = this.resolveCompletion) === null || _this$resolveCompleti === void 0 || _this$resolveCompleti.call(this);
363
775
  }
364
776
 
365
777
  /**
366
778
  * Send error
367
779
  */
368
780
  error(message) {
781
+ var _this$resolveCompleti2;
369
782
  if (this._state !== _constants.StreamState.STREAMING) return;
370
783
  this._state = _constants.StreamState.ERROR;
784
+ this.clearExpireTimer();
785
+ this.clearIdleTimer();
786
+ this.unregisterControlHandler();
787
+ this.pendingQueue.length = 0;
788
+ this.pendingBytes = 0;
371
789
  this.sendMessage(_constants.MessageType.STREAM_ERROR, {
372
790
  error: message
373
791
  });
792
+ this.emit(_constants.StreamEvent.ERROR, {
793
+ error: new Error(message)
794
+ });
795
+ this.emit(_constants.StreamEvent.STATE, {
796
+ state: this._state
797
+ });
798
+ this.cleanupAckWaiters();
799
+ this.clearAllListeners();
800
+ (_this$resolveCompleti2 = this.resolveCompletion) === null || _this$resolveCompleti2 === void 0 || _this$resolveCompleti2.call(this);
801
+ }
802
+
803
+ /**
804
+ * Abort stream transfer (is alias of cancel method)
805
+ */
806
+ abort(reason) {
807
+ this.cancel(reason);
374
808
  }
375
809
 
376
810
  /**
377
811
  * Cancel stream transfer
378
812
  */
379
813
  cancel(reason) {
814
+ var _this$resolveCompleti3;
380
815
  if (this._state !== _constants.StreamState.PENDING && this._state !== _constants.StreamState.STREAMING) return;
381
816
  this._state = _constants.StreamState.CANCELLED;
817
+ this.clearExpireTimer();
818
+ this.clearIdleTimer();
819
+ this.unregisterControlHandler();
820
+ this.pendingQueue.length = 0;
821
+ this.pendingBytes = 0;
822
+ this.emit(_constants.StreamEvent.CANCEL, {
823
+ reason,
824
+ remote: false
825
+ });
826
+ this.emit(_constants.StreamEvent.STATE, {
827
+ state: this._state
828
+ });
382
829
  if (this.context) {
383
830
  try {
384
831
  this.sendMessage(_constants.MessageType.STREAM_CANCEL, {
385
832
  reason
386
833
  });
387
- } catch (_unused) {
834
+ } catch (_unused8) {
388
835
  // ignore send failures on cancel
389
836
  }
390
837
  }
838
+ this.cleanupAckWaiters();
839
+ this.clearAllListeners();
840
+ (_this$resolveCompleti3 = this.resolveCompletion) === null || _this$resolveCompleti3 === void 0 || _this$resolveCompleti3.call(this);
391
841
  }
392
842
  }
393
843
  exports.IframeWritableStream = IframeWritableStream;