@mcp-b/do-runtime 0.2.2 → 0.2.3

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.3
4
+
5
+ ### Patch Changes
6
+
7
+ - e4b1be6: Close three input-lock laundering paths in HTTP bodies. `ReadableStream` async iteration, second-order `Blob` reads, and reader or stream lifecycle promises could previously resume outside the actor's input gate and make the next storage call throw "no input lock available in this context". They now route every surfaced continuation through the actor's gated I/O seam while preserving native stream cancellation and lock-release behavior.
8
+
3
9
  ## 0.2.2
4
10
 
5
11
  ### Patch Changes
package/dist/index.js CHANGED
@@ -3685,6 +3685,42 @@ var BODY_CONSUMERS = [
3685
3685
  "json",
3686
3686
  "text"
3687
3687
  ];
3688
+ /** A consumed Blob is a second-order body: every read it produces needs the same gate. */
3689
+ function gateBlob(ctx, blob) {
3690
+ const arrayBuffer = blob.arrayBuffer.bind(blob);
3691
+ const bytes = blob.bytes.bind(blob);
3692
+ const slice = blob.slice.bind(blob);
3693
+ const stream = blob.stream.bind(blob);
3694
+ const text = blob.text.bind(blob);
3695
+ Object.defineProperties(blob, {
3696
+ arrayBuffer: {
3697
+ configurable: true,
3698
+ writable: true,
3699
+ value: () => ctx.awaitIo(arrayBuffer())
3700
+ },
3701
+ bytes: {
3702
+ configurable: true,
3703
+ writable: true,
3704
+ value: () => ctx.awaitIo(bytes())
3705
+ },
3706
+ slice: {
3707
+ configurable: true,
3708
+ writable: true,
3709
+ value: (start, end, contentType) => gateBlob(ctx, slice(start, end, contentType))
3710
+ },
3711
+ stream: {
3712
+ configurable: true,
3713
+ writable: true,
3714
+ value: () => gateReadableStream(ctx, stream())
3715
+ },
3716
+ text: {
3717
+ configurable: true,
3718
+ writable: true,
3719
+ value: () => ctx.awaitIo(text())
3720
+ }
3721
+ });
3722
+ return blob;
3723
+ }
3688
3724
  /**
3689
3725
  * Wrap a `Request` or `Response` so every asynchronous step of reading it
3690
3726
  * resumes inside a gated slice.
@@ -3718,7 +3754,10 @@ function gateBody(ctx, value) {
3718
3754
  return clone;
3719
3755
  }
3720
3756
  if (BODY_CONSUMERS.includes(property)) {
3721
- const consume = (...args) => ctx.awaitIo(subject[property].apply(subject, args));
3757
+ const consume = (...args) => {
3758
+ const result = subject[property].apply(subject, args);
3759
+ return ctx.awaitIo(result, (value) => property === "blob" ? gateBlob(ctx, value) : value);
3760
+ };
3722
3761
  bound.set(property, consume);
3723
3762
  return consume;
3724
3763
  }
@@ -3748,11 +3787,45 @@ function gateResponseBody(ctx, response) {
3748
3787
  */
3749
3788
  var BYOB_READER_UNGATABLE_MESSAGE = "getReader({ mode: 'byob' }): a BYOB reader cannot be gated by this runtime, because `ReadableStream.getReader` is the only seam it has and a byte stream's `read(view)` returns the caller's own buffer. Read the body through `arrayBuffer()` or a default reader.";
3750
3789
  function gateReadableStream(ctx, stream) {
3790
+ const cancel = stream.cancel.bind(stream);
3751
3791
  const getReader = stream.getReader.bind(stream);
3752
3792
  const tee = stream.tee.bind(stream);
3753
3793
  const pipeThrough = stream.pipeThrough.bind(stream);
3754
3794
  const pipeTo = stream.pipeTo.bind(stream);
3795
+ const values = async function* (options) {
3796
+ const reader = gateReader(ctx, getReader());
3797
+ let finished = false;
3798
+ try {
3799
+ for (;;) {
3800
+ let result;
3801
+ try {
3802
+ result = await reader.read();
3803
+ } catch (exception) {
3804
+ finished = true;
3805
+ throw exception;
3806
+ }
3807
+ if (result.done) {
3808
+ finished = true;
3809
+ return;
3810
+ }
3811
+ yield result.value;
3812
+ }
3813
+ } finally {
3814
+ try {
3815
+ if (!finished && options?.preventCancel !== true) await reader.cancel();
3816
+ } finally {
3817
+ reader.releaseLock();
3818
+ }
3819
+ }
3820
+ };
3755
3821
  Object.defineProperties(stream, {
3822
+ cancel: {
3823
+ configurable: true,
3824
+ writable: true,
3825
+ value(reason) {
3826
+ return ctx.awaitIo(cancel(reason));
3827
+ }
3828
+ },
3756
3829
  getReader: {
3757
3830
  configurable: true,
3758
3831
  writable: true,
@@ -3782,15 +3855,47 @@ function gateReadableStream(ctx, stream) {
3782
3855
  value(destination, options) {
3783
3856
  return ctx.awaitIo(pipeTo(destination, options));
3784
3857
  }
3858
+ },
3859
+ values: {
3860
+ configurable: true,
3861
+ writable: true,
3862
+ value: values
3863
+ },
3864
+ [Symbol.asyncIterator]: {
3865
+ configurable: true,
3866
+ writable: true,
3867
+ value: values
3785
3868
  }
3786
3869
  });
3787
3870
  return stream;
3788
3871
  }
3789
3872
  function gateReader(ctx, reader) {
3873
+ const bound = /* @__PURE__ */ new Map();
3790
3874
  return new Proxy(reader, { get(subject, property) {
3791
- if (property === "read") return () => ctx.awaitIo(subject.read());
3875
+ const cached = bound.get(property);
3876
+ if (cached !== void 0) return cached;
3877
+ if (property === "closed") {
3878
+ const closed = ctx.awaitIo(subject.closed);
3879
+ bound.set(property, closed);
3880
+ return closed;
3881
+ }
3882
+ if (property === "read") {
3883
+ const read = () => ctx.awaitIo(subject.read());
3884
+ bound.set(property, read);
3885
+ return read;
3886
+ }
3887
+ if (property === "cancel") {
3888
+ const cancel = (reason) => ctx.awaitIo(subject.cancel(reason));
3889
+ bound.set(property, cancel);
3890
+ return cancel;
3891
+ }
3792
3892
  const value = Reflect.get(subject, property, subject);
3793
- return typeof value === "function" ? value.bind(subject) : value;
3893
+ if (typeof value === "function") {
3894
+ const method = value.bind(subject);
3895
+ bound.set(property, method);
3896
+ return method;
3897
+ }
3898
+ return value;
3794
3899
  } });
3795
3900
  }
3796
3901
  //#endregion