Renamed files to kebab-case - core - services#25803
Conversation
|
Important Review skippedToo many files! 19 files out of 169 files are above the max files limit of 150. You can disable this status message by setting the WalkthroughThe PR renames many modules from PascalCase to kebab-case and updates all corresponding require/imports across core services and tests. It replaces the previous posts service factory with a new exported PostsService class (moved to posts-service.js), removes the old PostsService.js, and adds posts-service-instance.js which exposes a getPostServiceInstance factory and re-exports the PostsService for tests. Numerous JSDoc typedef paths were adjusted to match renamed modules; other modules (offers, tiers, milestones, link-tracking/redirection, oembed, staff, slack, tinybird, recommendations, route-settings, etc.) had import path/casing updates only. Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
ghost/core/core/server/services/oembed/service.js (1)
15-20: Critical: Undefined identifierTwitterOembedProvider.The module is imported and assigned to the variable
TwitterOembedProviderwhich is not defined in scope. This will cause aReferenceErrorat runtime.🐛 Proposed fix
const Twitter = require('./twitter-oembed-provider'); -const twitter = new TwitterOembedProvider({ +const twitter = new Twitter({ config: { bearerToken: config.get('twitter').privateReadOnlyToken } });
🤖 Fix all issues with AI agents
In @ghost/core/core/server/api/endpoints/posts-public.js:
- Around line 6-7: Tests and remaining imports still reference the old
posts-service module; update
ghost/core/test/unit/server/services/posts/posts-service.test.js to require the
new service path used by getPostServiceInstance in
core/server/api/endpoints/posts-public.js, and then remove or consolidate the
legacy core/server/services/posts/posts-service.js and its wrapper in
posts-service-instance.js (or refactor posts-service-instance.js to directly
export the new implementation) so getPostServiceInstance and postsService no
longer pull in the old module.
In @ghost/core/core/server/services/oembed/service.js:
- Around line 8-13: The code imports the module as NFT but then attempts to
instantiate an undefined identifier NftoEmbedProvider, causing a ReferenceError;
fix by aligning the import and instantiation—either change the require to export
the same name used in new (e.g., require as NftoEmbedProvider) or instantiate
using the imported symbol NFT (create the instance as new NFT({...})), ensuring
the constructor name and the variable used for the new call (NftoEmbedProvider
or NFT) match and continue to pass the config
(config.get('opensea').privateReadOnlyApiKey) into the options.
In @ghost/core/core/server/services/oembed/twitter-oembed-provider.js:
- Line 13: Update the test that still references the old class name
`TwitterOEmbedProvider` so it matches the renamed implementation
`TwitterOembedProvider`: change the import/require to import
`TwitterOembedProvider` and update any describe/it blocks or variable references
(e.g., describe title, factory usages, mocks) that mention
`TwitterOEmbedProvider` to use `TwitterOembedProvider` instead to keep names
consistent.
In @ghost/core/core/server/services/posts/posts-service-instance.js:
- Around line 26-29: In getPostUrl, don’t rely on url.forPost mutating
jsonModel; capture and use its return value instead: call url.forPost with
post.id and the result of post.toJSON (jsonModel) and return the .url property
from the returned object; update getPostUrl to return that value and remove the
standalone url.forPost call that only relied on mutation (refer to getPostUrl,
post.toJSON, and url.forPost).
In @ghost/core/test/unit/server/services/oembed/nft-oembed.test.js:
- Line 2: The test has a variable name casing mismatch: the module is required
into NFTOembedProvider but the test instantiates NftoEmbedProvider, causing a
ReferenceError; update either the require identifier or the instantiation so
both use the exact same identifier (e.g., change the require to
NftoEmbedProvider or change the new expression to NFTOembedProvider) ensuring
NFTOembedProvider / NftoEmbedProvider references match exactly.
🧹 Nitpick comments (2)
ghost/core/.eslintrc.js (1)
104-109: Consider updating the comment to reflect services inclusion.The ESLint rule extension to cover service files is appropriate and aligns with the kebab-case migration. However, the comment on line 104 only mentions "Frontend files" but the rule now also applies to service files.
📝 Suggested comment update
{ - // Frontend files use kebab-case filenames with PascalCase class exports - files: ['core/frontend/**/*.js', 'core/server/services/**/*.{js,ts}'], + // Frontend and service files use kebab-case filenames with PascalCase class exports + files: ['core/frontend/**/*.js', 'core/server/services/**/*.{js,ts}'], rules: { 'ghost/filenames/match-exported-class': 'off', 'ghost/filenames/match-regex': ['error', '^[a-z0-9.-]+$', false] } },ghost/core/core/server/services/posts/posts-service.js (1)
406-416: Consider usingnew ObjectId()for consistency.Line 410 calls
ObjectId()without thenewkeyword, while Line 256 usesnew ObjectId(). Althoughbson-objectidsupports both patterns, usingnewconsistently improves code readability and follows the constructor convention.♻️ Proposed fix for consistency
if (typeof tier.id === 'string') { toInsert.push({ - id: ObjectId().toHexString(), + id: (new ObjectId()).toHexString(), post_id: postId, product_id: tier.id, sort_order: index }); }
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (135)
ghost/core/.eslintrc.jsghost/core/core/server/api/endpoints/pages.jsghost/core/core/server/api/endpoints/posts-public.jsghost/core/core/server/api/endpoints/posts.jsghost/core/core/server/api/endpoints/search-index-public.jsghost/core/core/server/api/endpoints/search-index.jsghost/core/core/server/api/endpoints/utils/serializers/output/mappers/posts.jsghost/core/core/server/lib/lexical.jsghost/core/core/server/services/activitypub/activity-pub-service-wrapper.jsghost/core/core/server/services/activitypub/activity-pub-service.tsghost/core/core/server/services/activitypub/index.jsghost/core/core/server/services/explore-ping/index.jsghost/core/core/server/services/explore/index.jsghost/core/core/server/services/link-redirection/index.jsghost/core/core/server/services/link-redirection/link-redirect-repository.jsghost/core/core/server/services/link-redirection/link-redirect.jsghost/core/core/server/services/link-redirection/link-redirects-service.jsghost/core/core/server/services/link-redirection/redirect-event.jsghost/core/core/server/services/link-tracking/click-event.jsghost/core/core/server/services/link-tracking/full-post-link.jsghost/core/core/server/services/link-tracking/index.jsghost/core/core/server/services/link-tracking/link-click-repository.jsghost/core/core/server/services/link-tracking/link-click-tracking-service.jsghost/core/core/server/services/link-tracking/post-link-repository.jsghost/core/core/server/services/link-tracking/post-link.jsghost/core/core/server/services/members/importer/MembersCSVImporter.jsghost/core/core/server/services/members/members-api/services/PaymentsService.jsghost/core/core/server/services/milestones/bookshelf-milestone-repository.jsghost/core/core/server/services/milestones/in-memory-milestone-repository.jsghost/core/core/server/services/milestones/milestone-created-event.jsghost/core/core/server/services/milestones/milestone-queries.jsghost/core/core/server/services/milestones/milestone.jsghost/core/core/server/services/milestones/milestones-service.jsghost/core/core/server/services/milestones/service.jsghost/core/core/server/services/oembed/nfto-embed-provider.jsghost/core/core/server/services/oembed/oembed-service.jsghost/core/core/server/services/oembed/service.jsghost/core/core/server/services/oembed/twitter-oembed-provider.jsghost/core/core/server/services/offers/application/OfferMapper.jsghost/core/core/server/services/offers/application/OffersAPI.jsghost/core/core/server/services/offers/application/UniqueChecker.jsghost/core/core/server/services/offers/domain/events/offer-code-change-event.jsghost/core/core/server/services/offers/domain/events/offer-created-event.jsghost/core/core/server/services/offers/domain/models/offer-amount.jsghost/core/core/server/services/offers/domain/models/offer-cadence.jsghost/core/core/server/services/offers/domain/models/offer-code.jsghost/core/core/server/services/offers/domain/models/offer-created-at.jsghost/core/core/server/services/offers/domain/models/offer-currency.jsghost/core/core/server/services/offers/domain/models/offer-description.jsghost/core/core/server/services/offers/domain/models/offer-duration.jsghost/core/core/server/services/offers/domain/models/offer-name.jsghost/core/core/server/services/offers/domain/models/offer-status.jsghost/core/core/server/services/offers/domain/models/offer-title.jsghost/core/core/server/services/offers/domain/models/offer-type.jsghost/core/core/server/services/offers/domain/models/offer.jsghost/core/core/server/services/offers/domain/models/shared/value-object.jsghost/core/core/server/services/offers/domain/models/stripe-coupon.jsghost/core/core/server/services/offers/offer-bookshelf-repository.jsghost/core/core/server/services/offers/offers-module.jsghost/core/core/server/services/offers/service.jsghost/core/core/server/services/posts/PostsService.jsghost/core/core/server/services/posts/posts-exporter.jsghost/core/core/server/services/posts/posts-service-instance.jsghost/core/core/server/services/posts/posts-service.jsghost/core/core/server/services/posts/stats/post-stats.jsghost/core/core/server/services/recommendations/index.jsghost/core/core/server/services/recommendations/recommendation-enabler-service.jsghost/core/core/server/services/recommendations/recommendation-service-wrapper.jsghost/core/core/server/services/route-settings/default-settings-manager.jsghost/core/core/server/services/route-settings/index.jsghost/core/core/server/services/route-settings/route-settings.jsghost/core/core/server/services/route-settings/settings-loader.jsghost/core/core/server/services/route-settings/settings-path-manager.jsghost/core/core/server/services/slack-notifications/service.jsghost/core/core/server/services/slack-notifications/slack-notifications-service.jsghost/core/core/server/services/slack-notifications/slack-notifications.jsghost/core/core/server/services/staff/index.jsghost/core/core/server/services/staff/staff-service-emails.jsghost/core/core/server/services/staff/staff-service.jsghost/core/core/server/services/tiers/in-memory-tier-repository.jsghost/core/core/server/services/tiers/service.jsghost/core/core/server/services/tiers/tier-activated-event.jsghost/core/core/server/services/tiers/tier-archived-event.jsghost/core/core/server/services/tiers/tier-created-event.jsghost/core/core/server/services/tiers/tier-name-change-event.jsghost/core/core/server/services/tiers/tier-price-change-event.jsghost/core/core/server/services/tiers/tier-repository.jsghost/core/core/server/services/tiers/tier.jsghost/core/core/server/services/tiers/tiers-api.jsghost/core/core/server/services/tinybird/index.jsghost/core/core/server/services/tinybird/tinybird-service-wrapper.jsghost/core/core/server/services/tinybird/tinybird-service.jsghost/core/test/unit/server/services/activitypub/activity-pub-service.test.tsghost/core/test/unit/server/services/link-redirection/link-redirect-repository.test.jsghost/core/test/unit/server/services/link-redirection/link-redirects-service.test.jsghost/core/test/unit/server/services/link-tracking/link-click-repository.test.jsghost/core/test/unit/server/services/link-tracking/link-click-tracking-service.test.jsghost/core/test/unit/server/services/link-tracking/post-link-repository.test.jsghost/core/test/unit/server/services/members/importer/members-csv-importer.test.jsghost/core/test/unit/server/services/members/members-api/services/payments-service.test.jsghost/core/test/unit/server/services/milestones/bookshelf-milestone-repository.test.jsghost/core/test/unit/server/services/milestones/in-memory-milestone-repository.test.jsghost/core/test/unit/server/services/milestones/milestone-queries.test.jsghost/core/test/unit/server/services/milestones/milestone.test.jsghost/core/test/unit/server/services/milestones/milestones-service.test.jsghost/core/test/unit/server/services/oembed/nft-oembed.test.jsghost/core/test/unit/server/services/oembed/oembed-service.test.jsghost/core/test/unit/server/services/oembed/twitter-embed.test.jsghost/core/test/unit/server/services/offers/application/offers-api.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-amount.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-cadence.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-code.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-currency.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-description.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-duration.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-name.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-status.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-title.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-type.test.jsghost/core/test/unit/server/services/offers/domain/models/offer.test.jsghost/core/test/unit/server/services/offers/domain/models/stripe-coupon.test.jsghost/core/test/unit/server/services/posts/posts-exporter.test.jsghost/core/test/unit/server/services/posts/posts-service.test.jsghost/core/test/unit/server/services/route-settings/route-settings.test.jsghost/core/test/unit/server/services/route-settings/settings-path-manager.test.jsghost/core/test/unit/server/services/settings/default-settings-manager.test.jsghost/core/test/unit/server/services/slack-notifications/index.test.jsghost/core/test/unit/server/services/slack-notifications/slack-notifications-service.test.jsghost/core/test/unit/server/services/slack-notifications/slack-notifications.test.jsghost/core/test/unit/server/services/staff/index.test.jsghost/core/test/unit/server/services/staff/staff-service.test.jsghost/core/test/unit/server/services/tiers/tier-repository.test.jsghost/core/test/unit/server/services/tiers/tier.test.jsghost/core/test/unit/server/services/tiers/tiers-api.test.jsghost/core/test/unit/server/services/tinybird/tinybird-service.test.js
💤 Files with no reviewable changes (1)
- ghost/core/core/server/services/posts/PostsService.js
🧰 Additional context used
🧠 Learnings (25)
📓 Common learnings
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use `PascalCase` for component identifiers in filenames while keeping ShadCN-generated files in kebab-case
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/**/*.{ts,tsx,js} : Use `camelCase` for function and variable names
📚 Learning: 2025-11-25T14:28:50.351Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T14:28:50.351Z
Learning: Analytics using Tinybird should reference scripts in `ghost/core/core/server/data/tinybird/scripts/` and datafiles in `ghost/core/core/server/data/tinybird/`
Applied to files:
ghost/core/core/server/services/tinybird/index.jsghost/core/core/server/services/tinybird/tinybird-service-wrapper.jsghost/core/test/unit/server/services/tinybird/tinybird-service.test.js
📚 Learning: 2025-12-01T08:42:35.320Z
Learnt from: jonatansberg
Repo: TryGhost/Ghost PR: 25552
File: e2e/helpers/environment/service-managers/dev-ghost-manager.ts:210-247
Timestamp: 2025-12-01T08:42:35.320Z
Learning: In e2e/helpers/environment/service-managers/dev-ghost-manager.ts, the hardcoded volume name 'ghost-dev_shared-config' at line 231 is intentional. E2E tests run under the 'ghost-dev-e2e' project namespace but deliberately mount the shared-config volume from the main 'ghost-dev' project to access Tinybird credentials created by yarn dev:forward. This is cross-project volume sharing by design.
Applied to files:
ghost/core/core/server/services/tinybird/index.jsghost/core/core/server/services/explore-ping/index.js
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Test names should be lowercase and follow the format 'what is tested - expected outcome'
Applied to files:
ghost/core/test/unit/server/services/tiers/tiers-api.test.jsghost/core/test/unit/server/services/members/members-api/services/payments-service.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-code.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-status.test.jsghost/core/test/unit/server/services/posts/posts-exporter.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-duration.test.jsghost/core/test/unit/server/services/offers/domain/models/stripe-coupon.test.jsghost/core/test/unit/server/services/milestones/in-memory-milestone-repository.test.jsghost/core/test/unit/server/services/tiers/tier.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-cadence.test.jsghost/core/test/unit/server/services/tiers/tier-repository.test.jsghost/core/test/unit/server/services/members/importer/members-csv-importer.test.jsghost/core/test/unit/server/services/link-redirection/link-redirect-repository.test.jsghost/core/test/unit/server/services/offers/domain/models/offer.test.jsghost/core/test/unit/server/services/slack-notifications/slack-notifications.test.jsghost/core/test/unit/server/services/link-tracking/link-click-tracking-service.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-title.test.jsghost/core/test/unit/server/services/milestones/bookshelf-milestone-repository.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-currency.test.jsghost/core/test/unit/server/services/route-settings/settings-path-manager.test.jsghost/core/test/unit/server/services/route-settings/route-settings.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-name.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-description.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-type.test.jsghost/core/test/unit/server/services/link-tracking/link-click-repository.test.jsghost/core/test/unit/server/services/tinybird/tinybird-service.test.js
📚 Learning: 2025-03-13T09:00:20.205Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/utils/pending-activity.ts:13-71
Timestamp: 2025-03-13T09:00:20.205Z
Learning: The pending activity utilities in the Ghost ActivityPub module are covered by tests in the file `apps/admin-x-activitypub/test/unit/utils/pending-activity.ts`.
Applied to files:
ghost/core/test/unit/server/services/activitypub/activity-pub-service.test.tsghost/core/core/server/services/activitypub/index.jsghost/core/core/server/services/activitypub/activity-pub-service-wrapper.jsghost/core/test/unit/server/services/staff/staff-service.test.js
📚 Learning: 2025-03-13T09:00:20.205Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/utils/pending-activity.ts:13-71
Timestamp: 2025-03-13T09:00:20.205Z
Learning: The pending activity utilities in Ghost's ActivityPub module are thoroughly tested in `apps/admin-x-activitypub/test/unit/utils/pending-activity.ts`, which covers `generatePendingActivityId`, `isPendingActivity`, and `generatePendingActivity` functions.
Applied to files:
ghost/core/test/unit/server/services/activitypub/activity-pub-service.test.tsghost/core/core/server/services/activitypub/index.jsghost/core/core/server/services/activitypub/activity-pub-service-wrapper.jsghost/core/test/unit/server/services/staff/staff-service.test.js
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the Ghost ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.
Applied to files:
ghost/core/test/unit/server/services/activitypub/activity-pub-service.test.tsghost/core/core/server/services/activitypub/index.js
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.
Applied to files:
ghost/core/test/unit/server/services/activitypub/activity-pub-service.test.ts
📚 Learning: 2025-08-22T08:55:11.602Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 24733
File: apps/admin-x-activitypub/src/api/activitypub.test.ts:1601-1627
Timestamp: 2025-08-22T08:55:11.602Z
Learning: In the Ghost ActivityPub module, `disableBluesky()` uses direct fetch where auth headers are manually passed, while `enableBluesky()` uses `fetchJSON` which abstracts auth header handling away. This is why the disable test needs to assert Authorization headers but the enable test doesn't.
Applied to files:
ghost/core/test/unit/server/services/activitypub/activity-pub-service.test.ts
📚 Learning: 2025-10-30T17:13:26.190Z
Learnt from: sam-lord
Repo: TryGhost/Ghost PR: 25303
File: ghost/core/core/server/services/email-service/BatchSendingService.js:19-19
Timestamp: 2025-10-30T17:13:26.190Z
Learning: In ghost/core/core/server/services/email-service/BatchSendingService.js and similar files in the Ghost codebase, prefer using `{...options}` spread syntax without explicit guards like `...(options || {})` when spreading potentially undefined objects, as the maintainer prefers cleaner syntax over defensive patterns when the behavior is safe.
Applied to files:
ghost/core/core/server/services/members/importer/MembersCSVImporter.jsghost/core/core/server/services/offers/service.jsghost/core/core/server/services/staff/index.jsghost/core/core/server/services/offers/application/OffersAPI.jsghost/core/test/unit/server/services/milestones/milestones-service.test.jsghost/core/.eslintrc.jsghost/core/core/server/api/endpoints/posts-public.jsghost/core/core/server/services/offers/offer-bookshelf-repository.jsghost/core/core/server/services/milestones/service.jsghost/core/core/server/services/explore-ping/index.jsghost/core/core/server/services/route-settings/index.js
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Use factory pattern for all test data creation instead of hard-coded data or direct database manipulation
Applied to files:
ghost/core/test/unit/server/services/offers/domain/models/offer-code.test.jsghost/core/test/unit/server/services/milestones/milestones-service.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-cadence.test.jsghost/core/test/unit/server/services/offers/domain/models/offer.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-currency.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-type.test.js
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Test suite names should follow the format 'Ghost Admin - Feature' or 'Ghost Public - Feature'
Applied to files:
ghost/core/test/unit/server/services/milestones/milestones-service.test.jsghost/core/test/unit/server/services/route-settings/route-settings.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-name.test.js
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Follow ESLint and `tailwindcss/*` plugin rules when writing styles
Applied to files:
ghost/core/.eslintrc.js
📚 Learning: 2025-05-29T07:45:35.714Z
Learnt from: ErisDS
Repo: TryGhost/Ghost PR: 23582
File: ghost/core/.c8rc.json:24-24
Timestamp: 2025-05-29T07:45:35.714Z
Learning: In Ghost project, app.js files under core/server/web are intentionally excluded from unit test coverage because they are not easily unit-testable due to being entry points with initialization code and side effects.
Applied to files:
ghost/core/.eslintrc.js
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Run `yarn lint` after making changes to fix any ESLint errors and warnings before committing
Applied to files:
ghost/core/.eslintrc.js
📚 Learning: 2025-11-25T14:28:50.351Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T14:28:50.351Z
Learning: Run `yarn lint` to lint all packages or `cd ghost/core && yarn lint` to lint Ghost core (server, shared, frontend, tests)
Applied to files:
ghost/core/.eslintrc.js
📚 Learning: 2025-09-03T12:28:11.174Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 24779
File: ghost/core/core/server/services/members/members-api/controllers/RouterController.js:34-51
Timestamp: 2025-09-03T12:28:11.174Z
Learning: The extractRefererOrRedirect function in Ghost's RouterController (ghost/core/core/server/services/members/members-api/controllers/RouterController.js) is working as intended according to maintainer kevinansfield. The current handling of autoRedirect, redirect parameter parsing, and referrer logic does not need the security-related changes suggested around autoRedirect coercion, relative URL handling, or same-origin checks.
Applied to files:
ghost/core/core/server/services/link-redirection/link-redirects-service.jsghost/core/core/server/services/link-redirection/link-redirect-repository.jsghost/core/core/server/services/link-redirection/redirect-event.jsghost/core/core/server/services/link-tracking/link-click-tracking-service.jsghost/core/core/server/services/link-redirection/index.jsghost/core/test/unit/server/services/link-redirection/link-redirects-service.test.js
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use `PascalCase` for component identifiers in filenames while keeping ShadCN-generated files in kebab-case
Applied to files:
ghost/core/core/server/services/offers/domain/models/offer.jsghost/core/core/server/services/tiers/tier.js
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Follow the AAA (Arrange, Act, Assert) pattern in test structure
Applied to files:
ghost/core/test/unit/server/services/link-redirection/link-redirect-repository.test.js
📚 Learning: 2025-12-02T16:28:27.396Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 25581
File: ghost/core/core/server/services/activitypub/ActivityPubServiceWrapper.js:35-67
Timestamp: 2025-12-02T16:28:27.396Z
Learning: In the Ghost codebase, concurrency concerns related to rapid settings changes triggering event listeners are pre-existing and systemic, affecting all settings event listeners. If addressed, concurrency protection should be implemented at the event/settings service level, not in individual service wrappers like ActivityPubServiceWrapper.
Applied to files:
ghost/core/core/server/services/activitypub/activity-pub-service-wrapper.js
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Each test receives fresh Ghost instance for automatic isolation
Applied to files:
ghost/core/test/unit/server/services/staff/staff-service.test.js
📚 Learning: 2025-11-05T16:42:12.989Z
Learnt from: rob-ghost
Repo: TryGhost/Ghost PR: 25356
File: apps/admin/test-utils/fixtures/query-client.tsx:17-35
Timestamp: 2025-11-05T16:42:12.989Z
Learning: In apps/admin/test-utils/fixtures/query-client.tsx, the createTestQueryClient function is intentionally duplicated from admin-x-framework to reduce external dependencies in the admin app's test utilities.
Applied to files:
ghost/core/test/unit/server/services/milestones/milestone-queries.test.js
📚 Learning: 2025-08-12T18:33:15.524Z
Learnt from: cmraible
Repo: TryGhost/Ghost PR: 24658
File: ghost/admin/package.json:3-3
Timestamp: 2025-08-12T18:33:15.524Z
Learning: In Ghost's admin package.json, third-party packages like ember-cli-postcss, ember-exam, and ember-power-select have their own independent versioning schemes that are unrelated to Ghost's version numbers. Version number coincidences between Ghost versions and these packages should not trigger update suggestions.
Applied to files:
ghost/core/core/server/services/explore-ping/index.js
📚 Learning: 2025-11-25T14:28:50.351Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T14:28:50.351Z
Learning: Core Ghost backend logic should be placed in `ghost/core/core/server/` directory structure organized by purpose (api, services, models, data/schema, etc.)
Applied to files:
ghost/core/core/server/services/explore-ping/index.js
📚 Learning: 2025-11-25T14:28:50.351Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T14:28:50.351Z
Learning: API routes should be defined in `ghost/core/core/server/api/`
Applied to files:
ghost/core/core/server/services/route-settings/index.js
🧬 Code graph analysis (7)
ghost/core/core/server/api/endpoints/search-index-public.js (1)
ghost/core/core/server/api/endpoints/search-index.js (1)
getPostServiceInstance(2-2)
ghost/core/core/server/services/link-tracking/index.js (1)
ghost/core/test/unit/server/services/link-tracking/link-click-tracking-service.test.js (1)
LinkClickTrackingService(1-1)
ghost/core/core/server/api/endpoints/search-index.js (1)
ghost/core/core/server/api/endpoints/search-index-public.js (1)
getPostServiceInstance(2-2)
ghost/core/core/server/services/slack-notifications/service.js (2)
ghost/core/test/unit/server/services/slack-notifications/slack-notifications-service.test.js (1)
SlackNotificationsService(3-3)ghost/core/test/unit/server/services/slack-notifications/slack-notifications.test.js (1)
SlackNotifications(3-3)
ghost/core/core/server/services/tiers/service.js (1)
ghost/core/test/unit/server/services/tiers/tier-repository.test.js (1)
TierRepository(4-4)
ghost/core/core/server/services/posts/posts-service.js (1)
ghost/core/core/server/services/posts/posts-service-instance.js (1)
postsExporter(19-33)
ghost/core/core/server/services/tiers/tier-repository.js (1)
ghost/core/test/unit/server/services/tiers/tier-repository.test.js (1)
TierRepository(4-4)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Ghost-CLI tests
- GitHub Check: Unit tests (Node 22.18.0)
- GitHub Check: Build & Push Docker Image
ghost/core/core/server/services/oembed/twitter-oembed-provider.js
Outdated
Show resolved
Hide resolved
b4694c7 to
b7038b3
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In @ghost/core/core/server/services/oembed/service.js:
- Around line 8-9: The code imports the provider as NFT but instantiates
NFTOEmbedProvider, causing a ReferenceError; fix by making the identifiers
consistent: either change the import to const NFTOEmbedProvider =
require('./nfto-embed-provider') if that export is named NFTOEmbedProvider, or
instantiate the imported symbol by replacing new NFTOEmbedProvider(...) with new
NFT(...) (and ensure the module.exports in nfto-embed-provider matches the
chosen name).
- Around line 15-16: The import name and constructor name mismatch causes a
ReferenceError: change the usage so the instantiated class matches the imported
symbol; either import the provider as TwitterOembedProvider or instantiate the
imported Twitter symbol (i.e., make the new expression use the same identifier
as the require call), updating the instantiation that assigns to the twitter
variable to use the correct class name.
🧹 Nitpick comments (1)
ghost/core/core/server/services/posts/posts-service.js (1)
405-417: Consider consistent ObjectId instantiation.Line 410 uses
ObjectId().toHexString()withoutnew, while line 256 uses(new ObjectId()).toHexString()withnew. While both patterns work with bson-objectid, using a consistent pattern throughout the file improves readability.Suggested fix for consistency
if (typeof tier.id === 'string') { toInsert.push({ - id: ObjectId().toHexString(), + id: (new ObjectId()).toHexString(), post_id: postId, product_id: tier.id, sort_order: index
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (135)
ghost/core/.eslintrc.jsghost/core/core/server/api/endpoints/pages.jsghost/core/core/server/api/endpoints/posts-public.jsghost/core/core/server/api/endpoints/posts.jsghost/core/core/server/api/endpoints/search-index-public.jsghost/core/core/server/api/endpoints/search-index.jsghost/core/core/server/api/endpoints/utils/serializers/output/mappers/posts.jsghost/core/core/server/lib/lexical.jsghost/core/core/server/services/activitypub/activity-pub-service-wrapper.jsghost/core/core/server/services/activitypub/activity-pub-service.tsghost/core/core/server/services/activitypub/index.jsghost/core/core/server/services/explore-ping/index.jsghost/core/core/server/services/explore/index.jsghost/core/core/server/services/link-redirection/index.jsghost/core/core/server/services/link-redirection/link-redirect-repository.jsghost/core/core/server/services/link-redirection/link-redirect.jsghost/core/core/server/services/link-redirection/link-redirects-service.jsghost/core/core/server/services/link-redirection/redirect-event.jsghost/core/core/server/services/link-tracking/click-event.jsghost/core/core/server/services/link-tracking/full-post-link.jsghost/core/core/server/services/link-tracking/index.jsghost/core/core/server/services/link-tracking/link-click-repository.jsghost/core/core/server/services/link-tracking/link-click-tracking-service.jsghost/core/core/server/services/link-tracking/post-link-repository.jsghost/core/core/server/services/link-tracking/post-link.jsghost/core/core/server/services/members/importer/MembersCSVImporter.jsghost/core/core/server/services/members/members-api/services/PaymentsService.jsghost/core/core/server/services/milestones/bookshelf-milestone-repository.jsghost/core/core/server/services/milestones/in-memory-milestone-repository.jsghost/core/core/server/services/milestones/milestone-created-event.jsghost/core/core/server/services/milestones/milestone-queries.jsghost/core/core/server/services/milestones/milestone.jsghost/core/core/server/services/milestones/milestones-service.jsghost/core/core/server/services/milestones/service.jsghost/core/core/server/services/oembed/nfto-embed-provider.jsghost/core/core/server/services/oembed/oembed-service.jsghost/core/core/server/services/oembed/service.jsghost/core/core/server/services/oembed/twitter-oembed-provider.jsghost/core/core/server/services/offers/application/OfferMapper.jsghost/core/core/server/services/offers/application/OffersAPI.jsghost/core/core/server/services/offers/application/UniqueChecker.jsghost/core/core/server/services/offers/domain/events/offer-code-change-event.jsghost/core/core/server/services/offers/domain/events/offer-created-event.jsghost/core/core/server/services/offers/domain/models/offer-amount.jsghost/core/core/server/services/offers/domain/models/offer-cadence.jsghost/core/core/server/services/offers/domain/models/offer-code.jsghost/core/core/server/services/offers/domain/models/offer-created-at.jsghost/core/core/server/services/offers/domain/models/offer-currency.jsghost/core/core/server/services/offers/domain/models/offer-description.jsghost/core/core/server/services/offers/domain/models/offer-duration.jsghost/core/core/server/services/offers/domain/models/offer-name.jsghost/core/core/server/services/offers/domain/models/offer-status.jsghost/core/core/server/services/offers/domain/models/offer-title.jsghost/core/core/server/services/offers/domain/models/offer-type.jsghost/core/core/server/services/offers/domain/models/offer.jsghost/core/core/server/services/offers/domain/models/shared/value-object.jsghost/core/core/server/services/offers/domain/models/stripe-coupon.jsghost/core/core/server/services/offers/offer-bookshelf-repository.jsghost/core/core/server/services/offers/offers-module.jsghost/core/core/server/services/offers/service.jsghost/core/core/server/services/posts/PostsService.jsghost/core/core/server/services/posts/posts-exporter.jsghost/core/core/server/services/posts/posts-service-instance.jsghost/core/core/server/services/posts/posts-service.jsghost/core/core/server/services/posts/stats/post-stats.jsghost/core/core/server/services/recommendations/index.jsghost/core/core/server/services/recommendations/recommendation-enabler-service.jsghost/core/core/server/services/recommendations/recommendation-service-wrapper.jsghost/core/core/server/services/route-settings/default-settings-manager.jsghost/core/core/server/services/route-settings/index.jsghost/core/core/server/services/route-settings/route-settings.jsghost/core/core/server/services/route-settings/settings-loader.jsghost/core/core/server/services/route-settings/settings-path-manager.jsghost/core/core/server/services/slack-notifications/service.jsghost/core/core/server/services/slack-notifications/slack-notifications-service.jsghost/core/core/server/services/slack-notifications/slack-notifications.jsghost/core/core/server/services/staff/index.jsghost/core/core/server/services/staff/staff-service-emails.jsghost/core/core/server/services/staff/staff-service.jsghost/core/core/server/services/tiers/in-memory-tier-repository.jsghost/core/core/server/services/tiers/service.jsghost/core/core/server/services/tiers/tier-activated-event.jsghost/core/core/server/services/tiers/tier-archived-event.jsghost/core/core/server/services/tiers/tier-created-event.jsghost/core/core/server/services/tiers/tier-name-change-event.jsghost/core/core/server/services/tiers/tier-price-change-event.jsghost/core/core/server/services/tiers/tier-repository.jsghost/core/core/server/services/tiers/tier.jsghost/core/core/server/services/tiers/tiers-api.jsghost/core/core/server/services/tinybird/index.jsghost/core/core/server/services/tinybird/tinybird-service-wrapper.jsghost/core/core/server/services/tinybird/tinybird-service.jsghost/core/test/unit/server/services/activitypub/activity-pub-service.test.tsghost/core/test/unit/server/services/link-redirection/link-redirect-repository.test.jsghost/core/test/unit/server/services/link-redirection/link-redirects-service.test.jsghost/core/test/unit/server/services/link-tracking/link-click-repository.test.jsghost/core/test/unit/server/services/link-tracking/link-click-tracking-service.test.jsghost/core/test/unit/server/services/link-tracking/post-link-repository.test.jsghost/core/test/unit/server/services/members/importer/members-csv-importer.test.jsghost/core/test/unit/server/services/members/members-api/services/payments-service.test.jsghost/core/test/unit/server/services/milestones/bookshelf-milestone-repository.test.jsghost/core/test/unit/server/services/milestones/in-memory-milestone-repository.test.jsghost/core/test/unit/server/services/milestones/milestone-queries.test.jsghost/core/test/unit/server/services/milestones/milestone.test.jsghost/core/test/unit/server/services/milestones/milestones-service.test.jsghost/core/test/unit/server/services/oembed/nft-oembed.test.jsghost/core/test/unit/server/services/oembed/oembed-service.test.jsghost/core/test/unit/server/services/oembed/twitter-embed.test.jsghost/core/test/unit/server/services/offers/application/offers-api.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-amount.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-cadence.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-code.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-currency.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-description.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-duration.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-name.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-status.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-title.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-type.test.jsghost/core/test/unit/server/services/offers/domain/models/offer.test.jsghost/core/test/unit/server/services/offers/domain/models/stripe-coupon.test.jsghost/core/test/unit/server/services/posts/posts-exporter.test.jsghost/core/test/unit/server/services/posts/posts-service.test.jsghost/core/test/unit/server/services/route-settings/route-settings.test.jsghost/core/test/unit/server/services/route-settings/settings-path-manager.test.jsghost/core/test/unit/server/services/settings/default-settings-manager.test.jsghost/core/test/unit/server/services/slack-notifications/index.test.jsghost/core/test/unit/server/services/slack-notifications/slack-notifications-service.test.jsghost/core/test/unit/server/services/slack-notifications/slack-notifications.test.jsghost/core/test/unit/server/services/staff/index.test.jsghost/core/test/unit/server/services/staff/staff-service.test.jsghost/core/test/unit/server/services/tiers/tier-repository.test.jsghost/core/test/unit/server/services/tiers/tier.test.jsghost/core/test/unit/server/services/tiers/tiers-api.test.jsghost/core/test/unit/server/services/tinybird/tinybird-service.test.js
💤 Files with no reviewable changes (1)
- ghost/core/core/server/services/posts/PostsService.js
✅ Files skipped from review due to trivial changes (1)
- ghost/core/core/server/services/offers/domain/models/offer-created-at.js
🚧 Files skipped from review as they are similar to previous changes (65)
- ghost/core/test/unit/server/services/offers/domain/models/offer-description.test.js
- ghost/core/test/unit/server/services/settings/default-settings-manager.test.js
- ghost/core/test/unit/server/services/slack-notifications/slack-notifications.test.js
- ghost/core/test/unit/server/services/oembed/oembed-service.test.js
- ghost/core/core/server/services/tiers/service.js
- ghost/core/test/unit/server/services/offers/domain/models/stripe-coupon.test.js
- ghost/core/core/server/services/tiers/tier-price-change-event.js
- ghost/core/core/server/services/offers/domain/models/offer-currency.js
- ghost/core/test/unit/server/services/tiers/tier-repository.test.js
- ghost/core/core/server/services/offers/domain/events/offer-code-change-event.js
- ghost/core/test/unit/server/services/oembed/twitter-embed.test.js
- ghost/core/test/unit/server/services/offers/domain/models/offer-code.test.js
- ghost/core/core/server/api/endpoints/posts.js
- ghost/core/core/server/services/offers/domain/models/offer-duration.js
- ghost/core/core/server/services/route-settings/index.js
- ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/posts.js
- ghost/core/test/unit/server/services/route-settings/settings-path-manager.test.js
- ghost/core/core/server/api/endpoints/posts-public.js
- ghost/core/test/unit/server/services/offers/domain/models/offer-title.test.js
- ghost/core/core/server/services/recommendations/index.js
- ghost/core/test/unit/server/services/offers/domain/models/offer-type.test.js
- ghost/core/test/unit/server/services/milestones/milestone.test.js
- ghost/core/core/server/api/endpoints/search-index.js
- ghost/core/test/unit/server/services/staff/index.test.js
- ghost/core/core/server/services/offers/application/UniqueChecker.js
- ghost/core/core/server/services/tiers/in-memory-tier-repository.js
- ghost/core/test/unit/server/services/link-tracking/link-click-repository.test.js
- ghost/core/test/unit/server/services/offers/domain/models/offer-duration.test.js
- ghost/core/test/unit/server/services/offers/application/offers-api.test.js
- ghost/core/test/unit/server/services/slack-notifications/index.test.js
- ghost/core/core/server/services/offers/application/OffersAPI.js
- ghost/core/core/server/services/offers/domain/models/offer.js
- ghost/core/core/server/services/tiers/tier-archived-event.js
- ghost/core/core/server/services/milestones/in-memory-milestone-repository.js
- ghost/core/core/server/services/activitypub/activity-pub-service-wrapper.js
- ghost/core/core/server/services/slack-notifications/service.js
- ghost/core/core/server/services/link-redirection/index.js
- ghost/core/test/unit/server/services/tiers/tier.test.js
- ghost/core/core/server/services/activitypub/index.js
- ghost/core/core/server/services/oembed/twitter-oembed-provider.js
- ghost/core/core/server/services/offers/service.js
- ghost/core/test/unit/server/services/offers/domain/models/offer.test.js
- ghost/core/core/server/services/offers/domain/models/offer-code.js
- ghost/core/test/unit/server/services/offers/domain/models/offer-currency.test.js
- ghost/core/core/server/services/milestones/service.js
- ghost/core/core/server/services/offers/domain/models/offer-cadence.js
- ghost/core/core/server/services/offers/domain/models/offer-amount.js
- ghost/core/core/server/services/offers/domain/models/offer-status.js
- ghost/core/test/unit/server/services/posts/posts-exporter.test.js
- ghost/core/core/server/services/link-tracking/index.js
- ghost/core/core/server/services/link-tracking/link-click-repository.js
- ghost/core/core/server/services/offers/domain/models/offer-name.js
- ghost/core/.eslintrc.js
- ghost/core/core/server/services/offers/domain/models/offer-title.js
- ghost/core/test/unit/server/services/tinybird/tinybird-service.test.js
- ghost/core/test/unit/server/services/members/importer/members-csv-importer.test.js
- ghost/core/core/server/services/tiers/tier.js
- ghost/core/core/server/services/link-redirection/link-redirect-repository.js
- ghost/core/core/server/services/offers/offers-module.js
- ghost/core/core/server/services/link-tracking/post-link-repository.js
- ghost/core/core/server/services/tiers/tier-activated-event.js
- ghost/core/core/server/services/tiers/tier-created-event.js
- ghost/core/core/server/services/tinybird/index.js
- ghost/core/test/unit/server/services/offers/domain/models/offer-name.test.js
- ghost/core/core/server/services/members/members-api/services/PaymentsService.js
🧰 Additional context used
🧠 Learnings (18)
📓 Common learnings
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use `PascalCase` for component identifiers in filenames while keeping ShadCN-generated files in kebab-case
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/**/*.{ts,tsx,js} : Use `camelCase` for function and variable names
📚 Learning: 2025-10-30T17:13:26.190Z
Learnt from: sam-lord
Repo: TryGhost/Ghost PR: 25303
File: ghost/core/core/server/services/email-service/BatchSendingService.js:19-19
Timestamp: 2025-10-30T17:13:26.190Z
Learning: In ghost/core/core/server/services/email-service/BatchSendingService.js and similar files in the Ghost codebase, prefer using `{...options}` spread syntax without explicit guards like `...(options || {})` when spreading potentially undefined objects, as the maintainer prefers cleaner syntax over defensive patterns when the behavior is safe.
Applied to files:
ghost/core/core/server/services/staff/index.jsghost/core/test/unit/server/services/milestones/milestones-service.test.jsghost/core/core/server/services/posts/posts-service-instance.jsghost/core/core/server/services/posts/posts-service.jsghost/core/core/server/services/offers/offer-bookshelf-repository.jsghost/core/core/server/services/explore-ping/index.js
📚 Learning: 2025-11-25T14:28:50.351Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T14:28:50.351Z
Learning: Analytics using Tinybird should reference scripts in `ghost/core/core/server/data/tinybird/scripts/` and datafiles in `ghost/core/core/server/data/tinybird/`
Applied to files:
ghost/core/core/server/services/tinybird/tinybird-service-wrapper.js
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Test names should be lowercase and follow the format 'what is tested - expected outcome'
Applied to files:
ghost/core/test/unit/server/services/link-redirection/link-redirect-repository.test.jsghost/core/test/unit/server/services/milestones/in-memory-milestone-repository.test.jsghost/core/test/unit/server/services/milestones/bookshelf-milestone-repository.test.jsghost/core/test/unit/server/services/link-tracking/post-link-repository.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-status.test.jsghost/core/test/unit/server/services/oembed/nft-oembed.test.jsghost/core/test/unit/server/services/members/members-api/services/payments-service.test.jsghost/core/test/unit/server/services/route-settings/route-settings.test.jsghost/core/test/unit/server/services/tiers/tiers-api.test.js
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Follow the AAA (Arrange, Act, Assert) pattern in test structure
Applied to files:
ghost/core/test/unit/server/services/link-redirection/link-redirect-repository.test.js
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Test suite names should follow the format 'Ghost Admin - Feature' or 'Ghost Public - Feature'
Applied to files:
ghost/core/test/unit/server/services/milestones/milestones-service.test.js
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Use factory pattern for all test data creation instead of hard-coded data or direct database manipulation
Applied to files:
ghost/core/test/unit/server/services/milestones/milestones-service.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-cadence.test.js
📚 Learning: 2025-09-03T12:28:11.174Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 24779
File: ghost/core/core/server/services/members/members-api/controllers/RouterController.js:34-51
Timestamp: 2025-09-03T12:28:11.174Z
Learning: The extractRefererOrRedirect function in Ghost's RouterController (ghost/core/core/server/services/members/members-api/controllers/RouterController.js) is working as intended according to maintainer kevinansfield. The current handling of autoRedirect, redirect parameter parsing, and referrer logic does not need the security-related changes suggested around autoRedirect coercion, relative URL handling, or same-origin checks.
Applied to files:
ghost/core/core/server/services/link-redirection/redirect-event.jsghost/core/core/server/services/posts/posts-service-instance.jsghost/core/core/server/services/link-redirection/link-redirects-service.jsghost/core/core/server/services/link-tracking/link-click-tracking-service.jsghost/core/test/unit/server/services/link-redirection/link-redirects-service.test.js
📚 Learning: 2025-03-13T09:00:20.205Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/utils/pending-activity.ts:13-71
Timestamp: 2025-03-13T09:00:20.205Z
Learning: The pending activity utilities in the Ghost ActivityPub module are covered by tests in the file `apps/admin-x-activitypub/test/unit/utils/pending-activity.ts`.
Applied to files:
ghost/core/test/unit/server/services/activitypub/activity-pub-service.test.tsghost/core/test/unit/server/services/staff/staff-service.test.js
📚 Learning: 2025-03-13T09:00:20.205Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/utils/pending-activity.ts:13-71
Timestamp: 2025-03-13T09:00:20.205Z
Learning: The pending activity utilities in Ghost's ActivityPub module are thoroughly tested in `apps/admin-x-activitypub/test/unit/utils/pending-activity.ts`, which covers `generatePendingActivityId`, `isPendingActivity`, and `generatePendingActivity` functions.
Applied to files:
ghost/core/test/unit/server/services/activitypub/activity-pub-service.test.tsghost/core/test/unit/server/services/staff/staff-service.test.js
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the Ghost ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.
Applied to files:
ghost/core/test/unit/server/services/activitypub/activity-pub-service.test.ts
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.
Applied to files:
ghost/core/test/unit/server/services/activitypub/activity-pub-service.test.ts
📚 Learning: 2025-08-22T08:55:11.602Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 24733
File: apps/admin-x-activitypub/src/api/activitypub.test.ts:1601-1627
Timestamp: 2025-08-22T08:55:11.602Z
Learning: In the Ghost ActivityPub module, `disableBluesky()` uses direct fetch where auth headers are manually passed, while `enableBluesky()` uses `fetchJSON` which abstracts auth header handling away. This is why the disable test needs to assert Authorization headers but the enable test doesn't.
Applied to files:
ghost/core/test/unit/server/services/activitypub/activity-pub-service.test.ts
📚 Learning: 2025-08-11T19:39:00.428Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 24651
File: ghost/core/test/utils/urlUtils.js:53-57
Timestamp: 2025-08-11T19:39:00.428Z
Learning: In Ghost's test utilities, when fixing specific issues like async behavior, it's preferred to maintain existing error handling patterns (even if suboptimal) to keep PRs focused on their primary objective. Error handling improvements can be addressed in separate, dedicated PRs.
Applied to files:
ghost/core/core/server/services/posts/posts-service-instance.js
📚 Learning: 2025-08-12T18:33:15.524Z
Learnt from: cmraible
Repo: TryGhost/Ghost PR: 24658
File: ghost/admin/package.json:3-3
Timestamp: 2025-08-12T18:33:15.524Z
Learning: In Ghost's admin package.json, third-party packages like ember-cli-postcss, ember-exam, and ember-power-select have their own independent versioning schemes that are unrelated to Ghost's version numbers. Version number coincidences between Ghost versions and these packages should not trigger update suggestions.
Applied to files:
ghost/core/core/server/services/explore-ping/index.js
📚 Learning: 2025-11-25T14:28:50.351Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T14:28:50.351Z
Learning: Core Ghost backend logic should be placed in `ghost/core/core/server/` directory structure organized by purpose (api, services, models, data/schema, etc.)
Applied to files:
ghost/core/core/server/services/explore-ping/index.js
📚 Learning: 2025-12-01T08:42:35.320Z
Learnt from: jonatansberg
Repo: TryGhost/Ghost PR: 25552
File: e2e/helpers/environment/service-managers/dev-ghost-manager.ts:210-247
Timestamp: 2025-12-01T08:42:35.320Z
Learning: In e2e/helpers/environment/service-managers/dev-ghost-manager.ts, the hardcoded volume name 'ghost-dev_shared-config' at line 231 is intentional. E2E tests run under the 'ghost-dev-e2e' project namespace but deliberately mount the shared-config volume from the main 'ghost-dev' project to access Tinybird credentials created by yarn dev:forward. This is cross-project volume sharing by design.
Applied to files:
ghost/core/core/server/services/explore-ping/index.js
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Each test receives fresh Ghost instance for automatic isolation
Applied to files:
ghost/core/test/unit/server/services/staff/staff-service.test.js
🧬 Code graph analysis (3)
ghost/core/core/server/services/staff/index.js (1)
ghost/core/test/unit/server/services/staff/staff-service.test.js (1)
StaffService(11-11)
ghost/core/core/server/services/posts/posts-service.js (4)
ghost/core/core/server/services/posts/posts-service-instance.js (1)
postsExporter(19-33)ghost/core/test/unit/server/services/stats/members.test.js (1)
toInsert(109-115)ghost/admin/mirage/config/posts.js (1)
visibilityFilter(43-43)apps/portal/src/utils/helpers.js (1)
allProducts(148-148)
ghost/core/core/server/api/endpoints/search-index-public.js (1)
ghost/core/core/server/api/endpoints/search-index.js (1)
getPostServiceInstance(2-2)
🔇 Additional comments (61)
ghost/core/test/unit/server/services/offers/domain/models/offer-cadence.test.js (1)
3-3: LGTM! Import path correctly updated to kebab-case.The require path has been properly updated from
OfferCadencetooffer-cadence, aligning with the repository's kebab-case filename convention. The class name remainsOfferCadenceas expected.ghost/core/core/server/services/offers/domain/events/offer-created-event.js (1)
1-1: LGTM! Typedef path correctly updated to kebab-case.The JSDoc typedef import path has been properly updated from PascalCase to kebab-case, aligning with the PR's objective of standardizing file naming conventions. The target model file exists at the new path and there are no remaining references to the old path.
ghost/core/core/server/services/offers/domain/models/offer-type.js (1)
1-1: Import path updated to kebab-case convention.The import path has been correctly updated to match the kebab-case naming convention. The target file exists at the new path and no stale references to the old PascalCase path remain in the codebase.
ghost/core/test/unit/server/services/link-redirection/link-redirect-repository.test.js (1)
6-7: Import paths correctly updated to kebab-case. All module references across the codebase consistently use the new kebab-case file paths, with no old PascalCase path references remaining.ghost/core/core/server/services/recommendations/recommendation-service-wrapper.js (1)
56-56: Import path correctly updated to kebab-case. All references to the old filename have been properly migrated across the codebase, and the new file path is in place. The class name correctly remains in PascalCase for the constructor.ghost/core/test/unit/server/services/offers/domain/models/offer-status.test.js (1)
3-3: LGTM! Import path correctly updated to kebab-case.The import path has been properly updated from PascalCase to kebab-case, aligning with the repository's filename working agreement.
ghost/core/test/unit/server/services/offers/domain/models/offer-amount.test.js (1)
3-3: LGTM! Import path correctly updated to kebab-case.The import path has been properly updated to use kebab-case while preserving the destructured import of the three amount types.
ghost/core/core/server/services/offers/domain/models/offer-description.js (1)
1-1: LGTM! Import path correctly updated to kebab-case.The ValueObject import path has been properly updated from PascalCase to kebab-case, consistent with the repository-wide refactoring.
ghost/core/core/server/services/staff/staff-service.js (1)
2-2: LGTM! Import paths correctly updated to kebab-case.The import paths have been properly updated from PascalCase to kebab-case, consistent with the repository's filename working agreement.
Also applies to: 16-16
ghost/core/core/server/services/staff/index.js (1)
11-11: LGTM! Import path correctly updated to kebab-case.The require path has been properly updated to match the new kebab-case filename convention.
ghost/core/test/unit/server/services/staff/staff-service.test.js (2)
5-5: LGTM! Test import paths correctly updated to kebab-case.The import paths in the test file have been properly updated to reference the renamed kebab-case module files.
Also applies to: 11-11
1-1147: No action required. The import paths for staff-service modules have already been correctly updated to kebab-case format. The test file and all related imports are using the proper paths (staff-serviceinstead ofStaffService), as confirmed by verification of the codebase.Likely an incorrect or invalid review comment.
ghost/core/core/server/services/offers/offer-bookshelf-repository.js (1)
4-4: LGTM! Path updates are complete and correct.The import statement on line 4 and all JSDoc type references (lines 96, 144, 162, 179, 198) have been correctly updated to the kebab-case path. The new file exists at
ghost/core/core/server/services/offers/domain/models/offer.js, and no remaining references to the old PascalCase path exist in the codebase.ghost/core/core/server/services/tiers/tier-name-change-event.js (1)
3-3: JSDoc import path correctly updated to kebab-case.The import path change from
'./Tier'to'./tier'correctly reflects the file rename to kebab-case naming convention. The target file exists at the correct location and no stale references remain.ghost/core/test/unit/server/services/oembed/nft-oembed.test.js (1)
2-2: LGTM! Import path and class usage correctly updated.The import path has been successfully updated to kebab-case, and the class instantiation is consistent with the renamed module.
Also applies to: 6-6
ghost/core/core/server/services/oembed/service.js (1)
5-5: LGTM! Import path correctly updated.The OEmbedService import path has been successfully updated to kebab-case.
ghost/core/test/unit/server/services/members/members-api/services/payments-service.test.js (1)
5-5: Import path change is correct and consistent.The file rename from
Tier.jstotier.jshas been completed and all test imports consistently use the new lowercase path. The relative import path../../../../../../../core/server/services/tiers/tiercorrectly resolves to the module location atghost/core/core/server/services/tiers/tier.js. No remaining references to the old path exist in the codebase.ghost/core/test/unit/server/services/link-redirection/link-redirects-service.test.js (1)
1-1: LGTM! Test import path correctly updated.The test file import path has been successfully updated to reference the renamed service module.
ghost/core/test/unit/server/services/link-tracking/post-link-repository.test.js (1)
4-4: LGTM! Test import path correctly updated.The import path has been successfully updated to match the kebab-case filename convention.
ghost/core/core/server/services/link-redirection/redirect-event.js (1)
4-4: LGTM! JSDoc typedef path correctly updated.The JSDoc import path has been updated to reference the kebab-case filename, ensuring type information remains accurate.
ghost/core/test/unit/server/services/link-tracking/link-click-tracking-service.test.js (1)
1-1: LGTM! All test import paths correctly updated.All module import paths have been successfully updated to match the new kebab-case filename convention.
Also applies to: 6-7, 12-12
ghost/core/core/server/services/link-redirection/link-redirects-service.js (1)
3-4: LGTM! Import paths correctly updated to kebab-case.The module import paths have been successfully updated to match the new kebab-case filename convention. Verification confirms that both
redirect-event.jsandlink-redirect.jsfiles exist with the correct naming, and no stale PascalCase references to old filenames remain in require statements throughout the codebase.ghost/core/core/server/services/link-tracking/link-click-tracking-service.js (1)
1-3: LGTM! Clean kebab-case path refactoring.The import path updates from PascalCase to kebab-case are consistent and correct across all four changed lines. The class identifiers correctly remain in PascalCase.
ghost/core/core/server/services/tinybird/tinybird-service-wrapper.js (1)
1-1: Import path correctly updated to kebab-case.The module path is correctly updated to
./tinybird-service. The new kebab-case file exists and all imports across the codebase (including tests) have been consistently updated to the new path.ghost/core/core/server/services/offers/domain/models/stripe-coupon.js (1)
1-1: LGTM! The require path change to kebab-case is correct, thevalue-object.jsfile exists at the target location, and all files importingStripeCouponare using the correct path. The domain model imports are consistent across the refactoring.ghost/core/core/server/services/offers/application/OfferMapper.js (1)
2-2: JSDoc import path correctly updated to match renamed file.The typedef import reference has been properly updated from
../domain/models/Offerto../domain/models/offerto align with the kebab-case filename refactoring. The target file exists at the new path.ghost/core/test/unit/server/services/milestones/milestone-queries.test.js (1)
27-27: Fix the require path - it points to a non-existent file.The current require path
../../../../../core/server/services/milestones/milestone-queriesresolves to a file that doesn't exist atghost/core/server/services/milestones/milestone-queries.js. The actual module is located atghost/core/core/server/services/milestones/milestone-queries.js.Update the require path to:
../../../../../core/core/server/services/milestones/milestone-queries⛔ Skipped due to learnings
Learnt from: ErisDS Repo: TryGhost/Ghost PR: 23582 File: ghost/core/.c8rc.json:24-24 Timestamp: 2025-05-29T07:45:35.714Z Learning: In Ghost project, app.js files under core/server/web are intentionally excluded from unit test coverage because they are not easily unit-testable due to being entry points with initialization code and side effects.Learnt from: CR Repo: TryGhost/Ghost PR: 0 File: e2e/AGENTS.md:0-0 Timestamp: 2025-11-24T17:29:43.865Z Learning: Applies to e2e/**/*.test.ts : Test suite names should follow the format 'Ghost Admin - Feature' or 'Ghost Public - Feature'Learnt from: kevinansfield Repo: TryGhost/Ghost PR: 24651 File: ghost/core/test/utils/urlUtils.js:53-57 Timestamp: 2025-08-11T19:39:00.428Z Learning: In Ghost's test utilities, when fixing specific issues like async behavior, it's preferred to maintain existing error handling patterns (even if suboptimal) to keep PRs focused on their primary objective. Error handling improvements can be addressed in separate, dedicated PRs.Learnt from: CR Repo: TryGhost/Ghost PR: 0 File: AGENTS.md:0-0 Timestamp: 2025-11-25T14:28:50.351Z Learning: Run `yarn test:integration` for integration tests in ghost/coreghost/core/test/unit/server/services/activitypub/activity-pub-service.test.ts (1)
2-2: Import path update is correct.The import path has been updated to kebab-case and resolves properly to the file at
ghost/core/core/server/services/activitypub/activity-pub-service.ts. The class nameActivityPubServicecorrectly remains PascalCase. No stale references to the old PascalCase filename were found.ghost/core/core/server/services/tiers/tiers-api.js (1)
3-3: LGTM! Import path updated to kebab-case.The module path change from
'./Tier'to'./tier'correctly aligns with the repository's kebab-case filename convention.ghost/core/test/unit/server/services/tiers/tiers-api.test.js (1)
3-4: LGTM! Test imports updated to kebab-case.Both module paths correctly updated to match the new kebab-case filenames:
TiersAPI→tiers-apiInMemoryTierRepository→in-memory-tier-repositoryghost/core/core/server/services/tiers/tier-repository.js (1)
1-1: All path and type references correctly updated to kebab-case.The imports and JSDoc type annotations are consistently using the new kebab-case paths:
- Module import:
require('./tier')- Typedef import:
import('./tiers-api')- Type annotations throughout the file correctly reference
import('./tier')All related files exist at the new paths (
tier.js,tiers-api.js,in-memory-tier-repository.js) and no old PascalCase imports remain. The refactoring is complete and type-safe.ghost/core/core/server/services/slack-notifications/slack-notifications-service.js (2)
4-4: LGTM: Typedef path updated to kebab-case.The typedef import path update from
Milestonetomilestoneis correct and maintains consistency with the kebab-case naming convention.
1-1: LGTM: Import path updated to kebab-case.The import path has been correctly updated from
MilestoneCreatedEventtomilestone-created-event, and all references across the codebase are consistent.ghost/core/core/server/services/milestones/milestones-service.js (1)
1-1: LGTM: Import path updated to kebab-case.The import path update from
./Milestoneto./milestonecorrectly reflects the file rename to kebab-case.ghost/core/test/unit/server/services/milestones/in-memory-milestone-repository.test.js (1)
3-4: LGTM: Test imports updated to kebab-case.Both import paths have been correctly updated to reference the renamed modules using kebab-case (
in-memory-milestone-repository) and lowercase (milestone) filenames.ghost/core/test/unit/server/services/milestones/bookshelf-milestone-repository.test.js (1)
10-10: LGTM: Test import updated to kebab-case.The import path has been correctly updated to
bookshelf-milestone-repositoryto match the renamed module file.ghost/core/core/server/services/milestones/milestone.js (1)
3-3: LGTM: Import path correctly updated to kebab-case.The import statement on line 3 properly reflects the renamed file
milestone-created-event.js, and no remaining old references to this module exist in the codebase.ghost/core/core/server/services/milestones/bookshelf-milestone-repository.js (2)
1-1: LGTM: Import path updated correctly.The import path has been correctly updated from
'./Milestone'to'./milestone'to match the kebab-case naming convention.
4-4: LGTM: Typedef import path updated correctly.The typedef import path has been correctly updated from
'./MilestonesService'to'./milestones-service'to align with the kebab-case naming convention.ghost/core/core/server/services/slack-notifications/slack-notifications.js (2)
20-20: LGTM: Typedef import path updated correctly.The typedef import path has been correctly updated to
'./slack-notifications-service'following the kebab-case naming convention.
50-50: LGTM: Cross-service type import updated correctly.The JSDoc parameter type import has been correctly updated to reference
'../milestones/milestone'following the kebab-case naming convention.ghost/core/test/unit/server/services/milestones/milestones-service.test.js (1)
2-4: LGTM: Test import paths updated correctly.All three require paths have been correctly updated to kebab-case while maintaining the PascalCase variable names for the imported modules. This is the correct approach for this refactoring.
ghost/core/test/unit/server/services/slack-notifications/slack-notifications-service.test.js (1)
3-5: Require paths correctly use kebab-case.Both imports properly reference modules using kebab-case file paths. The cross-service dependency on
milestone-created-eventis properly updated.ghost/core/core/server/services/members/importer/MembersCSVImporter.js (1)
36-37: LGTM! Tier module references have been successfully updated.The typedef path updates correctly reflect the kebab-case naming convention migration. The new
tier.jsmodule exists, no old PascalCase references remain, and all references consistently use the new kebab-case path.ghost/core/test/unit/server/services/route-settings/route-settings.test.js (1)
6-6: Import path is incorrect - will cause MODULE_NOT_FOUND error.The require statement uses 5 levels of relative path traversal (
../../../../../), which resolves toghost/core/core/server/services/route-settings/route-settings.js. However, the actual module location isghost/core/server/services/route-settings/route-settings.js.The correct require path should use 4 levels of traversal:
const RouteSettings = require('../../../../server/services/route-settings/route-settings');This import path error affects multiple test files in the route-settings directory (validate.test.js, settings-path-manager.test.js, yaml-parser.test.js, and settings-loader.test.js all use the same incorrect pattern).
⛔ Skipped due to learnings
Learnt from: kevinansfield Repo: TryGhost/Ghost PR: 24651 File: ghost/core/test/utils/urlUtils.js:53-57 Timestamp: 2025-08-11T19:39:00.428Z Learning: In Ghost's test utilities, when fixing specific issues like async behavior, it's preferred to maintain existing error handling patterns (even if suboptimal) to keep PRs focused on their primary objective. Error handling improvements can be addressed in separate, dedicated PRs.Learnt from: CR Repo: TryGhost/Ghost PR: 0 File: e2e/AGENTS.md:0-0 Timestamp: 2025-11-24T17:29:43.865Z Learning: Applies to e2e/**/*.test.ts : Test suite names should follow the format 'Ghost Admin - Feature' or 'Ghost Public - Feature'ghost/core/core/server/services/posts/posts-service.js (7)
1-27: LGTM! Clean dependency setup.The imports, error messages, and constructor follow good dependency injection patterns. The class properly stores all dependencies for use throughout the service.
29-49: LGTM! Proper error handling.The read operations are implemented correctly with appropriate NotFoundError handling when a post is not found.
62-107: LGTM! Well-structured edit logic.The editPost method properly validates email segments, handles newsletter email creation/retry, and provides an extensible eventHandler pattern for callers to respond to status changes.
134-218: LGTM! Comprehensive bulk operations.The bulkEdit method handles multiple actions with proper validation and domain event dispatching. The switch-like structure using if statements is clear and maintainable.
429-449: LGTM! Proper visibility filter parsing.The method correctly parses the visibility filter using nql and maps product slugs to tier objects, with appropriate error handling for invalid filters.
458-489: LGTM! Correct email and cache logic.Both methods implement appropriate logic: shouldSendEmail checks status transitions, and handleCacheInvalidation calculates different cache strategies based on post status changes.
491-572: LGTM! Complete post copying implementation.The copyPost method correctly clones a post with appropriate field selection, relationship copying, and safe defaults. The URL generation helper properly reconstructs the location URL for the copied post.
ghost/core/test/unit/server/services/posts/posts-service.test.js (1)
1-1: LGTM! Path update for kebab-case convention.The import path has been correctly updated to match the new kebab-case filename convention. The test logic remains unchanged.
ghost/core/core/server/lib/lexical.js (1)
74-77: LGTM! Correct instance pattern usage.The update to use
posts-service-instanceand callgetPostServiceInstance()is consistent with the refactoring to an instance-based pattern for the posts service.ghost/core/core/server/services/explore-ping/index.js (1)
8-8: LGTM! Consistent refactoring to instance pattern.The path update to
posts-service-instanceis correct and consistent with the broader refactoring. The usage on line 21 (posts()) properly obtains the service instance.ghost/core/core/server/api/endpoints/search-index-public.js (1)
2-3: LGTM! Consistent endpoint pattern.The update to use
getPostServiceInstancefromposts-service-instanceis consistent with other endpoint files (e.g., search-index.js) and correctly follows the instance pattern.ghost/core/core/server/services/explore/index.js (1)
4-4: LGTM!The require path has been correctly updated to use the new factory pattern with immediate invocation to obtain the PostsService instance.
ghost/core/core/server/services/posts/posts-service-instance.js (3)
8-43: LGTM! Factory pattern implementation is solid.The factory function correctly implements lazy dependency loading, which helps avoid circular dependency issues. The comment on line 38 explaining why
bindis avoided is helpful for maintainability.
3-3: Fix the incorrect require path.The require path on line 3 appears to be incorrect. From
server/services/posts/, going up three levels (../../../) reachescore/, then addingserver/api/...creates an invalid path.🐛 Proposed fix for the require path
-const url = require('../../../server/api/endpoints/utils/serializers/output/utils/url'); +const url = require('../../api/endpoints/utils/serializers/output/utils/url');Likely an incorrect or invalid review comment.
26-29: No issue found —url.forPost()correctly mutates the jsonModel parameter.The
url.forPost()function at line 6 setsattrs.url = urlService.getUrlByResourceId(...)and returns the modified object at line 42. The pattern of calling without capturing the return value is intentional and correct, as the function mutates the parameter in place.ghost/core/core/server/api/endpoints/pages.js (1)
4-4: Factory function returns fully compatible PostsService interface.The import path change to
posts-service-instanceis safe. The factory function properly instantiatesPostsServicewith dependencies injected and returns an instance with all required methods:handleCacheInvalidation,bulkEdit,bulkDestroy,generateCopiedPostLocationFromUrl, andcopyPost. All method calls in this file (lines 166, 207, 224, 260, 278) are supported.
Ember E2E Tests FailedTo view the Playwright test report locally, run: REPORT_DIR=$(mktemp -d) && gh run download 20813330957 -n playwright-report-ember -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR" |
React E2E Tests FailedTo view the Playwright test report locally, run: REPORT_DIR=$(mktemp -d) && gh run download 20813330957 -n playwright-report-react -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR" |
b7038b3 to
488a309
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
ghost/core/test/unit/server/services/milestones/in-memory-milestone-repository.test.js (1)
102-102: Pre-existing bug: Assignment operator used instead of comparison.Line 102 uses assignment
=instead of comparison===, causing the assertion to always pass. The same issue exists on line 111.🐛 Suggested fix
- assert(latestArrMilestone.type = 'arr'); + assert(latestArrMilestone.type === 'arr');Also apply the same fix to line 111:
- assert(latestArrMilestone.type = 'arr'); + assert(latestArrMilestone.type === 'arr');Note: This is pre-existing code, not introduced by this PR. Consider fixing in a follow-up.
🤖 Fix all issues with AI agents
In
@ghost/core/test/unit/server/services/milestones/in-memory-milestone-repository.test.js:
- Around line 3-4: Two assertions in the test use assignment instead of
comparison; locate the failing asserts that read like
assert(latestArrMilestone.type = 'arr') and the second similar one around the
other milestone assertion, and change the single equals to a strict equality
check (===) so they become assert(latestArrMilestone.type === 'arr') (and the
analogous assert for the other milestone variable).
🧹 Nitpick comments (4)
ghost/core/core/server/services/offers/domain/models/offer-amount.js (1)
31-53: Pre-existing bug: OfferFixedAmount.create returns wrong class.Line 49 instantiates
OfferPercentageAmountinstead ofOfferFixedAmount. This bug predates this PR but should be corrected.🐛 Proposed fix
- return new OfferPercentageAmount(amount); + return new OfferFixedAmount(amount);Do you want me to open a separate issue to track this bug fix?
ghost/core/.eslintrc.js (1)
104-104: Update the comment to reflect services inclusion.The comment states "Frontend files use kebab-case..." but the rule now also applies to
core/server/services/**/*.{js,ts}(line 105). Update the comment to mention both frontend and services files.📝 Suggested comment update
- // Frontend files use kebab-case filenames with PascalCase class exports + // Frontend and services files use kebab-case filenames with PascalCase class exportsghost/core/core/server/services/posts/posts-service.js (2)
1-7: Consider consolidating error imports.The
@tryghost/errorsmodule is imported twice: once destructured forBadRequestError(line 2) and once aserrors(line 4). While functionally correct, consolidating to a single import would be cleaner.♻️ Proposed consolidation
-const {BadRequestError} = require('@tryghost/errors'); -const tpl = require('@tryghost/tpl'); -const errors = require('@tryghost/errors'); +const errors = require('@tryghost/errors'); +const tpl = require('@tryghost/tpl');Then use
errors.BadRequestErrorin the code (lines 71, 444).
562-571: URL structure assumption in location generation.The
generateCopiedPostLocationFromUrlmethod assumes a specific URL structure, removing the last 4 segments (line 567) and reconstructing the path. This implementation is tightly coupled to the current API URL format.Consider documenting the expected URL format or adding validation to ensure the URL structure matches expectations before manipulation.
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
ghost/core/test/unit/frontend/helpers/__snapshots__/ghost-head.test.js.snapis excluded by!**/*.snap
📒 Files selected for processing (136)
ghost/core/.eslintrc.jsghost/core/core/server/api/endpoints/pages.jsghost/core/core/server/api/endpoints/posts-public.jsghost/core/core/server/api/endpoints/posts.jsghost/core/core/server/api/endpoints/search-index-public.jsghost/core/core/server/api/endpoints/search-index.jsghost/core/core/server/api/endpoints/utils/serializers/output/mappers/posts.jsghost/core/core/server/lib/lexical.jsghost/core/core/server/services/activitypub/activity-pub-service-wrapper.jsghost/core/core/server/services/activitypub/activity-pub-service.tsghost/core/core/server/services/activitypub/index.jsghost/core/core/server/services/explore-ping/index.jsghost/core/core/server/services/explore/index.jsghost/core/core/server/services/link-redirection/index.jsghost/core/core/server/services/link-redirection/link-redirect-repository.jsghost/core/core/server/services/link-redirection/link-redirect.jsghost/core/core/server/services/link-redirection/link-redirects-service.jsghost/core/core/server/services/link-redirection/redirect-event.jsghost/core/core/server/services/link-tracking/click-event.jsghost/core/core/server/services/link-tracking/full-post-link.jsghost/core/core/server/services/link-tracking/index.jsghost/core/core/server/services/link-tracking/link-click-repository.jsghost/core/core/server/services/link-tracking/link-click-tracking-service.jsghost/core/core/server/services/link-tracking/post-link-repository.jsghost/core/core/server/services/link-tracking/post-link.jsghost/core/core/server/services/members/importer/MembersCSVImporter.jsghost/core/core/server/services/members/members-api/services/PaymentsService.jsghost/core/core/server/services/milestones/bookshelf-milestone-repository.jsghost/core/core/server/services/milestones/in-memory-milestone-repository.jsghost/core/core/server/services/milestones/milestone-created-event.jsghost/core/core/server/services/milestones/milestone-queries.jsghost/core/core/server/services/milestones/milestone.jsghost/core/core/server/services/milestones/milestones-service.jsghost/core/core/server/services/milestones/service.jsghost/core/core/server/services/oembed/nft-oembed-provider.jsghost/core/core/server/services/oembed/oembed-service.jsghost/core/core/server/services/oembed/service.jsghost/core/core/server/services/oembed/twitter-oembed-provider.jsghost/core/core/server/services/offers/application/OfferMapper.jsghost/core/core/server/services/offers/application/OffersAPI.jsghost/core/core/server/services/offers/application/UniqueChecker.jsghost/core/core/server/services/offers/domain/events/offer-code-change-event.jsghost/core/core/server/services/offers/domain/events/offer-created-event.jsghost/core/core/server/services/offers/domain/models/offer-amount.jsghost/core/core/server/services/offers/domain/models/offer-cadence.jsghost/core/core/server/services/offers/domain/models/offer-code.jsghost/core/core/server/services/offers/domain/models/offer-created-at.jsghost/core/core/server/services/offers/domain/models/offer-currency.jsghost/core/core/server/services/offers/domain/models/offer-description.jsghost/core/core/server/services/offers/domain/models/offer-duration.jsghost/core/core/server/services/offers/domain/models/offer-name.jsghost/core/core/server/services/offers/domain/models/offer-status.jsghost/core/core/server/services/offers/domain/models/offer-title.jsghost/core/core/server/services/offers/domain/models/offer-type.jsghost/core/core/server/services/offers/domain/models/offer.jsghost/core/core/server/services/offers/domain/models/shared/value-object.jsghost/core/core/server/services/offers/domain/models/stripe-coupon.jsghost/core/core/server/services/offers/offer-bookshelf-repository.jsghost/core/core/server/services/offers/offers-module.jsghost/core/core/server/services/offers/service.jsghost/core/core/server/services/posts/PostsService.jsghost/core/core/server/services/posts/posts-exporter.jsghost/core/core/server/services/posts/posts-service-instance.jsghost/core/core/server/services/posts/posts-service.jsghost/core/core/server/services/posts/stats/post-stats.jsghost/core/core/server/services/recommendations/index.jsghost/core/core/server/services/recommendations/recommendation-enabler-service.jsghost/core/core/server/services/recommendations/recommendation-service-wrapper.jsghost/core/core/server/services/route-settings/default-settings-manager.jsghost/core/core/server/services/route-settings/index.jsghost/core/core/server/services/route-settings/route-settings.jsghost/core/core/server/services/route-settings/settings-loader.jsghost/core/core/server/services/route-settings/settings-path-manager.jsghost/core/core/server/services/slack-notifications/service.jsghost/core/core/server/services/slack-notifications/slack-notifications-service.jsghost/core/core/server/services/slack-notifications/slack-notifications.jsghost/core/core/server/services/staff/index.jsghost/core/core/server/services/staff/staff-service-emails.jsghost/core/core/server/services/staff/staff-service.jsghost/core/core/server/services/tiers/in-memory-tier-repository.jsghost/core/core/server/services/tiers/service.jsghost/core/core/server/services/tiers/tier-activated-event.jsghost/core/core/server/services/tiers/tier-archived-event.jsghost/core/core/server/services/tiers/tier-created-event.jsghost/core/core/server/services/tiers/tier-name-change-event.jsghost/core/core/server/services/tiers/tier-price-change-event.jsghost/core/core/server/services/tiers/tier-repository.jsghost/core/core/server/services/tiers/tier.jsghost/core/core/server/services/tiers/tiers-api.jsghost/core/core/server/services/tinybird/index.jsghost/core/core/server/services/tinybird/tinybird-service-wrapper.jsghost/core/core/server/services/tinybird/tinybird-service.jsghost/core/test/unit/server/services/activitypub/activity-pub-service.test.tsghost/core/test/unit/server/services/link-redirection/link-redirect-repository.test.jsghost/core/test/unit/server/services/link-redirection/link-redirects-service.test.jsghost/core/test/unit/server/services/link-tracking/link-click-repository.test.jsghost/core/test/unit/server/services/link-tracking/link-click-tracking-service.test.jsghost/core/test/unit/server/services/link-tracking/post-link-repository.test.jsghost/core/test/unit/server/services/members/importer/members-csv-importer.test.jsghost/core/test/unit/server/services/members/members-api/services/payments-service.test.jsghost/core/test/unit/server/services/milestones/bookshelf-milestone-repository.test.jsghost/core/test/unit/server/services/milestones/in-memory-milestone-repository.test.jsghost/core/test/unit/server/services/milestones/milestone-queries.test.jsghost/core/test/unit/server/services/milestones/milestone.test.jsghost/core/test/unit/server/services/milestones/milestones-service.test.jsghost/core/test/unit/server/services/oembed/nft-oembed.test.jsghost/core/test/unit/server/services/oembed/oembed-service.test.jsghost/core/test/unit/server/services/oembed/twitter-embed.test.jsghost/core/test/unit/server/services/offers/application/offers-api.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-amount.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-cadence.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-code.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-currency.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-description.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-duration.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-name.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-status.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-title.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-type.test.jsghost/core/test/unit/server/services/offers/domain/models/offer.test.jsghost/core/test/unit/server/services/offers/domain/models/stripe-coupon.test.jsghost/core/test/unit/server/services/posts/posts-exporter.test.jsghost/core/test/unit/server/services/posts/posts-service.test.jsghost/core/test/unit/server/services/route-settings/route-settings.test.jsghost/core/test/unit/server/services/route-settings/settings-loader.test.jsghost/core/test/unit/server/services/route-settings/settings-path-manager.test.jsghost/core/test/unit/server/services/settings/default-settings-manager.test.jsghost/core/test/unit/server/services/slack-notifications/index.test.jsghost/core/test/unit/server/services/slack-notifications/slack-notifications-service.test.jsghost/core/test/unit/server/services/slack-notifications/slack-notifications.test.jsghost/core/test/unit/server/services/staff/index.test.jsghost/core/test/unit/server/services/staff/staff-service.test.jsghost/core/test/unit/server/services/tiers/tier-repository.test.jsghost/core/test/unit/server/services/tiers/tier.test.jsghost/core/test/unit/server/services/tiers/tiers-api.test.jsghost/core/test/unit/server/services/tinybird/tinybird-service.test.js
💤 Files with no reviewable changes (1)
- ghost/core/core/server/services/posts/PostsService.js
✅ Files skipped from review due to trivial changes (1)
- ghost/core/core/server/services/tinybird/index.js
🚧 Files skipped from review as they are similar to previous changes (67)
- ghost/core/core/server/services/milestones/bookshelf-milestone-repository.js
- ghost/core/test/unit/server/services/offers/domain/models/offer-type.test.js
- ghost/core/core/server/services/milestones/milestone.js
- ghost/core/core/server/services/tiers/tier-archived-event.js
- ghost/core/test/unit/server/services/slack-notifications/index.test.js
- ghost/core/test/unit/server/services/offers/domain/models/offer-name.test.js
- ghost/core/core/server/services/tiers/tier-created-event.js
- ghost/core/core/server/services/offers/service.js
- ghost/core/test/unit/server/services/posts/posts-service.test.js
- ghost/core/core/server/services/offers/domain/models/offer-description.js
- ghost/core/core/server/services/link-tracking/link-click-tracking-service.js
- ghost/core/core/server/services/recommendations/recommendation-service-wrapper.js
- ghost/core/core/server/services/activitypub/activity-pub-service-wrapper.js
- ghost/core/core/server/services/offers/application/UniqueChecker.js
- ghost/core/test/unit/server/services/settings/default-settings-manager.test.js
- ghost/core/test/unit/server/services/posts/posts-exporter.test.js
- ghost/core/test/unit/server/services/link-redirection/link-redirects-service.test.js
- ghost/core/test/unit/server/services/milestones/milestone.test.js
- ghost/core/core/server/services/members/importer/MembersCSVImporter.js
- ghost/core/test/unit/server/services/offers/domain/models/stripe-coupon.test.js
- ghost/core/core/server/services/link-redirection/redirect-event.js
- ghost/core/test/unit/server/services/slack-notifications/slack-notifications.test.js
- ghost/core/core/server/services/tiers/tier-repository.js
- ghost/core/core/server/lib/lexical.js
- ghost/core/test/unit/server/services/slack-notifications/slack-notifications-service.test.js
- ghost/core/test/unit/server/services/oembed/twitter-embed.test.js
- ghost/core/core/server/services/offers/domain/models/offer-name.js
- ghost/core/test/unit/server/services/offers/domain/models/offer-currency.test.js
- ghost/core/core/server/services/offers/application/OffersAPI.js
- ghost/core/core/server/services/explore-ping/index.js
- ghost/core/core/server/services/milestones/milestones-service.js
- ghost/core/test/unit/server/services/tiers/tier-repository.test.js
- ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/posts.js
- ghost/core/test/unit/server/services/link-redirection/link-redirect-repository.test.js
- ghost/core/core/server/services/link-tracking/post-link-repository.js
- ghost/core/core/server/services/offers/domain/events/offer-code-change-event.js
- ghost/core/test/unit/server/services/members/members-api/services/payments-service.test.js
- ghost/core/test/unit/server/services/offers/domain/models/offer-description.test.js
- ghost/core/core/server/services/route-settings/index.js
- ghost/core/core/server/services/offers/domain/models/offer-currency.js
- ghost/core/core/server/services/tinybird/tinybird-service-wrapper.js
- ghost/core/core/server/services/offers/domain/models/offer-title.js
- ghost/core/test/unit/server/services/link-tracking/link-click-repository.test.js
- ghost/core/core/server/services/posts/posts-service-instance.js
- ghost/core/test/unit/server/services/milestones/milestone-queries.test.js
- ghost/core/test/unit/server/services/activitypub/activity-pub-service.test.ts
- ghost/core/core/server/services/slack-notifications/slack-notifications.js
- ghost/core/test/unit/server/services/link-tracking/link-click-tracking-service.test.js
- ghost/core/core/server/api/endpoints/posts.js
- ghost/core/core/server/services/milestones/in-memory-milestone-repository.js
- ghost/core/core/server/services/offers/domain/models/offer-duration.js
- ghost/core/core/server/services/tiers/tiers-api.js
- ghost/core/core/server/services/offers/offers-module.js
- ghost/core/test/unit/server/services/offers/domain/models/offer-status.test.js
- ghost/core/core/server/services/tiers/tier.js
- ghost/core/test/unit/server/services/link-tracking/post-link-repository.test.js
- ghost/core/core/server/services/explore/index.js
- ghost/core/test/unit/server/services/milestones/bookshelf-milestone-repository.test.js
- ghost/core/core/server/services/staff/index.js
- ghost/core/core/server/services/link-tracking/index.js
- ghost/core/test/unit/server/services/tiers/tiers-api.test.js
- ghost/core/core/server/services/offers/offer-bookshelf-repository.js
- ghost/core/core/server/services/offers/domain/models/offer-status.js
- ghost/core/core/server/api/endpoints/search-index-public.js
- ghost/core/test/unit/server/services/offers/domain/models/offer-duration.test.js
- ghost/core/core/server/services/tiers/tier-name-change-event.js
- ghost/core/core/server/services/link-redirection/link-redirects-service.js
🧰 Additional context used
🧠 Learnings (20)
📓 Common learnings
Learnt from: sagzy
Repo: TryGhost/Ghost PR: 24673
File: ghost/i18n/lib/i18n.js:34-35
Timestamp: 2025-11-24T11:12:15.712Z
Learning: In the Ghost i18n package (ghost/i18n/lib/i18n.js), changing existing locale codes requires backwards compatibility handling for users who have already configured those locales. Such changes should be done in a separate PR with migration logic rather than included in feature PRs.
Learnt from: sam-lord
Repo: TryGhost/Ghost PR: 25303
File: ghost/core/core/server/services/email-service/BatchSendingService.js:19-19
Timestamp: 2025-10-30T17:13:26.190Z
Learning: In ghost/core/core/server/services/email-service/BatchSendingService.js and similar files in the Ghost codebase, prefer using `{...options}` spread syntax without explicit guards like `...(options || {})` when spreading potentially undefined objects, as the maintainer prefers cleaner syntax over defensive patterns when the behavior is safe.
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T14:28:50.351Z
Learning: Core Ghost backend logic should be placed in `ghost/core/core/server/` directory structure organized by purpose (api, services, models, data/schema, etc.)
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use `PascalCase` for component identifiers in filenames while keeping ShadCN-generated files in kebab-case
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 24651
File: ghost/core/test/utils/urlUtils.js:53-57
Timestamp: 2025-08-11T19:39:00.428Z
Learning: In Ghost's test utilities, when fixing specific issues like async behavior, it's preferred to maintain existing error handling patterns (even if suboptimal) to keep PRs focused on their primary objective. Error handling improvements can be addressed in separate, dedicated PRs.
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Test names should be lowercase and follow the format 'what is tested - expected outcome'
Applied to files:
ghost/core/test/unit/server/services/offers/domain/models/offer.test.jsghost/core/test/unit/server/services/route-settings/route-settings.test.jsghost/core/test/unit/server/services/route-settings/settings-path-manager.test.jsghost/core/test/unit/server/services/members/importer/members-csv-importer.test.jsghost/core/test/unit/server/services/route-settings/settings-loader.test.jsghost/core/test/unit/server/services/oembed/nft-oembed.test.jsghost/core/test/unit/server/services/milestones/in-memory-milestone-repository.test.jsghost/core/test/unit/server/services/oembed/oembed-service.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-code.test.js
📚 Learning: 2026-01-08T10:26:38.700Z
Learnt from: rob-ghost
Repo: TryGhost/Ghost PR: 25791
File: ghost/core/core/server/api/endpoints/member-comment-ban.js:64-68
Timestamp: 2026-01-08T10:26:38.700Z
Learning: In the Ghost API, endpoints rely on the serialization layer to prepare frame.data[docName] as a non-empty array before query() executes. Endpoints access frame.data[docName][0] directly (e.g., frame.data.comment_bans[0], frame.data.members[0], frame.data.posts[0]) without per-endpoint validation. This pattern is common across API endpoints. When maintaining or creating endpoints, avoid duplicating validation for frame.data[docName] and ensure the serializer guarantees the shape and non-emptiness. If you add a new endpoint that uses this frame.data[docName], follow the same assumption and avoid redundant checks unless there's a documented exception.
Applied to files:
ghost/core/test/unit/server/services/offers/domain/models/offer.test.jsghost/core/test/unit/server/services/route-settings/route-settings.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-amount.test.jsghost/core/core/server/services/activitypub/index.jsghost/core/test/unit/server/services/offers/domain/models/offer-title.test.jsghost/core/test/unit/server/services/route-settings/settings-path-manager.test.jsghost/core/test/unit/server/services/staff/index.test.jsghost/core/core/server/services/offers/domain/models/offer-created-at.jsghost/core/core/server/services/offers/application/OfferMapper.jsghost/core/core/server/services/tiers/tier-activated-event.jsghost/core/test/unit/server/services/members/importer/members-csv-importer.test.jsghost/core/core/server/services/milestones/service.jsghost/core/test/unit/server/services/route-settings/settings-loader.test.jsghost/core/core/server/services/link-redirection/index.jsghost/core/core/server/api/endpoints/posts-public.jsghost/core/test/unit/server/services/staff/staff-service.test.jsghost/core/core/server/api/endpoints/pages.jsghost/core/core/server/services/members/members-api/services/PaymentsService.jsghost/core/test/unit/server/services/tiers/tier.test.jsghost/core/test/unit/server/services/milestones/milestones-service.test.jsghost/core/core/server/services/slack-notifications/service.jsghost/core/core/server/services/offers/domain/models/offer.jsghost/core/core/server/services/link-redirection/link-redirect-repository.jsghost/core/core/server/services/link-tracking/link-click-repository.jsghost/core/test/unit/server/services/offers/application/offers-api.test.jsghost/core/test/unit/server/services/oembed/nft-oembed.test.jsghost/core/test/unit/server/services/milestones/in-memory-milestone-repository.test.jsghost/core/core/server/services/offers/domain/events/offer-created-event.jsghost/core/core/server/services/oembed/service.jsghost/core/test/unit/server/services/oembed/oembed-service.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-code.test.jsghost/core/core/server/services/recommendations/index.jsghost/core/core/server/services/posts/posts-service.jsghost/core/test/unit/server/services/offers/domain/models/offer-cadence.test.jsghost/core/core/server/services/offers/domain/models/offer-type.jsghost/core/core/server/services/offers/domain/models/offer-cadence.jsghost/core/core/server/services/offers/domain/models/offer-amount.jsghost/core/core/server/api/endpoints/search-index.jsghost/core/core/server/services/tiers/in-memory-tier-repository.jsghost/core/core/server/services/tiers/service.jsghost/core/test/unit/server/services/tinybird/tinybird-service.test.jsghost/core/core/server/services/slack-notifications/slack-notifications-service.jsghost/core/core/server/services/staff/staff-service.jsghost/core/core/server/services/tiers/tier-price-change-event.jsghost/core/core/server/services/offers/domain/models/offer-code.jsghost/core/core/server/services/offers/domain/models/stripe-coupon.js
📚 Learning: 2025-03-13T09:00:20.205Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/utils/pending-activity.ts:13-71
Timestamp: 2025-03-13T09:00:20.205Z
Learning: The pending activity utilities in the Ghost ActivityPub module are covered by tests in the file `apps/admin-x-activitypub/test/unit/utils/pending-activity.ts`.
Applied to files:
ghost/core/core/server/services/activitypub/index.jsghost/core/test/unit/server/services/staff/staff-service.test.js
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the Ghost ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.
Applied to files:
ghost/core/core/server/services/activitypub/index.js
📚 Learning: 2025-03-13T09:00:20.205Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/utils/pending-activity.ts:13-71
Timestamp: 2025-03-13T09:00:20.205Z
Learning: The pending activity utilities in Ghost's ActivityPub module are thoroughly tested in `apps/admin-x-activitypub/test/unit/utils/pending-activity.ts`, which covers `generatePendingActivityId`, `isPendingActivity`, and `generatePendingActivity` functions.
Applied to files:
ghost/core/core/server/services/activitypub/index.jsghost/core/test/unit/server/services/staff/staff-service.test.js
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.
Applied to files:
ghost/core/core/server/services/activitypub/index.js
📚 Learning: 2025-09-03T12:28:11.174Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 24779
File: ghost/core/core/server/services/members/members-api/controllers/RouterController.js:34-51
Timestamp: 2025-09-03T12:28:11.174Z
Learning: The extractRefererOrRedirect function in Ghost's RouterController (ghost/core/core/server/services/members/members-api/controllers/RouterController.js) is working as intended according to maintainer kevinansfield. The current handling of autoRedirect, redirect parameter parsing, and referrer logic does not need the security-related changes suggested around autoRedirect coercion, relative URL handling, or same-origin checks.
Applied to files:
ghost/core/core/server/services/link-redirection/index.jsghost/core/core/server/services/link-redirection/link-redirect-repository.js
📚 Learning: 2025-06-13T11:57:58.226Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 23824
File: ghost/core/core/server/services/email-service/email-templates/partials/styles.hbs:919-926
Timestamp: 2025-06-13T11:57:58.226Z
Learning: In `ghost/core/core/server/services/email-service/email-templates/partials/styles.hbs`, some style blocks (e.g., `.latest-post p` and `.latest-post p a`) still use the legacy colour `#73818c` on purpose; they are later overridden by `emailCustomization` feature rules, as noted by inline TODO comments. These occurrences should not be flagged as inconsistencies.
Applied to files:
ghost/core/core/server/api/endpoints/posts-public.js
📚 Learning: 2025-08-11T19:39:00.428Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 24651
File: ghost/core/test/utils/urlUtils.js:53-57
Timestamp: 2025-08-11T19:39:00.428Z
Learning: In Ghost's test utilities, when fixing specific issues like async behavior, it's preferred to maintain existing error handling patterns (even if suboptimal) to keep PRs focused on their primary objective. Error handling improvements can be addressed in separate, dedicated PRs.
Applied to files:
ghost/core/core/server/api/endpoints/posts-public.js
📚 Learning: 2025-11-24T11:12:15.712Z
Learnt from: sagzy
Repo: TryGhost/Ghost PR: 24673
File: ghost/i18n/lib/i18n.js:34-35
Timestamp: 2025-11-24T11:12:15.712Z
Learning: In the Ghost i18n package (ghost/i18n/lib/i18n.js), changing existing locale codes requires backwards compatibility handling for users who have already configured those locales. Such changes should be done in a separate PR with migration logic rather than included in feature PRs.
Applied to files:
ghost/core/core/server/api/endpoints/posts-public.js
📚 Learning: 2025-10-30T17:13:26.190Z
Learnt from: sam-lord
Repo: TryGhost/Ghost PR: 25303
File: ghost/core/core/server/services/email-service/BatchSendingService.js:19-19
Timestamp: 2025-10-30T17:13:26.190Z
Learning: In ghost/core/core/server/services/email-service/BatchSendingService.js and similar files in the Ghost codebase, prefer using `{...options}` spread syntax without explicit guards like `...(options || {})` when spreading potentially undefined objects, as the maintainer prefers cleaner syntax over defensive patterns when the behavior is safe.
Applied to files:
ghost/core/core/server/api/endpoints/posts-public.jsghost/core/test/unit/server/services/milestones/milestones-service.test.jsghost/core/core/server/services/posts/posts-service.jsghost/core/.eslintrc.js
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Each test receives fresh Ghost instance for automatic isolation
Applied to files:
ghost/core/test/unit/server/services/staff/staff-service.test.js
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Use factory pattern for all test data creation instead of hard-coded data or direct database manipulation
Applied to files:
ghost/core/test/unit/server/services/offers/domain/models/offer-code.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-cadence.test.js
📚 Learning: 2025-11-25T14:28:50.351Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T14:28:50.351Z
Learning: Analytics using Tinybird should reference scripts in `ghost/core/core/server/data/tinybird/scripts/` and datafiles in `ghost/core/core/server/data/tinybird/`
Applied to files:
ghost/core/test/unit/server/services/tinybird/tinybird-service.test.js
📚 Learning: 2025-12-01T08:42:35.320Z
Learnt from: jonatansberg
Repo: TryGhost/Ghost PR: 25552
File: e2e/helpers/environment/service-managers/dev-ghost-manager.ts:210-247
Timestamp: 2025-12-01T08:42:35.320Z
Learning: In e2e/helpers/environment/service-managers/dev-ghost-manager.ts, the hardcoded volume name 'ghost-dev_shared-config' at line 231 is intentional. E2E tests run under the 'ghost-dev-e2e' project namespace but deliberately mount the shared-config volume from the main 'ghost-dev' project to access Tinybird credentials created by yarn dev:forward. This is cross-project volume sharing by design.
Applied to files:
ghost/core/test/unit/server/services/tinybird/tinybird-service.test.js
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Follow ESLint and `tailwindcss/*` plugin rules when writing styles
Applied to files:
ghost/core/.eslintrc.js
📚 Learning: 2025-05-29T07:45:35.714Z
Learnt from: ErisDS
Repo: TryGhost/Ghost PR: 23582
File: ghost/core/.c8rc.json:24-24
Timestamp: 2025-05-29T07:45:35.714Z
Learning: In Ghost project, app.js files under core/server/web are intentionally excluded from unit test coverage because they are not easily unit-testable due to being entry points with initialization code and side effects.
Applied to files:
ghost/core/.eslintrc.js
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Run `yarn lint` after making changes to fix any ESLint errors and warnings before committing
Applied to files:
ghost/core/.eslintrc.js
📚 Learning: 2025-11-25T14:28:50.351Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T14:28:50.351Z
Learning: Run `yarn lint` to lint all packages or `cd ghost/core && yarn lint` to lint Ghost core (server, shared, frontend, tests)
Applied to files:
ghost/core/.eslintrc.js
🧬 Code graph analysis (4)
ghost/core/core/server/services/link-redirection/index.js (1)
ghost/core/test/unit/server/services/link-redirection/link-redirects-service.test.js (1)
LinkRedirectsService(1-1)
ghost/core/core/server/services/slack-notifications/service.js (2)
ghost/core/test/unit/server/services/slack-notifications/slack-notifications-service.test.js (1)
SlackNotificationsService(3-3)ghost/core/test/unit/server/services/slack-notifications/slack-notifications.test.js (1)
SlackNotifications(3-3)
ghost/core/core/server/services/posts/posts-service.js (3)
ghost/core/core/server/services/posts/posts-service-instance.js (1)
postsExporter(19-33)ghost/admin/mirage/config/posts.js (1)
visibilityFilter(43-43)apps/portal/src/utils/helpers.js (1)
allProducts(148-148)
ghost/core/core/server/services/tiers/service.js (2)
ghost/core/test/unit/server/services/tiers/tiers-api.test.js (1)
TiersAPI(3-3)ghost/core/test/unit/server/services/tiers/tier-repository.test.js (1)
TierRepository(4-4)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: Legacy tests (Node 22.18.0, sqlite3)
- GitHub Check: Legacy tests (Node 22.18.0, mysql8)
- GitHub Check: Ghost-CLI tests
- GitHub Check: Acceptance tests (Node 22.18.0, mysql8)
- GitHub Check: Acceptance tests (Node 22.18.0, sqlite3)
- GitHub Check: Lint
- GitHub Check: Unit tests (Node 22.18.0)
- GitHub Check: Build & Push Docker Image
🔇 Additional comments (59)
ghost/core/test/unit/server/services/members/importer/members-csv-importer.test.js (1)
3-3: LGTM! Import path updated correctly.The import path has been correctly updated to reflect the file rename from PascalCase to kebab-case, consistent with the PR's objective to enforce kebab-case file naming conventions across the codebase.
ghost/core/core/server/services/tiers/in-memory-tier-repository.js (1)
4-4: LGTM!The typedef import path has been correctly updated from
'./Tier'to'./tier', aligning with the PR's kebab-case naming convention.ghost/core/test/unit/server/services/staff/index.test.js (1)
9-9: Import path update is correct.The require path correctly references the renamed kebab-case module (
milestone-created-event) while preserving the PascalCase class name. The kebab-case file exists and all references across the codebase have been updated consistently.ghost/core/test/unit/server/services/tinybird/tinybird-service.test.js (3)
122-122: Minor formatting improvement.The additional empty line improves readability between test cases.
176-176: EOF newline added per best practice.The trailing newline follows POSIX text file conventions.
2-2: Import path correctly updated to kebab-case convention.The import statement on line 2 has been properly updated from the PascalCase filename to the kebab-case version. Verification confirms no old PascalCase imports remain in the codebase, the renamed service file exists, and all references are consistent.
ghost/core/core/server/services/slack-notifications/service.js (2)
6-6: Type annotations correctly updated to kebab-case.The JSDoc type annotations have been properly updated to reference the renamed module using kebab-case convention.
Also applies to: 17-17
20-21: Dynamic requires correctly updated to kebab-case.The module imports have been properly updated to use kebab-case naming convention for both
slack-notifications-serviceandslack-notifications.ghost/core/core/server/services/slack-notifications/slack-notifications-service.js (1)
1-1: Import paths correctly updated to kebab-case.The milestone module references have been properly updated to use kebab-case naming convention (lines 1 and 4). Verification confirms no remaining old PascalCase references exist in the codebase.
ghost/core/core/server/services/recommendations/index.js (1)
1-1: LGTM! The require path correctly updates to kebab-case while maintaining the PascalCase class identifier.The change is straightforward and verified—the new kebab-case file exists, the old file is gone, and all consuming code throughout the codebase continues to work correctly via directory-level requires that load this index.js file.
ghost/core/test/unit/server/services/staff/staff-service.test.js (1)
5-5: LGTM! Test import paths correctly updated.The test file paths have been properly updated to match the kebab-case naming convention of the renamed service modules.
Also applies to: 11-11
ghost/core/core/server/services/staff/staff-service.js (1)
2-2: LGTM! Import paths correctly updated to kebab-case.The module path updates align with the PR's objective to standardize on kebab-case file naming. All references across the codebase have been properly migrated to the new paths.
ghost/core/test/unit/server/services/oembed/nft-oembed.test.js (1)
2-2: LGTM! Module path updated to kebab-case.The import path has been correctly updated to use the new kebab-case naming convention while maintaining the same exported class name.
ghost/core/core/server/services/oembed/service.js (1)
5-5: LGTM! All provider module paths updated consistently.All three require statements have been correctly updated to use kebab-case file naming while preserving class names and instantiation logic.
Also applies to: 8-8, 15-15
ghost/core/test/unit/server/services/oembed/oembed-service.test.js (1)
5-5: Module path correctly updated to kebab-case.The require path has been updated from PascalCase to kebab-case (
oembed-service), matching the actual module file naming. Verification confirms no stale references to old PascalCase paths remain in the codebase.ghost/core/test/unit/server/services/route-settings/settings-loader.test.js (1)
7-7: LGTM - Import path correctly updated.The require path has been updated to match the kebab-case filename convention while preserving the constructor usage and rewire functionality.
ghost/core/test/unit/server/services/route-settings/route-settings.test.js (1)
6-6: LGTM - Import path correctly updated.The require path has been updated to the kebab-case convention, maintaining the same constructor usage pattern in tests.
ghost/core/test/unit/server/services/route-settings/settings-path-manager.test.js (1)
4-4: LGTM - Import path correctly updated to kebab-case.The require path references the kebab-case filename (
settings-path-manager), while the exported constructor name (SettingsPathManager) remains unchanged. No orphaned references to old PascalCase filenames exist in the service code.ghost/core/core/server/services/link-redirection/index.js (1)
2-2: LGTM! Require paths updated correctly.Both require paths have been correctly updated to match the renamed kebab-case filenames. The changes are consistent with the repository's file naming standards.
Also applies to: 17-17
ghost/core/core/server/services/link-redirection/link-redirect-repository.js (1)
1-1: Require path correctly updated to kebab-case.The require statement has been properly updated from the old PascalCase filename to the kebab-case convention. All files in the link-redirection service directory follow the kebab-case naming pattern, and no remaining references to old PascalCase filenames exist.
ghost/core/core/server/services/milestones/service.js (1)
4-4: Import path updates are complete and consistent across the codebase.All references to the old PascalCase filenames have been properly updated to kebab-case paths (bookshelf-milestone-repository, milestone-queries, milestones-service). The new files exist with the correct naming convention, variable names appropriately remain PascalCase for class constructors, and test files have been updated consistently. No stale old-style import paths remain.
ghost/core/test/unit/server/services/milestones/milestones-service.test.js (1)
2-4: LGTM! All milestone service imports correctly updated.The require statements have been properly updated to use the new kebab-case filenames. The changes are consistent with the broader refactoring effort.
ghost/core/core/server/services/offers/domain/models/offer-created-at.js (1)
1-1: LGTM! Import path correctly updated to kebab-case.The ValueObject import path has been properly updated to match the kebab-case naming convention.
ghost/core/core/server/services/offers/domain/models/offer-type.js (1)
1-1: LGTM! Import path correctly updated to kebab-case.The ValueObject import path has been properly updated to follow kebab-case naming convention.
ghost/core/core/server/services/tiers/service.js (1)
8-12: LGTM! Import paths correctly updated to kebab-case.Both the TiersAPI and TierRepository import paths have been properly updated to follow kebab-case naming convention. The test files confirm these new paths are used consistently.
ghost/core/core/server/services/tiers/tier-activated-event.js (1)
3-3: LGTM! Type annotation correctly updated to kebab-case.The typedef import path for Tier has been properly updated to follow kebab-case naming convention.
ghost/core/test/unit/server/services/offers/application/offers-api.test.js (1)
7-7: LGTM! Test typedef correctly updated to kebab-case.The typedef import path for OfferBookshelfRepository has been properly updated to follow kebab-case naming convention.
ghost/core/core/server/services/members/members-api/services/PaymentsService.js (1)
3-6: LGTM! Import paths and JSDoc references correctly updated to kebab-case.All import paths for event classes and JSDoc type references have been systematically updated to match the kebab-case filename convention. The changes are consistent and align with the PR's stated objective of migrating to kebab-case filenames.
Also applies to: 60-60, 186-186, 214-214, 227-227, 401-401, 445-445
ghost/core/test/unit/server/services/offers/domain/models/offer-title.test.js (1)
3-3: LGTM! Test import correctly updated to kebab-case.The import path has been properly updated to reference the renamed module file.
ghost/core/test/unit/server/services/offers/domain/models/offer-code.test.js (1)
3-3: LGTM! Test import correctly updated to kebab-case.The import path has been properly updated to reference the renamed module file.
ghost/core/core/server/services/offers/domain/models/offer-cadence.js (1)
1-1: LGTM! ValueObject import correctly updated to kebab-case.The import path for the shared ValueObject module has been properly updated to match the kebab-case convention.
ghost/core/core/server/services/offers/domain/models/offer-code.js (1)
2-2: LGTM! ValueObject import correctly updated to kebab-case.The import path for the shared ValueObject module has been properly updated to match the kebab-case convention.
ghost/core/test/unit/server/services/offers/domain/models/offer.test.js (1)
4-6: LGTM! Import paths correctly updated to kebab-case.The module paths have been properly updated to reference the renamed files. The test logic remains unchanged.
ghost/core/test/unit/server/services/offers/domain/models/offer-amount.test.js (1)
3-3: LGTM! Import path correctly updated to kebab-case.The destructured import path has been properly updated to reference the renamed file.
ghost/core/core/server/services/offers/domain/models/offer-amount.js (1)
1-1: LGTM! Import path correctly updated to kebab-case.The ValueObject import path has been properly normalized to match the new file naming convention.
ghost/core/core/server/services/tiers/tier-price-change-event.js (1)
1-4: LGTM! JSDoc typedef import correctly updated to kebab-case.The type reference has been properly updated to match the renamed tier module.
ghost/core/core/server/services/offers/domain/models/stripe-coupon.js (1)
1-1: LGTM! Import path correctly updated to kebab-case.The ValueObject import path has been properly normalized to match the new file naming convention.
ghost/core/core/server/services/offers/application/OfferMapper.js (1)
2-2: LGTM! Typedef path updated correctly.The JSDoc typedef import path has been correctly updated to kebab-case, consistent with the file renaming initiative.
ghost/core/core/server/services/offers/domain/events/offer-created-event.js (1)
1-1: LGTM! Typedef path updated correctly.The typedef import path has been correctly updated to match the kebab-case filename convention.
ghost/core/test/unit/server/services/offers/domain/models/offer-cadence.test.js (1)
3-3: LGTM! Import path updated correctly.The require path has been correctly updated to kebab-case. Ensure the test suite passes to confirm all file renames are consistent.
ghost/core/test/unit/server/services/tiers/tier.test.js (1)
3-7: LGTM! All tier-related import paths updated consistently.All require paths have been systematically updated to kebab-case, maintaining consistency across the tier domain models and events. Ensure the test suite passes to confirm the refactoring is complete.
ghost/core/core/server/services/offers/domain/models/offer.js (1)
4-17: All 14 module paths are correctly updated to kebab-case and modules exist at their new locations.All referenced modules—12 value objects, 2 events, and stripe-coupon—have been verified to exist at their new kebab-case paths. No broken imports or cross-directory references remain. The systematic update is complete and safe.
ghost/core/.eslintrc.js (2)
105-105: LGTM! Services files correctly added to kebab-case enforcement.Extending the files array to include
core/server/services/**/*.{js,ts}aligns with the PR objective to enforce kebab-case naming convention in the services directory.
108-108: The review comment is based on a misunderstanding of the rule parameter.The third parameter
truein'ghost/filenames/match-regex': ['error', '^[a-z0-9.-]+$', true]does not enforce stricter matching—it enables an exception by ignoring files with named default exports that match their exported class names. This is the correct configuration for the services directory, where files likeUsers.js,AdapterManager.js, andSessionStore.jsexport matching class names. Changing fromfalsetotruemakes the rule more permissive, allowing these PascalCase filenames while still enforcing kebab-case for regular exports. Existing files will not fail linting; this change properly aligns the config with the codebase's naming convention.Likely an incorrect or invalid review comment.
ghost/core/core/server/services/link-tracking/link-click-repository.js (1)
1-1: Path updated correctly for kebab-case migration.The require path has been successfully updated to reflect the renamed file from
ClickEventtoclick-event. The target file exists and the change is complete with no remaining broken references to the old filename.ghost/core/core/server/services/activitypub/index.js (1)
1-1: LGTM! Correctly implements kebab-case naming convention.The module path has been correctly updated to reflect the file rename from
ActivityPubServiceWrappertoactivity-pub-service-wrapper, consistent with the repository's kebab-case filename convention.ghost/core/core/server/services/posts/posts-service.js (10)
19-27: LGTM!The constructor properly initializes dependencies using dependency injection. This follows standard Ghost patterns for service initialization.
34-49: LGTM!The
browsePostsandreadPostmethods implement standard query patterns with appropriate error handling for missing posts.
62-107: LGTM!The
editPostmethod properly validates email segments, handles newsletter email creation/retry logic, and supports optional event handling callbacks. The validation approach of usingMember.findPagewithlimit: 1to check filter validity is appropriate.
134-218: LGTM with comprehensive bulk operation handling.The
bulkEditmethod properly handles multiple bulk actions with validation, transactional updates, and domain event dispatching. The validation for visibility tiers (lines 175-189) and tag structure (lines 192-208) appropriately prevents incorrect usage.
279-351: LGTM on cascading delete implementation.The
#bulkDestroymethod properly handles cascading deletes across post-related and email-related tables. The decision to setsuppressions.email_idto null (lines 335-342) rather than deleting records preserves important suppression history.
365-427: LGTM on transactional post updates.The
#updatePostsmethod properly handles transactional updates including tier associations. The pattern of deleting all existingposts_productsentries (lines 398-402) and reinserting with proper sort_order (lines 405-421) ensures consistency.
429-449: LGTM on visibility filter parsing.The
getProductsFromVisibilityFiltermethod correctly uses NQL to parse visibility filters and extract product/tier information. The error handling (lines 443-447) appropriately wraps parsing errors with user-friendly messages.
458-489: LGTM on email and cache handling utilities.Both
shouldSendEmailandhandleCacheInvalidationimplement appropriate logic for their respective concerns. The cache invalidation correctly generates member-status-specific URLs (lines 476-482) for partial cache invalidation.
491-554: LGTM on post copy implementation.The
copyPostmethod comprehensively clones a post including all metadata, authors, tags, and tier associations. Setting the status to 'draft' (line 519) and appending "(Copy)" to the title (line 518) provides clear indication of the copied nature.
574-574: LGTM on class export.Exporting the
PostsServiceclass directly aligns with the new class-based architecture mentioned in the PR summary.ghost/core/core/server/api/endpoints/pages.js (1)
4-12: LGTM on service instance import update.The import path change from
posts-servicetoposts-service-instancealigns with the new service architecture. The usage pattern withgetPostServiceInstance()remains consistent with the existing codebase.ghost/core/core/server/api/endpoints/posts-public.js (1)
6-7: LGTM on service instance import update.The import path change from
posts-servicetoposts-service-instanceis consistent with the architectural refactor across all endpoints.ghost/core/core/server/api/endpoints/search-index.js (1)
2-3: LGTM on service instance import update.The import path change from
posts-servicetoposts-service-instancecompletes the consistent refactor pattern across all endpoint files.
ghost/core/test/unit/server/services/milestones/in-memory-milestone-repository.test.js
Show resolved
Hide resolved
React E2E Tests FailedTo view the Playwright test report locally, run: REPORT_DIR=$(mktemp -d) && gh run download 20814740267 -n playwright-report-react -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR" |
7191ca8 to
da98a52
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In @ghost/core/core/server/services/posts/posts-service.js:
- Around line 365-427: In the private method #updatePosts the code instantiates
ObjectId with ObjectId().toHexString() when building toInsert; change this to
use the constructor form new ObjectId().toHexString() to match the other usage
(e.g., new ObjectId() elsewhere) so instantiation is consistent and won't fail
at runtime; update the ObjectId call inside the loop that pushes into toInsert
accordingly.
- Around line 491-554: The copyPost method calls this.models.Post.findOne and
then immediately accesses existingPost.attributes; add a null check after the
findOne call (in copyPost) to detect when existingPost is null/undefined and
throw a NotFoundError (or the project's NotFound error class) with a clear
message so the function fails fast instead of throwing when accessing
attributes; place the check before any use of existingPost (before picking
attributes or related('authors')/('tags')) and ensure the thrown error uses the
project's standard error type for missing resources.
🧹 Nitpick comments (1)
ghost/core/core/server/services/posts/posts-service.js (1)
463-489: Consider adding explicit parentheses for clarity.Lines 467-468 and 472-473 contain compound boolean expressions that rely on operator precedence. While technically correct, explicit parentheses would improve readability.
♻️ Proposed refactor for clarity
if ( - model.get('status') === 'published' && model.wasChanged() || - model.get('status') === 'draft' && model.previous('status') === 'published' + (model.get('status') === 'published' && model.wasChanged()) || + (model.get('status') === 'draft' && model.previous('status') === 'published') ) { cacheInvalidate = true; } else if ( - model.get('status') === 'draft' && model.previous('status') !== 'published' || - model.get('status') === 'scheduled' && model.wasChanged() + (model.get('status') === 'draft' && model.previous('status') !== 'published') || + (model.get('status') === 'scheduled' && model.wasChanged()) ) {
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (136)
ghost/core/.eslintrc.jsghost/core/core/server/api/endpoints/pages.jsghost/core/core/server/api/endpoints/posts-public.jsghost/core/core/server/api/endpoints/posts.jsghost/core/core/server/api/endpoints/search-index-public.jsghost/core/core/server/api/endpoints/search-index.jsghost/core/core/server/api/endpoints/utils/serializers/output/mappers/posts.jsghost/core/core/server/lib/lexical.jsghost/core/core/server/services/activitypub/activity-pub-service-wrapper.jsghost/core/core/server/services/activitypub/activity-pub-service.tsghost/core/core/server/services/activitypub/index.jsghost/core/core/server/services/explore-ping/index.jsghost/core/core/server/services/explore/index.jsghost/core/core/server/services/link-redirection/index.jsghost/core/core/server/services/link-redirection/link-redirect-repository.jsghost/core/core/server/services/link-redirection/link-redirect.jsghost/core/core/server/services/link-redirection/link-redirects-service.jsghost/core/core/server/services/link-redirection/redirect-event.jsghost/core/core/server/services/link-tracking/click-event.jsghost/core/core/server/services/link-tracking/full-post-link.jsghost/core/core/server/services/link-tracking/index.jsghost/core/core/server/services/link-tracking/link-click-repository.jsghost/core/core/server/services/link-tracking/link-click-tracking-service.jsghost/core/core/server/services/link-tracking/post-link-repository.jsghost/core/core/server/services/link-tracking/post-link.jsghost/core/core/server/services/members/importer/MembersCSVImporter.jsghost/core/core/server/services/members/members-api/services/PaymentsService.jsghost/core/core/server/services/milestones/bookshelf-milestone-repository.jsghost/core/core/server/services/milestones/in-memory-milestone-repository.jsghost/core/core/server/services/milestones/milestone-created-event.jsghost/core/core/server/services/milestones/milestone-queries.jsghost/core/core/server/services/milestones/milestone.jsghost/core/core/server/services/milestones/milestones-service.jsghost/core/core/server/services/milestones/service.jsghost/core/core/server/services/oembed/nft-oembed-provider.jsghost/core/core/server/services/oembed/oembed-service.jsghost/core/core/server/services/oembed/service.jsghost/core/core/server/services/oembed/twitter-oembed-provider.jsghost/core/core/server/services/offers/application/OfferMapper.jsghost/core/core/server/services/offers/application/OffersAPI.jsghost/core/core/server/services/offers/application/UniqueChecker.jsghost/core/core/server/services/offers/domain/events/offer-code-change-event.jsghost/core/core/server/services/offers/domain/events/offer-created-event.jsghost/core/core/server/services/offers/domain/models/offer-amount.jsghost/core/core/server/services/offers/domain/models/offer-cadence.jsghost/core/core/server/services/offers/domain/models/offer-code.jsghost/core/core/server/services/offers/domain/models/offer-created-at.jsghost/core/core/server/services/offers/domain/models/offer-currency.jsghost/core/core/server/services/offers/domain/models/offer-description.jsghost/core/core/server/services/offers/domain/models/offer-duration.jsghost/core/core/server/services/offers/domain/models/offer-name.jsghost/core/core/server/services/offers/domain/models/offer-status.jsghost/core/core/server/services/offers/domain/models/offer-title.jsghost/core/core/server/services/offers/domain/models/offer-type.jsghost/core/core/server/services/offers/domain/models/offer.jsghost/core/core/server/services/offers/domain/models/shared/value-object.jsghost/core/core/server/services/offers/domain/models/stripe-coupon.jsghost/core/core/server/services/offers/offer-bookshelf-repository.jsghost/core/core/server/services/offers/offers-module.jsghost/core/core/server/services/offers/service.jsghost/core/core/server/services/posts/PostsService.jsghost/core/core/server/services/posts/posts-exporter.jsghost/core/core/server/services/posts/posts-service-instance.jsghost/core/core/server/services/posts/posts-service.jsghost/core/core/server/services/posts/stats/post-stats.jsghost/core/core/server/services/recommendations/index.jsghost/core/core/server/services/recommendations/recommendation-enabler-service.jsghost/core/core/server/services/recommendations/recommendation-service-wrapper.jsghost/core/core/server/services/route-settings/default-settings-manager.jsghost/core/core/server/services/route-settings/index.jsghost/core/core/server/services/route-settings/route-settings.jsghost/core/core/server/services/route-settings/settings-loader.jsghost/core/core/server/services/route-settings/settings-path-manager.jsghost/core/core/server/services/slack-notifications/service.jsghost/core/core/server/services/slack-notifications/slack-notifications-service.jsghost/core/core/server/services/slack-notifications/slack-notifications.jsghost/core/core/server/services/staff/index.jsghost/core/core/server/services/staff/staff-service-emails.jsghost/core/core/server/services/staff/staff-service.jsghost/core/core/server/services/tiers/in-memory-tier-repository.jsghost/core/core/server/services/tiers/service.jsghost/core/core/server/services/tiers/tier-activated-event.jsghost/core/core/server/services/tiers/tier-archived-event.jsghost/core/core/server/services/tiers/tier-created-event.jsghost/core/core/server/services/tiers/tier-name-change-event.jsghost/core/core/server/services/tiers/tier-price-change-event.jsghost/core/core/server/services/tiers/tier-repository.jsghost/core/core/server/services/tiers/tier.jsghost/core/core/server/services/tiers/tiers-api.jsghost/core/core/server/services/tinybird/index.jsghost/core/core/server/services/tinybird/tinybird-service-wrapper.jsghost/core/core/server/services/tinybird/tinybird-service.jsghost/core/test/unit/server/services/activitypub/activity-pub-service.test.tsghost/core/test/unit/server/services/link-redirection/link-redirect-repository.test.jsghost/core/test/unit/server/services/link-redirection/link-redirects-service.test.jsghost/core/test/unit/server/services/link-tracking/link-click-repository.test.jsghost/core/test/unit/server/services/link-tracking/link-click-tracking-service.test.jsghost/core/test/unit/server/services/link-tracking/post-link-repository.test.jsghost/core/test/unit/server/services/members/importer/members-csv-importer.test.jsghost/core/test/unit/server/services/members/members-api/services/payments-service.test.jsghost/core/test/unit/server/services/milestones/bookshelf-milestone-repository.test.jsghost/core/test/unit/server/services/milestones/in-memory-milestone-repository.test.jsghost/core/test/unit/server/services/milestones/milestone-queries.test.jsghost/core/test/unit/server/services/milestones/milestone.test.jsghost/core/test/unit/server/services/milestones/milestones-service.test.jsghost/core/test/unit/server/services/oembed/nft-oembed.test.jsghost/core/test/unit/server/services/oembed/oembed-service.test.jsghost/core/test/unit/server/services/oembed/twitter-embed.test.jsghost/core/test/unit/server/services/offers/application/offers-api.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-amount.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-cadence.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-code.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-currency.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-description.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-duration.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-name.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-status.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-title.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-type.test.jsghost/core/test/unit/server/services/offers/domain/models/offer.test.jsghost/core/test/unit/server/services/offers/domain/models/stripe-coupon.test.jsghost/core/test/unit/server/services/posts/posts-exporter.test.jsghost/core/test/unit/server/services/posts/posts-service.test.jsghost/core/test/unit/server/services/route-settings/route-settings.test.jsghost/core/test/unit/server/services/route-settings/settings-loader.test.jsghost/core/test/unit/server/services/route-settings/settings-path-manager.test.jsghost/core/test/unit/server/services/settings/default-settings-manager.test.jsghost/core/test/unit/server/services/slack-notifications/index.test.jsghost/core/test/unit/server/services/slack-notifications/slack-notifications-service.test.jsghost/core/test/unit/server/services/slack-notifications/slack-notifications.test.jsghost/core/test/unit/server/services/staff/index.test.jsghost/core/test/unit/server/services/staff/staff-service.test.jsghost/core/test/unit/server/services/tiers/tier-repository.test.jsghost/core/test/unit/server/services/tiers/tier.test.jsghost/core/test/unit/server/services/tiers/tiers-api.test.jsghost/core/test/unit/server/services/tinybird/tinybird-service.test.js
💤 Files with no reviewable changes (1)
- ghost/core/core/server/services/posts/PostsService.js
✅ Files skipped from review due to trivial changes (1)
- ghost/core/test/unit/server/services/offers/domain/models/offer-code.test.js
🚧 Files skipped from review as they are similar to previous changes (64)
- ghost/core/core/server/services/offers/domain/models/offer-currency.js
- ghost/core/test/unit/server/services/members/members-api/services/payments-service.test.js
- ghost/core/core/server/services/tiers/tier-created-event.js
- ghost/core/test/unit/server/services/milestones/milestones-service.test.js
- ghost/core/core/server/services/milestones/bookshelf-milestone-repository.js
- ghost/core/test/unit/server/services/milestones/milestone-queries.test.js
- ghost/core/core/server/services/offers/domain/models/offer-amount.js
- ghost/core/core/server/services/link-redirection/index.js
- ghost/core/core/server/services/members/importer/MembersCSVImporter.js
- ghost/core/core/server/services/oembed/service.js
- ghost/core/core/server/services/offers/domain/events/offer-code-change-event.js
- ghost/core/core/server/services/staff/staff-service.js
- ghost/core/core/server/services/offers/domain/events/offer-created-event.js
- ghost/core/core/server/services/link-tracking/post-link-repository.js
- ghost/core/core/server/services/slack-notifications/slack-notifications.js
- ghost/core/core/server/services/tiers/tier-repository.js
- ghost/core/core/server/api/endpoints/search-index.js
- ghost/core/test/unit/server/services/route-settings/settings-loader.test.js
- ghost/core/core/server/services/tiers/in-memory-tier-repository.js
- ghost/core/core/server/api/endpoints/posts-public.js
- ghost/core/core/server/services/posts/posts-service-instance.js
- ghost/core/test/unit/server/services/posts/posts-exporter.test.js
- ghost/core/core/server/services/offers/application/OffersAPI.js
- ghost/core/core/server/services/offers/domain/models/offer-cadence.js
- ghost/core/core/server/services/milestones/service.js
- ghost/core/test/unit/server/services/milestones/in-memory-milestone-repository.test.js
- ghost/core/core/server/services/offers/application/UniqueChecker.js
- ghost/core/test/unit/server/services/posts/posts-service.test.js
- ghost/core/test/unit/server/services/offers/domain/models/offer-currency.test.js
- ghost/core/test/unit/server/services/tiers/tiers-api.test.js
- ghost/core/test/unit/server/services/activitypub/activity-pub-service.test.ts
- ghost/core/core/server/services/tinybird/index.js
- ghost/core/test/unit/server/services/tiers/tier-repository.test.js
- ghost/core/core/server/services/link-tracking/link-click-repository.js
- ghost/core/core/server/services/tiers/service.js
- ghost/core/.eslintrc.js
- ghost/core/core/server/api/endpoints/posts.js
- ghost/core/core/server/services/link-tracking/index.js
- ghost/core/core/server/services/offers/domain/models/offer-name.js
- ghost/core/test/unit/server/services/link-redirection/link-redirect-repository.test.js
- ghost/core/core/server/services/offers/domain/models/offer-duration.js
- ghost/core/core/server/lib/lexical.js
- ghost/core/test/unit/server/services/oembed/nft-oembed.test.js
- ghost/core/core/server/services/offers/application/OfferMapper.js
- ghost/core/test/unit/server/services/offers/domain/models/offer-status.test.js
- ghost/core/core/server/services/offers/service.js
- ghost/core/test/unit/server/services/link-tracking/link-click-repository.test.js
- ghost/core/core/server/services/slack-notifications/slack-notifications-service.js
- ghost/core/core/server/api/endpoints/pages.js
- ghost/core/test/unit/server/services/offers/domain/models/offer-amount.test.js
- ghost/core/core/server/services/offers/domain/models/offer-code.js
- ghost/core/test/unit/server/services/tiers/tier.test.js
- ghost/core/test/unit/server/services/tinybird/tinybird-service.test.js
- ghost/core/core/server/services/explore-ping/index.js
- ghost/core/core/server/services/offers/offers-module.js
- ghost/core/core/server/services/tiers/tier.js
- ghost/core/core/server/services/offers/domain/models/offer-type.js
- ghost/core/core/server/services/members/members-api/services/PaymentsService.js
- ghost/core/test/unit/server/services/milestones/milestone.test.js
- ghost/core/core/server/services/tiers/tiers-api.js
- ghost/core/core/server/services/explore/index.js
- ghost/core/core/server/services/offers/domain/models/offer-status.js
- ghost/core/core/server/services/offers/domain/models/offer-description.js
- ghost/core/test/unit/server/services/route-settings/settings-path-manager.test.js
🧰 Additional context used
🧠 Learnings (13)
📓 Common learnings
Learnt from: sam-lord
Repo: TryGhost/Ghost PR: 25303
File: ghost/core/core/server/services/email-service/BatchSendingService.js:19-19
Timestamp: 2025-10-30T17:13:26.190Z
Learning: In ghost/core/core/server/services/email-service/BatchSendingService.js and similar files in the Ghost codebase, prefer using `{...options}` spread syntax without explicit guards like `...(options || {})` when spreading potentially undefined objects, as the maintainer prefers cleaner syntax over defensive patterns when the behavior is safe.
Learnt from: sagzy
Repo: TryGhost/Ghost PR: 24673
File: ghost/i18n/lib/i18n.js:34-35
Timestamp: 2025-11-24T11:12:15.712Z
Learning: In the Ghost i18n package (ghost/i18n/lib/i18n.js), changing existing locale codes requires backwards compatibility handling for users who have already configured those locales. Such changes should be done in a separate PR with migration logic rather than included in feature PRs.
📚 Learning: 2026-01-08T10:26:38.700Z
Learnt from: rob-ghost
Repo: TryGhost/Ghost PR: 25791
File: ghost/core/core/server/api/endpoints/member-comment-ban.js:64-68
Timestamp: 2026-01-08T10:26:38.700Z
Learning: In the Ghost API, endpoints rely on the serialization layer to prepare frame.data[docName] as a non-empty array before query() executes. Endpoints access frame.data[docName][0] directly (e.g., frame.data.comment_bans[0], frame.data.members[0], frame.data.posts[0]) without per-endpoint validation. This pattern is common across API endpoints. When maintaining or creating endpoints, avoid duplicating validation for frame.data[docName] and ensure the serializer guarantees the shape and non-emptiness. If you add a new endpoint that uses this frame.data[docName], follow the same assumption and avoid redundant checks unless there's a documented exception.
Applied to files:
ghost/core/test/unit/server/services/offers/application/offers-api.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-duration.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-cadence.test.jsghost/core/core/server/services/offers/domain/models/offer-created-at.jsghost/core/test/unit/server/services/offers/domain/models/offer-description.test.jsghost/core/core/server/services/link-redirection/redirect-event.jsghost/core/test/unit/server/services/slack-notifications/index.test.jsghost/core/test/unit/server/services/slack-notifications/slack-notifications-service.test.jsghost/core/core/server/api/endpoints/utils/serializers/output/mappers/posts.jsghost/core/core/server/services/tiers/tier-archived-event.jsghost/core/core/server/services/recommendations/recommendation-service-wrapper.jsghost/core/core/server/services/route-settings/index.jsghost/core/core/server/services/recommendations/index.jsghost/core/test/unit/server/services/link-tracking/post-link-repository.test.jsghost/core/core/server/services/milestones/in-memory-milestone-repository.jsghost/core/core/server/api/endpoints/search-index-public.jsghost/core/core/server/services/milestones/milestone.jsghost/core/core/server/services/offers/domain/models/stripe-coupon.jsghost/core/core/server/services/link-redirection/link-redirect-repository.jsghost/core/test/unit/server/services/offers/domain/models/offer-type.test.jsghost/core/test/unit/server/services/settings/default-settings-manager.test.jsghost/core/test/unit/server/services/staff/staff-service.test.jsghost/core/test/unit/server/services/offers/domain/models/stripe-coupon.test.jsghost/core/core/server/services/slack-notifications/service.jsghost/core/core/server/services/offers/domain/models/offer.jsghost/core/core/server/services/posts/posts-service.jsghost/core/core/server/services/milestones/milestones-service.jsghost/core/core/server/services/tinybird/tinybird-service-wrapper.jsghost/core/core/server/services/staff/index.jsghost/core/test/unit/server/services/link-tracking/link-click-tracking-service.test.jsghost/core/core/server/services/tiers/tier-activated-event.jsghost/core/test/unit/server/services/offers/domain/models/offer.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-name.test.jsghost/core/test/unit/server/services/members/importer/members-csv-importer.test.jsghost/core/core/server/services/offers/offer-bookshelf-repository.jsghost/core/test/unit/server/services/offers/domain/models/offer-title.test.jsghost/core/test/unit/server/services/link-redirection/link-redirects-service.test.jsghost/core/test/unit/server/services/milestones/bookshelf-milestone-repository.test.jsghost/core/test/unit/server/services/slack-notifications/slack-notifications.test.jsghost/core/core/server/services/activitypub/activity-pub-service-wrapper.jsghost/core/core/server/services/offers/domain/models/offer-title.jsghost/core/core/server/services/link-tracking/link-click-tracking-service.jsghost/core/test/unit/server/services/route-settings/route-settings.test.jsghost/core/core/server/services/tiers/tier-name-change-event.jsghost/core/core/server/services/activitypub/index.jsghost/core/core/server/services/link-redirection/link-redirects-service.jsghost/core/test/unit/server/services/staff/index.test.jsghost/core/test/unit/server/services/oembed/twitter-embed.test.jsghost/core/test/unit/server/services/oembed/oembed-service.test.jsghost/core/core/server/services/tiers/tier-price-change-event.js
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Test names should be lowercase and follow the format 'what is tested - expected outcome'
Applied to files:
ghost/core/test/unit/server/services/offers/domain/models/offer-duration.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-cadence.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-description.test.jsghost/core/test/unit/server/services/link-tracking/post-link-repository.test.jsghost/core/test/unit/server/services/link-tracking/link-click-tracking-service.test.jsghost/core/test/unit/server/services/offers/domain/models/offer.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-name.test.jsghost/core/test/unit/server/services/members/importer/members-csv-importer.test.jsghost/core/test/unit/server/services/milestones/bookshelf-milestone-repository.test.jsghost/core/test/unit/server/services/slack-notifications/slack-notifications.test.jsghost/core/test/unit/server/services/route-settings/route-settings.test.js
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Use factory pattern for all test data creation instead of hard-coded data or direct database manipulation
Applied to files:
ghost/core/test/unit/server/services/offers/domain/models/offer-cadence.test.jsghost/core/test/unit/server/services/offers/domain/models/offer-type.test.js
📚 Learning: 2025-09-03T12:28:11.174Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 24779
File: ghost/core/core/server/services/members/members-api/controllers/RouterController.js:34-51
Timestamp: 2025-09-03T12:28:11.174Z
Learning: The extractRefererOrRedirect function in Ghost's RouterController (ghost/core/core/server/services/members/members-api/controllers/RouterController.js) is working as intended according to maintainer kevinansfield. The current handling of autoRedirect, redirect parameter parsing, and referrer logic does not need the security-related changes suggested around autoRedirect coercion, relative URL handling, or same-origin checks.
Applied to files:
ghost/core/core/server/services/link-redirection/redirect-event.jsghost/core/core/server/services/link-redirection/link-redirect-repository.jsghost/core/test/unit/server/services/link-redirection/link-redirects-service.test.jsghost/core/core/server/services/link-tracking/link-click-tracking-service.jsghost/core/core/server/services/link-redirection/link-redirects-service.js
📚 Learning: 2025-03-13T09:00:20.205Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/utils/pending-activity.ts:13-71
Timestamp: 2025-03-13T09:00:20.205Z
Learning: The pending activity utilities in the Ghost ActivityPub module are covered by tests in the file `apps/admin-x-activitypub/test/unit/utils/pending-activity.ts`.
Applied to files:
ghost/core/test/unit/server/services/staff/staff-service.test.jsghost/core/core/server/services/activitypub/activity-pub-service-wrapper.jsghost/core/core/server/services/activitypub/index.js
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Each test receives fresh Ghost instance for automatic isolation
Applied to files:
ghost/core/test/unit/server/services/staff/staff-service.test.js
📚 Learning: 2025-03-13T09:00:20.205Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/utils/pending-activity.ts:13-71
Timestamp: 2025-03-13T09:00:20.205Z
Learning: The pending activity utilities in Ghost's ActivityPub module are thoroughly tested in `apps/admin-x-activitypub/test/unit/utils/pending-activity.ts`, which covers `generatePendingActivityId`, `isPendingActivity`, and `generatePendingActivity` functions.
Applied to files:
ghost/core/test/unit/server/services/staff/staff-service.test.jsghost/core/core/server/services/activitypub/activity-pub-service-wrapper.jsghost/core/core/server/services/activitypub/index.js
📚 Learning: 2025-10-30T17:13:26.190Z
Learnt from: sam-lord
Repo: TryGhost/Ghost PR: 25303
File: ghost/core/core/server/services/email-service/BatchSendingService.js:19-19
Timestamp: 2025-10-30T17:13:26.190Z
Learning: In ghost/core/core/server/services/email-service/BatchSendingService.js and similar files in the Ghost codebase, prefer using `{...options}` spread syntax without explicit guards like `...(options || {})` when spreading potentially undefined objects, as the maintainer prefers cleaner syntax over defensive patterns when the behavior is safe.
Applied to files:
ghost/core/core/server/services/posts/posts-service.jsghost/core/core/server/services/staff/index.jsghost/core/core/server/services/offers/offer-bookshelf-repository.js
📚 Learning: 2025-11-25T14:28:50.351Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T14:28:50.351Z
Learning: Analytics using Tinybird should reference scripts in `ghost/core/core/server/data/tinybird/scripts/` and datafiles in `ghost/core/core/server/data/tinybird/`
Applied to files:
ghost/core/core/server/services/tinybird/tinybird-service-wrapper.js
📚 Learning: 2025-12-02T16:28:27.396Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 25581
File: ghost/core/core/server/services/activitypub/ActivityPubServiceWrapper.js:35-67
Timestamp: 2025-12-02T16:28:27.396Z
Learning: In the Ghost codebase, concurrency concerns related to rapid settings changes triggering event listeners are pre-existing and systemic, affecting all settings event listeners. If addressed, concurrency protection should be implemented at the event/settings service level, not in individual service wrappers like ActivityPubServiceWrapper.
Applied to files:
ghost/core/core/server/services/activitypub/activity-pub-service-wrapper.js
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the Ghost ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.
Applied to files:
ghost/core/core/server/services/activitypub/index.js
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.
Applied to files:
ghost/core/core/server/services/activitypub/index.js
🧬 Code graph analysis (3)
ghost/core/core/server/services/slack-notifications/service.js (2)
ghost/core/test/unit/server/services/slack-notifications/slack-notifications-service.test.js (1)
SlackNotificationsService(3-3)ghost/core/test/unit/server/services/slack-notifications/slack-notifications.test.js (1)
SlackNotifications(3-3)
ghost/core/core/server/services/posts/posts-service.js (2)
ghost/core/core/server/services/posts/posts-service-instance.js (1)
postsExporter(19-33)ghost/admin/mirage/config/posts.js (1)
visibilityFilter(43-43)
ghost/core/core/server/services/staff/index.js (1)
ghost/core/test/unit/server/services/staff/staff-service.test.js (1)
StaffService(11-11)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: Unit tests (Node 22.18.0)
- GitHub Check: Legacy tests (Node 22.18.0, sqlite3)
- GitHub Check: Legacy tests (Node 22.18.0, mysql8)
- GitHub Check: Acceptance tests (Node 22.18.0, mysql8)
- GitHub Check: Ghost-CLI tests
- GitHub Check: Acceptance tests (Node 22.18.0, sqlite3)
- GitHub Check: Build & Push Docker Image
🔇 Additional comments (60)
ghost/core/test/unit/server/services/members/importer/members-csv-importer.test.js (1)
3-3: LGTM!The import path has been correctly updated to reference the renamed file (
tierinstead ofTier), aligning with the kebab-case migration stated in the PR objectives. The usage of theTierclass throughout the test remains appropriate and unchanged.ghost/core/test/unit/server/services/milestones/bookshelf-milestone-repository.test.js (1)
10-10: LGTM!The import path correctly updated to kebab-case while preserving the PascalCase class name convention. This aligns with the PR's file renaming objective.
ghost/core/core/server/services/milestones/milestone.js (1)
3-3: LGTM!Import path correctly updated to kebab-case for the
MilestoneCreatedEventmodule.ghost/core/core/server/services/milestones/milestones-service.js (1)
1-1: LGTM!Import path correctly updated to lowercase kebab-case. The
Milestoneclass usage throughout the service remains unaffected.ghost/core/core/server/services/milestones/in-memory-milestone-repository.js (1)
2-3: LGTM!JSDoc typedef import paths correctly updated to kebab-case (
./milestoneand./milestones-service), maintaining consistency with the other renamed modules in this PR.ghost/core/core/server/api/endpoints/utils/serializers/output/mappers/posts.js (1)
17-18: LGTM! Import path updated correctly.The import path has been updated to the new
posts-service-instancemodule while maintaining the same usage pattern. This change aligns with the kebab-case naming convention established in the PR objectives.ghost/core/core/server/api/endpoints/search-index-public.js (1)
2-3: LGTM! Import path updated correctly.The import path has been updated to the new
posts-service-instancemodule while maintaining the same usage pattern. This change is consistent with the kebab-case refactor and matches the updates made across other endpoint files.ghost/core/core/server/services/tiers/tier-activated-event.js (1)
3-3: LGTM: typedef path updated correctly.The typedef import path correctly reflects the kebab-case file rename from
Tiertotier.ghost/core/core/server/services/tiers/tier-price-change-event.js (1)
3-3: LGTM: typedef path updated correctly.The typedef import path correctly reflects the kebab-case file rename from
Tiertotier.ghost/core/core/server/services/tiers/tier-name-change-event.js (1)
3-3: LGTM: typedef path updated correctly.The typedef import path correctly reflects the kebab-case file rename from
Tiertotier.ghost/core/core/server/services/tiers/tier-archived-event.js (1)
3-3: LGTM: typedef path updated correctly.The typedef import path correctly reflects the kebab-case file rename from
Tiertotier.ghost/core/core/server/services/staff/index.js (1)
11-11: LGTM! Import path updated to kebab-case.The require path has been correctly updated from
./StaffServiceto./staff-servicewhile the exported class name remains unchanged.ghost/core/test/unit/server/services/staff/index.test.js (1)
9-9: LGTM! Import path updated to kebab-case.The require path for
MilestoneCreatedEventhas been correctly updated fromMilestoneCreatedEventtomilestone-created-eventwhile the exported class name remains unchanged.ghost/core/test/unit/server/services/staff/staff-service.test.js (1)
5-5: LGTM! Import paths updated to kebab-case.Both require paths have been correctly updated:
MilestoneCreatedEvent: fromMilestoneCreatedEventtomilestone-created-eventStaffService: fromStaffServicetostaff-serviceThe exported class names remain unchanged and test usage is unaffected.
Also applies to: 11-11
ghost/core/core/server/services/tinybird/tinybird-service-wrapper.js (1)
1-1: LGTM! Import path correctly updated to kebab-case.The import path has been properly updated from
./TinybirdServiceto./tinybird-serviceto align with the repository's kebab-case file naming convention. Verification confirms no lingering references to the old PascalCase import path exist in the codebase.ghost/core/test/unit/server/services/oembed/oembed-service.test.js (1)
5-5: LGTM! Clean path update to kebab-case.The require path has been correctly updated to match the new kebab-case naming convention. The class variable name appropriately remains PascalCase. Verification confirms all imports of the oembed-service module have been updated consistently across the codebase with no remaining old-style imports.
ghost/core/core/server/services/recommendations/recommendation-service-wrapper.js (1)
56-56: LGTM! Verify all references to the renamed file have been updated.The import path update correctly reflects the file rename to kebab-case. To ensure completeness of the refactoring, verify that no other references to the old PascalCase filename exist.
#!/bin/bash # Description: Verify all references to RecommendationEnablerService file have been updated # Search for any remaining references to the old PascalCase filename rg -n --type=js --type=ts -C2 "RecommendationEnablerService['\"]" -g '!node_modules' # Search for require/import statements that might reference the old file rg -n --type=js --type=ts -C2 "require\(.*RecommendationEnablerService" -g '!node_modules' rg -n --type=js --type=ts -C2 "from.*RecommendationEnablerService" -g '!node_modules'ghost/core/core/server/services/recommendations/index.js (1)
1-1: LGTM! The file rename refactoring is complete.The import path correctly reflects the file rename to kebab-case (
recommendation-service-wrapper.js), the old PascalCase file no longer exists, and no remaining references to the old filename were found in the codebase.ghost/core/core/server/services/link-redirection/link-redirect-repository.js (1)
1-1: LGTM! Path updated to kebab-case.The require path change from
./LinkRedirectto./link-redirectaligns with the repository's kebab-case naming convention.ghost/core/test/unit/server/services/link-tracking/post-link-repository.test.js (1)
4-4: LGTM! Test import updated to kebab-case.The require path correctly references the renamed module.
ghost/core/core/server/services/link-redirection/link-redirects-service.js (1)
3-4: LGTM! Import paths updated to kebab-case.Both module paths are correctly updated to match the renamed files.
ghost/core/test/unit/server/services/link-redirection/link-redirects-service.test.js (1)
1-1: LGTM! Test import updated to kebab-case.The require path correctly references the renamed service module.
ghost/core/core/server/services/link-redirection/redirect-event.js (2)
4-4: LGTM! Typedef import path updated to kebab-case.The JSDoc type reference correctly points to the renamed module.
1-24: Module reference updates are consistent across the codebase. All imports and requires for the renamed modules (link-redirect, redirect-event, link-redirects-service, link-redirect-repository, post-link-repository) have been properly updated throughout the codebase with no remaining references to the old PascalCase filenames.ghost/core/test/unit/server/services/link-tracking/link-click-tracking-service.test.js (1)
1-12: LGTM! Import paths correctly updated to kebab-case.The require statements have been properly updated to reference the renamed module files. The changes are consistent with the repository-wide migration to kebab-case naming conventions.
ghost/core/core/server/services/link-tracking/link-click-tracking-service.js (1)
1-25: LGTM! Module imports and JSDoc typedef paths correctly updated.All require statements and the JSDoc typedef import have been properly updated to reference the kebab-case module filenames. The changes maintain consistency across the codebase and align with the repository's filename conventions.
ghost/core/core/server/services/activitypub/activity-pub-service-wrapper.js (1)
1-1: Import path correctly updated to kebab-case.All references to the ActivityPubService module have been successfully migrated from PascalCase to kebab-case naming. No remaining old-style imports exist in the codebase.
ghost/core/core/server/services/activitypub/index.js (1)
1-1: LGTM! External imports of the activitypub service remain functional.The export path update from
'./ActivityPubServiceWrapper'to'./activity-pub-service-wrapper'is correct. External code inghost/core/core/boot.jsimports via the directory path (require('./server/services/activitypub')), which properly resolves to the index.js file. No production code imports the wrapper directly by filename.ghost/core/test/unit/server/services/offers/domain/models/offer-description.test.js (1)
3-3: Import path is correctly updated to kebab-case and consistent across all imports.The module has been renamed to
offer-description.jsand all imports throughout the codebase—including inOffersAPI.jsandoffer.js—are already using the kebab-case naming convention. No stale imports remain.ghost/core/test/unit/server/services/settings/default-settings-manager.test.js (1)
5-5: Path correctly updated for kebab-case rename.The require path on line 5 is correct. The renamed file exists at the expected location, and no stale references to the old PascalCase filename remain.
ghost/core/core/server/services/route-settings/index.js (1)
9-12: All require paths correctly updated to kebab-case format.All four module files exist with the corresponding kebab-case names (route-settings.js, settings-loader.js, default-settings-manager.js, settings-path-manager.js), and no stale PascalCase references remain in the codebase.
ghost/core/test/unit/server/services/route-settings/route-settings.test.js (1)
6-6: LGTM - Path updated for kebab-case rename.The require path correctly reflects the RouteSettings module rename to kebab-case. Verification confirms the file exists at the correct location and no stale references to the old PascalCase filename remain.
ghost/core/test/unit/server/services/slack-notifications/index.test.js (1)
5-5: LGTM! Import path correctly updated to kebab-case.The import path has been updated to match the renamed file while maintaining the same exported symbol.
ghost/core/test/unit/server/services/slack-notifications/slack-notifications.test.js (1)
3-3: LGTM! Require path correctly updated to kebab-case.The require path has been updated to match the renamed file.
ghost/core/test/unit/server/services/slack-notifications/slack-notifications-service.test.js (1)
3-3: LGTM! Both require paths correctly updated to kebab-case.The require paths for SlackNotificationsService and MilestoneCreatedEvent have been updated to match the renamed files.
Also applies to: 5-5
ghost/core/core/server/services/slack-notifications/service.js (1)
6-6: All JSDoc and require paths correctly updated to kebab-case—verification confirms no old references remain in the codebase.The verification script ran successfully and found no remaining PascalCase references, confirming the renaming is complete across the codebase.
ghost/core/test/unit/server/services/oembed/twitter-embed.test.js (1)
4-4: No action required — class names match correctly.The source file exports
TwitterOEmbedProvider(with capital 'O'), which matches the import and usage in the test. The import path change to kebab-case is correct. There is no class name mismatch.Likely an incorrect or invalid review comment.
ghost/core/core/server/services/offers/domain/models/offer-created-at.js (1)
1-1: LGTM! Import path updated to kebab-case.The import path change from
./shared/ValueObjectto./shared/value-objectcorrectly aligns with the repository's kebab-case filename convention.ghost/core/test/unit/server/services/offers/domain/models/offer-duration.test.js (1)
3-3: LGTM! Test import updated to match kebab-case filename.The require path correctly references the renamed
offer-durationmodule.ghost/core/core/server/services/offers/domain/models/stripe-coupon.js (1)
1-1: LGTM! Import path updated to kebab-case.The import path change from
./shared/ValueObjectto./shared/value-objectcorrectly aligns with the repository's kebab-case filename convention.ghost/core/test/unit/server/services/offers/domain/models/stripe-coupon.test.js (1)
2-2: LGTM! Test import updated to match kebab-case filename.The require path correctly references the renamed
stripe-couponmodule.ghost/core/test/unit/server/services/offers/domain/models/offer-cadence.test.js (1)
3-3: LGTM! Test import updated to match kebab-case filename.The require path correctly references the renamed
offer-cadencemodule.ghost/core/test/unit/server/services/offers/domain/models/offer-title.test.js (1)
3-3: LGTM! Import path updated to kebab-case.The import path has been correctly updated to match the renamed file.
ghost/core/core/server/services/offers/domain/models/offer-title.js (1)
1-1: LGTM! ValueObject import path updated to kebab-case.The import path has been correctly updated to match the renamed file.
ghost/core/test/unit/server/services/offers/domain/models/offer.test.js (1)
4-6: LGTM! Import paths updated to kebab-case.All three import paths have been correctly updated to match the renamed files.
ghost/core/test/unit/server/services/offers/domain/models/offer-type.test.js (1)
3-3: LGTM! Import path updated to kebab-case.The import path has been correctly updated to match the renamed file.
ghost/core/test/unit/server/services/offers/domain/models/offer-name.test.js (1)
3-3: LGTM! Import path correctly updated to kebab-case.The import path has been correctly updated to match the renamed file. All PascalCase imports of offer domain models have been replaced with kebab-case equivalents, and the ESLint rule enforcing kebab-case filenames for
core/server/services/**is in place inghost/core/.eslintrc.js.ghost/core/core/server/services/offers/offer-bookshelf-repository.js (1)
4-4: LGTM - import and typedef paths updated consistently.All references to the Offer domain model have been updated from PascalCase to kebab-case format. The changes are consistent across the require statement and all JSDoc type annotations.
Also applies to: 96-96, 144-144, 162-162, 179-179, 198-198
ghost/core/core/server/services/offers/domain/models/offer.js (1)
4-17: LGTM - all import paths updated consistently.All 14 require statements have been updated from PascalCase to kebab-case format, covering domain models, events, and value objects. The changes are comprehensive and consistent.
ghost/core/test/unit/server/services/offers/application/offers-api.test.js (1)
7-7: Typedef path correctly updated to kebab-case.The typedef import path has been properly updated to match the kebab-case filename convention, and the path correctly resolves to
ghost/core/core/server/services/offers/offer-bookshelf-repository.js. The typedef is consistently used throughout the test file for proper type casting.ghost/core/core/server/services/posts/posts-service.js (10)
1-27: LGTM!The imports and dependency injection pattern in the constructor are clean and follow best practices.
29-49: LGTM!The
browsePostsandreadPostmethods are well-implemented with appropriate error handling.
62-107: LGTM!The
editPostmethod properly validates email segments and handles email creation with appropriate error handling.
134-218: LGTM!The
bulkEditmethod handles multiple actions with proper validation and domain event dispatching. The special handling for revertingemail_onlyon unscheduled posts is a necessary business rule.
229-272: LGTM!The
#bulkAddTagsmethod correctly handles transactions, creates missing tags, and builds the post-tag relationships.
279-359: LGTM!The bulk destroy logic correctly handles cascade deletion across multiple tables and appropriately sets
email_idto null in the suppressions table rather than deleting those records.
429-449: LGTM!The
getProductsFromVisibilityFiltermethod correctly parses the NQL filter and matches products with appropriate error handling.
458-461: LGTM!The
shouldSendEmaillogic correctly determines when an email should be sent based on status transitions.
562-571: LGTM!The URL manipulation logic correctly generates the location for a copied post.
574-574: LGTM!The class is correctly exported for use by the instance factory.
da98a52 to
0288f9e
Compare
0288f9e to
6a8a3f2
Compare
6a8a3f2 to
1b0e743
Compare
ref #25803 * Renamed files to match to kebab-case format
ref #25803 * Renamed files to match to kebab-case format
ref https://linear.app/ghost/issue/NY-722/migrate-ghost-codebase-to-kebab-case-file-naming-convention
what
why
note: since services contain more than 300 files to be renamed, will do this in multiple PRs