b8d7ccb4dc8b9b031c6be39bf8379de4bb38a0da
14032 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b8d7ccb4dc |
fix(ui): use consistent row ids when duplicating array and block rows (#13679)
Fixes #13653. Duplicating array rows causes phantom rows to appear. This is because when duplicate the row locally, we use inconsistent row IDs, e.g. the `array.rows[0].id` does not match its `array.0.id` counterpart. This causes form state to lose the reference to the existing row, which the server interprets as new row as of #13551. Before: https://github.com/user-attachments/assets/9f7efc59-ebd9-4fbb-b643-c22d4d3140a3 After: https://github.com/user-attachments/assets/188db823-4ee5-4757-8b89-751c8d978ad9 --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1211210023936585 |
||
|
|
be47f65b7c |
fix: prevent enabling trash on folders (#13675)
### What? This PR updates the folder configuration types to explicitly omit the `trash` option from `CollectionConfig` when used for folders. ### Why? Currently, only documents are designed to support trashing. Allowing folders to be trashed introduces inconsistencies, since removing a folder does not properly remove its references from associated documents. By omitting `trash` from folder configurations, we prevent accidental use of unsupported behavior until proper design and handling for folder trashing is implemented. ### How? - Updated `RootFoldersConfiguration.collectionOverrides` type to use `Omit<CollectionConfig, 'trash'>` - Ensures `trash` cannot be enabled on folders |
||
|
|
0f6d748365 |
feat: adds new experimental.localizeStatus option (#13207)
### What? Adds a new `experimental.localizeStatus` config option, set to `false` by default. When `true`, the admin panel will display the document status based on the *current locale* instead of the _latest_ overall status. Also updates the edit view to only show a `changed` status when `autosave` is enabled. ### Why? Showing the status for the current locale is more accurate and useful in multi-locale setups. This update will become default behavior, able to be opted in by setting `experimental.localizeStatus: true` in the Payload config. This option will become depreciated in V4. ### How? When `localizeStatus` is `true`, we store the localized status in a new `localeStatus` field group within version data. The admin panel then reads from this field to display the correct status for the current locale. --------- Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com> |
||
|
|
a11586811e |
fix(ui): field.admin.condition data attribute missing document ID when document is being edited (#13676)
Fixes https://github.com/payloadcms/payload/issues/10379 During form state requests, the passed `data` did not have access to the document ID. This was because the data we use came from the client, passed as an argument. The client did not pass data that included the document ID. --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1211203844178567 |
||
|
|
9b109339ee |
fix(ui): await for publish success to update the UI (#13673)
Fixes #13664 Before: https://github.com/user-attachments/assets/083577f5-9006-4b35-9799-3df704ed84a8 After: https://github.com/user-attachments/assets/70a89a5b-bed4-447c-94f6-57bf3dbd2a70 --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1211210023936582 |
||
|
|
4e972c3fe2 |
docs(plugin-form-builder): add RadioField documentation (#13529)
### What? Adds comprehensive documentation for the RadioField component in the form builder plugin documentation. ### Why? Addresses review feedback from #11908 noting that the RadioField was not documented after being exported for end-user use. ### How? - Added RadioField section with complete property documentation - Added detailed Radio Options sub-section explaining option structure - Updated Select field documentation for consistency (added missing placeholder property and detailed options structure) - Added radio field to configuration example to show all available fields Fixes the documentation gap identified in #11908 review comments. |
||
|
|
cfb70f06bb |
ci(deps): bump the github_actions group across 3 directories with 4 updates (#13654)
Bumps the github_actions group with 4 updates in the / directory: [actions/checkout](https://github.com/actions/checkout), [slackapi/slack-github-action](https://github.com/slackapi/slack-github-action), [SethCohen/github-releases-to-discord](https://github.com/sethcohen/github-releases-to-discord) and [amannn/action-semantic-pull-request](https://github.com/amannn/action-semantic-pull-request). Bumps the github_actions group with 1 update in the /.github/actions/triage directory: [actions/checkout](https://github.com/actions/checkout). Bumps the github_actions group with 4 updates in the /.github/workflows directory: [actions/checkout](https://github.com/actions/checkout), [slackapi/slack-github-action](https://github.com/slackapi/slack-github-action), [SethCohen/github-releases-to-discord](https://github.com/sethcohen/github-releases-to-discord) and [amannn/action-semantic-pull-request](https://github.com/amannn/action-semantic-pull-request). Updates `actions/checkout` from 4 to 5 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/actions/checkout/releases">actions/checkout's releases</a>.</em></p> <blockquote> <h2>v5.0.0</h2> <h2>What's Changed</h2> <ul> <li>Update actions checkout to use node 24 by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2226">actions/checkout#2226</a></li> <li>Prepare v5.0.0 release by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2238">actions/checkout#2238</a></li> </ul> <h2>⚠️ Minimum Compatible Runner Version</h2> <p><strong>v2.327.1</strong><br /> <a href="https://github.com/actions/runner/releases/tag/v2.327.1">Release Notes</a></p> <p>Make sure your runner is updated to this version or newer to use this release.</p> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v4...v5.0.0">https://github.com/actions/checkout/compare/v4...v5.0.0</a></p> <h2>v4.3.0</h2> <h2>What's Changed</h2> <ul> <li>docs: update README.md by <a href="https://github.com/motss"><code>@motss</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1971">actions/checkout#1971</a></li> <li>Add internal repos for checking out multiple repositories by <a href="https://github.com/mouismail"><code>@mouismail</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1977">actions/checkout#1977</a></li> <li>Documentation update - add recommended permissions to Readme by <a href="https://github.com/benwells"><code>@benwells</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2043">actions/checkout#2043</a></li> <li>Adjust positioning of user email note and permissions heading by <a href="https://github.com/joshmgross"><code>@joshmgross</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2044">actions/checkout#2044</a></li> <li>Update README.md by <a href="https://github.com/nebuk89"><code>@nebuk89</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2194">actions/checkout#2194</a></li> <li>Update CODEOWNERS for actions by <a href="https://github.com/TingluoHuang"><code>@TingluoHuang</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2224">actions/checkout#2224</a></li> <li>Update package dependencies by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2236">actions/checkout#2236</a></li> <li>Prepare release v4.3.0 by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2237">actions/checkout#2237</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/motss"><code>@motss</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1971">actions/checkout#1971</a></li> <li><a href="https://github.com/mouismail"><code>@mouismail</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1977">actions/checkout#1977</a></li> <li><a href="https://github.com/benwells"><code>@benwells</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/2043">actions/checkout#2043</a></li> <li><a href="https://github.com/nebuk89"><code>@nebuk89</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/2194">actions/checkout#2194</a></li> <li><a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/2236">actions/checkout#2236</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v4...v4.3.0">https://github.com/actions/checkout/compare/v4...v4.3.0</a></p> <h2>v4.2.2</h2> <h2>What's Changed</h2> <ul> <li><code>url-helper.ts</code> now leverages well-known environment variables by <a href="https://github.com/jww3"><code>@jww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1941">actions/checkout#1941</a></li> <li>Expand unit test coverage for <code>isGhes</code> by <a href="https://github.com/jww3"><code>@jww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1946">actions/checkout#1946</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v4.2.1...v4.2.2">https://github.com/actions/checkout/compare/v4.2.1...v4.2.2</a></p> <h2>v4.2.1</h2> <h2>What's Changed</h2> <ul> <li>Check out other refs/* by commit if provided, fall back to ref by <a href="https://github.com/orhantoy"><code>@orhantoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1924">actions/checkout#1924</a></li> </ul> <h2>New Contributors</h2> <ul> <li><a href="https://github.com/Jcambass"><code>@Jcambass</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1919">actions/checkout#1919</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v4.2.0...v4.2.1">https://github.com/actions/checkout/compare/v4.2.0...v4.2.1</a></p> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/actions/checkout/blob/main/CHANGELOG.md">actions/checkout's changelog</a>.</em></p> <blockquote> <h1>Changelog</h1> <h2>V5.0.0</h2> <ul> <li>Update actions checkout to use node 24 by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2226">actions/checkout#2226</a></li> </ul> <h2>V4.3.0</h2> <ul> <li>docs: update README.md by <a href="https://github.com/motss"><code>@motss</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1971">actions/checkout#1971</a></li> <li>Add internal repos for checking out multiple repositories by <a href="https://github.com/mouismail"><code>@mouismail</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1977">actions/checkout#1977</a></li> <li>Documentation update - add recommended permissions to Readme by <a href="https://github.com/benwells"><code>@benwells</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2043">actions/checkout#2043</a></li> <li>Adjust positioning of user email note and permissions heading by <a href="https://github.com/joshmgross"><code>@joshmgross</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2044">actions/checkout#2044</a></li> <li>Update README.md by <a href="https://github.com/nebuk89"><code>@nebuk89</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2194">actions/checkout#2194</a></li> <li>Update CODEOWNERS for actions by <a href="https://github.com/TingluoHuang"><code>@TingluoHuang</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2224">actions/checkout#2224</a></li> <li>Update package dependencies by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2236">actions/checkout#2236</a></li> </ul> <h2>v4.2.2</h2> <ul> <li><code>url-helper.ts</code> now leverages well-known environment variables by <a href="https://github.com/jww3"><code>@jww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1941">actions/checkout#1941</a></li> <li>Expand unit test coverage for <code>isGhes</code> by <a href="https://github.com/jww3"><code>@jww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1946">actions/checkout#1946</a></li> </ul> <h2>v4.2.1</h2> <ul> <li>Check out other refs/* by commit if provided, fall back to ref by <a href="https://github.com/orhantoy"><code>@orhantoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1924">actions/checkout#1924</a></li> </ul> <h2>v4.2.0</h2> <ul> <li>Add Ref and Commit outputs by <a href="https://github.com/lucacome"><code>@lucacome</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1180">actions/checkout#1180</a></li> <li>Dependency updates by <a href="https://github.com/dependabot"><code>@dependabot</code></a>- <a href="https://redirect.github.com/actions/checkout/pull/1777">actions/checkout#1777</a>, <a href="https://redirect.github.com/actions/checkout/pull/1872">actions/checkout#1872</a></li> </ul> <h2>v4.1.7</h2> <ul> <li>Bump the minor-npm-dependencies group across 1 directory with 4 updates by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1739">actions/checkout#1739</a></li> <li>Bump actions/checkout from 3 to 4 by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1697">actions/checkout#1697</a></li> <li>Check out other refs/* by commit by <a href="https://github.com/orhantoy"><code>@orhantoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1774">actions/checkout#1774</a></li> <li>Pin actions/checkout's own workflows to a known, good, stable version. by <a href="https://github.com/jww3"><code>@jww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1776">actions/checkout#1776</a></li> </ul> <h2>v4.1.6</h2> <ul> <li>Check platform to set archive extension appropriately by <a href="https://github.com/cory-miller"><code>@cory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1732">actions/checkout#1732</a></li> </ul> <h2>v4.1.5</h2> <ul> <li>Update NPM dependencies by <a href="https://github.com/cory-miller"><code>@cory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1703">actions/checkout#1703</a></li> <li>Bump github/codeql-action from 2 to 3 by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1694">actions/checkout#1694</a></li> <li>Bump actions/setup-node from 1 to 4 by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1696">actions/checkout#1696</a></li> <li>Bump actions/upload-artifact from 2 to 4 by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1695">actions/checkout#1695</a></li> <li>README: Suggest <code>user.email</code> to be <code>41898282+github-actions[bot]@users.noreply.github.com</code> by <a href="https://github.com/cory-miller"><code>@cory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1707">actions/checkout#1707</a></li> </ul> <h2>v4.1.4</h2> <ul> <li>Disable <code>extensions.worktreeConfig</code> when disabling <code>sparse-checkout</code> by <a href="https://github.com/jww3"><code>@jww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1692">actions/checkout#1692</a></li> <li>Add dependabot config by <a href="https://github.com/cory-miller"><code>@cory-miller</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1688">actions/checkout#1688</a></li> <li>Bump the minor-actions-dependencies group with 2 updates by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1693">actions/checkout#1693</a></li> <li>Bump word-wrap from 1.2.3 to 1.2.5 by <a href="https://github.com/dependabot"><code>@dependabot</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1643">actions/checkout#1643</a></li> </ul> <h2>v4.1.3</h2> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href=" |
||
|
|
1c68ed5251 |
fix(ui): sidebar missing sticky top offset (#13652)
Regression from https://github.com/payloadcms/payload/pull/13340. Incorrect fallback value for the `--sidebar-wrap-top` css variable was used. ### Before <img width="2896" height="1200" alt="CleanShot 2025-09-01 at 10 03 39@2x" src="https://github.com/user-attachments/assets/caf054d2-ac61-4a96-b6a1-3981ce9f528f" /> ### After <img width="2894" height="1194" alt="CleanShot 2025-09-01 at 10 02 59@2x" src="https://github.com/user-attachments/assets/1e13e084-6f62-47bb-88b3-921ba5f3ff10" /> |
||
|
|
65b3845d92 |
fix(plugin-multi-tenant): skip baseFilter if user has access to all tenants (#13633)
Fixes https://github.com/payloadcms/payload/issues/13589#issuecomment-3234832478 ### Problem The baseFilter is being applied when a user has no tenant selected, is assigned to tenants BUT also passes the `userHasAccessToAllTenants` check. ### Fix Do not apply the baseFilter when no tenant is selected and the user passes the `userHasAccessToAllTenants` check. |
||
|
|
917c66fe44 |
fix(db-sqlite): convert Date to ISO 8601 string in queries (#11694)
### What? Restores the ability to query by `Date(...)` when using the `sqlite` adapter. ### Why? Queries involving JavaScript `Date`s return incorrect results because they are not converted to ISO 8601 strings by the `sqlite` adapter. ### How? Wraps comparison operators to convert `Date` parameters to ISO 8601 strings. Fixes #11692 --------- Co-authored-by: German Jablonski <43938777+GermanJablo@users.noreply.github.com> |
||
|
|
ac691b675b |
fix(ui): should not show publish specific locale button when no localized fields exist (#13459)
### What? Hides the `Publish in [specific locale]` button on collections and globals when no localized fields are present. ### Why? Currently, the `Publish in [specific locale]` shows on every collection/global as long as `localization` is enabled in the Payload config, however this is not necessary when the collection/global has no localized fields. ### How? Checks that localized fields exist prior to rendering the `Publish in [specific locale]` button. Fixes #13447 --------- Co-authored-by: German Jablonski <43938777+GermanJablo@users.noreply.github.com> |
||
|
|
fdab2712c0 |
feat(richtext-lexical): add options to hide block handles (#13647)
### What? Currently the `DraggableBlockPlugin` and `AddBlockHandlePlugin` components are automatically applied to every editor. For flexibility purposes, we want to allow these to be optionally removed when needed. ### Why? There are scenarios where you may want to enforce certain limitations on an editor, such as only allowing a single line of text. The draggable block element and add block button wouldn't play nicely with this scenario. Previously in order to do this, you needed to use custom css to hide the elements, which still technically allows them to be accessible to the end-user if they removed the CSS. This implementation ensures the handlers are properly removed when not wanted. ### How? Add `hideDraggableBlockElement` and `hideAddBlockButton` options to the lexical `admin` property. When these are set to `true`, the `DraggableBlockPlugin` and `AddBlockHandlePlugin` are not rendered to the DOM. Addresses #13636 |
||
|
|
a231a05b7c |
fix(plugin-nested-docs): prevent phantom breadcrumb row (#13628)
When saving a doc and regenerating the breadcrumbs array, a phantom row will append itself to the end of the array on save. This is because of fixes made in #13551 changed the way we merge array and block rows from the server. To fix this we need to ensure that row IDs are consistent across form state invocations, i.e. the hooks that mutate the array rows _cannot_ discard the row IDs. Before: https://github.com/user-attachments/assets/db715801-b4fd-4114-b39b-8d9b37fad979 After: https://github.com/user-attachments/assets/6da63a31-cd5d-43c1-a15e-caddbc540d56 --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1211175452200168 |
||
|
|
b99c324f1e |
perf(plugin-search): reindex collections in parallel, up to 80% faster (#13608)
Reindexing all collections in the search plugin was previously done in sequence which is slow and risks timing out under certain network conditions. By running these requests in parallel we are able to save **on average ~80%** in compute time. This test includes reindexing 2000 documents in total across 2 collections, 3 times over. The indexes themselves are relatively simple, containing only a couple simple fields each, so the savings would only increase with more complexity and/or more documents. Before: Attempt 1: 38434.87ms Attempt 2: 47852.61ms Attempt 3: 28407.79ms Avg: 38231.75ms After: Attempt 1: 7834.29ms Attempt 2: 7744.40ms Attempt 3: 7918.58ms Avg: 7832.42ms Total savings: ~79.51% --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1211162504205343 |
||
|
|
426f99ca99 |
fix(ui): json field type ignoring editorOptions (#13630)
### What? Fix `JSON` field so that it respects `admin.editorOptions` (e.g. `tabSize`, `insertSpaces`, etc.), matching the behavior of the `code` field. Also refactor `CodeEditor` to set indentation and whitespace options per-model instead of globally. ### Why? - Previously, the JSON field ignored `editorOptions` and always serialized with spaces (`tabSize: 2`). This caused inconsistencies when comparing JSON and code fields configured with the same options. - Monaco’s global defaults were being overridden in a way that leaked settings between editors, making per-field customization unreliable. ### How? - Updated `JSON` field to extract `tabSize` from `editorOptions` and pass it through consistently when serializing and mounting the editor. - Refactored CodeEditor to: - Disable `detectIndentation` globally. - Apply `insertSpaces`, `tabSize`, and `trimAutoWhitespace` on a per-model basis inside onMount. - Preserve all other `editorOptions` as before. Fixes #13583 --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1211177100283503 --------- Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com> |
||
|
|
4600c94cac |
fix(plugin-nested-docs): crumbs not syncing on non-versioned collections (#13629)
Fixes https://github.com/payloadcms/payload/issues/13563 When using the nested docs plugin with collections that do not have drafts enabled. It was not syncing breadcrumbs from parent changes. The root cause was returning early on any document that did not meet `doc._status !== 'published'` check, which was **_always_** the case for non-draft collections. ### Before ```ts if (doc._status !== 'published') { return } ``` ### After ```ts if (collection.versions.drafts && doc._status !== 'published') { return } ``` |
||
|
|
c1b4960795 | chore(release): v3.54.0 [skip ci] v3.54.0 | ||
|
|
97c41ce0c5 |
refactor: deprecate job queue runHooks property (#13617)
Setting `runHooks: true` is already discouraged and will make the job queue system a lot slower and less reliable. We have no test coverage for this and it's additional code we need to maintain. To further discourage using this property, this PR marks it as deprecated and for removal in 4.0. |
||
|
|
e0ffada80b |
feat: support parallel job queue tasks, speed up task running (#13614)
Currently, attempting to run tasks in parallel will result in DB errors. ## Solution The problem was caused due to inefficient db update calls. After each task completes, we need to update the log array in the payload-jobs collection. On postgres, that's a different table. Currently, the update works the following way: 1. Nuke the table 2. Re-insert every single row, including the new one This will throw db errors if multiple processes start doing that. Additionally, due to conflicts, new log rows may be lost. This PR makes use of the the [new db $push operation ](https://github.com/payloadcms/payload/pull/13453) we recently added to atomically push a new log row to the database in a single round-trip. This not only reduces the amount of db round trips (=> faster job queue system) but allows multiple tasks to perform this db operation in parallel, without conflicts. ## Problem **Example:** ```ts export const fastParallelTaskWorkflow: WorkflowConfig<'fastParallelTask'> = { slug: 'fastParallelTask', handler: async ({nlineTask }) => { const taskFunctions = [] for (let i = 0; i < 20; i++) { const idx = i + 1 taskFunctions.push(async () => { return await inlineTask(`parallel task ${idx}`, { input: { test: idx, }, task: () => { return { output: { taskID: idx.toString(), }, } }, }) }) } await Promise.all(taskFunctions.map((f) => f())) }, } ``` On SQLite, this would throw the following error: ```bash Caught error Error: UNIQUE constraint failed: payload_jobs_log.id at Object.next (/Users/alessio/Documents/GitHub/payload/node_modules/.pnpm/libsql@0.4.7/node_modules/libsql/index.js:335:20) at Statement.all (/Users/alessio/Documents/GitHub/payload/node_modules/.pnpm/libsql@0.4.7/node_modules/libsql/index.js:360:16) at executeStmt (/Users/alessio/Documents/GitHub/payload/node_modules/.pnpm/@libsql+client@0.14.0_bufferutil@4.0.8_utf-8-validate@6.0.5/node_modules/@libsql/client/lib-cjs/sqlite3.js:285:34) at Sqlite3Client.execute (/Users/alessio/Documents/GitHub/payload/node_modules/.pnpm/@libsql+client@0.14.0_bufferutil@4.0.8_utf-8-validate@6.0.5/node_modules/@libsql/client/lib-cjs/sqlite3.js:101:16) at /Users/alessio/Documents/GitHub/payload/node_modules/.pnpm/drizzle-orm@0.44.2_@libsql+client@0.14.0_bufferutil@4.0.8_utf-8-validate@6.0.5__@opentelemetr_asjmtflojkxlnxrshoh4fj5f6u/node_modules/src/libsql/session.ts:288:58 at LibSQLPreparedQuery.queryWithCache (/Users/alessio/Documents/GitHub/payload/node_modules/.pnpm/drizzle-orm@0.44.2_@libsql+client@0.14.0_bufferutil@4.0.8_utf-8-validate@6.0.5__@opentelemetr_asjmtflojkxlnxrshoh4fj5f6u/node_modules/src/sqlite-core/session.ts:79:18) at LibSQLPreparedQuery.values (/Users/alessio/Documents/GitHub/payload/node_modules/.pnpm/drizzle-orm@0.44.2_@libsql+client@0.14.0_bufferutil@4.0.8_utf-8-validate@6.0.5__@opentelemetr_asjmtflojkxlnxrshoh4fj5f6u/node_modules/src/libsql/session.ts:286:21) at LibSQLPreparedQuery.all (/Users/alessio/Documents/GitHub/payload/node_modules/.pnpm/drizzle-orm@0.44.2_@libsql+client@0.14.0_bufferutil@4.0.8_utf-8-validate@6.0.5__@opentelemetr_asjmtflojkxlnxrshoh4fj5f6u/node_modules/src/libsql/session.ts:214:27) at QueryPromise.all (/Users/alessio/Documents/GitHub/payload/node_modules/.pnpm/drizzle-orm@0.44.2_@libsql+client@0.14.0_bufferutil@4.0.8_utf-8-validate@6.0.5__@opentelemetr_asjmtflojkxlnxrshoh4fj5f6u/node_modules/src/sqlite-core/query-builders/insert.ts:402:26) at QueryPromise.execute (/Users/alessio/Documents/GitHub/payload/node_modules/.pnpm/drizzle-orm@0.44.2_@libsql+client@0.14.0_bufferutil@4.0.8_utf-8-validate@6.0.5__@opentelemetr_asjmtflojkxlnxrshoh4fj5f6u/node_modules/src/sqlite-core/query-builders/insert.ts:414:40) at QueryPromise.then (/Users/alessio/Documents/GitHub/payload/node_modules/.pnpm/drizzle-orm@0.44.2_@libsql+client@0.14.0_bufferutil@4.0.8_utf-8-validate@6.0.5__@opentelemetr_asjmtflojkxlnxrshoh4fj5f6u/node_modules/src/query-promise.ts:31:15) { rawCode: 1555, code: 'SQLITE_CONSTRAINT_PRIMARYKEY', libsqlError: true } ``` --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1211001438499053 |
||
|
|
303381e049 |
fix(ui): prevent infinite redirect if no user (#13615)
Conditionally send the user to the inactivity route if there was a user but refresh failed. You can get into an infinite loop if you call this function externally and a redirect is being used in the url. |
||
|
|
0a18306599 |
feat: configurable toast notifications (#13609)
Follow up to #12119. You can now configure the toast notifications used in the admin panel through the Payload config: ```ts import { buildConfig } from 'payload' export default buildConfig({ // ... admin: { // ... toast: { duration: 8000, limit: 1, // ... } } }) ``` _Note: the toast config is temporarily labeled as experimental to allow for changes to the API, if necessary._ --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1211139422639711 |
||
|
|
5ded64eaaf |
feat(db-*): support atomic array $push db updates (#13453)
This PR adds **atomic** `$push` **support for array fields**. It makes it possible to safely append new items to arrays, which is especially useful when running tasks in parallel (like job queues) where multiple processes might update the same record at the same time. By handling pushes atomically, we avoid race conditions and keep data consistent - especially on postgres, where the current implementation would nuke the entire array table before re-inserting every single array item. The feature works for both localized and unlocalized arrays, and supports pushing either single or multiple items at once. This PR is a requirement for reliably running parallel tasks in the job queue - see https://github.com/payloadcms/payload/pull/13452. Alongside documenting `$push`, this PR also adds documentation for `$inc`. ## Changes to updatedAt behavior https://github.com/payloadcms/payload/pull/13335 allows us to override the updatedAt property instead of the db always setting it to the current date. However, we are not able to skip updating the updatedAt property completely. This means, usage of $push results in 2 postgres db calls: 1. set updatedAt in main row 2. append array row in arrays table This PR changes the behavior to only automatically set updatedAt if it's undefined. If you explicitly set it to `null`, this now allows you to skip the db adapter automatically setting updatedAt. => This allows us to use $push in just one single db call ## Usage Examples ### Pushing a single item to an array ```ts const post = (await payload.db.updateOne({ data: { array: { $push: { text: 'some text 2', id: new mongoose.Types.ObjectId().toHexString(), }, }, }, collection: 'posts', id: post.id, })) ``` ### Pushing a single item to a localized array ```ts const post = (await payload.db.updateOne({ data: { arrayLocalized: { $push: { en: { text: 'some text 2', id: new mongoose.Types.ObjectId().toHexString(), }, es: { text: 'some text 2 es', id: new mongoose.Types.ObjectId().toHexString(), }, }, }, }, collection: 'posts', id: post.id, })) ``` ### Pushing multiple items to an array ```ts const post = (await payload.db.updateOne({ data: { array: { $push: [ { text: 'some text 2', id: new mongoose.Types.ObjectId().toHexString(), }, { text: 'some text 3', id: new mongoose.Types.ObjectId().toHexString(), }, ], }, }, collection: 'posts', id: post.id, })) ``` ### Pushing multiple items to a localized array ```ts const post = (await payload.db.updateOne({ data: { arrayLocalized: { $push: { en: { text: 'some text 2', id: new mongoose.Types.ObjectId().toHexString(), }, es: [ { text: 'some text 2 es', id: new mongoose.Types.ObjectId().toHexString(), }, { text: 'some text 3 es', id: new mongoose.Types.ObjectId().toHexString(), }, ], }, }, }, collection: 'posts', id: post.id, })) ``` --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1211110462564647 |
||
|
|
a67043854e |
fix: restore as draft function was passing a published status (#13599)
### What When using the `Restore as draft` action from the version view, the restored document incorrectly appears as `published` in the API. This issue was reported by a client. ### Why In the `restoreVersion` operation, the document is updated via `db.updateOne` (line 265). The update passes along the status stored in the version being restored but does not respect the `draft` query parameter. ### How Ensures that the result status is explicitly set to `draft` when the `draft` argument is `true`. |
||
|
|
ded8ec4117 |
docs: adds default populate video (#13586)
Uses the VideoDrawer block to link relevant YouTube video at the bottom of defaultPopulate section |
||
|
|
5c41966a32 |
ci: remove run-e2e-turbo flow (#13611)
Removing `run-e2e-turbo` flow as it was unused. Can re-introduce or make this the default in future if needed. |
||
|
|
6db07f0c03 | feat(plugin-multi-tenant): allow custom tenant field per collection (#13553) | ||
|
|
13c24afa63 |
feat: allow multiple, different payload instances using getPayload in same process (#13603)
Fixes https://github.com/payloadcms/payload/issues/13433. Testing release: `3.54.0-internal.90cf7d5` Previously, when calling `getPayload`, you would always use the same, cached payload instance within a single process, regardless of the arguments passed to the `getPayload` function. This resulted in the following issues - both are fixed by this PR: - If, in your frontend you're calling `getPayload` without `cron: true`, and you're hosting the Payload Admin Panel in the same process, crons will not be enabled even if you visit the admin panel which calls `getPayload` with `cron: true`. This will break jobs autorun depending on which page you visit first - admin panel or frontend - Within the same process, you are unable to use `getPayload` twice for different instances of payload with different Payload Configs. On postgres, you can get around this by manually calling new `BasePayload()` which skips the cache. This did not work on mongoose though, as mongoose was caching the models on a global singleton (this PR addresses this). In order to bust the cache for different Payload Config, this PR introduces a new, optional `key` property to `getPayload`. ## Mongoose - disable using global singleton This PR refactors the Payload Mongoose adapter to stop relying on the global mongoose singleton. Instead, each adapter instance now creates and manages its own scoped Connection object. ### Motivation Previously, calling `getPayload()` more than once in the same process would throw `Cannot overwrite model` errors because models were compiled into the global singleton. This prevented running multiple Payload instances side-by-side, even when pointing at different databases. ### Changes - Replace usage of `mongoose.connect()` / `mongoose.model()` with instance-scoped `createConnection()` and `connection.model()`. - Ensure models, globals, and versions are compiled per connection, not globally. - Added proper `close()` handling on `this.connection` instead of `mongoose.disconnect()`. --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1211114366468745 |
||
|
|
03a00ca37d |
fix(plugin-multi-tenant): prevent duplicate filters on referenced blocks (#13607)
Fixes https://github.com/payloadcms/payload/issues/13601 When using block references, tenant filter options were being applied n+1 every time a block reference was used. |
||
|
|
138938ec55 |
fix(ui): bulk edit overwriting fields within named tabs (#13600)
Fixes https://github.com/payloadcms/payload/issues/13429 Having a config like the following would remove data from the nested tabs array field when bulk editing. ```ts { type: 'tabs', tabs: [ { label: 'Tabs Tabs Array', fields: [ { type: 'tabs', tabs: [ { name: 'tabTab', fields: [ { name: 'tabTabArray', type: 'array', fields: [ { name: 'tabTabArrayText', type: 'text', } ] } ] } ] } ] } ] } ``` |
||
|
|
1dc346af04 |
fix(graphql): invalid enum names when values include brackets (#13597)
### What? Brackets (`[ ]`) in option values end up in GraphQL enum names via `formatName`, causing schema generation to fail. This PR adds a single rule to `formatName`: - replace `[` and `]` with `_` ### Why? Using `_` (instead of removing the brackets) is safer and more consistent: - Avoid collisions: removal can merge distinct strings (`"A[B]"` → `"AB"`). `_` keeps them distinct (`"A_B"`). - **Consistency**: `formatName` already maps punctuation to `_` (`. - / + , ( ) '`). Brackets follow the same rule. Readability: `mb-[150px]` → `mb__150px_` is clearer than `mb150px`. Digits/units safety: removal can jam characters (`w-[2/3]` → `w23`); `_` avoids that (`w_2_3_`). ### How? Update formatName to include a bracket replacement step: ``` .replace(/\[|\]/g, '_') ``` No other call sites or value semantics change; only names containing brackets are affected. Fixes #13466 --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1211141396953194 |
||
|
|
bd81936ad4 |
fix(ui): autosave in document drawer overwrites local changes (#13587)
Fixes #13574. When editing an autosave-enabled document within a document drawer, any changes made will while the autosave is processing are ultimately discarded from local form state. This makes is difficult or even impossible to edit fields. This is because we server-render, then replace, the entire document on every save. This includes form state, which is stale because it was rendered while new changes were still being made. We don't need to re-render the entire view on every save, though, only on create. We don't do this on the top-level edit view, for example. Instead, we only need to replace form state. This change is also a performance improvement because we are no longer rendering all components unnecessarily, especially on every autosave interval. Before: https://github.com/user-attachments/assets/e9c221bf-4800-4153-af55-8b82e93b3c26 After: https://github.com/user-attachments/assets/d77ef2f3-b98b-41d6-ba6c-b502b9bb99cc Note: ignore the flashing autosave status and doc controls. This is horrible and we're actively fixing it, but is outside the scope of this PR. --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1211139422639700 |
||
|
|
cf37433667 |
fix(ui): multiple logout requests being made in parallel (#13595)
Fixes https://github.com/payloadcms/payload/issues/13565 The logout operation was running twice and causing a race condition on user updates. This change ensures the logout operation only runs 1 time. Really this view should have 1 purpose and that is to show the inactivity view. Currently it has 2 purposes which is why it needs the useEffect — the root of this issue. Instead we should just call the `logOut` function from the logout button instead of it linking to a logout page. |
||
|
|
344ad69572 |
fix(next): richtext fields not read-only for locked documents (#13579)
### What?
Ensure the Rich Text field is read-only when viewing a locked document.
### Why?
When opening a locked doc in read-only mode, the Rich Text field could
still appear editable. This is inconsistent with other fields and allows
users to try typing into a locked document.
### How?
- Pass `readOnly={isTrashedDoc || isLocked}` into `buildFormState` in
the edit view renderer.
|
||
|
|
f260d0ab49 |
feat(plugin-multi-tenant): re-enable global selector on all views (#13575)
Fixes #13559 Re-enable the global tenant selector on all views. In the last release the global tenant filter was only enabled on tenant enabled collection list views. This change allows the global tenant filter to be selected on all non-document views. This is useful on custom views or custom components on views that may not be tenant-enabled. |
||
|
|
45c3be25b4 |
docs: blank-template-url-fix (#13580)
- encodes URL in Installation section so link renders properly - Fixes #13403 |
||
|
|
a92c251620 |
fix: hide jobs stats global by default (#13566)
The jobs stats global is used internally to get the scheduling system to work. Currently, if scheduling is enabled, the "Payload Jobs Stats" global is visible in the Payload Admin Panel: <img width="524" height="252" alt="Screenshot 2025-08-23 at 00 10 13@2x" src="https://github.com/user-attachments/assets/91702c93-4b58-4990-922b-8241ea9aa00e" /> Similarly to how we do it in the Payload Jobs collection, this PR hides the payload jobs stats global from the admin panel by default --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1211124797199081 |
||
|
|
810184269d |
fix(ui): auth-fields container renders despite no visible auth/API key/verify content (#13554)
### What? Prevents the Auth component from rendering an empty `.auth-fields` wrapper. ### Why? When `disableLocalStrategy` is true and `enableFields` is false, but `useAPIKey` is true while read access to API key fields is denied, the component still rendered the parent wrapper with a background—showing a blank box. ### How? Introduce `hasVisibleContent`: - `showAuthBlock = enableFields` - `showAPIKeyBlock = useAPIKey && canReadApiKey` - `showVerifyBlock = verify && isEditing` If none are true, return `null`. (`disableLocalStrategy` is already accounted for via `enableFields`.) Fixes #12089 --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1211117270523574 |
||
|
|
1e13474068 |
fix: deeply merge array and block rows from server form state (#13551)
Continuation of https://github.com/payloadcms/payload/pull/13501. When merging server form state with `acceptValues: true`, like on submit (not autosave), rows are not deeply merged causing custom row components, like row labels, to disappear. This is because we never attach components to the form state response unless it has re-rendered server-side, so unless we merge these rows with the current state, we lose them. Instead of allowing `acceptValues` to override all local changes to rows, we need to flag any newly added rows with `addedByServer` so they can bypass the merge strategy. Existing rows would continue to be merged as expected, and new rows are simply appended to the end. Discovered here: https://discord.com/channels/967097582721572934/967097582721572937/1408367321797365840 --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1211115023863814 |
||
|
|
598ff4cf7b | fix(ui): correctly pass query params to locked document requests (#10802) | ||
|
|
409dd56f90 |
fix(plugin-multi-tenant): autosave global documents not rendering (#13552)
Fixes https://github.com/payloadcms/payload/issues/13507 When enabling autosave on global multi-tenant documents, the page would not always render - more noticeably with smaller autosave intervals. |
||
|
|
5c16443431 |
fix: ensure updates to createdAt and updatedAt are respected (#13335)
Previously, when manually setting `createdAt` or `updatedAt` in a
`payload.db.*` or `payload.*` operation, the value may have been
ignored. In some cases it was _impossible_ to change the `updatedAt`
value, even when using direct db adapter calls. On top of that, this
behavior sometimes differed between db adapters. For example, mongodb
did accept `updatedAt` when calling `payload.db.updateVersion` -
postgres ignored it.
This PR changes this behavior to consistently respect `createdAt` and
`updatedAt` values for `payload.db.*` operations.
For `payload.*` operations, this also works with the following
exception:
- update operations do no respect `updatedAt`, as updates are commonly
performed by spreading the old data, e.g. `payload.update({ data:
{...oldData} })` - in these cases, we usually still want the `updatedAt`
to be updated. If you need to get around this, you can use the
`payload.db.updateOne` operation instead.
---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
- https://app.asana.com/0/0/1210919646303994
|
||
|
|
3408d7bdcd |
docs: fix small typo in documentation (#13545)
<!-- Thank you for the PR! Please go through the checklist below and make sure you've completed all the steps. Please review the [CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md) document in this repository if you haven't already. The following items will ensure that your PR is handled as smoothly as possible: - PR Title must follow conventional commits format. For example, `feat: my new feature`, `fix(plugin-seo): my fix`. - Minimal description explained as if explained to someone not immediately familiar with the code. - Provide before/after screenshots or code diffs if applicable. - Link any related issues/discussions from GitHub or Discord. - Add review comments if necessary to explain to the reviewer the logic behind a change --> ### What? Fixes a tiny typo in the documentation. <!-- ### Why? ### How? Fixes # --> Co-authored-by: lstellway <lstellway@users.noreply.github.com> |
||
|
|
3bc1d0895f |
fix(richtext-lexical): toolbar dropdown items are disabled when selecting via double-click (#13544)
Fixes https://github.com/payloadcms/payload/issues/13275 by ensuring that toolbar styles are updated on mount. This PR also improves the lexical test suite by adding data attributes that can be targeted more easily --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1211110462564657 |
||
|
|
ddb8ca4de2 |
fix(db-*): add delete version id for non-mongodb (#10613)
Fixes #10526 ### What? `updateVersion` throws error at deleting before version of draft. ### Why? When triggers `db.updateVersion` with non-mongodb environment, `saveVersion` in `payload/src/versions/saveVersions.ts` doesn't remove `id` in `versionData`. ### How? Add `delete versionData.id` for non-mongodb environments. --------- Co-authored-by: German Jablonski <43938777+GermanJablo@users.noreply.github.com> |
||
|
|
763cb61964 |
fix(next): update rest route handler types for Next.js 15.5 compatibility (#13521)
Fixes #13527. When upgrading to [Next.js 15.5](https://nextjs.org/blog/next-15-5) with Payload, you might experience a runtime or build error similar to this: ```ts Type error: Type 'typeof import("/src/app/(payload)/api/graphql/route")' does not satisfy the expected type 'RouteHandlerConfig<"/api/graphql">'. Types of property 'OPTIONS' are incompatible. Type '(request: Request, args: { params: Promise<{ slug: string[]; }>; }) => Promise<Response>' is not assignable to type '(request: NextRequest, context: { params: Promise<{}>; }) => void | Promise<void> | Response | Promise<Response>'. Types of parameters 'args' and 'context' are incompatible. Type '{ params: Promise<{}>; }' is not assignable to type '{ params: Promise<{ slug: string[]; }>; }'. Types of property 'params' are incompatible. Type 'Promise<{}>' is not assignable to type 'Promise<{ slug: string[]; }>'. Property 'slug' is missing in type '{}' but required in type '{ slug: string[]; }'. ``` This is because Next.js route types are now _stricter_. Our REST handler is nested within a catch-all `/api/[...slug]` route, so the slug param _will_ exist in the handler—but the _same_ handler is re-used for the `/api/graphql` OPTIONS route, which **_is not_** nested within the `slug` param and so it **_will not_** exist as the types suggest. --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1211115021865680 --------- Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com> |
||
|
|
60cfc31f65 | ci: ignore template bump commits in release notes [skip ci] | ||
|
|
32069e2056 |
templates: bump for v3.53.0 (#13541)
🤖 Automated bump of templates for v3.53.0
Triggered by user: @denolfe
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
|
||
|
|
4151a902f2 | chore(release): v3.53.0 [skip ci] v3.53.0 | ||
|
|
b65ca6832d |
fix(ui): thread id through instead of from routeParams (#13539)
Adjustment to https://github.com/payloadcms/payload/pull/13526 Prefer to thread ID through arguments instead of relying on routeParams which would mean that ID is always a string - would not work for PG dbs or non-text based ID's. |
||
|
|
76741eb722 |
chore(plugin-multi-tenant): missing collections warning (#13538)
Fixes https://github.com/payloadcms/payload/issues/13517 ⚠️ **Need to merge https://github.com/payloadcms/payload/pull/13379 first** Adds warning if collections are enabled but not found. This can happen if you add the multi-tenant before other plugins that add the collections you are attempting to add multi-tenancy to. |