@sproutboat/runtime 0.42.2 → 0.44.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/package.json +1 -1
  3. package/src/egress.test.ts +76 -0
  4. package/src/native-fetch-prelude.js +164 -14
  5. package/src/transport-embedded.js +78 -7
  6. package/test/native-base64.ts +12 -26
  7. package/test/native-bodies.ts +19 -26
  8. package/test/native-byte-text.ts +3 -19
  9. package/test/native-compile.ts +163 -0
  10. package/test/native-cpu-requests.ts +3 -20
  11. package/test/native-cpu-stamp.ts +3 -20
  12. package/test/native-crypto.ts +3 -22
  13. package/test/native-domexception.ts +3 -21
  14. package/test/native-errors.ts +12 -24
  15. package/test/native-events.ts +21 -25
  16. package/test/native-fetch-request.ts +11 -20
  17. package/test/native-getrandomvalues.ts +3 -21
  18. package/test/native-headers.ts +3 -21
  19. package/test/native-http.ts +10 -20
  20. package/test/native-iterators.ts +24 -27
  21. package/test/native-json-strings.ts +3 -21
  22. package/test/native-json.ts +15 -25
  23. package/test/native-jwt.ts +3 -22
  24. package/test/native-model-resolver.ts +5 -24
  25. package/test/native-r2-transfer.ts +101 -0
  26. package/test/native-raw-body.ts +7 -53
  27. package/test/native-request-log.ts +197 -0
  28. package/test/native-request-streaming.ts +3 -19
  29. package/test/native-request.ts +24 -26
  30. package/test/native-response-defaults.ts +4 -22
  31. package/test/native-service-bindings.ts +53 -27
  32. package/test/native-sockets-broker.ts +3 -20
  33. package/test/native-streaming.ts +4 -22
  34. package/test/native-streams.ts +22 -25
  35. package/test/native-subtle.ts +3 -21
  36. package/test/native-textdecoder-encodings.ts +3 -21
  37. package/test/native-timers.ts +4 -22
  38. package/test/native-unhandled-rejection.ts +3 -20
  39. package/test/native-url.ts +15 -25
  40. package/test/native-utf8.ts +15 -21
  41. package/test/native-waituntil.ts +7 -23
  42. package/test/native-web-apis.ts +3 -22
  43. package/test/native-websockets.ts +3 -19
@@ -1,8 +1,8 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
3
3
  import { tmpdir } from "node:os";
4
- import { join, resolve } from "node:path";
5
- import { ensurePorffor } from "../../toolchain/src/acquire";
4
+ import { join } from "node:path";
5
+ import { compileNative } from "./native-compile";
6
6
  import { createBroker, listen } from "../../wire/src/broker";
7
7
  import { EMPTY_BINDINGS, preludePath, TRANSPORT_MARKER, transportPath, wrapNativeFetchHandler } from "../src/wrap";
8
8
 
@@ -124,21 +124,18 @@ const brokerServer = listen(broker, "127.0.0.1", 0);
124
124
  const servers: ReturnType<typeof Bun.spawn>[] = [];
125
125
 
126
126
  // Compiles the handler at `date` and starts it; resolves to a GET helper for it.
