@sproutboat/toolchain 0.9.0 → 0.10.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.
- package/CHANGELOG.md +12 -0
- package/package.json +1 -1
- package/src/fetch-classes.ts +39 -7
- package/src/fingerprint.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @sproutboat/toolchain
|
|
2
2
|
|
|
3
|
+
## 0.10.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 6676ead: `env.BUCKET.put(key, request.body)`, `env.KV.put(key, request.body)` and `fetch(url, { body: request.body })` store and send the request body again. Since 0.19.0, `body` is a `ReadableStream`, and these took its string form. Incoming request bodies are also read as the bytes they are, so `arrayBuffer()`, `blob()`, `body` and KV `put` no longer UTF-8 encode a non-ASCII upload twice. R2 `put` accepts bytes as well.
|
|
8
|
+
|
|
9
|
+
## 0.10.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 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`.
|
|
14
|
+
|
|
3
15
|
## 0.9.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/package.json
CHANGED
package/src/fetch-classes.ts
CHANGED
|
@@ -65,7 +65,25 @@ 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
|
-
|
|
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._messageBytes(message, init);
|
|
81
|
+
message._bodyStream = new globalThis.ReadableStream({ start(c) { if (bytes.length) c.enqueue(bytes); c.close(); } });
|
|
82
|
+
// The value the stream was made from, for the bindings that take a body at once
|
|
83
|
+
// (R2 and KV put, outbound fetch): put(key, request.body) is the Workers idiom.
|
|
84
|
+
message._bodyStream._sbBody = message._bodyLatin1 === true ? bytes : init;
|
|
85
|
+
}
|
|
86
|
+
return message._bodyStream;
|
|
69
87
|
}
|
|
70
88
|
|
|
71
89
|
static _bodyText(body) {
|
|
@@ -105,7 +123,17 @@ export const REQUEST_CLASS_SOURCE = String.raw`class Request {
|
|
|
105
123
|
static _read(message, finish) {
|
|
106
124
|
const body = Request._consume(message);
|
|
107
125
|
if (Request._isStream(body)) return Request._streamBytes(body).then(finish);
|
|
108
|
-
return finish(Request.
|
|
126
|
+
return finish(Request._messageBytes(message, body));
|
|
127
|
+
}
|
|
128
|
+
// A body the native ingress delivered is its raw bytes, one character each
|
|
129
|
+
// (_bodyLatin1): those bytes, not the string UTF-8 encoded again.
|
|
130
|
+
static _messageBytes(message, body) {
|
|
131
|
+
if (message._bodyLatin1 === true && typeof body == 'string') {
|
|
132
|
+
const out = new Uint8Array(body.length);
|
|
133
|
+
for (let i = 0; i < body.length; i++) out[i] = body.charCodeAt(i) & 255;
|
|
134
|
+
return out;
|
|
135
|
+
}
|
|
136
|
+
return Request._bodyBytes(body);
|
|
109
137
|
}
|
|
110
138
|
|
|
111
139
|
constructor(input, init = undefined) {
|
|
@@ -119,7 +147,7 @@ export const REQUEST_CLASS_SOURCE = String.raw`class Request {
|
|
|
119
147
|
url = input.url;
|
|
120
148
|
method = input.method;
|
|
121
149
|
headers = input.headers;
|
|
122
|
-
body = input.
|
|
150
|
+
body = input._bodyStream != null ? input._bodyStream : input._bodyInit;
|
|
123
151
|
redirect = input.redirect;
|
|
124
152
|
} else if (input != null && Porffor.type(input) == Porffor.TYPES.object && input.href != null) url = input.href;
|
|
125
153
|
|
|
@@ -142,11 +170,13 @@ export const REQUEST_CLASS_SOURCE = String.raw`class Request {
|
|
|
142
170
|
if (init != null && init.body != null && init.body !== '' && (this.method == 'GET' || this.method == 'HEAD'))
|
|
143
171
|
throw new TypeError('Request with GET/HEAD method cannot have body.');
|
|
144
172
|
this.headers = new Headers(headers);
|
|
145
|
-
this.
|
|
173
|
+
this._bodyInit = Request._extractBody(body, this.headers);
|
|
146
174
|
if (redirect != null) this._redirect = String(redirect);
|
|
147
175
|
}
|
|
148
176
|
|
|
149
177
|
get redirect() { return this._redirect ?? 'follow'; }
|
|
178
|
+
get body() { return Request._bodyAsStream(this); }
|
|
179
|
+
set body(value) { this._bodyInit = value; this._bodyStream = null; }
|
|
150
180
|
get bodyUsed() { return this._bodyUsed === true; }
|
|
151
181
|
|
|
152
182
|
arrayBuffer() { return Request._read(this, (bytes) => bytes.buffer); }
|
|
@@ -179,7 +209,7 @@ export const RESPONSE_CLASS_SOURCE = String.raw`class Response {
|
|
|
179
209
|
throw new TypeError('Response with null body status cannot have body');
|
|
180
210
|
if (init?.statusText != null) this._statusText = String(init.statusText);
|
|
181
211
|
this.headers = new Headers(init?.headers);
|
|
182
|
-
this.
|
|
212
|
+
this._bodyInit = Request._extractBody(body, this.headers);
|
|
183
213
|
}
|
|
184
214
|
|
|
185
215
|
static error() {
|
|
@@ -192,6 +222,8 @@ export const RESPONSE_CLASS_SOURCE = String.raw`class Response {
|
|
|
192
222
|
get statusText() { return this._statusText ?? ''; }
|
|
193
223
|
get type() { return this._type ?? 'default'; }
|
|
194
224
|
get ok() { return this.status >= 200 && this.status < 300; }
|
|
225
|
+
get body() { return Request._bodyAsStream(this); }
|
|
226
|
+
set body(value) { this._bodyInit = value; this._bodyStream = null; }
|
|
195
227
|
get bodyUsed() { return this._bodyUsed === true; }
|
|
196
228
|
|
|
197
229
|
arrayBuffer() { return Request._read(this, (bytes) => bytes.buffer); }
|
|
@@ -199,7 +231,7 @@ export const RESPONSE_CLASS_SOURCE = String.raw`class Response {
|
|
|
199
231
|
clone() {
|
|
200
232
|
if (this._bodyUsed === true) throw new TypeError('Body has already been used');
|
|
201
233
|
const copy = new Response(null, { status: this.status == 0 ? 200 : this.status, statusText: this._statusText, headers: this.headers });
|
|
202
|
-
copy.
|
|
234
|
+
copy._bodyInit = this._bodyInit;
|
|
203
235
|
copy.status = this.status;
|
|
204
236
|
if (this._type != null) copy._type = this._type;
|
|
205
237
|
return copy;
|
|
@@ -223,7 +255,7 @@ export const RESPONSE_BYTES_ANCHOR =
|
|
|
223
255
|
export const RESPONSE_BYTES_INJECT =
|
|
224
256
|
" // sproutboat: byte bodies go out as their bytes. A Latin-1 string, one character per byte,\n" +
|
|
225
257
|
" // built in windows (concatenating one long string is quadratic), sent raw by the writer.\n" +
|
|
226
|
-
" let body = response.body;\n" +
|
|
258
|
+
" let body = response._bodyInit !== undefined ? response._bodyInit : response.body;\n" +
|
|
227
259
|
" let headersEntries = response.headers._entries;\n" +
|
|
228
260
|
" if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {\n" +
|
|
229
261
|
" const view = body instanceof ArrayBuffer ? new Uint8Array(body) : new Uint8Array(body.buffer, body.byteOffset, body.byteLength);\n" +
|
package/src/fingerprint.ts
CHANGED
|
@@ -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 = "
|
|
24
|
+
export const RELEASED_FINGERPRINT = "9a717433bb3b";
|
|
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 {
|