request-iframe 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.CN.md +174 -22
  2. package/README.md +166 -22
  3. package/esm/api/client.js +6 -5
  4. package/esm/api/server.js +3 -1
  5. package/esm/constants/index.js +38 -6
  6. package/esm/constants/messages.js +3 -1
  7. package/esm/core/client-server.js +8 -43
  8. package/esm/core/client.js +7 -7
  9. package/esm/core/response.js +16 -8
  10. package/esm/core/server.js +18 -9
  11. package/esm/message/channel.js +2 -1
  12. package/esm/message/dispatcher.js +63 -25
  13. package/esm/stream/readable-stream.js +57 -18
  14. package/esm/stream/stream-core.js +115 -2
  15. package/esm/stream/writable-stream.js +281 -27
  16. package/esm/utils/ack.js +36 -0
  17. package/esm/utils/debug.js +1 -1
  18. package/esm/utils/index.js +5 -3
  19. package/esm/utils/origin.js +3 -1
  20. package/library/api/client.d.ts.map +1 -1
  21. package/library/api/client.js +5 -4
  22. package/library/api/server.d.ts.map +1 -1
  23. package/library/api/server.js +3 -1
  24. package/library/constants/index.d.ts +36 -5
  25. package/library/constants/index.d.ts.map +1 -1
  26. package/library/constants/index.js +41 -8
  27. package/library/constants/messages.d.ts +3 -1
  28. package/library/constants/messages.d.ts.map +1 -1
  29. package/library/constants/messages.js +3 -1
  30. package/library/core/client-server.d.ts +4 -13
  31. package/library/core/client-server.d.ts.map +1 -1
  32. package/library/core/client-server.js +7 -42
  33. package/library/core/client.d.ts.map +1 -1
  34. package/library/core/client.js +6 -6
  35. package/library/core/response.d.ts +2 -2
  36. package/library/core/response.d.ts.map +1 -1
  37. package/library/core/response.js +15 -7
  38. package/library/core/server.d.ts +6 -2
  39. package/library/core/server.d.ts.map +1 -1
  40. package/library/core/server.js +18 -9
  41. package/library/message/channel.d.ts +1 -1
  42. package/library/message/channel.d.ts.map +1 -1
  43. package/library/message/channel.js +2 -1
  44. package/library/message/dispatcher.d.ts +10 -0
  45. package/library/message/dispatcher.d.ts.map +1 -1
  46. package/library/message/dispatcher.js +62 -24
  47. package/library/stream/readable-stream.d.ts.map +1 -1
  48. package/library/stream/readable-stream.js +56 -17
  49. package/library/stream/stream-core.d.ts +22 -1
  50. package/library/stream/stream-core.d.ts.map +1 -1
  51. package/library/stream/stream-core.js +114 -1
  52. package/library/stream/types.d.ts +115 -2
  53. package/library/stream/types.d.ts.map +1 -1
  54. package/library/stream/writable-stream.d.ts +20 -2
  55. package/library/stream/writable-stream.d.ts.map +1 -1
  56. package/library/stream/writable-stream.js +277 -23
  57. package/library/types/index.d.ts +3 -4
  58. package/library/types/index.d.ts.map +1 -1
  59. package/library/utils/ack.d.ts +2 -0
  60. package/library/utils/ack.d.ts.map +1 -0
  61. package/library/utils/ack.js +44 -0
  62. package/library/utils/debug.js +1 -1
  63. package/library/utils/index.d.ts.map +1 -1
  64. package/library/utils/index.js +4 -3
  65. package/library/utils/origin.d.ts.map +1 -1
  66. package/library/utils/origin.js +2 -1
  67. package/package.json +1 -1
  68. package/esm/utils/ack-meta.js +0 -53
  69. package/library/utils/ack-meta.d.ts +0 -2
  70. package/library/utils/ack-meta.d.ts.map +0 -1
  71. package/library/utils/ack-meta.js +0 -59
@@ -1,17 +1,20 @@
1
1
  import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator";
2
2
  import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
3
+ import _regeneratorRuntime from "@babel/runtime/regenerator";
3
4
  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; }
4
5
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
5
- import _regeneratorRuntime from "@babel/runtime/regenerator";
6
- import "core-js/modules/es.array.slice.js";
7
6
  import "core-js/modules/es.array.filter.js";
