@payloadcms/codemod 4.0.0-internal.2f0d313 → 4.0.0-internal.31ba7ee

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.
Files changed (375) hide show
  1. package/README.md +69 -39
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +28 -9
  4. package/dist/cli.js.map +1 -1
  5. package/dist/cli.parseFlags.d.ts +10 -1
  6. package/dist/cli.parseFlags.d.ts.map +1 -1
  7. package/dist/cli.parseFlags.js +16 -8
  8. package/dist/cli.parseFlags.js.map +1 -1
  9. package/dist/registry.d.ts.map +1 -1
  10. package/dist/registry.js +15 -1
  11. package/dist/registry.js.map +1 -1
  12. package/dist/runbook/payload-v4-upgrade.md +2 -2
  13. package/dist/runbook/v4.mdx +556 -31
  14. package/dist/transforms/add-override-access-true/basic.input.js +41 -0
  15. package/dist/transforms/add-override-access-true/basic.input.js.map +1 -0
  16. package/dist/transforms/add-override-access-true/basic.output.js +50 -0
  17. package/dist/transforms/add-override-access-true/basic.output.js.map +1 -0
  18. package/dist/transforms/add-override-access-true/comment-between-properties.input.js +11 -0
  19. package/dist/transforms/add-override-access-true/comment-between-properties.input.js.map +1 -0
  20. package/dist/transforms/add-override-access-true/comment-between-properties.output.js +12 -0
  21. package/dist/transforms/add-override-access-true/comment-between-properties.output.js.map +1 -0
  22. package/dist/transforms/add-override-access-true/index.d.ts +20 -0
  23. package/dist/transforms/add-override-access-true/index.d.ts.map +1 -0
  24. package/dist/transforms/add-override-access-true/index.js +208 -0
  25. package/dist/transforms/add-override-access-true/index.js.map +1 -0
  26. package/dist/transforms/add-override-access-true/javascript.input.d.ts +6 -0
  27. package/dist/transforms/add-override-access-true/javascript.input.d.ts.map +1 -0
  28. package/dist/transforms/add-override-access-true/javascript.input.js +24 -0
  29. package/dist/transforms/add-override-access-true/javascript.input.js.map +1 -0
  30. package/dist/transforms/add-override-access-true/javascript.output.d.ts +6 -0
  31. package/dist/transforms/add-override-access-true/javascript.output.d.ts.map +1 -0
  32. package/dist/transforms/add-override-access-true/javascript.output.js +26 -0
  33. package/dist/transforms/add-override-access-true/javascript.output.js.map +1 -0
  34. package/dist/transforms/add-override-access-true/non-matching.input.js +42 -0
  35. package/dist/transforms/add-override-access-true/non-matching.input.js.map +1 -0
  36. package/dist/transforms/add-override-access-true/non-matching.output.js +42 -0
  37. package/dist/transforms/add-override-access-true/non-matching.output.js.map +1 -0
  38. package/dist/transforms/add-override-access-true/single-line.input.js +13 -0
  39. package/dist/transforms/add-override-access-true/single-line.input.js.map +1 -0
  40. package/dist/transforms/add-override-access-true/single-line.output.js +17 -0
  41. package/dist/transforms/add-override-access-true/single-line.output.js.map +1 -0
  42. package/dist/transforms/add-override-access-true/trailing-comment.input.js +10 -0
  43. package/dist/transforms/add-override-access-true/trailing-comment.input.js.map +1 -0
  44. package/dist/transforms/add-override-access-true/trailing-comment.output.js +12 -0
  45. package/dist/transforms/add-override-access-true/trailing-comment.output.js.map +1 -0
  46. package/dist/transforms/migrate-aliased-exports/index.d.ts.map +1 -1
  47. package/dist/transforms/migrate-aliased-exports/index.js +6 -5
  48. package/dist/transforms/migrate-aliased-exports/index.js.map +1 -1
  49. package/dist/transforms/migrate-aliased-exports/request.input.js +6 -0
  50. package/dist/transforms/migrate-aliased-exports/request.input.js.map +1 -0
  51. package/dist/transforms/migrate-aliased-exports/request.output.js +5 -0
  52. package/dist/transforms/migrate-aliased-exports/request.output.js.map +1 -0
  53. package/dist/transforms/migrate-authorship-default/already-set.input.js +15 -0
  54. package/dist/transforms/migrate-authorship-default/already-set.input.js.map +1 -0
  55. package/dist/transforms/migrate-authorship-default/already-set.output.js +15 -0
  56. package/dist/transforms/migrate-authorship-default/already-set.output.js.map +1 -0
  57. package/dist/transforms/migrate-authorship-default/basic.input.js +16 -0
  58. package/dist/transforms/migrate-authorship-default/basic.input.js.map +1 -0
  59. package/dist/transforms/migrate-authorship-default/basic.output.js +18 -0
  60. package/dist/transforms/migrate-authorship-default/basic.output.js.map +1 -0
  61. package/dist/transforms/migrate-authorship-default/build-config.input.js +32 -0
  62. package/dist/transforms/migrate-authorship-default/build-config.input.js.map +1 -0
  63. package/dist/transforms/migrate-authorship-default/build-config.output.js +35 -0
  64. package/dist/transforms/migrate-authorship-default/build-config.output.js.map +1 -0
  65. package/dist/transforms/migrate-authorship-default/globals.input.js +16 -0
  66. package/dist/transforms/migrate-authorship-default/globals.input.js.map +1 -0
  67. package/dist/transforms/migrate-authorship-default/globals.output.js +17 -0
  68. package/dist/transforms/migrate-authorship-default/globals.output.js.map +1 -0
  69. package/dist/transforms/migrate-authorship-default/index.d.ts +11 -0
  70. package/dist/transforms/migrate-authorship-default/index.d.ts.map +1 -0
  71. package/dist/transforms/migrate-authorship-default/index.js +141 -0
  72. package/dist/transforms/migrate-authorship-default/index.js.map +1 -0
  73. package/dist/transforms/migrate-authorship-default/non-matching.input.js +18 -0
  74. package/dist/transforms/migrate-authorship-default/non-matching.input.js.map +1 -0
  75. package/dist/transforms/migrate-authorship-default/non-matching.output.js +18 -0
  76. package/dist/transforms/migrate-authorship-default/non-matching.output.js.map +1 -0
  77. package/dist/transforms/migrate-authorship-default/satisfies.input.js +11 -0
  78. package/dist/transforms/migrate-authorship-default/satisfies.input.js.map +1 -0
  79. package/dist/transforms/migrate-authorship-default/satisfies.output.js +13 -0
  80. package/dist/transforms/migrate-authorship-default/satisfies.output.js.map +1 -0
  81. package/dist/transforms/migrate-field-component-types/basic.input.js +8 -0
  82. package/dist/transforms/migrate-field-component-types/basic.input.js.map +1 -0
  83. package/dist/transforms/migrate-field-component-types/basic.output.js +8 -0
  84. package/dist/transforms/migrate-field-component-types/basic.output.js.map +1 -0
  85. package/dist/transforms/migrate-field-component-types/index.d.ts +3 -0
  86. package/dist/transforms/migrate-field-component-types/index.d.ts.map +1 -0
  87. package/dist/transforms/migrate-field-component-types/index.js +616 -0
  88. package/dist/transforms/migrate-field-component-types/index.js.map +1 -0
  89. package/dist/transforms/migrate-payload-request-creation/aliases.input.js +8 -0
  90. package/dist/transforms/migrate-payload-request-creation/aliases.input.js.map +1 -0
  91. package/dist/transforms/migrate-payload-request-creation/aliases.output.js +31 -0
  92. package/dist/transforms/migrate-payload-request-creation/aliases.output.js.map +1 -0
  93. package/dist/transforms/migrate-payload-request-creation/collision.input.js +9 -0
  94. package/dist/transforms/migrate-payload-request-creation/collision.input.js.map +1 -0
  95. package/dist/transforms/migrate-payload-request-creation/comments.input.js +9 -0
  96. package/dist/transforms/migrate-payload-request-creation/comments.input.js.map +1 -0
  97. package/dist/transforms/migrate-payload-request-creation/comments.output.js +34 -0
  98. package/dist/transforms/migrate-payload-request-creation/comments.output.js.map +1 -0
  99. package/dist/transforms/migrate-payload-request-creation/empty.input.js +5 -0
  100. package/dist/transforms/migrate-payload-request-creation/empty.input.js.map +1 -0
  101. package/dist/transforms/migrate-payload-request-creation/empty.output.js +7 -0
  102. package/dist/transforms/migrate-payload-request-creation/empty.output.js.map +1 -0
  103. package/dist/transforms/migrate-payload-request-creation/evaluation-order.input.js +10 -0
  104. package/dist/transforms/migrate-payload-request-creation/evaluation-order.input.js.map +1 -0
  105. package/dist/transforms/migrate-payload-request-creation/expression.input.js +8 -0
  106. package/dist/transforms/migrate-payload-request-creation/expression.input.js.map +1 -0
  107. package/dist/transforms/migrate-payload-request-creation/expression.output.js +8 -0
  108. package/dist/transforms/migrate-payload-request-creation/expression.output.js.map +1 -0
  109. package/dist/transforms/migrate-payload-request-creation/getters.input.js +52 -0
  110. package/dist/transforms/migrate-payload-request-creation/getters.input.js.map +1 -0
  111. package/dist/transforms/migrate-payload-request-creation/index.d.ts +3 -0
  112. package/dist/transforms/migrate-payload-request-creation/index.d.ts.map +1 -0
  113. package/dist/transforms/migrate-payload-request-creation/index.js +236 -0
  114. package/dist/transforms/migrate-payload-request-creation/index.js.map +1 -0
  115. package/dist/transforms/migrate-payload-request-creation/internal.input.js +6 -0
  116. package/dist/transforms/migrate-payload-request-creation/internal.input.js.map +1 -0
  117. package/dist/transforms/migrate-payload-request-creation/internal.output.js +6 -0
  118. package/dist/transforms/migrate-payload-request-creation/internal.output.js.map +1 -0
  119. package/dist/transforms/migrate-payload-request-creation/non-matching.input.js +5 -0
  120. package/dist/transforms/migrate-payload-request-creation/non-matching.input.js.map +1 -0
  121. package/dist/transforms/migrate-payload-request-creation/populated.input.js +7 -0
  122. package/dist/transforms/migrate-payload-request-creation/populated.input.js.map +1 -0
  123. package/dist/transforms/migrate-payload-request-creation/populated.output.js +30 -0
  124. package/dist/transforms/migrate-payload-request-creation/populated.output.js.map +1 -0
  125. package/dist/transforms/migrate-payload-request-creation/shadowed.input.js +7 -0
  126. package/dist/transforms/migrate-payload-request-creation/shadowed.input.js.map +1 -0
  127. package/dist/transforms/migrate-payload-request-creation/shadowed.output.js +9 -0
  128. package/dist/transforms/migrate-payload-request-creation/shadowed.output.js.map +1 -0
  129. package/dist/transforms/migrate-payload-request-creation/shorthand.input.js +12 -0
  130. package/dist/transforms/migrate-payload-request-creation/shorthand.input.js.map +1 -0
  131. package/dist/transforms/migrate-payload-request-creation/shorthand.output.js +12 -0
  132. package/dist/transforms/migrate-payload-request-creation/shorthand.output.js.map +1 -0
  133. package/dist/transforms/migrate-payload-request-creation/unsupported.input.js +9 -0
  134. package/dist/transforms/migrate-payload-request-creation/unsupported.input.js.map +1 -0
  135. package/dist/transforms/migrate-versions-default/index.js +5 -5
  136. package/dist/transforms/migrate-versions-default/index.js.map +1 -1
  137. package/dist/transforms/remove-default-locale-publish-option/all-value.input.js +13 -0
  138. package/dist/transforms/remove-default-locale-publish-option/all-value.input.js.map +1 -0
  139. package/dist/transforms/remove-default-locale-publish-option/basic.input.js +13 -0
  140. package/dist/transforms/remove-default-locale-publish-option/basic.input.js.map +1 -0
  141. package/dist/transforms/remove-default-locale-publish-option/basic.output.js +12 -0
  142. package/dist/transforms/remove-default-locale-publish-option/basic.output.js.map +1 -0
  143. package/dist/transforms/remove-default-locale-publish-option/dynamic.input.js +17 -0
  144. package/dist/transforms/remove-default-locale-publish-option/dynamic.input.js.map +1 -0
  145. package/dist/transforms/remove-default-locale-publish-option/index.d.ts +10 -0
  146. package/dist/transforms/remove-default-locale-publish-option/index.d.ts.map +1 -0
  147. package/dist/transforms/remove-default-locale-publish-option/index.js +103 -0
  148. package/dist/transforms/remove-default-locale-publish-option/index.js.map +1 -0
  149. package/dist/transforms/remove-default-locale-publish-option/no-match.input.js +10 -0
  150. package/dist/transforms/remove-default-locale-publish-option/no-match.input.js.map +1 -0
  151. package/dist/transforms/remove-default-locale-publish-option/no-match.output.js +10 -0
  152. package/dist/transforms/remove-default-locale-publish-option/no-match.output.js.map +1 -0
  153. package/dist/transforms/remove-default-locale-publish-option/reassigned.input.js +22 -0
  154. package/dist/transforms/remove-default-locale-publish-option/reassigned.input.js.map +1 -0
  155. package/dist/transforms/remove-default-locale-publish-option/shorthand.input.js +14 -0
  156. package/dist/transforms/remove-default-locale-publish-option/shorthand.input.js.map +1 -0
  157. package/dist/transforms/remove-default-locale-publish-option/shorthand.output.js +13 -0
  158. package/dist/transforms/remove-default-locale-publish-option/shorthand.output.js.map +1 -0
  159. package/dist/transforms/remove-publish-specific-locale/index.d.ts.map +1 -1
  160. package/dist/transforms/remove-publish-specific-locale/index.js.map +1 -1
  161. package/dist/transforms/remove-storage-always-insert-fields/aliased.input.js +11 -0
  162. package/dist/transforms/remove-storage-always-insert-fields/aliased.input.js.map +1 -0
  163. package/dist/transforms/remove-storage-always-insert-fields/aliased.output.js +10 -0
  164. package/dist/transforms/remove-storage-always-insert-fields/aliased.output.js.map +1 -0
  165. package/dist/transforms/remove-storage-always-insert-fields/basic.input.js +54 -0
  166. package/dist/transforms/remove-storage-always-insert-fields/basic.input.js.map +1 -0
  167. package/dist/transforms/remove-storage-always-insert-fields/basic.output.js +48 -0
  168. package/dist/transforms/remove-storage-always-insert-fields/basic.output.js.map +1 -0
  169. package/dist/transforms/remove-storage-always-insert-fields/index.d.ts +3 -0
  170. package/dist/transforms/remove-storage-always-insert-fields/index.d.ts.map +1 -0
  171. package/dist/transforms/remove-storage-always-insert-fields/index.js +71 -0
  172. package/dist/transforms/remove-storage-always-insert-fields/index.js.map +1 -0
  173. package/dist/transforms/remove-storage-always-insert-fields/no-match.input.js +27 -0
  174. package/dist/transforms/remove-storage-always-insert-fields/no-match.input.js.map +1 -0
  175. package/dist/transforms/remove-storage-always-insert-fields/no-match.output.js +27 -0
  176. package/dist/transforms/remove-storage-always-insert-fields/no-match.output.js.map +1 -0
  177. package/dist/transforms/remove-versions-true/index.js +4 -4
  178. package/dist/transforms/remove-versions-true/index.js.map +1 -1
  179. package/dist/transforms/rename-experimental-table-feature/alias.input.js +6 -0
  180. package/dist/transforms/rename-experimental-table-feature/alias.input.js.map +1 -0
  181. package/dist/transforms/rename-experimental-table-feature/alias.output.js +6 -0
  182. package/dist/transforms/rename-experimental-table-feature/alias.output.js.map +1 -0
  183. package/dist/transforms/rename-experimental-table-feature/basic.input.js +19 -0
  184. package/dist/transforms/rename-experimental-table-feature/basic.input.js.map +1 -0
  185. package/dist/transforms/rename-experimental-table-feature/basic.output.js +19 -0
  186. package/dist/transforms/rename-experimental-table-feature/basic.output.js.map +1 -0
  187. package/dist/transforms/rename-experimental-table-feature/collision.input.js +7 -0
  188. package/dist/transforms/rename-experimental-table-feature/collision.input.js.map +1 -0
  189. package/dist/transforms/rename-experimental-table-feature/index.d.ts +3 -0
  190. package/dist/transforms/rename-experimental-table-feature/index.d.ts.map +1 -0
  191. package/dist/transforms/rename-experimental-table-feature/index.js +44 -0
  192. package/dist/transforms/rename-experimental-table-feature/index.js.map +1 -0
  193. package/dist/transforms/rename-experimental-table-feature/no-match.input.js +9 -0
  194. package/dist/transforms/rename-experimental-table-feature/no-match.input.js.map +1 -0
  195. package/dist/upgrade/defaultTag.d.ts +9 -0
  196. package/dist/upgrade/defaultTag.d.ts.map +1 -0
  197. package/dist/upgrade/defaultTag.js +25 -0
  198. package/dist/upgrade/defaultTag.js.map +1 -0
  199. package/dist/upgrade/dispatch.d.ts +38 -0
  200. package/dist/upgrade/dispatch.d.ts.map +1 -0
  201. package/dist/upgrade/dispatch.js +132 -0
  202. package/dist/upgrade/dispatch.js.map +1 -0
  203. package/dist/upgrade/index.d.ts +2 -1
  204. package/dist/upgrade/index.d.ts.map +1 -1
  205. package/dist/upgrade/index.js +49 -24
  206. package/dist/upgrade/index.js.map +1 -1
  207. package/dist/upgrade/prompt.d.ts +6 -3
  208. package/dist/upgrade/prompt.d.ts.map +1 -1
  209. package/dist/upgrade/prompt.js +11 -9
  210. package/dist/upgrade/prompt.js.map +1 -1
  211. package/dist/upgrade/report.d.ts +3 -0
  212. package/dist/upgrade/report.d.ts.map +1 -1
  213. package/dist/upgrade/report.js +8 -2
  214. package/dist/upgrade/report.js.map +1 -1
  215. package/dist/upgrade/rewritePackageJson.d.ts +7 -0
  216. package/dist/upgrade/rewritePackageJson.d.ts.map +1 -1
  217. package/dist/upgrade/rewritePackageJson.js +24 -7
  218. package/dist/upgrade/rewritePackageJson.js.map +1 -1
  219. package/dist/upgrade/selectAgent.d.ts +45 -0
  220. package/dist/upgrade/selectAgent.d.ts.map +1 -0
  221. package/dist/upgrade/selectAgent.js +54 -0
  222. package/dist/upgrade/selectAgent.js.map +1 -0
  223. package/dist/upgrade/selfCommand.d.ts +13 -0
  224. package/dist/upgrade/selfCommand.d.ts.map +1 -0
  225. package/dist/upgrade/selfCommand.js +17 -0
  226. package/dist/upgrade/selfCommand.js.map +1 -0
  227. package/dist/upgrade/types.d.ts +5 -0
  228. package/dist/upgrade/types.d.ts.map +1 -1
  229. package/dist/upgrade/types.js +4 -0
  230. package/dist/upgrade/types.js.map +1 -1
  231. package/package.json +4 -3
  232. package/dist/cli.parseFlags.spec.d.ts +0 -2
  233. package/dist/cli.parseFlags.spec.d.ts.map +0 -1
  234. package/dist/cli.parseFlags.spec.js +0 -121
  235. package/dist/cli.parseFlags.spec.js.map +0 -1
  236. package/dist/runner.spec.d.ts +0 -2
  237. package/dist/runner.spec.d.ts.map +0 -1
  238. package/dist/runner.spec.js +0 -88
  239. package/dist/runner.spec.js.map +0 -1
  240. package/dist/transforms/example-noop/index.spec.d.ts +0 -2
  241. package/dist/transforms/example-noop/index.spec.d.ts.map +0 -1
  242. package/dist/transforms/example-noop/index.spec.js +0 -29
  243. package/dist/transforms/example-noop/index.spec.js.map +0 -1
  244. package/dist/transforms/globals-components-edit/index.spec.d.ts +0 -2
  245. package/dist/transforms/globals-components-edit/index.spec.d.ts.map +0 -1
  246. package/dist/transforms/globals-components-edit/index.spec.js +0 -37
  247. package/dist/transforms/globals-components-edit/index.spec.js.map +0 -1
  248. package/dist/transforms/migrate-after-operation-read/index.spec.d.ts +0 -2
  249. package/dist/transforms/migrate-after-operation-read/index.spec.d.ts.map +0 -1
  250. package/dist/transforms/migrate-after-operation-read/index.spec.js +0 -62
  251. package/dist/transforms/migrate-after-operation-read/index.spec.js.map +0 -1
  252. package/dist/transforms/migrate-aliased-exports/index.spec.d.ts +0 -2
  253. package/dist/transforms/migrate-aliased-exports/index.spec.d.ts.map +0 -1
  254. package/dist/transforms/migrate-aliased-exports/index.spec.js +0 -55
  255. package/dist/transforms/migrate-aliased-exports/index.spec.js.map +0 -1
  256. package/dist/transforms/migrate-azure-chunk-large-files/index.spec.d.ts +0 -2
  257. package/dist/transforms/migrate-azure-chunk-large-files/index.spec.d.ts.map +0 -1
  258. package/dist/transforms/migrate-azure-chunk-large-files/index.spec.js +0 -67
  259. package/dist/transforms/migrate-azure-chunk-large-files/index.spec.js.map +0 -1
  260. package/dist/transforms/migrate-block-references-to-blocks/index.spec.d.ts +0 -2
  261. package/dist/transforms/migrate-block-references-to-blocks/index.spec.d.ts.map +0 -1
  262. package/dist/transforms/migrate-block-references-to-blocks/index.spec.js +0 -45
  263. package/dist/transforms/migrate-block-references-to-blocks/index.spec.js.map +0 -1
  264. package/dist/transforms/migrate-build-script/index.spec.d.ts +0 -2
  265. package/dist/transforms/migrate-build-script/index.spec.d.ts.map +0 -1
  266. package/dist/transforms/migrate-build-script/index.spec.js +0 -81
  267. package/dist/transforms/migrate-build-script/index.spec.js.map +0 -1
  268. package/dist/transforms/migrate-db-types-subpath/index.spec.d.ts +0 -2
  269. package/dist/transforms/migrate-db-types-subpath/index.spec.d.ts.map +0 -1
  270. package/dist/transforms/migrate-db-types-subpath/index.spec.js +0 -46
  271. package/dist/transforms/migrate-db-types-subpath/index.spec.js.map +0 -1
  272. package/dist/transforms/migrate-disabled-fields/index.spec.d.ts +0 -2
  273. package/dist/transforms/migrate-disabled-fields/index.spec.d.ts.map +0 -1
  274. package/dist/transforms/migrate-disabled-fields/index.spec.js +0 -73
  275. package/dist/transforms/migrate-disabled-fields/index.spec.js.map +0 -1
  276. package/dist/transforms/migrate-document-title-context/index.spec.d.ts +0 -2
  277. package/dist/transforms/migrate-document-title-context/index.spec.d.ts.map +0 -1
  278. package/dist/transforms/migrate-document-title-context/index.spec.js +0 -86
  279. package/dist/transforms/migrate-document-title-context/index.spec.js.map +0 -1
  280. package/dist/transforms/migrate-force-select/index.spec.d.ts +0 -2
  281. package/dist/transforms/migrate-force-select/index.spec.d.ts.map +0 -1
  282. package/dist/transforms/migrate-force-select/index.spec.js +0 -107
  283. package/dist/transforms/migrate-force-select/index.spec.js.map +0 -1
  284. package/dist/transforms/migrate-hide-api-url/index.spec.d.ts +0 -2
  285. package/dist/transforms/migrate-hide-api-url/index.spec.d.ts.map +0 -1
  286. package/dist/transforms/migrate-hide-api-url/index.spec.js +0 -118
  287. package/dist/transforms/migrate-hide-api-url/index.spec.js.map +0 -1
  288. package/dist/transforms/migrate-import-export-hooks/index.spec.d.ts +0 -2
  289. package/dist/transforms/migrate-import-export-hooks/index.spec.d.ts.map +0 -1
  290. package/dist/transforms/migrate-import-export-hooks/index.spec.js +0 -65
  291. package/dist/transforms/migrate-import-export-hooks/index.spec.js.map +0 -1
  292. package/dist/transforms/migrate-list-view-select-api/index.spec.d.ts +0 -2
  293. package/dist/transforms/migrate-list-view-select-api/index.spec.d.ts.map +0 -1
  294. package/dist/transforms/migrate-list-view-select-api/index.spec.js +0 -37
  295. package/dist/transforms/migrate-list-view-select-api/index.spec.js.map +0 -1
  296. package/dist/transforms/migrate-next-generate-viewport-export/index.spec.d.ts +0 -2
  297. package/dist/transforms/migrate-next-generate-viewport-export/index.spec.d.ts.map +0 -1
  298. package/dist/transforms/migrate-next-generate-viewport-export/index.spec.js +0 -41
  299. package/dist/transforms/migrate-next-generate-viewport-export/index.spec.js.map +0 -1
  300. package/dist/transforms/migrate-next-subpath-exports/index.spec.d.ts +0 -2
  301. package/dist/transforms/migrate-next-subpath-exports/index.spec.d.ts.map +0 -1
  302. package/dist/transforms/migrate-next-subpath-exports/index.spec.js +0 -46
  303. package/dist/transforms/migrate-next-subpath-exports/index.spec.js.map +0 -1
  304. package/dist/transforms/migrate-slug-field/index.spec.d.ts +0 -2
  305. package/dist/transforms/migrate-slug-field/index.spec.d.ts.map +0 -1
  306. package/dist/transforms/migrate-slug-field/index.spec.js +0 -47
  307. package/dist/transforms/migrate-slug-field/index.spec.js.map +0 -1
  308. package/dist/transforms/migrate-storage-adapters-to-config/index.spec.d.ts +0 -2
  309. package/dist/transforms/migrate-storage-adapters-to-config/index.spec.d.ts.map +0 -1
  310. package/dist/transforms/migrate-storage-adapters-to-config/index.spec.js +0 -85
  311. package/dist/transforms/migrate-storage-adapters-to-config/index.spec.js.map +0 -1
  312. package/dist/transforms/migrate-versions-default/index.spec.d.ts +0 -2
  313. package/dist/transforms/migrate-versions-default/index.spec.d.ts.map +0 -1
  314. package/dist/transforms/migrate-versions-default/index.spec.js +0 -81
  315. package/dist/transforms/migrate-versions-default/index.spec.js.map +0 -1
  316. package/dist/transforms/remove-group-by-true/index.spec.d.ts +0 -2
  317. package/dist/transforms/remove-group-by-true/index.spec.d.ts.map +0 -1
  318. package/dist/transforms/remove-group-by-true/index.spec.js +0 -47
  319. package/dist/transforms/remove-group-by-true/index.spec.js.map +0 -1
  320. package/dist/transforms/remove-localize-status-config/index.spec.d.ts +0 -2
  321. package/dist/transforms/remove-localize-status-config/index.spec.d.ts.map +0 -1
  322. package/dist/transforms/remove-localize-status-config/index.spec.js +0 -38
  323. package/dist/transforms/remove-localize-status-config/index.spec.js.map +0 -1
  324. package/dist/transforms/remove-publish-specific-locale/index.spec.d.ts +0 -2
  325. package/dist/transforms/remove-publish-specific-locale/index.spec.d.ts.map +0 -1
  326. package/dist/transforms/remove-publish-specific-locale/index.spec.js +0 -47
  327. package/dist/transforms/remove-publish-specific-locale/index.spec.js.map +0 -1
  328. package/dist/transforms/remove-versions-true/index.spec.d.ts +0 -2
  329. package/dist/transforms/remove-versions-true/index.spec.d.ts.map +0 -1
  330. package/dist/transforms/remove-versions-true/index.spec.js +0 -38
  331. package/dist/transforms/remove-versions-true/index.spec.js.map +0 -1
  332. package/dist/transforms/rename-storage-adapters-to-storage/index.spec.d.ts +0 -2
  333. package/dist/transforms/rename-storage-adapters-to-storage/index.spec.d.ts.map +0 -1
  334. package/dist/transforms/rename-storage-adapters-to-storage/index.spec.js +0 -55
  335. package/dist/transforms/rename-storage-adapters-to-storage/index.spec.js.map +0 -1
  336. package/dist/transforms/rename-typescript-schema-to-json-schema/index.spec.d.ts +0 -2
  337. package/dist/transforms/rename-typescript-schema-to-json-schema/index.spec.d.ts.map +0 -1
  338. package/dist/transforms/rename-typescript-schema-to-json-schema/index.spec.js +0 -45
  339. package/dist/transforms/rename-typescript-schema-to-json-schema/index.spec.js.map +0 -1
  340. package/dist/upgrade/detectPackageManager.spec.d.ts +0 -2
  341. package/dist/upgrade/detectPackageManager.spec.d.ts.map +0 -1
  342. package/dist/upgrade/detectPackageManager.spec.js +0 -41
  343. package/dist/upgrade/detectPackageManager.spec.js.map +0 -1
  344. package/dist/upgrade/index.spec.d.ts +0 -2
  345. package/dist/upgrade/index.spec.d.ts.map +0 -1
  346. package/dist/upgrade/index.spec.js +0 -132
  347. package/dist/upgrade/index.spec.js.map +0 -1
  348. package/dist/upgrade/packaging.spec.d.ts +0 -2
  349. package/dist/upgrade/packaging.spec.d.ts.map +0 -1
  350. package/dist/upgrade/packaging.spec.js +0 -29
  351. package/dist/upgrade/packaging.spec.js.map +0 -1
  352. package/dist/upgrade/prompt.spec.d.ts +0 -2
  353. package/dist/upgrade/prompt.spec.d.ts.map +0 -1
  354. package/dist/upgrade/prompt.spec.js +0 -51
  355. package/dist/upgrade/prompt.spec.js.map +0 -1
  356. package/dist/upgrade/report.spec.d.ts +0 -2
  357. package/dist/upgrade/report.spec.d.ts.map +0 -1
  358. package/dist/upgrade/report.spec.js +0 -87
  359. package/dist/upgrade/report.spec.js.map +0 -1
  360. package/dist/upgrade/resolveVersions.spec.d.ts +0 -2
  361. package/dist/upgrade/resolveVersions.spec.d.ts.map +0 -1
  362. package/dist/upgrade/resolveVersions.spec.js +0 -82
  363. package/dist/upgrade/resolveVersions.spec.js.map +0 -1
  364. package/dist/upgrade/rewritePackageJson.spec.d.ts +0 -2
  365. package/dist/upgrade/rewritePackageJson.spec.d.ts.map +0 -1
  366. package/dist/upgrade/rewritePackageJson.spec.js +0 -108
  367. package/dist/upgrade/rewritePackageJson.spec.js.map +0 -1
  368. package/dist/upgrade/runInstall.spec.d.ts +0 -2
  369. package/dist/upgrade/runInstall.spec.d.ts.map +0 -1
  370. package/dist/upgrade/runInstall.spec.js +0 -48
  371. package/dist/upgrade/runInstall.spec.js.map +0 -1
  372. package/dist/utils/packageJson.spec.d.ts +0 -2
  373. package/dist/utils/packageJson.spec.d.ts.map +0 -1
  374. package/dist/utils/packageJson.spec.js +0 -41
  375. package/dist/utils/packageJson.spec.js.map +0 -1
