sproutboat 0.11.6 → 0.11.7

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/README.md CHANGED
@@ -150,6 +150,10 @@ files in the project and from its own `node_modules`, and may also export
150
150
  is a global, not a parameter, and every binding call is synchronous.
151
151
  [`examples/kitchen-sink/`](examples/kitchen-sink) uses every binding.
152
152
 
153
+ For objects larger than one request, use resumable R2 multipart uploads. The
154
+ [large-object guide](docs/r2-large-objects.md) covers part sizing, browser
155
+ chunking, resume state, cleanup, and the remaining download limitation.
156
+
153
157
  `sproutboat dev` runs against a real local broker, so bindings behave like
154
158
  production without a deploy — except `secrets`, which live only in the control
155
159
  plane. Put values in a `.dev.vars` file next to `sproutboat.jsonc`
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.6 · runs on Bun (use `bunx`, not `npx`)
6
+ **Package:** `sproutboat` 0.11.7 · runs on Bun (use `bunx`, not `npx`)
7
7
 
8
8
  ## Commands
9
9
 
@@ -74,7 +74,7 @@ usage: sproutboat <init [name] | check [project-dir] | types [project-dir] | dev
74
74
  | | |
75
75
  | --- | --- |
76
76
  | Zig | `0.16.0` (`zig cc -target x86_64-linux-musl`, static) |
77
- | Provenance stamp | `zig-musl/0.16.0+porffor/1f4ae4a+uws/360c276d` |
77
+ | Provenance stamp | `zig-musl/0.16.0+porffor/038f415+uws/360c276d` |
78
78
  | Artifact schema | `2` |
79
79
  | Runtime | `native-fetch` |
80
80
  | Capability profile | `http-sync-v0` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sproutboat",
3
- "version": "0.11.6",
3
+ "version": "0.11.7",
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.7.1",
68
- "@sproutboat/toolchain": "^0.4.1",
69
- "@sproutboat/wire": "^0.6.0",
67
+ "@sproutboat/runtime": "^0.9.3",
68
+ "@sproutboat/toolchain": "^0.4.5",
69
+ "@sproutboat/wire": "^0.7.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.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"
81
+ "@sproutboat/cli-darwin-arm64": "0.11.7",
82
+ "@sproutboat/cli-darwin-x64": "0.11.7",
83
+ "@sproutboat/cli-linux-arm64": "0.11.7",
84
+ "@sproutboat/cli-linux-x64": "0.11.7"
85
85
  },
86
86
  "engines": {
87
87
  "node": ">=18"
package/src/compile.ts CHANGED
@@ -209,6 +209,7 @@ export async function compileSprout(input: CompileInput): Promise<void> {
209
209
  input.compatibilityDate,
210
210
  input.appName,
211
211
  input.assets,
212
+ input.transport,
212
213
  ),
213
214
  );
214
215
 
package/src/dev.ts CHANGED
@@ -15,7 +15,7 @@ import { mkdir, readFile, rm } from "node:fs/promises";
15
15
  import { connect } from "node:net";
16
16
  import { dirname, resolve } from "node:path";
17
17
  import { buildArtifact } from "./build";
18
- import { createBroker, listen, type Bindings, type Broker } from "./broker";
18
+ import { createBroker, listen, listenTransfers, type Bindings, type Broker } from "./broker";
19
19
  import { jsonObject, parseJsonValue } from "./json";
20
20
  import { amber, dim, leaf, ok } from "./style";
21
21
  import type { SproutboatConfig } from "./config";
@@ -97,6 +97,8 @@ export type Running = {
97
97
  /** Set before a kill we initiated, so its exit code is not reported as a crash. */
98
98
  expected: boolean;
99
99
  port: number;
100
+ /** Private direct-transfer listener, absent in test candidates. */
101
+ transferPort?: number;
100
102
  enableDispatch: () => void;
101
103
  disableDispatch: () => void;
102
104
  };
@@ -142,10 +144,13 @@ async function start(input: DevInput, port: number): Promise<Running> {
142
144
  assetsDir: existsSync(assetsDir) ? assetsDir : undefined,
143
145
  dispatchEnabled: () => dispatchEnabled,
144
146
  });
145
- let server: ReturnType<typeof listen>;
147
+ let server: ReturnType<typeof listen> | undefined;
148
+ let transferServer: ReturnType<typeof listenTransfers> | undefined;
146
149
  try {
147
150
  server = listen(broker, "127.0.0.1", 0);
151
+ transferServer = listenTransfers(broker, "127.0.0.1", 0);
148
152
  } catch (error) {
153
+ server?.stop();
149
154
  broker.close();
150
155
  throw error;
151
156
  }
@@ -156,14 +161,15 @@ async function start(input: DevInput, port: number): Promise<Running> {
156
161
  env: {
157
162
  ...process.env,
158
163
  PORT: String(port),
159
- SB_BROKER_PORT: String(server.port),
164
+ SB_BROKER_PORT: String(server!.port),
160
165
  SB_BROKER_TOKEN: "sproutboat-dev",
161
166
  },
162
167
  stdout: "inherit",
163
168
  stderr: "inherit",
164
169
  });
165
170
  } catch (error) {
166
- server.stop();
171
+ server?.stop();
172
+ transferServer?.stop();
167
173
  broker.close();
168
174
  throw error;
169
175
  }
