@sproutboat/runtime 0.42.1 → 0.43.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 (42) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/package.json +1 -1
  3. package/src/native-fetch-prelude.js +186 -24
  4. package/src/transport-embedded.js +35 -6
  5. package/test/native-base64.ts +12 -26
  6. package/test/native-bodies.ts +19 -26
  7. package/test/native-byte-text.ts +3 -19
  8. package/test/native-compile.ts +163 -0
  9. package/test/native-cpu-requests.ts +3 -20
  10. package/test/native-cpu-stamp.ts +3 -20
  11. package/test/native-crypto.ts +3 -22
  12. package/test/native-domexception.ts +3 -21
  13. package/test/native-errors.ts +12 -24
  14. package/test/native-events.ts +21 -25
  15. package/test/native-fetch-request.ts +11 -20
  16. package/test/native-getrandomvalues.ts +3 -21
  17. package/test/native-headers.ts +3 -21
  18. package/test/native-http.ts +10 -20
  19. package/test/native-iterators.ts +24 -27
  20. package/test/native-json-strings.ts +3 -21
  21. package/test/native-json.ts +15 -25
  22. package/test/native-jwt.ts +3 -22
  23. package/test/native-model-resolver.ts +5 -24
  24. package/test/native-raw-body.ts +7 -53
  25. package/test/native-request-log.ts +197 -0
  26. package/test/native-request-streaming.ts +3 -19
  27. package/test/native-request.ts +24 -26
  28. package/test/native-response-defaults.ts +4 -22
  29. package/test/native-service-bindings.ts +53 -27
  30. package/test/native-sockets-broker.ts +65 -20
  31. package/test/native-sockets-starttls.ts +404 -0
  32. package/test/native-streaming.ts +4 -22
  33. package/test/native-streams.ts +22 -25
  34. package/test/native-subtle.ts +3 -21
  35. package/test/native-textdecoder-encodings.ts +3 -21
  36. package/test/native-timers.ts +4 -22
  37. package/test/native-unhandled-rejection.ts +3 -20
  38. package/test/native-url.ts +15 -25
  39. package/test/native-utf8.ts +15 -21
  40. package/test/native-waituntil.ts +7 -23
  41. package/test/native-web-apis.ts +3 -22
  42. 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 { preludePath, TRANSPORT_MARKER, transportPath, wrapNativeFetchHandler } from "../src/wrap";
7
7
 
8
8
  // A promise that rejects with nobody waiting on it (a binding call that failed and was never
