Compare commits
27 Commits
revert/doc
...
chore/file
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d5cc843a2 | ||
|
|
34920a7ec0 | ||
|
|
2650eb7d44 | ||
|
|
50c2f8bec2 | ||
|
|
f49eeb1a63 | ||
|
|
1d9ad6f2f1 | ||
|
|
30fc7e3012 | ||
|
|
1ccd7ef074 | ||
|
|
34c3a5193b | ||
|
|
81532cb9c9 | ||
|
|
f70c6fe3e7 | ||
|
|
e6b664284f | ||
|
|
fafaa04e1a | ||
|
|
babcd599da | ||
|
|
ac19b78968 | ||
|
|
b40c581a27 | ||
|
|
335af1b8c9 | ||
|
|
583a733334 | ||
|
|
6e5ddc8873 | ||
|
|
9ba740e472 | ||
|
|
50029532aa | ||
|
|
c80b6e92c4 | ||
|
|
a9580e05ac | ||
|
|
57d00ad2e9 | ||
|
|
a9ad7c771e | ||
|
|
7a40a9fc06 | ||
|
|
b1ae749311 |
3
.github/workflows/main.yml
vendored
3
.github/workflows/main.yml
vendored
@@ -187,7 +187,8 @@ jobs:
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: ${{ (startsWith(matrix.database, 'postgres') ) && 'postgis/postgis:16-3.4' || '' }}
|
||||
# Custom postgres 17 docker image that supports both pg-vector and postgis: https://github.com/payloadcms/postgis-vector
|
||||
image: ${{ (startsWith(matrix.database, 'postgres') ) && 'ghcr.io/payloadcms/postgis-vector:latest' || '' }}
|
||||
env:
|
||||
# must specify password for PG Docker container image, see: https://registry.hub.docker.com/_/postgres?tab=description&page=1&name=10
|
||||
POSTGRES_USER: ${{ env.POSTGRES_USER }}
|
||||
|
||||
@@ -16,14 +16,15 @@ The labels you provide for your Collections and Globals are used to name the Gra
|
||||
|
||||
At the top of your Payload Config you can define all the options to manage GraphQL.
|
||||
|
||||
| Option | Description |
|
||||
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `mutations` | Any custom Mutations to be added in addition to what Payload provides. [More](/docs/graphql/extending) |
|
||||
| `queries` | Any custom Queries to be added in addition to what Payload provides. [More](/docs/graphql/extending) |
|
||||
| `maxComplexity` | A number used to set the maximum allowed complexity allowed by requests [More](/docs/graphql/overview#query-complexity-limits) |
|
||||
| `disablePlaygroundInProduction` | A boolean that if false will enable the GraphQL playground, defaults to true. [More](/docs/graphql/overview#graphql-playground) |
|
||||
| `disable` | A boolean that if true will disable the GraphQL entirely, defaults to false. |
|
||||
| `validationRules` | A function that takes the ExecutionArgs and returns an array of ValidationRules. |
|
||||
| Option | Description |
|
||||
| ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `mutations` | Any custom Mutations to be added in addition to what Payload provides. [More](/docs/graphql/extending) |
|
||||
| `queries` | Any custom Queries to be added in addition to what Payload provides. [More](/docs/graphql/extending) |
|
||||
| `maxComplexity` | A number used to set the maximum allowed complexity allowed by requests [More](/docs/graphql/overview#query-complexity-limits) |
|
||||
| `disablePlaygroundInProduction` | A boolean that if false will enable the GraphQL playground in production environments, defaults to true. [More](/docs/graphql/overview#graphql-playground) |
|
||||
| `disableIntrospectionInProduction` | A boolean that if false will enable the GraphQL introspection in production environments, defaults to true. |
|
||||
| `disable` | A boolean that if true will disable the GraphQL entirely, defaults to false. |
|
||||
| `validationRules` | A function that takes the ExecutionArgs and returns an array of ValidationRules. |
|
||||
|
||||
## Collections
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ export default buildConfig({
|
||||
admin: {
|
||||
components: {
|
||||
// The `BeforeLogin` component renders a message that you see while logging into your admin panel.
|
||||
// Feel free to delete this at any time. Simply remove the line below and the import `BeforeLogin` statement on line 15.
|
||||
// Feel free to delete this at any time. Simply remove the line below.
|
||||
beforeLogin: ['@/components/BeforeLogin'],
|
||||
afterDashboard: ['@/components/AfterDashboard'],
|
||||
},
|
||||
|
||||
@@ -14,9 +14,12 @@ export const superAdminOrTenantAdminAccess: Access = ({ req }) => {
|
||||
return true
|
||||
}
|
||||
|
||||
return {
|
||||
tenant: {
|
||||
in: getUserTenantIDs(req.user, 'tenant-admin'),
|
||||
},
|
||||
const adminTenantAccessIDs = getUserTenantIDs(req.user, 'tenant-admin')
|
||||
const requestedTenant = req?.data?.tenant
|
||||
|
||||
if (requestedTenant && adminTenantAccessIDs.includes(requestedTenant)) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Access } from 'payload'
|
||||
|
||||
import type { User } from '../../../payload-types'
|
||||
import type { Tenant, User } from '../../../payload-types'
|
||||
|
||||
import { isSuperAdmin } from '../../../access/isSuperAdmin'
|
||||
import { getUserTenantIDs } from '../../../utilities/getUserTenantIDs'
|
||||
@@ -14,9 +14,20 @@ export const createAccess: Access<User> = ({ req }) => {
|
||||
return true
|
||||
}
|
||||
|
||||
if (!isSuperAdmin(req.user) && req.data?.roles?.includes('super-admin')) {
|
||||
return false
|
||||
}
|
||||
|
||||
const adminTenantAccessIDs = getUserTenantIDs(req.user, 'tenant-admin')
|
||||
|
||||
if (adminTenantAccessIDs.length) {
|
||||
const requestedTenants: Tenant['id'][] =
|
||||
req.data?.tenants?.map((t: { tenant: Tenant['id'] }) => t.tenant) ?? []
|
||||
|
||||
const hasAccessToAllRequestedTenants = requestedTenants.every((tenantID) =>
|
||||
adminTenantAccessIDs.includes(tenantID),
|
||||
)
|
||||
|
||||
if (hasAccessToAllRequestedTenants) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "payload-monorepo",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"workspaces": [
|
||||
@@ -151,8 +151,8 @@
|
||||
"create-payload-app": "workspace:*",
|
||||
"cross-env": "7.0.3",
|
||||
"dotenv": "16.4.7",
|
||||
"drizzle-kit": "0.31.0",
|
||||
"drizzle-orm": "0.43.1",
|
||||
"drizzle-kit": "0.31.4",
|
||||
"drizzle-orm": "0.44.2",
|
||||
"escape-html": "^1.0.3",
|
||||
"execa": "5.1.1",
|
||||
"form-data": "3.0.1",
|
||||
@@ -166,7 +166,7 @@
|
||||
"next": "15.3.2",
|
||||
"open": "^10.1.0",
|
||||
"p-limit": "^5.0.0",
|
||||
"pg": "8.11.3",
|
||||
"pg": "8.16.3",
|
||||
"playwright": "1.50.0",
|
||||
"playwright-core": "1.50.0",
|
||||
"prettier": "3.5.3",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/admin-bar",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "An admin bar for React apps using Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "create-payload-app",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-mongodb",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "The officially supported MongoDB database adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -77,6 +77,9 @@ const relationshipSort = ({
|
||||
) {
|
||||
const relationshipPath = segments.slice(0, i + 1).join('.')
|
||||
let sortFieldPath = segments.slice(i + 1, segments.length).join('.')
|
||||
if (sortFieldPath.endsWith('.id')) {
|
||||
sortFieldPath = sortFieldPath.split('.').slice(0, -1).join('.')
|
||||
}
|
||||
if (Array.isArray(field.relationTo)) {
|
||||
throw new APIError('Not supported')
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ export const updateOne: UpdateOne = async function updateOne(
|
||||
try {
|
||||
if (returning === false) {
|
||||
await Model.updateOne(query, data, options)
|
||||
transform({ adapter: this, data, fields, operation: 'read' })
|
||||
return null
|
||||
} else {
|
||||
result = await Model.findOneAndUpdate(query, data, options)
|
||||
|
||||
@@ -417,7 +417,7 @@ export const transform = ({
|
||||
|
||||
if (operation === 'read') {
|
||||
delete data['__v']
|
||||
data.id = data._id
|
||||
data.id = data._id || data.id
|
||||
delete data['_id']
|
||||
|
||||
if (data.id instanceof Types.ObjectId) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-postgres",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "The officially supported Postgres database adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
@@ -78,9 +78,9 @@
|
||||
"@payloadcms/drizzle": "workspace:*",
|
||||
"@types/pg": "8.10.2",
|
||||
"console-table-printer": "2.12.1",
|
||||
"drizzle-kit": "0.31.1",
|
||||
"drizzle-kit": "0.31.4",
|
||||
"drizzle-orm": "0.44.2",
|
||||
"pg": "8.11.3",
|
||||
"pg": "8.16.3",
|
||||
"prompts": "2.4.2",
|
||||
"to-snake-case": "1.0.0",
|
||||
"uuid": "10.0.0"
|
||||
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
updateMany,
|
||||
updateOne,
|
||||
updateVersion,
|
||||
upsert,
|
||||
} from '@payloadcms/drizzle'
|
||||
import {
|
||||
columnToCodeConverter,
|
||||
@@ -207,7 +208,7 @@ export function postgresAdapter(args: Args): DatabaseAdapterObj<PostgresAdapter>
|
||||
updateMany,
|
||||
updateOne,
|
||||
updateVersion,
|
||||
upsert: updateOne,
|
||||
upsert,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-sqlite",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "The officially supported SQLite database adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
@@ -76,7 +76,7 @@
|
||||
"@libsql/client": "0.14.0",
|
||||
"@payloadcms/drizzle": "workspace:*",
|
||||
"console-table-printer": "2.12.1",
|
||||
"drizzle-kit": "0.31.1",
|
||||
"drizzle-kit": "0.31.4",
|
||||
"drizzle-orm": "0.44.2",
|
||||
"prompts": "2.4.2",
|
||||
"to-snake-case": "1.0.0",
|
||||
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
updateMany,
|
||||
updateOne,
|
||||
updateVersion,
|
||||
upsert,
|
||||
} from '@payloadcms/drizzle'
|
||||
import { like, notLike } from 'drizzle-orm'
|
||||
import { createDatabaseAdapter, defaultBeginTransaction } from 'payload'
|
||||
@@ -189,7 +190,7 @@ export function sqliteAdapter(args: Args): DatabaseAdapterObj<SQLiteAdapter> {
|
||||
updateGlobalVersion,
|
||||
updateOne,
|
||||
updateVersion,
|
||||
upsert: updateOne,
|
||||
upsert,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-vercel-postgres",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "Vercel Postgres adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
@@ -78,9 +78,9 @@
|
||||
"@payloadcms/drizzle": "workspace:*",
|
||||
"@vercel/postgres": "^0.9.0",
|
||||
"console-table-printer": "2.12.1",
|
||||
"drizzle-kit": "0.31.1",
|
||||
"drizzle-kit": "0.31.4",
|
||||
"drizzle-orm": "0.44.2",
|
||||
"pg": "8.11.3",
|
||||
"pg": "8.16.3",
|
||||
"prompts": "2.4.2",
|
||||
"to-snake-case": "1.0.0",
|
||||
"uuid": "10.0.0"
|
||||
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
updateMany,
|
||||
updateOne,
|
||||
updateVersion,
|
||||
upsert,
|
||||
} from '@payloadcms/drizzle'
|
||||
import {
|
||||
columnToCodeConverter,
|
||||
@@ -202,7 +203,7 @@ export function vercelPostgresAdapter(args: Args = {}): DatabaseAdapterObj<Verce
|
||||
updateMany,
|
||||
updateOne,
|
||||
updateVersion,
|
||||
upsert: updateOne,
|
||||
upsert,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/drizzle",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "A library of shared functions used by different payload database adapters",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -78,6 +78,7 @@ export { updateJobs } from './updateJobs.js'
|
||||
export { updateMany } from './updateMany.js'
|
||||
export { updateOne } from './updateOne.js'
|
||||
export { updateVersion } from './updateVersion.js'
|
||||
export { upsert } from './upsert.js'
|
||||
export { upsertRow } from './upsertRow/index.js'
|
||||
export { buildCreateMigration } from './utilities/buildCreateMigration.js'
|
||||
export { buildIndexName } from './utilities/buildIndexName.js'
|
||||
|
||||
@@ -24,20 +24,26 @@ export const columnToCodeConverter: ColumnToCodeConverter = ({
|
||||
|
||||
const columnBuilderArgsArray: string[] = []
|
||||
|
||||
if (column.type === 'timestamp') {
|
||||
columnBuilderArgsArray.push(`mode: '${column.mode}'`)
|
||||
if (column.withTimezone) {
|
||||
columnBuilderArgsArray.push('withTimezone: true')
|
||||
switch (column.type) {
|
||||
case 'bit':
|
||||
case 'halfvec':
|
||||
case 'sparsevec':
|
||||
case 'vector': {
|
||||
if (column.dimensions) {
|
||||
columnBuilderArgsArray.push(`dimensions: ${column.dimensions}`)
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'timestamp': {
|
||||
columnBuilderArgsArray.push(`mode: '${column.mode}'`)
|
||||
if (column.withTimezone) {
|
||||
columnBuilderArgsArray.push('withTimezone: true')
|
||||
}
|
||||
|
||||
if (typeof column.precision === 'number') {
|
||||
columnBuilderArgsArray.push(`precision: ${column.precision}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (column.type === 'vector') {
|
||||
if (column.dimensions) {
|
||||
columnBuilderArgsArray.push(`dimensions: ${column.dimensions}`)
|
||||
if (typeof column.precision === 'number') {
|
||||
columnBuilderArgsArray.push(`precision: ${column.precision}`)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import type { ForeignKeyBuilder, IndexBuilder } from 'drizzle-orm/pg-core'
|
||||
|
||||
import {
|
||||
bit,
|
||||
boolean,
|
||||
foreignKey,
|
||||
halfvec,
|
||||
index,
|
||||
integer,
|
||||
jsonb,
|
||||
numeric,
|
||||
serial,
|
||||
sparsevec,
|
||||
text,
|
||||
timestamp,
|
||||
uniqueIndex,
|
||||
@@ -44,6 +47,14 @@ export const buildDrizzleTable = ({
|
||||
|
||||
for (const [key, column] of Object.entries(rawTable.columns)) {
|
||||
switch (column.type) {
|
||||
case 'bit': {
|
||||
const builder = bit(column.name, { dimensions: column.dimensions })
|
||||
|
||||
columns[key] = builder
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
case 'enum':
|
||||
if ('locale' in column) {
|
||||
columns[key] = adapter.enums.enum__locales(column.name)
|
||||
@@ -56,6 +67,21 @@ export const buildDrizzleTable = ({
|
||||
}
|
||||
break
|
||||
|
||||
case 'halfvec': {
|
||||
const builder = halfvec(column.name, { dimensions: column.dimensions })
|
||||
|
||||
columns[key] = builder
|
||||
break
|
||||
}
|
||||
|
||||
case 'sparsevec': {
|
||||
const builder = sparsevec(column.name, { dimensions: column.dimensions })
|
||||
|
||||
columns[key] = builder
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
case 'timestamp': {
|
||||
let builder = timestamp(column.name, {
|
||||
mode: column.mode,
|
||||
|
||||
@@ -281,12 +281,30 @@ export type VectorRawColumn = {
|
||||
type: 'vector'
|
||||
} & BaseRawColumn
|
||||
|
||||
export type HalfVecRawColumn = {
|
||||
dimensions?: number
|
||||
type: 'halfvec'
|
||||
} & BaseRawColumn
|
||||
|
||||
export type SparseVecRawColumn = {
|
||||
dimensions?: number
|
||||
type: 'sparsevec'
|
||||
} & BaseRawColumn
|
||||
|
||||
export type BinaryVecRawColumn = {
|
||||
dimensions?: number
|
||||
type: 'bit'
|
||||
} & BaseRawColumn
|
||||
|
||||
export type RawColumn =
|
||||
| ({
|
||||
type: 'boolean' | 'geometry' | 'jsonb' | 'numeric' | 'serial' | 'text' | 'varchar'
|
||||
} & BaseRawColumn)
|
||||
| BinaryVecRawColumn
|
||||
| EnumRawColumn
|
||||
| HalfVecRawColumn
|
||||
| IntegerRawColumn
|
||||
| SparseVecRawColumn
|
||||
| TimestampRawColumn
|
||||
| UUIDRawColumn
|
||||
| VectorRawColumn
|
||||
|
||||
@@ -18,6 +18,7 @@ export const updateOne: UpdateOne = async function updateOne(
|
||||
data,
|
||||
joins: joinQuery,
|
||||
locale,
|
||||
options = { upsert: false },
|
||||
req,
|
||||
returning,
|
||||
select,
|
||||
@@ -66,6 +67,13 @@ export const updateOne: UpdateOne = async function updateOne(
|
||||
}
|
||||
}
|
||||
|
||||
if (!idToUpdate && !options.upsert) {
|
||||
// TODO: In 4.0, if returning === false, we should differentiate between:
|
||||
// - No document found to update
|
||||
// - Document found, but returning === false
|
||||
return null
|
||||
}
|
||||
|
||||
const result = await upsertRow({
|
||||
id: idToUpdate,
|
||||
adapter: this,
|
||||
|
||||
20
packages/drizzle/src/upsert.ts
Normal file
20
packages/drizzle/src/upsert.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { Upsert } from 'payload'
|
||||
|
||||
import type { DrizzleAdapter } from './types.js'
|
||||
|
||||
export const upsert: Upsert = async function upsert(
|
||||
this: DrizzleAdapter,
|
||||
{ collection, data, joins, locale, req, returning, select, where },
|
||||
) {
|
||||
return this.updateOne({
|
||||
collection,
|
||||
data,
|
||||
joins,
|
||||
locale,
|
||||
options: { upsert: true },
|
||||
req,
|
||||
returning,
|
||||
select,
|
||||
where,
|
||||
})
|
||||
}
|
||||
@@ -13,6 +13,13 @@ import { deleteExistingArrayRows } from './deleteExistingArrayRows.js'
|
||||
import { deleteExistingRowsByPath } from './deleteExistingRowsByPath.js'
|
||||
import { insertArrays } from './insertArrays.js'
|
||||
|
||||
/**
|
||||
* If `id` is provided, it will update the row with that ID.
|
||||
* If `where` is provided, it will update the row that matches the `where`
|
||||
* If neither `id` nor `where` is provided, it will create a new row.
|
||||
*
|
||||
* This function replaces the entire row and does not support partial updates.
|
||||
*/
|
||||
export const upsertRow = async <T extends Record<string, unknown> | TypeWithID>({
|
||||
id,
|
||||
adapter,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/email-nodemailer",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "Payload Nodemailer Email Adapter",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/email-resend",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "Payload Resend Email Adapter",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/graphql",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -113,6 +113,7 @@ export function configToSchema(config: SanitizedConfig): {
|
||||
variables: args.variableValues,
|
||||
// onComplete: (complexity) => { console.log('Query Complexity:', complexity); },
|
||||
}),
|
||||
...(config.graphQL.disableIntrospectionInProduction ? [NoProductionIntrospection] : []),
|
||||
...(typeof config?.graphQL?.validationRules === 'function'
|
||||
? config.graphQL.validationRules(args)
|
||||
: []),
|
||||
@@ -123,3 +124,18 @@ export function configToSchema(config: SanitizedConfig): {
|
||||
validationRules,
|
||||
}
|
||||
}
|
||||
|
||||
const NoProductionIntrospection: GraphQL.ValidationRule = (context) => ({
|
||||
Field(node) {
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
if (node.name.value === '__schema' || node.name.value === '__type') {
|
||||
context.reportError(
|
||||
new GraphQL.GraphQLError(
|
||||
'GraphQL introspection is not allowed, but the query contained __schema or __type',
|
||||
{ nodes: [node] },
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/live-preview-react",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "The official React SDK for Payload Live Preview",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/live-preview-vue",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "The official Vue SDK for Payload Live Preview",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/live-preview",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "The official live preview JavaScript SDK for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/next",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -146,7 +146,7 @@ export const DefaultTemplate: React.FC<DefaultTemplateProps> = ({
|
||||
|
||||
return (
|
||||
<EntityVisibilityProvider visibleEntities={visibleEntities}>
|
||||
<BulkUploadProvider>
|
||||
<BulkUploadProvider drawerSlugPrefix={collectionSlug}>
|
||||
<ActionsProvider Actions={Actions}>
|
||||
{RenderServerComponent({
|
||||
clientProps,
|
||||
|
||||
@@ -6,6 +6,7 @@ import type {
|
||||
DocumentViewServerProps,
|
||||
DocumentViewServerPropsOnly,
|
||||
EditViewComponent,
|
||||
LivePreviewConfig,
|
||||
PayloadComponent,
|
||||
RenderDocumentVersionsProperties,
|
||||
} from 'payload'
|
||||
@@ -91,7 +92,6 @@ export const renderDocument = async ({
|
||||
payload: {
|
||||
config,
|
||||
config: {
|
||||
admin: { livePreview: livePreviewConfig },
|
||||
routes: { admin: adminRoute, api: apiRoute },
|
||||
serverURL,
|
||||
},
|
||||
@@ -329,6 +329,12 @@ export const renderDocument = async ({
|
||||
viewType,
|
||||
}
|
||||
|
||||
const livePreviewConfig: LivePreviewConfig = {
|
||||
...(config.admin.livePreview || {}),
|
||||
...(collectionConfig?.admin?.livePreview || {}),
|
||||
...(globalConfig?.admin?.livePreview || {}),
|
||||
}
|
||||
|
||||
const livePreviewURL =
|
||||
typeof livePreviewConfig?.url === 'function'
|
||||
? await livePreviewConfig.url({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/payload-cloud",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "The official Payload Cloud plugin",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -7,11 +7,11 @@ const dirname = path.dirname(filename)
|
||||
|
||||
async function build() {
|
||||
const resultIndex = await esbuild.build({
|
||||
entryPoints: ['src/exports/index.ts'],
|
||||
entryPoints: ['src/index.ts'],
|
||||
bundle: true,
|
||||
platform: 'node',
|
||||
format: 'esm',
|
||||
outfile: 'dist/exports/index.js',
|
||||
outfile: 'dist/index.js',
|
||||
splitting: false,
|
||||
external: [
|
||||
'lodash',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "payload",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "Node, React, Headless CMS and Application Framework built on Next.js",
|
||||
"keywords": [
|
||||
"admin panel",
|
||||
|
||||
@@ -123,6 +123,7 @@ export const addDefaultsToConfig = (config: Config): Config => {
|
||||
config.endpoints = config.endpoints ?? []
|
||||
config.globals = config.globals ?? []
|
||||
config.graphQL = {
|
||||
disableIntrospectionInProduction: true,
|
||||
disablePlaygroundInProduction: true,
|
||||
maxComplexity: 1000,
|
||||
schemaOutputFile: `${typeof process?.cwd === 'function' ? process.cwd() : ''}/schema.graphql`,
|
||||
|
||||
@@ -1029,6 +1029,17 @@ export type Config = {
|
||||
*/
|
||||
graphQL?: {
|
||||
disable?: boolean
|
||||
/**
|
||||
* Disable introspection queries in production.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
disableIntrospectionInProduction?: boolean
|
||||
/**
|
||||
* Disable the GraphQL Playground in production.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
disablePlaygroundInProduction?: boolean
|
||||
maxComplexity?: number
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { Dispatcher } from 'undici'
|
||||
|
||||
import { lookup } from 'dns/promises'
|
||||
import ipaddr from 'ipaddr.js'
|
||||
import { Agent, fetch as undiciFetch } from 'undici'
|
||||
|
||||
@@ -24,12 +25,26 @@ const isSafeIp = (ip: string) => {
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a hostname or IP address is safe to fetch from.
|
||||
* @param hostname a hostname or IP address
|
||||
* @returns
|
||||
*/
|
||||
const isSafe = async (hostname: string) => {
|
||||
try {
|
||||
if (ipaddr.isValid(hostname)) {
|
||||
return isSafeIp(hostname)
|
||||
}
|
||||
|
||||
const { address } = await lookup(hostname)
|
||||
return isSafeIp(address)
|
||||
} catch (_ignore) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const ssrfFilterInterceptor: Dispatcher.DispatcherComposeInterceptor = (dispatch) => {
|
||||
return (opts, handler) => {
|
||||
const url = new URL(opts.origin?.toString() + opts.path)
|
||||
if (!isSafeIp(url.hostname)) {
|
||||
throw new Error(`Blocked unsafe attempt to ${url}`)
|
||||
}
|
||||
return dispatch(opts, handler)
|
||||
}
|
||||
}
|
||||
@@ -40,11 +55,20 @@ const safeDispatcher = new Agent().compose(ssrfFilterInterceptor)
|
||||
* A "safe" version of undici's fetch that prevents SSRF attacks.
|
||||
*
|
||||
* - Utilizes a custom dispatcher that filters out requests to unsafe IP addresses.
|
||||
* - Validates domain names by resolving them to IP addresses and checking if they're safe.
|
||||
* - Undici was used because it supported interceptors as well as "credentials: include". Native fetch
|
||||
*/
|
||||
export const safeFetch = async (...args: Parameters<typeof undiciFetch>) => {
|
||||
const [url, options] = args
|
||||
const [unverifiedUrl, options] = args
|
||||
|
||||
try {
|
||||
const url = new URL(unverifiedUrl)
|
||||
|
||||
const isHostnameSafe = await isSafe(url.hostname)
|
||||
if (!isHostnameSafe) {
|
||||
throw new Error(`Blocked unsafe attempt to ${url.toString()}`)
|
||||
}
|
||||
|
||||
return await undiciFetch(url, {
|
||||
...options,
|
||||
dispatcher: safeDispatcher,
|
||||
@@ -56,11 +80,13 @@ export const safeFetch = async (...args: Parameters<typeof undiciFetch>) => {
|
||||
// The desired message we want to bubble up is in the cause
|
||||
throw new Error(error.cause.message)
|
||||
} else {
|
||||
let stringifiedUrl: string | undefined | URL = undefined
|
||||
if (typeof url === 'string' || url instanceof URL) {
|
||||
stringifiedUrl = url
|
||||
} else if (url instanceof Request) {
|
||||
stringifiedUrl = url.url
|
||||
let stringifiedUrl: string | undefined = undefined
|
||||
if (typeof unverifiedUrl === 'string') {
|
||||
stringifiedUrl = unverifiedUrl
|
||||
} else if (unverifiedUrl instanceof URL) {
|
||||
stringifiedUrl = unverifiedUrl.toString()
|
||||
} else if (unverifiedUrl instanceof Request) {
|
||||
stringifiedUrl = unverifiedUrl.url
|
||||
}
|
||||
|
||||
throw new Error(`Failed to fetch from ${stringifiedUrl}, ${error.message}`)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-cloud-storage",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "The official cloud storage plugin for Payload CMS",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-form-builder",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "Form builder plugin for Payload CMS",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-import-export",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "Import-Export plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -74,11 +74,12 @@ export const Preview = () => {
|
||||
return
|
||||
}
|
||||
|
||||
const { docs, totalDocs } = await res.json()
|
||||
const { docs, totalDocs }: { docs: Record<string, unknown>[]; totalDocs: number } =
|
||||
await res.json()
|
||||
|
||||
setResultCount(limit && limit < totalDocs ? limit : totalDocs)
|
||||
|
||||
const allKeys = Object.keys(docs[0] || {})
|
||||
const allKeys = Array.from(new Set(docs.flatMap((doc) => Object.keys(doc))))
|
||||
const defaultMetaFields = ['createdAt', 'updatedAt', '_status', 'id']
|
||||
|
||||
// Match CSV column ordering by building keys based on fields and regex
|
||||
@@ -96,13 +97,10 @@ export const Preview = () => {
|
||||
})
|
||||
: allKeys.filter((key) => !defaultMetaFields.includes(key))
|
||||
|
||||
const includedMeta = new Set(selectedKeys)
|
||||
const missingMetaFields = defaultMetaFields.flatMap((field) => {
|
||||
const regex = fieldToRegex(field)
|
||||
return allKeys.filter((key) => regex.test(key) && !includedMeta.has(key))
|
||||
})
|
||||
|
||||
const fieldKeys = [...selectedKeys, ...missingMetaFields]
|
||||
const fieldKeys =
|
||||
Array.isArray(fields) && fields.length > 0
|
||||
? selectedKeys // strictly only what was selected
|
||||
: [...selectedKeys, ...defaultMetaFields.filter((key) => allKeys.includes(key))]
|
||||
|
||||
// Build columns based on flattened keys
|
||||
const newColumns: Column[] = fieldKeys.map((key) => ({
|
||||
|
||||
@@ -111,23 +111,45 @@ export const createExport = async (args: CreateExportArgs) => {
|
||||
|
||||
if (download) {
|
||||
if (debug) {
|
||||
req.payload.logger.info('Starting download stream')
|
||||
req.payload.logger.info('Pre-scanning all columns before streaming')
|
||||
}
|
||||
|
||||
const allColumnsSet = new Set<string>()
|
||||
const allColumns: string[] = []
|
||||
let scanPage = 1
|
||||
let hasMore = true
|
||||
|
||||
while (hasMore) {
|
||||
const result = await payload.find({ ...findArgs, page: scanPage })
|
||||
|
||||
result.docs.forEach((doc) => {
|
||||
const flat = flattenObject({ doc, fields, toCSVFunctions })
|
||||
Object.keys(flat).forEach((key) => {
|
||||
if (!allColumnsSet.has(key)) {
|
||||
allColumnsSet.add(key)
|
||||
allColumns.push(key)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
hasMore = result.hasNextPage
|
||||
scanPage += 1
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
req.payload.logger.info(`Discovered ${allColumns.length} columns`)
|
||||
}
|
||||
|
||||
const encoder = new TextEncoder()
|
||||
let isFirstBatch = true
|
||||
let columns: string[] | undefined
|
||||
let page = 1
|
||||
let streamPage = 1
|
||||
|
||||
const stream = new Readable({
|
||||
async read() {
|
||||
const result = await payload.find({
|
||||
...findArgs,
|
||||
page,
|
||||
})
|
||||
const result = await payload.find({ ...findArgs, page: streamPage })
|
||||
|
||||
if (debug) {
|
||||
req.payload.logger.info(`Processing batch ${page} with ${result.docs.length} documents`)
|
||||
req.payload.logger.info(`Streaming batch ${streamPage} with ${result.docs.length} docs`)
|
||||
}
|
||||
|
||||
if (result.docs.length === 0) {
|
||||
@@ -135,19 +157,24 @@ export const createExport = async (args: CreateExportArgs) => {
|
||||
return
|
||||
}
|
||||
|
||||
const csvInput = result.docs.map((doc) => flattenObject({ doc, fields, toCSVFunctions }))
|
||||
const batchRows = result.docs.map((doc) => flattenObject({ doc, fields, toCSVFunctions }))
|
||||
|
||||
if (isFirstBatch) {
|
||||
columns = Object.keys(csvInput[0] ?? {})
|
||||
}
|
||||
const paddedRows = batchRows.map((row) => {
|
||||
const fullRow: Record<string, unknown> = {}
|
||||
for (const col of allColumns) {
|
||||
fullRow[col] = row[col] ?? ''
|
||||
}
|
||||
return fullRow
|
||||
})
|
||||
|
||||
const csvString = stringify(csvInput, {
|
||||
const csvString = stringify(paddedRows, {
|
||||
header: isFirstBatch,
|
||||
columns,
|
||||
columns: allColumns,
|
||||
})
|
||||
|
||||
this.push(encoder.encode(csvString))
|
||||
isFirstBatch = false
|
||||
streamPage += 1
|
||||
|
||||
if (!result.hasNextPage) {
|
||||
if (debug) {
|
||||
@@ -155,8 +182,6 @@ export const createExport = async (args: CreateExportArgs) => {
|
||||
}
|
||||
this.push(null) // End the stream
|
||||
}
|
||||
|
||||
page += 1
|
||||
},
|
||||
})
|
||||
|
||||
@@ -168,11 +193,15 @@ export const createExport = async (args: CreateExportArgs) => {
|
||||
})
|
||||
}
|
||||
|
||||
// Non-download path (buffered export)
|
||||
if (debug) {
|
||||
req.payload.logger.info('Starting file generation')
|
||||
}
|
||||
|
||||
const outputData: string[] = []
|
||||
let isFirstBatch = true
|
||||
const rows: Record<string, unknown>[] = []
|
||||
const columnsSet = new Set<string>()
|
||||
const columns: string[] = []
|
||||
let page = 1
|
||||
let hasNextPage = true
|
||||
|
||||
@@ -189,9 +218,19 @@ export const createExport = async (args: CreateExportArgs) => {
|
||||
}
|
||||
|
||||
if (isCSV) {
|
||||
const csvInput = result.docs.map((doc) => flattenObject({ doc, fields, toCSVFunctions }))
|
||||
outputData.push(stringify(csvInput, { header: isFirstBatch }))
|
||||
isFirstBatch = false
|
||||
const batchRows = result.docs.map((doc) => flattenObject({ doc, fields, toCSVFunctions }))
|
||||
|
||||
// Track discovered column keys
|
||||
batchRows.forEach((row) => {
|
||||
Object.keys(row).forEach((key) => {
|
||||
if (!columnsSet.has(key)) {
|
||||
columnsSet.add(key)
|
||||
columns.push(key)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
rows.push(...batchRows)
|
||||
} else {
|
||||
const jsonInput = result.docs.map((doc) => JSON.stringify(doc))
|
||||
outputData.push(jsonInput.join(',\n'))
|
||||
@@ -201,6 +240,23 @@ export const createExport = async (args: CreateExportArgs) => {
|
||||
page += 1
|
||||
}
|
||||
|
||||
if (isCSV) {
|
||||
const paddedRows = rows.map((row) => {
|
||||
const fullRow: Record<string, unknown> = {}
|
||||
for (const col of columns) {
|
||||
fullRow[col] = row[col] ?? ''
|
||||
}
|
||||
return fullRow
|
||||
})
|
||||
|
||||
outputData.push(
|
||||
stringify(paddedRows, {
|
||||
header: true,
|
||||
columns,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
const buffer = Buffer.from(format === 'json' ? `[${outputData.join(',')}]` : outputData.join(''))
|
||||
if (debug) {
|
||||
req.payload.logger.info(`${format} file generation complete`)
|
||||
|
||||
@@ -11,6 +11,7 @@ import { getCustomFieldFunctions } from './export/getCustomFieldFunctions.js'
|
||||
import { getSelect } from './export/getSelect.js'
|
||||
import { getExportCollection } from './getExportCollection.js'
|
||||
import { translations } from './translations/index.js'
|
||||
import { getFlattenedFieldKeys } from './utilities/getFlattenedFieldKeys.js'
|
||||
|
||||
export const importExportPlugin =
|
||||
(pluginConfig: ImportExportPluginConfig) =>
|
||||
@@ -112,13 +113,23 @@ export const importExportPlugin =
|
||||
select,
|
||||
})
|
||||
|
||||
const transformed = docs.map((doc) =>
|
||||
flattenObject({
|
||||
const possibleKeys = getFlattenedFieldKeys(collection.config.fields as FlattenedField[])
|
||||
|
||||
const transformed = docs.map((doc) => {
|
||||
const row = flattenObject({
|
||||
doc,
|
||||
fields,
|
||||
toCSVFunctions,
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
for (const key of possibleKeys) {
|
||||
if (!(key in row)) {
|
||||
row[key] = null
|
||||
}
|
||||
}
|
||||
|
||||
return row
|
||||
})
|
||||
|
||||
return Response.json({
|
||||
docs: transformed,
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import { type FlattenedField } from 'payload'
|
||||
|
||||
type FieldWithPresentational =
|
||||
| {
|
||||
fields?: FlattenedField[]
|
||||
name?: string
|
||||
tabs?: {
|
||||
fields: FlattenedField[]
|
||||
name?: string
|
||||
}[]
|
||||
type: 'collapsible' | 'row' | 'tabs'
|
||||
}
|
||||
| FlattenedField
|
||||
|
||||
export const getFlattenedFieldKeys = (fields: FieldWithPresentational[], prefix = ''): string[] => {
|
||||
const keys: string[] = []
|
||||
|
||||
fields.forEach((field) => {
|
||||
if (!('name' in field) || typeof field.name !== 'string') {
|
||||
return
|
||||
}
|
||||
|
||||
const name = prefix ? `${prefix}_${field.name}` : field.name
|
||||
|
||||
switch (field.type) {
|
||||
case 'array': {
|
||||
const subKeys = getFlattenedFieldKeys(field.fields as FlattenedField[], `${name}_0`)
|
||||
keys.push(...subKeys)
|
||||
break
|
||||
}
|
||||
case 'blocks':
|
||||
field.blocks.forEach((block) => {
|
||||
const blockKeys = getFlattenedFieldKeys(block.fields as FlattenedField[], `${name}_0`)
|
||||
keys.push(...blockKeys)
|
||||
})
|
||||
break
|
||||
case 'collapsible':
|
||||
case 'group':
|
||||
case 'row':
|
||||
keys.push(...getFlattenedFieldKeys(field.fields as FlattenedField[], name))
|
||||
break
|
||||
case 'relationship':
|
||||
if (field.hasMany) {
|
||||
// e.g. hasManyPolymorphic_0_value_id
|
||||
keys.push(`${name}_0_relationTo`, `${name}_0_value_id`)
|
||||
} else {
|
||||
// e.g. hasOnePolymorphic_id
|
||||
keys.push(`${name}_id`, `${name}_relationTo`)
|
||||
}
|
||||
break
|
||||
case 'tabs':
|
||||
if (field.tabs) {
|
||||
field.tabs.forEach((tab) => {
|
||||
if (tab.name) {
|
||||
const tabPrefix = prefix ? `${prefix}_${tab.name}` : tab.name
|
||||
keys.push(...getFlattenedFieldKeys(tab.fields, tabPrefix))
|
||||
} else {
|
||||
keys.push(...getFlattenedFieldKeys(tab.fields, prefix))
|
||||
}
|
||||
})
|
||||
}
|
||||
break
|
||||
default:
|
||||
if ('hasMany' in field && field.hasMany) {
|
||||
// Push placeholder for first index
|
||||
keys.push(`${name}_0`)
|
||||
} else {
|
||||
keys.push(name)
|
||||
}
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
return keys
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-multi-tenant",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "Multi Tenant plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-nested-docs",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "The official Nested Docs plugin for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-redirects",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "Redirects plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -28,6 +28,7 @@ export const redirectsPlugin =
|
||||
index: true,
|
||||
label: 'From URL',
|
||||
required: true,
|
||||
unique: true,
|
||||
},
|
||||
{
|
||||
name: 'to',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-search",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "Search plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-sentry",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "Sentry plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-seo",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "SEO plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-stripe",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "Stripe plugin for Payload",
|
||||
"keywords": [
|
||||
"payload",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/richtext-lexical",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "The officially supported Lexical richtext adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -67,6 +67,16 @@ $lexical-contenteditable-bottom-padding: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include small-break {
|
||||
.rich-text-lexical {
|
||||
&.error {
|
||||
> .rich-text-lexical__wrap {
|
||||
@include lightInputError;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
html[data-theme='dark'] {
|
||||
@@ -81,5 +91,15 @@ $lexical-contenteditable-bottom-padding: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include small-break {
|
||||
.rich-text-lexical {
|
||||
&.error {
|
||||
> .rich-text-lexical__wrap {
|
||||
@include darkInputError;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/richtext-slate",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "The officially supported Slate richtext adapter for Payload",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/storage-azure",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "Payload storage adapter for Azure Blob Storage",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/storage-gcs",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "Payload storage adapter for Google Cloud Storage",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/storage-s3",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "Payload storage adapter for Amazon S3",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/storage-uploadthing",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "Payload storage adapter for uploadthing",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/storage-vercel-blob",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"description": "Payload storage adapter for Vercel Blob Storage",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/translations",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/ui",
|
||||
"version": "3.44.0",
|
||||
"version": "3.45.0",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -52,7 +52,13 @@ export const AddNewRelation: React.FC<Props> = ({
|
||||
|
||||
const onSave: DocumentDrawerContextType['onSave'] = useCallback(
|
||||
({ doc, operation }) => {
|
||||
if (operation === 'create') {
|
||||
// if autosave is enabled, the operation will be 'update'
|
||||
const isAutosaveEnabled =
|
||||
typeof collectionConfig?.versions?.drafts === 'object'
|
||||
? collectionConfig.versions.drafts.autosave
|
||||
: false
|
||||
|
||||
if (operation === 'create' || (operation === 'update' && isAutosaveEnabled)) {
|
||||
// ensure the value is not already in the array
|
||||
let isNewValue = false
|
||||
if (!value) {
|
||||
|
||||
@@ -23,6 +23,7 @@ import { useLocale } from '../../providers/Locale/index.js'
|
||||
import { useTranslation } from '../../providers/Translation/index.js'
|
||||
import { formatTimeToNow } from '../../utilities/formatDocTitle/formatDateTitle.js'
|
||||
import { reduceFieldsToValuesWithValidation } from '../../utilities/reduceFieldsToValuesWithValidation.js'
|
||||
import { useDocumentDrawerContext } from '../DocumentDrawer/Provider.js'
|
||||
import { LeaveWithoutSaving } from '../LeaveWithoutSaving/index.js'
|
||||
import './index.scss'
|
||||
|
||||
@@ -56,10 +57,12 @@ export const Autosave: React.FC<Props> = ({ id, collection, global: globalDoc })
|
||||
updateSavedDocumentData,
|
||||
} = useDocumentInfo()
|
||||
|
||||
const { reportUpdate } = useDocumentEvents()
|
||||
const { dispatchFields, isValid, setBackgroundProcessing, setIsValid, setSubmitted } = useForm()
|
||||
const { onSave: onSaveFromDocumentDrawer } = useDocumentDrawerContext()
|
||||
|
||||
const [fields] = useAllFormFields()
|
||||
const { reportUpdate } = useDocumentEvents()
|
||||
const { dispatchFields, isValid, setBackgroundProcessing, setIsValid } = useForm()
|
||||
|
||||
const [formState] = useAllFormFields()
|
||||
const modified = useFormModified()
|
||||
const submitted = useFormSubmitted()
|
||||
|
||||
@@ -78,32 +81,27 @@ export const Autosave: React.FC<Props> = ({ id, collection, global: globalDoc })
|
||||
)
|
||||
|
||||
const [_saving, setSaving] = useState(false)
|
||||
|
||||
const saving = useDeferredValue(_saving)
|
||||
const debouncedFields = useDebounce(fields, interval)
|
||||
const fieldRef = useRef(fields)
|
||||
|
||||
const debouncedFormState = useDebounce(formState, interval)
|
||||
|
||||
const formStateRef = useRef(formState)
|
||||
const modifiedRef = useRef(modified)
|
||||
const localeRef = useRef(locale)
|
||||
/**
|
||||
* Track the validation internally so Autosave can determine when to run queue processing again
|
||||
* Helps us prevent infinite loops when the queue is processing and the form is invalid
|
||||
*/
|
||||
const isValidRef = useRef(isValid)
|
||||
|
||||
// Store fields in ref so the autosave func
|
||||
// can always retrieve the most to date copies
|
||||
// after the timeout has executed
|
||||
|
||||
fieldRef.current = fields
|
||||
formStateRef.current = formState
|
||||
|
||||
// Store modified in ref so the autosave func
|
||||
// can bail out if modified becomes false while
|
||||
// timing out during autosave
|
||||
|
||||
modifiedRef.current = modified
|
||||
|
||||
// Store locale in ref so the autosave func
|
||||
// can always retrieve the most to date locale
|
||||
|
||||
localeRef.current = locale
|
||||
|
||||
const { queueTask } = useQueues()
|
||||
@@ -155,14 +153,14 @@ export const Autosave: React.FC<Props> = ({ id, collection, global: globalDoc })
|
||||
|
||||
if (url) {
|
||||
if (modifiedRef.current) {
|
||||
const { data, valid } = reduceFieldsToValuesWithValidation(fieldRef.current, true)
|
||||
const { data, valid } = reduceFieldsToValuesWithValidation(formStateRef.current, true)
|
||||
|
||||
data._status = 'draft'
|
||||
|
||||
const skipSubmission =
|
||||
submitted && !valid && versionsConfig?.drafts && versionsConfig?.drafts?.validate
|
||||
|
||||
if (!skipSubmission && isValidRef.current) {
|
||||
if (!skipSubmission) {
|
||||
let res
|
||||
|
||||
try {
|
||||
@@ -183,6 +181,8 @@ export const Autosave: React.FC<Props> = ({ id, collection, global: globalDoc })
|
||||
// We need to log the time in order to figure out if we need to trigger the state off later
|
||||
endTimestamp = newDate.getTime()
|
||||
|
||||
const json = await res.json()
|
||||
|
||||
if (res.status === 200) {
|
||||
setLastUpdateTime(newDate.getTime())
|
||||
|
||||
@@ -192,13 +192,20 @@ export const Autosave: React.FC<Props> = ({ id, collection, global: globalDoc })
|
||||
updatedAt: newDate.toISOString(),
|
||||
})
|
||||
|
||||
// if onSaveFromDocumentDrawer is defined, call it
|
||||
if (typeof onSaveFromDocumentDrawer === 'function') {
|
||||
void onSaveFromDocumentDrawer({
|
||||
...json,
|
||||
operation: 'update',
|
||||
})
|
||||
}
|
||||
|
||||
if (!mostRecentVersionIsAutosaved) {
|
||||
incrementVersionCount()
|
||||
setMostRecentVersionIsAutosaved(true)
|
||||
setUnpublishedVersionCount((prev) => prev + 1)
|
||||
}
|
||||
}
|
||||
const json = await res.json()
|
||||
|
||||
if (versionsConfig?.drafts && versionsConfig?.drafts?.validate && json?.errors) {
|
||||
if (Array.isArray(json.errors)) {
|
||||
@@ -238,10 +245,7 @@ export const Autosave: React.FC<Props> = ({ id, collection, global: globalDoc })
|
||||
toast.error(err.message || i18n.t('error:unknown'))
|
||||
})
|
||||
|
||||
// Set valid to false internally so the queue doesn't process
|
||||
isValidRef.current = false
|
||||
setIsValid(false)
|
||||
setSubmitted(true)
|
||||
hideIndicator()
|
||||
return
|
||||
}
|
||||
@@ -252,9 +256,6 @@ export const Autosave: React.FC<Props> = ({ id, collection, global: globalDoc })
|
||||
// Manually update the data since this function doesn't fire the `submit` function from useForm
|
||||
if (document) {
|
||||
setIsValid(true)
|
||||
|
||||
// Reset internal state allowing the queue to process
|
||||
isValidRef.current = true
|
||||
updateSavedDocumentData(document)
|
||||
}
|
||||
}
|
||||
@@ -270,11 +271,6 @@ export const Autosave: React.FC<Props> = ({ id, collection, global: globalDoc })
|
||||
setBackgroundProcessing(false)
|
||||
},
|
||||
beforeProcess: () => {
|
||||
if (!isValidRef.current) {
|
||||
isValidRef.current = true
|
||||
return false
|
||||
}
|
||||
|
||||
setBackgroundProcessing(true)
|
||||
},
|
||||
},
|
||||
@@ -282,7 +278,8 @@ export const Autosave: React.FC<Props> = ({ id, collection, global: globalDoc })
|
||||
})
|
||||
|
||||
const didMount = useRef(false)
|
||||
const previousDebouncedFieldValues = useRef(reduceFieldsToValues(debouncedFields))
|
||||
const previousDebouncedData = useRef(reduceFieldsToValues(debouncedFormState))
|
||||
|
||||
// When debounced fields change, autosave
|
||||
useEffect(() => {
|
||||
/**
|
||||
@@ -295,16 +292,19 @@ export const Autosave: React.FC<Props> = ({ id, collection, global: globalDoc })
|
||||
|
||||
/**
|
||||
* Ensure autosave only runs if the form data changes, not every time the entire form state changes
|
||||
* Remove `updatedAt` from comparison as it changes on every autosave interval.
|
||||
*/
|
||||
const debouncedFieldValues = reduceFieldsToValues(debouncedFields)
|
||||
if (dequal(debouncedFieldValues, previousDebouncedFieldValues)) {
|
||||
const { updatedAt: _, ...formData } = reduceFieldsToValues(debouncedFormState)
|
||||
const { updatedAt: __, ...prevFormData } = previousDebouncedData.current
|
||||
|
||||
if (dequal(formData, prevFormData)) {
|
||||
return
|
||||
}
|
||||
|
||||
previousDebouncedFieldValues.current = debouncedFieldValues
|
||||
previousDebouncedData.current = formData
|
||||
|
||||
handleAutosave()
|
||||
}, [debouncedFields])
|
||||
}, [debouncedFormState])
|
||||
|
||||
/**
|
||||
* If component unmounts, clear the autosave timeout
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
'use client'
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation.js'
|
||||
import { formatAdminURL } from 'payload/shared'
|
||||
import React, { useCallback, useEffect } from 'react'
|
||||
|
||||
import type { EditFormProps } from './types.js'
|
||||
@@ -12,9 +10,7 @@ import { WatchChildErrors } from '../../../forms/WatchChildErrors/index.js'
|
||||
import { useConfig } from '../../../providers/Config/index.js'
|
||||
import { useDocumentEvents } from '../../../providers/DocumentEvents/index.js'
|
||||
import { useDocumentInfo } from '../../../providers/DocumentInfo/index.js'
|
||||
import { useEditDepth } from '../../../providers/EditDepth/index.js'
|
||||
import { OperationProvider } from '../../../providers/Operation/index.js'
|
||||
import { useRouteTransition } from '../../../providers/RouteTransition/index.js'
|
||||
import { useServerFunctions } from '../../../providers/ServerFunctions/index.js'
|
||||
import { abortAndIgnore, handleAbortRef } from '../../../utilities/abortAndIgnore.js'
|
||||
import { useDocumentDrawerContext } from '../../DocumentDrawer/Provider.js'
|
||||
@@ -23,7 +19,6 @@ import { MoveDocToFolder } from '../../FolderView/MoveDocToFolder/index.js'
|
||||
import { Upload_v4 } from '../../Upload/index.js'
|
||||
import { useFormsManager } from '../FormsManager/index.js'
|
||||
import './index.scss'
|
||||
import { BulkUploadProvider } from '../index.js'
|
||||
|
||||
const baseClass = 'collection-edit'
|
||||
|
||||
@@ -44,7 +39,6 @@ export function EditForm({
|
||||
getDocPreferences,
|
||||
hasSavePermission,
|
||||
initialState,
|
||||
isEditing,
|
||||
isInitializing,
|
||||
Upload: CustomUpload,
|
||||
} = useDocumentInfo()
|
||||
@@ -54,23 +48,14 @@ export function EditForm({
|
||||
const { getFormState } = useServerFunctions()
|
||||
|
||||
const {
|
||||
config: {
|
||||
folders,
|
||||
routes: { admin: adminRoute },
|
||||
},
|
||||
config: { folders },
|
||||
getEntityConfig,
|
||||
} = useConfig()
|
||||
|
||||
const abortOnChangeRef = React.useRef<AbortController>(null)
|
||||
|
||||
const collectionConfig = getEntityConfig({ collectionSlug: docSlug })
|
||||
const router = useRouter()
|
||||
const depth = useEditDepth()
|
||||
const params = useSearchParams()
|
||||
const { reportUpdate } = useDocumentEvents()
|
||||
const { startRouteTransition } = useRouteTransition()
|
||||
|
||||
const locale = params.get('locale')
|
||||
|
||||
const collectionSlug = collectionConfig.slug
|
||||
|
||||
@@ -89,31 +74,9 @@ export function EditForm({
|
||||
operation: 'create',
|
||||
})
|
||||
}
|
||||
|
||||
if (!isEditing && depth < 2) {
|
||||
// Redirect to the same locale if it's been set
|
||||
const redirectRoute = formatAdminURL({
|
||||
adminRoute,
|
||||
path: `/collections/${collectionSlug}/${json?.doc?.id}${locale ? `?locale=${locale}` : ''}`,
|
||||
})
|
||||
|
||||
startRouteTransition(() => router.push(redirectRoute))
|
||||
} else {
|
||||
resetUploadEdits()
|
||||
}
|
||||
resetUploadEdits()
|
||||
},
|
||||
[
|
||||
adminRoute,
|
||||
collectionSlug,
|
||||
depth,
|
||||
isEditing,
|
||||
locale,
|
||||
onSaveFromContext,
|
||||
reportUpdate,
|
||||
resetUploadEdits,
|
||||
router,
|
||||
startRouteTransition,
|
||||
],
|
||||
[collectionSlug, onSaveFromContext, reportUpdate, resetUploadEdits],
|
||||
)
|
||||
|
||||
const onChange: NonNullable<FormProps['onChange']>[0] = useCallback(
|
||||
@@ -150,54 +113,52 @@ export function EditForm({
|
||||
|
||||
return (
|
||||
<OperationProvider operation="create">
|
||||
<BulkUploadProvider>
|
||||
<Form
|
||||
action={action}
|
||||
className={`${baseClass}__form`}
|
||||
disabled={isInitializing || !hasSavePermission}
|
||||
initialState={isInitializing ? undefined : initialState}
|
||||
isInitializing={isInitializing}
|
||||
method="POST"
|
||||
onChange={[onChange]}
|
||||
onSuccess={onSave}
|
||||
submitted={submitted}
|
||||
>
|
||||
<DocumentFields
|
||||
BeforeFields={
|
||||
<React.Fragment>
|
||||
{CustomUpload || (
|
||||
<Upload_v4
|
||||
collectionSlug={collectionConfig.slug}
|
||||
customActions={[
|
||||
folders && collectionConfig.folders && (
|
||||
<MoveDocToFolder
|
||||
buttonProps={{
|
||||
buttonStyle: 'pill',
|
||||
size: 'small',
|
||||
}}
|
||||
folderCollectionSlug={folders.slug}
|
||||
folderFieldName={folders.fieldName}
|
||||
key="move-doc-to-folder"
|
||||
/>
|
||||
),
|
||||
].filter(Boolean)}
|
||||
initialState={initialState}
|
||||
resetUploadEdits={resetUploadEdits}
|
||||
updateUploadEdits={updateUploadEdits}
|
||||
uploadConfig={collectionConfig.upload}
|
||||
uploadEdits={uploadEdits}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
}
|
||||
docPermissions={docPermissions}
|
||||
fields={collectionConfig.fields}
|
||||
schemaPathSegments={[collectionConfig.slug]}
|
||||
/>
|
||||
<ReportAllErrors />
|
||||
<GetFieldProxy />
|
||||
</Form>
|
||||
</BulkUploadProvider>
|
||||
<Form
|
||||
action={action}
|
||||
className={`${baseClass}__form`}
|
||||
disabled={isInitializing || !hasSavePermission}
|
||||
initialState={isInitializing ? undefined : initialState}
|
||||
isInitializing={isInitializing}
|
||||
method="POST"
|
||||
onChange={[onChange]}
|
||||
onSuccess={onSave}
|
||||
submitted={submitted}
|
||||
>
|
||||
<DocumentFields
|
||||
BeforeFields={
|
||||
<React.Fragment>
|
||||
{CustomUpload || (
|
||||
<Upload_v4
|
||||
collectionSlug={collectionConfig.slug}
|
||||
customActions={[
|
||||
folders && collectionConfig.folders && (
|
||||
<MoveDocToFolder
|
||||
buttonProps={{
|
||||
buttonStyle: 'pill',
|
||||
size: 'small',
|
||||
}}
|
||||
folderCollectionSlug={folders.slug}
|
||||
folderFieldName={folders.fieldName}
|
||||
key="move-doc-to-folder"
|
||||
/>
|
||||
),
|
||||
].filter(Boolean)}
|
||||
initialState={initialState}
|
||||
resetUploadEdits={resetUploadEdits}
|
||||
updateUploadEdits={updateUploadEdits}
|
||||
uploadConfig={collectionConfig.upload}
|
||||
uploadEdits={uploadEdits}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
}
|
||||
docPermissions={docPermissions}
|
||||
fields={collectionConfig.fields}
|
||||
schemaPathSegments={[collectionConfig.slug]}
|
||||
/>
|
||||
<ReportAllErrors />
|
||||
<GetFieldProxy />
|
||||
</Form>
|
||||
</OperationProvider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -11,9 +11,11 @@ import type { FormProps } from '../../../forms/Form/index.js'
|
||||
import type { OnFieldSelect } from '../../FieldSelect/index.js'
|
||||
import type { FieldOption } from '../../FieldSelect/reduceFieldOptions.js'
|
||||
import type { State } from '../FormsManager/reducer.js'
|
||||
import type { EditManyBulkUploadsProps } from './index.js'
|
||||
|
||||
import { Button } from '../../../elements/Button/index.js'
|
||||
import { Form } from '../../../forms/Form/index.js'
|
||||
import { FieldPathContext } from '../../../forms/RenderFields/context.js'
|
||||
import { RenderField } from '../../../forms/RenderFields/RenderField.js'
|
||||
import { XIcon } from '../../../icons/X/index.js'
|
||||
import { useAuth } from '../../../providers/Auth/index.js'
|
||||
@@ -22,7 +24,7 @@ import { useTranslation } from '../../../providers/Translation/index.js'
|
||||
import { abortAndIgnore, handleAbortRef } from '../../../utilities/abortAndIgnore.js'
|
||||
import { FieldSelect } from '../../FieldSelect/index.js'
|
||||
import { useFormsManager } from '../FormsManager/index.js'
|
||||
import { baseClass, type EditManyBulkUploadsProps } from './index.js'
|
||||
import { baseClass } from './index.js'
|
||||
import './index.scss'
|
||||
import '../../../forms/RenderFields/index.scss'
|
||||
|
||||
@@ -169,23 +171,25 @@ export const EditManyBulkUploadsDrawerContent: React.FC<
|
||||
/>
|
||||
{selectedFields.length === 0 ? null : (
|
||||
<div className="render-fields">
|
||||
{selectedFields.map((option, i) => {
|
||||
const {
|
||||
value: { field, fieldPermissions, path },
|
||||
} = option
|
||||
<FieldPathContext value={undefined}>
|
||||
{selectedFields.map((option, i) => {
|
||||
const {
|
||||
value: { field, fieldPermissions, path },
|
||||
} = option
|
||||
|
||||
return (
|
||||
<RenderField
|
||||
clientFieldConfig={field}
|
||||
indexPath=""
|
||||
key={`${path}-${i}`}
|
||||
parentPath=""
|
||||
parentSchemaPath=""
|
||||
path={path}
|
||||
permissions={fieldPermissions}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
return (
|
||||
<RenderField
|
||||
clientFieldConfig={field}
|
||||
indexPath=""
|
||||
key={`${path}-${i}`}
|
||||
parentPath=""
|
||||
parentSchemaPath=""
|
||||
path={path}
|
||||
permissions={fieldPermissions}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</FieldPathContext>
|
||||
</div>
|
||||
)}
|
||||
<div className={`${baseClass}__sidebar-wrap`}>
|
||||
|
||||
@@ -118,7 +118,7 @@ export function FormsManagerProvider({ children }: FormsManagerProps) {
|
||||
|
||||
const { toggleLoadingOverlay } = useLoadingOverlay()
|
||||
const { closeModal } = useModal()
|
||||
const { collectionSlug, drawerSlug, initialFiles, onSuccess } = useBulkUpload()
|
||||
const { collectionSlug, drawerSlug, initialFiles, onSuccess, setInitialFiles } = useBulkUpload()
|
||||
|
||||
const [isUploading, setIsUploading] = React.useState(false)
|
||||
const [loadingText, setLoadingText] = React.useState('')
|
||||
@@ -366,13 +366,10 @@ export function FormsManagerProvider({ children }: FormsManagerProps) {
|
||||
setIsUploading(false)
|
||||
|
||||
const remainingForms = []
|
||||
const thumbnailIndexesToRemove = []
|
||||
|
||||
currentForms.forEach(({ errorCount }, i) => {
|
||||
if (errorCount) {
|
||||
remainingForms.push(currentForms[i])
|
||||
} else {
|
||||
thumbnailIndexesToRemove.push(i)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -401,8 +398,13 @@ export function FormsManagerProvider({ children }: FormsManagerProps) {
|
||||
totalErrorCount: remainingForms.reduce((acc, { errorCount }) => acc + errorCount, 0),
|
||||
},
|
||||
})
|
||||
|
||||
if (remainingForms.length === 0) {
|
||||
setInitialFiles(undefined)
|
||||
}
|
||||
},
|
||||
[
|
||||
setInitialFiles,
|
||||
actionURL,
|
||||
collectionSlug,
|
||||
getUploadHandler,
|
||||
|
||||
@@ -8,6 +8,7 @@ import React from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { useConfig } from '../../providers/Config/index.js'
|
||||
import { EditDepthProvider } from '../../providers/EditDepth/index.js'
|
||||
import { useTranslation } from '../../providers/Translation/index.js'
|
||||
import { UploadControlsProvider } from '../../providers/UploadControls/index.js'
|
||||
import { Drawer, useDrawerDepth } from '../Drawer/index.js'
|
||||
@@ -77,7 +78,9 @@ export function BulkUploadDrawer() {
|
||||
<Drawer gutter={false} Header={null} slug={drawerSlug}>
|
||||
<FormsManagerProvider>
|
||||
<UploadControlsProvider>
|
||||
<DrawerContent />
|
||||
<EditDepthProvider>
|
||||
<DrawerContent />
|
||||
</EditDepthProvider>
|
||||
</UploadControlsProvider>
|
||||
</FormsManagerProvider>
|
||||
</Drawer>
|
||||
@@ -86,61 +89,57 @@ export function BulkUploadDrawer() {
|
||||
|
||||
type BulkUploadContext = {
|
||||
collectionSlug: string
|
||||
currentActivePath: string
|
||||
drawerSlug: string
|
||||
initialFiles: FileList
|
||||
maxFiles: number
|
||||
onCancel: () => void
|
||||
onSuccess: (newDocs: JsonObject[], errorCount: number) => void
|
||||
setCollectionSlug: (slug: string) => void
|
||||
setCurrentActivePath: (path: string) => void
|
||||
setInitialFiles: (files: FileList) => void
|
||||
setMaxFiles: (maxFiles: number) => void
|
||||
setOnCancel: (onCancel: BulkUploadContext['onCancel']) => void
|
||||
setOnSuccess: (path: string, onSuccess: BulkUploadContext['onSuccess']) => void
|
||||
setOnSuccess: (onSuccess: BulkUploadContext['onSuccess']) => void
|
||||
}
|
||||
|
||||
const Context = React.createContext<BulkUploadContext>({
|
||||
collectionSlug: '',
|
||||
currentActivePath: undefined,
|
||||
drawerSlug: '',
|
||||
initialFiles: undefined,
|
||||
maxFiles: undefined,
|
||||
onCancel: () => null,
|
||||
onSuccess: () => null,
|
||||
setCollectionSlug: () => null,
|
||||
setCurrentActivePath: () => null,
|
||||
setInitialFiles: () => null,
|
||||
setMaxFiles: () => null,
|
||||
setOnCancel: () => null,
|
||||
setOnSuccess: () => null,
|
||||
})
|
||||
export function BulkUploadProvider({ children }: { readonly children: React.ReactNode }) {
|
||||
export function BulkUploadProvider({
|
||||
children,
|
||||
drawerSlugPrefix,
|
||||
}: {
|
||||
readonly children: React.ReactNode
|
||||
readonly drawerSlugPrefix?: string
|
||||
}) {
|
||||
const [collection, setCollection] = React.useState<string>()
|
||||
const [onSuccessFunctionMap, setOnSuccessFunctionMap] =
|
||||
React.useState<Record<string, BulkUploadContext['onSuccess']>>()
|
||||
const [onSuccessFunction, setOnSuccessFunction] = React.useState<BulkUploadContext['onSuccess']>()
|
||||
const [onCancelFunction, setOnCancelFunction] = React.useState<BulkUploadContext['onCancel']>()
|
||||
const [initialFiles, setInitialFiles] = React.useState<FileList>(undefined)
|
||||
const [maxFiles, setMaxFiles] = React.useState<number>(undefined)
|
||||
const [currentActivePath, setCurrentActivePath] = React.useState<string>(undefined)
|
||||
const drawerSlug = useBulkUploadDrawerSlug()
|
||||
const drawerSlug = `${drawerSlugPrefix ? `${drawerSlugPrefix}-` : ''}${useBulkUploadDrawerSlug()}`
|
||||
|
||||
const setCollectionSlug: BulkUploadContext['setCollectionSlug'] = (slug) => {
|
||||
setCollection(slug)
|
||||
}
|
||||
|
||||
const setOnSuccess: BulkUploadContext['setOnSuccess'] = React.useCallback((path, onSuccess) => {
|
||||
setOnSuccessFunctionMap((prev) => ({
|
||||
...prev,
|
||||
[path]: onSuccess,
|
||||
}))
|
||||
}, [])
|
||||
const setOnSuccess: BulkUploadContext['setOnSuccess'] = (onSuccess) => {
|
||||
setOnSuccessFunction(() => onSuccess)
|
||||
}
|
||||
|
||||
return (
|
||||
<Context
|
||||
value={{
|
||||
collectionSlug: collection,
|
||||
currentActivePath,
|
||||
drawerSlug,
|
||||
initialFiles,
|
||||
maxFiles,
|
||||
@@ -150,13 +149,11 @@ export function BulkUploadProvider({ children }: { readonly children: React.Reac
|
||||
}
|
||||
},
|
||||
onSuccess: (docIDs, errorCount) => {
|
||||
if (onSuccessFunctionMap && Object.hasOwn(onSuccessFunctionMap, currentActivePath)) {
|
||||
const onSuccessFunction = onSuccessFunctionMap[currentActivePath]
|
||||
if (typeof onSuccessFunction === 'function') {
|
||||
onSuccessFunction(docIDs, errorCount)
|
||||
}
|
||||
},
|
||||
setCollectionSlug,
|
||||
setCurrentActivePath,
|
||||
setInitialFiles,
|
||||
setMaxFiles,
|
||||
setOnCancel: setOnCancelFunction,
|
||||
|
||||
@@ -42,14 +42,16 @@ export const DocumentDrawerContent: React.FC<DocumentDrawerProps> = ({
|
||||
|
||||
const [DocumentView, setDocumentView] = useState<React.ReactNode>(undefined)
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const hasRenderedDocument = useRef(false)
|
||||
const hasInitialized = useRef(false)
|
||||
|
||||
const getDocumentView = useCallback(
|
||||
(docID?: number | string) => {
|
||||
(docID?: number | string, showLoadingIndicator: boolean = false) => {
|
||||
const controller = handleAbortRef(abortGetDocumentViewRef)
|
||||
|
||||
const fetchDocumentView = async () => {
|
||||
setIsLoading(true)
|
||||
if (showLoadingIndicator) {
|
||||
setIsLoading(true)
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await renderDocument({
|
||||
@@ -141,13 +143,13 @@ export const DocumentDrawerContent: React.FC<DocumentDrawerProps> = ({
|
||||
)
|
||||
|
||||
const clearDoc = useCallback(() => {
|
||||
getDocumentView()
|
||||
getDocumentView(undefined, true)
|
||||
}, [getDocumentView])
|
||||
|
||||
useEffect(() => {
|
||||
if (!DocumentView && !hasRenderedDocument.current) {
|
||||
getDocumentView(existingDocID)
|
||||
hasRenderedDocument.current = true
|
||||
if (!DocumentView && !hasInitialized.current) {
|
||||
getDocumentView(existingDocID, true)
|
||||
hasInitialized.current = true
|
||||
}
|
||||
}, [DocumentView, getDocumentView, existingDocID])
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ export const StaticFileDetails: React.FC<StaticFileDetailsProps> = (props) => {
|
||||
width={width as number}
|
||||
/>
|
||||
|
||||
{(enableAdjustments || customUploadActions) && (
|
||||
{(enableAdjustments || (hasImageSizes && doc.filename) || customUploadActions) && (
|
||||
<UploadActions
|
||||
customActions={customUploadActions}
|
||||
enableAdjustments={Boolean(enableAdjustments)}
|
||||
|
||||
@@ -27,12 +27,7 @@ export function ListBulkUploadButton({
|
||||
*/
|
||||
openBulkUpload?: () => void
|
||||
}) {
|
||||
const {
|
||||
drawerSlug: bulkUploadDrawerSlug,
|
||||
setCollectionSlug,
|
||||
setCurrentActivePath,
|
||||
setOnSuccess,
|
||||
} = useBulkUpload()
|
||||
const { drawerSlug: bulkUploadDrawerSlug, setCollectionSlug, setOnSuccess } = useBulkUpload()
|
||||
const { t } = useTranslation()
|
||||
const { openModal } = useModal()
|
||||
const router = useRouter()
|
||||
@@ -42,9 +37,8 @@ export function ListBulkUploadButton({
|
||||
openBulkUploadFromProps()
|
||||
} else {
|
||||
setCollectionSlug(collectionSlug)
|
||||
setCurrentActivePath(collectionSlug)
|
||||
openModal(bulkUploadDrawerSlug)
|
||||
setOnSuccess(collectionSlug, () => {
|
||||
setOnSuccess(() => {
|
||||
if (typeof onBulkUploadSuccess === 'function') {
|
||||
onBulkUploadSuccess()
|
||||
} else {
|
||||
@@ -58,7 +52,6 @@ export function ListBulkUploadButton({
|
||||
bulkUploadDrawerSlug,
|
||||
openModal,
|
||||
setCollectionSlug,
|
||||
setCurrentActivePath,
|
||||
setOnSuccess,
|
||||
onBulkUploadSuccess,
|
||||
openBulkUploadFromProps,
|
||||
|
||||
@@ -304,15 +304,16 @@ export const Upload_v4: React.FC<UploadProps_v4> = (props) => {
|
||||
setUploadStatus('failed')
|
||||
}
|
||||
}, [
|
||||
fileUrl,
|
||||
uploadConfig,
|
||||
setUploadStatus,
|
||||
handleFileChange,
|
||||
useServerSideFetch,
|
||||
api,
|
||||
collectionSlug,
|
||||
fileUrl,
|
||||
handleFileChange,
|
||||
id,
|
||||
serverURL,
|
||||
api,
|
||||
setUploadStatus,
|
||||
uploadConfig,
|
||||
uploadControlFileName,
|
||||
useServerSideFetch,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -185,7 +185,7 @@ export const WhereBuilder: React.FC<WhereBuilderProps> = (props) => {
|
||||
onClick={async () => {
|
||||
await addCondition({
|
||||
andIndex: 0,
|
||||
field: reducedFields[0],
|
||||
field: reducedFields.find((field) => !field.field.admin?.disableListFilter),
|
||||
orIndex: conditions.length,
|
||||
relation: 'or',
|
||||
})
|
||||
|
||||
@@ -779,7 +779,7 @@ export const RelationshipInput: React.FC<RelationshipInputProps> = (props) => {
|
||||
// and when the devtools are closed. Temporary solution, we can probably do better.
|
||||
setTimeout(() => {
|
||||
openListDrawer()
|
||||
}, 50)
|
||||
}, 100)
|
||||
} else if (appearance === 'select') {
|
||||
setMenuIsOpen(true)
|
||||
if (!hasLoadedFirstPageRef.current) {
|
||||
|
||||
@@ -118,14 +118,8 @@ export function UploadInput(props: UploadInputProps) {
|
||||
)
|
||||
|
||||
const { openModal } = useModal()
|
||||
const {
|
||||
drawerSlug,
|
||||
setCollectionSlug,
|
||||
setCurrentActivePath,
|
||||
setInitialFiles,
|
||||
setMaxFiles,
|
||||
setOnSuccess,
|
||||
} = useBulkUpload()
|
||||
const { drawerSlug, setCollectionSlug, setInitialFiles, setMaxFiles, setOnSuccess } =
|
||||
useBulkUpload()
|
||||
const { permissions } = useAuth()
|
||||
const { code } = useLocale()
|
||||
const { i18n, t } = useTranslation()
|
||||
@@ -294,7 +288,6 @@ export function UploadInput(props: UploadInputProps) {
|
||||
if (typeof maxRows === 'number') {
|
||||
setMaxFiles(maxRows)
|
||||
}
|
||||
setCurrentActivePath(path)
|
||||
openModal(drawerSlug)
|
||||
},
|
||||
[
|
||||
@@ -306,8 +299,6 @@ export function UploadInput(props: UploadInputProps) {
|
||||
setInitialFiles,
|
||||
maxRows,
|
||||
setMaxFiles,
|
||||
path,
|
||||
setCurrentActivePath,
|
||||
],
|
||||
)
|
||||
|
||||
@@ -461,7 +452,7 @@ export function UploadInput(props: UploadInputProps) {
|
||||
}, [populateDocs, activeRelationTo, value])
|
||||
|
||||
useEffect(() => {
|
||||
setOnSuccess(path, onUploadSuccess)
|
||||
setOnSuccess(onUploadSuccess)
|
||||
}, [value, path, onUploadSuccess, setOnSuccess])
|
||||
|
||||
const showDropzone =
|
||||
|
||||
@@ -4,12 +4,13 @@ import type { UploadFieldClientProps } from 'payload'
|
||||
|
||||
import React, { useMemo } from 'react'
|
||||
|
||||
import { BulkUploadProvider } from '../../elements/BulkUpload/index.js'
|
||||
import { useField } from '../../forms/useField/index.js'
|
||||
import { withCondition } from '../../forms/withCondition/index.js'
|
||||
import { useConfig } from '../../providers/Config/index.js'
|
||||
import './index.scss'
|
||||
import { mergeFieldStyles } from '../mergeFieldStyles.js'
|
||||
import { UploadInput } from './Input.js'
|
||||
import './index.scss'
|
||||
|
||||
export { UploadInput } from './Input.js'
|
||||
export type { UploadInputProps } from './Input.js'
|
||||
@@ -62,33 +63,35 @@ export function UploadComponent(props: UploadFieldClientProps) {
|
||||
const styles = useMemo(() => mergeFieldStyles(field), [field])
|
||||
|
||||
return (
|
||||
<UploadInput
|
||||
AfterInput={AfterInput}
|
||||
allowCreate={allowCreate !== false}
|
||||
api={config.routes.api}
|
||||
BeforeInput={BeforeInput}
|
||||
className={className}
|
||||
Description={Description}
|
||||
description={description}
|
||||
displayPreview={displayPreview}
|
||||
Error={Error}
|
||||
filterOptions={filterOptions}
|
||||
hasMany={hasMany}
|
||||
isSortable={isSortable}
|
||||
label={label}
|
||||
Label={Label}
|
||||
localized={localized}
|
||||
maxRows={maxRows}
|
||||
onChange={setValue}
|
||||
path={path}
|
||||
readOnly={readOnly || disabled}
|
||||
relationTo={relationTo}
|
||||
required={required}
|
||||
serverURL={config.serverURL}
|
||||
showError={showError}
|
||||
style={styles}
|
||||
value={value}
|
||||
/>
|
||||
<BulkUploadProvider drawerSlugPrefix={pathFromProps}>
|
||||
<UploadInput
|
||||
AfterInput={AfterInput}
|
||||
allowCreate={allowCreate !== false}
|
||||
api={config.routes.api}
|
||||
BeforeInput={BeforeInput}
|
||||
className={className}
|
||||
Description={Description}
|
||||
description={description}
|
||||
displayPreview={displayPreview}
|
||||
Error={Error}
|
||||
filterOptions={filterOptions}
|
||||
hasMany={hasMany}
|
||||
isSortable={isSortable}
|
||||
label={label}
|
||||
Label={Label}
|
||||
localized={localized}
|
||||
maxRows={maxRows}
|
||||
onChange={setValue}
|
||||
path={path}
|
||||
readOnly={readOnly || disabled}
|
||||
relationTo={relationTo}
|
||||
required={required}
|
||||
serverURL={config.serverURL}
|
||||
showError={showError}
|
||||
style={styles}
|
||||
value={value}
|
||||
/>
|
||||
</BulkUploadProvider>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -96,31 +96,10 @@ export const useField = <TValue,>(options?: Options): FieldType<TValue> => {
|
||||
})
|
||||
|
||||
if (!disableModifyingForm) {
|
||||
if (typeof setModified === 'function') {
|
||||
// Only update setModified to true if the form is not already set to modified. Otherwise the following could happen:
|
||||
// 1. Text field: someone types in it in an unmodified form
|
||||
// 2. After setTimeout triggers setModified(true): form is set to modified. Save Button becomes available. Good!
|
||||
// 3. Type something in text field
|
||||
// 4. Click on save button before setTimeout in useField has finished (so setModified(true) has not been run yet)
|
||||
// 5. Form is saved, setModified(false) is set in the Form/index.tsx `submit` function, "saved successfully" toast appears
|
||||
// 6. setModified(true) inside the timeout is run, form is set to modified again, even though it was already saved and thus set to unmodified. Bad! This should have happened before the form is saved. Now the form should be unmodified and stay that way
|
||||
// until a NEW change happens. Due to this, the "Leave without saving" modal appears even though it should not when leaving the page fast immediately after saving the document.
|
||||
// This is only an issue for forms which have already been set to modified true, as that causes the save button to be enabled. If we prevent this setTimeout to be run
|
||||
// for already-modified forms first place (which is unnecessary), we can avoid this issue. As for unmodified forms, this race issue will not happen, because you cannot click the save button faster
|
||||
// than the timeout in useField is run. That's because the save button won't even be enabled for clicking until the setTimeout in useField has run.
|
||||
// This fixes e2e test flakes, as e2e tests were often so fast that they were saving the form before the timeout in useField has run.
|
||||
// Specifically, this fixes the 'should not warn about unsaved changes when navigating to lexical editor with blocks node and then leaving the page after making a change and saving' lexical e2e test.
|
||||
if (modified === false) {
|
||||
// Update modified state after field value comes back
|
||||
// to avoid cursor jump caused by state value / DOM mismatch
|
||||
setTimeout(() => {
|
||||
setModified(true)
|
||||
}, 10)
|
||||
}
|
||||
}
|
||||
setModified(true)
|
||||
}
|
||||
},
|
||||
[setModified, path, dispatchField, disableFormData, hasRows, modified],
|
||||
[setModified, path, dispatchField, disableFormData, hasRows],
|
||||
)
|
||||
|
||||
// Store result from hook as ref
|
||||
|
||||
28
packages/ui/src/hooks/useControllableState.ts
Normal file
28
packages/ui/src/hooks/useControllableState.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
|
||||
/**
|
||||
* A hook for managing state that can be controlled by props but also overridden locally.
|
||||
* Props always take precedence if they change, but local state can override them temporarily.
|
||||
*/
|
||||
export function useControllableState<T>(
|
||||
propValue: T,
|
||||
defaultValue?: T,
|
||||
): [T, (value: ((prev: T) => T) | T) => void] {
|
||||
const [localValue, setLocalValue] = useState<T>(propValue ?? defaultValue)
|
||||
const initialRenderRef = useRef(true)
|
||||
|
||||
useEffect(() => {
|
||||
if (initialRenderRef.current) {
|
||||
initialRenderRef.current = false
|
||||
return
|
||||
}
|
||||
|
||||
setLocalValue(propValue)
|
||||
}, [propValue])
|
||||
|
||||
const setValue = useCallback((value: ((prev: T) => T) | T) => {
|
||||
setLocalValue(value)
|
||||
}, [])
|
||||
|
||||
return [localValue, setValue]
|
||||
}
|
||||
@@ -14,7 +14,7 @@ type QueuedTaskOptions = {
|
||||
* Can also be used to perform side effects before processing the queue
|
||||
* @returns {boolean} If `false`, the queue will not process
|
||||
*/
|
||||
beforeProcess?: () => boolean
|
||||
beforeProcess?: () => boolean | void
|
||||
}
|
||||
|
||||
type QueueTask = (fn: QueuedFunction, options?: QueuedTaskOptions) => void
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
'use client'
|
||||
import type { ClientUser, DocumentPreferences, SanitizedDocumentPermissions } from 'payload'
|
||||
import type { ClientUser, DocumentPreferences } from 'payload'
|
||||
|
||||
import * as qs from 'qs-esm'
|
||||
import React, { createContext, use, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
|
||||
import { useControllableState } from '../../hooks/useControllableState.js'
|
||||
import { useAuth } from '../../providers/Auth/index.js'
|
||||
import { requests } from '../../utilities/api.js'
|
||||
import { formatDocTitle } from '../../utilities/formatDocTitle/index.js'
|
||||
@@ -45,12 +46,11 @@ const DocumentInfo: React.FC<
|
||||
versionCount: versionCountFromProps,
|
||||
} = props
|
||||
|
||||
const [docPermissions, setDocPermissions] =
|
||||
useState<SanitizedDocumentPermissions>(docPermissionsFromProps)
|
||||
const [docPermissions, setDocPermissions] = useControllableState(docPermissionsFromProps)
|
||||
|
||||
const [hasSavePermission, setHasSavePermission] = useState<boolean>(hasSavePermissionFromProps)
|
||||
const [hasSavePermission, setHasSavePermission] = useControllableState(hasSavePermissionFromProps)
|
||||
|
||||
const [hasPublishPermission, setHasPublishPermission] = useState<boolean>(
|
||||
const [hasPublishPermission, setHasPublishPermission] = useControllableState(
|
||||
hasPublishPermissionFromProps,
|
||||
)
|
||||
|
||||
@@ -101,15 +101,24 @@ const DocumentInfo: React.FC<
|
||||
unpublishedVersionCountFromProps,
|
||||
)
|
||||
|
||||
const [documentIsLocked, setDocumentIsLocked] = useState<boolean | undefined>(isLockedFromProps)
|
||||
const [currentEditor, setCurrentEditor] = useState<ClientUser | null>(currentEditorFromProps)
|
||||
const [lastUpdateTime, setLastUpdateTime] = useState<number>(lastUpdateTimeFromProps)
|
||||
const [savedDocumentData, setSavedDocumentData] = useState(initialData)
|
||||
const [uploadStatus, setUploadStatus] = useState<'failed' | 'idle' | 'uploading'>('idle')
|
||||
const [documentIsLocked, setDocumentIsLocked] = useControllableState<boolean | undefined>(
|
||||
isLockedFromProps,
|
||||
)
|
||||
const [currentEditor, setCurrentEditor] = useControllableState<ClientUser | null>(
|
||||
currentEditorFromProps,
|
||||
)
|
||||
const [lastUpdateTime, setLastUpdateTime] = useControllableState<number>(lastUpdateTimeFromProps)
|
||||
const [savedDocumentData, setSavedDocumentData] = useControllableState(initialData)
|
||||
const [uploadStatus, setUploadStatus] = useControllableState<'failed' | 'idle' | 'uploading'>(
|
||||
'idle',
|
||||
)
|
||||
|
||||
const updateUploadStatus = useCallback((status: 'failed' | 'idle' | 'uploading') => {
|
||||
setUploadStatus(status)
|
||||
}, [])
|
||||
const updateUploadStatus = useCallback(
|
||||
(status: 'failed' | 'idle' | 'uploading') => {
|
||||
setUploadStatus(status)
|
||||
},
|
||||
[setUploadStatus],
|
||||
)
|
||||
|
||||
const { getPreference, setPreference } = usePreferences()
|
||||
const { code: locale } = useLocale()
|
||||
@@ -170,7 +179,7 @@ const DocumentInfo: React.FC<
|
||||
console.error('Failed to unlock the document', error)
|
||||
}
|
||||
},
|
||||
[serverURL, api, globalSlug],
|
||||
[serverURL, api, globalSlug, setDocumentIsLocked],
|
||||
)
|
||||
|
||||
const updateDocumentEditor = useCallback(
|
||||
@@ -279,7 +288,7 @@ const DocumentInfo: React.FC<
|
||||
(json) => {
|
||||
setSavedDocumentData(json)
|
||||
},
|
||||
[],
|
||||
[setSavedDocumentData],
|
||||
)
|
||||
|
||||
/**
|
||||
|
||||
@@ -226,6 +226,13 @@ export const LivePreviewProvider: React.FC<LivePreviewProviderProps> = ({
|
||||
)
|
||||
}, [isLivePreviewing, setPreference, collectionSlug, globalSlug])
|
||||
|
||||
const isLivePreviewEnabled = Boolean(
|
||||
operation !== 'create' &&
|
||||
((collectionSlug && config?.admin?.livePreview?.collections?.includes(collectionSlug)) ||
|
||||
(globalSlug && config.admin?.livePreview?.globals?.includes(globalSlug)) ||
|
||||
entityConfig?.admin?.livePreview),
|
||||
)
|
||||
|
||||
return (
|
||||
<LivePreviewContext
|
||||
value={{
|
||||
@@ -235,13 +242,7 @@ export const LivePreviewProvider: React.FC<LivePreviewProviderProps> = ({
|
||||
fieldSchemaJSON,
|
||||
iframeHasLoaded,
|
||||
iframeRef,
|
||||
isLivePreviewEnabled: Boolean(
|
||||
(operation !== 'create' &&
|
||||
collectionSlug &&
|
||||
config?.admin?.livePreview?.collections?.includes(collectionSlug)) ||
|
||||
(globalSlug && config.admin?.livePreview?.globals?.includes(globalSlug)) ||
|
||||
entityConfig?.admin?.livePreview,
|
||||
),
|
||||
isLivePreviewEnabled,
|
||||
isLivePreviewing,
|
||||
isPopupOpen,
|
||||
listeningForMessages,
|
||||
|
||||
@@ -69,12 +69,13 @@ export const Auth: React.FC<Props> = (props) => {
|
||||
})
|
||||
|
||||
if (operation === 'create') {
|
||||
showPasswordFields = typeof passwordPermissions === 'object' && passwordPermissions.create
|
||||
showPasswordFields =
|
||||
passwordPermissions === true ||
|
||||
(typeof passwordPermissions === 'object' && passwordPermissions.create)
|
||||
} else {
|
||||
showPasswordFields =
|
||||
typeof passwordPermissions === 'object' &&
|
||||
passwordPermissions.read &&
|
||||
passwordPermissions.update
|
||||
passwordPermissions === true ||
|
||||
(typeof passwordPermissions === 'object' && passwordPermissions.update)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -86,12 +86,7 @@ export function DefaultListView(props: ListViewClientProps) {
|
||||
} = useListQuery()
|
||||
|
||||
const { openModal } = useModal()
|
||||
const {
|
||||
drawerSlug: bulkUploadDrawerSlug,
|
||||
setCollectionSlug,
|
||||
setCurrentActivePath,
|
||||
setOnSuccess,
|
||||
} = useBulkUpload()
|
||||
const { drawerSlug: bulkUploadDrawerSlug, setCollectionSlug, setOnSuccess } = useBulkUpload()
|
||||
|
||||
const collectionConfig = getEntityConfig({ collectionSlug })
|
||||
|
||||
@@ -124,18 +119,9 @@ export function DefaultListView(props: ListViewClientProps) {
|
||||
|
||||
const openBulkUpload = React.useCallback(() => {
|
||||
setCollectionSlug(collectionSlug)
|
||||
setCurrentActivePath(collectionSlug)
|
||||
openModal(bulkUploadDrawerSlug)
|
||||
setOnSuccess(collectionSlug, () => router.refresh())
|
||||
}, [
|
||||
router,
|
||||
collectionSlug,
|
||||
bulkUploadDrawerSlug,
|
||||
openModal,
|
||||
setCollectionSlug,
|
||||
setCurrentActivePath,
|
||||
setOnSuccess,
|
||||
])
|
||||
setOnSuccess(() => router.refresh())
|
||||
}, [router, collectionSlug, bulkUploadDrawerSlug, openModal, setCollectionSlug, setOnSuccess])
|
||||
|
||||
useEffect(() => {
|
||||
if (!isInDrawer) {
|
||||
|
||||
322
pnpm-lock.yaml
generated
322
pnpm-lock.yaml
generated
@@ -44,7 +44,7 @@ importers:
|
||||
version: 1.50.0
|
||||
'@sentry/nextjs':
|
||||
specifier: ^8.33.1
|
||||
version: 8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.3.2(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(react@19.1.0)(webpack@5.96.1(@swc/core@1.11.29)(esbuild@0.25.5))
|
||||
version: 8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.3.2(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(react@19.1.0)(webpack@5.96.1(@swc/core@1.11.29))
|
||||
'@sentry/node':
|
||||
specifier: ^8.33.1
|
||||
version: 8.37.1
|
||||
@@ -97,11 +97,11 @@ importers:
|
||||
specifier: 16.4.7
|
||||
version: 16.4.7
|
||||
drizzle-kit:
|
||||
specifier: 0.31.0
|
||||
version: 0.31.0
|
||||
specifier: 0.31.4
|
||||
version: 0.31.4
|
||||
drizzle-orm:
|
||||
specifier: 0.43.1
|
||||
version: 0.43.1(@libsql/client@0.14.0(bufferutil@4.0.8))(@opentelemetry/api@1.9.0)(@types/pg@8.11.6)(@vercel/postgres@0.9.0)(pg@8.11.3)
|
||||
specifier: 0.44.2
|
||||
version: 0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8))(@opentelemetry/api@1.9.0)(@types/pg@8.11.6)(@vercel/postgres@0.9.0)(gel@2.0.1)(pg@8.16.3)
|
||||
escape-html:
|
||||
specifier: ^1.0.3
|
||||
version: 1.0.3
|
||||
@@ -142,8 +142,8 @@ importers:
|
||||
specifier: ^5.0.0
|
||||
version: 5.0.0
|
||||
pg:
|
||||
specifier: 8.11.3
|
||||
version: 8.11.3
|
||||
specifier: 8.16.3
|
||||
version: 8.16.3
|
||||
playwright:
|
||||
specifier: 1.50.0
|
||||
version: 1.50.0
|
||||
@@ -319,14 +319,14 @@ importers:
|
||||
specifier: 2.12.1
|
||||
version: 2.12.1
|
||||
drizzle-kit:
|
||||
specifier: 0.31.1
|
||||
version: 0.31.1
|
||||
specifier: 0.31.4
|
||||
version: 0.31.4
|
||||
drizzle-orm:
|
||||
specifier: 0.44.2
|
||||
version: 0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@vercel/postgres@0.9.0)(pg@8.11.3)
|
||||
version: 0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@vercel/postgres@0.9.0)(gel@2.0.1)(pg@8.16.3)
|
||||
pg:
|
||||
specifier: 8.11.3
|
||||
version: 8.11.3
|
||||
specifier: 8.16.3
|
||||
version: 8.16.3
|
||||
prompts:
|
||||
specifier: 2.4.2
|
||||
version: 2.4.2
|
||||
@@ -365,11 +365,11 @@ importers:
|
||||
specifier: 2.12.1
|
||||
version: 2.12.1
|
||||
drizzle-kit:
|
||||
specifier: 0.31.1
|
||||
version: 0.31.1
|
||||
specifier: 0.31.4
|
||||
version: 0.31.4
|
||||
drizzle-orm:
|
||||
specifier: 0.44.2
|
||||
version: 0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@vercel/postgres@0.9.0)(pg@8.11.3)
|
||||
version: 0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@vercel/postgres@0.9.0)(gel@2.0.1)(pg@8.16.3)
|
||||
prompts:
|
||||
specifier: 2.4.2
|
||||
version: 2.4.2
|
||||
@@ -408,14 +408,14 @@ importers:
|
||||
specifier: 2.12.1
|
||||
version: 2.12.1
|
||||
drizzle-kit:
|
||||
specifier: 0.31.1
|
||||
version: 0.31.1
|
||||
specifier: 0.31.4
|
||||
version: 0.31.4
|
||||
drizzle-orm:
|
||||
specifier: 0.44.2
|
||||
version: 0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@vercel/postgres@0.9.0)(pg@8.11.3)
|
||||
version: 0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@vercel/postgres@0.9.0)(gel@2.0.1)(pg@8.16.3)
|
||||
pg:
|
||||
specifier: 8.11.3
|
||||
version: 8.11.3
|
||||
specifier: 8.16.3
|
||||
version: 8.16.3
|
||||
prompts:
|
||||
specifier: 2.4.2
|
||||
version: 2.4.2
|
||||
@@ -455,7 +455,7 @@ importers:
|
||||
version: 2.0.3
|
||||
drizzle-orm:
|
||||
specifier: 0.44.2
|
||||
version: 0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@vercel/postgres@0.9.0)(pg@8.11.3)
|
||||
version: 0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@vercel/postgres@0.9.0)(gel@2.0.1)(pg@8.16.3)
|
||||
prompts:
|
||||
specifier: 2.4.2
|
||||
version: 2.4.2
|
||||
@@ -1143,7 +1143,7 @@ importers:
|
||||
dependencies:
|
||||
'@sentry/nextjs':
|
||||
specifier: ^8.33.1
|
||||
version: 8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(react@19.1.0)(webpack@5.96.1(@swc/core@1.11.29)(esbuild@0.25.5))
|
||||
version: 8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(react@19.1.0)(webpack@5.96.1(@swc/core@1.11.29))
|
||||
'@sentry/types':
|
||||
specifier: ^8.33.1
|
||||
version: 8.37.1
|
||||
@@ -2045,7 +2045,7 @@ importers:
|
||||
version: link:../packages/ui
|
||||
'@sentry/nextjs':
|
||||
specifier: ^8.33.1
|
||||
version: 8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.3.2(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(react@19.1.0)(webpack@5.96.1(@swc/core@1.11.29)(esbuild@0.25.5))
|
||||
version: 8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.3.2(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(react@19.1.0)(webpack@5.96.1(@swc/core@1.11.29))
|
||||
'@sentry/react':
|
||||
specifier: ^7.77.0
|
||||
version: 7.119.2(react@19.1.0)
|
||||
@@ -2077,11 +2077,11 @@ importers:
|
||||
specifier: 16.4.7
|
||||
version: 16.4.7
|
||||
drizzle-kit:
|
||||
specifier: 0.31.1
|
||||
version: 0.31.1
|
||||
specifier: 0.31.4
|
||||
version: 0.31.4
|
||||
drizzle-orm:
|
||||
specifier: 0.44.2
|
||||
version: 0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@opentelemetry/api@1.9.0)(@types/pg@8.11.6)(@vercel/postgres@0.9.0)(pg@8.11.3)
|
||||
version: 0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8))(@opentelemetry/api@1.9.0)(@types/pg@8.11.6)(@vercel/postgres@0.9.0)(gel@2.0.1)(pg@8.16.3)
|
||||
escape-html:
|
||||
specifier: 1.0.3
|
||||
version: 1.0.3
|
||||
@@ -2116,8 +2116,8 @@ importers:
|
||||
specifier: workspace:*
|
||||
version: link:../packages/payload
|
||||
pg:
|
||||
specifier: 8.11.3
|
||||
version: 8.11.3
|
||||
specifier: 8.16.3
|
||||
version: 8.16.3
|
||||
qs-esm:
|
||||
specifier: 7.0.2
|
||||
version: 7.0.2
|
||||
@@ -5040,6 +5040,9 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@petamoriken/float16@3.9.2':
|
||||
resolution: {integrity: sha512-VgffxawQde93xKxT3qap3OH+meZf7VaSB5Sqd4Rqc+FP5alWbpOyan/7tRbOAvynjpG3GpdtAuGU/NdhQpmrog==}
|
||||
|
||||
'@pkgjs/parseargs@0.11.0':
|
||||
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
|
||||
engines: {node: '>=14'}
|
||||
@@ -7057,10 +7060,6 @@ packages:
|
||||
buffer-from@1.1.2:
|
||||
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
||||
|
||||
buffer-writer@2.0.0:
|
||||
resolution: {integrity: sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
buffer@4.9.2:
|
||||
resolution: {integrity: sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==}
|
||||
|
||||
@@ -7651,103 +7650,10 @@ packages:
|
||||
resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
drizzle-kit@0.31.0:
|
||||
resolution: {integrity: sha512-pcKVT+GbfPA+bUovPIilgVOoq+onNBo/YQBG86sf3/GFHkN6lRJPm1l7dKN0IMAk57RQoIm4GUllRrasLlcaSg==}
|
||||
drizzle-kit@0.31.4:
|
||||
resolution: {integrity: sha512-tCPWVZWZqWVx2XUsVpJRnH9Mx0ClVOf5YUHerZ5so1OKSlqww4zy1R5ksEdGRcO3tM3zj0PYN6V48TbQCL1RfA==}
|
||||
hasBin: true
|
||||
|
||||
drizzle-kit@0.31.1:
|
||||
resolution: {integrity: sha512-PUjYKWtzOzPtdtQlTHQG3qfv4Y0XT8+Eas6UbxCmxTj7qgMf+39dDujf1BP1I+qqZtw9uzwTh8jYtkMuCq+B0Q==}
|
||||
hasBin: true
|
||||
|
||||
drizzle-orm@0.43.1:
|
||||
resolution: {integrity: sha512-dUcDaZtE/zN4RV/xqGrVSMpnEczxd5cIaoDeor7Zst9wOe/HzC/7eAaulywWGYXdDEc9oBPMjayVEDg0ziTLJA==}
|
||||
peerDependencies:
|
||||
'@aws-sdk/client-rds-data': '>=3'
|
||||
'@cloudflare/workers-types': '>=4'
|
||||
'@electric-sql/pglite': '>=0.2.0'
|
||||
'@libsql/client': '>=0.10.0'
|
||||
'@libsql/client-wasm': '>=0.10.0'
|
||||
'@neondatabase/serverless': '>=0.10.0'
|
||||
'@op-engineering/op-sqlite': '>=2'
|
||||
'@opentelemetry/api': ^1.4.1
|
||||
'@planetscale/database': '>=1.13'
|
||||
'@prisma/client': '*'
|
||||
'@tidbcloud/serverless': '*'
|
||||
'@types/better-sqlite3': '*'
|
||||
'@types/pg': '*'
|
||||
'@types/sql.js': '*'
|
||||
'@vercel/postgres': '>=0.8.0'
|
||||
'@xata.io/client': '*'
|
||||
better-sqlite3: '>=7'
|
||||
bun-types: '*'
|
||||
expo-sqlite: '>=14.0.0'
|
||||
gel: '>=2'
|
||||
knex: '*'
|
||||
kysely: '*'
|
||||
mysql2: '>=2'
|
||||
pg: '>=8'
|
||||
postgres: '>=3'
|
||||
prisma: '*'
|
||||
sql.js: '>=1'
|
||||
sqlite3: '>=5'
|
||||
peerDependenciesMeta:
|
||||
'@aws-sdk/client-rds-data':
|
||||
optional: true
|
||||
'@cloudflare/workers-types':
|
||||
optional: true
|
||||
'@electric-sql/pglite':
|
||||
optional: true
|
||||
'@libsql/client':
|
||||
optional: true
|
||||
'@libsql/client-wasm':
|
||||
optional: true
|
||||
'@neondatabase/serverless':
|
||||
optional: true
|
||||
'@op-engineering/op-sqlite':
|
||||
optional: true
|
||||
'@opentelemetry/api':
|
||||
optional: true
|
||||
'@planetscale/database':
|
||||
optional: true
|
||||
'@prisma/client':
|
||||
optional: true
|
||||
'@tidbcloud/serverless':
|
||||
optional: true
|
||||
'@types/better-sqlite3':
|
||||
optional: true
|
||||
'@types/pg':
|
||||
optional: true
|
||||
'@types/sql.js':
|
||||
optional: true
|
||||
'@vercel/postgres':
|
||||
optional: true
|
||||
'@xata.io/client':
|
||||
optional: true
|
||||
better-sqlite3:
|
||||
optional: true
|
||||
bun-types:
|
||||
optional: true
|
||||
expo-sqlite:
|
||||
optional: true
|
||||
gel:
|
||||
optional: true
|
||||
knex:
|
||||
optional: true
|
||||
kysely:
|
||||
optional: true
|
||||
mysql2:
|
||||
optional: true
|
||||
pg:
|
||||
optional: true
|
||||
postgres:
|
||||
optional: true
|
||||
prisma:
|
||||
optional: true
|
||||
sql.js:
|
||||
optional: true
|
||||
sqlite3:
|
||||
optional: true
|
||||
|
||||
drizzle-orm@0.44.2:
|
||||
resolution: {integrity: sha512-zGAqBzWWkVSFjZpwPOrmCrgO++1kZ5H/rZ4qTGeGOe18iXGVJWf3WPfHOVwFIbmi8kHjfJstC6rJomzGx8g/dQ==}
|
||||
peerDependencies:
|
||||
@@ -7893,6 +7799,10 @@ packages:
|
||||
resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
|
||||
engines: {node: '>=0.12'}
|
||||
|
||||
env-paths@3.0.0:
|
||||
resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
||||
environment@1.1.0:
|
||||
resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -8574,6 +8484,11 @@ packages:
|
||||
peerDependencies:
|
||||
next: '>=13.2.0'
|
||||
|
||||
gel@2.0.1:
|
||||
resolution: {integrity: sha512-gfem3IGvqKqXwEq7XseBogyaRwGsQGuE7Cw/yQsjLGdgiyqX92G1xENPCE0ltunPGcsJIa6XBOTx/PK169mOqw==}
|
||||
engines: {node: '>= 18.0.0'}
|
||||
hasBin: true
|
||||
|
||||
gensync@1.0.0-beta.2:
|
||||
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -9210,6 +9125,10 @@ packages:
|
||||
isexe@2.0.0:
|
||||
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||
|
||||
isexe@3.1.1:
|
||||
resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
isomorphic-unfetch@3.1.0:
|
||||
resolution: {integrity: sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==}
|
||||
|
||||
@@ -10282,9 +10201,6 @@ packages:
|
||||
package-json-from-dist@1.0.1:
|
||||
resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
|
||||
|
||||
packet-reader@1.0.0:
|
||||
resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==}
|
||||
|
||||
parent-module@1.0.1:
|
||||
resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -10357,11 +10273,11 @@ packages:
|
||||
perfect-debounce@1.0.0:
|
||||
resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
|
||||
|
||||
pg-cloudflare@1.1.1:
|
||||
resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==}
|
||||
pg-cloudflare@1.2.7:
|
||||
resolution: {integrity: sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==}
|
||||
|
||||
pg-connection-string@2.7.0:
|
||||
resolution: {integrity: sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==}
|
||||
pg-connection-string@2.9.1:
|
||||
resolution: {integrity: sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==}
|
||||
|
||||
pg-int8@1.0.1:
|
||||
resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
|
||||
@@ -10371,11 +10287,17 @@ packages:
|
||||
resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
pg-pool@3.7.0:
|
||||
resolution: {integrity: sha512-ZOBQForurqh4zZWjrgSwwAtzJ7QiRX0ovFkZr2klsen3Nm0aoh33Ls0fzfv3imeH/nw/O27cjdz5kzYJfeGp/g==}
|
||||
pg-pool@3.10.1:
|
||||
resolution: {integrity: sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==}
|
||||
peerDependencies:
|
||||
pg: '>=8.0'
|
||||
|
||||
pg-protocol@1.10.2:
|
||||
resolution: {integrity: sha512-Ci7jy8PbaWxfsck2dwZdERcDG2A0MG8JoQILs+uZNjABFuBuItAZCWUNz8sXRDMoui24rJw7WlXqgpMdBSN/vQ==}
|
||||
|
||||
pg-protocol@1.10.3:
|
||||
resolution: {integrity: sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==}
|
||||
|
||||
pg-protocol@1.7.0:
|
||||
resolution: {integrity: sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==}
|
||||
|
||||
@@ -10387,9 +10309,9 @@ packages:
|
||||
resolution: {integrity: sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
pg@8.11.3:
|
||||
resolution: {integrity: sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==}
|
||||
engines: {node: '>= 8.0.0'}
|
||||
pg@8.16.3:
|
||||
resolution: {integrity: sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==}
|
||||
engines: {node: '>= 16.0.0'}
|
||||
peerDependencies:
|
||||
pg-native: '>=3.0.1'
|
||||
peerDependenciesMeta:
|
||||
@@ -11216,6 +11138,10 @@ packages:
|
||||
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
shell-quote@1.8.3:
|
||||
resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
shelljs@0.8.5:
|
||||
resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -12351,6 +12277,11 @@ packages:
|
||||
engines: {node: '>= 8'}
|
||||
hasBin: true
|
||||
|
||||
which@4.0.0:
|
||||
resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==}
|
||||
engines: {node: ^16.13.0 || >=18.0.0}
|
||||
hasBin: true
|
||||
|
||||
why-is-node-running@2.3.0:
|
||||
resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -15972,6 +15903,9 @@ snapshots:
|
||||
'@oxc-resolver/binding-win32-x64-msvc@5.3.0':
|
||||
optional: true
|
||||
|
||||
'@petamoriken/float16@3.9.2':
|
||||
optional: true
|
||||
|
||||
'@pkgjs/parseargs@0.11.0':
|
||||
optional: true
|
||||
|
||||
@@ -16466,7 +16400,7 @@ snapshots:
|
||||
'@sentry/utils': 7.119.2
|
||||
localforage: 1.10.0
|
||||
|
||||
'@sentry/nextjs@8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.3.2(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(react@19.1.0)(webpack@5.96.1(@swc/core@1.11.29)(esbuild@0.25.5))':
|
||||
'@sentry/nextjs@8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.3.2(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(react@19.1.0)(webpack@5.96.1(@swc/core@1.11.29))':
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.9.0
|
||||
'@opentelemetry/instrumentation-http': 0.53.0(@opentelemetry/api@1.9.0)
|
||||
@@ -16480,7 +16414,7 @@ snapshots:
|
||||
'@sentry/types': 8.37.1
|
||||
'@sentry/utils': 8.37.1
|
||||
'@sentry/vercel-edge': 8.37.1
|
||||
'@sentry/webpack-plugin': 2.22.6(webpack@5.96.1(@swc/core@1.11.29)(esbuild@0.25.5))
|
||||
'@sentry/webpack-plugin': 2.22.6(webpack@5.96.1(@swc/core@1.11.29))
|
||||
chalk: 3.0.0
|
||||
next: 15.3.2(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4)
|
||||
resolve: 1.22.8
|
||||
@@ -16495,7 +16429,7 @@ snapshots:
|
||||
- supports-color
|
||||
- webpack
|
||||
|
||||
'@sentry/nextjs@8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(react@19.1.0)(webpack@5.96.1(@swc/core@1.11.29)(esbuild@0.25.5))':
|
||||
'@sentry/nextjs@8.37.1(@opentelemetry/core@1.27.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.54.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.27.0(@opentelemetry/api@1.9.0))(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4))(react@19.1.0)(webpack@5.96.1(@swc/core@1.11.29))':
|
||||
dependencies:
|
||||
'@opentelemetry/api': 1.9.0
|
||||
'@opentelemetry/instrumentation-http': 0.53.0(@opentelemetry/api@1.9.0)
|
||||
@@ -16509,7 +16443,7 @@ snapshots:
|
||||
'@sentry/types': 8.37.1
|
||||
'@sentry/utils': 8.37.1
|
||||
'@sentry/vercel-edge': 8.37.1
|
||||
'@sentry/webpack-plugin': 2.22.6(webpack@5.96.1(@swc/core@1.11.29)(esbuild@0.25.5))
|
||||
'@sentry/webpack-plugin': 2.22.6(webpack@5.96.1(@swc/core@1.11.29))
|
||||
chalk: 3.0.0
|
||||
next: 15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4)
|
||||
resolve: 1.22.8
|
||||
@@ -16619,12 +16553,12 @@ snapshots:
|
||||
'@sentry/types': 8.37.1
|
||||
'@sentry/utils': 8.37.1
|
||||
|
||||
'@sentry/webpack-plugin@2.22.6(webpack@5.96.1(@swc/core@1.11.29)(esbuild@0.25.5))':
|
||||
'@sentry/webpack-plugin@2.22.6(webpack@5.96.1(@swc/core@1.11.29))':
|
||||
dependencies:
|
||||
'@sentry/bundler-plugin-core': 2.22.6
|
||||
unplugin: 1.0.1
|
||||
uuid: 9.0.0
|
||||
webpack: 5.96.1(@swc/core@1.11.29)(esbuild@0.25.5)
|
||||
webpack: 5.96.1(@swc/core@1.11.29)
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
@@ -17481,13 +17415,13 @@ snapshots:
|
||||
'@types/pg@8.11.6':
|
||||
dependencies:
|
||||
'@types/node': 22.15.30
|
||||
pg-protocol: 1.7.0
|
||||
pg-protocol: 1.10.2
|
||||
pg-types: 4.0.2
|
||||
|
||||
'@types/pg@8.6.1':
|
||||
dependencies:
|
||||
'@types/node': 22.15.30
|
||||
pg-protocol: 1.7.0
|
||||
pg-protocol: 1.10.2
|
||||
pg-types: 2.2.0
|
||||
|
||||
'@types/pluralize@0.0.33': {}
|
||||
@@ -18477,8 +18411,6 @@ snapshots:
|
||||
|
||||
buffer-from@1.1.2: {}
|
||||
|
||||
buffer-writer@2.0.0: {}
|
||||
|
||||
buffer@4.9.2:
|
||||
dependencies:
|
||||
base64-js: 1.5.1
|
||||
@@ -19042,7 +18974,7 @@ snapshots:
|
||||
|
||||
dotenv@16.4.7: {}
|
||||
|
||||
drizzle-kit@0.31.0:
|
||||
drizzle-kit@0.31.4:
|
||||
dependencies:
|
||||
'@drizzle-team/brocli': 0.10.2
|
||||
'@esbuild-kit/esm-loader': 2.6.5
|
||||
@@ -19051,38 +18983,23 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
drizzle-kit@0.31.1:
|
||||
dependencies:
|
||||
'@drizzle-team/brocli': 0.10.2
|
||||
'@esbuild-kit/esm-loader': 2.6.5
|
||||
esbuild: 0.25.5
|
||||
esbuild-register: 3.6.0(esbuild@0.25.5)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
drizzle-orm@0.43.1(@libsql/client@0.14.0(bufferutil@4.0.8))(@opentelemetry/api@1.9.0)(@types/pg@8.11.6)(@vercel/postgres@0.9.0)(pg@8.11.3):
|
||||
optionalDependencies:
|
||||
'@libsql/client': 0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5)
|
||||
'@opentelemetry/api': 1.9.0
|
||||
'@types/pg': 8.11.6
|
||||
'@vercel/postgres': 0.9.0
|
||||
pg: 8.11.3
|
||||
|
||||
drizzle-orm@0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@vercel/postgres@0.9.0)(pg@8.11.3):
|
||||
drizzle-orm@0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@vercel/postgres@0.9.0)(gel@2.0.1)(pg@8.16.3):
|
||||
optionalDependencies:
|
||||
'@libsql/client': 0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5)
|
||||
'@opentelemetry/api': 1.9.0
|
||||
'@types/pg': 8.10.2
|
||||
'@vercel/postgres': 0.9.0
|
||||
pg: 8.11.3
|
||||
gel: 2.0.1
|
||||
pg: 8.16.3
|
||||
|
||||
drizzle-orm@0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5))(@opentelemetry/api@1.9.0)(@types/pg@8.11.6)(@vercel/postgres@0.9.0)(pg@8.11.3):
|
||||
drizzle-orm@0.44.2(@libsql/client@0.14.0(bufferutil@4.0.8))(@opentelemetry/api@1.9.0)(@types/pg@8.11.6)(@vercel/postgres@0.9.0)(gel@2.0.1)(pg@8.16.3):
|
||||
optionalDependencies:
|
||||
'@libsql/client': 0.14.0(bufferutil@4.0.8)(utf-8-validate@6.0.5)
|
||||
'@opentelemetry/api': 1.9.0
|
||||
'@types/pg': 8.11.6
|
||||
'@vercel/postgres': 0.9.0
|
||||
pg: 8.11.3
|
||||
gel: 2.0.1
|
||||
pg: 8.16.3
|
||||
|
||||
dunder-proto@1.0.1:
|
||||
dependencies:
|
||||
@@ -19134,6 +19051,9 @@ snapshots:
|
||||
|
||||
entities@6.0.1: {}
|
||||
|
||||
env-paths@3.0.0:
|
||||
optional: true
|
||||
|
||||
environment@1.1.0: {}
|
||||
|
||||
error-ex@1.3.2:
|
||||
@@ -19318,7 +19238,7 @@ snapshots:
|
||||
|
||||
esbuild-register@3.6.0(esbuild@0.25.5):
|
||||
dependencies:
|
||||
debug: 4.3.7
|
||||
debug: 4.4.1
|
||||
esbuild: 0.25.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -20157,6 +20077,18 @@ snapshots:
|
||||
dependencies:
|
||||
next: 15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.50.0)(babel-plugin-macros@3.1.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.77.4)
|
||||
|
||||
gel@2.0.1:
|
||||
dependencies:
|
||||
'@petamoriken/float16': 3.9.2
|
||||
debug: 4.4.1
|
||||
env-paths: 3.0.0
|
||||
semver: 7.7.1
|
||||
shell-quote: 1.8.3
|
||||
which: 4.0.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
optional: true
|
||||
|
||||
gensync@1.0.0-beta.2: {}
|
||||
|
||||
get-caller-file@2.0.5: {}
|
||||
@@ -20801,6 +20733,9 @@ snapshots:
|
||||
|
||||
isexe@2.0.0: {}
|
||||
|
||||
isexe@3.1.1:
|
||||
optional: true
|
||||
|
||||
isomorphic-unfetch@3.1.0:
|
||||
dependencies:
|
||||
node-fetch: 2.7.0
|
||||
@@ -22317,8 +22252,6 @@ snapshots:
|
||||
|
||||
package-json-from-dist@1.0.1: {}
|
||||
|
||||
packet-reader@1.0.0: {}
|
||||
|
||||
parent-module@1.0.1:
|
||||
dependencies:
|
||||
callsites: 3.1.0
|
||||
@@ -22383,18 +22316,22 @@ snapshots:
|
||||
|
||||
perfect-debounce@1.0.0: {}
|
||||
|
||||
pg-cloudflare@1.1.1:
|
||||
pg-cloudflare@1.2.7:
|
||||
optional: true
|
||||
|
||||
pg-connection-string@2.7.0: {}
|
||||
pg-connection-string@2.9.1: {}
|
||||
|
||||
pg-int8@1.0.1: {}
|
||||
|
||||
pg-numeric@1.0.2: {}
|
||||
|
||||
pg-pool@3.7.0(pg@8.11.3):
|
||||
pg-pool@3.10.1(pg@8.16.3):
|
||||
dependencies:
|
||||
pg: 8.11.3
|
||||
pg: 8.16.3
|
||||
|
||||
pg-protocol@1.10.2: {}
|
||||
|
||||
pg-protocol@1.10.3: {}
|
||||
|
||||
pg-protocol@1.7.0: {}
|
||||
|
||||
@@ -22416,17 +22353,15 @@ snapshots:
|
||||
postgres-interval: 3.0.0
|
||||
postgres-range: 1.1.4
|
||||
|
||||
pg@8.11.3:
|
||||
pg@8.16.3:
|
||||
dependencies:
|
||||
buffer-writer: 2.0.0
|
||||
packet-reader: 1.0.0
|
||||
pg-connection-string: 2.7.0
|
||||
pg-pool: 3.7.0(pg@8.11.3)
|
||||
pg-protocol: 1.7.0
|
||||
pg-connection-string: 2.9.1
|
||||
pg-pool: 3.10.1(pg@8.16.3)
|
||||
pg-protocol: 1.10.3
|
||||
pg-types: 2.2.0
|
||||
pgpass: 1.0.5
|
||||
optionalDependencies:
|
||||
pg-cloudflare: 1.1.1
|
||||
pg-cloudflare: 1.2.7
|
||||
|
||||
pgpass@1.0.5:
|
||||
dependencies:
|
||||
@@ -23326,6 +23261,9 @@ snapshots:
|
||||
|
||||
shebang-regex@3.0.0: {}
|
||||
|
||||
shell-quote@1.8.3:
|
||||
optional: true
|
||||
|
||||
shelljs@0.8.5:
|
||||
dependencies:
|
||||
glob: 7.2.3
|
||||
@@ -23901,17 +23839,16 @@ snapshots:
|
||||
ansi-escapes: 4.3.2
|
||||
supports-hyperlinks: 2.3.0
|
||||
|
||||
terser-webpack-plugin@5.3.10(@swc/core@1.11.29)(esbuild@0.25.5)(webpack@5.96.1(@swc/core@1.11.29)(esbuild@0.25.5)):
|
||||
terser-webpack-plugin@5.3.10(@swc/core@1.11.29)(webpack@5.96.1(@swc/core@1.11.29)):
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.25
|
||||
jest-worker: 27.5.1
|
||||
schema-utils: 3.3.0
|
||||
serialize-javascript: 6.0.2
|
||||
terser: 5.36.0
|
||||
webpack: 5.96.1(@swc/core@1.11.29)(esbuild@0.25.5)
|
||||
webpack: 5.96.1(@swc/core@1.11.29)
|
||||
optionalDependencies:
|
||||
'@swc/core': 1.11.29
|
||||
esbuild: 0.25.5
|
||||
|
||||
terser@5.36.0:
|
||||
dependencies:
|
||||
@@ -24618,7 +24555,7 @@ snapshots:
|
||||
|
||||
webpack-virtual-modules@0.5.0: {}
|
||||
|
||||
webpack@5.96.1(@swc/core@1.11.29)(esbuild@0.25.5):
|
||||
webpack@5.96.1(@swc/core@1.11.29):
|
||||
dependencies:
|
||||
'@types/eslint-scope': 3.7.7
|
||||
'@types/estree': 1.0.7
|
||||
@@ -24640,7 +24577,7 @@ snapshots:
|
||||
neo-async: 2.6.2
|
||||
schema-utils: 3.3.0
|
||||
tapable: 2.2.1
|
||||
terser-webpack-plugin: 5.3.10(@swc/core@1.11.29)(esbuild@0.25.5)(webpack@5.96.1(@swc/core@1.11.29)(esbuild@0.25.5))
|
||||
terser-webpack-plugin: 5.3.10(@swc/core@1.11.29)(webpack@5.96.1(@swc/core@1.11.29))
|
||||
watchpack: 2.4.2
|
||||
webpack-sources: 3.2.3
|
||||
transitivePeerDependencies:
|
||||
@@ -24734,6 +24671,11 @@ snapshots:
|
||||
dependencies:
|
||||
isexe: 2.0.0
|
||||
|
||||
which@4.0.0:
|
||||
dependencies:
|
||||
isexe: 3.1.1
|
||||
optional: true
|
||||
|
||||
why-is-node-running@2.3.0:
|
||||
dependencies:
|
||||
siginfo: 2.0.0
|
||||
|
||||
@@ -60,7 +60,7 @@ const BeforeDashboard: React.FC = () => {
|
||||
</ul>
|
||||
{'Pro Tip: This block is a '}
|
||||
<a
|
||||
href="https://payloadcms.com/docs/admin/custom-components/overview#base-component-overrides"
|
||||
href="https://payloadcms.com/docs/custom-components/overview"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
|
||||
@@ -24,10 +24,10 @@ export default buildConfig({
|
||||
admin: {
|
||||
components: {
|
||||
// The `BeforeLogin` component renders a message that you see while logging into your admin panel.
|
||||
// Feel free to delete this at any time. Simply remove the line below and the import `BeforeLogin` statement on line 15.
|
||||
// Feel free to delete this at any time. Simply remove the line below.
|
||||
beforeLogin: ['@/components/BeforeLogin'],
|
||||
// The `BeforeDashboard` component renders the 'welcome' block that you see after logging into your admin panel.
|
||||
// Feel free to delete this at any time. Simply remove the line below and the import `BeforeDashboard` statement on line 15.
|
||||
// Feel free to delete this at any time. Simply remove the line below.
|
||||
beforeDashboard: ['@/components/BeforeDashboard'],
|
||||
},
|
||||
importMap: {
|
||||
|
||||
@@ -60,7 +60,7 @@ const BeforeDashboard: React.FC = () => {
|
||||
</ul>
|
||||
{'Pro Tip: This block is a '}
|
||||
<a
|
||||
href="https://payloadcms.com/docs/admin/custom-components/overview#base-component-overrides"
|
||||
href="https://payloadcms.com/docs/custom-components/overview"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
|
||||
@@ -24,10 +24,10 @@ export default buildConfig({
|
||||
admin: {
|
||||
components: {
|
||||
// The `BeforeLogin` component renders a message that you see while logging into your admin panel.
|
||||
// Feel free to delete this at any time. Simply remove the line below and the import `BeforeLogin` statement on line 15.
|
||||
// Feel free to delete this at any time. Simply remove the line below.
|
||||
beforeLogin: ['@/components/BeforeLogin'],
|
||||
// The `BeforeDashboard` component renders the 'welcome' block that you see after logging into your admin panel.
|
||||
// Feel free to delete this at any time. Simply remove the line below and the import `BeforeDashboard` statement on line 15.
|
||||
// Feel free to delete this at any time. Simply remove the line below.
|
||||
beforeDashboard: ['@/components/BeforeDashboard'],
|
||||
},
|
||||
importMap: {
|
||||
|
||||
@@ -203,6 +203,13 @@ export interface User {
|
||||
hash?: string | null;
|
||||
loginAttempts?: number | null;
|
||||
lockUntil?: string | null;
|
||||
sessions?:
|
||||
| {
|
||||
id: string;
|
||||
createdAt?: string | null;
|
||||
expiresAt: string;
|
||||
}[]
|
||||
| null;
|
||||
password?: string | null;
|
||||
}
|
||||
/**
|
||||
@@ -341,6 +348,13 @@ export interface UsersSelect<T extends boolean = true> {
|
||||
hash?: T;
|
||||
loginAttempts?: T;
|
||||
lockUntil?: T;
|
||||
sessions?:
|
||||
| T
|
||||
| {
|
||||
id?: T;
|
||||
createdAt?: T;
|
||||
expiresAt?: T;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
|
||||
@@ -436,10 +436,15 @@ describe('Access Control', () => {
|
||||
const documentDrawer = page.locator(`[id^=doc-drawer_${createNotUpdateCollectionSlug}_1_]`)
|
||||
await expect(documentDrawer).toBeVisible()
|
||||
await expect(documentDrawer.locator('#action-save')).toBeVisible()
|
||||
|
||||
await documentDrawer.locator('#field-name').fill('name')
|
||||
await expect(documentDrawer.locator('#field-name')).toHaveValue('name')
|
||||
await documentDrawer.locator('#action-save').click()
|
||||
await expect(page.locator('.payload-toast-container')).toContainText('successfully')
|
||||
|
||||
await saveDocAndAssert(
|
||||
page,
|
||||
`[id^=doc-drawer_${createNotUpdateCollectionSlug}_1_] #action-save`,
|
||||
)
|
||||
|
||||
await expect(documentDrawer.locator('#action-save')).toBeHidden()
|
||||
await expect(documentDrawer.locator('#field-name')).toBeDisabled()
|
||||
})
|
||||
|
||||
@@ -95,7 +95,6 @@ export interface Config {
|
||||
'auth-collection': AuthCollection;
|
||||
'payload-locked-documents': PayloadLockedDocument;
|
||||
'payload-preferences': PayloadPreference;
|
||||
'payload-sessions': PayloadSession;
|
||||
'payload-migrations': PayloadMigration;
|
||||
};
|
||||
collectionsJoins: {};
|
||||
@@ -126,7 +125,6 @@ export interface Config {
|
||||
'auth-collection': AuthCollectionSelect<false> | AuthCollectionSelect<true>;
|
||||
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
|
||||
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
|
||||
'payload-sessions': PayloadSessionsSelect<false> | PayloadSessionsSelect<true>;
|
||||
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
|
||||
};
|
||||
db: {
|
||||
@@ -232,6 +230,13 @@ export interface User {
|
||||
hash?: string | null;
|
||||
loginAttempts?: number | null;
|
||||
lockUntil?: string | null;
|
||||
sessions?:
|
||||
| {
|
||||
id: string;
|
||||
createdAt?: string | null;
|
||||
expiresAt: string;
|
||||
}[]
|
||||
| null;
|
||||
password?: string | null;
|
||||
}
|
||||
/**
|
||||
@@ -249,6 +254,13 @@ export interface PublicUser {
|
||||
hash?: string | null;
|
||||
loginAttempts?: number | null;
|
||||
lockUntil?: string | null;
|
||||
sessions?:
|
||||
| {
|
||||
id: string;
|
||||
createdAt?: string | null;
|
||||
expiresAt: string;
|
||||
}[]
|
||||
| null;
|
||||
password?: string | null;
|
||||
}
|
||||
/**
|
||||
@@ -740,6 +752,13 @@ export interface AuthCollection {
|
||||
_verificationToken?: string | null;
|
||||
loginAttempts?: number | null;
|
||||
lockUntil?: string | null;
|
||||
sessions?:
|
||||
| {
|
||||
id: string;
|
||||
createdAt?: string | null;
|
||||
expiresAt: string;
|
||||
}[]
|
||||
| null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
@@ -893,26 +912,6 @@ export interface PayloadPreference {
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "payload-sessions".
|
||||
*/
|
||||
export interface PayloadSession {
|
||||
id: string;
|
||||
session: string;
|
||||
expiration: string;
|
||||
user:
|
||||
| {
|
||||
relationTo: 'users';
|
||||
value: string | User;
|
||||
}
|
||||
| {
|
||||
relationTo: 'public-users';
|
||||
value: string | PublicUser;
|
||||
};
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "payload-migrations".
|
||||
@@ -939,6 +938,13 @@ export interface UsersSelect<T extends boolean = true> {
|
||||
hash?: T;
|
||||
loginAttempts?: T;
|
||||
lockUntil?: T;
|
||||
sessions?:
|
||||
| T
|
||||
| {
|
||||
id?: T;
|
||||
createdAt?: T;
|
||||
expiresAt?: T;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
@@ -954,6 +960,13 @@ export interface PublicUsersSelect<T extends boolean = true> {
|
||||
hash?: T;
|
||||
loginAttempts?: T;
|
||||
lockUntil?: T;
|
||||
sessions?:
|
||||
| T
|
||||
| {
|
||||
id?: T;
|
||||
createdAt?: T;
|
||||
expiresAt?: T;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
@@ -1294,6 +1307,13 @@ export interface AuthCollectionSelect<T extends boolean = true> {
|
||||
_verificationToken?: T;
|
||||
loginAttempts?: T;
|
||||
lockUntil?: T;
|
||||
sessions?:
|
||||
| T
|
||||
| {
|
||||
id?: T;
|
||||
createdAt?: T;
|
||||
expiresAt?: T;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
@@ -1317,17 +1337,6 @@ export interface PayloadPreferencesSelect<T extends boolean = true> {
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "payload-sessions_select".
|
||||
*/
|
||||
export interface PayloadSessionsSelect<T extends boolean = true> {
|
||||
session?: T;
|
||||
expiration?: T;
|
||||
user?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "payload-migrations_select".
|
||||
|
||||
@@ -18,6 +18,7 @@ export const UploadCollection: CollectionConfig = {
|
||||
height: 100,
|
||||
},
|
||||
],
|
||||
adminThumbnail: () => 'https://payloadcms.com/images/universal-truth.jpg',
|
||||
adminThumbnail: () =>
|
||||
'https://raw.githubusercontent.com/payloadcms/website/refs/heads/main/public/images/universal-truth.jpg',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -363,6 +363,43 @@ describe('Document View', () => {
|
||||
})
|
||||
|
||||
describe('drawers', () => {
|
||||
test('document drawers do not unmount across save events', async () => {
|
||||
// Navigate to a post document
|
||||
await navigateToDoc(page, postsUrl)
|
||||
|
||||
// Open the relationship drawer
|
||||
await page
|
||||
.locator('.field-type.relationship .relationship--single-value__drawer-toggler')
|
||||
.click()
|
||||
|
||||
const drawer = page.locator('[id^=doc-drawer_posts_1_]')
|
||||
const drawerEditView = drawer.locator('.drawer__content .collection-edit')
|
||||
await expect(drawerEditView).toBeVisible()
|
||||
|
||||
const drawerTitleField = drawerEditView.locator('#field-title')
|
||||
const testTitle = 'Test Title for Persistence'
|
||||
await drawerTitleField.fill(testTitle)
|
||||
await expect(drawerTitleField).toHaveValue(testTitle)
|
||||
|
||||
await drawerEditView.evaluate((el) => {
|
||||
el.setAttribute('data-test-instance', 'This is a test')
|
||||
})
|
||||
|
||||
await expect(drawerEditView).toHaveAttribute('data-test-instance', 'This is a test')
|
||||
|
||||
await saveDocAndAssert(page, '[id^=doc-drawer_posts_1_] .drawer__content #action-save')
|
||||
|
||||
await expect(drawerEditView).toBeVisible()
|
||||
await expect(drawerTitleField).toHaveValue(testTitle)
|
||||
|
||||
// Verify the element instance hasn't changed (i.e., it wasn't re-mounted and discarded the custom attribute)
|
||||
await expect
|
||||
.poll(async () => {
|
||||
return await drawerEditView.getAttribute('data-test-instance')
|
||||
})
|
||||
.toBe('This is a test')
|
||||
})
|
||||
|
||||
test('document drawers are visually stacking', async () => {
|
||||
await navigateToDoc(page, postsUrl)
|
||||
await page.locator('#field-title').fill(title)
|
||||
|
||||
@@ -293,6 +293,13 @@ export interface User {
|
||||
hash?: string | null;
|
||||
loginAttempts?: number | null;
|
||||
lockUntil?: string | null;
|
||||
sessions?:
|
||||
| {
|
||||
id: string;
|
||||
createdAt?: string | null;
|
||||
expiresAt: string;
|
||||
}[]
|
||||
| null;
|
||||
password?: string | null;
|
||||
}
|
||||
/**
|
||||
@@ -820,6 +827,13 @@ export interface UsersSelect<T extends boolean = true> {
|
||||
hash?: T;
|
||||
loginAttempts?: T;
|
||||
lockUntil?: T;
|
||||
sessions?:
|
||||
| T
|
||||
| {
|
||||
id?: T;
|
||||
createdAt?: T;
|
||||
expiresAt?: T;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
|
||||
@@ -46,6 +46,16 @@ export default buildConfigWithDefaults({
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'categories-custom-id',
|
||||
versions: { drafts: true },
|
||||
fields: [
|
||||
{
|
||||
type: 'number',
|
||||
name: 'id',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: postsSlug,
|
||||
fields: [
|
||||
@@ -60,6 +70,11 @@ export default buildConfigWithDefaults({
|
||||
relationTo: 'categories',
|
||||
name: 'category',
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
relationTo: 'categories-custom-id',
|
||||
name: 'categoryCustomID',
|
||||
},
|
||||
{
|
||||
name: 'localized',
|
||||
type: 'text',
|
||||
@@ -516,6 +531,11 @@ export default buildConfigWithDefaults({
|
||||
type: 'json',
|
||||
virtual: 'post.category.id',
|
||||
},
|
||||
{
|
||||
name: 'postCategoryCustomID',
|
||||
type: 'number',
|
||||
virtual: 'post.categoryCustomID.id',
|
||||
},
|
||||
{
|
||||
name: 'postID',
|
||||
type: 'json',
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user