@sproutboat/runtime 0.12.0 → 0.12.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 CHANGED
@@ -1,5 +1,12 @@
1
1
  # @sproutboat/runtime
2
2
 
3
+ ## 0.12.1
4
+
5
+ ### Patch Changes
6
+
7
+ - ed9640a: Point Porffor patch diagnostics, runtime comments, and historical notes to the
8
+ shared upstream notes in the Sproutboat platform repository.
9
+
3
10
  ## 0.12.0
4
11
 
5
12
  ### Minor Changes
@@ -131,11 +138,11 @@
131
138
 
132
139
  Verified end-to-end against both transports (kitchen-sink's full conformance suite, 24/24 standalone + 27/27 broker) and with new unit tests for file-backed persistence, the `:memory:` fallback, and a real binary round-trip over the v1 wire protocol.
133
140
 
134
- **Known limitation found while verifying this, not fixed here:** a binary `put()` body (bytes ≥ 0x80) is corrupted on the way *in*, before it ever reaches storage — `porf_native_fetch_read_value` (the function every inline-C string read in the runtime goes through) always UTF-8-encodes a Latin-1-range string on read, which is correct for genuine text but wrong for opaque bytes coming off a raw HTTP body. This predates this change (the old SQLite-blob path had the identical corruption) and is not something the storage backend can fix — it needs an input-side counterpart to `#176`'s `x-sb-raw-body` marker, or real `ArrayBuffer`/`Uint8Array` body support (the upstream ask already filed as Draft G in `patches/UPSTREAM.md`).
141
+ **Known limitation found while verifying this, not fixed here:** a binary `put()` body (bytes ≥ 0x80) is corrupted on the way *in*, before it ever reaches storage: `porf_native_fetch_read_value` (the function every inline-C string read in the runtime goes through) always UTF-8-encodes a Latin-1-range string on read, which is correct for genuine text but wrong for opaque bytes coming off a raw HTTP body. This predates this change (the old SQLite-blob path had the identical corruption) and is not something the storage backend can fix. It needs an input-side counterpart to `#176`'s `x-sb-raw-body` marker, or real `ArrayBuffer`/`Uint8Array` body support (the upstream ask was filed as Porffor #386 and later retracted; see `https://github.com/baronunread/sproutboat/blob/main/patches/upstream/request-body-report-386.md`).
135
142
 
136
143
  ### Patch Changes
137
144
 
138
- - 188d8e4: Fix (baronunread/sproutboat#184): a `put()` body (R2, both transports) with bytes ≥ 0x80 was corrupted on the way *in*, before storage — `porf_native_fetch_read_value` (the one function every inline-C string read in the runtime funnels through) always UTF-8-encodes a Latin-1-range value, correct for genuine text, wrong for opaque bytes off a raw HTTP upload. A 256-byte binary upload landed as 384 stored bytes.
145
+ - 188d8e4: Fix (baronunread/sproutboat#184): a `put()` body (R2, both transports) with bytes ≥ 0x80 was corrupted on the way *in*, before storage: `porf_native_fetch_read_value` (the one function every inline-C string read in the runtime funnels through) always UTF-8-encodes a Latin-1-range value, correct for genuine text, wrong for opaque bytes off a raw HTTP upload. A 256-byte binary upload landed as 384 stored bytes.
139
146
 
140
147
  `__sbR2PutRaw` (embedded) and `__sbCallBin` (broker, the only caller that ever passes it a real body) now try `porf_native_fetch_read_raw_bytes` first, falling back to the normal encoding path only for a genuine multi-byte JS string (which isn't a "bytestring" and so isn't eligible for the raw read anyway). This predates the #56 storage change — the old SQLite-blob `put()` called the identical `read_value`, so it was equally corrupted; the existing R2 conformance check never caught it because it only ever exercises a plain ASCII string.
141
148
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sproutboat/runtime",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {
@@ -1,5 +1,5 @@
1
1
  // Prepended to every handler by tools/compile.ts, before Porffor's native-fetch
2
- // esbuild bundle. Porffor's runtime/fetch-globals.js (checked through alpha-4)
2
+ // bundled handler. Porffor's runtime/fetch-globals.js (checked through alpha-4)
3
3
  // gives URL (href/origin/pathname/search only) and Response without a static
4
4
  // json(). This adds, additively, the rest of the WHATWG surface Worker code
5
5
  // expects: URLSearchParams (read + write), URL.prototype.searchParams and the
@@ -9,10 +9,12 @@
9
9
  // formData()/bytes() (#60). Each is feature-detected; delete a block once
10
10
  // Porffor ships that global. crypto.scryptVerify (#153) is a Sproutboat
11
11
  // extension, not WHATWG, and stays.
12
- // Tracked upstream in patches/UPSTREAM.md.
12
+ // Tracked in the platform's upstream notes:
13
+ // https://github.com/baronunread/sproutboat/blob/main/patches/upstream/whatwg-fetch-globals.md
13
14
  //
14
15
  // Declared before it is referenced: a getter body that names a later top-level
15
- // class throws ReferenceError in Porffor (see patches/UPSTREAM.md draft B).
16
+ // class throws ReferenceError in Porffor. See:
17
+ // https://github.com/baronunread/sproutboat/blob/main/patches/upstream/class-declaration-scope.md
16
18
 
17
19
  // Duck-typing helpers, `typeof`-free (the repo's anti-slop lint bans `typeof`;
18
20
  // these express the same spec-mandated checks and are verified under Porffor
@@ -386,7 +388,8 @@ if (!("bytes" in Response.prototype)) {
386
388
  // Porffor's URL exposes href / origin / pathname / search only. Add the rest of
387
389
  // the WHATWG read surface, derived from `origin` (scheme://host[:port]).
388
390
  // `hash` is always '' server-side — browsers strip the fragment before the
389
- // request, so there is nothing to recover. Tracked upstream (patches/UPSTREAM.md).
391
+ // request, so there is nothing to recover. See:
392
+ // https://github.com/baronunread/sproutboat/blob/main/patches/upstream/whatwg-fetch-globals.md
390
393
  function __sbDefineURLAccessor(name, get) {
391
394
  if (!(name in URL.prototype)) Object.defineProperty(URL.prototype, name, { configurable: true, get });
392
395
  }
@@ -1816,7 +1819,8 @@ function __sbTriggerAuthed(request) {
1816
1819
  // #163 — the connection's remote address, for `request.cf.clientIp`.
1817
1820
  //
1818
1821
  // `x-sb-remote-addr` is the TCP peer, appended by the server (see
1819
- // patches/UPSTREAM.md #163) and stripped from anything a client sends, so it
1822
+ // https://github.com/baronunread/sproutboat/blob/main/patches/upstream/remote-address.md)
1823
+ // and stripped from anything a client sends, so it
1820
1824
  // cannot be forged. With no proxy in front, that is the client. Behind one, set
1821
1825
  // `SB_TRUSTED_PROXIES` to a comma-separated list of trusted CIDRs (or bare IPs):
1822
1826
  // when the peer is trusted, the client is the rightmost `x-forwarded-for` entry
@@ -1,7 +1,7 @@
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 { dirname, join, resolve } from "node:path";
4
+ import { join, resolve } from "node:path";
5
5
  import { ensurePorffor } from "../../toolchain/src/acquire";
6
6
  import { preludePath, TRANSPORT_MARKER, transportPath, wrapNativeFetchHandler } from "../src/wrap";
7
7
 
@@ -18,6 +18,7 @@ const expected = {
18
18
  };
19
19
 
20
20
  const handler = `
21
+ import { keyBytes } from "./crypto-helper.js";
21
22
  const hex = (buffer) => {
22
23
  const bytes = new Uint8Array(buffer);
23
24
  let out = "";
@@ -28,7 +29,7 @@ export default {
28
29
  async fetch() {
29
30
  const key = await crypto.subtle.importKey(
30
31
  "raw",
31
- new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]),
32
+ keyBytes(),
32
33
  { name: "HMAC", hash: "SHA-256" },
33
34
  false,
34
35
  ["sign"],
@@ -67,9 +68,8 @@ const workdir = await mkdtemp(join(tmpdir(), "sb-native-crypto-"));
67
68
  let server: ReturnType<typeof Bun.spawn> | undefined;
68
69
  try {
69
70
  const porffor = await ensurePorffor({ cacheRoot: workdir });
70
- const esbuild = Bun.which("esbuild");
71
71
  const node = Bun.which("node");
72
- if (!esbuild || !node) throw new Error("native crypto test needs esbuild and node on PATH");
72
+ if (!node) throw new Error("native crypto test needs node on PATH");
73
73
 
74
74
  const [core, broker] = await Promise.all([
75
75
  readFile(preludePath, "utf8"),
@@ -79,12 +79,15 @@ try {
79
79
  const prelude = core.replace(TRANSPORT_MARKER, broker);
80
80
  const generated = join(workdir, "crypto.generated.js");
81
81
  const binary = join(workdir, "crypto-native");
82
+ // This is a direct ESM import into Porffor, with no bundler in front of it.
83
+ await writeFile(join(workdir, "crypto-helper.js"),
84
+ "export const keyBytes = () => Uint8Array.from({ length: 16 }, (_, index) => index + 1);\n");
82
85
  await writeFile(generated, wrapNativeFetchHandler(handler, prelude));
83
86
  await rm(binary, { force: true });
84
87
 
85
88
  const compile = Bun.spawn([node, resolve(porffor, "runtime/index.js"), "native", generated, "-o", binary, "-s", "-O0"], {
86
89
  cwd: workdir,
87
- env: { ...process.env, PATH: `${dirname(esbuild)}:${process.env.PATH ?? ""}` },
90
+ env: process.env,
88
91
  stdout: "pipe",
89
92
  stderr: "pipe",
90
93
  });