127
- async function start(porffor: string, node: string, source: string, date: string) {
127
+ async function start(source: string, date: string) {
128
128
  const port = freePort();
129
129
  const generated = join(workdir, `fetch-request-${date}.generated.js`);
130
130
  const binary = join(workdir, `fetch-request-${date}`);
131
131
  await writeFile(generated, wrapNativeFetchHandler(handler, source, {}, EMPTY_BINDINGS, undefined, date));
132
- const compile = Bun.spawn(
133
- [node, resolve(porffor, "runtime/index.js"), "native", generated, "-o", binary, "-s", "-O0"],
134
- {
135
- cwd: workdir,
136
- stdout: "pipe",
137
- stderr: "pipe",
138
- },
139
- );
140
- const [exitCode, stderr] = await Promise.all([compile.exited, new Response(compile.stderr).text()]);
141
- assert.equal(exitCode, 0, `Porffor native compile at ${date} failed:\n${stderr}`);
132
+ await compileNative({
133
+ generated,
134
+ binary,
135
+ dir: workdir,
136
+ cacheRoot: workdir,
137
+ failure: `Porffor native compile at ${date} failed`,
138
+ });
142
139
  const server = Bun.spawn([binary], {
143
140
  cwd: workdir,
144
141
  env: {
@@ -174,19 +171,13 @@ async function start(porffor: string, node: string, source: string, date: string
174
171
  }
175
172
 
176
173
  try {
177
- const porffor = await ensurePorffor({ cacheRoot: workdir });
178
- const node = Bun.which("node");
179
- if (!node) throw new Error("native fetch-request test needs node on PATH");
180
174
  const [core, transport] = await Promise.all([
181
175
  readFile(preludePath, "utf8"),
182
176
  readFile(transportPath("broker"), "utf8"),
183
177
  ]);
184
178
  assert(core.includes(TRANSPORT_MARKER), "runtime prelude is missing its transport marker");
185
179
  const source = core.replace(TRANSPORT_MARKER, transport);
186
- const [strict, legacy] = await Promise.all([
187
- start(porffor, node, source, "2026-10-23"),
188
- start(porffor, node, source, "2026-10-22"),
189
- ]);
180
+ const [strict, legacy] = await Promise.all([start(source, "2026-10-23"), start(source, "2026-10-22")]);
190
181
 
191
182
  const echo = (method: string, from: string | null, type: string | null, body: string) => ({
192
183
  method,
@@ -1,8 +1,8 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
3
3
  import { tmpdir } from "node:os";
4
- import { join, resolve } from "node:path";
5
- import { ensurePorffor } from "../../toolchain/src/acquire";
4
+ import { join } from "node:path";
5
+ import { compileNative } from "./native-compile";
6
6
  import { preludePath, TRANSPORT_MARKER, transportPath, wrapNativeFetchHandler } from "../src/wrap";
7
7
 
8
8
  // crypto.getRandomValues on wider integer views, in a compiled handler, compared with Bun. It filled only the
@@ -50,10 +50,6 @@ function freePort(): number {
50
50
  const workdir = await mkdtemp(join(tmpdir(), "sb-native-getrandomvalues-"));
51
51
  let server: ReturnType<typeof Bun.spawn> | undefined;
52
52
  try {
53
- const porffor = await ensurePorffor();
54
- const node = Bun.which("node");
55
- if (!node) throw new Error("native streams test needs node on PATH");
56
-
57
53
  const [core, broker] = await Promise.all([readFile(preludePath, "utf8"), readFile(transportPath("broker"), "utf8")]);
58
54
  assert(core.includes(TRANSPORT_MARKER), "runtime prelude is missing its transport marker");
59
55
  const prelude = core.replace(TRANSPORT_MARKER, broker);
@@ -62,21 +58,7 @@ try {
62
58
  await writeFile(generated, wrapNativeFetchHandler(handler, prelude));
63
59
  await rm(binary, { force: true });
64
60
 
65
- const compile = Bun.spawn(
66
- [node, resolve(porffor, "runtime/index.js"), "native", generated, "-o", binary, "-s", "-O0"],
67
- {
68
- cwd: workdir,
69
- env: process.env,
70
- stdout: "pipe",
71
- stderr: "pipe",
72
- },
73
- );
74
- const [exitCode, stdout, stderr] = await Promise.all([
75
- compile.exited,
76
- new Response(compile.stdout).text(),
77
- new Response(compile.stderr).text(),
78
- ]);
79
- assert.equal(exitCode, 0, `Porffor native compile failed:\n${stderr}\n${stdout}`);
61
+ await compileNative({ generated, binary, dir: workdir });
80
62
 
81
63
  const port = freePort();
82
64
  server = Bun.spawn([binary], {
@@ -1,8 +1,8 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
3
3
  import { tmpdir } from "node:os";
4
- import { join, resolve } from "node:path";
5
- import { ensurePorffor } from "../../toolchain/src/acquire";
4
+ import { join } from "node:path";
5
+ import { compileNative } from "./native-compile";
6
6
  import { preludePath, TRANSPORT_MARKER, transportPath, wrapNativeFetchHandler } from "../src/wrap";
7
7
 
8
8
  // Headers validates a name and trims a value in C before it falls back to the regular expression and
@@ -54,10 +54,6 @@ function expectedValue(value: string): string {
54
54
  const workdir = await mkdtemp(join(tmpdir(), "sb-native-headers-"));
55
55
  let server: ReturnType<typeof Bun.spawn> | undefined;
56
56
  try {
57
- const porffor = await ensurePorffor();
58
- const node = Bun.which("node");
59
- if (!node) throw new Error("native headers test needs node on PATH");
60
-
61
57
  const [core, broker] = await Promise.all([readFile(preludePath, "utf8"), readFile(transportPath("broker"), "utf8")]);
62
58
  assert(core.includes(TRANSPORT_MARKER), "runtime prelude is missing its transport marker");
63
59
  const prelude = core.replace(TRANSPORT_MARKER, broker);
@@ -66,21 +62,7 @@ try {
66
62
  await writeFile(generated, wrapNativeFetchHandler(handler, prelude));
67
63
  await rm(binary, { force: true });
68
64
 
69
- const compile = Bun.spawn(
70
- [node, resolve(porffor, "runtime/index.js"), "native", generated, "-o", binary, "-s", "-O0"],
71
- {
72
- cwd: workdir,
73
- env: process.env,
74
- stdout: "pipe",
75
- stderr: "pipe",
76
- },
77
- );
78
- const [exitCode, stdout, stderr] = await Promise.all([
79
- compile.exited,
80
- new Response(compile.stdout).text(),
81
- new Response(compile.stderr).text(),
82
- ]);
83
- assert.equal(exitCode, 0, `Porffor native compile failed:\n${stderr}\n${stdout}`);
65
+ await compileNative({ generated, binary, dir: workdir });
84
66
 
85
67
  const port = freePort();
86
68
  server = Bun.spawn([binary], {
@@ -3,8 +3,8 @@ import { createHash } from "node:crypto";
3
3
  import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
4
4
  import { connect } from "node:net";
5
5
  import { tmpdir } from "node:os";
6
- import { join, resolve } from "node:path";
7
- import { ensurePorffor } from "../../toolchain/src/acquire";
6
+ import { join } from "node:path";
7
+ import { compileNative } from "./native-compile";
8
8
  import { preludePath, TRANSPORT_MARKER, transportPath, wrapNativeFetchHandler } from "../src/wrap";
9
9
 
10
10
  // The native HTTP server's wire behaviour with raw sockets: keep-alive, pipelining (responses in
@@ -188,6 +188,13 @@ async function suite(port: number, label: string) {
188
188
  assert.equal(got[0].body.toString(), "n=close");
189
189
  }
190
190
 
191
+ // #236: an HTTP/1.0 request line (nginx's default proxy_http_version) is answered, not refused with 505.
192
+ {
193
+ const got = await exchange(port, [Buffer.from("GET /n/old HTTP/1.0\r\nHost: t\r\n\r\n")], 1);
194
+ assert.equal(got[0].status, 200, `${label}: HTTP/1.0 request status`);
195
+ assert.equal(got[0].body.toString(), "n=old", `${label}: HTTP/1.0 request body`);
196
+ }
197
+
191
198
  // 150 connections at once, 4 pipelined requests each, with garbage-heavy requests mixed in.
192
199
  {
193
200
  const results = await Promise.all(
@@ -210,9 +217,6 @@ async function suite(port: number, label: string) {
210
217
  const workdir = await mkdtemp(join(tmpdir(), "sb-native-http-"));
211
218
  const servers: Array<ReturnType<typeof Bun.spawn>> = [];
212
219
  try {
213
- const porffor = await ensurePorffor();
214
- const node = Bun.which("node");
215
- if (!node) throw new Error("native http test needs node on PATH");
216
220
  const [core, broker] = await Promise.all([readFile(preludePath, "utf8"), readFile(transportPath("broker"), "utf8")]);
217
221
  assert(core.includes(TRANSPORT_MARKER), "runtime prelude is missing its transport marker");
218
222
  const generated = join(workdir, "http.generated.js");
@@ -228,21 +232,7 @@ try {
228
232
  "2026-10-21",
229
233
  ),
230
234
  );
231
- const compile = Bun.spawn(
232
- [node, resolve(porffor, "runtime/index.js"), "native", generated, "-o", binary, "-s", "-O0"],
233
- {
234
- cwd: workdir,
235
- env: process.env,
236
- stdout: "pipe",
237
- stderr: "pipe",
238
- },
239
- );
240
- const [exitCode, stdout, stderr] = await Promise.all([
241
- compile.exited,
242
- new Response(compile.stdout).text(),
243
- new Response(compile.stderr).text(),
244
- ]);
245
- assert.equal(exitCode, 0, `Porffor native compile failed:\n${stderr}\n${stdout}`);
235
+ await compileNative({ generated, binary, dir: workdir });
246
236
 
247
237
  for (const every of ["1", "8"]) {
248
238
  const port = freePort();
@@ -1,16 +1,31 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
3
3
  import { tmpdir } from "node:os";
4
- import { join, resolve } from "node:path";
5
- import { ensurePorffor } from "../../toolchain/src/acquire";
4
+ import { join } from "node:path";
5
+ import { compileNative } from "./native-compile";
6
6
  import { preludePath, TRANSPORT_MARKER, transportPath, wrapNativeFetchHandler } from "../src/wrap";
7
7
 
8
8
  const expected = {
9
- query: [["a", "1"], ["a", "2"], ["b", "3"]],
10
- keys: ["a", "a", "b"], values: ["1", "2", "3"],
11
- live: ["x", "y", true], form: [["x", "1"], ["x", "2"]],
12
- formLive: ["x", "y", true], self: true,
13
- headers: [["a", "1"], ["b", "2"]], headersMap: 2, headersFromEntries: { a: "1", b: "2" },
9
+ query: [
10
+ ["a", "1"],
11
+ ["a", "2"],
12
+ ["b", "3"],
13
+ ],
14
+ keys: ["a", "a", "b"],
15
+ values: ["1", "2", "3"],
16
+ live: ["x", "y", true],
17
+ form: [
18
+ ["x", "1"],
19
+ ["x", "2"],
20
+ ],
21
+ formLive: ["x", "y", true],
22
+ self: true,
23
+ headers: [
24
+ ["a", "1"],
25
+ ["b", "2"],
26
+ ],
27
+ headersMap: 2,
28
+ headersFromEntries: { a: "1", b: "2" },
14
29
  };
15
30
  const handler = `export default { fetch(request) {
16
31
  if (new URL(request.url).pathname === "/null") return new Response(null);
@@ -47,14 +62,7 @@ function freePort(): number {
47
62
  const workdir = await mkdtemp(join(tmpdir(), "sb-native-iterators-"));
48
63
  let server: ReturnType<typeof Bun.spawn> | undefined;
49
64
  try {
50
- const porffor = await ensurePorffor({ cacheRoot: workdir });
51
- const node = Bun.which("node");
52
- if (!node) throw new Error("native iterators test needs node on PATH");
53
-
54
- const [core, broker] = await Promise.all([
55
- readFile(preludePath, "utf8"),
56
- readFile(transportPath("broker"), "utf8"),
57
- ]);
65
+ const [core, broker] = await Promise.all([readFile(preludePath, "utf8"), readFile(transportPath("broker"), "utf8")]);
58
66
  assert(core.includes(TRANSPORT_MARKER), "runtime prelude is missing its transport marker");
59
67
  const prelude = core.replace(TRANSPORT_MARKER, broker);
60
68
  const generated = join(workdir, "iterators.generated.js");
@@ -62,18 +70,7 @@ try {
62
70
  await writeFile(generated, wrapNativeFetchHandler(handler, prelude));
63
71
  await rm(binary, { force: true });
64
72
 
65
- const compile = Bun.spawn([node, resolve(porffor, "runtime/index.js"), "native", generated, "-o", binary, "-s", "-O0"], {
66
- cwd: workdir,
67
- env: process.env,
68
- stdout: "pipe",
69
- stderr: "pipe",
70
- });
71
- const [exitCode, stdout, stderr] = await Promise.all([
72
- compile.exited,
73
- new Response(compile.stdout).text(),
74
- new Response(compile.stderr).text(),
75
- ]);
76
- assert.equal(exitCode, 0, `Porffor native compile failed:\n${stderr}\n${stdout}`);
73
+ await compileNative({ generated, binary, dir: workdir, cacheRoot: workdir });
77
74
 
78
75
  const port = freePort();
79
76
  server = Bun.spawn([binary], {
@@ -1,8 +1,8 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
3
3
  import { tmpdir } from "node:os";
4
- import { join, resolve } from "node:path";
5
- import { ensurePorffor } from "../../toolchain/src/acquire";
4
+ import { join } from "node:path";
5
+ import { compileNative } from "./native-compile";
6
6
  import { preludePath, TRANSPORT_MARKER, transportPath, wrapNativeFetchHandler } from "../src/wrap";
7
7
 
8
8
  // JSON.stringify of strings as the spec defines it (Bun is the reference): quotes and backslash,
@@ -61,10 +61,6 @@ function freePort(): number {
61
61
  const workdir = await mkdtemp(join(tmpdir(), "sb-native-json-strings-"));
62
62
  let server: ReturnType<typeof Bun.spawn> | undefined;
63
63
  try {
64
- const porffor = await ensurePorffor();
65
- const node = Bun.which("node");
66
- if (!node) throw new Error("native streams test needs node on PATH");
67
-
68
64
  const [core, broker] = await Promise.all([readFile(preludePath, "utf8"), readFile(transportPath("broker"), "utf8")]);
69
65
  assert(core.includes(TRANSPORT_MARKER), "runtime prelude is missing its transport marker");
70
66
  const prelude = core.replace(TRANSPORT_MARKER, broker);
@@ -73,21 +69,7 @@ try {
73
69
  await writeFile(generated, wrapNativeFetchHandler(handler, prelude));
74
70
  await rm(binary, { force: true });
75
71
 
76
- const compile = Bun.spawn(
77
- [node, resolve(porffor, "runtime/index.js"), "native", generated, "-o", binary, "-s", "-O0"],
78
- {
79
- cwd: workdir,
80
- env: process.env,
81
- stdout: "pipe",
82
- stderr: "pipe",
83
- },
84
- );
85
- const [exitCode, stdout, stderr] = await Promise.all([
86
- compile.exited,
87
- new Response(compile.stdout).text(),
88
- new Response(compile.stderr).text(),
89
- ]);
90
- assert.equal(exitCode, 0, `Porffor native compile failed:\n${stderr}\n${stdout}`);
72
+ await compileNative({ generated, binary, dir: workdir });
91
73
 
92
74
  const port = freePort();
93
75
  server = Bun.spawn([binary], {
@@ -1,8 +1,8 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
3
3
  import { tmpdir } from "node:os";
4
- import { join, resolve } from "node:path";
5
- import { ensurePorffor } from "../../toolchain/src/acquire";
4
+ import { join } from "node:path";
5
+ import { compileNative } from "./native-compile";
6
6
  import { preludePath, TRANSPORT_MARKER, transportPath, wrapNativeFetchHandler } from "../src/wrap";
7
7
 
8
8
  // JSON.stringify as the spec defines it (Bun is the reference): toJSON on Date, URL and
@@ -46,14 +46,7 @@ function freePort(): number {
46
46
  const workdir = await mkdtemp(join(tmpdir(), "sb-native-json-"));
47
47
  let server: ReturnType<typeof Bun.spawn> | undefined;
48
48
  try {
49
- const porffor = await ensurePorffor();
50
- const node = Bun.which("node");
51
- if (!node) throw new Error("native streams test needs node on PATH");
52
-
53
- const [core, broker] = await Promise.all([
54
- readFile(preludePath, "utf8"),
55
- readFile(transportPath("broker"), "utf8"),
56
- ]);
49
+ const [core, broker] = await Promise.all([readFile(preludePath, "utf8"), readFile(transportPath("broker"), "utf8")]);
57
50
  assert(core.includes(TRANSPORT_MARKER), "runtime prelude is missing its transport marker");
58
51
  const prelude = core.replace(TRANSPORT_MARKER, broker);
59
52
  const generated = join(workdir, "json.generated.js");
@@ -61,18 +54,7 @@ try {
61
54
  await writeFile(generated, wrapNativeFetchHandler(handler, prelude));
62
55
  await rm(binary, { force: true });
63
56
 
64
- const compile = Bun.spawn([node, resolve(porffor, "runtime/index.js"), "native", generated, "-o", binary, "-s", "-O0"], {
65
- cwd: workdir,
66
- env: process.env,
67
- stdout: "pipe",
68
- stderr: "pipe",
69
- });
70
- const [exitCode, stdout, stderr] = await Promise.all([
71
- compile.exited,
72
- new Response(compile.stdout).text(),
73
- new Response(compile.stderr).text(),
74
- ]);
75
- assert.equal(exitCode, 0, `Porffor native compile failed:\n${stderr}\n${stdout}`);
57
+ await compileNative({ generated, binary, dir: workdir });
76
58
 
77
59
  const port = freePort();
78
60
  server = Bun.spawn([binary], {
@@ -95,9 +77,17 @@ try {
95
77
  // SAFETY: the handler above is the only producer of this body: a JSON array with one
96
78
  // string per case; the comparison below reports any entry that differs.
97
79
  const got = JSON.parse(await (await fetch(`http://127.0.0.1:${port}/`)).text()) as string[];
98
- const diffs = CASES.flatMap((c, i) => (got[i] === want[i] ? [] : [`${c}\n want ${JSON.stringify(want[i])}\n got ${JSON.stringify(got[i])}`]));
99
- assert.deepEqual(diffs, [], `JSON.stringify differs from the spec in ${diffs.length} of ${CASES.length} cases:\n${diffs.join("\n")}`);
100
- console.log(`Native JSON.stringify honours toJSON in all ${CASES.length} cases: Date, URL, user objects, nested, undefined`);
80
+ const diffs = CASES.flatMap((c, i) =>
81
+ got[i] === want[i] ? [] : [`${c}\n want ${JSON.stringify(want[i])}\n got ${JSON.stringify(got[i])}`],
82
+ );
83
+ assert.deepEqual(
84
+ diffs,
85
+ [],
86
+ `JSON.stringify differs from the spec in ${diffs.length} of ${CASES.length} cases:\n${diffs.join("\n")}`,
87
+ );
88
+ console.log(
89
+ `Native JSON.stringify honours toJSON in all ${CASES.length} cases: Date, URL, user objects, nested, undefined`,
90
+ );
101
91
  } finally {
102
92
  if (server) {
103
93
  server.kill(9);
@@ -2,9 +2,8 @@ import assert from "node:assert/strict";
2
2
  import { createHmac } from "node:crypto";
3
3
  import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
4
4
  import { tmpdir } from "node:os";
5
- import { join, resolve } from "node:path";
6
- import { ensurePorffor } from "../../toolchain/src/acquire";
7
- import { bearsslEnv } from "./bearssl";
5
+ import { join } from "node:path";
6
+ import { compileNative } from "./native-compile";
8
7
  import { preludePath, TRANSPORT_MARKER, transportPath, wrapNativeFetchHandler } from "../src/wrap";
9
8
 
10
9
  // The pieces of a JWT check (base64url decode, TextEncoder, HMAC verify) against values that do
@@ -276,10 +275,6 @@ assert.equal(
276
275
  const workdir = await mkdtemp(join(tmpdir(), "sb-native-jwt-"));
277
276
  let server: ReturnType<typeof Bun.spawn> | undefined;
278
277
  try {
279
- const porffor = await ensurePorffor();
280
- const node = Bun.which("node");
281
- if (!node) throw new Error("native jwt test needs node on PATH");
282
-
283
278
  const [core, broker] = await Promise.all([readFile(preludePath, "utf8"), readFile(transportPath("broker"), "utf8")]);
284
279
  assert(core.includes(TRANSPORT_MARKER), "runtime prelude is missing its transport marker");
285
280
  const prelude = core.replace(TRANSPORT_MARKER, broker);
@@ -287,21 +282,7 @@ try {
287
282
  const binary = join(workdir, "jwt-native");
288
283
  await writeFile(generated, wrapNativeFetchHandler(handler, prelude));
289
284
 
290
- const compile = Bun.spawn(
291
- [node, resolve(porffor, "runtime/index.js"), "native", generated, "-o", binary, "-s", "-O0"],
292
- {
293
- cwd: workdir,
294
- env: { ...process.env, ...(await bearsslEnv()) },
295
- stdout: "pipe",
296
- stderr: "pipe",
297
- },
298
- );
299
- const [exitCode, stdout, stderr] = await Promise.all([
300
- compile.exited,
301
- new Response(compile.stdout).text(),
302
- new Response(compile.stderr).text(),
303
- ]);
304
- assert.equal(exitCode, 0, `Porffor native compile failed:\n${stderr}\n${stdout}`);
285
+ await compileNative({ generated, binary, dir: workdir, bearssl: true });
305
286
 
306
287
  const port = freePort();
307
288
  server = Bun.spawn([binary], {
@@ -1,11 +1,11 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
3
3
  import { tmpdir } from "node:os";
4
- import { join, resolve } from "node:path";
5
- import { ensurePorffor } from "../../toolchain/src/acquire";
4
+ import { join } from "node:path";
5
+ import { compileNative } from "./native-compile";
6
6
  import { preludePath, TRANSPORT_MARKER, transportPath, wrapNativeFetchHandler } from "../src/wrap";
7
7
 
8
- const expected = {a: "user", b: "account", again: "user", plural: "user", custom: "user"};
8
+ const expected = { a: "user", b: "account", again: "user", plural: "user", custom: "user" };
9
9
  const handler = `
10
10
  const makeResolver = ({usePlural, schema}) => {
11
11
  return (model) => {
@@ -31,7 +31,6 @@ export default {fetch() {
31
31
  custom:makeResolver({schema:{user:{modelName:"members"}},usePlural:false})("members")});
32
32
  }};`;
33
33
 
34
-
35
34
  function freePort(): number {
36
35
  const listener = Bun.listen({ hostname: "127.0.0.1", port: 0, socket: { data() {} } });
37
36
  const port = listener.port;
@@ -42,14 +41,7 @@ function freePort(): number {
42
41
  const workdir = await mkdtemp(join(tmpdir(), "sb-native-model-resolver-"));
43
42
  let server: ReturnType<typeof Bun.spawn> | undefined;
44
43
  try {
45
- const porffor = await ensurePorffor();
46
- const node = Bun.which("node");
47
- if (!node) throw new Error("native streams test needs node on PATH");
48
-
49
- const [core, broker] = await Promise.all([
50
- readFile(preludePath, "utf8"),
51
- readFile(transportPath("broker"), "utf8"),
52
- ]);
44
+ const [core, broker] = await Promise.all([readFile(preludePath, "utf8"), readFile(transportPath("broker"), "utf8")]);
53
45
  assert(core.includes(TRANSPORT_MARKER), "runtime prelude is missing its transport marker");
54
46
  const prelude = core.replace(TRANSPORT_MARKER, broker);
55
47
  const generated = join(workdir, "model-resolver.generated.js");
@@ -57,18 +49,7 @@ try {
57
49
  await writeFile(generated, wrapNativeFetchHandler(handler, prelude));
58
50
  await rm(binary, { force: true });
59
51
 
60
- const compile = Bun.spawn([node, resolve(porffor, "runtime/index.js"), "native", generated, "-o", binary, "-s", "-O0"], {
61
- cwd: workdir,
62
- env: process.env,
63
- stdout: "pipe",
64
- stderr: "pipe",
65
- });
66
- const [exitCode, stdout, stderr] = await Promise.all([
67
- compile.exited,
68
- new Response(compile.stdout).text(),
69
- new Response(compile.stderr).text(),
70
- ]);
71
- assert.equal(exitCode, 0, `Porffor native compile failed:\n${stderr}\n${stdout}`);
52
+ await compileNative({ generated, binary, dir: workdir });
72
53
 
73
54
  const port = freePort();
74
55
  server = Bun.spawn([binary], {
@@ -0,0 +1,101 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { compileNative } from "./native-compile";
6
+ import { createBroker, listen } from "../../wire/src/broker";
7
+ import { EMPTY_BINDINGS, preludePath, TRANSPORT_MARKER, transportPath, wrapNativeFetchHandler } from "../src/wrap";
8
+
9
+ // Direct R2 transfer on the promise-returning binding (compatibility date 2026-10-26 and later): createUploadUrl()
10
+ // and createDownloadUrl() exist there as on the legacy binding, return promises, and answer {url, expiresAt}.
11
+ const handler = `
12
+ export default {
13
+ async fetch(request, env) {
14
+ const url = new URL(request.url);
15
+ if (url.pathname === "/ping") return new Response("pong");
16
+ const up = env.UPLOADS.createUploadUrl("k", { maxBytes: 10 });
17
+ const upIsPromise = up instanceof Promise;
18
+ const upload = await up;
19
+ await env.UPLOADS.put("k", "hello");
20
+ const down = env.UPLOADS.createDownloadUrl("k");
21
+ const downIsPromise = down instanceof Promise;
22
+ const download = await down;
23
+ return Response.json({
24
+ upIsPromise,
25
+ downIsPromise,
26
+ upload: { url: typeof upload.url, expiresAt: typeof upload.expiresAt },
27
+ download: { url: typeof download.url, expiresAt: typeof download.expiresAt },
28
+ });
29
+ },
30
+ };
31
+ `;
32
+
33
+ function freePort(): number {
34
+ const listener = Bun.listen({ hostname: "127.0.0.1", port: 0, socket: { data() {} } });
35
+ const port = listener.port;
36
+ listener.stop(true);
37
+ return port;
38
+ }
39
+
40
+ const port = freePort();
41
+ const workdir = await mkdtemp(join(tmpdir(), "sb-native-r2-transfer-"));
42
+ const bindings = { ...EMPTY_BINDINGS, r2: ["UPLOADS"] };
43
+ const broker = createBroker({
44
+ db: join(workdir, "state.sqlite"),
45
+ token: "native-r2",
46
+ bindings: { ...bindings, resources: {} },
47
+ });
48
+ const brokerServer = listen(broker, "127.0.0.1", 0);
49
+ let server: ReturnType<typeof Bun.spawn> | undefined;
50
+ const get = async (path: string) => {
51
+ const res = await fetch(`http://127.0.0.1:${port}${path}`, { signal: AbortSignal.timeout(20_000) });
52
+ return { status: res.status, text: await res.text() };
53
+ };
54
+ try {
55
+ const [core, transport] = await Promise.all([
56
+ readFile(preludePath, "utf8"),
57
+ readFile(transportPath("broker"), "utf8"),
58
+ ]);
59
+ assert(core.includes(TRANSPORT_MARKER), "runtime prelude is missing its transport marker");
60
+ const generated = join(workdir, "r2.generated.js");
61
+ const binary = join(workdir, "r2-native");
62
+ await writeFile(
63
+ generated,
64
+ wrapNativeFetchHandler(handler, core.replace(TRANSPORT_MARKER, transport), {}, bindings, undefined, "2026-10-26"),
65
+ );
66
+ await compileNative({ generated, binary, dir: workdir, cacheRoot: workdir });
67
+ server = Bun.spawn([binary], {
68
+ cwd: workdir,
69
+ env: {
70
+ ...process.env,
71
+ PORT: String(port),
72
+ SB_BROKER_PORT: String(brokerServer.port),
73
+ SB_BROKER_TOKEN: "native-r2",
74
+ },
75
+ stdout: "ignore",
76
+ stderr: "inherit",
77
+ });
78
+ let started = false;
79
+ for (let attempt = 0; attempt < 50 && !started; attempt++) {
80
+ try {
81
+ started = (await get("/ping")).text === "pong";
82
+ } catch {
83
+ if (server.exitCode !== null) break;
84
+ await Bun.sleep(100);
85
+ }
86
+ }
87
+ assert(started, "compiled R2 handler did not start");
88
+ const answer = await get("/");
89
+ assert.equal(answer.status, 200, answer.text);
90
+ assert.deepEqual(JSON.parse(answer.text), {
91
+ upIsPromise: true,
92
+ downIsPromise: true,
93
+ upload: { url: "string", expiresAt: "string" },
94
+ download: { url: "string", expiresAt: "string" },
95
+ });
96
+ console.log("native R2 direct transfer: ok");
97
+ } finally {
98
+ server?.kill();
99
+ brokerServer.stop();
100
+ await rm(workdir, { recursive: true, force: true });
101
+ }