Compare commits

..

2 Commits

Author SHA1 Message Date
Sasha
4283bebc37 chore: union with Client / VercelClient 2024-10-18 22:28:11 +03:00
Sasha
4082680099 fix(drizzle): expose db.drizzle.$client type 2024-10-17 23:35:05 +03:00
166 changed files with 1264 additions and 1993 deletions

View File

@@ -122,19 +122,3 @@ This is how you can preview changes you made locally to the docs:
4. Add a `DOCS_DIR` environment variable to the `.env` file which points to the absolute path of your modified docs folder. For example `DOCS_DIR=/Users/yourname/Documents/GitHub/payload/docs`
5. Run `yarn run fetchDocs:local`. If this was successful, you should see no error messages and the following output: _Docs successfully written to /.../website/src/app/docs.json_. There could be error messages if you have incorrect markdown in your local docs folder. In this case, it will tell you how you can fix it
6. You're done! Now you can start the website locally using `yarn run dev` and preview the docs under [http://localhost:3000/docs/](http://localhost:3000/docs/)
## Internationalization (i18n)
If your PR adds a string to the UI, we need to make sure to translate it into all the languages that Payload supports. To do that:
- Find the appropriate internationalization file for your package. These are typically located in `packages/translations/src/languages`, although some packages (e.g., richtext-lexical) have separate i18n files for each feature.
- Add the string to the English locale "en".
- Translate it to other languages. You can use the `translateNewKeys` script if you have an OpenAI API key in your `.env` (under `OPENAI_KEY`), or you can use ChatGPT or Google translate - whatever is easier for you. For payload core translations (in packages/translations) you can run the `translateNewKeys` script using `cd packages/translations && pnpm translateNewKeys`. For lexical translations, you can run it using `cd packages/richtext-lexical && pnpm translateNewKeys`. External contributors can skip this step and leave it to us.
To display translation strings in the UI, make sure to use the `t` utility of the `useTranslation` hook:
```ts
const { t } = useTranslation()
// ...
t('yourStringKey')
```

View File

