Skip to content

feat!: replace draft operation option with version and action - #17918

Draft
nathanlentz wants to merge 30 commits into
mainfrom
feat/version-action-api
Draft

feat!: replace draft operation option with version and action#17918
nathanlentz wants to merge 30 commits into
mainfrom
feat/version-action-api

Conversation

@nathanlentz

@nathanlentz nathanlentz commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

This PR removes the public draft operation option and replaces it with version on reads and operation-specific action on writes.

Summary

The Boolean draft argument mixed content selection with publication transitions and conflicted with _status. Reads now select published, latest, or draft. Writes now declare saveDraft, publish, or unpublish. The same contract applies to Local API, REST, GraphQL, and the SDK.

How

  • Shared resolvers normalize intent once. Explicit action wins, then recognized _status, then the operation default.
  • Create and duplicate default to saveDraft. Update and restore default to publish. _status never infers unpublish.
  • typescript.strictDraftTypes is removed. Local API and SDK version/action typing is always strict starting in v4
  • Document _status, versions.drafts, UI copy, and low-level persistence booleans stay. Only the public operation argument is removed.
  • @payloadcms/codemod includes migrate-version-action-api for unambiguous call sites and notes for the rest.

New APIs at a glance

find() // published only
find({ version: 'published' }) // published only, explicit
find({ version: 'latest' }) // newest draft, else published
find({ version: 'draft' }) // draft only, no published fallback

create({ data }) // save draft
create({ data, action: 'saveDraft' }) // save draft, explicit
create({ data, action: 'publish' }) // publish
create({ data: { _status: 'published', ... } }) // publish, inferred from _status

update({ id, data, action: 'saveDraft' }) // save draft
update({ id, data, action: 'publish' }) // publish
update({ id, action: 'unpublish' }) // unpublish
update({ id, data }) // publish (omitted action defaults to publish)
update({ id, data: { _status: 'draft' } }) // save draft, inferred from _status
update({ id, data: { _status: 'published' } }) // publish, inferred from _status
update({ id, data, action: 'publish', locale: 'all' }) // publish all locales
update({ id, data, action: 'publish', locale: 'en' }) // publish English
update({ id, data, action: 'publish' }) // publish current/default locale
update({ id, action: 'unpublish', locale: 'all' }) // unpublish all locales
update({ id, action: 'unpublish', locale: 'en' }) // unpublish English
update({ id, action: 'unpublish' }) // unpublish current/default locale
update({ id, data, action: 'saveDraft', autosave: true }) // autosave, valid only with saveDraft

Breaking changes

  • Public read/write operations reject leftover draft. REST returns 400. GraphQL rejects the argument. Local API and SDK types reject it.
  • Omitted update with no recognized _status publishes, including draft-only documents.
  • There is no v4 compatibility shim and no strictVersionTypes replacement.

Create and duplicate now resolve write intent through resolveAction, so omitted action saves a draft and status is canonicalized from the effective action before validation.
Lock omitted REST create to saveDraft and prove duplicate infers from _status, honors conflicting action, and never treats unrecognized status as unpublish.
Replace GraphQL Boolean draft arguments with shared ReadVersion, CreateAction, UpdateAction, and RestoreAction enums so reads and writes use the same semantic strings as REST and the Local API.
Local reads and writes now match REST/GraphQL: leftover `draft` is no longer mapped at runtime. Callers use `version` and `action`, and hierarchy path computation honors the read version for localized ancestors.
@nathanlentz nathanlentz changed the title feat: replace draft operation option with version and action feat!: replace draft operation option with version and action Aug 24, 2026
@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

📦 esbuild Bundle Analysis for payload

This analysis was generated by esbuild-bundle-analyzer. 🤖

Meta File Out File Size (raw) Note
packages/next/meta_index.json esbuild/index.js 214.77 KB ✅ No change
packages/payload/meta_index.json esbuild/index.js 1.80 MB ⚠️ +9.55 KB (+0.5%)
packages/payload/meta_shared.json esbuild/exports/shared.js 549.31 KB ✅ No change
packages/richtext-lexical/meta_client.json esbuild/exports/client_optimized/index.js 286.46 KB ✅ -42 B (-0.0%)
packages/ui/meta_client.json esbuild/exports/client_optimized/index.js 36.54 KB ✅ No change
packages/ui/meta_shared.json esbuild/exports/shared_optimized/index.js 18.95 KB ✅ No change
Largest paths These visualization shows top 20 largest paths in the bundle.