@@ -22,10 +22,259 @@ The codemod is idempotent and safe to run on a partially-migrated project.
22
22
 
23
23
  ## Breaking Changes
24
24
 
25
+ ### Payload request creation is consolidated
26
+
27
+ Payload request creation now uses `createPayloadRequest` for local contexts and `createPayloadRequestFromWebRequest` to convert a Web `Request`. Admin context setup is exposed as `initAdminContext`.
28
+
29
+ | Old name | New name |
30
+ | --------------------------------------- | ------------------------------------------- |
31
+ | `createLocalReq` | `createPayloadRequest` |
32
+ | `CreateLocalReqOptions` (internal type) | `Omit<CreatePayloadRequestArgs, 'payload'>` |
33
+ | `createPayloadRequest` | `createPayloadRequestFromWebRequest` |
34
+ | `initReq` | `initAdminContext` |
35
+ | `InitReqArgs` | `InitAdminContextArgs` |
36
+ | `InitReqResult` | `AdminContext` |
37
+ | `InitReqCache` | `AdminContextCache` |
38
+ | `InitReqPartialResult` | `PartialAdminContext` |
39
+
40
+ `initAdminContext` and its cache types are primarily contracts for framework adapters. Custom Root layout adapters must rename their injected `initReq` callback to `initAdminContext`.
41
+
42
+ Local request creation now takes a single object argument containing `payload`. For a request without options:
43
+
44
+ ```diff
45
+ -import { createLocalReq } from 'payload'
46
+ +import { createPayloadRequest } from 'payload'
47
+
48
+ -const req = await createLocalReq({}, payload)
49
+ +const req = await createPayloadRequest({ payload })
50
+ ```
51
+
52
+ `CreatePayloadRequestArgs` is exported from `payload` and requires `payload`. Options-only values should use `Omit<CreatePayloadRequestArgs, 'payload'>`. The old `CreateLocalReqOptions` type was declared in the internal request utility, not exported from the package root; relative/private imports of that type require manual migration. The codemod handles an existing named import of that type only when its source is `payload`.
53
+
54
+ Web request conversion uses its new role-specific name:
55
+
56
+ ```diff
57
+ -import { createPayloadRequest } from 'payload'
58
+ +import { createPayloadRequestFromWebRequest } from 'payload'
59
+
60
+ -const req = await createPayloadRequest({ config, request })
61
+ +const req = await createPayloadRequestFromWebRequest({ config, request })
62
+ ```
63
+
64
+ #### Codemod
65
+
66
+ To migrate automatically, there's a codemod for this change available by running:
67
+
68
+ ```bash
69
+ npx @payloadcms/codemod --transform migrate-payload-request-creation
70
+ ```
71
+
72
+ Automatic call migration requires a plain identifier payload argument and either `{}` or an initialized local `const` options identifier. The options type must expose all seven old option properties, or be `any`. For that identifier form, the generated argument captures payload first and uses forwarding getters for `context`, `depth`, `fallbackLocale`, `locale`, `req`, `urlSuffix`, and `user`. This preserves inherited getters, their read order, and rejected promises from throwing getters without reading unrelated properties. Complex expressions, mutable/unresolved options bindings, narrow option types, collisions, and unsupported type usages are left unchanged with notes for manual migration.
73
+
74
+ ### JWT authentication is versioned without moving token data
75
+
76
+ Payload 4 keeps the decoded JWT payload flat, so integrations can continue reading `id`, `collection`, `sid`, `email`, and fields selected by `saveToJWT` from the same paths as Payload 3.
77
+
78
+ Payload now reserves `id`, `collection`, `email`, `sid`, `iat`, and `exp`. A `saveToJWT` alias using one of those names is ignored, and Payload writes the trusted value after collecting fields configured with `saveToJWT`. This prevents an alias such as `saveToJWT: 'id'` or `saveToJWT: 'collection'` from changing which user or collection the signed token authenticates as.
79
+
80
+ The token's signed protected header now contains `authVersion: 1`. This marker identifies tokens issued by the fixed authentication format. It lives in the header, rather than the payload, because earlier configurations could alias a field to any payload key. Payload rejects tokens that do not contain the supported header version.
81
+
82
+ ```ts
83
+ // Decoded payload: unchanged paths
84
+ {
85
+ id: 'user-id',
86
+ collection: 'users',
87
+ sid: 'session-id',
88
+ email: 'user@example.com',
89
+ role: 'super-admin', // from saveToJWT
90
+ iat: 1750000000,
91
+ exp: 1750007200,
92
+ }
93
+
94
+ // Signed protected header
95
+ {
96
+ alg: 'HS256',
97
+ typ: 'JWT',
98
+ authVersion: 1,
99
+ }
100
+ ```
101
+
102
+ Tokens issued by a patched Payload 3 release already contain the same `authVersion: 1` protected-header marker and remain valid after upgrading to Payload 4, provided the signing secrets and authentication collection configuration remain compatible.
103
+
104
+ Tokens issued by an unpatched Payload release do not contain the marker and are rejected. There is no safe compatibility fallback because a legacy token might have been signed after its trusted identity fields were overwritten. When upgrading directly from an unpatched release:
105
+
106
+ - Users must sign in again after deployment.
107
+ - Integrations must reissue stored bearer tokens.
108
+ - Expect a temporary spike in `401` responses until clients discard their stored tokens.
109
+
110
+ Raw-token consumers do not need to change token data paths, and `/me` returns the same data for a valid new token. External validators and integrations that mint Payload-compatible JWTs must set and check the protected-header version. Import Payload's shared version instead of hardcoding it:
111
+
112
+ ```ts
113
+ import { decodeProtectedHeader } from 'jose'
114
+ import { JWT_AUTH_VERSION } from 'payload'
115
+
116
+ if (decodeProtectedHeader(token).authVersion !== JWT_AUTH_VERSION) {
117
+ throw new Error('Unsupported Payload token')
118
+ }
119
+ ```
120
+
121
+ A token is also rejected if its `collection` is not auth-enabled or has `disableLocalStrategy` enabled.
122
+
123
+ See [Token Data](../authentication/token-data) for the full token data shape.
124
+
125
+ ### `overrideAccess` now defaults to `false` on Local API operations
126
+
127
+ Local API operations whose optional `overrideAccess` value previously defaulted to `true` now default it to `false`, 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
+
129
+ ```diff
130
+ const posts = await payload.find({
131
+ collection: 'posts',
132
+ + overrideAccess: true, // preserves the Payload 3 default
133
+ })
134
+ ```
135
+
136
+ - `overrideAccess: false` (the new default) — respect Access Control. Use this whenever the operation acts on behalf of a user, and pass `user` alongside it.
137
+ - `overrideAccess: true` — bypass Access Control. Use this for trusted server-side work such as cron jobs, seeding, and migrations.
138
+
139
+ The property stays optional on the affected operations, and there is no compile error for either TypeScript or JavaScript projects — a call that omitted `overrideAccess` before still compiles after upgrading, it just behaves differently: it now enforces Access Control instead of skipping it. Run the codemod to find every affected call site and restore the previous behavior explicitly:
140
+
141
+ ```bash
142
+ npx @payloadcms/codemod
143
+ ```
144
+
145
+ Review the results afterwards. Leave the property off, or set it to `overrideAccess: false`, wherever the operation acts on behalf of a user — that is the more secure outcome and is now the default.
146
+
147
+ #### Cases the codemod leaves behind
148
+
149
+ Run a typecheck after the codemod. Three patterns it cannot resolve:
150
+
151
+ - **Spread arguments** — `payload.find({ ...args })`. The codemod cannot tell whether `args` already supplies the property.
152
+ - **Aliased or cast receivers** — `const db = payload; db.find({ ... })` and `(payload as any).find({ ... })`. It matches `payload`, `.payload`, and their `.jobs` properties only.
153
+ - **Non-literal arguments** — `payload.find(options)`. The codemod cannot safely modify the value at the call site.
154
+
155
+ #### Jobs operations use the same default
156
+
157
+ `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
+
25
159
  ### Collection and Global Access Control callbacks receive `slug`
26
160
 
27
161
  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.
28
162
 
163
+ ### Custom bin scripts replaced by native CLI commands
164
+
165
+ The `config.bin` API has been removed. Custom commands are now registered through `config.cli.commands` and use the same schema-backed CLI API as Payload's built-in commands.
166
+
167
+ In Payload 3.x, config entries connected a command name to a script path:
168
+
169
+ ```ts
170
+ // payload.config.ts
171
+ export default buildConfig({
172
+ bin: [
173
+ {
174
+ key: 'seed',
175
+ scriptPath: path.resolve(dirname, 'seed.ts'),
176
+ },
177
+ ],
178
+ })
179
+ ```
180
+
181
+ The imported module had to expose a specially named `script` function:
182
+
183
+ ```ts
184
+ // seed.ts
185
+ import type { SanitizedConfig } from 'payload'
186
+
187
+ import payload from 'payload'
188
+
189
+ export const script = async (config: SanitizedConfig) => {
190
+ await payload.init({ config })
191
+ // Seed the database...
192
+ process.exit(0)
193
+ }
194
+ ```
195
+
196
+ In Payload 4.0, define a real CLI command instead:
197
+
198
+ ```ts
199
+ // seed.ts
200
+ import { strictObject, z } from 'payload'
201
+ import { defineCLICommand } from 'payload/cli'
202
+
203
+ export const seedCommand = defineCLICommand({
204
+ description: 'Seed the database.',
205
+ input: strictObject({
206
+ clear: z
207
+ ._default(z.boolean(), false)
208
+ .check(z.describe('Delete existing documents first.')),
209
+ }),
210
+ handler: async ({ args, getPayload }) => {
211
+ const payload = await getPayload()
212
+
213
+ if (args.clear) {
214
+ // Delete existing documents...
215
+ }
216
+
217
+ // Seed the database...
218
+ payload.logger.info('Successfully seeded!')
219
+ },
220
+ })
221
+ ```
222
+
223
+ Register the command export in your Payload config:
224
+
225
+ ```ts
226
+ // payload.config.ts
227
+ export default buildConfig({
228
+ cli: {
229
+ commands: {
230
+ seed: './seed.js#seedCommand',
231
+ },
232
+ },
233
+ })
234
+ ```
235
+
236
+ When migrating custom scripts:
237
+
238
+ - Replace `bin` entries with entries in the `cli.commands` map. The map key becomes the command name.
239
+ - Replace the exported `script(config)` function with a schema-backed command created by `defineCLICommand`.
240
+ - Register commands by import path to keep them out of the Payload config's module graph. Paths use the existing `PayloadComponent` syntax, resolve relative to `payload.config.ts`, and support default or named exports. Direct `CLICommand` values are also accepted.
241
+ - Define the handler's semantic input with a Standard Schema implementation such as Zod. Supported top-level properties become options automatically; use `cli` only to mark positional arguments, hide fields, or override unusual shell syntax.
242
+ - Use the provided `getPayload()` function so Payload can shut down database connections after the command finishes.
243
+ - Return an exit code from the handler when needed. Do not call `process.exit()` from the command.
244
+
245
+ Payload adds built-in command import references to `cli.commands` while sanitizing the config, then applies project entries over those defaults. All built-ins are named exports from `payload/cli/builtin`, and Payload imports a referenced module only once even when it provides several commands. The sanitized map therefore contains the complete local CLI, and built-in and custom commands use the same loader. Custom commands appear in `payload --help` and receive Standard Schema validation and generated shell help. A command with the same map key as a built-in command replaces it, while `false` disables that command. Set `cli: false` to disable the entire Payload CLI. Command names are case-sensitive. Because custom commands must be registered before the CLI parses input, the Payload config is now loaded before displaying root CLI help.
246
+
247
+ See the [Payload CLI documentation](../configuration/cli) for the complete API.
248
+
249
+ ### `migrateCLI` removed
250
+
251
+ 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
+
253
+ If application code needs migration behavior rather than command-line parsing, call the database adapter directly through an initialized Payload instance:
254
+
255
+ ```ts
256
+ await payload.db.migrate()
257
+ ```
258
+
259
+ The adapter also exposes the corresponding `migrateDown`, `migrateFresh`, `migrateRefresh`, `migrateReset`, `migrateStatus`, and `createMigration` methods.
260
+
261
+ If you need to execute the real CLI from JavaScript, use the existing generic `payload.bin()` method. It starts the same CLI process a user would run in a terminal and returns its exit code:
262
+
263
+ ```ts
264
+ import payload from 'payload'
265
+
266
+ const { code } = await payload.bin({
267
+ args: ['migrate:create', '--file', migrationPath],
268
+ cwd: projectDirectory,
269
+ })
270
+ ```
271
+
272
+ The spawned CLI discovers the Payload config from `cwd` or `PAYLOAD_CONFIG_PATH`. Configure a custom migration directory through your database adapter instead of passing it as a programmatic CLI override.
273
+
274
+ ### Generator helpers now return result objects
275
+
276
+ `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
+
29
278
  ### Default query depth reduced to `1`
