sproutboat 0.32.0 → 0.34.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.
- package/CHANGELOG.md +53 -0
- package/README.md +3 -2
- package/SURFACE.md +1 -1
- package/package.json +9 -9
- package/src/build.ts +4 -1
- package/types/sproutboat.d.ts +88 -12
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,59 @@ maintained going forward by the `release` skill.
|
|
|
10
10
|
|
|
11
11
|
## Unreleased
|
|
12
12
|
|
|
13
|
+
## [0.34.0] - 2026-10-26
|
|
14
|
+
|
|
15
|
+
Built on `@sproutboat/runtime` 0.33.0, `@sproutboat/toolchain` 0.17.0 and
|
|
16
|
+
`@sproutboat/wire` 0.20.1.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- **WebSockets from compatibility date 2026-10-30.** `WebSocketPair`, `WebSocket`
|
|
21
|
+
(`accept`, `send`, `close`, listeners), `new Response(null, { status: 101, webSocket })`,
|
|
22
|
+
`response.webSocket`, `WebSocketRequestResponsePair`, and the Durable Object hibernation API
|
|
23
|
+
(`acceptWebSocket`, `getWebSockets`, `getTags`, auto-responses, attachments,
|
|
24
|
+
`webSocketMessage` and `webSocketClose`). Behaviour follows workerd. Sockets reaching a client
|
|
25
|
+
are carried by sproutd (B19); a standalone executable or the platform answers a 101 with 501
|
|
26
|
+
for now, while a pair whose ends both stay in the worker (a Worker and its Durable Object)
|
|
27
|
+
works everywhere. Outbound `new WebSocket(url)` is not supported yet.
|
|
28
|
+
- Streamed request bodies on sproutd (B19), from compatibility date 2026-10-27.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- `types/sproutboat.d.ts` declares the WebSocket types and the hibernation handlers.
|
|
33
|
+
- The toolchain moves to 0.17.0, so compiled apps get a new fingerprint and rebuild once.
|
|
34
|
+
|
|
35
|
+
## [0.33.0] - 2026-10-25
|
|
36
|
+
|
|
37
|
+
Built on `@sproutboat/runtime` 0.32.0, `@sproutboat/wire` 0.20.0 and
|
|
38
|
+
`@sproutboat/config` 0.6.0.
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
|
|
42
|
+
- **Queue names and consumer settings.** A `queues` entry may be
|
|
43
|
+
`{ "binding": "JOBS", "queue": "jobs", "max_batch_size": 10, "max_batch_timeout": 1,
|
|
44
|
+
"max_retries": 3, "retry_delay": 0, "dead_letter_queue": "failed" }`. The settings
|
|
45
|
+
are named as in wrangler's `queues.consumers`, and `id` is optional there. `batch.queue`
|
|
46
|
+
is the queue's name. The build bakes the settings into `bindings.json` as `queueConfig`,
|
|
47
|
+
and `deploy` pins a provisioned id into such an object. These apply at every compatibility
|
|
48
|
+
date.
|
|
49
|
+
- **Queues like Cloudflare's from compatibility date 2026-10-31.**
|
|
50
|
+
- `send()` and `sendBatch()` return Promises of the queue's backlog, and `metrics()` is new.
|
|
51
|
+
- Bodies keep their content type (`json`, `text`, `bytes`, `v8`).
|
|
52
|
+
- Arguments are checked with workerd's errors.
|
|
53
|
+
- Messages carry `timestamp` as a Date of when they were sent, and `retry({ delaySeconds })`.
|
|
54
|
+
- The first of `ack()` and `retry()` wins.
|
|
55
|
+
- By default a retry is immediate, with at most 3 retries before the message is dropped or
|
|
56
|
+
sent to the dead letter queue.
|
|
57
|
+
- Queues conformance: 17/46 before, 41/46 after, standalone and through `sproutboat dev`.
|
|
58
|
+
The other 5 cases follow Cloudflare's documented behaviour where miniflare differs: real
|
|
59
|
+
backlog metrics, and first call wins.
|
|
60
|
+
|
|
61
|
+
### Changed
|
|
62
|
+
|
|
63
|
+
- `types/sproutboat.d.ts`: the Queues types follow Workers (`QueueSendResult`,
|
|
64
|
+
`QueueMetrics`, readonly message fields, `metadata`, retry options).
|
|
65
|
+
|
|
13
66
|
## [0.32.0] - 2026-10-25
|
|
14
67
|
|
|
15
68
|
Built on `@sproutboat/runtime` 0.31.0, `@sproutboat/toolchain` 0.16.1 and
|
package/README.md
CHANGED
|
@@ -129,7 +129,7 @@ complete export. Existing output files require `--force`.
|
|
|
129
129
|
"kv_namespaces": ["CACHE"], // bare name, or { "binding": "CACHE", "id": "kv_..." }
|
|
130
130
|
"d1_databases": ["DB"],
|
|
131
131
|
"r2_buckets": ["UPLOADS"],
|
|
132
|
-
"queues": ["JOBS"],
|
|
132
|
+
"queues": ["JOBS"], // or { "binding": "JOBS", "queue": "jobs", "max_retries": 3, "dead_letter_queue": "failed" }
|
|
133
133
|
"analytics_engine_datasets": ["METRICS"], // bare name only, no id
|
|
134
134
|
"durable_objects": { "COUNTER": "Counter" },
|
|
135
135
|
"ratelimiters": [{ "binding": "API", "limit": 100, "period": 60 }], // env.API.limit({ key })
|
|
@@ -168,7 +168,8 @@ Windows, build from WSL.
|
|
|
168
168
|
## Limits
|
|
169
169
|
|
|
170
170
|
Capability profile `http-sync-v0`: one synchronous `fetch` handler, optional
|
|
171
|
-
`scheduled` / `queue` handlers
|
|
171
|
+
`scheduled` / `queue` handlers. WebSockets (from compatibility date 2026-10-30) need
|
|
172
|
+
a host that carries them (sproutd). Binding values are
|
|
172
173
|
text/JSON, one at a time over a 32 MiB loopback frame. Sprout uploads cap at
|
|
173
174
|
16 MiB, assets at 64 MiB / 4096 files.
|
|
174
175
|
|
package/SURFACE.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sproutboat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.34.0",
|
|
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",
|
|
@@ -63,10 +63,10 @@
|
|
|
63
63
|
"dependencies": {
|
|
64
64
|
"@sproutboat/artifact": "^0.3.0",
|
|
65
65
|
"@sproutboat/assets": "^0.3.0",
|
|
66
|
-
"@sproutboat/config": "^0.
|
|
67
|
-
"@sproutboat/runtime": "^0.
|
|
68
|
-
"@sproutboat/toolchain": "^0.
|
|
69
|
-
"@sproutboat/wire": "^0.
|
|
66
|
+
"@sproutboat/config": "^0.6.0",
|
|
67
|
+
"@sproutboat/runtime": "^0.33.0",
|
|
68
|
+
"@sproutboat/toolchain": "^0.17.0",
|
|
69
|
+
"@sproutboat/wire": "^0.20.1"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@oxlint/plugins": "1.85.0",
|
|
@@ -78,10 +78,10 @@
|
|
|
78
78
|
"typescript": "7.0.2"
|
|
79
79
|
},
|
|
80
80
|
"optionalDependencies": {
|
|
81
|
-
"@sproutboat/cli-darwin-arm64": "0.
|
|
82
|
-
"@sproutboat/cli-darwin-x64": "0.
|
|
83
|
-
"@sproutboat/cli-linux-arm64": "0.
|
|
84
|
-
"@sproutboat/cli-linux-x64": "0.
|
|
81
|
+
"@sproutboat/cli-darwin-arm64": "0.34.0",
|
|
82
|
+
"@sproutboat/cli-darwin-x64": "0.34.0",
|
|
83
|
+
"@sproutboat/cli-linux-arm64": "0.34.0",
|
|
84
|
+
"@sproutboat/cli-linux-x64": "0.34.0"
|
|
85
85
|
},
|
|
86
86
|
"engines": {
|
|
87
87
|
"node": ">=18"
|
package/src/build.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { NATIVE_RESTORE_IDENTITY } from "./native-restore";
|
|
|
3
3
|
import { cp, mkdir, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
4
4
|
import { dirname, resolve } from "node:path";
|
|
5
5
|
import { readAssetRules, walkAssets, type AssetManifest } from "./assets";
|
|
6
|
-
import { resourceRefs, type SproutboatConfig } from "./config";
|
|
6
|
+
import { queueSettings, resourceRefs, type SproutboatConfig } from "./config";
|
|
7
7
|
import { ensureSqliteObject } from "./sqlite";
|
|
8
8
|
import { sqliteStamp } from "./sqlite";
|
|
9
9
|
import { bearsslStamp, ensureBearssl } from "./bearssl";
|
|
@@ -149,6 +149,9 @@ async function prepareBuild(input: BuildInput) {
|
|
|
149
149
|
d1: refsByKind.d1.map((ref) => ref.binding),
|
|
150
150
|
r2: refsByKind.r2.map((ref) => ref.binding),
|
|
151
151
|
queues: refsByKind.queue.map((ref) => ref.binding),
|
|
152
|
+
// Each queue's name and consumer settings (runtime 0.32): the sprout names batches and
|
|
153
|
+
// applies retries with them, the broker batches with them. Older readers ignore it.
|
|
154
|
+
queueConfig: queueSettings(input.config.queues),
|
|
152
155
|
analytics: input.config.analytics_engine_datasets ?? [],
|
|
153
156
|
do: Object.entries(input.config.durable_objects ?? {}).map(([binding, className]) => ({ binding, className })),
|
|
154
157
|
services: input.config.services ?? [],
|
package/types/sproutboat.d.ts
CHANGED
|
@@ -174,31 +174,64 @@ declare global {
|
|
|
174
174
|
|
|
175
175
|
// ------------------------------------------------------------ Queues
|
|
176
176
|
|
|
177
|
+
type QueuesContentType = "json" | "text" | "bytes" | "v8";
|
|
178
|
+
|
|
177
179
|
interface QueueSendOptions {
|
|
178
180
|
delaySeconds?: number;
|
|
181
|
+
/** From compatibility date 2026-10-31. */
|
|
182
|
+
contentType?: QueuesContentType;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Cloudflare's QueueMetrics; `oldestMessageTimestamp` in ms since the epoch. */
|
|
186
|
+
interface QueueMetrics {
|
|
187
|
+
backlogCount: number;
|
|
188
|
+
backlogBytes: number;
|
|
189
|
+
oldestMessageTimestamp?: number;
|
|
179
190
|
}
|
|
180
191
|
|
|
192
|
+
interface QueueSendResult {
|
|
193
|
+
metadata: { metrics: QueueMetrics };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* From compatibility date 2026-10-31, send() and sendBatch() return Promises of the queue's
|
|
198
|
+
* backlog after the send, as on Workers; earlier dates return nothing at once.
|
|
199
|
+
*/
|
|
181
200
|
interface Queue<Body = unknown> {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
201
|
+
send(body: Body, options?: QueueSendOptions): Promise<QueueSendResult>;
|
|
202
|
+
sendBatch(
|
|
203
|
+
messages: Iterable<{ body: Body; delaySeconds?: number; contentType?: QueuesContentType }>,
|
|
204
|
+
options?: { delaySeconds?: number },
|
|
205
|
+
): Promise<QueueSendResult>;
|
|
206
|
+
/** From compatibility date 2026-10-31. */
|
|
207
|
+
metrics(): Promise<QueueMetrics>;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
interface QueueRetryOptions {
|
|
211
|
+
delaySeconds?: number;
|
|
185
212
|
}
|
|
186
213
|
|
|
187
214
|
interface QueueMessage<Body = unknown> {
|
|
188
|
-
id: string;
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
215
|
+
readonly id: string;
|
|
216
|
+
/** When it was sent: a Date from compatibility date 2026-10-31, epoch ms before. */
|
|
217
|
+
readonly timestamp: Date;
|
|
218
|
+
readonly body: Body;
|
|
219
|
+
/** 1 on the first delivery. */
|
|
220
|
+
readonly attempts: number;
|
|
221
|
+
/** Mark as handled. A message neither acked nor retried counts as acked (retried if queue() throws). */
|
|
192
222
|
ack(): void;
|
|
193
|
-
/** Hand it back for another attempt. */
|
|
194
|
-
retry(): void;
|
|
223
|
+
/** Hand it back for another attempt. The first of ack() and retry() wins. */
|
|
224
|
+
retry(options?: QueueRetryOptions): void;
|
|
195
225
|
}
|
|
196
226
|
|
|
197
227
|
interface MessageBatch<Body = unknown> {
|
|
198
|
-
queue
|
|
199
|
-
|
|
228
|
+
/** The queue's name (`queue` in sproutboat.jsonc, else the binding name). */
|
|
229
|
+
readonly queue: string;
|
|
230
|
+
readonly messages: ReadonlyArray<QueueMessage<Body>>;
|
|
231
|
+
/** From compatibility date 2026-10-31. */
|
|
232
|
+
readonly metadata: { metrics: QueueMetrics };
|
|
200
233
|
ackAll(): void;
|
|
201
|
-
retryAll(): void;
|
|
234
|
+
retryAll(options?: QueueRetryOptions): void;
|
|
202
235
|
}
|
|
203
236
|
|
|
204
237
|
// --------------------------------------------------- Durable Objects
|
|
@@ -239,6 +272,46 @@ declare global {
|
|
|
239
272
|
id: DurableObjectId;
|
|
240
273
|
/** Synchronous, like every other binding. */
|
|
241
274
|
storage: DurableObjectStorage;
|
|
275
|
+
/**
|
|
276
|
+
* The WebSocket hibernation API, from compatibility date 2026-10-30. Messages and
|
|
277
|
+
* closes on an accepted socket go to the class's `webSocketMessage` and
|
|
278
|
+
* `webSocketClose`. On sproutd an open socket keeps the app's worker running.
|
|
279
|
+
*/
|
|
280
|
+
acceptWebSocket(ws: WebSocket, tags?: string[]): void;
|
|
281
|
+
getWebSockets(tag?: string): WebSocket[];
|
|
282
|
+
getTags(ws: WebSocket): string[];
|
|
283
|
+
setWebSocketAutoResponse(pair?: WebSocketRequestResponsePair): void;
|
|
284
|
+
getWebSocketAutoResponse(): WebSocketRequestResponsePair | null;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// ---------------------------------------------------------- WebSockets
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* From compatibility date 2026-10-30, as in workerd. Return the client end in a
|
|
291
|
+
* `new Response(null, { status: 101, webSocket: client })` and talk on the server end.
|
|
292
|
+
* Client sockets are carried by sproutd; a standalone or platform sprout answers a 101
|
|
293
|
+
* with 501 for now. Inbound messages are capped at 32 MiB (1009 above it). Outbound
|
|
294
|
+
* WebSockets (`new WebSocket(url)`) are not supported.
|
|
295
|
+
*/
|
|
296
|
+
class WebSocketPair {
|
|
297
|
+
0: WebSocket;
|
|
298
|
+
1: WebSocket;
|
|
299
|
+
}
|
|
300
|
+
class WebSocketRequestResponsePair {
|
|
301
|
+
constructor(request: string, response: string);
|
|
302
|
+
readonly request: string;
|
|
303
|
+
readonly response: string;
|
|
304
|
+
}
|
|
305
|
+
interface ResponseInit {
|
|
306
|
+
webSocket?: WebSocket | null;
|
|
307
|
+
}
|
|
308
|
+
interface Response {
|
|
309
|
+
readonly webSocket: WebSocket | null;
|
|
310
|
+
}
|
|
311
|
+
interface WebSocket {
|
|
312
|
+
accept(): void;
|
|
313
|
+
serializeAttachment(value: unknown): void;
|
|
314
|
+
deserializeAttachment(): unknown;
|
|
242
315
|
}
|
|
243
316
|
|
|
244
317
|
// -------------------------------------------------- Analytics Engine
|
|
@@ -333,6 +406,9 @@ declare global {
|
|
|
333
406
|
fetch(request: Request): Response | Promise<Response>;
|
|
334
407
|
/** Runs after `storage.setAlarm`, with no request in flight. */
|
|
335
408
|
alarm?(): void | Promise<void>;
|
|
409
|
+
/** A message on a socket taken with `state.acceptWebSocket` (from 2026-10-30). */
|
|
410
|
+
webSocketMessage?(ws: WebSocket, message: string | ArrayBuffer): void | Promise<void>;
|
|
411
|
+
webSocketClose?(ws: WebSocket, code: number, reason: string, wasClean: boolean): void | Promise<void>;
|
|
336
412
|
}
|
|
337
413
|
|
|
338
414
|
// ------------------------------------------------------- Version metadata
|