@payloadcms/codemod 4.0.0-internal.31ba7ee → 4.0.0-internal.3b50481
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.help.d.ts +4 -0
- package/dist/cli.help.d.ts.map +1 -0
- package/dist/cli.help.js +29 -0
- package/dist/cli.help.js.map +1 -0
- package/dist/cli.js +8 -0
- package/dist/cli.js.map +1 -1
- package/dist/cli.parseFlags.d.ts +1 -0
- package/dist/cli.parseFlags.d.ts.map +1 -1
- package/dist/cli.parseFlags.js +6 -0
- package/dist/cli.parseFlags.js.map +1 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +6 -0
- package/dist/registry.js.map +1 -1
- package/dist/runbook/payload-v4-upgrade.md +2 -2
- package/dist/runbook/v4.mdx +419 -66
- package/dist/transforms/migrate-admin-context-properties/basic.input.js +32 -0
- package/dist/transforms/migrate-admin-context-properties/basic.input.js.map +1 -0
- package/dist/transforms/migrate-admin-context-properties/basic.output.js +30 -0
- package/dist/transforms/migrate-admin-context-properties/basic.output.js.map +1 -0
- package/dist/transforms/migrate-admin-context-properties/index.d.ts +3 -0
- package/dist/transforms/migrate-admin-context-properties/index.d.ts.map +1 -0
- package/dist/transforms/migrate-admin-context-properties/index.js +302 -0
- package/dist/transforms/migrate-admin-context-properties/index.js.map +1 -0
- package/dist/transforms/migrate-admin-context-properties/no-match.input.js +30 -0
- package/dist/transforms/migrate-admin-context-properties/no-match.input.js.map +1 -0
- package/dist/transforms/migrate-admin-context-properties/no-match.output.js +30 -0
- package/dist/transforms/migrate-admin-context-properties/no-match.output.js.map +1 -0
- package/dist/transforms/migrate-field-component-types/index.d.ts.map +1 -1
- package/dist/transforms/migrate-field-component-types/index.js +4 -0
- package/dist/transforms/migrate-field-component-types/index.js.map +1 -1
- package/dist/transforms/migrate-sharp-to-transformer/basic.input.js +28 -0
- package/dist/transforms/migrate-sharp-to-transformer/basic.input.js.map +1 -0
- package/dist/transforms/migrate-sharp-to-transformer/basic.output.js +39 -0
- package/dist/transforms/migrate-sharp-to-transformer/basic.output.js.map +1 -0
- package/dist/transforms/migrate-sharp-to-transformer/existing-transformers.input.js +27 -0
- package/dist/transforms/migrate-sharp-to-transformer/existing-transformers.input.js.map +1 -0
- package/dist/transforms/migrate-sharp-to-transformer/existing-transformers.output.js +33 -0
- package/dist/transforms/migrate-sharp-to-transformer/existing-transformers.output.js.map +1 -0
- package/dist/transforms/migrate-sharp-to-transformer/index.d.ts +3 -0
- package/dist/transforms/migrate-sharp-to-transformer/index.d.ts.map +1 -0
- package/dist/transforms/migrate-sharp-to-transformer/index.js +430 -0
- package/dist/transforms/migrate-sharp-to-transformer/index.js.map +1 -0
- package/dist/transforms/migrate-sharp-to-transformer/no-match.input.js +11 -0
- package/dist/transforms/migrate-sharp-to-transformer/no-match.input.js.map +1 -0
- package/dist/transforms/migrate-sharp-to-transformer/no-match.output.js +11 -0
- package/dist/transforms/migrate-sharp-to-transformer/no-match.output.js.map +1 -0
- package/dist/transforms/remove-storage-always-insert-fields/index.d.ts.map +1 -1
- package/dist/transforms/remove-storage-always-insert-fields/index.js +4 -0
- package/dist/transforms/remove-storage-always-insert-fields/index.js.map +1 -1
- package/dist/transforms/rename-diff-value-props/basic.input.js +38 -0
- package/dist/transforms/rename-diff-value-props/basic.input.js.map +1 -0
- package/dist/transforms/rename-diff-value-props/basic.output.js +38 -0
- package/dist/transforms/rename-diff-value-props/basic.output.js.map +1 -0
- package/dist/transforms/rename-diff-value-props/index.d.ts +3 -0
- package/dist/transforms/rename-diff-value-props/index.d.ts.map +1 -0
- package/dist/transforms/rename-diff-value-props/index.js +328 -0
- package/dist/transforms/rename-diff-value-props/index.js.map +1 -0
- package/dist/transforms/rename-diff-value-props/no-match.input.js +8 -0
- package/dist/transforms/rename-diff-value-props/no-match.input.js.map +1 -0
- package/dist/transforms/rename-experimental-table-feature/index.d.ts.map +1 -1
- package/dist/transforms/rename-experimental-table-feature/index.js +7 -1
- package/dist/transforms/rename-experimental-table-feature/index.js.map +1 -1
- package/dist/upgrade/rewritePackageJson.d.ts.map +1 -1
- package/dist/upgrade/rewritePackageJson.js +52 -11
- package/dist/upgrade/rewritePackageJson.js.map +1 -1
- package/dist/utils/project.d.ts +7 -1
- package/dist/utils/project.d.ts.map +1 -1
- package/dist/utils/project.js +20 -7
- package/dist/utils/project.js.map +1 -1
- package/package.json +2 -2
package/dist/runbook/v4.mdx
CHANGED
|
@@ -6,8 +6,6 @@ desc: Upgrade guide for Payload 3.x projects migrating to 4.0.
|
|
|
6
6
|
keywords: local api, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
# Payload 3.0 to 4.0 Migration Guide
|
|
10
|
-
|
|
11
9
|
All breaking changes are listed below. If you encounter changes that are not explicitly listed here, please consider contributing to this documentation by submitting a PR.
|
|
12
10
|
|
|
13
11
|
## Codemod
|
|
@@ -122,9 +120,9 @@ A token is also rejected if its `collection` is not auth-enabled or has `disable
|
|
|
122
120
|
|
|
123
121
|
See [Token Data](../authentication/token-data) for the full token data shape.
|
|
124
122
|
|
|
125
|
-
###
|
|
123
|
+
### Local API access is now enforced by default #overrideaccess-now-defaults-to-false-on-local-api-operations
|
|
126
124
|
|
|
127
|
-
|
|
125
|
+
`overrideAccess` now defaults to `false` on Local API operations, so omitting it respects Access Control instead. Operations that do not expose `overrideAccess`, and operations such as `resetPassword` where it was already required or did not default to `true`, are unaffected by this change.
|
|
128
126
|
|
|
129
127
|
```diff
|
|
130
128
|
const posts = await payload.find({
|
|
@@ -156,7 +154,32 @@ Run a typecheck after the codemod. Three patterns it cannot resolve:
|
|
|
156
154
|
|
|
157
155
|
`payload.jobs.queue`, `run`, `runByID`, `cancel`, and `cancelByID` now also default `overrideAccess` to `false`. The option remains optional, but omitting it runs the corresponding `jobs.access.queue`, `jobs.access.run`, or `jobs.access.cancel` function. The codemod adds `overrideAccess: true` to these calls—including zero-argument `payload.jobs.run()` calls—to preserve Payload 3 behavior for review.
|
|
158
156
|
|
|
159
|
-
###
|
|
157
|
+
### On-demand validation adds a `validate` operation
|
|
158
|
+
|
|
159
|
+
The `Operation` type now includes `validate`. The `operation` argument for collection, global, and field `beforeValidate` and `beforeChange` hooks also includes `validate`.
|
|
160
|
+
|
|
161
|
+
On-demand validation runs these hooks without saving the candidate. Update exhaustive operation checks and skip external side effects that should only run during `create` or `update`:
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
import type { CollectionBeforeChangeHook } from 'payload'
|
|
165
|
+
|
|
166
|
+
const syncCustomer: CollectionBeforeChangeHook = async ({
|
|
167
|
+
data,
|
|
168
|
+
operation,
|
|
169
|
+
}) => {
|
|
170
|
+
if (operation === 'validate') {
|
|
171
|
+
return data
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
await syncCustomerWithStripe(data)
|
|
175
|
+
|
|
176
|
+
return data
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Sanitized collection and global access types now require a `validate` key. Payload supplies this key during configuration sanitization and uses `update` access by default. Code that constructs a sanitized access object directly must add the key.
|
|
181
|
+
|
|
182
|
+
### Access Control callbacks now receive document slugs
|
|
160
183
|
|
|
161
184
|
Collection and Global Access Control callback arguments now include the document's `slug`. This additive property is unlikely to affect most projects, but code that manually invokes access callbacks or `executeAccess` must now pass `slug`. Code that validates, enumerates, or compares the exact callback argument shape may also need updating.
|
|
162
185
|
|
|
@@ -246,7 +269,7 @@ Payload adds built-in command import references to `cli.commands` while sanitizi
|
|
|
246
269
|
|
|
247
270
|
See the [Payload CLI documentation](../configuration/cli) for the complete API.
|
|
248
271
|
|
|
249
|
-
###
|
|
272
|
+
### Legacy migration CLI export removed
|
|
250
273
|
|
|
251
274
|
The undocumented `migrateCLI` export has been removed. It accepted an already-sanitized config, a minimist-shaped `parsedArgs` object, and a special `migrationDir` override. This duplicated migration command registration and kept the old argument format alive after the main CLI moved to Commander.
|
|
252
275
|
|
|
@@ -275,7 +298,7 @@ The spawned CLI discovers the Payload config from `cwd` or `PAYLOAD_CONFIG_PATH`
|
|
|
275
298
|
|
|
276
299
|
`generateTypes` now returns `{ types, outputFile, written }` instead of returning the generated source directly when using `returnString: true`. `generateImportMap` now returns `{ outputFile, written }`, and `payload.db.generateSchema()` returns `{ outputFile }`. Code that ignores these return values does not need to change.
|
|
277
300
|
|
|
278
|
-
### Default query depth
|
|
301
|
+
### Default query depth has been reduced
|
|
279
302
|
|
|
280
303
|
Queries that omit `depth` now populate one relationship level instead of two. This reduces database work and response size by default.
|
|
281
304
|
|
|
@@ -350,7 +373,7 @@ export const Posts: CollectionConfig = {
|
|
|
350
373
|
}
|
|
351
374
|
```
|
|
352
375
|
|
|
353
|
-
### Authorship
|
|
376
|
+
### Authorship is now enabled by default
|
|
354
377
|
|
|
355
378
|
Collections and globals now track authorship by default: Payload injects `createdBy` and `updatedBy` polymorphic relationship fields (related to every auth-enabled collection) and stamps them from `req.user` on each write. Previously there was no authorship tracking; you now opt out with `authorship: false`.
|
|
356
379
|
|
|
@@ -389,7 +412,7 @@ The `admin.enableListViewSelectAPI` Collection Config property has been removed.
|
|
|
389
412
|
|
|
390
413
|
If you previously set `admin.enableListViewSelectAPI: true`, remove the property — the behavior is now default. The `migrate-list-view-select-api` transform in `@payloadcms/codemod` handles this automatically.
|
|
391
414
|
|
|
392
|
-
###
|
|
415
|
+
### Global component slots now match Collections
|
|
393
416
|
|
|
394
417
|
Global configs now use the same `admin.components` shape as Collections. The Edit View slot container is renamed from `elements` to `edit`, the `Description` slot is hoisted to top-level `admin.components.Description`, and `editMenuItems` is now supported.
|
|
395
418
|
|
|
@@ -417,7 +440,7 @@ Globals also now support custom views with arbitrary keys under `admin.component
|
|
|
417
440
|
|
|
418
441
|
This change is auto-migrated by the `globals-components-edit` codemod.
|
|
419
442
|
|
|
420
|
-
###
|
|
443
|
+
### Dynamic selection replaces static forced selection
|
|
421
444
|
|
|
422
445
|
The static `forceSelect` config has been removed. Each Collection and Global now accepts a top-level `select` function that receives the current `operation`, `req`, and the caller's `select`, and returns the final `select` to use. Returning `undefined` leaves the caller's `select` unchanged.
|
|
423
446
|
|
|
@@ -442,9 +465,9 @@ The same change applies to Globals.
|
|
|
442
465
|
|
|
443
466
|
Run `npx @payloadcms/codemod --transform migrate-force-select` to migrate automatically. The codemod handles object-literal `forceSelect` values, including nested ones (which previously deep-merged) — those are rewritten to call `deepMergeSimple` from `payload/shared`, with the import added automatically. Non-literal values, configs that already define a sibling `select`, and unsupported member kinds are surfaced as notes for manual review.
|
|
444
467
|
|
|
445
|
-
####
|
|
468
|
+
#### API tab visibility now uses a condition
|
|
446
469
|
|
|
447
|
-
Use the existing `admin.components.views.edit.api.tab.condition` to hide the API tab instead.
|
|
470
|
+
`admin.hideAPIURL` has been removed from Collections and Globals. Use the existing `admin.components.views.edit.api.tab.condition` to hide the API tab instead.
|
|
448
471
|
|
|
449
472
|
```diff
|
|
450
473
|
// collections/Posts.ts
|
|
@@ -472,7 +495,7 @@ export const PostsCollection: CollectionConfig = {
|
|
|
472
495
|
|
|
473
496
|
Run `npx @payloadcms/codemod --transform remove-hide-api-url` to migrate automatically.
|
|
474
497
|
|
|
475
|
-
### Aliased
|
|
498
|
+
### Aliased UI and Next.js re-exports have been removed
|
|
476
499
|
|
|
477
500
|
Several types and utilities that were re-exported from `@payloadcms/ui` and `@payloadcms/next/utilities` for backwards compatibility have been removed. The canonical exports live in `payload` and `payload/shared`; import directly from there.
|
|
478
501
|
|
|
@@ -558,7 +581,40 @@ To migrate automatically, there's a codemod for this change available by running
|
|
|
558
581
|
npx @payloadcms/codemod --transform migrate-field-component-types
|
|
559
582
|
```
|
|
560
583
|
|
|
561
|
-
### `
|
|
584
|
+
### Diff component props `comparisonValue` and `versionValue` renamed to `valueFrom` and `valueTo`
|
|
585
|
+
|
|
586
|
+
Custom Diff Components (`admin.components.Diff`) receive the two field values being compared as `valueFrom` and `valueTo`. The `comparisonValue` and `versionValue` props have been removed from `FieldDiffClientProps` and `FieldDiffServerProps`, including every field-specific variant such as `TextFieldDiffClientProps`.
|
|
587
|
+
|
|
588
|
+
| Old prop | New prop | Description |
|
|
589
|
+
| ----------------- | ----------- | ------------------------------------------------ |
|
|
590
|
+
| `comparisonValue` | `valueFrom` | Field value from the version being compared from |
|
|
591
|
+
| `versionValue` | `valueTo` | Field value from the version being compared to |
|
|
592
|
+
|
|
593
|
+
```diff
|
|
594
|
+
import type { TextFieldDiffClientProps } from 'payload'
|
|
595
|
+
import type React from 'react'
|
|
596
|
+
|
|
597
|
+
export const MyTextDiff: React.FC<TextFieldDiffClientProps> = ({
|
|
598
|
+
- comparisonValue,
|
|
599
|
+
- versionValue,
|
|
600
|
+
+ valueFrom,
|
|
601
|
+
+ valueTo,
|
|
602
|
+
}) => {
|
|
603
|
+
// ...
|
|
604
|
+
}
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
#### Codemod
|
|
608
|
+
|
|
609
|
+
To migrate automatically, there's a codemod for this change available by running:
|
|
610
|
+
|
|
611
|
+
```bash
|
|
612
|
+
npx @payloadcms/codemod --transform rename-diff-value-props
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
The codemod updates functions whose props are typed with a Diff props or component type imported from `payload` (e.g. `TextFieldDiffClientProps`), or with a type in the same file built from one. It renames destructured props and reads such as `props.comparisonValue`. Untyped components and props passed on to other components (e.g. `<OtherDiff comparisonValue={...} />`) aren't changed automatically; the codemod prints a note for each leftover in files that import a Diff type.
|
|
616
|
+
|
|
617
|
+
### Default value functions can now return promises ([#17522](https://github.com/payloadcms/payload/pull/17522))
|
|
562
618
|
|
|
563
619
|
The exported `DefaultValue` type's function variant previously declared a synchronous-only return type (`SerializableValue`), even though Payload has always awaited the result of a `defaultValue` function at runtime. The type now correctly reflects this and allows `Promise<SerializableValue> | SerializableValue`.
|
|
564
620
|
|
|
@@ -620,7 +676,7 @@ const interfaceName = global.typescript?.interface
|
|
|
620
676
|
|
|
621
677
|
Properties filled during sanitization, including `_sanitized`, `admin`, `custom`, `label`, standard access functions, hook arrays, endpoints, and flattened fields, remain required. Use `GlobalConfig` for input and `SanitizedGlobalConfig` after sanitization.
|
|
622
678
|
|
|
623
|
-
### User types
|
|
679
|
+
### User types have been consolidated
|
|
624
680
|
|
|
625
681
|
Payload's user types have been consolidated so each one has a clear job. This completes a cleanup already planned in 3.x: `UntypedUser` was deprecated for removal in 4.0, and `TypedUser` was marked to be renamed to `User` in 4.0.
|
|
626
682
|
|
|
@@ -686,7 +742,7 @@ If the field name is dynamic (configurable at runtime), index through `Record<st
|
|
|
686
742
|
const value = (user as Record<string, unknown>)[fieldNameFromConfig]
|
|
687
743
|
```
|
|
688
744
|
|
|
689
|
-
###
|
|
745
|
+
### Plugins now infer generated types automatically
|
|
690
746
|
|
|
691
747
|
The `<ConfigType>` type parameter on `multiTenantPlugin` and `searchPlugin` — and on `MultiTenantPluginConfig` / `SearchPluginConfig` — has been removed. It only existed to override the `user` / `locale` types, which Payload now infers from your generated types automatically: `userHasAccessToAllTenants` receives the generated `User`, and `search`'s `skipSync` callback receives `TypedLocale | undefined`.
|
|
692
748
|
|
|
@@ -697,9 +753,9 @@ Drop the type argument — your callbacks are typed correctly without it.
|
|
|
697
753
|
+ multiTenantPlugin({ ... })
|
|
698
754
|
```
|
|
699
755
|
|
|
700
|
-
###
|
|
756
|
+
### Plugin definitions now require a slug and named options
|
|
701
757
|
|
|
702
|
-
|
|
758
|
+
The experimental `definePlugin` helper now requires a `slug` and passes plugin options through a named `options` argument:
|
|
703
759
|
|
|
704
760
|
- **`slug` is now required.** It registers the plugin in the `plugins` map for cross-plugin discovery. Add one to every `definePlugin` call.
|
|
705
761
|
- **Options are a named `options` argument.** They were previously spread into the `plugin` callback's args alongside `config` and `plugins`; they're now under a single `options` property. `TOptions` is also no longer constrained to `Record<string, unknown>`, so `interface` and generic option types work.
|
|
@@ -713,7 +769,7 @@ export const myPlugin = definePlugin<MyPluginOptions>({
|
|
|
713
769
|
})
|
|
714
770
|
```
|
|
715
771
|
|
|
716
|
-
###
|
|
772
|
+
### Admin routing is now framework-agnostic
|
|
717
773
|
|
|
718
774
|
`@payloadcms/ui` no longer depends on `next` directly. Custom admin components (field components, custom views, plugins) that previously imported router hooks or the `Link` component from `next/*` must now import the framework-agnostic equivalents from `@payloadcms/ui`. Behavior is identical when running on Next.js — the `NextRouterAdapter` shipped by `@payloadcms/next` wires `next/navigation` hooks and `next/link` into the same context — but ui code can now also run on non-Next adapters (e.g. TanStack Start) without modification.
|
|
719
775
|
|
|
@@ -767,7 +823,7 @@ If you were relying on Next-specific extras like `prefetch()` on the router inst
|
|
|
767
823
|
/>
|
|
768
824
|
```
|
|
769
825
|
|
|
770
|
-
###
|
|
826
|
+
### Next.js subpath exports have moved to the UI package
|
|
771
827
|
|
|
772
828
|
The `./client`, `./rsc`, and `./templates` subpath exports on `@payloadcms/next` were thin re-exports of components that now live in `@payloadcms/ui`. All admin elements (DocumentHeader, FormHeader, HierarchyTypeField, Logo, Nav) and templates (DefaultTemplate, MinimalTemplate) have been relocated to `@payloadcms/ui` and no longer depend on `next/*` modules. Import them directly from the canonical source.
|
|
773
829
|
|
|
@@ -805,7 +861,7 @@ If you reference any of these components by string path in your Payload config (
|
|
|
805
861
|
|
|
806
862
|
Run `npx @payloadcms/codemod --transform migrate-next-subpath-exports` to migrate automatically. It rewrites both `import` declarations and string component paths.
|
|
807
863
|
|
|
808
|
-
###
|
|
864
|
+
### Document title state has moved to its own hook
|
|
809
865
|
|
|
810
866
|
For performance reasons, the document title state has been split out of `DocumentInfoContext` into its own `DocumentTitleContext`. Access it through the `useDocumentTitle` hook, which exposes the same `title` and `setDocumentTitle` API. Components that subscribed to `useDocumentInfo` solely for the title will no longer re-render when unrelated document state changes.
|
|
811
867
|
|
|
@@ -825,7 +881,7 @@ If you only used `useDocumentInfo` for `title` / `setDocumentTitle`, drop the im
|
|
|
825
881
|
|
|
826
882
|
Run `npx @payloadcms/codemod --transform migrate-document-title-context` to migrate automatically. The codemod splits mixed destructures, removes the `useDocumentInfo` import when no other properties are still used, and preserves any rename aliases (e.g. `{ title: docTitle }`).
|
|
827
883
|
|
|
828
|
-
###
|
|
884
|
+
### Locale hooks can now return null
|
|
829
885
|
|
|
830
886
|
The return type of `useLocale` is now `Locale | null`. It returns `null` when localization is disabled or configured without any locales. In Payload 3, the hook was typed as `Locale` and returned an empty object when localization was unavailable.
|
|
831
887
|
|
|
@@ -855,8 +911,59 @@ When a missing locale is already valid for the receiving API, optional chaining
|
|
|
855
911
|
const locale = useLocale()?.code
|
|
856
912
|
```
|
|
857
913
|
|
|
914
|
+
<<<<<<< HEAD
|
|
915
|
+
|
|
916
|
+
### `readOnly` removed from the `useField` return type
|
|
917
|
+
|
|
918
|
+
The deprecated `readOnly` property has been removed from `FieldType`, the object returned by `useField`. `useField` already stopped returning it in 3.x, so it was always `undefined` at runtime.
|
|
919
|
+
|
|
920
|
+
Read `readOnly` from your field component's props instead:
|
|
921
|
+
|
|
922
|
+
````diff
|
|
923
|
+
'use client'
|
|
924
|
+
import type { TextFieldClientComponent } from 'payload'
|
|
925
|
+
import { useField } from '@payloadcms/ui'
|
|
926
|
+
|
|
927
|
+
-export const MyField: TextFieldClientComponent = ({ path }) => {
|
|
928
|
+
- const { readOnly, value } = useField({ path })
|
|
929
|
+
+export const MyField: TextFieldClientComponent = ({ path, readOnly }) => {
|
|
930
|
+
+ const { value } = useField({ path })
|
|
931
|
+
|
|
932
|
+
### Deprecated `TableColumnsProvider` props removed
|
|
933
|
+
|
|
934
|
+
The following deprecated props have been removed from `TableColumnsProvider` and its `TableColumnsProviderProps` type:
|
|
935
|
+
|
|
936
|
+
- `docs`
|
|
937
|
+
- `enableRowSelections`
|
|
938
|
+
- `listPreferences`
|
|
939
|
+
- `preferenceKey`
|
|
940
|
+
- `renderRowTypes`
|
|
941
|
+
- `setTable`
|
|
942
|
+
- `sortColumnProps`
|
|
943
|
+
- `tableAppearance`
|
|
944
|
+
|
|
945
|
+
`TableColumnsProvider` has ignored these props since 3.26.0, when column state moved to the URL. Remove them where you render the provider. It now only accepts `children`, `collectionSlug`, `columnState`, and `LinkedCellOverride`.
|
|
946
|
+
|
|
947
|
+
```diff
|
|
948
|
+
<TableColumnsProvider
|
|
949
|
+
collectionSlug={collectionSlug}
|
|
950
|
+
columnState={columnState}
|
|
951
|
+
- enableRowSelections
|
|
952
|
+
- preferenceKey={preferenceKey}
|
|
953
|
+
- tableAppearance="condensed"
|
|
954
|
+
>
|
|
955
|
+
{children}
|
|
956
|
+
</TableColumnsProvider>
|
|
957
|
+
````
|
|
958
|
+
|
|
858
959
|
### RichText adapter `i18n` property removed
|
|
859
960
|
|
|
961
|
+
=======
|
|
962
|
+
|
|
963
|
+
### Rich text adapters no longer expose translations
|
|
964
|
+
|
|
965
|
+
> > > > > > > 7dd8d28c6a (cleans up headings that contained code blocks, updates titles on migration guides)
|
|
966
|
+
|
|
860
967
|
The `i18n` property on `RichTextAdapter` (and its return type from adapter provider functions) has been removed. It was deprecated in v3 in favour of merging translations directly into `config.i18n.translations` inside the adapter provider.
|
|
861
968
|
|
|
862
969
|
If you authored a custom rich-text adapter that returned an `i18n` object, move that logic into the adapter provider function and merge the translations into the config yourself:
|
|
@@ -912,12 +1019,66 @@ export const MyFeature = createServerFeature({
|
|
|
912
1019
|
|
|
913
1020
|
If an affected adapter or feature performs asynchronous work, move that work to an asynchronous plugin before config sanitization or to a runtime hook. Projects that only use `buildConfig(...)` and `lexicalEditor(...)` normally are not affected; `buildConfig` remains asynchronous.
|
|
914
1021
|
|
|
915
|
-
###
|
|
1022
|
+
### Transaction requests now use a single identifier property
|
|
916
1023
|
|
|
917
1024
|
The `transactionIDPromise` property on `PayloadRequest` has been removed. It was deprecated and unused — `transactionID` (which already supports `Promise<number | string>`) is the correct property to use for both resolved values and pending transactions.
|
|
918
1025
|
|
|
1026
|
+
<<<<<<< HEAD
|
|
1027
|
+
|
|
1028
|
+
### `headers` and `languageCode` removed from `AdminContext`
|
|
1029
|
+
|
|
1030
|
+
The `headers` and `languageCode` properties have been removed from `AdminContext`, the object returned by `initAdminContext`. Both duplicated values that are already available on `req`:
|
|
1031
|
+
|
|
1032
|
+
```diff
|
|
1033
|
+
const context = await initAdminContext(args)
|
|
1034
|
+
|
|
1035
|
+
-const userAgent = context.headers.get('user-agent')
|
|
1036
|
+
+const userAgent = context.req.headers.get('user-agent')
|
|
1037
|
+
|
|
1038
|
+
-const languageCode = context.languageCode
|
|
1039
|
+
+const languageCode = context.req.i18n.language
|
|
1040
|
+
```
|
|
1041
|
+
|
|
1042
|
+
`req.i18n.language` is typed as `string`. Cast it to `AcceptedLanguages` if you need the narrower type.
|
|
1043
|
+
|
|
1044
|
+
`languageCode` has also been removed from `PartialAdminContext`. Use `i18n.language` instead. `getRootLayoutData` from `@payloadcms/ui` no longer accepts `headers` or `languageCode` and reads both from `req`.
|
|
1045
|
+
|
|
1046
|
+
#### Codemod
|
|
1047
|
+
|
|
1048
|
+
To migrate automatically, run:
|
|
1049
|
+
|
|
1050
|
+
```bash
|
|
1051
|
+
npx @payloadcms/codemod --transform migrate-admin-context-properties
|
|
1052
|
+
```
|
|
1053
|
+
|
|
1054
|
+
The codemod rewrites property reads and destructures on `initAdminContext()` results and on parameters or variables typed as `AdminContext` or `PartialAdminContext`. It also updates `Pick<AdminContext, ...>` and `AdminContext['...']` types and drops both arguments from `getRootLayoutData()` calls. Element access such as `context['headers']`, and contexts passed through your own wrapper functions, are left unchanged for manual migration.
|
|
1055
|
+
|
|
1056
|
+
### `DiffMethod` type removed
|
|
1057
|
+
|
|
1058
|
+
The deprecated `DiffMethod` type has been removed from `payload`. It mirrored an enum from `react-diff-viewer-continued`, which Payload no longer depends on. Remove any imports of `DiffMethod`.
|
|
1059
|
+
|
|
1060
|
+
### `diffMethod` prop removed from Diff components
|
|
1061
|
+
|
|
1062
|
+
Custom Diff components (`admin.components.Diff`) no longer receive a `diffMethod` prop. It has been removed from `FieldDiffClientProps` and every field-specific variant like `TextFieldDiffClientProps`. Payload always passed the same `'diffWordsWithSpace'` value, so the prop carried no information. Each Diff component handles its own diffing.
|
|
1063
|
+
|
|
1064
|
+
Remove `diffMethod` from your props. If you passed it on to a diffing utility, pass `'diffWordsWithSpace'` directly instead:
|
|
1065
|
+
|
|
1066
|
+
```diff
|
|
1067
|
+
export const MyTextDiff: React.FC<TextFieldDiffClientProps> = ({
|
|
1068
|
+
- diffMethod,
|
|
1069
|
+
valueFrom,
|
|
1070
|
+
valueTo,
|
|
1071
|
+
}) => {
|
|
1072
|
+
```
|
|
1073
|
+
|
|
919
1074
|
### Collection `afterOperation` hook: `operation: 'read'` removed
|
|
920
1075
|
|
|
1076
|
+
=======
|
|
1077
|
+
|
|
1078
|
+
### Collection after-operation hooks no longer report read operations
|
|
1079
|
+
|
|
1080
|
+
> > > > > > > 7dd8d28c6a (cleans up headings that contained code blocks, updates titles on migration guides)
|
|
1081
|
+
|
|
921
1082
|
The deprecated `'read'` value for the `operation` argument of collection `afterOperation` hooks has been removed. Payload has not dispatched `afterOperation` with `operation: 'read'` for some time — the `find` and `findByID` operations report their own names (`'find'` and `'findByID'`) instead.
|
|
922
1083
|
|
|
923
1084
|
If a custom `afterOperation` hook branches on `operation === 'read'`, split it to handle `'find'` and `'findByID'`:
|
|
@@ -939,14 +1100,14 @@ Run `npx @payloadcms/codemod --transform migrate-after-operation-read` to migrat
|
|
|
939
1100
|
|
|
940
1101
|
The `operation: 'read'` value on the `beforeOperation` hook is unchanged.
|
|
941
1102
|
|
|
942
|
-
###
|
|
1103
|
+
### Static configuration defaults have been removed ([#17103](https://github.com/payloadcms/payload/pull/17103))
|
|
943
1104
|
|
|
944
|
-
The defaults object exported from payload has been removed. It was deprecated because it was a single shared object and mutating it (or any config that used it as a base) leaked changes into every other consumer of the defaults.
|
|
1105
|
+
The `defaults` object exported from `payload` has been removed. It was deprecated because it was a single shared object and mutating it (or any config that used it as a base) leaked changes into every other consumer of the defaults.
|
|
945
1106
|
|
|
946
1107
|
If you depended on reading a specific default value at runtime, read it from the sanitized config returned by buildConfig (or payload.config) instead of from the static defaults object.
|
|
947
1108
|
Alternatively, you can call `addDefaultsToConfig` to get the unsanitized config but populated with the default properties.
|
|
948
1109
|
|
|
949
|
-
###
|
|
1110
|
+
### Import and export field callbacks have moved to hooks
|
|
950
1111
|
|
|
951
1112
|
The `toCSV` and `fromCSV` field options in `custom['plugin-import-export']` have been removed. Use `hooks.beforeExport` and `hooks.beforeImport` instead — they work for both CSV and JSON formats.
|
|
952
1113
|
|
|
@@ -987,7 +1148,7 @@ The `fromCSV` → `hooks.beforeImport` change is non-breaking for the `data` par
|
|
|
987
1148
|
|
|
988
1149
|
Run `npx @payloadcms/codemod --transform migrate-import-export-hooks` to migrate automatically. The codemod moves the function values into the correct hook positions and merges with any existing `hooks` object. Review argument names in the migrated functions — `row` references must be renamed to `siblingData` and `doc` references renamed to `data`.
|
|
989
1150
|
|
|
990
|
-
###
|
|
1151
|
+
### Database adapter types now use the main package exports
|
|
991
1152
|
|
|
992
1153
|
The `/types` subpath export has been removed from all Drizzle-based database adapter packages. All types are available directly from the main package entry point.
|
|
993
1154
|
|
|
@@ -1025,19 +1186,28 @@ If you use a `declare module` augmentation to extend `GeneratedDatabaseSchema` (
|
|
|
1025
1186
|
|
|
1026
1187
|
Run `npx @payloadcms/codemod --transform migrate-db-types-subpath` to migrate automatically. The codemod handles import declarations, re-export declarations, and `declare module` augmentations.
|
|
1027
1188
|
|
|
1189
|
+
### `findMigrationDir` removed from `@payloadcms/drizzle`
|
|
1190
|
+
|
|
1191
|
+
The deprecated `findMigrationDir` re-export has been removed from `@payloadcms/drizzle`. Import it from `payload` instead:
|
|
1192
|
+
|
|
1193
|
+
```diff
|
|
1194
|
+
- import { findMigrationDir } from '@payloadcms/drizzle'
|
|
1195
|
+
+ import { findMigrationDir } from 'payload'
|
|
1196
|
+
```
|
|
1197
|
+
|
|
1028
1198
|
### Minimum supported Node.js version is now 24.15.0 ([#16540](https://github.com/payloadcms/payload/pull/16540))
|
|
1029
1199
|
|
|
1030
1200
|
The minimum supported Node.js version is now **24.15.0** (previously 18.20.2). Upgrade your runtime — along with any CI, Docker images, and deployment targets — to the latest node LTS version or newer before upgrading to 4.0.
|
|
1031
1201
|
|
|
1032
|
-
### Minimum supported Next.js version is now 16.
|
|
1202
|
+
### Minimum supported Next.js version is now 16.4.0 ([#16537](https://github.com/payloadcms/payload/pull/16537))
|
|
1033
1203
|
|
|
1034
|
-
Older versions of Next.js are no longer supported. Update Next.js to **16.
|
|
1204
|
+
Older versions of Next.js are no longer supported. Update Next.js to **16.4.0** or newer.
|
|
1035
1205
|
|
|
1036
1206
|
### Minimum supported TypeScript version is now 6.0.3 ([#16692](https://github.com/payloadcms/payload/pull/16692))
|
|
1037
1207
|
|
|
1038
1208
|
Payload's generated types are no longer guaranteed to work on TypeScript versions below **6.0.3** (previously 5.7.3). Upgrade the `typescript` dependency in your project to 6.0.3 or newer.
|
|
1039
1209
|
|
|
1040
|
-
###
|
|
1210
|
+
### The Slate rich text editor has been removed ([#16359](https://github.com/payloadcms/payload/pull/16359))
|
|
1041
1211
|
|
|
1042
1212
|
Starting in Payload 4.0, `@payloadcms/richtext-lexical` is the only supported rich text editor. The `@payloadcms/richtext-slate` package has been deleted and will no longer be published.
|
|
1043
1213
|
|
|
@@ -1052,7 +1222,7 @@ The rich text adapter pattern itself is unchanged — `editor: lexicalEditor({})
|
|
|
1052
1222
|
|
|
1053
1223
|
If you are still using Slate, **do not upgrade to 4.0 yet.** Stay on the latest 3.x release and complete the [Slate → Lexical migration](https://payloadcms.com/docs/rich-text/migration) first.
|
|
1054
1224
|
|
|
1055
|
-
###
|
|
1225
|
+
### Legacy Lexical HTML converters have been removed ([#16548](https://github.com/payloadcms/payload/pull/16548))
|
|
1056
1226
|
|
|
1057
1227
|
The deprecated `HTMLConverterFeature`, `lexicalHTML`, and the per-node `converters.html` API have been removed from `@payloadcms/richtext-lexical`. Use `convertLexicalToHTML` and `lexicalHTMLField` from the non-deprecated converter instead.
|
|
1058
1228
|
|
|
@@ -1076,7 +1246,7 @@ The deprecated `HTMLConverterFeature`, `lexicalHTML`, and the per-node `converte
|
|
|
1076
1246
|
|
|
1077
1247
|
Custom nodes that define `converters.html` should export their converters instead.
|
|
1078
1248
|
|
|
1079
|
-
### Lexical
|
|
1249
|
+
### Lexical now uses its built-in HTML element check ([#17104](https://github.com/payloadcms/payload/pull/17104))
|
|
1080
1250
|
|
|
1081
1251
|
The deprecated `isHTMLElement` utility has been removed from `@payloadcms/richtext-lexical`. It was a thin wrapper around `x instanceof HTMLElement` that duplicated a utility already exported by `lexical`. Import it directly from `lexical` instead.
|
|
1082
1252
|
|
|
@@ -1087,7 +1257,7 @@ The deprecated `isHTMLElement` utility has been removed from `@payloadcms/richte
|
|
|
1087
1257
|
|
|
1088
1258
|
`lexical`'s built-in `isHTMLElement` is the canonical implementation and narrows against the node's owning-document `HTMLElement`, so it is also safe across iframes and other realms.
|
|
1089
1259
|
|
|
1090
|
-
### Lexical
|
|
1260
|
+
### Lexical now uses the upstream Markdown package
|
|
1091
1261
|
|
|
1092
1262
|
`@payloadcms/richtext-lexical` no longer ships a vendored copy of `@lexical/markdown`. The package is now a direct dependency, every `@lexical/*` package was upgraded from `0.41.0` to `0.52.0`, and `@payloadcms/richtext-lexical/lexical/markdown` is a pure passthrough of `@lexical/markdown`.
|
|
1093
1263
|
|
|
@@ -1124,9 +1294,9 @@ If your app imports Lexical types or adds custom features, check these changes f
|
|
|
1124
1294
|
- `ReturnType<Node['exportJSON']>` can now infer partial JSON, making fields such as `version` and `url` optional. If your code expects the full format, use the node's explicit serialized type, such as `SerializedLinkNode`. A normal `node.exportJSON()` call still returns the full format.
|
|
1125
1295
|
- `LexicalNode.getCommonAncestor` was removed. Use `$getCommonAncestor(a, b)?.commonAncestor ?? null`; pass the parents of non-element nodes if you need the old method's behavior.
|
|
1126
1296
|
|
|
1127
|
-
### Lexical
|
|
1297
|
+
### Lexical table support is no longer experimental
|
|
1128
1298
|
|
|
1129
|
-
|
|
1299
|
+
`EXPERIMENTAL_TableFeature` has been renamed to `TableFeature` now that the table feature is no longer experimental.
|
|
1130
1300
|
|
|
1131
1301
|
```diff
|
|
1132
1302
|
- import { EXPERIMENTAL_TableFeature } from '@payloadcms/richtext-lexical'
|
|
@@ -1140,8 +1310,53 @@ The table feature is no longer experimental, so it drops its prefix.
|
|
|
1140
1310
|
|
|
1141
1311
|
Stored content is unaffected.
|
|
1142
1312
|
|
|
1313
|
+
<<<<<<< HEAD
|
|
1314
|
+
|
|
1315
|
+
### Lexical: `LexicalEditorProps` renamed to `LexicalEditorArgs`
|
|
1316
|
+
|
|
1317
|
+
The `LexicalEditorProps` type exported from `@payloadcms/richtext-lexical` has been renamed to `LexicalEditorArgs`. It describes the arguments of the `lexicalEditor` function, not component props. The shape is unchanged.
|
|
1318
|
+
|
|
1319
|
+
````diff
|
|
1320
|
+
- import type { LexicalEditorProps } from '@payloadcms/richtext-lexical'
|
|
1321
|
+
+ import type { LexicalEditorArgs } from '@payloadcms/richtext-lexical'
|
|
1322
|
+
|
|
1323
|
+
### Lexical: `UploadDataImproved` replaced by `UploadData` ([#18606](https://github.com/payloadcms/payload/pull/18606))
|
|
1324
|
+
|
|
1325
|
+
The internal `UploadDataImproved` type has been removed, and its definition is now used by the public `UploadData` type. Replace any `UploadDataImproved` imports and type references with `UploadData`:
|
|
1326
|
+
|
|
1327
|
+
```ts
|
|
1328
|
+
import type { UploadData } from '@payloadcms/richtext-lexical'
|
|
1329
|
+
````
|
|
1330
|
+
|
|
1331
|
+
### Lexical: the `version` of nodes is ignored
|
|
1332
|
+
|
|
1333
|
+
Lexical deprecated the `version` property of serialized nodes, and now ignores it when it loads a document. Payload no longer reads it either.
|
|
1334
|
+
|
|
1335
|
+
**Rich text written by the Payload 3.0 betas is no longer upgraded when it's loaded.** Payload used `version: 1` to detect two older shapes:
|
|
1336
|
+
|
|
1337
|
+
- Block nodes whose fields were wrapped in an extra `data` object (`fields.data`)
|
|
1338
|
+
- Relationship and upload nodes that stored the whole related document in `value`, instead of its ID
|
|
1339
|
+
|
|
1340
|
+
If your database may still contain rich text from the 3.0 betas, run `upgradeLexicalData` on the latest 3.x release before you upgrade to 4.0. It re-saves every document, which converts these shapes:
|
|
1341
|
+
|
|
1342
|
+
```ts
|
|
1343
|
+
import { upgradeLexicalData } from '@payloadcms/richtext-lexical'
|
|
1344
|
+
|
|
1345
|
+
await upgradeLexicalData({ payload })
|
|
1346
|
+
```
|
|
1347
|
+
|
|
1348
|
+
**`version` is optional in the JSON schema of Lexical nodes.** The CLI and MCP no longer require it, and leave it out of the schema they show agents. They still accept it, so existing documents, which contain `version`, stay valid input.
|
|
1349
|
+
|
|
1350
|
+
**Payload doesn't add or remove `version` when it saves rich text.** Rich text created by Lexical, like in the admin panel, still has `version: 1` on every node, because Lexical's JSON export writes it. Payload's own nodes used to write their own numbers instead (like `2` for blocks), and now write `1` as well. Rich text sent through the API, CLI or MCP is saved as sent, so it may not contain `version`. The generated types still include `version`, marked as deprecated, so don't rely on it being there.
|
|
1351
|
+
|
|
1143
1352
|
### Field `typescriptSchema` renamed to `jsonSchema`
|
|
1144
1353
|
|
|
1354
|
+
=======
|
|
1355
|
+
|
|
1356
|
+
### Field schema configuration now reflects JSON Schema
|
|
1357
|
+
|
|
1358
|
+
> > > > > > > 7dd8d28c6a (cleans up headings that contained code blocks, updates titles on migration guides)
|
|
1359
|
+
|
|
1145
1360
|
`typescriptSchema` accepted JSON Schema, not TypeScript, so the name lied. Renamed to `jsonSchema`. Same shape, same array of transforms. The underlying schema is now also consumed by the MCP plugin for runtime validation, not just type generation.
|
|
1146
1361
|
|
|
1147
1362
|
```diff
|
|
@@ -1153,9 +1368,9 @@ Stored content is unaffected.
|
|
|
1153
1368
|
}
|
|
1154
1369
|
```
|
|
1155
1370
|
|
|
1156
|
-
###
|
|
1371
|
+
### Rich text output schemas have been renamed
|
|
1157
1372
|
|
|
1158
|
-
|
|
1373
|
+
The Rich Text adapter's `outputSchema` property has been renamed to `jsonSchema`. The arguments object also gains `typeStringDefinitions: Set<string>` — a sink for raw TS source appended to `payload-types.ts`.
|
|
1159
1374
|
|
|
1160
1375
|
```diff
|
|
1161
1376
|
myAdapter: RichTextAdapter = {
|
|
@@ -1168,7 +1383,7 @@ Same story on the editor side. The arguments object also gains `typeStringDefini
|
|
|
1168
1383
|
|
|
1169
1384
|
`@payloadcms/richtext-lexical` is updated. Only third-party adapters need attention.
|
|
1170
1385
|
|
|
1171
|
-
###
|
|
1386
|
+
### Schema generation now returns definitions alongside JSON Schema
|
|
1172
1387
|
|
|
1173
1388
|
`configToJSONSchema` (from `payload`) used to return a `JSONSchema4` directly. It now returns an object with the schema plus the TS-source sink. Destructure to pull the schema out.
|
|
1174
1389
|
|
|
@@ -1179,7 +1394,7 @@ Same story on the editor side. The arguments object also gains `typeStringDefini
|
|
|
1179
1394
|
|
|
1180
1395
|
If you run your own type generation, append `[...typeStringDefinitions].join('\n\n')` to the compiled output. Otherwise ignore it.
|
|
1181
1396
|
|
|
1182
|
-
###
|
|
1397
|
+
### Field schema generation now uses object arguments
|
|
1183
1398
|
|
|
1184
1399
|
`fieldsToJSONSchema` used to take 6 positional arguments. It now takes one object. The new shape also requires `typeStringDefinitions`, and `forceInlineBlocks` is a top-level sibling instead of nested under `opts`.
|
|
1185
1400
|
|
|
@@ -1203,9 +1418,9 @@ If you run your own type generation, append `[...typeStringDefinitions].join('\n
|
|
|
1203
1418
|
+ })
|
|
1204
1419
|
```
|
|
1205
1420
|
|
|
1206
|
-
###
|
|
1421
|
+
### Entity schema generation now requires type definitions
|
|
1207
1422
|
|
|
1208
|
-
|
|
1423
|
+
`entityToJSONSchema` now requires a `typeStringDefinitions` argument at position 5, between `defaultIDType` and `collectionIDFieldTypes`. The old `opts: ConfigToJSONSchemaOptions` is replaced by an optional positional `forceInlineBlocks?: boolean` at the end.
|
|
1209
1424
|
|
|
1210
1425
|
```diff
|
|
1211
1426
|
entityToJSONSchema(
|
|
@@ -1221,7 +1436,7 @@ Inserted at position 5 (between `defaultIDType` and `collectionIDFieldTypes`). T
|
|
|
1221
1436
|
)
|
|
1222
1437
|
```
|
|
1223
1438
|
|
|
1224
|
-
### Lexical
|
|
1439
|
+
### Lexical nodes now provide their own JSON Schemas
|
|
1225
1440
|
|
|
1226
1441
|
`generatedTypes.modifyJSONSchema` on lexical features (and the sanitized `modifyJSONSchemas` array) is removed. Features now contribute per-node JSON Schemas via `createNode({ jsonSchema, node })` instead of mutating a whole-field schema after the fact. The editor composes per-node schemas into the field's union automatically.
|
|
1227
1442
|
|
|
@@ -1277,7 +1492,7 @@ The same rule applies to lexical block nodes — they now emit as `SerializedBlo
|
|
|
1277
1492
|
|
|
1278
1493
|
When two **different** blocks resolve to the same auto-derived name (same slug, different fields — e.g. a shared block overridden in one collection), the first keeps the clean name and any later block with a different shape gets a stable content-hash suffix (`Hero_3F2A1B0C`), so neither is silently mistyped. To choose the name yourself — or to resolve a collision with another type in your generated file (a collection, an array's `interfaceName`, etc.) — set an explicit `interfaceName` on the block. See [Block interface name collisions](/docs/typescript/generating-types#block-interface-name-collisions).
|
|
1279
1494
|
|
|
1280
|
-
###
|
|
1495
|
+
### Block references now share the blocks array
|
|
1281
1496
|
|
|
1282
1497
|
The `blockReferences` property on blocks fields has been removed. The `blocks` property now accepts both inline block configs and string slugs for blocks defined in the top-level Payload Config `blocks` array.
|
|
1283
1498
|
|
|
@@ -1308,7 +1523,7 @@ Inline blocks continue to work as before:
|
|
|
1308
1523
|
}
|
|
1309
1524
|
```
|
|
1310
1525
|
|
|
1311
|
-
### Lexical
|
|
1526
|
+
### Generated Lexical types are now strict
|
|
1312
1527
|
|
|
1313
1528
|
`richText` fields backed by `lexicalEditor` used to emit a permissive `{ [k: string]: unknown }` per field. They now emit a discriminated union of strict per-node interfaces (`SerializedTextNode`, `SerializedParagraphNode`, `SerializedHeadingNode`, `SerializedBlockNode`, etc.) under a hash-named alias like `LexicalNodes_AB12CD34`.
|
|
1314
1529
|
|
|
@@ -1339,7 +1554,7 @@ Element nodes are now generic over their `children`, and you pass the node union
|
|
|
1339
1554
|
|
|
1340
1555
|
`SerializedTextNode` is a leaf, so it stays bare. For the built-in nodes plus a few of your own, `WithDefaultNodes` threads the union for you - pass your custom node(s) as the generic (`type MyNodes = WithDefaultNodes<SerializedBlockNode<MyBlockData>>`), and they're usable anywhere a node can appear, not just at the top level. `DefaultTypedEditorState` with only built-in nodes needs no change.
|
|
1341
1556
|
|
|
1342
|
-
### Generated JSON Schema uses
|
|
1557
|
+
### Generated JSON Schema now uses modern definitions
|
|
1343
1558
|
|
|
1344
1559
|
Payload builds a JSON Schema from your config to generate `payload-types.ts`. That schema now stores its reusable types under the modern `$defs` key instead of the older `definitions` key, and every internal reference points at `#/$defs/...` instead of `#/definitions/...`. This follows the current JSON Schema spec.
|
|
1345
1560
|
|
|
@@ -1397,7 +1612,7 @@ jobs: {
|
|
|
1397
1612
|
|
|
1398
1613
|
Job documents can contain inputs, outputs, logs, and execution state, so avoid enabling write access unless your application specifically requires it.
|
|
1399
1614
|
|
|
1400
|
-
###
|
|
1615
|
+
### Job queries and hooks now have fixed behavior ([#16414](https://github.com/payloadcms/payload/pull/16414))
|
|
1401
1616
|
|
|
1402
1617
|
The deprecated `jobs.depth` and `jobs.runHooks` config options have been removed. Job operations now always use direct database calls with depth `0`.
|
|
1403
1618
|
|
|
@@ -1405,7 +1620,7 @@ If you relied on either option, remove it from your `jobs` config. If you depend
|
|
|
1405
1620
|
|
|
1406
1621
|
The `jobsCollectionOverrides` warning that detected extra hooks and prompted users to set `runHooks` has also been removed.
|
|
1407
1622
|
|
|
1408
|
-
###
|
|
1623
|
+
### The legacy base job type has been removed
|
|
1409
1624
|
|
|
1410
1625
|
The deprecated `BaseJob` type is no longer exported. Use `Job` everywhere you previously used `BaseJob`.
|
|
1411
1626
|
|
|
@@ -1463,9 +1678,9 @@ The deprecated `RunningJob` and `RunningJobSimple` types have also been removed.
|
|
|
1463
1678
|
|
|
1464
1679
|
JSON workflow steps without a `task` must now provide an `inlineTask` handler. This removes a type escape that previously allowed an incomplete step even though it could not run.
|
|
1465
1680
|
|
|
1466
|
-
###
|
|
1681
|
+
### Custom database adapters must support job claiming
|
|
1467
1682
|
|
|
1468
|
-
|
|
1683
|
+
Custom database adapters must now implement `updateJobs` so Payload can use a unique `processingToken` when claiming jobs and prevent multiple workers from running the same job. This adds an internal, nullable `processingToken` field to the `payload-jobs` collection. Existing SQL databases need a migration that adds this column. MongoDB does not require a migration.
|
|
1469
1684
|
|
|
1470
1685
|
The default `updateJobs` implementation has been removed because it could not safely claim jobs across multiple workers. If you maintain a custom database adapter, you must now provide `updateJobs`. It must only claim jobs that still match the provided `where` condition and, when a `processingToken` is provided, return only the jobs claimed with that token. The official Payload database adapters already implement this behavior.
|
|
1471
1686
|
|
|
@@ -1477,7 +1692,7 @@ Worker updates use a 30-second safety buffer by default, so an update only start
|
|
|
1477
1692
|
|
|
1478
1693
|
Existing SQL databases need a migration that replaces `processing` with the nullable, indexed `processingUntil` column. MongoDB does not require a migration.
|
|
1479
1694
|
|
|
1480
|
-
###
|
|
1695
|
+
### Job task results no longer include state ([#16416](https://github.com/payloadcms/payload/pull/16416))
|
|
1481
1696
|
|
|
1482
1697
|
Job task handlers and `inlineTask` callbacks can no longer use the `state` field to signal success or failure. Both shapes were deprecated in v3 and are now removed from tasks and inline tasks.
|
|
1483
1698
|
|
|
@@ -1502,7 +1717,7 @@ const myTask: TaskHandler<'MyTask'> = async ({ input }) => {
|
|
|
1502
1717
|
}
|
|
1503
1718
|
```
|
|
1504
1719
|
|
|
1505
|
-
###
|
|
1720
|
+
### Cron expressions now require explicit ranges ([#16546](https://github.com/payloadcms/payload/pull/16546))
|
|
1506
1721
|
|
|
1507
1722
|
The `sloppyRanges: true` setting that kept Croner v9 stepping syntax working after the v10 upgrade has been removed. Croner now enforces stricter cron parsing rules, so stepping expressions must use either `*` or an explicit range before the `/`.
|
|
1508
1723
|
|
|
@@ -1534,7 +1749,7 @@ Migrate any affected cron expressions:
|
|
|
1534
1749
|
+ cron: '30 * * * *'
|
|
1535
1750
|
```
|
|
1536
1751
|
|
|
1537
|
-
###
|
|
1752
|
+
### Relationship and upload limits now use row-based options ([#16547](https://github.com/payloadcms/payload/pull/16547))
|
|
1538
1753
|
|
|
1539
1754
|
The deprecated `min` and `max` properties have been removed from `relationship` and `upload` fields with `hasMany: true`. Use `minRows` and `maxRows` instead.
|
|
1540
1755
|
|
|
@@ -1553,7 +1768,7 @@ The deprecated `min` and `max` properties have been removed from `relationship`
|
|
|
1553
1768
|
|
|
1554
1769
|
The same change applies to `upload` fields with `hasMany: true`.
|
|
1555
1770
|
|
|
1556
|
-
###
|
|
1771
|
+
### Nested localized fields no longer need a compatibility flag ([#16693](https://github.com/payloadcms/payload/pull/16693))
|
|
1557
1772
|
|
|
1558
1773
|
`config.compatibility.allowLocalizedWithinLocalized` and the `PAYLOAD_DO_NOT_SANITIZE_LOCALIZED_PROPERTY` environment variable have been removed. Sanitize no longer strips `localized: true` from fields nested under a localized parent — `fieldShouldBeLocalized` decides this at runtime instead.
|
|
1559
1774
|
|
|
@@ -1567,7 +1782,7 @@ The same change applies to `upload` fields with `hasMany: true`.
|
|
|
1567
1782
|
- Remove the `compatibility` block from your config. If your MongoDB data still has the redundant nested-localized shape, flatten the config and run a data migration.
|
|
1568
1783
|
- Replace any direct `field.localized` reads in custom code with `fieldShouldBeLocalized({ field, parentIsLocalized })` from `payload/shared`.
|
|
1569
1784
|
|
|
1570
|
-
###
|
|
1785
|
+
### Publishing now defaults to the active locale
|
|
1571
1786
|
|
|
1572
1787
|
The Admin UI Publish button now always defaults to publishing only the active locale when a collection or global has localized fields and you have publish permission. "Publish all locales" is available as a secondary option in the Publish button's dropdown. This previously required opting in via `localization.defaultLocalePublishOption: 'active'`; the config option has been removed since active-locale-only is now the permanent default.
|
|
1573
1788
|
|
|
@@ -1589,7 +1804,7 @@ export default buildConfig({
|
|
|
1589
1804
|
|
|
1590
1805
|
Run `npx @payloadcms/codemod --transform remove-default-locale-publish-option` to remove the property automatically. The codemod surfaces a note for any config that had it set to `'all'`, since that combination previously changed the default and now has no config equivalent.
|
|
1591
1806
|
|
|
1592
|
-
### Storage adapters
|
|
1807
|
+
### Storage adapters have moved to top-level configuration ([#16596](https://github.com/payloadcms/payload/pull/16596))
|
|
1593
1808
|
|
|
1594
1809
|
Storage adapter packages (`@payloadcms/storage-s3`, `-azure`, `-gcs`, `-r2`, `-vercel-blob`) must now be placed under the top-level `storage` key in your Payload config instead of inside `plugins`.
|
|
1595
1810
|
|
|
@@ -1677,6 +1892,8 @@ If you typed `clientUploads.access`, replace `ClientUploadsAccess` with `UploadI
|
|
|
1677
1892
|
|
|
1678
1893
|
#### If you maintain a custom storage adapter
|
|
1679
1894
|
|
|
1895
|
+
File versioning adds a required `GeneratedAdapter.copyFile` method. Copy `from` to `to` (complete storage keys) without changing the source, reject an existing destination, and return only once the destination is readable. `moveFile` stays optional. See [Custom Storage Adapters](../upload/storage-adapters) for the full contract.
|
|
1896
|
+
|
|
1680
1897
|
Replace `clientUploadContext` with `uploadReference` in generated file metadata and static handler parameters. `handleUpload` no longer receives it because Payload skips uploading files that are already stored.
|
|
1681
1898
|
|
|
1682
1899
|
Set `uploadInstructions.enabled`. When false, Payload adds `adminHandler` to the import map without rendering it. When true, Payload also renders it and exposes the instructions and supporting endpoint.
|
|
@@ -1759,7 +1976,7 @@ uploadInstructions: {
|
|
|
1759
1976
|
|
|
1760
1977
|
Both are optional. Custom handler props are now passed as `props`, and the supporting route is available as `endpointPath` instead of `serverHandlerPath`. The old Admin component `enabled` prop was removed.
|
|
1761
1978
|
|
|
1762
|
-
###
|
|
1979
|
+
### The Uploadthing storage adapter has been removed
|
|
1763
1980
|
|
|
1764
1981
|
The Uploadthing storage adapter, deprecated in a previous release, has been deleted and will no longer be published. Migrate to another storage adapter such as `@payloadcms/storage-s3` before upgrading to 4.0.
|
|
1765
1982
|
|
|
@@ -1814,7 +2031,127 @@ azureStorage({
|
|
|
1814
2031
|
|
|
1815
2032
|
Run `npx @payloadcms/codemod --transform migrate-azure-chunk-large-files` to remove the flag automatically (it collapses `clientUploads: { chunkLargeFiles: true }` to `clientUploads: true` and keeps any other `clientUploads` options).
|
|
1816
2033
|
|
|
1817
|
-
###
|
|
2034
|
+
### Sharp is now an optional file transformer #sharp-moved-out-of-core-into-payloadcmstransformer-sharp
|
|
2035
|
+
|
|
2036
|
+
Image resizing is no longer built into `payload` itself. It now ships as an official [file transformer](../upload/transformers), `@payloadcms/transformer-sharp`, that you register under `upload.transformers`.
|
|
2037
|
+
|
|
2038
|
+
This affects four things:
|
|
2039
|
+
|
|
2040
|
+
1. The top-level `sharp` config property has been removed. Passing a Sharp instance now happens through `sharpTransformer({ sharp })` instead.
|
|
2041
|
+
2. Per-collection Sharp options — `constructorOptions`, `formatOptions`, `resizeOptions`, `trimOptions`, and `withMetadata` — have been removed from `CollectionConfig['upload']`. They're now authored through `sharpTransformer({ collections })`.
|
|
2042
|
+
- `imageSizes` has also been removed from `CollectionConfig['upload']`. Image sizes are now declared as `variants` on `sharpTransformer({ collections: { <slug>: { variants } } })`. The entries themselves (`name`, `width`, `height`, `generateImageName`, and so on) are unchanged. A collection that still sets `upload.imageSizes` fails the build.
|
|
2043
|
+
3. `sharp` is no longer a dependency of `payload` at all (previously a `devDependency`, not a runtime `dependency`). Install `@payloadcms/transformer-sharp` explicitly.
|
|
2044
|
+
4. The Sharp-specific types `SharpDependency`, `ImageUploadFormatOptions`, `ImageUploadTrimOptions`, and `SharpImageSizeOptions` are no longer exported from `payload`. Import `SharpDependency` from `@payloadcms/transformer-sharp`. For the option types, use `SharpCollectionConfig['formatOptions']`, `SharpCollectionConfig['trimOptions']`, and `NonNullable<SharpCollectionConfig['variants']>[number]` from the same package.
|
|
2045
|
+
|
|
2046
|
+
```sh
|
|
2047
|
+
pnpm add @payloadcms/transformer-sharp
|
|
2048
|
+
```
|
|
2049
|
+
|
|
2050
|
+
```diff
|
|
2051
|
+
+ import { sharpTransformer } from '@payloadcms/transformer-sharp'
|
|
2052
|
+
import { buildConfig } from 'payload'
|
|
2053
|
+
|
|
2054
|
+
export default buildConfig({
|
|
2055
|
+
collections: [
|
|
2056
|
+
{
|
|
2057
|
+
slug: 'media',
|
|
2058
|
+
upload: {
|
|
2059
|
+
- resizeOptions: { width: 2048, height: 2048 },
|
|
2060
|
+
- imageSizes: [{ name: 'thumbnail', width: 400, height: 300 }],
|
|
2061
|
+
},
|
|
2062
|
+
},
|
|
2063
|
+
],
|
|
2064
|
+
- sharp,
|
|
2065
|
+
upload: {
|
|
2066
|
+
+ transformers: [
|
|
2067
|
+
+ sharpTransformer({
|
|
2068
|
+
+ sharp,
|
|
2069
|
+
+ collections: {
|
|
2070
|
+
+ media: {
|
|
2071
|
+
+ resizeOptions: { width: 2048, height: 2048 },
|
|
2072
|
+
+ variants: [{ name: 'thumbnail', width: 400, height: 300 }],
|
|
2073
|
+
+ },
|
|
2074
|
+
+ },
|
|
2075
|
+
+ }),
|
|
2076
|
+
+ ],
|
|
2077
|
+
},
|
|
2078
|
+
})
|
|
2079
|
+
```
|
|
2080
|
+
|
|
2081
|
+
Run `npx @payloadcms/codemod --transform migrate-sharp-to-transformer` to migrate automatically. The codemod moves a top-level `sharp` dependency and inline collection Sharp options — including `crop` and `focalPoint` — into `sharpTransformer({ collections })` (writing `imageSizes` as `variants`), appends to an existing `upload.transformers` array if you already have one, and leaves anything it can't safely rewrite in place with a note — for example, a `collections` array built by a function call, or a collection defined outside the `buildConfig` call. Review those manually. For a config that already registers `sharpTransformer`, it only renames `imageSizes` to `variants` inside the existing `sharpTransformer` call and flags a leftover top-level `sharp` property; it doesn't move any remaining collection options.
|
|
2082
|
+
|
|
2083
|
+
Your `variants`, `crop`, and `focalPoint` are readable at `collection.upload.variants`/`.crop`/`.focalPoint` on the **sanitized** config (for example `req.payload.collections.media.config.upload.variants`). Code that read the sanitized `upload.imageSizes` in 3.x must read `upload.variants` instead. `sharpTransformer` writes a projection of what you configure back onto the sanitized collection at startup, so the Admin Panel and the rest of core see the same configuration. For a collection configured on `sharpTransformer`, a `crop` or `focalPoint` set on the transformer replaces the collection's own value; one left unset there keeps the collection's. Neither `imageSizes` nor `variants` can be set on the collection at all.
|
|
2084
|
+
|
|
2085
|
+
If you inject a custom Sharp build or version, pass it the same way:
|
|
2086
|
+
|
|
2087
|
+
```diff
|
|
2088
|
+
- buildConfig({ sharp: myCustomSharp })
|
|
2089
|
+
+ sharpTransformer({ sharp: myCustomSharp })
|
|
2090
|
+
```
|
|
2091
|
+
|
|
2092
|
+
`sharpTransformer` can also resize images at request time (`/api/media/file/photo.png?width=400`). This is new in Payload 4 and **disabled by default**, so a migrated config keeps serving only the original file and your pre-generated `variants`. The codemod doesn't enable it. To opt in, pass `dynamic: true` or `dynamic: { collections: ['media'] }` — see [Dynamic (request-time) resizing](../upload/transformers#dynamic-request-time-resizing), and [Securing dynamic resizing](../upload/transformers#securing-dynamic-resizing) before enabling it on a publicly readable collection.
|
|
2093
|
+
|
|
2094
|
+
#### Upload image data now uses variants
|
|
2095
|
+
|
|
2096
|
+
The group field that holds each generated image size on an upload document is now `variants` instead of `sizes`. Every place that reads or queries it changes:
|
|
2097
|
+
|
|
2098
|
+
```diff
|
|
2099
|
+
- doc.sizes?.thumbnail?.url
|
|
2100
|
+
+ doc.variants?.thumbnail?.url
|
|
2101
|
+
|
|
2102
|
+
- where: { 'sizes.thumbnail.filename': { equals: 'photo-400x300.png' } }
|
|
2103
|
+
+ where: { 'variants.thumbnail.filename': { equals: 'photo-400x300.png' } }
|
|
2104
|
+
|
|
2105
|
+
- select: { sizes: { thumbnail: true } }
|
|
2106
|
+
+ select: { variants: { thumbnail: true } }
|
|
2107
|
+
```
|
|
2108
|
+
|
|
2109
|
+
GraphQL queries select `variants { thumbnail { url } }` instead of `sizes { thumbnail { url } }`, and regenerated types expose `variants?: { ... }` (and `variants?: T | { ... }` on the `*Select` types) in place of `sizes`. Update any frontend code that reads `media.sizes`, including values you pass to helpers such as `getBestFitFromSizes({ sizes: doc.variants })`.
|
|
2110
|
+
|
|
2111
|
+
Existing documents must be migrated. Create the predefined migration for your database adapter and run it:
|
|
2112
|
+
|
|
2113
|
+
<Banner type="warning">
|
|
2114
|
+
Payload 4 uses `variants` as the top-level field for generated upload
|
|
2115
|
+
variants. If an upload collection already defines a custom field named
|
|
2116
|
+
`variants`, rename that field before you run the predefined migration. Choose
|
|
2117
|
+
a new name, update your config, and write a custom migration that moves the
|
|
2118
|
+
existing custom data from `variants` to the new field. Then run the predefined
|
|
2119
|
+
`sizes-to-variants` migration. If legacy `sizes` data or columns and custom
|
|
2120
|
+
`variants` data or columns are both present, the predefined migration stops
|
|
2121
|
+
before it changes data or indexes. It cannot choose a new name or merge this
|
|
2122
|
+
custom data for you.
|
|
2123
|
+
</Banner>
|
|
2124
|
+
|
|
2125
|
+
```sh
|
|
2126
|
+
# MongoDB
|
|
2127
|
+
payload migrate:create --file @payloadcms/db-mongodb/sizes-to-variants
|
|
2128
|
+
# Postgres
|
|
2129
|
+
payload migrate:create --file @payloadcms/db-postgres/sizes-to-variants
|
|
2130
|
+
# Vercel Postgres
|
|
2131
|
+
payload migrate:create --file @payloadcms/db-vercel-postgres/sizes-to-variants
|
|
2132
|
+
# SQLite
|
|
2133
|
+
payload migrate:create --file @payloadcms/db-sqlite/sizes-to-variants
|
|
2134
|
+
# Cloudflare D1
|
|
2135
|
+
payload migrate:create --file @payloadcms/db-d1-sqlite/sizes-to-variants
|
|
2136
|
+
|
|
2137
|
+
payload migrate
|
|
2138
|
+
```
|
|
2139
|
+
|
|
2140
|
+
**Postgres and SQLite (including Vercel Postgres and D1):** the columns are renamed in place from `sizes_<name>_<property>` to `variants_<name>_<property>`, on each upload collection's table and its versions table, along with their indexes. Use the predefined migration above **instead of** a plain `payload migrate:create` for this change. A generated migration would drop the `sizes_*` columns and add new `variants_*` columns, losing the stored image size data. The predefined migration also writes the schema snapshot, so the next generated migration starts from the renamed columns.
|
|
2141
|
+
|
|
2142
|
+
On MongoDB the migration renames `sizes` (and `version.sizes` on versions) with `$rename` and re-creates the indexes on those fields under the new path. Run the migration before the upgraded app writes to upload documents.
|
|
2143
|
+
|
|
2144
|
+
#### The file endpoint changes once any transformer is registered
|
|
2145
|
+
|
|
2146
|
+
Registering any transformer under `upload.transformers` — including `sharpTransformer()` with dynamic resizing off — routes every file request through the transformer-aware handler:
|
|
2147
|
+
|
|
2148
|
+
- A file must belong to an upload document to be served. A file that exists in storage without a matching document now returns `404`.
|
|
2149
|
+
- For files whose MIME type a transformer declares (every image, with `sharpTransformer()`), `access.read` can run twice per request: once with `req.fileTransform` set to `true` to decide a possible transform, and once without it for an ordinary read. Keep `access.read` cheap, and use `req.fileTransform` to restrict transformed reads — see [Access control](../upload/transformers#access-control).
|
|
2150
|
+
- Responses produced by a transformer always carry Payload's CORS headers, which `modifyResponseHeaders` can't override.
|
|
2151
|
+
|
|
2152
|
+
To link to a file from your own code, including with transformer query parameters, use the new `generatePayloadFileURL` helper (exported from `payload` and `payload/shared`). It always builds the access-controlled Payload file endpoint, so links never bypass `access.read` by pointing straight at storage.
|
|
2153
|
+
|
|
2154
|
+
### Storage prefixes are now always included
|
|
1818
2155
|
|
|
1819
2156
|
The `alwaysInsertFields` option (on `cloudStoragePlugin` and every `storage-*` adapter: `-s3`, `-azure`, `-gcs`, `-r2`, `-vercel-blob`) has been removed. The behavior it used to opt into — always inserting the hidden `prefix` field into the collection schema — is now unconditional, so the schema stays consistent whether or not a collection configures a `prefix` and whether or not the plugin is `enabled`.
|
|
1820
2157
|
|
|
@@ -1829,8 +2166,22 @@ The `alwaysInsertFields` option (on `cloudStoragePlugin` and every `storage-*` a
|
|
|
1829
2166
|
- Remove `alwaysInsertFields` from your storage adapter or `cloudStoragePlugin` config — it has no effect and no longer exists on the option types.
|
|
1830
2167
|
- For Postgres, SQLite, or D1 projects, run `payload migrate:create` to generate the migration that adds the `prefix` column, then run `payload migrate` to apply it.
|
|
1831
2168
|
|
|
2169
|
+
<<<<<<< HEAD
|
|
2170
|
+
|
|
2171
|
+
### Upload collections retain originals and file history
|
|
2172
|
+
|
|
2173
|
+
Upload collections now store `original` metadata and a `prefix` and `_objectKey` for each stored file (the main file, `original`, and each variant), so saved versions can find their files. New uploads keep their source under an `-original` filename. Update any code or infrastructure that relies on the old filename, URL, or object key.
|
|
2174
|
+
|
|
2175
|
+
**Postgres and SQLite:** after updating, run `payload migrate:create`, check that it adds these columns to each upload collection table and its versions table, then run `payload migrate`. Existing rows keep `null` values until their next write.
|
|
2176
|
+
|
|
1832
2177
|
### `FileSizeImproved` removed — use `FileSize` ([#16593](https://github.com/payloadcms/payload/pull/16593))
|
|
1833
2178
|
|
|
2179
|
+
=======
|
|
2180
|
+
|
|
2181
|
+
### File size types have been consolidated ([#16593](https://github.com/payloadcms/payload/pull/16593))
|
|
2182
|
+
|
|
2183
|
+
> > > > > > > 7dd8d28c6a (cleans up headings that contained code blocks, updates titles on migration guides)
|
|
2184
|
+
|
|
1834
2185
|
`FileSizeImproved` has been merged into `FileSize`. The `url`, `width`, `height`, `filesize`, `mimeType`, and `filename` properties now accept `null` directly on `FileSize`, matching what the database stores for sizes that were not generated.
|
|
1835
2186
|
|
|
1836
2187
|
```diff
|
|
@@ -1838,9 +2189,9 @@ The `alwaysInsertFields` option (on `cloudStoragePlugin` and every `storage-*` a
|
|
|
1838
2189
|
+ import type { FileSize } from 'payload'
|
|
1839
2190
|
```
|
|
1840
2191
|
|
|
1841
|
-
###
|
|
2192
|
+
### Image size admin controls have been consolidated ([#16593](https://github.com/payloadcms/payload/pull/16593))
|
|
1842
2193
|
|
|
1843
|
-
The
|
|
2194
|
+
The `disableListColumn`, `disableListFilter`, and `disableGroupBy` properties under `ImageSize.admin` have been replaced with a single `disabled` object, consistent with the shape used by all other fields.
|
|
1844
2195
|
|
|
1845
2196
|
```diff
|
|
1846
2197
|
{
|
|
@@ -1857,7 +2208,7 @@ The three individual boolean props have been replaced with a single `disabled` o
|
|
|
1857
2208
|
|
|
1858
2209
|
Run `npx @payloadcms/codemod --transform migrate-disabled-fields` to migrate automatically.
|
|
1859
2210
|
|
|
1860
|
-
###
|
|
2211
|
+
### MongoDB queries no longer use facet aggregation ([#16612](https://github.com/payloadcms/payload/pull/16612))
|
|
1861
2212
|
|
|
1862
2213
|
The `useFacet` option in `mongooseAdapter` config has been removed. Payload no longer uses `$facet` aggregation, so the option has no effect.
|
|
1863
2214
|
|
|
@@ -1869,7 +2220,7 @@ The `useFacet` option in `mongooseAdapter` config has been removed. Payload no l
|
|
|
1869
2220
|
})
|
|
1870
2221
|
```
|
|
1871
2222
|
|
|
1872
|
-
###
|
|
2223
|
+
### Search plugin API paths are now derived automatically ([#16597](https://github.com/payloadcms/payload/pull/16597))
|
|
1873
2224
|
|
|
1874
2225
|
The deprecated `apiBasePath` option on the search plugin has been removed. The plugin now derives the correct path from the root config automatically.
|
|
1875
2226
|
|
|
@@ -1881,7 +2232,7 @@ The sha1 HMAC fallback for API key authentication has been removed. API keys are
|
|
|
1881
2232
|
|
|
1882
2233
|
**How to migrate:** Open the user document for each affected API key holder and save it — this regenerates the key under sha256. Alternatively, generate a new API key from the admin panel.
|
|
1883
2234
|
|
|
1884
|
-
###
|
|
2235
|
+
### API key fields now use a factory
|
|
1885
2236
|
|
|
1886
2237
|
The `baseAPIKeyFields` export from `payload` has been removed. Use `createAPIKeyFields()` instead. It returns the same default auth API key storage fields, and also accepts overrides for projects or plugins that need to customize the generated field configs.
|
|
1887
2238
|
|
|
@@ -1910,9 +2261,9 @@ createAPIKeyFields({
|
|
|
1910
2261
|
})
|
|
1911
2262
|
```
|
|
1912
2263
|
|
|
1913
|
-
###
|
|
2264
|
+
### MCP plugin configuration and authentication have changed
|
|
1914
2265
|
|
|
1915
|
-
|
|
2266
|
+
`@payloadcms/plugin-mcp` has a new public config API, built-in tool inputs, and API-key authentication flow. Update your plugin config and recreate MCP credentials as user API keys.
|
|
1916
2267
|
|
|
1917
2268
|
**Collections and globals are now opt-out.** Drop the `enabled` flag. Every collection and global is exposed by default through the generic built-in tools. For collections, this includes tools like `getCollectionSchema`, `findDocuments`, `countDocuments`, `createDocuments`, `updateDocument`, `deleteDocuments`, `duplicateDocument`, and `findDistinct`, plus version tools when versions are enabled. For globals, this includes `getGlobalSchema`, `findGlobal`, and `updateGlobal`, plus version tools when versions are enabled. Turn individual operations off via simple config keys like `tools: { create: false }`.
|
|
1918
2269
|
|
|
@@ -2083,7 +2434,9 @@ By default, MCP items require an authenticated user. Built-in collection and glo
|
|
|
2083
2434
|
|
|
2084
2435
|
**Dependencies.** The plugin now builds on `@modelcontextprotocol/server` instead of `@modelcontextprotocol/sdk` + `mcp-handler`. It no longer depends on Zod directly; its built-in tools use the Zod Mini export from `payload`. If your custom tools use regular Zod, install Zod v4 in your project and import it directly.
|
|
2085
2436
|
|
|
2086
|
-
|
|
2437
|
+
**HTTP requests have a size limit.** `POST /api/mcp` now answers request bodies over 4 MiB with `413`. Base64 file uploads count toward this limit. Raise it with `mcp.maxRequestBodySize`.
|
|
2438
|
+
|
|
2439
|
+
### Admin UI styles now use CSS and semantic tokens
|
|
2087
2440
|
|
|
2088
2441
|
The Admin UI styles were migrated from SCSS to plain CSS. The `@payloadcms/ui/scss` package export and its SCSS source files have been removed, and the legacy `--theme-*` CSS variables were retired in favor of semantic `--color-*` tokens and the raw `--ramp-*` color palette.
|
|
2089
2442
|
|
|
@@ -2159,7 +2512,7 @@ If you previously used `rest: false`, remove the property; omitting `rest` disab
|
|
|
2159
2512
|
- `hasAPIKey` (3.x virtual field) is removed. Use `apiKeyLast4` and `docPermissions.fields.apiKey`.
|
|
2160
2513
|
- Custom `access.read` on the generated `apiKey` field is inert — reads are hardcoded to deny.
|
|
2161
2514
|
|
|
2162
|
-
###
|
|
2515
|
+
### Uploads now reject oversized files by default
|
|
2163
2516
|
|
|
2164
2517
|
The Payload-wide [`upload.abortOnLimit`](../upload/overview#payload-wide-upload-options) option now defaults to `true`. A file that is bigger than the configured `fileSize` limit now returns an HTTP 413 error by default. Previously, the file was truncated to the limit without any error, and the upload continued.
|
|
2165
2518
|
|