30
279
 
31
280
  Queries that omit `depth` now populate one relationship level instead of two. This reduces database work and response size by default.
@@ -35,6 +284,7 @@ If your application relies on two populated levels, prefer specifying `depth: 2`
35
284
  ```ts
36
285
  const posts = await payload.find({
37
286
  collection: 'posts',
287
+ overrideAccess: true,
38
288
  depth: 2,
39
289
  })
40
290
  ```
@@ -83,6 +333,56 @@ Run `migrate-versions-default` if you want zero schema changes — it adds `vers
83
333
 
84
334
  Run `remove-versions-true` to clean up any bare `versions: true` that is now the default.
85
335
 
336
+ ### Version read access inherits collection and global read access
337
+
338
+ When `readVersions` is not configured, version access now inherits the Collection or Global `read` Access Control instead of requiring an authenticated user by default.
339
+
340
+ This means a public `read` function can make historical and unpublished versions publicly readable, while a query constraint returned by `read` is also applied to version documents. If you want to preserve the previous authenticated-only behavior, configure `readVersions` explicitly:
341
+
342
+ ```ts
343
+ export const Posts: CollectionConfig = {
344
+ slug: 'posts',
345
+ access: {
346
+ read: () => true,
347
+ readVersions: ({ req: { user } }) => Boolean(user),
348
+ },
349
+ fields: [],
350
+ }
351
+ ```
352
+
353
+ ### Authorship (`createdBy` / `updatedBy`) is now enabled by default
354
+
355
+ 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
+
357
+ **What changes:**
358
+
359
+ - Every non-internal collection and global gains `createdBy` and `updatedBy` when your config has at least one auth collection. On SQL adapters this adds relationship rows (stored in each entity's `_rels` table), so existing projects need a database migration. In development the schema is pushed automatically; in production, generate a migration with `payload migrate:create` and run `payload migrate`. Existing documents are backfilled with `null`.
360
+ - `createdBy` is only derived on a document's first write, so editing a document whose `createdBy` is `null` (for example a migrated or anonymously created document) does not backfill the editor as the creator.
361
+ - Payload's internal system collections and globals are excluded automatically: `payload-jobs`, `payload-jobs-stats`, `payload-preferences`, `payload-locked-documents`, `payload-migrations`, `payload-query-presets`, and the KV collection.
362
+
363
+ **Plugins:** plugin-injected collections also receive authorship unless the plugin opts out. In this release:
364
+
365
+ - **Opted out** (no authorship): `@payloadcms/plugin-search`, `@payloadcms/payload-cloud`, and all `@payloadcms/plugin-ecommerce` collections.
366
+ - **Kept on** (they gain `createdBy` / `updatedBy`): `@payloadcms/plugin-redirects`, `@payloadcms/plugin-form-builder` (forms and form submissions), and `@payloadcms/plugin-import-export` (imports and exports). Existing projects using these plugins on a SQL adapter need a migration for those collections too. To opt one out, set `authorship: false` through that plugin's collection-override option.
367
+
368
+ **To opt out for an existing collection or global:**
369
+
370
+ ```diff
371
+ export const Posts: CollectionConfig = {
372
+ slug: 'posts',
373
+ fields: [],
374
+ + authorship: false,
375
+ }
376
+ ```
377
+
378
+ **A codemod is available** to preserve the previous behaviour:
379
+
380
+ ```bash
381
+ npx @payloadcms/codemod --transform migrate-authorship-default
382
+ ```
383
+
384
+ `migrate-authorship-default` adds `authorship: false` to every `CollectionConfig` and `GlobalConfig` in your source that does not already set `authorship`, so your own collections have zero schema changes. It only rewrites your config files — it cannot reach plugin-injected collections, so any plugin that keeps authorship on (above) still gains the fields and requires a migration (or an explicit `authorship: false` override).
385
+
86
386
  ### List View Select API is now the default
87
387
 
88
388
  The `admin.enableListViewSelectAPI` Collection Config property has been removed. The List View now always uses the [Select API](../queries/select) to query only the active columns, which was previously opt-in.
@@ -178,20 +478,20 @@ Several types and utilities that were re-exported from `@payloadcms/ui` and `@pa
178
478
 
