@sproutboat/toolchain 0.9.0 → 0.10.0

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
  # @sproutboat/toolchain
2
2
 
3
+ ## 0.10.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 4274bef: `Response.body` and `Request.body` are `ReadableStream`s for every body type, as in the Fetch spec and workerd. Before, they held the value given (a string, bytes or a `Blob`). `res.body.getReader()`, `res.body.pipeThrough(...)` and `new Response(other.body)` now work on any response, including the incoming request. The value as given stays internal, so responses are still written with a fixed length unless the body really is a stream. A body whose stream has been locked can no longer be read with `text()` and the other readers, which throw a `TypeError`.
8
+
3
9
  ## 0.9.0
4
10
 
5
11
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sproutboat/toolchain",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {
@@ -65,7 +65,22 @@ export const REQUEST_CLASS_SOURCE = String.raw`class Request {
65
65
  static _consume(message) {
66
66
  if (message._bodyUsed === true) throw new TypeError('Body has already been used');
67
67
  message._bodyUsed = true;
68
- return message.body;
68
+ // A stream the body getter handed out is what the reader reads: a locked one throws.
69
+ return message._bodyStream != null ? message._bodyStream : message._bodyInit;
70
+ }
71
+
72
+ // body is a ReadableStream, as in Fetch: a stream body itself, else a stream made once over
73
+ // the bytes. The value as given stays in _bodyInit, which the native writer, streaming and
74
+ // the prelude read; assigning body (the native ingress does) sets it.
75
+ static _bodyAsStream(message) {
76
+ const init = message._bodyInit;
77
+ if (init == null) return null;
78
+ if (Request._isStream(init)) return init;
79
+ if (message._bodyStream == null) {
80
+ const bytes = Request._bodyBytes(init);
81
+ message._bodyStream = new globalThis.ReadableStream({ start(c) { if (bytes.length) c.enqueue(bytes); c.close(); } });
82
+ }
83
+ return message._bodyStream;
69
84
  }
70
85
 
71
86
  static _bodyText(body) {
@@ -119,7 +134,7 @@ export const REQUEST_CLASS_SOURCE = String.raw`class Request {
119
134
  url = input.url;
120
135
  method = input.method;
121
136
  headers = input.headers;
122
- body = input.body;
137
+ body = input._bodyStream != null ? input._bodyStream : input._bodyInit;
123
138
  redirect = input.redirect;
124
139
  } else if (input != null && Porffor.type(input) == Porffor.TYPES.object && input.href != null) url = input.href;
125
140
 
@@ -142,11 +157,13 @@ export const REQUEST_CLASS_SOURCE = String.raw`class Request {
142
157
  if (init != null && init.body != null && init.body !== '' && (this.method == 'GET' || this.method == 'HEAD'))
143
158
  throw new TypeError('Request with GET/HEAD method cannot have body.');
144
159
  this.headers = new Headers(headers);
145
- this.body = Request._extractBody(body, this.headers);
160
+ this._bodyInit = Request._extractBody(body, this.headers);
146
161
  if (redirect != null) this._redirect = String(redirect);
147
162
  }
148
163
 
149
164
  get redirect() { return this._redirect ?? 'follow'; }
165
+ get body() { return Request._bodyAsStream(this); }
166
+ set body(value) { this._bodyInit = value; this._bodyStream = null; }
150
167
  get bodyUsed() { return this._bodyUsed === true; }
151
168
 
152
169
  arrayBuffer() { return Request._read(this, (bytes) => bytes.buffer); }
@@ -179,7 +196,7 @@ export const RESPONSE_CLASS_SOURCE = String.raw`class Response {
179
196
  throw new TypeError('Response with null body status cannot have body');
180
197
  if (init?.statusText != null) this._statusText = String(init.statusText);
181
198
  this.headers = new Headers(init?.headers);
182
- this.body = Request._extractBody(body, this.headers);
199
+ this._bodyInit = Request._extractBody(body, this.headers);
183
200
  }
184
201
 
185
202
  static error() {
@@ -192,6 +209,8 @@ export const RESPONSE_CLASS_SOURCE = String.raw`class Response {
192
209
  get statusText() { return this._statusText ?? ''; }
193
210
  get type() { return this._type ?? 'default'; }
194
211
  get ok() { return this.status >= 200 && this.status < 300; }
212
+ get body() { return Request._bodyAsStream(this); }
213
+ set body(value) { this._bodyInit = value; this._bodyStream = null; }
195
214
  get bodyUsed() { return this._bodyUsed === true; }
196
215
 
197
216
  arrayBuffer() { return Request._read(this, (bytes) => bytes.buffer); }
@@ -199,7 +218,7 @@ export const RESPONSE_CLASS_SOURCE = String.raw`class Response {
199
218
  clone() {
200
219
  if (this._bodyUsed === true) throw new TypeError('Body has already been used');
201
220
  const copy = new Response(null, { status: this.status == 0 ? 200 : this.status, statusText: this._statusText, headers: this.headers });
202
- copy.body = this.body;
221
+ copy._bodyInit = this._bodyInit;
203
222
  copy.status = this.status;
204
223
  if (this._type != null) copy._type = this._type;
205
224
  return copy;
@@ -223,7 +242,7 @@ export const RESPONSE_BYTES_ANCHOR =
223
242
  export const RESPONSE_BYTES_INJECT =
224
243
  " // sproutboat: byte bodies go out as their bytes. A Latin-1 string, one character per byte,\n" +
225
244
  " // built in windows (concatenating one long string is quadratic), sent raw by the writer.\n" +
226
- " let body = response.body;\n" +
245
+ " let body = response._bodyInit !== undefined ? response._bodyInit : response.body;\n" +
227
246
  " let headersEntries = response.headers._entries;\n" +
228
247
  " if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {\n" +
229
248
  " const view = body instanceof ArrayBuffer ? new Uint8Array(body) : new Uint8Array(body.buffer, body.byteOffset, body.byteLength);\n" +
@@ -21,7 +21,7 @@ export const PATCH_SOURCES = [
21
21
  * patch.ts as the module patch.ts is everywhere else. Kept current by fingerprint.test.ts
22
22
  * (UPDATE_FINGERPRINT=1 bun test packages/toolchain/src/fingerprint.test.ts).
23
23
  */
24
- export const RELEASED_FINGERPRINT = "5a243546eb20";
24
+ export const RELEASED_FINGERPRINT = "405c8f04fb7d";
25
25
 
26
26
  /** The fingerprint of the sources in dir, or null where they are not on disk. */
27
27
  export function sourceFingerprint(dir: string): string | null {