@@ -173,11 +179,13 @@ async function start(input: DevInput, port: number): Promise<Running> {
173
179
  artifactDir,
174
180
  broker,
175
181
  stopBroker: () => {
176
- server.stop();
182
+ server?.stop();
183
+ transferServer?.stop();
177
184
  broker.close();
178
185
  },
179
186
  expected: false,
180
187
  port,
188
+ transferPort: transferServer?.port,
181
189
  enableDispatch: () => {
182
190
  dispatchEnabled = true;
183
191
  },
@@ -262,6 +270,7 @@ export async function runDev(input: DevInput): Promise<void> {
262
270
  }
263
271
  running.enableDispatch();
264
272
  let activePort = running.port;
273
+ let activeTransferPort = running.transferPort;
265
274
  // Keep the public port stable while candidates boot on private ports. This is
266
275
  // what lets a failed startup leave the last known-good process reachable.
267
276
  let proxy: ReturnType<typeof Bun.serve>;
@@ -269,9 +278,13 @@ export async function runDev(input: DevInput): Promise<void> {
269
278
  proxy = Bun.serve({
270
279
  hostname: "127.0.0.1",
271
280
  port: input.port,
272
- fetch(request) {
281
+ maxRequestBodySize: 5 * 1024 * 1024 * 1024,
282
+ fetch(request, server) {
273
283
  const target = new URL(request.url);
274
- target.host = `127.0.0.1:${activePort}`;
284
+ const directTransfer = /^\/__sb\/r2\/transfer\/[A-Z][A-Z0-9_]*\/[0-9a-f]{24}$/.test(target.pathname);
285
+ if (directTransfer) server.timeout(request, 255);
286
+ if (directTransfer && !activeTransferPort) return new Response("direct transfers unavailable", { status: 503 });
287
+ target.host = `127.0.0.1:${directTransfer ? activeTransferPort : activePort}`;
275
288
  return fetch(target, {
276
289
  method: request.method,
277
290
  headers: request.headers,
@@ -373,6 +386,7 @@ export async function runDev(input: DevInput): Promise<void> {
373
386
  current = { ...current, ...next };
374
387
  const switchedAt = now();
375
388
  activePort = candidate.port;
389
+ activeTransferPort = candidate.transferPort;
376
390
  resetWatchers();
377
391
  stop(previous);
378
392
  watchExit(running);
@@ -74,8 +74,8 @@ declare global {
74
74
  httpMetadata: R2HttpMetadata;
75
75
  customMetadata?: Record<string, string>;
76
76
  /**
77
- * The object as a string. An object is held whole in memory on the way in
78
- * and on the way out, so keep them small.
77
+ * The object as a string. A get holds the object whole in memory, so keep
78
+ * downloads small until native download tickets are available.
79
79
  *
80
80
  * `new Response(obj.body, ...)` corrupts any non-ASCII/binary content: the
81
81
  * runtime cannot tell these bytes apart from a string a handler built, so
@@ -114,8 +114,48 @@ declare global {
114
114
  cursor?: string;
115
115
  }
116
116
 
117
+ interface R2UploadedPart {
118
+ partNumber: number;
119
+ etag: string;
120
+ }
121
+
122
+ interface R2MultipartUpload {
123
+ key: string;
124
+ uploadId: string;
125
+ uploadPart(partNumber: number, value: string): R2UploadedPart;
126
+ complete(parts: R2UploadedPart[]): R2ObjectHead;
127
+ abort(): void;
128
+ }
129
+
130
+ interface R2TransferTicket {
131
+ /** Same-origin reserved path. Send the file directly to this URL. */
132
+ url: string;
133
+ expiresAt: string;
134
+ }
135
+
136
+ interface R2UploadTicketOptions extends R2PutOptions {
137
+ /** Maximum accepted body size in bytes. Default 100 MiB. */
138
+ maxBytes?: number;
139
+ /** Ticket lifetime in seconds, clamped to 30-3600. */
140
+ expiresIn?: number;
141
+ /** Optional lowercase hexadecimal SHA-256 checksum. */
142
+ sha256?: string;
143
+ }
144
+
145
+ interface R2DownloadTicketOptions {
146
+ expiresIn?: number;
147
+ }
148
+
117
149
  interface R2Bucket {
118
150
  put(key: string, value: string, options?: R2PutOptions): void;
151
+ /** Starts a resumable upload whose individual parts stay memory-bounded. */
152
+ createMultipartUpload(key: string, options?: R2PutOptions): R2MultipartUpload;
153
+ /** Reconstructs a multipart handle after storing its key and upload id. */
154
+ resumeMultipartUpload(key: string, uploadId: string): R2MultipartUpload;
155
+ /** Broker-backed direct PUT. Not available in native standalone mode yet. */
156
+ createUploadUrl(key: string, options?: R2UploadTicketOptions): R2TransferTicket;
157
+ /** Broker-backed direct GET or HEAD, including Range. */
158
+ createDownloadUrl(key: string, options?: R2DownloadTicketOptions): R2TransferTicket;
119
159
  get(key: string): R2Object | null;
120
160
  /** Metadata without the body. */
121
161
  head(key: string): R2ObjectHead | null;