179
479
  **Pass-through re-exports** — same name, new source:
180
480
 
181
- | Symbol | Old source | New source |
182
- | ----------------------------- | ---------------------------- | ---------------- |
183
- | `Column` | `@payloadcms/ui` | `payload` |
184
- | `ListViewSlots` | `@payloadcms/ui` | `payload` |
185
- | `ListViewClientProps` | `@payloadcms/ui` | `payload` |
186
- | `EntityType` | `@payloadcms/ui/shared` | `payload/shared` |
187
- | `formatAdminURL` | `@payloadcms/ui/shared` | `payload/shared` |
188
- | `mergeListSearchAndWhere` | `@payloadcms/ui/shared` | `payload/shared` |
189
- | `mergeHeaders` | `@payloadcms/next/utilities` | `payload` |
190
- | `headersWithCors` | `@payloadcms/next/utilities` | `payload` |
191
- | `createPayloadRequest` | `@payloadcms/next/utilities` | `payload` |
192
- | `addDataAndFileToRequest` | `@payloadcms/next/utilities` | `payload` |
193
- | `sanitizeLocales` | `@payloadcms/next/utilities` | `payload` |
194
- | `addLocalesToRequestFromData` | `@payloadcms/next/utilities` | `payload` |
481
+ | Symbol | Old source | New source |
482
+ | ----------------------------- | ---------------------------- | ------------------------------------------------- |
483
+ | `Column` | `@payloadcms/ui` | `payload` |
484
+ | `ListViewSlots` | `@payloadcms/ui` | `payload` |
485
+ | `ListViewClientProps` | `@payloadcms/ui` | `payload` |
486
+ | `EntityType` | `@payloadcms/ui/shared` | `payload/shared` |
487
+ | `formatAdminURL` | `@payloadcms/ui/shared` | `payload/shared` |
488
+ | `mergeListSearchAndWhere` | `@payloadcms/ui/shared` | `payload/shared` |
489
+ | `mergeHeaders` | `@payloadcms/next/utilities` | `payload` |
490
+ | `headersWithCors` | `@payloadcms/next/utilities` | `payload` |
491
+ | `createPayloadRequest` | `@payloadcms/next/utilities` | `createPayloadRequestFromWebRequest` in `payload` |
492
+ | `addDataAndFileToRequest` | `@payloadcms/next/utilities` | `payload` |
493
+ | `sanitizeLocales` | `@payloadcms/next/utilities` | `payload` |
494
+ | `addLocalesToRequestFromData` | `@payloadcms/next/utilities` | `payload` |
195
495
 
