Compare commits
2 Commits
v3.0.0-bet
...
fix/expose
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4283bebc37 | ||
|
|
4082680099 |
@@ -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')
|
||||
```
|
||||
|
||||
@@ -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 = {
|
||||
// ...
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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 = {
|
||||
// ...
|
||||
|
||||
@@ -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} />
|
||||
|
||||
@@ -29,7 +29,7 @@ The lockDocuments property exists on both the Collection Config and the Global C
|
||||
Here’s an example configuration for document locking:
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig } from 'payload'
|
||||
import { CollectionConfig } from 'payload'
|
||||
|
||||
export const Posts: CollectionConfig = {
|
||||
slug: 'posts',
|
||||
|
||||
@@ -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 = {
|
||||
// ...
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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 = {
|
||||
// ...
|
||||
|
||||
@@ -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 = {
|
||||
// ...
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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({
|
||||
// ...
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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 = {
|
||||
// ...
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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 = {
|
||||
// ...
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
22
package.json
22
package.json
@@ -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": {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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') {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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' })
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
}
|
||||
@@ -55,6 +55,7 @@ export const buildOrderBy = ({
|
||||
pathSegments: sortPath.replace(/__/g, '.').split('.'),
|
||||
selectFields,
|
||||
tableName,
|
||||
useAlias: true,
|
||||
value: sortPath,
|
||||
})
|
||||
orderBy.column = sortTable?.[sortTableColumnName]
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
@@ -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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user