8
- import "core-js/modules/es.object.get-own-property-descriptors.js";
7
+ import "core-js/modules/es.array.iterator.js";
8
+ import "core-js/modules/es.array.slice.js";
9
+ import "core-js/modules/es.map.js";
9
10
  import "core-js/modules/es.promise.js";
10
11
  import "core-js/modules/es.promise.finally.js";
12
+ import "core-js/modules/es.object.get-own-property-descriptors.js";
11
13
  import "core-js/modules/es.regexp.to-string.js";
12
14
  import "core-js/modules/web.dom-collections.for-each.js";
13
- import { createPostMessage } from '../utils';
14
- import { MessageType, Messages, StreamType as StreamTypeConstant, StreamState as StreamStateConstant, StreamMode as StreamModeConstant, MessageRole, formatMessage, DefaultTimeout, StreamInternalMessageType } from '../constants';
15
+ import "core-js/modules/web.dom-collections.iterator.js";
16
+ import { createPostMessage, generateRequestId } from '../utils';
17
+ import { MessageType, Messages, StreamType as StreamTypeConstant, StreamState as StreamStateConstant, StreamMode as StreamModeConstant, MessageRole, formatMessage, DefaultTimeout, StreamInternalMessageType, StreamEvent } from '../constants';
15
18
  import { IframeStreamCore } from './stream-core';
16
19
 
17
20
  /**
@@ -23,7 +26,11 @@ function generateStreamId() {
23
26
 
24
27
  /**
25
28
  * IframeWritableStream - Server-side writable stream
26
- * Used to send stream data to the client
29
+ *
30
+ * Writer/producer stream.
31
+ * It can be created on either side:
32
+ * - Server → Client: used as response stream (via `res.sendStream(stream)`)
33
+ * - Client → Server: used as request body stream (via `client.sendStream(path, stream)`)
27
34
  */