196
496
  **Renamed types** — use the new canonical name from `payload`:
197
497
 
@@ -210,7 +510,53 @@ Several types and utilities that were re-exported from `@payloadcms/ui` and `@pa
210
510
  + import { headersWithCors, mergeHeaders } from 'payload'
211
511
  ```
212
512
 
213
- Run `npx @payloadcms/codemod --transform migrate-aliased-exports` to migrate automatically. Renamed types are imported using an `as` alias (e.g. `import type { CollectionPreferences as ListPreferences } from 'payload'`) so existing usages keep compiling — drop the alias and rename usages manually if you want to fully commit to the new name.
513
+ Running `npx @payloadcms/codemod --transform migrate-aliased-exports` preserves old local bindings by importing renamed symbols with an `as` alias (e.g. `import type { CollectionPreferences as ListPreferences } from 'payload'` or `import { createPayloadRequestFromWebRequest as createPayloadRequest } from 'payload'`). Run `migrate-payload-request-creation` to complete the request helper's identifier and call-site migration, or drop the other aliases and rename their usages manually if you want to fully commit to the new names.
514
+
515
+ ### Field component types replaced by props types
516
+
517
+ Field component aliases that wrapped props in `React.ComponentType<Props>` have been removed. Import the props type directly so each component can use `React.FC<Props>` or a function declaration.
518
+
519
+ ```diff
520
+ - import type { TextFieldClientComponent } from 'payload'
521
+ + import type { TextFieldClientProps } from 'payload'
522
+ + import type React from 'react'
523
+
524
+ - export const CustomTextField: TextFieldClientComponent = (props) => {
525
+ + export const CustomTextField: React.FC<TextFieldClientProps> = (props) => {
526
+ // ...
527
+ }
528
+ ```
529
+
530
+ Generic type arguments move to the props type:
531
+
532
+ ```diff
533
+ - import type { FieldErrorClientComponent, TextFieldClient } from 'payload'
534
+ + import type { FieldErrorClientProps, TextFieldClient } from 'payload'
535
+ + import type React from 'react'
536
+
537
+ - const Error: FieldErrorClientComponent<TextFieldClient> = (props) => null
538
+ + const Error: React.FC<FieldErrorClientProps<TextFieldClient>> = (props) => null
539
+ ```
540
+
541
+ Function declarations can annotate their parameter without a component wrapper:
542
+
543
+ ```tsx
544
+ import type { TextFieldServerProps } from 'payload'
545
+
546
+ export default async function CustomTextField(props: TextFieldServerProps) {
547
+ // ...
548
+ }
549
+ ```
550
+
551
+ This covers main field components, labels, descriptions, errors, diffs, and block row labels. Admin-view types such as `AdminViewComponent` and `DocumentTabComponent` are separate.
552
+
553
+ #### Codemod
554
+
555
+ To migrate automatically, there's a codemod for this change available by running:
556
+
557
+ ```bash
558
+ npx @payloadcms/codemod --transform migrate-field-component-types
559
+ ```
214
560
 
