Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Images binding #7424

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/beige-chicken-sort.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wrangler": minor
---

Add Images binding
4 changes: 3 additions & 1 deletion packages/wrangler/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,12 @@
"path-to-regexp": "^6.3.0",
"resolve": "^1.22.8",
"selfsigned": "^2.0.1",
"sharp": "^0.33.5",
"source-map": "^0.6.1",
"unenv": "npm:[email protected]",
"workerd": "1.20241106.1",
"xxhash-wasm": "^1.0.1"
"xxhash-wasm": "^1.0.1",
"zod": "^3.22.3"
},
"devDependencies": {
"@cloudflare/cli": "workspace:*",
Expand Down
59 changes: 59 additions & 0 deletions packages/wrangler/src/__tests__/configuration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2095,6 +2095,65 @@ describe("normalizeAndValidateConfig()", () => {
});
});

// Images
describe("[images]", () => {
it("should error if images is an array", () => {
const { diagnostics } = normalizeAndValidateConfig(
{ images: [] } as unknown as RawConfig,
undefined,
{ env: undefined }
);

expect(diagnostics.hasWarnings()).toBe(false);
expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
"Processing wrangler configuration:
- The field \\"images\\" should be an object but got []."
`);
});

it("should error if images is a string", () => {
const { diagnostics } = normalizeAndValidateConfig(
{ images: "BAD" } as unknown as RawConfig,
undefined,
{ env: undefined }
);

expect(diagnostics.hasWarnings()).toBe(false);
expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
"Processing wrangler configuration:
- The field \\"images\\" should be an object but got \\"BAD\\"."
`);
});

it("should error if images is a number", () => {
const { diagnostics } = normalizeAndValidateConfig(
{ images: 999 } as unknown as RawConfig,
undefined,
{ env: undefined }
);

expect(diagnostics.hasWarnings()).toBe(false);
expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
"Processing wrangler configuration:
- The field \\"images\\" should be an object but got 999."
`);
});

it("should error if ai is null", () => {
const { diagnostics } = normalizeAndValidateConfig(
{ images: null } as unknown as RawConfig,
undefined,
{ env: undefined }
);

expect(diagnostics.hasWarnings()).toBe(false);
expect(diagnostics.renderErrors()).toMatchInlineSnapshot(`
"Processing wrangler configuration:
- The field \\"images\\" should be an object but got null."
`);
});
});

// Worker Version Metadata
describe("[version_metadata]", () => {
it("should error if version_metadata is an array", () => {
Expand Down
31 changes: 31 additions & 0 deletions packages/wrangler/src/__tests__/deploy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11309,6 +11309,37 @@ export default{
});
});

describe("images", () => {
it("should upload images bindings", async () => {
writeWranglerConfig({
images: { binding: "IMAGES_BIND" },
});
await fs.promises.writeFile("index.js", `export default {};`);
mockSubDomainRequest();
mockUploadWorkerRequest({
expectedBindings: [
{
type: "images",
name: "IMAGES_BIND",
},
],
});

await runWrangler("deploy index.js");
expect(std.out).toMatchInlineSnapshot(`
"Total Upload: xx KiB / gzip: xx KiB
Worker Startup Time: 100 ms
Your worker has access to the following bindings:
- Images:
- Name: IMAGES_BIND
Uploaded test-name (TIMINGS)
Deployed test-name triggers (TIMINGS)
https://test-name.test-sub-domain.workers.dev
Current Version ID: Galaxy-Class"
`);
});
})

describe("python", () => {
it("should upload python module defined in wrangler.toml", async () => {
writeWranglerConfig({
Expand Down
3 changes: 2 additions & 1 deletion packages/wrangler/src/__tests__/dev.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1427,7 +1427,8 @@ describe.sequential("wrangler dev", () => {
--log-level Specify logging level [choices: \\"debug\\", \\"info\\", \\"log\\", \\"warn\\", \\"error\\", \\"none\\"] [default: \\"log\\"]
--show-interactive-dev-session Show interactive dev session (defaults to true if the terminal supports interactivity) [boolean]
--experimental-registry, --x-registry Use the experimental file based dev registry for multi-worker development [boolean] [default: true]
--experimental-vectorize-bind-to-prod Bind to production Vectorize indexes in local development mode [boolean] [default: false]",
--experimental-vectorize-bind-to-prod Bind to production Vectorize indexes in local development mode [boolean] [default: false]
--experimental-images-local-mode Use a local lower-fidelity implementation of the Images binding [boolean] [default: false]",
"warn": "",
}
`);
Expand Down
3 changes: 2 additions & 1 deletion packages/wrangler/src/__tests__/pages/pages.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ describe("pages", () => {
--log-level Specify logging level [choices: \\"debug\\", \\"info\\", \\"log\\", \\"warn\\", \\"error\\", \\"none\\"]
--show-interactive-dev-session Show interactive dev session (defaults to true if the terminal supports interactivity) [boolean]
--experimental-registry, --x-registry Use the experimental file based dev registry for multi-worker development [boolean] [default: true]
--experimental-vectorize-bind-to-prod Bind to production Vectorize indexes in local development mode [boolean] [default: false]"
--experimental-vectorize-bind-to-prod Bind to production Vectorize indexes in local development mode [boolean] [default: false]
--experimental-images-local-mode Use a local lower-fidelity implementation of the Images binding [boolean] [default: false]"
`);
});

Expand Down
3 changes: 3 additions & 0 deletions packages/wrangler/src/api/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export interface UnstableDevOptions {
devEnv?: boolean;
fileBasedRegistry?: boolean;
vectorizeBindToProd?: boolean;
imagesLocalMode?: boolean;
enableIpc?: boolean;
};
}
Expand Down Expand Up @@ -127,6 +128,7 @@ export async function unstable_dev(
testScheduled,
fileBasedRegistry = true,
vectorizeBindToProd,
imagesLocalMode,
// 2. options for alpha/beta products/libs
d1Databases,
enablePagesAssetsServiceBinding,
Expand Down Expand Up @@ -222,6 +224,7 @@ export async function unstable_dev(
experimentalDevEnv: undefined,
experimentalRegistry: fileBasedRegistry,
experimentalVectorizeBindToProd: vectorizeBindToProd ?? false,
experimentalImagesLocalMode: imagesLocalMode ?? false,
enableIpc: options?.experimental?.enableIpc,
};

Expand Down
2 changes: 2 additions & 0 deletions packages/wrangler/src/api/integrations/platform/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ async function getMiniflareOptionsFromConfig(
services: rawConfig.services,
serviceBindings: {},
migrations: rawConfig.migrations,
imagesLocalMode: false,
});

const persistOptions = getMiniflarePersistOptions(options.persist);
Expand Down Expand Up @@ -266,6 +267,7 @@ export function unstable_getMiniflareWorkerOptions(
services: [],
serviceBindings: {},
migrations: config.migrations,
imagesLocalMode: false,
});

// This function is currently only exported for the Workers Vitest pool.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ function createWorkerBundleFormData(
vars: config?.vars,
browser: config?.browser,
ai: config?.ai,
images: config?.images,
version_metadata: config?.version_metadata,
durable_objects: config?.durable_objects,
workflows: config?.workflows,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ async function resolveDevConfig(
registry: input.dev?.registry,
bindVectorizeToProd: input.dev?.bindVectorizeToProd ?? false,
multiworkerPrimary: input.dev?.multiworkerPrimary,
imagesLocalMode: input.dev?.imagesLocalMode ?? false,
} satisfies StartDevWorkerOptions["dev"];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export async function convertToConfigBundle(
services: bindings.services,
serviceBindings: fetchers,
bindVectorizeToProd: event.config.dev?.bindVectorizeToProd ?? false,
imagesLocalMode: event.config.dev?.imagesLocalMode ?? false,
testScheduled: !!event.config.dev.testScheduled,
};
}
Expand Down
4 changes: 4 additions & 0 deletions packages/wrangler/src/api/startDevWorker/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ export interface StartDevWorkerInput {
/** Whether to use Vectorize mixed mode -- the worker is run locally but accesses to Vectorize are made remotely */
bindVectorizeToProd?: boolean;

/** Whether to use Images local mode -- this is lower fidelity, but doesn't require network access */
imagesLocalMode?: boolean;

/** Treat this as the primary worker in a multiworker setup (i.e. the first Worker in Miniflare's options) */
multiworkerPrimary?: boolean;
};
Expand Down Expand Up @@ -241,6 +244,7 @@ export type Binding =
| { type: "text_blob"; source: File }
| { type: "browser" }
| { type: "ai" }
| { type: "images" }
| { type: "version_metadata" }
| { type: "data_blob"; source: BinaryFile }
| ({ type: "durable_object_namespace" } & NameOmit<CfDurableObject>)
Expand Down
8 changes: 8 additions & 0 deletions packages/wrangler/src/api/startDevWorker/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,11 @@ export function convertCfWorkerInitBindingstoBindings(
output[binding] = { type: "ai", ...x };
break;
}
case "images": {
const { binding, ...x } = info;
output[binding] = { type: "images", ...x };
break;
}
case "version_metadata": {
const { binding, ...x } = info;
output[binding] = { type: "version_metadata", ...x };
Expand Down Expand Up @@ -265,6 +270,7 @@ export async function convertBindingsToCfWorkerInitBindings(
text_blobs: undefined,
browser: undefined,
ai: undefined,
images: undefined,
version_metadata: undefined,
data_blobs: undefined,
durable_objects: undefined,
Expand Down Expand Up @@ -320,6 +326,8 @@ export async function convertBindingsToCfWorkerInitBindings(
bindings.browser = { binding: name };
} else if (binding.type === "ai") {
bindings.ai = { binding: name };
} else if (binding.type === "images") {
bindings.images = { binding: name };
} else if (binding.type === "version_metadata") {
bindings.version_metadata = { binding: name };
} else if (binding.type === "durable_object_namespace") {
Expand Down
1 change: 1 addition & 0 deletions packages/wrangler/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ export const defaultWranglerConfig: Config = {
services: [],
analytics_engine_datasets: [],
ai: undefined,
images: undefined,
version_metadata: undefined,

/*====================================================*/
Expand Down
15 changes: 15 additions & 0 deletions packages/wrangler/src/config/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,21 @@ export interface EnvironmentNonInheritable {
}
| undefined;

/**
* Binding to Cloudflare Images
*
* NOTE: This field is not automatically inherited from the top level environment,
* and so must be specified in every named environment.
*
* @default `{}`
* @nonInheritable
*/
images:
| {
binding: string;
}
| undefined;

/**
* Binding to the Worker Version's metadata
*/
Expand Down
13 changes: 13 additions & 0 deletions packages/wrangler/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ export const friendlyBindingNames: Record<
text_blobs: "Text Blobs",
browser: "Browser",
ai: "AI",
images: "Images",
version_metadata: "Worker Version Metadata",
unsafe: "Unsafe Metadata",
vars: "Vars",
Expand Down Expand Up @@ -290,6 +291,7 @@ export function printBindings(
text_blobs,
browser,
ai,
images,
version_metadata,
unsafe,
vars,
Expand Down Expand Up @@ -553,6 +555,17 @@ export function printBindings(
});
}

if (images !== undefined) {
const entries: [{ key: string; value: string }] = [
{ key: "Name", value: images.binding}
];

output.push({
name: friendlyBindingNames.images,
entries: entries,
});
}

if (pipelines?.length) {
output.push({
name: friendlyBindingNames.pipelines,
Expand Down
14 changes: 12 additions & 2 deletions packages/wrangler/src/config/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1469,7 +1469,7 @@ function normalizeAndValidateEnvironment(
rawEnv,
envName,
"browser",
validateBrowserBinding(envName),
validateNamedSimpleBinding(envName),
undefined
),
ai: notInheritable(
Expand All @@ -1482,6 +1482,16 @@ function normalizeAndValidateEnvironment(
validateAIBinding(envName),
undefined
),
images: notInheritable(
diagnostics,
topLevelEnv,
rawConfig,
rawEnv,
envName,
"images",
validateNamedSimpleBinding(envName),
undefined
),
pipelines: notInheritable(
diagnostics,
topLevelEnv,
Expand Down Expand Up @@ -2193,7 +2203,7 @@ const validateAssetsConfig: ValidatorFn = (diagnostics, field, value) => {
return isValid;
};

const validateBrowserBinding =
const validateNamedSimpleBinding =
(envName: string): ValidatorFn =>
(diagnostics, field, value, config) => {
const fieldPath =
Expand Down
1 change: 1 addition & 0 deletions packages/wrangler/src/deploy/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
wasm_modules: config.wasm_modules,
browser: config.browser,
ai: config.ai,
images: config.images,
version_metadata: config.version_metadata,
text_blobs: {
...config.text_blobs,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export type WorkerMetadataBinding =
| { type: "text_blob"; name: string; part: string }
| { type: "browser"; name: string }
| { type: "ai"; name: string; staging?: boolean }
| { type: "images", name: string; }
| { type: "version_metadata"; name: string }
| { type: "data_blob"; name: string; part: string }
| { type: "kv_namespace"; name: string; namespace_id: string }
Expand Down Expand Up @@ -419,6 +420,13 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
});
}

if (bindings.images !== undefined) {
metadataBindings.push({
name: bindings.images.binding,
type: "images",
});
}

if (bindings.version_metadata !== undefined) {
metadataBindings.push({
name: bindings.version_metadata.binding,
Expand Down
Loading
Loading