Commit Graph

2419 Commits

Author SHA1 Message Date
Sasha 827c9a16ad fix: disable lockedDocuments if the kv adapter uses a collection (#14453)
Fixes https://github.com/payloadcms/payload/discussions/14452

* Adds `collection.lockDocuments: false` to the KV storage collection if
the adapter has one.
* Filters `relationTo` for the locked documents collection with removing
collections that have `lockDocuments: false` in their config.
2025-11-03 15:44:07 +00:00
Jessica Rynkar 53d85574d8 feat: adds new UI option Duplicate In [Select Locales] (#13803)
### What
Adds new UI feature and update to the duplicate endpoint to support to
duplicating only select locales.

### Why
Currently, duplicating a document always copies all locales. To exclude
some, users must duplicate the full document and manually clear fields.
This feature was requested by the community and is part of planned
localization improvements.

### How
The new control opens a drawer where users can pick one or more locales
to duplicate. The duplicate endpoint now accepts `selectLocales` and
automatically clears any unselected locales before creating the new
draft document.

#### Feature tracked here:
https://github.com/payloadcms/payload/discussions/13444

---------

Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
2025-10-31 16:06:36 -04:00
Elliot DeNolf 64209271ea build: add kv-redis to publishList (#14416) 2025-10-30 14:13:18 -04:00
Elliot DeNolf d401424ca4 chore(release): v3.62.0 [skip ci] 2025-10-30 13:58:03 -04:00
Dan Ribbens 10a640cbf4 fix(db-mongodb): migration fails for cosmosDB (#14401)
fixes #10136
2025-10-30 13:50:33 -04:00
Alessio Gravili 0a37edbc97 feat: add access control for jobs queue and cancel operations (#14404)
Adds access control for jobs `queue` and `cancel` operations, similar to
what we have for `run` operations.

**Implementation:**
Both operations now support `overrideAccess` parameter (defaults to
`true`) and respect `jobsConfig.access.queue` and
`jobsConfig.access.cancel` functions. When `overrideAccess: false` is
passed without an authenticated request, operations throw `Forbidden`
error.

**Configuration:**
Access control functions can be defined in the Jobs Config under
`jobs.access.queue`, `jobs.access.run`, and `jobs.access.cancel`. Each
function receives `{ req }` and returns a boolean. If no custom access
control is defined, the default allows any authenticated user to perform
the operation.

**Example:**
```ts
// Configure access control
jobs: {
  access: {
    queue: ({ req }) => req.user?.roles?.includes('admin'),
    cancel: ({ req }) => req.user?.roles?.includes('admin'),
  }
}

// Use in Local API
await payload.jobs.cancel({
  where: { workflowSlug: { equals: 'sync' } },
  overrideAccess: false,
  req,
})
```
2025-10-30 16:27:24 +00:00
Paul a3df837872 feat: timezone config can now be added on a per field basis (#14410)
This PR enables you to add timezone specific config on a per field basis
so each field can support its own list of timezones

```ts
{
  name: 'date',
  type: 'date',
  timezone: {
    defaultTimezone: 'America/New_York',
    supportedTimezones: [
      { label: 'New York', value: 'America/New_York' },
      { label: 'Los Angeles', value: 'America/Los_Angeles' },
      { label: 'London', value: 'Europe/London' },
    ],
  },
}
```

You can also enforce a specific timezone by specifying just one with a
default value:

```ts
{
  name: 'date',
  type: 'date',
  timezone: {
    defaultTimezone: 'Europe/London',
    supportedTimezones: [
      { label: 'London', value: 'Europe/London' },
    ],
  },
}
```

It also fixes a bug with date fields in list view not showing the date
in the formatted timezone correctly.
2025-10-30 11:12:37 -04:00
Sasha ffb9a2e225 feat: add KV storage adapters (#9913)
Adds KV storage support to Payload Local API with an adapter pattern,
includes 3 adapters. We'll use it for the Realtime API.
You can access it via `payload.kv`:

```ts
interface KVAdapter {
  /**
   * Clears all entries in the store.
   * @returns A promise that resolves once the store is cleared.
   */
  clear(): Promise<void>

  /**
   * Deletes a value from the store by its key.
   * @param key - The key to delete.
   * @returns A promise that resolves once the key is deleted.
   */
  delete(key: string): Promise<void>

  /**
   * Retrieves a value from the store by its key.
   * @param key - The key to look up.
   * @returns A promise that resolves to the value, or `null` if not found.
   */
  get(key: string): Promise<KVStoreValue | null>

  /**
   * Checks if a key exists in the store.
   * @param key - The key to check.
   * @returns A promise that resolves to `true` if the key exists, otherwise `false`.
   */
  has(key: string): Promise<boolean>

  /**
   * Retrieves all the keys in the store.
   * @returns A promise that resolves to an array of keys.
   */
  keys(): Promise<string[]>

  /**
   * Sets a value in the store with the given key.
   * @param key - The key to associate with the value.
   * @param value - The value to store.
   * @returns A promise that resolves once the value is stored.
   */
  set(key: string, value: KVStoreValue): Promise<void>
}
```

To configure the adapter you can use the `kv` property of the root
config.
```ts
buildConfig({
  kv: adapter()
})
```

#### Database KV adapter (default)
No need to configure, as Payload uses it by default. It generates new
collection `payload-kv` and uses the current database adapter to access
it. The collection is hidden in the admin panel and access locked.
If you want to  override the generated collection:
```ts
import { databaseKVAdapter } from 'payload'

buildConfig({
  kv: databaseKVAdapter({
    kvCollectionOverrides: {
      slug: 'custom-kv',
      ...(process.env.DEBUG === 'true' && {
        admin: { hidden: false },
        access: {},
      }),
    },
  }),
})
```

#### In Memory KV adapter
Simple and very fast storage using memory. Don't use it on Vercel /
multiple instances or if you need data persistence.
```ts
import { inMemoryKVAdapter } from 'payload'

buildConfig({
  kv: inMemoryKVAdapter(),
})
```

#### Redis KV Adapter
Uses Redis. Probably the best option as it's faster than database,
persistent and works with Vercel / multiple instances, but requires
additional setup
```sh
pnpm add @payloadcms/kv-redis
```

```ts
import { redisKVAdapter } from '@payloadcms/kv-redis'

buildConfig({
  kv: redisKVAdapter({
    keyPrefix: "custom-prefix:", // defaults to 'payload-kv:'
    redisURL: "redis://127.0.0.1:6379" // defaults to process.env.REDIS_URL (Vercel generates this variable for you if you connect a project to Redis)
  }),
})
```
2025-10-30 17:05:38 +02:00
Patrik b766ae6570 fix: globals with versions return _status field when access denied (#14406)
### What?

Fixes an issue where globals with drafts/versions enabled would return
the `_status` field with its default value (`'draft'`) when access
control denied read access, instead of returning an empty object.

### Why?

When a global has:
1. Versions/drafts enabled (which adds the `_status` field with
`defaultValue: 'draft'`)
2. Read access control with a where clause (e.g., `return {_status:
{equals: 'published'}}`)

And a user without permissions requests the global (e.g., an unpublished
draft exists), the API was returning `{"_status": "draft"}` instead of
an empty object `{}`. This exposes the document's status even when
access is denied.

The issue occurred because when the DB query returned `null` (filtered
by access control), the `findOne` operation would set `doc = {}`, and
then the `afterRead` hook would populate default values including
`_status: 'draft'`.

### How?

- Modified `afterRead/promise.ts` to skip setting default values for
globals when:
- The document has no `id` (indicating it doesn't exist or was filtered)
  - AND access control is active (`!overrideAccess`)

Fixes #14096
2025-10-30 07:57:56 -07:00
Philipp Schneider d5fab43fe9 feat: make toast position configurable (#14405)
### What?

Extends the toast configuration introduced with #13609 to also allow to
configure the toast's positioning.

### Why?

Different users of Payload might prefer different positions. E.g. for
our own implementation we'd prefer a top (central) position, because
we've been having users overlook the toasts in the bottom right due to
usage of large screens, and saving / publishing buttons usually
triggering toasts being placed at the top (meaning the user looks at the
top).

### How?

The `position` configuration is a 1:1 pass-through of the
https://sonner.emilkowal.ski `position` configuration option. The
default remains unchanged compared to the status-quo, at
`'bottom-right'`.

### Screenshots

*`bottom-right`:* Unchanged to current placement.

*`bottom-center`:*

<img width="1283" height="1373" alt="image"
src="https://github.com/user-attachments/assets/ad2716d4-3a8b-467e-be03-80abf0e1233f"
/>

*`bottom-left`:*

<img width="1283" height="1375" alt="image"
src="https://github.com/user-attachments/assets/4f7b1efe-de1a-4a48-be5d-9746ba4e7296"
/>


*`top-left` - questionable, but IMHO that's fine because it's an opt-in
choice.*:

<img width="1276" height="1371" alt="image"
src="https://github.com/user-attachments/assets/ff11e2f5-0aa0-411a-9bcf-01083184a531"
/>

*`top-center`:*

<img width="1285" height="1384" alt="image"
src="https://github.com/user-attachments/assets/603e735f-36f5-4b5a-8e9f-b1c80b2a2907"
/>

*`top-left` - also covers interactive UI, but is therefore very hard to
overlook:*

<img width="1283" height="1370" alt="image"
src="https://github.com/user-attachments/assets/40ecdd57-a9d2-4d47-b8d9-26e64164e1ee"
/>
2025-10-29 23:45:32 +00:00
Alessio Gravili 1341f69e07 refactor: deprecate job queue depth property (#14402)
Similar to how we deprecated the `runHooks` property in
https://github.com/payloadcms/payload/pull/13617, this PR deprecates the
`depth` property.

Just like `runHooks`, setting depth to 1 or higher will massively slow
down the job system and make it less reliable. It's also adds a lot of
unnecessary code we will need to maintain.

To further discourage using this property, this PR marks it as
deprecated and for removal in 4.0.
2025-10-29 17:42:46 +00:00
Sasha 059185f88f fix(db-*): findMigrationDir in projects without src folder (#14381)
Fixes https://github.com/payloadcms/payload/issues/14379

Additionally, removes duplication of `findMigrationDir` between DB
adapters, deprecates the export from `@payloadcms/drizzle` and adds unit
testing to this function.
2025-10-28 18:57:26 +00:00
Elliot DeNolf bc6228caca chore(release): v3.61.1 [skip ci] 2025-10-24 17:19:20 -04:00
Jarrod Flesch 5a37909134 fix: filteredLocales in the client config are stale (#14326)
Fixes https://github.com/payloadcms/payload/issues/14299

Using `filterAvailableLocales` means they could change when the req
changes. When we get the client config from the cache, we need to
re-apply `filterAvailableLocales` so the config has the correct values.

This was manually being done in the Root layout, but every other place
that called getClientConfig was not i.e. the Root view. So this PR moves
that logic from the Root layout into the getClientConfig function.
2025-10-24 21:11:54 +00:00
Paul ad0e7b2252 fix(ui): preview button not responding to conditional URL (#14277)
Fixes #14241 and #11253.

Preview URL is now part of the form state as well so we can
conditionally show the preview button based on its value, previously
this was not possible because we were fetching the URL on click. Similar
to #14012.
2025-10-23 13:35:22 -04:00
Elliot DeNolf 32479f2705 chore(release): v3.61.0 [skip ci] 2025-10-23 11:35:55 -04:00
Elliot DeNolf 894cf0c034 build: add plugin-mcp to publishList (#14319)
Add `@payloadcms/plugin-mcp` to publish list.
2025-10-23 10:51:17 -04:00
Elliot DeNolf 2aba827958 build: add README sync for top-level -> packages/payload (#14316)
Adds check in `lint-staged` to see if the top-level README.md has been
modified, if so, it will copy it to `packages/payload/README.md`. This
is to ensure the README shown on the [npm package
page](https://www.npmjs.com/package/payload) is proper.

✅  This PR also performs the sync
2025-10-23 10:01:48 -04:00
Patrik a1671ec0f5 fix: user updatedAt modified during session operations (#14269)
### What?

The `updatedAt` field on user documents was being updated whenever
session operations occurred (login, logout, or token refresh), even
though only session metadata was changing and not the actual user data
itself.

### Why?

This behavior was introduced in PR #13335, which added explicit
`updatedAt = new Date().toISOString()` to all session operations to
ensure that `updatedAt` would always be bumped when using
`payload.db.updateOne()`.

However, this was incorrect for session operations specifically. The
`updatedAt` timestamp should only change when the user's actual data
(email, password, profile fields, etc.) is modified - not when internal
session tracking metadata changes. Session management is essentially
bookkeeping and shouldn't trigger timestamp updates that suggest the
user entity itself was modified.

### How?

Modified session operations to set `updatedAt = null` instead of
updating it:
- `addSessionToUser()` in `packages/payload/src/auth/sessions.ts`
- `logoutOperation()` in
`packages/payload/src/auth/operations/logout.ts`
- `refreshOperation()` in
`packages/payload/src/auth/operations/refresh.ts`

Setting `updatedAt = null` skips updating this field entirely, leaving
the existing timestamp unchanged.

Fixes #13626
2025-10-22 12:21:34 -07:00
Jarrod Flesch c2dcd122e1 chore: extract snapshot logic, correct types in version ops (#14290)
Adds a `createSnapshot` to improve readability in the save versions
file.

Corrects update/create version types for collections and globals.
2025-10-22 13:21:35 -04:00
Paul b252658533 chore: bump pino to v9.14.0 and pino-pretty (#14283)
Continuing from https://github.com/payloadcms/payload/pull/14262

Bumping `pino` to `9.14.0` and `pino-pretty` to `10.1.2`, both minor
version bumps
2025-10-22 15:33:22 +00:00
Patrik 1016cd0cd4 fix: typescript requires fields when draft: true despite passing draft: true (#14271)
### What?

Fixes TypeScript types for `payload.create()` to allow omitting required
fields when `draft: true` is specified, aligning type checking with
runtime validation behavior.

### Why?

According to the
[documentation](https://payloadcms.com/docs/versions/drafts#updating-or-creating-drafts),
when creating drafts, required fields don't need to be provided because
validation is skipped by default (unless `versions.drafts.validate` is
explicitly set to `true`). However, TypeScript was still enforcing
required fields at the type level, causing type errors even when `draft:
true` was set.

### How?

- Added new types `DraftDataFromCollectionSlug` and
`DraftDataFromCollection` that make all fields optional
- Changed `payload.create` options to use a discriminated union type:
- When `draft: true` → uses `DraftDataFromCollectionSlug` (all fields
optional)
- When `draft: false` or omitted → uses `RequiredDataFromCollectionSlug`
(required fields enforced)

The fix uses TypeScript's discriminated unions to provide accurate type
inference based on the `draft` parameter value.

Fixes #12578
2025-10-21 06:26:58 -07:00
Alessio Gravili d57be12cf9 chore: cleanup, short-circuit .split calls (#14245)
Just some small optimizations, extracted from
https://github.com/payloadcms/payload/pull/14244 to make the diff easier
to read:

- short circuit .split() calls => faster
- remove unused code and clean up code
2025-10-17 23:50:38 +00:00
Alessio Gravili 8b0ac01f41 docs: add jsdocs to RichText adapter (#14246)
Adds JSDocs to the RichText adapter return value.
2025-10-18 02:46:58 +03:00
Paul 88cb687733 fix: blocks access control not respecting update access whether on collection or on a per field basis (#14226)
Fixes https://github.com/payloadcms/payload/issues/14221
Fixes https://github.com/payloadcms/payload/issues/13569

Fixes a problem where blocks would have their fields disappear entirely
if update permissions were false on the collection or on the blocks
field itself.

This fixes that and makes sure that fields are always visible and that
the blocks fields are correctly being set to readOnly mode if you have
permissions to read but not update
2025-10-17 23:11:42 +01:00
Paul 2ced43dc56 fix: allow slugField to accept localized argument and fixed slug generation with custom field names (#14234)
Previously you had to use the overrides to enable localization for the
`slugField` however it's a very common request that slugs are localized
so we've added it to the args.

Now you can do:
```ts
slugField({ localized: true })
```

There was also a bug relating to using custom field names, even though
we accept it the hook would still look for `slug`, this has been fixed.

Todo:
- [x] Add e2e tests for general field and localized field
2025-10-17 17:07:16 +00:00
Alessio Gravili 54224c3ab7 perf(richtext-lexical): do not return i18n from editor adapter (#14228)
This PR deprecates the `i18n` property returned from the editor adapter.
Instead, we now merge `i18n` directly into the `config`, since the
config is already available in the richtext adapter provider. This
simplifies the logic (only one line of code) and eliminates the need to
manually read and merge i18n from the adapter.

Benefits:
- **Simpler implementation**: Merging i18n into the config is
straightforward
- **Reduced memory usage**: Each editor no longer maintains its own i18n
object - only a single, merged `config.i18n` is kept.
- **Improved developer experience**: The sanitized field config is now
smaller and easier to inspect. Console logging the config no longer
floods the output with hundreds of translation lines.

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211668727538425
2025-10-17 09:19:42 +01:00
Elliot DeNolf b26129cbc5 chore(release): v3.60.0 [skip ci] 2025-10-16 12:42:37 -04:00
Sasha 2b1b6eee65 fix: findDistinct by explicit ID paths in relationships and virtual fields (#14215)
Fixes `findDistinct` operation when the `field` argument is:
* an eplicit ID path inside a relationship like:
```ts
const distinct = await payload.findDistinct({ collection: 'posts', field: 'category.id' })
```
* A virtual field linked to a relationship ID field
```ts
[
  {
    type: 'relationship',
    relationTo: 'categories',
    name: 'category',
  },
  {
    type: 'number',
    name: 'categoryID',
    virtual: 'category.id',
  },
]

const distinct = await payload.findDistinct({ collection: 'posts', field: 'categoryID' })
```
2025-10-16 18:45:00 +03:00
Jessica Rynkar 623a1b8216 feat: accept multiple locales in fallbackLocale (#13822)
### What?
This change allows `fallbackLocale` to accept an **array of locales** in
find queries and locale configuration.

```ts
/** Local API **/
  await payload.findByID({
    id,
    collection,
    locale: 'en',
    fallbackLocale: ['fr', 'es'],
  })

/** REST API **/
  await fetch(`${baseURL}/api/${collectionSlug}?locale=en&fallbackLocale[]=fr&fallbackLocale[]=es`)

/** GraphQL **/
  await restClient.GRAPHQL_POST({
    body,
    query: { locale: 'en', fallbackLocale: ['fr', 'es']},
  })

/** Locale Configs **/
  locales: [
    {
      code: 'en',
      label: 'English',
      fallbackLocale: ['fr', 'es'],
    },
  ]
```

### Why?
This update is part of the planned [localization
enhancements](https://github.com/payloadcms/payload/discussions/10705).
Currently, only one fallback locale can be specified. If that locale
doesn’t contain data, the query returns nothing. To work around this,
users have to inspect the response themselves and then make additional
queries for other locales.

With this change, Payload handles that work automatically. Users only
need to provide a list of fallback locales in their preferred order, and
Payload will check each one until it finds a value.

### How?
Updates query handling across the local API, REST API, and GraphQL so
that `fallbackLocale` can accept either a **string** or an **array** of
locales. When an array is passed, Payload will iterate through them in
order and return the first found localized value. This behavior applies
to both collections and globals.

#### Feature request:
https://github.com/payloadcms/payload/discussions/13443

---------

Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
2025-10-16 12:02:59 +01:00
German Jablonski 13a1d90890 fix: validate Point Field to -180 to 180 for longitude and -90 to 90 for latitude (#14206)
Fixes #14185
2025-10-16 07:47:25 +00:00
Patrik ee8b3cff96 feat: adds settingsMenu to admin navigation sidebar (#14139)
### What?

Adds a new `admin.components.settingsMenu` configuration option that
allows users to inject custom menu items into the navigation. When
configured, a gear icon appears above the logout button in the
navigation sidebar. Clicking the gear icon opens a popup menu displaying
the custom components.

<img width="130" height="177" alt="Screenshot 2025-10-14 at 11 43 18 AM"
src="https://github.com/user-attachments/assets/5583e32d-cde3-45f0-9d58-a6724c12509f"
/>

<img width="174" height="239" alt="Screenshot 2025-10-14 at 11 43 37 AM"
src="https://github.com/user-attachments/assets/5dca8ea7-ed99-4bc1-89c2-bc6da7d8f323"
/>

### Why?

Users need a way to add custom actions and menu items to the admin
navigation without replacing the entire navigation component. This
provides a standardized location for admin-level utilities and actions
that don't fit into collection or global navigation.

### How?

- Added `settingsMenu` property to `admin.components` config type
definition
- Created `GearIcon` component following the existing icon patterns
- Exported `GearIcon` from the UI package exports
- Implemented `SettingsMenuButton` client component that renders a popup
menu with the gear icon trigger
- Integrated `SettingsMenuButton` into `DefaultNav` component,
positioned above the logout button
- Updated import map generation to include `settingsMenu` components
2025-10-15 11:21:29 -07:00
German Jablonski 747a049753 fix: urls in upload sizes are not encoded (#14205)
Fixes #14195
2025-10-15 18:02:03 +01:00
Patrik 3317207d57 fix: restoring trashed drafts with empty required fields fails validation (#14186)
### What?

This PR fixes the validation logic for restoring documents from trash.
Previously, restoring a trashed draft with empty required fields would
fail with "The following fields are invalid" errors, even when restoring
as a draft.

### Why?

When a draft document with empty required fields is trashed and then
restored:
- Trashing works correctly (skips validation since it's just setting
`deletedAt`)
- Restoring as draft should also work (it's just metadata, clearing
`deletedAt`)
- Restoring as published should require validation (content is being
published)

The previous fix only handled trashing (when `deletedAt` is set), but
didn't handle the restore case (when `deletedAt` is set to `null`).

### How?

Extended the `skipValidation` logic in `updateDocument()` to detect
restore operations.

Fixes #14180
2025-10-15 06:59:41 -07:00
Paul 077c6f5566 chore(email-nodemailer): bump nodemailer dependencies to latest (#14121)
Bumps nodemailer to `7.0.9` and types to `7.0.2` - there have been some
security fixes since our previously pinned version

Manually tested SMTP connection with this new version via our package
2025-10-09 14:36:39 +01:00
Alessio Gravili 07a1eff99c feat(richtext-lexical): client-side block markdown shortcuts, code block (#13813)
This PR:
- adds support for client-side markdown shortcuts for blocks with the
`admin.jsx` property. This was previously only supported server-side.
The code mimics the server-side implementation, as this could be used
for full markdown imports on the client-side, rather than just simple
markdown shortcuts
- exports a pre-made `CodeBlock` that can be used within the
`BlocksFeature`. Video Example:


https://github.com/user-attachments/assets/31f2d66b-64e9-4e9d-a2b6-89ddefda3a84



---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211217132290839
2025-10-07 20:15:45 +00:00
Elliot DeNolf fcaafaa394 chore(release): v3.59.1 [skip ci] 2025-10-07 16:05:08 -04:00
Elliot DeNolf c58d556343 chore(release): v3.59.0 [skip ci] 2025-10-07 10:18:21 -04:00
Jacob Fletcher b09ae6772f feat: slug field (#14007)
Discussion #8859. Requires #14012.

Exports a new `slugField`. This is a wrapper around the text field that
you can drop into any field schema.

A slug is a unique, indexed, URL-friendly string that identifies a
particular document, often used to construct the URL of a webpage. Slugs
are a fundamental concept for seemingly every project.

Traditionally, you'd build this field from scratch, but there are many
edge cases and nice-to-haves to makes this difficult to maintain by
hand. For example, it needs to automatically generate based on the value
of another field, provide UI to lock and re-generate the slug on-demand,
etc.

Fixes #13938.

When autosave is enabled, the slug is only ever generated once after the
initial create, leading to single character, or incomplete slugs.

For example, it is expected that "My Title" → "my-title, however ends up
as "m".

This PR overhauls the field to feel a lot more natural. Now, we only
generate the slug through:
1. The `create` operation, unless the user has modified the slug
manually
2. The `update` operation, if:
  a. Autosave is _not_ enabled and there is no slug
b. Autosave _is_ enabled, the doc is unpublished, and the user has not
modified the slug manually

The slug should stabilize after all above criteria have been met,
because the URL is typically derived from the slug. This is to protect
modifying potentially live URLs, breaking links, etc. without explicit
intent.

This fix, along with all the other features, is now standardized behind
the new `slugField`:

```ts
import type { CollectionConfig } from 'payload'
import { slugField } from 'payload'

export const MyCollection: CollectionConfig = {
  // ...
  fields: [
   // ...
   slugField()
  ]
}
```

In the future we could also make this field smart enough to auto
increment itself when its generated slug is not unique.

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211513433305005
2025-10-07 10:15:45 -04:00
Dan Ribbens 9ceee8ea3c chore: ci changes to add compatibility for mongodb alternates (#13898)
- Adds documentdb and cosmosdb to CI test matrix
- Adds missing generateDatabaseAdapter configs
- Adjust generateDatabaseAdapter firestore config to make it pure to the
compatibilityOptions we provide as an export

Creating as draft because I expect a few tests to fail based on previous
comments.

---------

Co-authored-by: Sasha <64744993+r1tsuu@users.noreply.github.com>
2025-10-06 16:48:02 -04:00
Sasha e4f8478e36 feat: support any depth for relationships in findDistinct (#14090)
Follow up to https://github.com/payloadcms/payload/pull/14026

```ts
// Supported before
const relationResult = await payload.findDistinct({ collection: 'posts', field: 'relation1.title' })
// Supported now
const relationResult = await payload.findDistinct({ collection: 'posts', field: 'relation1.relation2.title' })
const relationResult = await payload.findDistinct({ collection: 'posts', field: 'relation1.relation2.relation3.title' })
```
2025-10-06 22:44:14 +03:00
Paul 3cf3f93e1f fix: add detection for --experimental-https flag (#14085)
Follow up from https://github.com/payloadcms/payload/pull/14053

Add detection for `--experimental-https` flag based on `process.argv`
which may not be reliable so we're keeping the env var toggle in as well
2025-10-06 12:06:15 +00:00
Paul feaa395c14 fix: support USE_HTTPS for local hmr (#14053)
Closes https://github.com/payloadcms/payload/issues/12087

When using `--experimental-https` from Nextjs we have no way of
detecting that right now so I've added documentation on how to handle
this flag and added to support for `USE_HTTPS` to set the websocket
protocol for HMR to `wss` instead of `ws`
2025-10-04 01:01:27 +00:00
Said Akhrarov cd546b3125 feat(ui): add support for disabling join field row types (#12738)
### What?
This PR adds a new `admin.disableRowTypes` config to `'join'` fields
which hides the `"Type"` column from the relationship table.

### Why?
While the collection type column _can be_ helpful for providing
information, it's not always necessary and can sometimes be redundant
when the field only has a singular relationTo. Hiding it can be helpful
by removing visual noise and providing editors the data directly.

### How?
By threading `admin.disableRowTypes` directly to the `getTableState`
function of the `RelationshipTable` component.

**With row types** (via `admin.disableRowTypes: false | undefined` OR
default for polymorphic):

![image](https://github.com/user-attachments/assets/22b55477-cf56-4b0e-a845-e6f2b39efe3b)

**Without row types** (default for monomorphic):

![image](https://github.com/user-attachments/assets/3a2bb0ba-2d5e-4299-8689-249b2d3fefe2)
2025-10-03 11:10:10 +01:00
Sasha 4b6b0c51fb fix: update packages list for pnpm payload info (#14030)
Updates the packages list for `pnpm payload info` that's used in the
Environment Info issue template section.
2025-10-03 13:07:09 +03:00
Paul d826159fc0 fix(ui): add support back for custom live preview components (#14037)
Fixes https://github.com/payloadcms/payload/issues/13308

Adds support for a custom live preview component back, we previously
supported this and it was allowed via the config types but it wasn't
being rendered.

Now we export the `useLivePreviewContext` hook and the original
`LivePreviewWindow` component too so that end users can wrap the live
preview functionality with anything custom that they may need
2025-10-02 15:09:15 -04:00
Jacob Fletcher 2be6bb3c3b feat(ui): live preview conditions (#14012)
Supports live preview conditions. This is essentially access control for
live preview, where you may want to restrict who can use it based on
certain criteria, such as the current user or document data.

To do this, simply return null or undefined from your live preview url
functions:

```ts
url: ({ req }) => (req.user?.role === 'admin' ? '/hello-world' : null)
```

This is also useful for pages which derive their URL from document data,
e.g. a slug field, do not attempt to render the live preview window
until the URL is fully formatted.

For example, if you have a page in your front-end with the URL structure
of `/posts/[slug]`, the slug field is required before the page can
properly load. However, if the slug is not a required field, or when
drafts and/or autosave is enabled, the slug field might not yet have
data, leading to `/posts/undefined` or similar.

```ts
url: ({ data }) => data?.slug ? `/${data.slug}` : null
```

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211513433305000
2025-10-02 10:24:11 -04:00
Patrik 537f58b4bc feat: adds disableGroupBy to fields admin props (#14017)
### What?

Adds a new `disableGroupBy` admin config property for fields to control
their visibility in the list view GroupBy dropdown.

### Why

Previously, the GroupByBuilder was incorrectly using `disableListFilter`
to determine which fields to show in the group-by dropdown.

### How

- Added new `disableGroupBy` property to the field admin config types.
- Updated `GroupByBuilder` to filter fields based on `disableGroupBy`
instead of `disableListFilter`

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211511898438807
2025-10-02 06:22:32 -07:00
Sasha 9d6cae0445 feat: allow findDistinct on fields nested to relationships and on virtual fields (#14026)
This adds support for using `findDistinct`
https://github.com/payloadcms/payload/pull/13102 on fields:
* Nested to a relationship, for example `category.title`
* Virtual fields that are linked to relationships, for example
`categoryTitle`


```tsx
const Category: CollectionConfig = {
  slug: 'categories',
  fields: [
    {
      name: 'title',
      type: 'text',
    },
  ],
}

const Posts: CollectionConfig = {
  slug: 'posts',
  fields: [
    {
      name: 'category',
      type: 'relationship',
      relationTo: 'categories',
    },
    {
      name: 'categoryTitle',
      type: 'text',
      virtual: 'category.title',
    },
  ],
}

// Supported now
const relationResult = await payload.findDistinct({ collection: 'posts', field: 'category.title' })
// Supported now
const virtualResult = await payload.findDistinct({ collection: 'posts', field: 'categoryTitle' })
```
2025-10-02 05:36:30 +03:00
Alessio Gravili 9bcb7b0d9d feat: bundle types (#14020)
This PR updates the build process to generate a single
`dist/index.bundled.d.ts` file that bundles all `payload` package types.

Having one bundled declaration file makes it easy to load types into the
Monaco editor (e.g. for the new Code block), enabling full type
completion for the `payload` package.

## Example

```ts
 BlocksFeature({
        blocks: [
          CodeBlock({
            slug: 'PayloadCode',
            languages: {
              ts: 'TypeScript',
            },
            typescript: {
              fetchTypes: [
                {
                  filePath: 'file:///node_modules/payload/index.d.ts',
                  url: 'https://unpkg.com/payload@3.59.0-internal.e247081/dist/index.bundled.d.ts', // <= download bundled .d.ts
                },
              ],
              paths: {
                payload: ['file:///node_modules/payload/index.d.ts'],
              },
              typeRoots: ['node_modules/@types', 'node_modules/payload'],
            },
          }),
        ],
      }),
```

<img width="1506" height="866" alt="Screenshot 2025-10-01 at 12 38
54@2x"
src="https://github.com/user-attachments/assets/135b9b69-058a-42b9-afa0-daa328f64f38"
/>




---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211524241290884
2025-10-01 22:05:37 +00:00