215
561
  ### `DefaultValue` function variant is now typed to allow returning a `Promise` ([#17522](https://github.com/payloadcms/payload/pull/17522))
216
562
 
@@ -240,10 +586,9 @@ If you only use `DefaultValue` to type a `defaultValue` property passed into a C
240
586
 
241
587
  `SanitizedCollectionConfig` and its auth type no longer use `DeepRequired`. This reduces TypeScript checker work and makes optionality match runtime defaults.
242
588
 
243
- Properties Payload does not default, such as `access.readVersions`, `auth.cookies.domain`, `auth.useAPIKey`, and `auth.depth`, now require undefined handling:
589
+ Properties Payload does not default, such as `auth.cookies.domain`, `auth.useAPIKey`, and `auth.depth`, now require undefined handling:
244
590
 
245
591
  ```ts
246
- if (collection.access.readVersions) await collection.access.readVersions(args)
247
592
  const useAPIKey = collection.auth.useAPIKey ?? false
248
593
  ```
249
594
 
@@ -264,12 +609,11 @@ Properties filled during sanitization, including `auth.jwtOrder`, root routes, G
264
609
 
265
610
  ### Sanitized global config types now match runtime defaults
266
611
 
267
- `SanitizedGlobalConfig` no longer uses `DeepRequired`. This reduces TypeScript checker work and preserves optionality for values global sanitization does not populate, such as `access.readVersions`, `admin.components`, `graphQL`, `lockDocuments`, and `typescript`.
612
+ `SanitizedGlobalConfig` no longer uses `DeepRequired`. This reduces TypeScript checker work and preserves optionality for values global sanitization does not populate, such as `admin.components`, `graphQL`, `lockDocuments`, and `typescript`.
268
613
 
