This PR introduces a few changes to improve turbopack compatibility and
ensure e2e tests pass with turbopack enabled
## Changes to improve turbopack compatibility
- Use correct sideEffects configuration to fix scss issues
- Import scss directly instead of duplicating our scss rules
- Fix some scss rules that are not supported by turbopack
- Bump Next.js and all other dependencies used to build payload
## Changes to get tests to pass
For an unknown reason, flaky tests flake a lot more often in turbopack.
This PR does the following to get them to pass:
- add more `wait`s
- fix actual flakes by ensuring previous operations are properly awaited
## Blocking turbopack bugs
- [X] https://github.com/vercel/next.js/issues/76464
- Fix PR: https://github.com/vercel/next.js/pull/76545
- Once fixed: change `"sideEffectsDisabled":` back to `"sideEffects":`
## Non-blocking turbopack bugs
- [ ] https://github.com/vercel/next.js/issues/76956
## Related PRs
https://github.com/payloadcms/payload/pull/12653https://github.com/payloadcms/payload/pull/12652
This PR makes it possible to do polymorphic join querying by fields that
don't exist in all collections specified in `field.collection`, for
example:
```
const result = await payload.find({
collection: 'payload-folders',
joins: {
documentsAndFolders: {
where: {
and: [
{
relationTo: {
in: ['folderPoly1', 'folderPoly2'],
},
},
{
folderPoly2Title: { // this field exists only in the folderPoly2 collection, before it'd throw a query error.
equals: 'Poly 2 Title',
},
},
],
},
},
},
})
```
---------
Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
This PR uses the new `useQueue` hook for relationship react-select field
for loading options. This will reduce flakiness in our CI and ensure the
following:
- most recently triggered options loading request will not have its
result overwritten by a previous, delayed request
- reduce unnecessary, parallel requests - outdated requests are
discarded from the queue if a newer request exist
### What?
TypeScript says that it is possible to modify the tab of the default
view however, when you specify the path to the custom component, nothing
happens. I fixed it.
### How?
If a Component for the tab of the default view is defined in the config,
I return that Component instead of DocumentTab
### Example Configuration
config.ts
```ts
export const MenuGlobal: GlobalConfig = {
slug: menuSlug,
fields: [
{
name: 'globalText',
type: 'text',
},
],
admin: {
components: {
views: {
edit: {
api: {
tab: {
Component: './TestComponent.tsx',
},
},
},
},
},
},
}
```
./TestComponent.tsx
```tsx
const TestComponent = () => 'example'
export default TestComponent
```
### Before

### After

---------
Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com>
### What?
The browser was incorrectly setting the mimetype for `.glb` and `.gltf`
files to `application/octet-stream` when uploading when they should be
receiving proper types consistent with `glb` and `gltf`.
This patch adds logic to infer the correct `MIME` type for `.glb` files
(`model/gltf-binary`) & `gltf` files (`model/gltf+json`) based on file
extension during multipart processing, ensuring consistent MIME type
detection regardless of browser behavior.
Fixes#12620
### What?
Fixes `resetColumnsState` in `useTableColumns` react hook.
### Why?
`resetColumnsState` threw errors when being executed, e.g. `Uncaught (in
promise) TypeError: Cannot read properties of undefined (reading
'findIndex')`
### How?
Removes unnecessary parsing of URL query parameters in
`setActiveColumns` when resetting columns.
---------
Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com>
<!--
Thank you for the PR! Please go through the checklist below and make
sure you've completed all the steps.
Please review the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository if you haven't already.
The following items will ensure that your PR is handled as smoothly as
possible:
- PR Title must follow conventional commits format. For example, `feat:
my new feature`, `fix(plugin-seo): my fix`.
- Minimal description explained as if explained to someone not
immediately familiar with the code.
- Provide before/after screenshots or code diffs if applicable.
- Link any related issues/discussions from GitHub or Discord.
- Add review comments if necessary to explain to the reviewer the logic
behind a change
### What?
### Why?
### How?
Fixes #
-->
### What?
This PR fixes an issue with `plugin-seo` where the `MetaImageComponent`
would not allow creating a new upload document from the field.
### Why?
To allow users to upload new media documents for use as a meta image.
### How?
Threads `allowCreate` through to the underlying upload input.
Fixes#12616
Before:

After:

