Skip to content
Merged
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
29 changes: 29 additions & 0 deletions .changeset/plain-women-double.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
'astro': patch
---

#### :warning: Breaking change for live content collections only

Removes support for the `maxAge` property in `cacheHint` objects returned by live loaders. This did not make sense to set at the loader level, since the loader does not know how long each individual entry should be cached for.

If your live loader returns cache hints with `maxAge`, you need to remove this property:

```diff
return {
entries: [...],
cacheHint: {
tags: ['my-tag'],
- maxAge: 60,
lastModified: new Date(),
},
};
```

The `cacheHint` object now only supports `tags` and `lastModified` properties. If you want to set the max age for a page, you can set the headers manually:

```astro
---
Astro.headers.set('cdn-cache-control', 'maxage=3600');
---
```

9 changes: 9 additions & 0 deletions .changeset/tasty-snails-smile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'astro': patch
---

Adds missing `rendered` property to experimental live collections entry type

Live collections support a `rendered` property that allows you to provide pre-rendered HTML for each entry. While this property was documented and implemented, it was missing from the TypeScript types. This could lead to type errors when trying to use it in a TypeScript project.

No changes to your project code are necessary. You can continue to use the `rendered` property as before, and it will no longer produce TypeScript errors.
15 changes: 1 addition & 14 deletions packages/astro/src/content/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ export function createCollectionToGlobResultMap({

const cacheHintSchema = z.object({
tags: z.array(z.string()).optional(),
maxAge: z.number().optional(),
lastModified: z.date().optional(),
});

Expand Down Expand Up @@ -529,19 +528,13 @@ export function createGetLiveCollection({
// Aggregate cache hints from individual entries if any
if (processedEntries.length > 0) {
const entryTags = new Set<string>();
let minMaxAge: number | undefined;
let latestModified: Date | undefined;

for (const entry of processedEntries) {
if (entry.cacheHint) {
if (entry.cacheHint.tags) {
entry.cacheHint.tags.forEach((tag) => entryTags.add(tag));
}
if (typeof entry.cacheHint.maxAge === 'number') {
if (minMaxAge === undefined || entry.cacheHint.maxAge < minMaxAge) {
minMaxAge = entry.cacheHint.maxAge;
}
}
if (entry.cacheHint.lastModified instanceof Date) {
if (latestModified === undefined || entry.cacheHint.lastModified > latestModified) {
latestModified = entry.cacheHint.lastModified;
Expand All @@ -551,18 +544,12 @@ export function createGetLiveCollection({
}

// Merge collection and entry cache hints
if (entryTags.size > 0 || minMaxAge !== undefined || latestModified || cacheHint) {
if (entryTags.size > 0 || latestModified || cacheHint) {
const mergedCacheHint: CacheHint = {};
if (cacheHint?.tags || entryTags.size > 0) {
// Merge and dedupe tags
mergedCacheHint.tags = [...new Set([...(cacheHint?.tags || []), ...entryTags])];
}
if (cacheHint?.maxAge !== undefined || minMaxAge !== undefined) {
mergedCacheHint.maxAge =
cacheHint?.maxAge !== undefined && minMaxAge !== undefined
? Math.min(cacheHint.maxAge, minMaxAge)
: (cacheHint?.maxAge ?? minMaxAge);
}
if (cacheHint?.lastModified && latestModified) {
mergedCacheHint.lastModified =
cacheHint.lastModified > latestModified ? cacheHint.lastModified : latestModified;
Expand Down
6 changes: 4 additions & 2 deletions packages/astro/src/types/public/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,6 @@ export type GetDataEntryInfoReturnType = { data: Record<string, unknown>; rawDat
export interface CacheHint {
/** Cache tags */
tags?: Array<string>;
/** Maximum age of the response in seconds */
maxAge?: number;
/** Last modified time of the content */
lastModified?: Date;
}
Expand All @@ -140,6 +138,10 @@ export interface LiveDataEntry<TData extends Record<string, any> = Record<string
id: string;
/** The parsed entry data */
data: TData;
/** Optional rendered content */
rendered?: {
html: string;
};
/** A hint for how to cache this entry */
cacheHint?: CacheHint;
}
Expand Down
8 changes: 5 additions & 3 deletions packages/astro/test/fixtures/live-loaders/src/live.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ type EntryFilter = {
};

const entries = {
'123': { id: '123', data: { title: 'Page 123', age: 10 } },
'123': {
id: '123',
data: { title: 'Page 123', age: 10 },
rendered: { html: '<h1>Page 123</h1><p>This is rendered content.</p>' }
},
'456': { id: '456', data: { title: 'Page 456', age: 20 } },
'789': { id: '789', data: { title: 'Page 789', age: 30 } },
};
Expand Down Expand Up @@ -50,7 +54,6 @@ const loader: LiveLoader<Entry, EntryFilter, CollectionFilter, CustomError> = {
},
cacheHint: {
tags: [`page:${filter.id}`],
maxAge: 60,
lastModified: new Date('2025-01-01T00:00:00.000Z'),
},
};
Expand All @@ -68,7 +71,6 @@ const loader: LiveLoader<Entry, EntryFilter, CollectionFilter, CustomError> = {
: Object.values(entries),
cacheHint: {
tags: ['page'],
maxAge: 60,
lastModified: new Date('2025-01-02T00:00:00.000Z'),
},
};
Expand Down
28 changes: 28 additions & 0 deletions packages/astro/test/fixtures/live-loaders/src/pages/rendered.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
import { getLiveEntry, render } from "astro:content";

const { entry } = await getLiveEntry("liveStuff", "123");

if (!entry) {
return new Response(null, { status: 404 });
}

const { Content } = await render(entry);

export const prerender = false;
---

<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>Rendered Content Test</title>
</head>
<body>
<div id="rendered-content">
<Content />
</div>
</body>
</html>
46 changes: 28 additions & 18 deletions packages/astro/test/live-loaders.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,14 @@ describe('Live content collections', () => {
entry: {
id: '123',
data: { title: 'Page 123', age: 10 },
rendered: { html: '<h1>Page 123</h1><p>This is rendered content.</p>' },
cacheHint: {
tags: [`page:123`],
maxAge: 60,
lastModified: '2025-01-01T00:00:00.000Z',
lastModified: '2025-01-01T00:00:00.000Z',
},
},
cacheHint: {
tags: [`page:123`],
maxAge: 60,
lastModified: '2025-01-01T00:00:00.000Z',
},
});
Expand All @@ -63,13 +62,11 @@ describe('Live content collections', () => {
data: { title: 'Page 456', age: 20 },
cacheHint: {
tags: [`page:456`],
maxAge: 60,
lastModified: '2025-01-01T00:00:00.000Z',
lastModified: '2025-01-01T00:00:00.000Z',
},
},
cacheHint: {
tags: [`page:456`],
maxAge: 60,
lastModified: '2025-01-01T00:00:00.000Z',
},
});
Expand All @@ -78,6 +75,7 @@ describe('Live content collections', () => {
{
id: '123',
data: { title: 'Page 123', age: 10 },
rendered: { html: '<h1>Page 123</h1><p>This is rendered content.</p>' },
},
{
id: '456',
Expand All @@ -90,7 +88,6 @@ describe('Live content collections', () => {
],
cacheHint: {
tags: ['page'],
maxAge: 60,
lastModified: '2025-01-02T00:00:00.000Z',
},
});
Expand All @@ -109,14 +106,12 @@ describe('Live content collections', () => {
cacheHint: {
lastModified: '2025-01-01T00:00:00.000Z',
tags: [`page:456`],
maxAge: 60,
},
},
},
cacheHint: {
lastModified: '2025-01-01T00:00:00.000Z',
tags: [`page:456`],
maxAge: 60,
},
},
},
'passes dynamic filter to getEntry',
);
Expand All @@ -126,6 +121,7 @@ describe('Live content collections', () => {
{
id: '123',
data: { title: 'Page 123', age: 15 },
rendered: { html: '<h1>Page 123</h1><p>This is rendered content.</p>' },
},
{
id: '456',
Expand Down Expand Up @@ -154,6 +150,14 @@ describe('Live content collections', () => {
const data = await response.json();
assert.ok(data.error.includes('Use getLiveCollection() instead of getCollection()'));
});

it('can render live entry with rendered content', async () => {
const response = await fixture.fetch('/rendered');
assert.equal(response.status, 200);
const html = await response.text();
assert.ok(html.includes('<h1>Page 123</h1>'));
assert.ok(html.includes('<p>This is rendered content.</p>'));
});
});

describe('SSR', () => {
Expand All @@ -174,16 +178,15 @@ describe('Live content collections', () => {
entry: {
id: '123',
data: { title: 'Page 123', age: 10 },
rendered: { html: '<h1>Page 123</h1><p>This is rendered content.</p>' },
cacheHint: {
lastModified: '2025-01-01T00:00:00.000Z',
tags: [`page:123`],
maxAge: 60,
},
},
},
cacheHint: {
lastModified: '2025-01-01T00:00:00.000Z',
tags: [`page:123`],
maxAge: 60,
},
});
});
Expand All @@ -202,14 +205,12 @@ describe('Live content collections', () => {
cacheHint: {
lastModified: '2025-01-01T00:00:00.000Z',
tags: [`page:456`],
maxAge: 60,
},
},
},
cacheHint: {
lastModified: '2025-01-01T00:00:00.000Z',
tags: [`page:456`],
maxAge: 60,
},
},
},
'passes dynamic filter to getEntry',
);
Expand All @@ -222,5 +223,14 @@ describe('Live content collections', () => {
const data = await response.json();
assert.ok(data.error.includes('Use getLiveCollection() instead of getCollection()'));
});

it('can render live entry with rendered content', async () => {
const request = new Request('http://example.com/rendered');
const response = await app.render(request);
assert.equal(response.status, 200);
const html = await response.text();
assert.ok(html.includes('<h1>Page 123</h1>'));
assert.ok(html.includes('<p>This is rendered content.</p>'));
});
});
});
Loading