Compare commits

..

30 Commits

Author SHA1 Message Date
Elliot DeNolf
df023a52fd chore(release): v3.0.0-beta.102 [skip ci] 2024-09-09 17:07:31 -04:00
Dan Ribbens
d267cad482 fix: beforeDuplicate localized blocks and arrays (#8144)
fixes #7988
2024-09-09 21:02:56 +00:00
Germán Jabloñski
fa38dfc16c fix(richtext-lexical): indent regression (#8138)
## Description

Fixes #8038, which was broken in #7817

I'm not entirely sure if this change violates the original intent of the
"base" utility, which from what I understand was introduced for
scalability reasons. Either way, I think it's a good idea to keep the
indent at 40px all the time.

The reason for this is that browsers use 40px as the indentation setting
for lists, and using that setting the indented paragraphs and headings
match the lists. See https://github.com/facebook/lexical/pull/4025

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
- [x] I have made corresponding changes to the documentation
2024-09-09 20:48:05 +00:00
Jacob Fletcher
8e1a5c8dba feat!: explicitly types field components (#8136)
## Description

Currently, there is no way of typing custom server field components.
This is because internally, all field components are client components,
and so these were never fully typed. For example, the docs currently
indicate for all custom fields to be typed in this way:

Old:
    
```tsx
export const MyClientTextFieldComponent: React.FC<TextFieldProps>
```

But if your component is a server component, you will never receive the
fully typed `field` prop, `payload` prop, etc. unless you've typed that
yourself using some of the underlying utilities. So to fix this, every
field now explicitly exports a type for each environment:

New:

- Client component:
    ```tsx
    'use client'
    export const MyClientTextFieldComponent: TextFieldClientComponent
    ```

- Server component:
    ```tsx
    export const MyServerTextFieldComponent: TextFieldServerComponent
    ```

This pattern applies to every field type, where the field name is
prepended onto the component type.

```ts
import type {
  TextFieldClientComponent,
  TextFieldServerComponent,
  TextFieldClientProps,
  TextFieldServerProps,
  TextareaFieldClientComponent,
  TextareaFieldServerComponent,
  TextareaFieldClientProps,
  TextareaFieldServerProps,
  // ...and so on for each field type
} from 'payload'
```

## BREAKING CHANGES

We are no longer exporting `TextFieldProps` etc. for each field type.
Instead, we now export props for each client/server environment
explicitly. If you were previously importing one of these types into
your custom component, simply change the import name to reflect your
environment.

Old:

```tsx
import type { TextFieldProps } from 'payload'
``` 

New:

```tsx
import type { TextFieldClientProps, TextFieldServerProps } from 'payload'
``` 

Related: #7754. 

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] New feature (non-breaking change which adds functionality)
- [x] This change requires a documentation update

## Checklist:

- [x] Existing test suite passes locally with my changes
- [x] I have made corresponding changes to the documentation
2024-09-09 20:15:10 +00:00
Elliot DeNolf
a8c60c1c02 chore(release): v3.0.0-beta.101 [skip ci] 2024-09-09 16:04:45 -04:00
James Mikrut
d44fb2db37 fix: #6800, graphql parallel queries with different fallback locales (#8140)
## Description

Fixes #6800 where parallel GraphQL queries with different locales /
fallbackLocales do not return their data properly.
2024-09-09 16:01:58 -04:00
Dan Ribbens
852f9fc1fd fix!: multiple preferences for the same user and entry (#8100)
fixes #7762

This change mitigates having multiple preferences for one user but not
awaiting the change to a preference and reduces querying by skipping the
access control. In the event that a user has multiple preferences with
the same key, only the one with the latest updatedAt will be returned.

BREAKING CHANGES:
- payload/preferences/operations are no longer default exports

## Description

<!-- Please include a summary of the pull request and any related issues
it fixes. Please also include relevant motivation and context. -->

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

<!-- Please delete options that are not relevant. -->

- [ ] Chore (non-breaking change which does not add functionality)
- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [ ] Change to the
[templates](https://github.com/payloadcms/payload/tree/main/templates)
directory (does not affect core functionality)
- [ ] Change to the
[examples](https://github.com/payloadcms/payload/tree/main/examples)
directory (does not affect core functionality)
- [ ] This change requires a documentation update

## Checklist:

- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
- [ ] I have made corresponding changes to the documentation

---------

Co-authored-by: Paul Popus <paul@nouance.io>
2024-09-09 14:00:51 -04:00
Dan Ribbens
e2d803800d fix: removes transactions wrapping auth strategies and login (#8137)
## Description

By default all api requests are creating transactions due to the
authentication stategy. This change removes transactions for auth and
login requests. This should only happen when the database needs to make
changes in which case the auth strategy or login lockout updates will
invoke their own transactions still.

This should improve performance without any sacrifice to database
consistency.

Fixes #8092 

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

<!-- Please delete options that are not relevant. -->

- [ ] Chore (non-breaking change which does not add functionality)
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [ ] Change to the
[templates](https://github.com/payloadcms/payload/tree/main/templates)
directory (does not affect core functionality)
- [ ] Change to the
[examples](https://github.com/payloadcms/payload/tree/main/examples)
directory (does not affect core functionality)
- [ ] This change requires a documentation update

## Checklist:

- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] Existing test suite passes locally with my changes
- [ ] I have made corresponding changes to the documentation
2024-09-09 13:27:21 -04:00
Germán Jabloñski
7fa68d17f5 fix(ui): wrong block indication when an error occurred (#7963) 2024-09-09 10:20:03 -04:00
Paul
9ec431a5bd fix(ui): bulk select checkbox being selected by default when in drawer (#8126) 2024-09-09 06:47:35 +00:00
Paul
cadf815ef6 fix(ui): thumbnails when serverURL config is provided (#8124) 2024-09-09 06:16:43 +00:00
Paul
638382e7fd feat: add validation for useAsTitle to throw an error if it's an invalid or nested field (#8122) 2024-09-08 18:53:12 -06:00
Elliot DeNolf
08fdbcacc0 chore: proper error log format (#8105)
Fix some error log formatting to use `{ msg, err }` properly
2024-09-07 02:48:59 +00:00
Paul
b27e42c484 fix(ui): various issues around documents lists, listQuery provider and search params (#8081)
This PR fixes and improves:
- ListQuery provider is now the source of truth for searchParams instead
of having components use the `useSearchParams` hook
- Various issues with search params and filters sticking around when
navigating between collections
- Pagination and limits not working inside DocumentDrawer
- Searching and filtering causing a flash of overlay in DocumentDrawer,
this now only shows for the first load and on slow networks
- Preferences are now respected in DocumentDrawer
- Changing the limit now resets your page back to 1 in case the current
page no longer exists

Fixes https://github.com/payloadcms/payload/issues/7085
Fixes https://github.com/payloadcms/payload/pull/8081
Fixes https://github.com/payloadcms/payload/issues/8086
2024-09-06 15:51:09 -06:00
Tylan Davis
32cc1a5761 fix(ui): missing thumbnail for non-image files in bulk upload sidebar (#8102)
## Description

Uses the `Thumbnail` component used in other places for the bulk upload
file rows. Closes #8099

In the future, we should consider adding different thumbnail icons based
on the `mimeType` to better describe the files being uploaded.

Before:
![Screenshot 2024-09-06 at 4 51
56 PM](https://github.com/user-attachments/assets/35cd528c-5086-465e-8d3c-7bb66d7c35da)


After:
![Screenshot 2024-09-06 at 4 50
12 PM](https://github.com/user-attachments/assets/54d2b98d-ac11-481e-abe5-4be071c3c8f2)


- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] Existing test suite passes locally with my changes
- [ ] I have made corresponding changes to the documentation
2024-09-06 21:28:50 +00:00
Tylan Davis
38be69b7d3 fix(ui): better responsiveness for upload fields in sidebar (#8101)
## Description

Adjusts the styling for the Dropzone component for upload fields with
`admin.position: sidebar`.

Before:
![Screenshot 2024-09-06 at 4 10
28 PM](https://github.com/user-attachments/assets/221d43f9-f426-4a44-ba58-29123050c775)

After:
![Screenshot 2024-09-06 at 4 09
32 PM](https://github.com/user-attachments/assets/c4369a65-d842-4e65-9153-19244fcf5600)


- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] Existing test suite passes locally with my changes
- [ ] I have made corresponding changes to the documentation
2024-09-06 20:37:38 +00:00
Elliot DeNolf
6b82196f01 chore(release): v3.0.0-beta.100 [skip ci] 2024-09-06 15:25:41 -04:00
Tylan Davis
ead12c8a49 fix(ui, next): adjust modal alignment and padding (#7931)
## Description

Updates styling on modals and auth forms for more consistent spacing and
alignment.

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
- [ ] I have made corresponding changes to the documentation
2024-09-06 14:54:39 -04:00
Jacob Fletcher
6253ec5d1a fix(ui): optimizes the relationship field by sharing a single document drawer across all values (#8094)
## Description

Currently, the relationship field's _value(s)_ each render and controls
its own document drawer. This has led to `hasMany` relationships
processing a potentially large number of drawers unnecessarily. But the
real problem is when attempting to perform side-effects as a result of a
drawer action. Currently, when you change the value of a relationship
field, all drawers within are (rightfully) unmounted because the
component representing the value was itself unmounted. This meant that
you could not update the title of a document, for example, then update
the underlying field's value, without also closing the document drawer
outright. This is needed in order to support things like creating and
duplicating documents within document drawers (#7679).

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [x] Existing test suite passes locally with my changes
2024-09-06 14:00:53 -04:00
Jacob Fletcher
f9ae56ec88 fix(ui): handles falsey relationship options on reset (#8095) 2024-09-06 12:55:09 -04:00
Sasha
0688c2b79d fix(db-postgres): sanitize tab/group path for table name (#8009)
## Description

Fixes https://github.com/payloadcms/payload/issues/7109

Example of table structures that lead to the problem with camelCased
group / tab names.
`group_field_array_localized` - `groupField` -> `array` (has a localized
field inside)
`group_field_array_nested_array` - `groupField` -> `array` ->
`nestedArray`

<!-- Please include a summary of the pull request and any related issues
it fixes. Please also include relevant motivation and context. -->

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

<!-- Please delete options that are not relevant. -->

- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
2024-09-06 11:43:47 -04:00
Sasha
c6246618ba fix(cpa): detect package manager from command execution environment (#8087)
Previously, on some machines this command:
`pnpx create-payload-app@beta app` created a project using `npm`,
instead of `pnpm`, the same with `yarn`.

Also, the way we detected the package manager was always prioritizing
`pnpm`, even if they executed the command with `yarn` / `npm`. Now we
are relying only on from which package manager user executed
`create-payload-app`.

The code for detection is grabbed from create-next-app
https://github.com/vercel/next.js/blob/canary/packages/create-next-app/helpers/get-pkg-manager.ts
2024-09-06 08:57:20 -04:00
Alexander
b69826a81e feat(cpa): add support for bun package manager in v3 installer (#7709)
Adds support for bun package manger in v3, enabled with `--use-bun`
flag.

Related: #6932 (for v2)
2024-09-05 23:50:03 -04:00
Paul
e80da7cb75 chore: add jsdocs for authentication types and add missing config to docs (#8082) 2024-09-06 00:04:13 +00:00
Francisco Lourenço
6f512b6ca8 docs: fix TextFieldProps in client field component example (#8080)
## Description

Without using `React.FC<>`, the type needs to be placed on the right
side of the props object.

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

<!-- Please delete options that are not relevant. -->

- [x] Chore (non-breaking change which does not add functionality)

## Checklist:

- [ ] ~I have added tests that prove my fix is effective or that my
feature works~
- [ ] ~Existing test suite passes locally with my changes~
- [x] I have made corresponding changes to the documentation
2024-09-05 15:41:48 -06:00
Elliot DeNolf
22ee8bf383 chore(release): v3.0.0-beta.99 [skip ci] 2024-09-05 12:38:08 -04:00
Jacob Fletcher
308fad8a7a fix(ui): significantly optimizes relationship field (#8063)
## Description

Reduces the number of client-side requests made by the relationship
field component, and fixes the visual "blink" of the field's value on
initial load. Does so through a new `useIgnoredEffect` hook that allows
this component's effects to be precisely triggered based on whether a
only _subset_ of its dependencies have changed, which looks something
like this:

```tsx
// ...
useIgnoredEffect(() => {
  // Do something
}, [deps], [ignoredDeps])
```

"Ignored deps" are still treated as normal dependencies of the
underlying `useEffect` hook, but they do not cause the provided function
to execute. This is useful if you have a list of dependencies that
change often, but need to scope your effect's logic to explicit
dependencies within that list. This is a typical pattern in React using
refs, just standardized within a reusable hook.

This significantly reduces the overall number of re-renders and
duplicative API requests within the relationship field because the
`useEffect` hooks that control the fetching of these related documents
were running unnecessarily often. In the future, we really ought to
leverage the `RelationshipProvider` used in the List View so that we can
also reduce the number of duplicative requests across _unrelated fields_
within the same document.

Before:


https://github.com/user-attachments/assets/ece7c85e-20fb-49f6-b393-c5e9d5176192

After:


https://github.com/user-attachments/assets/9f0a871e-f10f-4fd6-a58b-8146ece288c4

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
2024-09-04 21:37:00 -04:00
Jessica Chowdhury
6427b7eb29 fix: only show restore as draft option when drafts enabled (#8066)
## Description

In version comparison view, the `Restore as draft` button should only be
visible when `versions.drafts: true`.

Before:
<img width="1414" alt="Screenshot 2024-09-04 at 3 33 21 PM"
src="https://github.com/user-attachments/assets/1f96d804-46d7-443a-99ea-7b6481839b47">

After:
<img width="1307" alt="Screenshot 2024-09-04 at 3 38 42 PM"
src="https://github.com/user-attachments/assets/d2621ddd-2b14-4dab-936c-29a5521444de">


- [X] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [X] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [ ] I have added tests that prove my fix is effective or that my
feature works
- [X] Existing test suite passes locally with my changes
- [ ] I have made corresponding changes to the documentation
2024-09-04 19:54:34 +00:00
Sasha
3a657847f2 fix(db-postgres): query hasMany text/number in array/blocks (#8003)
## Description

Fixes https://github.com/payloadcms/payload/issues/7671

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)
## Checklist:

- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
2024-09-04 11:53:43 -04:00
Elliot DeNolf
8212c0d65f chore(eslint): silence some warnings that always get auto-fixed 2024-09-04 11:26:36 -04:00
196 changed files with 2992 additions and 1038 deletions

11
.vscode/settings.json vendored
View File

@@ -31,8 +31,15 @@
"editor.formatOnSave": true
},
"editor.formatOnSaveMode": "file",
// All ESLint rules to 'warn' to differentate from TypeScript's 'error' level
"eslint.rules.customizations": [{ "rule": "*", "severity": "warn" }],
"eslint.rules.customizations": [
// Defaultt all ESLint errors to 'warn' to differentate from TypeScript's 'error' level
{ "rule": "*", "severity": "warn" },
// Silence some warnings that will get auto-fixed
{ "rule": "perfectionist/*", "severity": "off", "fixable": true },
{ "rule": "curly", "severity": "off", "fixable": true },
{ "rule": "object-shorthand", "severity": "off", "fixable": true }
],
"typescript.tsdk": "node_modules/typescript/lib",
// Load .git-blame-ignore-revs file
"gitlens.advanced.blame.customArguments": ["--ignore-revs-file", ".git-blame-ignore-revs"],

View File

@@ -431,14 +431,14 @@ export const MyClientComponent: React.FC = () => {
See [Using Hooks](#using-hooks) for more details.
</Banner>
All [Field Components](./fields) automatically receive their respective Client Field Config through a common [`field`](./fields#the-field-prop) prop:
All [Field Components](./fields) automatically receive their respective Field Config through a common [`field`](./fields#the-field-prop) prop:
```tsx
'use client'
import React from 'react'
import type { TextFieldProps } from 'payload'
import type { TextFieldClientComponent } from 'payload'
export const MyClientFieldComponent: TextFieldProps = ({ field: { name } }) => {
export const MyClientFieldComponent: TextFieldClientComponent = ({ field: { name } }) => {
return (
<p>
{`This field's name is ${name}`}

View File

@@ -136,7 +136,7 @@ All Field Components receive the following props:
| Property | Description |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`docPreferences`** | An object that contains the [Preferences](./preferences) for the document.
| **`field`** | The sanitized, client-friendly version of the field's config. [More details](#the-field-prop) |
| **`field`** | The field's config. [More details](#the-field-prop) |
| **`locale`** | The locale of the field. [More details](../configuration/localization). |
| **`readOnly`** | A boolean value that represents if the field is read-only or not. |
| **`user`** | The currently authenticated user. [More details](../authentication/overview). |
@@ -175,46 +175,46 @@ export const CustomTextField: React.FC = () => {
#### TypeScript
When building Custom Field Components, you can import the component props to ensure type safety in your component. There is an explicit type for the Field Component, one for every [Field Type](../fields/overview). The convention is to append `Props` to the type of field, i.e. `TextFieldProps`.
When building Custom Field Components, you can import the component type to ensure type safety. There is an explicit type for the Field Component, one for every [Field Type](../fields/overview) and for every client/server environment. The convention is to prepend the field type onto the target type, i.e. `TextFieldClientComponent`:
```tsx
import type {
ArrayFieldProps,
BlocksFieldProps,
CheckboxFieldProps,
CodeFieldProps,
CollapsibleFieldProps,
DateFieldProps,
EmailFieldProps,
GroupFieldProps,
HiddenFieldProps,
JSONFieldProps,
NumberFieldProps,
PointFieldProps,
RadioFieldProps,
RelationshipFieldProps,
RichTextFieldProps,
RowFieldProps,
SelectFieldProps,
TabsFieldProps,
TextFieldProps,
TextareaFieldProps,
UploadFieldProps
TextFieldClientComponent,
TextFieldServerComponent,
TextFieldClientProps,
TextFieldServerProps,
// ...and so on for each Field Type
} from 'payload'
```
### The `field` Prop
All Field Components are passed a client-friendly version of their Field Config through a common `field` prop. Since the raw Field Config is [non-serializable](https://react.dev/reference/rsc/use-client#serializable-types), Payload sanitized it into a [Client Config](./components#accessing-the-payload-config) that is safe to pass into Client Components.
All Field Components are passed their own Field Config through a common `field` prop. Within a Server Component, this is the raw Field Config. Within Client Components, however, the raw Field Config is [non-serializable](https://react.dev/reference/rsc/use-client#serializable-types). Instead, Client Components receives a [Client Config](./components#accessing-the-payload-config), which is a sanitizes version of the Field Config that is safe to pass into Client Components.
The exact shape of this prop is unique to the specific [Field Type](../fields/overview) being rendered, minus all non-serializable properties. Any [Custom Components](../components) are also resolved into a "mapped component" that is safe to pass.
```tsx
import React from 'react'
import type { TextFieldServerComponent } from 'payload'
export const MyServerTextField: TextFieldServerComponent = ({ payload, field: { name } }) => {
const result = await payload.find({
collection: 'myCollection',
depth: 1,
})
// ...
}
```
Client Component:
```tsx
'use client'
import React from 'react'
import type { TextFieldProps } from 'payload'
import type { TextFieldClientComponent } from 'payload'
export const MyClientFieldComponent: React.FC<TextFieldProps> = ({ field: { name } }) => {
export const MyClientTextField: TextFieldClientComponent = ({ field: { name } }) => {
return (
<p>
{`This field's name is ${name}`}
@@ -238,40 +238,18 @@ The following additional properties are also provided to the `field` prop:
#### TypeScript
When building Custom Field Components, you can import the client field props to ensure type safety in your component. There is an explicit type for the Field Component, one for every [Field Type](../fields/overview). The convention is to append `Client` to the type of field, i.e. `TextFieldClient`.
When building Custom Field Components, you can import the client field props to ensure type safety in your component. There is an explicit type for the Field Component, one for every [Field Type](../fields/overview) and server/client environment. The convention is to prepend the field type onto the target type, i.e. `TextFieldClientComponent`:
```tsx
import type {
ArrayFieldClient,
BlocksFieldClient,
CheckboxFieldClient,
CodeFieldClient,
CollapsibleFieldClient,
DateFieldClient,
EmailFieldClient,
GroupFieldClient,
HiddenFieldClient,
JSONFieldClient,
NumberFieldClient,
PointFieldClient,
RadioFieldClient,
RelationshipFieldClient,
RichTextFieldClient,
RowFieldClient,
SelectFieldClient,
TabsFieldClient,
TextFieldClient,
TextareaFieldClient,
UploadFieldClient
TextFieldClientComponent,
TextFieldServerComponent,
TextFieldClientProps,
TextFieldServerProps,
// ...and so on for each Field Type
} from 'payload'
```
When working on the client, you will never have access to objects of type `Field`. This is reserved for the server-side. Instead, you can use `ClientField` which is a union type of all the client fields:
```tsx
import type { ClientField } from 'payload'
```
### The Cell Component
The Cell Component is rendered in the table of the List View. It represents the value of the field when displayed in a table cell.
@@ -353,7 +331,7 @@ When building Custom Label Components, you can import the component props to ens
import type {
TextFieldLabelServerComponent,
TextFieldLabelClientComponent,
// And so on for each Field Type
// ...and so on for each Field Type
} from 'payload'
```

View File

@@ -85,6 +85,7 @@ The following options are available:
| **`lockTime`** | Set the time (in milliseconds) that a user should be locked out if they fail authentication more times than `maxLoginAttempts` allows for. |
| **`loginWithUsername`** | Ability to allow users to login with username/password. [More](/docs/authentication/overview#login-with-username) |
| **`maxLoginAttempts`** | Only allow a user to attempt logging in X amount of times. Automatically locks out a user from authenticating if this limit is passed. Set to `0` to disable. |
| **`removeTokenFromResponses`** | Set to true if you want to remove the token from the returned authentication API responses such as login or refresh. |
| **`strategies`** | Advanced - an array of custom authentification strategies to extend this collection's authentication with. [More details](./custom-strategies). |
| **`tokenExpiration`** | How long (in seconds) to keep the user logged in. JWTs and HTTP-only cookies will both expire at the same time. |
| **`useAPIKey`** | Payload Authentication provides for API keys to be set on each user within an Authentication-enabled Collection. [More details](./api-keys). |

View File

@@ -5,13 +5,13 @@ export const recordLastLoggedInTenant: AfterLoginHook = async ({ req, user }) =>
const relatedOrg = await req.payload
.find({
collection: 'tenants',
depth: 0,
limit: 1,
where: {
'domains.domain': {
in: [req.headers.host],
},
},
depth: 0,
limit: 1,
})
?.then((res) => res.docs?.[0])
@@ -24,7 +24,10 @@ export const recordLastLoggedInTenant: AfterLoginHook = async ({ req, user }) =>
req,
})
} catch (err: unknown) {
req.payload.logger.error(`Error recording last logged in tenant for user ${user.id}: ${err}`)
req.payload.logger.error({
err,
msg: `Error recording last logged in tenant for user ${user.id}`,
})
}
return user

View File

@@ -1,6 +1,6 @@
{
"name": "payload-monorepo",
"version": "3.0.0-beta.98",
"version": "3.0.0-beta.102",
"private": true,
"type": "module",
"scripts": {

View File

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

View File

@@ -37,6 +37,8 @@ async function installDeps(args: {
installCmd = 'yarn'
} else if (packageManager === 'pnpm') {
installCmd = 'pnpm install'
} else if (packageManager === 'bun') {
installCmd = 'bun install'
}
try {

View File

@@ -1,12 +1,8 @@
import execa from 'execa'
import fse from 'fs-extra'
import type { CliArgs, PackageManager } from '../types.js'
export async function getPackageManager(args: {
cliArgs?: CliArgs
projectDir: string
}): Promise<PackageManager> {
export function getPackageManager(args: { cliArgs?: CliArgs; projectDir: string }): PackageManager {
const { cliArgs, projectDir } = args
try {
@@ -18,15 +14,11 @@ export async function getPackageManager(args: {
detected = 'yarn'
} else if (cliArgs?.['--use-npm'] || fse.existsSync(`${projectDir}/package-lock.json`)) {
detected = 'npm'
} else if (cliArgs?.['--use-bun'] || fse.existsSync(`${projectDir}/bun.lockb`)) {
detected = 'bun'
} else {
// Otherwise check for existing commands
if (await commandExists('pnpm')) {
detected = 'pnpm'
} else if (await commandExists('yarn')) {
detected = 'yarn'
} else {
detected = 'npm'
}
// Otherwise check the execution environment
detected = getEnvironmentPackageManager()
}
return detected
@@ -35,11 +27,20 @@ export async function getPackageManager(args: {
}
}
async function commandExists(command: string): Promise<boolean> {
try {
await execa.command(`command -v ${command}`)
return true
} catch {
return false
function getEnvironmentPackageManager(): PackageManager {
const userAgent = process.env.npm_config_user_agent || ''
if (userAgent.startsWith('yarn')) {
return 'yarn'
}
if (userAgent.startsWith('pnpm')) {
return 'pnpm'
}
if (userAgent.startsWith('bun')) {
return 'bun'
}
return 'npm'
}

View File

@@ -49,6 +49,7 @@ export class Main {
// Package manager
'--no-deps': Boolean,
'--use-bun': Boolean,
'--use-npm': Boolean,
'--use-pnpm': Boolean,
'--use-yarn': Boolean,
@@ -132,7 +133,7 @@ export class Main {
? path.dirname(nextConfigPath)
: path.resolve(process.cwd(), slugify(projectName))
const packageManager = await getPackageManager({ cliArgs: this.args, projectDir })
const packageManager = getPackageManager({ cliArgs: this.args, projectDir })
if (nextConfigPath) {
p.log.step(

View File

@@ -16,6 +16,7 @@ export interface Args extends arg.Spec {
'--secret': StringConstructor
'--template': StringConstructor
'--template-branch': StringConstructor
'--use-bun': BooleanConstructor
'--use-npm': BooleanConstructor
'--use-pnpm': BooleanConstructor
'--use-yarn': BooleanConstructor

View File

@@ -40,6 +40,7 @@ export function helpMessage(): void {
--use-npm Use npm to install dependencies
--use-yarn Use yarn to install dependencies
--use-pnpm Use pnpm to install dependencies
--use-bun Use bun to install dependencies (experimental)
--no-deps Do not install any dependencies
-h Show help
`)

View File

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

View File

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

View File

@@ -23,7 +23,7 @@ const connectWithReconnect = async function ({
} else {
try {
result = await adapter.pool.connect()
} catch (err) {
} catch (ignore) {
setTimeout(() => {
payload.logger.info('Reconnecting to postgres')
void connectWithReconnect({ adapter, payload, reconnect: true })
@@ -38,7 +38,7 @@ const connectWithReconnect = async function ({
if (err.code === 'ECONNRESET') {
void connectWithReconnect({ adapter, payload, reconnect: true })
}
} catch (err) {
} catch (ignore) {
// swallow error
}
})
@@ -76,7 +76,7 @@ export const connect: Connect = async function connect(
}
}
} catch (err) {
this.payload.logger.error(`Error: cannot connect to Postgres. Details: ${err.message}`, err)
this.payload.logger.error({ err, msg: `Error: cannot connect to Postgres: ${err.message}` })
if (typeof this.rejectInitializing === 'function') {
this.rejectInitializing()
}

View File

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

View File

@@ -37,7 +37,7 @@ export const connect: Connect = async function connect(
}
}
} catch (err) {
this.payload.logger.error(`Error: cannot connect to SQLite. Details: ${err.message}`, err)
this.payload.logger.error({ err, msg: `Error: cannot connect to SQLite: ${err.message}` })
if (typeof this.rejectInitializing === 'function') {
this.rejectInitializing()
}

View File

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

View File

@@ -39,7 +39,7 @@ export const connect: Connect = async function connect(
}
}
} catch (err) {
this.payload.logger.error(`Error: cannot connect to Postgres. Details: ${err.message}`, err)
this.payload.logger.error({ err, msg: `Error: cannot connect to Postgres: ${err.message}` })
if (typeof this.rejectInitializing === 'function') {
this.rejectInitializing()
}

View File

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

View File

@@ -80,6 +80,7 @@ export const buildFindManyArgs = ({
depth,
fields,
path: '',
tablePath: '',
topLevelArgs: result,
topLevelTableName: tableName,
})

View File

@@ -14,6 +14,7 @@ type TraverseFieldArgs = {
depth?: number
fields: Field[]
path: string
tablePath: string
topLevelArgs: Record<string, unknown>
topLevelTableName: string
}
@@ -26,6 +27,7 @@ export const traverseFields = ({
depth,
fields,
path,
tablePath,
topLevelArgs,
topLevelTableName,
}: TraverseFieldArgs) => {
@@ -53,6 +55,7 @@ export const traverseFields = ({
depth,
fields: field.fields,
path,
tablePath,
topLevelArgs,
topLevelTableName,
})
@@ -63,6 +66,7 @@ export const traverseFields = ({
if (field.type === 'tabs') {
field.tabs.forEach((tab) => {
const tabPath = tabHasName(tab) ? `${path}${tab.name}_` : path
const tabTablePath = tabHasName(tab) ? `${tablePath}${toSnakeCase(tab.name)}_` : tablePath
traverseFields({
_locales,
@@ -72,6 +76,7 @@ export const traverseFields = ({
depth,
fields: tab.fields,
path: tabPath,
tablePath: tabTablePath,
topLevelArgs,
topLevelTableName,
})
@@ -92,7 +97,7 @@ export const traverseFields = ({
}
const arrayTableName = adapter.tableNameMap.get(
`${currentTableName}_${path}${toSnakeCase(field.name)}`,
`${currentTableName}_${tablePath}${toSnakeCase(field.name)}`,
)
const arrayTableNameWithLocales = `${arrayTableName}${adapter.localesSuffix}`
@@ -116,6 +121,7 @@ export const traverseFields = ({
depth,
fields: field.fields,
path: '',
tablePath: '',
topLevelArgs,
topLevelTableName,
})
@@ -172,6 +178,7 @@ export const traverseFields = ({
depth,
fields: block.fields,
path: '',
tablePath: '',
topLevelArgs,
topLevelTableName,
})
@@ -180,7 +187,7 @@ export const traverseFields = ({
break
case 'group':
case 'group': {
traverseFields({
_locales,
adapter,
@@ -189,11 +196,13 @@ export const traverseFields = ({
depth,
fields: field.fields,
path: `${path}${field.name}_`,
tablePath: `${tablePath}${toSnakeCase(field.name)}_`,
topLevelArgs,
topLevelTableName,
})
break
}
default: {
break

View File

@@ -257,10 +257,10 @@ export const getTableColumnFromPath = ({
tableType = 'numbers'
columnName = 'number'
}
newTableName = `${tableName}_${tableType}`
newTableName = `${rootTableName}_${tableType}`
const joinConstraints = [
eq(adapter.tables[tableName].id, adapter.tables[newTableName].parent),
eq(adapter.tables[newTableName].path, `${constraintPath}${field.name}`),
eq(adapter.tables[rootTableName].id, adapter.tables[newTableName].parent),
like(adapter.tables[newTableName].path, `${constraintPath}${field.name}`),
]
if (locale && field.localized && adapter.payload.config.localization) {

View File

@@ -37,6 +37,7 @@ export const beginTransaction: BeginTransaction = async function beginTransactio
return done
}
reject = () => {
// eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
rej()
return done
}
@@ -57,7 +58,7 @@ export const beginTransaction: BeginTransaction = async function beginTransactio
resolve,
}
} catch (err) {
this.payload.logger.error(`Error: cannot begin transaction: ${err.message}`, err)
this.payload.logger.error({ err, msg: `Error: cannot begin transaction: ${err.message}` })
process.exit(1)
}

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/live-preview-react",
"version": "3.0.0-beta.98",
"version": "3.0.0-beta.102",
"description": "The official React SDK for Payload Live Preview",
"homepage": "https://payloadcms.com",
"repository": {

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/next",
"version": "3.0.0-beta.98",
"version": "3.0.0-beta.102",
"homepage": "https://payloadcms.com",
"repository": {
"type": "git",

View File

@@ -12,14 +12,14 @@
position: relative;
display: flex;
flex-direction: column;
gap: var(--base);
gap: base(0.8);
padding: base(2);
}
&__content {
display: flex;
flex-direction: column;
gap: var(--base);
gap: base(0.4);
> * {
margin: 0;
@@ -28,7 +28,7 @@
&__controls {
display: flex;
gap: var(--base);
gap: base(0.4);
.btn {
margin: 0;

View File

@@ -14,14 +14,14 @@
&--width-normal {
.template-minimal__wrap {
max-width: 500px;
max-width: base(24);
width: 100%;
}
}
&--width-wide {
.template-minimal__wrap {
max-width: 1024px;
max-width: base(48);
width: 100%;
}
}

View File

@@ -1,6 +1,16 @@
@import '../../scss/styles.scss';
.create-first-user {
display: flex;
flex-direction: column;
gap: base(0.4);
> form > .field-type {
margin-bottom: var(--base);
& .form-submit {
margin: 0;
}
}
}

View File

@@ -28,7 +28,6 @@ import {
useListQuery,
useModal,
useRouteCache,
useSearchParams,
useStepNav,
useTranslation,
useWindowInfo,
@@ -54,8 +53,7 @@ export const DefaultListView: React.FC = () => {
newDocumentURL,
} = useListInfo()
const { data, defaultLimit, handlePageChange, handlePerPageChange } = useListQuery()
const { searchParams } = useSearchParams()
const { data, defaultLimit, handlePageChange, handlePerPageChange, params } = useListQuery()
const { openModal } = useModal()
const { clearRouteCache } = useRouteCache()
const { setCollectionSlug, setOnSuccess } = useBulkUpload()
@@ -226,9 +224,7 @@ export const DefaultListView: React.FC = () => {
</div>
<PerPage
handleChange={(limit) => void handlePerPageChange(limit)}
limit={
isNumber(searchParams?.limit) ? Number(searchParams.limit) : defaultLimit
}
limit={isNumber(params?.limit) ? Number(params.limit) : defaultLimit}
limits={collectionConfig?.admin?.pagination?.limits}
resetPage={data.totalDocs <= data.pagingCounter}
/>

View File

@@ -4,6 +4,10 @@ import { formatAdminURL } from '@payloadcms/ui/shared'
import LinkImport from 'next/link.js'
import React, { Fragment, useEffect } from 'react'
import './index.scss'
const baseClass = 'logout'
const Link = (LinkImport.default || LinkImport) as unknown as typeof LinkImport.default
export const LogoutClient: React.FC<{
@@ -26,7 +30,7 @@ export const LogoutClient: React.FC<{
if (isLoggingOut) {
return (
<Fragment>
<div className={`${baseClass}__wrap`}>
{inactivity && <h2>{t('authentication:loggedOutInactivity')}</h2>}
{!inactivity && <h2>{t('authentication:loggedOutSuccessfully')}</h2>}
<Button
@@ -43,7 +47,7 @@ export const LogoutClient: React.FC<{
>
{t('authentication:logBackIn')}
</Button>
</Fragment>
</div>
)
}

View File

@@ -1,19 +1,22 @@
@import '../../scss/styles.scss';
.logout {
display: flex;
flex-direction: column;
align-items: center;
flex-wrap: wrap;
min-height: 100vh;
&__wrap {
& > *:first-child {
margin-top: 0;
}
z-index: 1;
position: relative;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: base(0.8);
width: 100%;
max-width: base(36);
& > *:last-child {
margin-bottom: 0;
}
.btn {
& > * {
margin: 0;
}
}

View File

@@ -25,7 +25,7 @@ export const LogoutView: React.FC<
} = initPageResult
return (
<div className={`${baseClass}__wrap`}>
<div className={`${baseClass}`}>
<LogoutClient
adminRoute={adminRoute}
inactivity={inactivity}

View File

@@ -38,8 +38,10 @@ export const NotFoundClient: React.FC<{
.join(' ')}
>
<Gutter className={`${baseClass}__wrap`}>
<h1>{t('general:nothingFound')}</h1>
<p>{t('general:sorryNotFound')}</p>
<div className={`${baseClass}__content`}>
<h1>{t('general:nothingFound')}</h1>
<p>{t('general:sorryNotFound')}</p>
</div>
<Button
className={`${baseClass}__button`}
el="link"

View File

@@ -13,6 +13,24 @@
}
}
&__wrap {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: base(0.8);
max-width: base(36);
}
&__content {
display: flex;
flex-direction: column;
gap: base(0.4);
> * {
margin: 0;
}
}
&__button {
margin: 0;
}

View File

@@ -75,21 +75,23 @@ export const ResetPasswordClient: React.FC<Args> = ({ token }) => {
method="POST"
onSuccess={onSuccess}
>
<PasswordField
field={{
name: 'password',
label: i18n.t('authentication:newPassword'),
required: true,
}}
/>
<ConfirmPasswordField />
<HiddenField
field={{
name: 'token',
}}
forceUsePathFromProps
value={token}
/>
<div className={'inputWrap'}>
<PasswordField
field={{
name: 'password',
label: i18n.t('authentication:newPassword'),
required: true,
}}
/>
<ConfirmPasswordField />
<HiddenField
field={{
name: 'token',
}}
forceUsePathFromProps
value={token}
/>
</div>
<FormSubmit size="large">{i18n.t('authentication:resetPassword')}</FormSubmit>
</Form>
)

View File

@@ -1,5 +1,31 @@
@import '../../scss/styles.scss';
.reset-password {
form > .field-type {
margin-bottom: var(--base);
&__wrap {
z-index: 1;
position: relative;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: base(0.8);
max-width: base(36);
& > form {
width: 100%;
& > .inputWrap {
display: flex;
flex-direction: column;
gap: base(0.8);
> * {
margin: 0;
}
}
}
& > .btn {
margin: 0;
}
}
}

View File

@@ -1,11 +1,10 @@
import type { AdminViewProps } from 'payload'
import { Button, Translation } from '@payloadcms/ui'
import { formatAdminURL } from '@payloadcms/ui/shared'
import { Button } from '@payloadcms/ui'
import { formatAdminURL, Translation } from '@payloadcms/ui/shared'
import LinkImport from 'next/link.js'
import React from 'react'
import { MinimalTemplate } from '../../templates/Minimal/index.js'
import { ResetPasswordClient } from './index.client.js'
import './index.scss'
@@ -37,42 +36,37 @@ export const ResetPassword: React.FC<AdminViewProps> = ({ initPageResult, params
if (user) {
return (
<MinimalTemplate className={resetPasswordBaseClass}>
<div className={`${resetPasswordBaseClass}__wrap`}>
<h1>{i18n.t('authentication:alreadyLoggedIn')}</h1>
<p>
<Translation
elements={{
'0': ({ children }) => (
<Link
href={formatAdminURL({
adminRoute,
path: accountRoute,
})}
>
{children}
</Link>
),
}}
i18nKey="authentication:loggedInChangePassword"
t={i18n.t}
/>
</p>
<br />
<Button buttonStyle="secondary" el="link" Link={Link} to={adminRoute}>
{i18n.t('general:backToDashboard')}
</Button>
</div>
</MinimalTemplate>
<div className={`${resetPasswordBaseClass}__wrap`}>
<h1>{i18n.t('authentication:alreadyLoggedIn')}</h1>
<p>
<Translation
elements={{
'0': ({ children }) => (
<Link
href={formatAdminURL({
adminRoute,
path: accountRoute,
})}
>
{children}
</Link>
),
}}
i18nKey="authentication:loggedInChangePassword"
t={i18n.t}
/>
</p>
<Button buttonStyle="secondary" el="link" Link={Link} size="large" to={adminRoute}>
{i18n.t('general:backToDashboard')}
</Button>
</div>
)
}
return (
<MinimalTemplate className={resetPasswordBaseClass}>
<div className={`${resetPasswordBaseClass}__wrap`}>
<h1>{i18n.t('authentication:resetPassword')}</h1>
<ResetPasswordClient token={token} />
</div>
</MinimalTemplate>
<div className={`${resetPasswordBaseClass}__wrap`}>
<h1>{i18n.t('authentication:resetPassword')}</h1>
<ResetPasswordClient token={token} />
</div>
)
}

View File

@@ -38,24 +38,40 @@
@include blur-bg;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
&__toggle {
@extend %btn-reset;
}
}
.btn {
[dir='ltr'] & {
margin-right: var(--base);
}
[dir='rtl'] & {
margin-left: var(--base);
}
&__wrapper {
z-index: 1;
position: relative;
display: flex;
flex-direction: column;
gap: base(0.8);
padding: base(2);
max-width: base(36);
}
&__content {
display: flex;
flex-direction: column;
gap: base(0.4);
> * {
margin: 0;
}
}
&__modal-template {
position: relative;
z-index: 1;
&__controls {
display: flex;
gap: base(0.4);
.btn {
margin: 0;
}
}
}

View File

@@ -18,7 +18,6 @@ import { toast } from 'sonner'
import type { Props } from './types.js'
import { MinimalTemplate } from '../../../templates/Minimal/index.js'
import './index.scss'
const baseClass = 'restore-version'
@@ -36,11 +35,14 @@ const Restore: React.FC<Props> = ({
}) => {
const {
config: {
collections,
routes: { admin: adminRoute, api: apiRoute },
serverURL,
},
} = useConfig()
const collectionConfig = collections.find((collection) => collection.slug === collectionSlug)
const { toggleModal } = useModal()
const [processing, setProcessing] = useState(false)
const router = useRouter()
@@ -54,7 +56,8 @@ const Restore: React.FC<Props> = ({
let fetchURL = `${serverURL}${apiRoute}`
let redirectURL: string
const canRestoreAsDraft = status !== 'draft'
const canRestoreAsDraft = status !== 'draft' && collectionConfig?.versions?.drafts
if (collectionSlug) {
fetchURL = `${fetchURL}/${collectionSlug}/versions/${versionID}?draft=${draft}`
@@ -119,20 +122,25 @@ const Restore: React.FC<Props> = ({
)}
</div>
<Modal className={`${baseClass}__modal`} slug={modalSlug}>
<MinimalTemplate className={`${baseClass}__modal-template`}>
<h1>{t('version:confirmVersionRestoration')}</h1>
<p>{restoreMessage}</p>
<Button
buttonStyle="secondary"
onClick={processing ? undefined : () => toggleModal(modalSlug)}
type="button"
>
{t('general:cancel')}
</Button>
<Button onClick={processing ? undefined : () => void handleRestore()}>
{processing ? t('version:restoring') : t('general:confirm')}
</Button>
</MinimalTemplate>
<div className={`${baseClass}__wrapper`}>
<div className={`${baseClass}__content`}>
<h1>{t('version:confirmVersionRestoration')}</h1>
<p>{restoreMessage}</p>
</div>
<div className={`${baseClass}__controls`}>
<Button
buttonStyle="secondary"
onClick={processing ? undefined : () => toggleModal(modalSlug)}
size="large"
type="button"
>
{t('general:cancel')}
</Button>
<Button onClick={processing ? undefined : () => void handleRestore()}>
{processing ? t('version:restoring') : t('general:confirm')}
</Button>
</div>
</div>
</Modal>
</Fragment>
)

View File

@@ -1,6 +1,6 @@
{
"name": "payload",
"version": "3.0.0-beta.98",
"version": "3.0.0-beta.102",
"description": "Node, React, Headless CMS and Application Framework built on Next.js",
"keywords": [
"admin panel",

View File

@@ -13,7 +13,7 @@ import type {
} from '../fields/config/types.js'
import type { SanitizedGlobalConfig } from '../globals/config/types.js'
import type { JsonObject, Payload, PayloadRequest, RequestContext } from '../types/index.js'
import type { RichTextFieldProps } from './fields/RichText.js'
import type { RichTextFieldClientProps } from './fields/RichText.js'
import type { CreateMappedComponent } from './types.js'
export type AfterReadRichTextHookArgs<
@@ -261,7 +261,7 @@ export type RichTextAdapter<
ExtraFieldProperties = any,
> = {
CellComponent: PayloadComponent<never>
FieldComponent: PayloadComponent<never, RichTextFieldProps>
FieldComponent: PayloadComponent<never, RichTextFieldClientProps>
} & RichTextAdapterBase<Value, AdapterProps, ExtraFieldProperties>
export type RichTextAdapterProvider<

View File

@@ -3,21 +3,38 @@ import type { MarkOptional } from 'ts-essentials'
import type { ArrayField, ArrayFieldClient } from '../../fields/config/types.js'
import type { ArrayFieldValidation } from '../../fields/validations.js'
import type { FieldErrorClientComponent, FieldErrorServerComponent } from '../forms/Error.js'
import type {
ClientFieldBase,
FieldClientComponent,
FieldServerComponent,
ServerFieldBase,
} from '../forms/Field.js'
import type {
FieldDescriptionClientComponent,
FieldDescriptionServerComponent,
FieldLabelClientComponent,
FieldLabelServerComponent,
FormFieldBase,
MappedComponent,
} from '../types.js'
type ArrayFieldClientWithoutType = MarkOptional<ArrayFieldClient, 'type'>
export type ArrayFieldProps = {
type ArrayFieldBaseClientProps = {
readonly CustomRowLabel?: MappedComponent
readonly validate?: ArrayFieldValidation
} & Omit<FormFieldBase<ArrayFieldClientWithoutType>, 'validate'>
}
export type ArrayFieldClientProps = ArrayFieldBaseClientProps &
ClientFieldBase<ArrayFieldClientWithoutType>
export type ArrayFieldServerProps = ServerFieldBase<ArrayField>
export type ArrayFieldServerComponent = FieldServerComponent<ArrayField>
export type ArrayFieldClientComponent = FieldClientComponent<
ArrayFieldClientWithoutType,
ArrayFieldBaseClientProps
>
export type ArrayFieldLabelServerComponent = FieldLabelServerComponent<ArrayField>

View File

@@ -3,19 +3,36 @@ import type { MarkOptional } from 'ts-essentials'
import type { BlockField, BlockFieldClient } from '../../fields/config/types.js'
import type { BlockFieldValidation } from '../../fields/validations.js'
import type { FieldErrorClientComponent, FieldErrorServerComponent } from '../forms/Error.js'
import type {
ClientFieldBase,
FieldClientComponent,
FieldServerComponent,
ServerFieldBase,
} from '../forms/Field.js'
import type {
FieldDescriptionClientComponent,
FieldDescriptionServerComponent,
FieldLabelClientComponent,
FieldLabelServerComponent,
FormFieldBase,
} from '../types.js'
type BlocksFieldClientWithoutType = MarkOptional<BlockFieldClient, 'type'>
export type BlockFieldProps = {
type BlocksFieldBaseClientProps = {
readonly validate?: BlockFieldValidation
} & Omit<FormFieldBase<BlocksFieldClientWithoutType>, 'validate'>
}
export type BlocksFieldClientProps = BlocksFieldBaseClientProps &
ClientFieldBase<BlocksFieldClientWithoutType>
export type BlocksFieldServerProps = ServerFieldBase<BlockField>
export type BlocksFieldServerComponent = FieldServerComponent<BlockField>
export type BlocksFieldClientComponent = FieldClientComponent<
BlocksFieldClientWithoutType,
BlocksFieldBaseClientProps
>
export type BlockFieldLabelServerComponent = FieldLabelServerComponent<BlockField>

View File

@@ -3,24 +3,41 @@ import type { MarkOptional } from 'ts-essentials'
import type { CheckboxField, CheckboxFieldClient } from '../../fields/config/types.js'
import type { CheckboxFieldValidation } from '../../fields/validations.js'
import type { FieldErrorClientComponent, FieldErrorServerComponent } from '../forms/Error.js'
import type {
ClientFieldBase,
FieldClientComponent,
FieldServerComponent,
ServerFieldBase,
} from '../forms/Field.js'
import type {
FieldDescriptionClientComponent,
FieldDescriptionServerComponent,
FieldLabelClientComponent,
FieldLabelServerComponent,
FormFieldBase,
} from '../types.js'
type CheckboxFieldClientWithoutType = MarkOptional<CheckboxFieldClient, 'type'>
export type CheckboxFieldProps = {
type CheckboxFieldBaseClientProps = {
readonly checked?: boolean
readonly disableFormData?: boolean
readonly id?: string
readonly onChange?: (value: boolean) => void
readonly partialChecked?: boolean
readonly validate?: CheckboxFieldValidation
} & Omit<FormFieldBase<CheckboxFieldClientWithoutType>, 'validate'>
}
export type CheckboxFieldClientProps = CheckboxFieldBaseClientProps &
ClientFieldBase<CheckboxFieldClientWithoutType>
export type CheckboxFieldServerProps = ServerFieldBase<CheckboxField>
export type CheckboxFieldServerComponent = FieldServerComponent<CheckboxField>
export type CheckboxFieldClientComponent = FieldClientComponent<
CheckboxFieldClientWithoutType,
CheckboxFieldBaseClientProps
>
export type CheckboxFieldLabelServerComponent = FieldLabelServerComponent<CheckboxField>

View File

@@ -3,20 +3,37 @@ import type { MarkOptional } from 'ts-essentials'
import type { CodeField, CodeFieldClient } from '../../fields/config/types.js'
import type { CodeFieldValidation } from '../../fields/validations.js'
import type { FieldErrorClientComponent, FieldErrorServerComponent } from '../forms/Error.js'
import type {
ClientFieldBase,
FieldClientComponent,
FieldServerComponent,
ServerFieldBase,
} from '../forms/Field.js'
import type {
FieldDescriptionClientComponent,
FieldDescriptionServerComponent,
FieldLabelClientComponent,
FieldLabelServerComponent,
FormFieldBase,
} from '../types.js'
type CodeFieldClientWithoutType = MarkOptional<CodeFieldClient, 'type'>
export type CodeFieldProps = {
type CodeFieldBaseClientProps = {
readonly autoComplete?: string
readonly validate?: CodeFieldValidation
} & Omit<FormFieldBase<CodeFieldClientWithoutType>, 'validate'>
readonly valiCode?: CodeFieldValidation
}
export type CodeFieldClientProps = ClientFieldBase<CodeFieldClientWithoutType> &
CodeFieldBaseClientProps
export type CodeFieldServerProps = ServerFieldBase<CodeField>
export type CodeFieldServerComponent = FieldServerComponent<CodeField>
export type CodeFieldClientComponent = FieldClientComponent<
CodeFieldClientWithoutType,
CodeFieldBaseClientProps
>
export type CodeFieldLabelServerComponent = FieldLabelServerComponent<CodeField>

View File

@@ -2,17 +2,29 @@ import type { MarkOptional } from 'ts-essentials'
import type { CollapsibleField, CollapsibleFieldClient } from '../../fields/config/types.js'
import type { FieldErrorClientComponent, FieldErrorServerComponent } from '../forms/Error.js'
import type {
ClientFieldBase,
FieldClientComponent,
FieldServerComponent,
ServerFieldBase,
} from '../forms/Field.js'
import type {
FieldDescriptionClientComponent,
FieldDescriptionServerComponent,
FieldLabelClientComponent,
FieldLabelServerComponent,
FormFieldBase,
} from '../types.js'
type CollapsibleFieldClientWithoutType = MarkOptional<CollapsibleFieldClient, 'type'>
export type CollapsibleFieldProps = FormFieldBase<CollapsibleFieldClientWithoutType>
export type CollapsibleFieldClientProps = ClientFieldBase<CollapsibleFieldClientWithoutType>
export type CollapsibleFieldServerProps = ServerFieldBase<CollapsibleField>
export type CollapsibleFieldServerComponent = FieldServerComponent<CollapsibleField>
export type CollapsibleFieldClientComponent =
FieldClientComponent<CollapsibleFieldClientWithoutType>
export type CollapsibleFieldLabelServerComponent = FieldLabelServerComponent<CollapsibleField>

View File

@@ -3,19 +3,36 @@ import type { MarkOptional } from 'ts-essentials'
import type { DateField, DateFieldClient } from '../../fields/config/types.js'
import type { DateFieldValidation } from '../../fields/validations.js'
import type { FieldErrorClientComponent, FieldErrorServerComponent } from '../forms/Error.js'
import type {
ClientFieldBase,
FieldClientComponent,
FieldServerComponent,
ServerFieldBase,
} from '../forms/Field.js'
import type {
FieldDescriptionClientComponent,
FieldDescriptionServerComponent,
FieldLabelClientComponent,
FieldLabelServerComponent,
FormFieldBase,
} from '../types.js'
type DateFieldClientWithoutType = MarkOptional<DateFieldClient, 'type'>
export type DateFieldProps = {
type DateFieldBaseClientProps = {
readonly validate?: DateFieldValidation
} & Omit<FormFieldBase<DateFieldClientWithoutType>, 'validate'>
}
export type DateFieldClientProps = ClientFieldBase<DateFieldClientWithoutType> &
DateFieldBaseClientProps
export type DateFieldServerProps = ServerFieldBase<DateField>
export type DateFieldServerComponent = FieldServerComponent<DateField>
export type DateFieldClientComponent = FieldClientComponent<
DateFieldClientWithoutType,
DateFieldBaseClientProps
>
export type DateFieldLabelServerComponent = FieldLabelServerComponent<DateField>

View File

@@ -3,20 +3,37 @@ import type { MarkOptional } from 'ts-essentials'
import type { EmailField, EmailFieldClient } from '../../fields/config/types.js'
import type { EmailFieldValidation } from '../../fields/validations.js'
import type { FieldErrorClientComponent, FieldErrorServerComponent } from '../forms/Error.js'
import type {
ClientFieldBase,
FieldClientComponent,
FieldServerComponent,
ServerFieldBase,
} from '../forms/Field.js'
import type {
FieldDescriptionClientComponent,
FieldDescriptionServerComponent,
FieldLabelClientComponent,
FieldLabelServerComponent,
FormFieldBase,
} from '../types.js'
type EmailFieldClientWithoutType = MarkOptional<EmailFieldClient, 'type'>
export type EmailFieldProps = {
type EmailFieldBaseClientProps = {
readonly autoComplete?: string
readonly validate?: EmailFieldValidation
} & Omit<FormFieldBase<EmailFieldClientWithoutType>, 'validate'>
}
export type EmailFieldClientProps = ClientFieldBase<EmailFieldClientWithoutType> &
EmailFieldBaseClientProps
export type EmailFieldServerProps = ServerFieldBase<EmailField>
export type EmailFieldServerComponent = FieldServerComponent<EmailField>
export type EmailFieldClientComponent = FieldClientComponent<
EmailFieldClientWithoutType,
EmailFieldBaseClientProps
>
export type EmailFieldLabelServerComponent = FieldLabelServerComponent<EmailField>

View File

@@ -2,17 +2,28 @@ import type { MarkOptional } from 'ts-essentials'
import type { GroupField, GroupFieldClient } from '../../fields/config/types.js'
import type { FieldErrorClientComponent, FieldErrorServerComponent } from '../forms/Error.js'
import type {
ClientFieldBase,
FieldClientComponent,
FieldServerComponent,
ServerFieldBase,
} from '../forms/Field.js'
import type {
FieldDescriptionClientComponent,
FieldDescriptionServerComponent,
FieldLabelClientComponent,
FieldLabelServerComponent,
FormFieldBase,
} from '../types.js'
type GroupFieldClientWithoutType = MarkOptional<GroupFieldClient, 'type'>
export type GroupFieldProps = FormFieldBase<GroupFieldClientWithoutType>
export type GroupFieldClientProps = ClientFieldBase<GroupFieldClientWithoutType>
export type GroupFieldServerProps = ServerFieldBase<GroupField>
export type GroupFieldServerComponent = FieldServerComponent<GroupField>
export type GroupFieldClientComponent = FieldClientComponent<GroupFieldClientWithoutType>
export type GroupFieldLabelServerComponent = FieldLabelServerComponent<GroupField>

View File

@@ -3,19 +3,36 @@ import type { MarkOptional } from 'ts-essentials'
import type { JSONField, JSONFieldClient } from '../../fields/config/types.js'
import type { JSONFieldValidation } from '../../fields/validations.js'
import type { FieldErrorClientComponent, FieldErrorServerComponent } from '../forms/Error.js'
import type {
ClientFieldBase,
FieldClientComponent,
FieldServerComponent,
ServerFieldBase,
} from '../forms/Field.js'
import type {
FieldDescriptionClientComponent,
FieldDescriptionServerComponent,
FieldLabelClientComponent,
FieldLabelServerComponent,
FormFieldBase,
} from '../types.js'
type JSONFieldClientWithoutType = MarkOptional<JSONFieldClient, 'type'>
export type JSONFieldProps = {
type JSONFieldBaseClientProps = {
readonly validate?: JSONFieldValidation
} & Omit<FormFieldBase<JSONFieldClientWithoutType>, 'validate'>
}
export type JSONFieldClientProps = ClientFieldBase<JSONFieldClientWithoutType> &
JSONFieldBaseClientProps
export type JSONFieldServerProps = ServerFieldBase<JSONField>
export type JSONFieldServerComponent = FieldServerComponent<JSONField>
export type JSONFieldClientComponent = FieldClientComponent<
JSONFieldClientWithoutType,
JSONFieldBaseClientProps
>
export type JSONFieldLabelServerComponent = FieldLabelServerComponent<JSONField>

View File

@@ -3,20 +3,37 @@ import type { MarkOptional } from 'ts-essentials'
import type { NumberField, NumberFieldClient } from '../../fields/config/types.js'
import type { NumberFieldValidation } from '../../fields/validations.js'
import type { FieldErrorClientComponent, FieldErrorServerComponent } from '../forms/Error.js'
import type {
ClientFieldBase,
FieldClientComponent,
FieldServerComponent,
ServerFieldBase,
} from '../forms/Field.js'
import type {
FieldDescriptionClientComponent,
FieldDescriptionServerComponent,
FieldLabelClientComponent,
FieldLabelServerComponent,
FormFieldBase,
} from '../types.js'
type NumberFieldClientWithoutType = MarkOptional<NumberFieldClient, 'type'>
export type NumberFieldProps = {
type NumberFieldBaseClientProps = {
readonly onChange?: (e: number) => void
readonly validate?: NumberFieldValidation
} & Omit<FormFieldBase<NumberFieldClientWithoutType>, 'validate'>
}
export type NumberFieldClientProps = ClientFieldBase<NumberFieldClientWithoutType> &
NumberFieldBaseClientProps
export type NumberFieldServerProps = ServerFieldBase<NumberField>
export type NumberFieldServerComponent = FieldServerComponent<NumberField>
export type NumberFieldClientComponent = FieldClientComponent<
NumberFieldClientWithoutType,
NumberFieldBaseClientProps
>
export type NumberFieldLabelServerComponent = FieldLabelServerComponent<NumberField>

View File

@@ -3,19 +3,36 @@ import type { MarkOptional } from 'ts-essentials'
import type { PointField, PointFieldClient } from '../../fields/config/types.js'
import type { PointFieldValidation } from '../../fields/validations.js'
import type { FieldErrorClientComponent, FieldErrorServerComponent } from '../forms/Error.js'
import type {
ClientFieldBase,
FieldClientComponent,
FieldServerComponent,
ServerFieldBase,
} from '../forms/Field.js'
import type {
FieldDescriptionClientComponent,
FieldDescriptionServerComponent,
FieldLabelClientComponent,
FieldLabelServerComponent,
FormFieldBase,
} from '../types.js'
type PointFieldClientWithoutType = MarkOptional<PointFieldClient, 'type'>
export type PointFieldProps = {
type PointFieldBaseClientProps = {
readonly validate?: PointFieldValidation
} & Omit<FormFieldBase<PointFieldClientWithoutType>, 'validate'>
}
export type PointFieldClientProps = ClientFieldBase<PointFieldClientWithoutType> &
PointFieldBaseClientProps
export type PointFieldServerProps = ServerFieldBase<PointField>
export type PointFieldServerComponent = FieldServerComponent<PointField>
export type PointFieldClientComponent = FieldClientComponent<
PointFieldClientWithoutType,
PointFieldBaseClientProps
>
export type PointFieldLabelServerComponent = FieldLabelServerComponent<PointField>

View File

@@ -3,21 +3,38 @@ import type { MarkOptional } from 'ts-essentials'
import type { RadioField, RadioFieldClient } from '../../fields/config/types.js'
import type { RadioFieldValidation } from '../../fields/validations.js'
import type { FieldErrorClientComponent, FieldErrorServerComponent } from '../forms/Error.js'
import type {
ClientFieldBase,
FieldClientComponent,
FieldServerComponent,
ServerFieldBase,
} from '../forms/Field.js'
import type {
FieldDescriptionClientComponent,
FieldDescriptionServerComponent,
FieldLabelClientComponent,
FieldLabelServerComponent,
FormFieldBase,
} from '../types.js'
type RadioFieldClientWithoutType = MarkOptional<RadioFieldClient, 'type'>
export type RadioFieldProps = {
type RadioFieldBaseClientProps = {
readonly onChange?: OnChange
readonly validate?: RadioFieldValidation
readonly value?: string
} & Omit<FormFieldBase<RadioFieldClientWithoutType>, 'validate'>
}
export type RadioFieldClientProps = ClientFieldBase<RadioFieldClientWithoutType> &
RadioFieldBaseClientProps
export type RadioFieldServerProps = ServerFieldBase<RadioField>
export type RadioFieldServerComponent = FieldServerComponent<RadioField>
export type RadioFieldClientComponent = FieldClientComponent<
RadioFieldClientWithoutType,
RadioFieldBaseClientProps
>
export type OnChange<T = string> = (value: T) => void

View File

@@ -3,19 +3,36 @@ import type { MarkOptional } from 'ts-essentials'
import type { RelationshipField, RelationshipFieldClient } from '../../fields/config/types.js'
import type { RelationshipFieldValidation } from '../../fields/validations.js'
import type { FieldErrorClientComponent, FieldErrorServerComponent } from '../forms/Error.js'
import type {
ClientFieldBase,
FieldClientComponent,
FieldServerComponent,
ServerFieldBase,
} from '../forms/Field.js'
import type {
FieldDescriptionClientComponent,
FieldDescriptionServerComponent,
FieldLabelClientComponent,
FieldLabelServerComponent,
FormFieldBase,
} from '../types.js'
type RelationshipFieldClientWithoutType = MarkOptional<RelationshipFieldClient, 'type'>
export type RelationshipFieldProps = {
type RelationshipFieldBaseClientProps = {
readonly validate?: RelationshipFieldValidation
} & Omit<FormFieldBase<RelationshipFieldClientWithoutType>, 'validate'>
}
export type RelationshipFieldClientProps = ClientFieldBase<RelationshipFieldClientWithoutType> &
RelationshipFieldBaseClientProps
export type RelationshipFieldServerProps = ServerFieldBase<RelationshipField>
export type RelationshipFieldServerComponent = FieldServerComponent<RelationshipField>
export type RelationshipFieldClientComponent = FieldClientComponent<
RelationshipFieldClientWithoutType,
RelationshipFieldBaseClientProps
>
export type RelationshipFieldLabelServerComponent = FieldLabelServerComponent<RelationshipField>

View File

@@ -3,23 +3,44 @@ import type { MarkOptional } from 'ts-essentials'
import type { RichTextField, RichTextFieldClient } from '../../fields/config/types.js'
import type { RichTextFieldValidation } from '../../fields/validations.js'
import type { FieldErrorClientComponent, FieldErrorServerComponent } from '../forms/Error.js'
import type {
ClientFieldBase,
FieldClientComponent,
FieldServerComponent,
ServerFieldBase,
} from '../forms/Field.js'
import type {
FieldDescriptionClientComponent,
FieldDescriptionServerComponent,
FieldLabelClientComponent,
FieldLabelServerComponent,
FormFieldBase,
} from '../types.js'
type RichTextFieldClientWithoutType = MarkOptional<RichTextFieldClient, 'type'>
export type RichTextFieldProps<
type RichTextFieldBaseClientProps<
TValue extends object = any,
TAdapterProps = any,
TExtraProperties = object,
> = {
readonly validate?: RichTextFieldValidation
} & Omit<FormFieldBase<RichTextFieldClientWithoutType>, 'validate'>
}
export type RichTextFieldClientProps<
TValue extends object = any,
TAdapterProps = any,
TExtraProperties = object,
> = ClientFieldBase<RichTextFieldClientWithoutType> &
RichTextFieldBaseClientProps<TValue, TAdapterProps, TExtraProperties>
export type RichTextFieldServerProps = ServerFieldBase<RichTextField>
export type RichTextFieldServerComponent = FieldServerComponent<RichTextField>
export type RichTextFieldClientComponent = FieldClientComponent<
RichTextFieldClientWithoutType,
RichTextFieldBaseClientProps
>
export type RichTextFieldLabelServerComponent = FieldLabelServerComponent<RichTextField>

View File

@@ -1,6 +1,12 @@
import type { MarkOptional } from 'ts-essentials'
import type { RowField, RowFieldClient } from '../../fields/config/types.js'
import type {
ClientFieldBase,
FieldClientComponent,
FieldServerComponent,
ServerFieldBase,
} from '../forms/Field.js'
import type {
FieldDescriptionClientComponent,
FieldDescriptionServerComponent,
@@ -8,15 +14,26 @@ import type {
FieldErrorServerComponent,
FieldLabelClientComponent,
FieldLabelServerComponent,
FormFieldBase,
} from '../types.js'
type RowFieldClientWithoutType = MarkOptional<RowFieldClient, 'type'>
export type RowFieldProps = {
type RowFieldBaseClientProps = {
readonly forceRender?: boolean
readonly indexPath: string
} & FormFieldBase<RowFieldClientWithoutType>
}
export type RowFieldClientProps = ClientFieldBase<RowFieldClientWithoutType> &
RowFieldBaseClientProps
export type RowFieldServerProps = ServerFieldBase<RowField>
export type RowFieldServerComponent = FieldServerComponent<RowField>
export type RowFieldClientComponent = FieldClientComponent<
RowFieldClientWithoutType,
RowFieldBaseClientProps
>
export type RowFieldLabelServerComponent = FieldLabelServerComponent<RowField>

View File

@@ -3,21 +3,38 @@ import type { MarkOptional } from 'ts-essentials'
import type { SelectField, SelectFieldClient } from '../../fields/config/types.js'
import type { SelectFieldValidation } from '../../fields/validations.js'
import type { FieldErrorClientComponent, FieldErrorServerComponent } from '../forms/Error.js'
import type {
ClientFieldBase,
FieldClientComponent,
FieldServerComponent,
ServerFieldBase,
} from '../forms/Field.js'
import type {
FieldDescriptionClientComponent,
FieldDescriptionServerComponent,
FieldLabelClientComponent,
FieldLabelServerComponent,
FormFieldBase,
} from '../types.js'
type SelectFieldClientWithoutType = MarkOptional<SelectFieldClient, 'type'>
export type SelectFieldProps = {
type SelectFieldBaseClientProps = {
readonly onChange?: (e: string | string[]) => void
readonly validate?: SelectFieldValidation
readonly value?: string
} & Omit<FormFieldBase<SelectFieldClientWithoutType>, 'validate'>
}
export type SelectFieldClientProps = ClientFieldBase<SelectFieldClientWithoutType> &
SelectFieldBaseClientProps
export type SelectFieldServerProps = ServerFieldBase<SelectField>
export type SelectFieldServerComponent = FieldServerComponent<SelectField>
export type SelectFieldClientComponent = FieldClientComponent<
SelectFieldClientWithoutType,
SelectFieldBaseClientProps
>
export type SelectFieldLabelServerComponent = FieldLabelServerComponent<SelectField>

View File

@@ -8,21 +8,32 @@ import type {
UnnamedTab,
} from '../../fields/config/types.js'
import type { FieldErrorClientComponent, FieldErrorServerComponent } from '../forms/Error.js'
import type {
ClientFieldBase,
FieldClientComponent,
FieldServerComponent,
ServerFieldBase,
} from '../forms/Field.js'
import type {
FieldDescriptionClientComponent,
FieldDescriptionServerComponent,
FieldLabelClientComponent,
FieldLabelServerComponent,
FormFieldBase,
} from '../types.js'
export type ClientTab =
| ({ fields: ClientField[] } & Omit<NamedTab, 'fields'>)
| ({ fields: ClientField[] } & Omit<UnnamedTab, 'fields'>)
export type TabsFieldClientWithoutType = MarkOptional<TabsFieldClient, 'type'>
type TabsFieldClientWithoutType = MarkOptional<TabsFieldClient, 'type'>
export type TabsFieldProps = FormFieldBase<TabsFieldClientWithoutType>
export type TabsFieldClientProps = ClientFieldBase<TabsFieldClientWithoutType>
export type TabsFieldServerProps = ServerFieldBase<TabsField>
export type TabsFieldServerComponent = FieldServerComponent<TabsField>
export type TabsFieldClientComponent = FieldClientComponent<TabsFieldClientWithoutType>
export type TabsFieldLabelServerComponent = FieldLabelServerComponent<TabsField>

View File

@@ -4,21 +4,38 @@ import type { MarkOptional } from 'ts-essentials'
import type { TextField, TextFieldClient } from '../../fields/config/types.js'
import type { TextFieldValidation } from '../../fields/validations.js'
import type { FieldErrorClientComponent, FieldErrorServerComponent } from '../forms/Error.js'
import type {
ClientFieldBase,
FieldClientComponent,
FieldServerComponent,
ServerFieldBase,
} from '../forms/Field.js'
import type {
FieldDescriptionClientComponent,
FieldDescriptionServerComponent,
FieldLabelClientComponent,
FieldLabelServerComponent,
FormFieldBase,
} from '../types.js'
type TextFieldClientWithoutType = MarkOptional<TextFieldClient, 'type'>
export type TextFieldProps = {
type TextFieldBaseClientProps = {
readonly inputRef?: React.RefObject<HTMLInputElement>
readonly onKeyDown?: React.KeyboardEventHandler<HTMLInputElement>
readonly validate?: TextFieldValidation
} & Omit<FormFieldBase<TextFieldClientWithoutType>, 'validate'>
}
export type TextFieldClientProps = ClientFieldBase<TextFieldClientWithoutType> &
TextFieldBaseClientProps
export type TextFieldServerProps = ServerFieldBase<TextField>
export type TextFieldServerComponent = FieldServerComponent<TextField>
export type TextFieldClientComponent = FieldClientComponent<
TextFieldClientWithoutType,
TextFieldBaseClientProps
>
export type TextFieldLabelServerComponent = FieldLabelServerComponent<TextField>

View File

@@ -4,21 +4,38 @@ import type { MarkOptional } from 'ts-essentials'
import type { TextareaField, TextareaFieldClient } from '../../fields/config/types.js'
import type { TextareaFieldValidation } from '../../fields/validations.js'
import type { FieldErrorClientComponent, FieldErrorServerComponent } from '../forms/Error.js'
import type {
ClientFieldBase,
FieldClientComponent,
FieldServerComponent,
ServerFieldBase,
} from '../forms/Field.js'
import type {
FieldDescriptionClientComponent,
FieldDescriptionServerComponent,
FieldLabelClientComponent,
FieldLabelServerComponent,
FormFieldBase,
} from '../types.js'
type TextareaFieldClientWithoutType = MarkOptional<TextareaFieldClient, 'type'>
export type TextareaFieldProps = {
type TextareaFieldBaseClientProps = {
readonly inputRef?: React.Ref<HTMLInputElement>
readonly onKeyDown?: React.KeyboardEventHandler<HTMLInputElement>
readonly validate?: TextareaFieldValidation
} & Omit<FormFieldBase<TextareaFieldClientWithoutType>, 'validate'>
}
export type TextareaFieldClientProps = ClientFieldBase<TextareaFieldClientWithoutType> &
TextareaFieldBaseClientProps
export type TextareaFieldServerProps = ServerFieldBase<TextareaField>
export type TextareaFieldServerComponent = FieldServerComponent<TextareaField>
export type TextareaFieldClientComponent = FieldClientComponent<
TextareaFieldClientWithoutType,
TextareaFieldBaseClientProps
>
export type TextareaFieldLabelServerComponent = FieldLabelServerComponent<TextareaField>

View File

@@ -3,19 +3,36 @@ import type { MarkOptional } from 'ts-essentials'
import type { UploadField, UploadFieldClient } from '../../fields/config/types.js'
import type { UploadFieldValidation } from '../../fields/validations.js'
import type { FieldErrorClientComponent, FieldErrorServerComponent } from '../forms/Error.js'
import type {
ClientFieldBase,
FieldClientComponent,
FieldServerComponent,
ServerFieldBase,
} from '../forms/Field.js'
import type {
FieldDescriptionClientComponent,
FieldDescriptionServerComponent,
FieldLabelClientComponent,
FieldLabelServerComponent,
FormFieldBase,
} from '../types.js'
type UploadFieldClientWithoutType = MarkOptional<UploadFieldClient, 'type'>
export type UploadFieldProps = {
type UploadFieldBaseClientProps = {
readonly validate?: UploadFieldValidation
} & Omit<FormFieldBase<UploadFieldClientWithoutType>, 'validate'>
}
export type UploadFieldClientProps = ClientFieldBase<UploadFieldClientWithoutType> &
UploadFieldBaseClientProps
export type UploadFieldServerProps = ServerFieldBase<UploadField>
export type UploadFieldServerComponent = FieldServerComponent<UploadField>
export type UploadFieldClientComponent = FieldClientComponent<
UploadFieldClientWithoutType,
UploadFieldBaseClientProps
>
export type UploadFieldLabelServerComponent = FieldLabelServerComponent<UploadField>

View File

@@ -1,13 +1,10 @@
import type { MarkOptional } from 'ts-essentials'
import type { LabelFunction, ServerProps } from '../../config/types.js'
import type { ClientField, Field } from '../../fields/config/types.js'
import type { Field } from '../../fields/config/types.js'
import type { MappedComponent } from '../types.js'
import type { ClientFieldWithOptionalType } from './Field.js'
export type DescriptionFunction = LabelFunction
type ClientFieldWithOptionalType = MarkOptional<ClientField, 'type'>
export type FieldDescriptionClientComponent<
TFieldClient extends ClientFieldWithOptionalType = ClientFieldWithOptionalType,
> = React.ComponentType<FieldDescriptionClientProps<TFieldClient>>

View File

@@ -1,8 +1,7 @@
import type { MarkOptional } from 'ts-essentials'
import type { ServerProps } from '../../config/types.js'
import type { ClientField, Field } from '../../fields/config/types.js'
import type { Field } from '../../fields/config/types.js'
import type { MappedComponent } from '../types.js'
import type { ClientFieldWithOptionalType } from './Field.js'
export type GenericErrorProps = {
readonly alignCaret?: 'center' | 'left' | 'right'
@@ -12,8 +11,6 @@ export type GenericErrorProps = {
readonly showError?: boolean
}
type ClientFieldWithOptionalType = MarkOptional<ClientField, 'type'>
export type FieldErrorClientProps<
TFieldClient extends ClientFieldWithOptionalType = ClientFieldWithOptionalType,
> = {

View File

@@ -2,24 +2,36 @@ import type { MarkOptional } from 'ts-essentials'
import type { User } from '../../auth/types.js'
import type { Locale } from '../../config/types.js'
import type { ClientField, Validate } from '../../fields/config/types.js'
import type { ClientField, Field, Validate } from '../../fields/config/types.js'
import type { DocumentPreferences } from '../../preferences/types.js'
import type { FieldDescriptionClientProps } from './Description.js'
import type { FieldErrorClientProps } from './Error.js'
import type { FieldLabelClientProps } from './Label.js'
import type { FieldDescriptionClientProps, FieldDescriptionServerProps } from './Description.js'
import type { FieldErrorClientProps, FieldErrorServerProps } from './Error.js'
import type { FieldLabelClientProps, FieldLabelServerProps } from './Label.js'
export type FormFieldBase<
TFieldClient extends MarkOptional<ClientField, 'type'> = MarkOptional<ClientField, 'type'>,
export type ClientFieldWithOptionalType = MarkOptional<ClientField, 'type'>
export type ClientFieldBase<
TFieldClient extends ClientFieldWithOptionalType = ClientFieldWithOptionalType,
> = {
readonly descriptionProps?: FieldDescriptionClientProps<TFieldClient>
readonly docPreferences?: DocumentPreferences
readonly errorProps?: FieldErrorClientProps<TFieldClient>
readonly field: TFieldClient
readonly labelProps?: FieldLabelClientProps<TFieldClient>
} & FormFieldBase
export type ServerFieldBase<TFieldServer extends Field = Field> = {
readonly descriptionProps?: FieldDescriptionServerProps<TFieldServer>
readonly errorProps?: FieldErrorServerProps<TFieldServer>
readonly field: TFieldServer
readonly labelProps?: FieldLabelServerProps<TFieldServer>
} & FormFieldBase
export type FormFieldBase = {
readonly docPreferences?: DocumentPreferences
/**
* `forceRender` is added by RenderField automatically.
*/
readonly forceRender?: boolean
readonly labelProps?: FieldLabelClientProps<TFieldClient>
readonly locale?: Locale
/**
* `readOnly` is added by RenderField automatically. This should be used instead of `field.admin.readOnly`.
@@ -28,3 +40,13 @@ export type FormFieldBase<
readonly user?: User
readonly validate?: Validate
}
export type FieldClientComponent<
TFieldClient extends ClientFieldWithOptionalType = ClientFieldWithOptionalType,
AdditionalProps extends Record<string, unknown> = Record<string, unknown>,
> = React.ComponentType<AdditionalProps & ClientFieldBase<TFieldClient>>
export type FieldServerComponent<
TFieldServer extends Field = Field,
AdditionalProps extends Record<string, unknown> = Record<string, unknown>,
> = React.ComponentType<AdditionalProps & ServerFieldBase<TFieldServer>>

View File

@@ -1,8 +1,7 @@
import type { MarkOptional } from 'ts-essentials'
import type { ServerProps, StaticLabel } from '../../config/types.js'
import type { ClientField, Field } from '../../fields/config/types.js'
import type { MappedComponent } from '../types.js'
import type { ClientFieldWithOptionalType } from './Field.js'
export type GenericLabelProps = {
readonly as?: 'label' | 'span'
@@ -13,8 +12,6 @@ export type GenericLabelProps = {
readonly unstyled?: boolean
}
type ClientFieldWithOptionalType = MarkOptional<ClientField, 'type'>
export type FieldLabelClientProps<
TFieldClient extends ClientFieldWithOptionalType = ClientFieldWithOptionalType,
> = {

View File

@@ -24,13 +24,16 @@ export type {
} from './elements/WithServerSideProps.js'
export type {
ArrayFieldClientComponent,
ArrayFieldClientProps,
ArrayFieldDescriptionClientComponent,
ArrayFieldDescriptionServerComponent,
ArrayFieldErrorClientComponent,
ArrayFieldErrorServerComponent,
ArrayFieldLabelClientComponent,
ArrayFieldLabelServerComponent,
ArrayFieldProps,
ArrayFieldServerComponent,
ArrayFieldServerProps,
} from './fields/Array.js'
export type {
@@ -40,190 +43,247 @@ export type {
BlockFieldErrorServerComponent,
BlockFieldLabelClientComponent,
BlockFieldLabelServerComponent,
BlockFieldProps,
BlocksFieldClientComponent,
BlocksFieldClientProps,
BlocksFieldServerComponent,
BlocksFieldServerProps,
} from './fields/Blocks.js'
export type {
CheckboxFieldClientComponent,
CheckboxFieldClientProps,
CheckboxFieldDescriptionClientComponent,
CheckboxFieldDescriptionServerComponent,
CheckboxFieldErrorClientComponent,
CheckboxFieldErrorServerComponent,
CheckboxFieldLabelClientComponent,
CheckboxFieldLabelServerComponent,
CheckboxFieldProps,
CheckboxFieldServerComponent,
CheckboxFieldServerProps,
} from './fields/Checkbox.js'
export type {
CodeFieldClientComponent,
CodeFieldClientProps,
CodeFieldDescriptionClientComponent,
CodeFieldDescriptionServerComponent,
CodeFieldErrorClientComponent,
CodeFieldErrorServerComponent,
CodeFieldLabelClientComponent,
CodeFieldLabelServerComponent,
CodeFieldProps,
CodeFieldServerComponent,
CodeFieldServerProps,
} from './fields/Code.js'
export type {
CollapsibleFieldClientComponent,
CollapsibleFieldClientProps,
CollapsibleFieldDescriptionClientComponent,
CollapsibleFieldDescriptionServerComponent,
CollapsibleFieldErrorClientComponent,
CollapsibleFieldErrorServerComponent,
CollapsibleFieldLabelClientComponent,
CollapsibleFieldLabelServerComponent,
CollapsibleFieldProps,
CollapsibleFieldServerComponent,
CollapsibleFieldServerProps,
} from './fields/Collapsible.js'
export type {
DateFieldClientComponent,
DateFieldClientProps,
DateFieldDescriptionClientComponent,
DateFieldDescriptionServerComponent,
DateFieldErrorClientComponent,
DateFieldErrorServerComponent,
DateFieldLabelClientComponent,
DateFieldLabelServerComponent,
DateFieldProps,
DateFieldServerComponent,
DateFieldServerProps,
} from './fields/Date.js'
export type {
EmailFieldClientComponent,
EmailFieldClientProps,
EmailFieldDescriptionClientComponent,
EmailFieldDescriptionServerComponent,
EmailFieldErrorClientComponent,
EmailFieldErrorServerComponent,
EmailFieldLabelClientComponent,
EmailFieldLabelServerComponent,
EmailFieldProps,
EmailFieldServerComponent,
EmailFieldServerProps,
} from './fields/Email.js'
export type {
GroupFieldClientComponent,
GroupFieldClientProps,
GroupFieldDescriptionClientComponent,
GroupFieldDescriptionServerComponent,
GroupFieldErrorClientComponent,
GroupFieldErrorServerComponent,
GroupFieldLabelClientComponent,
GroupFieldLabelServerComponent,
GroupFieldProps,
GroupFieldServerComponent,
GroupFieldServerProps,
} from './fields/Group.js'
export type { HiddenFieldProps } from './fields/Hidden.js'
export type {
JSONFieldClientComponent,
JSONFieldClientProps,
JSONFieldDescriptionClientComponent,
JSONFieldDescriptionServerComponent,
JSONFieldErrorClientComponent,
JSONFieldErrorServerComponent,
JSONFieldLabelClientComponent,
JSONFieldLabelServerComponent,
JSONFieldProps,
JSONFieldServerComponent,
JSONFieldServerProps,
} from './fields/JSON.js'
export type {
NumberFieldClientComponent,
NumberFieldClientProps,
NumberFieldDescriptionClientComponent,
NumberFieldDescriptionServerComponent,
NumberFieldErrorClientComponent,
NumberFieldErrorServerComponent,
NumberFieldLabelClientComponent,
NumberFieldLabelServerComponent,
NumberFieldProps,
NumberFieldServerComponent,
NumberFieldServerProps,
} from './fields/Number.js'
export type {
PointFieldClientComponent,
PointFieldClientProps,
PointFieldDescriptionClientComponent,
PointFieldDescriptionServerComponent,
PointFieldErrorClientComponent,
PointFieldErrorServerComponent,
PointFieldLabelClientComponent,
PointFieldLabelServerComponent,
PointFieldProps,
PointFieldServerComponent,
PointFieldServerProps,
} from './fields/Point.js'
export type {
RadioFieldClientComponent,
RadioFieldClientProps,
RadioFieldDescriptionClientComponent,
RadioFieldDescriptionServerComponent,
RadioFieldErrorClientComponent,
RadioFieldErrorServerComponent,
RadioFieldLabelClientComponent,
RadioFieldLabelServerComponent,
RadioFieldProps,
RadioFieldServerComponent,
RadioFieldServerProps,
} from './fields/Radio.js'
export type {
RelationshipFieldClientComponent,
RelationshipFieldClientProps,
RelationshipFieldDescriptionClientComponent,
RelationshipFieldDescriptionServerComponent,
RelationshipFieldErrorClientComponent,
RelationshipFieldErrorServerComponent,
RelationshipFieldLabelClientComponent,
RelationshipFieldLabelServerComponent,
RelationshipFieldProps,
RelationshipFieldServerComponent,
RelationshipFieldServerProps,
} from './fields/Relationship.js'
export type {
RichTextFieldClientComponent,
RichTextFieldClientProps,
RichTextFieldDescriptionClientComponent,
RichTextFieldDescriptionServerComponent,
RichTextFieldErrorClientComponent,
RichTextFieldErrorServerComponent,
RichTextFieldLabelClientComponent,
RichTextFieldLabelServerComponent,
RichTextFieldProps,
RichTextFieldServerComponent,
RichTextFieldServerProps,
} from './fields/RichText.js'
export type {
RowFieldClientComponent,
RowFieldClientProps,
RowFieldDescriptionClientComponent,
RowFieldDescriptionServerComponent,
RowFieldErrorClientComponent,
RowFieldErrorServerComponent,
RowFieldLabelClientComponent,
RowFieldLabelServerComponent,
RowFieldProps,
RowFieldServerComponent,
RowFieldServerProps,
} from './fields/Row.js'
export type {
SelectFieldClientComponent,
SelectFieldClientProps,
SelectFieldDescriptionClientComponent,
SelectFieldDescriptionServerComponent,
SelectFieldErrorClientComponent,
SelectFieldErrorServerComponent,
SelectFieldLabelClientComponent,
SelectFieldLabelServerComponent,
SelectFieldProps,
SelectFieldServerComponent,
SelectFieldServerProps,
} from './fields/Select.js'
export type {
ClientTab,
TabsFieldClientComponent,
TabsFieldClientProps,
TabsFieldDescriptionClientComponent,
TabsFieldDescriptionServerComponent,
TabsFieldErrorClientComponent,
TabsFieldErrorServerComponent,
TabsFieldLabelClientComponent,
TabsFieldLabelServerComponent,
TabsFieldProps,
TabsFieldServerComponent,
TabsFieldServerProps,
} from './fields/Tabs.js'
export type {
TextFieldClientComponent,
TextFieldClientProps,
TextFieldDescriptionClientComponent,
TextFieldDescriptionServerComponent,
TextFieldErrorClientComponent,
TextFieldErrorServerComponent,
TextFieldLabelClientComponent,
TextFieldLabelServerComponent,
TextFieldProps,
TextFieldServerComponent,
TextFieldServerProps,
} from './fields/Text.js'
export type {
TextareaFieldClientComponent,
TextareaFieldClientProps,
TextareaFieldDescriptionClientComponent,
TextareaFieldDescriptionServerComponent,
TextareaFieldErrorClientComponent,
TextareaFieldErrorServerComponent,
TextareaFieldLabelClientComponent,
TextareaFieldLabelServerComponent,
TextareaFieldProps,
TextareaFieldServerComponent,
TextareaFieldServerProps,
} from './fields/Textarea.js'
export type {
UploadFieldClientComponent,
UploadFieldClientProps,
UploadFieldDescriptionClientComponent,
UploadFieldDescriptionServerComponent,
UploadFieldErrorClientComponent,
UploadFieldErrorServerComponent,
UploadFieldLabelClientComponent,
UploadFieldLabelServerComponent,
UploadFieldProps,
UploadFieldServerComponent,
UploadFieldServerProps,
} from './fields/Upload.js'
export type {

View File

@@ -2,8 +2,6 @@ import type { TypedUser } from '../../index.js'
import type { PayloadRequest } from '../../types/index.js'
import type { Permissions } from '../types.js'
import { commitTransaction } from '../../utilities/commitTransaction.js'
import { initTransaction } from '../../utilities/initTransaction.js'
import { killTransaction } from '../../utilities/killTransaction.js'
import { executeAuthStrategies } from '../executeAuthStrategies.js'
import { getAccessResults } from '../getAccessResults.js'
@@ -25,8 +23,6 @@ export const auth = async (args: Required<AuthArgs>): Promise<AuthResult> => {
const { payload } = req
try {
const shouldCommit = await initTransaction(req)
const { responseHeaders, user } = await executeAuthStrategies({
headers,
payload,
@@ -39,10 +35,6 @@ export const auth = async (args: Required<AuthArgs>): Promise<AuthResult> => {
req,
})
if (shouldCommit) {
await commitTransaction(req)
}
return {
permissions,
responseHeaders,

View File

@@ -12,8 +12,6 @@ import type { User } from '../types.js'
import { buildAfterOperation } from '../../collections/operations/utils.js'
import { AuthenticationError, LockedAuth, ValidationError } from '../../errors/index.js'
import { afterRead } from '../../fields/hooks/afterRead/index.js'
import { commitTransaction } from '../../utilities/commitTransaction.js'
import { initTransaction } from '../../utilities/initTransaction.js'
import { killTransaction } from '../../utilities/killTransaction.js'
import sanitizeInternalFields from '../../utilities/sanitizeInternalFields.js'
import { getFieldsToSign } from '../getFieldsToSign.js'
@@ -43,8 +41,6 @@ export const loginOperation = async <TSlug extends CollectionSlug>(
let args = incomingArgs
try {
const shouldCommit = await initTransaction(args.req)
// /////////////////////////////////////
// beforeOperation - Collection
// /////////////////////////////////////
@@ -202,10 +198,6 @@ export const loginOperation = async <TSlug extends CollectionSlug>(
})
}
if (shouldCommit) {
await commitTransaction(req)
}
throw new AuthenticationError(req.t)
}
@@ -334,10 +326,6 @@ export const loginOperation = async <TSlug extends CollectionSlug>(
// Return results
// /////////////////////////////////////
if (shouldCommit) {
await commitTransaction(req)
}
return result
} catch (error: unknown) {
await killTransaction(args.req)

View File

@@ -132,24 +132,70 @@ export type LoginWithUsernameOptions =
}
export interface IncomingAuthType {
/**
* Set cookie options, including secure, sameSite, and domain. For advanced users.
*/
cookies?: {
domain?: string
sameSite?: 'Lax' | 'None' | 'Strict' | boolean
secure?: boolean
}
/**
* How many levels deep a user document should be populated when creating the JWT and binding the user to the req. Defaults to 0 and should only be modified if absolutely necessary, as this will affect performance.
* @default 0
*/
depth?: number
/**
* Advanced - disable Payload's built-in local auth strategy. Only use this property if you have replaced Payload's auth mechanisms with your own.
*/
disableLocalStrategy?: true
/**
* Customize the way that the forgotPassword operation functions.
* @link https://payloadcms.com/docs/beta/authentication/email#forgot-password
*/
forgotPassword?: {
generateEmailHTML?: GenerateForgotPasswordEmailHTML
generateEmailSubject?: GenerateForgotPasswordEmailSubject
}
/**
* Set the time (in milliseconds) that a user should be locked out if they fail authentication more times than maxLoginAttempts allows for.
*/
lockTime?: number
/**
* Ability to allow users to login with username/password.
*
* @link https://payloadcms.com/docs/beta/authentication/overview#login-with-username
*/
loginWithUsername?: boolean | LoginWithUsernameOptions
/**
* Only allow a user to attempt logging in X amount of times. Automatically locks out a user from authenticating if this limit is passed. Set to 0 to disable.
*/
maxLoginAttempts?: number
/***
* Set to true if you want to remove the token from the returned authentication API responses such as login or refresh.
*/
removeTokenFromResponses?: true
/**
* Advanced - an array of custom authentification strategies to extend this collection's authentication with.
* @link https://payloadcms.com/docs/beta/authentication/custom-strategies
*/
strategies?: AuthStrategy[]
/**
* Controls how many seconds the token will be valid for. Default is 2 hours.
* @default 7200
* @link https://payloadcms.com/docs/beta/authentication/overview#config-options
*/
tokenExpiration?: number
/**
* Payload Authentication provides for API keys to be set on each user within an Authentication-enabled Collection.
* @default false
* @link https://payloadcms.com/docs/beta/authentication/api-keys
*/
useAPIKey?: boolean
/**
* Set to true or pass an object with verification options to require users to verify by email before they are allowed to log into your app.
* @link https://payloadcms.com/docs/beta/authentication/email#email-verification
*/
verify?:
| {
generateEmailHTML?: GenerateVerifyEmailHTML

View File

@@ -14,6 +14,7 @@ import baseVersionFields from '../../versions/baseFields.js'
import { versionDefaults } from '../../versions/defaults.js'
import { authDefaults, defaults, loginWithUsernameDefaults } from './defaults.js'
import { sanitizeAuthFields, sanitizeUploadFields } from './reservedFieldNames.js'
import { validateUseAsTitle } from './useAsTitle.js'
export const sanitizeCollection = async (
config: Config,
@@ -44,6 +45,8 @@ export const sanitizeCollection = async (
validRelationships,
})
validateUseAsTitle(sanitized)
if (sanitized.timestamps !== false) {
// add default timestamps fields only as needed
let hasUpdatedAt = null

View File

@@ -0,0 +1,204 @@
import type { Config } from '../../config/types.js'
import type { CollectionConfig } from '../../index.js'
import { InvalidConfiguration } from '../../errors/InvalidConfiguration.js'
import { sanitizeCollection } from './sanitize.js'
describe('sanitize - collections -', () => {
const config = {
collections: [],
globals: [],
} as Partial<Config>
describe('validate useAsTitle -', () => {
const defaultCollection: CollectionConfig = {
slug: 'collection-with-defaults',
fields: [
{
name: 'title',
type: 'text',
},
],
}
it('should throw on invalid field', async () => {
const collectionConfig: CollectionConfig = {
...defaultCollection,
admin: {
useAsTitle: 'invalidField',
},
}
await expect(async () => {
await sanitizeCollection(
// @ts-expect-error
{
...config,
collections: [collectionConfig],
},
collectionConfig,
)
}).rejects.toThrow(InvalidConfiguration)
})
it('should not throw on valid field', async () => {
const collectionConfig: CollectionConfig = {
...defaultCollection,
admin: {
useAsTitle: 'title',
},
}
await expect(async () => {
await sanitizeCollection(
// @ts-expect-error
{
...config,
collections: [collectionConfig],
},
collectionConfig,
)
}).not.toThrow()
})
it('should not throw on valid field inside tabs', async () => {
const collectionConfig: CollectionConfig = {
...defaultCollection,
admin: {
useAsTitle: 'title',
},
fields: [
{
type: 'tabs',
tabs: [
{
label: 'General',
fields: [
{
name: 'title',
type: 'text',
},
],
},
],
},
],
}
await expect(async () => {
await sanitizeCollection(
// @ts-expect-error
{
...config,
collections: [collectionConfig],
},
collectionConfig,
)
}).not.toThrow()
})
it('should not throw on valid field inside collapsibles', async () => {
const collectionConfig: CollectionConfig = {
...defaultCollection,
admin: {
useAsTitle: 'title',
},
fields: [
{
type: 'collapsible',
label: 'Collapsible',
fields: [
{
name: 'title',
type: 'text',
},
],
},
],
}
await expect(async () => {
await sanitizeCollection(
// @ts-expect-error
{
...config,
collections: [collectionConfig],
},
collectionConfig,
)
}).not.toThrow()
})
it('should throw on nested useAsTitle', async () => {
const collectionConfig: CollectionConfig = {
...defaultCollection,
admin: {
useAsTitle: 'content.title',
},
}
await expect(async () => {
await sanitizeCollection(
// @ts-expect-error
{
...config,
collections: [collectionConfig],
},
collectionConfig,
)
}).rejects.toThrow(InvalidConfiguration)
})
it('should not throw on default field: id', async () => {
const collectionConfig: CollectionConfig = {
...defaultCollection,
admin: {
useAsTitle: 'id',
},
}
await expect(async () => {
await sanitizeCollection(
// @ts-expect-error
{
...config,
collections: [collectionConfig],
},
collectionConfig,
)
}).not.toThrow()
})
it('should not throw on default field: email if auth is enabled', async () => {
const collectionConfig: CollectionConfig = {
...defaultCollection,
auth: true,
admin: {
useAsTitle: 'email',
},
}
await expect(async () => {
await sanitizeCollection(
// @ts-expect-error
{
...config,
collections: [collectionConfig],
},
collectionConfig,
)
}).not.toThrow()
})
it('should throw on default field: email if auth is not enabled', async () => {
const collectionConfig: CollectionConfig = {
...defaultCollection,
admin: {
useAsTitle: 'email',
},
}
await expect(async () => {
await sanitizeCollection(
// @ts-expect-error
{
...config,
collections: [collectionConfig],
},
collectionConfig,
)
}).rejects.toThrow(InvalidConfiguration)
})
})
})

View File

@@ -0,0 +1,43 @@
import type { CollectionConfig } from '../../index.js'
import { InvalidConfiguration } from '../../errors/InvalidConfiguration.js'
import { fieldAffectsData } from '../../fields/config/types.js'
import flattenFields from '../../utilities/flattenTopLevelFields.js'
/**
* Validate useAsTitle for collections.
*/
export const validateUseAsTitle = (config: CollectionConfig) => {
if (config.admin.useAsTitle.includes('.')) {
throw new InvalidConfiguration(
`"useAsTitle" cannot be a nested field. Please specify a top-level field in the collection "${config.slug}"`,
)
}
if (config?.admin && config.admin?.useAsTitle && config.admin.useAsTitle !== 'id') {
const fields = flattenFields(config.fields)
const useAsTitleField = fields.find((field) => {
if (fieldAffectsData(field) && config.admin) {
return field.name === config.admin.useAsTitle
}
return false
})
// If auth is enabled then we don't need to
if (config.auth) {
if (config.admin.useAsTitle !== 'email') {
if (!useAsTitleField) {
throw new InvalidConfiguration(
`The field "${config.admin.useAsTitle}" specified in "admin.useAsTitle" does not exist in the collection "${config.slug}"`,
)
}
}
} else {
if (!useAsTitleField) {
throw new InvalidConfiguration(
`The field "${config.admin.useAsTitle}" specified in "admin.useAsTitle" does not exist in the collection "${config.slug}"`,
)
}
}
}
}

View File

@@ -136,8 +136,8 @@ const batchAndLoadDocs =
depth,
docID: doc.id,
draft,
fallbackLocale: req.fallbackLocale,
locale: req.locale,
fallbackLocale,
locale,
overrideAccess,
showHiddenFields,
transactionID: req.transactionID,

View File

@@ -0,0 +1,18 @@
import ObjectIdImport from 'bson-objectid'
import type { FieldHook } from '../config/types.js'
const ObjectId = (ObjectIdImport.default ||
ObjectIdImport) as unknown as typeof ObjectIdImport.default
/**
* Arrays and Blocks need to clear ids beforeDuplicate
*/
export const baseBeforeDuplicateArrays: FieldHook = ({ value }) => {
if (value) {
value = value.map((item) => {
item.id = new ObjectId().toHexString()
return item
})
return value
}
}

View File

@@ -12,6 +12,7 @@ import {
} from '../../errors/index.js'
import { MissingEditorProp } from '../../errors/MissingEditorProp.js'
import { formatLabels, toWords } from '../../utilities/formatLabels.js'
import { baseBeforeDuplicateArrays } from '../baseFields/baseBeforeDuplicateArrays.js'
import { baseBlockFields } from '../baseFields/baseBlockFields.js'
import { baseIDField } from '../baseFields/baseIDField.js'
import { setDefaultBeforeDuplicate } from '../setDefaultBeforeDuplicate.js'
@@ -130,6 +131,15 @@ export const sanitizeFields = async ({
if (field.type === 'array' && field.fields) {
field.fields.push(baseIDField)
if (field.localized) {
if (!field.hooks) {
field.hooks = {}
}
if (!field.hooks.beforeDuplicate) {
field.hooks.beforeDuplicate = []
}
field.hooks.beforeDuplicate.push(baseBeforeDuplicateArrays)
}
}
if ((field.type === 'blocks' || field.type === 'array') && field.label) {
@@ -210,6 +220,15 @@ export const sanitizeFields = async ({
}
if (field.type === 'blocks' && field.blocks) {
if (field.localized) {
if (!field.hooks) {
field.hooks = {}
}
if (!field.hooks.beforeDuplicate) {
field.hooks.beforeDuplicate = []
}
field.hooks.beforeDuplicate.push(baseBeforeDuplicateArrays)
}
for (const block of field.blocks) {
if (block._sanitized === true) {
continue

View File

@@ -7,92 +7,92 @@ import type { DeepUndefinable } from 'ts-essentials'
import type { RichTextAdapter, RichTextAdapterProvider } from '../../admin/RichText.js'
import type {
ArrayFieldClientProps,
ArrayFieldErrorClientComponent,
ArrayFieldErrorServerComponent,
ArrayFieldLabelClientComponent,
ArrayFieldLabelServerComponent,
ArrayFieldProps,
BlockFieldErrorClientComponent,
BlockFieldErrorServerComponent,
BlockFieldProps,
BlocksFieldClientProps,
CheckboxFieldClientProps,
CheckboxFieldErrorClientComponent,
CheckboxFieldErrorServerComponent,
CheckboxFieldLabelClientComponent,
CheckboxFieldLabelServerComponent,
CheckboxFieldProps,
ClientTab,
CodeFieldClientProps,
CodeFieldErrorClientComponent,
CodeFieldErrorServerComponent,
CodeFieldLabelClientComponent,
CodeFieldLabelServerComponent,
CodeFieldProps,
CollapsibleFieldClientProps,
CollapsibleFieldLabelClientComponent,
CollapsibleFieldLabelServerComponent,
CollapsibleFieldProps,
ConditionalDateProps,
DateFieldClientProps,
DateFieldErrorClientComponent,
DateFieldErrorServerComponent,
DateFieldLabelClientComponent,
DateFieldLabelServerComponent,
DateFieldProps,
Description,
EmailFieldClientProps,
EmailFieldErrorClientComponent,
EmailFieldErrorServerComponent,
EmailFieldLabelClientComponent,
EmailFieldLabelServerComponent,
EmailFieldProps,
FieldDescriptionClientComponent,
FieldDescriptionServerComponent,
GroupFieldClientProps,
GroupFieldLabelClientComponent,
GroupFieldLabelServerComponent,
GroupFieldProps,
HiddenFieldProps,
JSONFieldClientProps,
JSONFieldErrorClientComponent,
JSONFieldErrorServerComponent,
JSONFieldLabelClientComponent,
JSONFieldLabelServerComponent,
JSONFieldProps,
MappedComponent,
NumberFieldClientProps,
NumberFieldErrorClientComponent,
NumberFieldErrorServerComponent,
NumberFieldLabelClientComponent,
NumberFieldLabelServerComponent,
NumberFieldProps,
PointFieldClientProps,
PointFieldErrorClientComponent,
PointFieldErrorServerComponent,
PointFieldLabelClientComponent,
PointFieldLabelServerComponent,
PointFieldProps,
RadioFieldClientProps,
RadioFieldErrorClientComponent,
RadioFieldErrorServerComponent,
RadioFieldLabelClientComponent,
RadioFieldLabelServerComponent,
RadioFieldProps,
RelationshipFieldClientProps,
RelationshipFieldErrorClientComponent,
RelationshipFieldErrorServerComponent,
RelationshipFieldLabelClientComponent,
RelationshipFieldLabelServerComponent,
RelationshipFieldProps,
RichTextFieldProps,
RowFieldProps,
RichTextFieldClientProps,
RowFieldClientProps,
RowLabelComponent,
SelectFieldClientProps,
SelectFieldErrorClientComponent,
SelectFieldErrorServerComponent,
SelectFieldLabelClientComponent,
SelectFieldLabelServerComponent,
SelectFieldProps,
StaticDescription,
TabsFieldProps,
TabsFieldClientProps,
TextareaFieldClientProps,
TextareaFieldErrorClientComponent,
TextareaFieldErrorServerComponent,
TextareaFieldLabelClientComponent,
TextareaFieldLabelServerComponent,
TextareaFieldProps,
TextFieldErrorClientComponent,
TextFieldErrorServerComponent,
TextFieldLabelClientComponent,
TextFieldLabelServerComponent,
UploadFieldProps,
UploadFieldClientProps,
} from '../../admin/types.js'
import type { SanitizedCollectionConfig, TypeWithID } from '../../collections/config/types.js'
import type {
@@ -1425,26 +1425,26 @@ export type ClientField =
| UploadFieldClient
export type ClientFieldProps =
| ArrayFieldProps
| BlockFieldProps
| CheckboxFieldProps
| CodeFieldProps
| CollapsibleFieldProps
| DateFieldProps
| EmailFieldProps
| GroupFieldProps
| ArrayFieldClientProps
| BlocksFieldClientProps
| CheckboxFieldClientProps
| CodeFieldClientProps
| CollapsibleFieldClientProps
| DateFieldClientProps
| EmailFieldClientProps
| GroupFieldClientProps
| HiddenFieldProps
| JSONFieldProps
| NumberFieldProps
| PointFieldProps
| RadioFieldProps
| RelationshipFieldProps
| RichTextFieldProps
| RowFieldProps
| SelectFieldProps
| TabsFieldProps
| TextareaFieldProps
| UploadFieldProps
| JSONFieldClientProps
| NumberFieldClientProps
| PointFieldClientProps
| RadioFieldClientProps
| RelationshipFieldClientProps
| RichTextFieldClientProps
| RowFieldClientProps
| SelectFieldClientProps
| TabsFieldClientProps
| TextareaFieldClientProps
| UploadFieldClientProps
type ExtractFieldTypes<T> = T extends { type: infer U } ? U : never

View File

@@ -1,15 +1,12 @@
import type { Document, Where } from '../../types/index.js'
import type { PreferenceRequest } from '../types.js'
import defaultAccess from '../../auth/defaultAccess.js'
import executeAccess from '../../auth/executeAccess.js'
import { NotFound } from '../../errors/NotFound.js'
import { UnauthorizedError } from '../../errors/UnathorizedError.js'
async function deleteOperation(args: PreferenceRequest): Promise<Document> {
export async function deleteOperation(args: PreferenceRequest): Promise<Document> {
const {
key,
overrideAccess,
req: { payload },
req,
user,
@@ -19,10 +16,6 @@ async function deleteOperation(args: PreferenceRequest): Promise<Document> {
throw new UnauthorizedError(req.t)
}
if (!overrideAccess) {
await executeAccess({ req }, defaultAccess)
}
const where: Where = {
and: [
{ key: { equals: key } },
@@ -42,5 +35,3 @@ async function deleteOperation(args: PreferenceRequest): Promise<Document> {
}
throw new NotFound(req.t)
}
export default deleteOperation

View File

@@ -2,7 +2,7 @@ import type { TypedCollection } from '../../index.js'
import type { Where } from '../../types/index.js'
import type { PreferenceRequest } from '../types.js'
async function findOne(args: PreferenceRequest): Promise<TypedCollection['_preference']> {
export async function findOne(args: PreferenceRequest): Promise<TypedCollection['_preference']> {
const {
key,
req: { payload },
@@ -22,11 +22,14 @@ async function findOne(args: PreferenceRequest): Promise<TypedCollection['_prefe
],
}
return await payload.db.findOne({
const { docs } = await payload.db.find({
collection: 'payload-preferences',
limit: 1,
pagination: false,
req,
sort: '-updatedAt',
where,
})
}
export default findOne
return docs?.[0] || null
}

View File

@@ -1,13 +1,11 @@
import type { Where } from '../../types/index.js'
import type { PreferenceUpdateRequest } from '../types.js'
import defaultAccess from '../../auth/defaultAccess.js'
import executeAccess from '../../auth/executeAccess.js'
import { UnauthorizedError } from '../../errors/UnathorizedError.js'
async function update(args: PreferenceUpdateRequest) {
export async function update(args: PreferenceUpdateRequest) {
const {
key,
overrideAccess,
req: { payload },
req,
user,
@@ -20,10 +18,12 @@ async function update(args: PreferenceUpdateRequest) {
const collection = 'payload-preferences'
const filter = {
key: { equals: key },
'user.relationTo': { equals: user.collection },
'user.value': { equals: user.id },
const where: Where = {
and: [
{ key: { equals: key } },
{ 'user.value': { equals: user.id } },
{ 'user.relationTo': { equals: user.collection } },
],
}
const preference = {
@@ -35,27 +35,23 @@ async function update(args: PreferenceUpdateRequest) {
value,
}
if (!overrideAccess) {
await executeAccess({ req }, defaultAccess)
}
let result
try {
// try/catch because we attempt to update without first reading to check if it exists first to save on db calls
await payload.db.updateOne({
result = await payload.db.updateOne({
collection,
data: preference,
req,
where: filter,
where,
})
} catch (err: unknown) {
await payload.db.create({
result = await payload.db.create({
collection,
data: preference,
req,
})
}
return preference
return result
}
export default update

View File

@@ -3,7 +3,7 @@ import httpStatus from 'http-status'
import type { PayloadHandler } from '../../config/types.js'
import type { PayloadRequest } from '../../types/index.js'
import deleteOperation from '../operations/delete.js'
import { deleteOperation } from '../operations/delete.js'
export const deleteHandler: PayloadHandler = async (incomingReq): Promise<Response> => {
// We cannot import the addDataAndFileToRequest utility here from the 'next' package because of dependency issues

View File

@@ -3,7 +3,7 @@ import httpStatus from 'http-status'
import type { PayloadHandler } from '../../config/types.js'
import type { PayloadRequest } from '../../types/index.js'
import findOne from '../operations/findOne.js'
import { findOne } from '../operations/findOne.js'
export const findByIDHandler: PayloadHandler = async (incomingReq): Promise<Response> => {
// We cannot import the addDataAndFileToRequest utility here from the 'next' package because of dependency issues

View File

@@ -3,7 +3,7 @@ import httpStatus from 'http-status'
import type { PayloadHandler } from '../../config/types.js'
import type { PayloadRequest } from '../../types/index.js'
import update from '../operations/update.js'
import { update } from '../operations/update.js'
export const updateHandler: PayloadHandler = async (incomingReq) => {
// We cannot import the addDataAndFileToRequest utility here from the 'next' package because of dependency issues

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-cloud-storage",
"version": "3.0.0-beta.98",
"version": "3.0.0-beta.102",
"description": "The official cloud storage plugin for Payload CMS",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-cloud",
"version": "3.0.0-beta.98",
"version": "3.0.0-beta.102",
"description": "The official Payload Cloud plugin",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-form-builder",
"version": "3.0.0-beta.98",
"version": "3.0.0-beta.102",
"description": "Form builder plugin for Payload CMS",
"keywords": [
"payload",

View File

@@ -1,6 +1,6 @@
'use client'
import type { SelectFieldProps, SelectFieldValidation } from 'payload'
import type { SelectFieldClientProps, SelectFieldValidation } from 'payload'
import { SelectField, useForm } from '@payloadcms/ui'
import React, { useEffect, useState } from 'react'
@@ -8,7 +8,7 @@ import React, { useEffect, useState } from 'react'
import type { SelectFieldOption } from '../../types.js'
export const DynamicFieldSelector: React.FC<
{ validate: SelectFieldValidation } & SelectFieldProps
{ validate: SelectFieldValidation } & SelectFieldClientProps
> = (props) => {
const { fields, getDataByPath } = useForm()

View File

@@ -1,6 +1,6 @@
'use client'
import type { Data, TextFieldProps } from 'payload'
import type { Data, TextFieldClientComponent } from 'payload'
import { TextField, useLocale, useWatchForm } from '@payloadcms/ui'
import React, { useEffect, useState } from 'react'
@@ -10,7 +10,7 @@ type FieldWithID = {
name: string
}
export const DynamicPriceSelector: React.FC<TextFieldProps> = (props) => {
export const DynamicPriceSelector: TextFieldClientComponent = (props) => {
const { field } = props
const { fields, getData, getDataByPath } = useWatchForm()

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-nested-docs",
"version": "3.0.0-beta.98",
"version": "3.0.0-beta.102",
"description": "The official Nested Docs plugin for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-redirects",
"version": "3.0.0-beta.98",
"version": "3.0.0-beta.102",
"description": "Redirects plugin for Payload",
"keywords": [
"payload",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-relationship-object-ids",
"version": "3.0.0-beta.98",
"version": "3.0.0-beta.102",
"description": "A Payload plugin to store all relationship IDs as ObjectIDs",
"repository": {
"type": "git",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-search",
"version": "3.0.0-beta.98",
"version": "3.0.0-beta.102",
"description": "Search plugin for Payload",
"keywords": [
"payload",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-seo",
"version": "3.0.0-beta.98",
"version": "3.0.0-beta.102",
"description": "SEO plugin for Payload",
"keywords": [
"payload",

View File

@@ -1,7 +1,7 @@
'use client'
import type { FieldType, Options } from '@payloadcms/ui'
import type { TextareaFieldProps } from 'payload'
import type { TextareaFieldClientProps } from 'payload'
import {
FieldLabel,
@@ -25,7 +25,7 @@ const { maxLength, minLength } = defaults.description
type MetaDescriptionProps = {
readonly hasGenerateDescriptionFn: boolean
} & TextareaFieldProps
} & TextareaFieldClientProps
export const MetaDescriptionComponent: React.FC<MetaDescriptionProps> = (props) => {
const {

View File

@@ -1,7 +1,7 @@
'use client'
import type { FieldType, Options } from '@payloadcms/ui'
import type { UploadFieldProps } from 'payload'
import type { UploadFieldClientProps } from 'payload'
import {
FieldLabel,
@@ -23,7 +23,7 @@ import { Pill } from '../../ui/Pill.js'
type MetaImageProps = {
readonly hasGenerateImageFn: boolean
} & UploadFieldProps
} & UploadFieldClientProps
export const MetaImageComponent: React.FC<MetaImageProps> = (props) => {
const {

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