From 3fe901af8c59b60bccbe0c0e9cc3c347ea28a40f Mon Sep 17 00:00:00 2001 From: Fabien O'Carroll Date: Tue, 23 Jun 2026 08:16:51 +0000 Subject: [PATCH] Cleaned up custom-redirects types.ts - types.ts was an ownership-free dumping ground accidentally committed during the recent service refactor; it gave the two types no clear owner - moved RedirectConfig into its own redirect-config.ts: it is the shared domain type (parser produces it, validation checks it, the service orchestrates it, adapters persist it) with no single consumer, so it gets a dedicated, named home rather than a peer's file - moved the RedirectsStore port into redirects-service.ts next to the existing RedirectManagerLike: its sole consumer is RedirectsService, so the consumer owns the contract and adapters implement it (correct ports-and-adapters direction) - type-only change: every other edit just repoints an import; no runtime behaviour changed --- .../core/server/adapters/redirects/FileStore.ts | 3 ++- .../adapters/redirects/S3RedirectsStore.ts | 3 ++- .../custom-redirects/redirect-config-parser.ts | 2 +- .../services/custom-redirects/redirect-config.ts | 7 +++++++ .../custom-redirects/redirects-service.ts | 11 ++++++++++- .../server/services/custom-redirects/types.ts | 16 ---------------- .../services/custom-redirects/validation.js | 2 +- .../custom-redirects/helpers/in-memory-store.ts | 3 ++- .../custom-redirects/helpers/store-contract.ts | 2 +- 9 files changed, 26 insertions(+), 23 deletions(-) create mode 100644 ghost/core/core/server/services/custom-redirects/redirect-config.ts delete mode 100644 ghost/core/core/server/services/custom-redirects/types.ts diff --git a/ghost/core/core/server/adapters/redirects/FileStore.ts b/ghost/core/core/server/adapters/redirects/FileStore.ts index 98b8b55fa6e..a8b86a5692f 100644 --- a/ghost/core/core/server/adapters/redirects/FileStore.ts +++ b/ghost/core/core/server/adapters/redirects/FileStore.ts @@ -4,7 +4,8 @@ import path from 'path'; import RedirectsStoreBase from './RedirectsStoreBase'; import {parseJson, parseYaml} from '../../services/custom-redirects/redirect-config-parser'; import {getBackupRedirectsFilePath} from '../../services/custom-redirects/utils'; -import type {RedirectConfig, RedirectsStore} from '../../services/custom-redirects/types'; +import type {RedirectConfig} from '../../services/custom-redirects/redirect-config'; +import type {RedirectsStore} from '../../services/custom-redirects/redirects-service'; const YAML_FILENAME = 'redirects.yaml'; const JSON_FILENAME = 'redirects.json'; diff --git a/ghost/core/core/server/adapters/redirects/S3RedirectsStore.ts b/ghost/core/core/server/adapters/redirects/S3RedirectsStore.ts index 37e53f3c337..a0b4360fdf3 100644 --- a/ghost/core/core/server/adapters/redirects/S3RedirectsStore.ts +++ b/ghost/core/core/server/adapters/redirects/S3RedirectsStore.ts @@ -14,7 +14,8 @@ import * as errors from '@tryghost/errors'; import RedirectsStoreBase from './RedirectsStoreBase'; import {parseJson} from '../../services/custom-redirects/redirect-config-parser'; import {getBackupRedirectsFilePath} from '../../services/custom-redirects/utils'; -import type {RedirectConfig, RedirectsStore} from '../../services/custom-redirects/types'; +import type {RedirectConfig} from '../../services/custom-redirects/redirect-config'; +import type {RedirectsStore} from '../../services/custom-redirects/redirects-service'; const DEFAULT_FILENAME = 'redirects.json'; diff --git a/ghost/core/core/server/services/custom-redirects/redirect-config-parser.ts b/ghost/core/core/server/services/custom-redirects/redirect-config-parser.ts index 688ea556440..c4940032cf6 100644 --- a/ghost/core/core/server/services/custom-redirects/redirect-config-parser.ts +++ b/ghost/core/core/server/services/custom-redirects/redirect-config-parser.ts @@ -2,7 +2,7 @@ import yaml from 'js-yaml'; import tpl from '@tryghost/tpl'; import * as errors from '@tryghost/errors'; -import type {RedirectConfig} from './types'; +import type {RedirectConfig} from './redirect-config'; const messages = { jsonParse: 'Could not parse JSON: {context}.', diff --git a/ghost/core/core/server/services/custom-redirects/redirect-config.ts b/ghost/core/core/server/services/custom-redirects/redirect-config.ts new file mode 100644 index 00000000000..e706e8a82f4 --- /dev/null +++ b/ghost/core/core/server/services/custom-redirects/redirect-config.ts @@ -0,0 +1,7 @@ +export interface RedirectConfig { + from: string; + /** Capture groups from `from` can be referenced as `$1`, `$2`, etc. */ + to: string; + /** `true` → HTTP 301, otherwise HTTP 302. */ + permanent?: boolean; +} diff --git a/ghost/core/core/server/services/custom-redirects/redirects-service.ts b/ghost/core/core/server/services/custom-redirects/redirects-service.ts index b34bf5626da..50a542bdc88 100644 --- a/ghost/core/core/server/services/custom-redirects/redirects-service.ts +++ b/ghost/core/core/server/services/custom-redirects/redirects-service.ts @@ -3,7 +3,16 @@ import tpl from '@tryghost/tpl'; import * as errors from '@tryghost/errors'; import DynamicRedirectManager from '../lib/dynamic-redirect-manager'; -import type {RedirectConfig, RedirectsStore} from './types'; +import type {RedirectConfig} from './redirect-config'; + +/** + * Concurrent `replaceAll` calls have no ordering guarantee — serialize + * externally if that matters. + */ +export interface RedirectsStore { + getAll(): Promise; + replaceAll(redirects: RedirectConfig[]): Promise; +} const messages = { redirectsRegister: 'Could not register custom redirects.', diff --git a/ghost/core/core/server/services/custom-redirects/types.ts b/ghost/core/core/server/services/custom-redirects/types.ts deleted file mode 100644 index 98be0ebada2..00000000000 --- a/ghost/core/core/server/services/custom-redirects/types.ts +++ /dev/null @@ -1,16 +0,0 @@ -export interface RedirectConfig { - from: string; - /** Capture groups from `from` can be referenced as `$1`, `$2`, etc. */ - to: string; - /** `true` → HTTP 301, otherwise HTTP 302. */ - permanent?: boolean; -} - -/** - * Concurrent `replaceAll` calls have no ordering guarantee — serialize - * externally if that matters. - */ -export interface RedirectsStore { - getAll(): Promise; - replaceAll(redirects: RedirectConfig[]): Promise; -} diff --git a/ghost/core/core/server/services/custom-redirects/validation.js b/ghost/core/core/server/services/custom-redirects/validation.js index 61223d7cc00..9249c42b190 100644 --- a/ghost/core/core/server/services/custom-redirects/validation.js +++ b/ghost/core/core/server/services/custom-redirects/validation.js @@ -13,7 +13,7 @@ const messages = { * non-empty `from` / `to` strings, with `from` compilable as a RegExp. * Throws on the first failure rather than collecting errors. * - * @param {import('./types').RedirectConfig[]} redirects + * @param {import('./redirect-config').RedirectConfig[]} redirects */ const validate = (redirects) => { if (!_.isArray(redirects)) { diff --git a/ghost/core/test/unit/server/services/custom-redirects/helpers/in-memory-store.ts b/ghost/core/test/unit/server/services/custom-redirects/helpers/in-memory-store.ts index 56eee609875..b24635212de 100644 --- a/ghost/core/test/unit/server/services/custom-redirects/helpers/in-memory-store.ts +++ b/ghost/core/test/unit/server/services/custom-redirects/helpers/in-memory-store.ts @@ -1,4 +1,5 @@ -import type {RedirectConfig, RedirectsStore} from '../../../../../../core/server/services/custom-redirects/types'; +import type {RedirectConfig} from '../../../../../../core/server/services/custom-redirects/redirect-config'; +import type {RedirectsStore} from '../../../../../../core/server/services/custom-redirects/redirects-service'; export class InMemoryStore implements RedirectsStore { private redirects: RedirectConfig[] = []; diff --git a/ghost/core/test/unit/server/services/custom-redirects/helpers/store-contract.ts b/ghost/core/test/unit/server/services/custom-redirects/helpers/store-contract.ts index 1cfd3558f22..054bdd1dda2 100644 --- a/ghost/core/test/unit/server/services/custom-redirects/helpers/store-contract.ts +++ b/ghost/core/test/unit/server/services/custom-redirects/helpers/store-contract.ts @@ -1,6 +1,6 @@ import assert from 'node:assert/strict'; -import type {RedirectsStore} from '../../../../../../core/server/services/custom-redirects/types'; +import type {RedirectsStore} from '../../../../../../core/server/services/custom-redirects/redirects-service'; interface ContractOptions { createStore: () => RedirectsStore | Promise;