[pull] main from TryGhost:main - #1446
Merged
Merged
Conversation
ref https://linear.app/ghost/project/4b2edbd66469/ - Milestone 5 Each commit references an individual Linear issue - Skips duplicate posts and updates existing posts when imported data is newer - Matches posts by source ID or slug - Reconciles and creates authors and tags, with safe Owner fallbacks and warnings - Adds full coverage across all changed CSV importer modules
`db-date` held the one codec that turns a database value into something the domain can use, and a second was about to join it for booleans, where SQLite answers 0 or 1 and MySQL answers true or false. Two of them loose in lib is how lib became a drawer of unrelated things, so they sit in a folder that names what they are and gives the next one somewhere obvious to go. The boolean codec arrived without tests, and the interesting part of it is not that 0 and 1 become false and true but what it does with the rest of the range: a boolean column is a tinyint underneath, and both engines read every non-zero value in it as true, so the codec does too rather than failing a read over a value the database itself is happy with. ref https://linear.app/ghost/issue/BER-3872 Claude-Session: https://claude.ai/code/session_018jPEJMYLoUzNvYbdxWb3dY
ref https://linear.app/ghost/issue/BER-3872 Storage, validation, CSV columns, the import mapping, the member form and filters all read the parts a composite type declares, and the part labels fail the build until they name a new one. The one-line summary did not: it was written out per type, so a part left out of it was collected, stored, exported and filtered on while being invisible in every list cell and detail row. It now comes from declaration order, and the only thing stated by hand is which parts read as one run, so that a state and a postal code still read "NY 00001". That is typed against the parts each type declares, which makes a part added upstream appear on its own and a part renamed or removed upstream fail the build here rather than going quietly missing from what a publisher sees.
Stripe Checkout can be asked to collect a shipping address, a phone number or a tax number alongside the payment, and the fake server the end-to-end tests run against knew none of it. Anything built on top would have been tested against a server that accepted whatever it was sent, which is the opposite of what a fake is for. The rules it now enforces were measured against the live API at the version Ghost pins rather than read from the reference, which disagreed with the API in three of five probes, so a request the real Stripe would refuse now fails a test here first, including the one refusal that is easy to miss: a tax number cannot be collected for a customer Stripe may not rename. Modelling those rules turned up one of its own. Stripe's SDK form-encodes its request bodies, so a checkout that switched tax collection off arrives carrying the string "false" rather than the boolean, and reading that flag for truthiness refused a checkout that had asked for no tax number at all. The existing tests missed it because they post JSON, where the flag is a real boolean, so the refusal only appeared under the encoding Ghost actually uses. A captured fixture also carried the real street and postcode of a payment made while capturing it, and carries invented ones instead now that nothing asserts on their contents. ref https://linear.app/ghost/issue/BER-3872 Claude-Session: https://claude.ai/code/session_018jPEJMYLoUzNvYbdxWb3dY
ref https://linear.app/ghost/issue/BER-3890 The personalised gift purchase form has grown out of bounds. Splitting it into smaller components and porting to TS in preparation for reworking the state management.
Every value carries the writer that put it there: a type and an id, held per leaf path so the parts of a composite can name different writers, and merged with the value so a leaf names whoever wrote what it holds rather than whoever wrote its first. The type is the namespace its id resolves in — a user, an integration, or the binding that routes a value in from a checkout — which leaves an import as the one writer with no id to give, until import runs are tracked. The values service takes the writer as a required argument with no default, so no call site can omit one and inherit the identity of whichever wrote first, and a request carrying neither an authenticated user nor an integration is refused rather than credited to a writer nobody can resolve. The migration clears the values table rather than invent a writer for rows that carry none, which the feature flag makes safe. ref https://linear.app/ghost/issue/BER-3872 Claude-Session: https://claude.ai/code/session_018jPEJMYLoUzNvYbdxWb3dY
no ref This change should have no user impact. Four options were never used: `errorMessage`, `errorContext`, `errorHelp`, and `async`. We can remove them and simplify the function a bit.
no ref - Scope tier option lookup to the open command listbox. - Wait for each tier option to become visible before clicking it. - Match tier names exactly to avoid unrelated option matches. This removes the race between the asynchronous mocked tiers request and the React Query result rendering in the combobox.
no ref The shell (`routes.tsx`, `layout/`) deep-imported domain internals, and only analytics had a declared public surface. Every domain now exposes an `api.ts` mirroring `analytics/api.ts`, and dependency-cruiser keeps it that way.
closes https://linear.app/ghost/issue/NY-1515/find-a-way-to-catch-stripped-query-parameter-issue-in-development Local development and tests previously bypassed Ghost(Pro)'s query parameter filtering, allowing new parameters to work locally but fail after deployment. This makes Ghost the canonical source for the policy and applies it before requests reach Ghost's route handlers, so missing parameters are caught earlier. The production allowlist must still be updated separately until infrastructure consumes Ghost's exported policy. ## Summary - add a canonical, machine-readable policy containing documented public and Content API query parameters - validate the policy with Zod, including its schema version, required fields, valid names, and duplicate entries - filter frontend and Content API requests against their respective allowlists while preserving API and internal-route exemptions - enable filtering in the root development environment, Ghost's server-side test configurations, and Playwright test containers while leaving it disabled by default - preserve Express 4 and Express 5 query handling, repeated allowed parameters, and warning logs for stripped parameters - add a TypeScript exporter that validates and writes the policy deterministically for future infrastructure integration - cover policy validation, exporting, filtering, exemptions, and query parsing behavior with tests --------- Co-authored-by: Steve Larson <9larsons@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )