fix(cloudinary): persist clientUploadContext fields on Payload 3.82+#148
Draft
jhb-dev wants to merge 1 commit into
Draft
fix(cloudinary): persist clientUploadContext fields on Payload 3.82+#148jhb-dev wants to merge 1 commit into
jhb-dev wants to merge 1 commit into
Conversation
6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Restores persistence of
cloudinaryPublicIdandurlfor client-side uploads after the upstream regression in@payloadcms/plugin-cloud-storagev3.82+.Root cause
In Payload 3.82,
@payloadcms/plugin-cloud-storagechanged itsafterChangehook to skipadapter.handleUpload(...)whenever the incoming file carries aclientUploadContext(upstream PR #16115). For stateless backends like S3 this is fine — the file is already at a deterministic key. But this plugin usedhandleUploadto copypublicId/secureUrlfromclientUploadContextinto the doc'scloudinaryPublicIdandurlfields. With that call skipped:cloudinaryPublicIdis never persisted →adapter.generateURLreturnsundefined→data.urlends up unset →/api/{collection}/file/{filename}→"missing on disk".Fix
Adds a collection-level
beforeChangehook on each Cloudinary-enabled collection. The hook readsreq.file.clientUploadContextand writes bothcloudinaryPublicIdandurldirectly intodata. Collection-level hooks run after field-level hooks, so this also overrides the URL field whosebeforeChangesaw nocloudinaryPublicId.Also drops the now-dead
if (clientUploadContext)branch fromsrc/handleUpload.ts— upstream filters those files out before calling the adapter, so the branch is unreachable.Trade-off — this is a workaround
This restores correctness without depending on upstream. The deeper problem is an upstream API contract violation: the
HandleUploadtype still declaresclientUploadContextas a parameter, but the runtime filters those calls away (afterChange.js:35). An alternate PR (refactor the plugin to be stateless like@payloadcms/storage-s3) is opened separately.Test plan
src/index.test.tsreproduces the bug: runs the plugin and exercises the resulting collection'sbeforeChangehook with a simulatedreq.file.clientUploadContext; assertsdata.cloudinaryPublicIdanddata.urlare set. Failed before the fix, passes after.pnpm typecheckclean.pnpm lintclean.pnpm buildproduces a cleandist/with no*.test.*files.cloudinary/dev— start the dev app, upload an image to theimagescollection (which usesdisablePayloadAccessControl: true+clientUploads: true), confirm the doc lands withcloudinaryPublicIdandurlpopulated and the admin preview renders.Adds vitest scaffolding
This plugin had no test setup; the PR mirrors what
vercel-deploymentsalready does:vitestdevDep,test/test:watchscripts,vitest.config.ts, and atsconfig.build.jsonthat excludes test files from the published build (matching swc's--ignoreflag).