chore: initial next poc

This commit is contained in:
James
2023-11-07 13:59:59 -05:00
parent 6b1b4ffd27
commit 0996f8cccb
43 changed files with 1074 additions and 1132 deletions

2
.vscode/launch.json vendored
View File

@@ -27,7 +27,7 @@
"type": "node-terminal"
},
{
"command": "pnpm run dev:postgres fields",
"command": "pnpm run dev:postgres versions",
"cwd": "${workspaceFolder}",
"name": "Run Dev Postgres",
"request": "launch",

View File

@@ -11,6 +11,13 @@
"url": "https://payloadcms.com"
},
"main": "./dist/index.js",
"exports": {
".": {
"import": "./src/index.ts",
"require": "./src/index.ts",
"types": "./src/index.ts"
}
},
"types": "./dist/index.d.ts",
"scripts": {
"build": "pnpm build:swc && pnpm build:types",

View File

@@ -1,5 +1,5 @@
/* eslint-disable no-restricted-syntax, no-await-in-loop */
import type { CreateMigration } from 'payload/database'
// import type { CreateMigration } from 'payload/database'
import fs from 'fs'
import path from 'path'
@@ -18,11 +18,7 @@ ${downSQL ?? ` // Migration code`}
};
`
export const createMigration: CreateMigration = async function createMigration({
file,
migrationName,
payload,
}) {
export const createMigration = async function createMigration({ file, migrationName, payload }) {
const dir = payload.db.migrationDir
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir)
@@ -38,8 +34,8 @@ export const createMigration: CreateMigration = async function createMigration({
// Check if predefined migration exists
if (fs.existsSync(cleanPath)) {
const { down, up } = require(cleanPath)
migrationFileContent = migrationTemplate(up, down)
// const { down, up } = require(cleanPath)
// migrationFileContent = migrationTemplate(up, down)
} else {
payload.logger.error({
msg: `Canned migration ${predefinedMigrationName} not found.`,

5
packages/dev/next-env.d.ts vendored Normal file
View File

@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

View File

@@ -0,0 +1,15 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
// experimental: {
// serverComponentsExternalPackages: ['@payloadcms/db-mongodb'],
// },
// transpilePackages: ['@payloadcms/db-mongodb'],
webpack: (config) => {
return {
...config,
externals: [...config.externals, 'mongoose'],
}
},
}
module.exports = nextConfig

25
packages/dev/package.json Normal file
View File

@@ -0,0 +1,25 @@
{
"name": "dev",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"next": "14.0.2-canary.18",
"react": "^18",
"react-dom": "^18"
},
"devDependencies": {
"@payloadcms/db-mongodb": "workspace:*",
"@payloadcms/richtext-lexical": "workspace:*",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"payload": "workspace:*",
"typescript": "^5"
}
}

View File

@@ -0,0 +1,4 @@
import { dashboard } from 'payload/pages'
import config from '../../../payload.config'
export default dashboard({ config })

View File

@@ -0,0 +1,4 @@
import { initHandler } from 'payload/handlers'
import config from 'payload-config'
export const GET = initHandler({ config })

View File

@@ -0,0 +1,12 @@
export const metadata = {
title: 'Next.js',
description: 'Generated by Next.js',
}
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}

View File

@@ -0,0 +1,5 @@
export const GET = (request) => {
return Response.json({
hello: 'elliot',
})
}

View File

@@ -0,0 +1,11 @@
import { mongooseAdapter } from '@payloadcms/db-mongodb'
// import { lexicalEditor } from '@payloadcms/richtext-lexical'
import { buildConfig } from 'payload/config'
export default buildConfig({
db: mongooseAdapter({
url: process.env.DATABASE_URI,
}),
// editor: lexicalEditor({}),
secret: process.env.PAYLOAD_SECRET,
})

View File

@@ -0,0 +1,27 @@
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"noEmit": true,
"incremental": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"plugins": [
{
"name": "next"
}
],
"paths": {
"payload-config": ["./src/payload.config.ts"]
}
},
"include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"],
"references": [{ "path": "../payload" }]
}

View File

@@ -1,15 +0,0 @@
{
"$schema": "https://json.schemastore.org/nodemon.json",
"exec": "ts-node ../../test/dev.ts -- -I",
"ext": "ts,js,json",
"ignore": [
".git",
"node_modules",
"node_modules/**/node_modules",
"src/admin",
"src/**/*.spec.ts",
"test/**/payload-types.ts"
],
"watch": ["src/**/*.ts", "../../test/", "../../packages/**/*.ts"],
"stdin": false
}

View File

@@ -54,59 +54,38 @@
"@faceless-ui/scroll-info": "1.3.0",
"@faceless-ui/window-info": "2.1.1",
"@monaco-editor/react": "4.5.1",
"@swc/core": "1.3.76",
"@swc/register": "0.1.10",
"body-parser": "1.20.2",
"body-scroll-lock": "4.0.0-beta.0",
"bson-objectid": "2.0.4",
"compression": "1.7.4",
"conf": "10.2.0",
"connect-history-api-fallback": "1.6.0",
"console-table-printer": "2.11.2",
"dataloader": "2.2.2",
"date-fns": "2.30.0",
"deep-equal": "2.2.2",
"deepmerge": "4.3.1",
"dotenv": "8.6.0",
"express": "4.18.2",
"express-fileupload": "1.4.0",
"express-rate-limit": "5.5.1",
"file-type": "16.5.4",
"find-up": "4.1.0",
"flatley": "5.2.0",
"fs-extra": "10.1.0",
"get-tsconfig": "4.6.2",
"graphql": "16.8.1",
"graphql-http": "1.21.0",
"graphql-playground-middleware-express": "1.7.23",
"graphql-query-complexity": "0.12.0",
"graphql-scalars": "1.22.2",
"graphql-type-json": "0.3.2",
"html-webpack-plugin": "5.5.3",
"http-status": "1.6.2",
"i18next": "22.5.1",
"i18next-browser-languagedetector": "6.1.8",
"i18next-http-middleware": "3.3.2",
"is-hotkey": "0.2.0",
"is-plain-object": "5.0.0",
"isomorphic-fetch": "3.0.0",
"joi": "17.9.2",
"json-schema-to-typescript": "11.0.3",
"jsonwebtoken": "9.0.1",
"jwt-decode": "3.1.2",
"md5": "2.3.0",
"method-override": "3.0.0",
"micro-memoize": "4.1.2",
"minimist": "1.2.8",
"mkdirp": "1.0.4",
"monaco-editor": "0.38.0",
"nodemailer": "6.9.4",
"object-to-formdata": "4.5.1",
"passport": "0.6.0",
"passport-anonymous": "1.0.1",
"passport-headerapikey": "1.2.2",
"passport-jwt": "4.0.1",
"passport-local": "1.0.0",
"pino": "8.15.0",
"pino-pretty": "10.2.0",
"pluralize": "8.0.0",
@@ -114,25 +93,17 @@
"process": "0.11.10",
"qs": "6.11.2",
"qs-middleware": "1.0.3",
"react": "18.2.0",
"react-animate-height": "2.1.2",
"react-datepicker": "4.16.0",
"react-diff-viewer-continued": "3.2.6",
"react-dom": "18.2.0",
"react-helmet": "6.1.0",
"react-i18next": "11.18.6",
"react-image-crop": "10.1.8",
"react-router-dom": "5.3.4",
"react-router-navigation-prompt": "1.9.6",
"react-select": "5.7.4",
"react-toastify": "8.2.0",
"sanitize-filename": "1.6.3",
"sass": "1.69.4",
"scheduler": "0.23.0",
"scmp": "2.1.0",
"sharp": "0.32.6",
"swc-loader": "0.2.3",
"terser-webpack-plugin": "5.3.9",
"ts-essentials": "7.0.3",
"use-context-selector": "1.4.1",
"uuid": "8.3.2"

View File

@@ -0,0 +1,13 @@
'use client'
import React from 'react'
import type { ClientConfig } from '../../../exports/config'
export const ClientComponent: React.FC<{ config: ClientConfig }> = ({ config }) => {
return (
<p>
This is a test client component and I have received the Payload config via props. No Webpack
aliases required!
</p>
)
}

View File

@@ -0,0 +1,18 @@
import React from 'react'
import type { SanitizedConfig } from '../../../config/types'
import { createClientConfig } from '../../../config/createClientConfig'
import { ClientComponent } from './TestClientComponent'
export const dashboard = ({ config: configPromise }: { config: Promise<SanitizedConfig> }) =>
async function () {
const config = await createClientConfig(configPromise)
return (
<React.Fragment>
<h1>Dashboard (rendered on server)</h1>
<ClientComponent config={config} />
</React.Fragment>
)
}

View File

@@ -0,0 +1,17 @@
import type { SanitizedConfig } from '../../exports/config'
import { getPayload } from '../..'
import init from '../operations/init'
export const initHandler = ({ config }: { config: Promise<SanitizedConfig> }) =>
async function (request: Request, { params }: { params: { collection: string } }) {
const payload = await getPayload({ config })
request.payload = payload
const initialized = await init({
collection: params.collection,
req: request,
})
return Response.json({ initialized })
}

View File

@@ -1,21 +0,0 @@
import type { NextFunction, Response } from 'express'
import type { PayloadRequest } from '../../express/types'
import init from '../operations/init'
export default async function initHandler(
req: PayloadRequest,
res: Response,
next: NextFunction,
): Promise<any> {
try {
const initialized = await init({
collection: req.collection.config.slug,
req,
})
return res.status(200).json({ initialized })
} catch (error) {
return next(error)
}
}

View File

@@ -1,8 +1,8 @@
import type { MarkOptional } from 'ts-essentials'
import crypto from 'crypto'
import fs from 'fs'
import { promisify } from 'util'
// import fs from 'fs'
// import { promisify } from 'util'
import type { GeneratedTypes } from '../../'
import type { PayloadRequest } from '../../express/types'
@@ -22,9 +22,9 @@ import { afterChange } from '../../fields/hooks/afterChange'
import { afterRead } from '../../fields/hooks/afterRead'
import { beforeChange } from '../../fields/hooks/beforeChange'
import { beforeValidate } from '../../fields/hooks/beforeValidate'
import { generateFileData } from '../../uploads/generateFileData'
// import { generateFileData } from '../../uploads/generateFileData'
import { unlinkTempFiles } from '../../uploads/unlinkTempFiles'
import { uploadFiles } from '../../uploads/uploadFiles'
// import { uploadFiles } from '../../uploads/uploadFiles'
import { commitTransaction } from '../../utilities/commitTransaction'
import { initTransaction } from '../../utilities/initTransaction'
import { killTransaction } from '../../utilities/killTransaction'
@@ -32,7 +32,7 @@ import sanitizeInternalFields from '../../utilities/sanitizeInternalFields'
import { saveVersion } from '../../versions/saveVersion'
import { buildAfterOperation } from './utils'
const unlinkFile = promisify(fs.unlink)
// const unlinkFile = promisify(fs.unlink)
export type CreateUpdateType = { [field: number | string | symbol]: unknown }
@@ -123,17 +123,17 @@ async function create<TSlug extends keyof GeneratedTypes['collections']>(
// Generate data for all files and sizes
// /////////////////////////////////////
const { data: newFileData, files: filesToUpload } = await generateFileData({
collection,
config,
data,
overwriteExistingFiles,
req,
throwOnMissingFile:
!shouldSaveDraft && collection.config.upload.filesRequiredOnCreate !== false,
})
// const { data: newFileData, files: filesToUpload } = await generateFileData({
// collection,
// config,
// data,
// overwriteExistingFiles,
// req,
// throwOnMissingFile:
// !shouldSaveDraft && collection.config.upload.filesRequiredOnCreate !== false,
// })
data = newFileData
// data = newFileData
// /////////////////////////////////////
// beforeValidate - Fields
@@ -174,9 +174,9 @@ async function create<TSlug extends keyof GeneratedTypes['collections']>(
// Write files to local storage
// /////////////////////////////////////
if (!collectionConfig.upload.disableLocalStorage) {
await uploadFiles(payload, filesToUpload, req.t)
}
// if (!collectionConfig.upload.disableLocalStorage) {
// await uploadFiles(payload, filesToUpload, req.t)
// }
// /////////////////////////////////////
// beforeChange - Collection

View File

@@ -17,10 +17,10 @@ import { afterChange } from '../../fields/hooks/afterChange'
import { afterRead } from '../../fields/hooks/afterRead'
import { beforeChange } from '../../fields/hooks/beforeChange'
import { beforeValidate } from '../../fields/hooks/beforeValidate'
import { deleteAssociatedFiles } from '../../uploads/deleteAssociatedFiles'
import { generateFileData } from '../../uploads/generateFileData'
// import { deleteAssociatedFiles } from '../../uploads/deleteAssociatedFiles'
// import { generateFileData } from '../../uploads/generateFileData'
import { unlinkTempFiles } from '../../uploads/unlinkTempFiles'
import { uploadFiles } from '../../uploads/uploadFiles'
// import { uploadFiles } from '../../uploads/uploadFiles'
import { commitTransaction } from '../../utilities/commitTransaction'
import { initTransaction } from '../../utilities/initTransaction'
import { killTransaction } from '../../utilities/killTransaction'
@@ -151,21 +151,20 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
// Generate data for all files and sizes
// /////////////////////////////////////
const { data: newFileData, files: filesToUpload } = await generateFileData({
collection,
config,
data: bulkUpdateData,
overwriteExistingFiles,
req,
throwOnMissingFile: false,
})
// const { data: newFileData, files: filesToUpload } = await generateFileData({
// collection,
// config,
// data: bulkUpdateData,
// overwriteExistingFiles,
// req,
// throwOnMissingFile: false,
// })
const errors = []
const promises = docs.map(async (doc) => {
const { id } = doc
let data = {
...newFileData,
...bulkUpdateData,
}
@@ -181,14 +180,14 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
showHiddenFields: true,
})
await deleteAssociatedFiles({
collectionConfig,
config,
doc,
files: filesToUpload,
overrideDelete: false,
t,
})
// await deleteAssociatedFiles({
// collectionConfig,
// config,
// doc,
// // files: filesToUpload,
// overrideDelete: false,
// t,
// })
// /////////////////////////////////////
// beforeValidate - Fields
@@ -228,9 +227,9 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
// Write files to local storage
// /////////////////////////////////////
if (!collectionConfig.upload.disableLocalStorage) {
await uploadFiles(payload, filesToUpload, t)
}
// if (!collectionConfig.upload.disableLocalStorage) {
// await uploadFiles(payload, filesToUpload, t)
// }
// /////////////////////////////////////
// beforeChange - Collection

View File

@@ -16,10 +16,10 @@ import { afterChange } from '../../fields/hooks/afterChange'
import { afterRead } from '../../fields/hooks/afterRead'
import { beforeChange } from '../../fields/hooks/beforeChange'
import { beforeValidate } from '../../fields/hooks/beforeValidate'
import { deleteAssociatedFiles } from '../../uploads/deleteAssociatedFiles'
import { generateFileData } from '../../uploads/generateFileData'
// import { deleteAssociatedFiles } from '../../uploads/deleteAssociatedFiles'
// import { generateFileData } from '../../uploads/generateFileData'
import { unlinkTempFiles } from '../../uploads/unlinkTempFiles'
import { uploadFiles } from '../../uploads/uploadFiles'
// import { uploadFiles } from '../../uploads/uploadFiles'
import { commitTransaction } from '../../utilities/commitTransaction'
import { initTransaction } from '../../utilities/initTransaction'
import { killTransaction } from '../../utilities/killTransaction'
@@ -139,29 +139,29 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
// Generate data for all files and sizes
// /////////////////////////////////////
const { data: newFileData, files: filesToUpload } = await generateFileData({
collection,
config,
data,
overwriteExistingFiles,
req,
throwOnMissingFile: false,
})
// const { data: newFileData, files: filesToUpload } = await generateFileData({
// collection,
// config,
// data,
// overwriteExistingFiles,
// req,
// throwOnMissingFile: false,
// })
data = newFileData
// data = newFileData
// /////////////////////////////////////
// Delete any associated files
// /////////////////////////////////////
await deleteAssociatedFiles({
collectionConfig,
config,
doc: docWithLocales,
files: filesToUpload,
overrideDelete: false,
t,
})
// await deleteAssociatedFiles({
// collectionConfig,
// config,
// doc: docWithLocales,
// files: filesToUpload,
// overrideDelete: false,
// t,
// })
// /////////////////////////////////////
// beforeValidate - Fields
@@ -201,9 +201,9 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
// Write files to local storage
// /////////////////////////////////////
if (!collectionConfig.upload.disableLocalStorage) {
await uploadFiles(payload, filesToUpload, t)
}
// if (!collectionConfig.upload.disableLocalStorage) {
// await uploadFiles(payload, filesToUpload, t)
// }
// /////////////////////////////////////
// beforeChange - Collection

View File

@@ -0,0 +1,54 @@
import type { Field } from '../fields/config/types'
import type { ClientConfig, SanitizedConfig } from './types'
const sanitizeFields = (fields: Field[]): Field[] =>
fields.map((field) => {
const sanitized = { ...field }
if ('access' in field) delete field.access
if ('hooks' in field) delete field.hooks
if ('validate' in field) delete field.validate
if ('defaultValue' in field) delete field.defaultValue
if ('fields' in field) {
field.fields = sanitizeFields(field.fields)
}
if ('admin' in field) {
if ('components' in field.admin) {
delete field.admin.components
}
}
return sanitized
})
export const createClientConfig = async (
configPromise: Promise<SanitizedConfig>,
): Promise<ClientConfig> => {
const config = await configPromise
const clientConfig = { ...config }
delete clientConfig.endpoints
delete clientConfig.db
clientConfig.collections = config.collections.map((collection) => {
const sanitized = { ...collection }
sanitized.fields = sanitizeFields(sanitized.fields)
delete sanitized.hooks
delete sanitized.access
delete sanitized.endpoints
return sanitized
})
clientConfig.globals = config.globals.map((global) => {
const sanitized = { ...global }
sanitized.fields = sanitizeFields(sanitized.fields)
delete sanitized.hooks
delete sanitized.access
delete sanitized.endpoints
return sanitized
})
return clientConfig
}

View File

@@ -27,6 +27,7 @@ import type {
} from '../collections/config/types'
import type { BaseDatabaseAdapter } from '../database/types'
import type { PayloadRequest } from '../express/types'
import type { ClientConfigField, Field } from '../fields/config/types'
import type { GlobalConfig, SanitizedGlobalConfig } from '../globals/config/types'
import type { Payload } from '../payload'
import type { Where } from '../types'
@@ -118,8 +119,7 @@ export type InitOptions = {
* The passed config should match the config file, and if it doesn't, there could be mismatches between the admin UI
* and the backend functionality
*/
config?: Promise<SanitizedConfig>
config: Promise<SanitizedConfig>
/**
* Disable connect to the database on init
*/
@@ -149,20 +149,17 @@ export type InitOptions = {
local?: boolean
loggerDestination?: DestinationStream
/**
* Specify options for the built-in Pino logger that Payload uses for internal logging.
*
* See Pino Docs for options: https://getpino.io/#/docs/api?id=options
*/
loggerOptions?: LoggerOptions
/**
* A function that is called immediately following startup that receives the Payload instance as it's only argument.
*/
onInit?: (payload: Payload) => Promise<void> | void
/** Secure string that Payload will use for any encryption workflows */
secret: string
}
/**
@@ -509,12 +506,12 @@ export type Config = {
cookiePrefix?: string
/** Either a whitelist array of URLS to allow CORS requests from, or a wildcard string ('*') to accept incoming requests from any domain. */
cors?: '*' | string[]
/** A whitelist array of URLs to allow Payload cookies to be accepted from as a form of CSRF protection. */
csrf?: string[]
/** Extension point to add your custom data. */
custom?: Record<string, any>
/** Pass in a database adapter for use on this project. */
db: (args: { payload: Payload }) => BaseDatabaseAdapter
/** Enable to expose more detailed error information. */
@@ -666,6 +663,8 @@ export type Config = {
/** @default "/playground" */
graphQLPlayground?: string
}
/** Secure string that Payload will use for any encryption workflows */
secret: string
/**
* Define the absolute URL of your app including the protocol, for example `https://example.org`.
* No paths allowed, only protocol, domain and (optionally) port.
@@ -703,6 +702,15 @@ export type SanitizedConfig = Omit<
}
}
export type ClientConfig = Omit<SanitizedConfig, 'db' | 'endpoints' | 'webpack'> & {
collections: (Omit<SanitizedCollectionConfig, 'access' | 'endpoints' | 'fields' | 'hooks'> & {
fields: ClientConfigField[]
})[]
globals: (Omit<SanitizedGlobalConfig, 'access' | 'endpoints' | 'fields' | 'hooks'> & {
fields: ClientConfigField[]
})[]
}
export type EntityDescription =
| (() => string)
| React.ComponentType<any>

