Compare commits
1 Commits
v3.0.0-bet
...
v3.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5427ad3b8 |
@@ -1,804 +0,0 @@
|
||||
# 🚧 **DRAFT:** 3.0 Migration Guide / Breaking Changes
|
||||
|
||||
> [!IMPORTANT]
|
||||
> This document will continue to be updated and cleaned up until the 3.0 release.
|
||||
|
||||
## What has changed?
|
||||
|
||||
The core logic and principles of Payload remain the same for 3.0. The brunt of the changes are at the HTTP layer and the Admin Panel. These aspects were moved to be based upon Next.js.
|
||||
|
||||
## To migrate from Payload 2.0 to 3.0:
|
||||
|
||||
1. Delete the `admin.bundler` property from your Payload config:
|
||||
|
||||
Payload no longer bundles the admin panel. Instead, we rely directly on Next.js for bundling. This also means that the `@payloadcms/bundler-webpack` and `@payloadcms/bundler-vite` packages have been deprecated. You can completely uninstall those from your project by removing them from your `package.json` file and re-running your package manager’s installation process, i.e. `pnpm i`.
|
||||
|
||||
2. Add the `secret` property to your Payload config. This used to be set in the `payload.init()` function of your `server.ts` file. Move it to `payload.config.ts` instead:
|
||||
|
||||
```tsx
|
||||
// payload.config.ts
|
||||
|
||||
buildConfig({
|
||||
// ...
|
||||
secret: process.env.PAYLOAD_SECRET
|
||||
})
|
||||
```
|
||||
|
||||
3. The `admin.css` and `admin.scss` properties in the Payload config have been removed:
|
||||
|
||||
Instead for any global styles you can:
|
||||
|
||||
- use `(payload)/custom.scss` to import or add your own styles, eg. for tailwind
|
||||
- plugins that need to inject global styles should do so via the provider config at `admin.components.providers` :
|
||||
|
||||
```tsx
|
||||
// payload.config.js
|
||||
|
||||
//...
|
||||
admin: {
|
||||
components: {
|
||||
providers: [
|
||||
MyProvider
|
||||
]
|
||||
}
|
||||
},
|
||||
//...
|
||||
|
||||
// MyProvider.tsx
|
||||
|
||||
import React from 'react'
|
||||
import './globals.css'
|
||||
|
||||
const MyProvider: React.FC<{children?: any}= ({ children }) ={
|
||||
return (
|
||||
<React.fragment>
|
||||
{children}
|
||||
</React.fragment>
|
||||
)
|
||||
}
|
||||
|
||||
export default Provider
|
||||
```
|
||||
|
||||
4. The `admin.indexHTML` property has been removed.
|
||||
5. The `collection.admin.hooks` property has been removed.
|
||||
|
||||
Instead, use the new `beforeDuplicate` field-level hook which take the usual field hook arguments.
|
||||
|
||||
|
||||
```tsx
|
||||
// TODO: add snippet here of old vs new
|
||||
```
|
||||
|
||||
6. Import all Payload React components via the `@payloadcms/ui` package instead of `payload`:
|
||||
|
||||
If you were previously importing components into your app from the `payload` package, for example to create a custom component, you will need to:
|
||||
|
||||
- Change your import paths (see below)
|
||||
- Migrate your component (if necessary, see next bullet)
|
||||
|
||||
```tsx
|
||||
import { Button } from '@payloadcms/ui/elements/Button'
|
||||
|
||||
// TODO: Add new full list of exports
|
||||
```
|
||||
|
||||
7. Migrate all Custom Components to Server Components.
|
||||
|
||||
All Custom Components are now server-rendered, and therefore, cannot use state or hooks directly. If you’re using Custom Components in your app that requires state or hooks, define your component in a separate file with the `'use client'` directive at the top, then render *that* client component within your server component. Remember you can only pass serializable props to this component, so props cannot be blindly spread.
|
||||
|
||||
```tsx
|
||||
import React from 'react'
|
||||
import type { ServerOnlyProps } from './types.ts'
|
||||
import MyClientComponent from './client.tsx'
|
||||
|
||||
export const MyServerComponent: React.FC<ServerOnlyProps= (serverOnlyProps) ={
|
||||
const clientOnlyProps = {
|
||||
// ... sanitize server-only props here as needed
|
||||
}
|
||||
|
||||
return (
|
||||
<MyClientComponent {...clientOnlyProps} />
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
8. The `custom` property in the Payload config, i.e. collections, globals, blocks, and fields are now ***server only***.
|
||||
|
||||
Use `admin.custom` properties will be available in both server and client bundles.
|
||||
|
||||
|
||||
```tsx
|
||||
// payload.config.ts
|
||||
|
||||
buildConfig({
|
||||
// Server Only
|
||||
custom: {
|
||||
someProperty: 'value'
|
||||
},
|
||||
admin: {
|
||||
custom: {
|
||||
name: 'Customer portal' // Available in server and client
|
||||
}
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
9. The `admin.description` property on field configs no longer attaches `data` to its args:
|
||||
|
||||
This is because we cannot pass your `description` function to the client for execution (functions are not serializable, and state is held on the client). To display dynamic descriptions that use current `data` or `path`, you must now pass a custom component and subscribe to the field’s state yourself using Payload’s React hooks:
|
||||
|
||||
|
||||
```tsx
|
||||
// TODO: add config snippet for total clarity
|
||||
|
||||
import React from 'react'
|
||||
// TODO: get rest of imports
|
||||
|
||||
export const CustomFieldDescriptionComponent: DescriptionComponent = () ={
|
||||
const { path } = useFieldProps()
|
||||
const { value } = useFormFields(([fields]) =fields[path])
|
||||
|
||||
return (
|
||||
<div>
|
||||
{`Component description: ${path} - ${value}`}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
10. The `admin.label` property on the `collapsible` field no longer attaches `data` to its args.
|
||||
|
||||
This is because we cannot pass your `label` function to the client for execution (functions are not serializable, and state is held on the client). To display dynamic labels that use current `data` or `path`, you must now pass a custom component and subscribe to the field’s state yourself using Payload’s React hooks:
|
||||
|
||||
|
||||
```tsx
|
||||
// TODO: add config snippet for total clarity
|
||||
import React from 'react'
|
||||
// TODO: get rest of imports
|
||||
|
||||
export const CustomFieldLabelComponent: LabelComponent = () => {
|
||||
const { path } = useFieldProps()
|
||||
const { value } = useFormFields(([fields]) =fields[path])
|
||||
|
||||
return (
|
||||
<div>
|
||||
{`Component label: ${path} - ${value}`}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
11. The `admin.components.Cell` no longer receives `rowData` or `cellData`.
|
||||
|
||||
If using a custom component, you must now get the data yourself via the `useTableCell` hook, i.e. `const { cellData, rowData } = useTableCell()`.
|
||||
|
||||
|
||||
```tsx
|
||||
// TODO: add config snippet for total clarity
|
||||
|
||||
import React from 'react'
|
||||
// TODO: get rest of imports
|
||||
|
||||
export const CustomCellComponent: CellComponent = () ={
|
||||
const { cellData, rowData } = useTableCell()
|
||||
|
||||
return (
|
||||
<div>
|
||||
{`Component cell: ${cellData}`}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
12. `admin.components.RowLabel` no longer accepts a function, instead use a custom component and make use of the `useRowLabel` hook:
|
||||
|
||||
```tsx
|
||||
// ❌ Before
|
||||
|
||||
// Field config
|
||||
{
|
||||
type: 'array',
|
||||
admin: {
|
||||
components: {
|
||||
RowLabel: ({ data }) ={
|
||||
console.log(data)
|
||||
return data?.title || 'Untitled'
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```tsx
|
||||
// ✅ After
|
||||
|
||||
// Field config
|
||||
{
|
||||
type: 'array',
|
||||
admin: {
|
||||
components: {
|
||||
RowLabel: ArrayRowLabel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Custom Component
|
||||
'use client'
|
||||
|
||||
import type { RowLabelComponent } from 'payload/types'
|
||||
|
||||
import { useRowLabel } from '@payloadcms/ui/forms/RowLabel/Context'
|
||||
import React from 'react'
|
||||
|
||||
export const ArrayRowLabel: RowLabelComponent = () ={
|
||||
const { data } = useRowLabel<{ title: string }>()
|
||||
return (
|
||||
<div>{data.title || 'Untitled'}</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
13. The `admin.components.views[].Tab.pillLabel` has been replaced with `admin.components.views[].Tab.Pill`
|
||||
|
||||
```tsx
|
||||
// Collection.ts
|
||||
|
||||
// ❌ Before
|
||||
|
||||
{
|
||||
admin: {
|
||||
components: {
|
||||
views: {
|
||||
Edit: {
|
||||
Tab: {
|
||||
pillLabel: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// ✅ After
|
||||
|
||||
{
|
||||
admin: {
|
||||
components: {
|
||||
views: {
|
||||
Edit: {
|
||||
Tab: {
|
||||
Pill: MyPill,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
14. The `useTitle` hook has been absorbed by the `useDocumentInfo` hook.
|
||||
|
||||
Now, you can get title directly from document info context, i.e. `const { title } = useDocumentInfo()`.
|
||||
|
||||
15. The `Fields` type was renamed to `FormState`:
|
||||
|
||||
This was changed for improved semantics. If you were previously importing this type in your own application, simply change the import name:
|
||||
|
||||
|
||||
```tsx
|
||||
// ❌ Before
|
||||
|
||||
import type { Fields } from 'payload/types'
|
||||
|
||||
// ✅ After
|
||||
|
||||
import type { FormState } from 'payload/types'
|
||||
```
|
||||
|
||||
16. The `useDocumentInfo` hook no longer returns a `SanizitedCollectionConfig` or `SanitizedGlobalConfig`:
|
||||
|
||||
This is because the configs themselves are not serializable and so they cannot be thread through to the client, i.e. the `DocumentInfoContext`. Instead, various properties of the config are passed instead, like `collectionSlug` and `globalSlug`. You can use these to access a client-side config, if needed, through the `useConfig` hook (see next bullet).
|
||||
|
||||
17. The `useConfig` hook now returns a `ClientConfig` and not a `SanizitedConfig`.
|
||||
|
||||
This is because the config itself is not serializable and so it is not able to be thread through to the client, i.e. the `ConfigContext`.
|
||||
|
||||
18. `DocumentTabProps` no longer contains `id` or `isEditing`.
|
||||
|
||||
Instead you can use the `useDocumentInfo` hook to get this information (see above).
|
||||
|
||||
19. The args of the `livePreview.url` function have changed.
|
||||
|
||||
It no longer receives `documentInfo` as an arg, and instead, now has `collectionConfig` and `globalConfig`.
|
||||
|
||||
20. The `href` and `isActive` functions in the view tab config no longer sends the `match` or `location` arguments.
|
||||
|
||||
This is is a property specific to React Router, not Next.js. If you need to do fancy matching similar to this, use a custom tab that fires of some hooks, i.e. `usePathname()` and run it through your own utility functions.
|
||||
|
||||
21. The `views.Edit` or `views.Edit.Default` or `views.Edit.Default.Component` properties are no longer of type `AdminViewComponent` like the other views.
|
||||
|
||||
Instead, their new type is `React.FC<EditViewProps>` where you now only receive the config slug. This is because of how custom edit views need to be rendered server-side, then returned by a client-component (i.e. the document drawer). There’s an example of this adapter pattern in the first sections of this page.
|
||||
|
||||
22. `beforeDuplicate` field hooks have been added to `unique` fields to automatically add “- Copy” to the end.
|
||||
23. The `useCollapsible` hook has had slight changes to its property names:
|
||||
|
||||
`collapsed` is now `isCollapsed` and `withinCollapsible` is now `isWithinCollapsible`.
|
||||
|
||||
24. Components that return a function have webpack errors.
|
||||
|
||||
Will need to document the following (if intended as a breaking change)
|
||||
|
||||
|
||||

|
||||
|
||||
25. The `admin.favicon` property is now `admin.icons` and the types have changed
|
||||
|
||||
Reference: https://github.com/payloadcms/payload/pull/6347
|
||||
|
||||
|
||||
```tsx
|
||||
// payload.config.ts
|
||||
|
||||
// ❌ Before
|
||||
|
||||
{
|
||||
// ...
|
||||
admin: {
|
||||
favicon: 'path-to-favicon.svg'
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ After
|
||||
|
||||
{
|
||||
// ...
|
||||
admin: {
|
||||
icons: [{
|
||||
path: 'path-to-favicon.svg',
|
||||
sizes: '32x32'
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
See also: https://nextjs.org/docs/app/api-reference/functions/generate-metadata#icons
|
||||
|
||||
26. The `admin.meta.ogImage` property has been replaced by `admin.meta.openGraph.images`
|
||||
|
||||
Reference: https://github.com/payloadcms/payload/pull/6227
|
||||
|
||||
```tsx
|
||||
// payload.config.ts
|
||||
|
||||
// ❌ Before
|
||||
{
|
||||
admin: {
|
||||
meta: {
|
||||
ogImage: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```tsx
|
||||
// ✅ After
|
||||
|
||||
{
|
||||
admin: {
|
||||
meta: {
|
||||
openGraph: {
|
||||
images: []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
See also : https://nextjs.org/docs/app/api-reference/functions/generate-metadata#opengraph
|
||||
|
||||
27. The `admin.logoutRoute` and `admin.inactivityRoute` properties have been consolidated into a single `admin.routes` property
|
||||
|
||||
Reference: https://github.com/payloadcms/payload/pull/6354
|
||||
|
||||
To migrate, simply move those two keys as follows:
|
||||
|
||||
```tsx
|
||||
// payload.config.ts
|
||||
|
||||
// ❌ Before
|
||||
|
||||
{
|
||||
// ...
|
||||
admin: {
|
||||
logoutRoute: '/custom-logout',
|
||||
inactivityRoute: '/custom-inactivity'
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ After
|
||||
|
||||
{
|
||||
// ...
|
||||
admin: {
|
||||
routes: {
|
||||
logout: '/custom-logout',
|
||||
inactivity: '/custom-inactivity'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Environment variables
|
||||
|
||||
- Environment variables prefixed with `PAYLOAD_PUBLIC` will no longer be available on the client. In order to access them on the client, those will now have to be prefixed with `NEXT_PUBLIC` instead
|
||||
|
||||
## i18n
|
||||
|
||||
- `useTranslation()` hook no longer takes any options, any translations using shorthand accessors will need to use the entire `group:key`
|
||||
|
||||
```tsx
|
||||
// ❌ Before
|
||||
|
||||
const { i18n, t } = useTranslation('general')
|
||||
return <p>{t('cancel')}</p>
|
||||
|
||||
// ✅ After
|
||||
|
||||
const { i18n, t } = useTranslation()
|
||||
return <p>{t('general:cancel')}</p>
|
||||
```
|
||||
|
||||
- `react-i18n` was removed, the `Trans` component from react-i18n has been replaced with a payload provided solution. You can instead `import { Translation } from "@payloadcms/ui"`
|
||||
|
||||
```tsx
|
||||
// Translation string example
|
||||
// "loggedInChangePassword": "To change your password, go to your <0>account</0> and edit your password there."
|
||||
|
||||
// ❌ Before
|
||||
|
||||
<Trans i18nKey="loggedInChangePassword" t={t}>
|
||||
<Link to={`${admin}/account`}>account</Link>
|
||||
</Trans>
|
||||
|
||||
// ✅ After
|
||||
|
||||
<Translation
|
||||
t={t}
|
||||
i18nKey="authentication:loggedInChangePassword"
|
||||
elements={{
|
||||
'0': ({ children }) => <Link href={`${admin}/account`} children={children} />,
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
## Description and Label handling
|
||||
|
||||
https://github.com/payloadcms/payload/pull/6264
|
||||
|
||||
- Globals config: `admin.description` no longer accepts a custom component. You will have to move it to `admin.components.elements.Description`
|
||||
- Collections config: `admin.description` no longer accepts a custom component. You will have to move it to `admin.components.edit.Description`
|
||||
- All Fields: `field.admin.description` no longer accepts a custom component. You will have to move it to `field.admin.components.Description`
|
||||
- Collapsible Field: `field.label` no longer accepts a custom component. You will have to move it to `field.admin.components.RowLabel`
|
||||
- Array Field: `field.admin.components.RowLabel` no longer accepts strings or records
|
||||
- If you are using our exported field components in your own app, their `labelProps` property has been stripped down and no longer contains the `label` and `required` prop. Those can now only be configured at the top-level.
|
||||
|
||||
## Custom Endpoints
|
||||
|
||||
- `root` endpoints no longer exist on the config. If you want to create a “root” endpoint, you can add them to the api folder within your Payload application. See Next docs: https://nextjs.org/docs/app/api-reference/file-conventions/route
|
||||
- Endpoint handlers
|
||||
- arguments
|
||||
- ❌ Before: `(req, res, next)`
|
||||
- ✅ After: `(req)`
|
||||
- return
|
||||
- ❌ Before: `res.json`, `res.send`, etc.
|
||||
- ✅ After: a valid HTTP [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
|
||||
|
||||
```tsx
|
||||
// ❌ Before
|
||||
|
||||
{
|
||||
path: '/whoami/:parameter',
|
||||
method: 'post',
|
||||
handler: (req, res) => {
|
||||
res.json({
|
||||
parameter: req.params.parameter,
|
||||
name: req.body.name,
|
||||
age: req.body.age,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ After
|
||||
|
||||
{
|
||||
path: '/whoami/:parameter',
|
||||
method: 'post',
|
||||
handler: (req) => {
|
||||
return Response.json({
|
||||
parameter: req.routeParams.parameter,
|
||||
// ^^ `params` is now `routeParams`
|
||||
name: req.data.name,
|
||||
age: req.data.age,
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- Handlers no longer resolve `data` for you on the request, use `req.json()` or you can use our utilities
|
||||
|
||||
```tsx
|
||||
// ❌ Before
|
||||
|
||||
{
|
||||
path: '/whoami/:parameter',
|
||||
method: 'post',
|
||||
handler: async (req) => {
|
||||
return Response.json({
|
||||
name: req.data.name, // data will be undefined
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ After
|
||||
|
||||
import { addDataAndFileToRequest } from '@payloadcms/next/utilities'
|
||||
{
|
||||
path: '/whoami/:parameter',
|
||||
method: 'post',
|
||||
handler: async (req) => {
|
||||
// mutates req, must be awaited
|
||||
await addDataAndFileToRequest(req)
|
||||
|
||||
return Response.json({
|
||||
name: req.data.name, // data is now available
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- Handlers no longer resolve `locale` and `fallbackLocale` for you
|
||||
|
||||
```tsx
|
||||
// ❌ Before
|
||||
|
||||
{
|
||||
path: '/whoami/:parameter',
|
||||
method: 'post',
|
||||
handler: async (req) => {
|
||||
return Response.json({
|
||||
// will be undefined
|
||||
fallbackLocale: req.fallbackLocale,
|
||||
locale: req.locale,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ After
|
||||
|
||||
import { addLocalesToRequest } from '@payloadcms/next/utilities'
|
||||
{
|
||||
path: '/whoami/:parameter',
|
||||
method: 'post',
|
||||
handler: async (req) => {
|
||||
// mutates req
|
||||
addLocalesToRequest(req)
|
||||
|
||||
return Response.json({
|
||||
fallbackLocale: req.fallbackLocale,
|
||||
locale: req.locale,
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Req (Hooks, Access-control, etc)
|
||||
|
||||
- The `req` used to extend the Express Request, now it extends the [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request). So you may need to change things in your code, for example if you are relying on `req.headers['content-type']` you will now need to to use `req.headers.get('content-type')`
|
||||
|
||||
## Uploads
|
||||
|
||||
- `staticDir` must now be an absolute path. Before it would attempt to use the location of the payload config and merge the relative path set for staticDir.
|
||||
- `staticURL` has been removed. If you were using this format URLs when using an external provider, you can leverage the `generateFileURL` functions in order to do the same.
|
||||
|
||||
## Email Adapters
|
||||
|
||||
Email functionality has been abstracted out into email adapters.
|
||||
|
||||
- All existing nodemailer functionality was abstracted into the `@payloadcms/email-nodemailer` package
|
||||
- No longer configured with ethereal.email by default.
|
||||
- Ability to pass email into the `init` function has been removed.
|
||||
- Warning will be given on startup if email not configured. Any `sendEmail` call will simply log the To address and subject.
|
||||
- A Resend adapter is now also available via the `@payloadcms/email-resend` package.
|
||||
|
||||
### If you used the default email configuration in 2.0 (nodemailer):
|
||||
|
||||
```tsx
|
||||
// ❌ Before
|
||||
|
||||
// via payload.init
|
||||
payload.init({
|
||||
email: {
|
||||
transport: someNodemailerTransport
|
||||
fromName: 'hello',
|
||||
fromAddress: 'hello@example.com',
|
||||
},
|
||||
})
|
||||
// or via email in payload.config.ts
|
||||
export default buildConfig({
|
||||
email: {
|
||||
transport: someNodemailerTransport
|
||||
fromName: 'hello',
|
||||
fromAddress: 'hello@example.com',
|
||||
},
|
||||
})
|
||||
|
||||
// ✅ After
|
||||
|
||||
// Using new nodemailer adapter package
|
||||
|
||||
import { nodemailerAdapter } from '@payloadcms/email-nodemailer'
|
||||
|
||||
export default buildConfig({
|
||||
email: nodemailerAdapter() // This will be the old ethereal.email functionality
|
||||
})
|
||||
|
||||
// or pass in transport
|
||||
|
||||
export default buildConfig({
|
||||
email: nodemailerAdapter({
|
||||
defaultFromAddress: 'info@payloadcms.com',
|
||||
defaultFromName: 'Payload',
|
||||
transport: await nodemailer.createTransport({
|
||||
host: process.env.SMTP_HOST,
|
||||
port: 587,
|
||||
auth: {
|
||||
user: process.env.SMTP_USER,
|
||||
pass: process.env.SMTP_PASS,
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### Removal of rate-limiting
|
||||
|
||||
- Now only available if using custom server and using express or similar
|
||||
|
||||
# Plugins
|
||||
|
||||
- *All* plugins have been standardized to use *named exports* (as opposed to default exports). Most also have a suffix of `Plugin` to make it clear what is being imported.
|
||||
|
||||
```tsx
|
||||
// ❌ Before
|
||||
|
||||
import seo from '@payloadcms/plugin-seo'
|
||||
import stripePlugin from '@payloadcms/plugin-stripe'
|
||||
|
||||
// ✅ After
|
||||
|
||||
import { seoPlugin } from '@payloadcms/plugin-seo'
|
||||
import { stripePlugin } from '@payloadcms/plugin-stripe'
|
||||
|
||||
// etc.
|
||||
```
|
||||
|
||||
## `@payloadcms/plugin-cloud-storage`
|
||||
|
||||
- The adapters that are exported from `@payloadcms/plugin-cloud-storage` (ie. `@payloadcms/plugin-cloud-storage/s3`) package have been removed.
|
||||
- New *standalone* packages have been created for each of the existing adapters. Please see the documentation for the one that you use.
|
||||
- `@payloadcms/plugin-cloud-storage` is still fully supported but should only to be used if you are providing a custom adapter that does not have a dedicated package.
|
||||
- If you have created a custom adapter, the type must now provide a `name` property.
|
||||
|
||||
| Service | Package |
|
||||
| -------------------- | ---------------------------------------------------------------------------- |
|
||||
| Vercel Blob | https://github.com/payloadcms/payload/tree/beta/packages/storage-vercel-blob |
|
||||
| AWS S3 | https://github.com/payloadcms/payload/tree/beta/packages/storage-s3 |
|
||||
| Azure | https://github.com/payloadcms/payload/tree/beta/packages/storage-azure |
|
||||
| Google Cloud Storage | https://github.com/payloadcms/payload/tree/beta/packages/storage-gcs |
|
||||
|
||||
```tsx
|
||||
// ❌ Before (required peer dependencies depending on adapter)
|
||||
|
||||
import { cloudStorage } from '@payloadcms/plugin-cloud-storage'
|
||||
import { s3Adapter } from '@payloadcms/plugin-cloud-storage/s3'
|
||||
|
||||
plugins: [
|
||||
cloudStorage({
|
||||
collections: {
|
||||
[mediaSlug]: {
|
||||
adapter: s3Adapter({
|
||||
bucket: process.env.S3_BUCKET,
|
||||
config: {
|
||||
credentials: {
|
||||
accessKeyId: process.env.S3_ACCESS_KEY_ID,
|
||||
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
|
||||
},
|
||||
region: process.env.S3_REGION,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
||||
// ✅ After
|
||||
|
||||
import { s3Storage } from '@payloadcms/storage-s3'
|
||||
|
||||
plugins: [
|
||||
s3Storage({
|
||||
collections: {
|
||||
[mediaSlug]: true,
|
||||
},
|
||||
bucket: process.env.S3_BUCKET,
|
||||
config: {
|
||||
credentials: {
|
||||
accessKeyId: process.env.S3_ACCESS_KEY_ID,
|
||||
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
|
||||
},
|
||||
region: process.env.S3_REGION,
|
||||
},
|
||||
}),
|
||||
],
|
||||
```
|
||||
|
||||
## `@payloadcms/plugin-form-builder`
|
||||
|
||||
- Field overrides for form and form submission collections now accept a function with a `defaultFields` inside the args instead of an array of config
|
||||
|
||||
```tsx
|
||||
// ❌ Before
|
||||
|
||||
fields: [
|
||||
{
|
||||
name: 'custom',
|
||||
type: 'text',
|
||||
}
|
||||
]
|
||||
|
||||
// ✅ After
|
||||
fields: ({ defaultFields }) => {
|
||||
return [
|
||||
...defaultFields,
|
||||
{
|
||||
name: 'custom',
|
||||
type: 'text',
|
||||
},
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## `@payloadcms/plugin-redirects`
|
||||
|
||||
- Field overrides for the redirects collection now accepts a function with a `defaultFields` inside the args instead of an array of config
|
||||
|
||||
```tsx
|
||||
// ❌ Before
|
||||
|
||||
fields: [
|
||||
{
|
||||
name: 'custom',
|
||||
type: 'text',
|
||||
}
|
||||
]
|
||||
|
||||
// ✅ After
|
||||
fields: ({ defaultFields }) => {
|
||||
return [
|
||||
...defaultFields,
|
||||
{
|
||||
name: 'custom',
|
||||
type: 'text',
|
||||
},
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## `@payloadcms/richtext-lexical`
|
||||
|
||||
// TODO: Needs comprehensive breaking changes / migration steps
|
||||
|
||||
### Custom Features
|
||||
|
||||
- Previously, a Feature would contain both server code (e.g. population promises) and client code (e.g. toolbar items). Now, they have been split up into server features and client features
|
||||
@@ -618,45 +618,3 @@ export const Orders: CollectionConfig = {
|
||||
**req** will have the **payload** object and can be used inside your endpoint handlers for making
|
||||
calls like req.payload.find() that will make use of access control and hooks.
|
||||
</Banner>
|
||||
|
||||
## Method Override for GET Requests
|
||||
|
||||
Payload supports a method override feature that allows you to send GET requests using the HTTP POST method. This can be particularly useful in scenarios when the query string in a regular GET request is too long.
|
||||
|
||||
### How to Use
|
||||
|
||||
To use this feature, include the `X-HTTP-Method-Override` header set to `GET` in your POST request. The parameters should be sent in the body of the request with the `Content-Type` set to `application/x-www-form-urlencoded`.
|
||||
|
||||
### Example
|
||||
|
||||
Here is an example of how to use the method override to perform a GET request:
|
||||
|
||||
#### Using Method Override (POST)
|
||||
|
||||
```ts
|
||||
const res = await fetch(`${api}/${collectionSlug}`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Accept-Language': i18n.language,
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'X-HTTP-Method-Override': 'GET',
|
||||
},
|
||||
body: qs.stringify({
|
||||
depth: 1,
|
||||
locale: 'en',
|
||||
}),
|
||||
})
|
||||
```
|
||||
|
||||
#### Equivalent Regular GET Request
|
||||
|
||||
```ts
|
||||
const res = await fetch(`${api}/${collectionSlug}?depth=1&locale=en`, {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Accept-Language': i18n.language,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "payload-monorepo",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "create-payload-app",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -5,7 +5,7 @@ import path from 'path'
|
||||
import type { DbType, StorageAdapterType } from '../types.js'
|
||||
|
||||
import { warning } from '../utils/log.js'
|
||||
import { dbReplacements, storageReplacements } from './replacements.js'
|
||||
import { configReplacements, dbReplacements, storageReplacements } from './replacements.js'
|
||||
|
||||
/** Update payload config with necessary imports and adapters */
|
||||
export async function configurePayloadConfig(args: {
|
||||
@@ -15,8 +15,8 @@ export async function configurePayloadConfig(args: {
|
||||
}
|
||||
packageJsonName?: string
|
||||
projectDirOrConfigPath: { payloadConfigPath: string } | { projectDir: string }
|
||||
sharp?: boolean
|
||||
storageAdapter?: StorageAdapterType
|
||||
sharp?: boolean
|
||||
}): Promise<void> {
|
||||
if (!args.dbType) {
|
||||
return
|
||||
@@ -93,32 +93,32 @@ export async function configurePayloadConfig(args: {
|
||||
const dbReplacement = dbReplacements[args.dbType]
|
||||
|
||||
configLines = replaceInConfigLines({
|
||||
endMatch: `// database-adapter-config-end`,
|
||||
lines: configLines,
|
||||
replacement: dbReplacement.configReplacement(args.envNames?.dbUri),
|
||||
startMatch: `// database-adapter-config-start`,
|
||||
endMatch: `// database-adapter-config-end`,
|
||||
lines: configLines,
|
||||
})
|
||||
|
||||
configLines = replaceInConfigLines({
|
||||
lines: configLines,
|
||||
replacement: [dbReplacement.importReplacement],
|
||||
startMatch: '// database-adapter-import',
|
||||
lines: configLines,
|
||||
})
|
||||
|
||||
// Storage Adapter Replacement
|
||||
if (args.storageAdapter) {
|
||||
const replacement = storageReplacements[args.storageAdapter]
|
||||
configLines = replaceInConfigLines({
|
||||
lines: configLines,
|
||||
replacement: replacement.configReplacement,
|
||||
startMatch: '// storage-adapter-placeholder',
|
||||
lines: configLines,
|
||||
})
|
||||
|
||||
if (replacement?.importReplacement) {
|
||||
configLines = replaceInConfigLines({
|
||||
lines: configLines,
|
||||
replacement: [replacement.importReplacement],
|
||||
startMatch: '// storage-adapter-import-placeholder',
|
||||
lines: configLines,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -126,14 +126,14 @@ export async function configurePayloadConfig(args: {
|
||||
// Sharp Replacement (provided by default, only remove if explicitly set to false)
|
||||
if (args.sharp === false) {
|
||||
configLines = replaceInConfigLines({
|
||||
lines: configLines,
|
||||
replacement: [],
|
||||
startMatch: 'sharp,',
|
||||
lines: configLines,
|
||||
})
|
||||
configLines = replaceInConfigLines({
|
||||
lines: configLines,
|
||||
replacement: [],
|
||||
startMatch: "import sharp from 'sharp'",
|
||||
lines: configLines,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -146,16 +146,16 @@ export async function configurePayloadConfig(args: {
|
||||
}
|
||||
|
||||
function replaceInConfigLines({
|
||||
endMatch,
|
||||
lines,
|
||||
replacement,
|
||||
startMatch,
|
||||
endMatch,
|
||||
lines,
|
||||
}: {
|
||||
replacement: string[]
|
||||
startMatch: string
|
||||
/** Optional endMatch to replace multiple lines */
|
||||
endMatch?: string
|
||||
lines: string[]
|
||||
replacement: string[]
|
||||
startMatch: string
|
||||
}) {
|
||||
if (!replacement) {
|
||||
return lines
|
||||
|
||||
@@ -66,9 +66,9 @@ const diskReplacement: StorageAdapterReplacement = {
|
||||
}
|
||||
|
||||
export const storageReplacements: Record<StorageAdapterType, StorageAdapterReplacement> = {
|
||||
localDisk: diskReplacement,
|
||||
payloadCloud: payloadCloudReplacement,
|
||||
vercelBlobStorage: vercelBlobStorageReplacement,
|
||||
localDisk: diskReplacement,
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,8 @@ import chalk from 'chalk'
|
||||
import { Syntax, parseModule } from 'esprima-next'
|
||||
import fs from 'fs'
|
||||
|
||||
import { log , warning } from '../utils/log.js'
|
||||
import { warning } from '../utils/log.js'
|
||||
import { log } from '../utils/log.js'
|
||||
|
||||
export const withPayloadStatement = {
|
||||
cjs: `const { withPayload } = require('@payloadcms/next/withPayload')\n`,
|
||||
|
||||
@@ -77,4 +77,4 @@ export type NextAppDetails = {
|
||||
|
||||
export type NextConfigType = 'cjs' | 'esm'
|
||||
|
||||
export type StorageAdapterType = 'localDisk' | 'payloadCloud' | 'vercelBlobStorage'
|
||||
export type StorageAdapterType = 'payloadCloud' | 'vercelBlobStorage' | 'localDisk'
|
||||
|
||||
@@ -3,7 +3,8 @@ import chalk from 'chalk'
|
||||
import path from 'path'
|
||||
import terminalLink from 'terminal-link'
|
||||
|
||||
import type { PackageManager , ProjectTemplate } from '../types.js'
|
||||
import type { ProjectTemplate } from '../types.js'
|
||||
import type { PackageManager } from '../types.js'
|
||||
|
||||
import { getValidTemplates } from '../lib/templates.js'
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-mongodb",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"description": "The officially supported MongoDB database adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { CreateGlobalVersion } from 'payload/database'
|
||||
import type { Document , PayloadRequestWithData } from 'payload/types'
|
||||
import type { PayloadRequestWithData } from 'payload/types'
|
||||
import type { Document } from 'payload/types'
|
||||
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { CreateVersion } from 'payload/database'
|
||||
import type { Document , PayloadRequestWithData } from 'payload/types'
|
||||
import type { PayloadRequestWithData } from 'payload/types'
|
||||
import type { Document } from 'payload/types'
|
||||
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { DeleteOne } from 'payload/database'
|
||||
import type { Document , PayloadRequestWithData } from 'payload/types'
|
||||
import type { PayloadRequestWithData } from 'payload/types'
|
||||
import type { Document } from 'payload/types'
|
||||
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { MongooseQueryOptions } from 'mongoose'
|
||||
import type { FindOne } from 'payload/database'
|
||||
import type { Document , PayloadRequestWithData } from 'payload/types'
|
||||
import type { PayloadRequestWithData } from 'payload/types'
|
||||
import type { Document } from 'payload/types'
|
||||
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import type { Payload } from 'payload'
|
||||
import type { PathToQuery } from 'payload/database'
|
||||
import type { Field , Operator } from 'payload/types'
|
||||
import type { Field } from 'payload/types'
|
||||
import type { Operator } from 'payload/types'
|
||||
|
||||
import ObjectIdImport from 'bson-objectid'
|
||||
import mongoose from 'mongoose'
|
||||
import { getLocalizedPaths } from 'payload/database'
|
||||
import { fieldAffectsData , validOperators } from 'payload/types'
|
||||
import { fieldAffectsData } from 'payload/types'
|
||||
import { validOperators } from 'payload/types'
|
||||
|
||||
import type { MongooseAdapter } from '../index.js'
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import type { FilterQuery } from 'mongoose'
|
||||
import type { Payload } from 'payload'
|
||||
import type { Field, Operator , Where } from 'payload/types'
|
||||
import type { Operator, Where } from 'payload/types'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
import deepmerge from 'deepmerge'
|
||||
import { validOperators } from 'payload/types'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-postgres",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"description": "The officially supported Postgres database adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/email-nodemailer",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"description": "Payload Nodemailer Email Adapter",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/email-resend",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"description": "Payload Resend Email Adapter",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -10,7 +10,6 @@ const baseRules = {
|
||||
'no-use-before-define': 'off',
|
||||
'object-shorthand': 'warn',
|
||||
'no-useless-escape': 'warn',
|
||||
'import/no-duplicates': 'warn',
|
||||
'perfectionist/sort-objects': [
|
||||
'error',
|
||||
{
|
||||
@@ -124,7 +123,7 @@ module.exports = {
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: ['import'], // Plugins are defined in the overrides to be more specific and only target the files they are meant for.
|
||||
plugins: [], // Plugins are defined in the overrides to be more specific and only target the files they are meant for.
|
||||
overrides: [
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
"@typescript-eslint/parser": "7.3.1",
|
||||
"eslint": "8.57.0",
|
||||
"eslint-config-prettier": "9.1.0",
|
||||
"eslint-plugin-import": "2.25.2",
|
||||
"eslint-plugin-jest": "27.9.0",
|
||||
"eslint-plugin-jest-dom": "5.1.0",
|
||||
"eslint-plugin-jsx-a11y": "6.8.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/graphql",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Collection, PayloadRequestWithData , Where } from 'payload/types'
|
||||
import type { PayloadRequestWithData, Where } from 'payload/types'
|
||||
import type { Collection } from 'payload/types'
|
||||
|
||||
import { countOperation } from 'payload/operations'
|
||||
import { isolateObjectProperty } from 'payload/utilities'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { GeneratedTypes } from 'payload'
|
||||
import type { Collection , PayloadRequestWithData } from 'payload/types'
|
||||
import type { PayloadRequestWithData } from 'payload/types'
|
||||
import type { Collection } from 'payload/types'
|
||||
import type { MarkOptional } from 'ts-essentials'
|
||||
|
||||
import { createOperation } from 'payload/operations'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { GeneratedTypes } from 'payload'
|
||||
import type { Collection , PayloadRequestWithData } from 'payload/types'
|
||||
import type { PayloadRequestWithData } from 'payload/types'
|
||||
import type { Collection } from 'payload/types'
|
||||
|
||||
import { deleteByIDOperation } from 'payload/operations'
|
||||
import { isolateObjectProperty } from 'payload/utilities'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { GeneratedTypes } from 'payload'
|
||||
import type { Collection , PayloadRequestWithData } from 'payload/types'
|
||||
import type { PayloadRequestWithData } from 'payload/types'
|
||||
import type { Collection } from 'payload/types'
|
||||
|
||||
import { duplicateOperation } from 'payload/operations'
|
||||
import { isolateObjectProperty } from 'payload/utilities'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { PaginatedDocs } from 'payload/database'
|
||||
import type { Collection, PayloadRequestWithData , Where } from 'payload/types'
|
||||
import type { PayloadRequestWithData, Where } from 'payload/types'
|
||||
import type { Collection } from 'payload/types'
|
||||
|
||||
import { findOperation } from 'payload/operations'
|
||||
import { isolateObjectProperty } from 'payload/utilities'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { GeneratedTypes } from 'payload'
|
||||
import type { Collection , PayloadRequestWithData } from 'payload/types'
|
||||
import type { PayloadRequestWithData } from 'payload/types'
|
||||
import type { Collection } from 'payload/types'
|
||||
|
||||
import { findByIDOperation } from 'payload/operations'
|
||||
import { isolateObjectProperty } from 'payload/utilities'
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Collection , PayloadRequestWithData, TypeWithID } from 'payload/types'
|
||||
import type { PayloadRequestWithData } from 'payload/types'
|
||||
import type { Collection, TypeWithID } from 'payload/types'
|
||||
import type { TypeWithVersion } from 'payload/versions'
|
||||
|
||||
import { findVersionByIDOperation } from 'payload/operations'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { PaginatedDocs } from 'payload/database'
|
||||
import type { Collection, PayloadRequestWithData , Where } from 'payload/types'
|
||||
import type { PayloadRequestWithData, Where } from 'payload/types'
|
||||
import type { Collection } from 'payload/types'
|
||||
|
||||
import { findVersionsOperation } from 'payload/operations'
|
||||
import { isolateObjectProperty } from 'payload/utilities'
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Collection , PayloadRequestWithData } from 'payload/types'
|
||||
import type { PayloadRequestWithData } from 'payload/types'
|
||||
import type { Collection } from 'payload/types'
|
||||
|
||||
import { restoreVersionOperation } from 'payload/operations'
|
||||
import { isolateObjectProperty } from 'payload/utilities'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { GeneratedTypes } from 'payload'
|
||||
import type { Collection , PayloadRequestWithData } from 'payload/types'
|
||||
import type { PayloadRequestWithData } from 'payload/types'
|
||||
import type { Collection } from 'payload/types'
|
||||
|
||||
import { updateByIDOperation } from 'payload/operations'
|
||||
import { isolateObjectProperty } from 'payload/utilities'
|
||||
|
||||
@@ -37,7 +37,8 @@ import {
|
||||
GraphQLString,
|
||||
} from 'graphql'
|
||||
import { fieldAffectsData, optionIsObject, tabHasName } from 'payload/types'
|
||||
import { flattenTopLevelFields , toWords } from 'payload/utilities'
|
||||
import { toWords } from 'payload/utilities'
|
||||
import { flattenTopLevelFields } from 'payload/utilities'
|
||||
|
||||
import { GraphQLJSON } from '../packages/graphql-type-json/index.js'
|
||||
import combineParentName from '../utilities/combineParentName.js'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/live-preview-react",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"description": "The official live preview React SDK for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/live-preview",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"description": "The official live preview JavaScript SDK for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/next",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import httpStatus from 'http-status'
|
||||
import { extractJWT , generatePayloadCookie } from 'payload/auth'
|
||||
import { extractJWT } from 'payload/auth'
|
||||
import { generatePayloadCookie } from 'payload/auth'
|
||||
import { refreshOperation } from 'payload/operations'
|
||||
|
||||
import type { CollectionRouteHandler } from '../types.js'
|
||||
|
||||
@@ -407,11 +407,6 @@ export const POST =
|
||||
let res: Response
|
||||
let collection: Collection
|
||||
|
||||
const overrideHttpMethod = request.headers.get('X-HTTP-Method-Override')
|
||||
if (overrideHttpMethod === 'GET') {
|
||||
return await GET(config)(request, { params: { slug } })
|
||||
}
|
||||
|
||||
try {
|
||||
req = await createPayloadRequest({
|
||||
config,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import type { CustomPayloadRequestProperties, PayloadRequest, SanitizedConfig } from 'payload/types'
|
||||
|
||||
import { initI18n } from '@payloadcms/translations'
|
||||
import { executeAuthStrategies , parseCookies } from 'payload/auth'
|
||||
import { executeAuthStrategies } from 'payload/auth'
|
||||
import { parseCookies } from 'payload/auth'
|
||||
import { getDataLoader } from 'payload/utilities'
|
||||
import qs from 'qs'
|
||||
import { URL } from 'url'
|
||||
@@ -58,17 +59,6 @@ export const createPayloadRequest = async ({
|
||||
fallbackLocale = locales.fallbackLocale
|
||||
}
|
||||
|
||||
const overrideHttpMethod = request.headers.get('X-HTTP-Method-Override')
|
||||
const queryToParse = overrideHttpMethod === 'GET' ? await request.text() : urlProperties.search
|
||||
|
||||
const query = queryToParse
|
||||
? qs.parse(queryToParse, {
|
||||
arrayLimit: 1000,
|
||||
depth: 10,
|
||||
ignoreQueryPrefix: true,
|
||||
})
|
||||
: {}
|
||||
|
||||
const customRequest: CustomPayloadRequestProperties = {
|
||||
context: {},
|
||||
fallbackLocale,
|
||||
@@ -85,7 +75,13 @@ export const createPayloadRequest = async ({
|
||||
payloadUploadSizes: {},
|
||||
port: urlProperties.port,
|
||||
protocol: urlProperties.protocol,
|
||||
query,
|
||||
query: urlProperties.search
|
||||
? qs.parse(urlProperties.search, {
|
||||
arrayLimit: 1000,
|
||||
depth: 10,
|
||||
ignoreQueryPrefix: true,
|
||||
})
|
||||
: {},
|
||||
routeParams: params || {},
|
||||
search: urlProperties.search,
|
||||
searchParams: urlProperties.searchParams,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { I18nClient } from '@payloadcms/translations'
|
||||
import type { Locale } from 'payload/config'
|
||||
import type { InitPageResult, PayloadRequestWithData, VisibleEntities } from 'payload/types'
|
||||
|
||||
import { initI18n } from '@payloadcms/translations'
|
||||
@@ -23,6 +22,7 @@ export const initPage = async ({
|
||||
searchParams,
|
||||
}: Args): Promise<InitPageResult> => {
|
||||
const headers = getHeaders()
|
||||
const localeParam = searchParams?.locale as string
|
||||
const payload = await getPayloadHMR({ config: configPromise })
|
||||
|
||||
const {
|
||||
@@ -34,6 +34,10 @@ export const initPage = async ({
|
||||
} = payload.config
|
||||
|
||||
const queryString = `${qs.stringify(searchParams ?? {}, { addQueryPrefix: true })}`
|
||||
const defaultLocale =
|
||||
localization && localization.defaultLocale ? localization.defaultLocale : 'en'
|
||||
const localeCode = localeParam || defaultLocale
|
||||
const locale = localization && findLocaleFromCode(localization, localeCode)
|
||||
const cookies = parseCookies(headers)
|
||||
const language = getRequestLanguage({ config: payload.config, cookies, headers })
|
||||
|
||||
@@ -60,6 +64,7 @@ export const initPage = async ({
|
||||
const req = await createLocalReq(
|
||||
{
|
||||
fallbackLocale: null,
|
||||
locale: locale.code,
|
||||
req: {
|
||||
host: headers.get('host'),
|
||||
i18n,
|
||||
@@ -74,53 +79,9 @@ export const initPage = async ({
|
||||
)
|
||||
|
||||
const { permissions, user } = await payload.auth({ headers, req })
|
||||
|
||||
req.user = user
|
||||
|
||||
const localeParam = searchParams?.locale as string
|
||||
let locale: Locale
|
||||
|
||||
if (localization) {
|
||||
const defaultLocaleCode = localization.defaultLocale ? localization.defaultLocale : 'en'
|
||||
let localeCode: string = localeParam
|
||||
|
||||
if (!localeCode) {
|
||||
try {
|
||||
localeCode = await payload
|
||||
.find({
|
||||
collection: 'payload-preferences',
|
||||
depth: 0,
|
||||
limit: 1,
|
||||
user,
|
||||
where: {
|
||||
and: [
|
||||
{
|
||||
'user.relationTo': {
|
||||
equals: payload.config.admin.user,
|
||||
},
|
||||
},
|
||||
{
|
||||
'user.value': {
|
||||
equals: user.id,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: {
|
||||
equals: 'locale',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
?.then((res) => res.docs?.[0]?.value as string)
|
||||
} catch (error) {} // eslint-disable-line no-empty
|
||||
}
|
||||
|
||||
locale = findLocaleFromCode(localization, localeCode)
|
||||
|
||||
if (!locale) locale = findLocaleFromCode(localization, defaultLocaleCode)
|
||||
req.locale = locale.code
|
||||
}
|
||||
|
||||
const visibleEntities: VisibleEntities = {
|
||||
collections: collections
|
||||
.map(({ slug, admin: { hidden } }) => (!isEntityHidden({ hidden, user }) ? slug : null))
|
||||
|
||||
@@ -2,7 +2,8 @@ import type { Metadata } from 'next'
|
||||
import type { Icon } from 'next/dist/lib/metadata/types/metadata-types.js'
|
||||
import type { MetaConfig } from 'payload/config'
|
||||
|
||||
import { payloadFaviconDark , payloadFaviconLight, staticOGImage } from '@payloadcms/ui/assets'
|
||||
import { staticOGImage } from '@payloadcms/ui/assets'
|
||||
import { payloadFaviconDark, payloadFaviconLight } from '@payloadcms/ui/assets'
|
||||
import QueryString from 'qs'
|
||||
|
||||
const defaultOpenGraph = {
|
||||
|
||||
@@ -91,7 +91,7 @@ export const Account: React.FC<AdminViewProps> = async ({
|
||||
initialParams={{
|
||||
depth: 0,
|
||||
'fallback-locale': 'null',
|
||||
locale: locale?.code,
|
||||
locale: locale.code,
|
||||
uploadEdits: undefined,
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -26,7 +26,7 @@ export const getDocumentData = async (args: {
|
||||
id,
|
||||
collectionSlug: collectionConfig?.slug,
|
||||
globalSlug: globalConfig?.slug,
|
||||
locale: locale?.code,
|
||||
locale: locale.code,
|
||||
operation: (collectionConfig && id) || globalConfig ? 'update' : 'create',
|
||||
schemaPath: collectionConfig?.slug || globalConfig?.slug,
|
||||
},
|
||||
|
||||
@@ -10,7 +10,6 @@ import { EditDepthProvider } from '@payloadcms/ui/providers/EditDepth'
|
||||
import { FormQueryParamsProvider } from '@payloadcms/ui/providers/FormQueryParams'
|
||||
import { isEditing as getIsEditing } from '@payloadcms/ui/utilities/isEditing'
|
||||
import { notFound, redirect } from 'next/navigation.js'
|
||||
import QueryString from 'qs'
|
||||
import React from 'react'
|
||||
|
||||
import type { GenerateEditViewMetadata } from './getMetaBySegment.js'
|
||||
@@ -86,14 +85,10 @@ export const Document: React.FC<AdminViewProps> = async ({
|
||||
}
|
||||
|
||||
action = `${serverURL}${apiRoute}/${collectionSlug}${isEditing ? `/${id}` : ''}`
|
||||
const apiQueryParams = QueryString.stringify(
|
||||
{
|
||||
draft: collectionConfig.versions?.drafts ? 'true' : undefined,
|
||||
locale: locale?.code,
|
||||
},
|
||||
{ addQueryPrefix: true },
|
||||
)
|
||||
apiURL = `${serverURL}${apiRoute}/${collectionSlug}/${id}${apiQueryParams}`
|
||||
|
||||
apiURL = `${serverURL}${apiRoute}/${collectionSlug}/${id}?locale=${locale.code}${
|
||||
collectionConfig.versions?.drafts ? '&draft=true' : ''
|
||||
}`
|
||||
|
||||
const editConfig = collectionConfig?.admin?.components?.views?.Edit
|
||||
ViewOverride = typeof editConfig === 'function' ? editConfig : null
|
||||
@@ -123,14 +118,9 @@ export const Document: React.FC<AdminViewProps> = async ({
|
||||
|
||||
action = `${serverURL}${apiRoute}/globals/${globalSlug}`
|
||||
|
||||
const apiQueryParams = QueryString.stringify(
|
||||
{
|
||||
draft: globalConfig.versions?.drafts ? 'true' : undefined,
|
||||
locale: locale?.code,
|
||||
},
|
||||
{ addQueryPrefix: true },
|
||||
)
|
||||
apiURL = `${serverURL}${apiRoute}/${globalSlug}${apiQueryParams}`
|
||||
apiURL = `${serverURL}${apiRoute}/${globalSlug}?locale=${locale.code}${
|
||||
globalConfig.versions?.drafts ? '&draft=true' : ''
|
||||
}`
|
||||
|
||||
const editConfig = globalConfig?.admin?.components?.views?.Edit
|
||||
ViewOverride = typeof editConfig === 'function' ? editConfig : null
|
||||
@@ -171,7 +161,7 @@ export const Document: React.FC<AdminViewProps> = async ({
|
||||
depth: 0,
|
||||
draft: true,
|
||||
fallbackLocale: null,
|
||||
locale: locale?.code,
|
||||
locale: locale.code,
|
||||
req,
|
||||
user,
|
||||
})
|
||||
@@ -216,15 +206,12 @@ export const Document: React.FC<AdminViewProps> = async ({
|
||||
/>
|
||||
)}
|
||||
<HydrateClientUser permissions={permissions} user={user} />
|
||||
<EditDepthProvider
|
||||
depth={1}
|
||||
key={`${collectionSlug || globalSlug}${locale?.code ? `-${locale?.code}` : ''}`}
|
||||
>
|
||||
<EditDepthProvider depth={1} key={`${collectionSlug || globalSlug}-${locale.code}`}>
|
||||
<FormQueryParamsProvider
|
||||
initialParams={{
|
||||
depth: 0,
|
||||
'fallback-locale': 'null',
|
||||
locale: locale?.code,
|
||||
locale: locale.code,
|
||||
uploadEdits: undefined,
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { CollectionPermission, GlobalPermission } from 'payload/auth'
|
||||
import type { Document , OptionObject } from 'payload/types'
|
||||
import type { OptionObject } from 'payload/types'
|
||||
import type { Document } from 'payload/types'
|
||||
|
||||
export type CompareOption = {
|
||||
label: string
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "payload",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"description": "Node, React, Headless CMS and Application Framework built on Next.js",
|
||||
"keywords": [
|
||||
"admin panel",
|
||||
|
||||
@@ -42,7 +42,7 @@ export type InitPageResult = {
|
||||
docID?: string
|
||||
globalConfig?: SanitizedGlobalConfig
|
||||
languageOptions: LanguageOptions
|
||||
locale?: Locale
|
||||
locale: Locale
|
||||
permissions: Permissions
|
||||
req: PayloadRequestWithData
|
||||
translations: ClientTranslationsObject
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { GeneratedTypes, Payload , RequestContext } from '../../../index.js'
|
||||
import type { Payload, RequestContext } from '../../../index.js'
|
||||
import type { GeneratedTypes } from '../../../index.js'
|
||||
import type { PayloadRequestWithData } from '../../../types/index.js'
|
||||
import type { Result } from '../login.js'
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { GeneratedTypes, Payload , RequestContext } from '../../../index.js'
|
||||
import type { Payload, RequestContext } from '../../../index.js'
|
||||
import type { GeneratedTypes } from '../../../index.js'
|
||||
import type { PayloadRequestWithData } from '../../../types/index.js'
|
||||
import type { Result } from '../resetPassword.js'
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { GeneratedTypes, Payload , RequestContext } from '../../../index.js'
|
||||
import type { Payload, RequestContext } from '../../../index.js'
|
||||
import type { GeneratedTypes } from '../../../index.js'
|
||||
import type { PayloadRequestWithData } from '../../../types/index.js'
|
||||
|
||||
import { APIError } from '../../../errors/index.js'
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { GeneratedTypes, Payload , RequestContext } from '../../../index.js'
|
||||
import type { Payload, RequestContext } from '../../../index.js'
|
||||
import type { GeneratedTypes } from '../../../index.js'
|
||||
import type { PayloadRequestWithData } from '../../../types/index.js'
|
||||
|
||||
import { APIError } from '../../../errors/index.js'
|
||||
|
||||
@@ -2,7 +2,8 @@ import jwt from 'jsonwebtoken'
|
||||
import url from 'url'
|
||||
|
||||
import type { BeforeOperationHook, Collection } from '../../collections/config/types.js'
|
||||
import type { Document , PayloadRequestWithData } from '../../types/index.js'
|
||||
import type { PayloadRequestWithData } from '../../types/index.js'
|
||||
import type { Document } from '../../types/index.js'
|
||||
|
||||
import { buildAfterOperation } from '../../collections/operations/utils.js'
|
||||
import { Forbidden } from '../../errors/index.js'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { MarkOptional } from 'ts-essentials'
|
||||
|
||||
import type { GeneratedTypes , Payload } from '../../../index.js'
|
||||
import type { GeneratedTypes } from '../../../index.js'
|
||||
import type { Payload } from '../../../index.js'
|
||||
import type { Document, PayloadRequestWithData, RequestContext } from '../../../types/index.js'
|
||||
import type { File } from '../../../uploads/types.js'
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { GeneratedTypes , Payload } from '../../../index.js'
|
||||
import type { Document, PayloadRequestWithData , RequestContext, Where } from '../../../types/index.js'
|
||||
import type { Payload } from '../../../index.js'
|
||||
import type { GeneratedTypes } from '../../../index.js'
|
||||
import type { PayloadRequestWithData, RequestContext } from '../../../types/index.js'
|
||||
import type { Document, Where } from '../../../types/index.js'
|
||||
import type { BulkOperationResult } from '../../config/types.js'
|
||||
|
||||
import { APIError } from '../../../errors/index.js'
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { Config } from './types.js'
|
||||
|
||||
export const defaults: Omit<Config, 'db' | 'editor' | 'secret'> = {
|
||||
admin: {
|
||||
avatar: 'gravatar',
|
||||
avatar: 'default',
|
||||
components: {},
|
||||
custom: {},
|
||||
dateFormat: 'MMMM do yyyy, h:mm a',
|
||||
|
||||
@@ -69,17 +69,7 @@ export default joi.object({
|
||||
meta: joi.object().keys({
|
||||
defaultOGImageType: joi.string().valid('off', 'dynamic', 'static'),
|
||||
description: joi.string(),
|
||||
icons: joi.array().items(
|
||||
joi.object().keys({
|
||||
type: joi.string(),
|
||||
color: joi.string(),
|
||||
fetchPriority: joi.string().valid('auto', 'high', 'low'),
|
||||
media: joi.string(),
|
||||
rel: joi.string(),
|
||||
sizes: joi.string(),
|
||||
url: joi.string(),
|
||||
}),
|
||||
),
|
||||
icons: joi.array().items(joi.object()),
|
||||
openGraph: openGraphSchema,
|
||||
titleSuffix: joi.string(),
|
||||
}),
|
||||
|
||||
@@ -10,7 +10,7 @@ const ogImageObj = joi.object({
|
||||
|
||||
export const openGraphSchema = joi.object({
|
||||
description: joi.string(),
|
||||
images: joi.alternatives().try(ogImageObj, joi.array().items(ogImageObj)),
|
||||
images: joi.alternatives().try(joi.array().items(joi.string()), joi.array().items(ogImageObj)),
|
||||
title: joi.string(),
|
||||
siteName: joi.string(),
|
||||
url: joi.string(),
|
||||
})
|
||||
|
||||
@@ -120,7 +120,7 @@ export type MetaConfig = {
|
||||
*
|
||||
* For example browser tabs, phone home screens, and search engine results.
|
||||
*/
|
||||
icons?: IconConfig[]
|
||||
icons?: IconConfig
|
||||
/**
|
||||
* Overrides the auto-generated <meta name="keywords"> of admin pages
|
||||
* @example `"CMS, Payload, Custom"`
|
||||
|
||||
@@ -19,7 +19,8 @@ import type {
|
||||
} from '../../config/types.js'
|
||||
import type { DBIdentifierName } from '../../database/types.js'
|
||||
import type { Field } from '../../fields/config/types.js'
|
||||
import type { PayloadRequestWithData, RequestContext , Where } from '../../types/index.js'
|
||||
import type { PayloadRequestWithData, RequestContext } from '../../types/index.js'
|
||||
import type { Where } from '../../types/index.js'
|
||||
import type { IncomingGlobalVersions, SanitizedGlobalVersions } from '../../versions/types.js'
|
||||
|
||||
export type TypeWithID = {
|
||||
|
||||
@@ -429,16 +429,6 @@ export class BasePayload<TGeneratedTypes extends GeneratedTypes> {
|
||||
this.email = consoleEmailAdapter({ payload: this })
|
||||
}
|
||||
|
||||
// Warn if image resizing is enabled but sharp is not installed
|
||||
if (
|
||||
!this.config.sharp &&
|
||||
this.config.collections.some((c) => c.upload.imageSizes || c.upload.formatOptions)
|
||||
) {
|
||||
this.logger.warn(
|
||||
`Image resizing is enabled for one or more collections, but sharp not installed. Please install 'sharp' and pass into the config.`,
|
||||
)
|
||||
}
|
||||
|
||||
this.sendEmail = this.email['sendEmail']
|
||||
|
||||
serverInitTelemetry(this)
|
||||
|
||||
@@ -8,11 +8,11 @@ export async function cropImage({ cropData, dimensions, file, sharp }) {
|
||||
try {
|
||||
const { height, width, x, y } = cropData
|
||||
|
||||
const fileIsAnimatedType = ['image/avif', 'image/gif', 'image/webp'].includes(file.mimetype)
|
||||
const fileIsAnimated = ['image/avif', 'image/gif', 'image/webp'].includes(file.mimetype)
|
||||
|
||||
const sharpOptions: SharpOptions = {}
|
||||
|
||||
if (fileIsAnimatedType) sharpOptions.animated = true
|
||||
if (fileIsAnimated) sharpOptions.animated = true
|
||||
|
||||
const formattedCropData = {
|
||||
height: percentToPixel(height, dimensions.height),
|
||||
|
||||
@@ -113,7 +113,7 @@ export const generateFileData = async <T>({
|
||||
let newData = data
|
||||
const filesToSave: FileToSave[] = []
|
||||
const fileData: Partial<FileData> = {}
|
||||
const fileIsAnimatedType = ['image/avif', 'image/gif', 'image/webp'].includes(file.mimetype)
|
||||
const fileIsAnimated = ['image/avif', 'image/gif', 'image/webp'].includes(file.mimetype)
|
||||
const cropData =
|
||||
typeof uploadEdits === 'object' && 'crop' in uploadEdits ? uploadEdits.crop : undefined
|
||||
|
||||
@@ -131,9 +131,9 @@ export const generateFileData = async <T>({
|
||||
|
||||
const sharpOptions: SharpOptions = {}
|
||||
|
||||
if (fileIsAnimatedType) sharpOptions.animated = true
|
||||
if (fileIsAnimated) sharpOptions.animated = true
|
||||
|
||||
if (sharp && (fileIsAnimatedType || fileHasAdjustments)) {
|
||||
if (sharp && (fileIsAnimated || fileHasAdjustments)) {
|
||||
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 {
|
||||
@@ -217,7 +217,7 @@ export const generateFileData = async <T>({
|
||||
}
|
||||
fileData.width = info.width
|
||||
fileData.height = info.height
|
||||
if (fileIsAnimatedType) {
|
||||
if (fileIsAnimated) {
|
||||
const metadata = await sharpFile.metadata()
|
||||
fileData.height = metadata.pages ? info.height / metadata.pages : info.height
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ export const getBaseUploadFields = ({ collection, config }: Options): Field[] =>
|
||||
hooks: {
|
||||
afterRead: [
|
||||
({ data, value }) => {
|
||||
if (value && size.height && size.width) return value
|
||||
if (value) return value
|
||||
|
||||
const sizeFilename = data?.sizes?.[size.name]?.filename
|
||||
|
||||
|
||||
@@ -255,10 +255,10 @@ export async function resizeAndTransformImageSizes({
|
||||
}
|
||||
|
||||
// Determine if the file is animated
|
||||
const fileIsAnimatedType = ['image/avif', 'image/gif', 'image/webp'].includes(file.mimetype)
|
||||
const fileIsAnimated = ['image/avif', 'image/gif', 'image/webp'].includes(file.mimetype)
|
||||
const sharpOptions: SharpOptions = {}
|
||||
|
||||
if (fileIsAnimatedType) sharpOptions.animated = true
|
||||
if (fileIsAnimated) sharpOptions.animated = true
|
||||
|
||||
const sharpBase: Sharp | undefined = sharp(file.tempFilePath || file.data, sharpOptions).rotate() // pass rotate() to auto-rotate based on EXIF data. https://github.com/payloadcms/payload/pull/3081
|
||||
|
||||
@@ -279,26 +279,16 @@ export async function resizeAndTransformImageSizes({
|
||||
const metadata = await sharpBase.metadata()
|
||||
|
||||
if (incomingFocalPoint && applyPayloadAdjustments(imageResizeConfig, dimensions)) {
|
||||
let { height: resizeHeight, width: resizeWidth } = imageResizeConfig
|
||||
|
||||
const { height: resizeHeight, width: resizeWidth } = imageResizeConfig
|
||||
const resizeAspectRatio = resizeWidth / resizeHeight
|
||||
const originalAspectRatio = dimensions.width / dimensions.height
|
||||
|
||||
// Calculate resizeWidth based on original aspect ratio if it's undefined
|
||||
if (resizeHeight && !resizeWidth) {
|
||||
resizeWidth = Math.round(resizeHeight * originalAspectRatio)
|
||||
}
|
||||
|
||||
// Calculate resizeHeight based on original aspect ratio if it's undefined
|
||||
if (resizeWidth && !resizeHeight) {
|
||||
resizeHeight = Math.round(resizeWidth / originalAspectRatio)
|
||||
}
|
||||
const prioritizeHeight = resizeAspectRatio < originalAspectRatio
|
||||
|
||||
// Scale the image up or down to fit the resize dimensions
|
||||
const scaledImage = imageToResize.resize({
|
||||
height: resizeHeight,
|
||||
width: resizeWidth,
|
||||
height: prioritizeHeight ? resizeHeight : null,
|
||||
width: prioritizeHeight ? null : resizeWidth,
|
||||
})
|
||||
|
||||
const { info: scaledImageInfo } = await scaledImage.toBuffer({ resolveWithObject: true })
|
||||
|
||||
const safeResizeWidth = resizeWidth ?? scaledImageInfo.width
|
||||
@@ -308,16 +298,10 @@ export async function resizeAndTransformImageSizes({
|
||||
)
|
||||
const safeOffsetX = Math.min(Math.max(0, leftFocalEdge), maxOffsetX)
|
||||
|
||||
const isAnimated = fileIsAnimatedType && metadata.pages
|
||||
const safeResizeHeight = resizeHeight ?? scaledImageInfo.height
|
||||
|
||||
let safeResizeHeight = resizeHeight ?? scaledImageInfo.height
|
||||
|
||||
if (isAnimated && resizeHeight === undefined) {
|
||||
safeResizeHeight = scaledImageInfo.height / metadata.pages
|
||||
}
|
||||
|
||||
const maxOffsetY = isAnimated
|
||||
? safeResizeHeight - (resizeHeight ?? safeResizeHeight)
|
||||
const maxOffsetY = fileIsAnimated
|
||||
? resizeHeight - safeResizeHeight
|
||||
: scaledImageInfo.height - safeResizeHeight
|
||||
|
||||
const topFocalEdge = Math.round(
|
||||
@@ -326,7 +310,7 @@ export async function resizeAndTransformImageSizes({
|
||||
const safeOffsetY = Math.min(Math.max(0, topFocalEdge), maxOffsetY)
|
||||
|
||||
// extract the focal area from the scaled image
|
||||
resized = (fileIsAnimatedType ? imageToResize : scaledImage).extract({
|
||||
resized = (fileIsAnimated ? imageToResize : scaledImage).extract({
|
||||
height: safeResizeHeight,
|
||||
left: safeOffsetX,
|
||||
top: safeOffsetY,
|
||||
@@ -380,7 +364,7 @@ export async function resizeAndTransformImageSizes({
|
||||
name: imageResizeConfig.name,
|
||||
filename: imageNameWithDimensions,
|
||||
filesize: size,
|
||||
height: fileIsAnimatedType && metadata.pages ? height / metadata.pages : height,
|
||||
height: fileIsAnimated && metadata.pages ? height / metadata.pages : height,
|
||||
mimeType: mimeInfo?.mime || mimeType,
|
||||
sizesToSave: [{ buffer: bufferData, path: imagePath }],
|
||||
width,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-cloud-storage",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"description": "The official cloud storage plugin for Payload CMS",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { StaticHandler } from '@payloadcms/plugin-cloud-storage/types'
|
||||
import type { CollectionConfig } from 'payload/types'
|
||||
import type { CollectionConfig, PayloadRequestWithData, UploadConfig } from 'payload/types'
|
||||
|
||||
import { head } from '@vercel/blob'
|
||||
import path from 'path'
|
||||
@@ -17,7 +17,7 @@ export const getStaticHandler = (
|
||||
): StaticHandler => {
|
||||
return async (req, { params: { filename } }) => {
|
||||
try {
|
||||
const prefix = await getFilePrefix({ collection, filename, req })
|
||||
const prefix = await getFilePrefix({ collection, req, filename })
|
||||
|
||||
const fileUrl = `${baseUrl}/${path.posix.join(prefix, filename)}`
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { CollectionConfig, Field , GroupField, TextField } from 'payload/types'
|
||||
import type { GroupField, TextField } from 'payload/types'
|
||||
import type { CollectionConfig, Field } from 'payload/types'
|
||||
|
||||
import path from 'path'
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { CollectionConfig, Field , GroupField, TextField } from 'payload/types'
|
||||
import type { GroupField, TextField } from 'payload/types'
|
||||
import type { CollectionConfig, Field } from 'payload/types'
|
||||
|
||||
import path from 'path'
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { CollectionAfterDeleteHook, CollectionConfig , FileData, TypeWithID } from 'payload/types'
|
||||
import type { FileData, TypeWithID } from 'payload/types'
|
||||
import type { CollectionAfterDeleteHook, CollectionConfig } from 'payload/types'
|
||||
|
||||
import type { GeneratedAdapter, TypeWithPrefix } from '../types.js'
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { CollectionBeforeChangeHook, CollectionConfig , FileData, TypeWithID } from 'payload/types'
|
||||
import type { FileData, TypeWithID } from 'payload/types'
|
||||
import type { CollectionBeforeChangeHook, CollectionConfig } from 'payload/types'
|
||||
|
||||
import type { GeneratedAdapter } from '../types.js'
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import type { CollectionConfig, Field, FileData , ImageSize , PayloadRequestWithData, TypeWithID } from 'payload/types'
|
||||
import type { Field, FileData, ImageSize } from 'payload/types'
|
||||
import type { TypeWithID } from 'payload/types'
|
||||
import type { CollectionConfig, PayloadRequestWithData } from 'payload/types'
|
||||
|
||||
export interface File {
|
||||
buffer: Buffer
|
||||
|
||||
@@ -2,12 +2,12 @@ import type { CollectionConfig, PayloadRequestWithData, UploadConfig } from 'pay
|
||||
|
||||
export async function getFilePrefix({
|
||||
collection,
|
||||
filename,
|
||||
req,
|
||||
filename,
|
||||
}: {
|
||||
collection: CollectionConfig
|
||||
filename: string
|
||||
req: PayloadRequestWithData
|
||||
filename: string
|
||||
}): Promise<string> {
|
||||
const imageSizes = (collection?.upload as UploadConfig)?.imageSizes || []
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-cloud",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"description": "The official Payload Cloud plugin",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-form-builder",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"description": "Form builder plugin for Payload CMS",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-nested-docs",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"description": "The official Nested Docs plugin for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-redirects",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"description": "Redirects plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-relationship-object-ids",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"description": "A Payload plugin to store all relationship IDs as ObjectIDs",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-search",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"description": "Search plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-seo",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"description": "SEO plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-stripe",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"description": "Stripe plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { PayloadRequest, PayloadRequestWithData } from 'payload/types'
|
||||
import type { PayloadRequestWithData } from 'payload/types'
|
||||
|
||||
import { addDataAndFileToRequest } from '@payloadcms/next/utilities'
|
||||
import { Forbidden } from 'payload/errors'
|
||||
|
||||
import type { StripePluginConfig } from '../types.js'
|
||||
@@ -9,17 +8,13 @@ import { stripeProxy } from '../utilities/stripeProxy.js'
|
||||
|
||||
export const stripeREST = async (args: {
|
||||
pluginConfig: StripePluginConfig
|
||||
req: PayloadRequest
|
||||
req: PayloadRequestWithData
|
||||
}): Promise<any> => {
|
||||
let responseStatus = 200
|
||||
let responseJSON
|
||||
|
||||
const { pluginConfig, req } = args
|
||||
|
||||
await addDataAndFileToRequest({ request: req })
|
||||
|
||||
const requestWithData = req as PayloadRequestWithData
|
||||
|
||||
const {
|
||||
data: {
|
||||
stripeArgs, // example: ['cus_MGgt3Tuj3D66f2'] or [{ limit: 100 }, { stripeAccount: 'acct_1J9Z4pKZ4Z4Z4Z4Z' }]
|
||||
@@ -27,7 +22,7 @@ export const stripeREST = async (args: {
|
||||
},
|
||||
payload,
|
||||
user,
|
||||
} = requestWithData
|
||||
} = req
|
||||
|
||||
const { stripeSecretKey } = pluginConfig
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Config as PayloadConfig } from 'payload/config'
|
||||
import type { PayloadRequest } from 'payload/types'
|
||||
import type { PayloadRequestWithData } from 'payload/types'
|
||||
|
||||
import Stripe from 'stripe'
|
||||
|
||||
@@ -10,7 +10,7 @@ import { handleWebhooks } from '../webhooks/index.js'
|
||||
export const stripeWebhooks = async (args: {
|
||||
config: PayloadConfig
|
||||
pluginConfig: StripePluginConfig
|
||||
req: PayloadRequest
|
||||
req: PayloadRequestWithData
|
||||
}): Promise<any> => {
|
||||
const { config, pluginConfig, req } = args
|
||||
let returnStatus = 200
|
||||
@@ -47,7 +47,6 @@ export const stripeWebhooks = async (args: {
|
||||
event,
|
||||
payload: req.payload,
|
||||
pluginConfig,
|
||||
req,
|
||||
stripe,
|
||||
})
|
||||
|
||||
@@ -58,7 +57,6 @@ export const stripeWebhooks = async (args: {
|
||||
event,
|
||||
payload: req.payload,
|
||||
pluginConfig,
|
||||
req,
|
||||
stripe,
|
||||
})
|
||||
}
|
||||
@@ -71,7 +69,6 @@ export const stripeWebhooks = async (args: {
|
||||
event,
|
||||
payload: req.payload,
|
||||
pluginConfig,
|
||||
req,
|
||||
stripe,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { Payload } from 'payload'
|
||||
import type { Config as PayloadConfig } from 'payload/config'
|
||||
import type { PayloadRequest } from 'payload/types'
|
||||
import type Stripe from 'stripe'
|
||||
|
||||
export type StripeWebhookHandler<T = any> = (args: {
|
||||
@@ -8,7 +7,6 @@ export type StripeWebhookHandler<T = any> = (args: {
|
||||
event: T
|
||||
payload: Payload
|
||||
pluginConfig?: StripePluginConfig
|
||||
req: PayloadRequest
|
||||
stripe: Stripe
|
||||
}) => void
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
import type { CustomComponent } from 'payload/config'
|
||||
import type { UIField } from 'payload/types'
|
||||
|
||||
import { CopyToClipboard } from '@payloadcms/ui/elements/CopyToClipboard'
|
||||
import { useFieldProps } from '@payloadcms/ui/forms/FieldPropsProvider'
|
||||
// import CopyToClipboard from 'payload/dist/admin/components/elements/CopyToClipboard'
|
||||
import { useFormFields } from '@payloadcms/ui/forms/Form'
|
||||
import React from 'react'
|
||||
|
||||
@@ -29,7 +29,7 @@ export const LinkToDoc: CustomComponent<UIField> = () => {
|
||||
>
|
||||
View in Stripe
|
||||
</span>
|
||||
<CopyToClipboard value={href} />
|
||||
{/* <CopyToClipboard value={href} /> */}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
|
||||
@@ -20,5 +20,5 @@
|
||||
"src/**/*.spec.tsx"
|
||||
],
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.d.ts", "src/**/*.json"],
|
||||
"references": [{ "path": "../payload" }, { "path": "../ui" }, { "path": "../next" }]
|
||||
"references": [{ "path": "../payload" }, { "path": "../ui" }]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/richtext-lexical",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"description": "The officially supported Lexical richtext adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { BaseSelection ,
|
||||
import type { BaseSelection } from 'lexical'
|
||||
import type {
|
||||
DOMConversionMap,
|
||||
DOMConversionOutput,
|
||||
EditorConfig,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import type { Transformer } from '@lexical/markdown'
|
||||
import type { GenericLanguages, I18nClient } from '@payloadcms/translations'
|
||||
import type { JSONSchema4 } from 'json-schema'
|
||||
import type { Klass, LexicalEditor, LexicalNode, LexicalNodeReplacement , SerializedEditorState , SerializedLexicalNode } from 'lexical'
|
||||
import type { Klass, LexicalEditor, LexicalNode, SerializedEditorState } from 'lexical'
|
||||
import type { SerializedLexicalNode } from 'lexical'
|
||||
import type { LexicalNodeReplacement } from 'lexical'
|
||||
import type { RequestContext } from 'payload'
|
||||
import type { SanitizedConfig } from 'payload/config'
|
||||
import type {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Klass, LexicalNode , LexicalNodeReplacement } from 'lexical'
|
||||
import type { Klass, LexicalNode } from 'lexical'
|
||||
import type { LexicalNodeReplacement } from 'lexical'
|
||||
|
||||
import type { SanitizedClientEditorConfig, SanitizedServerEditorConfig } from '../config/types.js'
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/* eslint-disable perfectionist/sort-objects */
|
||||
/* eslint-disable regexp/no-obscure-range */
|
||||
/* eslint-disable @typescript-eslint/no-redundant-type-constituents */
|
||||
/* eslint-disable regexp/no-misleading-unicode-character */
|
||||
//This copy-and-pasted from lexical here: https://github.com/facebook/lexical/blob/c2ceee223f46543d12c574e62155e619f9a18a5d/packages/lexical/src/LexicalConstants.ts
|
||||
|
||||
import type { ElementFormatType, TextFormatType, TextModeType } from 'lexical'
|
||||
import type { ElementFormatType, TextFormatType } from 'lexical'
|
||||
export type TextDetailType = 'directionless' | 'unmergable'
|
||||
import type { TextModeType } from 'lexical'
|
||||
|
||||
// DOM
|
||||
export const NodeFormat = {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Collection , PayloadRequestWithData } from 'payload/types'
|
||||
import type { PayloadRequestWithData } from 'payload/types'
|
||||
import type { Collection } from 'payload/types'
|
||||
|
||||
import { createDataloaderCacheKey } from 'payload/utilities'
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/richtext-slate",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"description": "The officially supported Slate richtext adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Collection, Field, PayloadRequestWithData, RichTextField } from 'payload/types'
|
||||
import type { PayloadRequestWithData } from 'payload/types'
|
||||
import type { Collection, Field, RichTextField } from 'payload/types'
|
||||
|
||||
import { createDataloaderCacheKey } from 'payload/utilities'
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/storage-azure",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"description": "Payload storage adapter for Azure Blob Storage",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/storage-gcs",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"description": "Payload storage adapter for Google Cloud Storage",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/storage-s3",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"description": "Payload storage adapter for Amazon S3",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/storage-uploadthing",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"description": "Payload storage adapter for uploadthing",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/storage-vercel-blob",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"description": "Payload storage adapter for Vercel Blob Storage",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import type {
|
||||
Adapter,
|
||||
PluginOptions as CloudStoragePluginOptions,
|
||||
CollectionOptions, GeneratedAdapter } from '@payloadcms/plugin-cloud-storage/types'
|
||||
CollectionOptions,
|
||||
} from '@payloadcms/plugin-cloud-storage/types'
|
||||
import type { Adapter, GeneratedAdapter } from '@payloadcms/plugin-cloud-storage/types'
|
||||
import type { Config, Plugin } from 'payload/config'
|
||||
|
||||
import { cloudStoragePlugin } from '@payloadcms/plugin-cloud-storage'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/translations",
|
||||
"version": "3.0.0-beta.47",
|
||||
"version": "3.0.0-beta.46",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user