sproutboat 0.11.4 → 0.11.6

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
@@ -8,7 +8,44 @@ a breaking change.
8
8
  Reconstructed from git history on 2026-09-03 for everything through v0.4.11;
9
9
  maintained going forward by the `release` skill.
10
10
 
11
- ## [Unreleased]
11
+ ## [0.11.6] - 2026-09-15
12
+ ### Fixed
13
+ - The 100% CPU livelock on a `fetch` handler that resolves a promise with a
14
+ plain object (baronunread/sproutboat#168). Root cause was
15
+ `__Porffor_promise_resolve`'s `.then` duck-type probe spinning forever on a
16
+ prototype-chain fixed point instead of terminating; patched in
17
+ `@sproutboat/toolchain` alongside the other local Porffor patches. Bumped
18
+ `@sproutboat/toolchain` to `^0.4.1` to pick it up.
19
+ - `new Response(r2Object.body)` corrupted any binary R2 object (each byte
20
+ 0x80-0xFF doubled on the wire): the runtime couldn't tell opaque bytes
21
+ apart from a Latin-1-range string, so it UTF-8-encoded them a second time
22
+ (baronunread/sproutboat#177). Added `R2Object.toResponse(init?)`, which
23
+ serves the bytes unmodified. Bumped `@sproutboat/runtime` to `^0.7.1` and
24
+ `@sproutboat/wire` to `^0.6.0` to pick this up, along with the R2
25
+ file-backed storage rework (baronunread/sproutboat#56) both depend on.
26
+ - `sproutboat-env.d.ts`'s generated secret comment named the wrong command
27
+ (`secrets set`; the real one is `secrets put`), `SproutboatHandler` claimed
28
+ there is no `ctx`/`ctx.waitUntil` (shipped since #57/#171), and
29
+ `docs/standalone.md` still said a missing secret refuses the boot (0.11.1
30
+ changed that to throwing on first use instead).
31
+
32
+ ## [0.11.5] - 2026-09-14
33
+ ### Fixed
34
+ - Bumped `@sproutboat/runtime` to `^0.6.6`. `__sbEntry` called `handlers.fetch()`
35
+ (and the `scheduled`/`queue`/`alarm` trigger paths) with no try/catch at all:
36
+ any synchronous throw, or a rejected async handler promise, propagated all
37
+ the way up and crashed the whole process: not just the request that hit
38
+ it, but every other in-flight and future request on that sprout, until
39
+ whatever supervises it restarted the binary (baronunread/sproutboat#179). A
40
+ throwing/rejecting `fetch()` now returns a 500 instead. `scheduled()`/
41
+ `alarm()` still reply `204` either way (no failure signal existed before
42
+ this either); a throwing `queue()` falls through to the existing
43
+ default-ack pass, same as a handler that never calls `ack()`/`retry()` on
44
+ every message.
45
+ - Upgrading the CLI is the only way to pick this up. The runtime prelude is
46
+ embedded in the CLI binary when that binary is built, so a project's own
47
+ `@sproutboat/runtime` dependency has no effect on `--standalone` or
48
+ `--target host` builds.
12
49
 
13
50
  ## [0.11.4] - 2026-09-14
14
51
  ### Fixed
@@ -507,7 +544,8 @@ its own package.
507
544
  - Renamed the package to `sproutboat` (was `@sproutboat/cli`); dropped the
508
545
  `sprout` bin alias in favour of a user-defined shell alias.
509
546
 
510
- [Unreleased]: https://github.com/baronunread/sproutboat-cli/compare/v0.11.4...HEAD
547
+ [Unreleased]: https://github.com/baronunread/sproutboat-cli/compare/v0.11.5...HEAD
548
+ [0.11.5]: https://github.com/baronunread/sproutboat-cli/compare/v0.11.4...v0.11.5
511
549
  [0.11.4]: https://github.com/baronunread/sproutboat-cli/compare/v0.11.3...v0.11.4
512
550
  [0.11.3]: https://github.com/baronunread/sproutboat-cli/compare/v0.11.2...v0.11.3
513
551
  [0.11.2]: https://github.com/baronunread/sproutboat-cli/compare/v0.11.1...v0.11.2
package/SURFACE.md CHANGED
@@ -3,7 +3,7 @@
3
3
  > Generated by `src/surface.test.ts` from `src/surface.ts` + the pinned
4
4
  > toolchain constants. Do not edit by hand — run `UPDATE_SURFACE=1 bun test`.
5
5
 
6
- **Package:** `sproutboat` 0.11.4 · runs on Bun (use `bunx`, not `npx`)
6
+ **Package:** `sproutboat` 0.11.6 · runs on Bun (use `bunx`, not `npx`)
7
7
 
8
8
  ## Commands
9
9
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sproutboat",
3
- "version": "0.11.4",
3
+ "version": "0.11.6",
4
4
  "description": "Wrangler-shaped CLI for Sproutboat. Deploys workers to any control plane via --api-url / SPROUTBOAT_API_URL.",
5
5
  "keywords": [
6
6
  "cli",
@@ -64,9 +64,9 @@
64
64
  "@sproutboat/artifact": "^0.2.0",
65
65
  "@sproutboat/assets": "^0.2.0",
66
66
  "@sproutboat/config": "^0.3.0",
67
- "@sproutboat/runtime": "^0.6.5",
68
- "@sproutboat/toolchain": "^0.4.0",
69
- "@sproutboat/wire": "^0.5.0",
67
+ "@sproutboat/runtime": "^0.7.1",
68
+ "@sproutboat/toolchain": "^0.4.1",
69
+ "@sproutboat/wire": "^0.6.0",
70
70
  "esbuild": "^0.28.2"
71
71
  },
72
72
  "devDependencies": {
@@ -78,10 +78,10 @@
78
78
  "typescript": "7.0.2"
79
79
  },
80
80
  "optionalDependencies": {
81
- "@sproutboat/cli-darwin-arm64": "0.11.4",
82
- "@sproutboat/cli-darwin-x64": "0.11.4",
83
- "@sproutboat/cli-linux-arm64": "0.11.4",
84
- "@sproutboat/cli-linux-x64": "0.11.4"
81
+ "@sproutboat/cli-darwin-arm64": "0.11.6",
82
+ "@sproutboat/cli-darwin-x64": "0.11.6",
83
+ "@sproutboat/cli-linux-arm64": "0.11.6",
84
+ "@sproutboat/cli-linux-x64": "0.11.6"
85
85
  },
86
86
  "engines": {
87
87
  "node": ">=18"
package/src/types-gen.ts CHANGED
@@ -34,7 +34,7 @@ function entries(config: SproutboatConfig): Entry[] {
34
34
  out.push({ name, type: "string", note: `vars: ${JSON.stringify(value)}` });
35
35
  }
36
36
  for (const name of config.secrets ?? []) {
37
- out.push({ name, type: "string", note: "secret, set with `sproutboat secrets set`" });
37
+ out.push({ name, type: "string", note: "secret, set with `sproutboat secrets put`" });
38
38
  }
39
39
  for (const { binding } of resourceRefs(config.kv_namespaces)) out.push({ name: binding, type: "KVNamespace" });
40
40
  for (const { binding } of resourceRefs(config.d1_databases)) out.push({ name: binding, type: "D1Database" });
@@ -56,20 +56,46 @@ declare global {
56
56
 
57
57
  // ---------------------------------------------------------------- R2
58
58
 
59
+ interface R2HttpMetadata {
60
+ contentType?: string;
61
+ contentDisposition?: string;
62
+ contentEncoding?: string;
63
+ contentLanguage?: string;
64
+ cacheControl?: string;
65
+ }
66
+
59
67
  interface R2Object {
60
68
  key: string;
61
69
  size: number;
62
70
  etag: string;
71
+ /** `etag`, quoted, ready for an `ETag` response header. */
72
+ httpEtag: string;
63
73
  uploaded: string;
74
+ httpMetadata: R2HttpMetadata;
64
75
  customMetadata?: Record<string, string>;
65
76
  /**
66
77
  * The object as a string. An object is held whole in memory on the way in
67
78
  * and on the way out, so keep them small.
79
+ *
80
+ * `new Response(obj.body, ...)` corrupts any non-ASCII/binary content: the
81
+ * runtime cannot tell these bytes apart from a string a handler built, so
82
+ * it UTF-8-encodes them a second time (baronunread/sproutboat#177). Use
83
+ * `obj.toResponse()` instead for binary content — it serves the bytes
84
+ * unmodified.
68
85
  */
69
86
  body: string;
87
+ text(): string;
88
+ json(): unknown;
89
+ /**
90
+ * A `Response` that serves `body` byte-for-byte, unlike `new
91
+ * Response(obj.body, ...)` (see `body` above). `init.headers`/`.status`
92
+ * merge in as usual; `content-type` defaults from `httpMetadata` and
93
+ * `etag` from `httpEtag` when not set.
94
+ */
95
+ toResponse(init?: { status?: number; headers?: HeadersInit }): Response;
70
96
  }
71
97
 
72
- type R2ObjectHead = Omit<R2Object, "body">;
98
+ type R2ObjectHead = Omit<R2Object, "body" | "text" | "json" | "toResponse">;
73
99
 
74
100
  interface R2Objects {
75
101
  objects: R2ObjectHead[];
@@ -78,6 +104,7 @@ declare global {
78
104
  }
79
105
 
80
106
  interface R2PutOptions {
107
+ httpMetadata?: R2HttpMetadata;
81
108
  customMetadata?: Record<string, string>;
82
109
  }
83
110
 
@@ -226,16 +253,22 @@ declare global {
226
253
  scheduledTime: number;
227
254
  }
228
255
 
256
+ /**
257
+ * `env` is a global, not a parameter. `ctx` is the second argument, and its
258
+ * only member is `waitUntil`: work that outlives the response, drained
259
+ * in-process and capped at 25s.
260
+ */
261
+ interface ExecutionContext {
262
+ waitUntil(promise: Promise<unknown>): void;
263
+ }
264
+
229
265
  /**
230
266
  * The default export.
231
- *
232
- * `fetch` takes only a request: `env` is a global, and there is no `ctx`, so
233
- * there is no `ctx.waitUntil`. Use a queue for work that outlives a response.
234
267
  */
235
268
  interface SproutboatHandler<QueueBody = unknown> {
236
- fetch(request: Request): Response | Promise<Response>;
237
- scheduled?(event: ScheduledEvent): void | Promise<void>;
238
- queue?(batch: MessageBatch<QueueBody>): void | Promise<void>;
269
+ fetch(request: Request, ctx: ExecutionContext): Response | Promise<Response>;
270
+ scheduled?(event: ScheduledEvent, ctx: ExecutionContext): void | Promise<void>;
271
+ queue?(batch: MessageBatch<QueueBody>, ctx: ExecutionContext): void | Promise<void>;
239
272
  }
240
273
 
241
274
  /**