@sproutboat/runtime 0.19.0 → 0.19.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 +6 -0
- package/package.json +1 -1
- package/src/native-fetch-prelude.js +14 -1
- package/test/native-request.ts +1 -1
- package/test/native-utf8.ts +12 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @sproutboat/runtime
|
|
2
2
|
|
|
3
|
+
## 0.19.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
|
+
|
|
3
9
|
## 0.19.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
package/package.json
CHANGED
|
@@ -4045,7 +4045,14 @@ function __sbFromUtf8(bytes) {
|
|
|
4045
4045
|
// #232 — a fetch() request body for the wire: text as-is, and an ArrayBuffer or
|
|
4046
4046
|
// any view as its bytes (one char per byte) flagged bodyBinary, instead of
|
|
4047
4047
|
// String(view), which sent "0,1,2,...".
|
|
4048
|
+
// A body stream the runtime made over a value (Response/Request body): that value. Bindings
|
|
4049
|
+
// that take a body at once (R2 and KV put, outbound fetch) read it without draining the
|
|
4050
|
+
// stream, so put(key, request.body) works as in Workers. Other values pass through.
|
|
4051
|
+
function __sbUnwrapBody(value) {
|
|
4052
|
+
return value != null && value._sbBody !== undefined ? value._sbBody : value;
|
|
4053
|
+
}
|
|
4048
4054
|
function __sbRequestBody(body) {
|
|
4055
|
+
body = __sbUnwrapBody(body);
|
|
4049
4056
|
if (body == null) return { body: null };
|
|
4050
4057
|
if (!__sbIsStr(body) && body.byteLength !== undefined) {
|
|
4051
4058
|
const bytes = body.buffer !== undefined ? new Uint8Array(body.buffer, body.byteOffset, body.byteLength) : body;
|
|
@@ -4966,6 +4973,7 @@ globalThis.__sbInstallBindings = function (target, bindings) {
|
|
|
4966
4973
|
return type === "json" ? JSON.parse(text) : text;
|
|
4967
4974
|
},
|
|
4968
4975
|
put(key, value, options) {
|
|
4976
|
+
value = __sbUnwrapBody(value);
|
|
4969
4977
|
const msg = { ns, key: String(key), value: "" };
|
|
4970
4978
|
if (value != null && !__sbIsStr(value) && value.byteLength !== undefined) {
|
|
4971
4979
|
// ArrayBuffer or any ArrayBufferView: its bytes, not String(view) ("0,1,2,...").
|
|
@@ -5011,12 +5019,15 @@ globalThis.__sbInstallBindings = function (target, bindings) {
|
|
|
5011
5019
|
const name = bindings.r2[i];
|
|
5012
5020
|
target[name] = {
|
|
5013
5021
|
put(key, value, options) {
|
|
5022
|
+
value = __sbUnwrapBody(value);
|
|
5014
5023
|
const o = options || {};
|
|
5015
5024
|
// #56 — the body goes out of band where the transport allows it.
|
|
5025
|
+
// Bytes go in as bytes: a byte view or the bytes of a body stream (request.body).
|
|
5026
|
+
const raw = value == null ? "" : !__sbIsStr(value) && value.byteLength !== undefined ? __sbToBytes(value.buffer !== undefined ? new Uint8Array(value.buffer, value.byteOffset, value.byteLength) : value) : String(value);
|
|
5016
5027
|
return __sbR2Put(
|
|
5017
5028
|
name,
|
|
5018
5029
|
String(key),
|
|
5019
|
-
|
|
5030
|
+
raw,
|
|
5020
5031
|
o.httpMetadata || {},
|
|
5021
5032
|
o.customMetadata || {},
|
|
5022
5033
|
).object;
|
|
@@ -5802,6 +5813,8 @@ async function __sbQueueWithDrain(result, tasks) {
|
|
|
5802
5813
|
function __sbDecodeIngress(request) {
|
|
5803
5814
|
const raw = __sbRawBody(request);
|
|
5804
5815
|
if (raw == null) return;
|
|
5816
|
+
// Raw bytes, one character each: arrayBuffer(), blob() and body read them as bytes.
|
|
5817
|
+
request._bodyLatin1 = true;
|
|
5805
5818
|
let decoded = null;
|
|
5806
5819
|
request.text = function () {
|
|
5807
5820
|
if (decoded === null) decoded = __sbFromUtf8(raw);
|
package/test/native-request.ts
CHANGED
|
@@ -20,7 +20,7 @@ const CASES = [
|
|
|
20
20
|
];
|
|
21
21
|
const handler = `export default {async fetch(request) {
|
|
22
22
|
// The incoming request: its body is a ReadableStream, and text() still reads it.
|
|
23
|
-
if (request.method === "POST") return new Response((request.body instanceof ReadableStream) + ":" +
|
|
23
|
+
if (request.method === "POST") { const viaStream = await new Response(request.body).text(); return new Response((request.body instanceof ReadableStream) + ":" + viaStream); }
|
|
24
24
|
const cases = [
|
|
25
25
|
${CASES.map((c) => ` () => (${c}),`).join("\n")}
|
|
26
26
|
];
|
package/test/native-utf8.ts
CHANGED
|
@@ -17,7 +17,14 @@ const text = "/café/日本/😀/x";
|
|
|
17
17
|
const handler = `
|
|
18
18
|
const URL = "shadowed";
|
|
19
19
|
export default {
|
|
20
|
-
async fetch() {
|
|
20
|
+
async fetch(request) {
|
|
21
|
+
// put(key, request.body) and fetch(url, { body: request.body }) are the Workers idiom:
|
|
22
|
+
// body is a ReadableStream, and the bindings take the value it was made from.
|
|
23
|
+
if (request.method === "POST") {
|
|
24
|
+
env.UPLOADS.put("upload", request.body);
|
|
25
|
+
await env.KV.put("upload", request.body);
|
|
26
|
+
return Response.json({ r2: env.UPLOADS.get("upload").text(), kv: await env.KV.get("upload") });
|
|
27
|
+
}
|
|
21
28
|
await env.KV.put("k", ${JSON.stringify(text)});
|
|
22
29
|
const kv = await env.KV.get("k");
|
|
23
30
|
await env.DB.prepare("CREATE TABLE t (v TEXT)").run();
|
|
@@ -38,7 +45,7 @@ function freePort(): number {
|
|
|
38
45
|
}
|
|
39
46
|
|
|
40
47
|
const workdir = await mkdtemp(join(tmpdir(), "sb-native-utf8-"));
|
|
41
|
-
const bindings = { ...EMPTY_BINDINGS, kv: ["KV"], d1: ["DB"] };
|
|
48
|
+
const bindings = { ...EMPTY_BINDINGS, kv: ["KV"], d1: ["DB"], r2: ["UPLOADS"] };
|
|
42
49
|
const broker = createBroker({
|
|
43
50
|
db: ":memory:",
|
|
44
51
|
token: "native-utf8",
|
|
@@ -113,7 +120,9 @@ try {
|
|
|
113
120
|
);
|
|
114
121
|
assert.match(body.error, /tabellà/);
|
|
115
122
|
assert.equal(body.url, "shadowed");
|
|
116
|
-
|
|
123
|
+
const stored = await (await fetch(`http://127.0.0.1:${port}/`, { method: "POST", body: "uploaded é" })).json();
|
|
124
|
+
assert.deepEqual(stored, { r2: "uploaded é", kv: "uploaded é" }, "put(key, request.body) stores the request body");
|
|
125
|
+
console.log("Non-ASCII KV, D1 and error text round-trip through the broker; a handler-level URL stays scoped; put(key, request.body) stores the body");
|
|
117
126
|
} finally {
|
|
118
127
|
if (server) {
|
|
119
128
|
server.kill(9);
|