28
35
  export class IframeWritableStream extends IframeStreamCore {
29
36
  constructor(options = {}) {
@@ -40,11 +47,14 @@ export class IframeWritableStream extends IframeStreamCore {
40
47
  /** pull/ack protocol */
41
48
  _defineProperty(this, "pullCredit", 0);
42
49
  _defineProperty(this, "seq", 0);
50
+ _defineProperty(this, "pendingBytes", 0);
43
51
  _defineProperty(this, "pendingQueue", []);
44
52
  _defineProperty(this, "pumping", false);
45
53
  _defineProperty(this, "completionPromise", null);
46
54
  _defineProperty(this, "resolveCompletion", null);
47
55
  _defineProperty(this, "rejectCompletion", null);
56
+ _defineProperty(this, "ackWaiters", new Map());
57
+ _defineProperty(this, "ackReceiverRegistered", false);
48
58
  this.mode = (_options$mode = options.mode) !== null && _options$mode !== void 0 ? _options$mode : StreamModeConstant.PULL;
49
59
  this.iterator = options.iterator;
50
60
  this.nextFn = options.next;
@@ -52,6 +62,72 @@ export class IframeWritableStream extends IframeStreamCore {
52
62
  // Default to async-timeout length to avoid leaking long-lived streams
53
63
  this.expireTimeout = (_options$expireTimeou = options.expireTimeout) !== null && _options$expireTimeou !== void 0 ? _options$expireTimeou : DefaultTimeout.ASYNC;
54
64
  this.streamTimeout = options.streamTimeout;
65
+ this.maxPendingChunks = options.maxPendingChunks;
66
+ this.maxPendingBytes = options.maxPendingBytes;
67
+ }
68
+ enqueue(item) {
69
+ var max = this.maxPendingChunks;
70
+ if (typeof max === 'number' && max > 0 && this.pendingQueue.length >= max) {
71
+ throw new Error(formatMessage(Messages.STREAM_PENDING_QUEUE_OVERFLOW, max));
72
+ }
73
+ var bytes = this.estimateChunkBytes(item.data);
74
+ var maxBytes = this.maxPendingBytes;
75
+ if (typeof maxBytes === 'number' && maxBytes > 0) {
76
+ var next = this.pendingBytes + bytes;
77
+ if (!Number.isFinite(next) || next > maxBytes) {
78
+ throw new Error(formatMessage(Messages.STREAM_PENDING_BYTES_OVERFLOW, maxBytes));
79
+ }
80
+ }
81
+ this.pendingQueue.push(_objectSpread(_objectSpread({}, item), {}, {
82
+ bytes
83
+ }));
84
+ this.pendingBytes += bytes;
85
+ }
86
+ estimateChunkBytes(data) {
87
+ if (data === null || data === undefined) return 0;
88
+ if (typeof data === 'string') return this.utf8ByteLength(data);
89
+ try {
90
+ // ArrayBuffer
91
+ if (typeof ArrayBuffer !== 'undefined' && data instanceof ArrayBuffer) {
92
+ return data.byteLength;
93
+ }
94
+ // TypedArray / DataView
95
+ if (typeof ArrayBuffer !== 'undefined' && ArrayBuffer.isView && ArrayBuffer.isView(data)) {
96
+ return data.byteLength;
97
+ }
98
+ } catch (_unused) {
99
+ /** ignore */
100
+ }
101
+ try {
102
+ // Blob / File
103
+ if (typeof Blob !== 'undefined' && data instanceof Blob) {
104
+ return data.size;
105
+ }
106
+ } catch (_unused2) {
107
+ /** ignore */
108
+ }
109
+
110
+ // Strategy C: only count well-defined types; other values are not counted.
111
+ return 0;
112
+ }
113
+ utf8ByteLength(text) {
114
+ try {
115
+ if (typeof TextEncoder !== 'undefined') {
116
+ return new TextEncoder().encode(text).length;
117
+ }
118
+ } catch (_unused3) {
119
+ /** ignore */
120
+ }
121
+ try {
122
+ // eslint-disable-next-line no-undef
123
+ if (typeof Buffer !== 'undefined') {
124
+ // eslint-disable-next-line no-undef
125
+ return Buffer.byteLength(text, 'utf8');
126
+ }
127
+ } catch (_unused4) {
128
+ /** ignore */
129
+ }
130
+ return text.length;
55
131
  }
56
132
 
57
133
  /** Get stream state */
@@ -84,14 +160,19 @@ export class IframeWritableStream extends IframeStreamCore {
84
160
  {
85
161
  var credit = typeof data.credit === 'number' && data.credit > 0 ? data.credit : 1;
86
162
  this.pullCredit += credit;
163
+ this.emit(StreamEvent.PULL, {
164
+ credit,
165
+ totalCredit: this.pullCredit
166
+ });
87
167
  // Try flushing buffered chunks or pumping generator
88
168
  this.flush();
89
169
  break;
90
170
  }
91
- case StreamInternalMessageType.ACK:
92
- // Ack is treated as heartbeat; no further action required
93
- break;
94
171
  case StreamInternalMessageType.CANCEL:
172
+ this.emit(StreamEvent.CANCEL, {
173
+ reason: data.reason,
174
+ remote: true
175
+ });
95
176
  this.cancel(data.reason);
96
177
  break;
97
178
  default:
@@ -166,8 +247,11 @@ export class IframeWritableStream extends IframeStreamCore {
166
247
  return _context2.abrupt("return");
167
248
  case 4:
168
249
  try {
250
+ _this2.emit(StreamEvent.TIMEOUT, {
251
+ timeout
252
+ });
169
253
  _this2.error(formatMessage(Messages.STREAM_TIMEOUT, timeout));
170
- } catch (_unused) {
254
+ } catch (_unused5) {
171
255
  /** ignore */
172
256
  }
173
257
  case 5:
@@ -208,6 +292,54 @@ export class IframeWritableStream extends IframeStreamCore {
208
292
  }
209
293
  return true;
210
294
  }
295
+ ensureAckReceiver() {
296
+ if (this.ackReceiverRegistered) return;
297
+ if (!this.context) return;
298
+ var ch = this.context.channel;
299
+ if (typeof ch.addReceiver !== 'function' || typeof ch.removeReceiver !== 'function') return;
300
+ this.ackReceiver = (data, context) => {
301
+ if (!data || data.type !== MessageType.ACK) return;
302
+ var pending = this.ackWaiters.get(data.requestId);
303
+ if (!pending) return;
304
+ if (context && !context.handledBy) {
305
+ context.handledBy = `stream:${this.streamId}`;
306
+ }
307
+ clearTimeout(pending.timeoutId);
308
+ this.ackWaiters.delete(data.requestId);
309
+ pending.resolve(true);
310
+ };
311
+ ch.addReceiver(this.ackReceiver);
312
+ this.ackReceiverRegistered = true;
313
+ }
314
+ cleanupAckWaiters() {
315
+ this.ackWaiters.forEach(p => {
316
+ clearTimeout(p.timeoutId);
317
+ p.resolve(false);
318
+ });
319
+ this.ackWaiters.clear();
320
+ if (this.ackReceiverRegistered && this.ackReceiver && this.context) {
321
+ var ch = this.context.channel;
322
+ if (typeof ch.removeReceiver === 'function') {
323
+ try {
324
+ ch.removeReceiver(this.ackReceiver);
325
+ } catch (_unused6) {
326
+ /** ignore */
327
+ }
328
+ }
329
+ }
330
+ this.ackReceiverRegistered = false;
331
+ this.ackReceiver = undefined;
332
+ }
333
+ registerAckWaiter(requestId, timeoutMs, resolve) {
334
+ var timeoutId = setTimeout(() => {
335
+ this.ackWaiters.delete(requestId);
336
+ resolve(false);
337
+ }, timeoutMs);
338
+ this.ackWaiters.set(requestId, {
339
+ resolve,
340
+ timeoutId
341
+ });
342
+ }
211
343
  clearExpireTimer() {
212
344
  if (this.expireTimer) {
213
345
  clearTimeout(this.expireTimer);
@@ -221,8 +353,11 @@ export class IframeWritableStream extends IframeStreamCore {
221
353
  this.expireTimer = setTimeout(() => {
222
354
  if (this._state !== StreamStateConstant.STREAMING) return;
223
355
  try {
356
+ this.emit(StreamEvent.EXPIRED, {
357
+ timeout: expireTimeout
358
+ });
224
359
  this.error(formatMessage(Messages.STREAM_EXPIRED, expireTimeout));
225
- } catch (_unused2) {
360
+ } catch (_unused7) {
226
361
  /** ignore timer-triggered send failures */
227
362
  }
228
363
  }, expireTimeout);
@@ -268,6 +403,13 @@ export class IframeWritableStream extends IframeStreamCore {
268
403
  metadata: _this3.metadata,
269
404
  autoResolve: _this3.autoResolve
270
405
  });
406
+ _this3.emit(StreamEvent.START, {
407
+ streamId: _this3.streamId,
408
+ type: _this3.type,
409
+ chunked: _this3.chunked,
410
+ mode: _this3.mode,
411
+ metadata: _this3.metadata
412
+ });
271
413
  _context3.prev = 3;
272
414
  if (!(_this3.mode === StreamModeConstant.PUSH)) {
273
415
  _context3.next = 5;
@@ -310,7 +452,9 @@ export class IframeWritableStream extends IframeStreamCore {
310
452
  /**
311
453
  * Push a chunk manually (mode === 'push').
312
454
  */
313
- write(data, done = false) {
455
+
456
+ write(data, doneOrOptions = false, options) {
457
+ var _opts$ackTimeout;
314
458
  if (this.mode !== StreamModeConstant.PUSH) {
315
459
  throw new Error(Messages.STREAM_WRITE_ONLY_IN_PUSH_MODE);
316
460
  }
@@ -323,12 +467,39 @@ export class IframeWritableStream extends IframeStreamCore {
323
467
  if (this._state !== StreamStateConstant.STREAMING) {
324
468
  throw new Error(Messages.STREAM_ENDED);
325
469
  }
326
- // push mode now buffers and sends based on pull credit
327
- this.pendingQueue.push({
328
- data,
329
- done
470
+ var done = typeof doneOrOptions === 'boolean' ? doneOrOptions : false;
471
+ var opts = typeof doneOrOptions === 'object' ? doneOrOptions : options;
472
+ var requireAck = (opts === null || opts === void 0 ? void 0 : opts.requireAck) === true;
473
+ var ackTimeout = (_opts$ackTimeout = opts === null || opts === void 0 ? void 0 : opts.ackTimeout) !== null && _opts$ackTimeout !== void 0 ? _opts$ackTimeout : DefaultTimeout.ACK;
474
+ if (!requireAck) {
475
+ // push mode now buffers and sends based on pull credit
476
+ this.enqueue({
477
+ data,
478
+ done
479
+ });
480
+ this.emit(StreamEvent.WRITE, {
481
+ data,
482
+ done
483
+ });
484
+ this.flush();
485
+ return;
486
+ }
487
+ return new Promise(resolve => {
488
+ var ackRequestId = generateRequestId();
489
+ this.enqueue({
490
+ data,
491
+ done,
492
+ requireAck: true,
493
+ ackRequestId,
494
+ ackTimeout,
495
+ resolveAck: resolve
496
+ });
497
+ this.emit(StreamEvent.WRITE, {
498
+ data,
499
+ done
500
+ });
501
+ this.flush();
330
502
  });
331
- this.flush();
332
503
  }
333
504
 
334
505
  /**
@@ -343,7 +514,7 @@ export class IframeWritableStream extends IframeStreamCore {
343
514
  // In push mode, end means enqueue a terminal marker if nothing queued
344
515
  if (this.mode === StreamModeConstant.PUSH) {
345
516
  if (this.pendingQueue.length === 0) {
346
- this.pendingQueue.push({
517
+ this.enqueue({
347
518
  data: undefined,
348
519
  done: true
349
520
  });
@@ -403,7 +574,7 @@ export class IframeWritableStream extends IframeStreamCore {
403
574
  _this4.endInternal();
404
575
  return _context4.abrupt("continue", 7);
405
576
  case 6:
406
- _this4.pendingQueue.push({
577
+ _this4.enqueue({
407
578
  data: r.value,
408
579
  done: false
409
580
  });
@@ -433,7 +604,7 @@ export class IframeWritableStream extends IframeStreamCore {
433
604
  return Promise.resolve(_this4.nextFn());
434
605
  case 11:
435
606
  result = _context4.sent;
436
- _this4.pendingQueue.push({
607
+ _this4.enqueue({
437
608
  data: result.data,
438
609
  done: result.done
439
610
  });
@@ -478,11 +649,46 @@ export class IframeWritableStream extends IframeStreamCore {
478
649
  /**
479
650
  * Send data chunk
480
651
  */
481
- sendData(data, done = false) {
482
- this.sendMessage(MessageType.STREAM_DATA, {
483
- data: this.encodeData(data),
484
- done,
485
- seq: this.seq++
652
+ sendData(data, done = false, options) {
653
+ var _this$context$serverI2, _options$requestId;
654
+ if (!this.context) {
655
+ throw new Error(Messages.STREAM_NOT_BOUND);
656
+ }
657
+ var seq = this.seq++;
658
+ var isClientStream = this.context.clientId !== undefined && this.context.serverId === undefined;
659
+ var role = isClientStream ? MessageRole.CLIENT : MessageRole.SERVER;
660
+ var creatorId = (_this$context$serverI2 = this.context.serverId) !== null && _this$context$serverI2 !== void 0 ? _this$context$serverI2 : this.context.clientId;
661
+ var message = createPostMessage(MessageType.STREAM_DATA, (_options$requestId = options === null || options === void 0 ? void 0 : options.requestId) !== null && _options$requestId !== void 0 ? _options$requestId : this.context.requestId, {
662
+ secretKey: this.context.secretKey,
663
+ requireAck: options === null || options === void 0 ? void 0 : options.requireAck,
664
+ /**
665
+ * When per-frame requireAck is enabled, include a unique identifier in ack.
666
+ * - seq is the stream frame sequence number.
667
+ *
668
+ * NOTE: ack is an internal reserved field (not part of public API).
669
+ */
670
+ ack: options !== null && options !== void 0 && options.requireAck ? {
671
+ id: `${this.streamId}:${seq}`
672
+ } : undefined,
673
+ body: {
674
+ streamId: this.streamId,
675
+ data: this.encodeData(data),
676
+ done,
677
+ seq
678
+ },
679
+ role,
680
+ creatorId,
681
+ targetId: this.context.targetId
682
+ });
683
+ var ok = this.context.channel.send(this.context.targetWindow, message, this.context.targetOrigin);
684
+ if (!ok) {
685
+ this._state = StreamStateConstant.CANCELLED;
686
+ this.clearExpireTimer();
687
+ throw new Error(formatMessage(Messages.STREAM_CANCELLED, 'Target window closed'));
688
+ }
689
+ this.emit(StreamEvent.SEND, {
690
+ seq,
691
+ done
486
692
  });
487
693
  }
488
694
  flush() {
@@ -494,9 +700,25 @@ export class IframeWritableStream extends IframeStreamCore {
494
700
  }
495
701
  while (this.pullCredit > 0 && this.pendingQueue.length > 0 && this._state === StreamStateConstant.STREAMING) {
496
702
  var item = this.pendingQueue.shift();
703
+ this.pendingBytes -= item.bytes;
497
704
  this.pullCredit--;
498
705
  try {
499
- this.sendData(item.data, item.done);
706
+ if (item.requireAck && item.ackRequestId && item.resolveAck) {
707
+ this.ensureAckReceiver();
708
+ if (this.ackReceiverRegistered) {
709
+ var _item$ackTimeout;
710
+ this.registerAckWaiter(item.ackRequestId, (_item$ackTimeout = item.ackTimeout) !== null && _item$ackTimeout !== void 0 ? _item$ackTimeout : DefaultTimeout.ACK, item.resolveAck);
711
+ this.sendData(item.data, item.done, {
712
+ requestId: item.ackRequestId,
713
+ requireAck: true
714
+ });
715
+ } else {
716
+ item.resolveAck(false);
717
+ this.sendData(item.data, item.done);
718
+ }
719
+ } else {
720
+ this.sendData(item.data, item.done);
721
+ }
500
722
  } catch (e) {
501
723
  var _this$rejectCompletio;
502
724
  // send failure treated as cancellation
@@ -504,6 +726,9 @@ export class IframeWritableStream extends IframeStreamCore {
504
726
  this.clearExpireTimer();
505
727
  this.clearIdleTimer();
506
728
  this.unregisterControlHandler();
729
+ this.pendingQueue.length = 0;
730
+ this.pendingBytes = 0;
731
+ this.cleanupAckWaiters();
507
732
  (_this$rejectCompletio = this.rejectCompletion) === null || _this$rejectCompletio === void 0 || _this$rejectCompletio.call(this, e instanceof Error ? e : new Error(String(e)));
508
733
  throw e;
509
734
  }
@@ -531,7 +756,15 @@ export class IframeWritableStream extends IframeStreamCore {
531
756
  this.clearExpireTimer();
532
757
  this.clearIdleTimer();
533
758
  this.unregisterControlHandler();
759
+ this.pendingQueue.length = 0;
760
+ this.pendingBytes = 0;
534
761
  this.sendMessage(MessageType.STREAM_END);
762
+ this.emit(StreamEvent.END);
763
+ this.emit(StreamEvent.STATE, {
764
+ state: this._state
765
+ });
766
+ this.cleanupAckWaiters();
767
+ this.clearAllListeners();
535
768
  (_this$resolveCompleti = this.resolveCompletion) === null || _this$resolveCompleti === void 0 || _this$resolveCompleti.call(this);
536
769
  }
537
770
 
@@ -545,9 +778,19 @@ export class IframeWritableStream extends IframeStreamCore {
545
778
  this.clearExpireTimer();
546
779
  this.clearIdleTimer();
547
780
  this.unregisterControlHandler();
781
+ this.pendingQueue.length = 0;
782
+ this.pendingBytes = 0;
548
783
  this.sendMessage(MessageType.STREAM_ERROR, {
549
784
  error: message
550
785
  });
786
+ this.emit(StreamEvent.ERROR, {
787
+ error: new Error(message)
788
+ });
789
+ this.emit(StreamEvent.STATE, {
790
+ state: this._state
791
+ });
792
+ this.cleanupAckWaiters();
793
+ this.clearAllListeners();
551
794
  (_this$resolveCompleti2 = this.resolveCompletion) === null || _this$resolveCompleti2 === void 0 || _this$resolveCompleti2.call(this);
552
795
  }
553
796
 
@@ -568,15 +811,26 @@ export class IframeWritableStream extends IframeStreamCore {
568
811
  this.clearExpireTimer();
569
812
  this.clearIdleTimer();
570
813
  this.unregisterControlHandler();
814
+ this.pendingQueue.length = 0;
815
+ this.pendingBytes = 0;
816
+ this.emit(StreamEvent.CANCEL, {
817
+ reason,
818
+ remote: false
819
+ });
820
+ this.emit(StreamEvent.STATE, {
821
+ state: this._state
822
+ });
571
823
  if (this.context) {
572
824
  try {
573
825
  this.sendMessage(MessageType.STREAM_CANCEL, {
574
826
  reason
575
827
  });
576
- } catch (_unused3) {
828
+ } catch (_unused8) {
577
829
  // ignore send failures on cancel
578
830
  }
579
831
  }
832
+ this.cleanupAckWaiters();
833
+ this.clearAllListeners();
580
834
  (_this$resolveCompleti3 = this.resolveCompletion) === null || _this$resolveCompleti3 === void 0 || _this$resolveCompleti3.call(this);
581
835
  }
582
836
  }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * ack is an internal reserved protocol field (previously named ackMeta).
3
+ *
4
+ * Semantics (ACK-only workflow):
5
+ * - If `ack` exists, it should contain a required `id` (string|number) for matching.
6
+ * - Fixed shape: { id, meta?: string } (meta is optional and must be a string if present).
7
+ * - Matching is by `id` only; never deep-compare other fields.
8
+ *
9
+ * @internal
10
+ */
11
+ export function getAckId(ack) {
12
+ if (!ack || typeof ack !== 'object') return undefined;
13
+ var id = ack.id;
14
+ return typeof id === 'string' || typeof id === 'number' ? id : undefined;
15
+ }
16
+
17
+ /**
18
+ * @internal
19
+ */
20
+ export function getAckMeta(ack) {
21
+ if (!ack || typeof ack !== 'object') return undefined;
22
+ var meta = ack.meta;
23
+ return typeof meta === 'string' ? meta : undefined;
24
+ }
25
+
26
+ /**
27
+ * Match ack by `id` only.
28
+ * @internal
29
+ */
30
+ export function isAckMatch(expected, received) {
31
+ if (expected === undefined) return true;
32
+ var expectedId = getAckId(expected);
33
+ var receivedId = getAckId(received);
34
+ if (expectedId === undefined || receivedId === undefined) return false;
35
+ return expectedId === receivedId;
36
+ }
@@ -222,7 +222,7 @@ function setupClientMessageDebugging(clientImpl) {
222
222
  log('info', Messages.DEBUG_CLIENT_SENDING_PING, {
223
223
  requestId
224
224
  });
225
- } else if (type === MessageType.RECEIVED) {
225
+ } else if (type === MessageType.ACK) {
226
226
  log('info', Messages.DEBUG_CLIENT_SENDING_RECEIVED_ACK, {
227
227
  requestId
228
228
  });
@@ -7,6 +7,8 @@ import "core-js/modules/web.dom-collections.iterator.js";
7
7
  import "core-js/modules/web.url.js";
8
8
  import "core-js/modules/web.url.to-json.js";
9
9
  import "core-js/modules/web.url-search-params.js";
10
+ import { OriginConstant } from '../constants';
11
+
10
12
  /**
11
13
  * Generate unique request ID
12
14
  */
@@ -26,12 +28,12 @@ export function generateInstanceId() {
26
28
  */
27
29
  export function getIframeTargetOrigin(iframe) {
28
30
  if (!iframe.src) {
29
- return '*';
31
+ return OriginConstant.ANY;
30
32
  }
31
33
  try {
32
34
  return new URL(iframe.src).origin;
33
35
  } catch (e) {
34
- return '*';
36
+ return OriginConstant.ANY;
35
37
  }
36
38
  }
37
39
  export function isPromise(value) {
@@ -78,7 +80,7 @@ export * from './path-match';
78
80
  // Export origin matching functions
79
81
  export * from './origin';
80
82
 
81
- // ackMeta is a reserved protocol field (internal). Do not export helpers publicly.
83
+ // ack is a reserved protocol field (internal). Do not export helpers publicly.
82
84
 
83
85
  // Export Cookie-related functions
84
86
  export * from './cookie';
@@ -1,6 +1,8 @@
1
1
  import "core-js/modules/es.regexp.constructor.js";
2
2
  import "core-js/modules/es.regexp.exec.js";
3
3
  import "core-js/modules/es.regexp.to-string.js";
4
+ import { OriginConstant } from '../constants';
5
+
4
6
  /**
5
7
  * Origin matcher type (supports string, RegExp, Array)
6
8
  */
@@ -14,7 +16,7 @@ import "core-js/modules/es.regexp.to-string.js";
14
16
  * - Array: any item matches.
15
17
  */
16
18
  export function matchOrigin(origin, matcher) {
17
- if (matcher === '*') return true;
19
+ if (matcher === OriginConstant.ANY) return true;
18
20
  if (typeof matcher === 'string') {
19
21
  return origin === matcher;
20
22
  }
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,UAAU,CAAC;AAO1F;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,iBAAiB,GAAG,MAAM,EAClC,OAAO,CAAC,EAAE,0BAA0B,GACnC,mBAAmB,CAuDrB;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAKtE"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,UAAU,CAAC;AAO1F;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,iBAAiB,GAAG,MAAM,EAClC,OAAO,CAAC,EAAE,0BAA0B,GACnC,mBAAmB,CAwDrB;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAKtE"}
@@ -20,7 +20,7 @@ var _constants = require("../constants");
20
20
  */
21
21
  function requestIframeClient(target, options) {
22
22
  var targetWindow = null;
23
- var targetOrigin = '*';
23
+ var targetOrigin = _constants.OriginConstant.ANY;
24
24
  if (target.tagName === 'IFRAME') {
25
25
  var iframe = target;
26
26
  targetWindow = iframe.contentWindow;
@@ -33,7 +33,7 @@ function requestIframeClient(target, options) {
33
33
  }
34
34
  } else {
35
35
  targetWindow = target;
36
- targetOrigin = '*';
36
+ targetOrigin = _constants.OriginConstant.ANY;
37
37
  }
38
38
 
39
39
  // Allow user to override targetOrigin explicitly
@@ -50,8 +50,9 @@ function requestIframeClient(target, options) {
50
50
  // Create ClientServer (internally obtains or creates a shared MessageChannel)
51
51
  var server = new _clientServer.RequestIframeClientServer({
52
52
  secretKey,
53
- ackTimeout: options === null || options === void 0 ? void 0 : options.ackTimeout,
54
- autoOpen: options === null || options === void 0 ? void 0 : options.autoOpen
53
+ autoOpen: options === null || options === void 0 ? void 0 : options.autoOpen,
54
+ autoAckMaxMetaLength: options === null || options === void 0 ? void 0 : options.autoAckMaxMetaLength,
55
+ autoAckMaxIdLength: options === null || options === void 0 ? void 0 : options.autoAckMaxIdLength
55
56
  }, instanceId);
56
57
 
57
58
  // Create client instance
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/api/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,UAAU,CAAC;AAK3E;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,CAAC,EAAE,0BAA0B,GACnC,mBAAmB,CAmCrB;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAKtE"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/api/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,UAAU,CAAC;AAK3E;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,CAAC,EAAE,0BAA0B,GACnC,mBAAmB,CAqCrB;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAKtE"}
@@ -38,7 +38,9 @@ function requestIframeServer(options) {
38
38
  autoOpen: options === null || options === void 0 ? void 0 : options.autoOpen,
39
39
  allowedOrigins: options === null || options === void 0 ? void 0 : options.allowedOrigins,
40
40
  validateOrigin: options === null || options === void 0 ? void 0 : options.validateOrigin,
41
- maxConcurrentRequestsPerClient: options === null || options === void 0 ? void 0 : options.maxConcurrentRequestsPerClient
41
+ maxConcurrentRequestsPerClient: options === null || options === void 0 ? void 0 : options.maxConcurrentRequestsPerClient,
42
+ autoAckMaxMetaLength: options === null || options === void 0 ? void 0 : options.autoAckMaxMetaLength,
43
+ autoAckMaxIdLength: options === null || options === void 0 ? void 0 : options.autoAckMaxIdLength
42
44
  });
43
45
 
44
46
  // If trace mode is enabled, register debug listeners