@@ -79,7 +79,7 @@ Returns a boolean which allows/denies access to the `create` request.
To add create Access Control to a Collection, use the `create` property in the [Collection Config](../collections/overview):
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const CollectionWithCreateAccess: CollectionConfig = {
// ...

View File

@@ -33,7 +33,7 @@ Access Control is specific to the operation of the request.
To add Access Control to a Field, use the `access` property in the [Field Config](../fields/overview):
```ts
import type { CollectionConfig } from 'payload';
import { CollectionConfig } from 'payload';
export const Posts: CollectionConfig = {
slug: 'posts',

View File

@@ -11,7 +11,7 @@ The behavior of [Collections](../configuration/collections) within the [Admin Pa
To configure Admin Options for Collections, use the `admin` property in your Collection Config:
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...
@@ -89,7 +89,7 @@ It is possible to display a Preview Button within the Edit View of the Admin Pan
To configure the Preview Button, set the `admin.preview` property to a function in your [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const Posts: CollectionConfig = {
// ...
@@ -126,7 +126,7 @@ All Collections receive their own List View which displays a paginated list of d
To configure pagination options, use the `admin.pagination` property in your [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const Posts: CollectionConfig = {
// ...
@@ -155,7 +155,7 @@ In the List View, there is a "search" box that allows you to quickly find a docu
To define which fields should be searched, use the `admin.listSearchableFields` property in your [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const Posts: CollectionConfig = {
// ...

View File

@@ -217,7 +217,6 @@ Client Component:
'use client'
import React from 'react'
import type { TextFieldClientComponent } from 'payload'
import { TextField } from '@payloadcms/ui'
export const MyTextField: TextFieldClientComponent = ({ field }) => {
return <TextField field={field} />

View File

@@ -29,7 +29,7 @@ The lockDocuments property exists on both the Collection Config and the Global C
Heres an example configuration for document locking:
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const Posts: CollectionConfig = {
slug: 'posts',

View File

@@ -151,7 +151,7 @@ Collection Metadata is the metadata that is applied to all pages within any give
To customize Collection Metadata, use the `admin.meta` key within your Collection Config:
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...

View File

@@ -38,7 +38,7 @@ At its core a strategy simply takes information from the incoming request and re
Your `authenticate` method should return an object containing a Payload user document and any optional headers that you'd like Payload to set for you when we return a response.
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const Users: CollectionConfig = {
slug: 'users',

View File

@@ -15,7 +15,7 @@ Email Verification forces users to prove they have access to the email address t
To enable Email Verification, use the `auth.verify` property on your [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const Customers: CollectionConfig = {
// ...
@@ -42,7 +42,7 @@ The following options are available:
Function that accepts one argument, containing `{ req, token, user }`, that allows for overriding the HTML within emails that are sent to users indicating how to validate their account. The function should return a string that supports HTML, which can optionally be a full HTML email.
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const Customers: CollectionConfig = {
// ...
@@ -74,7 +74,7 @@ export const Customers: CollectionConfig = {
Similarly to the above `generateEmailHTML`, you can also customize the subject of the email. The function argument are the same but you can only return a string - not HTML.
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const Customers: CollectionConfig = {
// ...
@@ -95,7 +95,7 @@ export const Customers: CollectionConfig = {
You can customize how the Forgot Password workflow operates with the following options on the `auth.forgotPassword` property:
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const Customers: CollectionConfig = {
// ...
@@ -119,7 +119,7 @@ The following options are available:
This function allows for overriding the HTML within emails that are sent to users attempting to reset their password. The function should return a string that supports HTML, which can be a full HTML email.
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const Customers: CollectionConfig = {
// ...
@@ -179,7 +179,7 @@ The following arguments are passed to the `generateEmailHTML` function:
Similarly to the above `generateEmailHTML`, you can also customize the subject of the email. The function argument are the same but you can only return a string - not HTML.
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const Customers: CollectionConfig = {
// ...

View File

@@ -25,7 +25,7 @@ When Authentication is enabled on a [Collection](../configuration/collections),
To enable Authentication on a Collection, use the `auth` property in the [Collection Config](../configuration/collection#auth):
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const Users: CollectionConfig = {
// ...
@@ -48,7 +48,7 @@ Any [Collection](../configuration/collections) can opt-in to supporting Authenti
To enable Authentication on a Collection, use the `auth` property in the [Collection Config](../configuration/collections):
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const Admins: CollectionConfig = {
// ...

View File

@@ -37,7 +37,7 @@ It's often best practice to write your Collections in separate files and then im
Here is what a simple Collection Config might look like:
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const Posts: CollectionConfig = {
slug: 'posts',

View File

@@ -117,7 +117,7 @@ While Payload's built-in features come fully translated, you may also want to tr
To do this, provide the translations wherever applicable, keyed to the language code:
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const Articles: CollectionConfig = {
slug: 'articles',

View File

@@ -216,7 +216,7 @@ Cross-origin resource sharing (CORS) can be configured with either a whitelist a
Here's an example showing how to allow incoming requests from any domain:
```ts
import { buildConfig } from 'payload'
import { buildConfig } from 'payload/config'
export default buildConfig({
// ...
@@ -227,7 +227,7 @@ export default buildConfig({
Here's an example showing how to append a new header (`x-custom-header`) in `Access-Control-Allow-Headers`:
```ts
import { buildConfig } from 'payload'
import { buildConfig } from 'payload/config'
export default buildConfig({
// ...

View File

@@ -24,7 +24,7 @@ Arrays are useful for many different types of content from simple to complex, su
To create an Array Field, set the `type` to `array` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyArrayField: Field = {
// ...
@@ -69,7 +69,7 @@ _\* An asterisk denotes that a property is required._
To customize the appearance and behavior of the Array Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyArrayField: Field = {
// ...
@@ -92,7 +92,7 @@ The Array Field inherits all of the default options from the base [Field Admin C
In this example, we have an Array Field called `slider` that contains a set of fields for a simple image slider. Each row in the array has a `title`, `image`, and `caption`. We also customize the row label to display the title if it exists, or a default label if it doesn't.
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -24,7 +24,7 @@ Blocks are a great way to create a flexible content model that can be used to bu
To add a Blocks Field, set the `type` to `blocks` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyBlocksField: Field = {
// ...
@@ -67,7 +67,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the Blocks Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyBlocksField: Field = {
// ...

View File

@@ -18,7 +18,7 @@ The Checkbox Field saves a boolean in the database.
To add a Checkbox Field, set the `type` to `checkbox` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyCheckboxField: Field = {
// ...
@@ -53,7 +53,7 @@ _\* An asterisk denotes that a property is required._
Here is an example of a Checkbox Field in a Collection:
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -19,7 +19,7 @@ The Code Field saves a string in the database, but provides the [Admin Panel](..
To add a Code Field, set the `type` to `code` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyBlocksField: Field = {
// ...
@@ -57,7 +57,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the Code Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyCodeField: Field = {
// ...
@@ -79,7 +79,7 @@ The Code Field inherits all of the default options from the base [Field Admin Co
`collections/ExampleCollection.ts
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -18,7 +18,7 @@ The Collapsible Field is presentational-only and only affects the Admin Panel. B
To add a Collapsible Field, set the `type` to `collapsible` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyCollapsibleField: Field = {
// ...
@@ -47,7 +47,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the Collapsible Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyCollapsibleField: Field = {
// ...
@@ -68,7 +68,7 @@ The Collapsible Field inherits all of the default options from the base [Field A
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -18,7 +18,7 @@ The Date Field saves a Date in the database and provides the [Admin Panel](../ad
To add a Date Field, set the `type` to `date` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyDateField: Field = {
// ...
@@ -53,7 +53,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the Date Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyDateField: Field = {
// ...
@@ -97,7 +97,7 @@ When only `pickerAppearance` is set, an equivalent format will be rendered in th
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -18,7 +18,7 @@ The Email Field enforces that the value provided is a valid email address.
To create an Email Field, set the `type` to `email` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyEmailField: Field = {
// ...
@@ -54,7 +54,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the Email Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyEmailField: Field = {
// ...
@@ -76,7 +76,7 @@ The Email Field inherits all of the default options from the base [Field Admin C
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -18,7 +18,7 @@ The Group Field allows [Fields](./overview) to be nested under a common property
To add a Group Field, set the `type` to `group` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyGroupField: Field = {
// ...
@@ -58,7 +58,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the Group Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyGroupField: Field = {
// ...
@@ -79,7 +79,7 @@ The Group Field inherits all of the default options from the base [Field Admin C
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -30,7 +30,7 @@ collection you are joining. This will reference the collection and path of the f
To add a Relationship Field, set the `type` to `join` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyJoinField: Field = {
// highlight-start

View File

@@ -19,7 +19,7 @@ The JSON Field saves actual JSON in the database, which differs from the Code fi
To add a JSON Field, set the `type` to `json` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyJSONField: Field = {
// ...
@@ -56,7 +56,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the JSON Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyJSONField: Field = {
// ...
@@ -77,7 +77,7 @@ The JSON Field inherits all of the default options from the base [Field Admin Co
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
@@ -102,7 +102,7 @@ If you only provide a URL to a schema, Payload will fetch the desired schema if
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
@@ -135,7 +135,7 @@ export const ExampleCollection: CollectionConfig = {
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -18,7 +18,7 @@ The Number Field stores and validates numeric entry and supports additional nume
To add a Number Field, set the `type` to `number` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyNumberField: Field = {
// ...
@@ -59,7 +59,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the Number Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyNumberField: Field = {
// ...
@@ -82,7 +82,7 @@ The Number Field inherits all of the default options from the base [Field Admin
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -18,7 +18,7 @@ The Point Field saves a pair of coordinates in the database and assigns an index
To add a Point Field, set the `type` to `point` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyPointField: Field = {
// ...
@@ -59,7 +59,7 @@ _\* An asterisk denotes that a property is required._
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -18,7 +18,7 @@ The Radio Field allows for the selection of one value from a predefined set of p
To add a Radio Field, set the `type` to `radio` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyRadioField: Field = {
// ...
@@ -69,7 +69,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the Radio Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyRadioField: Field = {
// ...
@@ -90,7 +90,7 @@ The Radio Field inherits all of the default options from the base [Field Admin C
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -24,7 +24,7 @@ The Relationship field is used in a variety of ways, including:
To add a Relationship Field, set the `type` to `relationship` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyRelationshipField: Field = {
// ...
@@ -74,7 +74,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the Relationship Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyRelationshipField: Field = {
// ...
@@ -159,7 +159,7 @@ called with an argument object with the following properties:
## Example
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -63,7 +63,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the Rich Text Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyRichTextField: Field = {
// ...

View File

@@ -18,7 +18,7 @@ The Row Field is presentational-only and only affects the [Admin Panel](../admin
To add a Row Field, set the `type` to `row` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyRowField: Field = {
// ...
@@ -46,7 +46,7 @@ _\* An asterisk denotes that a property is required._
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -18,7 +18,7 @@ The Select Field provides a dropdown-style interface for choosing options from a
To add a Select Field, set the `type` to `select` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MySelectField: Field = {
// ...
@@ -71,7 +71,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the Select Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MySelectField: Field = {
// ...
@@ -93,7 +93,7 @@ The Select Field inherits all of the default options from the base [Field Admin
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',
@@ -156,7 +156,8 @@ You can import the existing Select component directly from Payload, then extend
```ts
import * as React from 'react';
import { SelectInput, useAuth, useField } from '@payloadcms/ui';
import { SelectInput, useField } from 'payload/components/forms';
import { useAuth } from 'payload/components/utilities';
type CustomSelectProps = {
path: string;

View File

@@ -18,7 +18,7 @@ The Tabs Field is presentational-only and only affects the [Admin Panel](../admi
To add a Tabs Field, set the `type` to `tabs` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyTabsField: Field = {
// ...
@@ -59,7 +59,7 @@ _\* An asterisk denotes that a property is required._
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -18,7 +18,7 @@ The Text Field is one of the most commonly used fields. It saves a string to the
To add a Text Field, set the `type` to `text` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyTextField: Field = {
// ...
@@ -59,7 +59,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the Text Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyTextField: Field = {
// ...
@@ -82,7 +82,7 @@ The Text Field inherits all of the default options from the base [Field Admin Co
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -18,7 +18,7 @@ The Textarea Field is nearly identical to the [Text Field](./text) but it featur
To add a Textarea Field, set the `type` to `textarea` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyTextareaField: Field = {
// ...
@@ -56,7 +56,7 @@ _\* An asterisk denotes that a property is required._
The customize the appearance and behavior of the Textarea Field in the [Admin Panel](../admin/overview), you can use the `admin` option:
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyTextareaField: Field = {
// ...
@@ -79,7 +79,7 @@ The Textarea Field inherits all of the default options from the base [Field Admi
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -18,7 +18,7 @@ With the UI Field, you can:
To add a UI Field, set the `type` to `ui` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyUIField: Field = {
// ...
@@ -44,7 +44,7 @@ _\* An asterisk denotes that a property is required._
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -25,7 +25,7 @@ caption="Admin Panel screenshot of an Upload field"
To create an Upload Field, set the `type` to `upload` in your [Field Config](./overview):
```ts
import type { Field } from 'payload'
import type { Field } from 'payload/types'
export const MyUploadField: Field = {
// ...
@@ -73,7 +73,7 @@ _\* An asterisk denotes that a property is required._
`collections/ExampleCollection.ts`
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const ExampleCollection: CollectionConfig = {
slug: 'example-collection',

View File

@@ -13,7 +13,7 @@ In Payload the schema is controlled by your collections and globals. All you nee
Install `@payloadcms/graphql` as a dev dependency:
```bash
pnpm add @payloadcms/graphql@beta -D
pnpm add @payloadcms/graphql --save-dev
```
Run the following command to generate the schema:

View File

@@ -36,7 +36,7 @@ Forms can be as simple or complex as you need, from a basic contact form, to a m
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
```bash
pnpm add @payloadcms/plugin-form-builder@beta
pnpm add @payloadcms/plugin-form-builder
```
## Basic Usage

View File

@@ -48,7 +48,7 @@ Install the plugin using any JavaScript package manager like [Yarn](https://yarn
or [PNPM](https://pnpm.io):
```bash
pnpm add @payloadcms/plugin-nested-docs@beta
pnpm add @payloadcms/plugin-nested-docs
```
## Basic Usage
@@ -177,7 +177,7 @@ You can also extend the built-in `parent` and `breadcrumbs` fields per collectio
and `createBreadcrumbField` methods. They will merge your customizations overtop the plugin's base field configurations.
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
import { createParentField } from '@payloadcms/plugin-nested-docs/fields'
import { createBreadcrumbsField } from '@payloadcms/plugin-nested-docs/fields'

View File

@@ -32,7 +32,7 @@ For example, if you have a page at `/about` and you want to change it to `/about
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
```bash
pnpm add @payloadcms/plugin-redirects@beta
pnpm add @payloadcms/plugin-redirects
```
## Basic Usage

View File

@@ -39,7 +39,7 @@ This plugin is a great way to implement a fast, immersive search experience such
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
```bash
pnpm add @payloadcms/plugin-search@beta
pnpm add @payloadcms/plugin-search
```
## Basic Usage

View File

@@ -39,7 +39,7 @@ This multi-faceted software offers a range of features that will help you manage
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
```bash
pnpm add @payloadcms/plugin-sentry@beta
pnpm add @payloadcms/plugin-sentry
```
## Sentry for Next.js setup

View File

@@ -37,7 +37,7 @@ To help you visualize what your page might look like in a search engine, a previ
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
```bash
pnpm add @payloadcms/plugin-seo@beta
pnpm add @payloadcms/plugin-seo
```
## Basic Usage

View File

@@ -39,7 +39,7 @@ The beauty of this plugin is the entirety of your application's content and busi
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
```bash
pnpm add @payloadcms/plugin-stripe@beta
pnpm add @payloadcms/plugin-stripe
```
## Basic Usage

View File

@@ -591,7 +591,7 @@ Each endpoint object needs to have:
Example:
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
// a collection of 'orders' with an additional route for tracking details, reachable at /api/orders/:id/tracking
export const Orders: CollectionConfig = {

View File

@@ -34,7 +34,7 @@ export default buildConfig({
And here's an example for how to install the Slate editor on a field-by-field basis, while customizing its options:
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
import { slateEditor } from '@payloadcms/richtext-slate'
export const Pages: CollectionConfig = {

View File

@@ -43,7 +43,7 @@ Every Payload Collection can opt-in to supporting Uploads by specifying the `upl
</Banner>
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const Media: CollectionConfig = {
slug: 'media',
@@ -217,7 +217,7 @@ You can specify how Payload retrieves admin thumbnails for your upload-enabled C
1. `adminThumbnail` as a **string**, equal to one of your provided image size names.
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const Media: CollectionConfig = {
slug: 'media',
@@ -246,7 +246,7 @@ export const Media: CollectionConfig = {
2. `adminThumbnail` as a **function** that takes the document's data and sends back a full URL to load the thumbnail.
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const Media: CollectionConfig = {
slug: 'media',
@@ -267,7 +267,7 @@ Some example values are: `image/*`, `audio/*`, `video/*`, `image/png`, `applicat
**Example mimeTypes usage:**
```ts
import type { CollectionConfig } from 'payload'
import { CollectionConfig } from 'payload'
export const Media: CollectionConfig = {
slug: 'media',

View File

@@ -22,7 +22,7 @@ Payload offers additional storage adapters to handle file uploads. These adapter
### Installation
```sh
pnpm add @payloadcms/storage-vercel-blob@beta
pnpm add @payloadcms/storage-vercel-blob
```
### Usage
@@ -71,7 +71,7 @@ export default buildConfig({
### Installation
```sh
pnpm add @payloadcms/storage-s3@beta
pnpm add @payloadcms/storage-s3
```
### Usage
@@ -119,7 +119,7 @@ See the the [AWS SDK Package](https://github.com/aws/aws-sdk-js-v3) and [`S3Clie
### Installation
```sh
pnpm add @payloadcms/storage-azure@beta
pnpm add @payloadcms/storage-azure
```
### Usage
@@ -168,7 +168,7 @@ export default buildConfig({
### Installation
```sh
pnpm add @payloadcms/storage-gcs@beta
pnpm add @payloadcms/storage-gcs
```
### Usage
@@ -218,7 +218,7 @@ export default buildConfig({
### Installation
```sh
pnpm add @payloadcms/storage-uploadthing@beta
pnpm add @payloadcms/storage-uploadthing
```
### Usage
@@ -261,7 +261,7 @@ If you need to create a custom storage adapter, you can use the [`@payloadcms/pl
### Installation
`pnpm add @payloadcms/plugin-cloud-storage@beta`
`pnpm add @payloadcms/plugin-cloud-storage`
### Usage

View File

@@ -1,6 +1,6 @@
{
"name": "payload-monorepo",
"version": "3.0.0-beta.118",
"version": "3.0.0-beta.116",
"private": true,
"type": "module",
"scripts": {
@@ -105,12 +105,12 @@
"devDependencies": {
"@jest/globals": "29.7.0",
"@libsql/client": "0.14.0",
"@next/bundle-analyzer": "15.0.0",
"@next/bundle-analyzer": "15.0.0-canary.173",
"@payloadcms/db-postgres": "workspace:*",
"@payloadcms/eslint-config": "workspace:*",
"@payloadcms/eslint-plugin": "workspace:*",
"@payloadcms/live-preview-react": "workspace:*",
"@playwright/test": "1.48.1",
"@playwright/test": "1.46.0",
"@sentry/nextjs": "^8.33.1",
"@sentry/node": "^8.33.1",
"@swc-node/register": "1.10.9",
@@ -145,15 +145,15 @@
"lint-staged": "15.2.7",
"minimist": "1.2.8",
"mongodb-memory-server": "^9.0",
"next": "15.0.0",
"next": "15.0.0-canary.173",
"open": "^10.1.0",
"p-limit": "^5.0.0",
"playwright": "1.48.1",
"playwright-core": "1.48.1",
"playwright": "1.46.0",
"playwright-core": "1.46.0",
"prettier": "3.3.3",
"prompts": "2.4.2",
"react": "19.0.0-rc-65a56d0e-20241020",
"react-dom": "19.0.0-rc-65a56d0e-20241020",
"react": "19.0.0-rc-3edc000d-20240926",
"react-dom": "19.0.0-rc-3edc000d-20240926",
"rimraf": "3.0.2",
"semver": "^7.5.4",
"sharp": "0.32.6",
@@ -164,11 +164,11 @@
"tempy": "1.0.1",
"tsx": "4.19.1",
"turbo": "^2.1.3",
"typescript": "5.6.3"
"typescript": "5.6.2"
},
"peerDependencies": {
"react": "^19.0.0 || ^19.0.0-rc-65a56d0e-20241020",
"react-dom": "^19.0.0 || ^19.0.0-rc-65a56d0e-20241020"
"react": "^19.0.0 || ^19.0.0-rc-3edc000d-20240926",
"react-dom": "^19.0.0 || ^19.0.0-rc-3edc000d-20240926"
},
"packageManager": "pnpm@9.7.1",
"engines": {

View File

@@ -1,6 +1,6 @@
{
"name": "create-payload-app",
"version": "3.0.0-beta.118",
"version": "3.0.0-beta.116",
"homepage": "https://payloadcms.com",
"repository": {
"type": "git",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/db-mongodb",
"version": "3.0.0-beta.118",
"version": "3.0.0-beta.116",
"description": "The officially supported MongoDB database adapter for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -6,23 +6,12 @@ import { combineQueries, flattenWhereToOperators } from 'payload'
import type { MongooseAdapter } from './index.js'
import { buildSortParam } from './queries/buildSortParam.js'
import { buildJoinAggregation } from './utilities/buildJoinAggregation.js'
import { sanitizeInternalFields } from './utilities/sanitizeInternalFields.js'
import { withSession } from './withSession.js'
export const queryDrafts: QueryDrafts = async function queryDrafts(
this: MongooseAdapter,
{
collection,
joins,
limit,
locale,
page,
pagination,
req = {} as PayloadRequest,
sort: sortArg,
where,
},
{ collection, limit, locale, page, pagination, req = {} as PayloadRequest, sort: sortArg, where },
) {
const VersionModel = this.versions[collection]
const collectionConfig = this.payload.collections[collection].config
@@ -100,29 +89,7 @@ export const queryDrafts: QueryDrafts = async function queryDrafts(
paginationOptions.options.limit = limit
}
let result
const aggregate = await buildJoinAggregation({
adapter: this,
collection,
collectionConfig,
joins,
limit,
locale,
query: versionQuery,
versions: true,
})
// build join aggregation
if (aggregate) {
result = await VersionModel.aggregatePaginate(
VersionModel.aggregate(aggregate),
paginationOptions,
)
} else {
result = await VersionModel.paginate(versionQuery, paginationOptions)
}
const result = await VersionModel.paginate(versionQuery, paginationOptions)
const docs = JSON.parse(JSON.stringify(result.docs))
return {

View File

@@ -15,8 +15,6 @@ type BuildJoinAggregationArgs = {
locale: string
// the where clause for the top collection
query?: Where
/** whether the query is from drafts */
versions?: boolean
}
export const buildJoinAggregation = async ({
@@ -27,7 +25,6 @@ export const buildJoinAggregation = async ({
limit,
locale,
query,
versions,
}: BuildJoinAggregationArgs): Promise<PipelineStage[] | undefined> => {
if (Object.keys(collectionConfig.joins).length === 0 || joins === false) {
return
@@ -93,7 +90,7 @@ export const buildJoinAggregation = async ({
if (adapter.payload.config.localization && locale === 'all') {
adapter.payload.config.localization.localeCodes.forEach((code) => {
const as = `${versions ? `version.${join.schemaPath}` : join.schemaPath}${code}`
const as = `${join.schemaPath}${code}`
aggregate.push(
{
@@ -101,7 +98,7 @@ export const buildJoinAggregation = async ({
as: `${as}.docs`,
foreignField: `${join.field.on}${code}`,
from: slug,
localField: versions ? 'parent' : '_id',
localField: '_id',
pipeline,
},
},
@@ -134,7 +131,7 @@ export const buildJoinAggregation = async ({
} else {
const localeSuffix =
join.field.localized && adapter.payload.config.localization && locale ? `.${locale}` : ''
const as = `${versions ? `version.${join.schemaPath}` : join.schemaPath}${localeSuffix}`
const as = `${join.schemaPath}${localeSuffix}`
aggregate.push(
{
@@ -142,7 +139,7 @@ export const buildJoinAggregation = async ({
as: `${as}.docs`,
foreignField: `${join.field.on}${localeSuffix}`,
from: slug,
localField: versions ? 'parent' : '_id',
localField: '_id',
pipeline,
},
},

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/db-postgres",
"version": "3.0.0-beta.118",
"version": "3.0.0-beta.116",
"description": "The officially supported Postgres database adapter for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -66,7 +66,7 @@ export const connect: Connect = async function connect(
}
const logger = this.logger || false
this.drizzle = drizzle({ client: this.pool, logger, schema: this.schema })
this.drizzle = drizzle(this.pool, { logger, schema: this.schema })
if (!hotReload) {
if (process.env.PAYLOAD_DROP_DATABASE === 'true') {

View File

@@ -9,7 +9,7 @@ import type {
import type { DrizzleAdapter } from '@payloadcms/drizzle/types'
import type { DrizzleConfig } from 'drizzle-orm'
import type { PgSchema, PgTableFn, PgTransactionConfig } from 'drizzle-orm/pg-core'
import type { Pool, PoolConfig } from 'pg'
import type { Client, Pool, PoolConfig } from 'pg'
export type Args = {
/**
@@ -62,7 +62,9 @@ declare module 'payload' {
afterSchemaInit: PostgresSchemaHook[]
beforeSchemaInit: PostgresSchemaHook[]
beginTransaction: (options?: PgTransactionConfig) => Promise<null | number | string>
drizzle: PostgresDB
drizzle: {
$client: Client | Pool
} & PostgresDB
enums: Record<string, GenericEnum>
/**
* An object keyed on each table, with a key value pair where the constraint name is the key, followed by the dot-notation field name

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/db-sqlite",
"version": "3.0.0-beta.118",
"version": "3.0.0-beta.116",
"description": "The officially supported SQLite database adapter for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,5 +1,4 @@
import type { DrizzleAdapter } from '@payloadcms/drizzle/types'
import type { LibSQLDatabase } from 'drizzle-orm/libsql'
import type { Connect } from 'payload'
import { createClient } from '@libsql/client'

View File

@@ -70,6 +70,7 @@ export const init: Init = async function init(this: SQLiteAdapter) {
disableNotNull: !!collection?.versions?.drafts,
disableUnique: false,
fields: collection.fields,
joins: collection.joins,
locales,
tableName,
timestamps: collection.timestamps,

View File

@@ -61,6 +61,7 @@ type Args = {
disableRelsTableUnique?: boolean
disableUnique: boolean
fields: Field[]
joins?: SanitizedJoins
locales?: [string, ...string[]]
rootRelationships?: Set<string>
rootRelationsToBuild?: RelationMap
@@ -94,6 +95,7 @@ export const buildTable = ({
disableRelsTableUnique,
disableUnique = false,
fields,
joins,
locales,
rootRelationships,
rootRelationsToBuild,
@@ -142,6 +144,7 @@ export const buildTable = ({
disableUnique,
fields,
indexes,
joins,
locales,
localesColumns,
localesIndexes,

View File

@@ -44,6 +44,7 @@ type Args = {
fields: (Field | TabAsField)[]
forceLocalized?: boolean
indexes: Record<string, (cols: GenericColumns) => IndexBuilder>
joins?: SanitizedJoins
locales: [string, ...string[]]
localesColumns: Record<string, SQLiteColumnBuilder>
localesIndexes: Record<string, (cols: GenericColumns) => IndexBuilder>
@@ -83,6 +84,7 @@ export const traverseFields = ({
fields,
forceLocalized,
indexes,
joins,
locales,
localesColumns,
localesIndexes,
@@ -667,6 +669,7 @@ export const traverseFields = ({
fields: field.fields,
forceLocalized,
indexes,
joins,
locales,
localesColumns,
localesIndexes,
@@ -722,6 +725,7 @@ export const traverseFields = ({
fields: field.fields,
forceLocalized: field.localized,
indexes,
joins,
locales,
localesColumns,
localesIndexes,
@@ -778,6 +782,7 @@ export const traverseFields = ({
fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })),
forceLocalized,
indexes,
joins,
locales,
localesColumns,
localesIndexes,
@@ -834,6 +839,7 @@ export const traverseFields = ({
fields: field.fields,
forceLocalized,
indexes,
joins,
locales,
localesColumns,
localesIndexes,
@@ -931,6 +937,30 @@ export const traverseFields = ({
break
case 'join': {
// fieldName could be 'posts' or 'group_posts'
// using `on` as the key for the relation
const localized = adapter.payload.config.localization && field.localized
const fieldSchemaPath = `${fieldPrefix || ''}${field.name}`
let target: string
const joinConfig = joins[field.collection].find(
({ schemaPath }) => fieldSchemaPath === schemaPath,
)
if (joinConfig.targetField.hasMany) {
target = `${adapter.tableNameMap.get(toSnakeCase(field.collection))}${adapter.relationshipsSuffix}`
} else {
target = `${adapter.tableNameMap.get(toSnakeCase(field.collection))}${localized ? adapter.localesSuffix : ''}`
}
relationsToBuild.set(fieldName, {
type: 'many',
// joins are not localized on the parent table
localized: false,
relationName: field.on.replaceAll('.', '_'),
target,
})
break
}
default:
break
}

View File

@@ -167,7 +167,9 @@ declare module 'payload' {
extends Omit<Args, 'idType' | 'logger' | 'migrationDir' | 'pool'>,
DrizzleAdapter {
beginTransaction: (options?: SQLiteTransactionConfig) => Promise<null | number | string>
drizzle: LibSQLDatabase
drizzle: { $client: Client } & LibSQLDatabase<
Record<string, GenericRelation | GenericTable> & Record<string, unknown>
>
/**
* An object keyed on each table, with a key value pair where the constraint name is the key, followed by the dot-notation field name
* Used for returning properly formed errors from unique fields

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/db-vercel-postgres",
"version": "3.0.0-beta.118",
"version": "3.0.0-beta.116",
"description": "Vercel Postgres adapter for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -26,8 +26,7 @@ export const connect: Connect = async function connect(
const logger = this.logger || false
// Passed the poolOptions if provided,
// else have vercel/postgres detect the connection string from the environment
this.drizzle = drizzle({
client: this.poolOptions ? new VercelPool(this.poolOptions) : sql,
this.drizzle = drizzle(this.poolOptions ? new VercelPool(this.poolOptions) : sql, {
logger,
schema: this.schema,
})

View File

@@ -7,7 +7,7 @@ import type {
PostgresSchemaHook,
} from '@payloadcms/drizzle/postgres'
import type { DrizzleAdapter } from '@payloadcms/drizzle/types'
import type { VercelPool, VercelPostgresPoolConfig } from '@vercel/postgres'
import type { VercelClient, VercelPool, VercelPostgresPoolConfig } from '@vercel/postgres'
import type { DrizzleConfig } from 'drizzle-orm'
import type { PgSchema, PgTableFn, PgTransactionConfig } from 'drizzle-orm/pg-core'
@@ -56,6 +56,9 @@ export type Args = {
}
export type VercelPostgresAdapter = {
drizzle: {
$client: VercelClient | VercelPool
} & PostgresDB
pool?: VercelPool
poolOptions?: Args['pool']
} & BasePostgresAdapter
@@ -67,7 +70,9 @@ declare module 'payload' {
afterSchemaInit: PostgresSchemaHook[]
beforeSchemaInit: PostgresSchemaHook[]
beginTransaction: (options?: PgTransactionConfig) => Promise<null | number | string>
drizzle: PostgresDB
drizzle: {
$client: VercelClient | VercelPool
} & PostgresDB
enums: Record<string, GenericEnum>
/**
* An object keyed on each table, with a key value pair where the constraint name is the key, followed by the dot-notation field name

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/drizzle",
"version": "3.0.0-beta.118",
"version": "3.0.0-beta.116",
"description": "A library of shared functions used by different payload database adapters",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -16,7 +16,6 @@ type BuildFindQueryArgs = {
joins?: BuildQueryJoinAliases
locale?: string
tableName: string
versions?: boolean
}
export type Result = {
@@ -35,7 +34,6 @@ export const buildFindManyArgs = ({
joins = [],
locale,
tableName,
versions,
}: BuildFindQueryArgs): Record<string, unknown> => {
const result: Result = {
extras: {},
@@ -99,7 +97,6 @@ export const buildFindManyArgs = ({
tablePath: '',
topLevelArgs: result,
topLevelTableName: tableName,
versions,
})
return result

View File

@@ -14,7 +14,6 @@ type Args = {
adapter: DrizzleAdapter
fields: Field[]
tableName: string
versions?: boolean
} & Omit<FindArgs, 'collection'>
export const findMany = async function find({
@@ -29,7 +28,6 @@ export const findMany = async function find({
skip,
sort,
tableName,
versions,
where: whereArg,
}: Args) {
const db = adapter.sessions[await req.transactionID]?.db || adapter.drizzle
@@ -73,7 +71,6 @@ export const findMany = async function find({
joinQuery,
joins,
tableName,
versions,
})
selectDistinctMethods.push({ args: [offset], method: 'offset' })

View File

@@ -1,3 +1,4 @@
import type { DBQueryConfig } from 'drizzle-orm'
import type { LibSQLDatabase } from 'drizzle-orm/libsql'
import type { Field, JoinQuery } from 'payload'
@@ -25,7 +26,6 @@ type TraverseFieldArgs = {
tablePath: string
topLevelArgs: Record<string, unknown>
topLevelTableName: string
versions?: boolean
}
export const traverseFields = ({
@@ -42,7 +42,6 @@ export const traverseFields = ({
tablePath,
topLevelArgs,
topLevelTableName,
versions,
}: TraverseFieldArgs) => {
fields.forEach((field) => {
if (fieldIsVirtual(field)) {
@@ -100,7 +99,6 @@ export const traverseFields = ({
tablePath: tabTablePath,
topLevelArgs,
topLevelTableName,
versions,
})
})
@@ -225,7 +223,6 @@ export const traverseFields = ({
tablePath: `${tablePath}${toSnakeCase(field.name)}_`,
topLevelArgs,
topLevelTableName,
versions,
})
break
@@ -236,156 +233,87 @@ export const traverseFields = ({
if (joinQuery === false) {
break
}
const {
limit: limitArg = 10,
sort,
where,
} = joinQuery[`${path.replaceAll('_', '.')}${field.name}`] || {}
let limit = limitArg
if (limit !== 0) {
// get an additional document and slice it later to determine if there is a next page
limit += 1
}
const fields = adapter.payload.collections[field.collection].config.fields
const joinCollectionTableName = adapter.tableNameMap.get(toSnakeCase(field.collection))
let joinTableName = `${adapter.tableNameMap.get(toSnakeCase(field.collection))}${
field.localized && adapter.payload.config.localization ? adapter.localesSuffix : ''
}`
const joins: BuildQueryJoinAliases = []
const buildQueryResult = buildQuery({
adapter,
fields,
joins,
locale,
sort,
tableName: joinCollectionTableName,
where,
})
let subQueryWhere = buildQueryResult.where
const orderBy = buildQueryResult.orderBy
let joinLocalesCollectionTableName: string | undefined
const currentIDColumn = versions
? adapter.tables[currentTableName].parent
: adapter.tables[currentTableName].id
// Handle hasMany _rels table
if (field.hasMany) {
const joinRelsCollectionTableName = `${joinCollectionTableName}${adapter.relationshipsSuffix}`
const db = adapter.drizzle as LibSQLDatabase
if (field.localized) {
joinLocalesCollectionTableName = joinRelsCollectionTableName
joinTableName = adapter.tableNameMap.get(toSnakeCase(field.collection))
}
const joinTable = `${joinTableName}${adapter.relationshipsSuffix}`
let columnReferenceToCurrentID: string
if (versions) {
columnReferenceToCurrentID = `${topLevelTableName.replace('_', '').replace(new RegExp(`${adapter.versionsSuffix}$`), '')}_id`
} else {
columnReferenceToCurrentID = `${topLevelTableName}_id`
}
joins.push({
type: 'innerJoin',
condition: and(
eq(
adapter.tables[joinRelsCollectionTableName].parent,
adapter.tables[joinCollectionTableName].id,
),
eq(
sql.raw(`"${joinRelsCollectionTableName}"."${columnReferenceToCurrentID}"`),
currentIDColumn,
),
eq(adapter.tables[joinRelsCollectionTableName].path, field.on),
),
table: adapter.tables[joinRelsCollectionTableName],
})
} else {
// Handle localized without hasMany
const foreignColumn = field.on.replaceAll('.', '_')
if (field.localized) {
joinLocalesCollectionTableName = `${joinCollectionTableName}${adapter.localesSuffix}`
joins.push({
const joins: BuildQueryJoinAliases = [
{
type: 'innerJoin',
condition: and(
eq(adapter.tables[joinTable].parent, adapter.tables[joinTableName].id),
eq(
adapter.tables[joinLocalesCollectionTableName]._parentID,
adapter.tables[joinCollectionTableName].id,
),
eq(
adapter.tables[joinLocalesCollectionTableName][foreignColumn],
currentIDColumn,
sql.raw(`"${joinTable}"."${topLevelTableName}_id"`),
adapter.tables[currentTableName].id,
),
eq(adapter.tables[joinTable].path, field.on),
),
table: adapter.tables[joinLocalesCollectionTableName],
})
// Handle without localized and without hasMany, just a condition append to where. With localized the inner join handles eq.
} else {
const constraint = eq(
adapter.tables[joinCollectionTableName][foreignColumn],
currentIDColumn,
)
table: adapter.tables[joinTable],
},
]
if (subQueryWhere) {
subQueryWhere = and(subQueryWhere, constraint)
} else {
subQueryWhere = constraint
}
}
}
const chainedMethods: ChainedMethods = []
joins.forEach(({ type, condition, table }) => {
chainedMethods.push({
args: [table, condition],
method: type ?? 'leftJoin',
const { orderBy, where: subQueryWhere } = buildQuery({
adapter,
fields,
joins,
locale,
sort,
tableName: joinCollectionTableName,
where: {},
})
})
if (limit !== 0) {
chainedMethods.push({
args: [limit],
method: 'limit',
})
}
const chainedMethods: ChainedMethods = []
const db = adapter.drizzle as LibSQLDatabase
const subQuery = chainMethods({
methods: chainedMethods,
query: db
.select({
id: adapter.tables[joinCollectionTableName].id,
...(joinLocalesCollectionTableName && {
locale:
adapter.tables[joinLocalesCollectionTableName].locale ||
adapter.tables[joinLocalesCollectionTableName]._locale,
}),
joins.forEach(({ type, condition, table }) => {
chainedMethods.push({
args: [table, condition],
method: type ?? 'leftJoin',
})
.from(adapter.tables[joinCollectionTableName])
.where(subQueryWhere)
.orderBy(orderBy.order(orderBy.column)),
})
})
const columnName = `${path.replaceAll('.', '_')}${field.name}`
const subQuery = chainMethods({
methods: chainedMethods,
query: db
.select({
id: adapter.tables[joinTableName].id,
...(field.localized && {
locale: adapter.tables[joinTable].locale,
}),
})
.from(adapter.tables[joinTableName])
.where(subQueryWhere)
.orderBy(orderBy.order(orderBy.column))
.limit(limit),
})
const jsonObjectSelect = field.localized
? sql.raw(
`'_parentID', "id", '_locale', "${adapter.tables[joinLocalesCollectionTableName].locale ? 'locale' : '_locale'}"`,
)
: sql.raw(`'id', "id"`)
const columnName = `${path.replaceAll('.', '_')}${field.name}`
if (adapter.name === 'sqlite') {
currentArgs.extras[columnName] = sql`
const jsonObjectSelect = field.localized
? sql.raw(`'_parentID', "id", '_locale', "locale"`)
: sql.raw(`'id', "id"`)
if (adapter.name === 'sqlite') {
currentArgs.extras[columnName] = sql`
COALESCE((
SELECT json_group_array(json_object(${jsonObjectSelect}))
FROM (
@@ -393,8 +321,8 @@ export const traverseFields = ({
) AS ${sql.raw(`${columnName}_sub`)}
), '[]')
`.as(columnName)
} else {
currentArgs.extras[columnName] = sql`
} else {
currentArgs.extras[columnName] = sql`
COALESCE((
SELECT json_agg(json_build_object(${jsonObjectSelect}))
FROM (
@@ -402,8 +330,41 @@ export const traverseFields = ({
) AS ${sql.raw(`${columnName}_sub`)}
), '[]'::json)
`.as(columnName)
}
break
}
const selectFields = {}
const withJoin: DBQueryConfig<'many', true, any, any> = {
columns: selectFields,
}
if (limit) {
withJoin.limit = limit
}
if (field.localized) {
withJoin.columns._locale = true
withJoin.columns._parentID = true
} else {
withJoin.columns.id = true
withJoin.columns.parent = true
}
const { orderBy, where: joinWhere } = buildQuery({
adapter,
fields,
joins,
locale,
sort,
tableName: joinTableName,
where,
})
if (joinWhere) {
withJoin.where = () => joinWhere
}
withJoin.orderBy = orderBy.order(orderBy.column)
currentArgs.with[`${path.replaceAll('.', '_')}${field.name}`] = withJoin
break
}

View File

@@ -57,6 +57,7 @@ export const init: Init = async function init(this: BasePostgresAdapter) {
disableNotNull: !!collection?.versions?.drafts,
disableUnique: false,
fields: collection.fields,
joins: collection.joins,
tableName,
timestamps: collection.timestamps,
versions: false,

View File

@@ -50,6 +50,7 @@ type Args = {
disableRelsTableUnique?: boolean
disableUnique: boolean
fields: Field[]
joins?: SanitizedJoins
rootRelationships?: Set<string>
rootRelationsToBuild?: RelationMap
rootTableIDColType?: string
@@ -82,6 +83,7 @@ export const buildTable = ({
disableRelsTableUnique = false,
disableUnique = false,
fields,
joins,
rootRelationships,
rootRelationsToBuild,
rootTableIDColType,
@@ -131,6 +133,7 @@ export const buildTable = ({
disableUnique,
fields,
indexes,
joins,
localesColumns,
localesIndexes,
newTableName: tableName,

View File

@@ -50,6 +50,7 @@ type Args = {
fields: (Field | TabAsField)[]
forceLocalized?: boolean
indexes: Record<string, (cols: GenericColumns) => IndexBuilder>
joins?: SanitizedJoins
localesColumns: Record<string, PgColumnBuilder>
localesIndexes: Record<string, (cols: GenericColumns) => IndexBuilder>
newTableName: string
@@ -88,6 +89,7 @@ export const traverseFields = ({
fields,
forceLocalized,
indexes,
joins,
localesColumns,
localesIndexes,
newTableName,
@@ -670,6 +672,7 @@ export const traverseFields = ({
fields: field.fields,
forceLocalized,
indexes,
joins,
localesColumns,
localesIndexes,
newTableName,
@@ -724,6 +727,7 @@ export const traverseFields = ({
fields: field.fields,
forceLocalized: field.localized,
indexes,
joins,
localesColumns,
localesIndexes,
newTableName: `${parentTableName}_${columnName}`,
@@ -779,6 +783,7 @@ export const traverseFields = ({
fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })),
forceLocalized,
indexes,
joins,
localesColumns,
localesIndexes,
newTableName,
@@ -834,6 +839,7 @@ export const traverseFields = ({
fields: field.fields,
forceLocalized,
indexes,
joins,
localesColumns,
localesIndexes,
newTableName,
@@ -930,6 +936,30 @@ export const traverseFields = ({
break
case 'join': {
// fieldName could be 'posts' or 'group_posts'
// using `on` as the key for the relation
const localized = adapter.payload.config.localization && field.localized
const fieldSchemaPath = `${fieldPrefix || ''}${field.name}`
let target: string
const joinConfig = joins[field.collection].find(
({ schemaPath }) => fieldSchemaPath === schemaPath,
)
if (joinConfig.targetField.hasMany) {
target = `${adapter.tableNameMap.get(toSnakeCase(field.collection))}${adapter.relationshipsSuffix}`
} else {
target = `${adapter.tableNameMap.get(toSnakeCase(field.collection))}${localized ? adapter.localesSuffix : ''}`
}
relationsToBuild.set(fieldName, {
type: 'many',
// joins are not localized on the parent table
localized: false,
relationName: field.on.replaceAll('.', '_'),
target,
})
break
}
default:
break
}

View File

@@ -21,7 +21,7 @@ import type {
} from 'drizzle-orm/pg-core'
import type { PgTableFn } from 'drizzle-orm/pg-core/table'
import type { Payload, PayloadRequest } from 'payload'
import type { ClientConfig, QueryResult } from 'pg'
import type { Client, ClientConfig, Pool, QueryResult } from 'pg'
import type { extendDrizzleTable, Operators } from '../index.js'
import type { BuildQueryJoinAliases, DrizzleAdapter, TransactionPg } from '../types.js'
@@ -134,7 +134,9 @@ export type BasePostgresAdapter = {
defaultDrizzleSnapshot: DrizzleSnapshotJSON
deleteWhere: DeleteWhere
disableCreateDatabase: boolean
drizzle: PostgresDB
drizzle: {
$client: Client | Pool
} & PostgresDB
dropDatabase: DropDatabase
enums: Record<string, GenericEnum>
execute: Execute<unknown>

View File

@@ -1,25 +0,0 @@
import type { SQL } from 'drizzle-orm'
import type { PgTableWithColumns } from 'drizzle-orm/pg-core'
import type { GenericTable } from '../types.js'
import type { BuildQueryJoinAliases } from './buildQuery.js'
import { getNameFromDrizzleTable } from '../utilities/getNameFromDrizzleTable.js'
export const addJoinTable = ({
type,
condition,
joins,
table,
}: {
condition: SQL
joins: BuildQueryJoinAliases
table: GenericTable | PgTableWithColumns<any>
type?: 'innerJoin' | 'leftJoin' | 'rightJoin'
}) => {
const name = getNameFromDrizzleTable(table)
if (!joins.some((eachJoin) => getNameFromDrizzleTable(eachJoin.table) === name)) {
joins.push({ type, condition, table })
}
}

View File

@@ -55,6 +55,7 @@ export const buildOrderBy = ({
pathSegments: sortPath.replace(/__/g, '.').split('.'),
selectFields,
tableName,
useAlias: true,
value: sortPath,
})
orderBy.column = sortTable?.[sortTableColumnName]

View File

@@ -13,7 +13,6 @@ import type { DrizzleAdapter, GenericColumn } from '../types.js'
import type { BuildQueryJoinAliases } from './buildQuery.js'
import { isPolymorphicRelationship } from '../utilities/isPolymorphicRelationship.js'
import { addJoinTable } from './addJoinTable.js'
import { getTableAlias } from './getTableAlias.js'
type Constraint = {
@@ -54,6 +53,7 @@ type Args = {
* If creating a new table name for arrays and blocks, this suffix should be appended to the table name
*/
tableNameSuffix?: string
useAlias?: boolean
/**
* The raw value of the query before sanitization
*/
@@ -79,6 +79,7 @@ export const getTableColumnFromPath = ({
selectFields,
tableName,
tableNameSuffix = '',
useAlias,
value,
}: Args): TableColumn => {
const fieldPath = incomingSegments[0]
@@ -140,6 +141,7 @@ export const getTableColumnFromPath = ({
selectFields,
tableName: newTableName,
tableNameSuffix,
useAlias,
value,
})
}
@@ -160,6 +162,7 @@ export const getTableColumnFromPath = ({
selectFields,
tableName: newTableName,
tableNameSuffix: `${tableNameSuffix}${toSnakeCase(field.name)}_`,
useAlias,
value,
})
}
@@ -178,6 +181,7 @@ export const getTableColumnFromPath = ({
selectFields,
tableName: newTableName,
tableNameSuffix,
useAlias,
value,
})
}
@@ -186,9 +190,8 @@ export const getTableColumnFromPath = ({
if (locale && field.localized && adapter.payload.config.localization) {
newTableName = `${tableName}${adapter.localesSuffix}`
addJoinTable({
joins.push({
condition: eq(adapter.tables[tableName].id, adapter.tables[newTableName]._parentID),
joins,
table: adapter.tables[newTableName],
})
if (locale !== 'all') {
@@ -214,6 +217,7 @@ export const getTableColumnFromPath = ({
selectFields,
tableName: newTableName,
tableNameSuffix: `${tableNameSuffix}${toSnakeCase(field.name)}_`,
useAlias,
value,
})
}
@@ -225,12 +229,11 @@ export const getTableColumnFromPath = ({
)
if (locale && field.localized && adapter.payload.config.localization) {
addJoinTable({
joins.push({
condition: and(
eq(adapter.tables[tableName].id, adapter.tables[newTableName].parent),
eq(adapter.tables[newTableName]._locale, locale),
),
joins,
table: adapter.tables[newTableName],
})
if (locale !== 'all') {
@@ -241,9 +244,8 @@ export const getTableColumnFromPath = ({
})
}
} else {
addJoinTable({
joins.push({
condition: eq(adapter.tables[tableName].id, adapter.tables[newTableName].parent),
joins,
table: adapter.tables[newTableName],
})
}
@@ -274,9 +276,8 @@ export const getTableColumnFromPath = ({
]
if (locale && field.localized && adapter.payload.config.localization) {
addJoinTable({
joins.push({
condition: and(...joinConstraints, eq(adapter.tables[newTableName]._locale, locale)),
joins,
table: adapter.tables[newTableName],
})
if (locale !== 'all') {
@@ -287,9 +288,8 @@ export const getTableColumnFromPath = ({
})
}
} else {
addJoinTable({
joins.push({
condition: and(...joinConstraints),
joins,
table: adapter.tables[newTableName],
})
}
@@ -313,12 +313,11 @@ export const getTableColumnFromPath = ({
constraintPath = `${constraintPath}${field.name}.%.`
if (locale && field.localized && adapter.payload.config.localization) {
addJoinTable({
joins.push({
condition: and(
eq(arrayParentTable.id, adapter.tables[newTableName]._parentID),
eq(adapter.tables[newTableName]._locale, locale),
),
joins,
table: adapter.tables[newTableName],
})
if (locale !== 'all') {
@@ -329,9 +328,8 @@ export const getTableColumnFromPath = ({
})
}
} else {
addJoinTable({
joins.push({
condition: eq(arrayParentTable.id, adapter.tables[newTableName]._parentID),
joins,
table: adapter.tables[newTableName],
})
}
@@ -347,6 +345,7 @@ export const getTableColumnFromPath = ({
rootTableName,
selectFields,
tableName: newTableName,
useAlias,
value,
})
}
@@ -405,6 +404,7 @@ export const getTableColumnFromPath = ({
rootTableName,
selectFields: blockSelectFields,
tableName: newTableName,
useAlias,
value,
})
} catch (error) {
@@ -641,6 +641,7 @@ export const getTableColumnFromPath = ({
rootTableName: newTableName,
selectFields,
tableName: newTableName,
useAlias,
value,
})
} else if (
@@ -693,6 +694,7 @@ export const getTableColumnFromPath = ({
pathSegments: pathSegments.slice(1),
selectFields,
tableName: newTableName,
useAlias,
value,
})
}
@@ -714,11 +716,12 @@ export const getTableColumnFromPath = ({
const parentTable = aliasTable || adapter.tables[tableName]
newTableName = `${tableName}${adapter.localesSuffix}`
newTable = adapter.tables[newTableName]
newTable = useAlias
? getTableAlias({ adapter, tableName: newTableName }).newAliasTable
: adapter.tables[newTableName]
addJoinTable({
joins.push({
condition: eq(parentTable.id, newTable._parentID),
joins,
table: newTable,
})

View File

@@ -1,4 +1,4 @@
import type { JoinQuery, PayloadRequest, QueryDrafts, SanitizedCollectionConfig } from 'payload'
import type { PayloadRequest, QueryDrafts, SanitizedCollectionConfig } from 'payload'
import { buildVersionCollectionFields, combineQueries } from 'payload'
import toSnakeCase from 'to-snake-case'
@@ -9,17 +9,7 @@ import { findMany } from './find/findMany.js'
export const queryDrafts: QueryDrafts = async function queryDrafts(
this: DrizzleAdapter,
{
collection,
joins,
limit,
locale,
page = 1,
pagination,
req = {} as PayloadRequest,
sort,
where,
},
{ collection, limit, locale, page = 1, pagination, req = {} as PayloadRequest, sort, where },
) {
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
const tableName = this.tableNameMap.get(
@@ -32,7 +22,6 @@ export const queryDrafts: QueryDrafts = async function queryDrafts(
const result = await findMany({
adapter: this,
fields,
joins,
limit,
locale,
page,
@@ -40,7 +29,6 @@ export const queryDrafts: QueryDrafts = async function queryDrafts(
req,
sort,
tableName,
versions: true,
where: combinedWhere,
})

View File

@@ -1,9 +0,0 @@
import type { Table } from 'drizzle-orm'
export const getNameFromDrizzleTable = (table: Table): string => {
const symbol = Object.getOwnPropertySymbols(table).find((symb) =>
symb.description.includes('Name'),
)
return table[symbol]
}

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/email-nodemailer",
"version": "3.0.0-beta.118",
"version": "3.0.0-beta.116",
"description": "Payload Nodemailer Email Adapter",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/email-resend",
"version": "3.0.0-beta.118",
"version": "3.0.0-beta.116",
"description": "Payload Resend Email Adapter",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -33,7 +33,7 @@
"eslint-plugin-react-hooks": "5.1.0-rc-a19a8ab4-20240829",
"eslint-plugin-regexp": "2.6.0",
"globals": "15.9.0",
"typescript": "5.6.3",
"typescript": "5.6.2",
"typescript-eslint": "8.3.0"
}
}

View File

@@ -32,7 +32,7 @@
"eslint-plugin-react-hooks": "5.1.0-rc-a19a8ab4-20240829",
"eslint-plugin-regexp": "2.6.0",
"globals": "15.9.0",
"typescript": "5.6.3",
"typescript": "5.6.2",
"typescript-eslint": "8.3.0"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/graphql",
"version": "3.0.0-beta.118",
"version": "3.0.0-beta.116",
"homepage": "https://payloadcms.com",
"repository": {
"type": "git",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/live-preview-react",
"version": "3.0.0-beta.118",
"version": "3.0.0-beta.116",
"description": "The official React SDK for Payload Live Preview",
"homepage": "https://payloadcms.com",
"repository": {
@@ -43,8 +43,8 @@
"payload": "workspace:*"
},
"peerDependencies": {
"react": "^19.0.0 || ^19.0.0-rc-65a56d0e-20241020",
"react-dom": "^19.0.0 || ^19.0.0-rc-65a56d0e-20241020"
"react": "^19.0.0 || ^19.0.0-rc-3edc000d-20240926",
"react-dom": "^19.0.0 || ^19.0.0-rc-3edc000d-20240926"
},
"publishConfig": {
"exports": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/live-preview-vue",
"version": "3.0.0-beta.118",
"version": "3.0.0-beta.116",
"description": "The official Vue SDK for Payload Live Preview",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/live-preview",
"version": "3.0.0-beta.118",
"version": "3.0.0-beta.116",
"description": "The official live preview JavaScript SDK for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/next",
"version": "3.0.0-beta.118",
"version": "3.0.0-beta.116",
"homepage": "https://payloadcms.com",
"repository": {
"type": "git",
@@ -96,7 +96,7 @@
"@babel/preset-env": "^7.24.5",
"@babel/preset-react": "^7.24.1",
"@babel/preset-typescript": "^7.24.1",
"@next/eslint-plugin-next": "15.0.0",
"@next/eslint-plugin-next": "15.0.0-canary.173",
"@payloadcms/eslint-config": "workspace:*",
"@types/busboy": "1.5.4",
"@types/react": "npm:types-react@19.0.0-rc.1",
@@ -112,7 +112,7 @@
},
"peerDependencies": {
"graphql": "^16.8.1",
"next": "^15.0.0",
"next": "^15.0.0-canary.173",
"payload": "workspace:*"
},
"engines": {

View File

@@ -66,17 +66,17 @@ export const RootLayout = async ({
dependencyVersions: {
next: {
required: false,
version: '>=15.0.0',
version: '>=15.0.0-canary.173',
},
react: {
customVersionParser: customReactVersionParser,
required: false,
version: '>=19.0.0-rc-65a56d0e-20241020',
version: '>=19.0.0-rc-3edc000d-20240926',
},
'react-dom': {
customVersionParser: customReactVersionParser,
required: false,
version: '>=19.0.0-rc-65a56d0e-20241020',
version: '>=19.0.0-rc-3edc000d-20240926',
},
},
})

View File

@@ -10,16 +10,14 @@ import type { CollectionRouteHandler } from '../types.js'
import { headersWithCors } from '../../../utilities/headersWithCors.js'
export const deleteDoc: CollectionRouteHandler = async ({ collection, req }) => {
const { depth, overrideLock, where } = req.query as {
const { depth, where } = req.query as {
depth?: string
overrideLock?: string
where?: Where
}
const result = await deleteOperation({
collection,
depth: isNumber(depth) ? Number(depth) : undefined,
overrideLock: Boolean(overrideLock === 'true'),
req,
where,
})

View File

@@ -14,7 +14,6 @@ export const deleteByID: CollectionRouteHandlerWithID = async ({
}) => {
const { searchParams } = req
const depth = searchParams.get('depth')
const overrideLock = searchParams.get('overrideLock')
const id = sanitizeCollectionID({
id: incomingID,
@@ -26,7 +25,6 @@ export const deleteByID: CollectionRouteHandlerWithID = async ({
id,
collection,
depth: isNumber(depth) ? depth : undefined,
overrideLock: Boolean(overrideLock === 'true'),
req,
})

View File

@@ -10,11 +10,10 @@ import type { CollectionRouteHandler } from '../types.js'
import { headersWithCors } from '../../../utilities/headersWithCors.js'
export const update: CollectionRouteHandler = async ({ collection, req }) => {
const { depth, draft, limit, overrideLock, where } = req.query as {
const { depth, draft, limit, where } = req.query as {
depth?: string
draft?: string
limit?: string
overrideLock?: string
where?: Where
}
@@ -24,7 +23,6 @@ export const update: CollectionRouteHandler = async ({ collection, req }) => {
depth: isNumber(depth) ? Number(depth) : undefined,
draft: draft === 'true',
limit: isNumber(limit) ? Number(limit) : undefined,
overrideLock: Boolean(overrideLock === 'true'),
req,
where,
})

View File

@@ -16,7 +16,6 @@ export const updateByID: CollectionRouteHandlerWithID = async ({
const depth = searchParams.get('depth')
const autosave = searchParams.get('autosave') === 'true'
const draft = searchParams.get('draft') === 'true'
const overrideLock = searchParams.get('overrideLock')
const publishSpecificLocale = req.query.publishSpecificLocale as string | undefined
const id = sanitizeCollectionID({
@@ -32,7 +31,6 @@ export const updateByID: CollectionRouteHandlerWithID = async ({
data: req.data,
depth: isNumber(depth) ? Number(depth) : undefined,
draft,
overrideLock: Boolean(overrideLock === 'true'),
publishSpecificLocale,
req,
})

View File

@@ -343,7 +343,7 @@ export const DefaultEditView: React.FC = () => {
const shouldShowDocumentLockedModal =
documentIsLocked &&
currentEditor &&
currentEditor.id !== user?.id &&
currentEditor.id !== user.id &&
!isReadOnlyForIncomingUser &&
!showTakeOverModal &&
!documentLockStateRef.current?.hasShownLockedModal

View File

@@ -1,6 +1,6 @@
{
"name": "payload",
"version": "3.0.0-beta.118",
"version": "3.0.0-beta.116",
"description": "Node, React, Headless CMS and Application Framework built on Next.js",
"keywords": [
"admin panel",
@@ -85,7 +85,7 @@
},
"dependencies": {
"@monaco-editor/react": "4.6.0",
"@next/env": "^15.0.0",
"@next/env": "^15.0.0-canary.173",
"@payloadcms/translations": "workspace:*",
"@types/busboy": "1.5.4",
"ajv": "8.17.1",
@@ -98,11 +98,11 @@
"get-tsconfig": "^4.7.2",
"http-status": "1.6.2",
"image-size": "^1.1.1",
"jose": "5.9.2",
"json-schema-to-typescript": "15.0.1",
"jsonwebtoken": "9.0.2",
"minimist": "1.2.8",
"pino": "9.5.0",
"pino-pretty": "11.3.0",
"pino": "9.3.1",
"pino-pretty": "11.2.1",
"pluralize": "8.0.0",
"sanitize-filename": "1.6.3",
"scmp": "2.1.0",
@@ -114,6 +114,7 @@
"@hyrious/esbuild-plugin-commonjs": "^0.2.4",
"@payloadcms/eslint-config": "workspace:*",
"@types/json-schema": "7.0.15",
"@types/jsonwebtoken": "8.5.9",
"@types/minimist": "1.2.2",
"@types/nodemailer": "6.4.14",
"@types/pluralize": "0.0.33",

View File

@@ -5,11 +5,9 @@ import type { ClientFieldWithOptionalType } from './Field.js'
export type GenericLabelProps = {
readonly as?: 'label' | 'span'
readonly hideLocale?: boolean
readonly htmlFor?: string
readonly Label?: MappedComponent
readonly label?: StaticLabel
readonly localized?: boolean
readonly required?: boolean
readonly unstyled?: boolean
}

View File

@@ -1,21 +0,0 @@
import { SignJWT } from 'jose'
export const jwtSign = async ({
fieldsToSign,
secret,
tokenExpiration,
}: {
fieldsToSign: Record<string, unknown>
secret: string
tokenExpiration: number
}) => {
const secretKey = new TextEncoder().encode(secret)
const issuedAt = Math.floor(Date.now() / 1000)
const exp = issuedAt + tokenExpiration
const token = await new SignJWT(fieldsToSign)
.setProtectedHeader({ alg: 'HS256', typ: 'JWT' })
.setIssuedAt(issuedAt)
.setExpirationTime(exp)
.sign(secretKey)
return { exp, token }
}

View File

@@ -1,3 +1,5 @@
import jwt from 'jsonwebtoken'
import type {
AuthOperationsFromCollectionSlug,
Collection,
@@ -14,7 +16,6 @@ import { killTransaction } from '../../utilities/killTransaction.js'
import sanitizeInternalFields from '../../utilities/sanitizeInternalFields.js'
import { getFieldsToSign } from '../getFieldsToSign.js'
import isLocked from '../isLocked.js'
import { jwtSign } from '../jwt.js'
import { authenticateLocalStrategy } from '../strategies/local/authenticate.js'
import { incrementLoginAttempts } from '../strategies/local/incrementLoginAttempts.js'
import { resetLoginAttempts } from '../strategies/local/resetLoginAttempts.js'
@@ -233,10 +234,8 @@ export const loginOperation = async <TSlug extends CollectionSlug>(
})) || user
}, Promise.resolve())
const { exp, token } = await jwtSign({
fieldsToSign,
secret,
tokenExpiration: collectionConfig.auth.tokenExpiration,
const token = jwt.sign(fieldsToSign, secret, {
expiresIn: collectionConfig.auth.tokenExpiration,
})
req.user = user
@@ -309,7 +308,7 @@ export const loginOperation = async <TSlug extends CollectionSlug>(
}, Promise.resolve())
let result: { user: DataFromCollectionSlug<TSlug> } & Result = {
exp,
exp: (jwt.decode(token) as jwt.JwtPayload).exp,
token,
user,
}

View File

@@ -1,4 +1,4 @@
import { decodeJwt } from 'jose'
import jwt from 'jsonwebtoken'
import type { Collection } from '../../collections/config/types.js'
import type { PayloadRequest } from '../../types/index.js'
@@ -70,7 +70,7 @@ export const meOperation = async (args: Arguments): Promise<MeOperationResult> =
result.user = user
if (currentToken) {
const decoded = decodeJwt(currentToken)
const decoded = jwt.decode(currentToken) as jwt.JwtPayload
if (decoded) {
result.exp = decoded.exp
}

View File

@@ -1,3 +1,4 @@
import jwt from 'jsonwebtoken'
import url from 'url'
import type { BeforeOperationHook, Collection } from '../../collections/config/types.js'
@@ -9,7 +10,6 @@ import { commitTransaction } from '../../utilities/commitTransaction.js'
import { initTransaction } from '../../utilities/initTransaction.js'
import { killTransaction } from '../../utilities/killTransaction.js'
import { getFieldsToSign } from '../getFieldsToSign.js'
import { jwtSign } from '../jwt.js'
export type Result = {
exp: number
@@ -102,12 +102,12 @@ export const refreshOperation = async (incomingArgs: Arguments): Promise<Result>
user: args?.req?.user,
})
const { exp, token: refreshedToken } = await jwtSign({
fieldsToSign,
secret,
tokenExpiration: collectionConfig.auth.tokenExpiration,
const refreshedToken = jwt.sign(fieldsToSign, secret, {
expiresIn: collectionConfig.auth.tokenExpiration,
})
const exp = (jwt.decode(refreshedToken) as Record<string, unknown>).exp as number
result = {
exp,
refreshedToken,

View File

@@ -1,4 +1,5 @@
import httpStatus from 'http-status'
import jwt from 'jsonwebtoken'
import type { Collection } from '../../collections/config/types.js'
import type { PayloadRequest } from '../../types/index.js'
@@ -8,7 +9,6 @@ import { commitTransaction } from '../../utilities/commitTransaction.js'
import { initTransaction } from '../../utilities/initTransaction.js'
import { killTransaction } from '../../utilities/killTransaction.js'
import { getFieldsToSign } from '../getFieldsToSign.js'
import { jwtSign } from '../jwt.js'
import { authenticateLocalStrategy } from '../strategies/local/authenticate.js'
import { generatePasswordSaltHash } from '../strategies/local/generatePasswordSaltHash.js'
@@ -118,10 +118,8 @@ export const resetPasswordOperation = async (args: Arguments): Promise<Result> =
user,
})
const { token } = await jwtSign({
fieldsToSign,
secret,
tokenExpiration: collectionConfig.auth.tokenExpiration,
const token = jwt.sign(fieldsToSign, secret, {
expiresIn: collectionConfig.auth.tokenExpiration,
})
const fullUser = await payload.findByID({

View File

@@ -1,4 +1,4 @@
import { jwtVerify } from 'jose'
import jwt from 'jsonwebtoken'
import type { Payload, Where } from '../../types/index.js'
import type { AuthStrategyFunction, User } from '../index.js'
@@ -81,8 +81,8 @@ export const JWTAuthentication: AuthStrategyFunction = async ({
return { user: null }
}
const secretKey = new TextEncoder().encode(payload.secret)
const { payload: decodedPayload } = await jwtVerify<JWTToken>(token, secretKey)
const decodedPayload = jwt.verify(token, payload.secret) as jwt.JwtPayload & JWTToken
const collection = payload.collections[decodedPayload.collection]
const user = await payload.findByID({

Some files were not shown because too many files have changed in this diff Show More