Compare commits

..

50 Commits

Author SHA1 Message Date
Kendell Joseph
008437d62f chore: adds db name 2024-07-30 11:59:48 -04:00
Kendell Joseph
95112a873b chore: uses memory server first 2024-07-30 11:59:48 -04:00
Kendell Joseph
25da9b62d1 chore: changes url preference priority 2024-07-30 11:59:48 -04:00
Kendell Joseph
1e5e07489c chore: adds build schema options 2024-07-30 11:59:48 -04:00
Kendell Joseph
bcb1d4eb57 chore: adds memory server uri as option 2024-07-30 11:59:48 -04:00
Kendell Joseph
268fff2645 chore: init test 2024-07-30 11:59:48 -04:00
Kendell Joseph
349d68c719 chore: makes arg optional 2024-07-30 11:59:11 -04:00
Kendell Joseph
9fefe79998 chore: uses default schemaOptions when using mongoose 2024-07-30 11:59:11 -04:00
Kendell Joseph
9f73743806 chore: preserves sibling data 2024-07-30 11:58:29 -04:00
Kendell Joseph
cb00bc5856 chore: implements arg for sibling document keys 2024-07-30 11:58:29 -04:00
Kendell Joseph
bd22bb28aa chore: adds arg for document keys 2024-07-30 11:58:29 -04:00
Kendell Joseph
ed1e460ae7 chore: sends adapter to models 2024-07-30 11:58:29 -04:00
Kendell Joseph
78ac4fd89e chore: uses mongoose adapter 2024-07-30 11:58:29 -04:00
Kendell Joseph
c328c42b72 chore: use mongoose adapter 2024-07-30 11:58:29 -04:00
Kendell Joseph
8ede4d0098 chore: removes unused config 2024-07-30 11:58:29 -04:00
Kendell Joseph
31ad34595e chore: uses mongoose adapter 2024-07-30 11:58:29 -04:00
Kendell Joseph
33278aa8d8 chore: updates documentation 2024-07-30 11:58:29 -04:00
Kendell Joseph
aad186c8b4 chore: adds schema options 2024-07-30 11:58:28 -04:00
Elliot DeNolf
b5b2bb1907 fix(db-postgres): proper migrations table detection query (#7436)
Fixes postgres sql query to detect migrations table.

`error: syntax error at or near "exists"`
2024-07-30 11:38:28 -04:00
Elliot DeNolf
6f5cf5d916 feat(cpa): warn on unsupported Next.js version (#7434)
Improves messaging if running an unsupported version of Next.js.

Closes #7430
2024-07-30 11:14:57 -04:00
Alessio Gravili
aaf3a39f7e chore: upgrade typescript from 5.5.3 to 5.5.4 (#7435)
TypeScript 5.5.4 apparently speeds up linting by a lot:
https://github.com/microsoft/TypeScript/issues/59101
2024-07-30 15:09:28 +00:00
Jessica Chowdhury
5ef2951829 fix: formats locales for version comparison view (#7433)
Closes #7381
2024-07-30 10:24:55 -04:00
Paul
a943487fca fix: hide force unlock button if the user has no permissions to interact with it (#7418) 2024-07-29 20:49:57 +00:00
Jarrod Flesch
3a941c7c8a chore: duplicates prev value PRs from v2 (#7414)
Updates V3 with V2 PR's
- previousVersion type https://github.com/payloadcms/payload/pull/6805
- tests from https://github.com/payloadcms/payload/pull/6805
2024-07-29 16:28:28 -04:00
Dan Ribbens
354588898f feat(db-*, payload): better transactions (#7395)
## Description

### payload
- Removes calls to beginTransaction and commitTransaction from read
operations

### db-sqlite, db-postgres
- beginTransaction() options are passed through and used to create a
transaction
- declare module type adds beginTransaction with proper transaction
config args for postgres and sqlite
2024-07-29 15:35:19 -04:00
Jessica Chowdhury
ada9978a8c fix: page param not getting reset when applying filters (#7243)
Closes #7188

In the collection list view, after adding a filter, the page number
should be reset since the doc count will have changed.

---------

Co-authored-by: Jarrod Flesch <jarrodmflesch@gmail.com>
2024-07-29 13:25:43 -04:00
Jacob Fletcher
874279c530 fix(next): infinite loop when logging into root admin (#7412) 2024-07-29 12:57:57 -04:00
Paul
7ed6634bc5 fix: types for the 'validate' property across fields so internal validation functions can be reused (#7394)
Fixes the types for validate functions so that internal validation
functions can be re-used

Currently this has a type error
```ts
validate: (value, args) => {
  return text(value, args)
},
```
2024-07-29 12:36:28 -04:00
Michel v. Varendorff
09a0ee3ab9 fix: export default was not found in graphql (#6975) 2024-07-29 11:37:58 -04:00
Lynn Dylan Hurley
67acab2cd5 fix(searchPlugin): ensure search updates are unique to collection (#6363) 2024-07-29 11:33:59 -04:00
Jarrod Flesch
b3dc6cc811 chore: extends buildConfigWithDefaults to accept options arg (#7411) 2024-07-29 11:10:46 -04:00
Jarrod Flesch
5cd0c7ec7d fix: layout preferences for array/blocks were being saved twice in dev mode (#7396)
Fixes an issue where preferences for array/block collapsible's were not
being set correctly. React strict mode surfaced this issue.
2024-07-29 09:59:05 -04:00
Elliot DeNolf
cd592cb3a2 chore(release): v3.0.0-beta.71 [skip ci] 2024-07-29 08:43:45 -04:00
Dan Ribbens
6d066c2ba4 fix(db-sqlite): migration template errors (#7404)
- Fix migration template for sqlite
- Add declare for payload.db.drizzle as type LibSQLDatabase
- Correct drizzle snapshot version
2024-07-27 22:10:09 -04:00
Dan Ribbens
1dc428823a fix(db-postgres): migration template type error (#7403)
Fixes #7402

This fixes a regression from changes to the postgres migration template
that were incorrect. It also fixes other type errors for
`payload.db.drizzle` which needed to be declared for postgres to avoid
confusing it with Libsql for SQLite.
2024-07-27 21:52:36 -04:00
James Mikrut
c8da9b148c fix: merges headers safely in nextjs route handlers (#7399)
## Description

Merges headers safely within Payload-handled Next.js route handlers.
2024-07-27 16:34:06 +00:00
Jacob Fletcher
2021028d64 fix(ui): stacking drawers (#7397) 2024-07-27 09:33:31 -04:00
Paul
2ea56fe0f8 fix(docs): update import path for validation functions for fields (#7392) 2024-07-26 18:32:58 +00:00
Jacob Fletcher
ea16119af7 chore(cpa): replaces missing type assertion (#7391) 2024-07-26 14:13:46 -04:00
Jacob Fletcher
97837f0708 feat(ui)!: passes field props to custom components (#7360)
## Description

Currently, there is no way to read field props from within a custom
field component, i.e. `admin.components.Description`. For example, if
you set `maxLength: 100` on your field, your custom description
component cannot read it from `props.maxLength` or any other methods.
Because these components are rendered on the server, there is also no
way of using `admin.component.Field` to inject custom props yourself,
either. To support this, we can simply pass the base component props
into these components on the server, as expected. This has also led to
custom field component props becoming more strictly typed within the
config.

This change is considered breaking only because the types have changed.
This only affects you if you were previously importing the following
types into your own custom components. To migrate, simply change the
import paths for that type.

Old:
```ts
import type {
  ArrayFieldProps,
  ReducedBlock,
  BlocksFieldProps,
  CheckboxFieldProps,
  CodeFieldProps,
  CollapsibleFieldProps,
  DateFieldProps,
  EmailFieldProps,
  GroupFieldProps,
  HiddenFieldProps,
  JSONFieldProps,
  NumberFieldProps,
  PointFieldProps,
  RadioFieldProps,
  RelationshipFieldProps,
  RichTextComponentProps,
  RowFieldProps,
  SelectFieldProps,
  TabsFieldProps,
  TextFieldProps,
  TextareaFieldProps,
  UploadFieldProps,
  ErrorProps,
  FormFieldBase, 
  FieldComponentProps,
  FieldMap,
  MappedField,
  MappedTab,
  ReducedBlock,
} from '@payloadcms/ui'
```

New:
```ts
import type {
  FormFieldBase, 
  // etc.
} from 'payload'
```

Custom field components are now much more strongly typed. To make this
happen, an explicit type for every custom component has been generated
for every field type. The convention is to append
`DescriptionComponent`, `LabelComponent`, and `ErrorComponent` onto the
end of the field name, i.e. `TextFieldDescriptionComponent`. Here's an
example:

```ts
import type { TextFieldDescriptionComponent } from 'payload'

import React from 'react'

export const CustomDescription: TextFieldDescriptionComponent = (props) => {
  return (
    <div id="custom-field-description">{`The max length of this field is: ${props?.maxLength}`}</div>
  )
}
```

Here's the full list of all new types:

Label Components:

```ts
import type {
  ArrayFieldLabelComponent,
  BlocksFieldLabelComponent,
  CheckboxFieldLabelComponent,
  CodeFieldLabelComponent,
  CollapsibleFieldLabelComponent,
  DateFieldLabelComponent,
  EmailFieldLabelComponent,
  GroupFieldLabelComponent,
  HiddenFieldLabelComponent,
  JSONFieldLabelComponent,
  NumberFieldLabelComponent,
  PointFieldLabelComponent,
  RadioFieldLabelComponent,
  RelationshipFieldLabelComponent,
  RichTextFieldLabelComponent,
  RowFieldLabelComponent,
  SelectFieldLabelComponent,
  TabsFieldLabelComponent,
  TextFieldLabelComponent,
  TextareaFieldLabelComponent,
  UploadFieldLabelComponent
} from 'payload'
```

Error Components:

```tsx
import type {
  ArrayFieldErrorComponent,
  BlocksFieldErrorComponent,
  CheckboxFieldErrorComponent,
  CodeFieldErrorComponent,
  CollapsibleFieldErrorComponent,
  DateFieldErrorComponent,
  EmailFieldErrorComponent,
  GroupFieldErrorComponent,
  HiddenFieldErrorComponent,
  JSONFieldErrorComponent,
  NumberFieldErrorComponent,
  PointFieldErrorComponent,
  RadioFieldErrorComponent,
  RelationshipFieldErrorComponent,
  RichTextFieldErrorComponent,
  RowFieldErrorComponent,
  SelectFieldErrorComponent,
  TabsFieldErrorComponent,
  TextFieldErrorComponent,
  TextareaFieldErrorComponent,
  UploadFieldErrorComponent
} from 'payload'
```

Description Components:

```tsx
import type {
  ArrayFieldDescriptionComponent,
  BlocksFieldDescriptionComponent,
  CheckboxFieldDescriptionComponent,
  CodeFieldDescriptionComponent,
  CollapsibleFieldDescriptionComponent,
  DateFieldDescriptionComponent,
  EmailFieldDescriptionComponent,
  GroupFieldDescriptionComponent,
  HiddenFieldDescriptionComponent,
  JSONFieldDescriptionComponent,
  NumberFieldDescriptionComponent,
  PointFieldDescriptionComponent,
  RadioFieldDescriptionComponent,
  RelationshipFieldDescriptionComponent,
  RichTextFieldDescriptionComponent,
  RowFieldDescriptionComponent,
  SelectFieldDescriptionComponent,
  TabsFieldDescriptionComponent,
  TextFieldDescriptionComponent,
  TextareaFieldDescriptionComponent,
  UploadFieldDescriptionComponent
} from 'payload'
```

This PR also:
- Standardizes the `FieldBase['label']` type with a new `LabelStatic`
type. This makes type usage much more consistent across components.
- Simplifies some of the typings in the field component map, removes
unneeded `<Omit>`, etc.
- Fixes misc. linting issues around voiding promises

- [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)

## 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-07-26 14:03:25 -04:00
Paul
e734d51760 chore(ui)!: update the names of internal components so that they respect eslint rules (#7362)
So `_Upload` becomes `UploadComponent` which doesnt break the naming
convention of react components and **we no longer export these internal
components**
2024-07-26 17:50:23 +00:00
Patrik
5655266daa fix(ui): handle abort() call signal error (#7390)
## Description

Swallows `.abort()` call signal errors

- [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-07-26 13:16:22 -04:00
James Mikrut
f9e5573c1e feat: adds keepAfterRead to plugin-relationship-objectid (#7388)
## Description

Duplicate of
https://github.com/payloadcms/plugin-relationship-object-ids/pull/6 for
3.x
2024-07-26 15:39:39 +00:00
Paul
e823051a8e fix(ui): spacing in row fields by using gap instead of inner margins (#7387) 2024-07-26 15:34:30 +00:00
Elliot DeNolf
49df61d9ec chore(release): v3.0.0-beta.70 [skip ci] 2024-07-26 11:16:04 -04:00
Paul
4704c8db2a feat(templates): add live preview breakpoints for mobile, tablet and desktop to website template (#7384) 2024-07-26 14:41:59 +00:00
Elliot DeNolf
a64f37e014 feat(cpa): support next.config.ts (#7367)
Support new `next.config.ts` config file.

Had to do some weird gymnastics around `swc` in order to use it within
unit tests. Had to pass through the `parsed.span.end` value of any
previous iteration and account for it.

Looks to be an open issue here:
https://github.com/swc-project/swc/issues/1366

Fixes #7318
2024-07-26 10:33:46 -04:00
Elliot DeNolf
55c6ce92b0 fix(db-postgres): properly reference drizzle createTableName function (#7383)
Fix incorrect relative import of drizzle package's `createTableName`
function. Now uses proper package import.

Fixes #7373
2024-07-26 10:08:31 -04:00
Elliot DeNolf
2ecbcee378 chore(release): v3.0.0-beta.69 [skip ci] 2024-07-25 22:17:04 -04:00
James Mikrut
70f2e1698a fix: filterOptions for upload fields (#7347)
## Description

Fixes uploads `filterOptions` not being respected in the Payload admin
UI.

Needs a test written, fixes to types in build, as well as any tests that
fail due to this change in CI.

- [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-07-25 21:42:36 +00:00
316 changed files with 3986 additions and 3106 deletions

View File

@@ -38,6 +38,7 @@ jobs:
db-\*
db-mongodb
db-postgres
db-sqlite
email-nodemailer
eslint
graphql

View File

@@ -266,12 +266,10 @@ export const myField: Field = {
_For details on how to build Custom Components, see [Building Custom Components](./components#building-custom-components)._
All Label Components receive the following props:
Custom Label Components receive all [Field Component](#the-field-component) props, plus the following props:
| Property | Description |
| -------------- | ---------------------------------------------------------------- |
| **`label`** | Label value provided in field, it can be used with i18n. |
| **`required`** | The `admin.required` property defined in the [Field Config](../fields/overview). |
| **`schemaPath`** | The path to the field in the schema. Similar to `path`, but without dynamic indices. |
<Banner type="success">
@@ -279,6 +277,36 @@ All Label Components receive the following props:
All [Custom Server Components](./components) receive the `payload` and `i18n` properties by default. See [Building Custom Components](./components#building-custom-components) for more details.
</Banner>
#### TypeScript
When building Custom Error Components, you can import the component props to ensure type safety in your component. There is an explicit type for the Error Component, one for every [Field Type](../fields/overview). The convention is to append `ErrorComponent` to the type of field, i.e. `TextFieldErrorComponent`.
```tsx
import type {
ArrayFieldLabelComponent,
BlocksFieldLabelComponent,
CheckboxFieldLabelComponent,
CodeFieldLabelComponent,
CollapsibleFieldLabelComponent,
DateFieldLabelComponent,
EmailFieldLabelComponent,
GroupFieldLabelComponent,
HiddenFieldLabelComponent,
JSONFieldLabelComponent,
NumberFieldLabelComponent,
PointFieldLabelComponent,
RadioFieldLabelComponent,
RelationshipFieldLabelComponent,
RichTextFieldLabelComponent,
RowFieldLabelComponent,
SelectFieldLabelComponent,
TabsFieldLabelComponent,
TextFieldLabelComponent,
TextareaFieldLabelComponent,
UploadFieldLabelComponent
} from 'payload'
```
### The Error Component
The Error Component is rendered when a field fails validation. It is typically displayed beneath the field input in a visually-compelling style.
@@ -301,7 +329,7 @@ export const myField: Field = {
_For details on how to build Custom Components, see [Building Custom Components](./components#building-custom-components)._
All Error Components receive the following props:
Custom Error Components receive all [Field Component](#the-field-component) props, plus the following props:
| Property | Description |
| --------------- | ------------------------------------------------------------- |
@@ -312,6 +340,36 @@ All Error Components receive the following props:
All [Custom Server Components](./components) receive the `payload` and `i18n` properties by default. See [Building Custom Components](./components#building-custom-components) for more details.
</Banner>
#### TypeScript
When building Custom Error Components, you can import the component props to ensure type safety in your component. There is an explicit type for the Error Component, one for every [Field Type](../fields/overview). The convention is to append `ErrorComponent` to the type of field, i.e. `TextFieldErrorComponent`.
```tsx
import type {
ArrayFieldErrorComponent,
BlocksFieldErrorComponent,
CheckboxFieldErrorComponent,
CodeFieldErrorComponent,
CollapsibleFieldErrorComponent,
DateFieldErrorComponent,
EmailFieldErrorComponent,
GroupFieldErrorComponent,
HiddenFieldErrorComponent,
JSONFieldErrorComponent,
NumberFieldErrorComponent,
PointFieldErrorComponent,
RadioFieldErrorComponent,
RelationshipFieldErrorComponent,
RichTextFieldErrorComponent,
RowFieldErrorComponent,
SelectFieldErrorComponent,
TabsFieldErrorComponent,
TextFieldErrorComponent,
TextareaFieldErrorComponent,
UploadFieldErrorComponent
} from 'payload'
```
### The Description Property
Field Descriptions are used to provide additional information to the editor about a field, such as special instructions. Their placement varies from field to field, but typically are displayed with subtle style differences beneath the field inputs.
@@ -406,7 +464,7 @@ export const MyCollectionConfig: SanitizedCollectionConfig = {
_For details on how to build a Custom Description, see [Building Custom Components](./components#building-custom-components)._
All Description Components receive the following props:
Custom Description Components receive all [Field Component](#the-field-component) props, plus the following props:
| Property | Description |
| -------------- | ---------------------------------------------------------------- |
@@ -417,6 +475,36 @@ All Description Components receive the following props:
All [Custom Server Components](./components) receive the `payload` and `i18n` properties by default. See [Building Custom Components](./components#building-custom-components) for more details.
</Banner>
#### TypeScript
When building Custom Description Components, you can import the component props to ensure type safety in your component. There is an explicit type for the Description Component, one for every [Field Type](../fields/overview). The convention is to append `DescriptionComponent` to the type of field, i.e. `TextFieldDescriptionComponent`.
```tsx
import type {
ArrayFieldDescriptionComponent,
BlocksFieldDescriptionComponent,
CheckboxFieldDescriptionComponent,
CodeFieldDescriptionComponent,
CollapsibleFieldDescriptionComponent,
DateFieldDescriptionComponent,
EmailFieldDescriptionComponent,
GroupFieldDescriptionComponent,
HiddenFieldDescriptionComponent,
JSONFieldDescriptionComponent,
NumberFieldDescriptionComponent,
PointFieldDescriptionComponent,
RadioFieldDescriptionComponent,
RelationshipFieldDescriptionComponent,
RichTextFieldDescriptionComponent,
RowFieldDescriptionComponent,
SelectFieldDescriptionComponent,
TabsFieldDescriptionComponent,
TextFieldDescriptionComponent,
TextareaFieldDescriptionComponent,
UploadFieldDescriptionComponent
} from 'payload'
```
### afterInput and beforeInput
With these properties you can add multiple components _before_ and _after_ the input element, as their name suggests. This is useful when you need to render additional elements alongside the field without replacing the entire field component.

View File

@@ -33,6 +33,9 @@ export default buildConfig({
| Option | Description |
| -------------------- | ----------- |
| `autoPluralization` | Tell Mongoose to auto-pluralize any collection names if it encounters any singular words used as collection `slug`s. |
| `schemaOptions` | Customize schema options for all Mongoose schemas created internally. |
| `collections` | Options on a collection-by-collection basis. [More](#collections-options) |
| `globals` | Options for the Globals collection created by Payload. [More](#globals-options) |
| `connectOptions` | Customize MongoDB connection options. Payload will connect to your MongoDB database using default options which you can override and extend to include all the [options](https://mongoosejs.com/docs/connections.html#options) available to mongoose. |
| `disableIndexHints` | Set to true to disable hinting to MongoDB to use 'id' as index. This is currently done when counting documents for pagination, as it increases the speed of the count function used in that query. Disabling this optimization might fix some problems with AWS DocumentDB. Defaults to false |
| `migrationDir` | Customize the directory that migrations are stored. |
@@ -49,3 +52,27 @@ You can access Mongoose models as follows:
- Collection models - `payload.db.collections[myCollectionSlug]`
- Globals model - `payload.db.globals`
- Versions model (both collections and globals) - `payload.db.versions[myEntitySlug]`
### Collections Options
You can configure the way the MongoDB adapter works on a collection-by-collection basis, including customizing Mongoose `schemaOptions` for each collection schema created.
Example:
```ts
const db = mongooseAdapter({
url: 'your-url-here',
collections: {
users: {
//
schemaOptions: {
strict: false,
}
}
}
})
```
### Global Options
Payload automatically creates a single `globals` collection that correspond with any Payload globals that you define. When you initialize the `mongooseAdapter`, you can specify settings here for your globals in a similar manner to how you can for collections above. Right now, the only property available is `schemaOptions` but more may be added in the future.

View File

@@ -294,7 +294,7 @@ When using custom validation functions, Payload will use yours in place of the d
To reuse default field validations, call them from within your custom validation function:
```ts
import { text } from 'payload/fields/validations'
import { text } from 'payload/shared'
const field: Field = {
name: 'notBad',

View File

@@ -193,7 +193,7 @@ You can learn more about writing queries [here](/docs/queries/overview).
When a relationship field has both <strong>filterOptions</strong> and a custom{' '}
<strong>validate</strong> function, the api will not validate <strong>filterOptions</strong>{' '}
unless you call the default relationship field validation function imported from{' '}
<strong>payload/fields/validations</strong> in your validate function.
<strong>payload/shared</strong> in your validate function.
</Banner>
## How the data is saved

View File

@@ -123,5 +123,5 @@ You can learn more about writing queries [here](/docs/queries/overview).
When an upload field has both <strong>filterOptions</strong> and a custom{' '}
<strong>validate</strong> function, the api will not validate <strong>filterOptions</strong>{' '}
unless you call the default upload field validation function imported from{' '}
<strong>payload/fields/validations</strong> in your validate function.
<strong>payload/shared</strong> in your validate function.
</Banner>

View File

@@ -335,7 +335,6 @@ import {
FieldMap,
File,
Form,
FormFieldBase,
FormLoadingOverlayToggle,
FormSubmit,
GenerateConfirmation,

View File

@@ -1,6 +1,6 @@
{
"name": "payload-monorepo",
"version": "3.0.0-beta.68",
"version": "3.0.0-beta.71",
"private": true,
"type": "module",
"scripts": {
@@ -150,7 +150,7 @@
"tempy": "1.0.1",
"tsx": "4.16.2",
"turbo": "^1.13.3",
"typescript": "5.5.3"
"typescript": "5.5.4"
},
"peerDependencies": {
"react": "^19.0.0 || ^19.0.0-rc-6230622a1a-20240610",

View File

@@ -1,6 +1,6 @@
{
"name": "create-payload-app",
"version": "3.0.0-beta.68",
"version": "3.0.0-beta.71",
"homepage": "https://payloadcms.com",
"repository": {
"type": "git",
@@ -50,6 +50,7 @@
"dependencies": {
"@clack/prompts": "^0.7.0",
"@sindresorhus/slugify": "^1.1.0",
"@swc/core": "^1.6.13",
"arg": "^5.0.0",
"chalk": "^4.1.0",
"comment-json": "^4.2.3",

View File

@@ -79,7 +79,7 @@ export async function initNext(args: InitNextArgs): Promise<InitNextResult> {
const installSpinner = p.spinner()
installSpinner.start('Installing Payload and dependencies...')
const configurationResult = installAndConfigurePayload({
const configurationResult = await installAndConfigurePayload({
...args,
nextAppDetails,
nextConfigType,
@@ -143,15 +143,16 @@ async function addPayloadConfigToTsConfig(projectDir: string, isSrcDir: boolean)
}
}
function installAndConfigurePayload(
async function installAndConfigurePayload(
args: {
nextAppDetails: NextAppDetails
nextConfigType: NextConfigType
useDistFiles?: boolean
} & InitNextArgs,
):
): Promise<
| { payloadConfigPath: string; success: true }
| { payloadConfigPath?: string; reason: string; success: false } {
| { payloadConfigPath?: string; reason: string; success: false }
> {
const {
'--debug': debug,
nextAppDetails: { isSrcDir, nextAppDir, nextConfigPath } = {},
@@ -212,7 +213,7 @@ function installAndConfigurePayload(
copyRecursiveSync(templateSrcDir, path.dirname(nextConfigPath), debug)
// Wrap next.config.js with withPayload
wrapNextConfig({ nextConfigPath, nextConfigType })
await wrapNextConfig({ nextConfigPath, nextConfigType })
return {
payloadConfigPath: path.resolve(nextAppDir, '../payload.config.ts'),
@@ -239,25 +240,63 @@ async function installDeps(projectDir: string, packageManager: PackageManager, d
export async function getNextAppDetails(projectDir: string): Promise<NextAppDetails> {
const isSrcDir = fs.existsSync(path.resolve(projectDir, 'src'))
// Match next.config.js, next.config.ts, next.config.mjs, next.config.cjs
const nextConfigPath: string | undefined = (
await globby('next.config.*js', { absolute: true, cwd: projectDir })
await globby('next.config.(\\w)?(t|j)s', { absolute: true, cwd: projectDir })
)?.[0]
if (!nextConfigPath || nextConfigPath.length === 0) {
return {
hasTopLevelLayout: false,
isSrcDir,
isSupportedNextVersion: false,
nextConfigPath: undefined,
nextVersion: null,
}
}
const packageObj = await fse.readJson(path.resolve(projectDir, 'package.json'))
// Check if Next.js version is new enough
let nextVersion = null
if (packageObj.dependencies?.next) {
nextVersion = packageObj.dependencies.next
// Match versions using regex matching groups
const versionMatch = /(?<major>\d+)/.exec(nextVersion)
if (!versionMatch) {
p.log.warn(`Could not determine Next.js version from ${nextVersion}`)
return {
hasTopLevelLayout: false,
isSrcDir,
isSupportedNextVersion: false,
nextConfigPath,
nextVersion,
}
}
const { major } = versionMatch.groups as { major: string }
const majorVersion = parseInt(major)
if (majorVersion < 15) {
return {
hasTopLevelLayout: false,
isSrcDir,
isSupportedNextVersion: false,
nextConfigPath,
nextVersion,
}
}
}
const isSupportedNextVersion = true
// Check if Payload already installed
if (packageObj.dependencies?.payload) {
return {
hasTopLevelLayout: false,
isPayloadInstalled: true,
isSrcDir,
isSupportedNextVersion,
nextConfigPath,
nextVersion,
}
}
@@ -280,14 +319,27 @@ export async function getNextAppDetails(projectDir: string): Promise<NextAppDeta
? fs.existsSync(path.resolve(nextAppDir, 'layout.tsx'))
: false
return { hasTopLevelLayout, isSrcDir, nextAppDir, nextConfigPath, nextConfigType: configType }
return {
hasTopLevelLayout,
isSrcDir,
isSupportedNextVersion,
nextAppDir,
nextConfigPath,
nextConfigType: configType,
nextVersion,
}
}
function getProjectType(args: {
nextConfigPath: string
packageObj: Record<string, unknown>
}): 'cjs' | 'esm' {
}): NextConfigType {
const { nextConfigPath, packageObj } = args
if (nextConfigPath.endsWith('.ts')) {
return 'ts'
}
if (nextConfigPath.endsWith('.mjs')) {
return 'esm'
}

View File

@@ -3,6 +3,35 @@ import { jest } from '@jest/globals'
import { parseAndModifyConfigContent, withPayloadStatement } from './wrap-next-config.js'
const tsConfigs = {
defaultNextConfig: `import type { NextConfig } from "next";
const nextConfig: NextConfig = {};
export default nextConfig;`,
nextConfigExportNamedDefault: `import type { NextConfig } from "next";
const nextConfig: NextConfig = {};
const wrapped = someFunc(asdf);
export { wrapped as default };
`,
nextConfigWithFunc: `import type { NextConfig } from "next";
const nextConfig: NextConfig = {};
export default someFunc(nextConfig);
`,
nextConfigWithFuncMultiline: `import type { NextConfig } from "next";
const nextConfig: NextConfig = {};
export default someFunc(
nextConfig
);
`,
nextConfigWithSpread: `import type { NextConfig } from "next";
const nextConfig: NextConfig = {
...someConfig,
};
export default nextConfig;
`,
}
const esmConfigs = {
defaultNextConfig: `/** @type {import('next').NextConfig} */
const nextConfig = {};
@@ -52,27 +81,66 @@ module.exports = nextConfig;
}
describe('parseAndInsertWithPayload', () => {
describe('ts', () => {
const configType = 'ts'
const importStatement = withPayloadStatement[configType]
it('should parse the default next config', async () => {
const { modifiedConfigContent } = await parseAndModifyConfigContent(
tsConfigs.defaultNextConfig,
configType,
)
expect(modifiedConfigContent).toContain(importStatement)
expect(modifiedConfigContent).toContain('withPayload(nextConfig)')
})
it('should parse the config with a function', async () => {
const { modifiedConfigContent: modifiedConfigContent2 } = await parseAndModifyConfigContent(
tsConfigs.nextConfigWithFunc,
configType,
)
expect(modifiedConfigContent2).toContain('withPayload(someFunc(nextConfig))')
})
it('should parse the config with a multi-lined function', async () => {
const { modifiedConfigContent } = await parseAndModifyConfigContent(
tsConfigs.nextConfigWithFuncMultiline,
configType,
)
expect(modifiedConfigContent).toContain(importStatement)
expect(modifiedConfigContent).toMatch(/withPayload\(someFunc\(\n {2}nextConfig\n\)\)/)
})
it('should parse the config with a spread', async () => {
const { modifiedConfigContent } = await parseAndModifyConfigContent(
tsConfigs.nextConfigWithSpread,
configType,
)
expect(modifiedConfigContent).toContain(importStatement)
expect(modifiedConfigContent).toContain('withPayload(nextConfig)')
})
})
describe('esm', () => {
const configType = 'esm'
const importStatement = withPayloadStatement[configType]
it('should parse the default next config', () => {
const { modifiedConfigContent } = parseAndModifyConfigContent(
it('should parse the default next config', async () => {
const { modifiedConfigContent } = await parseAndModifyConfigContent(
esmConfigs.defaultNextConfig,
configType,
)
expect(modifiedConfigContent).toContain(importStatement)
expect(modifiedConfigContent).toContain('withPayload(nextConfig)')
})
it('should parse the config with a function', () => {
const { modifiedConfigContent } = parseAndModifyConfigContent(
it('should parse the config with a function', async () => {
const { modifiedConfigContent } = await parseAndModifyConfigContent(
esmConfigs.nextConfigWithFunc,
configType,
)
expect(modifiedConfigContent).toContain('withPayload(someFunc(nextConfig))')
})
it('should parse the config with a function on a new line', () => {
const { modifiedConfigContent } = parseAndModifyConfigContent(
it('should parse the config with a multi-lined function', async () => {
const { modifiedConfigContent } = await parseAndModifyConfigContent(
esmConfigs.nextConfigWithFuncMultiline,
configType,
)
@@ -80,8 +148,8 @@ describe('parseAndInsertWithPayload', () => {
expect(modifiedConfigContent).toMatch(/withPayload\(someFunc\(\n {2}nextConfig\n\)\)/)
})
it('should parse the config with a spread', () => {
const { modifiedConfigContent } = parseAndModifyConfigContent(
it('should parse the config with a spread', async () => {
const { modifiedConfigContent } = await parseAndModifyConfigContent(
esmConfigs.nextConfigWithSpread,
configType,
)
@@ -90,10 +158,10 @@ describe('parseAndInsertWithPayload', () => {
})
// Unsupported: export { wrapped as default }
it('should give warning with a named export as default', () => {
it('should give warning with a named export as default', async () => {
const warnLogSpy = jest.spyOn(p.log, 'warn').mockImplementation(() => {})
const { modifiedConfigContent, success } = parseAndModifyConfigContent(
const { modifiedConfigContent, success } = await parseAndModifyConfigContent(
esmConfigs.nextConfigExportNamedDefault,
configType,
)
@@ -109,39 +177,39 @@ describe('parseAndInsertWithPayload', () => {
describe('cjs', () => {
const configType = 'cjs'
const requireStatement = withPayloadStatement[configType]
it('should parse the default next config', () => {
const { modifiedConfigContent } = parseAndModifyConfigContent(
it('should parse the default next config', async () => {
const { modifiedConfigContent } = await parseAndModifyConfigContent(
cjsConfigs.defaultNextConfig,
configType,
)
expect(modifiedConfigContent).toContain(requireStatement)
expect(modifiedConfigContent).toContain('withPayload(nextConfig)')
})
it('should parse anonymous default config', () => {
const { modifiedConfigContent } = parseAndModifyConfigContent(
it('should parse anonymous default config', async () => {
const { modifiedConfigContent } = await parseAndModifyConfigContent(
cjsConfigs.anonConfig,
configType,
)
expect(modifiedConfigContent).toContain(requireStatement)
expect(modifiedConfigContent).toContain('withPayload({})')
})
it('should parse the config with a function', () => {
const { modifiedConfigContent } = parseAndModifyConfigContent(
it('should parse the config with a function', async () => {
const { modifiedConfigContent } = await parseAndModifyConfigContent(
cjsConfigs.nextConfigWithFunc,
configType,
)
expect(modifiedConfigContent).toContain('withPayload(someFunc(nextConfig))')
})
it('should parse the config with a function on a new line', () => {
const { modifiedConfigContent } = parseAndModifyConfigContent(
it('should parse the config with a multi-lined function', async () => {
const { modifiedConfigContent } = await parseAndModifyConfigContent(
cjsConfigs.nextConfigWithFuncMultiline,
configType,
)
expect(modifiedConfigContent).toContain(requireStatement)
expect(modifiedConfigContent).toMatch(/withPayload\(someFunc\(\n {2}nextConfig\n\)\)/)
})
it('should parse the config with a named export as default', () => {
const { modifiedConfigContent } = parseAndModifyConfigContent(
it('should parse the config with a named export as default', async () => {
const { modifiedConfigContent } = await parseAndModifyConfigContent(
cjsConfigs.nextConfigExportNamedDefault,
configType,
)
@@ -149,8 +217,8 @@ describe('parseAndInsertWithPayload', () => {
expect(modifiedConfigContent).toContain('withPayload(wrapped)')
})
it('should parse the config with a spread', () => {
const { modifiedConfigContent } = parseAndModifyConfigContent(
it('should parse the config with a spread', async () => {
const { modifiedConfigContent } = await parseAndModifyConfigContent(
cjsConfigs.nextConfigWithSpread,
configType,
)

View File

@@ -1,25 +1,27 @@
import type { Program } from 'esprima-next'
import type { ExportDefaultExpression, ModuleItem } from '@swc/core'
import swc from '@swc/core'
import chalk from 'chalk'
import { Syntax, parseModule } from 'esprima-next'
import fs from 'fs'
import type { NextConfigType } from '../types.js'
import { log, warning } from '../utils/log.js'
export const withPayloadStatement = {
cjs: `const { withPayload } = require('@payloadcms/next/withPayload')\n`,
esm: `import { withPayload } from '@payloadcms/next/withPayload'\n`,
cjs: `const { withPayload } = require("@payloadcms/next/withPayload");`,
esm: `import { withPayload } from "@payloadcms/next/withPayload";`,
ts: `import { withPayload } from "@payloadcms/next/withPayload";`,
}
type NextConfigType = 'cjs' | 'esm'
export const wrapNextConfig = (args: {
export const wrapNextConfig = async (args: {
nextConfigPath: string
nextConfigType: NextConfigType
}) => {
const { nextConfigPath, nextConfigType: configType } = args
const configContent = fs.readFileSync(nextConfigPath, 'utf8')
const { modifiedConfigContent: newConfig, success } = parseAndModifyConfigContent(
const { modifiedConfigContent: newConfig, success } = await parseAndModifyConfigContent(
configContent,
configType,
)
@@ -34,113 +36,142 @@ export const wrapNextConfig = (args: {
/**
* Parses config content with AST and wraps it with withPayload function
*/
export function parseAndModifyConfigContent(
export async function parseAndModifyConfigContent(
content: string,
configType: NextConfigType,
): { modifiedConfigContent: string; success: boolean } {
content = withPayloadStatement[configType] + content
): Promise<{ modifiedConfigContent: string; success: boolean }> {
content = withPayloadStatement[configType] + '\n' + content
let ast: Program | undefined
try {
ast = parseModule(content, { loc: true })
} catch (error: unknown) {
if (error instanceof Error) {
warning(`Unable to parse Next config. Error: ${error.message} `)
warnUserWrapNotSuccessful(configType)
}
return {
modifiedConfigContent: content,
success: false,
}
}
console.log({ configType, content })
if (configType === 'esm') {
const exportDefaultDeclaration = ast.body.find(
(p) => p.type === Syntax.ExportDefaultDeclaration,
) as Directive | undefined
if (configType === 'cjs' || configType === 'esm') {
try {
const ast = parseModule(content, { loc: true })
const exportNamedDeclaration = ast.body.find(
(p) => p.type === Syntax.ExportNamedDeclaration,
) as ExportNamedDeclaration | undefined
if (configType === 'cjs') {
// Find `module.exports = X`
const moduleExports = ast.body.find(
(p) =>
p.type === Syntax.ExpressionStatement &&
p.expression?.type === Syntax.AssignmentExpression &&
p.expression.left?.type === Syntax.MemberExpression &&
p.expression.left.object?.type === Syntax.Identifier &&
p.expression.left.object.name === 'module' &&
p.expression.left.property?.type === Syntax.Identifier &&
p.expression.left.property.name === 'exports',
// eslint-disable-next-line @typescript-eslint/no-explicit-any
) as any
if (!exportDefaultDeclaration && !exportNamedDeclaration) {
throw new Error('Could not find ExportDefaultDeclaration in next.config.js')
}
if (moduleExports && moduleExports.expression.right?.loc) {
const modifiedConfigContent = insertBeforeAndAfter(
content,
moduleExports.expression.right.loc,
)
return { modifiedConfigContent, success: true }
}
if (exportDefaultDeclaration && exportDefaultDeclaration.declaration?.loc) {
const modifiedConfigContent = insertBeforeAndAfter(
content,
exportDefaultDeclaration.declaration.loc,
)
return { modifiedConfigContent, success: true }
} else if (exportNamedDeclaration) {
const exportSpecifier = exportNamedDeclaration.specifiers.find(
(s) =>
s.type === 'ExportSpecifier' &&
s.exported?.name === 'default' &&
s.local?.type === 'Identifier' &&
s.local?.name,
)
if (exportSpecifier) {
warning('Could not automatically wrap next.config.js with withPayload.')
warning('Automatic wrapping of named exports as default not supported yet.')
warnUserWrapNotSuccessful(configType)
return {
return Promise.resolve({
modifiedConfigContent: content,
success: false,
})
} else if (configType === 'esm') {
const exportDefaultDeclaration = ast.body.find(
(p) => p.type === Syntax.ExportDefaultDeclaration,
) as Directive | undefined
const exportNamedDeclaration = ast.body.find(
(p) => p.type === Syntax.ExportNamedDeclaration,
) as ExportNamedDeclaration | undefined
if (!exportDefaultDeclaration && !exportNamedDeclaration) {
throw new Error('Could not find ExportDefaultDeclaration in next.config.js')
}
if (exportDefaultDeclaration && exportDefaultDeclaration.declaration?.loc) {
const modifiedConfigContent = insertBeforeAndAfter(
content,
exportDefaultDeclaration.declaration.loc,
)
return { modifiedConfigContent, success: true }
} else if (exportNamedDeclaration) {
const exportSpecifier = exportNamedDeclaration.specifiers.find(
(s) =>
s.type === 'ExportSpecifier' &&
s.exported?.name === 'default' &&
s.local?.type === 'Identifier' &&
s.local?.name,
)
if (exportSpecifier) {
warning('Could not automatically wrap next.config.js with withPayload.')
warning('Automatic wrapping of named exports as default not supported yet.')
warnUserWrapNotSuccessful(configType)
return {
modifiedConfigContent: content,
success: false,
}
}
}
warning('Could not automatically wrap Next config with withPayload.')
warnUserWrapNotSuccessful(configType)
return Promise.resolve({
modifiedConfigContent: content,
success: false,
})
}
} catch (error: unknown) {
if (error instanceof Error) {
warning(`Unable to parse Next config. Error: ${error.message} `)
warnUserWrapNotSuccessful(configType)
}
return {
modifiedConfigContent: content,
success: false,
}
}
} else if (configType === 'ts') {
const { moduleItems, parseOffset } = await compileTypeScriptFileToAST(content)
warning('Could not automatically wrap Next config with withPayload.')
warnUserWrapNotSuccessful(configType)
return {
modifiedConfigContent: content,
success: false,
}
} else if (configType === 'cjs') {
// Find `module.exports = X`
const moduleExports = ast.body.find(
(p) =>
p.type === Syntax.ExpressionStatement &&
p.expression?.type === Syntax.AssignmentExpression &&
p.expression.left?.type === Syntax.MemberExpression &&
p.expression.left.object?.type === Syntax.Identifier &&
p.expression.left.object.name === 'module' &&
p.expression.left.property?.type === Syntax.Identifier &&
p.expression.left.property.name === 'exports',
// eslint-disable-next-line @typescript-eslint/no-explicit-any
) as any
const exportDefaultDeclaration = moduleItems.find(
(m) =>
m.type === 'ExportDefaultExpression' &&
(m.expression.type === 'Identifier' || m.expression.type === 'CallExpression'),
) as ExportDefaultExpression | undefined
if (moduleExports && moduleExports.expression.right?.loc) {
const modifiedConfigContent = insertBeforeAndAfter(
if (exportDefaultDeclaration) {
if (!('span' in exportDefaultDeclaration.expression)) {
warning('Could not automatically wrap Next config with withPayload.')
warnUserWrapNotSuccessful(configType)
return Promise.resolve({
modifiedConfigContent: content,
success: false,
})
}
const modifiedConfigContent = insertBeforeAndAfterSWC(
content,
moduleExports.expression.right.loc,
exportDefaultDeclaration.expression.span,
parseOffset,
)
return { modifiedConfigContent, success: true }
}
return {
modifiedConfigContent: content,
success: false,
}
}
warning('Could not automatically wrap Next config with withPayload.')
warnUserWrapNotSuccessful(configType)
return {
return Promise.resolve({
modifiedConfigContent: content,
success: false,
}
})
}
function warnUserWrapNotSuccessful(configType: NextConfigType) {
// Output directions for user to update next.config.js
const withPayloadMessage = `
${chalk.bold(`Please manually wrap your existing next.config.js with the withPayload function. Here is an example:`)}
${chalk.bold(`Please manually wrap your existing Next config with the withPayload function. Here is an example:`)}
${withPayloadStatement[configType]}
@@ -148,7 +179,7 @@ function warnUserWrapNotSuccessful(configType: NextConfigType) {
// Your Next.js config here
}
${configType === 'esm' ? 'export default withPayload(nextConfig)' : 'module.exports = withPayload(nextConfig)'}
${configType === 'cjs' ? 'module.exports = withPayload(nextConfig)' : 'export default withPayload(nextConfig)'}
`
@@ -186,7 +217,7 @@ type Loc = {
start: { column: number; line: number }
}
function insertBeforeAndAfter(content: string, loc: Loc) {
function insertBeforeAndAfter(content: string, loc: Loc): string {
const { end, start } = loc
const lines = content.split('\n')
@@ -205,3 +236,57 @@ function insertBeforeAndAfter(content: string, loc: Loc) {
return lines.join('\n')
}
function insertBeforeAndAfterSWC(
content: string,
span: ModuleItem['span'],
/**
* WARNING: This is ONLY for unit tests. Defaults to 0 otherwise.
*
* @see compileTypeScriptFileToAST
*/
parseOffset: number,
): string {
const { end: preOffsetEnd, start: preOffsetStart } = span
const start = preOffsetStart - parseOffset
const end = preOffsetEnd - parseOffset
const insert = (pos: number, text: string): string => {
return content.slice(0, pos) + text + content.slice(pos)
}
// insert ) after end
content = insert(end - 1, ')')
// insert withPayload before start
content = insert(start - 1, 'withPayload(')
return content
}
/**
* Compile typescript to AST using the swc compiler
*/
async function compileTypeScriptFileToAST(
fileContent: string,
): Promise<{ moduleItems: ModuleItem[]; parseOffset: number }> {
let parseOffset = 0
/**
* WARNING: This is ONLY for unit tests.
*
* Multiple instances of swc DO NOT reset the .span.end value.
* During unit tests, the .spawn.end value is read and accounted for.
*
* https://github.com/swc-project/swc/issues/1366
*/
if (process.env.NODE_ENV === 'test') {
parseOffset = (await swc.parse('')).span.end
}
const module = await swc.parse(fileContent, {
syntax: 'typescript',
})
return { moduleItems: module.body, parseOffset }
}

View File

@@ -85,7 +85,22 @@ export class Main {
// Detect if inside Next.js project
const nextAppDetails = await getNextAppDetails(process.cwd())
const { hasTopLevelLayout, isPayloadInstalled, nextAppDir, nextConfigPath } = nextAppDetails
const {
hasTopLevelLayout,
isPayloadInstalled,
isSupportedNextVersion,
nextAppDir,
nextConfigPath,
nextVersion,
} = nextAppDetails
if (nextConfigPath && !isSupportedNextVersion) {
p.log.warn(
`Next.js v${nextVersion} is unsupported. Next.js >= 15 is required to use Payload.`,
)
p.outro(feedbackOutro())
process.exit(0)
}
// Upgrade Payload in existing project
if (isPayloadInstalled && nextConfigPath) {

View File

@@ -70,11 +70,13 @@ export type NextAppDetails = {
hasTopLevelLayout: boolean
isPayloadInstalled?: boolean
isSrcDir: boolean
isSupportedNextVersion: boolean
nextAppDir?: string
nextConfigPath?: string
nextConfigType?: NextConfigType
nextVersion: null | string
}
export type NextConfigType = 'cjs' | 'esm'
export type NextConfigType = 'cjs' | 'esm' | 'ts'
export type StorageAdapterType = 'localDisk' | 'payloadCloud' | 'vercelBlobStorage'

View File

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

View File

@@ -1,6 +1,6 @@
import type { CollationOptions, TransactionOptions } from 'mongodb'
import type { MongoMemoryReplSet } from 'mongodb-memory-server'
import type { ClientSession, ConnectOptions, Connection } from 'mongoose'
import type { ClientSession, ConnectOptions, Connection, SchemaOptions } from 'mongoose'
import type { BaseDatabaseAdapter, DatabaseAdapterObj, Payload } from 'payload'
import fs from 'fs'
@@ -66,6 +66,15 @@ export interface Args {
* Defaults to disabled.
*/
collation?: Omit<CollationOptions, 'locale'>
/** Define Mongoose options on a collection-by-collection basis.
*/
collections?: {
[slug: string]: {
/** Define Mongoose schema options for a given collection.
*/
schemaOptions?: SchemaOptions
}
}
/** Extra configuration options */
connectOptions?: {
/** Set false to disable $facet aggregation in non-supporting databases, Defaults to true */
@@ -73,23 +82,40 @@ export interface Args {
} & ConnectOptions
/** Set to true to disable hinting to MongoDB to use 'id' as index. This is currently done when counting documents for pagination. Disabling this optimization might fix some problems with AWS DocumentDB. Defaults to false */
disableIndexHints?: boolean
/** Define Mongoose options for the globals collection.
*/
globals?: {
schemaOptions?: SchemaOptions
}
migrationDir?: string
/**
* typed as any to avoid dependency
*/
mongoMemoryServer?: MongoMemoryReplSet
/** Define default Mongoose schema options for all schemas created.
*/
schemaOptions?: SchemaOptions
transactionOptions?: TransactionOptions | false
/** The URL to connect to MongoDB or false to start payload and prevent connecting */
url: false | string
}
export type MongooseAdapter = {
collectionOptions: {
[slug: string]: {
schemaOptions?: SchemaOptions
}
}
collections: {
[slug: string]: CollectionModel
}
connection: Connection
globals: GlobalModel
globalsOptions: {
schemaOptions?: SchemaOptions
}
mongoMemoryServer: MongoMemoryReplSet
schemaOptions?: SchemaOptions
sessions: Record<number | string, ClientSession>
versions: {
[slug: string]: CollectionModel
@@ -100,13 +126,22 @@ export type MongooseAdapter = {
declare module 'payload' {
export interface DatabaseAdapter
extends Omit<BaseDatabaseAdapter, 'sessions'>,
Omit<Args, 'migrationDir'> {
Omit<Args, 'collections' | 'globals' | 'migrationDir'> {
collectionOptions: {
[slug: string]: {
schemaOptions?: SchemaOptions
}
}
collections: {
[slug: string]: CollectionModel
}
connection: Connection
globals: GlobalModel
globalsOptions: {
schemaOptions?: SchemaOptions
}
mongoMemoryServer: MongoMemoryReplSet
schemaOptions?: SchemaOptions
sessions: Record<number | string, ClientSession>
transactionOptions: TransactionOptions
versions: {
@@ -117,10 +152,13 @@ declare module 'payload' {
export function mongooseAdapter({
autoPluralization = true,
collections,
connectOptions,
disableIndexHints = false,
globals,
migrationDir: migrationDirArg,
mongoMemoryServer,
schemaOptions,
transactionOptions = {},
url,
}: Args): DatabaseAdapterObj {
@@ -133,17 +171,21 @@ export function mongooseAdapter({
// Mongoose-specific
autoPluralization,
collectionOptions: collections || {},
collections: {},
connectOptions: connectOptions || {},
connection: undefined,
count,
disableIndexHints,
globals: undefined,
globalsOptions: globals || {},
mongoMemoryServer,
schemaOptions: schemaOptions || {},
sessions: {},
transactionOptions: transactionOptions === false ? undefined : transactionOptions,
url,
versions: {},
// DatabaseAdapter
beginTransaction: transactionOptions ? beginTransaction : undefined,
commitTransaction,

View File

@@ -16,20 +16,22 @@ import { getDBName } from './utilities/getDBName.js'
export const init: Init = function init(this: MongooseAdapter) {
this.payload.config.collections.forEach((collection: SanitizedCollectionConfig) => {
const schema = buildCollectionSchema(collection, this.payload.config)
const schema = buildCollectionSchema(collection, this)
if (collection.versions) {
const versionModelName = getDBName({ config: collection, versions: true })
const versionCollectionFields = buildVersionCollectionFields(collection)
const versionSchema = buildSchema(this.payload.config, versionCollectionFields, {
const versionSchema = buildSchema(this, versionCollectionFields, {
disableUnique: true,
draftsEnabled: true,
indexSortableFields: this.payload.config.indexSortableFields,
options: {
minimize: false,
timestamps: false,
...this.schemaOptions,
...(this.collectionOptions[collection.slug]?.schemaOptions || {}),
},
})
@@ -57,7 +59,7 @@ export const init: Init = function init(this: MongooseAdapter) {
this.collections[collection.slug] = model
})
const model = buildGlobalModel(this.payload.config)
const model = buildGlobalModel(this)
this.globals = model
this.payload.config.globals.forEach((global) => {
@@ -66,13 +68,15 @@ export const init: Init = function init(this: MongooseAdapter) {
const versionGlobalFields = buildVersionGlobalFields(global)
const versionSchema = buildSchema(this.payload.config, versionGlobalFields, {
const versionSchema = buildSchema(this, versionGlobalFields, {
disableUnique: true,
draftsEnabled: true,
indexSortableFields: this.payload.config.indexSortableFields,
options: {
minimize: false,
timestamps: false,
...this.schemaOptions,
...(this.globalsOptions.schemaOptions || {}),
},
})

View File

@@ -1,27 +1,29 @@
import type { PaginateOptions, Schema } from 'mongoose'
import type { SanitizedCollectionConfig, SanitizedConfig } from 'payload'
import type { SanitizedCollectionConfig } from 'payload'
import paginate from 'mongoose-paginate-v2'
import type { MongooseAdapter } from '../index.js'
import getBuildQueryPlugin from '../queries/buildQuery.js'
import buildSchema from './buildSchema.js'
const buildCollectionSchema = (
collection: SanitizedCollectionConfig,
config: SanitizedConfig,
schemaOptions = {},
adapter: MongooseAdapter,
): Schema => {
const schema = buildSchema(config, collection.fields, {
const schema = buildSchema(adapter, collection.fields, {
draftsEnabled: Boolean(typeof collection?.versions === 'object' && collection.versions.drafts),
indexSortableFields: config.indexSortableFields,
indexSortableFields: adapter.payload.config.indexSortableFields,
options: {
minimize: false,
timestamps: collection.timestamps !== false,
...schemaOptions,
...adapter.schemaOptions,
...(adapter.collectionOptions[collection.slug]?.schemaOptions || {}),
},
})
if (config.indexSortableFields && collection.timestamps !== false) {
if (adapter.payload.config.indexSortableFields && collection.timestamps !== false) {
schema.index({ updatedAt: 1 })
schema.index({ createdAt: 1 })
}

View File

@@ -1,27 +1,34 @@
import type { SanitizedConfig } from 'payload'
import mongoose from 'mongoose'
import type { MongooseAdapter } from '../index.js'
import type { GlobalModel } from '../types.js'
import getBuildQueryPlugin from '../queries/buildQuery.js'
import buildSchema from './buildSchema.js'
export const buildGlobalModel = (config: SanitizedConfig): GlobalModel | null => {
if (config.globals && config.globals.length > 0) {
export const buildGlobalModel = (adapter: MongooseAdapter): GlobalModel | null => {
if (adapter.payload.config.globals && adapter.payload.config.globals.length > 0) {
const globalsSchema = new mongoose.Schema(
{},
{ discriminatorKey: 'globalType', minimize: false, timestamps: true },
{
discriminatorKey: 'globalType',
minimize: false,
...adapter.schemaOptions,
...(adapter.globalsOptions.schemaOptions || {}),
timestamps: true,
},
)
globalsSchema.plugin(getBuildQueryPlugin())
const Globals = mongoose.model('globals', globalsSchema, 'globals') as unknown as GlobalModel
Object.values(config.globals).forEach((globalConfig) => {
const globalSchema = buildSchema(config, globalConfig.fields, {
Object.values(adapter.payload.config.globals).forEach((globalConfig) => {
const globalSchema = buildSchema(adapter, globalConfig.fields, {
options: {
minimize: false,
...adapter.schemaOptions,
...(adapter.globalsOptions.schemaOptions || {}),
},
})
Globals.discriminator(globalConfig.slug, globalSchema)

View File

@@ -19,7 +19,6 @@ import type {
RelationshipField,
RichTextField,
RowField,
SanitizedConfig,
SanitizedLocalizationConfig,
SelectField,
Tab,
@@ -37,6 +36,8 @@ import {
tabHasName,
} from 'payload/shared'
import type { MongooseAdapter } from '../index.js'
export type BuildSchemaOptions = {
allowIDField?: boolean
disableUnique?: boolean
@@ -48,7 +49,7 @@ export type BuildSchemaOptions = {
type FieldSchemaGenerator = (
field: Field,
schema: Schema,
config: SanitizedConfig,
adapter: MongooseAdapter,
buildSchemaOptions: BuildSchemaOptions,
) => void
@@ -87,10 +88,10 @@ const localizeSchema = (
if (fieldIsLocalized(entity) && localization && Array.isArray(localization.locales)) {
return {
type: localization.localeCodes.reduce(
(localeSchema, locale) => ({
...localeSchema,
[locale]: schema,
}),
(localeSchema, locale) => {
localeSchema[locale] = schema
return localeSchema
},
{
_id: false,
},
@@ -102,7 +103,7 @@ const localizeSchema = (
}
const buildSchema = (
config: SanitizedConfig,
adapter: MongooseAdapter,
configFields: Field[],
buildSchemaOptions: BuildSchemaOptions = {},
): Schema => {
@@ -130,7 +131,7 @@ const buildSchema = (
const addFieldSchema: FieldSchemaGenerator = fieldToSchemaMap[field.type]
if (addFieldSchema) {
addFieldSchema(field, schema, config, buildSchemaOptions)
addFieldSchema(field, schema, adapter, buildSchemaOptions)
}
}
})
@@ -142,20 +143,22 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
array: (
field: ArrayField,
schema: Schema,
config: SanitizedConfig,
adapter: MongooseAdapter,
buildSchemaOptions: BuildSchemaOptions,
) => {
const baseSchema = {
...formatBaseSchema(field, buildSchemaOptions),
type: [
buildSchema(config, field.fields, {
buildSchema(adapter, field.fields, {
allowIDField: true,
disableUnique: buildSchemaOptions.disableUnique,
draftsEnabled: buildSchemaOptions.draftsEnabled,
options: {
minimize: false,
...(buildSchemaOptions.options || {}),
_id: false,
id: false,
minimize: false,
timestamps: false,
},
}),
],
@@ -163,36 +166,54 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
}
schema.add({
[field.name]: localizeSchema(field, baseSchema, config.localization),
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
})
},
blocks: (
field: BlockField,
schema: Schema,
config: SanitizedConfig,
adapter: MongooseAdapter,
buildSchemaOptions: BuildSchemaOptions,
): void => {
const fieldSchema = {
type: [new mongoose.Schema({}, { _id: false, discriminatorKey: 'blockType' })],
type: [
new mongoose.Schema(
{},
{
_id: false,
discriminatorKey: 'blockType',
...(buildSchemaOptions.options || {}),
timestamps: false,
},
),
],
default: undefined,
}
schema.add({
[field.name]: localizeSchema(field, fieldSchema, config.localization),
[field.name]: localizeSchema(field, fieldSchema, adapter.payload.config.localization),
})
field.blocks.forEach((blockItem: Block) => {
const blockSchema = new mongoose.Schema({}, { _id: false, id: false })
const blockSchema = new mongoose.Schema(
{},
{
...(buildSchemaOptions.options || {}),
_id: false,
id: false,
timestamps: false,
},
)
blockItem.fields.forEach((blockField) => {
const addFieldSchema: FieldSchemaGenerator = fieldToSchemaMap[blockField.type]
if (addFieldSchema) {
addFieldSchema(blockField, blockSchema, config, buildSchemaOptions)
addFieldSchema(blockField, blockSchema, adapter, buildSchemaOptions)
}
})
if (field.localized && config.localization) {
config.localization.localeCodes.forEach((localeCode) => {
if (field.localized && adapter.payload.config.localization) {
adapter.payload.config.localization.localeCodes.forEach((localeCode) => {
// @ts-expect-error Possible incorrect typing in mongoose types, this works
schema.path(`${field.name}.${localeCode}`).discriminator(blockItem.slug, blockSchema)
})
@@ -205,69 +226,69 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
checkbox: (
field: CheckboxField,
schema: Schema,
config: SanitizedConfig,
adapter: MongooseAdapter,
buildSchemaOptions: BuildSchemaOptions,
): void => {
const baseSchema = { ...formatBaseSchema(field, buildSchemaOptions), type: Boolean }
schema.add({
[field.name]: localizeSchema(field, baseSchema, config.localization),
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
})
},
code: (
field: CodeField,
schema: Schema,
config: SanitizedConfig,
adapter: MongooseAdapter,
buildSchemaOptions: BuildSchemaOptions,
): void => {
const baseSchema = { ...formatBaseSchema(field, buildSchemaOptions), type: String }
schema.add({
[field.name]: localizeSchema(field, baseSchema, config.localization),
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
})
},
collapsible: (
field: CollapsibleField,
schema: Schema,
config: SanitizedConfig,
adapter: MongooseAdapter,
buildSchemaOptions: BuildSchemaOptions,
): void => {
field.fields.forEach((subField: Field) => {
const addFieldSchema: FieldSchemaGenerator = fieldToSchemaMap[subField.type]
if (addFieldSchema) {
addFieldSchema(subField, schema, config, buildSchemaOptions)
addFieldSchema(subField, schema, adapter, buildSchemaOptions)
}
})
},
date: (
field: DateField,
schema: Schema,
config: SanitizedConfig,
adapter: MongooseAdapter,
buildSchemaOptions: BuildSchemaOptions,
): void => {
const baseSchema = { ...formatBaseSchema(field, buildSchemaOptions), type: Date }
schema.add({
[field.name]: localizeSchema(field, baseSchema, config.localization),
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
})
},
email: (
field: EmailField,
schema: Schema,
config: SanitizedConfig,
adapter: MongooseAdapter,
buildSchemaOptions: BuildSchemaOptions,
): void => {
const baseSchema = { ...formatBaseSchema(field, buildSchemaOptions), type: String }
schema.add({
[field.name]: localizeSchema(field, baseSchema, config.localization),
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
})
},
group: (
field: GroupField,
schema: Schema,
config: SanitizedConfig,
adapter: MongooseAdapter,
buildSchemaOptions: BuildSchemaOptions,
): void => {
const formattedBaseSchema = formatBaseSchema(field, buildSchemaOptions)
@@ -280,26 +301,28 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
const baseSchema = {
...formattedBaseSchema,
type: buildSchema(config, field.fields, {
type: buildSchema(adapter, field.fields, {
disableUnique: buildSchemaOptions.disableUnique,
draftsEnabled: buildSchemaOptions.draftsEnabled,
indexSortableFields,
options: {
minimize: false,
...(buildSchemaOptions.options || {}),
_id: false,
id: false,
minimize: false,
timestamps: false,
},
}),
}
schema.add({
[field.name]: localizeSchema(field, baseSchema, config.localization),
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
})
},
json: (
field: JSONField,
schema: Schema,
config: SanitizedConfig,
adapter: MongooseAdapter,
buildSchemaOptions: BuildSchemaOptions,
): void => {
const baseSchema = {
@@ -308,13 +331,13 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
}
schema.add({
[field.name]: localizeSchema(field, baseSchema, config.localization),
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
})
},
number: (
field: NumberField,
schema: Schema,
config: SanitizedConfig,
adapter: MongooseAdapter,
buildSchemaOptions: BuildSchemaOptions,
): void => {
const baseSchema = {
@@ -323,13 +346,13 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
}
schema.add({
[field.name]: localizeSchema(field, baseSchema, config.localization),
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
})
},
point: (
field: PointField,
schema: Schema,
config: SanitizedConfig,
adapter: MongooseAdapter,
buildSchemaOptions: BuildSchemaOptions,
): void => {
const baseSchema: SchemaTypeOptions<unknown> = {
@@ -348,7 +371,7 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
}
schema.add({
[field.name]: localizeSchema(field, baseSchema, config.localization),
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
})
if (field.index === true || field.index === undefined) {
@@ -357,8 +380,8 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
indexOptions.sparse = true
indexOptions.unique = true
}
if (field.localized && config.localization) {
config.localization.locales.forEach((locale) => {
if (field.localized && adapter.payload.config.localization) {
adapter.payload.config.localization.locales.forEach((locale) => {
schema.index({ [`${field.name}.${locale.code}`]: '2dsphere' }, indexOptions)
})
} else {
@@ -369,7 +392,7 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
radio: (
field: RadioField,
schema: Schema,
config: SanitizedConfig,
adapter: MongooseAdapter,
buildSchemaOptions: BuildSchemaOptions,
): void => {
const baseSchema = {
@@ -382,21 +405,21 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
}
schema.add({
[field.name]: localizeSchema(field, baseSchema, config.localization),
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
})
},
relationship: (
field: RelationshipField,
schema: Schema,
config: SanitizedConfig,
adapter: MongooseAdapter,
buildSchemaOptions: BuildSchemaOptions,
) => {
const hasManyRelations = Array.isArray(field.relationTo)
let schemaToReturn: { [key: string]: any } = {}
if (field.localized && config.localization) {
if (field.localized && adapter.payload.config.localization) {
schemaToReturn = {
type: config.localization.localeCodes.reduce((locales, locale) => {
type: adapter.payload.config.localization.localeCodes.reduce((locales, locale) => {
let localeSchema: { [key: string]: any } = {}
if (hasManyRelations) {
@@ -465,7 +488,7 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
richText: (
field: RichTextField,
schema: Schema,
config: SanitizedConfig,
adapter: MongooseAdapter,
buildSchemaOptions: BuildSchemaOptions,
): void => {
const baseSchema = {
@@ -474,27 +497,27 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
}
schema.add({
[field.name]: localizeSchema(field, baseSchema, config.localization),
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
})
},
row: (
field: RowField,
schema: Schema,
config: SanitizedConfig,
adapter: MongooseAdapter,
buildSchemaOptions: BuildSchemaOptions,
): void => {
field.fields.forEach((subField: Field) => {
const addFieldSchema: FieldSchemaGenerator = fieldToSchemaMap[subField.type]
if (addFieldSchema) {
addFieldSchema(subField, schema, config, buildSchemaOptions)
addFieldSchema(subField, schema, adapter, buildSchemaOptions)
}
})
},
select: (
field: SelectField,
schema: Schema,
config: SanitizedConfig,
adapter: MongooseAdapter,
buildSchemaOptions: BuildSchemaOptions,
): void => {
const baseSchema = {
@@ -514,39 +537,41 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
[field.name]: localizeSchema(
field,
field.hasMany ? [baseSchema] : baseSchema,
config.localization,
adapter.payload.config.localization,
),
})
},
tabs: (
field: TabsField,
schema: Schema,
config: SanitizedConfig,
adapter: MongooseAdapter,
buildSchemaOptions: BuildSchemaOptions,
): void => {
field.tabs.forEach((tab) => {
if (tabHasName(tab)) {
const baseSchema = {
type: buildSchema(config, tab.fields, {
type: buildSchema(adapter, tab.fields, {
disableUnique: buildSchemaOptions.disableUnique,
draftsEnabled: buildSchemaOptions.draftsEnabled,
options: {
minimize: false,
...(buildSchemaOptions.options || {}),
_id: false,
id: false,
minimize: false,
timestamps: false,
},
}),
}
schema.add({
[tab.name]: localizeSchema(tab, baseSchema, config.localization),
[tab.name]: localizeSchema(tab, baseSchema, adapter.payload.config.localization),
})
} else {
tab.fields.forEach((subField: Field) => {
const addFieldSchema: FieldSchemaGenerator = fieldToSchemaMap[subField.type]
if (addFieldSchema) {
addFieldSchema(subField, schema, config, buildSchemaOptions)
addFieldSchema(subField, schema, adapter, buildSchemaOptions)
}
})
}
@@ -555,7 +580,7 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
text: (
field: TextField,
schema: Schema,
config: SanitizedConfig,
adapter: MongooseAdapter,
buildSchemaOptions: BuildSchemaOptions,
): void => {
const baseSchema = {
@@ -564,25 +589,25 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
}
schema.add({
[field.name]: localizeSchema(field, baseSchema, config.localization),
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
})
},
textarea: (
field: TextareaField,
schema: Schema,
config: SanitizedConfig,
adapter: MongooseAdapter,
buildSchemaOptions: BuildSchemaOptions,
): void => {
const baseSchema = { ...formatBaseSchema(field, buildSchemaOptions), type: String }
schema.add({
[field.name]: localizeSchema(field, baseSchema, config.localization),
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
})
},
upload: (
field: UploadField,
schema: Schema,
config: SanitizedConfig,
adapter: MongooseAdapter,
buildSchemaOptions: BuildSchemaOptions,
): void => {
const baseSchema = {
@@ -592,7 +617,7 @@ const fieldToSchemaMap: Record<string, FieldSchemaGenerator> = {
}
schema.add({
[field.name]: localizeSchema(field, baseSchema, config.localization),
[field.name]: localizeSchema(field, baseSchema, adapter.payload.config.localization),
})
},
}

View File

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

View File

@@ -65,7 +65,7 @@ export const createMigration: CreateMigration = async function createMigration(
const sqlStatementsUp = await generateMigration(drizzleJsonBefore, drizzleJsonAfter)
const sqlStatementsDown = await generateMigration(drizzleJsonAfter, drizzleJsonBefore)
const sqlExecute = 'await db.execute(sql`'
const sqlExecute = 'await payload.db.drizzle.execute(sql`'
if (sqlStatementsUp?.length) {
upSQL = `${sqlExecute}\n ${sqlStatementsUp?.join('\n')}\`)`

View File

@@ -6,11 +6,11 @@ export const getMigrationTemplate = ({
upSQL,
}: MigrationTemplateArgs): string => `import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'
${imports ? `${imports}\n` : ''}
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
export async function up({ payload, req }: MigrateUpArgs): Promise<void> {
${upSQL}
}
export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {
export async function down({ payload, req }: MigrateDownArgs): Promise<void> {
${downSQL}
}
`

View File

@@ -1,14 +1,12 @@
/* eslint-disable no-param-reassign */
import type { SanitizedCollectionConfig } from 'payload'
import type { Init } from 'payload'
import type { Init, SanitizedCollectionConfig } from 'payload'
import { createTableName } from '@payloadcms/drizzle'
import { pgEnum, pgSchema, pgTable } from 'drizzle-orm/pg-core'
import { buildVersionCollectionFields, buildVersionGlobalFields } from 'payload'
import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from './types.js'
import { createTableName } from '../../drizzle/src/createTableName.js'
import { buildTable } from './schema/build.js'
export const init: Init = function init(this: PostgresAdapter) {

View File

@@ -156,6 +156,8 @@ declare module 'payload' {
export interface DatabaseAdapter
extends Omit<Args, 'idType' | 'logger' | 'migrationDir' | 'pool'>,
DrizzleAdapter {
beginTransaction: (options?: PgTransactionConfig) => Promise<null | number | string>
drizzle: PostgresDB
enums: Record<string, GenericEnum>
/**
* An object keyed on each table, with a key value pair where the constraint name is the key, followed by the dot-notation field name

View File

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

View File

@@ -66,7 +66,7 @@ export const createMigration: CreateMigration = async function createMigration(
const sqlStatementsUp = await generateSQLiteMigration(drizzleJsonBefore, drizzleJsonAfter)
const sqlStatementsDown = await generateSQLiteMigration(drizzleJsonAfter, drizzleJsonBefore)
// need to create tables as separate statements
const sqlExecute = 'await db.run(sql`'
const sqlExecute = 'await payload.db.drizzle.run(sql`'
if (sqlStatementsUp?.length) {
upSQL = sqlStatementsUp

View File

@@ -10,5 +10,5 @@ export const defaultDrizzleSnapshot: DrizzleSQLiteSnapshotJSON = {
enums: {},
prevId: '00000000-0000-0000-0000-00000000000',
tables: {},
version: '3',
version: '5',
}

View File

@@ -6,11 +6,11 @@ export const getMigrationTemplate = ({
upSQL,
}: MigrationTemplateArgs): string => `import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-sqlite'
${imports ? `${imports}\n` : ''}
export async function up({ db, payload, req }: MigrateUpArgs): Promise<void> {
export async function up({ payload, req }: MigrateUpArgs): Promise<void> {
${upSQL}
}
export async function down({ db, payload, req }: MigrateDownArgs): Promise<void> {
export async function down({ payload, req }: MigrateDownArgs): Promise<void> {
${downSQL}
}
`

View File

@@ -132,12 +132,10 @@ export type SQLiteAdapter = {
export type IDType = 'integer' | 'numeric' | 'text'
export type MigrateUpArgs = {
db: LibSQLDatabase
payload: Payload
req?: Partial<PayloadRequest>
}
export type MigrateDownArgs = {
db: LibSQLDatabase
payload: Payload
req?: Partial<PayloadRequest>
}
@@ -146,6 +144,8 @@ declare module 'payload' {
export interface DatabaseAdapter
extends Omit<Args, 'idType' | 'logger' | 'migrationDir' | 'pool'>,
DrizzleAdapter {
beginTransaction: (options?: SQLiteTransactionConfig) => Promise<null | number | string>
drizzle: LibSQLDatabase
/**
* An object keyed on each table, with a key value pair where the constraint name is the key, followed by the dot-notation field name
* Used for returning properly formed errors from unique fields

View File

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

View File

@@ -6,6 +6,7 @@ import type { DrizzleAdapter, DrizzleTransaction } from '../types.js'
export const beginTransaction: BeginTransaction = async function beginTransaction(
this: DrizzleAdapter,
options: DrizzleAdapter['transactionOptions'],
) {
let id
try {
@@ -41,7 +42,7 @@ export const beginTransaction: BeginTransaction = async function beginTransactio
}
transactionReady()
})
}, this.transactionOptions)
}, options || this.transactionOptions)
.catch(() => {
// swallow
})

View File

@@ -1,5 +1,4 @@
import type { Field, SanitizedConfig , TabAsField } from 'payload'
import type { Field, SanitizedConfig, TabAsField } from 'payload'
import { fieldAffectsData } from 'payload/shared'

View File

@@ -5,7 +5,7 @@ export const migrationTableExists = async (adapter: DrizzleAdapter): Promise<boo
if (adapter.name === 'postgres') {
const prependSchema = adapter.schemaName ? `"${adapter.schemaName}".` : ''
statement = `SELECT to_regclass('${prependSchema}"payload_migrations"') exists;`
statement = `SELECT to_regclass('${prependSchema}"payload_migrations"') AS exists;`
}
if (adapter.name === 'sqlite') {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/email-nodemailer",
"version": "3.0.0-beta.68",
"version": "3.0.0-beta.71",
"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.68",
"version": "3.0.0-beta.71",
"description": "Payload Resend Email Adapter",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -39,7 +39,7 @@ export const index = deepMerge(
},
},
rules: {
'react-hooks/rules-of-hooks': 'warn',
'react-hooks/rules-of-hooks': 'error',
},
},
)

View File

@@ -33,7 +33,7 @@
"eslint-plugin-react-hooks": "5.1.0-rc-85acf2d195-20240711",
"eslint-plugin-regexp": "2.6.0",
"globals": "15.8.0",
"typescript": "5.5.3",
"typescript": "5.5.4",
"typescript-eslint": "7.16.0"
}
}

View File

@@ -32,7 +32,7 @@
"eslint-plugin-react-hooks": "5.1.0-rc-85acf2d195-20240711",
"eslint-plugin-regexp": "2.6.0",
"globals": "15.8.0",
"typescript": "5.5.3",
"typescript": "5.5.4",
"typescript-eslint": "7.16.0"
}
}

View File

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

View File

@@ -1,3 +1,3 @@
export { GraphQLJSON, GraphQLJSONObject } from '../packages/graphql-type-json/index.js'
export { buildPaginatedListType } from '../schema/buildPaginatedListType.js'
export { default as GraphQL } from 'graphql'
export * as GraphQL from 'graphql'

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/live-preview-react",
"version": "3.0.0-beta.68",
"version": "3.0.0-beta.71",
"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.68",
"version": "3.0.0-beta.71",
"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.68",
"version": "3.0.0-beta.71",
"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.68",
"version": "3.0.0-beta.71",
"homepage": "https://payloadcms.com",
"repository": {
"type": "git",

View File

@@ -134,12 +134,8 @@ export const POST =
resHeaders.append(key, headers[key])
}
if (req.responseHeaders) {
mergeHeaders(req.responseHeaders, resHeaders)
}
return new Response(apiResponse.body, {
headers: resHeaders,
headers: req.responseHeaders ? mergeHeaders(req.responseHeaders, resHeaders) : resHeaders,
status: apiResponse.status,
})
}

View File

@@ -167,7 +167,13 @@ const handleCustomEndpoints = async ({
if (res instanceof Response) {
if (req.responseHeaders) {
mergeHeaders(req.responseHeaders, res.headers)
const mergedResponse = new Response(res.body, {
headers: mergeHeaders(req.responseHeaders, res.headers),
status: res.status,
statusText: res.statusText,
})
return mergedResponse
}
return res
@@ -379,7 +385,13 @@ export const GET =
if (res instanceof Response) {
if (req.responseHeaders) {
mergeHeaders(req.responseHeaders, res.headers)
const mergedResponse = new Response(res.body, {
headers: mergeHeaders(req.responseHeaders, res.headers),
status: res.status,
statusText: res.statusText,
})
return mergedResponse
}
return res
@@ -555,7 +567,13 @@ export const POST =
if (res instanceof Response) {
if (req.responseHeaders) {
mergeHeaders(req.responseHeaders, res.headers)
const mergedResponse = new Response(res.body, {
headers: mergeHeaders(req.responseHeaders, res.headers),
status: res.status,
statusText: res.statusText,
})
return mergedResponse
}
return res
@@ -643,7 +661,13 @@ export const DELETE =
if (res instanceof Response) {
if (req.responseHeaders) {
mergeHeaders(req.responseHeaders, res.headers)
const mergedResponse = new Response(res.body, {
headers: mergeHeaders(req.responseHeaders, res.headers),
status: res.status,
statusText: res.statusText,
})
return mergedResponse
}
return res
@@ -732,7 +756,13 @@ export const PATCH =
if (res instanceof Response) {
if (req.responseHeaders) {
mergeHeaders(req.responseHeaders, res.headers)
const mergedResponse = new Response(res.body, {
headers: mergeHeaders(req.responseHeaders, res.headers),
status: res.status,
statusText: res.statusText,
})
return mergedResponse
}
return res

View File

@@ -7,7 +7,7 @@ import type {
import { notFound } from 'next/navigation.js'
import { isAdminAuthRoute, isAdminRoute } from './shared.js'
import { getRouteWithoutAdmin, isAdminAuthRoute, isAdminRoute } from './shared.js'
export const handleAdminPage = ({
adminRoute,
@@ -20,9 +20,9 @@ export const handleAdminPage = ({
permissions: Permissions
route: string
}) => {
if (isAdminRoute(route, adminRoute)) {
const baseAdminRoute = adminRoute && adminRoute !== '/' ? route.replace(adminRoute, '') : route
const routeSegments = baseAdminRoute.split('/').filter(Boolean)
if (isAdminRoute({ adminRoute, config, route })) {
const routeWithoutAdmin = getRouteWithoutAdmin({ adminRoute, route })
const routeSegments = routeWithoutAdmin.split('/').filter(Boolean)
const [entityType, entitySlug, createOrID] = routeSegments
const collectionSlug = entityType === 'collections' ? entitySlug : undefined
const globalSlug = entityType === 'globals' ? entitySlug : undefined
@@ -47,7 +47,7 @@ export const handleAdminPage = ({
}
}
if (!permissions.canAccessAdmin && !isAdminAuthRoute(config, route, adminRoute)) {
if (!permissions.canAccessAdmin && !isAdminAuthRoute({ adminRoute, config, route })) {
notFound()
}

View File

@@ -22,7 +22,7 @@ export const handleAuthRedirect = ({
routes: { admin: adminRoute },
} = config
if (!isAdminAuthRoute(config, route, adminRoute)) {
if (!isAdminAuthRoute({ adminRoute, config, route })) {
if (searchParams && 'redirect' in searchParams) delete searchParams.redirect
const redirectRoute = encodeURIComponent(
@@ -36,7 +36,7 @@ export const handleAuthRedirect = ({
const customLoginRoute =
typeof redirectUnauthenticatedUser === 'string' ? redirectUnauthenticatedUser : undefined
const loginRoute = isAdminRoute(route, adminRoute)
const loginRoute = isAdminRoute({ adminRoute, config, route })
? adminLoginRoute
: customLoginRoute || loginRouteFromConfig

View File

@@ -11,16 +11,42 @@ const authRouteKeys: (keyof SanitizedConfig['admin']['routes'])[] = [
'reset',
]
export const isAdminRoute = (route: string, adminRoute: string) => {
return route.startsWith(adminRoute)
export const isAdminRoute = ({
adminRoute,
config,
route,
}: {
adminRoute: string
config: SanitizedConfig
route: string
}): boolean => {
return route.startsWith(adminRoute) && !isAdminAuthRoute({ adminRoute, config, route })
}
export const isAdminAuthRoute = (config: SanitizedConfig, route: string, adminRoute: string) => {
export const isAdminAuthRoute = ({
adminRoute,
config,
route,
}: {
adminRoute: string
config: SanitizedConfig
route: string
}): boolean => {
const authRoutes = config.admin?.routes
? Object.entries(config.admin.routes)
.filter(([key]) => authRouteKeys.includes(key as keyof SanitizedConfig['admin']['routes']))
.map(([_, value]) => value)
: []
return authRoutes.some((r) => route.replace(adminRoute, '').startsWith(r))
return authRoutes.some((r) => getRouteWithoutAdmin({ adminRoute, route }).startsWith(r))
}
export const getRouteWithoutAdmin = ({
adminRoute,
route,
}: {
adminRoute: string
route: string
}): string => {
return adminRoute && adminRoute !== '/' ? route.replace(adminRoute, '') : route
}

View File

@@ -1,33 +1,11 @@
const headersToJoin = ['set-cookie', 'warning', 'www-authenticate', 'proxy-authenticate', 'vary']
export const mergeHeaders = (sourceHeaders: Headers, destinationHeaders: Headers): Headers => {
// Create a new Headers object
const combinedHeaders = new Headers(destinationHeaders)
export function mergeHeaders(sourceHeaders: Headers, destinationHeaders: Headers): void {
// Create a map to store combined headers
const combinedHeaders = new Headers()
// Add existing destination headers to the combined map
destinationHeaders.forEach((value, key) => {
combinedHeaders.set(key, value)
})
// Add source headers to the combined map, joining specific headers
// Append sourceHeaders to combinedHeaders
sourceHeaders.forEach((value, key) => {
const lowerKey = key.toLowerCase()
if (headersToJoin.includes(lowerKey)) {
if (combinedHeaders.has(key)) {
combinedHeaders.set(key, `${combinedHeaders.get(key)}, ${value}`)
} else {
combinedHeaders.set(key, value)
}
} else {
combinedHeaders.set(key, value)
}
combinedHeaders.append(key, value)
})
// Clear the destination headers and set the combined headers
destinationHeaders.forEach((_, key) => {
destinationHeaders.delete(key)
})
combinedHeaders.forEach((value, key) => {
destinationHeaders.append(key, value)
})
return combinedHeaders
}

View File

@@ -7,13 +7,14 @@ import {
EmailField,
PasswordField,
TextField,
useAuth,
useConfig,
useDocumentInfo,
useFormFields,
useFormModified,
useTranslation,
} from '@payloadcms/ui'
import React, { useCallback, useEffect, useState } from 'react'
import React, { useCallback, useEffect, useMemo, useState } from 'react'
import { toast } from 'sonner'
import type { Props } from './types.js'
@@ -38,6 +39,7 @@ export const Auth: React.FC<Props> = (props) => {
verify,
} = props
const { permissions } = useAuth()
const [changingPassword, setChangingPassword] = useState(requirePassword)
const enableAPIKey = useFormFields(([fields]) => (fields && fields?.enableAPIKey) || null)
const dispatchFields = useFormFields((reducer) => reducer[1])
@@ -50,6 +52,23 @@ export const Auth: React.FC<Props> = (props) => {
serverURL,
} = useConfig()
const hasPermissionToUnlock: boolean = useMemo(() => {
const collection = permissions?.collections?.[collectionSlug]
if (collection) {
const unlock = 'unlock' in collection ? collection.unlock : undefined
if (unlock) {
// current types for permissions do not include auth permissions, this will be fixed in another branch soon, for now we need to ignore the types
// @todo: fix types
// @ts-expect-error
return unlock.permission
}
}
return false
}, [permissions, collectionSlug])
const handleChangePassword = useCallback(
(state: boolean) => {
if (!state) {
@@ -153,11 +172,11 @@ export const Auth: React.FC<Props> = (props) => {
{t('authentication:changePassword')}
</Button>
)}
{operation === 'update' && (
{operation === 'update' && hasPermissionToUnlock && (
<Button
buttonStyle="secondary"
disabled={disabled}
onClick={() => unlock()}
onClick={() => void unlock()}
size="small"
>
{t('authentication:forceUnlock')}

View File

@@ -1,11 +1,11 @@
'use client'
import type { FormProps } from '@payloadcms/ui'
import type { FieldMap } from '@payloadcms/ui/utilities/buildComponentMap'
import type {
ClientCollectionConfig,
ClientConfig,
ClientGlobalConfig,
Data,
FieldMap,
LivePreviewConfig,
} from 'payload'

View File

@@ -1,6 +1,5 @@
import type { StepNavItem } from '@payloadcms/ui'
import type { FieldMap } from '@payloadcms/ui/utilities/buildComponentMap'
import type { ClientCollectionConfig, ClientGlobalConfig } from 'payload'
import type { ClientCollectionConfig, ClientGlobalConfig, FieldMap } from 'payload'
import type React from 'react'
import { getTranslation } from '@payloadcms/translations'

View File

@@ -81,6 +81,8 @@ export const DefaultVersionView: React.FC<DefaultVersionsViewProps> = ({
const canUpdate = docPermissions?.update?.permission
const localeValues = locales.map((locale) => locale.value)
return (
<main className={baseClass}>
<SetViewActions actions={componentMap?.actionsMap?.Edit?.Version} />
@@ -136,11 +138,7 @@ export const DefaultVersionView: React.FC<DefaultVersionsViewProps> = ({
fieldMap={fieldMap}
fieldPermissions={docPermissions?.fields}
i18n={i18n}
locales={
locales
? locales.map(({ label }) => (typeof label === 'string' ? label : undefined))
: []
}
locales={localeValues}
version={
globalConfig
? {

View File

@@ -1,4 +1,4 @@
import type { MappedField } from '@payloadcms/ui/utilities/buildComponentMap'
import type { MappedField } from 'payload'
import { getTranslation } from '@payloadcms/translations'
import { getUniqueListBy } from 'payload/shared'

View File

@@ -1,8 +1,8 @@
'use client'
import type { ClientCollectionConfig } from 'payload'
import type { ClientCollectionConfig, MappedField } from 'payload'
import { getTranslation } from '@payloadcms/translations'
import { type MappedField, useConfig } from '@payloadcms/ui'
import { useConfig } from '@payloadcms/ui'
import { fieldAffectsData, fieldIsPresentationalOnly } from 'payload/shared'
import React from 'react'
import ReactDiffViewerImport from 'react-diff-viewer-continued'

View File

@@ -1,7 +1,5 @@
import type { I18nClient } from '@payloadcms/translations'
import type { SelectFieldProps } from '@payloadcms/ui'
import type { MappedField } from '@payloadcms/ui/utilities/buildComponentMap'
import type { OptionObject, SelectField } from 'payload'
import type { MappedField, OptionObject, SelectField, SelectFieldProps } from 'payload'
import { getTranslation } from '@payloadcms/translations'
import React from 'react'

View File

@@ -1,4 +1,4 @@
import type { MappedField, TabsFieldProps } from '@payloadcms/ui'
import type { MappedField, TabsFieldProps } from 'payload'
import React from 'react'

View File

@@ -1,6 +1,5 @@
import type { I18nClient } from '@payloadcms/translations'
import type { FieldMap, MappedField } from '@payloadcms/ui/utilities/buildComponentMap'
import type { FieldPermissions } from 'payload'
import type { FieldMap, FieldPermissions, MappedField } from 'payload'
import type React from 'react'
import type { DiffMethod } from 'react-diff-viewer-continued'

View File

@@ -1,6 +1,5 @@
import type { I18nClient } from '@payloadcms/translations'
import type { FieldMap, MappedField } from '@payloadcms/ui/utilities/buildComponentMap'
import type { FieldPermissions } from 'payload'
import type { FieldMap, FieldPermissions, MappedField } from 'payload'
import type { DiffMethod } from 'react-diff-viewer-continued'
import type { DiffComponents } from './fields/types.js'

View File

@@ -99,9 +99,8 @@ export const VersionView: EditViewComponent = async (props) => {
const localeOptions: OptionObject[] =
localization &&
localization?.locales &&
localization.locales.map(({ code, label }) => ({
label: typeof label === 'string' ? label : '',
label,
value: code,
}))

View File

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

View File

@@ -8,6 +8,7 @@ import type {
RelationshipField,
SelectField,
} from '../../fields/config/types.js'
import type { FormFieldBase } from '../types.js'
export type RowData = Record<string, any>
@@ -20,7 +21,7 @@ export type CellComponentProps = {
dateDisplayFormat?: DateField['admin']['date']['displayFormat']
fieldType?: Field['type']
isFieldAffectingData?: boolean
label?: Record<string, string> | string
label?: FormFieldBase['label']
labels?: Labels
link?: boolean
name: FieldBase['name']

View File

@@ -0,0 +1,22 @@
import type { ArrayField } from '../../fields/config/types.js'
import type { ErrorComponent } from '../forms/Error.js'
import type { FieldMap } from '../forms/FieldMap.js'
import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js'
export type ArrayFieldProps = {
CustomRowLabel?: React.ReactNode
fieldMap: FieldMap
forceRender?: boolean
isSortable?: boolean
labels?: ArrayField['labels']
maxRows?: ArrayField['maxRows']
minRows?: ArrayField['minRows']
name?: string
width?: string
} & FormFieldBase
export type ArrayFieldLabelComponent = LabelComponent<'array'>
export type ArrayFieldDescriptionComponent = DescriptionComponent<'array'>
export type ArrayFieldErrorComponent = ErrorComponent<'array'>

View File

@@ -0,0 +1,32 @@
import type { Block, BlockField } from '../../fields/config/types.js'
import type { ErrorComponent } from '../forms/Error.js'
import type { FieldMap } from '../forms/FieldMap.js'
import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js'
export type BlocksFieldProps = {
blocks?: ReducedBlock[]
forceRender?: boolean
isSortable?: boolean
labels?: BlockField['labels']
maxRows?: number
minRows?: number
name?: string
slug?: string
width?: string
} & FormFieldBase
export type ReducedBlock = {
LabelComponent: Block['admin']['components']['Label']
custom?: Record<any, string>
fieldMap: FieldMap
imageAltText?: string
imageURL?: string
labels: BlockField['labels']
slug: string
}
export type BlocksFieldLabelComponent = LabelComponent<'blocks'>
export type BlocksFieldDescriptionComponent = DescriptionComponent<'blocks'>
export type BlocksFieldErrorComponent = ErrorComponent<'blocks'>

View File

@@ -0,0 +1,19 @@
import type { ErrorComponent } from '../forms/Error.js'
import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js'
export type CheckboxFieldProps = {
checked?: boolean
disableFormData?: boolean
id?: string
name?: string
onChange?: (val: boolean) => void
partialChecked?: boolean
path?: string
width?: string
} & FormFieldBase
export type CheckboxFieldLabelComponent = LabelComponent<'checkbox'>
export type CheckboxFieldDescriptionComponent = DescriptionComponent<'checkbox'>
export type CheckboxFieldErrorComponent = ErrorComponent<'checkbox'>

View File

@@ -0,0 +1,17 @@
import type { CodeField } from '../../fields/config/types.js'
import type { ErrorComponent } from '../forms/Error.js'
import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js'
export type CodeFieldProps = {
editorOptions?: CodeField['admin']['editorOptions']
language?: CodeField['admin']['language']
name?: string
path?: string
width: string
} & FormFieldBase
export type CodeFieldLabelComponent = LabelComponent<'code'>
export type CodeFieldDescriptionComponent = DescriptionComponent<'code'>
export type CodeFieldErrorComponent = ErrorComponent<'code'>

View File

@@ -0,0 +1,15 @@
import type { ErrorComponent } from '../forms/Error.js'
import type { FieldMap } from '../forms/FieldMap.js'
import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js'
export type CollapsibleFieldProps = {
fieldMap: FieldMap
initCollapsed?: boolean
width?: string
} & FormFieldBase
export type CollapsibleFieldLabelComponent = LabelComponent<'collapsible'>
export type CollapsibleFieldDescriptionComponent = DescriptionComponent<'collapsible'>
export type CollapsibleFieldErrorComponent = ErrorComponent<'collapsible'>

View File

@@ -0,0 +1,17 @@
import type { DateField } from '../../fields/config/types.js'
import type { ErrorComponent } from '../forms/Error.js'
import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js'
export type DateFieldProps = {
date?: DateField['admin']['date']
name?: string
path?: string
placeholder?: DateField['admin']['placeholder'] | string
width?: string
} & FormFieldBase
export type DateFieldLabelComponent = LabelComponent<'date'>
export type DateFieldDescriptionComponent = DescriptionComponent<'date'>
export type DateFieldErrorComponent = ErrorComponent<'date'>

View File

@@ -0,0 +1,17 @@
import type { EmailField } from '../../fields/config/types.js'
import type { ErrorComponent } from '../forms/Error.js'
import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js'
export type EmailFieldProps = {
autoComplete?: string
name?: string
path?: string
placeholder?: EmailField['admin']['placeholder']
width?: string
} & FormFieldBase
export type EmailFieldLabelComponent = LabelComponent<'email'>
export type EmailFieldDescriptionComponent = DescriptionComponent<'email'>
export type EmailFieldErrorComponent = ErrorComponent<'email'>

View File

@@ -0,0 +1,17 @@
import type { ErrorComponent } from '../forms/Error.js'
import type { FieldMap } from '../forms/FieldMap.js'
import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js'
export type GroupFieldProps = {
fieldMap: FieldMap
forceRender?: boolean
hideGutter?: boolean
name?: string
width?: string
} & FormFieldBase
export type GroupFieldLabelComponent = LabelComponent<'group'>
export type GroupFieldDescriptionComponent = DescriptionComponent<'group'>
export type GroupFieldErrorComponent = ErrorComponent<'group'>

View File

@@ -0,0 +1,16 @@
import type { ErrorComponent } from '../forms/Error.js'
import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js'
export type HiddenFieldProps = {
disableModifyingForm?: false
forceUsePathFromProps?: boolean
name?: string
path?: string
value?: unknown
} & FormFieldBase
export type HiddenFieldLabelComponent = LabelComponent<'hidden'>
export type HiddenFieldDescriptionComponent = DescriptionComponent<'hidden'>
export type HiddenFieldErrorComponent = ErrorComponent<'hidden'>

View File

@@ -0,0 +1,17 @@
import type { JSONField } from '../../fields/config/types.js'
import type { ErrorComponent } from '../forms/Error.js'
import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js'
export type JSONFieldProps = {
editorOptions?: JSONField['admin']['editorOptions']
jsonSchema?: Record<string, unknown>
name?: string
path?: string
width?: string
} & FormFieldBase
export type JSONFieldLabelComponent = LabelComponent<'json'>
export type JSONFieldDescriptionComponent = DescriptionComponent<'json'>
export type JSONFieldErrorComponent = ErrorComponent<'json'>

View File

@@ -0,0 +1,22 @@
import type { NumberField } from '../../fields/config/types.js'
import type { ErrorComponent } from '../forms/Error.js'
import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js'
export type NumberFieldProps = {
hasMany?: boolean
max?: number
maxRows?: number
min?: number
name?: string
onChange?: (e: number) => void
path?: string
placeholder?: NumberField['admin']['placeholder']
step?: number
width?: string
} & FormFieldBase
export type NumberFieldLabelComponent = LabelComponent<'number'>
export type NumberFieldDescriptionComponent = DescriptionComponent<'number'>
export type NumberFieldErrorComponent = ErrorComponent<'number'>

View File

@@ -0,0 +1,16 @@
import type { ErrorComponent } from '../forms/Error.js'
import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js'
export type PointFieldProps = {
name?: string
path?: string
placeholder?: string
step?: number
width?: string
} & FormFieldBase
export type PointFieldLabelComponent = LabelComponent<'point'>
export type PointFieldDescriptionComponent = DescriptionComponent<'point'>
export type PointFieldErrorComponent = ErrorComponent<'point'>

View File

@@ -0,0 +1,21 @@
import type { Option } from '../../fields/config/types.js'
import type { ErrorComponent } from '../forms/Error.js'
import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js'
export type RadioFieldProps = {
layout?: 'horizontal' | 'vertical'
name?: string
onChange?: OnChange
options?: Option[]
path?: string
value?: string
width?: string
} & FormFieldBase
export type OnChange<T = string> = (value: T) => void
export type RadioFieldLabelComponent = LabelComponent<'radio'>
export type RadioFieldDescriptionComponent = DescriptionComponent<'radio'>
export type RadioFieldErrorComponent = ErrorComponent<'radio'>

View File

@@ -0,0 +1,19 @@
import type { RelationshipField } from '../../fields/config/types.js'
import type { ErrorComponent } from '../forms/Error.js'
import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js'
export type RelationshipFieldProps = {
allowCreate?: RelationshipField['admin']['allowCreate']
hasMany?: boolean
isSortable?: boolean
name: string
relationTo?: RelationshipField['relationTo']
sortOptions?: RelationshipField['admin']['sortOptions']
width?: string
} & FormFieldBase
export type RelationshipFieldLabelComponent = LabelComponent<'relationship'>
export type RelationshipFieldDescriptionComponent = DescriptionComponent<'relationship'>
export type RelationshipFieldErrorComponent = ErrorComponent<'relationship'>

View File

@@ -0,0 +1,15 @@
import type { ErrorComponent } from '../forms/Error.js'
import type { MappedField } from '../forms/FieldMap.js'
import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js'
export type RichTextComponentProps = {
name: string
richTextComponentMap?: Map<string, MappedField[] | React.ReactNode>
width?: string
} & FormFieldBase
export type RichTextFieldLabelComponent = LabelComponent<'richText'>
export type RichTextFieldDescriptionComponent = DescriptionComponent<'richText'>
export type RichTextFieldErrorComponent = ErrorComponent<'richText'>

View File

@@ -0,0 +1,18 @@
import type { DescriptionComponent, FormFieldBase, LabelComponent } from 'payload'
import type { ErrorComponent } from '../forms/Error.js'
import type { FieldMap } from '../forms/FieldMap.js'
export type RowFieldProps = {
fieldMap: FieldMap
forceRender?: boolean
indexPath: string
path?: string
width?: string
} & FormFieldBase
export type RowFieldLabelComponent = LabelComponent<'row'>
export type RowFieldDescriptionComponent = DescriptionComponent<'row'>
export type RowFieldErrorComponent = ErrorComponent<'row'>

View File

@@ -0,0 +1,21 @@
import type { Option } from '../../fields/config/types.js'
import type { ErrorComponent } from '../forms/Error.js'
import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js'
export type SelectFieldProps = {
hasMany?: boolean
isClearable?: boolean
isSortable?: boolean
name?: string
onChange?: (e: string | string[]) => void
options?: Option[]
path?: string
value?: string
width?: string
} & FormFieldBase
export type SelectFieldLabelComponent = LabelComponent<'select'>
export type SelectFieldDescriptionComponent = DescriptionComponent<'select'>
export type SelectFieldErrorComponent = ErrorComponent<'select'>

View File

@@ -0,0 +1,24 @@
import type { TabsField } from '../../fields/config/types.js'
import type { ErrorComponent } from '../forms/Error.js'
import type { FieldMap } from '../forms/FieldMap.js'
import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js'
export type TabsFieldProps = {
forceRender?: boolean
name?: string
path?: string
tabs?: MappedTab[]
width?: string
} & FormFieldBase
export type MappedTab = {
fieldMap?: FieldMap
label: TabsField['tabs'][0]['label']
name?: string
}
export type TabsFieldLabelComponent = LabelComponent<'tabs'>
export type TabsFieldDescriptionComponent = DescriptionComponent<'tabs'>
export type TabsFieldErrorComponent = ErrorComponent<'tabs'>

View File

@@ -0,0 +1,23 @@
import type { TextField } from '../../fields/config/types.js'
import type { ErrorComponent } from '../forms/Error.js'
import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js'
export type TextFieldProps = {
hasMany?: boolean
inputRef?: React.MutableRefObject<HTMLInputElement>
maxLength?: number
maxRows?: number
minLength?: number
minRows?: number
name?: string
onKeyDown?: React.KeyboardEventHandler<HTMLInputElement>
path?: string
placeholder?: TextField['admin']['placeholder']
width?: string
} & FormFieldBase
export type TextFieldLabelComponent = LabelComponent<'text'>
export type TextFieldDescriptionComponent = DescriptionComponent<'text'>
export type TextFieldErrorComponent = ErrorComponent<'text'>

View File

@@ -0,0 +1,19 @@
import type { TextareaField } from '../../fields/config/types.js'
import type { ErrorComponent } from '../forms/Error.js'
import type { DescriptionComponent, FormFieldBase, LabelComponent } from '../types.js'
export type TextareaFieldProps = {
maxLength?: number
minLength?: number
name?: string
path?: string
placeholder?: TextareaField['admin']['placeholder']
rows?: number
width?: string
} & FormFieldBase
export type TextareaFieldLabelComponent = LabelComponent<'textarea'>
export type TextareaFieldDescriptionComponent = DescriptionComponent<'textarea'>
export type TextareaFieldErrorComponent = ErrorComponent<'textarea'>

View File

@@ -0,0 +1,17 @@
import type { DescriptionComponent, FormFieldBase, LabelComponent, UploadField } from 'payload'
import type { ErrorComponent } from '../forms/Error.js'
export type UploadFieldProps = {
filterOptions?: UploadField['filterOptions']
name?: string
path?: string
relationTo?: UploadField['relationTo']
width?: string
} & FormFieldBase
export type UploadFieldLabelComponent = LabelComponent<'upload'>
export type UploadFieldDescriptionComponent = DescriptionComponent<'upload'>
export type UploadFieldErrorComponent = ErrorComponent<'upload'>

View File

@@ -0,0 +1,86 @@
import type { ArrayFieldProps } from './Array.js'
import type { BlocksFieldProps } from './Blocks.js'
import type { CheckboxFieldProps } from './Checkbox.js'
import type { CodeFieldProps } from './Code.js'
import type { CollapsibleFieldProps } from './Collapsible.js'
import type { DateFieldProps } from './Date.js'
import type { EmailFieldProps } from './Email.js'
import type { GroupFieldProps } from './Group.js'
import type { HiddenFieldProps } from './Hidden.js'
import type { JSONFieldProps } from './JSON.js'
import type { NumberFieldProps } from './Number.js'
import type { PointFieldProps } from './Point.js'
import type { RadioFieldProps } from './Radio.js'
import type { RelationshipFieldProps } from './Relationship.js'
import type { RichTextComponentProps } from './RichText.js'
import type { RowFieldProps } from './Row.js'
import type { SelectFieldProps } from './Select.js'
import type { TabsFieldProps } from './Tabs.js'
import type { TextFieldProps } from './Text.js'
import type { TextareaFieldProps } from './Textarea.js'
import type { UploadFieldProps } from './Upload.js'
export type FieldComponentProps =
| ({
type: 'array'
} & ArrayFieldProps)
| ({
type: 'blocks'
} & BlocksFieldProps)
| ({
type: 'checkbox'
} & CheckboxFieldProps)
| ({
type: 'code'
} & CodeFieldProps)
| ({
type: 'collapsible'
} & CollapsibleFieldProps)
| ({
type: 'date'
} & DateFieldProps)
| ({
type: 'email'
} & EmailFieldProps)
| ({
type: 'group'
} & GroupFieldProps)
| ({
type: 'hidden'
} & HiddenFieldProps)
| ({
type: 'json'
} & JSONFieldProps)
| ({
type: 'number'
} & NumberFieldProps)
| ({
type: 'point'
} & PointFieldProps)
| ({
type: 'radio'
} & RadioFieldProps)
| ({
type: 'relationship'
} & RelationshipFieldProps)
| ({
type: 'richText'
} & RichTextComponentProps)
| ({
type: 'row'
} & RowFieldProps)
| ({
type: 'select'
} & SelectFieldProps)
| ({
type: 'tabs'
} & TabsFieldProps)
| ({
type: 'text'
} & TextFieldProps)
| ({
type: 'textarea'
} & TextareaFieldProps)
| ({
type: 'upload'
} & UploadFieldProps)

View File

@@ -1,7 +1,19 @@
export type ErrorProps = {
import type { CustomComponent, ServerProps } from '../../config/types.js'
import type { FieldComponentProps } from '../types.js'
import type { FieldTypes } from './FieldTypes.js'
export type GenericErrorProps = {
CustomError?: React.ReactNode
alignCaret?: 'center' | 'left' | 'right'
message?: string
path?: string
showError?: boolean
}
export type ErrorProps<T extends keyof FieldTypes = any> = {
type: T
} & FieldComponentProps &
GenericErrorProps &
Partial<ServerProps>
export type ErrorComponent<T extends keyof FieldTypes = any> = CustomComponent<ErrorProps<T>>

View File

@@ -0,0 +1,32 @@
import type { User } from '../../auth/types.js'
import type { LabelStatic, Locale } from '../../config/types.js'
import type { Validate } from '../../fields/config/types.js'
import type { DocumentPreferences } from '../../preferences/types.js'
import type { ErrorProps } from './Error.js'
import type { FieldDescriptionProps } from './FieldDescription.js'
import type { SanitizedLabelProps } from './Label.js'
export type FormFieldBase = {
AfterInput?: React.ReactNode
BeforeInput?: React.ReactNode
CustomDescription?: React.ReactNode
CustomError?: React.ReactNode
CustomLabel?: React.ReactNode
className?: string
custom?: Record<string, any>
descriptionProps?: Omit<FieldDescriptionProps, 'type'>
disabled?: boolean
docPreferences?: DocumentPreferences
errorProps?: Omit<ErrorProps, 'type'>
label?: LabelStatic | false
labelProps?: SanitizedLabelProps
locale?: Locale
localized?: boolean
path?: string
readOnly?: boolean
required?: boolean
rtl?: boolean
style?: React.CSSProperties
user?: User
validate?: Validate
}

View File

@@ -1,18 +1,24 @@
import type React from 'react'
import type { CustomComponent, LabelFunction } from '../../config/types.js'
import type { Payload } from '../../index.js'
import type { CustomComponent, LabelFunction, ServerProps } from '../../config/types.js'
import type { FieldComponentProps } from '../types.js'
import type { FieldTypes } from './FieldTypes.js'
export type DescriptionFunction = LabelFunction
export type DescriptionComponent = CustomComponent<FieldDescriptionProps>
export type DescriptionComponent<T extends keyof FieldTypes = any> = CustomComponent<
FieldDescriptionProps<T>
>
export type Description = DescriptionFunction | Record<string, string> | string
export type FieldDescriptionProps = {
export type GenericDescriptionProps = {
CustomDescription?: React.ReactNode
className?: string
description?: Record<string, string> | string
marginPlacement?: 'bottom' | 'top'
payload?: Payload
}
export type FieldDescriptionProps<T extends keyof FieldTypes = any> = {
type: T
} & FieldComponentProps &
GenericDescriptionProps &
Partial<ServerProps>

View File

@@ -0,0 +1,24 @@
import type { CellComponentProps, FieldComponentProps } from '../types.js'
import type { FieldTypes } from './FieldTypes.js'
export type MappedField = {
CustomCell?: React.ReactNode
CustomField?: React.ReactNode
cellComponentProps: CellComponentProps
custom?: Record<any, string>
disableBulkEdit?: boolean
disableListColumn?: boolean
disableListFilter?: boolean
disabled?: boolean
fieldComponentProps: FieldComponentProps
fieldIsPresentational: boolean
isFieldAffectingData: boolean
isHidden?: boolean
isSidebar?: boolean
localized: boolean
name?: string
type: keyof FieldTypes
unique?: boolean
}
export type FieldMap = MappedField[]

View File

@@ -1,11 +1,24 @@
export type LabelProps = {
CustomLabel?: React.ReactNode
import type { CustomComponent, ServerProps } from '../../config/types.js'
import type { FieldComponentProps } from '../fields/index.js'
import type { FormFieldBase } from './Field.js'
import type { FieldTypes } from './FieldTypes.js'
export type GenericLabelProps = {
as?: 'label' | 'span'
htmlFor?: string
label?: Record<string, string> | string
required?: boolean
schemaPath?: string
unstyled?: boolean
}
} & FormFieldBase
export type SanitizedLabelProps = Omit<LabelProps, 'label' | 'required'>
export type LabelProps<T extends keyof FieldTypes = any> = {
type: T
} & FieldComponentProps &
GenericLabelProps &
Partial<ServerProps>
export type SanitizedLabelProps<T extends keyof FieldTypes = any> = Omit<
LabelProps<T>,
'label' | 'required'
>
export type LabelComponent<T extends keyof FieldTypes = any> = CustomComponent<LabelProps<T>>

View File

@@ -7,6 +7,7 @@ export type { CustomPreviewButton } from './elements/PreviewButton.js'
export type { CustomPublishButton } from './elements/PublishButton.js'
export type { CustomSaveButton } from './elements/SaveButton.js'
export type { CustomSaveDraftButton } from './elements/SaveDraftButton.js'
export type {
DocumentTab,
DocumentTabComponent,
@@ -14,20 +15,191 @@ export type {
DocumentTabConfig,
DocumentTabProps,
} from './elements/Tab.js'
export type { CustomUpload } from './elements/Upload.js'
export type {
WithServerSidePropsComponent,
WithServerSidePropsComponentProps,
} from './elements/WithServerSideProps.js'
export type { ErrorProps } from './forms/Error.js'
export type {
ArrayFieldDescriptionComponent,
ArrayFieldErrorComponent,
ArrayFieldLabelComponent,
ArrayFieldProps,
} from './fields/Array.js'
export type { ReducedBlock } from './fields/Blocks.js'
export type {
BlocksFieldDescriptionComponent,
BlocksFieldErrorComponent,
BlocksFieldLabelComponent,
BlocksFieldProps,
} from './fields/Blocks.js'
export type {
CheckboxFieldDescriptionComponent,
CheckboxFieldErrorComponent,
CheckboxFieldLabelComponent,
CheckboxFieldProps,
} from './fields/Checkbox.js'
export type {
CodeFieldDescriptionComponent,
CodeFieldErrorComponent,
CodeFieldLabelComponent,
CodeFieldProps,
} from './fields/Code.js'
export type {
CollapsibleFieldDescriptionComponent,
CollapsibleFieldErrorComponent,
CollapsibleFieldLabelComponent,
CollapsibleFieldProps,
} from './fields/Collapsible.js'
export type {
DateFieldDescriptionComponent,
DateFieldErrorComponent,
DateFieldLabelComponent,
DateFieldProps,
} from './fields/Date.js'
export type {
EmailFieldDescriptionComponent,
EmailFieldErrorComponent,
EmailFieldLabelComponent,
EmailFieldProps,
} from './fields/Email.js'
export type {
GroupFieldDescriptionComponent,
GroupFieldErrorComponent,
GroupFieldLabelComponent,
GroupFieldProps,
} from './fields/Group.js'
export type {
HiddenFieldDescriptionComponent,
HiddenFieldErrorComponent,
HiddenFieldLabelComponent,
HiddenFieldProps,
} from './fields/Hidden.js'
export type {
JSONFieldDescriptionComponent,
JSONFieldErrorComponent,
JSONFieldLabelComponent,
JSONFieldProps,
} from './fields/JSON.js'
export type {
NumberFieldDescriptionComponent,
NumberFieldErrorComponent,
NumberFieldLabelComponent,
NumberFieldProps,
} from './fields/Number.js'
export type {
PointFieldDescriptionComponent,
PointFieldErrorComponent,
PointFieldLabelComponent,
PointFieldProps,
} from './fields/Point.js'
export type {
RadioFieldDescriptionComponent,
RadioFieldErrorComponent,
RadioFieldLabelComponent,
RadioFieldProps,
} from './fields/Radio.js'
export type {
RelationshipFieldDescriptionComponent,
RelationshipFieldErrorComponent,
RelationshipFieldLabelComponent,
RelationshipFieldProps,
} from './fields/Relationship.js'
export type {
RichTextComponentProps,
RichTextFieldDescriptionComponent,
RichTextFieldErrorComponent,
RichTextFieldLabelComponent,
} from './fields/RichText.js'
export type {
RowFieldDescriptionComponent,
RowFieldErrorComponent,
RowFieldLabelComponent,
RowFieldProps,
} from './fields/Row.js'
export type {
SelectFieldDescriptionComponent,
SelectFieldErrorComponent,
SelectFieldLabelComponent,
SelectFieldProps,
} from './fields/Select.js'
export type { MappedTab } from './fields/Tabs.js'
export type {
TabsFieldDescriptionComponent,
TabsFieldErrorComponent,
TabsFieldLabelComponent,
TabsFieldProps,
} from './fields/Tabs.js'
export type {
TextFieldDescriptionComponent,
TextFieldErrorComponent,
TextFieldLabelComponent,
TextFieldProps,
} from './fields/Text.js'
export type {
TextareaFieldDescriptionComponent,
TextareaFieldErrorComponent,
TextareaFieldLabelComponent,
TextareaFieldProps,
} from './fields/Textarea.js'
export type {
UploadFieldDescriptionComponent,
UploadFieldErrorComponent,
UploadFieldLabelComponent,
UploadFieldProps,
} from './fields/Upload.js'
export type { FieldComponentProps } from './fields/index.js'
export type { ErrorComponent, ErrorProps, GenericErrorProps } from './forms/Error.js'
export type { FormFieldBase } from './forms/Field.js'
export type {
Description,
DescriptionComponent,
DescriptionFunction,
FieldDescriptionProps,
GenericDescriptionProps,
} from './forms/FieldDescription.js'
export type { MappedField } from './forms/FieldMap.js'
export type { FieldMap } from './forms/FieldMap.js'
export type { Data, FilterOptionsResult, FormField, FormState, Row } from './forms/Form.js'
export type { LabelProps, SanitizedLabelProps } from './forms/Label.js'
export type {
GenericLabelProps,
LabelComponent,
LabelProps,
SanitizedLabelProps,
} from './forms/Label.js'
export type { RowLabel, RowLabelComponent } from './forms/RowLabel.js'

View File

@@ -2,7 +2,6 @@ import type { AllOperations, PayloadRequest } from '../types/index.js'
import type { Permissions } from './types.js'
import { getEntityPolicies } from '../utilities/getEntityPolicies.js'
import isolateObjectProperty from '../utilities/isolateObjectProperty.js'
type GetAccessResultsArgs = {
req: PayloadRequest
@@ -45,9 +44,7 @@ export async function getAccessResults({ req }: GetAccessResultsArgs): Promise<P
type: 'collection',
entity: collection,
operations: collectionOperations,
// Do not re-use our existing req object, as we need a new req.transactionID. Cannot re-use our existing one, as this is run in parallel (Promise.all above) which is
// not supported on the same transaction ID. Not passing a transaction ID here creates a new transaction ID.
req: isolateObjectProperty(req, 'transactionID'),
req,
})
results.collections = {
...results.collections,
@@ -68,9 +65,7 @@ export async function getAccessResults({ req }: GetAccessResultsArgs): Promise<P
type: 'global',
entity: global,
operations: globalOperations,
// Do not re-use our existing req object, as we need a new req.transactionID. Cannot re-use our existing one, as this is run in parallel (Promise.all above) which is
// not supported on the same transaction ID. Not passing a transaction ID here creates a new transaction ID.
req: isolateObjectProperty(req, 'transactionID'),
req,
})
results.globals = {
...results.globals,

View File

@@ -1,8 +1,6 @@
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 { adminInit as adminInitTelemetry } from '../../utilities/telemetry/events/adminInit.js'
import { getAccessResults } from '../getAccessResults.js'
@@ -17,10 +15,7 @@ export const accessOperation = async (args: Arguments): Promise<Permissions> =>
adminInitTelemetry(req)
try {
const shouldCommit = await initTransaction(req)
const results = getAccessResults({ req })
if (shouldCommit) await commitTransaction(req)
return results
return getAccessResults({ req })
} catch (e: unknown) {
await killTransaction(req)
throw e

View File

@@ -23,6 +23,7 @@ import type {
EntityDescriptionComponent,
GeneratePreviewURL,
LabelFunction,
LabelStatic,
LivePreviewConfig,
OpenGraphConfig,
} from '../../config/types.js'
@@ -438,8 +439,8 @@ export type CollectionConfig<TSlug extends CollectionSlug = any> = {
* Label configuration
*/
labels?: {
plural?: LabelFunction | Record<string, string> | string
singular?: LabelFunction | Record<string, string> | string
plural?: LabelFunction | LabelStatic
singular?: LabelFunction | LabelStatic
}
slug: string
/**

View File

@@ -6,8 +6,6 @@ import type { Collection } from '../config/types.js'
import executeAccess from '../../auth/executeAccess.js'
import { combineQueries } from '../../database/combineQueries.js'
import { validateQueryPaths } from '../../database/queryValidation/validateQueryPaths.js'
import { commitTransaction } from '../../utilities/commitTransaction.js'
import { initTransaction } from '../../utilities/initTransaction.js'
import { killTransaction } from '../../utilities/killTransaction.js'
import { buildAfterOperation } from './utils.js'
@@ -25,8 +23,6 @@ export const countOperation = async <TSlug extends CollectionSlug>(
let args = incomingArgs
try {
const shouldCommit = await initTransaction(args.req)
// /////////////////////////////////////
// beforeOperation - Collection
// /////////////////////////////////////
@@ -102,8 +98,6 @@ export const countOperation = async <TSlug extends CollectionSlug>(
// Return results
// /////////////////////////////////////
if (shouldCommit) await commitTransaction(req)
return result
} catch (error: unknown) {
await killTransaction(args.req)

View File

@@ -1,7 +1,7 @@
import httpStatus from 'http-status'
import type { AccessResult } from '../../config/types.js'
import type { CollectionSlug, GeneratedTypes } from '../../index.js'
import type { CollectionSlug } from '../../index.js'
import type { PayloadRequest, Where } from '../../types/index.js'
import type { BeforeOperationHook, Collection, DataFromCollectionSlug } from '../config/types.js'

View File

@@ -1,4 +1,4 @@
import type { CollectionSlug, JsonObject, TypeWithID } from '../../index.js'
import type { CollectionSlug } from '../../index.js'
import type { PayloadRequest } from '../../types/index.js'
import type { BeforeOperationHook, Collection, DataFromCollectionSlug } from '../config/types.js'

View File

@@ -2,9 +2,7 @@ import type { CollectionPermission } from '../../auth/index.js'
import type { AllOperations, PayloadRequest } from '../../types/index.js'
import type { Collection } from '../config/types.js'
import { commitTransaction } from '../../utilities/commitTransaction.js'
import { getEntityPolicies } from '../../utilities/getEntityPolicies.js'
import { initTransaction } from '../../utilities/initTransaction.js'
import { killTransaction } from '../../utilities/killTransaction.js'
const allOperations: AllOperations[] = ['create', 'read', 'update', 'delete']
@@ -37,8 +35,6 @@ export async function docAccessOperation(args: Arguments): Promise<CollectionPer
}
try {
const shouldCommit = await initTransaction(req)
const result = await getEntityPolicies({
id,
type: 'collection',
@@ -47,8 +43,6 @@ export async function docAccessOperation(args: Arguments): Promise<CollectionPer
req,
})
if (shouldCommit) await commitTransaction(req)
return result
} catch (e: unknown) {
await killTransaction(req)

View File

@@ -8,8 +8,6 @@ import executeAccess from '../../auth/executeAccess.js'
import { combineQueries } from '../../database/combineQueries.js'
import { validateQueryPaths } from '../../database/queryValidation/validateQueryPaths.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 { buildVersionCollectionFields } from '../../versions/buildCollectionFields.js'
import { appendVersionToQueryKey } from '../../versions/drafts/appendVersionToQueryKey.js'
@@ -38,8 +36,6 @@ export const findOperation = async <TSlug extends CollectionSlug>(
let args = incomingArgs
try {
const shouldCommit = await initTransaction(args.req)
// /////////////////////////////////////
// beforeOperation - Collection
// /////////////////////////////////////
@@ -253,8 +249,6 @@ export const findOperation = async <TSlug extends CollectionSlug>(
// Return results
// /////////////////////////////////////
if (shouldCommit) await commitTransaction(req)
return result
} catch (error: unknown) {
await killTransaction(args.req)

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