@@ -50,9 +50,6 @@ for (const date of ["2026-09-28", "2026-10-24"]) {
50
50
  const workdir = await mkdtemp(join(tmpdir(), "sb-native-unhandled-"));
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 unhandled rejection test needs node on PATH");
56
53
  const [core, broker] = await Promise.all([
57
54
  readFile(preludePath, "utf8"),
58
55
  readFile(transportPath("broker"), "utf8"),
@@ -64,21 +61,7 @@ for (const date of ["2026-09-28", "2026-10-24"]) {
64
61
  generated,
65
62
  wrapNativeFetchHandler(handler, core.replace(TRANSPORT_MARKER, broker), undefined, undefined, undefined, date),
66
63
  );
67
- const compile = Bun.spawn(
68
- [node, resolve(porffor, "runtime/index.js"), "native", generated, "-o", binary, "-s", "-O0"],
69
- {
70
- cwd: workdir,
71
- env: process.env,
72
- stdout: "pipe",
73
- stderr: "pipe",
74
- },
75
- );
76
- const [exitCode, stdout, stderr] = await Promise.all([
77
- compile.exited,
78
- new Response(compile.stdout).text(),
79
- new Response(compile.stderr).text(),
80
- ]);
81
- assert.equal(exitCode, 0, `Porffor native compile failed:\n${stderr}\n${stdout}`);
64
+ await compileNative({ generated, binary, dir: workdir });
82
65
 
83
66
  const port = freePort();
84
67
  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
  // URL as WHATWG defines it (Bun's URL is the reference): base URLs, setters on every
@@ -84,14 +84,7 @@ function freePort(): number {
84
84
  const workdir = await mkdtemp(join(tmpdir(), "sb-native-url-"));
85
85
  let server: ReturnType<typeof Bun.spawn> | undefined;
86
86
  try {
87
- const porffor = await ensurePorffor();
88
- const node = Bun.which("node");
89
- if (!node) throw new Error("native streams test needs node on PATH");
90
-
91
- const [core, broker] = await Promise.all([
92
- readFile(preludePath, "utf8"),
93
- readFile(transportPath("broker"), "utf8"),
94
- ]);
87
+ const [core, broker] = await Promise.all([readFile(preludePath, "utf8"), readFile(transportPath("broker"), "utf8")]);
95
88
  assert(core.includes(TRANSPORT_MARKER), "runtime prelude is missing its transport marker");
96
89
  const prelude = core.replace(TRANSPORT_MARKER, broker);
97
90
  const generated = join(workdir, "url.generated.js");
@@ -99,18 +92,7 @@ try {
99
92
  await writeFile(generated, wrapNativeFetchHandler(handler, prelude));
100
93
  await rm(binary, { force: true });
101
94
 
102
- const compile = Bun.spawn([node, resolve(porffor, "runtime/index.js"), "native", generated, "-o", binary, "-s", "-O0"], {
103
- cwd: workdir,
104
- env: process.env,
105
- stdout: "pipe",
106
- stderr: "pipe",
107
- });
108
- const [exitCode, stdout, stderr] = await Promise.all([
109
- compile.exited,
110
- new Response(compile.stdout).text(),
111
- new Response(compile.stderr).text(),
112
- ]);
113
- assert.equal(exitCode, 0, `Porffor native compile failed:\n${stderr}\n${stdout}`);
95
+ await compileNative({ generated, binary, dir: workdir });
114
96
 
115
97
  const port = freePort();
116
98
  server = Bun.spawn([binary], {
@@ -133,9 +115,17 @@ try {
133
115
  // SAFETY: the handler above is the only producer of this body: a JSON array with one
134
116
  // string per case; the comparison below reports any entry that differs.
135
117
  const got = JSON.parse(await (await fetch(`http://127.0.0.1:${port}/`)).text()) as string[];
136
- const diffs = CASES.flatMap((c, i) => (got[i] === want[i] ? [] : [`${c}\n want ${JSON.stringify(want[i])}\n got ${JSON.stringify(got[i])}`]));
137
- assert.deepEqual(diffs, [], `URL differs from WHATWG in ${diffs.length} of ${CASES.length} cases:\n${diffs.join("\n")}`);
138
- console.log(`Native URL matches WHATWG URL in all ${CASES.length} cases: base URLs, setters, hash, ports, dot segments, encoding, searchParams`);
118
+ const diffs = CASES.flatMap((c, i) =>
119
+ got[i] === want[i] ? [] : [`${c}\n want ${JSON.stringify(want[i])}\n got ${JSON.stringify(got[i])}`],
120
+ );
121
+ assert.deepEqual(
122
+ diffs,
123
+ [],
124
+ `URL differs from WHATWG in ${diffs.length} of ${CASES.length} cases:\n${diffs.join("\n")}`,
125
+ );
126
+ console.log(
127
+ `Native URL matches WHATWG URL in all ${CASES.length} cases: base URLs, setters, hash, ports, dot segments, encoding, searchParams`,
128
+ );
139
129
  } finally {
140
130
  if (server) {
141
131
  server.kill(9);
@@ -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
 
@@ -54,10 +54,6 @@ const broker = createBroker({
54
54
  const brokerServer = listen(broker, "127.0.0.1", 0);
55
55
  let server: ReturnType<typeof Bun.spawn> | undefined;
56
56
  try {
57
- const porffor = await ensurePorffor({ cacheRoot: workdir });
58
- const node = Bun.which("node");
59
- if (!node) throw new Error("native UTF-8 test needs node on PATH");
60
-
61
57
  const [core, transport] = await Promise.all([
62
58
  readFile(preludePath, "utf8"),
63
59
  readFile(transportPath("broker"), "utf8"),
@@ -65,20 +61,12 @@ try {
65
61
  assert(core.includes(TRANSPORT_MARKER), "runtime prelude is missing its transport marker");
66
62
  const generated = join(workdir, "utf8.generated.js");
67
63
  const binary = join(workdir, "utf8-native");
68
- await writeFile(generated, wrapNativeFetchHandler(handler, core.replace(TRANSPORT_MARKER, transport), {}, bindings, undefined, "2026-10-21"));
64
+ await writeFile(
65
+ generated,
66
+ wrapNativeFetchHandler(handler, core.replace(TRANSPORT_MARKER, transport), {}, bindings, undefined, "2026-10-21"),
67
+ );
69
68
 
70
- const compile = Bun.spawn([node, resolve(porffor, "runtime/index.js"), "native", generated, "-o", binary, "-s", "-O0"], {
71
- cwd: workdir,
72
- env: process.env,
73
- stdout: "pipe",
74
- stderr: "pipe",
75
- });
76
- const [exitCode, stdout, stderr] = await Promise.all([
77
- compile.exited,
78
- new Response(compile.stdout).text(),
79
- new Response(compile.stderr).text(),
80
- ]);
81
- assert.equal(exitCode, 0, `Porffor native compile failed:\n${stderr}\n${stdout}`);
69
+ await compileNative({ generated, binary, dir: workdir, cacheRoot: workdir });
82
70
 
83
71
  const port = freePort();
84
72
  server = Bun.spawn([binary], {
@@ -121,8 +109,14 @@ try {
121
109
  assert.match(body.error, /tabellà/);
122
110
  assert.equal(body.url, "shadowed");
123
111
  const stored = await (await fetch(`http://127.0.0.1:${port}/`, { method: "POST", body: "uploaded é" })).json();
124
- assert.deepEqual(stored, { r2: "uploaded é", kv: "uploaded é", stream: "uploaded é" }, "put(key, request.body) stores the request body, and body reads as UTF-8");
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");
112
+ assert.deepEqual(
113
+ stored,
114
+ { r2: "uploaded é", kv: "uploaded é", stream: "uploaded é" },
115
+ "put(key, request.body) stores the request body, and body reads as UTF-8",
116
+ );
117
+ console.log(
118
+ "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",
119
+ );
126
120
  } finally {
127
121
  if (server) {
128
122
  server.kill(9);
@@ -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
  // ctx.waitUntil work runs after the response, as in workerd: the response does not wait
@@ -32,14 +32,7 @@ function freePort(): number {
32
32
  const workdir = await mkdtemp(join(tmpdir(), "sb-native-waituntil-"));
33
33
  let server: ReturnType<typeof Bun.spawn> | undefined;
34
34
  try {
35
- const porffor = await ensurePorffor();
36
- const node = Bun.which("node");
37
- if (!node) throw new Error("native streams test needs node on PATH");
38
-
39
- const [core, broker] = await Promise.all([
40
- readFile(preludePath, "utf8"),
41
- readFile(transportPath("broker"), "utf8"),
42
- ]);
35
+ const [core, broker] = await Promise.all([readFile(preludePath, "utf8"), readFile(transportPath("broker"), "utf8")]);
43
36
  assert(core.includes(TRANSPORT_MARKER), "runtime prelude is missing its transport marker");
44
37
  const prelude = core.replace(TRANSPORT_MARKER, broker);
45
38
  const generated = join(workdir, "waituntil.generated.js");
@@ -47,18 +40,7 @@ try {
47
40
  await writeFile(generated, wrapNativeFetchHandler(handler, prelude, undefined, undefined, undefined, "2026-10-01"));
48
41
  await rm(binary, { force: true });
49
42
 
50
- const compile = Bun.spawn([node, resolve(porffor, "runtime/index.js"), "native", generated, "-o", binary, "-s", "-O0"], {
51
- cwd: workdir,
52
- env: process.env,
53
- stdout: "pipe",
54
- stderr: "pipe",
55
- });
56
- const [exitCode, stdout, stderr] = await Promise.all([
57
- compile.exited,
58
- new Response(compile.stdout).text(),
59
- new Response(compile.stderr).text(),
60
- ]);
61
- assert.equal(exitCode, 0, `Porffor native compile failed:\n${stderr}\n${stdout}`);
43
+ await compileNative({ generated, binary, dir: workdir });
62
44
 
63
45
  const port = freePort();
64
46
  server = Bun.spawn([binary], {
@@ -97,7 +79,9 @@ try {
97
79
  assert.equal(await cron.text(), "");
98
80
  assert.equal(await (await fetch(`http://127.0.0.1:${port}/cron`)).text(), "*/5 * * * *");
99
81
  assert.equal(server.exitCode, null, "the server exited after a scheduled trigger");
100
- console.log(`Native waitUntil runs after the response (${Math.round(elapsed)} ms) and completes; scheduled() answers 204`);
82
+ console.log(
83
+ `Native waitUntil runs after the response (${Math.round(elapsed)} ms) and completes; scheduled() answers 204`,
84
+ );
101
85
  } finally {
102
86
  if (server) {
103
87
  server.kill(9);
@@ -1,9 +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";
6
- import { bearsslEnv } from "./bearssl";
4
+ import { join } from "node:path";
5
+ import { compileNative } from "./native-compile";
7
6
  import { preludePath, TRANSPORT_MARKER, transportPath, wrapNativeFetchHandler } from "../src/wrap";
8
7
 
9
8
  // Web API behaviour the W2 conformance suite found wrong, compared with Bun: Headers
@@ -59,10 +58,6 @@ function freePort(): number {
59
58
  const workdir = await mkdtemp(join(tmpdir(), "sb-native-web-apis-"));
60
59
  let server: ReturnType<typeof Bun.spawn> | undefined;
61
60
  try {
62
- const porffor = await ensurePorffor();
63
- const node = Bun.which("node");
64
- if (!node) throw new Error("native streams test needs node on PATH");
65
-
66
61
  const [core, broker] = await Promise.all([readFile(preludePath, "utf8"), readFile(transportPath("broker"), "utf8")]);
67
62
  assert(core.includes(TRANSPORT_MARKER), "runtime prelude is missing its transport marker");
68
63
  const prelude = core.replace(TRANSPORT_MARKER, broker);
@@ -71,21 +66,7 @@ try {
71
66
  await writeFile(generated, wrapNativeFetchHandler(handler, prelude));
72
67
  await rm(binary, { force: true });
73
68
 
74
- const compile = Bun.spawn(
75
- [node, resolve(porffor, "runtime/index.js"), "native", generated, "-o", binary, "-s", "-O0"],
76
- {
77
- cwd: workdir,
78
- env: { ...process.env, ...(await bearsslEnv()) },
79
- stdout: "pipe",
80
- stderr: "pipe",
81
- },
82
- );
83
- const [exitCode, stdout, stderr] = await Promise.all([
84
- compile.exited,
85
- new Response(compile.stdout).text(),
86
- new Response(compile.stderr).text(),
87
- ]);
88
- assert.equal(exitCode, 0, `Porffor native compile failed:\n${stderr}\n${stdout}`);
69
+ await compileNative({ generated, binary, dir: workdir, bearssl: true });
89
70
 
90
71
  const port = freePort();
91
72
  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
  // WebSockets from compatibility date 2026-10-30: a WebSocketPair whose ends both stay in
@@ -105,9 +105,6 @@ function freePort(): number {
105
105
  const workdir = await mkdtemp(join(tmpdir(), "sb-native-websockets-"));
106
106
  const servers: ReturnType<typeof Bun.spawn>[] = [];
107
107
  try {
108
- const porffor = await ensurePorffor();
109
- const node = Bun.which("node");
110
- if (!node) throw new Error("native WebSocket test needs node on PATH");
111
108
  const [core, broker] = await Promise.all([readFile(preludePath, "utf8"), readFile(transportPath("broker"), "utf8")]);
112
109
  assert(core.includes(TRANSPORT_MARKER), "runtime prelude is missing its transport marker");
113
110
  const start = async (date: string) => {
@@ -117,20 +114,7 @@ try {
117
114
  generated,
118
115
  wrapNativeFetchHandler(handler, core.replace(TRANSPORT_MARKER, broker), {}, undefined, undefined, date),
119
116
  );
120
- const compile = Bun.spawn(
121
- [node, resolve(porffor, "runtime/index.js"), "native", generated, "-o", binary, "-s", "-O0"],
122
- {
123
- cwd: workdir,
124
- stdout: "pipe",
125
- stderr: "pipe",
126
- },
127
- );
128
- const [exitCode, stdout, stderr] = await Promise.all([
129
- compile.exited,
130
- new Response(compile.stdout).text(),
131
- new Response(compile.stderr).text(),
132
- ]);
133
- assert.equal(exitCode, 0, `Porffor native compile failed:\n${stderr}\n${stdout}`);
117
+ await compileNative({ generated, binary, dir: workdir });
134
118
  const port = freePort();
135
119
  const server = Bun.spawn([binary], {
136
120
  cwd: workdir,