Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
b305c97
fix: use `@astrojs/cloudflare/image-endpoint` for Cloudflare dev server
rururux Feb 8, 2026
015b212
Change the entrypoint configuration method
rururux Feb 10, 2026
26a8abd
Merge branch 'main' into cloudflare-image-component
rururux Feb 10, 2026
aabfd32
Merge branch 'fix/hahahah-picomatch-cjs' into cloudflare-image-component
Princesseuh Feb 12, 2026
31e58e9
fix(assets): precompile dev deny glob pattern to avoid CJS deps
Princesseuh Feb 11, 2026
2d9a5ec
feat: change default to bindings
Princesseuh Feb 12, 2026
9152bfd
fix: explain comment
Princesseuh Feb 12, 2026
24d2e58
Merge branch 'main' into cloudflare-image-component
Princesseuh Feb 12, 2026
04380dc
fix: use generic endpoint in passthrough
Princesseuh Feb 12, 2026
f518c40
chore: lockfile
Princesseuh Feb 12, 2026
b4dd89c
Merge branch 'main' into cloudflare-image-component
Princesseuh Feb 13, 2026
442d013
chore: changeset
Princesseuh Feb 13, 2026
943c250
Merge remote-tracking branch 'origin/main' into cloudflare-image-comp…
OliverSpeir Feb 16, 2026
260c122
feat: cache cloudflare-binding transforms
OliverSpeir Feb 15, 2026
66a44d7
feat: fix/improve compile
OliverSpeir Feb 16, 2026
a829eb6
Merge branch 'main' into cloudflare-image-component
Princesseuh Feb 23, 2026
f0a8d3f
remove `debug` package
rururux Feb 23, 2026
6d1c5bd
fix changesets
OliverSpeir Feb 23, 2026
d62a625
Update cloudflare-image-service-object.md
OliverSpeir Feb 23, 2026
ee449bd
Update cloudflare-image-component.md
OliverSpeir Feb 23, 2026
233774b
Apply suggestion from @sarah11918
Princesseuh Feb 23, 2026
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
6 changes: 5 additions & 1 deletion packages/integrations/cloudflare/src/utils/image-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ export function setImageConfig(
command: HookParameters<'astro:config:setup'>['command'],
logger: AstroIntegrationLogger,
) {
if (command === 'dev' && config.endpoint.entrypoint === undefined) {
config.endpoint.entrypoint = '@astrojs/cloudflare/image-endpoint';
}

switch (service) {
case 'passthrough':
return { ...config, service: passthroughImageService() };
Expand All @@ -39,7 +43,7 @@ export function setImageConfig(
...config,
service: sharpImageService(),
endpoint: {
entrypoint: command === 'dev' ? undefined : '@astrojs/cloudflare/image-endpoint',
entrypoint: '@astrojs/cloudflare/image-endpoint',
},
};

Expand Down
150 changes: 108 additions & 42 deletions packages/integrations/cloudflare/test/compile-image-service.test.js
Original file line number Diff line number Diff line change
@@ -1,63 +1,129 @@
import * as assert from 'node:assert/strict';
import { after, before, describe, it } from 'node:test';
import * as cheerio from 'cheerio';
import { loadFixture } from './_test-utils.js';

describe('CompileImageService', () => {
let fixture;
let previewServer;

before(async () => {
fixture = await loadFixture({
root: './fixtures/compile-image-service/',
});
await fixture.build();
previewServer = await fixture.preview();
});

after(async () => {
await previewServer.stop();
});
describe('dev', () => {
let devServer;

it('forbids http://', async () => {
const res = await fixture.fetch('/_image?href=http://placehold.co/600x400');
const html = await res.text();
const status = res.status;
assert.equal(html, 'Forbidden');
assert.equal(status, 403);
});
before(async () => {
devServer = await fixture.startDevServer();
});

it('forbids https://', async () => {
const res = await fixture.fetch('/_image?href=https://placehold.co/600x400');
const html = await res.text();
const status = res.status;
assert.equal(html, 'Forbidden');
assert.equal(status, 403);
});
after(async () => {
await devServer.stop();
});

it('forbids //', async () => {
const res = await fixture.fetch('/_image?href=//placehold.co/600x400');
const html = await res.text();
const status = res.status;
assert.equal(html, 'Blocked');
assert.equal(status, 403);
});
it('forbids http://', async () => {
const res = await fixture.fetch('/_image?href=http://placehold.co/600x400');
const html = await res.text();
const status = res.status;
assert.equal(html, 'Forbidden');
assert.equal(status, 403);
});

it('allows trusted with redirect', async () => {
const res = await fixture.fetch(
'/_image?href=https://astro.build/_astro/HeroBackground.B0iWl89K_2hpsgp.webp',
{ redirect: 'manual' },
);
const header = res.headers.get('location');
const status = res.status;
assert.equal(header, 'https://astro.build/_astro/HeroBackground.B0iWl89K_2hpsgp.webp');
assert.equal(status, 302);
it('forbids https://', async () => {
const res = await fixture.fetch('/_image?href=https://placehold.co/600x400');
const html = await res.text();
const status = res.status;
assert.equal(html, 'Forbidden');
assert.equal(status, 403);
});

it('forbids //', async () => {
const res = await fixture.fetch('/_image?href=//placehold.co/600x400');
const html = await res.text();
const status = res.status;
assert.equal(html, 'Blocked');
assert.equal(status, 403);
});

it('allows trusted with redirect', async () => {
const res = await fixture.fetch(
'/_image?href=https://astro.build/_astro/HeroBackground.B0iWl89K_2hpsgp.webp',
{ redirect: 'manual' },
);
const header = res.headers.get('location');
const status = res.status;
assert.equal(header, 'https://astro.build/_astro/HeroBackground.B0iWl89K_2hpsgp.webp');
assert.equal(status, 302);
});

// On the dev server, the URL for local images includes an absolute path,
// making it difficult to specify statically. Thus, we retrieve it dynamically from the HTML.
it('allows local', async () => {
const html = await fixture.fetch('/blog/post').then((res) => res.text());
const $ = cheerio.load(html);
const res = await fixture.fetch($('img').attr('src'));
const blob = await res.blob();
const status = res.status;
assert.equal(blob.type, 'image/jpeg');
assert.equal(status, 200);
});
});

it('allows local', async () => {
const res = await fixture.fetch('/_image?href=/_astro/placeholder.gLBdjEDe.jpg');
const blob = await res.blob();
const status = res.status;
assert.equal(blob.type, 'image/jpeg');
assert.equal(status, 200);
describe('preview', () => {
let previewServer;

before(async () => {
await fixture.build();
previewServer = await fixture.preview();
});

after(async () => {
await previewServer.stop();
});

it('forbids http://', async () => {
const res = await fixture.fetch('/_image?href=http://placehold.co/600x400');
const html = await res.text();
const status = res.status;
assert.equal(html, 'Forbidden');
assert.equal(status, 403);
});

it('forbids https://', async () => {
const res = await fixture.fetch('/_image?href=https://placehold.co/600x400');
const html = await res.text();
const status = res.status;
assert.equal(html, 'Forbidden');
assert.equal(status, 403);
});

it('forbids //', async () => {
const res = await fixture.fetch('/_image?href=//placehold.co/600x400');
const html = await res.text();
const status = res.status;
assert.equal(html, 'Blocked');
assert.equal(status, 403);
});

it('allows trusted with redirect', async () => {
const res = await fixture.fetch(
'/_image?href=https://astro.build/_astro/HeroBackground.B0iWl89K_2hpsgp.webp',
{ redirect: 'manual' },
);
const header = res.headers.get('location');
const status = res.status;
assert.equal(header, 'https://astro.build/_astro/HeroBackground.B0iWl89K_2hpsgp.webp');
assert.equal(status, 302);
});

it('allows local', async () => {
const res = await fixture.fetch('/_image?href=/_astro/placeholder.gLBdjEDe.jpg');
const blob = await res.blob();
const status = res.status;
assert.equal(blob.type, 'image/jpeg');
assert.equal(status, 200);
});
});
});