View File

@@ -1,8 +1,8 @@
import fs from 'fs'
import path from 'path'
import type { Payload } from '../../'
import type { Migration } from '../types'
// import type { Payload } from '../../'
// import type { Migration } from '../types'
/**
* Read the migration files from disk
@@ -27,10 +27,12 @@ export const readMigrationFiles = async ({
return path.resolve(payload.db.migrationDir, file)
})
return files.map((filePath) => {
// eslint-disable-next-line @typescript-eslint/no-var-requires,import/no-dynamic-require
const migration = require(filePath) as Migration
migration.name = path.basename(filePath).split('.')?.[0]
return migration
})
return files
// return files.map((filePath) => {
// // eslint-disable-next-line @typescript-eslint/no-var-requires,import/no-dynamic-require
// const migration = require(filePath) as Migration
// migration.name = path.basename(filePath).split('.')?.[0]
// return migration
// })
}

View File

@@ -1,4 +1,4 @@
export {
export type {
BaseDatabaseAdapter,
BeginTransaction,
CommitTransaction,
@@ -78,7 +78,7 @@ export { migrationsCollection } from '../database/migrations/migrationsCollectio
export { readMigrationFiles } from '../database/migrations/readMigrationFiles'
export { EntityPolicies, PathToQuery } from '../database/queryValidation/types'
export type { EntityPolicies, PathToQuery } from '../database/queryValidation/types'
export { validateQueryPaths } from '../database/queryValidation/validateQueryPaths'

View File

@@ -0,0 +1 @@
export { initHandler } from '../auth/handlers/init'

View File

@@ -0,0 +1 @@
export { dashboard } from '../admin/pages/dashboard'

View File

@@ -15,7 +15,7 @@ export type {
export type { CellComponentProps } from '../admin/components/views/collections/List/Cell/types'
export { FileData, ImageSize, IncomingUploadType } from '../uploads/types'
export type { FileData, ImageSize, IncomingUploadType } from '../uploads/types'
export type {
CustomPublishButtonProps,

View File

@@ -503,6 +503,8 @@ export type Field =
| UIField
| UploadField
export type ClientConfigField = Omit<Field, 'access' | 'defaultValue' | 'hooks' | 'validate'>
export type FieldAffectingData =
| ArrayField
| BlockField

View File

@@ -1,35 +1,421 @@
import type { TypeWithID } from './collections/config/types'
import type { InitOptions } from './config/types'
import type { BaseDatabaseAdapter } from './database/types'
import type { Express, Router } from 'express'
import type { ExecutionResult, GraphQLSchema, ValidationRule } from 'graphql'
import type { OperationArgs, Request as graphQLRequest } from 'graphql-http/lib/handler'
import type { SendMailOptions } from 'nodemailer'
import type pino from 'pino'
import crypto from 'crypto'
import type { Result as ForgotPasswordResult } from './auth/operations/forgotPassword'
import type { Options as ForgotPasswordOptions } from './auth/operations/local/forgotPassword'
import type { Options as LoginOptions } from './auth/operations/local/login'
import type { Options as ResetPasswordOptions } from './auth/operations/local/resetPassword'
import type { Options as UnlockOptions } from './auth/operations/local/unlock'
import type { Options as VerifyEmailOptions } from './auth/operations/local/verifyEmail'
import type { Result as LoginResult } from './auth/operations/login'
import type { Result as ResetPasswordResult } from './auth/operations/resetPassword'
import type { BulkOperationResult, Collection, TypeWithID } from './collections/config/types'
import type { Options as CreateOptions } from './collections/operations/local/create'
import type {
ByIDOptions as DeleteByIDOptions,
ManyOptions as DeleteManyOptions,
Options as DeleteOptions,
} from './collections/operations/local/delete'
import type { Options as FindOptions } from './collections/operations/local/find'
import type { Options as FindByIDOptions } from './collections/operations/local/findByID'
import type { Options as FindVersionByIDOptions } from './collections/operations/local/findVersionByID'
import type { Options as FindVersionsOptions } from './collections/operations/local/findVersions'
import type { Options as RestoreVersionOptions } from './collections/operations/local/restoreVersion'
import type {
ByIDOptions as UpdateByIDOptions,
ManyOptions as UpdateManyOptions,
Options as UpdateOptions,
} from './collections/operations/local/update'
import type { EmailOptions, InitOptions, SanitizedConfig } from './config/types'
import type { PaginatedDocs } from './database/types'
import type { BuildEmailResult } from './email/types'
import type { ErrorHandler } from './express/middleware/errorHandler'
import type { RequestContext } from './express/types'
import type { TypeWithID as GlobalTypeWithID } from './globals/config/types'
import type { Payload as LocalPayload } from './payload'
import type { TypeWithID as GlobalTypeWithID, Globals } from './globals/config/types'
import type { Options as FindGlobalOptions } from './globals/operations/local/findOne'
import type { Options as FindGlobalVersionByIDOptions } from './globals/operations/local/findVersionByID'
import type { Options as FindGlobalVersionsOptions } from './globals/operations/local/findVersions'
import type { Options as RestoreGlobalVersionOptions } from './globals/operations/local/restoreVersion'
import type { Options as UpdateGlobalOptions } from './globals/operations/local/update'
import type { TypeWithVersion } from './versions/types'
import { initHTTP } from './initHTTP'
import { BasePayload } from './payload'
import { decrypt, encrypt } from './auth/crypto'
import localOperations from './collections/operations/local'
import buildEmail from './email/build'
import { defaults as emailDefaults } from './email/defaults'
import sendEmail from './email/sendEmail'
import localGlobalOperations from './globals/operations/local'
import registerGraphQLSchema from './graphql/registerSchema'
import Logger from './utilities/logger'
import { serverInit as serverInitTelemetry } from './utilities/telemetry/events/serverInit'
export { getPayload } from './payload'
/**
* @description Payload
*/
export class Payload<TGeneratedTypes extends GeneratedTypes> {
Mutation: { fields: { [key: string]: any }; name: string } = { name: 'Mutation', fields: {} }
require('isomorphic-fetch')
Query: { fields: { [key: string]: any }; name: string } = { name: 'Query', fields: {} }
export class Payload extends BasePayload<GeneratedTypes> {
async init(options: InitOptions): Promise<LocalPayload> {
const payload = await initHTTP(options)
Object.assign(this, payload)
collections: {
[slug: number | string | symbol]: Collection
} = {}
config: SanitizedConfig
/**
* @description Performs create operation
* @param options
* @returns created document
*/
create = async <T extends keyof TGeneratedTypes['collections']>(
options: CreateOptions<T>,
): Promise<TGeneratedTypes['collections'][T]> => {
const { create } = localOperations
return create<T>(this, options)
}
db: DatabaseAdapter
decrypt = decrypt
email: BuildEmailResult
emailOptions: EmailOptions
encrypt = encrypt
errorHandler: ErrorHandler
express?: Express
extensions: (args: {
args: OperationArgs<any>
req: graphQLRequest<unknown, unknown>
result: ExecutionResult
}) => Promise<any>
/**
* @description Find documents with criteria
* @param options
* @returns documents satisfying query
*/
find = async <T extends keyof TGeneratedTypes['collections']>(
options: FindOptions<T>,
): Promise<PaginatedDocs<TGeneratedTypes['collections'][T]>> => {
const { find } = localOperations
return find<T>(this, options)
}
findByID = async <T extends keyof TGeneratedTypes['collections']>(
options: FindByIDOptions<T>,
): Promise<TGeneratedTypes['collections'][T]> => {
const { findByID } = localOperations
return findByID<T>(this, options)
}
findGlobal = async <T extends keyof TGeneratedTypes['globals']>(
options: FindGlobalOptions<T>,
): Promise<TGeneratedTypes['globals'][T]> => {
const { findOne } = localGlobalOperations
return findOne<T>(this, options)
}
/**
* @description Find global version by ID
* @param options
* @returns global version with specified ID
*/
findGlobalVersionByID = async <T extends keyof TGeneratedTypes['globals']>(
options: FindGlobalVersionByIDOptions<T>,
): Promise<TypeWithVersion<TGeneratedTypes['globals'][T]>> => {
const { findVersionByID } = localGlobalOperations
return findVersionByID<T>(this, options)
}
/**
* @description Find global versions with criteria
* @param options
* @returns versions satisfying query
*/
findGlobalVersions = async <T extends keyof TGeneratedTypes['globals']>(
options: FindGlobalVersionsOptions<T>,
): Promise<PaginatedDocs<TypeWithVersion<TGeneratedTypes['globals'][T]>>> => {
const { findVersions } = localGlobalOperations
return findVersions<T>(this, options)
}
/**
* @description Find version by ID
* @param options
* @returns version with specified ID
*/
findVersionByID = async <T extends keyof TGeneratedTypes['collections']>(
options: FindVersionByIDOptions<T>,
): Promise<TypeWithVersion<TGeneratedTypes['collections'][T]>> => {
const { findVersionByID } = localOperations
return findVersionByID<T>(this, options)
}
/**
* @description Find versions with criteria
* @param options
* @returns versions satisfying query
*/
findVersions = async <T extends keyof TGeneratedTypes['collections']>(
options: FindVersionsOptions<T>,
): Promise<PaginatedDocs<TypeWithVersion<TGeneratedTypes['collections'][T]>>> => {
const { findVersions } = localOperations
return findVersions<T>(this, options)
}
forgotPassword = async <T extends keyof TGeneratedTypes['collections']>(
options: ForgotPasswordOptions<T>,
): Promise<ForgotPasswordResult> => {
const { forgotPassword } = localOperations.auth
return forgotPassword<T>(this, options)
}
getAPIURL = (): string => `${this.config.serverURL}${this.config.routes.api}`
getAdminURL = (): string => `${this.config.serverURL}${this.config.routes.admin}`
globals: Globals
local: boolean
logger: pino.Logger
login = async <T extends keyof TGeneratedTypes['collections']>(
options: LoginOptions<T>,
): Promise<LoginResult & { user: TGeneratedTypes['collections'][T] }> => {
const { login } = localOperations.auth
return login<T>(this, options)
}
/**
* @description Find document by ID
* @param options
* @returns document with specified ID
*/
resetPassword = async <T extends keyof TGeneratedTypes['collections']>(
options: ResetPasswordOptions<T>,
): Promise<ResetPasswordResult> => {
const { resetPassword } = localOperations.auth
return resetPassword<T>(this, options)
}
/**
* @description Restore global version by ID
* @param options
* @returns version with specified ID
*/
restoreGlobalVersion = async <T extends keyof TGeneratedTypes['globals']>(
options: RestoreGlobalVersionOptions<T>,
): Promise<TGeneratedTypes['globals'][T]> => {
const { restoreVersion } = localGlobalOperations
return restoreVersion<T>(this, options)
}
/**
* @description Restore version by ID
* @param options
* @returns version with specified ID
*/
restoreVersion = async <T extends keyof TGeneratedTypes['collections']>(
options: RestoreVersionOptions<T>,
): Promise<TGeneratedTypes['collections'][T]> => {
const { restoreVersion } = localOperations
return restoreVersion<T>(this, options)
}
router?: Router
schema: GraphQLSchema
secret: string
sendEmail: (message: SendMailOptions) => Promise<unknown>
types: {
arrayTypes: any
blockInputTypes: any
blockTypes: any
fallbackLocaleInputType?: any
groupTypes: any
localeInputType?: any
tabTypes: any
}
unlock = async <T extends keyof TGeneratedTypes['collections']>(
options: UnlockOptions<T>,
): Promise<boolean> => {
const { unlock } = localOperations.auth
return unlock(this, options)
}
updateGlobal = async <T extends keyof TGeneratedTypes['globals']>(
options: UpdateGlobalOptions<T>,
): Promise<TGeneratedTypes['globals'][T]> => {
const { update } = localGlobalOperations
return update<T>(this, options)
}
validationRules: (args: OperationArgs<any>) => ValidationRule[]
verifyEmail = async <T extends keyof TGeneratedTypes['collections']>(
options: VerifyEmailOptions<T>,
): Promise<boolean> => {
const { verifyEmail } = localOperations.auth
return verifyEmail(this, options)
}
versions: {
[slug: string]: any // TODO: Type this
} = {}
delete<T extends keyof TGeneratedTypes['collections']>(
options: DeleteOptions<T>,
): Promise<BulkOperationResult<T> | TGeneratedTypes['collections'][T]> {
const { deleteLocal } = localOperations
return deleteLocal<T>(this, options)
}
/**
* @description delete one or more documents
* @param options
* @returns Updated document(s)
*/
delete<T extends keyof TGeneratedTypes['collections']>(
options: DeleteByIDOptions<T>,
): Promise<TGeneratedTypes['collections'][T]>
delete<T extends keyof TGeneratedTypes['collections']>(
options: DeleteManyOptions<T>,
): Promise<BulkOperationResult<T>>
/**
* @description Initializes Payload
* @param options
*/
// @ts-expect-error // TODO: TypeScript hallucinating again. fix later
async init(options: InitOptions): Promise<Payload> {
this.logger = Logger('payload', options.loggerOptions, options.loggerDestination)
this.config = await options.config
if (!this.config.secret) {
throw new Error('Error: missing secret key. A secret key is needed to secure Payload.')
}
this.secret = crypto.createHash('sha256').update(this.config.secret).digest('hex').slice(0, 32)
this.local = options.local
this.globals = {
config: this.config.globals,
}
this.config.collections.forEach((collection) => {
this.collections[collection.slug] = {
config: collection,
}
})
this.db = this.config.db({ payload: this })
this.db.payload = this
if (this.db?.init) {
await this.db.init(this)
}
if (!options.disableDBConnect && this.db.connect) {
await this.db.connect(this)
}
this.logger.info('Starting Payload...')
// Configure email service
const emailOptions = options.email ? { ...options.email } : this.config.email
if (options.email && this.config.email) {
this.logger.warn(
'Email options provided in both init options and config. Using init options.',
)
}
this.emailOptions = emailOptions ?? emailDefaults
this.email = buildEmail(this.emailOptions, this.logger)
this.sendEmail = sendEmail.bind(this)
if (!this.config.graphQL.disable) {
registerGraphQLSchema(this)
}
serverInitTelemetry(this)
if (!options.disableOnInit) {
if (typeof options.onInit === 'function') await options.onInit(this)
if (typeof this.config.onInit === 'function') await this.config.onInit(this)
}
return payload
return this
}
update<T extends keyof TGeneratedTypes['collections']>(
options: UpdateManyOptions<T>,
): Promise<BulkOperationResult<T>>
/**
* @description Update one or more documents
* @param options
* @returns Updated document(s)
*/
update<T extends keyof TGeneratedTypes['collections']>(
options: UpdateByIDOptions<T>,
): Promise<TGeneratedTypes['collections'][T]>
update<T extends keyof TGeneratedTypes['collections']>(
options: UpdateOptions<T>,
): Promise<BulkOperationResult<T> | TGeneratedTypes['collections'][T]> {
const { update } = localOperations
return update<T>(this, options)
}
}
const payload = new Payload()
const initialized = new Payload()
export default payload
module.exports = payload
export default initialized
let cached = global._payload
if (!cached) {
// eslint-disable-next-line no-multi-assign
cached = global._payload = { payload: null, promise: null }
}
export const getPayload = async (options?: InitOptions): Promise<Payload<GeneratedTypes>> => {
if (cached.payload) {
return cached.payload
}
if (!cached.promise) {
cached.promise = new Payload<GeneratedTypes>().init(options)
}
try {
cached.payload = await cached.promise
} catch (e) {
cached.promise = null
throw e
}
return cached.payload
}
type GeneratedTypes = {
collections: {
@@ -40,6 +426,6 @@ type GeneratedTypes = {
}
}
type DatabaseAdapter = BaseDatabaseAdapter
// type DatabaseAdapter = BaseDatabaseAdapter
export type { DatabaseAdapter, GeneratedTypes, RequestContext }
export type { GeneratedTypes, RequestContext }

View File

@@ -1,94 +0,0 @@
/* eslint-disable no-param-reassign */
import type { NextFunction, Response } from 'express'
import express from 'express'
import type { InitOptions } from './config/types'
import type { PayloadRequest } from './express/types'
import type { Payload } from './payload'
import initAuth from './auth/init'
import access from './auth/requestHandlers/access'
import { getDataLoader } from './collections/dataloader'
import initCollectionsHTTP from './collections/initHTTP'
import initAdmin from './express/admin'
import expressMiddleware from './express/middleware'
import authenticate from './express/middleware/authenticate'
import errorHandler from './express/middleware/errorHandler'
import identifyAPI from './express/middleware/identifyAPI'
import mountEndpoints from './express/mountEndpoints'
import initStatic from './express/static'
import initGlobalsHTTP from './globals/initHTTP'
import graphQLHandler from './graphql/graphQLHandler'
import initGraphQLPlayground from './graphql/initPlayground'
import { getPayload } from './payload'
export const initHTTP = async (incomingOptions: InitOptions): Promise<Payload> => {
const options = { ...incomingOptions }
if (typeof options.local === 'undefined') options.local = false
// Disable onInit because it will be called in top-level Payload
options.disableOnInit = true
const payload = await getPayload(options)
if (!options.local) {
payload.router = express.Router()
payload.router.use(...expressMiddleware(payload))
initAuth(payload)
initCollectionsHTTP(payload)
initGlobalsHTTP(payload)
options.express.use((req: PayloadRequest, res, next) => {
req.payload = payload
next()
})
options.express.use((req: PayloadRequest, res: Response, next: NextFunction): void => {
req.payloadDataLoader = getDataLoader(req)
return next()
})
payload.express = options.express
if (payload.config.rateLimit.trustProxy) {
payload.express.set('trust proxy', 1)
}
await initAdmin(payload)
payload.router.get('/access', access)
if (!payload.config.graphQL.disable) {
payload.router.use(
payload.config.routes.graphQL,
(req, res, next): void => {
if (req.method === 'OPTIONS') {
res.sendStatus(204)
} else {
next()
}
},
identifyAPI('GraphQL'),
(req: PayloadRequest, res: Response, next) => graphQLHandler(req, res)(req, res, next),
)
initGraphQLPlayground(payload)
}
mountEndpoints(options.express, payload.router, payload.config.endpoints)
// Bind router to API
payload.express.use(payload.config.routes.api, payload.router)
// Enable static routes for all collections permitting upload
initStatic(payload)
payload.errorHandler = errorHandler(payload.config, payload.logger)
payload.router.use(payload.errorHandler)
payload.authenticate = authenticate(payload.config)
}
return payload
}

View File

@@ -1,438 +0,0 @@
import type { Express, Router } from 'express'
import type { ExecutionResult, GraphQLSchema, ValidationRule } from 'graphql'
// @ts-expect-error // TODO Fix this - moduleResolution 16 breaks this import
import type { OperationArgs, Request as graphQLRequest } from 'graphql-http/lib/handler'
import type { SendMailOptions } from 'nodemailer'
import type pino from 'pino'
import crypto from 'crypto'
import path from 'path'
import type { DatabaseAdapter, GeneratedTypes } from './' // Must import from Payload to support declare module
import type { Result as ForgotPasswordResult } from './auth/operations/forgotPassword'
import type { Options as ForgotPasswordOptions } from './auth/operations/local/forgotPassword'
import type { Options as LoginOptions } from './auth/operations/local/login'
import type { Options as ResetPasswordOptions } from './auth/operations/local/resetPassword'
import type { Options as UnlockOptions } from './auth/operations/local/unlock'
import type { Options as VerifyEmailOptions } from './auth/operations/local/verifyEmail'
import type { Result as LoginResult } from './auth/operations/login'
import type { Result as ResetPasswordResult } from './auth/operations/resetPassword'
import type { BulkOperationResult, Collection } from './collections/config/types'
import type { Options as CreateOptions } from './collections/operations/local/create'
import type {
ByIDOptions as DeleteByIDOptions,
ManyOptions as DeleteManyOptions,
Options as DeleteOptions,
} from './collections/operations/local/delete'
import type { Options as FindOptions } from './collections/operations/local/find'
import type { Options as FindByIDOptions } from './collections/operations/local/findByID'
import type { Options as FindVersionByIDOptions } from './collections/operations/local/findVersionByID'
import type { Options as FindVersionsOptions } from './collections/operations/local/findVersions'
import type { Options as RestoreVersionOptions } from './collections/operations/local/restoreVersion'
import type {
ByIDOptions as UpdateByIDOptions,
ManyOptions as UpdateManyOptions,
Options as UpdateOptions,
} from './collections/operations/local/update'
import type { EmailOptions, InitOptions, SanitizedConfig } from './config/types'
import type { PaginatedDocs } from './database/types'
import type { BuildEmailResult } from './email/types'
import type { PayloadAuthenticate } from './express/middleware/authenticate'
import type { ErrorHandler } from './express/middleware/errorHandler'
import type { Globals } from './globals/config/types'
import type { Options as FindGlobalOptions } from './globals/operations/local/findOne'
import type { Options as FindGlobalVersionByIDOptions } from './globals/operations/local/findVersionByID'
import type { Options as FindGlobalVersionsOptions } from './globals/operations/local/findVersions'
import type { Options as RestoreGlobalVersionOptions } from './globals/operations/local/restoreVersion'
import type { Options as UpdateGlobalOptions } from './globals/operations/local/update'
import type { TypeWithVersion } from './versions/types'
import { decrypt, encrypt } from './auth/crypto'
import localOperations from './collections/operations/local'
import findConfig from './config/find'
import buildEmail from './email/build'
import { defaults as emailDefaults } from './email/defaults'
import sendEmail from './email/sendEmail'
import localGlobalOperations from './globals/operations/local'
import registerGraphQLSchema from './graphql/registerSchema'
import Logger from './utilities/logger'
import { serverInit as serverInitTelemetry } from './utilities/telemetry/events/serverInit'
/**
* @description Payload
*/
export class BasePayload<TGeneratedTypes extends GeneratedTypes> {
Mutation: { fields: { [key: string]: any }; name: string } = { name: 'Mutation', fields: {} }
Query: { fields: { [key: string]: any }; name: string } = { name: 'Query', fields: {} }
authenticate: PayloadAuthenticate
collections: {
[slug: number | string | symbol]: Collection
} = {}
config: SanitizedConfig
/**
* @description Performs create operation
* @param options
* @returns created document
*/
create = async <T extends keyof TGeneratedTypes['collections']>(
options: CreateOptions<T>,
): Promise<TGeneratedTypes['collections'][T]> => {
const { create } = localOperations
return create<T>(this, options)
}
db: DatabaseAdapter
decrypt = decrypt
email: BuildEmailResult
emailOptions: EmailOptions
encrypt = encrypt
errorHandler: ErrorHandler
express?: Express
extensions: (args: {
args: OperationArgs<any>
req: graphQLRequest<unknown, unknown>
result: ExecutionResult
}) => Promise<any>
/**
* @description Find documents with criteria
* @param options
* @returns documents satisfying query
*/
find = async <T extends keyof TGeneratedTypes['collections']>(
options: FindOptions<T>,
): Promise<PaginatedDocs<TGeneratedTypes['collections'][T]>> => {
const { find } = localOperations
return find<T>(this, options)
}
findByID = async <T extends keyof TGeneratedTypes['collections']>(
options: FindByIDOptions<T>,
): Promise<TGeneratedTypes['collections'][T]> => {
const { findByID } = localOperations
return findByID<T>(this, options)
}
findGlobal = async <T extends keyof TGeneratedTypes['globals']>(
options: FindGlobalOptions<T>,
): Promise<TGeneratedTypes['globals'][T]> => {
const { findOne } = localGlobalOperations
return findOne<T>(this, options)
}
/**
* @description Find global version by ID
* @param options
* @returns global version with specified ID
*/
findGlobalVersionByID = async <T extends keyof TGeneratedTypes['globals']>(
options: FindGlobalVersionByIDOptions<T>,
): Promise<TypeWithVersion<TGeneratedTypes['globals'][T]>> => {
const { findVersionByID } = localGlobalOperations
return findVersionByID<T>(this, options)
}
/**
* @description Find global versions with criteria
* @param options
* @returns versions satisfying query
*/
findGlobalVersions = async <T extends keyof TGeneratedTypes['globals']>(
options: FindGlobalVersionsOptions<T>,
): Promise<PaginatedDocs<TypeWithVersion<TGeneratedTypes['globals'][T]>>> => {
const { findVersions } = localGlobalOperations
return findVersions<T>(this, options)
}
/**
* @description Find version by ID
* @param options
* @returns version with specified ID
*/
findVersionByID = async <T extends keyof TGeneratedTypes['collections']>(
options: FindVersionByIDOptions<T>,
): Promise<TypeWithVersion<TGeneratedTypes['collections'][T]>> => {
const { findVersionByID } = localOperations
return findVersionByID<T>(this, options)
}
/**
* @description Find versions with criteria
* @param options
* @returns versions satisfying query
*/
findVersions = async <T extends keyof TGeneratedTypes['collections']>(
options: FindVersionsOptions<T>,
): Promise<PaginatedDocs<TypeWithVersion<TGeneratedTypes['collections'][T]>>> => {
const { findVersions } = localOperations
return findVersions<T>(this, options)
}
forgotPassword = async <T extends keyof TGeneratedTypes['collections']>(
options: ForgotPasswordOptions<T>,
): Promise<ForgotPasswordResult> => {
const { forgotPassword } = localOperations.auth
return forgotPassword<T>(this, options)
}
getAPIURL = (): string => `${this.config.serverURL}${this.config.routes.api}`
getAdminURL = (): string => `${this.config.serverURL}${this.config.routes.admin}`
globals: Globals
local: boolean
logger: pino.Logger
login = async <T extends keyof TGeneratedTypes['collections']>(
options: LoginOptions<T>,
): Promise<LoginResult & { user: TGeneratedTypes['collections'][T] }> => {
const { login } = localOperations.auth
return login<T>(this, options)
}
/**
* @description Find document by ID
* @param options
* @returns document with specified ID
*/
resetPassword = async <T extends keyof TGeneratedTypes['collections']>(
options: ResetPasswordOptions<T>,
): Promise<ResetPasswordResult> => {
const { resetPassword } = localOperations.auth
return resetPassword<T>(this, options)
}
/**
* @description Restore global version by ID
* @param options
* @returns version with specified ID
*/
restoreGlobalVersion = async <T extends keyof TGeneratedTypes['globals']>(
options: RestoreGlobalVersionOptions<T>,
): Promise<TGeneratedTypes['globals'][T]> => {
const { restoreVersion } = localGlobalOperations
return restoreVersion<T>(this, options)
}
/**
* @description Restore version by ID
* @param options
* @returns version with specified ID
*/
restoreVersion = async <T extends keyof TGeneratedTypes['collections']>(
options: RestoreVersionOptions<T>,
): Promise<TGeneratedTypes['collections'][T]> => {
const { restoreVersion } = localOperations
return restoreVersion<T>(this, options)
}
router?: Router
schema: GraphQLSchema
secret: string
sendEmail: (message: SendMailOptions) => Promise<unknown>
types: {
arrayTypes: any
blockInputTypes: any
blockTypes: any
fallbackLocaleInputType?: any
groupTypes: any
localeInputType?: any
tabTypes: any
}
unlock = async <T extends keyof TGeneratedTypes['collections']>(
options: UnlockOptions<T>,
): Promise<boolean> => {
const { unlock } = localOperations.auth
return unlock(this, options)
}
updateGlobal = async <T extends keyof TGeneratedTypes['globals']>(
options: UpdateGlobalOptions<T>,
): Promise<TGeneratedTypes['globals'][T]> => {
const { update } = localGlobalOperations
return update<T>(this, options)
}
validationRules: (args: OperationArgs<any>) => ValidationRule[]
verifyEmail = async <T extends keyof TGeneratedTypes['collections']>(
options: VerifyEmailOptions<T>,
): Promise<boolean> => {
const { verifyEmail } = localOperations.auth
return verifyEmail(this, options)
}
versions: {
[slug: string]: any // TODO: Type this
} = {}
delete<T extends keyof TGeneratedTypes['collections']>(
options: DeleteOptions<T>,
): Promise<BulkOperationResult<T> | TGeneratedTypes['collections'][T]> {
const { deleteLocal } = localOperations
return deleteLocal<T>(this, options)
}
/**
* @description delete one or more documents
* @param options
* @returns Updated document(s)
*/
delete<T extends keyof TGeneratedTypes['collections']>(
options: DeleteByIDOptions<T>,
): Promise<TGeneratedTypes['collections'][T]>
delete<T extends keyof TGeneratedTypes['collections']>(
options: DeleteManyOptions<T>,
): Promise<BulkOperationResult<T>>
/**
* @description Initializes Payload
* @param options
*/
// @ts-expect-error // TODO: TypeScript hallucinating again. fix later
async init(options: InitOptions): Promise<Payload> {
this.logger = Logger('payload', options.loggerOptions, options.loggerDestination)
if (!options.secret) {
throw new Error('Error: missing secret key. A secret key is needed to secure Payload.')
}
this.secret = crypto.createHash('sha256').update(options.secret).digest('hex').slice(0, 32)
this.local = options.local
if (options.config) {
this.config = await options.config
const configPath = findConfig()
this.config = {
...this.config,
paths: {
config: configPath,
configDir: path.dirname(configPath),
rawConfig: configPath,
},
}
} else {
// eslint-disable-next-line @typescript-eslint/no-var-requires, global-require
const loadConfig = require('./config/load').default
this.config = await loadConfig(this.logger)
}
this.globals = {
config: this.config.globals,
}
this.config.collections.forEach((collection) => {
this.collections[collection.slug] = {
config: collection,
}
})
this.db = this.config.db({ payload: this })
this.db.payload = this
if (this.db?.init) {
await this.db.init(this)
}
if (!options.disableDBConnect && this.db.connect) {
await this.db.connect(this)
}
this.logger.info('Starting Payload...')
// Configure email service
const emailOptions = options.email ? { ...options.email } : this.config.email
if (options.email && this.config.email) {
this.logger.warn(
'Email options provided in both init options and config. Using init options.',
)
}
this.emailOptions = emailOptions ?? emailDefaults
this.email = buildEmail(this.emailOptions, this.logger)
this.sendEmail = sendEmail.bind(this)
if (!this.config.graphQL.disable) {
registerGraphQLSchema(this)
}
serverInitTelemetry(this)
if (!options.disableOnInit) {
if (typeof options.onInit === 'function') await options.onInit(this)
if (typeof this.config.onInit === 'function') await this.config.onInit(this)
}
return this
}
update<T extends keyof TGeneratedTypes['collections']>(
options: UpdateManyOptions<T>,
): Promise<BulkOperationResult<T>>
/**
* @description Update one or more documents
* @param options
* @returns Updated document(s)
*/
update<T extends keyof TGeneratedTypes['collections']>(
options: UpdateByIDOptions<T>,
): Promise<TGeneratedTypes['collections'][T]>
update<T extends keyof TGeneratedTypes['collections']>(
options: UpdateOptions<T>,
): Promise<BulkOperationResult<T> | TGeneratedTypes['collections'][T]> {
const { update } = localOperations
return update<T>(this, options)
}
}
export type Payload = BasePayload<GeneratedTypes>
let cached = global._payload
if (!cached) {
// eslint-disable-next-line no-multi-assign
cached = global._payload = { payload: null, promise: null }
}
export const getPayload = async (options: InitOptions): Promise<Payload> => {
if (cached.payload) {
return cached.payload
}
if (!cached.promise) {
cached.promise = new BasePayload<GeneratedTypes>().init(options)
}
try {
cached.payload = await cached.promise
} catch (e) {
cached.promise = null
throw e
}
return cached.payload
}

777
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff