Compare commits
50 Commits
feat/forma
...
feat/beta-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
008437d62f | ||
|
|
95112a873b | ||
|
|
25da9b62d1 | ||
|
|
1e5e07489c | ||
|
|
bcb1d4eb57 | ||
|
|
268fff2645 | ||
|
|
349d68c719 | ||
|
|
9fefe79998 | ||
|
|
9f73743806 | ||
|
|
cb00bc5856 | ||
|
|
bd22bb28aa | ||
|
|
ed1e460ae7 | ||
|
|
78ac4fd89e | ||
|
|
c328c42b72 | ||
|
|
8ede4d0098 | ||
|
|
31ad34595e | ||
|
|
33278aa8d8 | ||
|
|
aad186c8b4 | ||
|
|
b5b2bb1907 | ||
|
|
6f5cf5d916 | ||
|
|
aaf3a39f7e | ||
|
|
5ef2951829 | ||
|
|
a943487fca | ||
|
|
3a941c7c8a | ||
|
|
354588898f | ||
|
|
ada9978a8c | ||
|
|
874279c530 | ||
|
|
7ed6634bc5 | ||
|
|
09a0ee3ab9 | ||
|
|
67acab2cd5 | ||
|
|
b3dc6cc811 | ||
|
|
5cd0c7ec7d | ||
|
|
cd592cb3a2 | ||
|
|
6d066c2ba4 | ||
|
|
1dc428823a | ||
|
|
c8da9b148c | ||
|
|
2021028d64 | ||
|
|
2ea56fe0f8 | ||
|
|
ea16119af7 | ||
|
|
97837f0708 | ||
|
|
e734d51760 | ||
|
|
5655266daa | ||
|
|
f9e5573c1e | ||
|
|
e823051a8e | ||
|
|
49df61d9ec | ||
|
|
4704c8db2a | ||
|
|
a64f37e014 | ||
|
|
55c6ce92b0 | ||
|
|
2ecbcee378 | ||
|
|
70f2e1698a |
1
.github/workflows/pr-title.yml
vendored
1
.github/workflows/pr-title.yml
vendored
@@ -38,6 +38,7 @@ jobs:
|
||||
db-\*
|
||||
db-mongodb
|
||||
db-postgres
|
||||
db-sqlite
|
||||
email-nodemailer
|
||||
eslint
|
||||
graphql
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -335,7 +335,6 @@ import {
|
||||
FieldMap,
|
||||
File,
|
||||
Form,
|
||||
FormFieldBase,
|
||||
FormLoadingOverlayToggle,
|
||||
FormSubmit,
|
||||
GenerateConfirmation,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 || {}),
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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),
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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')}\`)`
|
||||
|
||||
@@ -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}
|
||||
}
|
||||
`
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -10,5 +10,5 @@ export const defaultDrizzleSnapshot: DrizzleSQLiteSnapshotJSON = {
|
||||
enums: {},
|
||||
prevId: '00000000-0000-0000-0000-00000000000',
|
||||
tables: {},
|
||||
version: '3',
|
||||
version: '5',
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
}
|
||||
`
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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
|
||||
})
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
|
||||
import type { Field, SanitizedConfig , TabAsField } from 'payload'
|
||||
import type { Field, SanitizedConfig, TabAsField } from 'payload'
|
||||
|
||||
import { fieldAffectsData } from 'payload/shared'
|
||||
|
||||
|
||||
@@ -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') {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -39,7 +39,7 @@ export const index = deepMerge(
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'react-hooks/rules-of-hooks': 'warn',
|
||||
'react-hooks/rules-of-hooks': 'error',
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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')}
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
? {
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { MappedField, TabsFieldProps } from '@payloadcms/ui'
|
||||
import type { MappedField, TabsFieldProps } from 'payload'
|
||||
|
||||
import React from 'react'
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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,
|
||||
}))
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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']
|
||||
|
||||
22
packages/payload/src/admin/fields/Array.ts
Normal file
22
packages/payload/src/admin/fields/Array.ts
Normal 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'>
|
||||
32
packages/payload/src/admin/fields/Blocks.ts
Normal file
32
packages/payload/src/admin/fields/Blocks.ts
Normal 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'>
|
||||
19
packages/payload/src/admin/fields/Checkbox.ts
Normal file
19
packages/payload/src/admin/fields/Checkbox.ts
Normal 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'>
|
||||
17
packages/payload/src/admin/fields/Code.ts
Normal file
17
packages/payload/src/admin/fields/Code.ts
Normal 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'>
|
||||
15
packages/payload/src/admin/fields/Collapsible.ts
Normal file
15
packages/payload/src/admin/fields/Collapsible.ts
Normal 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'>
|
||||
17
packages/payload/src/admin/fields/Date.ts
Normal file
17
packages/payload/src/admin/fields/Date.ts
Normal 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'>
|
||||
17
packages/payload/src/admin/fields/Email.ts
Normal file
17
packages/payload/src/admin/fields/Email.ts
Normal 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'>
|
||||
17
packages/payload/src/admin/fields/Group.ts
Normal file
17
packages/payload/src/admin/fields/Group.ts
Normal 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'>
|
||||
16
packages/payload/src/admin/fields/Hidden.ts
Normal file
16
packages/payload/src/admin/fields/Hidden.ts
Normal 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'>
|
||||
17
packages/payload/src/admin/fields/JSON.ts
Normal file
17
packages/payload/src/admin/fields/JSON.ts
Normal 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'>
|
||||
22
packages/payload/src/admin/fields/Number.ts
Normal file
22
packages/payload/src/admin/fields/Number.ts
Normal 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'>
|
||||
16
packages/payload/src/admin/fields/Point.ts
Normal file
16
packages/payload/src/admin/fields/Point.ts
Normal 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'>
|
||||
21
packages/payload/src/admin/fields/Radio.ts
Normal file
21
packages/payload/src/admin/fields/Radio.ts
Normal 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'>
|
||||
19
packages/payload/src/admin/fields/Relationship.ts
Normal file
19
packages/payload/src/admin/fields/Relationship.ts
Normal 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'>
|
||||
15
packages/payload/src/admin/fields/RichText.ts
Normal file
15
packages/payload/src/admin/fields/RichText.ts
Normal 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'>
|
||||
18
packages/payload/src/admin/fields/Row.ts
Normal file
18
packages/payload/src/admin/fields/Row.ts
Normal 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'>
|
||||
21
packages/payload/src/admin/fields/Select.ts
Normal file
21
packages/payload/src/admin/fields/Select.ts
Normal 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'>
|
||||
24
packages/payload/src/admin/fields/Tabs.ts
Normal file
24
packages/payload/src/admin/fields/Tabs.ts
Normal 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'>
|
||||
23
packages/payload/src/admin/fields/Text.ts
Normal file
23
packages/payload/src/admin/fields/Text.ts
Normal 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'>
|
||||
19
packages/payload/src/admin/fields/Textarea.ts
Normal file
19
packages/payload/src/admin/fields/Textarea.ts
Normal 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'>
|
||||
17
packages/payload/src/admin/fields/Upload.ts
Normal file
17
packages/payload/src/admin/fields/Upload.ts
Normal 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'>
|
||||
86
packages/payload/src/admin/fields/index.ts
Normal file
86
packages/payload/src/admin/fields/index.ts
Normal 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)
|
||||
@@ -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>>
|
||||
|
||||
32
packages/payload/src/admin/forms/Field.ts
Normal file
32
packages/payload/src/admin/forms/Field.ts
Normal 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
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
24
packages/payload/src/admin/forms/FieldMap.ts
Normal file
24
packages/payload/src/admin/forms/FieldMap.ts
Normal 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[]
|
||||
@@ -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>>
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
/**
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user