Skip to content
Closed
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
11 changes: 5 additions & 6 deletions src/cloudflare/internal/images-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,11 @@ class TransformationResultImpl implements ImageTransformationResult {
: stream;
}

response(): Response {
return new Response(this.image(), {
headers: {
'content-type': this.contentType(),
},
});
response(options?: ImageTransformationResponseOptions): Response {
const headers = new Headers(options?.headers);
headers.set('content-type', this.contentType());

return new Response(this.image(), { headers });
}
}

Expand Down
7 changes: 6 additions & 1 deletion src/cloudflare/internal/images.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,11 +267,16 @@ type ImageTransformationOutputOptions = {
encoding?: 'base64';
};

type ImageTransformationResponseOptions = {
headers?: HeadersInit;
};

interface ImageTransformationResult {
/**
* The image as a response, ready to store in cache or return to users
* @param options Options that apply to the returned response, e.g. additional headers
*/
response(): Response;
response(options?: ImageTransformationResponseOptions): Response;
/**
* The content type of the returned image
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,60 @@ const expectedSpans = [
'http.response.body.size': 359n,
closed: true,
},
{
name: 'images_output',
'cloudflare.binding.type': 'Images',
'cloudflare.images.options.format': 'image/avif',
closed: true,
},
{
name: 'fetch',
'network.protocol.name': 'http',
'network.protocol.version': 'HTTP/1.1',
'http.request.method': 'POST',
'url.full': 'https://js.images.cloudflare.com/transform',
'http.request.header.content-type':
'multipart/form-data; boundary=<dynamic>',
'http.response.status_code': 200n,
'http.response.body.size': 60n,
closed: true,
},
{
name: 'images_output',
'cloudflare.binding.type': 'Images',
'cloudflare.images.options.format': 'image/avif',
closed: true,
},
{
name: 'fetch',
'network.protocol.name': 'http',
'network.protocol.version': 'HTTP/1.1',
'http.request.method': 'POST',
'url.full': 'https://js.images.cloudflare.com/transform',
'http.request.header.content-type':
'multipart/form-data; boundary=<dynamic>',
'http.response.status_code': 200n,
'http.response.body.size': 60n,
closed: true,
},
{
name: 'images_output',
'cloudflare.binding.type': 'Images',
'cloudflare.images.options.format': 'image/avif',
closed: true,
},
{
name: 'fetch',
'network.protocol.name': 'http',
'network.protocol.version': 'HTTP/1.1',
'http.request.method': 'POST',
'url.full': 'https://js.images.cloudflare.com/transform',
'http.request.header.content-type':
'multipart/form-data; boundary=<dynamic>',
'http.response.status_code': 200n,
'http.response.body.size': 60n,
closed: true,
},
{
name: 'images_output',
'cloudflare.binding.type': 'Images',
Expand Down
73 changes: 73 additions & 0 deletions src/cloudflare/internal/test/images/images-api-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,79 @@ export const test_images_transform = {
},
};

export const test_images_response_default_headers = {
/**
* @param {unknown} _
* @param {Env} env
*/
async test(_, env) {
const result = await env.images
.input(inputStream(['png']))
.output({ format: 'image/avif' });

const response = result.response();

assert.strictEqual(
response.headers.get('content-type'),
'application/json'
);
assert.strictEqual(response.headers.get('cache-control'), null);
},
};

export const test_images_response_custom_headers = {
/**
* @param {unknown} _
* @param {Env} env
*/
async test(_, env) {
const result = await env.images
.input(inputStream(['png']))
.output({ format: 'image/avif' });

const response = result.response({
headers: {
'Cache-Control': 'public, max-age=3600, stale-while-revalidate=86400',
'Cache-Tag': 'my-tag',
},
});

assert.strictEqual(
response.headers.get('cache-control'),
'public, max-age=3600, stale-while-revalidate=86400'
);
assert.strictEqual(response.headers.get('cache-tag'), 'my-tag');
// content-type still reflects the transformed image
assert.strictEqual(
response.headers.get('content-type'),
'application/json'
);
},
};

