feat(db-postgres)!: relationship column (#6339)

BREAKING CHANGE:

Moves `upload` field and `relationship` fields with `hasMany: false` &
`relationTo: string` from the many-to-many `_rels` join table to simple
columns. This only affects Postgres database users.

## TL;DR

We have dramatically simplified the storage of simple relationships in
relational databases to boost performance and align with more expected
relational paradigms. If you are using the beta Postgres adapter, and
you need to keep simple relationship data, you'll need to run a
migration script that we provide you.

### Background

For example, prior to this update, a collection of "posts" with a simple
`hasMany: false` and `relationTo: 'categories'` field would have a
`posts_rels` table where the category relations would be stored.

This was somewhat unnecessary as simple relations like this can be
expressed with a `category_id` column which is configured as a foreign
key. This also introduced added complexity for dealing directly with the
database if all you have are simple relations.

### Who needs to migrate

You need to migrate if you are using the beta Postgres database adapter
and any of the following applies to you.

- If you have versions enabled on any collection / global
- If you use the `upload` field 
- If you have relationship fields that are `hasMany: false` (default)
and `relationTo` to a single category ([has
one](https://payloadcms.com/docs/fields/relationship#has-one)) relations

### We have a migration for you

Even though the Postgres adapter is in beta, we've prepared a predefined
migration that will work out of the box for you to migrate from an
earlier version of the adapter to the most recent version easily.

It makes the schema changes in step with actually moving the data from
the old locations to the new before adding any null constraints and
dropping the old columns and tables.

### How to migrate

The steps to preserve your data while making this update are as follows.
These steps are the same whether you are moving from Payload v2 to v3 or
a previous version of v3 beta to the most recent v3 beta.

**Important: during these steps, don't start the dev server unless you
have `push: false` set on your Postgres adapter.**

#### Step 1 - backup

Always back up your database before performing big changes, especially
in production cases.

#### Step 2 - create a pre-update migration 
Before updating to new Payload and Postgres adapter versions, run
`payload migrate:create` without any other config changes to have a
prior snapshot of the schema from the previous adapter version

#### Step 3 - if you're migrating a dev DB, delete the dev `push` row
from your `payload_migrations` table

If you're migrating a dev database where you have the default setting to
push database changes directly to your DB, and you need to preserve data
in your development database, then you need to delete a `dev` migration
record from your database.

Connect directly to your database in any tool you'd like and delete the
dev push record from the `payload_migrations` table using the following
SQL statement:

```sql
DELETE FROM payload_migrations where batch = -1`
```

#### Step 4 - update Payload and Postgres versions to most recent

Update packages, making sure you have matching versions across all
`@payloadcms/*` and `payload` packages (including
`@payloadcms/db-postgres`)

#### Step 5 - create the predefined migration

Run the following command to create the predefined migration we've
provided:

```
payload migrate:create --file @payloadcms/db-postgres/relationships-v2-v3
```

#### Step 6 - migrate!

Run migrations with the following command: 

```
payload migrate
```

Assuming the migration worked, you can proceed to commit this change and
distribute it to be run on all other environments.

Note that if two servers connect to the same database, only one should
be running migrations to avoid transaction conflicts.

Related discussion:
https://github.com/payloadcms/payload/discussions/4163

---------

Co-authored-by: James <james@trbl.design>
Co-authored-by: PatrikKozak <patrik@payloadcms.com>
This commit is contained in:
Dan Ribbens
2024-05-30 14:09:11 -04:00
committed by GitHub
parent b86d4c647f
commit edfa85bcd5
163 changed files with 4372 additions and 629 deletions

View File

@@ -1,5 +1,5 @@
import type { CountryField } from 'payload-plugin-form-builder/dist/types' import type { CountryField } from 'payload-plugin-form-builder/dist/types'
import type { Control, FieldErrorsImpl, FieldValues } from 'react-hook-form'; import type { Control, FieldErrorsImpl, FieldValues } from 'react-hook-form'
import React from 'react' import React from 'react'
import { Controller } from 'react-hook-form' import { Controller } from 'react-hook-form'

View File

@@ -1,5 +1,5 @@
import type { SelectField } from 'payload-plugin-form-builder/dist/types' import type { SelectField } from 'payload-plugin-form-builder/dist/types'
import type { Control, FieldErrorsImpl, FieldValues } from 'react-hook-form'; import type { Control, FieldErrorsImpl, FieldValues } from 'react-hook-form'
import React from 'react' import React from 'react'
import { Controller } from 'react-hook-form' import { Controller } from 'react-hook-form'

View File

@@ -1,5 +1,5 @@
import type { StateField } from 'payload-plugin-form-builder/dist/types' import type { StateField } from 'payload-plugin-form-builder/dist/types'
import type { Control, FieldErrorsImpl, FieldValues } from 'react-hook-form'; import type { Control, FieldErrorsImpl, FieldValues } from 'react-hook-form'
import React from 'react' import React from 'react'
import { Controller } from 'react-hook-form' import { Controller } from 'react-hook-form'

View File

@@ -1,5 +1,5 @@
'use client' 'use client'
import type React from 'react'; import type React from 'react'
import { useModal } from '@faceless-ui/modal' import { useModal } from '@faceless-ui/modal'
import { usePathname } from 'next/navigation' import { usePathname } from 'next/navigation'

View File

@@ -1,4 +1,4 @@
import type { Ref } from 'react'; import type { Ref } from 'react'
import React, { forwardRef } from 'react' import React, { forwardRef } from 'react'

View File

@@ -3,10 +3,10 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev -p 3001",
"build": "next build", "build": "next build",
"start": "next start -p 3001", "dev": "next dev -p 3001",
"lint": "next lint" "lint": "next lint",
"start": "next start -p 3001"
}, },
"dependencies": { "dependencies": {
"@payloadcms/live-preview-react": "3.0.0-beta.28", "@payloadcms/live-preview-react": "3.0.0-beta.28",

View File

@@ -3,10 +3,10 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev -p 3001",
"build": "next build", "build": "next build",
"start": "next start -p 3001", "dev": "next dev -p 3001",
"lint": "next lint" "lint": "next lint",
"start": "next start -p 3001"
}, },
"dependencies": { "dependencies": {
"@payloadcms/live-preview-react": "3.0.0-beta.28", "@payloadcms/live-preview-react": "3.0.0-beta.28",

View File

@@ -1,4 +1,4 @@
import type { ElementType } from 'react'; import type { ElementType } from 'react'
import Link from 'next/link' import Link from 'next/link'
import React from 'react' import React from 'react'

View File

@@ -1,4 +1,4 @@
import type { Ref } from 'react'; import type { Ref } from 'react'
import React, { forwardRef } from 'react' import React, { forwardRef } from 'react'

View File

@@ -125,7 +125,7 @@ const link: LinkType = ({ appearances, disableLabel = false, overrides = {} } =
] ]
if (appearances) { if (appearances) {
appearanceOptionsToUse = appearances.map(appearance => appearanceOptions[appearance]) appearanceOptionsToUse = appearances.map((appearance) => appearanceOptions[appearance])
} }
linkResult.fields.push({ linkResult.fields.push({

View File

@@ -1,22 +1,24 @@
/* eslint-disable no-restricted-syntax, no-await-in-loop */ /* eslint-disable no-restricted-syntax, no-await-in-loop */
import type { CreateMigration } from 'payload/database' import type { CreateMigration, MigrationTemplateArgs } from 'payload/database'
import fs from 'fs' import fs from 'fs'
import path from 'path' import path from 'path'
import { getPredefinedMigration } from 'payload/database'
import { fileURLToPath } from 'url' import { fileURLToPath } from 'url'
const migrationTemplate = (upSQL?: string, downSQL?: string) => `import { const migrationTemplate = ({ downSQL, imports, upSQL }: MigrationTemplateArgs): string => `import {
MigrateUpArgs, MigrateUpArgs,
MigrateDownArgs, MigrateDownArgs,
} from "@payloadcms/db-mongodb"; } from '@payloadcms/db-mongodb'
${imports}
export async function up({ payload }: MigrateUpArgs): Promise<void> { export async function up({ payload }: MigrateUpArgs): Promise<void> {
${upSQL ?? ` // Migration code`} ${upSQL ?? ` // Migration code`}
}; }
export async function down({ payload }: MigrateDownArgs): Promise<void> { export async function down({ payload }: MigrateDownArgs): Promise<void> {
${downSQL ?? ` // Migration code`} ${downSQL ?? ` // Migration code`}
}; }
` `
export const createMigration: CreateMigration = async function createMigration({ export const createMigration: CreateMigration = async function createMigration({
@@ -31,36 +33,14 @@ export const createMigration: CreateMigration = async function createMigration({
if (!fs.existsSync(dir)) { if (!fs.existsSync(dir)) {
fs.mkdirSync(dir) fs.mkdirSync(dir)
} }
const predefinedMigration = await getPredefinedMigration({
dirname,
file,
migrationName,
payload,
})
let migrationFileContent: string | undefined const migrationFileContent = migrationTemplate(predefinedMigration)
// Check for predefined migration.
// Either passed in via --file or prefixed with @payloadcms/db-mongodb/
if (file || migrationName?.startsWith('@payloadcms/db-mongodb/')) {
if (!file) file = migrationName
const predefinedMigrationName = file.replace('@payloadcms/db-mongodb/', '')
migrationName = predefinedMigrationName
const cleanPath = path.join(dirname, `../predefinedMigrations/${predefinedMigrationName}.js`)
// Check if predefined migration exists
if (fs.existsSync(cleanPath)) {
let migration = await eval(
`${typeof require === 'function' ? 'require' : 'import'}(${cleanPath})`,
)
if ('default' in migration) migration = migration.default
const { down, up } = migration
migrationFileContent = migrationTemplate(up, down)
} else {
payload.logger.error({
msg: `Canned migration ${predefinedMigrationName} not found.`,
})
process.exit(1)
}
} else {
migrationFileContent = migrationTemplate()
}
const [yyymmdd, hhmmss] = new Date().toISOString().split('T') const [yyymmdd, hhmmss] = new Date().toISOString().split('T')
const formattedDate = yyymmdd.replace(/\D/g, '') const formattedDate = yyymmdd.replace(/\D/g, '')

View File

@@ -21,6 +21,11 @@
"import": "./src/types.ts", "import": "./src/types.ts",
"require": "./src/types.ts", "require": "./src/types.ts",
"types": "./src/types.ts" "types": "./src/types.ts"
},
"./migration-utils": {
"import": "./src/exports/migration-utils.ts",
"require": "./src/exports/migration-utils.ts",
"types": "./src/exports/migration-utils.ts"
} }
}, },
"main": "./src/index.ts", "main": "./src/index.ts",
@@ -30,11 +35,12 @@
"mock.js" "mock.js"
], ],
"scripts": { "scripts": {
"build": "pnpm build:swc && pnpm build:types", "build": "pnpm build:swc && pnpm build:types && pnpm renamePredefinedMigrations",
"build:swc": "swc ./src -d ./dist --config-file .swcrc", "build:swc": "swc ./src -d ./dist --config-file .swcrc",
"build:types": "tsc --emitDeclarationOnly --outDir dist", "build:types": "tsc --emitDeclarationOnly --outDir dist",
"clean": "rimraf {dist,*.tsbuildinfo}", "clean": "rimraf {dist,*.tsbuildinfo}",
"prepublishOnly": "pnpm clean && pnpm turbo build" "prepublishOnly": "pnpm clean && pnpm turbo build",
"renamePredefinedMigrations": "tsx ./scripts/renamePredefinedMigrations.ts"
}, },
"dependencies": { "dependencies": {
"@libsql/client": "^0.5.2", "@libsql/client": "^0.5.2",
@@ -66,6 +72,11 @@
"import": "./dist/types.js", "import": "./dist/types.js",
"require": "./dist/types.js", "require": "./dist/types.js",
"types": "./dist/types.d.ts" "types": "./dist/types.d.ts"
},
"./migration-utils": {
"import": "./dist/exports/migration-utils.js",
"require": "./dist/exports/migration-utils.js",
"types": "./dist/exports/migration-utils.d.ts"
} }
}, },
"main": "./dist/index.js", "main": "./dist/index.js",

View File

@@ -0,0 +1,13 @@
const imports = `import { migratePostgresV2toV3 } from '@payloadcms/migratePostgresV2toV3'`;
const up = ` await migratePostgresV2toV3({
// enables logging of changes that will be made to the database
debug: false,
// skips calls that modify schema or data
dryRun: false,
payload,
req,
})
`;
export { imports, up };
//# sourceMappingURL=relationships-v2-v3.js.map

View File

@@ -0,0 +1,18 @@
import fs from 'fs'
import path from 'path'
/**
* Changes built .js files to .mjs to for ESM imports
*/
const rename = () => {
fs.readdirSync(path.resolve('./dist/predefinedMigrations'))
.filter((f) => {
return f.endsWith('.js')
})
.forEach((file) => {
const newPath = path.join('./dist/predefinedMigrations', file)
fs.renameSync(newPath, newPath.replace('.js', '.mjs'))
})
}
rename()

View File