Fixes#6871
To review this PR, use `pnpm dev lexical` and the auto-created document
in the `lexical fields` collection. Select any input within the blocks
and press `cmd+a`. The selection should contain the entire input.
I made sure that `cmd+a` still works fine inside the editor but outside
of inputs.
When cloning a new project from the examples dir via create-payload-app,
the corresponding `.env` file is not being generated. This is because
the `--example` flag does not prompt for database credentials, which
ultimately skips this step.
For example:
```bash
npx create-payload-app --example live-preview
```
The result will include the provided `.env.example`, but lacks a `.env`.
We were previously writing to the `.env.example` file, which is
unexpected. We should only be writing to the `.env` file itself. To do
this, we only write the `.env.example` to memory as needed, instead of
the file system.
This PR also simplifies the logic needed to set default vars, and
improves the testing coverage overall.
Type inferences broke as a result of migrating to ts strict mode in
#12298. This leads to compile-time errors that may prevent build.
Here is an example:
```ts
export interface Page {
id: string;
slug: string;
title: string;
// ...
}
/**
* Type 'Page' does not satisfy the constraint 'Record<string, unknown>'.
* Index signature for type 'string' is missing in type 'Page'.
*/
const { data } = useLivePreview<Page>({
depth: 2,
initialData: initialPage,
serverURL: PAYLOAD_SERVER_URL,
})
```
The problem is that Payload generated type _interfaces_ do not satisfy
the `Record<string, unknown>` type. This is because interfaces are a
possible target for declaration merging, so their properties are not
fully known. More details on this
[here](https://github.com/microsoft/TypeScript/issues/42825).
This PR also cleans up the JSDocs.
Technically you could already set `label: undefined` and it would be
supported by group fields but the types didn't reflect this.
So now you can create an unnamed group field like this:
```ts
{
type: 'group',
fields: [
{
type: 'text',
name: 'insideGroupWithNoLabel',
},
],
},
```
This will remove the label while still visually grouping the fields.

---------
Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com>
Exposes a new argument to authentication strategies which allows the
author to determine if this auth strategy has the capability of setting
response headers or not.
This is useful because some auth strategies may want to set headers, but
in Next.js server components (AKA the admin panel), it's not possible to
set headers. It is, however, possible to set headers within API
responses and similar contexts.
So, an author might decide to only run operations that require setting
headers (i.e. refreshing an access token) if the auth strategy is being
executed in contexts where setting headers is possible.
Following up on https://github.com/payloadcms/payload/pull/12515, we
could instead use the same `parseCookies` method that Next.js uses. This
handles a few edge-cases differently:
- correctly strips whitespace
- parses attributes without explicit values
I think it's a good idea to match the behavior of Next.js as close as
possible here. [This](https://github.com/vercel/edge-runtime/pull/374)
is a good example of how the Next.js behavior behaves differently.
## Example
Input: `'my_value=true; Secure; HttpOnly'`
Previous Output:
```
Map(3) {
'my_value' => 'true',
'Secure' => '',
'HttpOnly' => '',
}
```
New Output:
```
Map(3) {
'my_value' => 'true',
'Secure' => 'true',
'HttpOnly' => 'true'
}
```
this has been already reported here:
https://github.com/payloadcms/payload/issues/10591
`parseCookies.ts` tries to decode cookie's values using `decodeURI()`
and throws an Error when it fails
Since it does that on all cookies set on the current domain, there's no
control on which cookie get evaluated; for instance ads networks,
analytics providers, external fonts, etc... all set cookies with
different encodings.
### Taking in consideration:
- HTTP specs doesn't define a standard way for cookie value encoding but
simply provide recommendations:
[RFC6265](https://httpwg.org/specs/rfc6265.html#sane-set-cookie)
> To maximize compatibility with user agents, servers that wish to store
arbitrary data in a cookie-value SHOULD encode that data, for example,
using Base64
- NextJS does a pretty similar parsing and ignore invalid encoded values
https://github.com/vercel/edge-runtime/blob/main/packages/cookies/src/serialize.ts
`function parseCookie(cookie: string)`
```typescript
try {
map.set(key, decodeURIComponent(value ?? 'true'))
} catch {
// ignore invalid encoded values
}
```
### With the current implementation:
- it's impossible to login because `parseCookies.ts` get called and if
fails to parse throws and error
- requests to `/api/users/me` fail for the same reason
### Fix
the pull request address these issues by simply ignoring decoding
errors:
CURRENT:
```typescript
try {
const decodedValue = decodeURI(encodedValue)
list.set(key, decodedValue)
} catch (e) {
throw new APIError(`Error decoding cookie value for key ${key}: ${e.message}`)
}
```
AFTER THIS PULL REQUEST
```typescript
try {
const decodedValue = decodeURI(encodedValue)
list.set(key, decodedValue)
} catch {
// ignore invalid encoded values
}
```
Fixes#12588
Previously, the new `disableBlockName` was not respected for lexical
blocks. This PR adds a new e2e test and does some clean-up of previous
e2e tests
There are still things to improve.
- We're inconsistent with our use of capital letters. There are still
sentences where every word starts with a capital letter, and it looks
ugly (this also happens in English, but to a lesser extent).
- We're inconsistent with the use of punctuation at the end.
- Sentences with variables like {{count}} can result in inconsistencies
if it's 1 and the noun is plural.
- The same thing happens in Spanish, but also with gender. It's
impossible to know without the context in which it's used.
---------
Co-authored-by: Paul Popus <paul@payloadcms.com>
To reproduce this bug, insert the following feature into the richtext
editor:
```ts
BlocksFeature({
inlineBlocks: [
{
slug: 'inline-media',
fields: [
{
name: 'media',
type: 'relationship',
relationTo: ['media'],
admin: {
appearance: 'drawer',
},
},
],
},
],
}),
```
Then try opening the relationship field drawer. The inline block drawer
will close.
Note: Interestingly, at least in Chrome, this only happens with DevTools
closed. It worked fine with DevTools open. It probably has to do with
capturing events like focus.
The current solution is a 50ms delay. I couldn't test it with CPU
throttle because it disappears when I close the devtools. If you
encounter this bug, please open an issue so we can increase the delay
or, better yet, find a more elegant solution.
### What?
1. Adds logic to automatically update the `importMap.js` file with the
project name provided by the user.
2. Adds an updated version of the `README.md` file that we had when this
template existed outside of the monorepo
([here](https://github.com/payloadcms/plugin-template/blob/main/README.md))
to provide clear instructions of required steps.
### Why?
1. The plugin template when installed via `npx create-payload-app` asks
the user for a project name, however the exports from `importMap.js` do
not get updated to the provided name. This throws errors when running
the project and prevents it from building.
2. The `/dev` folder requires the `.env.example` to be copied and
renamed to `.env` - the project will not run until this is done. The
template lacks instructions that this is a required step.
### How?
1. Updates
`packages/create-payload-app/src/lib/configure-plugin-project.ts` to
read the `importMap.js` file and replace the placeholder plugin name
with the name provided by the users. Adds a test to
`packages/create-payload-app/src/lib/create-project.spec.ts` to verify
that this file gets updated correctly.
2. Adds instructions on using this template to the `README.md` file,
ensuring key steps (like adding the `.env` file) are clearly stated.
Additional housekeeping updates:
- Removed Jest and replaced it with Vitest for testing
- Updated the base test approach to use Vitest instead of Jest
- Removed `NextRESTClient` in favor of directly creating Request objects
- Abstracted `getCustomEndpointHandler` function
- Added ensureIndexes: true to the mongooseAdapter configuration
- Removed the custom server from the dev folder
- Updated the pnpm dev script to "dev": "next dev dev --turbo"
- Removed `admin.autoLogin`
Fixes#12198
### What
Continuation of #7355 by extending the functionality to named tabs.
Updates `flattenFields` to hoist nested fields inside named tabs to the
top-level field array when `moveSubFieldsToTop` is enabled.
Also fixes an issue where group fields with custom cells were being
flattened out.
Now, group fields with a custom cell components remain available as
top-level columns.
Fixes#12563
You can now specify exactly who can change the constraints within a
query preset.
For example, you want to ensure that only "admins" are allowed to set a
preset to "everyone".
To do this, you can use the new `queryPresets.filterConstraints`
property. When a user lacks the permission to change a constraint, the
option will either be hidden from them or disabled if it is already set.
```ts
import { buildConfig } from 'payload'
const config = buildConfig({
// ...
queryPresets: {
// ...
filterConstraints: ({ req, options }) =>
!req.user?.roles?.includes('admin')
? options.filter(
(option) =>
(typeof option === 'string' ? option : option.value) !==
'everyone',
)
: options,
},
})
```
The `filterConstraints` functions takes the same arguments as
`reduceOptions` property on select fields introduced in #12487.
The translations would sometimes be using the wrong meanings in other
languages, for example Locale becoming "location" or "region" depending
on the phrase or translation.
Some words were also not being used consistently across the UI which
could cause some confusion if they're interchanged.
I've fixed these instances for locale specifically in dutch and spanish.
I've also updated the prompt with more context around what's being
translated and some examples, over time we should add to the examples so
that translations are better guarded against changing meanings.
---------
Co-authored-by: Germán Jabloñski <43938777+GermanJablo@users.noreply.github.com>
The translation engine previously rendered the English term locale as
“lokalitet” or “steder” in Norwegian, which in practice refers to a
geographic location rather than a language variant. This has been
changed throughout. I have also made some other small improvements to
the Norwegian translations.
### What?
Fixes an infinite loop that may occur when invoking the findUp() utility
to search for a file by name and no result is found.
### Why?
If triggered, the infinite loop will hang the entire application. For
example, this occurs when trying to boot Payload from a Cloudflare
Worker, as reported in #12327
### How?
By checking whether it has reached the root directory before analysing
the parent folder