@cloudflare/sandbox 1.0.1 → 1.0.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.
package/README.md CHANGED
@@ -99,7 +99,7 @@ npm run check
99
99
  npm test
100
100
  ```
101
101
 
102
- [Testing](docs/testing.md) explains what these commands check, how to build behind a TLS-inspecting proxy, and how to test in production. [Releasing](docs/releasing.md) explains how maintainers publish the package and its image.
102
+ [Testing](docs/testing.md) explains what these commands check, how to build behind a TLS-inspecting proxy, and how to test in production. [Releasing](docs/releasing.md) explains how to describe a change for the release notes, and how maintainers publish the package and its image.
103
103
 
104
104
  ## License
105
105
 
package/dist/index.mjs CHANGED
@@ -985,11 +985,12 @@ async function readFile(container, path, options) {
985
985
  stderr: "pipe"
986
986
  });
987
987
  let control;
988
- let output;
988
+ let stdout;
989
989
  try {
990
990
  control = session.openStderrControl();
991
- output = session.openStdoutReader();
991
+ stdout = new StdoutReader(session.openStdoutReader());
992
992
  const opening = await control.readFrame();
993
+ stdout.stopReadingAhead();
993
994
  if (opening.kind === "fileError") {
994
995
  await control.expectEnd();
995
996
  await session.settle();
@@ -1001,17 +1002,69 @@ async function readFile(container, path, options) {
1001
1002
  if (opening.kind !== "success") throw protocolError("sandbox-shim returned data before file bytes");
1002
1003
  const terminal = readTerminalControl(control);
1003
1004
  terminal.catch(() => void 0);
1004
- return new Response(responseBody(session, control, output, terminal, path));
1005
+ return new Response(responseBody(session, control, stdout, terminal, path));
1005
1006
  } catch (error) {
1006
- terminateRead(session, control, output, error);
1007
+ terminateRead(session, control, stdout, error);
1007
1008
  throw error;
1008
1009
  }
1009
1010
  }
1010
- function responseBody(session, control, output, terminalFrame, path) {
1011
+ /**
1012
+ * Reads the shim's stdout, reading ahead until `readFile` has the opening frame from stderr.
1013
+ *
1014
+ * A container transport can carry stdout and stderr over one connection with shared backpressure,
1015
+ * and can deliver file bytes before the opening frame, or between its fragments. The rest of the
1016
+ * frame then arrives only after something reads stdout. The shim writes the whole opening frame
1017
+ * before any file byte, so the read-ahead stops once that frame is complete. After that, the
1018
+ * response body reads stdout on demand, so the caller's pace applies backpressure again.
1019
+ *
1020
+ * The read-ahead has no size limit, because stopping before the frame is complete would bring the
1021
+ * hang back. How much it holds depends on how far the transport lets stdout run ahead of stderr.
1022
+ */
1023
+ var StdoutReader = class {
1024
+ #reader;
1025
+ #chunks = [];
1026
+ #pending;
1027
+ #readingAhead = true;
1028
+ constructor(reader) {
1029
+ this.#reader = reader;
1030
+ this.#readNext();
1031
+ }
1032
+ /** Stops reading ahead. Chunks already read stay queued for `read()`. */
1033
+ stopReadingAhead() {
1034
+ this.#readingAhead = false;
1035
+ }
1036
+ read() {
1037
+ const chunk = this.#chunks.shift();
1038
+ if (chunk !== void 0) return Promise.resolve({
1039
+ done: false,
1040
+ value: chunk
1041
+ });
1042
+ const pending = this.#pending ?? this.#reader.read();
1043
+ this.#pending = void 0;
1044
+ return pending;
1045
+ }
1046
+ discard(reason) {
1047
+ this.#reader.cancel(reason).then(() => this.#reader.releaseLock(), () => this.#reader.releaseLock());
1048
+ }
1049
+ releaseLock() {
1050
+ this.#reader.releaseLock();
1051
+ }
1052
+ #readNext() {
1053
+ const read = this.#reader.read();
1054
+ this.#pending = read;
1055
+ read.then((result) => {
1056
+ if (this.#pending !== read || result.done) return;
1057
+ this.#pending = void 0;
1058
+ this.#chunks.push(result.value);
1059
+ if (this.#readingAhead) this.#readNext();
1060
+ }, () => void 0);
1061
+ }
1062
+ };
1063
+ function responseBody(session, control, stdout, terminalFrame, path) {
1011
1064
  return new ReadableStream({
1012
1065
  pull: async (controller) => {
1013
1066
  try {
1014
- const next = await session.waitFor(output.read());
1067
+ const next = await session.waitFor(stdout.read());
1015
1068
  if (!next.done) {
1016
1069
  controller.enqueue(next.value);
1017
1070
  return;
@@ -1025,16 +1078,16 @@ function responseBody(session, control, output, terminalFrame, path) {
1025
1078
  }, terminal.errno, terminal.detail);
1026
1079
  }
1027
1080
  if (terminal.kind !== "success") throw protocolError("sandbox-shim returned data after file bytes");
1028
- output.releaseLock();
1081
+ stdout.releaseLock();
1029
1082
  control.releaseLock();
1030
1083
  session.finish();
1031
1084
  controller.close();
1032
1085
  } catch (error) {
1033
- terminateRead(session, control, output, error);
1086
+ terminateRead(session, control, stdout, error);
1034
1087
  controller.error(error);
1035
1088
  }
1036
1089
  },
1037
- cancel: (reason) => terminateRead(session, control, output, reason)
1090
+ cancel: (reason) => terminateRead(session, control, stdout, reason)
1038
1091
  });
1039
1092
  }
1040
1093
  async function readTerminalControl(control) {
@@ -1042,10 +1095,10 @@ async function readTerminalControl(control) {
1042
1095
  await control.expectEnd();
1043
1096
  return frame;
1044
1097
  }
1045
- function terminateRead(session, control, output, reason) {
1098
+ function terminateRead(session, control, stdout, reason) {
1046
1099
  session.terminate();
1047
1100
  control?.discard(reason);
1048
- if (output !== void 0) output.cancel(reason).then(() => output.releaseLock(), () => output.releaseLock());
1101
+ stdout?.discard(reason);
1049
1102
  }
1050
1103
  //#endregion
1051
1104
  //#region src/files/stat-file.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudflare/sandbox",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Structured workspace file operations for Cloudflare sandboxes",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -23,6 +23,6 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "aws4fetch": "1.0.20",
26
- "zod": "4.6.5"
26
+ "zod": "4.6.7"
27
27
  }
28
28
  }