@@ -21,7 +21,7 @@ export const count: Count = async function count(
const db = this.sessions[req.transactionID]?.db || this.drizzle const db = this.sessions[req.transactionID]?.db || this.drizzle
const table = this.tables[tableName] const table = this.tables[tableName]
const { joinAliases, joins, where } = await buildQuery({ const { joins, where } = await buildQuery({
adapter: this, adapter: this,
fields: collectionConfig.fields, fields: collectionConfig.fields,
locale, locale,
@@ -31,13 +31,6 @@ export const count: Count = async function count(
const selectCountMethods: ChainedMethods = [] const selectCountMethods: ChainedMethods = []
joinAliases.forEach(({ condition, table }) => {
selectCountMethods.push({
args: [table, condition],
method: 'leftJoin',
})
})
Object.entries(joins).forEach(([joinTable, condition]) => { Object.entries(joins).forEach(([joinTable, condition]) => {
if (joinTable) { if (joinTable) {
selectCountMethods.push({ selectCountMethods.push({

View File

@@ -1,40 +1,30 @@
/* eslint-disable no-restricted-syntax, no-await-in-loop */ /* eslint-disable no-restricted-syntax, no-await-in-loop */
import type { DrizzleSnapshotJSON } from 'drizzle-kit/payload' import type { DrizzleSnapshotJSON } from 'drizzle-kit/payload'
import type { CreateMigration } from 'payload/database' import type { CreateMigration, MigrationTemplateArgs } from 'payload/database'
import fs from 'fs' import fs from 'fs'
import { createRequire } from 'module' import { createRequire } from 'module'
import path from 'path'
import { getPredefinedMigration } from 'payload/database'
import prompts from 'prompts' import prompts from 'prompts'
import { fileURLToPath } from 'url'
import type { PostgresAdapter } from './types.js' import type { PostgresAdapter } from './types.js'
const require = createRequire(import.meta.url) const require = createRequire(import.meta.url)
const migrationTemplate = ( const migrationTemplate = ({
upSQL?: string, downSQL,
downSQL?: string, imports,
) => `import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres' upSQL,
}: MigrationTemplateArgs): string => `import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres'
export async function up({ payload }: MigrateUpArgs): Promise<void> { ${imports ? `${imports}\n` : ''}
${ export async function up({ payload, req }: MigrateUpArgs): Promise<void> {
upSQL ${upSQL}
? `await payload.db.drizzle.execute(sql\`
${upSQL}\`);
`
: '// Migration code'
}
}; };
export async function down({ payload }: MigrateDownArgs): Promise<void> { export async function down({ payload, req }: MigrateDownArgs): Promise<void> {
${ ${downSQL}
downSQL
? `await payload.db.drizzle.execute(sql\`
${downSQL}\`);
`
: '// Migration code'
}
}; };
` `
@@ -55,78 +45,95 @@ const getDefaultDrizzleSnapshot = (): DrizzleSnapshotJSON => ({
export const createMigration: CreateMigration = async function createMigration( export const createMigration: CreateMigration = async function createMigration(
this: PostgresAdapter, this: PostgresAdapter,
{ forceAcceptWarning, migrationName, payload }, { file, forceAcceptWarning, migrationName, payload },
) { ) {
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
const dir = payload.db.migrationDir const dir = payload.db.migrationDir
if (!fs.existsSync(dir)) { if (!fs.existsSync(dir)) {
fs.mkdirSync(dir) fs.mkdirSync(dir)
} }
const { generateDrizzleJson, generateMigration } = require('drizzle-kit/payload') const { generateDrizzleJson, generateMigration } = require('drizzle-kit/payload')
const drizzleJsonAfter = generateDrizzleJson(this.schema)
const [yyymmdd, hhmmss] = new Date().toISOString().split('T') const [yyymmdd, hhmmss] = new Date().toISOString().split('T')
const formattedDate = yyymmdd.replace(/\D/g, '') const formattedDate = yyymmdd.replace(/\D/g, '')
const formattedTime = hhmmss.split('.')[0].replace(/\D/g, '') const formattedTime = hhmmss.split('.')[0].replace(/\D/g, '')
let imports: string = ''
let downSQL: string
let upSQL: string
;({ downSQL, imports, upSQL } = await getPredefinedMigration({
dirname,
file,
migrationName,
payload,
}))
const timestamp = `${formattedDate}_${formattedTime}` const timestamp = `${formattedDate}_${formattedTime}`
const fileName = migrationName const name = migrationName || file?.split('/').slice(2).join('/')
? `${timestamp}_${migrationName.replace(/\W/g, '_')}` const fileName = `${timestamp}${name ? `_${name.replace(/\W/g, '_')}` : ''}`
: `${timestamp}`
const filePath = `${dir}/${fileName}` const filePath = `${dir}/${fileName}`
let drizzleJsonBefore = getDefaultDrizzleSnapshot() let drizzleJsonBefore = getDefaultDrizzleSnapshot()
// Get latest migration snapshot if (!upSQL) {
const latestSnapshot = fs // Get latest migration snapshot
.readdirSync(dir) const latestSnapshot = fs
.filter((file) => file.endsWith('.json')) .readdirSync(dir)
.sort() .filter((file) => file.endsWith('.json'))
.reverse()?.[0] .sort()
.reverse()?.[0]
if (latestSnapshot) { if (latestSnapshot) {
const latestSnapshotJSON = JSON.parse( drizzleJsonBefore = JSON.parse(
fs.readFileSync(`${dir}/${latestSnapshot}`, 'utf8'), fs.readFileSync(`${dir}/${latestSnapshot}`, 'utf8'),
) as DrizzleSnapshotJSON ) as DrizzleSnapshotJSON
drizzleJsonBefore = latestSnapshotJSON
}
const drizzleJsonAfter = generateDrizzleJson(this.schema)
const sqlStatementsUp = await generateMigration(drizzleJsonBefore, drizzleJsonAfter)
const sqlStatementsDown = await generateMigration(drizzleJsonAfter, drizzleJsonBefore)
if (!sqlStatementsUp.length && !sqlStatementsDown.length && !forceAcceptWarning) {
const { confirm: shouldCreateBlankMigration } = await prompts(
{
name: 'confirm',
type: 'confirm',
initial: false,
message: 'No schema changes detected. Would you like to create a blank migration file?',
},
{
onCancel: () => {
process.exit(0)
},
},
)
if (!shouldCreateBlankMigration) {
process.exit(0)
} }
}
// write schema const sqlStatementsUp = await generateMigration(drizzleJsonBefore, drizzleJsonAfter)
fs.writeFileSync(`${filePath}.json`, JSON.stringify(drizzleJsonAfter, null, 2)) const sqlStatementsDown = await generateMigration(drizzleJsonAfter, drizzleJsonBefore)
const sqlExecute = 'await payload.db.drizzle.execute(sql`'
if (sqlStatementsUp?.length) {
upSQL = `${sqlExecute}\n ${sqlStatementsUp?.join('\n')}\`)`
}
if (sqlStatementsDown?.length) {
downSQL = `${sqlExecute}\n ${sqlStatementsDown?.join('\n')}\`)`
}
if (!upSQL?.length && !downSQL?.length && !forceAcceptWarning) {
const { confirm: shouldCreateBlankMigration } = await prompts(
{
name: 'confirm',
type: 'confirm',
initial: false,
message: 'No schema changes detected. Would you like to create a blank migration file?',
},
{
onCancel: () => {
process.exit(0)
},
},
)
if (!shouldCreateBlankMigration) {
process.exit(0)
}
}
// write schema
fs.writeFileSync(`${filePath}.json`, JSON.stringify(drizzleJsonAfter, null, 2))
}
// write migration // write migration
fs.writeFileSync( fs.writeFileSync(
`${filePath}.ts`, `${filePath}.ts`,
migrationTemplate( migrationTemplate({
sqlStatementsUp.length ? sqlStatementsUp?.join('\n') : undefined, downSQL: downSQL || ` // Migration code`,
sqlStatementsDown.length ? sqlStatementsDown?.join('\n') : undefined, imports,
), upSQL: upSQL || ` // Migration code`,
}),
) )
payload.logger.info({ msg: `Migration created at ${filePath}.ts` }) payload.logger.info({ msg: `Migration created at ${filePath}.ts` })
} }

View File

@@ -45,17 +45,12 @@ export async function createVersion<T extends TypeWithID>(
const table = this.tables[tableName] const table = this.tables[tableName]
const relationshipsTable = this.tables[`${tableName}${this.relationshipsSuffix}`]
if (collection.versions.drafts) { if (collection.versions.drafts) {
await db.execute(sql` await db.execute(sql`
UPDATE ${table} UPDATE ${table}
SET latest = false SET latest = false
FROM ${relationshipsTable} WHERE ${table.id} != ${result.id}
WHERE ${table.id} = ${relationshipsTable.parent} AND ${table.parent} = ${parent}
AND ${relationshipsTable.path} = ${'parent'}
AND ${relationshipsTable[`${collectionSlug}ID`]} = ${parent}
AND ${table.id} != ${result.id};
`) `)
} }

View File

@@ -22,7 +22,7 @@ export const deleteOne: DeleteOne = async function deleteOne(
let docToDelete: Record<string, unknown> let docToDelete: Record<string, unknown>
const { joinAliases, joins, selectFields, where } = await buildQuery({ const { joins, selectFields, where } = await buildQuery({
adapter: this, adapter: this,
fields: collection.fields, fields: collection.fields,
locale: req.locale, locale: req.locale,
@@ -34,7 +34,6 @@ export const deleteOne: DeleteOne = async function deleteOne(
adapter: this, adapter: this,
chainedMethods: [{ args: [1], method: 'limit' }], chainedMethods: [{ args: [1], method: 'limit' }],
db, db,
joinAliases,
joins, joins,
selectFields, selectFields,
tableName, tableName,
@@ -59,6 +58,7 @@ export const deleteOne: DeleteOne = async function deleteOne(
} }
const result = transform({ const result = transform({
adapter: this,
config: this.payload.config, config: this.payload.config,
data: docToDelete, data: docToDelete,
fields: collection.fields, fields: collection.fields,

View File

@@ -0,0 +1 @@
export { migratePostgresV2toV3 } from '../predefinedMigrations/v2-v3/index.js'

View File

@@ -12,7 +12,11 @@ type BuildFindQueryArgs = {
tableName: string tableName: string
} }
export type Result = DBQueryConfig<'many', true, any, any> export type Result = DBQueryConfig<'many', true, any, any> & {
with?: DBQueryConfig<'many', true, any, any> & {
_locales?: DBQueryConfig<'many', true, any, any>
}
}
// Generate the Drizzle query for findMany based on // Generate the Drizzle query for findMany based on
// a collection field structure // a collection field structure
@@ -31,6 +35,7 @@ export const buildFindManyArgs = ({
id: false, id: false,
_parentID: false, _parentID: false,
}, },
with: {},
} }
if (adapter.tables[`${tableName}_texts`]) { if (adapter.tables[`${tableName}_texts`]) {

View File

@@ -41,7 +41,7 @@ export const findMany = async function find({
let hasNextPage: boolean let hasNextPage: boolean
let pagingCounter: number let pagingCounter: number
const { joinAliases, joins, orderBy, selectFields, where } = await buildQuery({ const { joins, orderBy, selectFields, where } = await buildQuery({
adapter, adapter,
fields, fields,
locale, locale,
@@ -76,7 +76,6 @@ export const findMany = async function find({
adapter, adapter,
chainedMethods: selectDistinctMethods, chainedMethods: selectDistinctMethods,
db, db,
joinAliases,
joins, joins,
selectFields, selectFields,
tableName, tableName,
@@ -122,29 +121,19 @@ export const findMany = async function find({
if (pagination !== false && (orderedIDs ? orderedIDs?.length <= limit : true)) { if (pagination !== false && (orderedIDs ? orderedIDs?.length <= limit : true)) {
const selectCountMethods: ChainedMethods = [] const selectCountMethods: ChainedMethods = []
joins.forEach(({ condition, table }) => {
joinAliases.forEach(({ condition, table }) => {
selectCountMethods.push({ selectCountMethods.push({
args: [table, condition], args: [table, condition],
method: 'leftJoin', method: 'leftJoin',
}) })
}) })
Object.entries(joins).forEach(([joinTable, condition]) => {
if (joinTable) {
selectCountMethods.push({
args: [adapter.tables[joinTable], condition],
method: 'leftJoin',
})
}
})
const countResult = await chainMethods({ const countResult = await chainMethods({
methods: selectCountMethods, methods: selectCountMethods,
query: db query: db
.select({ .select({
count: sql<number>`count count: sql<number>`count
(DISTINCT ${adapter.tables[tableName].id})`, (DISTINCT ${adapter.tables[tableName].id})`,
}) })
.from(table) .from(table)
.where(where), .where(where),
@@ -172,6 +161,7 @@ export const findMany = async function find({
const docs = rawDocs.map((data: TypeWithID) => { const docs = rawDocs.map((data: TypeWithID) => {
return transform({ return transform({
adapter,
config: adapter.payload.config, config: adapter.payload.config,
data, data,
fields, fields,

View File

@@ -8,9 +8,9 @@ import type { PostgresAdapter } from '../types.js'
import type { Result } from './buildFindManyArgs.js' import type { Result } from './buildFindManyArgs.js'
type TraverseFieldArgs = { type TraverseFieldArgs = {
_locales: Record<string, unknown> _locales: Result
adapter: PostgresAdapter adapter: PostgresAdapter
currentArgs: Record<string, unknown> currentArgs: Result
currentTableName: string currentTableName: string
depth?: number depth?: number
fields: Field[] fields: Field[]
@@ -31,6 +31,19 @@ export const traverseFields = ({
topLevelTableName, topLevelTableName,
}: TraverseFieldArgs) => { }: TraverseFieldArgs) => {
fields.forEach((field) => { fields.forEach((field) => {
// handle simple relationship
if (
depth > 0 &&
(field.type === 'upload' ||
(field.type === 'relationship' && !field.hasMany && typeof field.relationTo === 'string'))
) {
if (field.localized) {
_locales.with[`${path}${field.name}`] = true
} else {
currentArgs.with[`${path}${field.name}`] = true
}
}
if (field.type === 'collapsible' || field.type === 'row') { if (field.type === 'collapsible' || field.type === 'row') {
traverseFields({ traverseFields({
_locales, _locales,
@@ -84,11 +97,19 @@ export const traverseFields = ({
const arrayTableNameWithLocales = `${arrayTableName}${adapter.localesSuffix}` const arrayTableNameWithLocales = `${arrayTableName}${adapter.localesSuffix}`
if (adapter.tables[arrayTableNameWithLocales]) withArray.with._locales = _locales if (adapter.tables[arrayTableNameWithLocales]) {
withArray.with._locales = {
columns: {
id: false,
_parentID: false,
},
with: {},
}
}
currentArgs.with[`${path}${field.name}`] = withArray currentArgs.with[`${path}${field.name}`] = withArray
traverseFields({ traverseFields({
_locales, _locales: withArray.with._locales,
adapter, adapter,
currentArgs: withArray, currentArgs: withArray,
currentTableName: arrayTableName, currentTableName: arrayTableName,
@@ -137,12 +158,14 @@ export const traverseFields = ({
) )
if (adapter.tables[`${tableName}${adapter.localesSuffix}`]) { if (adapter.tables[`${tableName}${adapter.localesSuffix}`]) {
withBlock.with._locales = _locales withBlock.with._locales = {
with: {},
}
} }
topLevelArgs.with[blockKey] = withBlock topLevelArgs.with[blockKey] = withBlock
traverseFields({ traverseFields({
_locales, _locales: withBlock.with._locales,
adapter, adapter,
currentArgs: withBlock, currentArgs: withBlock,
currentTableName: tableName, currentTableName: tableName,

View File

@@ -4,6 +4,7 @@ import type { SanitizedCollectionConfig } from 'payload/types'
import { pgEnum, pgSchema, pgTable } from 'drizzle-orm/pg-core' import { pgEnum, pgSchema, pgTable } from 'drizzle-orm/pg-core'
import { buildVersionCollectionFields, buildVersionGlobalFields } from 'payload/versions' import { buildVersionCollectionFields, buildVersionGlobalFields } from 'payload/versions'
import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from './types.js' import type { PostgresAdapter } from './types.js'
@@ -25,16 +26,25 @@ export const init: Init = function init(this: PostgresAdapter) {
} }
this.payload.config.collections.forEach((collection: SanitizedCollectionConfig) => { this.payload.config.collections.forEach((collection: SanitizedCollectionConfig) => {
const tableName = createTableName({ createTableName({
adapter: this, adapter: this,
config: collection, config: collection,
}) })
if (collection.versions) {
createTableName({
adapter: this,
config: collection,
versions: true,
versionsCustomName: true,
})
}
})
this.payload.config.collections.forEach((collection: SanitizedCollectionConfig) => {
const tableName = this.tableNameMap.get(toSnakeCase(collection.slug))
buildTable({ buildTable({
adapter: this, adapter: this,
buildNumbers: true,
buildRelationships: true,
buildTexts: true,
disableNotNull: !!collection?.versions?.drafts, disableNotNull: !!collection?.versions?.drafts,
disableUnique: false, disableUnique: false,
fields: collection.fields, fields: collection.fields,
@@ -44,19 +54,13 @@ export const init: Init = function init(this: PostgresAdapter) {
}) })
if (collection.versions) { if (collection.versions) {
const versionsTableName = createTableName({ const versionsTableName = this.tableNameMap.get(
adapter: this, `_${toSnakeCase(collection.slug)}${this.versionsSuffix}`,
config: collection, )
versions: true,
versionsCustomName: true,
})
const versionFields = buildVersionCollectionFields(collection) const versionFields = buildVersionCollectionFields(collection)
buildTable({ buildTable({
adapter: this, adapter: this,
buildNumbers: true,
buildRelationships: true,
buildTexts: true,
disableNotNull: !!collection.versions?.drafts, disableNotNull: !!collection.versions?.drafts,
disableUnique: true, disableUnique: true,
fields: versionFields, fields: versionFields,
@@ -72,9 +76,6 @@ export const init: Init = function init(this: PostgresAdapter) {
buildTable({ buildTable({
adapter: this, adapter: this,
buildNumbers: true,
buildRelationships: true,
buildTexts: true,
disableNotNull: !!global?.versions?.drafts, disableNotNull: !!global?.versions?.drafts,
disableUnique: false, disableUnique: false,
fields: global.fields, fields: global.fields,
@@ -94,9 +95,6 @@ export const init: Init = function init(this: PostgresAdapter) {
buildTable({ buildTable({
adapter: this, adapter: this,
buildNumbers: true,
buildRelationships: true,
buildTexts: true,
disableNotNull: !!global.versions?.drafts, disableNotNull: !!global.versions?.drafts,
disableUnique: true, disableUnique: true,
fields: versionFields, fields: versionFields,

View File

@@ -0,0 +1,10 @@
const imports = `import { migratePostgresV2toV3 } from '@payloadcms/db-postgres/migration-utils'`
const upSQL = ` await migratePostgresV2toV3({
// enables logging of changes that will be made to the database
debug: false,
payload,
req,
})
`
export { imports, upSQL }

View File

@@ -0,0 +1,237 @@
import type { Payload } from 'payload'
import type { Field, PayloadRequestWithData } from 'payload/types'
import type { DrizzleTransaction, PostgresAdapter } from '../../../types.js'
import type { DocsToResave } from '../types.js'
import { upsertRow } from '../../../upsertRow/index.js'
import { traverseFields } from './traverseFields.js'
type Args = {
adapter: PostgresAdapter
collectionSlug?: string
db: DrizzleTransaction
debug: boolean
docsToResave: DocsToResave
fields: Field[]
globalSlug?: string
isVersions: boolean
payload: Payload
req: PayloadRequestWithData
tableName: string
}
export const fetchAndResave = async ({
adapter,
collectionSlug,
db,
debug,
docsToResave,
fields,
globalSlug,
isVersions,
payload,
req,
tableName,
}: Args) => {
for (const [id, rows] of Object.entries(docsToResave)) {
if (collectionSlug) {
const collectionConfig = payload.collections[collectionSlug].config
if (collectionConfig) {
if (isVersions) {
const doc = await payload.findVersionByID({
id,
collection: collectionSlug,
depth: 0,
fallbackLocale: null,
locale: 'all',
req,
showHiddenFields: true,
})
if (debug) {
payload.logger.info(
`The collection "${collectionConfig.slug}" version with ID ${id} will be migrated`,
)
}
traverseFields({
doc,
fields,
path: '',
rows,
})
try {
await upsertRow({
id: doc.id,
adapter,
data: doc,
db,
fields,
ignoreResult: true,
operation: 'update',
req,
tableName,
})
} catch (err) {
payload.logger.error(
`"${collectionConfig.slug}" version with ID ${doc.id} FAILED TO MIGRATE`,
)
throw err
}
if (debug) {
payload.logger.info(
`"${collectionConfig.slug}" version with ID ${doc.id} migrated successfully!`,
)
}
} else {
const doc = await payload.findByID({
id,
collection: collectionSlug,
depth: 0,
fallbackLocale: null,
locale: 'all',
req,
showHiddenFields: true,
})
if (debug) {
payload.logger.info(
`The collection "${collectionConfig.slug}" with ID ${doc.id} will be migrated`,
)
}
traverseFields({
doc,
fields,
path: '',
rows,
})
try {
await upsertRow({
id: doc.id,
adapter,
data: doc,
db,
fields,
ignoreResult: true,
operation: 'update',
req,
tableName,
})
} catch (err) {
payload.logger.error(
`The collection "${collectionConfig.slug}" with ID ${doc.id} has FAILED TO MIGRATE`,
)
throw err
}
if (debug) {
payload.logger.info(
`The collection "${collectionConfig.slug}" with ID ${doc.id} has migrated successfully!`,
)
}
}
}
}
if (globalSlug) {
const globalConfig = payload.config.globals?.find((global) => global.slug === globalSlug)
if (globalConfig) {
if (isVersions) {
const { docs } = await payload.findGlobalVersions({
slug: globalSlug,
depth: 0,
fallbackLocale: null,
limit: 0,
locale: 'all',
req,
showHiddenFields: true,
})
if (debug) {
payload.logger.info(`${docs.length} global "${globalSlug}" versions will be migrated`)
}
for (const doc of docs) {
traverseFields({
doc,
fields,
path: '',
rows,
})
try {
await upsertRow({
id: doc.id,
adapter,
data: doc,
db,
fields,
ignoreResult: true,
operation: 'update',
req,
tableName,
})
} catch (err) {
payload.logger.error(`"${globalSlug}" version with ID ${doc.id} FAILED TO MIGRATE`)
throw err
}
if (debug) {
payload.logger.info(
`"${globalSlug}" version with ID ${doc.id} migrated successfully!`,
)
}
}
} else {
const doc = await payload.findGlobal({
slug: globalSlug,
depth: 0,
fallbackLocale: null,
locale: 'all',
req,
showHiddenFields: true,
})
traverseFields({
doc,
fields,
path: '',
rows,
})
try {
await upsertRow({
id: doc.id,
adapter,
data: doc,
db,
fields,
ignoreResult: true,
operation: 'update',
req,
tableName,
})
} catch (err) {
payload.logger.error(`The global "${globalSlug}" has FAILED TO MIGRATE`)
throw err
}
if (debug) {
payload.logger.info(`The global "${globalSlug}" has migrated successfully!`)
}
}
}
}
}
}

View File

@@ -0,0 +1,215 @@
import type { Field } from 'payload/types'
import { tabHasName } from 'payload/types'
type Args = {
doc: Record<string, unknown>
fields: Field[]
locale?: string
path: string
rows: Record<string, unknown>[]
}
export const traverseFields = ({ doc, fields, locale, path, rows }: Args) => {
fields.forEach((field) => {
switch (field.type) {
case 'group': {
const newPath = `${path ? `${path}.` : ''}${field.name}`
const newDoc = doc?.[field.name]
if (typeof newDoc === 'object' && newDoc !== null) {
if (field.localized) {
Object.entries(newDoc).forEach(([locale, localeDoc]) => {
return traverseFields({
doc: localeDoc,
fields: field.fields,
locale,
path: newPath,
rows,
})
})
} else {
return traverseFields({
doc: newDoc as Record<string, unknown>,
fields: field.fields,
path: newPath,
rows,
})
}
}
break
}
case 'row':
case 'collapsible': {
return traverseFields({
doc,
fields: field.fields,
path,
rows,
})
}
case 'array': {
const rowData = doc?.[field.name]
if (field.localized && typeof rowData === 'object' && rowData !== null) {
Object.entries(rowData).forEach(([locale, localeRows]) => {
if (Array.isArray(localeRows)) {
localeRows.forEach((row, i) => {
return traverseFields({
doc: row as Record<string, unknown>,
fields: field.fields,
locale,
path: `${path ? `${path}.` : ''}${field.name}.${i}`,
rows,
})
})
}
})
}
if (Array.isArray(rowData)) {
rowData.forEach((row, i) => {
return traverseFields({
doc: row as Record<string, unknown>,
fields: field.fields,
path: `${path ? `${path}.` : ''}${field.name}.${i}`,
rows,
})
})
}
break
}
case 'blocks': {
const rowData = doc?.[field.name]
if (field.localized && typeof rowData === 'object' && rowData !== null) {
Object.entries(rowData).forEach(([locale, localeRows]) => {
if (Array.isArray(localeRows)) {
localeRows.forEach((row, i) => {
const matchedBlock = field.blocks.find((block) => block.slug === row.blockType)
if (matchedBlock) {
return traverseFields({
doc: row as Record<string, unknown>,
fields: matchedBlock.fields,
locale,
path: `${path ? `${path}.` : ''}${field.name}.${i}`,
rows,
})
}
})
}
})
}
if (Array.isArray(rowData)) {
rowData.forEach((row, i) => {
const matchedBlock = field.blocks.find((block) => block.slug === row.blockType)
if (matchedBlock) {
return traverseFields({
doc: row as Record<string, unknown>,
fields: matchedBlock.fields,
path: `${path ? `${path}.` : ''}${field.name}.${i}`,
rows,
})
}
})
}
break
}
case 'tabs': {
return field.tabs.forEach((tab) => {
if (tabHasName(tab)) {
const newDoc = doc?.[tab.name]
const newPath = `${path ? `${path}.` : ''}${tab.name}`
if (typeof newDoc === 'object' && newDoc !== null) {
if (tab.localized) {
Object.entries(newDoc).forEach(([locale, localeDoc]) => {
return traverseFields({
doc: localeDoc,
fields: tab.fields,
locale,
path: newPath,
rows,
})
})
} else {
return traverseFields({
doc: newDoc as Record<string, unknown>,
fields: tab.fields,
path: newPath,
rows,
})
}
}
} else {
traverseFields({
doc,
fields: tab.fields,
path,
rows,
})
}
})
}
case 'relationship':
case 'upload': {
if (typeof field.relationTo === 'string') {
if (field.type === 'upload' || !field.hasMany) {
const relationshipPath = `${path ? `${path}.` : ''}${field.name}`
if (field.localized) {
const matchedRelationshipsWithLocales = rows.filter(
(row) => row.path === relationshipPath,
)
if (matchedRelationshipsWithLocales.length && !doc[field.name]) {
doc[field.name] = {}
}
const newDoc = doc[field.name] as Record<string, unknown>
matchedRelationshipsWithLocales.forEach((localeRow) => {
if (typeof localeRow.locale === 'string') {
const [, id] = Object.entries(localeRow).find(
([key, val]) =>
val !== null && !['id', 'locale', 'order', 'parent_id', 'path'].includes(key),
)
newDoc[localeRow.locale] = id
}
})
} else {
const matchedRelationship = rows.find((row) => {
const matchesPath = row.path === relationshipPath
if (locale) return matchesPath && locale === row.locale
return row.path === relationshipPath
})
if (matchedRelationship) {
const [, id] = Object.entries(matchedRelationship).find(
([key, val]) =>
val !== null && !['id', 'locale', 'order', 'parent_id', 'path'].includes(key),
)
doc[field.name] = id
}
}
}
}
}
}
})
}

View File

@@ -0,0 +1,74 @@
export type Groups =
| 'addColumn'
| 'addConstraint'
| 'dropColumn'
| 'dropConstraint'
| 'dropTable'
| 'notNull'
/**
* Convert an "ADD COLUMN" statement to an "ALTER COLUMN" statement
* example: ALTER TABLE "pages_blocks_my_block" ADD COLUMN "person_id" integer NOT NULL;
* to: ALTER TABLE "pages_blocks_my_block" ALTER COLUMN "person_id" SET NOT NULL;
* @param sql
*/
function convertAddColumnToAlterColumn(sql) {
// Regular expression to match the ADD COLUMN statement with its constraints
const regex = /ALTER TABLE ("[^"]+") ADD COLUMN ("[^"]+") [\w\s]+ NOT NULL;/
// Replace the matched part with "ALTER COLUMN ... SET NOT NULL;"
return sql.replace(regex, 'ALTER TABLE $1 ALTER COLUMN $2 SET NOT NULL;')
}
export const groupUpSQLStatements = (list: string[]): Record<Groups, string[]> => {
const groups = {
addColumn: 'ADD COLUMN',
// example: ALTER TABLE "posts" ADD COLUMN "category_id" integer
addConstraint: 'ADD CONSTRAINT',
//example:
// DO $$ BEGIN
// ALTER TABLE "pages_blocks_my_block" ADD CONSTRAINT "pages_blocks_my_block_person_id_users_id_fk" FOREIGN KEY ("person_id") REFERENCES "users"("id") ON DELETE cascade ON UPDATE no action;
// EXCEPTION
// WHEN duplicate_object THEN null;
// END $$;
dropColumn: 'DROP COLUMN',
// example: ALTER TABLE "_posts_v_rels" DROP COLUMN IF EXISTS "posts_id";
dropConstraint: 'DROP CONSTRAINT',
// example: ALTER TABLE "_posts_v_rels" DROP CONSTRAINT "_posts_v_rels_posts_fk";
dropTable: 'DROP TABLE',
// example: DROP TABLE "pages_rels";
notNull: 'NOT NULL',
// example: ALTER TABLE "pages_blocks_my_block" ALTER COLUMN "person_id" SET NOT NULL;
}
const result = Object.keys(groups).reduce((result, group: Groups) => {
result[group] = []
return result
}, {}) as Record<Groups, string[]>
for (const line of list) {
Object.entries(groups).some(([key, value]) => {
if (line.endsWith('NOT NULL;')) {
// split up the ADD COLUMN and ALTER COLUMN NOT NULL statements
// example: ALTER TABLE "pages_blocks_my_block" ADD COLUMN "person_id" integer NOT NULL;
// becomes two separate statements:
// 1. ALTER TABLE "pages_blocks_my_block" ADD COLUMN "person_id" integer;
// 2. ALTER TABLE "pages_blocks_my_block" ALTER COLUMN "person_id" SET NOT NULL;
result.addColumn.push(line.replace(' NOT NULL;', ';'))
result.notNull.push(convertAddColumnToAlterColumn(line))
return true
}
if (line.includes(value)) {
result[key].push(line)
return true
}
})
}
return result
}

View File

@@ -0,0 +1,278 @@
import type { DrizzleSnapshotJSON } from 'drizzle-kit/payload'
import type { Payload } from 'payload'
import type { PayloadRequestWithData } from 'payload/types'
import { sql } from 'drizzle-orm'
import fs from 'fs'
import { createRequire } from 'module'
import { buildVersionCollectionFields, buildVersionGlobalFields } from 'payload/versions'
import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from '../../types.js'
import type { PathsToQuery } from './types.js'
import { groupUpSQLStatements } from './groupUpSQLStatements.js'
import { migrateRelationships } from './migrateRelationships.js'
import { traverseFields } from './traverseFields.js'
const require = createRequire(import.meta.url)
type Args = {
debug?: boolean
payload: Payload
req: PayloadRequestWithData
}
/**
* Moves upload and relationship columns from the join table and into the tables while moving data
* This is done in the following order:
* ADD COLUMNs
* -- manipulate data to move relationships to new columns
* ADD CONSTRAINTs
* NOT NULLs
* DROP TABLEs
* DROP CONSTRAINTs
* DROP COLUMNs
* @param debug
* @param payload
* @param req
*/
export const migratePostgresV2toV3 = async ({ debug, payload, req }: Args) => {
const adapter = payload.db as PostgresAdapter
const db = adapter.sessions[req.transactionID]?.db
const dir = payload.db.migrationDir
// get the drizzle migrateUpSQL from drizzle using the last schema
const { generateDrizzleJson, generateMigration } = require('drizzle-kit/payload')
const drizzleJsonAfter = generateDrizzleJson(adapter.schema)
// Get latest migration snapshot
const latestSnapshot = fs
.readdirSync(dir)
.filter((file) => file.endsWith('.json'))
.sort()
.reverse()?.[0]
if (!latestSnapshot) {
throw new Error(
`No previous migration schema file found! A prior migration from v2 is required to migrate to v3.`,
)
}
const drizzleJsonBefore = JSON.parse(
fs.readFileSync(`${dir}/${latestSnapshot}`, 'utf8'),
) as DrizzleSnapshotJSON
const generatedSQL = await generateMigration(drizzleJsonBefore, drizzleJsonAfter)
if (!generatedSQL.length) {
payload.logger.info(`No schema changes needed.`)
process.exit(0)
}
const sqlUpStatements = groupUpSQLStatements(generatedSQL)
const addColumnsStatement = sqlUpStatements.addColumn.join('\n')
if (debug) {
payload.logger.info('CREATING NEW RELATIONSHIP COLUMNS')
payload.logger.info(addColumnsStatement)
}
await db.execute(sql.raw(addColumnsStatement))
for (const collection of payload.config.collections) {
const tableName = adapter.tableNameMap.get(toSnakeCase(collection.slug))
const pathsToQuery: PathsToQuery = new Set()
traverseFields({
adapter,
collectionSlug: collection.slug,
columnPrefix: '',
db,
disableNotNull: false,
fields: collection.fields,
isVersions: false,
newTableName: tableName,
parentTableName: tableName,
path: '',
pathsToQuery,
payload,
rootTableName: tableName,
})
await migrateRelationships({
adapter,
collectionSlug: collection.slug,
db,
debug,
fields: collection.fields,
isVersions: false,
pathsToQuery,
payload,
req,
tableName,
})
if (collection.versions) {
const versionsTableName = adapter.tableNameMap.get(
`_${toSnakeCase(collection.slug)}${adapter.versionsSuffix}`,
)
const versionFields = buildVersionCollectionFields(collection)
const versionPathsToQuery: PathsToQuery = new Set()
traverseFields({
adapter,
collectionSlug: collection.slug,
columnPrefix: '',
db,
disableNotNull: true,
fields: versionFields,
isVersions: true,
newTableName: versionsTableName,
parentTableName: versionsTableName,
path: '',
pathsToQuery: versionPathsToQuery,
payload,
rootTableName: versionsTableName,
})
await migrateRelationships({
adapter,
collectionSlug: collection.slug,
db,
debug,
fields: versionFields,
isVersions: true,
pathsToQuery: versionPathsToQuery,
payload,
req,
tableName: versionsTableName,
})
}
}
for (const global of payload.config.globals) {
const tableName = adapter.tableNameMap.get(toSnakeCase(global.slug))
const pathsToQuery: PathsToQuery = new Set()
traverseFields({
adapter,
columnPrefix: '',
db,
disableNotNull: false,
fields: global.fields,
globalSlug: global.slug,
isVersions: false,
newTableName: tableName,
parentTableName: tableName,
path: '',
pathsToQuery,
payload,
rootTableName: tableName,
})
await migrateRelationships({
adapter,
db,
debug,
fields: global.fields,
globalSlug: global.slug,
isVersions: false,
pathsToQuery,
payload,
req,
tableName,
})
if (global.versions) {
const versionsTableName = adapter.tableNameMap.get(
`_${toSnakeCase(global.slug)}${adapter.versionsSuffix}`,
)
const versionFields = buildVersionGlobalFields(global)
const versionPathsToQuery: PathsToQuery = new Set()
traverseFields({
adapter,
columnPrefix: '',
db,
disableNotNull: true,
fields: versionFields,
globalSlug: global.slug,
isVersions: true,
newTableName: versionsTableName,
parentTableName: versionsTableName,
path: '',
pathsToQuery: versionPathsToQuery,
payload,
rootTableName: versionsTableName,
})
await migrateRelationships({
adapter,
db,
debug,
fields: versionFields,
globalSlug: global.slug,
isVersions: true,
pathsToQuery: versionPathsToQuery,
payload,
req,
tableName: versionsTableName,
})
}
}
// ADD CONSTRAINT
const addConstraintsStatement = sqlUpStatements.addConstraint.join('\n')
if (debug) {
payload.logger.info('ADDING CONSTRAINTS')
payload.logger.info(addConstraintsStatement)
}
await db.execute(sql.raw(addConstraintsStatement))
// NOT NULL
const notNullStatements = sqlUpStatements.notNull.join('\n')
if (debug) {
payload.logger.info('NOT NULL CONSTRAINTS')
payload.logger.info(notNullStatements)
}
await db.execute(sql.raw(notNullStatements))
// DROP TABLE
const dropTablesStatement = sqlUpStatements.dropTable.join('\n')
if (debug) {
payload.logger.info('DROPPING TABLES')
payload.logger.info(dropTablesStatement)
}
await db.execute(sql.raw(dropTablesStatement))
// DROP CONSTRAINT
const dropConstraintsStatement = sqlUpStatements.dropConstraint.join('\n')
if (debug) {
payload.logger.info('DROPPING CONSTRAINTS')
payload.logger.info(dropConstraintsStatement)
}
await db.execute(sql.raw(dropConstraintsStatement))
// DROP COLUMN
const dropColumnsStatement = sqlUpStatements.dropColumn.join('\n')
if (debug) {
payload.logger.info('DROPPING COLUMNS')
payload.logger.info(dropColumnsStatement)
}
await db.execute(sql.raw(dropColumnsStatement))
}

View File

@@ -0,0 +1,102 @@
import type { Field, Payload, PayloadRequestWithData } from 'payload/types'
import { sql } from 'drizzle-orm'
import type { DrizzleTransaction, PostgresAdapter } from '../../types.js'
import type { DocsToResave, PathsToQuery } from './types.js'
import { fetchAndResave } from './fetchAndResave/index.js'
type Args = {
adapter: PostgresAdapter
collectionSlug?: string
db: DrizzleTransaction
debug: boolean
fields: Field[]
globalSlug?: string
isVersions: boolean
pathsToQuery: PathsToQuery
payload: Payload
req: PayloadRequestWithData
tableName: string
}
export const migrateRelationships = async ({
adapter,
collectionSlug,
db,
debug,
fields,
globalSlug,
isVersions,
pathsToQuery,
payload,
req,
tableName,
}: Args) => {
if (pathsToQuery.size === 0) return
let offset = 0
let paginationResult
const where = Array.from(pathsToQuery).reduce((statement, path, i) => {
return (statement += `
"${tableName}${adapter.relationshipsSuffix}"."path" LIKE '${path}'${pathsToQuery.size !== i + 1 ? ' OR' : ''}
`)
}, '')
while (typeof paginationResult === 'undefined' || paginationResult.rows.length > 0) {
const paginationStatement = `SELECT DISTINCT parent_id FROM ${tableName}${adapter.relationshipsSuffix} WHERE
${where} ORDER BY parent_id LIMIT 500 OFFSET ${offset * 500};
`
paginationResult = await adapter.drizzle.execute(sql.raw(`${paginationStatement}`))
if (paginationResult.rows.length === 0) return
offset += 1
const statement = `SELECT * FROM ${tableName}${adapter.relationshipsSuffix} WHERE
(${where}) AND parent_id IN (${paginationResult.rows.map((row) => row.parent_id).join(', ')});
`
if (debug) {
payload.logger.info('FINDING ROWS TO MIGRATE')
payload.logger.info(statement)
}
const result = await adapter.drizzle.execute(sql.raw(`${statement}`))
const docsToResave: DocsToResave = {}
result.rows.forEach((row) => {
const parentID = row.parent_id
if (typeof parentID === 'string' || typeof parentID === 'number') {
if (!docsToResave[parentID]) docsToResave[parentID] = []
docsToResave[parentID].push(row)
}
})
await fetchAndResave({
adapter,
collectionSlug,
db,
debug,
docsToResave,
fields,
globalSlug,
isVersions,
payload,
req,
tableName,
})
}
const deleteStatement = `DELETE FROM ${tableName}${adapter.relationshipsSuffix} WHERE ${where}`
if (debug) {
payload.logger.info('DELETING ROWS')
payload.logger.info(deleteStatement)
}
await db.execute(sql.raw(`${deleteStatement}`))
}

View File

@@ -0,0 +1,116 @@
import type { Payload } from 'payload'
import { type Field, tabHasName } from 'payload/types'
import toSnakeCase from 'to-snake-case'
import type { DrizzleTransaction, PostgresAdapter } from '../../types.js'
import type { PathsToQuery } from './types.js'
type Args = {
adapter: PostgresAdapter
collectionSlug?: string
columnPrefix: string
db: DrizzleTransaction
disableNotNull: boolean
fields: Field[]
globalSlug?: string
isVersions: boolean
newTableName: string
parentTableName: string
path: string
pathsToQuery: PathsToQuery
payload: Payload
rootTableName: string
}
export const traverseFields = (args: Args) => {
args.fields.forEach((field) => {
switch (field.type) {
case 'group': {
let newTableName = `${args.newTableName}_${toSnakeCase(field.name)}`
if (field.localized && args.payload.config.localization) {
newTableName += args.adapter.localesSuffix
}
return traverseFields({
...args,
columnPrefix: `${args.columnPrefix}${toSnakeCase(field.name)}_`,
fields: field.fields,
newTableName,
path: `${args.path ? `${args.path}.` : ''}${field.name}`,
})
}
case 'row':
case 'collapsible': {
return traverseFields({
...args,
fields: field.fields,
})
}
case 'array': {
const newTableName = args.adapter.tableNameMap.get(
`${args.newTableName}_${toSnakeCase(field.name)}`,
)
return traverseFields({
...args,
columnPrefix: '',
fields: field.fields,
newTableName,
parentTableName: newTableName,
path: `${args.path ? `${args.path}.` : ''}${field.name}.%`,
})
}
case 'blocks': {
return field.blocks.forEach((block) => {
const newTableName = args.adapter.tableNameMap.get(
`${args.rootTableName}_blocks_${toSnakeCase(block.slug)}`,
)
traverseFields({
...args,
columnPrefix: '',
fields: block.fields,
newTableName,
parentTableName: newTableName,
path: `${args.path ? `${args.path}.` : ''}${field.name}.%`,
})
})
}
case 'tabs': {
return field.tabs.forEach((tab) => {
if (tabHasName(tab)) {
args.columnPrefix = `${args.columnPrefix}_${toSnakeCase(tab.name)}_`
args.path = `${args.path ? `${args.path}.` : ''}${tab.name}`
args.newTableName = `${args.newTableName}_${toSnakeCase(tab.name)}`
if (tab.localized && args.payload.config.localization) {
args.newTableName += args.adapter.localesSuffix
}
}
traverseFields({
...args,
fields: tab.fields,
})
})
}
case 'relationship':
case 'upload': {
if (typeof field.relationTo === 'string') {
if (field.type === 'upload' || !field.hasMany) {
args.pathsToQuery.add(`${args.path ? `${args.path}.` : ''}${field.name}`)
}
}
return null
}
}
})
}

View File

@@ -0,0 +1,9 @@
/**
* Set of all paths which should be moved
* This will be built up into one WHERE query
*/
export type PathsToQuery = Set<string>
export type DocsToResave = {
[id: number | string]: Record<string, unknown>[]
}

View File

@@ -2,14 +2,13 @@ import type { SQL } from 'drizzle-orm'
import type { Field, Where } from 'payload/types' import type { Field, Where } from 'payload/types'
import type { GenericColumn, PostgresAdapter } from '../types.js' import type { GenericColumn, PostgresAdapter } from '../types.js'
import type { BuildQueryJoinAliases, BuildQueryJoins } from './buildQuery.js' import type { BuildQueryJoinAliases } from './buildQuery.js'
import { parseParams } from './parseParams.js' import { parseParams } from './parseParams.js'
export async function buildAndOrConditions({ export async function buildAndOrConditions({
adapter, adapter,
fields, fields,
joinAliases,
joins, joins,
locale, locale,
selectFields, selectFields,
@@ -20,8 +19,7 @@ export async function buildAndOrConditions({
collectionSlug?: string collectionSlug?: string
fields: Field[] fields: Field[]
globalSlug?: string globalSlug?: string
joinAliases: BuildQueryJoinAliases joins: BuildQueryJoinAliases
joins: BuildQueryJoins
locale?: string locale?: string
selectFields: Record<string, GenericColumn> selectFields: Record<string, GenericColumn>
tableName: string tableName: string
@@ -38,7 +36,6 @@ export async function buildAndOrConditions({
const result = await parseParams({ const result = await parseParams({
adapter, adapter,
fields, fields,
joinAliases,
joins, joins,
locale, locale,
selectFields, selectFields,

View File

@@ -26,8 +26,7 @@ type BuildQueryArgs = {
} }
type Result = { type Result = {
joinAliases: BuildQueryJoinAliases joins: BuildQueryJoinAliases
joins: BuildQueryJoins
orderBy: { orderBy: {
column: GenericColumn column: GenericColumn
order: typeof asc | typeof desc order: typeof asc | typeof desc
@@ -46,8 +45,7 @@ const buildQuery = async function buildQuery({
const selectFields: Record<string, GenericColumn> = { const selectFields: Record<string, GenericColumn> = {
id: adapter.tables[tableName].id, id: adapter.tables[tableName].id,
} }
const joins: BuildQueryJoins = {} const joins: BuildQueryJoinAliases = []
const joinAliases: BuildQueryJoinAliases = []
const orderBy: Result['orderBy'] = { const orderBy: Result['orderBy'] = {
column: null, column: null,
@@ -70,7 +68,6 @@ const buildQuery = async function buildQuery({
adapter, adapter,
collectionPath: sortPath, collectionPath: sortPath,
fields, fields,
joinAliases,
joins, joins,
locale, locale,
pathSegments: sortPath.replace(/__/g, '.').split('.'), pathSegments: sortPath.replace(/__/g, '.').split('.'),
@@ -105,7 +102,6 @@ const buildQuery = async function buildQuery({
where = await parseParams({ where = await parseParams({
adapter, adapter,
fields, fields,
joinAliases,
joins, joins,
locale, locale,
selectFields, selectFields,
@@ -115,7 +111,6 @@ const buildQuery = async function buildQuery({
} }
return { return {
joinAliases,
joins, joins,
orderBy, orderBy,
selectFields, selectFields,

View File

@@ -12,7 +12,7 @@ import toSnakeCase from 'to-snake-case'
import { v4 as uuid } from 'uuid' import { v4 as uuid } from 'uuid'
import type { GenericColumn, GenericTable, PostgresAdapter } from '../types.js' import type { GenericColumn, GenericTable, PostgresAdapter } from '../types.js'
import type { BuildQueryJoinAliases, BuildQueryJoins } from './buildQuery.js' import type { BuildQueryJoinAliases } from './buildQuery.js'
type Constraint = { type Constraint = {
columnName: string columnName: string
@@ -38,8 +38,7 @@ type Args = {
constraintPath?: string constraintPath?: string
constraints?: Constraint[] constraints?: Constraint[]
fields: (Field | TabAsField)[] fields: (Field | TabAsField)[]
joinAliases: BuildQueryJoinAliases joins: BuildQueryJoinAliases
joins: BuildQueryJoins
locale?: string locale?: string
pathSegments: string[] pathSegments: string[]
rootTableName?: string rootTableName?: string
@@ -67,7 +66,6 @@ export const getTableColumnFromPath = ({
constraintPath: incomingConstraintPath, constraintPath: incomingConstraintPath,
constraints = [], constraints = [],
fields, fields,
joinAliases,
joins, joins,
locale: incomingLocale, locale: incomingLocale,
pathSegments: incomingSegments, pathSegments: incomingSegments,
@@ -129,7 +127,6 @@ export const getTableColumnFromPath = ({
...tab, ...tab,
type: 'tab', type: 'tab',
})), })),
joinAliases,
joins, joins,
locale, locale,
pathSegments: pathSegments.slice(1), pathSegments: pathSegments.slice(1),
@@ -150,7 +147,6 @@ export const getTableColumnFromPath = ({
constraintPath: `${constraintPath}${field.name}.`, constraintPath: `${constraintPath}${field.name}.`,
constraints, constraints,
fields: field.fields, fields: field.fields,
joinAliases,
joins, joins,
locale, locale,
pathSegments: pathSegments.slice(1), pathSegments: pathSegments.slice(1),
@@ -169,7 +165,6 @@ export const getTableColumnFromPath = ({
constraintPath, constraintPath,
constraints, constraints,
fields: field.fields, fields: field.fields,
joinAliases,
joins, joins,
locale, locale,
pathSegments: pathSegments.slice(1), pathSegments: pathSegments.slice(1),
@@ -185,10 +180,10 @@ export const getTableColumnFromPath = ({
if (locale && field.localized && adapter.payload.config.localization) { if (locale && field.localized && adapter.payload.config.localization) {
newTableName = `${tableName}${adapter.localesSuffix}` newTableName = `${tableName}${adapter.localesSuffix}`
joins[tableName] = eq( joins.push({
adapter.tables[tableName].id, condition: eq(adapter.tables[tableName].id, adapter.tables[newTableName]._parentID),
adapter.tables[newTableName]._parentID, table: adapter.tables[newTableName],
) })
if (locale !== 'all') { if (locale !== 'all') {
constraints.push({ constraints.push({
columnName: '_locale', columnName: '_locale',
@@ -205,7 +200,6 @@ export const getTableColumnFromPath = ({
constraintPath: `${constraintPath}${field.name}.`, constraintPath: `${constraintPath}${field.name}.`,
constraints, constraints,
fields: field.fields, fields: field.fields,
joinAliases,
joins, joins,
locale, locale,
pathSegments: pathSegments.slice(1), pathSegments: pathSegments.slice(1),
@@ -224,10 +218,13 @@ export const getTableColumnFromPath = ({
) )
if (locale && field.localized && adapter.payload.config.localization) { if (locale && field.localized && adapter.payload.config.localization) {
joins[newTableName] = and( joins.push({
eq(adapter.tables[tableName].id, adapter.tables[newTableName].parent), condition: and(
eq(adapter.tables[newTableName]._locale, locale), eq(adapter.tables[tableName].id, adapter.tables[newTableName].parent),
) eq(adapter.tables[newTableName]._locale, locale),
),
table: adapter.tables[newTableName],
})
if (locale !== 'all') { if (locale !== 'all') {
constraints.push({ constraints.push({
columnName: '_locale', columnName: '_locale',
@@ -236,10 +233,10 @@ export const getTableColumnFromPath = ({
}) })
} }
} else { } else {
joins[newTableName] = eq( joins.push({
adapter.tables[tableName].id, condition: eq(adapter.tables[tableName].id, adapter.tables[newTableName].parent),
adapter.tables[newTableName].parent, table: adapter.tables[newTableName],
) })
} }
return { return {
@@ -268,10 +265,10 @@ export const getTableColumnFromPath = ({
] ]
if (locale && field.localized && adapter.payload.config.localization) { if (locale && field.localized && adapter.payload.config.localization) {
joins[newTableName] = and( joins.push({
...joinConstraints, condition: and(...joinConstraints, eq(adapter.tables[newTableName]._locale, locale)),
eq(adapter.tables[newTableName]._locale, locale), table: adapter.tables[newTableName],
) })
if (locale !== 'all') { if (locale !== 'all') {
constraints.push({ constraints.push({
columnName: 'locale', columnName: 'locale',
@@ -280,7 +277,10 @@ export const getTableColumnFromPath = ({
}) })
} }
} else { } else {
joins[newTableName] = and(...joinConstraints) joins.push({
condition: and(...joinConstraints),
table: adapter.tables[newTableName],
})
} }
return { return {
@@ -300,10 +300,13 @@ export const getTableColumnFromPath = ({
constraintPath = `${constraintPath}${field.name}.%.` constraintPath = `${constraintPath}${field.name}.%.`
if (locale && field.localized && adapter.payload.config.localization) { if (locale && field.localized && adapter.payload.config.localization) {
joins[newTableName] = and( joins.push({
eq(adapter.tables[tableName].id, adapter.tables[newTableName]._parentID), condition: and(
eq(adapter.tables[newTableName]._locale, locale), eq(adapter.tables[tableName].id, adapter.tables[newTableName]._parentID),
) eq(adapter.tables[newTableName]._locale, locale),
),
table: adapter.tables[newTableName],
})
if (locale !== 'all') { if (locale !== 'all') {
constraints.push({ constraints.push({
columnName: '_locale', columnName: '_locale',
@@ -312,10 +315,10 @@ export const getTableColumnFromPath = ({
}) })
} }
} else { } else {
joins[newTableName] = eq( joins.push({
adapter.tables[tableName].id, condition: eq(adapter.tables[tableName].id, adapter.tables[newTableName]._parentID),
adapter.tables[newTableName]._parentID, table: adapter.tables[newTableName],
) })
} }
return getTableColumnFromPath({ return getTableColumnFromPath({
adapter, adapter,
@@ -323,7 +326,6 @@ export const getTableColumnFromPath = ({
constraintPath, constraintPath,
constraints, constraints,
fields: field.fields, fields: field.fields,
joinAliases,
joins, joins,
locale, locale,
pathSegments: pathSegments.slice(1), pathSegments: pathSegments.slice(1),
@@ -344,18 +346,19 @@ export const getTableColumnFromPath = ({
const blockTypes = Array.isArray(value) ? value : [value] const blockTypes = Array.isArray(value) ? value : [value]
blockTypes.forEach((blockType) => { blockTypes.forEach((blockType) => {
const block = field.blocks.find((block) => block.slug === blockType) const block = field.blocks.find((block) => block.slug === blockType)
newTableName = adapter.tableNameMap.get( newTableName = adapter.tableNameMap.get(
`${tableName}_blocks_${toSnakeCase(block.slug)}`, `${tableName}_blocks_${toSnakeCase(block.slug)}`,
) )
const newAliasTableName = toSnakeCase(uuid())
const newAliasTable = alias(adapter.tables[newTableName], newAliasTableName)
joins[newTableName] = eq( joins.push({
adapter.tables[tableName].id, condition: eq(adapter.tables[tableName].id, newAliasTable._parentID),
adapter.tables[newTableName]._parentID, table: newAliasTable,
) })
constraints.push({ constraints.push({
columnName: '_path', columnName: '_path',
table: adapter.tables[newTableName], table: newAliasTable,
value: pathSegments[0], value: pathSegments[0],
}) })
}) })
@@ -381,7 +384,6 @@ export const getTableColumnFromPath = ({
constraintPath, constraintPath,
constraints: blockConstraints, constraints: blockConstraints,
fields: block.fields, fields: block.fields,
joinAliases,
joins, joins,
locale, locale,
pathSegments: pathSegments.slice(1), pathSegments: pathSegments.slice(1),
@@ -400,10 +402,16 @@ export const getTableColumnFromPath = ({
constraints = constraints.concat(blockConstraints) constraints = constraints.concat(blockConstraints)
selectFields = { ...selectFields, ...blockSelectFields } selectFields = { ...selectFields, ...blockSelectFields }
if (field.localized && adapter.payload.config.localization) { if (field.localized && adapter.payload.config.localization) {
joins[newTableName] = and( joins.push({
eq(adapter.tables[tableName].id, adapter.tables[newTableName]._parentID), condition: and(
eq(adapter.tables[newTableName]._locale, locale), eq(
) (aliasTable || adapter.tables[tableName]).id,
adapter.tables[newTableName]._parentID,
),
eq(adapter.tables[newTableName]._locale, locale),
),
table: adapter.tables[newTableName],
})
if (locale) { if (locale) {
constraints.push({ constraints.push({
columnName: '_locale', columnName: '_locale',
@@ -412,10 +420,13 @@ export const getTableColumnFromPath = ({
}) })
} }
} else { } else {
joins[newTableName] = eq( joins.push({
adapter.tables[tableName].id, condition: eq(
adapter.tables[newTableName]._parentID, (aliasTable || adapter.tables[tableName]).id,
) adapter.tables[newTableName]._parentID,
),
table: adapter.tables[newTableName],
})
} }
return true return true
}) })
@@ -434,116 +445,178 @@ export const getTableColumnFromPath = ({
case 'relationship': case 'relationship':
case 'upload': { case 'upload': {
let relationshipFields
const relationTableName = `${rootTableName}${adapter.relationshipsSuffix}`
const newCollectionPath = pathSegments.slice(1).join('.') const newCollectionPath = pathSegments.slice(1).join('.')
const aliasRelationshipTableName = uuid() if (Array.isArray(field.relationTo) || (field.type === 'relationship' && field.hasMany)) {
const aliasRelationshipTable = alias( let relationshipFields
adapter.tables[relationTableName], const relationTableName = `${rootTableName}${adapter.relationshipsSuffix}`
aliasRelationshipTableName, const aliasRelationshipTableName = uuid()
) const aliasRelationshipTable = alias(
adapter.tables[relationTableName],
aliasRelationshipTableName,
)
// Join in the relationships table // Join in the relationships table
if (locale && field.localized && adapter.payload.config.localization) { if (locale && field.localized && adapter.payload.config.localization) {
joinAliases.push({ joins.push({
condition: and( condition: and(
eq((aliasTable || adapter.tables[rootTableName]).id, aliasRelationshipTable.parent), eq((aliasTable || adapter.tables[rootTableName]).id, aliasRelationshipTable.parent),
eq(aliasRelationshipTable.locale, locale), eq(aliasRelationshipTable.locale, locale),
like(aliasRelationshipTable.path, `${constraintPath}${field.name}`), like(aliasRelationshipTable.path, `${constraintPath}${field.name}`),
), ),
table: aliasRelationshipTable, table: aliasRelationshipTable,
}) })
if (locale !== 'all') { if (locale !== 'all') {
constraints.push({ constraints.push({
columnName: 'locale', columnName: 'locale',
table: aliasRelationshipTable,
value: locale,
})
}
} else {
// Join in the relationships table
joins.push({
condition: and(
eq((aliasTable || adapter.tables[rootTableName]).id, aliasRelationshipTable.parent),
like(aliasRelationshipTable.path, `${constraintPath}${field.name}`),
),
table: aliasRelationshipTable, table: aliasRelationshipTable,
value: locale,
}) })
} }
} else {
// Join in the relationships table
joinAliases.push({
condition: and(
eq((aliasTable || adapter.tables[rootTableName]).id, aliasRelationshipTable.parent),
like(aliasRelationshipTable.path, `${constraintPath}${field.name}`),
),
table: aliasRelationshipTable,
})
}
selectFields[`${relationTableName}.path`] = aliasRelationshipTable.path selectFields[`${relationTableName}.path`] = aliasRelationshipTable.path
let newAliasTable let newAliasTable
if (typeof field.relationTo === 'string') { if (typeof field.relationTo === 'string') {
const relationshipConfig = adapter.payload.collections[field.relationTo].config const relationshipConfig = adapter.payload.collections[field.relationTo].config
newTableName = adapter.tableNameMap.get(toSnakeCase(relationshipConfig.slug)) newTableName = adapter.tableNameMap.get(toSnakeCase(relationshipConfig.slug))
// parent to relationship join table // parent to relationship join table
relationshipFields = relationshipConfig.fields relationshipFields = relationshipConfig.fields
newAliasTable = alias(adapter.tables[newTableName], toSnakeCase(uuid())) newAliasTable = alias(adapter.tables[newTableName], toSnakeCase(uuid()))
joinAliases.push({ joins.push({
condition: eq(newAliasTable.id, aliasRelationshipTable[`${field.relationTo}ID`]), condition: eq(newAliasTable.id, aliasRelationshipTable[`${field.relationTo}ID`]),
table: newAliasTable, table: newAliasTable,
}) })
if (newCollectionPath === '' || newCollectionPath === 'id') { if (newCollectionPath === '' || newCollectionPath === 'id') {
return {
columnName: `${field.relationTo}ID`,
constraints,
field,
table: aliasRelationshipTable,
}
}
} else if (newCollectionPath === 'value') {
const tableColumnsNames = field.relationTo.map((relationTo) => {
const relationTableName = adapter.tableNameMap.get(
toSnakeCase(adapter.payload.collections[relationTo].config.slug),
)
return `"${aliasRelationshipTableName}"."${relationTableName}_id"`
})
return { return {
columnName: `${field.relationTo}ID`,
constraints, constraints,
field, field,
rawColumn: sql.raw(`COALESCE(${tableColumnsNames.join(', ')})`),
table: aliasRelationshipTable, table: aliasRelationshipTable,
} }
} } else if (newCollectionPath === 'relationTo') {
} else if (newCollectionPath === 'value') { const relationTo = Array.isArray(field.relationTo)
const tableColumnsNames = field.relationTo.map((relationTo) => { ? field.relationTo
const relationTableName = adapter.tableNameMap.get( : [field.relationTo]
toSnakeCase(adapter.payload.collections[relationTo].config.slug),
)
return `"${aliasRelationshipTableName}"."${relationTableName}_id"` return {
constraints,
field,
getNotNullColumnByValue: (val) => {
const matchedRelation = relationTo.find((relation) => relation === val)
if (matchedRelation) return `${matchedRelation}ID`
return undefined
},
table: aliasRelationshipTable,
}
} else {
throw new APIError('Not supported')
}
return getTableColumnFromPath({
adapter,
aliasTable: newAliasTable,
collectionPath: newCollectionPath,
constraints,
fields: relationshipFields,
joins,
locale,
pathSegments: pathSegments.slice(1),
rootTableName: newTableName,
selectFields,
tableName: newTableName,
value,
}) })
return { } else if (
constraints, pathSegments.length > 1 &&
field, !(pathSegments.length === 2 && pathSegments[1] === 'id')
rawColumn: sql.raw(`COALESCE(${tableColumnsNames.join(', ')})`), ) {
table: aliasRelationshipTable, // simple relationships
} const columnName = `${columnPrefix}${field.name}`
} else if (newCollectionPath === 'relationTo') { const newTableName = adapter.tableNameMap.get(
const relationTo = Array.isArray(field.relationTo) ? field.relationTo : [field.relationTo] toSnakeCase(adapter.payload.collections[field.relationTo].config.slug),
)
const aliasTableName = uuid()
const newAliasTable = alias(adapter.tables[newTableName], aliasTableName)
return { if (field.localized && adapter.payload.config.localization) {
constraints, const aliasLocaleTableName = uuid()
field, const aliasLocaleTable = alias(
getNotNullColumnByValue: (val) => { adapter.tables[`${rootTableName}${adapter.localesSuffix}`],
const matchedRelation = relationTo.find((relation) => relation === val) aliasLocaleTableName,
if (matchedRelation) return `${matchedRelation}ID` )
return undefined joins.push({
}, condition: and(
table: aliasRelationshipTable, eq(aliasLocaleTable._parentID, adapter.tables[rootTableName].id),
eq(aliasLocaleTable._locale, locale),
),
table: aliasLocaleTable,
})
joins.push({
condition: eq(aliasLocaleTable[columnName], newAliasTable.id),
table: newAliasTable,
})
} else {
joins.push({
condition: eq(
newAliasTable.id,
aliasTable ? aliasTable[columnName] : adapter.tables[tableName][columnName],
),
table: newAliasTable,
})
} }
} else {
throw new APIError('Not supported') return getTableColumnFromPath({
adapter,
aliasTable: newAliasTable,
collectionPath: newCollectionPath,
constraintPath: '',
constraints,
fields: adapter.payload.collections[field.relationTo].config.fields,
joins,
locale,
pathSegments: pathSegments.slice(1),
selectFields,
tableName: newTableName,
value,
})
} }
break
}
return getTableColumnFromPath({ default: {
adapter, // fall through
aliasTable: newAliasTable, break
collectionPath: newCollectionPath,
constraints,
fields: relationshipFields,
joinAliases,
joins,
locale,
pathSegments: pathSegments.slice(1),
rootTableName: newTableName,
selectFields,
tableName: newTableName,
value,
})
} }
} }
@@ -551,11 +624,13 @@ export const getTableColumnFromPath = ({
if (field.localized && adapter.payload.config.localization) { if (field.localized && adapter.payload.config.localization) {
// If localized, we go to localized table and set aliasTable to undefined // If localized, we go to localized table and set aliasTable to undefined
// so it is not picked up below to be used as targetTable // so it is not picked up below to be used as targetTable
const parentTable = aliasTable || adapter.tables[tableName]
newTableName = `${tableName}${adapter.localesSuffix}` newTableName = `${tableName}${adapter.localesSuffix}`
const parentTable = aliasTable || adapter.tables[tableName] joins.push({
condition: eq(parentTable.id, adapter.tables[newTableName]._parentID),
joins[newTableName] = eq(parentTable.id, adapter.tables[newTableName]._parentID) table: adapter.tables[newTableName],
})
aliasTable = undefined aliasTable = undefined

View File

@@ -7,7 +7,7 @@ import { QueryError } from 'payload/errors'
import { validOperators } from 'payload/types' import { validOperators } from 'payload/types'
import type { GenericColumn, PostgresAdapter } from '../types.js' import type { GenericColumn, PostgresAdapter } from '../types.js'
import type { BuildQueryJoinAliases, BuildQueryJoins } from './buildQuery.js' import type { BuildQueryJoinAliases } from './buildQuery.js'
import { buildAndOrConditions } from './buildAndOrConditions.js' import { buildAndOrConditions } from './buildAndOrConditions.js'
import { convertPathToJSONTraversal } from './createJSONQuery/convertPathToJSONTraversal.js' import { convertPathToJSONTraversal } from './createJSONQuery/convertPathToJSONTraversal.js'
@@ -19,8 +19,7 @@ import { sanitizeQueryValue } from './sanitizeQueryValue.js'
type Args = { type Args = {
adapter: PostgresAdapter adapter: PostgresAdapter
fields: Field[] fields: Field[]
joinAliases: BuildQueryJoinAliases joins: BuildQueryJoinAliases
joins: BuildQueryJoins
locale: string locale: string
selectFields: Record<string, GenericColumn> selectFields: Record<string, GenericColumn>
tableName: string tableName: string
@@ -30,7 +29,6 @@ type Args = {
export async function parseParams({ export async function parseParams({
adapter, adapter,
fields, fields,
joinAliases,
joins, joins,
locale, locale,
selectFields, selectFields,
@@ -55,7 +53,6 @@ export async function parseParams({
const builtConditions = await buildAndOrConditions({ const builtConditions = await buildAndOrConditions({
adapter, adapter,
fields, fields,
joinAliases,
joins, joins,
locale, locale,
selectFields, selectFields,
@@ -86,7 +83,6 @@ export async function parseParams({
adapter, adapter,
collectionPath: relationOrPath, collectionPath: relationOrPath,
fields, fields,
joinAliases,
joins, joins,
locale, locale,
pathSegments: relationOrPath.replace(/__/g, '.').split('.'), pathSegments: relationOrPath.replace(/__/g, '.').split('.'),

View File

@@ -2,7 +2,7 @@ import type { QueryPromise, SQL } from 'drizzle-orm'
import type { ChainedMethods } from '../find/chainMethods.js' import type { ChainedMethods } from '../find/chainMethods.js'
import type { DrizzleDB, PostgresAdapter } from '../types.js' import type { DrizzleDB, PostgresAdapter } from '../types.js'
import type { BuildQueryJoinAliases, BuildQueryJoins } from './buildQuery.js' import type { BuildQueryJoinAliases } from './buildQuery.js'
import { chainMethods } from '../find/chainMethods.js' import { chainMethods } from '../find/chainMethods.js'
import { type GenericColumn } from '../types.js' import { type GenericColumn } from '../types.js'
@@ -11,8 +11,7 @@ type Args = {
adapter: PostgresAdapter adapter: PostgresAdapter
chainedMethods?: ChainedMethods chainedMethods?: ChainedMethods
db: DrizzleDB db: DrizzleDB
joinAliases: BuildQueryJoinAliases joins: BuildQueryJoinAliases
joins: BuildQueryJoins
selectFields: Record<string, GenericColumn> selectFields: Record<string, GenericColumn>
tableName: string tableName: string
where: SQL where: SQL
@@ -25,33 +24,23 @@ export const selectDistinct = ({
adapter, adapter,
chainedMethods = [], chainedMethods = [],
db, db,
joinAliases,
joins, joins,
selectFields, selectFields,
tableName, tableName,
where, where,
}: Args): QueryPromise<Record<string, GenericColumn> & { id: number | string }[]> => { }: Args): QueryPromise<Record<string, GenericColumn> & { id: number | string }[]> => {
if (Object.keys(joins).length > 0 || joinAliases.length > 0) { if (Object.keys(joins).length > 0) {
if (where) { if (where) {
chainedMethods.push({ args: [where], method: 'where' }) chainedMethods.push({ args: [where], method: 'where' })
} }
joinAliases.forEach(({ condition, table }) => { joins.forEach(({ condition, table }) => {
chainedMethods.push({ chainedMethods.push({
args: [table, condition], args: [table, condition],
method: 'leftJoin', method: 'leftJoin',
}) })
}) })
Object.entries(joins).forEach(([joinTable, condition]) => {
if (joinTable) {
chainedMethods.push({
args: [adapter.tables[joinTable], condition],
method: 'leftJoin',
})
}
})
return chainMethods({ return chainMethods({
methods: chainedMethods, methods: chainedMethods,
query: db.selectDistinct(selectFields).from(adapter.tables[tableName]), query: db.selectDistinct(selectFields).from(adapter.tables[tableName]),

View File

@@ -34,17 +34,18 @@ export type BaseExtraConfig = Record<
(cols: GenericColumns) => ForeignKeyBuilder | IndexBuilder | UniqueConstraintBuilder (cols: GenericColumns) => ForeignKeyBuilder | IndexBuilder | UniqueConstraintBuilder
> >
export type RelationMap = Map<string, { localized: boolean; target: string; type: 'many' | 'one' }>
type Args = { type Args = {
adapter: PostgresAdapter adapter: PostgresAdapter
baseColumns?: Record<string, PgColumnBuilder> baseColumns?: Record<string, PgColumnBuilder>
baseExtraConfig?: BaseExtraConfig baseExtraConfig?: BaseExtraConfig
buildNumbers?: boolean buildNumbers?: boolean
buildRelationships?: boolean buildRelationships?: boolean
buildTexts?: boolean
disableNotNull: boolean disableNotNull: boolean
disableUnique: boolean disableUnique: boolean
fields: Field[] fields: Field[]
rootRelationsToBuild?: Map<string, string> rootRelationsToBuild?: RelationMap
rootRelationships?: Set<string> rootRelationships?: Set<string>
rootTableIDColType?: string rootTableIDColType?: string
rootTableName?: string rootTableName?: string
@@ -56,16 +57,13 @@ type Args = {
type Result = { type Result = {
hasManyNumberField: 'index' | boolean hasManyNumberField: 'index' | boolean
hasManyTextField: 'index' | boolean hasManyTextField: 'index' | boolean
relationsToBuild: Map<string, string> relationsToBuild: RelationMap
} }
export const buildTable = ({ export const buildTable = ({
adapter, adapter,
baseColumns = {}, baseColumns = {},
baseExtraConfig = {}, baseExtraConfig = {},
buildNumbers,
buildRelationships,
buildTexts,
disableNotNull, disableNotNull,
disableUnique = false, disableUnique = false,
fields, fields,
@@ -77,6 +75,7 @@ export const buildTable = ({
timestamps, timestamps,
versions, versions,
}: Args): Result => { }: Args): Result => {
const isRoot = !incomingRootTableName
const rootTableName = incomingRootTableName || tableName const rootTableName = incomingRootTableName || tableName
const columns: Record<string, PgColumnBuilder> = baseColumns const columns: Record<string, PgColumnBuilder> = baseColumns
const indexes: Record<string, (cols: GenericColumns) => IndexBuilder> = {} const indexes: Record<string, (cols: GenericColumns) => IndexBuilder> = {}
@@ -93,7 +92,7 @@ export const buildTable = ({
let relationshipsTable: GenericTable | PgTableWithColumns<any> let relationshipsTable: GenericTable | PgTableWithColumns<any>
// Drizzle relations // Drizzle relations
const relationsToBuild: Map<string, string> = new Map() const relationsToBuild: RelationMap = new Map()
const idColType: IDType = setColumnID({ adapter, columns, fields }) const idColType: IDType = setColumnID({ adapter, columns, fields })
@@ -106,9 +105,6 @@ export const buildTable = ({
hasManyTextField, hasManyTextField,
} = traverseFields({ } = traverseFields({
adapter, adapter,
buildNumbers,
buildRelationships,
buildTexts,
columns, columns,
disableNotNull, disableNotNull,
disableUnique, disableUnique,
@@ -126,6 +122,15 @@ export const buildTable = ({
versions, versions,
}) })
// split the relationsToBuild by localized and non-localized
const localizedRelations = new Map()
const nonLocalizedRelations = new Map()
relationsToBuild.forEach(({ type, localized, target }, key) => {
const map = localized ? localizedRelations : nonLocalizedRelations
map.set(key, { type, target })
})
if (timestamps) { if (timestamps) {
columns.createdAt = timestamp('created_at', { columns.createdAt = timestamp('created_at', {
mode: 'string', mode: 'string',
@@ -159,7 +164,7 @@ export const buildTable = ({
adapter.tables[tableName] = table adapter.tables[tableName] = table
if (hasLocalizedField) { if (hasLocalizedField || localizedRelations.size) {
const localeTableName = `${tableName}${adapter.localesSuffix}` const localeTableName = `${tableName}${adapter.localesSuffix}`
localesColumns.id = serial('id').primaryKey() localesColumns.id = serial('id').primaryKey()
localesColumns._locale = adapter.enums.enum__locales('_locale').notNull() localesColumns._locale = adapter.enums.enum__locales('_locale').notNull()
@@ -187,114 +192,134 @@ export const buildTable = ({
adapter.tables[localeTableName] = localesTable adapter.tables[localeTableName] = localesTable
const localesTableRelations = relations(localesTable, ({ one }) => ({ adapter.relations[`relations_${localeTableName}`] = relations(localesTable, ({ many, one }) => {
_parentID: one(table, { const result: Record<string, Relation<string>> = {}
result._parentID = one(table, {
fields: [localesTable._parentID], fields: [localesTable._parentID],
references: [table.id], references: [table.id],
}), // name the relationship by what the many() relationName is
})) relationName: '_locales',
})
adapter.relations[`relations_${localeTableName}`] = localesTableRelations localizedRelations.forEach(({ type, target }, key) => {
if (type === 'one') {
result[key] = one(adapter.tables[target], {
fields: [localesTable[key]],
references: [adapter.tables[target].id],
relationName: key,
})
}
if (type === 'many') {
result[key] = many(adapter.tables[target], {
relationName: key,
})
}
})
return result
})
} }
if (hasManyTextField && buildTexts) { if (isRoot) {
const textsTableName = `${rootTableName}_texts` if (hasManyTextField) {
const columns: Record<string, PgColumnBuilder> = { const textsTableName = `${rootTableName}_texts`
id: serial('id').primaryKey(), const columns: Record<string, PgColumnBuilder> = {
order: integer('order').notNull(), id: serial('id').primaryKey(),
parent: parentIDColumnMap[idColType]('parent_id').notNull(), order: integer('order').notNull(),
path: varchar('path').notNull(), parent: parentIDColumnMap[idColType]('parent_id').notNull(),
text: varchar('text'), path: varchar('path').notNull(),
} text: varchar('text'),
if (hasLocalizedManyTextField) {
columns.locale = adapter.enums.enum__locales('locale')
}
textsTable = adapter.pgSchema.table(textsTableName, columns, (cols) => {
const config: Record<string, ForeignKeyBuilder | IndexBuilder> = {
orderParentIdx: index(`${textsTableName}_order_parent_idx`).on(cols.order, cols.parent),
parentFk: foreignKey({
name: `${textsTableName}_parent_fk`,
columns: [cols.parent],
foreignColumns: [table.id],
}).onDelete('cascade'),
}
if (hasManyTextField === 'index') {
config.text_idx = index(`${textsTableName}_text_idx`).on(cols.text)
} }
if (hasLocalizedManyTextField) { if (hasLocalizedManyTextField) {
config.localeParent = index(`${textsTableName}_locale_parent`).on(cols.locale, cols.parent) columns.locale = adapter.enums.enum__locales('locale')
} }
return config textsTable = adapter.pgSchema.table(textsTableName, columns, (cols) => {
}) const config: Record<string, ForeignKeyBuilder | IndexBuilder> = {
orderParentIdx: index(`${textsTableName}_order_parent_idx`).on(cols.order, cols.parent),
parentFk: foreignKey({
name: `${textsTableName}_parent_fk`,
columns: [cols.parent],
foreignColumns: [table.id],
}).onDelete('cascade'),
}
adapter.tables[textsTableName] = textsTable if (hasManyTextField === 'index') {
config.text_idx = index(`${textsTableName}_text_idx`).on(cols.text)
}
const textsTableRelations = relations(textsTable, ({ one }) => ({ if (hasLocalizedManyTextField) {
parent: one(table, { config.localeParent = index(`${textsTableName}_locale_parent`).on(
fields: [textsTable.parent], cols.locale,
references: [table.id], cols.parent,
}), )
})) }
adapter.relations[`relations_${textsTableName}`] = textsTableRelations return config
} })
if (hasManyNumberField && buildNumbers) { adapter.tables[textsTableName] = textsTable
const numbersTableName = `${rootTableName}_numbers`
const columns: Record<string, PgColumnBuilder> = { adapter.relations[`relations_${textsTableName}`] = relations(textsTable, ({ one }) => ({
id: serial('id').primaryKey(), parent: one(table, {
number: numeric('number'), fields: [textsTable.parent],
order: integer('order').notNull(), references: [table.id],
parent: parentIDColumnMap[idColType]('parent_id').notNull(), relationName: '_texts',
path: varchar('path').notNull(), }),
}))
} }
if (hasLocalizedManyNumberField) { if (hasManyNumberField) {
columns.locale = adapter.enums.enum__locales('locale') const numbersTableName = `${rootTableName}_numbers`
} const columns: Record<string, PgColumnBuilder> = {
id: serial('id').primaryKey(),
numbersTable = adapter.pgSchema.table(numbersTableName, columns, (cols) => { number: numeric('number'),
const config: Record<string, ForeignKeyBuilder | IndexBuilder> = { order: integer('order').notNull(),
orderParentIdx: index(`${numbersTableName}_order_parent_idx`).on(cols.order, cols.parent), parent: parentIDColumnMap[idColType]('parent_id').notNull(),
parentFk: foreignKey({ path: varchar('path').notNull(),
name: `${numbersTableName}_parent_fk`,
columns: [cols.parent],
foreignColumns: [table.id],
}).onDelete('cascade'),
}
if (hasManyNumberField === 'index') {
config.numberIdx = index(`${numbersTableName}_number_idx`).on(cols.number)
} }
if (hasLocalizedManyNumberField) { if (hasLocalizedManyNumberField) {
config.localeParent = index(`${numbersTableName}_locale_parent`).on( columns.locale = adapter.enums.enum__locales('locale')
cols.locale,
cols.parent,
)
} }
return config numbersTable = adapter.pgSchema.table(numbersTableName, columns, (cols) => {
}) const config: Record<string, ForeignKeyBuilder | IndexBuilder> = {
orderParentIdx: index(`${numbersTableName}_order_parent_idx`).on(cols.order, cols.parent),
parentFk: foreignKey({
name: `${numbersTableName}_parent_fk`,
columns: [cols.parent],
foreignColumns: [table.id],
}).onDelete('cascade'),
}
adapter.tables[numbersTableName] = numbersTable if (hasManyNumberField === 'index') {
config.numberIdx = index(`${numbersTableName}_number_idx`).on(cols.number)
}
const numbersTableRelations = relations(numbersTable, ({ one }) => ({ if (hasLocalizedManyNumberField) {
parent: one(table, { config.localeParent = index(`${numbersTableName}_locale_parent`).on(
fields: [numbersTable.parent], cols.locale,
references: [table.id], cols.parent,
}), )
})) }
adapter.relations[`relations_${numbersTableName}`] = numbersTableRelations return config
} })
adapter.tables[numbersTableName] = numbersTable
adapter.relations[`relations_${numbersTableName}`] = relations(numbersTable, ({ one }) => ({
parent: one(table, {
fields: [numbersTable.parent],
references: [table.id],
relationName: '_numbers',
}),
}))
}
if (buildRelationships) {
if (relationships.size) { if (relationships.size) {
const relationshipColumns: Record<string, PgColumnBuilder> = { const relationshipColumns: Record<string, PgColumnBuilder> = {
id: serial('id').primaryKey(), id: serial('id').primaryKey(),
@@ -308,7 +333,6 @@ export const buildTable = ({
} }
const relationExtraConfig: BaseExtraConfig = {} const relationExtraConfig: BaseExtraConfig = {}
const relationshipsTableName = `${tableName}${adapter.relationshipsSuffix}` const relationshipsTableName = `${tableName}${adapter.relationshipsSuffix}`
relationships.forEach((relationTo) => { relationships.forEach((relationTo) => {
@@ -319,7 +343,6 @@ export const buildTable = ({
throwValidationError: true, throwValidationError: true,
}) })
let colType = adapter.idType === 'uuid' ? 'uuid' : 'integer' let colType = adapter.idType === 'uuid' ? 'uuid' : 'integer'
const relatedCollectionCustomIDType = const relatedCollectionCustomIDType =
adapter.payload.collections[relationshipConfig.slug]?.customIDType adapter.payload.collections[relationshipConfig.slug]?.customIDType
@@ -371,51 +394,63 @@ export const buildTable = ({
adapter.tables[relationshipsTableName] = relationshipsTable adapter.tables[relationshipsTableName] = relationshipsTable
const relationshipsTableRelations = relations(relationshipsTable, ({ one }) => { adapter.relations[`relations_${relationshipsTableName}`] = relations(
const result: Record<string, Relation<string>> = { relationshipsTable,
parent: one(table, { ({ one }) => {
fields: [relationshipsTable.parent], const result: Record<string, Relation<string>> = {
references: [table.id], parent: one(table, {
relationName: '_rels', fields: [relationshipsTable.parent],
}), references: [table.id],
} relationName: '_rels',
}),
}
relationships.forEach((relationTo) => { relationships.forEach((relationTo) => {
const relatedTableName = createTableName({ const relatedTableName = createTableName({
adapter, adapter,
config: adapter.payload.collections[relationTo].config, config: adapter.payload.collections[relationTo].config,
throwValidationError: true, throwValidationError: true,
})
const idColumnName = `${relationTo}ID`
result[idColumnName] = one(adapter.tables[relatedTableName], {
fields: [relationshipsTable[idColumnName]],
references: [adapter.tables[relatedTableName].id],
relationName: relationTo,
})
}) })
const idColumnName = `${relationTo}ID`
result[idColumnName] = one(adapter.tables[relatedTableName], {
fields: [relationshipsTable[idColumnName]],
references: [adapter.tables[relatedTableName].id],
})
})
return result return result
}) },
)
adapter.relations[`relations_${relationshipsTableName}`] = relationshipsTableRelations
} }
} }
const tableRelations = relations(table, ({ many }) => { adapter.relations[`relations_${tableName}`] = relations(table, ({ many, one }) => {
const result: Record<string, Relation<string>> = {} const result: Record<string, Relation<string>> = {}
relationsToBuild.forEach((val, key) => { nonLocalizedRelations.forEach(({ type, target }, key) => {
result[key] = many(adapter.tables[val]) if (type === 'one') {
result[key] = one(adapter.tables[target], {
fields: [table[key]],
references: [adapter.tables[target].id],
relationName: key,
})
}
if (type === 'many') {
result[key] = many(adapter.tables[target], { relationName: key })
}
}) })
if (hasLocalizedField) { if (hasLocalizedField) {
result._locales = many(localesTable) result._locales = many(localesTable, { relationName: '_locales' })
} }
if (hasManyTextField) { if (hasManyTextField) {
result._texts = many(textsTable) result._texts = many(textsTable, { relationName: '_texts' })
} }
if (hasManyNumberField) { if (hasManyNumberField) {
result._numbers = many(numbersTable) result._numbers = many(numbersTable, { relationName: '_numbers' })
} }
if (relationships.size && relationshipsTable) { if (relationships.size && relationshipsTable) {
@@ -427,7 +462,5 @@ export const buildTable = ({
return result return result
}) })
adapter.relations[`relations_${tableName}`] = tableRelations
return { hasManyNumberField, hasManyTextField, relationsToBuild } return { hasManyNumberField, hasManyTextField, relationsToBuild }
} }

View File

@@ -24,7 +24,7 @@ import { fieldAffectsData, optionIsObject } from 'payload/types'
import toSnakeCase from 'to-snake-case' import toSnakeCase from 'to-snake-case'
import type { GenericColumns, IDType, PostgresAdapter } from '../types.js' import type { GenericColumns, IDType, PostgresAdapter } from '../types.js'
import type { BaseExtraConfig } from './build.js' import type { BaseExtraConfig, RelationMap } from './build.js'
import { hasLocalesTable } from '../utilities/hasLocalesTable.js' import { hasLocalesTable } from '../utilities/hasLocalesTable.js'
import { buildTable } from './build.js' import { buildTable } from './build.js'
@@ -36,9 +36,6 @@ import { validateExistingBlockIsIdentical } from './validateExistingBlockIsIdent
type Args = { type Args = {
adapter: PostgresAdapter adapter: PostgresAdapter
buildNumbers: boolean
buildRelationships: boolean
buildTexts: boolean
columnPrefix?: string columnPrefix?: string
columns: Record<string, PgColumnBuilder> columns: Record<string, PgColumnBuilder>
disableNotNull: boolean disableNotNull: boolean
@@ -51,9 +48,9 @@ type Args = {
localesIndexes: Record<string, (cols: GenericColumns) => IndexBuilder> localesIndexes: Record<string, (cols: GenericColumns) => IndexBuilder>
newTableName: string newTableName: string
parentTableName: string parentTableName: string
relationsToBuild: Map<string, string> relationsToBuild: RelationMap
relationships: Set<string> relationships: Set<string>
rootRelationsToBuild?: Map<string, string> rootRelationsToBuild?: RelationMap
rootTableIDColType: string rootTableIDColType: string
rootTableName: string rootTableName: string
versions: boolean versions: boolean
@@ -70,9 +67,6 @@ type Result = {
export const traverseFields = ({ export const traverseFields = ({
adapter, adapter,
buildNumbers,
buildRelationships,
buildTexts,
columnPrefix, columnPrefix,
columns, columns,
disableNotNull, disableNotNull,
@@ -121,7 +115,13 @@ export const traverseFields = ({
// If field is localized, // If field is localized,
// add the column to the locale table instead of main table // add the column to the locale table instead of main table
if (adapter.payload.config.localization && (field.localized || forceLocalized)) { if (
adapter.payload.config.localization &&
(field.localized || forceLocalized) &&
field.type !== 'array' &&
field.type !== 'blocks' &&
(('hasMany' in field && field.hasMany !== true) || !('hasMany' in field))
) {
hasLocalizedField = true hasLocalizedField = true
targetTable = localesColumns targetTable = localesColumns
targetIndexes = localesIndexes targetIndexes = localesIndexes
@@ -250,6 +250,7 @@ export const traverseFields = ({
parentTableName: newTableName, parentTableName: newTableName,
prefix: `${newTableName}_`, prefix: `${newTableName}_`,
throwValidationError, throwValidationError,
versionsCustomName: versions,
}) })
const baseColumns: Record<string, PgColumnBuilder> = { const baseColumns: Record<string, PgColumnBuilder> = {
order: integer('order').notNull(), order: integer('order').notNull(),
@@ -264,7 +265,7 @@ export const traverseFields = ({
name: `${selectTableName}_parent_fk`, name: `${selectTableName}_parent_fk`,
columns: [cols.parent], columns: [cols.parent],
foreignColumns: [adapter.tables[parentTableName].id], foreignColumns: [adapter.tables[parentTableName].id],
}), }).onDelete('cascade'),
parentIdx: (cols) => index(`${selectTableName}_parent_idx`).on(cols.parent), parentIdx: (cols) => index(`${selectTableName}_parent_idx`).on(cols.parent),
} }
@@ -285,24 +286,28 @@ export const traverseFields = ({
disableNotNull, disableNotNull,
disableUnique, disableUnique,
fields: [], fields: [],
rootTableName,
tableName: selectTableName, tableName: selectTableName,
versions, versions,
}) })
relationsToBuild.set(fieldName, selectTableName) relationsToBuild.set(fieldName, {
type: 'many',
// selects have their own localized table, independent of the base table.
localized: false,
target: selectTableName,
})
const selectTableRelations = relations(adapter.tables[selectTableName], ({ one }) => { adapter.relations[`relations_${selectTableName}`] = relations(
const result: Record<string, Relation<string>> = { adapter.tables[selectTableName],
({ one }) => ({
parent: one(adapter.tables[parentTableName], { parent: one(adapter.tables[parentTableName], {
fields: [adapter.tables[selectTableName].parent], fields: [adapter.tables[selectTableName].parent],
references: [adapter.tables[parentTableName].id], references: [adapter.tables[parentTableName].id],
relationName: fieldName,
}), }),
} }),
)
return result
})
adapter.relations[`relation_${selectTableName}`] = selectTableRelations
} else { } else {
targetTable[fieldName] = adapter.enums[enumName](fieldName) targetTable[fieldName] = adapter.enums[enumName](fieldName)
} }
@@ -376,28 +381,49 @@ export const traverseFields = ({
hasManyNumberField = subHasManyNumberField hasManyNumberField = subHasManyNumberField
} }
relationsToBuild.set(fieldName, arrayTableName) relationsToBuild.set(fieldName, {
type: 'many',
const arrayTableRelations = relations(adapter.tables[arrayTableName], ({ many, one }) => { // arrays have their own localized table, independent of the base table.
const result: Record<string, Relation<string>> = { localized: false,
_parentID: one(adapter.tables[parentTableName], { target: arrayTableName,
fields: [adapter.tables[arrayTableName]._parentID],
references: [adapter.tables[parentTableName].id],
}),
}
if (hasLocalesTable(field.fields)) {
result._locales = many(adapter.tables[`${arrayTableName}${adapter.localesSuffix}`])
}
subRelationsToBuild.forEach((val, key) => {
result[key] = many(adapter.tables[val])
})
return result
}) })
adapter.relations[`relations_${arrayTableName}`] = arrayTableRelations adapter.relations[`relations_${arrayTableName}`] = relations(
adapter.tables[arrayTableName],
({ many, one }) => {
const result: Record<string, Relation<string>> = {
_parentID: one(adapter.tables[parentTableName], {
fields: [adapter.tables[arrayTableName]._parentID],
references: [adapter.tables[parentTableName].id],
relationName: fieldName,
}),
}
if (hasLocalesTable(field.fields)) {
result._locales = many(adapter.tables[`${arrayTableName}${adapter.localesSuffix}`], {
relationName: '_locales',
})
}
subRelationsToBuild.forEach(({ type, localized, target }, key) => {
if (type === 'one') {
const arrayWithLocalized = localized
? `${arrayTableName}${adapter.localesSuffix}`
: arrayTableName
result[key] = one(adapter.tables[target], {
fields: [adapter.tables[arrayWithLocalized][key]],
references: [adapter.tables[target].id],
relationName: key,
})
}
if (type === 'many') {
result[key] = many(adapter.tables[target], { relationName: key })
}
})
return result
},
)
break break
} }
@@ -468,31 +494,43 @@ export const traverseFields = ({
hasManyNumberField = subHasManyNumberField hasManyNumberField = subHasManyNumberField
} }
const blockTableRelations = relations( adapter.relations[`relations_${blockTableName}`] = relations(
adapter.tables[blockTableName], adapter.tables[blockTableName],
({ many, one }) => { ({ many, one }) => {
const result: Record<string, Relation<string>> = { const result: Record<string, Relation<string>> = {
_parentID: one(adapter.tables[rootTableName], { _parentID: one(adapter.tables[rootTableName], {
fields: [adapter.tables[blockTableName]._parentID], fields: [adapter.tables[blockTableName]._parentID],
references: [adapter.tables[rootTableName].id], references: [adapter.tables[rootTableName].id],
relationName: `_blocks_${block.slug}`,
}), }),
} }
if (hasLocalesTable(block.fields)) { if (hasLocalesTable(block.fields)) {
result._locales = many( result._locales = many(
adapter.tables[`${blockTableName}${adapter.localesSuffix}`], adapter.tables[`${blockTableName}${adapter.localesSuffix}`],
{ relationName: '_locales' },
) )
} }
subRelationsToBuild.forEach((val, key) => { subRelationsToBuild.forEach(({ type, localized, target }, key) => {
result[key] = many(adapter.tables[val]) if (type === 'one') {
const blockWithLocalized = localized
? `${blockTableName}${adapter.localesSuffix}`
: blockTableName
result[key] = one(adapter.tables[target], {
fields: [adapter.tables[blockWithLocalized][key]],
references: [adapter.tables[target].id],
relationName: key,
})
}
if (type === 'many') {
result[key] = many(adapter.tables[target], { relationName: key })
}
}) })
return result return result
}, },
) )
adapter.relations[`relations_${blockTableName}`] = blockTableRelations
} else if (process.env.NODE_ENV !== 'production' && !versions) { } else if (process.env.NODE_ENV !== 'production' && !versions) {
validateExistingBlockIsIdentical({ validateExistingBlockIsIdentical({
block, block,
@@ -502,7 +540,13 @@ export const traverseFields = ({
tableLocales: adapter.tables[`${blockTableName}${adapter.localesSuffix}`], tableLocales: adapter.tables[`${blockTableName}${adapter.localesSuffix}`],
}) })
} }
rootRelationsToBuild.set(`_blocks_${block.slug}`, blockTableName) // blocks relationships are defined from the collection or globals table down to the block, bypassing any subBlocks
rootRelationsToBuild.set(`_blocks_${block.slug}`, {
type: 'many',
// blocks are not localized on the parent table
localized: false,
target: blockTableName,
})
}) })
break break
@@ -520,9 +564,6 @@ export const traverseFields = ({
hasManyTextField: groupHasManyTextField, hasManyTextField: groupHasManyTextField,
} = traverseFields({ } = traverseFields({
adapter, adapter,
buildNumbers,
buildRelationships,
buildTexts,
columnPrefix, columnPrefix,
columns, columns,
disableNotNull, disableNotNull,
@@ -563,9 +604,6 @@ export const traverseFields = ({
hasManyTextField: groupHasManyTextField, hasManyTextField: groupHasManyTextField,
} = traverseFields({ } = traverseFields({
adapter, adapter,
buildNumbers,
buildRelationships,
buildTexts,
columnPrefix: `${columnName}_`, columnPrefix: `${columnName}_`,
columns, columns,
disableNotNull: disableNotNullFromHere, disableNotNull: disableNotNullFromHere,
@@ -607,9 +645,6 @@ export const traverseFields = ({
hasManyTextField: tabHasManyTextField, hasManyTextField: tabHasManyTextField,
} = traverseFields({ } = traverseFields({
adapter, adapter,
buildNumbers,
buildRelationships,
buildTexts,
columnPrefix, columnPrefix,
columns, columns,
disableNotNull: disableNotNullFromHere, disableNotNull: disableNotNullFromHere,
@@ -651,9 +686,6 @@ export const traverseFields = ({
hasManyTextField: rowHasManyTextField, hasManyTextField: rowHasManyTextField,
} = traverseFields({ } = traverseFields({
adapter, adapter,
buildNumbers,
buildRelationships,
buildTexts,
columnPrefix, columnPrefix,
columns, columns,
disableNotNull: disableNotNullFromHere, disableNotNull: disableNotNullFromHere,
@@ -687,13 +719,45 @@ export const traverseFields = ({
case 'upload': case 'upload':
if (Array.isArray(field.relationTo)) { if (Array.isArray(field.relationTo)) {
field.relationTo.forEach((relation) => relationships.add(relation)) field.relationTo.forEach((relation) => relationships.add(relation))
} else { } else if (field.type === 'relationship' && field.hasMany) {
relationships.add(field.relationTo) relationships.add(field.relationTo)
} } else {
// simple relationships get a column on the targetTable with a foreign key to the relationTo table
const relationshipConfig = adapter.payload.collections[field.relationTo].config
if (field.localized && adapter.payload.config.localization) { const tableName = adapter.tableNameMap.get(toSnakeCase(field.relationTo))
// get the id type of the related collection
let colType = adapter.idType === 'uuid' ? 'uuid' : 'integer'
const relatedCollectionCustomID = relationshipConfig.fields.find(
(field) => fieldAffectsData(field) && field.name === 'id',
)
if (relatedCollectionCustomID?.type === 'number') colType = 'numeric'
if (relatedCollectionCustomID?.type === 'text') colType = 'varchar'
// make the foreign key column for relationship using the correct id column type
targetTable[fieldName] = parentIDColumnMap[colType](`${columnName}_id`).references(
() => adapter.tables[tableName].id,
{ onDelete: 'set null' },
)
// add relationship to table
relationsToBuild.set(fieldName, {
type: 'one',
localized: adapter.payload.config.localization && field.localized,
target: tableName,
})
// add notNull when not required
if (!disableNotNull && field.required && !field.admin?.condition) {
targetTable[fieldName].notNull()
}
break
}
if (adapter.payload.config.localization && field.localized) {
hasLocalizedRelationshipField = true hasLocalizedRelationshipField = true
} }
break break
default: default:

View File

@@ -2,11 +2,14 @@
import type { SanitizedConfig } from 'payload/config' import type { SanitizedConfig } from 'payload/config'
import type { Field, TypeWithID } from 'payload/types' import type { Field, TypeWithID } from 'payload/types'
import type { PostgresAdapter } from '../../types.js'
import { createBlocksMap } from '../../utilities/createBlocksMap.js' import { createBlocksMap } from '../../utilities/createBlocksMap.js'
import { createPathMap } from '../../utilities/createRelationshipMap.js' import { createPathMap } from '../../utilities/createRelationshipMap.js'
import { traverseFields } from './traverseFields.js' import { traverseFields } from './traverseFields.js'
type TransformArgs = { type TransformArgs = {
adapter: PostgresAdapter
config: SanitizedConfig config: SanitizedConfig
data: Record<string, unknown> data: Record<string, unknown>
fallbackLocale?: false | string fallbackLocale?: false | string
@@ -16,7 +19,12 @@ type TransformArgs = {
// This is the entry point to transform Drizzle output data // This is the entry point to transform Drizzle output data
// into the shape Payload expects based on field schema // into the shape Payload expects based on field schema
export const transform = <T extends TypeWithID>({ config, data, fields }: TransformArgs): T => { export const transform = <T extends TypeWithID>({
adapter,
config,
data,
fields,
}: TransformArgs): T => {
let relationships: Record<string, Record<string, unknown>[]> = {} let relationships: Record<string, Record<string, unknown>[]> = {}
let texts: Record<string, Record<string, unknown>[]> = {} let texts: Record<string, Record<string, unknown>[]> = {}
let numbers: Record<string, Record<string, unknown>[]> = {} let numbers: Record<string, Record<string, unknown>[]> = {}
@@ -40,6 +48,7 @@ export const transform = <T extends TypeWithID>({ config, data, fields }: Transf
const deletions = [] const deletions = []
const result = traverseFields<T>({ const result = traverseFields<T>({
adapter,
blocks, blocks,
config, config,
dataRef: { dataRef: {

View File

@@ -30,10 +30,6 @@ export const transformRelationship = ({ field, locale, ref, relations }: Args) =
value: matchedRelation[1], value: matchedRelation[1],
} }
} }
} else {
// Handle hasOne
const relatedData = relation[`${field.relationTo}ID`]
result = relatedData
} }
} }
} else { } else {

View File

@@ -4,6 +4,7 @@ import type { Field, TabAsField } from 'payload/types'
import { fieldAffectsData } from 'payload/types' import { fieldAffectsData } from 'payload/types'
import type { PostgresAdapter } from '../../types.js'
import type { BlocksMap } from '../../utilities/createBlocksMap.js' import type { BlocksMap } from '../../utilities/createBlocksMap.js'
import { transformHasManyNumber } from './hasManyNumber.js' import { transformHasManyNumber } from './hasManyNumber.js'
@@ -11,6 +12,10 @@ import { transformHasManyText } from './hasManyText.js'
import { transformRelationship } from './relationship.js' import { transformRelationship } from './relationship.js'
type TraverseFieldsArgs = { type TraverseFieldsArgs = {
/**
* The DB adapter
*/
adapter: PostgresAdapter
/** /**
* Pre-formatted blocks map * Pre-formatted blocks map
*/ */
@@ -60,6 +65,7 @@ type TraverseFieldsArgs = {
// Traverse fields recursively, transforming data // Traverse fields recursively, transforming data
// for each field type into required Payload shape // for each field type into required Payload shape
export const traverseFields = <T extends Record<string, unknown>>({ export const traverseFields = <T extends Record<string, unknown>>({
adapter,
blocks, blocks,
config, config,
dataRef, dataRef,
@@ -77,6 +83,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
const formatted = fields.reduce((result, field) => { const formatted = fields.reduce((result, field) => {
if (field.type === 'tabs') { if (field.type === 'tabs') {
traverseFields({ traverseFields({
adapter,
blocks, blocks,
config, config,
dataRef, dataRef,
@@ -97,6 +104,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
(field.type === 'tab' && !('name' in field)) (field.type === 'tab' && !('name' in field))
) { ) {
traverseFields({ traverseFields({
adapter,
blocks, blocks,
config, config,
dataRef, dataRef,
@@ -114,6 +122,11 @@ export const traverseFields = <T extends Record<string, unknown>>({
if (fieldAffectsData(field)) { if (fieldAffectsData(field)) {
const fieldName = `${fieldPrefix || ''}${field.name}` const fieldName = `${fieldPrefix || ''}${field.name}`
const fieldData = table[fieldName] const fieldData = table[fieldName]
const localizedFieldData = {}
const valuesToTransform: {
ref: Record<string, unknown>
table: Record<string, unknown>
}[] = []
if (fieldPrefix) { if (fieldPrefix) {
deletions.push(() => delete table[fieldName]) deletions.push(() => delete table[fieldName])
@@ -134,6 +147,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
} }
const rowResult = traverseFields<T>({ const rowResult = traverseFields<T>({
adapter,
blocks, blocks,
config, config,
dataRef: data, dataRef: data,
@@ -168,6 +182,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
} }
return traverseFields<T>({ return traverseFields<T>({
adapter,
blocks, blocks,
config, config,
dataRef: row, dataRef: row,
@@ -212,6 +227,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
if (block) { if (block) {
const blockResult = traverseFields<T>({ const blockResult = traverseFields<T>({
adapter,
blocks, blocks,
config, config,
dataRef: row, dataRef: row,
@@ -243,6 +259,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
if (block) { if (block) {
return traverseFields<T>({ return traverseFields<T>({
adapter,
blocks, blocks,
config, config,
dataRef: row, dataRef: row,
@@ -266,49 +283,63 @@ export const traverseFields = <T extends Record<string, unknown>>({
} }
if (field.type === 'relationship' || field.type === 'upload') { if (field.type === 'relationship' || field.type === 'upload') {
const relationPathMatch = relationships[`${sanitizedPath}${field.name}`] if (typeof field.relationTo === 'string' && !('hasMany' in field && field.hasMany)) {
if (!relationPathMatch) { if (
if ('hasMany' in field && field.hasMany) { field.localized &&
if (field.localized && config.localization && config.localization.locales) { config.localization &&
result[field.name] = { config.localization.locales &&
[config.localization.defaultLocale]: [], Array.isArray(table?._locales)
) {
table._locales.forEach((localeRow) => {
result[field.name] = { [localeRow._locale]: localeRow[fieldName] }
})
} else {
valuesToTransform.push({ ref: result, table })
}
} else {
const relationPathMatch = relationships[`${sanitizedPath}${field.name}`]
if (!relationPathMatch) {
if ('hasMany' in field && field.hasMany) {
if (field.localized && config.localization && config.localization.locales) {
result[field.name] = {
[config.localization.defaultLocale]: [],
}
} else {
result[field.name] = []
} }
} else {
result[field.name] = []
} }
return result
} }
return result if (field.localized) {
} result[field.name] = {}
const relationsByLocale: Record<string, Record<string, unknown>[]> = {}
if (field.localized) { relationPathMatch.forEach((row) => {
result[field.name] = {} if (typeof row.locale === 'string') {
const relationsByLocale: Record<string, Record<string, unknown>[]> = {} if (!relationsByLocale[row.locale]) relationsByLocale[row.locale] = []
relationsByLocale[row.locale].push(row)
}
})
relationPathMatch.forEach((row) => { Object.entries(relationsByLocale).forEach(([locale, relations]) => {
if (typeof row.locale === 'string') { transformRelationship({
if (!relationsByLocale[row.locale]) relationsByLocale[row.locale] = [] field,
relationsByLocale[row.locale].push(row) locale,
} ref: result,
}) relations,
})
Object.entries(relationsByLocale).forEach(([locale, relations]) => { })
} else {
transformRelationship({ transformRelationship({
field, field,
locale,
ref: result, ref: result,
relations, relations: relationPathMatch,
}) })
}) }
} else { return result
transformRelationship({
field,
ref: result,
relations: relationPathMatch,
})
} }
return result
} }
if (field.type === 'text' && field?.hasMany) { if (field.type === 'text' && field?.hasMany) {
@@ -397,12 +428,6 @@ export const traverseFields = <T extends Record<string, unknown>>({
return result return result
} }
const localizedFieldData = {}
const valuesToTransform: {
ref: Record<string, unknown>
table: Record<string, unknown>
}[] = []
if (field.localized && Array.isArray(table._locales)) { if (field.localized && Array.isArray(table._locales)) {
table._locales.forEach((localeRow) => { table._locales.forEach((localeRow) => {
valuesToTransform.push({ ref: localizedFieldData, table: localeRow }) valuesToTransform.push({ ref: localizedFieldData, table: localeRow })
@@ -414,6 +439,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
valuesToTransform.forEach(({ ref, table }) => { valuesToTransform.forEach(({ ref, table }) => {
const fieldData = table[`${fieldPrefix || ''}${field.name}`] const fieldData = table[`${fieldPrefix || ''}${field.name}`]
const locale = table?._locale const locale = table?._locale
let val = fieldData
switch (field.type) { switch (field.type) {
case 'tab': case 'tab':
@@ -428,6 +454,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
Object.entries(ref).forEach(([groupLocale, groupLocaleData]) => { Object.entries(ref).forEach(([groupLocale, groupLocaleData]) => {
ref[groupLocale] = traverseFields<Record<string, unknown>>({ ref[groupLocale] = traverseFields<Record<string, unknown>>({
adapter,
blocks, blocks,
config, config,
dataRef: groupLocaleData as Record<string, unknown>, dataRef: groupLocaleData as Record<string, unknown>,
@@ -448,6 +475,7 @@ export const traverseFields = <T extends Record<string, unknown>>({
const groupData = {} const groupData = {}
ref[field.name] = traverseFields<Record<string, unknown>>({ ref[field.name] = traverseFields<Record<string, unknown>>({
adapter,
blocks, blocks,
config, config,
dataRef: groupData as Record<string, unknown>, dataRef: groupData as Record<string, unknown>,
@@ -465,65 +493,55 @@ export const traverseFields = <T extends Record<string, unknown>>({
} }
} }
break return
} }
case 'text': { case 'text': {
let val = fieldData
if (typeof fieldData === 'string') { if (typeof fieldData === 'string') {
val = String(fieldData) val = String(fieldData)
} }
if (typeof locale === 'string') {
ref[locale] = val
} else {
result[field.name] = val
}
break break
} }
case 'number': { case 'number': {
let val = fieldData
if (typeof fieldData === 'string') { if (typeof fieldData === 'string') {
val = Number.parseFloat(fieldData) val = Number.parseFloat(fieldData)
} }
if (typeof locale === 'string') {
ref[locale] = val
} else {
result[field.name] = val
}
break break
} }
case 'date': { case 'date': {
let val = fieldData
if (typeof fieldData === 'string') { if (typeof fieldData === 'string') {
val = new Date(fieldData).toISOString() val = new Date(fieldData).toISOString()
} }
if (typeof locale === 'string') { break
ref[locale] = val }
} else {
result[field.name] = val case 'relationship':
case 'upload': {
if (
val &&
typeof field.relationTo === 'string' &&
adapter.payload.collections[field.relationTo].customIDType === 'number'
) {
val = Number(val)
} }
break break
} }
default: { default: {
if (typeof locale === 'string') {
ref[locale] = fieldData
} else {
result[field.name] = fieldData
}
break break
} }
} }
if (typeof locale === 'string') {
ref[locale] = val
} else {
result[field.name] = val
}
}) })
if (Object.keys(localizedFieldData).length > 0) { if (Object.keys(localizedFieldData).length > 0) {

View File

@@ -354,7 +354,10 @@ export const traverseFields = ({
if (field.type === 'relationship' || field.type === 'upload') { if (field.type === 'relationship' || field.type === 'upload') {
const relationshipPath = `${path || ''}${field.name}` const relationshipPath = `${path || ''}${field.name}`
if (field.localized) { if (
field.localized &&
(Array.isArray(field.relationTo) || ('hasMany' in field && field.hasMany))
) {
if (typeof fieldData === 'object') { if (typeof fieldData === 'object') {
Object.entries(fieldData).forEach(([localeKey, localeData]) => { Object.entries(fieldData).forEach(([localeKey, localeData]) => {
if (localeData === null) { if (localeData === null) {
@@ -376,7 +379,8 @@ export const traverseFields = ({
}) })
}) })
} }
} else { return
} else if (Array.isArray(field.relationTo) || ('hasMany' in field && field.hasMany)) {
if (fieldData === null || (Array.isArray(fieldData) && fieldData.length === 0)) { if (fieldData === null || (Array.isArray(fieldData) && fieldData.length === 0)) {
relationshipsToDelete.push({ path: relationshipPath }) relationshipsToDelete.push({ path: relationshipPath })
return return
@@ -390,9 +394,30 @@ export const traverseFields = ({
field, field,
relationships, relationships,
}) })
return
} else {
if (
!field.localized &&
fieldData &&
typeof fieldData === 'object' &&
'id' in fieldData &&
fieldData?.id
) {
fieldData = fieldData.id
} else if (field.localized) {
if (typeof fieldData === 'object') {
Object.entries(fieldData).forEach(([localeKey, localeData]) => {
if (typeof localeData === 'object') {
if (localeData && 'id' in localeData && localeData?.id) {
fieldData[localeKey] = localeData.id
}
} else {
fieldData[localeKey] = localeData
}
})
}
}
} }
return
} }
if (field.type === 'text' && field.hasMany) { if (field.type === 'text' && field.hasMany) {

View File

@@ -18,7 +18,7 @@ export const updateOne: UpdateOne = async function updateOne(
const whereToUse = whereArg || { id: { equals: id } } const whereToUse = whereArg || { id: { equals: id } }
let idToUpdate = id let idToUpdate = id
const { joinAliases, joins, selectFields, where } = await buildQuery({ const { joins, selectFields, where } = await buildQuery({
adapter: this, adapter: this,
fields: collection.fields, fields: collection.fields,
locale, locale,
@@ -30,7 +30,6 @@ export const updateOne: UpdateOne = async function updateOne(
adapter: this, adapter: this,
chainedMethods: [{ args: [1], method: 'limit' }], chainedMethods: [{ args: [1], method: 'limit' }],
db, db,
joinAliases,
joins, joins,
selectFields, selectFields,
tableName, tableName,

View File

@@ -20,6 +20,7 @@ export const upsertRow = async <T extends TypeWithID>({
data, data,
db, db,
fields, fields,
ignoreResult,
operation, operation,
path = '', path = '',
req, req,
@@ -323,6 +324,8 @@ export const upsertRow = async <T extends TypeWithID>({
: error : error
} }
if (ignoreResult) return data as T
// ////////////////////////////////// // //////////////////////////////////
// RETRIEVE NEWLY UPDATED ROW // RETRIEVE NEWLY UPDATED ROW
// ////////////////////////////////// // //////////////////////////////////
@@ -343,6 +346,7 @@ export const upsertRow = async <T extends TypeWithID>({
// ////////////////////////////////// // //////////////////////////////////
const result = transform<T>({ const result = transform<T>({
adapter,
config: adapter.payload.config, config: adapter.payload.config,
data: doc, data: doc,
fields, fields,

View File

@@ -8,6 +8,11 @@ type BaseArgs = {
data: Record<string, unknown> data: Record<string, unknown>
db: DrizzleDB db: DrizzleDB
fields: Field[] fields: Field[]
/**
* When true, skips reading the data back from the database and returns the input data
* @default false
*/
ignoreResult?: boolean
path?: string path?: string
req: PayloadRequestWithData req: PayloadRequestWithData
tableName: string tableName: string

6
packages/payload/auth.d.ts vendored Normal file
View File

@@ -0,0 +1,6 @@
export * from './dist/auth/index.js';
export { default as executeAccess } from './dist/auth/executeAccess.js';
export { getAccessResults } from './dist/auth/getAccessResults.js';
export { getAuthenticatedUser } from './dist/auth/getAuthenticatedUser.js';
export type { AuthStrategyFunction, AuthStrategyFunctionArgs, CollectionPermission, FieldPermissions, GlobalPermission, IncomingAuthType, Permission, Permissions, User, VerifyConfig, } from './dist/auth/types.js';
//# sourceMappingURL=auth.d.ts.map

6
packages/payload/auth.js Normal file
View File

@@ -0,0 +1,6 @@
export * from './dist/auth/index.js';
export { default as executeAccess } from './dist/auth/executeAccess.js';
export { getAccessResults } from './dist/auth/getAccessResults.js';
export { getAuthenticatedUser } from './dist/auth/getAuthenticatedUser.js';
//# sourceMappingURL=auth.js.map

14
packages/payload/components.d.ts vendored Normal file
View File

@@ -0,0 +1,14 @@
export { default as Banner } from './dist/admin/components/elements/Banner';
export { default as Button } from './dist/admin/components/elements/Button';
export { ErrorPill } from './dist/admin/components/elements/ErrorPill';
export { default as Pill } from './dist/admin/components/elements/Pill';
export { default as Popup } from './dist/admin/components/elements/Popup';
export { ShimmerEffect } from './dist/admin/components/elements/ShimmerEffect';
export { default as Tooltip } from './dist/admin/components/elements/Tooltip';
export { default as Check } from './dist/admin/components/icons/Check';
export { default as Chevron } from './dist/admin/components/icons/Chevron';
export { default as Menu } from './dist/admin/components/icons/Menu';
export { default as Search } from './dist/admin/components/icons/Search';
export { default as X } from './dist/admin/components/icons/X';
export { default as MinimalTemplate } from './dist/admin/components/templates/Minimal';
//# sourceMappingURL=components.d.ts.map

View File

@@ -0,0 +1,71 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
Banner: function() {
return _Banner.default;
},
Button: function() {
return _Button.default;
},
Check: function() {
return _Check.default;
},
Chevron: function() {
return _Chevron.default;
},
ErrorPill: function() {
return _ErrorPill.ErrorPill;
},
Menu: function() {
return _Menu.default;
},
MinimalTemplate: function() {
return _Minimal.default;
},
Pill: function() {
return _Pill.default;
},
Popup: function() {
return _Popup.default;
},
Search: function() {
return _Search.default;
},
ShimmerEffect: function() {
return _ShimmerEffect.ShimmerEffect;
},
Tooltip: function() {
return _Tooltip.default;
},
X: function() {
return _X.default;
}
});
const _Banner = /*#__PURE__*/ _interop_require_default(require("./dist/admin/components/elements/Banner"));
const _Button = /*#__PURE__*/ _interop_require_default(require("./dist/admin/components/elements/Button"));
const _ErrorPill = require("./dist/admin/components/elements/ErrorPill");
const _Pill = /*#__PURE__*/ _interop_require_default(require("./dist/admin/components/elements/Pill"));
const _Popup = /*#__PURE__*/ _interop_require_default(require("./dist/admin/components/elements/Popup"));
const _ShimmerEffect = require("./dist/admin/components/elements/ShimmerEffect");
const _Tooltip = /*#__PURE__*/ _interop_require_default(require("./dist/admin/components/elements/Tooltip"));
const _Check = /*#__PURE__*/ _interop_require_default(require("./dist/admin/components/icons/Check"));
const _Chevron = /*#__PURE__*/ _interop_require_default(require("./dist/admin/components/icons/Chevron"));
const _Menu = /*#__PURE__*/ _interop_require_default(require("./dist/admin/components/icons/Menu"));
const _Search = /*#__PURE__*/ _interop_require_default(require("./dist/admin/components/icons/Search"));
const _X = /*#__PURE__*/ _interop_require_default(require("./dist/admin/components/icons/X"));
const _Minimal = /*#__PURE__*/ _interop_require_default(require("./dist/admin/components/templates/Minimal"));
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9leHBvcnRzL2NvbXBvbmVudHMudHMiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IHsgZGVmYXVsdCBhcyBCYW5uZXIgfSBmcm9tICcuLi9hZG1pbi9jb21wb25lbnRzL2VsZW1lbnRzL0Jhbm5lcidcbmV4cG9ydCB7IGRlZmF1bHQgYXMgQnV0dG9uIH0gZnJvbSAnLi4vYWRtaW4vY29tcG9uZW50cy9lbGVtZW50cy9CdXR0b24nXG5cbmV4cG9ydCB7IEVycm9yUGlsbCB9IGZyb20gJy4uL2FkbWluL2NvbXBvbmVudHMvZWxlbWVudHMvRXJyb3JQaWxsJ1xuZXhwb3J0IHsgZGVmYXVsdCBhcyBQaWxsIH0gZnJvbSAnLi4vYWRtaW4vY29tcG9uZW50cy9lbGVtZW50cy9QaWxsJ1xuXG5leHBvcnQgeyBkZWZhdWx0IGFzIFBvcHVwIH0gZnJvbSAnLi4vYWRtaW4vY29tcG9uZW50cy9lbGVtZW50cy9Qb3B1cCdcblxuZXhwb3J0IHsgU2hpbW1lckVmZmVjdCB9IGZyb20gJy4uL2FkbWluL2NvbXBvbmVudHMvZWxlbWVudHMvU2hpbW1lckVmZmVjdCdcbmV4cG9ydCB7IGRlZmF1bHQgYXMgVG9vbHRpcCB9IGZyb20gJy4uL2FkbWluL2NvbXBvbmVudHMvZWxlbWVudHMvVG9vbHRpcCdcbmV4cG9ydCB7IGRlZmF1bHQgYXMgQ2hlY2sgfSBmcm9tICcuLi9hZG1pbi9jb21wb25lbnRzL2ljb25zL0NoZWNrJ1xuZXhwb3J0IHsgZGVmYXVsdCBhcyBDaGV2cm9uIH0gZnJvbSAnLi4vYWRtaW4vY29tcG9uZW50cy9pY29ucy9DaGV2cm9uJ1xuZXhwb3J0IHsgZGVmYXVsdCBhcyBNZW51IH0gZnJvbSAnLi4vYWRtaW4vY29tcG9uZW50cy9pY29ucy9NZW51J1xuZXhwb3J0IHsgZGVmYXVsdCBhcyBTZWFyY2ggfSBmcm9tICcuLi9hZG1pbi9jb21wb25lbnRzL2ljb25zL1NlYXJjaCdcbmV4cG9ydCB7IGRlZmF1bHQgYXMgWCB9IGZyb20gJy4uL2FkbWluL2NvbXBvbmVudHMvaWNvbnMvWCdcbmV4cG9ydCB7IGRlZmF1bHQgYXMgTWluaW1hbFRlbXBsYXRlIH0gZnJvbSAnLi4vYWRtaW4vY29tcG9uZW50cy90ZW1wbGF0ZXMvTWluaW1hbCdcbiJdLCJuYW1lcyI6WyJCYW5uZXIiLCJCdXR0b24iLCJDaGVjayIsIkNoZXZyb24iLCJFcnJvclBpbGwiLCJNZW51IiwiTWluaW1hbFRlbXBsYXRlIiwiUGlsbCIsIlBvcHVwIiwiU2VhcmNoIiwiU2hpbW1lckVmZmVjdCIsIlRvb2x0aXAiLCJYIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7OztJQUFvQkEsTUFBTTtlQUFOQSxlQUFNOztJQUNOQyxNQUFNO2VBQU5BLGVBQU07O0lBU05DLEtBQUs7ZUFBTEEsY0FBSzs7SUFDTEMsT0FBTztlQUFQQSxnQkFBTzs7SUFSbEJDLFNBQVM7ZUFBVEEsb0JBQVM7O0lBU0VDLElBQUk7ZUFBSkEsYUFBSTs7SUFHSkMsZUFBZTtlQUFmQSxnQkFBZTs7SUFYZkMsSUFBSTtlQUFKQSxhQUFJOztJQUVKQyxLQUFLO2VBQUxBLGNBQUs7O0lBT0xDLE1BQU07ZUFBTkEsZUFBTTs7SUFMakJDLGFBQWE7ZUFBYkEsNEJBQWE7O0lBQ0ZDLE9BQU87ZUFBUEEsZ0JBQU87O0lBS1BDLENBQUM7ZUFBREEsVUFBQzs7OytEQWRhOytEQUNBOzJCQUVSOzZEQUNNOzhEQUVDOytCQUVIO2dFQUNLOzhEQUNGO2dFQUNFOzZEQUNIOytEQUNFOzBEQUNMO2dFQUNjIn0=

View File

@@ -0,0 +1,14 @@
export { default as Button } from '../dist/admin/components/elements/Button';
export { default as Card } from '../dist/admin/components/elements/Card';
export { Collapsible } from '../dist/admin/components/elements/Collapsible';
export { DocumentDrawer, DocumentDrawerToggler, baseClass as DocumentDrawerBaseClass, useDocumentDrawer, } from '../dist/admin/components/elements/DocumentDrawer';
export { Drawer, DrawerToggler, formatDrawerSlug } from '../dist/admin/components/elements/Drawer';
export { useDrawerSlug } from '../dist/admin/components/elements/Drawer/useDrawerSlug';
export { default as Eyebrow } from '../dist/admin/components/elements/Eyebrow';
export { Gutter } from '../dist/admin/components/elements/Gutter';
export { AppHeader } from '../dist/admin/components/elements/Header';
export { ListDrawer, ListDrawerToggler, baseClass as ListDrawerBaseClass, formatListDrawerSlug, useListDrawer, } from '../dist/admin/components/elements/ListDrawer';
export { Description, DescriptionComponent, DescriptionFunction, } from '../dist/admin/components/forms/FieldDescription/types';
export { useNav } from '../dist/admin/components/elements/Nav/context';
export { default as NavGroup } from '../dist/admin/components/elements/NavGroup';
//# sourceMappingURL=elements.d.ts.map

View File

@@ -0,0 +1,104 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
AppHeader: function() {
return _Header.AppHeader;
},
Button: function() {
return _Button.default;
},
Card: function() {
return _Card.default;
},
Collapsible: function() {
return _Collapsible.Collapsible;
},
Description: function() {
return _types.Description;
},
DescriptionComponent: function() {
return _types.DescriptionComponent;
},
DescriptionFunction: function() {
return _types.DescriptionFunction;
},
DocumentDrawer: function() {
return _DocumentDrawer.DocumentDrawer;
},
DocumentDrawerBaseClass: function() {
return _DocumentDrawer.baseClass;
},
DocumentDrawerToggler: function() {
return _DocumentDrawer.DocumentDrawerToggler;
},
Drawer: function() {
return _Drawer.Drawer;
},
DrawerToggler: function() {
return _Drawer.DrawerToggler;
},
Eyebrow: function() {
return _Eyebrow.default;
},
Gutter: function() {
return _Gutter.Gutter;
},
ListDrawer: function() {
return _ListDrawer.ListDrawer;
},
ListDrawerBaseClass: function() {
return _ListDrawer.baseClass;
},
ListDrawerToggler: function() {
return _ListDrawer.ListDrawerToggler;
},
NavGroup: function() {
return _NavGroup.default;
},
formatDrawerSlug: function() {
return _Drawer.formatDrawerSlug;
},
formatListDrawerSlug: function() {
return _ListDrawer.formatListDrawerSlug;
},
useDocumentDrawer: function() {
return _DocumentDrawer.useDocumentDrawer;
},
useDrawerSlug: function() {
return _useDrawerSlug.useDrawerSlug;
},
useListDrawer: function() {
return _ListDrawer.useListDrawer;
},
useNav: function() {
return _context.useNav;
}
});
const _Button = /*#__PURE__*/ _interop_require_default(require("../dist/admin/components/elements/Button"));
const _Card = /*#__PURE__*/ _interop_require_default(require("../dist/admin/components/elements/Card"));
const _Collapsible = require("../dist/admin/components/elements/Collapsible");
const _DocumentDrawer = require("../dist/admin/components/elements/DocumentDrawer");
const _Drawer = require("../dist/admin/components/elements/Drawer");
const _useDrawerSlug = require("../dist/admin/components/elements/Drawer/useDrawerSlug");
const _Eyebrow = /*#__PURE__*/ _interop_require_default(require("../dist/admin/components/elements/Eyebrow"));
const _Gutter = require("../dist/admin/components/elements/Gutter");
const _Header = require("../dist/admin/components/elements/Header");
const _ListDrawer = require("../dist/admin/components/elements/ListDrawer");
const _types = require("../dist/admin/components/forms/FieldDescription/types");
const _context = require("../dist/admin/components/elements/Nav/context");
const _NavGroup = /*#__PURE__*/ _interop_require_default(require("../dist/admin/components/elements/NavGroup"));
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9leHBvcnRzL2NvbXBvbmVudHMvZWxlbWVudHMudHMiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IHsgZGVmYXVsdCBhcyBCdXR0b24gfSBmcm9tICcuLi8uLi9hZG1pbi9jb21wb25lbnRzL2VsZW1lbnRzL0J1dHRvbidcbmV4cG9ydCB7IGRlZmF1bHQgYXMgQ2FyZCB9IGZyb20gJy4uLy4uL2FkbWluL2NvbXBvbmVudHMvZWxlbWVudHMvQ2FyZCdcbmV4cG9ydCB7IENvbGxhcHNpYmxlIH0gZnJvbSAnLi4vLi4vYWRtaW4vY29tcG9uZW50cy9lbGVtZW50cy9Db2xsYXBzaWJsZSdcbmV4cG9ydCB7XG4gIERvY3VtZW50RHJhd2VyLFxuICBEb2N1bWVudERyYXdlclRvZ2dsZXIsXG4gIGJhc2VDbGFzcyBhcyBEb2N1bWVudERyYXdlckJhc2VDbGFzcyxcbiAgdXNlRG9jdW1lbnREcmF3ZXIsXG59IGZyb20gJy4uLy4uL2FkbWluL2NvbXBvbmVudHMvZWxlbWVudHMvRG9jdW1lbnREcmF3ZXInXG5leHBvcnQgeyBEcmF3ZXIsIERyYXdlclRvZ2dsZXIsIGZvcm1hdERyYXdlclNsdWcgfSBmcm9tICcuLi8uLi9hZG1pbi9jb21wb25lbnRzL2VsZW1lbnRzL0RyYXdlcidcblxuZXhwb3J0IHsgdXNlRHJhd2VyU2x1ZyB9IGZyb20gJy4uLy4uL2FkbWluL2NvbXBvbmVudHMvZWxlbWVudHMvRHJhd2VyL3VzZURyYXdlclNsdWcnXG5cbmV4cG9ydCB7IGRlZmF1bHQgYXMgRXllYnJvdyB9IGZyb20gJy4uLy4uL2FkbWluL2NvbXBvbmVudHMvZWxlbWVudHMvRXllYnJvdydcbmV4cG9ydCB7IEd1dHRlciB9IGZyb20gJy4uLy4uL2FkbWluL2NvbXBvbmVudHMvZWxlbWVudHMvR3V0dGVyJ1xuZXhwb3J0IHsgQXBwSGVhZGVyIH0gZnJvbSAnLi4vLi4vYWRtaW4vY29tcG9uZW50cy9lbGVtZW50cy9IZWFkZXInXG5cbmV4cG9ydCB7XG4gIExpc3REcmF3ZXIsXG4gIExpc3REcmF3ZXJUb2dnbGVyLFxuICBiYXNlQ2xhc3MgYXMgTGlzdERyYXdlckJhc2VDbGFzcyxcbiAgZm9ybWF0TGlzdERyYXdlclNsdWcsXG4gIHVzZUxpc3REcmF3ZXIsXG59IGZyb20gJy4uLy4uL2FkbWluL2NvbXBvbmVudHMvZWxlbWVudHMvTGlzdERyYXdlcidcblxuZXhwb3J0IHtcbiAgRGVzY3JpcHRpb24sXG4gIERlc2NyaXB0aW9uQ29tcG9uZW50LFxuICBEZXNjcmlwdGlvbkZ1bmN0aW9uLFxufSBmcm9tICcuLi8uLi9hZG1pbi9jb21wb25lbnRzL2Zvcm1zL0ZpZWxkRGVzY3JpcHRpb24vdHlwZXMnXG5cbmV4cG9ydCB7IHVzZU5hdiB9IGZyb20gJy4uLy4uL2FkbWluL2NvbXBvbmVudHMvZWxlbWVudHMvTmF2L2NvbnRleHQnXG5leHBvcnQgeyBkZWZhdWx0IGFzIE5hdkdyb3VwIH0gZnJvbSAnLi4vLi4vYWRtaW4vY29tcG9uZW50cy9lbGVtZW50cy9OYXZHcm91cCdcbiJdLCJuYW1lcyI6WyJBcHBIZWFkZXIiLCJCdXR0b24iLCJDYXJkIiwiQ29sbGFwc2libGUiLCJEZXNjcmlwdGlvbiIsIkRlc2NyaXB0aW9uQ29tcG9uZW50IiwiRGVzY3JpcHRpb25GdW5jdGlvbiIsIkRvY3VtZW50RHJhd2VyIiwiRG9jdW1lbnREcmF3ZXJCYXNlQ2xhc3MiLCJiYXNlQ2xhc3MiLCJEb2N1bWVudERyYXdlclRvZ2dsZXIiLCJEcmF3ZXIiLCJEcmF3ZXJUb2dnbGVyIiwiRXllYnJvdyIsIkd1dHRlciIsIkxpc3REcmF3ZXIiLCJMaXN0RHJhd2VyQmFzZUNsYXNzIiwiTGlzdERyYXdlclRvZ2dsZXIiLCJOYXZHcm91cCIsImZvcm1hdERyYXdlclNsdWciLCJmb3JtYXRMaXN0RHJhd2VyU2x1ZyIsInVzZURvY3VtZW50RHJhd2VyIiwidXNlRHJhd2VyU2x1ZyIsInVzZUxpc3REcmF3ZXIiLCJ1c2VOYXYiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7O0lBZVNBLFNBQVM7ZUFBVEEsaUJBQVM7O0lBZkVDLE1BQU07ZUFBTkEsZUFBTTs7SUFDTkMsSUFBSTtlQUFKQSxhQUFJOztJQUNmQyxXQUFXO2VBQVhBLHdCQUFXOztJQXdCbEJDLFdBQVc7ZUFBWEEsa0JBQVc7O0lBQ1hDLG9CQUFvQjtlQUFwQkEsMkJBQW9COztJQUNwQkMsbUJBQW1CO2VBQW5CQSwwQkFBbUI7O0lBeEJuQkMsY0FBYztlQUFkQSw4QkFBYzs7SUFFREMsdUJBQXVCO2VBQXBDQyx5QkFBUzs7SUFEVEMscUJBQXFCO2VBQXJCQSxxQ0FBcUI7O0lBSWRDLE1BQU07ZUFBTkEsY0FBTTs7SUFBRUMsYUFBYTtlQUFiQSxxQkFBYTs7SUFJVkMsT0FBTztlQUFQQSxnQkFBTzs7SUFDbEJDLE1BQU07ZUFBTkEsY0FBTTs7SUFJYkMsVUFBVTtlQUFWQSxzQkFBVTs7SUFFR0MsbUJBQW1CO2VBQWhDUCxxQkFBUzs7SUFEVFEsaUJBQWlCO2VBQWpCQSw2QkFBaUI7O0lBYUNDLFFBQVE7ZUFBUkEsaUJBQVE7O0lBdkJJQyxnQkFBZ0I7ZUFBaEJBLHdCQUFnQjs7SUFZOUNDLG9CQUFvQjtlQUFwQkEsZ0NBQW9COztJQWRwQkMsaUJBQWlCO2VBQWpCQSxpQ0FBaUI7O0lBSVZDLGFBQWE7ZUFBYkEsNEJBQWE7O0lBV3BCQyxhQUFhO2VBQWJBLHlCQUFhOztJQVNOQyxNQUFNO2VBQU5BLGVBQU07OzsrREEvQm1COzZEQUNGOzZCQUNKO2dDQU1yQjt3QkFDaUQ7K0JBRTFCO2dFQUVLO3dCQUNaO3dCQUNHOzRCQVFuQjt1QkFNQTt5QkFFZ0I7aUVBQ2EifQ==

View File

@@ -0,0 +1,2 @@
export type { Props } from '../../dist/admin/components/forms/field-types/Array/types';
//# sourceMappingURL=Array.d.ts.map

View File

@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9

View File

@@ -0,0 +1,9 @@
export { BlockRow } from '../../dist/admin/components/forms/field-types/Blocks/BlockRow';
export { BlocksDrawer } from '../../dist/admin/components/forms/field-types/Blocks/BlocksDrawer';
export { default as BlockSearch } from '../../dist/admin/components/forms/field-types/Blocks/BlocksDrawer/BlockSearch';
export type { Props as BlocksDrawerProps } from '../../dist/admin/components/forms/field-types/Blocks/BlocksDrawer/types';
export { RowActions } from '../../dist/admin/components/forms/field-types/Blocks/RowActions';
export { default as SectionTitle } from '../../dist/admin/components/forms/field-types/Blocks/SectionTitle/index';
export type { Props as SectionTitleProps } from '../../dist/admin/components/forms/field-types/Blocks/SectionTitle/types';
export type { Props } from '../../dist/admin/components/forms/field-types/Blocks/types';
//# sourceMappingURL=Blocks.d.ts.map

View File

@@ -0,0 +1,39 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
BlockRow: function() {
return _BlockRow.BlockRow;
},
BlockSearch: function() {
return _BlockSearch.default;
},
BlocksDrawer: function() {
return _BlocksDrawer.BlocksDrawer;
},
RowActions: function() {
return _RowActions.RowActions;
},
SectionTitle: function() {
return _index.default;
}
});
const _BlockRow = require("../../dist/admin/components/forms/field-types/Blocks/BlockRow");
const _BlocksDrawer = require("../../dist/admin/components/forms/field-types/Blocks/BlocksDrawer");
const _BlockSearch = /*#__PURE__*/ _interop_require_default(require("../../dist/admin/components/forms/field-types/Blocks/BlocksDrawer/BlockSearch"));
const _RowActions = require("../../dist/admin/components/forms/field-types/Blocks/RowActions");
const _index = /*#__PURE__*/ _interop_require_default(require("../../dist/admin/components/forms/field-types/Blocks/SectionTitle/index"));
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9leHBvcnRzL2NvbXBvbmVudHMvZmllbGRzL0Jsb2Nrcy50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgeyBCbG9ja1JvdyB9IGZyb20gJy4uLy4uLy4uL2FkbWluL2NvbXBvbmVudHMvZm9ybXMvZmllbGQtdHlwZXMvQmxvY2tzL0Jsb2NrUm93J1xuZXhwb3J0IHsgQmxvY2tzRHJhd2VyIH0gZnJvbSAnLi4vLi4vLi4vYWRtaW4vY29tcG9uZW50cy9mb3Jtcy9maWVsZC10eXBlcy9CbG9ja3MvQmxvY2tzRHJhd2VyJ1xuZXhwb3J0IHsgZGVmYXVsdCBhcyBCbG9ja1NlYXJjaCB9IGZyb20gJy4uLy4uLy4uL2FkbWluL2NvbXBvbmVudHMvZm9ybXMvZmllbGQtdHlwZXMvQmxvY2tzL0Jsb2Nrc0RyYXdlci9CbG9ja1NlYXJjaCdcbmV4cG9ydCB0eXBlIHsgUHJvcHMgYXMgQmxvY2tzRHJhd2VyUHJvcHMgfSBmcm9tICcuLi8uLi8uLi9hZG1pbi9jb21wb25lbnRzL2Zvcm1zL2ZpZWxkLXR5cGVzL0Jsb2Nrcy9CbG9ja3NEcmF3ZXIvdHlwZXMnXG5leHBvcnQgeyBSb3dBY3Rpb25zIH0gZnJvbSAnLi4vLi4vLi4vYWRtaW4vY29tcG9uZW50cy9mb3Jtcy9maWVsZC10eXBlcy9CbG9ja3MvUm93QWN0aW9ucydcbmV4cG9ydCB7IGRlZmF1bHQgYXMgU2VjdGlvblRpdGxlIH0gZnJvbSAnLi4vLi4vLi4vYWRtaW4vY29tcG9uZW50cy9mb3Jtcy9maWVsZC10eXBlcy9CbG9ja3MvU2VjdGlvblRpdGxlL2luZGV4J1xuZXhwb3J0IHR5cGUgeyBQcm9wcyBhcyBTZWN0aW9uVGl0bGVQcm9wcyB9IGZyb20gJy4uLy4uLy4uL2FkbWluL2NvbXBvbmVudHMvZm9ybXMvZmllbGQtdHlwZXMvQmxvY2tzL1NlY3Rpb25UaXRsZS90eXBlcydcbmV4cG9ydCB0eXBlIHsgUHJvcHMgfSBmcm9tICcuLi8uLi8uLi9hZG1pbi9jb21wb25lbnRzL2Zvcm1zL2ZpZWxkLXR5cGVzL0Jsb2Nrcy90eXBlcydcbiJdLCJuYW1lcyI6WyJCbG9ja1JvdyIsIkJsb2NrU2VhcmNoIiwiQmxvY2tzRHJhd2VyIiwiUm93QWN0aW9ucyIsIlNlY3Rpb25UaXRsZSJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7SUFBU0EsUUFBUTtlQUFSQSxrQkFBUTs7SUFFR0MsV0FBVztlQUFYQSxvQkFBVzs7SUFEdEJDLFlBQVk7ZUFBWkEsMEJBQVk7O0lBR1pDLFVBQVU7ZUFBVkEsc0JBQVU7O0lBQ0NDLFlBQVk7ZUFBWkEsY0FBWTs7OzBCQUxQOzhCQUNJO29FQUNVOzRCQUVaOzhEQUNhIn0=

View File

@@ -0,0 +1,2 @@
export type { Props } from '../../dist/admin/components/views/collections/List/Cell/types';
//# sourceMappingURL=Cell.d.ts.map

View File

@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9

View File

@@ -0,0 +1,2 @@
export type { Props } from '../../dist/admin/components/forms/field-types/Checkbox/types';
//# sourceMappingURL=Checkbox.d.ts.map

View File

@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9

View File

@@ -0,0 +1,2 @@
export type { Props } from '../../dist/admin/components/forms/field-types/Code/types';
//# sourceMappingURL=Code.d.ts.map

View File

@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9

View File

@@ -0,0 +1,2 @@
export type { Props } from '../../dist/admin/components/forms/field-types/DateTime/types';
//# sourceMappingURL=DateTime.d.ts.map

View File

@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9

View File

@@ -0,0 +1,2 @@
export type { Props } from '../../dist/admin/components/forms/field-types/Email/types';
//# sourceMappingURL=Email.d.ts.map

View File

@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9

View File

@@ -0,0 +1,2 @@
export type { Props } from '../../dist/admin/components/forms/field-types/Group/types';
//# sourceMappingURL=Group.d.ts.map

View File

@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9

View File

@@ -0,0 +1,2 @@
export type { Props } from '../../dist/admin/components/forms/field-types/JSON/types';
//# sourceMappingURL=Json.d.ts.map

View File

@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9

View File

@@ -0,0 +1,2 @@
export type { Props } from '../../dist/admin/components/forms/field-types/Number/types';
//# sourceMappingURL=Number.d.ts.map

View File

@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9

View File

@@ -0,0 +1,2 @@
export type { Props } from '../../dist/admin/components/forms/field-types/Password/types';
//# sourceMappingURL=Password.d.ts.map

View File

@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9

View File

@@ -0,0 +1,2 @@
export type { Props } from '../../../dist/admin/components/forms/field-types/RadioGroup/RadioInput/types';
//# sourceMappingURL=RadioInput.d.ts.map

View File

@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9

View File

@@ -0,0 +1,2 @@
export type { Props } from '../../../dist/admin/components/forms/field-types/RadioGroup/types';
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9

View File

@@ -0,0 +1,3 @@
export { default as RelationshipComponent } from '../../dist/admin/components/forms/field-types/Relationship';
export type { Option, Props, ValueWithRelation, } from '../../dist/admin/components/forms/field-types/Relationship/types';
//# sourceMappingURL=Relationship.d.ts.map

View File

@@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "RelationshipComponent", {
enumerable: true,
get: function() {
return _Relationship.default;
}
});
const _Relationship = /*#__PURE__*/ _interop_require_default(require("../../dist/admin/components/forms/field-types/Relationship"));
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9leHBvcnRzL2NvbXBvbmVudHMvZmllbGRzL1JlbGF0aW9uc2hpcC50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgeyBkZWZhdWx0IGFzIFJlbGF0aW9uc2hpcENvbXBvbmVudCB9IGZyb20gJy4uLy4uLy4uL2FkbWluL2NvbXBvbmVudHMvZm9ybXMvZmllbGQtdHlwZXMvUmVsYXRpb25zaGlwJ1xuZXhwb3J0IHR5cGUge1xuICBPcHRpb24sXG4gIFByb3BzLFxuICBWYWx1ZVdpdGhSZWxhdGlvbixcbn0gZnJvbSAnLi4vLi4vLi4vYWRtaW4vY29tcG9uZW50cy9mb3Jtcy9maWVsZC10eXBlcy9SZWxhdGlvbnNoaXAvdHlwZXMnXG4iXSwibmFtZXMiOlsiUmVsYXRpb25zaGlwQ29tcG9uZW50Il0sIm1hcHBpbmdzIjoiOzs7OytCQUFvQkE7OztlQUFBQSxxQkFBcUI7OztxRUFBUSJ9

View File

@@ -0,0 +1,2 @@
export type { RichTextFieldProps } from '../../dist/admin/components/forms/field-types/RichText/types';
//# sourceMappingURL=RichText.d.ts.map

View File

@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9

View File

@@ -0,0 +1,2 @@
export type { Props } from '../../dist/admin/components/forms/field-types/Row/types';
//# sourceMappingURL=Row.d.ts.map

View File

@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9

View File

@@ -0,0 +1,3 @@
export { default as SelectComponent } from '../../dist/admin/components/forms/field-types/Select';
export type { Props } from '../../dist/admin/components/forms/field-types/Select/types';
//# sourceMappingURL=Select.d.ts.map

View File

@@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "SelectComponent", {
enumerable: true,
get: function() {
return _Select.default;
}
});
const _Select = /*#__PURE__*/ _interop_require_default(require("../../dist/admin/components/forms/field-types/Select"));
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9leHBvcnRzL2NvbXBvbmVudHMvZmllbGRzL1NlbGVjdC50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgeyBkZWZhdWx0IGFzIFNlbGVjdENvbXBvbmVudCB9IGZyb20gJy4uLy4uLy4uL2FkbWluL2NvbXBvbmVudHMvZm9ybXMvZmllbGQtdHlwZXMvU2VsZWN0J1xuZXhwb3J0IHR5cGUgeyBQcm9wcyB9IGZyb20gJy4uLy4uLy4uL2FkbWluL2NvbXBvbmVudHMvZm9ybXMvZmllbGQtdHlwZXMvU2VsZWN0L3R5cGVzJ1xuIl0sIm5hbWVzIjpbIlNlbGVjdENvbXBvbmVudCJdLCJtYXBwaW5ncyI6Ijs7OzsrQkFBb0JBOzs7ZUFBQUEsZUFBZTs7OytEQUFRIn0=

View File

@@ -0,0 +1,2 @@
export type { Props } from '../../dist/admin/components/forms/field-types/Text/types';
//# sourceMappingURL=Text.d.ts.map

View File

@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9

View File

@@ -0,0 +1,2 @@
export type { Props } from '../../dist/admin/components/forms/field-types/Textarea/types';
//# sourceMappingURL=Textarea.d.ts.map

View File

@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9

View File

@@ -0,0 +1,2 @@
export type { Props } from '../../dist/admin/components/forms/field-types/Upload/types';
//# sourceMappingURL=Upload.d.ts.map

View File

@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9

38
packages/payload/components/forms.d.ts vendored Normal file
View File

@@ -0,0 +1,38 @@
export { default as Error } from '../dist/admin/components/forms/Error';
export { default as FieldDescription } from '../dist/admin/components/forms/FieldDescription';
export { default as Form } from '../dist/admin/components/forms/Form';
export { default as buildInitialState } from '../dist/admin/components/forms/Form/buildInitialState';
export { useAllFormFields, useForm, useFormFields, useFormModified, useFormProcessing, useFormSubmitted,
/**
* @deprecated useWatchForm is no longer preferred. If you need all form fields, prefer `useAllFormFields`.
*/
useWatchForm, } from '../dist/admin/components/forms/Form/context';
export { createNestedFieldPath } from '../dist/admin/components/forms/Form/createNestedFieldPath';
export { default as getSiblingData } from '../dist/admin/components/forms/Form/getSiblingData';
export { default as reduceFieldsToValues } from '../dist/admin/components/forms/Form/reduceFieldsToValues';
export { default as Label } from '../dist/admin/components/forms/Label';
export { default as RenderFields } from '../dist/admin/components/forms/RenderFields';
export { default as Submit } from '../dist/admin/components/forms/Submit';
export { default as FormSubmit } from '../dist/admin/components/forms/Submit';
export { fieldTypes } from '../dist/admin/components/forms/field-types';
export { default as Checkbox } from '../dist/admin/components/forms/field-types/Checkbox';
export { default as Collapsible } from '../dist/admin/components/forms/field-types/Collapsible';
export { default as Date } from '../dist/admin/components/forms/field-types/DateTime';
export { DateTimeInput } from '../dist/admin/components/forms/field-types/DateTime/Input';
export { default as Group } from '../dist/admin/components/forms/field-types/Group';
export { default as HiddenInput } from '../dist/admin/components/forms/field-types/HiddenInput';
export { default as Select } from '../dist/admin/components/forms/field-types/Select';
export { default as SelectInput } from '../dist/admin/components/forms/field-types/Select/Input';
export { default as Text } from '../dist/admin/components/forms/field-types/Text';
export { default as TextInput } from '../dist/admin/components/forms/field-types/Text/Input';
export { default as Textarea } from '../dist/admin/components/forms/field-types/Textarea';
export { default as TextareaInput } from '../dist/admin/components/forms/field-types/Textarea/Input';
export { default as Upload } from '../dist/admin/components/forms/field-types/Upload';
export { default as UploadInput } from '../dist/admin/components/forms/field-types/Upload/Input';
/**
* @deprecated This method is now called useField. The useFieldType alias will be removed in an upcoming version.
*/
export { default as useFieldType } from '../dist/admin/components/forms/useField';
export { default as useField } from '../dist/admin/components/forms/useField';
export { default as withCondition } from '../dist/admin/components/forms/withCondition';
//# sourceMappingURL=forms.d.ts.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,7 @@
export { default as AccountGraphic } from '../dist/admin/components/graphics/Account';
export { default as DefaultBlockImageGraphic } from '../dist/admin/components/graphics/DefaultBlockImage';
export { default as FileGraphic } from '../dist/admin/components/graphics/File';
export { default as IconGraphic } from '../dist/admin/components/graphics/Icon';
export { default as LogoGraphic } from '../dist/admin/components/graphics/Logo';
export { default as SearchGraphic } from '../dist/admin/components/graphics/Search';
//# sourceMappingURL=graphics.d.ts.map

View File

@@ -0,0 +1,43 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
AccountGraphic: function() {
return _Account.default;
},
DefaultBlockImageGraphic: function() {
return _DefaultBlockImage.default;
},
FileGraphic: function() {
return _File.default;
},
IconGraphic: function() {
return _Icon.default;
},
LogoGraphic: function() {
return _Logo.default;
},
SearchGraphic: function() {
return _Search.default;
}
});
const _Account = /*#__PURE__*/ _interop_require_default(require("../dist/admin/components/graphics/Account"));
const _DefaultBlockImage = /*#__PURE__*/ _interop_require_default(require("../dist/admin/components/graphics/DefaultBlockImage"));
const _File = /*#__PURE__*/ _interop_require_default(require("../dist/admin/components/graphics/File"));
const _Icon = /*#__PURE__*/ _interop_require_default(require("../dist/admin/components/graphics/Icon"));
const _Logo = /*#__PURE__*/ _interop_require_default(require("../dist/admin/components/graphics/Logo"));
const _Search = /*#__PURE__*/ _interop_require_default(require("../dist/admin/components/graphics/Search"));
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9leHBvcnRzL2NvbXBvbmVudHMvZ3JhcGhpY3MudHMiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IHsgZGVmYXVsdCBhcyBBY2NvdW50R3JhcGhpYyB9IGZyb20gJy4uLy4uL2FkbWluL2NvbXBvbmVudHMvZ3JhcGhpY3MvQWNjb3VudCdcbmV4cG9ydCB7IGRlZmF1bHQgYXMgRGVmYXVsdEJsb2NrSW1hZ2VHcmFwaGljIH0gZnJvbSAnLi4vLi4vYWRtaW4vY29tcG9uZW50cy9ncmFwaGljcy9EZWZhdWx0QmxvY2tJbWFnZSdcbmV4cG9ydCB7IGRlZmF1bHQgYXMgRmlsZUdyYXBoaWMgfSBmcm9tICcuLi8uLi9hZG1pbi9jb21wb25lbnRzL2dyYXBoaWNzL0ZpbGUnXG5leHBvcnQgeyBkZWZhdWx0IGFzIEljb25HcmFwaGljIH0gZnJvbSAnLi4vLi4vYWRtaW4vY29tcG9uZW50cy9ncmFwaGljcy9JY29uJ1xuZXhwb3J0IHsgZGVmYXVsdCBhcyBMb2dvR3JhcGhpYyB9IGZyb20gJy4uLy4uL2FkbWluL2NvbXBvbmVudHMvZ3JhcGhpY3MvTG9nbydcbmV4cG9ydCB7IGRlZmF1bHQgYXMgU2VhcmNoR3JhcGhpYyB9IGZyb20gJy4uLy4uL2FkbWluL2NvbXBvbmVudHMvZ3JhcGhpY3MvU2VhcmNoJ1xuIl0sIm5hbWVzIjpbIkFjY291bnRHcmFwaGljIiwiRGVmYXVsdEJsb2NrSW1hZ2VHcmFwaGljIiwiRmlsZUdyYXBoaWMiLCJJY29uR3JhcGhpYyIsIkxvZ29HcmFwaGljIiwiU2VhcmNoR3JhcGhpYyJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7SUFBb0JBLGNBQWM7ZUFBZEEsZ0JBQWM7O0lBQ2RDLHdCQUF3QjtlQUF4QkEsMEJBQXdCOztJQUN4QkMsV0FBVztlQUFYQSxhQUFXOztJQUNYQyxXQUFXO2VBQVhBLGFBQVc7O0lBQ1hDLFdBQVc7ZUFBWEEsYUFBVzs7SUFDWEMsYUFBYTtlQUFiQSxlQUFhOzs7Z0VBTFM7MEVBQ1U7NkRBQ2I7NkRBQ0E7NkRBQ0E7K0RBQ0UifQ==

15
packages/payload/components/hooks.d.ts vendored Normal file
View File

@@ -0,0 +1,15 @@
export { useStepNav } from '../dist/admin/components/elements/StepNav';
export { useTableColumns } from '../dist/admin/components/elements/TableColumns';
export { useDocumentEvents } from '../dist/admin/components/utilities/DocumentEvents';
export { default as useDebounce } from '../dist/admin/hooks/useDebounce';
export { useDebouncedCallback } from '../dist/admin/hooks/useDebouncedCallback';
export { useDelay } from '../dist/admin/hooks/useDelay';
export { useDelayedRender } from '../dist/admin/hooks/useDelayedRender';
export { default as useHotkey } from '../dist/admin/hooks/useHotkey';
export { default as useIntersect } from '../dist/admin/hooks/useIntersect';
export { default as useMountEffect } from '../dist/admin/hooks/useMountEffect';
export { default as usePayloadAPI } from '../dist/admin/hooks/usePayloadAPI';
export { default as useThrottledEffect } from '../dist/admin/hooks/useThrottledEffect';
export { default as useThumbnail } from '../dist/admin/hooks/useThumbnail';
export { default as useTitle, formatUseAsTitle } from '../dist/admin/hooks/useTitle';
//# sourceMappingURL=hooks.d.ts.map

View File

@@ -0,0 +1,119 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
formatUseAsTitle: function() {
return _useTitle.formatUseAsTitle;
},
useDebounce: function() {
return _useDebounce.default;
},
useDebouncedCallback: function() {
return _useDebouncedCallback.useDebouncedCallback;
},
useDelay: function() {
return _useDelay.useDelay;
},
useDelayedRender: function() {
return _useDelayedRender.useDelayedRender;
},
useDocumentEvents: function() {
return _DocumentEvents.useDocumentEvents;
},
useHotkey: function() {
return _useHotkey.default;
},
useIntersect: function() {
return _useIntersect.default;
},
useMountEffect: function() {
return _useMountEffect.default;
},
usePayloadAPI: function() {
return _usePayloadAPI.default;
},
useStepNav: function() {
return _StepNav.useStepNav;
},
useTableColumns: function() {
return _TableColumns.useTableColumns;
},
useThrottledEffect: function() {
return _useThrottledEffect.default;
},
useThumbnail: function() {
return _useThumbnail.default;
},
useTitle: function() {
return _useTitle.default;
}
});
const _StepNav = require("../dist/admin/components/elements/StepNav");
const _TableColumns = require("../dist/admin/components/elements/TableColumns");
const _DocumentEvents = require("../dist/admin/components/utilities/DocumentEvents");
const _useDebounce = /*#__PURE__*/ _interop_require_default(require("../dist/admin/hooks/useDebounce"));
const _useDebouncedCallback = require("../dist/admin/hooks/useDebouncedCallback");
const _useDelay = require("../dist/admin/hooks/useDelay");
const _useDelayedRender = require("../dist/admin/hooks/useDelayedRender");
const _useHotkey = /*#__PURE__*/ _interop_require_default(require("../dist/admin/hooks/useHotkey"));
const _useIntersect = /*#__PURE__*/ _interop_require_default(require("../dist/admin/hooks/useIntersect"));
const _useMountEffect = /*#__PURE__*/ _interop_require_default(require("../dist/admin/hooks/useMountEffect"));
const _usePayloadAPI = /*#__PURE__*/ _interop_require_default(require("../dist/admin/hooks/usePayloadAPI"));
const _useThrottledEffect = /*#__PURE__*/ _interop_require_default(require("../dist/admin/hooks/useThrottledEffect"));
const _useThumbnail = /*#__PURE__*/ _interop_require_default(require("../dist/admin/hooks/useThumbnail"));
const _useTitle = /*#__PURE__*/ _interop_require_wildcard(require("../dist/admin/hooks/useTitle"));
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
function _getRequireWildcardCache(nodeInterop) {
if (typeof WeakMap !== "function") return null;
var cacheBabelInterop = new WeakMap();
var cacheNodeInterop = new WeakMap();
return (_getRequireWildcardCache = function(nodeInterop) {
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
})(nodeInterop);
}
function _interop_require_wildcard(obj, nodeInterop) {
if (!nodeInterop && obj && obj.__esModule) {
return obj;
}
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
return {
default: obj
};
}
var cache = _getRequireWildcardCache(nodeInterop);
if (cache && cache.has(obj)) {
return cache.get(obj);
}
var newObj = {
__proto__: null
};
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
for(var key in obj){
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
if (desc && (desc.get || desc.set)) {
Object.defineProperty(newObj, key, desc);
} else {
newObj[key] = obj[key];
}
}
}
newObj.default = obj;
if (cache) {
cache.set(obj, newObj);
}
return newObj;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9leHBvcnRzL2NvbXBvbmVudHMvaG9va3MudHMiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IHsgdXNlU3RlcE5hdiB9IGZyb20gJy4uLy4uL2FkbWluL2NvbXBvbmVudHMvZWxlbWVudHMvU3RlcE5hdidcbmV4cG9ydCB7IHVzZVRhYmxlQ29sdW1ucyB9IGZyb20gJy4uLy4uL2FkbWluL2NvbXBvbmVudHMvZWxlbWVudHMvVGFibGVDb2x1bW5zJ1xuZXhwb3J0IHsgdXNlRG9jdW1lbnRFdmVudHMgfSBmcm9tICcuLi8uLi9hZG1pbi9jb21wb25lbnRzL3V0aWxpdGllcy9Eb2N1bWVudEV2ZW50cydcbmV4cG9ydCB7IGRlZmF1bHQgYXMgdXNlRGVib3VuY2UgfSBmcm9tICcuLi8uLi9hZG1pbi9ob29rcy91c2VEZWJvdW5jZSdcbmV4cG9ydCB7IHVzZURlYm91bmNlZENhbGxiYWNrIH0gZnJvbSAnLi4vLi4vYWRtaW4vaG9va3MvdXNlRGVib3VuY2VkQ2FsbGJhY2snXG5leHBvcnQgeyB1c2VEZWxheSB9IGZyb20gJy4uLy4uL2FkbWluL2hvb2tzL3VzZURlbGF5J1xuZXhwb3J0IHsgdXNlRGVsYXllZFJlbmRlciB9IGZyb20gJy4uLy4uL2FkbWluL2hvb2tzL3VzZURlbGF5ZWRSZW5kZXInXG5leHBvcnQgeyBkZWZhdWx0IGFzIHVzZUhvdGtleSB9IGZyb20gJy4uLy4uL2FkbWluL2hvb2tzL3VzZUhvdGtleSdcbmV4cG9ydCB7IGRlZmF1bHQgYXMgdXNlSW50ZXJzZWN0IH0gZnJvbSAnLi4vLi4vYWRtaW4vaG9va3MvdXNlSW50ZXJzZWN0J1xuZXhwb3J0IHsgZGVmYXVsdCBhcyB1c2VNb3VudEVmZmVjdCB9IGZyb20gJy4uLy4uL2FkbWluL2hvb2tzL3VzZU1vdW50RWZmZWN0J1xuZXhwb3J0IHsgZGVmYXVsdCBhcyB1c2VQYXlsb2FkQVBJIH0gZnJvbSAnLi4vLi4vYWRtaW4vaG9va3MvdXNlUGF5bG9hZEFQSSdcbmV4cG9ydCB7IGRlZmF1bHQgYXMgdXNlVGhyb3R0bGVkRWZmZWN0IH0gZnJvbSAnLi4vLi4vYWRtaW4vaG9va3MvdXNlVGhyb3R0bGVkRWZmZWN0J1xuZXhwb3J0IHsgZGVmYXVsdCBhcyB1c2VUaHVtYm5haWwgfSBmcm9tICcuLi8uLi9hZG1pbi9ob29rcy91c2VUaHVtYm5haWwnXG5leHBvcnQgeyBkZWZhdWx0IGFzIHVzZVRpdGxlLCBmb3JtYXRVc2VBc1RpdGxlIH0gZnJvbSAnLi4vLi4vYWRtaW4vaG9va3MvdXNlVGl0bGUnXG4iXSwibmFtZXMiOlsiZm9ybWF0VXNlQXNUaXRsZSIsInVzZURlYm91bmNlIiwidXNlRGVib3VuY2VkQ2FsbGJhY2siLCJ1c2VEZWxheSIsInVzZURlbGF5ZWRSZW5kZXIiLCJ1c2VEb2N1bWVudEV2ZW50cyIsInVzZUhvdGtleSIsInVzZUludGVyc2VjdCIsInVzZU1vdW50RWZmZWN0IiwidXNlUGF5bG9hZEFQSSIsInVzZVN0ZXBOYXYiLCJ1c2VUYWJsZUNvbHVtbnMiLCJ1c2VUaHJvdHRsZWRFZmZlY3QiLCJ1c2VUaHVtYm5haWwiLCJ1c2VUaXRsZSJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7SUFhOEJBLGdCQUFnQjtlQUFoQkEsMEJBQWdCOztJQVYxQkMsV0FBVztlQUFYQSxvQkFBVzs7SUFDdEJDLG9CQUFvQjtlQUFwQkEsMENBQW9COztJQUNwQkMsUUFBUTtlQUFSQSxrQkFBUTs7SUFDUkMsZ0JBQWdCO2VBQWhCQSxrQ0FBZ0I7O0lBSmhCQyxpQkFBaUI7ZUFBakJBLGlDQUFpQjs7SUFLTkMsU0FBUztlQUFUQSxrQkFBUzs7SUFDVEMsWUFBWTtlQUFaQSxxQkFBWTs7SUFDWkMsY0FBYztlQUFkQSx1QkFBYzs7SUFDZEMsYUFBYTtlQUFiQSxzQkFBYTs7SUFWeEJDLFVBQVU7ZUFBVkEsbUJBQVU7O0lBQ1ZDLGVBQWU7ZUFBZkEsNkJBQWU7O0lBVUpDLGtCQUFrQjtlQUFsQkEsMkJBQWtCOztJQUNsQkMsWUFBWTtlQUFaQSxxQkFBWTs7SUFDWkMsUUFBUTtlQUFSQSxpQkFBUTs7O3lCQWJEOzhCQUNLO2dDQUNFO29FQUNLO3NDQUNGOzBCQUNaO2tDQUNRO2tFQUNJO3FFQUNHO3VFQUNFO3NFQUNEOzJFQUNLO3FFQUNOO2tFQUNjIn0=

View File

@@ -0,0 +1,3 @@
export { default as Chevron } from '../dist/admin/components/icons/Chevron';
export { default as X } from '../dist/admin/components/icons/X';
//# sourceMappingURL=icons.d.ts.map

View File

@@ -0,0 +1,27 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
Chevron: function() {
return _Chevron.default;
},
X: function() {
return _X.default;
}
});
const _Chevron = /*#__PURE__*/ _interop_require_default(require("../dist/admin/components/icons/Chevron"));
const _X = /*#__PURE__*/ _interop_require_default(require("../dist/admin/components/icons/X"));
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9leHBvcnRzL2NvbXBvbmVudHMvaWNvbnMudHMiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IHsgZGVmYXVsdCBhcyBDaGV2cm9uIH0gZnJvbSAnLi4vLi4vYWRtaW4vY29tcG9uZW50cy9pY29ucy9DaGV2cm9uJ1xuZXhwb3J0IHsgZGVmYXVsdCBhcyBYIH0gZnJvbSAnLi4vLi4vYWRtaW4vY29tcG9uZW50cy9pY29ucy9YJ1xuIl0sIm5hbWVzIjpbIkNoZXZyb24iLCJYIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7OztJQUFvQkEsT0FBTztlQUFQQSxnQkFBTzs7SUFDUEMsQ0FBQztlQUFEQSxVQUFDOzs7Z0VBRGM7MERBQ04ifQ==

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