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
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ const features: Feature[] = [{
title: 'LLMs.txt',
description: 'Serve llms.txt, per-entry markdown exports, and Accept: text/markdown content negotiation for AI and LLM tooling',
flag: 'llmsTxt'
}, {
title: 'Get helper deduplication',
description: 'Deduplicate identical {{#get}} helper queries within a single request to avoid redundant database calls',
flag: 'getHelperDeduplication'
}];

const AlphaFeatures: React.FC = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const AnnouncementBarPreview: React.FC<AnnouncementBarSettings> = ({announcement
announcementContent,
visibilityMemo
),
Accept: 'text/plain'
Accept: 'text/html'
},
mode: 'cors',
credentials: 'include'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ const ThemePreview: React.FC<ThemePreviewProps> = ({settings,url}) => {
headers: {
'Content-Type': 'text/html;charset=utf-8',
'x-ghost-preview': previewData,
Accept: 'text/plain'
Accept: 'text/html'
},
mode: 'cors',
credentials: 'include'
Expand Down
1 change: 1 addition & 0 deletions apps/admin-x-settings/test/acceptance/site/design.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ test.describe('Design settings', async () => {

const matchingHeader = previewHeaders.headers()['x-ghost-preview'];
expect(matchingHeader).toContain('cd5786');
expect(previewHeaders.headers().accept).toBe('text/html');
await expect(modal.getByTestId('toggle-unsplash-button')).toBeVisible();
await modal.getByRole('button', {name: 'Save'}).click();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const hbs = require('../engine');
const urlUtils = require('../../../../shared/url-utils');
const customThemeSettingsCache = require('../../../../shared/custom-theme-settings-cache');
const preview = require('../preview');
const config = require('../../../../shared/config');
const labs = require('../../../../shared/labs');

function updateLocalTemplateOptions(req, res, next) {
const localTemplateOptions = hbs.getLocalTemplateOptions(res.locals);
Expand Down Expand Up @@ -39,7 +39,7 @@ function updateLocalTemplateOptions(req, res, next) {
status: req.member.status
} : null;

const enableDeduplication = config.get('optimization:getHelper:deduplication');
const enableDeduplication = labs.isSet('getHelperDeduplication');

hbs.updateLocalTemplateOptions(res.locals, _.merge({}, localTemplateOptions, {
data: {
Expand Down
3 changes: 2 additions & 1 deletion ghost/core/core/shared/labs.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ const PRIVATE_FEATURES = [
'indexnow',
'pictureImageFormats',
'smarterCounts',
'llmsTxt'
'llmsTxt',
'getHelperDeduplication'
];

module.exports.GA_KEYS = [...GA_FEATURES];
Expand Down
2 changes: 1 addition & 1 deletion ghost/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@
"rewire": "9.0.1",
"sinon": "catalog:",
"supertest": "6.3.4",
"tmp": "0.2.5",
"tmp": "0.2.6",
"tsx": "4.21.0",
"typescript": "catalog:",
"validator": "catalog:",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Object {
"emailUniqueid": true,
"explore": true,
"featurebaseFeedback": true,
"getHelperDeduplication": true,
"giftSubscriptions": true,
"importMemberTier": true,
"indexnow": true,
Expand Down
9 changes: 0 additions & 9 deletions ghost/core/test/unit/frontend/helpers/get.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,6 @@ describe('{{#get}} helper', function () {
});

it('should deduplicate identical queries when enabled', async function () {
configUtils.set('optimization:getHelper:deduplication', true);
locals = {root: {_locals: {}}, _queryCache: new Map()};

// First call
Expand All @@ -641,7 +640,6 @@ describe('{{#get}} helper', function () {
});

it('should make separate API calls for different queries when enabled', async function () {
configUtils.set('optimization:getHelper:deduplication', true);
locals = {root: {_locals: {}}, _queryCache: new Map()};

// First call
Expand All @@ -663,8 +661,6 @@ describe('{{#get}} helper', function () {
});

it('should include member uuid in cache key', async function () {
configUtils.set('optimization:getHelper:deduplication', true);

// Call with member A
const localsA = {root: {_locals: {}}, _queryCache: new Map(), member: {uuid: 'member-a'}};
await get.call(
Expand All @@ -686,7 +682,6 @@ describe('{{#get}} helper', function () {
});

it('should not cache failed API requests', async function () {
configUtils.set('optimization:getHelper:deduplication', true);
locals = {root: {_locals: {}}, _queryCache: new Map()};

// Set up stub to fail first, then succeed
Expand All @@ -712,7 +707,6 @@ describe('{{#get}} helper', function () {
});

it('should work without _queryCache in data', async function () {
configUtils.set('optimization:getHelper:deduplication', true);
// No _queryCache in locals
locals = {root: {_locals: {}}};

Expand All @@ -728,7 +722,6 @@ describe('{{#get}} helper', function () {
});

it('should deduplicate queries with same parameters in different order', async function () {
configUtils.set('optimization:getHelper:deduplication', true);
locals = {root: {_locals: {}}, _queryCache: new Map()};

// First call
Expand All @@ -750,7 +743,6 @@ describe('{{#get}} helper', function () {
});

it('should handle concurrent identical requests', async function () {
configUtils.set('optimization:getHelper:deduplication', true);
locals = {root: {_locals: {}}, _queryCache: new Map()};

let resolveBrowse;
Expand Down Expand Up @@ -788,7 +780,6 @@ describe('{{#get}} helper', function () {
});

it('should not reuse the same response object instance across renders', async function () {
configUtils.set('optimization:getHelper:deduplication', true);
locals = {root: {_locals: {}}, _queryCache: new Map()};

await get.call(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,33 @@ describe('Themes middleware', function () {
`admin_url should end with /ghost/ but got: ${data.site.admin_url}`);
assert.equal(data.site.admin_url, 'https://admin.example.com/ghost/');
});

it('does not add a _queryCache when the getHelperDeduplication flag is disabled', async function () {
// fakeLabsData does not include getHelperDeduplication, so the flag is off
await request(app)
.get('/')
.expect(200);

sinon.assert.calledOnce(hbsUpdateLocalTemplateOptionsStub);
const templateOptions = hbsUpdateLocalTemplateOptionsStub.firstCall.args[1];
const data = templateOptions.data;

assert.ok(!('_queryCache' in data), '_queryCache should not be set when the flag is disabled');
});

it('adds a _queryCache Map when the getHelperDeduplication flag is enabled', async function () {
fakeLabsData.getHelperDeduplication = true;

await request(app)
.get('/')
.expect(200);

sinon.assert.calledOnce(hbsUpdateLocalTemplateOptionsStub);
const templateOptions = hbsUpdateLocalTemplateOptionsStub.firstCall.args[1];
const data = templateOptions.data;

assert.ok(data._queryCache instanceof Map, '_queryCache should be a Map when the flag is enabled');
});
});

describe('Preview Mode', function () {
Expand Down
10 changes: 8 additions & 2 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,5 @@ minimumReleaseAgeExclude:
- "@types/react@18.3.29"
# Renovate security update: @playwright/test@1.60.0
- "@playwright/test@1.60.0"
# Renovate security update: tmp@0.2.6
- tmp@0.2.6
Loading