export const test_images_response_content_type_not_overridable = {
/**
* @param {unknown} _
* @param {Env} env
*/
async test(_, env) {
const result = await env.images
.input(inputStream(['png']))
.output({ format: 'image/avif' });

// Attempting to set a conflicting content-type
// the transformed image's actual content type always wins.
const response = result.response({
headers: { 'Content-Type': 'text/plain' },
});

assert.strictEqual(
response.headers.get('content-type'),
'application/json'
);
},
};

export const test_images_nested_draw = {
/**
* @param {unknown} _
Expand Down
7 changes: 6 additions & 1 deletion types/defines/images.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,11 +264,16 @@ type ImageTransformationOutputOptions = {
encoding?: 'base64';
};

type ImageTransformationResponseOptions = {
headers?: HeadersInit;
};

interface ImageTransformationResult {
/**
* The image as a response, ready to store in cache or return to users
* @param options Options that apply to the returned response, e.g. additional headers
*/
response(): Response;
response(options?: ImageTransformationResponseOptions): Response;
/**
* The content type of the returned image
*/
Expand Down
6 changes: 5 additions & 1 deletion types/generated-snapshot/experimental/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14836,11 +14836,15 @@ interface ImageTransformer {
type ImageTransformationOutputOptions = {
encoding?: "base64";
};
type ImageTransformationResponseOptions = {
headers?: HeadersInit;
};
interface ImageTransformationResult {
/**
* The image as a response, ready to store in cache or return to users
* @param options Options that apply to the returned response, e.g. additional headers
*/
response(): Response;
response(options?: ImageTransformationResponseOptions): Response;
/**
* The content type of the returned image
*/
Expand Down
6 changes: 5 additions & 1 deletion types/generated-snapshot/experimental/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14863,11 +14863,15 @@ export interface ImageTransformer {
export type ImageTransformationOutputOptions = {
encoding?: "base64";
};
export type ImageTransformationResponseOptions = {
headers?: HeadersInit;
};
export interface ImageTransformationResult {
/**
* The image as a response, ready to store in cache or return to users
* @param options Options that apply to the returned response, e.g. additional headers
*/
response(): Response;
response(options?: ImageTransformationResponseOptions): Response;
/**
* The content type of the returned image
*/
Expand Down
6 changes: 5 additions & 1 deletion types/generated-snapshot/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14188,11 +14188,15 @@ interface ImageTransformer {
type ImageTransformationOutputOptions = {
encoding?: "base64";
};
type ImageTransformationResponseOptions = {
headers?: HeadersInit;
};
interface ImageTransformationResult {
/**
* The image as a response, ready to store in cache or return to users
* @param options Options that apply to the returned response, e.g. additional headers
*/
response(): Response;
response(options?: ImageTransformationResponseOptions): Response;
/**
* The content type of the returned image
*/
Expand Down
6 changes: 5 additions & 1 deletion types/generated-snapshot/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14215,11 +14215,15 @@ export interface ImageTransformer {
export type ImageTransformationOutputOptions = {
encoding?: "base64";
};
export type ImageTransformationResponseOptions = {
headers?: HeadersInit;
};
export interface ImageTransformationResult {
/**
* The image as a response, ready to store in cache or return to users
* @param options Options that apply to the returned response, e.g. additional headers
*/
response(): Response;
response(options?: ImageTransformationResponseOptions): Response;
/**
* The content type of the returned image
*/
Expand Down
35 changes: 35 additions & 0 deletions types/test/types/images.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) 2026 Cloudflare, Inc.
// Licensed under the Apache 2.0 license found in the LICENSE file or at:
// https://opensource.org/licenses/Apache-2.0

function expectType<T>(_value: T) {}

export const handler: ExportedHandler<{ IMAGES: ImagesBinding }> = {
async fetch(request, env) {
const result = await env.IMAGES.input(request.body!).output({
format: 'image/webp',
});

// response() can be called with no arguments, as before.
expectType<Response>(result.response());

// response() accepts additional headers, e.g. for cache control.
expectType<Response>(
result.response({
headers: {
'Cache-Control': 'public, max-age=3600, stale-while-revalidate=86400',
},
})
);

// headers can also be a Headers instance or an iterable of pairs.
expectType<Response>(
result.response({ headers: new Headers({ 'Cache-Tag': 'my-tag' }) })
);
expectType<Response>(
result.response({ headers: [['Cache-Tag', 'my-tag']] })
);

return result.response();
},
};
Loading