Compare commits

...

25 Commits

Author SHA1 Message Date
Dan Ribbens
26a2f8d0d7 test(db-postgres): reproduce graphql transaction bug 2023-10-19 16:23:31 -04:00
Dan Ribbens
c7a02803a0 wip configurable mongodb transactions 2023-10-19 10:19:55 -04:00
Take Weiland
bcdd2d626f fix: handle graphQL: false on globals when building policy type (#3729) 2023-10-19 09:13:51 -04:00
Elliot DeNolf
67682248c8 chore: more master -> main readme renames 2023-10-19 09:08:40 -04:00
Jacob Fletcher
7c52d6ee28 Merge pull request #3745 from payloadcms/fix/misc-admin
Fix/misc admin
2023-10-19 09:06:20 -04:00
Elliot DeNolf
bc65b53ce5 chore(release): eslint-config-payload/1.0.0 2023-10-18 21:37:35 -04:00
Elliot DeNolf
c8cc6ea1cc chore(script): more prompts during publish 2023-10-18 21:29:26 -04:00
Jacob Fletcher
4e05e6fd85 fix(a11y): tab indices 2023-10-18 17:55:55 -04:00
Jacob Fletcher
6988a68eaf fix: renders id as fallback title in DeleteDocument 2023-10-18 17:55:55 -04:00
Jacob Fletcher
a272692726 chore: refines drawer and blur styles 2023-10-18 17:55:39 -04:00
Dan Ribbens
229e4459cb fix(db-postgres): block and array inserts error (#3714)
Co-authored-by: James <james@trbl.design>
2023-10-18 16:53:26 -04:00
Take Weiland
056585ed31 fix: properly handles hideAPIURL (#3721) 2023-10-18 16:36:57 -04:00
Elliot DeNolf
b545433ee6 chore(templates): add payload helper npm script 2023-10-18 16:11:58 -04:00
Elliot DeNolf
4c938b5f9e chore(plugin-nested-docs): lint fix (#3740) 2023-10-18 14:40:48 -04:00
Elliot DeNolf
f1d8fa9999 chore: add 2.0 announcement banner 2023-10-18 14:38:58 -04:00
Jarrod Flesch
1670a603f6 chore: adjust where sharp types are imported from (#3645) 2023-10-18 11:44:49 -04:00
Elliot DeNolf
22f1fa8fc9 chore: update issue template and repro guide 2023-10-18 11:43:11 -04:00
Jacob Fletcher
370e8d1938 chore: replaces bg blur in document controls (#3736) 2023-10-18 11:40:25 -04:00
Elliot DeNolf
3a3eab761e fix: filesRequiredOnCreate typing, tests, linting (#3737) 2023-10-18 11:27:55 -04:00
Jacob Fletcher
238f7e1b94 chore(examples/live-preview): pins @payloadcms/live-preview-react to latest 2023-10-18 10:18:01 -04:00
Jacob Fletcher
58e2083882 Merge pull request #3719 from payloadcms/fix/live-preview/uploads
Fix/live preview/uploads
2023-10-17 17:05:30 -04:00
Jacob Fletcher
20cde242fb fix(live-preview): properly handles uploads and hasOne monomorphic relationships 2023-10-17 17:00:59 -04:00
Elliot DeNolf
f50a392d59 chore(script): update release script [skip ci] 2023-10-17 17:00:14 -04:00
Elliot DeNolf
fa1740d906 chore: update changelog 2023-10-17 16:51:24 -04:00
Jacob Fletcher
ebd5e6ae8f chore: types fieldSchemaToJSON 2023-10-17 16:39:36 -04:00
98 changed files with 2891 additions and 1364 deletions

View File

@@ -10,7 +10,12 @@ body:
id: reproduction-link
attributes:
label: Link to reproduction
description: Please add a link to a reproduction. See the fork [reproduction-guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md) for more information.
description: Want us to look into your issue faster? Follow the [reproduction-guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md) for more information.
validations:
required: false
- type: textarea
attributes:
label: Describe the Bug
validations:
required: true
- type: textarea
@@ -19,11 +24,6 @@ body:
description: Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the linked minimal reproduction. Screenshots can be provided in the issue body below. If using code blocks, make sure that [syntax highlighting is correct](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting) and double check that the rendered preview is not broken.
validations:
required: true
- type: textarea
attributes:
label: Describe the Bug
validations:
required: true
- type: input
id: version
attributes:

View File

@@ -1,10 +1,11 @@
# Reproduction Guide
1. [fork](https://github.com/payloadcms/payload/fork) this repo
2. run `yarn` to install dependencies
3. open up the `test/_community` directory
4. add any necessary `collections/globals/fields` in this directory to recreate the issue you are experiencing
5. run `yarn dev _community` to start the admin panel
1. [Fork](https://github.com/payloadcms/payload/fork) this repo
2. Optionally, create a new branch for your reproduction
3. Run `pnpm install` to install dependencies
4. Open up the `test/_community` directory
5. Add any necessary `collections/globals/fields` in this directory to recreate the issue you are experiencing
6. Run `pnpm dev _community` to start the admin panel
**NOTE:** The goal is to isolate the problem by reducing the number of `collections/globals/fields` you add to the `test/_community` folder. This folder is _not_ meant for you to copy your project into, but rather recreate the issue you are experiencing with minimal config.
@@ -21,7 +22,7 @@
- `config.ts` - This is the _granular_ Payload config for testing. It should be as lightweight as possible. Reference existing configs for an example
- `int.spec.ts` [Optional] - This is the test file run by jest. Any test file must have a `*int.spec.ts` suffix.
- `e2e.spec.ts` [Optional] - This is the end-to-end test file that will load up the admin UI using the above config and run Playwright tests.
- `payload-types.ts` - Generated types from `config.ts`. Generate this file by running `yarn dev:generate-types _community`.
- `payload-types.ts` - Generated types from `config.ts`. Generate this file by running `pnpm dev:generate-types _community`.
The directory split up in this way specifically to reduce friction when creating tests and to add the ability to boot up Payload with that specific config. You should modify the files in `test/_community` to get started.
@@ -44,7 +45,7 @@ There are a couple ways run integration tests:
- **Manually** - you can run all int tests in the `/test/_community/int.spec.ts` file by running the following command:
```bash
yarn test:int _community
pnpm test:int _community
```
### Running E2E tests (Admin Panel UI tests)

View File

@@ -1,4 +1,12 @@
## [2.0.10](https://github.com/payloadcms/payload/compare/v2.0.9...v2.0.10) (2023-10-17)
### Features
* filesRequired is optional for uploads ([#3668](https://github.com/payloadcms/payload/pull/3668)) ([48de897](https://github.com/payloadcms/payload/commit/48de89794b2c5d94183090b0830fd355d8d6c6f3))
### Bug Fixes
* Register first user verify update missing transaction id / req ([#3665](https://github.com/payloadcms/payload/pull/3665)) ([68c5a5751](https://github.com/payloadcms/payload/commit/68c5a57515ffbba37c9194a75d0f672bdb10d96b))
## [2.0.8](https://github.com/payloadcms/payload/compare/v2.0.7...v2.0.8) (2023-10-17)

View File

@@ -11,19 +11,23 @@ keywords: documentation, getting started, guide, Content Management System, cms,
title="Payload Introduction - Closing the Gap Between Headless CMS and Application Frameworks"
/>
Payload is a headless CMS and application framework. It's meant to provide a massive boost to your
development process, but importantly, stay out of your way as your apps get more complex.
<Banner type="success">
Payload is a headless CMS and application framework. Its meant to provide a massive boost to your
development process, but importantly, stay out of your way as your apps get more complex.
<strong>Payload 2.0 has been released!</strong>
<br />
Includes Postgres support, Live Preview, Lexical Editor, and more. <a href="/blog/payload-2-0">Read the announcement</a>.
</Banner>
Out of the box, Payload gives you a lot of the things that you often need when developing a new website, web app, or native app:
- A MongoDB database to store your data
- A database to store your data (Postgres and MongoDB supported)
- A way to store, retrieve, and manipulate data of any shape via full REST and GraphQL APIs
- Authentication—complete with commonly required functionality like registration, email verification, login, & password reset
- Deep access control to your data, based on document or field-level functions
- File storage and access control
- A beautiful admin UI thats generated specifically to suit your data
- A beautiful admin UI that's generated specifically to suit your data
## What does "headless" mean?

View File

@@ -1,6 +1,6 @@
# Form Builder Example Front-End
This is a [Next.js](https://nextjs.org) app using the [Pages Router](https://nextjs.org/docs/pages). It was made explicitly for Payload's [Form Builder Example](https://github.com/payloadcms/payload/tree/master/examples/form-builder/payload).
This is a [Next.js](https://nextjs.org) app using the [Pages Router](https://nextjs.org/docs/pages). It was made explicitly for Payload's [Form Builder Example](https://github.com/payloadcms/payload/tree/main/examples/form-builder/payload).
> This example uses the Pages Router, the legacy API of Next.js. If your app is using the latest [App Router](https://nextjs.org/docs/app), we will add an example for that soon.
@@ -8,7 +8,7 @@ This is a [Next.js](https://nextjs.org) app using the [Pages Router](https://nex
### Payload
First you'll need a running Payload app. There is one made explicitly for this example and [can be found here](https://github.com/payloadcms/payload/tree/master/examples/form-builder/payload). If you have not done so already, clone it down and follow the setup instructions there.
First you'll need a running Payload app. There is one made explicitly for this example and [can be found here](https://github.com/payloadcms/payload/tree/main/examples/form-builder/payload). If you have not done so already, clone it down and follow the setup instructions there.
### Next.js App
@@ -18,7 +18,7 @@ First you'll need a running Payload app. There is one made explicitly for this e
4. `yarn dev` or `npm run dev` to start the server
5. `open http://localhost:3001` to see the result
Once running you will find a couple seeded pages on your local environment with some basic instructions. You can also start editing the pages by modifying the documents within Payload. See the [Form Builder Example](https://github.com/payloadcms/payload/tree/master/examples/form-builder/payload) for full details.
Once running you will find a couple seeded pages on your local environment with some basic instructions. You can also start editing the pages by modifying the documents within Payload. See the [Form Builder Example](https://github.com/payloadcms/payload/tree/main/examples/form-builder/payload) for full details.
## Learn More

View File

@@ -1,14 +1,14 @@
# Payload Live Preview Example Front-End
This is a [Next.js](https://nextjs.org) app using the [App Router](https://nextjs.org/docs/app). It was made explicitly for Payload's [Live Preview Example](https://github.com/payloadcms/payload/tree/master/examples/live-preview/payload).
This is a [Next.js](https://nextjs.org) app using the [App Router](https://nextjs.org/docs/app). It was made explicitly for Payload's [Live Preview Example](https://github.com/payloadcms/payload/tree/main/examples/live-preview/payload).
> This example uses the App Router, the latest API of Next.js. If your app is using the legacy [Pages Router](https://nextjs.org/docs/pages), check out the official [Pages Router Example](https://github.com/payloadcms/payload/tree/master/examples/live-preview/next-pages).
> This example uses the App Router, the latest API of Next.js. If your app is using the legacy [Pages Router](https://nextjs.org/docs/pages), check out the official [Pages Router Example](https://github.com/payloadcms/payload/tree/main/examples/live-preview/next-pages).
## Getting Started
### Payload
First you'll need a running Payload app. There is one made explicitly for this example and [can be found here](https://github.com/payloadcms/payload/tree/master/examples/live-preview/payload). If you have not done so already, clone it down and follow the setup instructions there. This will provide all the necessary APIs that your Next.js app requires for authentication.
First you'll need a running Payload app. There is one made explicitly for this example and [can be found here](https://github.com/payloadcms/payload/tree/main/examples/live-preview/payload). If you have not done so already, clone it down and follow the setup instructions there. This will provide all the necessary APIs that your Next.js app requires for authentication.
### Next.js
@@ -18,7 +18,7 @@ First you'll need a running Payload app. There is one made explicitly for this e
4. `yarn dev` or `npm run dev` to start the server
5. `open http://localhost:3001` to see the result
Once running you will find a couple seeded pages on your local environment with some basic instructions. You can also start editing the pages by modifying the documents within Payload. See the [Live Preview Example](https://github.com/payloadcms/payload/tree/master/examples/live-preview/payload) for full details.
Once running you will find a couple seeded pages on your local environment with some basic instructions. You can also start editing the pages by modifying the documents within Payload. See the [Live Preview Example](https://github.com/payloadcms/payload/tree/main/examples/live-preview/payload) for full details.
## Learn More
@@ -32,6 +32,6 @@ You can check out [the Payload GitHub repository](https://github.com/payloadcms/
## Deployment
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new) from the creators of Next.js. You could also combine this app into a [single Express server](https://github.com/payloadcms/payload/tree/master/examples/custom-server) and deploy in to [Payload Cloud](https://payloadcms.com/new/import).
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new) from the creators of Next.js. You could also combine this app into a [single Express server](https://github.com/payloadcms/payload/tree/main/examples/custom-server) and deploy in to [Payload Cloud](https://payloadcms.com/new/import).
Check out our [Payload deployment documentation](https://payloadcms.com/docs/production/deployment) or the [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.

View File

@@ -9,7 +9,7 @@
"lint": "next lint"
},
"dependencies": {
"@payloadcms/live-preview-react": "^1.0.0-beta.3",
"@payloadcms/live-preview-react": "latest",
"escape-html": "^1.0.3",
"next": "^13.4.8",
"payload-admin-bar": "^1.0.6",

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,14 @@
# Payload Live Preview Example Front-End
This is a [Next.js](https://nextjs.org) app using the [Pages Router](https://nextjs.org/docs/pages). It was made explicitly for Payload's [Live Preview Example](https://github.com/payloadcms/payload/tree/master/examples/live-preview/payload).
This is a [Next.js](https://nextjs.org) app using the [Pages Router](https://nextjs.org/docs/pages). It was made explicitly for Payload's [Live Preview Example](https://github.com/payloadcms/payload/tree/main/examples/live-preview/payload).
> This example uses the Pages Router, the legacy API of Next.js. If your app is using the latest [App Router](https://nextjs.org/docs/app), check out the official [App Router Example](https://github.com/payloadcms/payload/tree/master/examples/live-preview/next-app).
> This example uses the Pages Router, the legacy API of Next.js. If your app is using the latest [App Router](https://nextjs.org/docs/app), check out the official [App Router Example](https://github.com/payloadcms/payload/tree/main/examples/live-preview/next-app).
## Getting Started
### Payload
First you'll need a running Payload app. There is one made explicitly for this example and [can be found here](https://github.com/payloadcms/payload/tree/master/examples/live-preview/payload). If you have not done so already, clone it down and follow the setup instructions there. This will provide all the necessary APIs that your Next.js app requires for authentication.
First you'll need a running Payload app. There is one made explicitly for this example and [can be found here](https://github.com/payloadcms/payload/tree/main/examples/live-preview/payload). If you have not done so already, clone it down and follow the setup instructions there. This will provide all the necessary APIs that your Next.js app requires for authentication.
### Next.js
@@ -18,7 +18,7 @@ First you'll need a running Payload app. There is one made explicitly for this e
4. `yarn dev` or `npm run dev` to start the server
5. `open http://localhost:3001` to see the result
Once running you will find a couple seeded pages on your local environment with some basic instructions. You can also start editing the pages by modifying the documents within Payload. See the [Live Preview Example](https://github.com/payloadcms/payload/tree/master/examples/live-preview/payload) for full details.
Once running you will find a couple seeded pages on your local environment with some basic instructions. You can also start editing the pages by modifying the documents within Payload. See the [Live Preview Example](https://github.com/payloadcms/payload/tree/main/examples/live-preview/payload) for full details.
## Learn More
@@ -32,6 +32,6 @@ You can check out [the Payload GitHub repository](https://github.com/payloadcms/
## Deployment
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new) from the creators of Next.js. You could also combine this app into a [single Express server](https://github.com/payloadcms/payload/tree/master/examples/custom-server) and deploy in to [Payload Cloud](https://payloadcms.com/new/import).
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new) from the creators of Next.js. You could also combine this app into a [single Express server](https://github.com/payloadcms/payload/tree/main/examples/custom-server) and deploy in to [Payload Cloud](https://payloadcms.com/new/import).
Check out our [Payload deployment documentation](https://payloadcms.com/docs/production/deployment) or the [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.

View File

@@ -9,7 +9,7 @@
"lint": "next lint"
},
"dependencies": {
"@payloadcms/live-preview-react": "^1.0.0-beta.3",
"@payloadcms/live-preview-react": "latest",
"@types/escape-html": "^1.0.2",
"escape-html": "^1.0.3",
"next": "^13.4.8",

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
# Payload Live Preview Example
The [Payload Live Preview Example](https://github.com/payloadcms/payload/tree/master/examples/live-preview/payload) demonstrates how to implement [Live Preview](https://payloadcms.com/docs/live-preview) in [Payload](https://github.com/payloadcms/payload). With Live Preview you can render your front-end application directly within the Admin panel. As you type, your changes take effect in real-time. No need to save a draft or publish your changes.
The [Payload Live Preview Example](https://github.com/payloadcms/payload/tree/main/examples/live-preview/payload) demonstrates how to implement [Live Preview](https://payloadcms.com/docs/live-preview) in [Payload](https://github.com/payloadcms/payload). With Live Preview you can render your front-end application directly within the Admin panel. As you type, your changes take effect in real-time. No need to save a draft or publish your changes.
There are various fully working front-ends made explicitly for this example, including:
@@ -40,7 +40,7 @@ See the [Collections](https://payloadcms.com/docs/configuration/collections) doc
}
```
For additional help with authentication, see the [Authentication](https://payloadcms.com/docs/authentication/overview#authentication-overview) docs or the official [Auth Example](https://github.com/payloadcms/payload/tree/master/examples/auth).
For additional help with authentication, see the [Authentication](https://payloadcms.com/docs/authentication/overview#authentication-overview) docs or the official [Auth Example](https://github.com/payloadcms/payload/tree/main/examples/auth).
- #### Pages

View File

@@ -1,6 +1,6 @@
# Redirects Example Front-End
This is a [Next.js](https://nextjs.org) app using the [Pages Router](https://nextjs.org/docs/pages). It was made explicitly for Payload's [Redirects Example](https://github.com/payloadcms/payload/tree/master/examples/redireects/payload).
This is a [Next.js](https://nextjs.org) app using the [Pages Router](https://nextjs.org/docs/pages). It was made explicitly for Payload's [Redirects Example](https://github.com/payloadcms/payload/tree/main/examples/redireects/payload).
> This example uses the Pages Router, the legacy API of Next.js. If your app is using the latest [App Router](https://nextjs.org/docs/app), we will soon add a new example for you to use soon.
@@ -8,7 +8,7 @@ This is a [Next.js](https://nextjs.org) app using the [Pages Router](https://nex
### Payload
First you'll need a running Payload app. There is one made explicitly for this example and [can be found here](https://github.com/payloadcms/payload/tree/master/examples/redirects/payload). If you have not done so already, clone it down and follow the setup instructions there.
First you'll need a running Payload app. There is one made explicitly for this example and [can be found here](https://github.com/payloadcms/payload/tree/main/examples/redirects/payload). If you have not done so already, clone it down and follow the setup instructions there.
### Next.js App

View File

@@ -6,6 +6,12 @@ module.exports = {
extends: ['plugin:@typescript-eslint/disable-type-checked'],
files: ['*.js', '*.cjs', '*.json', '*.md', '*.yml', '*.yaml'],
},
{
files: ['**/*.ts'],
rules: {
'@typescript-eslint/no-redundant-type-constituents': 'off',
},
},
{
files: ['package.json', 'tsconfig.json'],
rules: {

View File

@@ -29,9 +29,12 @@
"prompts": "2.4.2",
"uuid": "9.0.0"
},
"peerDependencies": {
},
"devDependencies": {
"@payloadcms/eslint-config": "workspace:*",
"@types/mongoose-aggregate-paginate-v2": "1.0.9",
"mongodb": "^6.1.0",
"mongodb-memory-server": "8.13.0",
"payload": "workspace:*"
},

View File

@@ -1,3 +1,4 @@
import type { TransactionOptions } from 'mongodb'
import type { ClientSession, ConnectOptions, Connection } from 'mongoose'
import type { Payload } from 'payload'
import type { BaseDatabaseAdapter } from 'payload/database'
@@ -6,8 +7,6 @@ import mongoose from 'mongoose'
import path from 'path'
import { createDatabaseAdapter } from 'payload/database'
export type { MigrateDownArgs, MigrateUpArgs } from './types'
import type { CollectionModel, GlobalModel } from './types'
import { connect } from './connect'
@@ -38,6 +37,8 @@ import { updateGlobalVersion } from './updateGlobalVersion'
import { updateOne } from './updateOne'
import { updateVersion } from './updateVersion'
export type { MigrateDownArgs, MigrateUpArgs } from './types'
export interface Args {
/** Set to false to disable auto-pluralization of collection names, Defaults to true */
autoPluralization?: boolean
@@ -47,6 +48,10 @@ export interface Args {
useFacet?: boolean
}
migrationDir?: string
/**
* set to false to disable using transactions
*/
transactions?: TransactionOptions | false
/** The URL to connect to MongoDB or false to start payload and prevent connecting */
url: false | string
}
@@ -60,6 +65,7 @@ export type MongooseAdapter = BaseDatabaseAdapter &
globals: GlobalModel
mongoMemoryServer: any
sessions: Record<number | string, ClientSession>
transactionOptions: TransactionOptions | false
versions: {
[slug: string]: CollectionModel
}
@@ -70,7 +76,7 @@ type MongooseAdapterResult = (args: { payload: Payload }) => MongooseAdapter
declare module 'payload' {
export interface DatabaseAdapter
extends Omit<BaseDatabaseAdapter, 'sessions'>,
Omit<Args, 'migrationDir'> {
Omit<Args, 'migrationDir' | 'transactions'> {
collections: {
[slug: string]: CollectionModel
}
@@ -88,6 +94,7 @@ export function mongooseAdapter({
autoPluralization = true,
connectOptions,
migrationDir: migrationDirArg,
transactions,
url,
}: Args): MongooseAdapterResult {
function adapter({ payload }: { payload: Payload }) {
@@ -108,6 +115,12 @@ export function mongooseAdapter({
globals: undefined,
mongoMemoryServer: undefined,
sessions: {},
transactionOptions: transactions ?? {
readConcern: { level: 'local' },
// TODO: this needs to be dynamic based on the operation
readPreference: 'nearest',
// readPreference: 'primary', // primary for write
},
url,
versions: {},

View File

@@ -1,4 +1,3 @@
// @ts-expect-error // TODO: Fix this import
import type { TransactionOptions } from 'mongodb'
import type { BeginTransaction } from 'payload/database'
@@ -7,8 +6,11 @@ import { v4 as uuid } from 'uuid'
let transactionsNotAvailable: boolean
export const beginTransaction: BeginTransaction = async function beginTransaction(
options: TransactionOptions = {},
options?: TransactionOptions,
) {
if (this.transactionOptions === false) {
return null
}
let id = null
if (!this.connection) {
throw new APIError('beginTransaction called while no connection to the database exists')
@@ -27,7 +29,7 @@ export const beginTransaction: BeginTransaction = async function beginTransactio
if (this.sessions[id].inTransaction()) {
this.payload.logger.warn('beginTransaction called while transaction already exists')
} else {
await this.sessions[id].startTransaction(options)
await this.sessions[id].startTransaction(options || this.transactionOptions)
}
}
return id

View File

@@ -128,7 +128,7 @@ export const traverseFields = ({
with: {},
}
if (adapter.tables[`${topLevelTableName}_${toSnakeCase(block.slug)}_locales`])
if (adapter.tables[`${topLevelTableName}_blocks_${toSnakeCase(block.slug)}_locales`])
withBlock.with._locales = _locales
topLevelArgs.with[blockKey] = withBlock

View File

@@ -0,0 +1,13 @@
import type { Field } from 'payload/types'
export const idToUUID = (fields: Field[]): Field[] =>
fields.map((field) => {
if ('name' in field && field.name === 'id') {
return {
...field,
name: '_uuid',
}
}
return field
})

View File

@@ -26,6 +26,7 @@ import type { GenericColumns, PostgresAdapter } from '../types'
import { hasLocalesTable } from '../utilities/hasLocalesTable'
import { buildTable } from './build'
import { createIndex } from './createIndex'
import { idToUUID } from './idToUUID'
import { parentIDColumnMap } from './parentIDColumnMap'
import { validateExistingBlockIsIdentical } from './validateExistingBlockIsIdentical'
@@ -281,7 +282,7 @@ export const traverseFields = ({
baseExtraConfig,
disableNotNull: disableNotNullFromHere,
disableUnique,
fields: field.fields,
fields: disableUnique ? idToUUID(field.fields) : field.fields,
rootRelationsToBuild,
rootRelationships: relationships,
rootTableIDColType,
@@ -349,7 +350,7 @@ export const traverseFields = ({
baseExtraConfig,
disableNotNull: disableNotNullFromHere,
disableUnique,
fields: block.fields,
fields: disableUnique ? idToUUID(block.fields) : block.fields,
rootRelationsToBuild,
rootRelationships: relationships,
rootTableIDColType,

View File

@@ -22,7 +22,7 @@ export const validateExistingBlockIsIdentical = ({
const fieldNames = flattenTopLevelFields(block.fields).flatMap((field) => field.name)
Object.keys(table).forEach((fieldName) => {
if (!['_locale', '_order', '_parentID', '_path'].includes(fieldName)) {
if (!['_locale', '_order', '_parentID', '_path', '_uuid'].includes(fieldName)) {
if (fieldNames.indexOf(fieldName) === -1) {
throw new InvalidConfiguration(
`The table ${rootTableName} has multiple blocks with slug ${block.slug}, but the schemas do not match. One block includes the field ${fieldName}, while the other block does not.`,

View File

@@ -31,6 +31,7 @@ export const transform = <T extends TypeWithID>({ config, data, fields }: Transf
}
const blocks = createBlocksMap(data)
const deletions = []
const result = traverseFields<T>({
blocks,
@@ -38,6 +39,7 @@ export const transform = <T extends TypeWithID>({ config, data, fields }: Transf
dataRef: {
id: data.id,
},
deletions,
fieldPrefix: '',
fields,
numbers,
@@ -46,5 +48,7 @@ export const transform = <T extends TypeWithID>({ config, data, fields }: Transf
table: data,
})
deletions.forEach((deletion) => deletion())
return result
}

View File

@@ -22,6 +22,10 @@ type TraverseFieldsArgs = {
* The data reference to be mutated within this recursive function
*/
dataRef: Record<string, unknown>
/**
* Data that needs to be removed from the result after all fields have populated
*/
deletions: (() => void)[]
/**
* Column prefix can be built up by group and named tab fields
*/
@@ -54,6 +58,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
blocks,
config,
dataRef,
deletions,
fieldPrefix,
fields,
numbers,
@@ -69,6 +74,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
blocks,
config,
dataRef,
deletions,
fieldPrefix,
fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })),
numbers,
@@ -87,6 +93,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
blocks,
config,
dataRef,
deletions,
fieldPrefix,
fields: field.fields,
numbers,
@@ -99,7 +106,6 @@ export const traverseFields = <T extends Record<string, unknown>>({
if (fieldAffectsData(field)) {
const fieldName = `${fieldPrefix || ''}${field.name}`
const fieldData = table[fieldName]
if (field.type === 'array') {
if (Array.isArray(fieldData)) {
if (field.localized) {
@@ -109,11 +115,16 @@ export const traverseFields = <T extends Record<string, unknown>>({
const locale = row._locale
const data = {}
delete row._locale
if (row._uuid) {
row.id = row._uuid
delete row._uuid
}
const rowResult = traverseFields<T>({
blocks,
config,
dataRef: data,
deletions,
fieldPrefix: '',
fields: field.fields,
numbers,
@@ -129,10 +140,15 @@ export const traverseFields = <T extends Record<string, unknown>>({
}, {})
} else {
result[field.name] = fieldData.map((row, i) => {
if (row._uuid) {
row.id = row._uuid
delete row._uuid
}
return traverseFields<T>({
blocks,
config,
dataRef: row,
deletions,
fieldPrefix: '',
fields: field.fields,
numbers,
@@ -155,6 +171,10 @@ export const traverseFields = <T extends Record<string, unknown>>({
result[field.name] = {}
blocks[blockFieldPath].forEach((row) => {
if (row._uuid) {
row.id = row._uuid
delete row._uuid
}
if (typeof row._locale === 'string') {
if (!result[field.name][row._locale]) result[field.name][row._locale] = []
result[field.name][row._locale].push(row)
@@ -171,6 +191,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
blocks,
config,
dataRef: row,
deletions,
fieldPrefix: '',
fields: block.fields,
numbers,
@@ -189,6 +210,10 @@ export const traverseFields = <T extends Record<string, unknown>>({
} else {
result[field.name] = blocks[blockFieldPath].map((row, i) => {
delete row._order
if (row._uuid) {
row.id = row._uuid
delete row._uuid
}
const block = field.blocks.find(({ slug }) => slug === row.blockType)
if (block) {
@@ -196,6 +221,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
blocks,
config,
dataRef: row,
deletions,
fieldPrefix: '',
fields: block.fields,
numbers,
@@ -345,6 +371,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
blocks,
config,
dataRef: groupLocaleData as Record<string, unknown>,
deletions,
fieldPrefix: groupFieldPrefix,
fields: field.fields,
numbers,
@@ -360,6 +387,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
blocks,
config,
dataRef: groupData as Record<string, unknown>,
deletions,
fieldPrefix: groupFieldPrefix,
fields: field.fields,
numbers,
@@ -425,5 +453,9 @@ export const traverseFields = <T extends Record<string, unknown>>({
return result
}, dataRef)
if (Array.isArray(table._locales)) {
deletions.push(() => delete table._locales)
}
return formatted as T
}

View File

@@ -1,12 +1,14 @@
/* eslint-disable no-param-reassign */
import type { ArrayField } from 'payload/types'
import type { PostgresAdapter } from '../../types'
import type { ArrayRowToInsert, BlockRowToInsert, RelationshipToDelete } from './types'
import { isArrayOfRows } from '../../utilities/isArrayOfRows'
import { traverseFields } from './traverseFields'
type Args = {
adapter: PostgresAdapter
arrayTableName: string
baseTableName: string
blocks: {
@@ -25,6 +27,7 @@ type Args = {
}
export const transformArray = ({
adapter,
arrayTableName,
baseTableName,
blocks,
@@ -38,6 +41,7 @@ export const transformArray = ({
selects,
}: Args) => {
const newRows: ArrayRowToInsert[] = []
const hasUUID = adapter.tables[arrayTableName]._uuid
if (isArrayOfRows(data)) {
data.forEach((arrayRow, i) => {
@@ -49,6 +53,16 @@ export const transformArray = ({
},
}
// If we have declared a _uuid field on arrays,
// that means the ID has to be unique,
// and our ids within arrays are not unique.
// So move the ID to a uuid field for storage
// and allow the database to generate a serial id automatically
if (hasUUID) {
newRow.row._uuid = arrayRow.id
delete arrayRow.id
}
if (locale) {
newRow.locales[locale] = {
_locale: locale,
@@ -60,6 +74,7 @@ export const transformArray = ({
}
traverseFields({
adapter,
arrays: newRow.arrays,
baseTableName,
blocks,

View File

@@ -3,11 +3,13 @@ import type { BlockField } from 'payload/types'
import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from '../../types'
import type { BlockRowToInsert, RelationshipToDelete } from './types'
import { traverseFields } from './traverseFields'
type Args = {
adapter: PostgresAdapter
baseTableName: string
blocks: {
[blockType: string]: BlockRowToInsert[]
@@ -24,6 +26,7 @@ type Args = {
}
}
export const transformBlocks = ({
adapter,
baseTableName,
blocks,
data,
@@ -56,7 +59,20 @@ export const transformBlocks = ({
const blockTableName = `${baseTableName}_blocks_${blockType}`
const hasUUID = adapter.tables[blockTableName]._uuid
// If we have declared a _uuid field on arrays,
// that means the ID has to be unique,
// and our ids within arrays are not unique.
// So move the ID to a uuid field for storage
// and allow the database to generate a serial id automatically
if (hasUUID) {
newRow.row._uuid = blockRow.id
delete blockRow.id
}
traverseFields({
adapter,
arrays: newRow.arrays,
baseTableName,
blocks,

View File

@@ -1,18 +1,26 @@
/* eslint-disable no-param-reassign */
import type { Field } from 'payload/types'
import type { PostgresAdapter } from '../../types'
import type { RowToInsert } from './types'
import { traverseFields } from './traverseFields'
type Args = {
adapter: PostgresAdapter
data: Record<string, unknown>
fields: Field[]
path?: string
tableName: string
}
export const transformForWrite = ({ data, fields, path = '', tableName }: Args): RowToInsert => {
export const transformForWrite = ({
adapter,
data,
fields,
path = '',
tableName,
}: Args): RowToInsert => {
// Split out the incoming data into rows to insert / delete
const rowToInsert: RowToInsert = {
arrays: {},
@@ -28,6 +36,7 @@ export const transformForWrite = ({ data, fields, path = '', tableName }: Args):
// This function is responsible for building up the
// above rowToInsert
traverseFields({
adapter,
arrays: rowToInsert.arrays,
baseTableName: tableName,
blocks: rowToInsert.blocks,

View File

@@ -4,6 +4,7 @@ import type { Field } from 'payload/types'
import { fieldAffectsData } from 'payload/types'
import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from '../../types'
import type { ArrayRowToInsert, BlockRowToInsert, RelationshipToDelete } from './types'
import { isArrayOfRows } from '../../utilities/isArrayOfRows'
@@ -14,6 +15,7 @@ import { transformRelationship } from './relationships'
import { transformSelects } from './selects'
type Args = {
adapter: PostgresAdapter
arrays: {
[tableName: string]: ArrayRowToInsert[]
}
@@ -56,6 +58,7 @@ type Args = {
}
export const traverseFields = ({
adapter,
arrays,
baseTableName,
blocks,
@@ -95,6 +98,7 @@ export const traverseFields = ({
Object.entries(data[field.name]).forEach(([localeKey, localeData]) => {
if (Array.isArray(localeData)) {
const newRows = transformArray({
adapter,
arrayTableName,
baseTableName,
blocks,
@@ -114,6 +118,7 @@ export const traverseFields = ({
}
} else {
const newRows = transformArray({
adapter,
arrayTableName,
baseTableName,
blocks,
@@ -138,6 +143,7 @@ export const traverseFields = ({
Object.entries(data[field.name]).forEach(([localeKey, localeData]) => {
if (Array.isArray(localeData)) {
transformBlocks({
adapter,
baseTableName,
blocks,
data: localeData,
@@ -154,6 +160,7 @@ export const traverseFields = ({
}
} else if (isArrayOfRows(fieldData)) {
transformBlocks({
adapter,
baseTableName,
blocks,
data: fieldData,
@@ -174,6 +181,7 @@ export const traverseFields = ({
if (field.localized) {
Object.entries(data[field.name]).forEach(([localeKey, localeData]) => {
traverseFields({
adapter,
arrays,
baseTableName,
blocks,
@@ -195,6 +203,7 @@ export const traverseFields = ({
})
} else {
traverseFields({
adapter,
arrays,
baseTableName,
blocks,
@@ -225,6 +234,7 @@ export const traverseFields = ({
if (tab.localized) {
Object.entries(data[tab.name]).forEach(([localeKey, localeData]) => {
traverseFields({
adapter,
arrays,
baseTableName,
blocks,
@@ -246,6 +256,7 @@ export const traverseFields = ({
})
} else {
traverseFields({
adapter,
arrays,
baseTableName,
blocks,
@@ -267,6 +278,7 @@ export const traverseFields = ({
}
} else {
traverseFields({
adapter,
arrays,
baseTableName,
blocks,
@@ -290,6 +302,7 @@ export const traverseFields = ({
if (field.type === 'row' || field.type === 'collapsible') {
traverseFields({
adapter,
arrays,
baseTableName,
blocks,

View File

@@ -28,6 +28,7 @@ export const upsertRow = async <T extends TypeWithID>({
// Split out the incoming data into the corresponding:
// base row, locales, relationships, blocks, and arrays
const rowToInsert = transformForWrite({
adapter,
data,
fields,
path,
@@ -107,6 +108,9 @@ export const upsertRow = async <T extends TypeWithID>({
rowToInsert.blocks[blockName].forEach((blockRow) => {
blockRow.row._parentID = insertedRow.id
if (!blocksToInsert[blockName]) blocksToInsert[blockName] = []
if (blockRow.row.uuid) {
delete blockRow.row.uuid
}
blocksToInsert[blockName].push(blockRow)
})
})

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/eslint-config",
"version": "0.0.1",
"version": "1.0.0",
"description": "Payload styles for ESLint and Prettier",
"license": "MIT",
"author": {

View File

@@ -1,9 +1,11 @@
import type { fieldSchemaToJSON } from 'payload/utilities'
import { traverseFields } from './traverseFields'
export type MergeLiveDataArgs<T> = {
apiRoute?: string
depth: number
fieldSchema: Record<string, unknown>[]
fieldSchema: ReturnType<typeof fieldSchemaToJSON>
incomingData: T
initialData: T
serverURL: string

View File

@@ -1,9 +1,11 @@
import type { fieldSchemaToJSON } from 'payload/utilities'
import { promise } from './promise'
type Args<T> = {
apiRoute?: string
depth: number
fieldSchema: Record<string, unknown>[]
fieldSchema: ReturnType<typeof fieldSchemaToJSON>
incomingData: T
populationPromises: Promise<void>[]
result: T
@@ -19,12 +21,11 @@ export const traverseFields = <T>({
result,
serverURL,
}: Args<T>): void => {
fieldSchema.forEach((field) => {
if ('name' in field && typeof field.name === 'string') {
// TODO: type this
const fieldName = field.name
fieldSchema.forEach((fieldJSON) => {
if ('name' in fieldJSON && typeof fieldJSON.name === 'string') {
const fieldName = fieldJSON.name
switch (field.type) {
switch (fieldJSON.type) {
case 'array':
if (Array.isArray(incomingData[fieldName])) {
result[fieldName] = incomingData[fieldName].map((row, i) => {
@@ -38,7 +39,7 @@ export const traverseFields = <T>({
traverseFields({
apiRoute,
depth,
fieldSchema: field.fields as Record<string, unknown>[], // TODO: type this
fieldSchema: fieldJSON.fields,
incomingData: row,
populationPromises,
result: newRow,
@@ -53,7 +54,7 @@ export const traverseFields = <T>({
case 'blocks':
if (Array.isArray(incomingData[fieldName])) {
result[fieldName] = incomingData[fieldName].map((row, i) => {
const matchedBlock = field.blocks[row.blockType]
const matchedBlock = fieldJSON.blocks[row.blockType]
const hasExistingRow =
Array.isArray(result[fieldName]) &&
@@ -70,7 +71,7 @@ export const traverseFields = <T>({
traverseFields({
apiRoute,
depth,
fieldSchema: matchedBlock.fields as Record<string, unknown>[], // TODO: type this
fieldSchema: matchedBlock.fields,
incomingData: row,
populationPromises,
result: newRow,
@@ -82,7 +83,7 @@ export const traverseFields = <T>({
}
break
case 'tab':
case 'tabs':
case 'group':
if (!result[fieldName]) {
result[fieldName] = {}
@@ -91,7 +92,7 @@ export const traverseFields = <T>({
traverseFields({
apiRoute,
depth,
fieldSchema: field.fields as Record<string, unknown>[], // TODO: type this
fieldSchema: fieldJSON.fields,
incomingData: incomingData[fieldName] || {},
populationPromises,
result: result[fieldName],
@@ -102,7 +103,7 @@ export const traverseFields = <T>({
case 'upload':
case 'relationship':
if (field.hasMany && Array.isArray(incomingData[fieldName])) {
if (fieldJSON.hasMany && Array.isArray(incomingData[fieldName])) {
const existingValue = Array.isArray(result[fieldName]) ? [...result[fieldName]] : []
result[fieldName] = Array.isArray(result[fieldName])
? [...result[fieldName]].slice(0, incomingData[fieldName].length)
@@ -110,7 +111,7 @@ export const traverseFields = <T>({
incomingData[fieldName].forEach((relation, i) => {
// Handle `hasMany` polymorphic
if (Array.isArray(field.relationTo)) {
if (Array.isArray(fieldJSON.relationTo)) {
const existingID = existingValue[i]?.value?.id
if (
@@ -134,7 +135,7 @@ export const traverseFields = <T>({
)
}
} else {
// Handle `hasMany` singular
// Handle `hasMany` monomorphic
const existingID = existingValue[i]?.id
if (existingID !== relation) {
@@ -143,7 +144,7 @@ export const traverseFields = <T>({
id: relation,
accessor: i,
apiRoute,
collection: String(field.relationTo),
collection: String(fieldJSON.relationTo),
depth,
ref: result[fieldName],
serverURL,
@@ -154,7 +155,7 @@ export const traverseFields = <T>({
})
} else {
// Handle `hasOne` polymorphic
if (Array.isArray(field.relationTo)) {
if (Array.isArray(fieldJSON.relationTo)) {
const hasNewValue =
typeof incomingData[fieldName] === 'object' && incomingData[fieldName] !== null
const hasOldValue =
@@ -190,31 +191,37 @@ export const traverseFields = <T>({
result[fieldName] = null
}
} else {
const hasNewValue =
typeof incomingData[fieldName] === 'object' && incomingData[fieldName] !== null
const hasOldValue =
typeof result[fieldName] === 'object' && result[fieldName] !== null
// Handle `hasOne` monomorphic
const newID: string =
(typeof incomingData[fieldName] === 'string' && incomingData[fieldName]) ||
(typeof incomingData[fieldName] === 'object' &&
incomingData[fieldName] !== null &&
incomingData[fieldName].id) ||
''
const newValue = hasNewValue ? incomingData[fieldName].value : ''
const oldID: string =
(typeof result[fieldName] === 'string' && result[fieldName]) ||
(typeof result[fieldName] === 'object' &&
result[fieldName] !== null &&
result[fieldName].id) ||
''
const oldValue = hasOldValue ? result[fieldName].value : ''
if (newValue !== oldValue) {
if (newValue) {
if (newID !== oldID) {
if (newID) {
populationPromises.push(
promise({
id: newValue,
id: newID,
accessor: fieldName,
apiRoute,
collection: String(field.relationTo),
collection: String(fieldJSON.relationTo),
depth,
ref: result as Record<string, unknown>,
serverURL,
}),
)
} else {
result[fieldName] = null
}
} else {
result[fieldName] = null
}
}
}

View File

@@ -31,13 +31,14 @@ const DeleteDocument: React.FC<Props> = (props) => {
routes: { admin, api },
serverURL,
} = useConfig()
const { setModified } = useForm()
const [deleting, setDeleting] = useState(false)
const { toggleModal } = useModal()
const history = useHistory()
const { i18n, t } = useTranslation('general')
const title = useTitle({ collection })
const titleToRender = titleFromProps || title
const titleToRender = titleFromProps || title || id
const modalSlug = `delete-${id}`

View File

@@ -1,7 +1,7 @@
@import '../../../scss/styles.scss';
.doc-controls {
@include blur-bg;
@include blur-bg-light;
position: sticky;
top: 0;
width: 100%;
@@ -9,7 +9,7 @@
display: flex;
align-items: center;
&::after {
&__divider {
content: '';
display: block;
position: absolute;

View File

@@ -225,6 +225,7 @@ export const DocumentControls: React.FC<{
)}
</div>
</div>
<div className={`${baseClass}__divider`} />
</Gutter>
)
}

View File

@@ -10,6 +10,19 @@
justify-content: center;
align-items: center;
white-space: nowrap;
// Use a pseudo element for the accessability so that it doesn't take up DOM space
// Also because the parent element has `overflow: hidden` which would clip an outline
&:focus-visible::after {
content: '';
border: var(--accessibility-outline);
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
pointer-events: none;
}
}
&:focus:not(:focus-visible) {

View File

@@ -64,6 +64,7 @@ export const DocumentTab: React.FC<DocumentTabProps & DocumentTabConfig> = (prop
className={`${baseClass}__link`}
to={href}
{...(newTab && { rel: 'noopener noreferrer', target: '_blank' })}
tabIndex={isActive ? -1 : 0}
>
<span className={`${baseClass}__label`}>
{labelToRender}

View File

@@ -43,7 +43,7 @@ export const tabs: DocumentTabConfig[] = [
// API
{
condition: ({ collection, global }) =>
!collection?.admin?.hideAPIURL || !global?.admin?.hideAPIURL,
(collection && !collection?.admin?.hideAPIURL) || (global && !global?.admin?.hideAPIURL),
href: '/api',
label: 'API',
},

View File

@@ -1,6 +1,6 @@
@import '../../../scss/styles.scss';
$transTime: 200ms;
$transTime: 200;
.drawer {
display: flex;
@@ -9,7 +9,7 @@ $transTime: 200ms;
height: 100vh;
&__blur-bg {
@include blur-bg();
@include blur-bg;
position: absolute;
z-index: 1;
top: 0;
@@ -17,7 +17,7 @@ $transTime: 200ms;
bottom: 0;
left: 0;
opacity: 0;
transition: all $transTime linear;
transition: all #{$transTime}ms linear;
}
&__content {
@@ -27,7 +27,8 @@ $transTime: 200ms;
z-index: 2;
width: calc(100% - var(--gutter-h));
overflow: hidden;
transition: all $transTime linear;
transition: all #{$transTime}ms linear;
background-color: var(--theme-bg);
}
&__content-children {
@@ -40,14 +41,14 @@ $transTime: 200ms;
&--is-open {
.drawer {
&__content,
&__blur-bg,
&__close {
&__blur-bg {
opacity: 1;
}
&__close {
transition: opacity $transTime linear;
transition-delay: $transTime;
opacity: 0.1;
transition: opacity #{$transTime}ms linear;
transition-delay: #{$transTime / 2}ms;
}
&__content {
@@ -68,7 +69,7 @@ $transTime: 200ms;
transition: none;
transition-delay: 0ms;
flex-grow: 1;
background: transparent;
background: var(--theme-elevation-800);
&:active,
&:focus {
@@ -120,7 +121,15 @@ $transTime: 200ms;
}
html[data-theme='dark'] {
.drawer__close {
background: rgba(0, 0, 0, 0.2);
.drawer {
&__close {
background: var(--color-base-1000);
}
&--is-open {
.drawer__close {
opacity: 0.25;
}
}
}
}

View File

@@ -1,5 +1,4 @@
import { Modal, useModal } from '@faceless-ui/modal'
import { useWindowInfo } from '@faceless-ui/window-info'
import React, { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
@@ -52,9 +51,6 @@ export const Drawer: React.FC<Props> = ({
}) => {
const { t } = useTranslation('general')
const { closeModal, modalState } = useModal()
const {
breakpoints: { m: midBreak },
} = useWindowInfo()
const drawerDepth = useEditDepth()
const [isOpen, setIsOpen] = useState(false)
const [animateIn, setAnimateIn] = useState(false)
@@ -72,7 +68,12 @@ export const Drawer: React.FC<Props> = ({
return (
<Modal
className={[className, baseClass, animateIn && `${baseClass}--is-open`]
className={[
className,
baseClass,
animateIn && `${baseClass}--is-open`,
drawerDepth > 1 && `${baseClass}--nested`,
]
.filter(Boolean)
.join(' ')}
slug={slug}
@@ -80,7 +81,7 @@ export const Drawer: React.FC<Props> = ({
zIndex: zBase + drawerDepth,
}}
>
{drawerDepth === 1 && <div className={`${baseClass}__blur-bg`} />}
{(!drawerDepth || drawerDepth === 1) && <div className={`${baseClass}__blur-bg`} />}
<button
aria-label={t('close')}
className={`${baseClass}__close`}
@@ -89,7 +90,7 @@ export const Drawer: React.FC<Props> = ({
type="button"
/>
<div className={`${baseClass}__content`}>
<div className={`${baseClass}__blur-bg`} />
<div className={`${baseClass}__blur-bg-content`} />
<Gutter className={`${baseClass}__content-children`} left={gutter} right={gutter}>
<EditDepthContext.Provider value={drawerDepth + 1}>
{header && header}

View File

@@ -7,27 +7,36 @@
background-color: transparent;
outline: none;
position: relative;
@include blur-bg;
--hamburger-padding: 8px;
--hamburger-size: 9px;
--hamburger-line-gap: 3px;
padding: var(--hamburger-padding);
border: 1px solid var(--theme-elevation-150);
color: var(--theme-text);
border-radius: 3px;
&:hover {
border: 1px solid var(--theme-elevation-500);
background-color: var(--theme-elevation-100);
}
&__wrapper {
border: 1px solid var(--theme-elevation-150);
padding: var(--hamburger-padding);
border-radius: 3px;
position: relative;
z-index: 1;
height: 100%;
width: 100%;
&:focus {
outline: none;
&:hover {
border: 1px solid var(--theme-elevation-500);
background-color: var(--theme-elevation-100);
}
&:focus {
outline: none;
}
}
&__icon {
position: relative;
z-index: 1;
height: var(--hamburger-size);
width: var(--hamburger-size);
display: flex;

View File

@@ -14,32 +14,34 @@ export const Hamburger: React.FC<{
const { closeIcon = 'x', isActive = false } = props
return (
<div className={[baseClass].filter(Boolean).join(' ')}>
<div className={`${baseClass}__icon`}>
{!isActive && (
<div className={`${baseClass}__lines`} title={t('open')}>
<div className={`${baseClass}__line ${baseClass}__top`} />
<div className={`${baseClass}__line ${baseClass}__middle`} />
<div className={`${baseClass}__line ${baseClass}__bottom`} />
</div>
)}
{isActive && (
<div
aria-label={closeIcon === 'collapse' ? t('collapse') : t('close')}
className={`${baseClass}__close-icon`}
title={closeIcon === 'collapse' ? t('collapse') : t('close')}
>
{closeIcon === 'x' && (
<React.Fragment>
<div className={`${baseClass}__line ${baseClass}__x-left`} />
<div className={`${baseClass}__line ${baseClass}__x-right`} />
</React.Fragment>
)}
{closeIcon === 'collapse' && (
<Chevron className={`${baseClass}__collapse-chevron`} direction="left" />
)}
</div>
)}
<div className={baseClass}>
<div className={`${baseClass}__wrapper`}>
<div className={`${baseClass}__icon`}>
{!isActive && (
<div className={`${baseClass}__lines`} title={t('open')}>
<div className={`${baseClass}__line ${baseClass}__top`} />
<div className={`${baseClass}__line ${baseClass}__middle`} />
<div className={`${baseClass}__line ${baseClass}__bottom`} />
</div>
)}
{isActive && (
<div
aria-label={closeIcon === 'collapse' ? t('collapse') : t('close')}
className={`${baseClass}__close-icon`}
title={closeIcon === 'collapse' ? t('collapse') : t('close')}
>
{closeIcon === 'x' && (
<React.Fragment>
<div className={`${baseClass}__line ${baseClass}__x-left`} />
<div className={`${baseClass}__line ${baseClass}__x-right`} />
</React.Fragment>
)}
{closeIcon === 'collapse' && (
<Chevron className={`${baseClass}__collapse-chevron`} direction="left" />
)}
</div>
)}
</div>
</div>
</div>
)

View File

@@ -24,7 +24,6 @@
}
&__bg {
@include blur-bg;
opacity: 0;
position: absolute;
left: 0;
@@ -58,9 +57,28 @@
}
&__account {
position: relative;
&:focus:not(:focus-visible) {
opacity: 1;
}
// Use a pseudo element for the accessability so that it doesn't take up DOM space
// Also because the parent element has `overflow: hidden` which would clip an outline
&:focus-visible {
outline: none;
&::after {
content: '';
border: var(--accessibility-outline);
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
pointer-events: none;
}
}
}
&__controls-wrapper {

View File

@@ -14,7 +14,7 @@ import './index.scss'
const baseClass = 'app-header'
export const AppHeader: React.FC = (props) => {
export const AppHeader: React.FC = () => {
const { t } = useTranslation()
const {
@@ -29,7 +29,7 @@ export const AppHeader: React.FC = (props) => {
<div className={`${baseClass}__bg`} />
<div className={`${baseClass}__content`}>
<div className={`${baseClass}__wrapper`}>
<NavToggler className={`${baseClass}__mobile-nav-toggler`}>
<NavToggler className={`${baseClass}__mobile-nav-toggler`} tabIndex={-1}>
<Hamburger />
</NavToggler>
<div className={`${baseClass}__controls-wrapper`}>
@@ -46,6 +46,7 @@ export const AppHeader: React.FC = (props) => {
<Link
aria-label={t('authentication:account')}
className={`${baseClass}__account`}
tabIndex={0}
to={`${adminRoute}/account`}
>
<Account />

View File

@@ -8,20 +8,22 @@ import RenderCustomComponent from '../../utilities/RenderCustomComponent'
const baseClass = 'nav'
const DefaultLogout = () => {
const DefaultLogout: React.FC<{
tabIndex?: number
}> = ({ tabIndex }) => {
const { t } = useTranslation('authentication')
const config = useConfig()
const {
admin: {
components: { logout },
logoutRoute,
},
admin: { logoutRoute },
routes: { admin },
} = config
return (
<Link
aria-label={t('logOut')}
className={`${baseClass}__log-out`}
tabIndex={tabIndex}
to={`${admin}${logoutRoute}`}
>
<LogOut />
@@ -29,7 +31,9 @@ const DefaultLogout = () => {
)
}
const Logout: React.FC = () => {
const Logout: React.FC<{
tabIndex?: number
}> = ({ tabIndex = 0 }) => {
const {
admin: {
components: {
@@ -40,7 +44,15 @@ const Logout: React.FC = () => {
} = {},
} = useConfig()
return <RenderCustomComponent CustomComponent={CustomLogout} DefaultComponent={DefaultLogout} />
return (
<RenderCustomComponent
CustomComponent={CustomLogout}
DefaultComponent={DefaultLogout}
componentProps={{
tabIndex,
}}
/>
)
}
export default Logout

View File

@@ -12,8 +12,9 @@ export const NavToggler: React.FC<{
children?: React.ReactNode
className?: string
id?: string
tabIndex?: number
}> = (props) => {
const { id, children, className } = props
const { id, children, className, tabIndex = 0 } = props
const { t } = useTranslation('general')
@@ -43,6 +44,7 @@ export const NavToggler: React.FC<{
})
}
}}
tabIndex={tabIndex}
type="button"
>
{children}

View File

@@ -16,7 +16,6 @@
}
&__header {
@include blur-bg;
position: absolute;
top: 0;
width: 100%;

View File

@@ -102,6 +102,7 @@ const DefaultNav: React.FC = () => {
className={`${baseClass}__link`}
id={id}
key={i}
tabIndex={!navOpen ? -1 : undefined}
to={href}
>
<span className={`${baseClass}__link-icon`}>
@@ -117,7 +118,7 @@ const DefaultNav: React.FC = () => {
{Array.isArray(afterNavLinks) &&
afterNavLinks.map((Component, i) => <Component key={i} />)}
<div className={`${baseClass}__controls`}>
<Logout />
<Logout tabIndex={!navOpen ? -1 : undefined} />
</div>
</nav>
</div>
@@ -128,6 +129,7 @@ const DefaultNav: React.FC = () => {
onClick={() => {
setNavOpen(false)
}}
tabIndex={!navOpen ? -1 : undefined}
type="button"
>
<Hamburger isActive />

View File

@@ -1,8 +1,9 @@
import React, { useEffect, useState } from 'react'
import AnimateHeight from 'react-animate-height'
import Chevron from '../../icons/Chevron'
import { usePreferences } from '../../utilities/Preferences'
import { useNav } from '../Nav/context'
import './index.scss'
const baseClass = 'nav-group'
@@ -16,6 +17,7 @@ const NavGroup: React.FC<Props> = ({ children, label }) => {
const [collapsed, setCollapsed] = useState(true)
const [animate, setAnimate] = useState(false)
const { getPreference, setPreference } = usePreferences()
const { navOpen } = useNav()
const preferencesKey = `collapsed-${label}-groups`
@@ -44,13 +46,12 @@ const NavGroup: React.FC<Props> = ({ children, label }) => {
return (
<div
id={`nav-group-${label}`}
className={[`${baseClass}`, `${label}`, collapsed && `${baseClass}--collapsed`]
.filter(Boolean)
.join(' ')}
id={`nav-group-${label}`}
>
<button
type="button"
className={[
`${baseClass}__toggle`,
`${baseClass}__toggle--${collapsed ? 'collapsed' : 'open'}`,
@@ -58,6 +59,8 @@ const NavGroup: React.FC<Props> = ({ children, label }) => {
.filter(Boolean)
.join(' ')}
onClick={toggleCollapsed}
tabIndex={!navOpen ? -1 : 0}
type="button"
>
<div className={`${baseClass}__label`}>{label}</div>
<div className={`${baseClass}__indicator`}>
@@ -67,7 +70,7 @@ const NavGroup: React.FC<Props> = ({ children, label }) => {
/>
</div>
</button>
<AnimateHeight height={collapsed ? 0 : 'auto'} duration={animate ? 200 : 0}>
<AnimateHeight duration={animate ? 200 : 0} height={collapsed ? 0 : 'auto'}>
<div className={`${baseClass}__content`}>{children}</div>
</AnimateHeight>
</div>

View File

@@ -36,7 +36,7 @@ export const PopupTrigger: React.FC<Props> = (props) => {
}
return (
<button className={classes} onClick={() => setActive(!active)} type="button">
<button className={classes} onClick={() => setActive(!active)} tabIndex={0} type="button">
{button}
</button>
)

View File

@@ -13,6 +13,27 @@
background: linear-gradient(to right, transparent, var(--theme-bg));
}
// Use a pseudo element for the accessability so that it doesn't take up DOM space
// Also because the parent element has `overflow: hidden` which would clip an outline
&__home {
position: relative;
&:focus-visible {
outline: none;
&::after {
content: '';
border: var(--accessibility-outline);
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
pointer-events: none;
}
}
}
* {
display: block;
}

View File

@@ -9,6 +9,8 @@ import { getTranslation } from '../../../../utilities/getTranslation'
import { useConfig } from '../../utilities/Config'
import './index.scss'
const baseClass = 'step-nav'
const Context = createContext({} as ContextType)
const StepNavProvider: React.FC<{ children?: React.ReactNode }> = ({ children }) => {
@@ -41,32 +43,34 @@ const StepNav: React.FC<{
} = config
return (
<nav className={['step-nav', className].filter(Boolean).join(' ')}>
<Fragment>
{stepNav.length > 0 ? (
<Fragment>
<Link to={admin}>
<nav className={[baseClass, className].filter(Boolean).join(' ')}>
<Link className={`${baseClass}__home`} tabIndex={0} to={admin}>
<IconGraphic />
</Link>
<span>/</span>
</Fragment>
) : (
<IconGraphic />
)}
{stepNav.map((item, i) => {
const StepLabel = <span key={i}>{getTranslation(item.label, i18n)}</span>
const Step =
stepNav.length === i + 1 ? (
StepLabel
) : (
<Fragment key={i}>
{item.url ? <Link to={item.url}>{StepLabel}</Link> : StepLabel}
<span>/</span>
</Fragment>
)
{stepNav.map((item, i) => {
const StepLabel = <span key={i}>{getTranslation(item.label, i18n)}</span>
const Step =
stepNav.length === i + 1 ? (
StepLabel
) : (
<Fragment key={i}>
{item.url ? <Link to={item.url}>{StepLabel}</Link> : StepLabel}
<span>/</span>
</Fragment>
)
return Step
})}
</nav>
return Step
})}
</nav>
) : (
<div className={[baseClass, className].filter(Boolean).join(' ')}>
<IconGraphic />
</div>
)}
</Fragment>
)
}

View File

@@ -1,6 +1,7 @@
.graphic-account {
vector-effect: non-scaling-stroke;
overflow: visible;
position: relative;
&__bg {
fill: var(--theme-elevation-50);

View File

@@ -9,11 +9,11 @@ const css = `
width: 18px;
height: 18px;
}
.graphic-icon path {
fill: var(--theme-elevation-1000);
}
@media (max-width: 768px) {
.graphic-icon {
width: 16px;

View File

@@ -26,7 +26,7 @@ const Default: React.FC<Props> = ({ children, className }) => {
const { t } = useTranslation('general')
const { navOpen, setNavOpen } = useNav()
const { navOpen } = useNav()
return (
<Fragment>
@@ -35,6 +35,11 @@ const Default: React.FC<Props> = ({ children, className }) => {
keywords={`${t('dashboard')}, Payload`}
title={t('dashboard')}
/>
<div className={`${baseClass}__nav-toggler-wrapper`} id="nav-toggler">
<NavToggler className={`${baseClass}__nav-toggler`}>
<Hamburger closeIcon="collapse" isActive={navOpen} />
</NavToggler>
</div>
<div
className={[baseClass, className, navOpen && `${baseClass}--nav-open`]
.filter(Boolean)
@@ -46,11 +51,6 @@ const Default: React.FC<Props> = ({ children, className }) => {
{children}
</div>
</div>
<div className={`${baseClass}__nav-toggler-wrapper`} id="nav-toggler">
<NavToggler className={`${baseClass}__nav-toggler`}>
<Hamburger closeIcon="collapse" isActive={navOpen} />
</NavToggler>
</div>
</Fragment>
)
}

View File

@@ -41,9 +41,11 @@ export const GlobalRoutes: React.FC<GlobalEditViewProps> = (props) => {
<Unauthorized />
)}
</Route>
<Route exact key={`${global.slug}-api`} path={`${adminRoute}/globals/${global.slug}/api`}>
{permissions?.read ? <CustomGlobalComponent view="API" {...props} /> : <Unauthorized />}
</Route>
{global?.admin?.hideAPIURL !== true && (
<Route exact key={`${global.slug}-api`} path={`${adminRoute}/globals/${global.slug}/api`}>
{permissions?.read ? <CustomGlobalComponent view="API" {...props} /> : <Unauthorized />}
</Route>
)}
<Route
exact
key={`${global.slug}-view-version`}

View File

@@ -41,13 +41,19 @@ export const CollectionRoutes: React.FC<CollectionEditViewProps> = (props) => {
<Unauthorized />
)}
</Route>
<Route
exact
key={`${collection.slug}-api`}
path={`${adminRoute}/collections/${collection.slug}/:id/api`}
>
{permissions?.read ? <CustomCollectionComponent view="API" {...props} /> : <Unauthorized />}
</Route>
{collection?.admin?.hideAPIURL !== true && (
<Route
exact
key={`${collection.slug}-api`}
path={`${adminRoute}/collections/${collection.slug}/:id/api`}
>
{permissions?.read ? (
<CustomCollectionComponent view="API" {...props} />
) : (
<Unauthorized />
)}
</Route>
)}
<Route
exact
key={`${collection.slug}-view-version`}

View File

@@ -129,7 +129,7 @@ $focus-box-shadow: 0 0 0 $style-stroke-width-m var(--theme-success-500);
// STYLE MIXINS
//////////////////////////////
@mixin blur-bg($color: var(--theme-bg)) {
@mixin blur-bg($color: var(--theme-bg), $opacity: 0.75) {
&:before,
&:after {
content: ' ';
@@ -142,14 +142,18 @@ $focus-box-shadow: 0 0 0 $style-stroke-width-m var(--theme-success-500);
&:before {
background: $color;
opacity: 0.85;
opacity: $opacity;
}
&:after {
backdrop-filter: blur(5px);
backdrop-filter: blur(8px);
}
}
@mixin blur-bg-light {
@include blur-bg(var(--theme-bg), 0.3);
}
@mixin formInput() {
@include inputShadow;
font-family: var(--font-body);

View File

@@ -162,8 +162,8 @@ const collectionSchema = joi.object().keys({
adminThumbnail: joi.alternatives().try(joi.string(), joi.func()),
crop: joi.bool(),
disableLocalStorage: joi.bool(),
focalPoint: joi.bool(),
filesRequiredOnCreate: joi.bool(),
focalPoint: joi.bool(),
formatOptions: joi.object().keys({
format: joi.string(),
options: joi.object(),

View File

@@ -7,7 +7,9 @@ export { configToJSONSchema, entityToJSONSchema } from '../utilities/configToJSO
export { createArrayFromCommaDelineated } from '../utilities/createArrayFromCommaDelineated'
export { deepCopyObject } from '../utilities/deepCopyObject'
export { deepMerge } from '../utilities/deepMerge'
export { fieldSchemaToJSON } from '../utilities/fieldSchemaToJSON'
export { default as flattenTopLevelFields } from '../utilities/flattenTopLevelFields'
export { formatLabels, formatNames, toWords } from '../utilities/formatLabels'
export { getIDType } from '../utilities/getIDType'

View File

@@ -3,7 +3,6 @@ import type { EditorProps } from '@monaco-editor/react'
import type { TFunction } from 'i18next'
import type { CSSProperties } from 'react'
import monacoeditor from 'monaco-editor' // IMPORTANT - DO NOT REMOVE: This is required for pnpm's default isolated mode to work - even though the import is not used. This is due to a typescript bug: https://github.com/microsoft/TypeScript/issues/47663#issuecomment-1519138189. (tsbugisolatedmode)
import type { ConditionalDateProps } from '../../admin/components/elements/DatePicker/types'
import type { Description } from '../../admin/components/forms/FieldDescription/types'
import type { RowLabel } from '../../admin/components/forms/RowLabel/types'

View File

@@ -217,6 +217,9 @@ export default function buildPoliciesType(payload: Payload): GraphQLObjectType {
})
Object.values(payload.config.globals).forEach((global: SanitizedGlobalConfig) => {
if (global.graphQL === false) {
return
}
const globalPolicyType = buildPolicyType({
entity: global,
type: 'global',

View File

@@ -1,5 +1,5 @@
import type { UploadedFile } from 'express-fileupload'
import type { Sharp } from 'sharp'
import type { Sharp, SharpOptions } from 'sharp'
import { fromBuffer } from 'file-type'
import mkdirp from 'mkdirp'
@@ -98,12 +98,13 @@ export const generateFileData = async <T>({
let fileBuffer
let ext
let mime: string
const isSharpRequired = fileSupportsResize && (resizeOptions || formatOptions || trimOptions)
const sharpOptions: sharp.SharpOptions = {}
const sharpOptions: SharpOptions = {}
if (fileIsAnimated) sharpOptions.animated = true
if (fileSupportsResize && (resizeOptions || formatOptions || trimOptions)) {
if (isSharpRequired) {
if (file.tempFilePath) {
sharpFile = sharp(file.tempFilePath, sharpOptions).rotate() // pass rotate() to auto-rotate based on EXIF data. https://github.com/payloadcms/payload/pull/3081
} else {
@@ -170,7 +171,7 @@ export const generateFileData = async <T>({
fileData.filename = fsSafeName
let fileForResize = file
if (cropData) {
if (isSharpRequired && cropData) {
const { data: croppedImage, info } = await cropImage({ cropData, dimensions, file })
filesToSave.push({

View File

@@ -1,4 +1,5 @@
import type { UploadedFile } from 'express-fileupload'
import type { OutputInfo } from 'sharp'
import { fromBuffer } from 'file-type'
import fs from 'fs'
@@ -65,7 +66,7 @@ const getSanitizedImageData = (sourceImage: string): SanitizedImageData => {
*/
const createImageName = (
outputImageName: string,
{ height, width }: sharp.OutputInfo,
{ height, width }: OutputInfo,
extension: string,
) => `${outputImageName}-${width}x${height}.${extension}`

View File

@@ -63,6 +63,7 @@ export type IncomingUploadType = {
adminThumbnail?: GetAdminThumbnail | string
crop?: boolean
disableLocalStorage?: boolean
filesRequiredOnCreate?: boolean
focalPoint?: boolean
/** Options for original upload file only. For sizes, set each formatOptions individually. */
formatOptions?: ImageUploadFormatOptions
@@ -80,6 +81,7 @@ export type Upload = {
adminThumbnail?: GetAdminThumbnail | string
crop?: boolean
disableLocalStorage?: boolean
filesRequiredOnCreate?: boolean
focalPoint?: boolean
formatOptions?: ImageUploadFormatOptions
handlers?: any[]
@@ -90,7 +92,6 @@ export type Upload = {
staticOptions?: serveStatic.ServeStaticOptions<express.Response<any, Record<string, any>>>
staticURL: string
trimOptions?: ImageUploadTrimOptions
filesRequiredOnCreate?: boolean
}
export type File = {

View File

@@ -1,6 +1,17 @@
import type { FieldTypes } from '../exports/config'
import type { Field } from '../fields/config/types'
export const fieldSchemaToJSON = (fields: Field[]): Record<string, unknown>[] => {
export type FieldSchemaJSON = {
blocks?: FieldSchemaJSON // TODO: conditionally add based on `type`
fields?: FieldSchemaJSON // TODO: conditionally add based on `type`
hasMany?: boolean // TODO: conditionally add based on `type`
name: string
relationTo?: string // TODO: conditionally add based on `type`
slug?: string // TODO: conditionally add based on `type`
type: keyof FieldTypes
}[]
export const fieldSchemaToJSON = (fields: Field[]): FieldSchemaJSON => {
return fields.reduce((acc, field) => {
let result = acc

View File

@@ -3,6 +3,7 @@ import type { PayloadRequest } from '../express/types'
import type { SanitizedGlobalConfig } from '../globals/config/types'
import type { Payload } from '../payload'
import { deepCopyObject } from '../utilities/deepCopyObject'
import sanitizeInternalFields from '../utilities/sanitizeInternalFields'
import { enforceMaxVersions } from './enforceMaxVersions'
@@ -30,7 +31,7 @@ export const saveVersion = async ({
let result
let createNewVersion = true
const now = new Date().toISOString()
const versionData = { ...doc }
const versionData = deepCopyObject(doc)
if (draft) versionData._status = 'draft'
if (versionData._id) delete versionData._id

View File

@@ -28,6 +28,12 @@ module.exports = {
'perfectionist/sort-vue-attributes': 'off',
},
},
{
files: ['**/*.ts'],
rules: {
'perfectionist/sort-objects': 'off',
},
},
],
parserOptions: {
project: ['./tsconfig.json'],

View File

@@ -1,3 +0,0 @@
module.exports = {
extends: ['@payloadcms'],
}

View File

@@ -1,4 +1,4 @@
import { RelationshipField } from 'payload/types'
import type { RelationshipField } from 'payload/types'
const createParentField = (
relationTo: string,

View File

@@ -1,6 +1,7 @@
import type { CollectionAfterChangeHook, CollectionConfig } from 'payload/types'
import type { PluginConfig } from '../types'
import populateBreadcrumbs from '../utilities/populateBreadcrumbs'
const resaveChildren =

View File

@@ -1,10 +1,11 @@
import type { Plugin } from 'payload/config'
import type { PluginConfig } from './types'
import createBreadcrumbsField from './fields/breadcrumbs'
import createParentField from './fields/parent'
import resaveChildren from './hooks/resaveChildren'
import resaveSelfAfterCreate from './hooks/resaveSelfAfterCreate'
import type { PluginConfig } from './types'
import populateBreadcrumbs from './utilities/populateBreadcrumbs'
const nestedDocs =

View File

@@ -1,7 +1,7 @@
export interface Breadcrumb {
url?: string
label: string
doc: string
label: string
url?: string
}
export type GenerateURL = (
@@ -15,9 +15,9 @@ export type GenerateLabel = (
) => string
export interface PluginConfig {
collections: string[]
generateURL?: GenerateURL
generateLabel?: GenerateLabel
parentFieldSlug?: string
breadcrumbsFieldSlug?: string
collections: string[]
generateLabel?: GenerateLabel
generateURL?: GenerateURL
parentFieldSlug?: string
}

View File

@@ -1,6 +1,7 @@
import type { CollectionConfig } from 'payload/types'
import type { PluginConfig } from '../types'
import formatBreadcrumb from './formatBreadcrumb'
import getParents from './getParents'

View File

@@ -8,6 +8,7 @@ const git = simpleGit()
const packagesDir = path.resolve(__dirname, '../../packages')
export type PackageDetails = {
commitMessage: string
name: string
newCommits: number
shortName: string
@@ -33,7 +34,8 @@ export const getPackageDetails = async (): Promise<PackageDetails[]> => {
const publishedVersion = json?.['dist-tags']?.latest
const publishDate = json?.time?.[publishedVersion]
const prevGitTag = `${dirName}/${packageJson.version}`
const prevGitTag =
dirName === 'payload' ? `v${packageJson.version}` : `${dirName}/${packageJson.version}`
const prevGitTagHash = await git.revparse(prevGitTag)
const newCommits = await git.log({
@@ -42,6 +44,7 @@ export const getPackageDetails = async (): Promise<PackageDetails[]> => {
})
return {
commitMessage: newCommits.latest?.message ?? '',
name: packageJson.name as string,
newCommits: newCommits.total,
shortName: dirName,
@@ -63,15 +66,20 @@ export const showPackageDetails = (details: PackageDetails[]) => {
${details
.map((p) => {
const name = p?.newCommits ? chalk.bold.green(p?.shortName.padEnd(28)) : p?.shortName.padEnd(28)
const publishData = `${p?.publishedVersion} at ${p?.publishDate
.split(':')
.slice(0, 2)
.join(':')
.replace('T', ' ')}`
const newCommits = `${p?.newCommits ? `${chalk.bold.green(p?.newCommits)} new commits` : ''}`
const name = p?.newCommits
? chalk.bold.green(p?.shortName.padEnd(28))
: chalk.dim(p?.shortName.padEnd(28))
const publishData = `${p?.publishedVersion.padEnd(8)}${p?.publishDate.split('T')[0]}`
const newCommits = p?.newCommits ? chalk.bold.green(`${p?.newCommits} `) : ' '
const commitMessage = p?.commitMessage
? chalk.dim(
p.commitMessage.length < 57
? p.commitMessage
: p.commitMessage.substring(0, 60).concat('...'),
)
: ''
return ` ${name}${publishData} ${newCommits}`
return ` ${name}${newCommits}${publishData} ${commitMessage}`
})
.join('\n')}

View File

@@ -17,6 +17,10 @@ async function main() {
abort(`Invalid bump type: ${bump}.\n\nMust be one of: ${semver.RELEASE_TYPES.join(', ')}`)
}
if (bump.startsWith('pre') && tag === 'latest') {
abort(`Prerelease bumps must have tag: beta or canary`)
}
const packageDetails = await getPackageDetails()
showPackageDetails(packageDetails)
@@ -63,7 +67,7 @@ async function main() {
${packagesToRelease
.map((p) => {
const { shortName, version } = packageMap[p]
return ` ${shortName.padEnd(24)} ${version} -> ${semver.inc(version, bump)}`
return ` ${shortName.padEnd(24)} ${version} -> ${semver.inc(version, bump, tag)}`
})
.join('\n')}
`)
@@ -77,29 +81,43 @@ ${packagesToRelease
const results: { name: string; success: boolean }[] = []
for (const pkg of packagesToRelease) {
const { packagePath, shortName } = packageMap[pkg]
const { packagePath, shortName, name: registryName } = packageMap[pkg]
try {
console.log(chalk.bold(`\n\nPublishing ${shortName}...\n\n`))
console.log(chalk.bold(`\n\n🚀 Publishing ${shortName}...\n\n`))
let npmVersionCmd = `npm --no-git-tag-version --prefix ${packagePath} version ${bump}`
if (tag !== 'latest') {
npmVersionCmd += ` --preid ${tag}`
}
execSync(npmVersionCmd, execOpts)
execSync(`git add ${packagePath}/package.json`, execOpts)
const packageObj = await fse.readJson(`${packagePath}/package.json`)
const newVersion = packageObj.version
const tagName = `${shortName}/${newVersion}`
execSync(`git commit -m "chore(release): ${tagName}"`, execOpts)
execSync(`git tag -a ${tagName} -m "${tagName}"`, execOpts)
const shouldCommit = await confirm(`🧑‍💻 Commit Release?`)
if (shouldCommit) {
execSync(`git add ${packagePath}/package.json`, execOpts)
execSync(`git commit -m "chore(release): ${tagName} [skip ci]" `, execOpts)
}
const shouldTag = await confirm(`🏷️ Tag ${tagName}?`)
if (shouldTag) {
execSync(`git tag -a ${tagName} -m "${tagName}"`, execOpts)
if (pkg === 'payload') {
execSync(`git tag -a v${newVersion} -m "v${newVersion}"`, execOpts)
}
}
let publishCmd = `pnpm publish -C ${packagePath} --no-git-checks`
if (tag !== 'latest') {
publishCmd += ` --tag ${tag}`
}
execSync(publishCmd, execOpts)
const shouldPublish = await confirm(`🚢 Publish ${registryName}${chalk.yellow('@' + tag)}?`)
if (shouldPublish) {
execSync(publishCmd, execOpts)
}
results.push({ name: shortName, success: true })
} catch (error) {

View File

@@ -2,7 +2,7 @@
A blank template for [Payload](https://github.com/payloadcms/payload) to help you get up and running quickly. This repo may have been created by running `npx create-payload-app` and selecting the "blank" template or by cloning this template on [Payload Cloud](https://payloadcms.com/new/clone/blank).
See the official [Examples Directory](https://github.com/payloadcms/payload/tree/master/examples) for details on how to use Payload in a variety of different ways.
See the official [Examples Directory](https://github.com/payloadcms/payload/tree/main/examples) for details on how to use Payload in a variety of different ways.
## Development

View File

@@ -12,7 +12,8 @@
"serve": "cross-env PAYLOAD_CONFIG_PATH=dist/payload.config.js NODE_ENV=production node dist/server.js",
"copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png}\" dist/",
"generate:types": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:types",
"generate:graphQLSchema": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:graphQLSchema"
"generate:graphQLSchema": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:graphQLSchema",
"payload": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload"
},
"dependencies": {
"@payloadcms/bundler-webpack": "^1.0.0",

View File

@@ -18,12 +18,13 @@
"generate:types": "cross-env PAYLOAD_CONFIG_PATH=src/payload/payload.config.ts payload generate:types",
"generate:graphQLSchema": "PAYLOAD_CONFIG_PATH=src/payload/payload.config.ts payload generate:graphQLSchema",
"lint": "eslint src",
"lint:fix": "eslint --fix --ext .ts,.tsx src"
"lint:fix": "eslint --fix --ext .ts,.tsx src",
"payload": "cross-env PAYLOAD_CONFIG_PATH=src/payload/payload.config.ts payload"
},
"dependencies": {
"@payloadcms/bundler-webpack": "^1.0.0",
"@payloadcms/db-mongodb": "^1.0.0",
"@payloadcms/plugin-cloud": "^2.0.0",
"@payloadcms/plugin-cloud": "^2.0.0",
"@payloadcms/plugin-nested-docs": "^1.0.8",
"@payloadcms/plugin-redirects": "^1.0.0",
"@payloadcms/plugin-seo": "^1.0.10",

View File

@@ -1004,6 +1004,11 @@
dependencies:
tslib "^2.0.0"
"@drizzle-team/studio@^0.0.5":
version "0.0.5"
resolved "https://registry.npmjs.org/@drizzle-team/studio/-/studio-0.0.5.tgz#d2488ab4e8e755cc69287e2267cc4033a0e6ca35"
integrity sha512-ps5qF0tMxWRVu+V5gvCRrQNqlY92aTnIKdq27gm9LZMSdaKYZt6AVvSK1dlUMzs6Rt0Jm80b+eWct6xShBKhIw==
"@emotion/babel-plugin@^11.11.0":
version "11.11.0"
resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz#c2d872b6a7767a9d176d007f5b31f7d504bb5d6c"
@@ -1103,6 +1108,132 @@
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz#d0fce5d07b0620caa282b5131c297bb60f9d87e6"
integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==
"@esbuild-kit/core-utils@^3.3.2":
version "3.3.2"
resolved "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz#186b6598a5066f0413471d7c4d45828e399ba96c"
integrity sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==
dependencies:
esbuild "~0.18.20"
source-map-support "^0.5.21"
"@esbuild-kit/esm-loader@^2.5.5":
version "2.6.5"
resolved "https://registry.npmjs.org/@esbuild-kit/esm-loader/-/esm-loader-2.6.5.tgz#6eedee46095d7d13b1efc381e2211ed1c60e64ea"
integrity sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==
dependencies:
"@esbuild-kit/core-utils" "^3.3.2"
get-tsconfig "^4.7.0"
"@esbuild/android-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz#984b4f9c8d0377443cc2dfcef266d02244593622"
integrity sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==
"@esbuild/android-arm@0.18.20":
version "0.18.20"
resolved "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz#fedb265bc3a589c84cc11f810804f234947c3682"
integrity sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==
"@esbuild/android-x64@0.18.20":
version "0.18.20"
resolved "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz#35cf419c4cfc8babe8893d296cd990e9e9f756f2"
integrity sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==
"@esbuild/darwin-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz#08172cbeccf95fbc383399a7f39cfbddaeb0d7c1"
integrity sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==
"@esbuild/darwin-x64@0.18.20":
version "0.18.20"
resolved "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz#d70d5790d8bf475556b67d0f8b7c5bdff053d85d"
integrity sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==
"@esbuild/freebsd-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz#98755cd12707f93f210e2494d6a4b51b96977f54"
integrity sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==
"@esbuild/freebsd-x64@0.18.20":
version "0.18.20"
resolved "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz#c1eb2bff03915f87c29cece4c1a7fa1f423b066e"
integrity sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==
"@esbuild/linux-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz#bad4238bd8f4fc25b5a021280c770ab5fc3a02a0"
integrity sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==
"@esbuild/linux-arm@0.18.20":
version "0.18.20"
resolved "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz#3e617c61f33508a27150ee417543c8ab5acc73b0"
integrity sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==
"@esbuild/linux-ia32@0.18.20":
version "0.18.20"
resolved "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz#699391cccba9aee6019b7f9892eb99219f1570a7"
integrity sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==
"@esbuild/linux-loong64@0.18.20":
version "0.18.20"
resolved "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz#e6fccb7aac178dd2ffb9860465ac89d7f23b977d"
integrity sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==
"@esbuild/linux-mips64el@0.18.20":
version "0.18.20"
resolved "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz#eeff3a937de9c2310de30622a957ad1bd9183231"
integrity sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==
"@esbuild/linux-ppc64@0.18.20":
version "0.18.20"
resolved "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz#2f7156bde20b01527993e6881435ad79ba9599fb"
integrity sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==
"@esbuild/linux-riscv64@0.18.20":
version "0.18.20"
resolved "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz#6628389f210123d8b4743045af8caa7d4ddfc7a6"
integrity sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==
"@esbuild/linux-s390x@0.18.20":
version "0.18.20"
resolved "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz#255e81fb289b101026131858ab99fba63dcf0071"
integrity sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==
"@esbuild/linux-x64@0.18.20":
version "0.18.20"
resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz#c7690b3417af318a9b6f96df3031a8865176d338"
integrity sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==
"@esbuild/netbsd-x64@0.18.20":
version "0.18.20"
resolved "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz#30e8cd8a3dded63975e2df2438ca109601ebe0d1"
integrity sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==
"@esbuild/openbsd-x64@0.18.20":
version "0.18.20"
resolved "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz#7812af31b205055874c8082ea9cf9ab0da6217ae"
integrity sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==
"@esbuild/sunos-x64@0.18.20":
version "0.18.20"
resolved "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz#d5c275c3b4e73c9b0ecd38d1ca62c020f887ab9d"
integrity sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==
"@esbuild/win32-arm64@0.18.20":
version "0.18.20"
resolved "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz#73bc7f5a9f8a77805f357fab97f290d0e4820ac9"
integrity sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==
"@esbuild/win32-ia32@0.18.20":
version "0.18.20"
resolved "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz#ec93cbf0ef1085cc12e71e0d661d20569ff42102"
integrity sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==
"@esbuild/win32-x64@0.18.20":
version "0.18.20"
resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz#786c5f41f043b07afb1af37683d7c33668858f6d"
integrity sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==
"@eslint-community/eslint-utils@^4.2.0":
version "4.4.0"
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
@@ -1283,6 +1414,71 @@
resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz#08d6c5e20cf7e4cc02fd181c4b0c225cd31dbb60"
integrity sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==
"@libsql/client@^0.3.1":
version "0.3.5"
resolved "https://registry.npmjs.org/@libsql/client/-/client-0.3.5.tgz#dfb80f4bd1969faeec7b550aabc68ba0653aa212"
integrity sha512-4fZxGh0qKW5dtp1yuQLRvRAtbt02V4jzjM9sHSmz5k25xZTLg7/GlNudKdqKZrjJXEV5PvDNsczupBtedZZovw==
dependencies:
"@libsql/hrana-client" "^0.5.5"
js-base64 "^3.7.5"
libsql "^0.1.22"
"@libsql/darwin-arm64@0.1.29":
version "0.1.29"
resolved "https://registry.npmjs.org/@libsql/darwin-arm64/-/darwin-arm64-0.1.29.tgz#0772d8054ec8187efaa717d46fb1900845beb0d6"
integrity sha512-lYSQaJV6Jsjuh/zms7i6QCL2GLQT17zeFVUZBNQxje0GyUtpvvfkz10S32n5+cY1TRbsbBCKzYhYxnTaTdD7Eg==
"@libsql/darwin-x64@0.1.29":
version "0.1.29"
resolved "https://registry.npmjs.org/@libsql/darwin-x64/-/darwin-x64-0.1.29.tgz#4f6e1a2b47606203048de283f1fee437a8db4ce7"
integrity sha512-LFf6oSiBCQ0jJmh4WrQQKpa99qZVdY6KPNHU621hPV6LarEA/59YErMfAZOQY5A0iAhAzJ9ml0npt0Z63MU9dA==
"@libsql/hrana-client@^0.5.5":
version "0.5.5"
resolved "https://registry.npmjs.org/@libsql/hrana-client/-/hrana-client-0.5.5.tgz#5861aba59a593588283f9c33573ec5f571c35735"
integrity sha512-i+hDBpiV719poqEiHupUUZYKJ9YSbCRFe5Q2PQ0v3mHIftePH6gayLjp2u6TXbqbO/Dv6y8yyvYlBXf/kFfRZA==
dependencies:
"@libsql/isomorphic-fetch" "^0.1.10"
"@libsql/isomorphic-ws" "^0.1.5"
js-base64 "^3.7.5"
node-fetch "^3.3.2"
"@libsql/isomorphic-fetch@^0.1.10":
version "0.1.10"
resolved "https://registry.npmjs.org/@libsql/isomorphic-fetch/-/isomorphic-fetch-0.1.10.tgz#55f3e092a85e05babbd46b01521cbd6d43e38963"
integrity sha512-dH0lMk50gKSvEKD78xWMu60SY1sjp1sY//iFLO0XMmBwfVfG136P9KOk06R4maBdlb8KMXOzJ1D28FR5ZKnHTA==
dependencies:
"@types/node-fetch" "^2.2.6"
node-fetch "^2.6.12"
"@libsql/isomorphic-ws@^0.1.5":
version "0.1.5"
resolved "https://registry.npmjs.org/@libsql/isomorphic-ws/-/isomorphic-ws-0.1.5.tgz#e2d1faf965ba0f3be9301fbf5640164d03c4e606"
integrity sha512-DtLWIH29onUYR00i0GlQ3UdcTRC6EP4u9w/h9LxpUZJWRMARk6dQwZ6Jkd+QdwVpuAOrdxt18v0K2uIYR3fwFg==
dependencies:
"@types/ws" "^8.5.4"
ws "^8.13.0"
"@libsql/linux-arm64-gnu@0.1.29":
version "0.1.29"
resolved "https://registry.npmjs.org/@libsql/linux-arm64-gnu/-/linux-arm64-gnu-0.1.29.tgz#022466bd610d53c94411a43e2b82c553bde85eb9"
integrity sha512-9Mtz3xRUpj1gRMFqi0+R/e2hAq711u0ogTvvlNWKjA9OZZfPgk0vLlT1Pqm0Pf1gyVc393RafZcCGP3FZMkjmg==
"@libsql/linux-x64-gnu@0.1.29":
version "0.1.29"
resolved "https://registry.npmjs.org/@libsql/linux-x64-gnu/-/linux-x64-gnu-0.1.29.tgz#b3919986f76a9710f47de6827d774dcf0289d70b"
integrity sha512-EE3YpxqSRXnvxwPq38A9j1/poqzEJrDXnGEKcVcWqC2FB/qPJHxnAITBUNYicovdrrZYWRE5EM0LdSO6CMWi6A==
"@libsql/linux-x64-musl@0.1.29":
version "0.1.29"
resolved "https://registry.npmjs.org/@libsql/linux-x64-musl/-/linux-x64-musl-0.1.29.tgz#08cc1ab41be18387f2b786853f2a5e9466b86c74"
integrity sha512-CJc+hKbUzF0JpAqQHT7P3SPb/NLCWsPzBD6j8fvHH34JLRYaIu9H8WrXsxAcVF65B7Jvat/CXnSe+iOctonBow==
"@libsql/win32-x64-msvc@0.1.29":
version "0.1.29"
resolved "https://registry.npmjs.org/@libsql/win32-x64-msvc/-/win32-x64-msvc-0.1.29.tgz#c1c004a732694ca0db3aa1134e2e304fef988e0b"
integrity sha512-pIdMzY+NOKHDHiTUGLwMd3TICUiHbcxPedPRYJAI6ofTSu77JEmkmfkXcCOL8L8B9gio4ZuNeok+1i/ZPf6oeQ==
"@monaco-editor/loader@^1.3.3":
version "1.3.3"
resolved "https://registry.yarnpkg.com/@monaco-editor/loader/-/loader-1.3.3.tgz#7f1742bd3cc21c0362a46a4056317f6e5215cfca"
@@ -1297,6 +1493,11 @@
dependencies:
"@monaco-editor/loader" "^1.3.3"
"@neon-rs/load@^0.0.4":
version "0.0.4"
resolved "https://registry.npmjs.org/@neon-rs/load/-/load-0.0.4.tgz#2a2a3292c6f1fef043f49886712d3c96a547532e"
integrity sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==
"@next/env@13.4.19":
version "13.4.19"
resolved "https://registry.yarnpkg.com/@next/env/-/env-13.4.19.tgz#46905b4e6f62da825b040343cbc233144e9578d3"
@@ -1425,6 +1626,19 @@
prompts "2.4.2"
uuid "9.0.0"
"@payloadcms/db-postgres@^0.1.9":
version "0.1.9"
resolved "https://registry.npmjs.org/@payloadcms/db-postgres/-/db-postgres-0.1.9.tgz#17f3bbd6cd39ba3c9b0cf939953d1e11dac2cca8"
integrity sha512-PWtA1otpyCQasRZzA9U17BQNFaPjXRBFQOjx+CU6FiFjLkNkSEO4iK5Pdwsz23UrTlUbwNvgWd1u1+Xm4PHp9A==
dependencies:
"@libsql/client" "^0.3.1"
console-table-printer "2.11.2"
drizzle-kit "0.19.13-e99bac1"
drizzle-orm "0.28.5"
pg "8.11.3"
prompts "2.4.2"
to-snake-case "1.0.0"
"@payloadcms/eslint-config@^0.0.1":
version "0.0.1"
resolved "https://registry.yarnpkg.com/@payloadcms/eslint-config/-/eslint-config-0.0.1.tgz#4324702ddef6c773b3f3033795a13e6b50c95a92"
@@ -2221,6 +2435,14 @@
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca"
integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==
"@types/node-fetch@^2.2.6":
version "2.6.6"
resolved "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.6.tgz#b72f3f4bc0c0afee1c0bc9cff68e041d01e3e779"
integrity sha512-95X8guJYhfqiuVVhRFxVQcf4hW/2bCuoPwDasMf/531STFoNoWTT7YDnWdXHEZKqAGUigmpG31r2FE70LwnzJw==
dependencies:
"@types/node" "*"
form-data "^4.0.0"
"@types/node@*", "@types/node@>=8.1.0":
version "20.5.9"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.5.9.tgz#a70ec9d8fa0180a314c3ede0e20ea56ff71aed9a"
@@ -2326,6 +2548,13 @@
"@types/node" "*"
"@types/webidl-conversions" "*"
"@types/ws@^8.5.4":
version "8.5.7"
resolved "https://registry.npmjs.org/@types/ws/-/ws-8.5.7.tgz#1ca585074fe5d2c81dec7a3d451f244a2a6d83cb"
integrity sha512-6UrLjiDUvn40CMrAubXuIVtj2PEfKDffJS7ychvnPU44j+KVeXmdHHTgqcM/dxLUTHxlXHiFM8Skmb8ozGdTnQ==
dependencies:
"@types/node" "*"
"@types/yargs-parser@*":
version "21.0.1"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.1.tgz#07773d7160494d56aa882d7531aac7319ea67c3b"
@@ -2948,6 +3177,11 @@ buffer-from@^1.0.0:
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
buffer-writer@2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04"
integrity sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==
buffer@4.9.2:
version "4.9.2"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8"
@@ -3024,6 +3258,11 @@ camel-case@^4.1.2:
pascal-case "^3.1.2"
tslib "^2.0.3"
camelcase@^7.0.1:
version "7.0.1"
resolved "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz#f02e50af9fd7782bc8b88a3558c32fd3a388f048"
integrity sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==
caniuse-api@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0"
@@ -3061,6 +3300,11 @@ chalk@^4.0.0:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
chalk@^5.2.0:
version "5.3.0"
resolved "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385"
integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==
charenc@0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
@@ -3108,7 +3352,7 @@ clean-css@^5.2.2:
dependencies:
source-map "~0.6.0"
cli-color@^2.0.2:
cli-color@^2.0.0, cli-color@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/cli-color/-/cli-color-2.0.3.tgz#73769ba969080629670f3f2ef69a4bf4e7cc1879"
integrity sha512-OkoZnxyC4ERN3zLzZaY9Emb7f/MhBOIpePv0Ycok0fJYT+Ouo00UBEIwsVsr0yoow++n5YWlSUgST9GKhNHiRQ==
@@ -3224,6 +3468,11 @@ commander@^8.3.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==
commander@^9.4.1:
version "9.5.0"
resolved "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30"
integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==
compressible@~2.0.16:
version "2.0.18"
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba"
@@ -3561,6 +3810,11 @@ d@1, d@^1.0.1:
es5-ext "^0.10.50"
type "^1.0.1"
data-uri-to-buffer@^4.0.0:
version "4.0.1"
resolved "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e"
integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==
dataloader@2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.2.2.tgz#216dc509b5abe39d43a9b9d97e6e5e473dfbe3e0"
@@ -3687,7 +3941,7 @@ detect-file@^1.0.0:
resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7"
integrity sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==
detect-libc@^2.0.0, detect-libc@^2.0.1:
detect-libc@2.0.2, detect-libc@^2.0.0, detect-libc@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.2.tgz#8ccf2ba9315350e1241b88d0ac3b0e1fbd99605d"
integrity sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==
@@ -3702,6 +3956,13 @@ diff@^5.1.0:
resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40"
integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==
difflib@~0.2.1:
version "0.2.4"
resolved "https://registry.npmjs.org/difflib/-/difflib-0.2.4.tgz#b5e30361a6db023176d562892db85940a718f47e"
integrity sha512-9YVwmMb0wQHQNr5J9m6BSj6fk4pfGITGQOOs+D9Fl+INODWFOfvhIU1hNv6GgR1RBoC/9NJcwu77zShxV0kT7w==
dependencies:
heap ">= 0.2.0"
dir-glob@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
@@ -3818,6 +4079,36 @@ dotenv@8.6.0, dotenv@^8.2.0:
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b"
integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==
dreamopt@~0.8.0:
version "0.8.0"
resolved "https://registry.npmjs.org/dreamopt/-/dreamopt-0.8.0.tgz#5bcc80be7097e45fc489c342405ab68140a8c1d9"
integrity sha512-vyJTp8+mC+G+5dfgsY+r3ckxlz+QMX40VjPQsZc5gxVAxLmi64TBoVkP54A/pRAXMXsbu2GMMBrZPxNv23waMg==
dependencies:
wordwrap ">=0.0.2"
drizzle-kit@0.19.13-e99bac1:
version "0.19.13-e99bac1"
resolved "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.19.13-e99bac1.tgz#150e37b3b28d1e614eb63cd623367183902167b3"
integrity sha512-25Ahwm7CoaXCcrSuDjNqNH7vwrjt1s+I4O1yXVDBwYnyRe4RY8ZD57s5iA9PE5C+IJwBJtxBSXYWkat+efb3ug==
dependencies:
"@drizzle-team/studio" "^0.0.5"
"@esbuild-kit/esm-loader" "^2.5.5"
camelcase "^7.0.1"
chalk "^5.2.0"
commander "^9.4.1"
esbuild "^0.18.6"
esbuild-register "^3.4.2"
glob "^8.1.0"
hanji "^0.0.5"
json-diff "0.9.0"
minimatch "^7.4.3"
zod "^3.20.2"
drizzle-orm@0.28.5:
version "0.28.5"
resolved "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.28.5.tgz#67aa2a0fbe7d7a2066856188c2ac48106e14046f"
integrity sha512-6r6Iw4c38NAmW6TiKH3TUpGUQ1YdlEoLJOQptn8XPx3Z63+vFNKfAiANqrIiYZiMjKR9+NYAL219nFrmo1duXA==
duplexer@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6"
@@ -4033,6 +4324,41 @@ es6-weak-map@^2.0.3:
es6-iterator "^2.0.3"
es6-symbol "^3.1.1"
esbuild-register@^3.4.2:
version "3.5.0"
resolved "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.5.0.tgz#449613fb29ab94325c722f560f800dd946dc8ea8"
integrity sha512-+4G/XmakeBAsvJuDugJvtyF1x+XJT4FMocynNpxrvEBViirpfUn2PgNpCHedfWhF4WokNsO/OvMKrmJOIJsI5A==
dependencies:
debug "^4.3.4"
esbuild@^0.18.6, esbuild@~0.18.20:
version "0.18.20"
resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz#4709f5a34801b43b799ab7d6d82f7284a9b7a7a6"
integrity sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==
optionalDependencies:
"@esbuild/android-arm" "0.18.20"
"@esbuild/android-arm64" "0.18.20"
"@esbuild/android-x64" "0.18.20"
"@esbuild/darwin-arm64" "0.18.20"
"@esbuild/darwin-x64" "0.18.20"
"@esbuild/freebsd-arm64" "0.18.20"
"@esbuild/freebsd-x64" "0.18.20"
"@esbuild/linux-arm" "0.18.20"
"@esbuild/linux-arm64" "0.18.20"
"@esbuild/linux-ia32" "0.18.20"
"@esbuild/linux-loong64" "0.18.20"
"@esbuild/linux-mips64el" "0.18.20"
"@esbuild/linux-ppc64" "0.18.20"
"@esbuild/linux-riscv64" "0.18.20"
"@esbuild/linux-s390x" "0.18.20"
"@esbuild/linux-x64" "0.18.20"
"@esbuild/netbsd-x64" "0.18.20"
"@esbuild/openbsd-x64" "0.18.20"
"@esbuild/sunos-x64" "0.18.20"
"@esbuild/win32-arm64" "0.18.20"
"@esbuild/win32-ia32" "0.18.20"
"@esbuild/win32-x64" "0.18.20"
escalade@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
@@ -4395,6 +4721,14 @@ fastq@^1.6.0:
dependencies:
reusify "^1.0.4"
fetch-blob@^3.1.2, fetch-blob@^3.1.4:
version "3.2.0"
resolved "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9"
integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==
dependencies:
node-domexception "^1.0.0"
web-streams-polyfill "^3.0.3"
file-entry-cache@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
@@ -4534,6 +4868,13 @@ form-data@^4.0.0:
combined-stream "^1.0.8"
mime-types "^2.1.12"
formdata-polyfill@^4.0.10:
version "4.0.10"
resolved "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423"
integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==
dependencies:
fetch-blob "^3.1.2"
forwarded@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
@@ -4638,6 +4979,13 @@ get-tsconfig@4.6.2:
dependencies:
resolve-pkg-maps "^1.0.0"
get-tsconfig@^4.7.0:
version "4.7.2"
resolved "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz#0dcd6fb330391d46332f4c6c1bf89a6514c2ddce"
integrity sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==
dependencies:
resolve-pkg-maps "^1.0.0"
github-from-package@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce"
@@ -4817,6 +5165,14 @@ gzip-size@^6.0.0:
dependencies:
duplexer "^0.1.2"
hanji@^0.0.5:
version "0.0.5"
resolved "https://registry.npmjs.org/hanji/-/hanji-0.0.5.tgz#22a5092e53b2a83ed6172c488ae0d68eb3119213"
integrity sha512-Abxw1Lq+TnYiL4BueXqMau222fPSPMFtya8HdpWsz/xVAhifXou71mPh/kY2+08RgFcVccjG3uZHs6K5HAe3zw==
dependencies:
lodash.throttle "^4.1.1"
sisteransi "^1.0.5"
has-bigints@^1.0.1, has-bigints@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa"
@@ -4868,6 +5224,11 @@ he@^1.2.0:
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
"heap@>= 0.2.0":
version "0.2.7"
resolved "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz#1e6adf711d3f27ce35a81fe3b7bd576c2260a8fc"
integrity sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==
help-me@^4.0.1:
version "4.2.0"
resolved "https://registry.yarnpkg.com/help-me/-/help-me-4.2.0.tgz#50712bfd799ff1854ae1d312c36eafcea85b0563"
@@ -5417,6 +5778,11 @@ joycon@^3.1.1:
resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03"
integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==
js-base64@^3.7.5:
version "3.7.5"
resolved "https://registry.npmjs.org/js-base64/-/js-base64-3.7.5.tgz#21e24cf6b886f76d6f5f165bfcd69cc55b9e3fca"
integrity sha512-3MEt5DTINKqfScXKfJFrRbxkrnk2AxPWGBL/ycjz4dK8iqiSJ06UxD8jh8xuh6p10TX4t2+7FsBYVxxQbMg+qA==
js-beautify@^1.6.12:
version "1.14.9"
resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.14.9.tgz#a5db728bc5a0d84d3b1a597c376b29bd4d39c8e5"
@@ -5449,6 +5815,15 @@ json-buffer@3.0.1:
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
json-diff@0.9.0:
version "0.9.0"
resolved "https://registry.npmjs.org/json-diff/-/json-diff-0.9.0.tgz#e7c536798053cb409113d7403c774849e8a0d7ff"
integrity sha512-cVnggDrVkAAA3OvFfHpFEhOnmcsUpleEKq4d4O8sQWWSH40MBrWstKigVB1kGrgLWzuom+7rRdaCsnBD6VyObQ==
dependencies:
cli-color "^2.0.0"
difflib "~0.2.1"
dreamopt "~0.8.0"
json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
@@ -5610,6 +5985,21 @@ levn@^0.4.1:
prelude-ls "^1.2.1"
type-check "~0.4.0"
libsql@^0.1.22:
version "0.1.29"
resolved "https://registry.npmjs.org/libsql/-/libsql-0.1.29.tgz#d3b929890d1a03ddb0a9dc054d069b041c1bdf28"
integrity sha512-m81nXwDjz7WIwAblmhGJ68f6wJPtClG2N9kNxYG92vB7sALUGU3THza2J2+Mfg0YC+cy+hehorb7zHETj8doKw==
dependencies:
"@neon-rs/load" "^0.0.4"
detect-libc "2.0.2"
optionalDependencies:
"@libsql/darwin-arm64" "0.1.29"
"@libsql/darwin-x64" "0.1.29"
"@libsql/linux-arm64-gnu" "0.1.29"
"@libsql/linux-x64-gnu" "0.1.29"
"@libsql/linux-x64-musl" "0.1.29"
"@libsql/win32-x64-msvc" "0.1.29"
lilconfig@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52"
@@ -5751,6 +6141,11 @@ lodash.snakecase@4.1.1:
resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d"
integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==
lodash.throttle@^4.1.1:
version "4.1.1"
resolved "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4"
integrity sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==
lodash.uniq@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
@@ -5969,6 +6364,13 @@ minimatch@^5.0.1:
dependencies:
brace-expansion "^2.0.1"
minimatch@^7.4.3:
version "7.4.6"
resolved "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz#845d6f254d8f4a5e4fd6baf44d5f10c8448365fb"
integrity sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==
dependencies:
brace-expansion "^2.0.1"
minimist@1.2.8, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.6:
version "1.2.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
@@ -6161,13 +6563,27 @@ node-addon-api@^5.0.0:
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762"
integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==
node-fetch@^2.6.1:
node-domexception@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5"
integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==
node-fetch@^2.6.1, node-fetch@^2.6.12:
version "2.7.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
dependencies:
whatwg-url "^5.0.0"
node-fetch@^3.3.2:
version "3.3.2"
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz#d1e889bacdf733b4ff3b2b243eb7a12866a0b78b"
integrity sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==
dependencies:
data-uri-to-buffer "^4.0.0"
fetch-blob "^3.1.4"
formdata-polyfill "^4.0.10"
node-releases@^2.0.13:
version "2.0.13"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d"
@@ -6368,6 +6784,11 @@ p-try@^2.0.0:
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
packet-reader@1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz#9238e5480dedabacfe1fe3f2771063f164157d74"
integrity sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==
param-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5"
@@ -6625,6 +7046,64 @@ peek-readable@^4.1.0:
resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-4.1.0.tgz#4ece1111bf5c2ad8867c314c81356847e8a62e72"
integrity sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==
pg-cloudflare@^1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz#e6d5833015b170e23ae819e8c5d7eaedb472ca98"
integrity sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==
pg-connection-string@^2.6.2:
version "2.6.2"
resolved "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz#713d82053de4e2bd166fab70cd4f26ad36aab475"
integrity sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==
pg-int8@1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c"
integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==
pg-pool@^3.6.1:
version "3.6.1"
resolved "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz#5a902eda79a8d7e3c928b77abf776b3cb7d351f7"
integrity sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==
pg-protocol@^1.6.0:
version "1.6.0"
resolved "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz#4c91613c0315349363af2084608db843502f8833"
integrity sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==
pg-types@^2.1.0:
version "2.2.0"
resolved "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz#2d0250d636454f7cfa3b6ae0382fdfa8063254a3"
integrity sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==
dependencies:
pg-int8 "1.0.1"
postgres-array "~2.0.0"
postgres-bytea "~1.0.0"
postgres-date "~1.0.4"
postgres-interval "^1.1.0"
pg@8.11.3:
version "8.11.3"
resolved "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz#d7db6e3fe268fcedd65b8e4599cda0b8b4bf76cb"
integrity sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==
dependencies:
buffer-writer "2.0.0"
packet-reader "1.0.0"
pg-connection-string "^2.6.2"
pg-pool "^3.6.1"
pg-protocol "^1.6.0"
pg-types "^2.1.0"
pgpass "1.x"
optionalDependencies:
pg-cloudflare "^1.1.1"
pgpass@1.x:
version "1.0.5"
resolved "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz#9b873e4a564bb10fa7a7dbd55312728d422a223d"
integrity sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==
dependencies:
split2 "^4.1.0"
picocolors@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
@@ -7228,6 +7707,28 @@ postcss@8.4.31, postcss@^8.2.15, postcss@^8.4.24:
picocolors "^1.0.0"
source-map-js "^1.0.2"
postgres-array@~2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e"
integrity sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==
postgres-bytea@~1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35"
integrity sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==
postgres-date@~1.0.4:
version "1.0.7"
resolved "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz#51bc086006005e5061c591cee727f2531bf641a8"
integrity sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==
postgres-interval@^1.1.0:
version "1.2.0"
resolved "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz#b460c82cb1587507788819a06aa0fffdb3544695"
integrity sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==
dependencies:
xtend "^4.0.0"
prebuild-install@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45"
@@ -8149,7 +8650,7 @@ source-list-map@^2.0.0:
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
source-map-support@^0.5.13, source-map-support@~0.5.20:
source-map-support@^0.5.13, source-map-support@^0.5.21, source-map-support@~0.5.20:
version "0.5.21"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
@@ -8174,7 +8675,7 @@ sparse-bitfield@^3.0.3:
dependencies:
memory-pager "^1.0.2"
split2@^4.0.0:
split2@^4.0.0, split2@^4.1.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4"
integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==
@@ -8504,6 +9005,11 @@ to-fast-properties@^2.0.0:
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==
to-no-case@^1.0.0:
version "1.0.2"
resolved "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz#c722907164ef6b178132c8e69930212d1b4aa16a"
integrity sha512-Z3g735FxuZY8rodxV4gH7LxClE4H0hTIyHNIHdk+vpQxjLm0cwnKXq/OFVZ76SOQmto7txVcwSCwkU5kqp+FKg==
to-regex-range@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
@@ -8511,6 +9017,20 @@ to-regex-range@^5.0.1:
dependencies:
is-number "^7.0.0"
to-snake-case@1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/to-snake-case/-/to-snake-case-1.0.0.tgz#ce746913897946019a87e62edfaeaea4c608ab8c"
integrity sha512-joRpzBAk1Bhi2eGEYBjukEWHOe/IvclOkiJl3DtA91jV6NwQ3MwXA4FHYeqk8BNp/D8bmi9tcNbRu/SozP0jbQ==
dependencies:
to-space-case "^1.0.0"
to-space-case@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz#b052daafb1b2b29dc770cea0163e5ec0ebc9fc17"
integrity sha512-rLdvwXZ39VOn1IxGL3V6ZstoTbwLRckQmn/U8ZDLuWwIXNpuZDhQ3AiRUlhTbOXFVE9C+dR51wM0CBDhk31VcA==
dependencies:
to-no-case "^1.0.0"
toidentifier@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
@@ -8826,6 +9346,11 @@ watchpack@2.4.0, watchpack@^2.4.0:
glob-to-regexp "^0.4.1"
graceful-fs "^4.1.2"
web-streams-polyfill@^3.0.3:
version "3.2.1"
resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6"
integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
@@ -9020,6 +9545,11 @@ wildcard@^2.0.0:
resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67"
integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==
wordwrap@>=0.0.2:
version "1.0.0"
resolved "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==
wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
@@ -9039,6 +9569,11 @@ ws@^7.3.1:
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591"
integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==
ws@^8.13.0:
version "8.14.2"
resolved "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f"
integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==
xss@^1.0.6:
version "1.0.14"
resolved "https://registry.yarnpkg.com/xss/-/xss-1.0.14.tgz#4f3efbde75ad0d82e9921cc3c95e6590dd336694"
@@ -9047,7 +9582,7 @@ xss@^1.0.6:
commander "^2.20.3"
cssfilter "0.0.10"
xtend@~4.0.1:
xtend@^4.0.0, xtend@~4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
@@ -9099,3 +9634,8 @@ zod@3.21.4:
version "3.21.4"
resolved "https://registry.yarnpkg.com/zod/-/zod-3.21.4.tgz#10882231d992519f0a10b5dd58a38c9dabbb64db"
integrity sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==
zod@^3.20.2:
version "3.22.4"
resolved "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz#f31c3a9386f61b1f228af56faa9255e845cf3fff"
integrity sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==

View File

@@ -17,12 +17,14 @@
"generate:types": "cross-env PAYLOAD_CONFIG_PATH=src/payload/payload.config.ts payload generate:types",
"generate:graphQLSchema": "PAYLOAD_CONFIG_PATH=src/payload/payload.config.ts payload generate:graphQLSchema",
"lint": "eslint src",
"lint:fix": "eslint --fix --ext .ts,.tsx src"
"lint:fix": "eslint --fix --ext .ts,.tsx src",
"payload": "cross-env PAYLOAD_CONFIG_PATH=src/payload/payload.config.ts payload"
},
"dependencies": {
"@payloadcms/bundler-webpack": "^1.0.0",
"@payloadcms/db-mongodb": "^1.0.0",
"@payloadcms/plugin-cloud": "^2.0.0",
"@payloadcms/db-postgres": "^0.1.9",
"@payloadcms/plugin-cloud": "^2.0.0",
"@payloadcms/plugin-form-builder": "^1.0.13",
"@payloadcms/plugin-nested-docs": "^1.0.8",
"@payloadcms/plugin-redirects": "^1.0.0",

View File

@@ -1,5 +1,6 @@
import { webpackBundler } from '@payloadcms/bundler-webpack' // bundler-import
import { mongooseAdapter } from '@payloadcms/db-mongodb' // database-adapter-import
// import { mongooseAdapter } from '@payloadcms/db-mongodb' // database-adapter-import
import { postgresAdapter } from '@payloadcms/db-postgres'
import { payloadCloud } from '@payloadcms/plugin-cloud'
import nestedDocs from '@payloadcms/plugin-nested-docs'
import redirects from '@payloadcms/plugin-redirects'
@@ -61,8 +62,13 @@ export default buildConfig({
},
editor: slateEditor({}), // editor-config
// database-adapter-config-start
db: mongooseAdapter({
url: process.env.DATABASE_URI,
// db: mongooseAdapter({
// url: process.env.DATABASE_URI,
// }),
db: postgresAdapter({
pool: {
connectionString: process.env.DATABASE_URI,
},
}),
// database-adapter-config-end
serverURL: process.env.PAYLOAD_PUBLIC_SERVER_URL,

View File

@@ -1677,10 +1677,10 @@
prompts "2.4.2"
uuid "9.0.0"
"@payloadcms/db-postgres@^0.1.8":
version "0.1.8"
resolved "https://registry.npmjs.org/@payloadcms/db-postgres/-/db-postgres-0.1.8.tgz#8603056a8e5de115af59aaa2715abfcf9a6d7ece"
integrity sha512-3fXUVZeH1GT5oU/92MglREiQ5gU3IhF9lgLQR9W8GcGF9vlTY7k5I1qUzGALmIRu6FTCOXwDWTDMZxmmmt198w==
"@payloadcms/db-postgres@^0.1.9":
version "0.1.9"
resolved "https://registry.npmjs.org/@payloadcms/db-postgres/-/db-postgres-0.1.9.tgz#17f3bbd6cd39ba3c9b0cf939953d1e11dac2cca8"
integrity sha512-PWtA1otpyCQasRZzA9U17BQNFaPjXRBFQOjx+CU6FiFjLkNkSEO4iK5Pdwsz23UrTlUbwNvgWd1u1+Xm4PHp9A==
dependencies:
"@libsql/client" "^0.3.1"
console-table-printer "2.11.2"

View File

@@ -0,0 +1,11 @@
import type { CollectionConfig } from '../../../packages/payload/src/collections/config/types'
import { noApiViewCollection } from '../shared'
export const CollectionNoApiView: CollectionConfig = {
slug: noApiViewCollection,
admin: {
hideAPIURL: true,
},
fields: [],
}

View File

@@ -11,6 +11,7 @@ import { CollectionGroup1B } from './collections/Group1B'
import { CollectionGroup2A } from './collections/Group2A'
import { CollectionGroup2B } from './collections/Group2B'
import { CollectionHidden } from './collections/Hidden'
import { CollectionNoApiView } from './collections/NoApiView'
import { Posts } from './collections/Posts'
import { Users } from './collections/Users'
import AfterDashboard from './components/AfterDashboard'
@@ -25,7 +26,8 @@ import { Global } from './globals/Global'
import { GlobalGroup1A } from './globals/Group1A'
import { GlobalGroup1B } from './globals/Group1B'
import { GlobalHidden } from './globals/Hidden'
import { postsSlug } from './shared'
import { GlobalNoApiView } from './globals/NoApiView'
import { noApiViewCollection, postsSlug } from './shared'
export interface Post {
createdAt: Date
@@ -77,6 +79,7 @@ export default buildConfigWithDefaults({
Posts,
Users,
CollectionHidden,
CollectionNoApiView,
CustomViews1,
CustomViews2,
CollectionGroup1A,
@@ -87,6 +90,7 @@ export default buildConfigWithDefaults({
],
globals: [
GlobalHidden,
GlobalNoApiView,
Global,
CustomGlobalViews1,
CustomGlobalViews2,
@@ -139,5 +143,10 @@ export default buildConfigWithDefaults({
point: [5, -5],
},
})
await payload.create({
collection: noApiViewCollection,
data: {},
})
},
})

View File

@@ -21,6 +21,8 @@ import {
globalSlug,
group1Collection1Slug,
group1GlobalSlug,
noApiViewCollection,
noApiViewGlobal,
postsSlug,
slugPluralLabel,
} from './shared'
@@ -153,6 +155,32 @@ describe('admin', () => {
await page.goto(url.global('hidden-global'))
await expect(page.locator('.not-found')).toContainText('Nothing found')
})
test('should not show API tab on collection when disabled in config', async () => {
await page.goto(url.collection(noApiViewCollection))
await page.locator('.collection-list .table a').click()
await expect(page.locator('.doc-tabs__tabs-container')).not.toContainText('API')
})
test('should not enable API route on collection when disabled in config', async () => {
const collectionItems = await payload.find({
collection: noApiViewCollection,
limit: 1,
})
expect(collectionItems.docs.length).toBe(1)
await page.goto(`${url.collection(noApiViewCollection)}/${collectionItems.docs[0].id}/api`)
await expect(page.locator('.not-found')).toHaveCount(1)
})
test('should not show API tab on global when disabled in config', async () => {
await page.goto(url.global(noApiViewGlobal))
await expect(page.locator('.doc-tabs__tabs-container')).not.toContainText('API')
})
test('should not enable API route on global when disabled in config', async () => {
await page.goto(`${url.global(noApiViewGlobal)}/api`)
await expect(page.locator('.not-found')).toHaveCount(1)
})
})
describe('ui', () => {

View File

@@ -0,0 +1,10 @@
import type { GlobalConfig } from '../../../packages/payload/src/globals/config/types'
import { noApiViewGlobal } from '../shared'
export const GlobalNoApiView: GlobalConfig = {
slug: noApiViewGlobal,
admin: {
hideAPIURL: true,
},
fields: [],
}

View File

@@ -11,3 +11,7 @@ export const slugPluralLabel = 'Posts'
export const globalSlug = 'global'
export const group1GlobalSlug = 'group-globals-one'
export const noApiViewCollection = 'collection-no-api-view'
export const noApiViewGlobal = 'global-no-api-view'

View File

@@ -12,14 +12,13 @@ export interface Relation {
const openAccess = {
create: () => true,
delete: () => true,
read: () => true,
update: () => true,
delete: () => true,
}
const collectionWithName = (collectionSlug: string): CollectionConfig => {
return {
slug: collectionSlug,
access: openAccess,
fields: [
{
@@ -27,55 +26,36 @@ const collectionWithName = (collectionSlug: string): CollectionConfig => {
type: 'text',
},
],
slug: collectionSlug,
}
}
export const slug = 'posts'
export const relationSlug = 'relation'
export const transactionSlug = 'transactions'
export const pointSlug = 'point'
export default buildConfigWithDefaults({
graphQL: {
schemaOutputFile: path.resolve(__dirname, 'schema.graphql'),
queries: (GraphQL) => {
return {
QueryWithInternalError: {
type: new GraphQL.GraphQLObjectType({
name: 'QueryWithInternalError',
fields: {
text: {
type: GraphQL.GraphQLString,
},
},
}),
resolve: () => {
// Throwing an internal error with potentially sensitive data
throw new Error('Lost connection to the Pentagon. Secret data: ******')
},
},
}
},
},
collections: [
{
slug: 'users',
auth: true,
access: openAccess,
auth: true,
fields: [],
slug: 'users',
},
{
slug: pointSlug,
access: openAccess,
fields: [
{
type: 'point',
name: 'point',
type: 'point',
},
],
slug: pointSlug,
},
{
slug,
access: openAccess,
fields: [
{
@@ -92,173 +72,173 @@ export default buildConfigWithDefaults({
},
{
name: 'min',
type: 'number',
min: 10,
type: 'number',
},
// Relationship
{
name: 'relationField',
type: 'relationship',
relationTo: relationSlug,
type: 'relationship',
},
{
name: 'relationToCustomID',
type: 'relationship',
relationTo: 'custom-ids',
type: 'relationship',
},
// Relation hasMany
{
name: 'relationHasManyField',
type: 'relationship',
relationTo: relationSlug,
hasMany: true,
relationTo: relationSlug,
type: 'relationship',
},
// Relation multiple relationTo
{
name: 'relationMultiRelationTo',
type: 'relationship',
relationTo: [relationSlug, 'dummy'],
type: 'relationship',
},
// Relation multiple relationTo hasMany
{
name: 'relationMultiRelationToHasMany',
type: 'relationship',
relationTo: [relationSlug, 'dummy'],
hasMany: true,
relationTo: [relationSlug, 'dummy'],
type: 'relationship',
},
{
name: 'A1',
type: 'group',
fields: [
{
type: 'text',
name: 'A2',
defaultValue: 'textInRowInGroup',
type: 'text',
},
],
type: 'group',
},
{
name: 'B1',
type: 'group',
fields: [
{
type: 'collapsible',
label: 'Collapsible',
fields: [
{
type: 'text',
name: 'B2',
defaultValue: 'textInRowInGroup',
type: 'text',
},
],
label: 'Collapsible',
type: 'collapsible',
},
],
type: 'group',
},
{
name: 'C1',
type: 'group',
fields: [
{
type: 'text',
name: 'C2Text',
type: 'text',
},
{
type: 'row',
fields: [
{
type: 'collapsible',
label: 'Collapsible2',
fields: [
{
name: 'C2',
type: 'group',
fields: [
{
type: 'row',
fields: [
{
type: 'collapsible',
label: 'Collapsible2',
fields: [
{
type: 'text',
name: 'C3',
type: 'text',
},
],
label: 'Collapsible2',
type: 'collapsible',
},
],
type: 'row',
},
],
type: 'group',
},
],
label: 'Collapsible2',
type: 'collapsible',
},
],
type: 'row',
},
],
type: 'group',
},
{
type: 'tabs',
tabs: [
{
label: 'Tab1',
name: 'D1',
fields: [
{
name: 'D2',
type: 'group',
fields: [
{
type: 'row',
fields: [
{
type: 'collapsible',
label: 'Collapsible2',
fields: [
{
type: 'tabs',
tabs: [
{
label: 'Tab1',
fields: [
{
name: 'D3',
type: 'group',
fields: [
{
type: 'row',
fields: [
{
type: 'collapsible',
label: 'Collapsible2',
fields: [
{
type: 'text',
name: 'D4',
type: 'text',
},
],
label: 'Collapsible2',
type: 'collapsible',
},
],
type: 'row',
},
],
type: 'group',
},
],
label: 'Tab1',
},
],
type: 'tabs',
},
],
label: 'Collapsible2',
type: 'collapsible',
},
],
type: 'row',
},
],
type: 'group',
},
],
label: 'Tab1',
},
],
type: 'tabs',
},
],
slug,
},
{
slug: 'custom-ids',
access: {
read: () => true,
},
@@ -272,45 +252,87 @@ export default buildConfigWithDefaults({
type: 'text',
},
],
slug: 'custom-ids',
},
collectionWithName(relationSlug),
collectionWithName('dummy'),
{
slug: 'payload-api-test-ones',
access: {
read: () => true,
},
fields: [
{
name: 'payloadAPI',
type: 'text',
hooks: {
afterRead: [({ req }) => req.payloadAPI],
},
type: 'text',
},
],
slug: 'payload-api-test-ones',
},
{
slug: 'payload-api-test-twos',
access: {
read: () => true,
},
fields: [
{
name: 'payloadAPI',
type: 'text',
hooks: {
afterRead: [({ req }) => req.payloadAPI],
},
type: 'text',
},
{
name: 'relation',
type: 'relationship',
relationTo: 'payload-api-test-ones',
type: 'relationship',
},
],
slug: 'payload-api-test-twos',
},
{
access: openAccess,
fields: [
{
name: 'transactionID',
hooks: {
beforeChange: [({ req }) => req.transactionID],
},
type: 'text',
},
{
name: 'sessions',
hooks: {
beforeChange: [({ req }) => Object.keys(req.payload.db.sessions)],
},
type: 'json',
},
],
slug: transactionSlug,
},
],
graphQL: {
queries: (GraphQL) => {
return {
QueryWithInternalError: {
resolve: () => {
// Throwing an internal error with potentially sensitive data
throw new Error('Lost connection to the Pentagon. Secret data: ******')
},
type: new GraphQL.GraphQLObjectType({
name: 'QueryWithInternalError',
fields: {
text: {
type: GraphQL.GraphQLString,
},
},
}),
},
}
},
schemaOutputFile: path.resolve(__dirname, 'schema.graphql'),
},
onInit: async (payload) => {
const user = await payload.create({
collection: 'users',
@@ -331,8 +353,8 @@ export default buildConfigWithDefaults({
await payload.create({
collection: slug,
data: {
title: 'has custom ID relation',
relationToCustomID: 1,
title: 'has custom ID relation',
},
})
@@ -353,23 +375,23 @@ export default buildConfigWithDefaults({
await payload.create({
collection: slug,
data: {
title: 'with-description',
description: 'description',
title: 'with-description',
},
})
await payload.create({
collection: slug,
data: {
title: 'numPost1',
number: 1,
title: 'numPost1',
},
})
await payload.create({
collection: slug,
data: {
title: 'numPost2',
number: 2,
title: 'numPost2',
},
})
@@ -390,15 +412,15 @@ export default buildConfigWithDefaults({
await payload.create({
collection: slug,
data: {
title: 'rel to hasMany',
relationHasManyField: rel1.id,
title: 'rel to hasMany',
},
})
await payload.create({
collection: slug,
data: {
title: 'rel to hasMany 2',
relationHasManyField: rel2.id,
title: 'rel to hasMany 2',
},
})
@@ -406,11 +428,11 @@ export default buildConfigWithDefaults({
await payload.create({
collection: slug,
data: {
title: 'rel to multi',
relationMultiRelationTo: {
relationTo: relationSlug,
value: rel2.id,
},
title: 'rel to multi',
},
})
@@ -418,7 +440,6 @@ export default buildConfigWithDefaults({
await payload.create({
collection: slug,
data: {
title: 'rel to multi hasMany',
relationMultiRelationToHasMany: [
{
relationTo: relationSlug,
@@ -429,6 +450,7 @@ export default buildConfigWithDefaults({
value: rel2.id,
},
],
title: 'rel to multi hasMany',
},
})

View File

@@ -4,6 +4,7 @@ import type { Post } from './payload-types'
import payload from '../../packages/payload/src'
import { mapAsync } from '../../packages/payload/src/utilities/mapAsync'
import { devUser } from '../credentials'
import { initPayloadTest } from '../helpers/configHelpers'
import configPromise, { pointSlug, slug } from './config'
@@ -685,11 +686,11 @@ describe('collections-graphql', () => {
// language=graphQL
const query = `query {
Posts(where: { title: { exists: true }}) {
docs {
badFieldName
Posts(where: { title: { exists: true }}) {
docs {
badFieldName
}
}
}
}`
await client.request(query).catch((err) => {
error = err
@@ -702,12 +703,12 @@ describe('collections-graphql', () => {
let error
// language=graphQL
const query = `mutation {
createPost(data: {min: 1}) {
id
min
createdAt
updatedAt
}
createPost(data: {min: 1}) {
id
min
createdAt
updatedAt
}
}`
await client.request(query).catch((err) => {
@@ -722,21 +723,21 @@ describe('collections-graphql', () => {
let error
// language=graphQL
const query = `mutation createTest {
test1:createUser(data: { email: "test@test.com", password: "test" }) {
email
}
test1:createUser(data: { email: "test@test.com", password: "test" }) {
email
}
test2:createUser(data: { email: "test2@test.com", password: "" }) {
email
}
test2:createUser(data: { email: "test2@test.com", password: "" }) {
email
}
test3:createUser(data: { email: "test@test.com", password: "test" }) {
email
}
test3:createUser(data: { email: "test@test.com", password: "test" }) {
email
}
test4:createUser(data: { email: "", password: "test" }) {
email
}
test4:createUser(data: { email: "", password: "test" }) {
email
}
}`
await client.request(query).catch((err) => {
@@ -775,9 +776,9 @@ describe('collections-graphql', () => {
let error
// language=graphQL
const query = `query {
QueryWithInternalError {
text
}
QueryWithInternalError {
text
}
}`
await client.request(query).catch((err) => {
@@ -792,6 +793,46 @@ describe('collections-graphql', () => {
expect(error.response.errors[0].extensions.name).toEqual('Error')
})
})
if (['postgres'].includes(process.env.PAYLOAD_DATABASE)) {
describe('Transactions', () => {
let token
let user
beforeAll(async () => {
// language=graphQL
const query = `mutation {
loginUser(email: "${devUser.email}", password: "${devUser.password}") {
token
user {
id
email
}
}
}`
const response = await client.request(query)
user = response.loginUser.user
token = response.loginUser.token
client.setHeaders({ Authorization: `JWT ${token}` })
})
it('should use transaction', async () => {
const query = `mutation {
createTransaction(data: {}) {
id
transactionID
sessions
}
}`
const response = await client.request(query)
const doc = response.createTransaction
expect(doc.transactionID).toBeDefined()
expect(doc.sessions).toBeDefined()
expect(doc.sessions).toContain(doc.transactionID)
})
})
}
})
async function createPost(overrides?: Partial<Post>) {

View File

@@ -80,6 +80,12 @@ export default buildConfigWithDefaults({
},
],
},
{
slug: 'without-graphql',
access,
graphQL: false,
fields: [],
},
],
onInit: async (payload) => {
await payload.create({

View File

@@ -224,5 +224,13 @@ describe('globals', () => {
expect(doc).toMatchObject(data)
})
it('should not show globals with disabled graphql', async () => {
const query = `query {
WithoutGraphql { __typename }
}`
await expect(client.request(query)).rejects.toHaveProperty('message')
})
})
})

View File

@@ -5,6 +5,7 @@ export const Media: CollectionConfig = {
upload: true,
access: {
read: () => true,
create: () => true,
},
fields: [
{

View File

@@ -401,6 +401,24 @@ export default buildConfigWithDefaults({
Uploads1,
Uploads2,
AdminThumbnailCol,
{
slug: 'optional-file',
upload: {
staticURL: '/optional',
staticDir: './optional',
filesRequiredOnCreate: false,
},
fields: [],
},
{
slug: 'required-file',
upload: {
staticURL: '/required',
staticDir: './required',
filesRequiredOnCreate: true,
},
fields: [],
},
],
onInit: async (payload) => {
const uploadsDir = path.resolve(__dirname, './media')

View File

@@ -540,6 +540,48 @@ describe('Collections - Uploads', () => {
expect(await fileExists(path.join(__dirname, doc.filename))).toBe(false)
})
describe('filesRequiredOnCreate', () => {
// eslint-disable-next-line @typescript-eslint/require-await
it('should allow file to be optional if filesRequiredOnCreate is false', async () => {
expect(
async () =>
await payload.create({
// @ts-ignore
collection: 'optional-file',
data: {},
}),
).not.toThrow()
})
it('should throw an error if no file and filesRequiredOnCreate is true', async () => {
await expect(async () =>
payload.create({
// @ts-ignore
collection: 'required-file',
data: {},
}),
).rejects.toThrow(
expect.objectContaining({
name: 'MissingFile',
message: 'No files were uploaded.',
}),
)
})
it('should throw an error if no file and filesRequiredOnCreate is not defined', async () => {
await expect(async () =>
payload.create({
collection: mediaSlug,
data: {},
}),
).rejects.toThrow(
expect.objectContaining({
name: 'MissingFile',
message: 'No files were uploaded.',
}),
)
})
})
})
async function fileExists(fileName: string): Promise<boolean> {

View File

@@ -11,13 +11,23 @@ export interface Config {
relation: Relation
audio: Audio
'gif-resize': GifResize
'no-image-sizes': NoImageSize
'crop-only': CropOnly
'focal-only': FocalOnly
media: Media
enlarge: Enlarge
reduce: Reduce
'media-trim': MediaTrim
'unstored-media': UnstoredMedia
'externally-served-media': ExternallyServedMedia
'uploads-1': Uploads1
'uploads-2': Uploads2
'admin-thumbnail': AdminThumbnail
'optional-file': OptionalFile
'required-file': RequiredFile
users: User
'payload-preferences': PayloadPreference
'payload-migrations': PayloadMigration
}
globals: {}
}
@@ -70,6 +80,14 @@ export interface Media {
filesize?: number
filename?: string
}
accidentalSameSize?: {
url?: string
width?: number
height?: number
mimeType?: string
filesize?: number
filename?: string
}
tablet?: {
url?: string
width?: number
@@ -94,6 +112,62 @@ export interface Media {
filesize?: number
filename?: string
}
focalTest?: {
url?: string
width?: number
height?: number
mimeType?: string
filesize?: number
filename?: string
}
focalTest2?: {
url?: string
width?: number
height?: number
mimeType?: string
filesize?: number
filename?: string
}
focalTest3?: {
url?: string
width?: number
height?: number
mimeType?: string
filesize?: number
filename?: string
}
focalTest4?: {
url?: string
width?: number
height?: number
mimeType?: string
filesize?: number
filename?: string
}
focalTest5?: {
url?: string
width?: number
height?: number
mimeType?: string
filesize?: number
filename?: string
}
focalTest6?: {
url?: string
width?: number
height?: number
mimeType?: string
filesize?: number
filename?: string
}
focalTest7?: {
url?: string
width?: number
height?: number
mimeType?: string
filesize?: number
filename?: string
}
}
}
export interface Audio {
@@ -131,6 +205,189 @@ export interface GifResize {
}
}
}
export interface NoImageSize {
id: string
updatedAt: string
createdAt: string
url?: string
filename?: string
mimeType?: string
filesize?: number
width?: number
height?: number
}
export interface CropOnly {
id: string
updatedAt: string
createdAt: string
url?: string
filename?: string
mimeType?: string
filesize?: number
width?: number
height?: number
sizes?: {
focalTest?: {
url?: string
width?: number
height?: number
mimeType?: string
filesize?: number
filename?: string
}
focalTest2?: {
url?: string
width?: number
height?: number
mimeType?: string
filesize?: number
filename?: string
}
focalTest3?: {
url?: string
width?: number
height?: number
mimeType?: string
filesize?: number
filename?: string
}
}
}
export interface FocalOnly {
id: string
updatedAt: string
createdAt: string
url?: string
filename?: string
mimeType?: string
filesize?: number
width?: number
height?: number
sizes?: {
focalTest?: {
url?: string
width?: number
height?: number
mimeType?: string
filesize?: number
filename?: string
}
focalTest2?: {
url?: string
width?: number
height?: number
mimeType?: string
filesize?: number
filename?: string
}
focalTest3?: {
url?: string
width?: number
height?: number
mimeType?: string
filesize?: number
filename?: string
}
}
}
export interface Enlarge {
id: string
updatedAt: string
createdAt: string
url?: string
filename?: string
mimeType?: string
filesize?: number
width?: number
height?: number
sizes?: {
accidentalSameSize?: {
url?: string
width?: number
height?: number
mimeType?: string
filesize?: number
filename?: string
}
sameSizeWithNewFormat?: {
url?: string
width?: number
height?: number
mimeType?: string
filesize?: number
filename?: string
}
resizedLarger?: {
url?: string
width?: number
height?: number
mimeType?: string
filesize?: number
filename?: string
}
resizedSmaller?: {
url?: string
width?: number
height?: number
mimeType?: string
filesize?: number
filename?: string
}
widthLowerHeightLarger?: {
url?: string
width?: number
height?: number
mimeType?: string
filesize?: number
filename?: string
}
}
}
export interface Reduce {
id: string
updatedAt: string
createdAt: string
url?: string
filename?: string
mimeType?: string
filesize?: number
width?: number
height?: number
sizes?: {
accidentalSameSize?: {
url?: string
width?: number
height?: number
mimeType?: string
filesize?: number
filename?: string
}
sameSizeWithNewFormat?: {
url?: string
width?: number
height?: number
mimeType?: string
filesize?: number
filename?: string
}
resizedLarger?: {
url?: string
width?: number
height?: number
mimeType?: string
filesize?: number
filename?: string
}
resizedSmaller?: {
url?: string
width?: number
height?: number
mimeType?: string
filesize?: number
filename?: string
}
}
}
export interface MediaTrim {
id: string
updatedAt: string
@@ -217,6 +474,39 @@ export interface Uploads2 {
width?: number
height?: number
}
export interface AdminThumbnail {
id: string
updatedAt: string
createdAt: string
url?: string
filename?: string
mimeType?: string
filesize?: number
width?: number
height?: number
}
export interface OptionalFile {
id: string
updatedAt: string
createdAt: string
url?: string
filename?: string
mimeType?: string
filesize?: number
width?: number
height?: number
}
export interface RequiredFile {
id: string
updatedAt: string
createdAt: string
url?: string
filename?: string
mimeType?: string
filesize?: number
width?: number
height?: number
}
export interface User {
id: string
updatedAt: string
@@ -228,5 +518,58 @@ export interface User {
hash?: string
loginAttempts?: number
lockUntil?: string
password?: string
password: string
}
export interface PayloadPreference {
id: string
user: {
relationTo: 'users'
value: string | User
}
key?: string
value?:
| {
[k: string]: unknown
}
| unknown[]
| string
| number
| boolean
| null
updatedAt: string
createdAt: string
}
export interface PayloadMigration {
id: string
name?: string
batch?: number
updatedAt: string
createdAt: string
}
declare module 'payload' {
export interface GeneratedTypes {
collections: {
relation: Relation
audio: Audio
'gif-resize': GifResize
'no-image-sizes': NoImageSize
'crop-only': CropOnly
'focal-only': FocalOnly
media: Media
enlarge: Enlarge
reduce: Reduce
'media-trim': MediaTrim
'unstored-media': UnstoredMedia
'externally-served-media': ExternallyServedMedia
'uploads-1': Uploads1
'uploads-2': Uploads2
'admin-thumbnail': AdminThumbnail
'optional-file': OptionalFile
'required-file': RequiredFile
users: User
'payload-preferences': PayloadPreference
'payload-migrations': PayloadMigration
}
}
}

View File

@@ -4,21 +4,6 @@ import { CustomPublishButton } from '../elements/CustomSaveButton'
import { draftSlug } from '../shared'
const DraftPosts: CollectionConfig = {
slug: draftSlug,
admin: {
useAsTitle: 'title',
defaultColumns: ['title', 'description', 'createdAt', '_status'],
preview: () => 'https://payloadcms.com',
components: {
edit: {
PublishButton: CustomPublishButton,
},
},
},
versions: {
maxPerDoc: 35,
drafts: true,
},
access: {
read: ({ req: { user } }) => {
if (user) {
@@ -42,74 +27,82 @@ const DraftPosts: CollectionConfig = {
},
readVersions: ({ req: { user } }) => Boolean(user),
},
admin: {
components: {
edit: {
PublishButton: CustomPublishButton,
},
},
defaultColumns: ['title', 'description', 'createdAt', '_status'],
preview: () => 'https://payloadcms.com',
useAsTitle: 'title',
},
fields: [
{
name: 'title',
label: 'Title',
type: 'text',
required: true,
unique: true,
localized: true,
required: true,
type: 'text',
unique: true,
},
{
name: 'description',
label: 'Description',
type: 'textarea',
required: true,
type: 'textarea',
},
{
name: 'radio',
type: 'radio',
options: [
{
value: 'test',
label: { en: 'Test en', es: 'Test es' },
value: 'test',
},
],
type: 'radio',
},
{
name: 'select',
type: 'select',
hasMany: true,
options: [
{
value: 'test1',
label: { en: 'Test1 en', es: 'Test1 es' },
value: 'test1',
},
{
value: 'test2',
label: { en: 'Test2 en', es: 'Test2 es' },
value: 'test2',
},
],
type: 'select',
},
{
name: 'layout',
type: 'blocks',
name: 'blocksField',
blocks: [
{
slug: 'cta',
fields: [
{
name: 'linkGroup',
type: 'group',
fields: [
{
name: 'links',
type: 'array',
fields: [
{
name: 'url',
type: 'text',
},
],
},
],
name: 'text',
type: 'text',
},
{
name: 'localized',
localized: true,
type: 'text',
},
],
slug: 'block',
},
],
type: 'blocks',
},
],
slug: draftSlug,
versions: {
drafts: true,
maxPerDoc: 35,
},
}
export default DraftPosts

View File

@@ -25,10 +25,19 @@ export default buildConfigWithDefaults({
},
})
const blocksField = [
{
blockType: 'block',
localized: 'text',
text: 'text',
},
]
const { id: draftID } = await payload.create({
collection: draftSlug,
data: {
id: 1,
blocksField,
description: 'draft description',
radio: 'test',
title: 'draft title',
@@ -41,6 +50,7 @@ export default buildConfigWithDefaults({
data: {
id: 2,
_status: 'published',
blocksField,
description: 'published description',
radio: 'test',
title: 'published title',
@@ -51,6 +61,7 @@ export default buildConfigWithDefaults({
await payload.create({
collection: draftSlug,
data: {
blocksField,
description: 'published description',
title: titleToDelete,
},

View File

@@ -241,9 +241,9 @@ describe('Versions', () => {
})
describe('Restore', () => {
it('should versions be in correct order', async () => {
it('should return findVersions in correct order', async () => {
const somePost = await payload.create({
collection,
collection: draftSlug,
data: {
description: 'description 1',
title: 'first post',
@@ -251,59 +251,116 @@ describe('Versions', () => {
})
const updatedPost = await payload.update({
id: collectionLocalPostID,
collection,
id: somePost.id,
collection: draftSlug,
data: {
title: 'This should be the latest version',
},
})
const versions = await payload.findVersions({
collection,
collection: draftSlug,
where: {
parent: { equals: somePost.id },
},
})
expect(versions.docs[0].version.title).toBe(updatedPost.title)
})
it('should allow a version to be restored', async () => {
const title2 = 'Another updated post title in EN'
const updated = 'updated'
const updatedPost = await payload.update({
id: collectionLocalPostID,
collection,
const versionedPost = await payload.create({
collection: draftSlug,
data: {
description: 'version description',
title: 'version title',
},
draft: true,
})
// @ts-ignore
let updatedPost = await payload.update({
id: versionedPost.id,
collection: draftSlug,
data: {
blocksField: [
{
blockType: 'block',
localized: 'text',
text: 'text',
},
],
title: title2,
},
draft: true,
})
// @ts-ignore
updatedPost = await payload.update({
id: versionedPost.id,
collection: draftSlug,
data: {
blocksField: [
{
id: updatedPost.blocksField[0].id,
blockName: 'breakpoint',
blockType: 'block',
localized: updated,
text: updated,
},
],
title: title2,
},
draft: true,
})
expect(updatedPost.title).toBe(title2)
expect(updatedPost.blocksField[0].text).toBe(updated)
expect(updatedPost.blocksField[0].localized).toBe(updated)
// Make sure it was updated correctly
const draftFromUpdatedPost = await payload.findByID({
id: collectionLocalPostID,
collection,
id: versionedPost.id,
collection: draftSlug,
draft: true,
})
expect(draftFromUpdatedPost.title).toBe(title2)
expect(draftFromUpdatedPost.blocksField).toHaveLength(1)
expect(draftFromUpdatedPost.blocksField[0].localized).toStrictEqual(updated)
const versions = await payload.findVersions({
collection,
collection: draftSlug,
where: {
parent: {
equals: versionedPost.id,
},
},
})
// restore to latest version
const versionToRestore = versions.docs[versions.docs.length - 1]
// restore to previous version
const restoredVersion = await payload.restoreVersion({
id: versions.docs[1].id,
collection,
id: versionToRestore.id,
collection: draftSlug,
})
expect(restoredVersion.title).toBeDefined()
expect({ ...restoredVersion }).toMatchObject({
...versionToRestore.version,
updatedAt: restoredVersion.updatedAt,
})
const latestDraft = await payload.findByID({
id: collectionLocalPostID,
collection,
id: versionedPost.id,
collection: draftSlug,
draft: true,
})
expect(latestDraft.title).toBe(versions.docs[1].version.title)
expect(latestDraft).toMatchObject({
...versionToRestore.version,
updatedAt: latestDraft.updatedAt,
})
expect(latestDraft.blocksField).toHaveLength(0)
})
})