Meta file: packages/next/meta_index.json, Out file: esbuild/index.js

Path Size
../../node_modules ${{\color{Goldenrod}{ ████████████████████████▊ }}}$ 99.0%, 210.74 KB
dist/adapters/router.js ${{\color{Goldenrod}{ }}}$ 0.3%, 718 B
dist/adapters/server.js ${{\color{Goldenrod}{ }}}$ 0.3%, 533 B
dist/adapters/layout.js ${{\color{Goldenrod}{ }}}$ 0.2%, 526 B
dist/adapters/views.js ${{\color{Goldenrod}{ }}}$ 0.2%, 409 B
dist/esbuildEntry.js ${{\color{Goldenrod}{ }}}$ 0.0%, 0 B

Meta file: packages/payload/meta_index.json, Out file: esbuild/index.js

Path Size
../../node_modules ${{\color{Goldenrod}{ ██████████████████▏ }}}$ 72.6%, 1.30 MB
dist/collections/operations ${{\color{Goldenrod}{ ▋ }}}$ 2.7%, 48.80 KB
dist/fields/hooks ${{\color{Goldenrod}{ ▋ }}}$ 2.5%, 45.02 KB
dist/utilities/configToJSONSchema.js ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 15.88 KB
dist/auth/operations ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 15.60 KB
dist/globals/operations ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 15.31 KB
dist/queues/operations ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 14.29 KB
dist/fields/config ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 13.63 KB
dist/utilities/telemetry ${{\color{Goldenrod}{ ▏ }}}$ 0.7%, 11.88 KB
dist/fields/validations.js ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 10.76 KB
dist/collections/config ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 9.94 KB
dist/cli/commands ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 9.92 KB
dist/config/orderable ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 8.12 KB
dist/database/migrations ${{\color{Goldenrod}{ }}}$ 0.4%, 7.99 KB
dist/uploads/fetchAPI-multipart ${{\color{Goldenrod}{ }}}$ 0.4%, 7.87 KB
dist/index.js ${{\color{Goldenrod}{ }}}$ 0.4%, 7.79 KB
dist/hierarchy/utils ${{\color{Goldenrod}{ }}}$ 0.4%, 7.74 KB
dist/utilities/entityInputSchema ${{\color{Goldenrod}{ }}}$ 0.4%, 7.34 KB
dist/config/sanitize.js ${{\color{Goldenrod}{ }}}$ 0.4%, 7.07 KB
dist/collections/endpoints ${{\color{Goldenrod}{ }}}$ 0.4%, 6.32 KB
(other) ${{\color{Goldenrod}{ ██████▊ }}}$ 27.4%, 489.73 KB

Meta file: packages/payload/meta_shared.json, Out file: esbuild/exports/shared.js

Path Size
../../node_modules ${{\color{Goldenrod}{ ██████████████████████▎ }}}$ 89.2%, 485.60 KB
dist/fields/validations.js ${{\color{Goldenrod}{ ▌ }}}$ 2.0%, 10.73 KB
dist/fields/config ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 5.83 KB
dist/utilities/traverseFields.js ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 4.45 KB
dist/collections/config ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 3.33 KB
dist/config/orderable ${{\color{Goldenrod}{ ▏ }}}$ 0.6%, 3.13 KB
dist/fields/baseFields ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 2.79 KB
dist/utilities/deepCopyObject.js ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 2.69 KB
dist/config/client.js ${{\color{Goldenrod}{ ▏ }}}$ 0.5%, 2.69 KB
dist/auth/cookies.js ${{\color{Goldenrod}{ }}}$ 0.3%, 1.55 KB
dist/utilities/flattenTopLevelFields.js ${{\color{Goldenrod}{ }}}$ 0.3%, 1.42 KB
dist/utilities/getVersionsConfig.js ${{\color{Goldenrod}{ }}}$ 0.2%, 1.04 KB
dist/globals/config ${{\color{Goldenrod}{ }}}$ 0.2%, 939 B
dist/utilities/flattenAllFields.js ${{\color{Goldenrod}{ }}}$ 0.1%, 794 B
dist/utilities/unflatten.js ${{\color{Goldenrod}{ }}}$ 0.1%, 779 B
dist/utilities/sanitizeUserDataForEmail.js ${{\color{Goldenrod}{ }}}$ 0.1%, 713 B
dist/auth/extractJWT.js ${{\color{Goldenrod}{ }}}$ 0.1%, 696 B
dist/utilities/getFieldPermissions.js ${{\color{Goldenrod}{ }}}$ 0.1%, 651 B
dist/utilities/getSafeRedirect.js ${{\color{Goldenrod}{ }}}$ 0.1%, 632 B
dist/errors/ValidationError.js ${{\color{Goldenrod}{ }}}$ 0.1%, 577 B
(other) ${{\color{Goldenrod}{ ██▋ }}}$ 10.8%, 59.04 KB

Meta file: packages/richtext-lexical/meta_client.json, Out file: esbuild/exports/client_optimized/index.js

Path Size
dist/features/blocks ${{\color{Goldenrod}{ ███▎ }}}$ 13.1%, 37.20 KB
dist/lexical/ui ${{\color{Goldenrod}{ ███ }}}$ 12.1%, 34.20 KB
dist/lexical/plugins ${{\color{Goldenrod}{ ██▉ }}}$ 11.7%, 33.01 KB
dist/features/table ${{\color{Goldenrod}{ ██▍ }}}$ 9.6%, 27.18 KB
dist/features/link ${{\color{Goldenrod}{ █▋ }}}$ 6.6%, 18.82 KB
dist/features/toolbars ${{\color{Goldenrod}{ █▌ }}}$ 6.2%, 17.45 KB
dist/features/upload ${{\color{Goldenrod}{ █▎ }}}$ 5.0%, 14.28 KB
dist/features/textState ${{\color{Goldenrod}{ ▉ }}}$ 3.9%, 11.08 KB
dist/lexical/utils ${{\color{Goldenrod}{ ▉ }}}$ 3.5%, 10.02 KB
dist/features/relationship ${{\color{Goldenrod}{ ▊ }}}$ 3.4%, 9.61 KB
dist/features/converters ${{\color{Goldenrod}{ ▊ }}}$ 3.0%, 8.36 KB
dist/utilities/fieldsDrawer ${{\color{Goldenrod}{ ▋ }}}$ 2.9%, 8.12 KB
dist/features/debug ${{\color{Goldenrod}{ ▋ }}}$ 2.6%, 7.40 KB
dist/lexical/config ${{\color{Goldenrod}{ ▍ }}}$ 1.8%, 5.14 KB
dist/features/lists ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 3.64 KB
dist/features/format ${{\color{Goldenrod}{ ▎ }}}$ 1.2%, 3.28 KB
dist/lexical/LexicalEditor.js ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 3.23 KB
dist/features/horizontalRule ${{\color{Goldenrod}{ ▎ }}}$ 1.1%, 3.18 KB
dist/field/Field.js ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 2.88 KB
dist/lexical/nodes ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 2.66 KB
(other) ${{\color{Goldenrod}{ █████████████████████▋ }}}$ 86.9%, 246.05 KB

Meta file: packages/ui/meta_client.json, Out file: esbuild/exports/client_optimized/index.js

Path Size
dist/exports/client ${{\color{Goldenrod}{ █████████████████████████ }}}$ 100.0%, 26.90 KB

Meta file: packages/ui/meta_shared.json, Out file: esbuild/exports/shared_optimized/index.js

