sproutboat 0.35.0 → 0.37.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 +47 -0
- package/README.md +6 -3
- package/SURFACE.md +1 -1
- package/package.json +8 -8
- package/src/main.ts +1 -1
- package/src/types-gen.ts +14 -5
- package/types/sproutboat.d.ts +144 -6
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,53 @@ maintained going forward by the `release` skill.
|
|
|
10
10
|
|
|
11
11
|
## Unreleased
|
|
12
12
|
|
|
13
|
+
## [0.37.0] - 2026-10-27
|
|
14
|
+
|
|
15
|
+
Built on `@sproutboat/runtime` 0.34.1, `@sproutboat/toolchain` 0.19.1 and
|
|
16
|
+
`@sproutboat/wire` 0.20.2.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- **A failed binding call you do not await no longer ends the process.** An un-awaited
|
|
21
|
+
`db.prepare("INSERT ...").run()` that broke a foreign key used to take the whole binary down, even
|
|
22
|
+
with a `try` / `catch` around the call. As on Workers, an unhandled rejection is now logged
|
|
23
|
+
(`Uncaught (in promise) ...`), fires `unhandledrejection` when the app has a listener
|
|
24
|
+
(`addEventListener("unhandledrejection", ...)`), and the process and other requests keep going. An
|
|
25
|
+
un-awaited `async function` that throws is reported the same way. At every compatibility date.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- **Types for the Promise bindings.** `sproutboat types` gives D1 (from compatibility date 2026-10-24),
|
|
30
|
+
KV (2026-10-25) and R2 (2026-10-26) bindings the Promise types (`D1DatabaseAsync`,
|
|
31
|
+
`KVNamespaceAsync`, `R2BucketAsync`); earlier dates keep the synchronous ones.
|
|
32
|
+
- Docs, examples and type comments no longer say binding calls are synchronous at every date.
|
|
33
|
+
|
|
34
|
+
## [0.36.0] - 2026-10-26
|
|
35
|
+
|
|
36
|
+
Built on `@sproutboat/runtime` 0.34.0, `@sproutboat/toolchain` 0.19.0 and
|
|
37
|
+
`@sproutboat/wire` 0.20.2.
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
|
|
41
|
+
- **WebSockets everywhere, from compatibility date 2026-11-01.** Standalone executables and the
|
|
42
|
+
platform carry the client end of a 101 response (before the date they answer 501), and the
|
|
43
|
+
platform edge proxies the upgrade.
|
|
44
|
+
- **Outbound WebSockets, from 2026-11-01.** `new WebSocket(url, protocols)` and
|
|
45
|
+
`fetch(url, { headers: { Upgrade: "websocket" } })` (the Response's `webSocket`, then
|
|
46
|
+
`accept()`); returning that Response from a handler relays the client's socket to the upstream.
|
|
47
|
+
`ws`, `wss`, `http` and `https` URLs. `sproutboat check` and `build` accept `WebSocket(` from
|
|
48
|
+
that date and refuse it before. They run on standalone executables and sproutd; `sproutboat dev`
|
|
49
|
+
and the platform (broker transport) reject `fetch` upgrades for now.
|
|
50
|
+
- **Hibernation on sproutd, from 2026-11-01.** A Durable Object's hibernatable socket no longer
|
|
51
|
+
keeps its worker running: the worker is reaped while the socket idles, the auto-response is
|
|
52
|
+
answered without waking it, and a message or close starts a worker that constructs the object
|
|
53
|
+
again with `getWebSockets()` as it was.
|
|
54
|
+
|
|
55
|
+
### Changed
|
|
56
|
+
|
|
57
|
+
- `types/sproutboat.d.ts` and the README describe the above. The toolchain moves to 0.19.0, so
|
|
58
|
+
compiled apps get a new fingerprint and rebuild once.
|
|
59
|
+
|
|
13
60
|
## [0.35.0] - 2026-10-26
|
|
14
61
|
|
|
15
62
|
Built on `@sproutboat/runtime` 0.33.0, `@sproutboat/toolchain` 0.18.0 and
|
package/README.md
CHANGED
|
@@ -146,7 +146,9 @@ store instead.
|
|
|
146
146
|
The handler is `export default { fetch(request) }`. It may import from other
|
|
147
147
|
files in the project and from its own `node_modules`, and may also export
|
|
148
148
|
`scheduled(event)` / `queue(batch)` handlers and Durable Object classes. `env`
|
|
149
|
-
is a global, not a parameter
|
|
149
|
+
is a global, not a parameter. Binding calls are synchronous until the compatibility
|
|
150
|
+
date that makes each one return a Promise, as on Workers (D1 from 2026-10-24, KV from
|
|
151
|
+
2026-10-25, R2 from 2026-10-26); `await` works at every date, so always await them.
|
|
150
152
|
[`examples/kitchen-sink/`](examples/kitchen-sink) uses every binding.
|
|
151
153
|
|
|
152
154
|
For objects larger than one request, use resumable R2 multipart uploads. The
|
|
@@ -168,8 +170,9 @@ Windows, build from WSL.
|
|
|
168
170
|
## Limits
|
|
169
171
|
|
|
170
172
|
Capability profile `http-sync-v0`: one synchronous `fetch` handler, optional
|
|
171
|
-
`scheduled` / `queue` handlers. WebSockets (from compatibility date 2026-10-30
|
|
172
|
-
|
|
173
|
+
`scheduled` / `queue` handlers. WebSockets (from compatibility date 2026-10-30; standalone
|
|
174
|
+
and platform sprouts from 2026-11-01) and outbound `new WebSocket(url)` (2026-11-01) are
|
|
175
|
+
supported. Binding values are
|
|
173
176
|
text/JSON, one at a time over a 32 MiB loopback frame. Sprout uploads cap at
|
|
174
177
|
16 MiB, assets at 64 MiB / 4096 files.
|
|
175
178
|
|
package/SURFACE.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sproutboat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.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",
|
|
@@ -64,9 +64,9 @@
|
|
|
64
64
|
"@sproutboat/artifact": "^0.3.0",
|
|
65
65
|
"@sproutboat/assets": "^0.3.0",
|
|
66
66
|
"@sproutboat/config": "^0.6.0",
|
|
67
|
-
"@sproutboat/runtime": "^0.
|
|
68
|
-
"@sproutboat/toolchain": "^0.
|
|
69
|
-
"@sproutboat/wire": "^0.20.
|
|
67
|
+
"@sproutboat/runtime": "^0.34.1",
|
|
68
|
+
"@sproutboat/toolchain": "^0.19.1",
|
|
69
|
+
"@sproutboat/wire": "^0.20.2"
|
|
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.37.0",
|
|
82
|
+
"@sproutboat/cli-darwin-x64": "0.37.0",
|
|
83
|
+
"@sproutboat/cli-linux-arm64": "0.37.0",
|
|
84
|
+
"@sproutboat/cli-linux-x64": "0.37.0"
|
|
85
85
|
},
|
|
86
86
|
"engines": {
|
|
87
87
|
"node": ">=18"
|
package/src/main.ts
CHANGED
|
@@ -235,7 +235,7 @@ async function readProject(directory = process.cwd()) {
|
|
|
235
235
|
} catch (cause) {
|
|
236
236
|
fail(cause instanceof BundleError ? cause.message : String(cause));
|
|
237
237
|
}
|
|
238
|
-
const supported = validateHttpSyncSource(bundle.code);
|
|
238
|
+
const supported = validateHttpSyncSource(bundle.code, project.config.compatibility_date);
|
|
239
239
|
if (!supported.ok) fail(supported.errors.join("\n"));
|
|
240
240
|
return { ...project, sourcePath, source, bundle };
|
|
241
241
|
}
|
package/src/types-gen.ts
CHANGED
|
@@ -26,6 +26,11 @@ const HEADER = `// Generated by \`sproutboat types\`. Do not edit.
|
|
|
26
26
|
|
|
27
27
|
type Entry = { name: string; type: string; note?: string };
|
|
28
28
|
|
|
29
|
+
/** Whether the binding's calls return Promises at the project's compatibility date. */
|
|
30
|
+
function asyncAt(config: SproutboatConfig, date: string): boolean {
|
|
31
|
+
return config.compatibility_date >= date;
|
|
32
|
+
}
|
|
33
|
+
|
|
29
34
|
function entries(config: SproutboatConfig): Entry[] {
|
|
30
35
|
const out: Entry[] = [];
|
|
31
36
|
|
|
@@ -35,9 +40,12 @@ function entries(config: SproutboatConfig): Entry[] {
|
|
|
35
40
|
for (const name of config.secrets ?? []) {
|
|
36
41
|
out.push({ name, type: "string", note: "secret, set with `sproutboat secrets put`" });
|
|
37
42
|
}
|
|
38
|
-
for (const { binding } of resourceRefs(config.kv_namespaces))
|
|
39
|
-
|
|
40
|
-
for (const { binding } of resourceRefs(config.
|
|
43
|
+
for (const { binding } of resourceRefs(config.kv_namespaces))
|
|
44
|
+
out.push({ name: binding, type: asyncAt(config, "2026-10-25") ? "KVNamespaceAsync" : "KVNamespace" });
|
|
45
|
+
for (const { binding } of resourceRefs(config.d1_databases))
|
|
46
|
+
out.push({ name: binding, type: asyncAt(config, "2026-10-24") ? "D1DatabaseAsync" : "D1Database" });
|
|
47
|
+
for (const { binding } of resourceRefs(config.r2_buckets))
|
|
48
|
+
out.push({ name: binding, type: asyncAt(config, "2026-10-26") ? "R2BucketAsync" : "R2Bucket" });
|
|
41
49
|
for (const { binding } of resourceRefs(config.queues)) out.push({ name: binding, type: "Queue" });
|
|
42
50
|
for (const name of config.analytics_engine_datasets ?? []) {
|
|
43
51
|
out.push({ name, type: "AnalyticsEngineDataset" });
|
|
@@ -87,8 +95,9 @@ declare global {
|
|
|
87
95
|
* Bindings from sproutboat.jsonc, for project "${config.name}".
|
|
88
96
|
*
|
|
89
97
|
* ${handlerNote}
|
|
90
|
-
*
|
|
91
|
-
*
|
|
98
|
+
* D1 (from 2026-10-24), KV (2026-10-25) and R2 (2026-10-26) return Promises, as on
|
|
99
|
+
* Workers, so a project at or after those dates gets the *Async types; before them
|
|
100
|
+
* the calls are synchronous. \`await\` works at every date.
|
|
92
101
|
*/
|
|
93
102
|
const env: {
|
|
94
103
|
${body}
|
package/types/sproutboat.d.ts
CHANGED
|
@@ -6,7 +6,11 @@
|
|
|
6
6
|
* generates a project's `sproutboat-env.d.ts`, which references this file and
|
|
7
7
|
* declares `env` with that project's bindings.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
9
|
+
* KV, D1 and R2 are synchronous before the compatibility date that makes each one return a
|
|
10
|
+
* Promise (D1 2026-10-24, KV 2026-10-25, R2 2026-10-26): `KVNamespace`, `D1Database` and
|
|
11
|
+
* `R2Bucket` are the synchronous types, `KVNamespaceAsync`, `D1DatabaseAsync` and
|
|
12
|
+
* `R2BucketAsync` the Promise types, and `sproutboat types` gives each binding the one for the
|
|
13
|
+
* project's date. `await` works at every date. Before compatibility date 2026-09-28, `env` is a
|
|
10
14
|
* global only and `ctx` is the second handler argument. From that date, `env`
|
|
11
15
|
* is also passed as the second argument and `ctx` as the third.
|
|
12
16
|
*/
|
|
@@ -22,7 +26,7 @@ declare global {
|
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
interface KVNamespace {
|
|
25
|
-
/** The stored string, or `null`. Synchronous
|
|
29
|
+
/** The stored string, or `null`. Synchronous before 2026-10-25 (a Promise from then). An expired key reads as absent. */
|
|
26
30
|
get(key: string): string | null;
|
|
27
31
|
put(key: string, value: string, options?: KVPutOptions): void;
|
|
28
32
|
delete(key: string): void;
|
|
@@ -30,6 +34,33 @@ declare global {
|
|
|
30
34
|
list(prefix?: string): string[];
|
|
31
35
|
}
|
|
32
36
|
|
|
37
|
+
/**
|
|
38
|
+
* KV from compatibility date 2026-10-25: every method returns a Promise, as on Workers.
|
|
39
|
+
* `sproutboat types` picks this type for a project at or after that date.
|
|
40
|
+
*/
|
|
41
|
+
interface KVNamespaceAsync {
|
|
42
|
+
get(key: string, type?: "text"): Promise<string | null>;
|
|
43
|
+
get<T = unknown>(key: string, type: "json"): Promise<T | null>;
|
|
44
|
+
get(key: string, type: "arrayBuffer"): Promise<ArrayBuffer | null>;
|
|
45
|
+
get(key: string, type: "stream"): Promise<ReadableStream | null>;
|
|
46
|
+
getWithMetadata<M = unknown>(
|
|
47
|
+
key: string,
|
|
48
|
+
type?: "text" | "json" | "arrayBuffer" | "stream",
|
|
49
|
+
): Promise<{ value: unknown; metadata: M | null; cacheStatus: string | null }>;
|
|
50
|
+
put(
|
|
51
|
+
key: string,
|
|
52
|
+
value: string | ArrayBuffer | ArrayBufferView | ReadableStream,
|
|
53
|
+
options?: KVPutOptions & { metadata?: unknown },
|
|
54
|
+
): Promise<void>;
|
|
55
|
+
delete(key: string): Promise<void>;
|
|
56
|
+
list(options?: { prefix?: string; limit?: number; cursor?: string }): Promise<{
|
|
57
|
+
keys: Array<{ name: string; expiration?: number; metadata?: unknown }>;
|
|
58
|
+
list_complete: boolean;
|
|
59
|
+
cursor?: string;
|
|
60
|
+
cacheStatus: string | null;
|
|
61
|
+
}>;
|
|
62
|
+
}
|
|
63
|
+
|
|
33
64
|
// ---------------------------------------------------------------- D1
|
|
34
65
|
|
|
35
66
|
interface D1Meta {
|
|
@@ -63,6 +94,48 @@ declare global {
|
|
|
63
94
|
exec(sql: string): void;
|
|
64
95
|
}
|
|
65
96
|
|
|
97
|
+
/** D1 from compatibility date 2026-10-24: workerd's meta, with D1's keys. */
|
|
98
|
+
interface D1MetaAsync {
|
|
99
|
+
served_by: string;
|
|
100
|
+
duration: number;
|
|
101
|
+
changes: number;
|
|
102
|
+
last_row_id: number;
|
|
103
|
+
changed_db: boolean;
|
|
104
|
+
size_after: number;
|
|
105
|
+
rows_read: number;
|
|
106
|
+
rows_written: number;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
interface D1ResultAsync<T = Record<string, unknown>> {
|
|
110
|
+
results: T[];
|
|
111
|
+
success: boolean;
|
|
112
|
+
meta: D1MetaAsync;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* From compatibility date 2026-10-24 `all()`, `run()`, `first()` and `raw()` return Promises, as
|
|
117
|
+
* on Workers. `sproutboat types` picks this type for a project at or after that date.
|
|
118
|
+
*/
|
|
119
|
+
interface D1PreparedStatementAsync {
|
|
120
|
+
/** Positional `?` parameters, in order. Bytes bind as a BLOB. */
|
|
121
|
+
bind(...values: Array<string | number | boolean | null | ArrayBuffer | ArrayBufferView>): D1PreparedStatementAsync;
|
|
122
|
+
all<T = Record<string, unknown>>(): Promise<D1ResultAsync<T>>;
|
|
123
|
+
run<T = Record<string, unknown>>(): Promise<D1ResultAsync<T>>;
|
|
124
|
+
first<T = Record<string, unknown>>(): Promise<T | null>;
|
|
125
|
+
first<T = unknown>(column: string): Promise<T | null>;
|
|
126
|
+
raw<T = unknown[]>(options?: { columnNames?: false }): Promise<T[]>;
|
|
127
|
+
raw<T = unknown[]>(options: { columnNames: true }): Promise<[string[], ...T[]]>;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
interface D1DatabaseAsync {
|
|
131
|
+
prepare(sql: string): D1PreparedStatementAsync;
|
|
132
|
+
batch<T = Record<string, unknown>>(statements: D1PreparedStatementAsync[]): Promise<D1ResultAsync<T>[]>;
|
|
133
|
+
/** Runs a script line by line and counts the lines. */
|
|
134
|
+
exec(sql: string): Promise<{ count: number; duration: number }>;
|
|
135
|
+
/** A Sproutboat addition: an online, integrity-checked copy of the database. */
|
|
136
|
+
backup(name?: string): Promise<{ path: string; bytes: number }>;
|
|
137
|
+
}
|
|
138
|
+
|
|
66
139
|
// ---------------------------------------------------------------- R2
|
|
67
140
|
|
|
68
141
|
interface R2HttpMetadata {
|
|
@@ -172,6 +245,65 @@ declare global {
|
|
|
172
245
|
list(options?: R2ListOptions): R2Objects;
|
|
173
246
|
}
|
|
174
247
|
|
|
248
|
+
/**
|
|
249
|
+
* R2 from compatibility date 2026-10-26: every method returns a Promise, as on Workers, and
|
|
250
|
+
* `uploaded` is a Date. `sproutboat types` picks these types for a project at or after that date.
|
|
251
|
+
*/
|
|
252
|
+
interface R2ObjectAsync {
|
|
253
|
+
key: string;
|
|
254
|
+
size: number;
|
|
255
|
+
etag: string;
|
|
256
|
+
httpEtag: string;
|
|
257
|
+
checksums: { md5?: ArrayBuffer };
|
|
258
|
+
uploaded: Date;
|
|
259
|
+
httpMetadata?: R2HttpMetadata;
|
|
260
|
+
customMetadata?: Record<string, string>;
|
|
261
|
+
range?: { offset: number; length: number };
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
interface R2ObjectBodyAsync extends R2ObjectAsync {
|
|
265
|
+
body: ReadableStream;
|
|
266
|
+
bodyUsed: boolean;
|
|
267
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
268
|
+
text(): Promise<string>;
|
|
269
|
+
json<T = unknown>(): Promise<T>;
|
|
270
|
+
blob(): Promise<Blob>;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
interface R2MultipartUploadAsync {
|
|
274
|
+
key: string;
|
|
275
|
+
uploadId: string;
|
|
276
|
+
uploadPart(
|
|
277
|
+
partNumber: number,
|
|
278
|
+
value: string | ArrayBuffer | ArrayBufferView | ReadableStream | Blob,
|
|
279
|
+
): Promise<R2UploadedPart>;
|
|
280
|
+
complete(parts: R2UploadedPart[]): Promise<R2ObjectAsync>;
|
|
281
|
+
abort(): Promise<void>;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
interface R2BucketAsync extends Pick<R2Bucket, "createUploadUrl" | "createDownloadUrl"> {
|
|
285
|
+
head(key: string): Promise<R2ObjectAsync | null>;
|
|
286
|
+
get(
|
|
287
|
+
key: string,
|
|
288
|
+
options?: { range?: { offset?: number; length?: number; suffix?: number }; onlyIf?: unknown },
|
|
289
|
+
): Promise<R2ObjectBodyAsync | null>;
|
|
290
|
+
put(
|
|
291
|
+
key: string,
|
|
292
|
+
value: string | ArrayBuffer | ArrayBufferView | ReadableStream | Blob | null,
|
|
293
|
+
options?: { httpMetadata?: R2HttpMetadata | Headers; customMetadata?: Record<string, string>; onlyIf?: unknown },
|
|
294
|
+
): Promise<R2ObjectAsync | null>;
|
|
295
|
+
delete(keys: string | string[]): Promise<void>;
|
|
296
|
+
list(options?: {
|
|
297
|
+
prefix?: string;
|
|
298
|
+
limit?: number;
|
|
299
|
+
cursor?: string;
|
|
300
|
+
delimiter?: string;
|
|
301
|
+
include?: Array<"httpMetadata" | "customMetadata">;
|
|
302
|
+
}): Promise<{ objects: R2ObjectAsync[]; truncated: boolean; cursor?: string; delimitedPrefixes: string[] }>;
|
|
303
|
+
createMultipartUpload(key: string, options?: R2PutOptions): Promise<R2MultipartUploadAsync>;
|
|
304
|
+
resumeMultipartUpload(key: string, uploadId: string): R2MultipartUploadAsync;
|
|
305
|
+
}
|
|
306
|
+
|
|
175
307
|
// ------------------------------------------------------------ Queues
|
|
176
308
|
|
|
177
309
|
type QueuesContentType = "json" | "text" | "bytes" | "v8";
|
|
@@ -275,7 +407,10 @@ declare global {
|
|
|
275
407
|
/**
|
|
276
408
|
* The WebSocket hibernation API, from compatibility date 2026-10-30. Messages and
|
|
277
409
|
* 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
|
|
410
|
+
* `webSocketClose`. On sproutd an open socket keeps the app's worker running before
|
|
411
|
+
* compatibility date 2026-11-01; from it the worker is reaped while the socket idles and
|
|
412
|
+
* the object is constructed again (with `getWebSockets()` as it was) when a message or
|
|
413
|
+
* close arrives.
|
|
279
414
|
*/
|
|
280
415
|
acceptWebSocket(ws: WebSocket, tags?: string[]): void;
|
|
281
416
|
getWebSockets(tag?: string): WebSocket[];
|
|
@@ -289,9 +424,12 @@ declare global {
|
|
|
289
424
|
/**
|
|
290
425
|
* From compatibility date 2026-10-30, as in workerd. Return the client end in a
|
|
291
426
|
* `new Response(null, { status: 101, webSocket: client })` and talk on the server end.
|
|
292
|
-
* Client sockets are carried by sproutd
|
|
293
|
-
*
|
|
294
|
-
*
|
|
427
|
+
* Client sockets are carried by sproutd, and from compatibility date 2026-11-01 by
|
|
428
|
+
* standalone and platform sprouts too (before it they answer a 101 with 501). Inbound
|
|
429
|
+
* messages are capped at 32 MiB (1009 above it). From 2026-11-01 `new WebSocket(url,
|
|
430
|
+
* protocols)` and `fetch(url, { headers: { Upgrade: "websocket" } })` connect out; the
|
|
431
|
+
* Response's `webSocket` needs `accept()`, and returning it from a handler relays the client.
|
|
432
|
+
* Outbound sockets need the embedded transport (standalone, sproutd), not `sproutboat dev`.
|
|
295
433
|
*/
|
|
296
434
|
class WebSocketPair {
|
|
297
435
|
0: WebSocket;
|