269
614
  Code that consumes these properties may now need to handle `undefined`:
270
615
 
271
616
  ```ts
272
- if (global.access.readVersions) await global.access.readVersions(args)
273
617
  const views = global.admin.components?.views
274
618
  const interfaceName = global.typescript?.interface
275
619
  ```
@@ -481,6 +825,36 @@ If you only used `useDocumentInfo` for `title` / `setDocumentTitle`, drop the im
481
825
 
482
826
  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 }`).
483
827
 
828
+ ### `useLocale` returns `null` without an active locale
829
+
830
+ 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
+
832
+ Custom Client Components that read locale properties directly must handle the nullable value:
833
+
834
+ ```diff
835
+ 'use client'
836
+ import { useLocale } from '@payloadcms/ui'
837
+
838
+ const Greeting: React.FC = () => {
839
+ - const { code } = useLocale()
840
+ + const locale = useLocale()
841
+ +
842
+ + if (!locale) {
843
+ + return null
844
+ + }
845
+ +
846
+ + const { code } = locale
847
+
848
+ return <span>{code}</span>
849
+ }
850
+ ```
851
+
852
+ When a missing locale is already valid for the receiving API, optional chaining is sufficient:
853
+
854
+ ```ts
855
+ const locale = useLocale()?.code
856
+ ```
857
+
484
858
  ### RichText adapter `i18n` property removed
485
859
 
486
860
  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.
@@ -713,9 +1087,9 @@ The deprecated `isHTMLElement` utility has been removed from `@payloadcms/richte
713
1087
 
714
1088
  `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.
715
1089
 
716
- ### Lexical: vendored `@lexical/markdown` fork removed and lexical upgraded to `0.48.0`
1090
+ ### Lexical: vendored `@lexical/markdown` fork removed and lexical upgraded to `0.52.0`
717
1091
 
718
- `@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.48.0`, and `@payloadcms/richtext-lexical/lexical/markdown` is a pure passthrough of `@lexical/markdown`.
1092
+ `@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`.
719
1093
 
720
1094
  Most projects use markdown through the editor or through `convertMarkdownToLexical` / `convertLexicalToMarkdown` and need no changes. You are only affected if you import from `@payloadcms/richtext-lexical/lexical/markdown` directly.
721
1095
 
@@ -739,7 +1113,16 @@ If you keep calling `$convertFromMarkdownString` directly, pass `editorConfig.fe
739
1113
 
740
1114
  **Whitespace-only markdown table cells now import as an empty paragraph** instead of a text node of padding spaces. This comes from the lexical upgrade, not Payload's own code. No action is required unless you assert on those padding spaces in stored content.
741
1115
 
742
- If you import lexical APIs directly through `@payloadcms/richtext-lexical/lexical/*`, also review the [lexical breaking changes](https://github.com/facebook/lexical/releases) between `0.41.0` and `0.48.0`. None of them require action for typical Payload usage.
1116
+ If you import lexical APIs directly through `@payloadcms/richtext-lexical/lexical/*`, also review the [lexical breaking changes](https://github.com/facebook/lexical/releases) between `0.41.0` and `0.52.0`. None of them require action for typical Payload usage.
1117
+
1118
+ If your app imports Lexical types or adds custom features, check these changes from `0.50.0`. They also apply to imports through `@payloadcms/richtext-lexical/lexical` and its subpaths:
1119
+
1120
+ - Lexical packages are ESM only. Use ESM imports; CommonJS `require()` needs Node.js 20.19 or later.
1121
+ - `SELECTION_CHANGE_COMMAND` now runs before DOM reconciliation. Defer DOM lookups and floating UI positioning with `$onUpdate(() => editor.read('latest', callback))`, or use an update listener for work that should run after every commit.
1122
+ - Call `node.getLatest().exportJSON()` when serializing a node reference retained across an update. Payload continues to write the full JSON format; compact export is not enabled.
1123
+ - `SerializedEditorState<T>`, `SerializedElementNode<T>`, and `SerializedRootNode<T>` no longer accept a type argument. Remove `<T>` if you only need the base node type. For a document with your own node types, use Payload's `TypedEditorState<T>` instead. Without a type argument, Lexical's `children` are still typed as `SerializedLexicalNode[]`.
1124
+ - `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
+ - `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.
743
1126
 
744
1127
  ### Lexical: `EXPERIMENTAL_TableFeature` renamed to `TableFeature`
745
1128
 
@@ -988,6 +1371,32 @@ And update any `$ref` pointers you wrote by hand to point at `$defs`:
988
1371
 
989
1372
  Do not keep your additions on `definitions` while new ones land on `$defs`.
990
1373
 
1374
+ ### Jobs: access is enforced by default
1375
+
1376
+ The generated `payload-jobs` collection now denies generic create, read, update, and delete access by default. This affects REST and GraphQL CRUD, plus Local API collection operations because `overrideAccess` now defaults to `false`.
1377
+
1378
+ Dedicated operations such as `payload.jobs.queue()`, `payload.jobs.run()`, `payload.jobs.runByID()`, `payload.jobs.cancel()`, and `payload.jobs.cancelByID()` also default `overrideAccess` to `false`. They now run the corresponding `jobs.access.queue`, `jobs.access.run`, or `jobs.access.cancel` function when the option is omitted. Add `overrideAccess: true` to trusted server-side calls that must preserve the Payload 3 behavior of bypassing Jobs access control.
1379
+
1380
+ If your application intentionally exposes the internal payload-jobs collection, configure only the access it needs through `jobsCollectionOverrides`. For example, the following enables read-only Admin Panel access for trusted administrators:
1381
+
1382
+ ```ts
1383
+ jobs: {
1384
+ jobsCollectionOverrides: ({ defaultJobsCollection }) => ({
1385
+ ...defaultJobsCollection,
1386
+ access: {
1387
+ ...defaultJobsCollection.access,
1388
+ read: ({ req }) => Boolean(req.user?.roles?.includes('admin')),
1389
+ },
1390
+ admin: {
1391
+ ...defaultJobsCollection.admin,
1392
+ hidden: false,
1393
+ },
1394
+ }),
1395
+ }
1396
+ ```
1397
+
1398
+ Job documents can contain inputs, outputs, logs, and execution state, so avoid enabling write access unless your application specifically requires it.
1399
+
991
1400
  ### Jobs: `jobs.depth` and `jobs.runHooks` have been removed ([#16414](https://github.com/payloadcms/payload/pull/16414))
992
1401
 
993
1402
  The deprecated `jobs.depth` and `jobs.runHooks` config options have been removed. Job operations now always use direct database calls with depth `0`.
@@ -1158,6 +1567,28 @@ The same change applies to `upload` fields with `hasMany: true`.
1158
1567
  - 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.
1159
1568
  - Replace any direct `field.localized` reads in custom code with `fieldShouldBeLocalized({ field, parentIsLocalized })` from `payload/shared`.
1160
1569
 
1570
+ ### Admin UI Publish button now defaults to the active locale, and `localization.defaultLocalePublishOption` has been removed
1571
+
1572
+ 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
+
1574
+ **What changes:**
1575
+
1576
+ - `localization.defaultLocalePublishOption` no longer exists as a config property or type — remove it from your config.
1577
+ - If you had it set to `'active'`, there is no change in Admin UI behavior.
1578
+ - If you had it set to `'all'`, or left it unset, the Publish button now publishes only the active locale by default. Use the "Publish all locales" option in the button's dropdown to publish every locale at once.
1579
+
1580
+ ```diff
1581
+ export default buildConfig({
1582
+ localization: {
1583
+ defaultLocale: 'en',
1584
+ - defaultLocalePublishOption: 'active',
1585
+ locales: ['en', 'es'],
1586
+ },
1587
+ })
1588
+ ```
1589
+
1590
+ 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
+
1161
1592
  ### Storage adapters must be declared under `storage`, not `plugins` ([#16596](https://github.com/payloadcms/payload/pull/16596))
1162
1593
 
1163
1594
  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`.
@@ -1383,6 +1814,21 @@ azureStorage({
1383
1814
 
1384
1815
  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).
1385
1816
 
1817
+ ### Storage adapters always insert the `prefix` field now — `alwaysInsertFields` removed
1818
+
1819
+ 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
+
1821
+ **Who is affected:**
1822
+
1823
+ - Anyone with an upload collection that uses a storage adapter or `cloudStoragePlugin`. Every such collection now gets a hidden `prefix` field, including collections that never configured a `prefix` and collections where the plugin is disabled (`enabled: false`).
1824
+ - Anyone who set `alwaysInsertFields` explicitly — the option no longer exists on any storage adapter or plugin option type.
1825
+ - Postgres, SQLite, and D1 users: the new field is a new database column. Running `payload migrate:create` will generate a migration that adds a `prefix` column to every affected collection's table.
1826
+
1827
+ **How to migrate:**
1828
+
1829
+ - Remove `alwaysInsertFields` from your storage adapter or `cloudStoragePlugin` config — it has no effect and no longer exists on the option types.
1830
+ - 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
+
1386
1832
  ### `FileSizeImproved` removed — use `FileSize` ([#16593](https://github.com/payloadcms/payload/pull/16593))
1387
1833
 
1388
1834
  `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.
@@ -1468,7 +1914,7 @@ createAPIKeyFields({
1468
1914
 
1469
1915
  The MCP plugin was refactored. The public config API, the built-in tool inputs, and API-key authentication changed. Update your plugin config and recreate MCP credentials as user API keys.
1470
1916
 
1471
- **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`, `createDocument`, `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 }`.
1917
+ **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 }`.
1472
1918
 
1473
1919
  ```diff
1474
1920
  mcpPlugin({
@@ -1533,7 +1979,9 @@ const result = await req.payload.find({
1533
1979
  })
1534
1980
  ```
1535
1981
 
1536
- For custom collection and global tools, do not add `collectionSlug` / `globalSlug` yourself. Payload adds the slug input to the MCP schema automatically, and the handler receives the resolved slug as a top-level argument.
1982
+ Collection and global tool calls now use `slug` instead of `collectionSlug` or `globalSlug`. Update saved tool inputs and any code that calls these tools directly.
1983
+
1984
+ For custom collection and global tools, do not add `slug` yourself. Payload adds it to the MCP schema automatically, and the handler receives the resolved slug as a top-level argument.
1537
1985
 
1538
1986
  ```ts
1539
1987
  defineCollectionTool({
@@ -1541,24 +1989,41 @@ defineCollectionTool({
1541
1989
  input: z.object({
1542
1990
  id: z.string(),
1543
1991
  }),
1544
- }).handler(async ({ collectionSlug, input, req }) => {
1545
- // collectionSlug is resolved from the tool call
1992
+ }).handler(async ({ slug, input, req }) => {
1993
+ // slug is resolved from the tool call
1546
1994
  })
1547
1995
  ```
1548
1996
 
1549
- **Built-in collection tools are now generic.** Per-collection names like `createPosts` / `updatePosts` were replaced by `createDocument` / `updateDocument`. Pass the target collection as `collectionSlug`; document fields live under `data` alongside options like `depth`, `draft` and `locale`. For update and delete calls, pass `id` or `where` as top-level arguments. `_status`, `id`, `createdAt` and `updatedAt` are no longer accepted inside `data`; set publish state with a custom tool if you need to.
1997
+ **Built-in collection tools are now generic.** Per-collection names like `createPosts` / `updatePosts` were replaced by `createDocuments` / `updateDocument`. Pass the target collection as `slug`. For creates, pass a `documents` array whose items contain `data` and an optional `file`; for updates, pass `data` with an `id` or `where` at the top level. Options such as `depth`, `draft`, and `locale` also remain at the top level. `_status`, `id`, `createdAt`, and `updatedAt` are no longer accepted inside `data`; set publish state with a custom tool if you need to.
1550
1998
 
1551
1999
  ```ts
1552
- // arguments to a `createDocument` tool call
2000
+ // arguments to a `createDocuments` tool call
1553
2001
 
1554
2002
  // before
1555
2003
  { title: 'Hello', _status: 'draft', draft: true, depth: 2 }
1556
2004
 
1557
2005
  // after
1558
- { collectionSlug: 'posts', data: { title: 'Hello' }, draft: true, depth: 2 }
2006
+ {
2007
+ slug: 'posts',
2008
+ documents: [{ data: { title: 'Hello' } }],
2009
+ draft: true,
2010
+ depth: 2,
2011
+ }
2012
+ ```
2013
+
2014
+ `createDocuments` and `updateDocument` now return affected document IDs instead of complete documents by default. Add `returning: true` to preserve the previous response. If the call uses `select`, it must also set `returning: true`.
2015
+
2016
+ **MCP URL uploads require an explicit allow-list.** Previously, MCP could download a URL when the upload collection's `pasteURL` was unset. Configure `upload.pasteURL.allowList` for collections that accept MCP URL uploads. The initial URL and any redirect targets must match; `skipSafeFetch` does not bypass this requirement. Base64 and upload-reference inputs are unaffected.
2017
+
2018
+ ```ts
2019
+ upload: {
2020
+ pasteURL: {
2021
+ allowList: [{ hostname: 'cdn.example.com', protocol: 'https' }],
2022
+ },
2023
+ }
1559
2024
  ```
1560
2025
 
1561
- **Auth tools are per-collection and opt-in.** The old `experimental.tools.auth` block is gone. Opt in per auth collection via its `tools` map, and pass the target auth collection as `collectionSlug`. Like every MCP item, auth tools now use default MCP access unless you provide an `access` callback, so unauthenticated auth tools such as `login` need `access: () => true`.
2026
+ **Auth tools are per-collection and opt-in.** The old `experimental.tools.auth` block is gone. Opt in per auth collection via its `tools` map, and pass the target auth collection as `slug`. Like every MCP item, auth tools now use default MCP access unless you provide an `access` callback, so unauthenticated auth tools such as `login` need `access: () => true`.
1562
2027
 
1563
2028
  ```diff
1564
2029
  - mcpPlugin({ experimental: { tools: { auth: { enabled: true } } } })
@@ -1616,7 +2081,7 @@ Because the `payload-mcp-api-keys` collection config was removed, run your norma
1616
2081
 
1617
2082
  By default, MCP items require an authenticated user. Built-in collection and global tools also include Payload permission checks in their default `access` callbacks. If you provide an `access` callback for a built-in tool, it replaces the default callback.
1618
2083
 
1619
- **Dependencies.** The plugin now builds on `@modelcontextprotocol/server` instead of `@modelcontextprotocol/sdk` + `mcp-handler`, and moved to `zod` v4. If your custom tools import `zod` directly, make sure it resolves to v4.
2084
+ **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.
1620
2085
 
1621
2086
  ### `@payloadcms/ui/scss` export removed and legacy `--theme-*` tokens retired
1622
2087
 
@@ -1650,3 +2115,63 @@ The Admin UI styles were migrated from SCSS to plain CSS. The `@payloadcms/ui/sc
1650
2115
  The `--theme-elevation-*` scale was removed. Use the semantic `--color-*` tokens (e.g. `--color-bg`, `--color-text`, `--color-border`, `--color-icon`) or the raw `--ramp-*` palette instead. See [Customizing CSS](../admin/customizing-css) for the full list of available tokens.
1651
2116
 
1652
2117
  Any custom component `.scss` files you authored should be renamed to `.css` and updated to plain CSS syntax — Payload no longer ships a SCSS toolchain.
2118
+
2119
+ ### Ecommerce Stripe confirmations use an atomic transaction claim
2120
+
2121
+ Stripe confirmation now atomically claims the existing ecommerce transaction before creating an order,
2122
+ marking the cart as purchased, and decrementing inventory. Retries return the canonical order only when
2123
+ the succeeded transaction and order are linked in both directions. Ambiguous state fails closed.
2124
+
2125
+ PostgreSQL users must generate and run a database migration that adds `processing` to the ecommerce
2126
+ transaction status enum. No order field, unique index, or data backfill is required. The public
2127
+ `confirm-order` response continues to include `transactionID`, including on retries. This is a breaking
2128
+ security change for custom payment adapters: after provider validation, they must call the new
2129
+ `finalizeOrder` argument instead of creating orders themselves. The bundled Stripe adapter is updated in
2130
+ the same release. See the [payment confirmation guidance](../ecommerce/payments#confirmation-retries) for
2131
+ transactionless database failure behavior and reconciliation guidance.
2132
+
2133
+ ### Stripe REST proxy requires an explicit method allowlist
2134
+
2135
+ `@payloadcms/plugin-stripe` no longer accepts `rest: true`. To enable the REST proxy in v4, use
2136
+ an object with a non-empty `allowedMethods` array of exact Stripe SDK method names, such as
2137
+ `subscriptions.list`:
2138
+
2139
+ ```diff
2140
+ stripePlugin({
2141
+ - rest: true,
2142
+ + rest: {
2143
+ + allowedMethods: ['subscriptions.list'],
2144
+ + },
2145
+ })
2146
+ ```
2147
+
2148
+ Wildcards are not supported. The optional `rest.access` callback can further restrict the
2149
+ endpoint. Without it, the proxy requires an authenticated user allowed to access the Payload
2150
+ admin panel. See the [Stripe REST proxy documentation](../plugins/stripe#stripe-rest-proxy) for
2151
+ configuration and security guidance.
2152
+
2153
+ If you previously used `rest: false`, remove the property; omitting `rest` disables the proxy.
2154
+
2155
+ ### API keys: v4-only changes on top of the shared 3.x/4 security fix
2156
+
2157
+ - `apiKeyLast4` is a new persisted field on every `auth.useAPIKey` collection. Generate and run a schema migration. Legacy rows backfill on the next successful API-key authentication.
2158
+ - `enableAPIKey` is removed. Revoke by setting `apiKey: null`.
2159
+ - `hasAPIKey` (3.x virtual field) is removed. Use `apiKeyLast4` and `docPermissions.fields.apiKey`.
2160
+ - Custom `access.read` on the generated `apiKey` field is inert — reads are hardcoded to deny.
2161
+
2162
+ ### Upload `abortOnLimit` now defaults to `true`
2163
+
2164
+ 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
+
2166
+ If your application depends on the old truncate-on-limit behavior, set `abortOnLimit: false` explicitly:
2167
+
2168
+ ```diff
2169
+ export default buildConfig({
2170
+ upload: {
2171
+ + abortOnLimit: false,
2172
+ limits: {
2173
+ fileSize: 5000000,
2174
+ },
2175
+ },
2176
+ })
2177
+ ```