Path Size
dist/graphics/Logo ${{\color{Goldenrod}{ ███████▋ }}}$ 30.5%, 5.57 KB
../../node_modules ${{\color{Goldenrod}{ ███▌ }}}$ 14.5%, 2.65 KB
dist/graphics/Icon ${{\color{Goldenrod}{ ██ }}}$ 8.3%, 1.51 KB
dist/utilities/formatDocTitle ${{\color{Goldenrod}{ █▊ }}}$ 7.2%, 1.32 KB
dist/providers/TableColumns ${{\color{Goldenrod}{ █▏ }}}$ 4.7%, 866 B
dist/utilities/getGlobalData.js ${{\color{Goldenrod}{ █ }}}$ 4.2%, 762 B
dist/utilities/api.js ${{\color{Goldenrod}{ █ }}}$ 4.1%, 756 B
dist/utilities/groupNavItems.js ${{\color{Goldenrod}{ █ }}}$ 4.1%, 745 B
dist/elements/Translation ${{\color{Goldenrod}{ ▋ }}}$ 2.7%, 493 B
dist/utilities/handleTakeOver.js ${{\color{Goldenrod}{ ▌ }}}$ 2.4%, 440 B
dist/utilities/traverseForLocalizedFields.js ${{\color{Goldenrod}{ ▌ }}}$ 2.3%, 419 B
dist/elements/withMergedProps ${{\color{Goldenrod}{ ▍ }}}$ 1.9%, 339 B
dist/utilities/getNavGroups.js ${{\color{Goldenrod}{ ▍ }}}$ 1.9%, 338 B
dist/utilities/getVisibleEntities.js ${{\color{Goldenrod}{ ▍ }}}$ 1.8%, 329 B
dist/elements/WithServerSideProps ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 232 B
dist/layouts/Root ${{\color{Goldenrod}{ ▎ }}}$ 1.3%, 230 B
dist/utilities/handleGoBack.js ${{\color{Goldenrod}{ ▎ }}}$ 1.0%, 180 B
dist/fields/mergeFieldStyles.js ${{\color{Goldenrod}{ ▏ }}}$ 0.9%, 158 B
dist/forms/Form ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 152 B
dist/utilities/handleBackToDashboard.js ${{\color{Goldenrod}{ ▏ }}}$ 0.8%, 152 B
(other) ${{\color{Goldenrod}{ █████████████████▍ }}}$ 69.5%, 12.68 KB
Details

Next to the size is how much the size has increased or decreased compared with the base branch of this PR.

  • ‼️: Size increased by 20% or more. Special attention should be given to this.
  • ⚠️: Size increased in acceptable range (lower than 20%).
  • ✅: No change or even downsized.
  • 🗑️: The out file is deleted: not found in base branch.
  • 🆕: The out file is newly found: will be added to base branch.

…view

# Conflicts:
#	docs/configuration/overview.mdx
#	docs/migration-guide/v4.mdx
#	docs/plugins/mcp.mdx
#	packages/payload/src/collections/config/types.ts
#	packages/payload/src/collections/operations/delete.ts
#	packages/payload/src/index.ts
#	packages/payload/src/utilities/fieldValueExists.ts
#	packages/plugin-cloud-storage/src/hooks/afterChange.ts
#	packages/plugin-mcp/src/mcp/builtin/collections/createTool.ts
#	packages/plugin-mcp/src/mcp/builtin/collections/duplicateTool.ts
#	packages/plugin-mcp/src/mcp/builtin/collections/findTool.ts
#	packages/plugin-mcp/src/mcp/builtin/collections/restoreVersionTool.ts
#	packages/plugin-mcp/src/mcp/builtin/collections/updateTool.ts
#	packages/plugin-mcp/src/mcp/builtin/globals/updateTool.ts
#	test/collections-graphql/int.spec.ts
#	test/database/int.spec.ts
#	test/database/pg-replica/int.spec.ts
#	test/hooks/config.ts
#	test/hooks/int.spec.ts
#	test/plugin-cloud-storage/buildPluginCloudStorageIntConfig.ts
#	test/plugin-cloud-storage/int.spec.ts
#	test/plugin-mcp/config.ts
#	test/plugin-mcp/int.spec.ts
#	test/select/int.spec.ts
#	test/types/config.ts
#	test/v4/baseConfig.ts
#	test/versions/int.spec.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant