Skip to content

Commit 62545c9

Browse files
authored
[Stream] Add worker bindings support (#12957)
1 parent 71ab981 commit 62545c9

File tree

20 files changed

+270
-2
lines changed

20 files changed

+270
-2
lines changed

.changeset/four-teachers-double.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"wrangler": minor
3+
"@cloudflare/workers-utils": minor
4+
---
5+
6+
Add Stream binding support to Wrangler and workers-utils
7+
8+
Wrangler and workers-utils now recognize the `stream` binding in configuration, deployment metadata, and generated worker types. This enables projects to declare Stream bindings in `wrangler.json` and have the binding represented consistently across validation, metadata mapping, and type generation.

packages/workers-utils/src/config/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ export const defaultWranglerConfig: Config = {
341341
analytics_engine_datasets: [],
342342
ai: undefined,
343343
images: undefined,
344+
stream: undefined,
344345
media: undefined,
345346
version_metadata: undefined,
346347
unsafe_hello_world: [],

packages/workers-utils/src/config/environment.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1113,6 +1113,23 @@ export interface EnvironmentNonInheritable {
11131113
}
11141114
| undefined;
11151115

1116+
/**
1117+
* Binding to Cloudflare Stream
1118+
*
1119+
* NOTE: This field is not automatically inherited from the top level environment,
1120+
* and so must be specified in every named environment.
1121+
*
1122+
* @default {}
1123+
* @nonInheritable
1124+
*/
1125+
stream:
1126+
| {
1127+
binding: string;
1128+
/** Whether the Stream binding should be remote or not in local development */
1129+
remote?: boolean;
1130+
}
1131+
| undefined;
1132+
11161133
/**
11171134
* Binding to the Worker Version's metadata
11181135
*/

packages/workers-utils/src/config/validation.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ export type ConfigBindingFieldName =
8888
| "browser"
8989
| "ai"
9090
| "images"
91+
| "stream"
9192
| "media"
9293
| "version_metadata"
9394
| "unsafe"
@@ -124,6 +125,7 @@ export const friendlyBindingNames: Record<ConfigBindingFieldName, string> = {
124125
browser: "Browser",
125126
ai: "AI",
126127
images: "Images",
128+
stream: "Stream",
127129
media: "Media",
128130
version_metadata: "Worker Version Metadata",
129131
unsafe: "Unsafe Metadata",
@@ -157,6 +159,7 @@ const bindingTypeFriendlyNames: Record<Binding["type"], string> = {
157159
browser: "Browser",
158160
ai: "AI",
159161
images: "Images",
162+
stream: "Stream",
160163
version_metadata: "Worker Version Metadata",
161164
data_blob: "Data Blob",
162165
durable_object_namespace: "Durable Object",
@@ -1802,6 +1805,16 @@ function normalizeAndValidateEnvironment(
18021805
validateNamedSimpleBinding(envName),
18031806
undefined
18041807
),
1808+
stream: notInheritable(
1809+
diagnostics,
1810+
topLevelEnv,
1811+
rawConfig,
1812+
rawEnv,
1813+
envName,
1814+
"stream",
1815+
validateNamedSimpleBinding(envName),
1816+
undefined
1817+
),
18051818
media: notInheritable(
18061819
diagnostics,
18071820
topLevelEnv,
@@ -2917,6 +2930,7 @@ const validateUnsafeBinding: ValidatorFn = (diagnostics, field, value) => {
29172930
"pipeline",
29182931
"worker_loader",
29192932
"vpc_service",
2933+
"stream",
29202934
"media",
29212935
];
29222936

packages/workers-utils/src/map-worker-metadata-bindings.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,13 @@ export function mapWorkerMetadataBindings(
9494
};
9595
}
9696
break;
97+
case "stream":
98+
{
99+
configObj.stream = {
100+
binding: binding.name,
101+
};
102+
}
103+
break;
97104
case "media":
98105
{
99106
configObj.media = {

packages/workers-utils/src/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import type {
2929
CfSecretsStoreSecrets,
3030
CfSendEmailBindings,
3131
CfService,
32+
CfStreamBinding,
3233
CfTailConsumer,
3334
CfUnsafeBinding,
3435
CfUserLimits,
@@ -62,6 +63,7 @@ export type WorkerMetadataBinding =
6263
| { type: "browser"; name: string; raw?: boolean }
6364
| { type: "ai"; name: string; staging?: boolean; raw?: boolean }
6465
| { type: "images"; name: string; raw?: boolean }
66+
| { type: "stream"; name: string }
6567
| { type: "version_metadata"; name: string }
6668
| { type: "data_blob"; name: string; part: string }
6769
| { type: "kv_namespace"; name: string; namespace_id: string; raw?: boolean }
@@ -298,6 +300,7 @@ export type Binding =
298300
| ({ type: "browser" } & BindingOmit<CfBrowserBinding>)
299301
| ({ type: "ai" } & BindingOmit<CfAIBinding>)
300302
| ({ type: "images" } & BindingOmit<CfImagesBinding>)
303+
| ({ type: "stream" } & BindingOmit<CfStreamBinding>)
301304
| { type: "version_metadata" }
302305
| { type: "data_blob"; source: BinaryFile }
303306
| ({ type: "durable_object_namespace" } & NameOmit<CfDurableObject>)

packages/workers-utils/src/worker.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,14 @@ export interface CfMediaBinding {
148148
remote?: boolean;
149149
}
150150

151+
/**
152+
* A binding to Cloudflare Stream
153+
*/
154+
export interface CfStreamBinding {
155+
binding: string;
156+
remote?: boolean;
157+
}
158+
151159
/**
152160
* A binding to the Worker Version's metadata
153161
*/

packages/workers-utils/tests/config/validation/normalize-and-validate-config.test.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ describe("normalizeAndValidateConfig()", () => {
129129
compliance_region: undefined,
130130
images: undefined,
131131
media: undefined,
132+
stream: undefined,
132133
} satisfies Config);
133134
expect(diagnostics.hasErrors()).toBe(false);
134135
expect(diagnostics.hasWarnings()).toBe(false);
@@ -2294,6 +2295,69 @@ describe("normalizeAndValidateConfig()", () => {
22942295
});
22952296
});
22962297

2298+
// Stream
2299+
describe("[stream]", () => {
2300+
it("should error if stream is an array", ({ expect }) => {
2301+
const { diagnostics } = normalizeAndValidateConfig(
2302+
{ stream: [] } as unknown as RawConfig,
2303+
undefined,
2304+
undefined,
2305+
{ env: undefined }
2306+
);
2307+
2308+
expect(diagnostics.hasWarnings()).toBe(false);
2309+
expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
2310+
"Processing wrangler configuration:
2311+
- The field "stream" should be an object but got []."
2312+
`);
2313+
});
2314+
2315+
it("should error if stream is a string", ({ expect }) => {
2316+
const { diagnostics } = normalizeAndValidateConfig(
2317+
{ stream: "BAD" } as unknown as RawConfig,
2318+
undefined,
2319+
undefined,
2320+
{ env: undefined }
2321+
);
2322+
2323+
expect(diagnostics.hasWarnings()).toBe(false);
2324+
expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
2325+
"Processing wrangler configuration:
2326+
- The field "stream" should be an object but got "BAD"."
2327+
`);
2328+
});
2329+
2330+
it("should error if stream is a number", ({ expect }) => {
2331+
const { diagnostics } = normalizeAndValidateConfig(
2332+
{ stream: 999 } as unknown as RawConfig,
2333+
undefined,
2334+
undefined,
2335+
{ env: undefined }
2336+
);
2337+
2338+
expect(diagnostics.hasWarnings()).toBe(false);
2339+
expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
2340+
"Processing wrangler configuration:
2341+
- The field "stream" should be an object but got 999."
2342+
`);
2343+
});
2344+
2345+
it("should error if stream is null", ({ expect }) => {
2346+
const { diagnostics } = normalizeAndValidateConfig(
2347+
{ stream: null } as unknown as RawConfig,
2348+
undefined,
2349+
undefined,
2350+
{ env: undefined }
2351+
);
2352+
2353+
expect(diagnostics.hasWarnings()).toBe(false);
2354+
expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
2355+
"Processing wrangler configuration:
2356+
- The field "stream" should be an object but got null."
2357+
`);
2358+
});
2359+
});
2360+
22972361
// Worker Version Metadata
22982362
describe("[version_metadata]", () => {
22992363
it("should error if version_metadata is an array", ({ expect }) => {

packages/wrangler/src/__tests__/api/startDevWorker/utils.test.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import { assert, describe, it } from "vitest";
2-
import { convertConfigBindingsToStartWorkerBindings } from "../../../api/startDevWorker/utils";
2+
import {
3+
convertConfigBindingsToStartWorkerBindings,
4+
convertStartDevOptionsToBindings,
5+
} from "../../../api/startDevWorker/utils";
36

47
describe("convertConfigBindingsToStartWorkerBindings", () => {
58
it("converts config bindings into startWorker bindings", async ({
@@ -56,6 +59,7 @@ describe("convertConfigBindingsToStartWorkerBindings", () => {
5659
service: "my-service",
5760
},
5861
],
62+
stream: { binding: "MY_STREAM" },
5963
mtls_certificates: [
6064
{
6165
binding: "MTLS",
@@ -123,6 +127,9 @@ describe("convertConfigBindingsToStartWorkerBindings", () => {
123127
service: "my-service",
124128
type: "service",
125129
},
130+
MY_STREAM: {
131+
type: "stream",
132+
},
126133
MY_VECTORIZE: {
127134
index_name: "idx",
128135
type: "vectorize",
@@ -193,4 +200,17 @@ describe("convertConfigBindingsToStartWorkerBindings", () => {
193200
assert(result.MY_DB.type === "d1");
194201
expect(result.MY_DB.database_id).toBe("staging-db-id");
195202
});
203+
204+
it("converts programmatic dev stream bindings", ({ expect }) => {
205+
const result = convertStartDevOptionsToBindings({
206+
stream: { binding: "MY_STREAM", remote: true },
207+
});
208+
209+
expect(result).toEqual({
210+
MY_STREAM: {
211+
remote: true,
212+
type: "stream",
213+
},
214+
});
215+
});
196216
});

packages/wrangler/src/__tests__/create-worker-upload-form/bindings.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@ describe("createWorkerUploadForm — bindings", () => {
326326
{ bindingName: "BROWSER", type: "browser" as const },
327327
{ bindingName: "AI", type: "ai" as const },
328328
{ bindingName: "IMAGES", type: "images" as const },
329+
{ bindingName: "STREAM", type: "stream" as const },
329330
{ bindingName: "MEDIA", type: "media" as const },
330331
{ bindingName: "VERSION", type: "version_metadata" as const },
331332
{ bindingName: "ASSETS", type: "assets" as const },

0 commit comments

Comments
 (0)