Compare commits
3 Commits
feat/lexic
...
chore/sani
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6be26f6dd5 | ||
|
|
9d5f801488 | ||
|
|
5f6fad69fb |
@@ -27,27 +27,32 @@ import {
|
||||
import { sanitizeCompoundIndexes } from './sanitizeCompoundIndexes.js'
|
||||
import { validateUseAsTitle } from './useAsTitle.js'
|
||||
|
||||
export const sanitizeCollection = async (
|
||||
config: Config,
|
||||
collection: CollectionConfig,
|
||||
export const sanitizeCollection = async ({
|
||||
collectionConfig,
|
||||
config,
|
||||
richTextSanitizationPromises,
|
||||
validRelationships: _validRelationships,
|
||||
}: {
|
||||
collectionConfig: CollectionConfig
|
||||
config: Config
|
||||
/**
|
||||
* If this property is set, RichText fields won't be sanitized immediately. Instead, they will be added to this array as promises
|
||||
* so that you can sanitize them together, after the config has been sanitized.
|
||||
*/
|
||||
richTextSanitizationPromises?: Array<(config: SanitizedConfig) => Promise<void>>,
|
||||
_validRelationships?: string[],
|
||||
): Promise<SanitizedCollectionConfig> => {
|
||||
if (collection._sanitized) {
|
||||
return collection as SanitizedCollectionConfig
|
||||
richTextSanitizationPromises?: Array<(config: SanitizedConfig) => Promise<void>>
|
||||
validRelationships?: string[]
|
||||
}): Promise<SanitizedCollectionConfig> => {
|
||||
if (collectionConfig._sanitized) {
|
||||
return collectionConfig as SanitizedCollectionConfig
|
||||
}
|
||||
|
||||
collection._sanitized = true
|
||||
collectionConfig._sanitized = true
|
||||
|
||||
// /////////////////////////////////
|
||||
// Make copy of collection config
|
||||
// /////////////////////////////////
|
||||
|
||||
const sanitized: CollectionConfig = addDefaultsToCollectionConfig(collection)
|
||||
const sanitized: CollectionConfig = addDefaultsToCollectionConfig(collectionConfig)
|
||||
|
||||
// /////////////////////////////////
|
||||
// Sanitize fields
|
||||
@@ -148,7 +153,7 @@ export const sanitizeCollection = async (
|
||||
}
|
||||
|
||||
if (sanitized.timestamps === false) {
|
||||
throw new TimestampsRequired(collection)
|
||||
throw new TimestampsRequired(collectionConfig)
|
||||
}
|
||||
|
||||
sanitized.versions.maxPerDoc =
|
||||
@@ -231,15 +236,15 @@ export const sanitizeCollection = async (
|
||||
sanitized.auth.loginWithUsername = false
|
||||
}
|
||||
|
||||
if (!collection?.admin?.useAsTitle) {
|
||||
if (!collectionConfig?.admin?.useAsTitle) {
|
||||
sanitized.admin!.useAsTitle = sanitized.auth.loginWithUsername ? 'username' : 'email'
|
||||
}
|
||||
|
||||
sanitized.fields = mergeBaseFields(sanitized.fields, getBaseAuthFields(sanitized.auth))
|
||||
}
|
||||
|
||||
if (collection?.admin?.pagination?.limits?.length) {
|
||||
sanitized.admin!.pagination!.limits = collection.admin.pagination.limits
|
||||
if (collectionConfig?.admin?.pagination?.limits?.length) {
|
||||
sanitized.admin!.pagination!.limits = collectionConfig.admin.pagination.limits
|
||||
}
|
||||
|
||||
validateUseAsTitle(sanitized)
|
||||
|
||||
@@ -29,14 +29,13 @@ describe('sanitize - collections -', () => {
|
||||
},
|
||||
}
|
||||
await expect(async () => {
|
||||
await sanitizeCollection(
|
||||
// @ts-expect-error
|
||||
{
|
||||
await sanitizeCollection({
|
||||
config: {
|
||||
...config,
|
||||
collections: [collectionConfig],
|
||||
},
|
||||
collectionConfig,
|
||||
)
|
||||
} as Config,
|
||||
collection: collectionConfig,
|
||||
})
|
||||
}).rejects.toThrow(InvalidConfiguration)
|
||||
})
|
||||
|
||||
@@ -48,14 +47,13 @@ describe('sanitize - collections -', () => {
|
||||
},
|
||||
}
|
||||
await expect(async () => {
|
||||
await sanitizeCollection(
|
||||
// @ts-expect-error
|
||||
{
|
||||
await sanitizeCollection({
|
||||
config: {
|
||||
...config,
|
||||
collections: [collectionConfig],
|
||||
},
|
||||
collectionConfig,
|
||||
)
|
||||
} as Config,
|
||||
collection: collectionConfig,
|
||||
})
|
||||
}).not.toThrow()
|
||||
})
|
||||
|
||||
@@ -83,14 +81,13 @@ describe('sanitize - collections -', () => {
|
||||
],
|
||||
}
|
||||
await expect(async () => {
|
||||
await sanitizeCollection(
|
||||
// @ts-expect-error
|
||||
{
|
||||
await sanitizeCollection({
|
||||
config: {
|
||||
...config,
|
||||
collections: [collectionConfig],
|
||||
},
|
||||
collectionConfig,
|
||||
)
|
||||
} as Config,
|
||||
collection: collectionConfig,
|
||||
})
|
||||
}).not.toThrow()
|
||||
})
|
||||
|
||||
@@ -114,14 +111,13 @@ describe('sanitize - collections -', () => {
|
||||
],
|
||||
}
|
||||
await expect(async () => {
|
||||
await sanitizeCollection(
|
||||
// @ts-expect-error
|
||||
{
|
||||
await sanitizeCollection({
|
||||
config: {
|
||||
...config,
|
||||
collections: [collectionConfig],
|
||||
},
|
||||
collectionConfig,
|
||||
)
|
||||
} as Config,
|
||||
collection: collectionConfig,
|
||||
})
|
||||
}).not.toThrow()
|
||||
})
|
||||
|
||||
@@ -133,14 +129,13 @@ describe('sanitize - collections -', () => {
|
||||
},
|
||||
}
|
||||
await expect(async () => {
|
||||
await sanitizeCollection(
|
||||
// @ts-expect-error
|
||||
{
|
||||
await sanitizeCollection({
|
||||
config: {
|
||||
...config,
|
||||
collections: [collectionConfig],
|
||||
},
|
||||
collectionConfig,
|
||||
)
|
||||
} as Config,
|
||||
collection: collectionConfig,
|
||||
})
|
||||
}).rejects.toThrow(InvalidConfiguration)
|
||||
})
|
||||
|
||||
@@ -152,14 +147,13 @@ describe('sanitize - collections -', () => {
|
||||
},
|
||||
}
|
||||
await expect(async () => {
|
||||
await sanitizeCollection(
|
||||
// @ts-expect-error
|
||||
{
|
||||
await sanitizeCollection({
|
||||
config: {
|
||||
...config,
|
||||
collections: [collectionConfig],
|
||||
},
|
||||
collectionConfig,
|
||||
)
|
||||
} as Config,
|
||||
collection: collectionConfig,
|
||||
})
|
||||
}).not.toThrow()
|
||||
})
|
||||
|
||||
@@ -172,14 +166,13 @@ describe('sanitize - collections -', () => {
|
||||
},
|
||||
}
|
||||
await expect(async () => {
|
||||
await sanitizeCollection(
|
||||
// @ts-expect-error
|
||||
{
|
||||
await sanitizeCollection({
|
||||
config: {
|
||||
...config,
|
||||
collections: [collectionConfig],
|
||||
},
|
||||
collectionConfig,
|
||||
)
|
||||
} as Config,
|
||||
collection: collectionConfig,
|
||||
})
|
||||
}).not.toThrow()
|
||||
})
|
||||
it('should throw on default field: email if auth is not enabled', async () => {
|
||||
@@ -190,14 +183,13 @@ describe('sanitize - collections -', () => {
|
||||
},
|
||||
}
|
||||
await expect(async () => {
|
||||
await sanitizeCollection(
|
||||
// @ts-expect-error
|
||||
{
|
||||
await sanitizeCollection({
|
||||
config: {
|
||||
...config,
|
||||
collections: [collectionConfig],
|
||||
},
|
||||
collectionConfig,
|
||||
)
|
||||
} as Config,
|
||||
collection: collectionConfig,
|
||||
})
|
||||
}).rejects.toThrow(InvalidConfiguration)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -32,7 +32,7 @@ import {
|
||||
lockedDocumentsCollectionSlug,
|
||||
} from '../locked-documents/config.js'
|
||||
import { getPreferencesCollection, preferencesCollectionSlug } from '../preferences/config.js'
|
||||
import { getQueryPresetsConfig, queryPresetsCollectionSlug } from '../query-presets/config.js'
|
||||
import { getQueryPresetsCollection, queryPresetsCollectionSlug } from '../query-presets/config.js'
|
||||
import { getDefaultJobsCollection, jobsCollectionSlug } from '../queues/config/index.js'
|
||||
import { flattenBlock } from '../utilities/flattenAllFields.js'
|
||||
import { getSchedulePublishTask } from '../versions/schedule/job.js'
|
||||
@@ -92,6 +92,7 @@ const sanitizeAdminConfig = (configToSanitize: Config): Partial<SanitizedConfig>
|
||||
supportedTimezones: defaultTimezones,
|
||||
}
|
||||
}
|
||||
|
||||
// Timezones supported by the Intl API
|
||||
const _internalSupportedTimezones = Intl.supportedValuesOf('timeZone')
|
||||
|
||||
@@ -110,30 +111,30 @@ const sanitizeAdminConfig = (configToSanitize: Config): Partial<SanitizedConfig>
|
||||
export const sanitizeConfig = async (incomingConfig: Config): Promise<SanitizedConfig> => {
|
||||
const configWithDefaults = addDefaultsToConfig(incomingConfig)
|
||||
|
||||
const config: Partial<SanitizedConfig> = sanitizeAdminConfig(configWithDefaults)
|
||||
const sanitizedConfig: Partial<SanitizedConfig> = sanitizeAdminConfig(configWithDefaults)
|
||||
|
||||
// Add orderable fields
|
||||
setupOrderable(config as SanitizedConfig)
|
||||
setupOrderable(sanitizedConfig as SanitizedConfig)
|
||||
|
||||
if (!config.endpoints) {
|
||||
config.endpoints = []
|
||||
if (!sanitizedConfig.endpoints) {
|
||||
sanitizedConfig.endpoints = []
|
||||
}
|
||||
|
||||
for (const endpoint of authRootEndpoints) {
|
||||
config.endpoints.push(endpoint)
|
||||
sanitizedConfig.endpoints.push(endpoint)
|
||||
}
|
||||
|
||||
if (config.localization && config.localization.locales?.length > 0) {
|
||||
// clone localization config so to not break everything
|
||||
const firstLocale = config.localization.locales[0]
|
||||
if (sanitizedConfig.localization && sanitizedConfig.localization.locales?.length > 0) {
|
||||
// clone localization sanitizedConfig so to not break everything
|
||||
const firstLocale = sanitizedConfig.localization.locales[0]
|
||||
if (typeof firstLocale === 'string') {
|
||||
config.localization.localeCodes = [
|
||||
...(config.localization as unknown as LocalizationConfigWithNoLabels).locales,
|
||||
sanitizedConfig.localization.localeCodes = [
|
||||
...(sanitizedConfig.localization as unknown as LocalizationConfigWithNoLabels).locales,
|
||||
]
|
||||
|
||||
// is string[], so convert to Locale[]
|
||||
config.localization.locales = (
|
||||
config.localization as unknown as LocalizationConfigWithNoLabels
|
||||
sanitizedConfig.localization.locales = (
|
||||
sanitizedConfig.localization as unknown as LocalizationConfigWithNoLabels
|
||||
).locales.map((locale) => ({
|
||||
code: locale,
|
||||
label: locale,
|
||||
@@ -142,10 +143,12 @@ export const sanitizeConfig = async (incomingConfig: Config): Promise<SanitizedC
|
||||
}))
|
||||
} else {
|
||||
// is Locale[], so convert to string[] for localeCodes
|
||||
config.localization.localeCodes = config.localization.locales.map((locale) => locale.code)
|
||||
sanitizedConfig.localization.localeCodes = sanitizedConfig.localization.locales.map(
|
||||
(locale) => locale.code,
|
||||
)
|
||||
|
||||
config.localization.locales = (
|
||||
config.localization as LocalizationConfigWithLabels
|
||||
sanitizedConfig.localization.locales = (
|
||||
sanitizedConfig.localization as LocalizationConfigWithLabels
|
||||
).locales.map((locale) => ({
|
||||
...locale,
|
||||
toString: () => locale.code,
|
||||
@@ -153,7 +156,7 @@ export const sanitizeConfig = async (incomingConfig: Config): Promise<SanitizedC
|
||||
}
|
||||
|
||||
// Default fallback to true if not provided
|
||||
config.localization.fallback = config.localization?.fallback ?? true
|
||||
sanitizedConfig.localization.fallback = sanitizedConfig.localization?.fallback ?? true
|
||||
}
|
||||
|
||||
const i18nConfig: SanitizedConfig['i18n'] = {
|
||||
@@ -179,9 +182,10 @@ export const sanitizeConfig = async (incomingConfig: Config): Promise<SanitizedC
|
||||
i18nConfig.translations
|
||||
}
|
||||
|
||||
config.i18n = i18nConfig
|
||||
sanitizedConfig.i18n = i18nConfig
|
||||
|
||||
const richTextSanitizationPromises: Array<(config: SanitizedConfig) => Promise<void>> = []
|
||||
const richTextSanitizationPromises: Array<(sanitizedConfig: SanitizedConfig) => Promise<void>> =
|
||||
[]
|
||||
|
||||
const schedulePublishCollections: CollectionSlug[] = []
|
||||
|
||||
@@ -191,20 +195,20 @@ export const sanitizeConfig = async (incomingConfig: Config): Promise<SanitizedC
|
||||
|
||||
const collectionSlugs = new Set<CollectionSlug>()
|
||||
|
||||
await addFolderCollections(config as unknown as Config)
|
||||
await addFolderCollections(sanitizedConfig as unknown as Config)
|
||||
|
||||
const validRelationships = [
|
||||
...(config.collections?.map((c) => c.slug) ?? []),
|
||||
...(sanitizedConfig.collections?.map((c) => c.slug) ?? []),
|
||||
jobsCollectionSlug,
|
||||
lockedDocumentsCollectionSlug,
|
||||
preferencesCollectionSlug,
|
||||
]
|
||||
|
||||
/**
|
||||
* Blocks sanitization needs to happen before collections, as collection/global join field sanitization needs config.blocks
|
||||
* Blocks sanitization needs to happen before collections, as collection/global join field sanitization needs sanitizedConfig.blocks
|
||||
* to be populated with the sanitized blocks
|
||||
*/
|
||||
config.blocks = []
|
||||
sanitizedConfig.blocks = []
|
||||
|
||||
if (incomingConfig.blocks?.length) {
|
||||
for (const block of incomingConfig.blocks) {
|
||||
@@ -222,7 +226,7 @@ export const sanitizeConfig = async (incomingConfig: Config): Promise<SanitizedC
|
||||
: sanitizedBlock.labels
|
||||
|
||||
sanitizedBlock.fields = await sanitizeFields({
|
||||
config: config as unknown as Config,
|
||||
config: incomingConfig,
|
||||
existingFieldNames: new Set(),
|
||||
fields: sanitizedBlock.fields,
|
||||
parentIsLocalized: false,
|
||||
@@ -232,84 +236,84 @@ export const sanitizeConfig = async (incomingConfig: Config): Promise<SanitizedC
|
||||
|
||||
const flattenedSanitizedBlock = flattenBlock({ block })
|
||||
|
||||
config.blocks.push(flattenedSanitizedBlock)
|
||||
sanitizedConfig.blocks.push(flattenedSanitizedBlock)
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < config.collections!.length; i++) {
|
||||
if (collectionSlugs.has(config.collections![i]!.slug)) {
|
||||
throw new DuplicateCollection('slug', config.collections![i]!.slug)
|
||||
for (let i = 0; i < sanitizedConfig.collections!.length; i++) {
|
||||
if (collectionSlugs.has(sanitizedConfig.collections![i]!.slug)) {
|
||||
throw new DuplicateCollection('slug', sanitizedConfig.collections![i]!.slug)
|
||||
}
|
||||
|
||||
collectionSlugs.add(config.collections![i]!.slug)
|
||||
collectionSlugs.add(sanitizedConfig.collections![i]!.slug)
|
||||
|
||||
const draftsConfig = config.collections![i]?.versions?.drafts
|
||||
const draftsConfig = sanitizedConfig.collections![i]?.versions?.drafts
|
||||
|
||||
if (typeof draftsConfig === 'object' && draftsConfig.schedulePublish) {
|
||||
schedulePublishCollections.push(config.collections![i]!.slug)
|
||||
schedulePublishCollections.push(sanitizedConfig.collections![i]!.slug)
|
||||
}
|
||||
|
||||
if (config.collections![i]!.enableQueryPresets) {
|
||||
queryPresetsCollections.push(config.collections![i]!.slug)
|
||||
if (sanitizedConfig.collections![i]!.enableQueryPresets) {
|
||||
queryPresetsCollections.push(sanitizedConfig.collections![i]!.slug)
|
||||
|
||||
if (!validRelationships.includes(queryPresetsCollectionSlug)) {
|
||||
validRelationships.push(queryPresetsCollectionSlug)
|
||||
}
|
||||
}
|
||||
|
||||
config.collections![i] = await sanitizeCollection(
|
||||
config as unknown as Config,
|
||||
config.collections![i]!,
|
||||
sanitizedConfig.collections![i] = await sanitizeCollection({
|
||||
collectionConfig: sanitizedConfig.collections![i]!,
|
||||
config: incomingConfig,
|
||||
richTextSanitizationPromises,
|
||||
validRelationships,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
if (config.globals!.length > 0) {
|
||||
for (let i = 0; i < config.globals!.length; i++) {
|
||||
const draftsConfig = config.globals![i]?.versions?.drafts
|
||||
if (sanitizedConfig.globals!.length > 0) {
|
||||
for (let i = 0; i < sanitizedConfig.globals!.length; i++) {
|
||||
const draftsConfig = sanitizedConfig.globals![i]?.versions?.drafts
|
||||
|
||||
if (typeof draftsConfig === 'object' && draftsConfig.schedulePublish) {
|
||||
schedulePublishGlobals.push(config.globals![i]!.slug)
|
||||
schedulePublishGlobals.push(sanitizedConfig.globals![i]!.slug)
|
||||
}
|
||||
|
||||
config.globals![i] = await sanitizeGlobal(
|
||||
config as unknown as Config,
|
||||
config.globals![i]!,
|
||||
sanitizedConfig.globals![i] = await sanitizeGlobal({
|
||||
config: incomingConfig,
|
||||
globalConfig: sanitizedConfig.globals![i]!,
|
||||
richTextSanitizationPromises,
|
||||
validRelationships,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (schedulePublishCollections.length || schedulePublishGlobals.length) {
|
||||
;((config.jobs ??= {} as SanitizedJobsConfig).tasks ??= []).push(
|
||||
;((sanitizedConfig.jobs ??= {} as SanitizedJobsConfig).tasks ??= []).push(
|
||||
getSchedulePublishTask({
|
||||
adminUserSlug: config.admin!.user,
|
||||
adminUserSlug: sanitizedConfig.admin!.user,
|
||||
collections: schedulePublishCollections,
|
||||
globals: schedulePublishGlobals,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
;(config.jobs ??= {} as SanitizedJobsConfig).enabled = Boolean(
|
||||
;(sanitizedConfig.jobs ??= {} as SanitizedJobsConfig).enabled = Boolean(
|
||||
(Array.isArray(configWithDefaults.jobs?.tasks) && configWithDefaults.jobs?.tasks?.length) ||
|
||||
(Array.isArray(configWithDefaults.jobs?.workflows) &&
|
||||
configWithDefaults.jobs?.workflows?.length),
|
||||
)
|
||||
|
||||
// Need to add default jobs collection before locked documents collections
|
||||
if (config.jobs.enabled) {
|
||||
let defaultJobsCollection = getDefaultJobsCollection(config as unknown as Config)
|
||||
if (sanitizedConfig.jobs.enabled) {
|
||||
let defaultJobsCollection = getDefaultJobsCollection(sanitizedConfig as unknown as Config)
|
||||
|
||||
if (typeof config.jobs.jobsCollectionOverrides === 'function') {
|
||||
defaultJobsCollection = config.jobs.jobsCollectionOverrides({
|
||||
if (typeof sanitizedConfig.jobs.jobsCollectionOverrides === 'function') {
|
||||
defaultJobsCollection = sanitizedConfig.jobs.jobsCollectionOverrides({
|
||||
defaultJobsCollection,
|
||||
})
|
||||
|
||||
const hooks = defaultJobsCollection?.hooks
|
||||
// @todo - delete this check in 4.0
|
||||
if (hooks && config?.jobs?.runHooks !== true) {
|
||||
if (hooks && sanitizedConfig?.jobs?.runHooks !== true) {
|
||||
for (const [hookKey, hook] of Object.entries(hooks)) {
|
||||
const defaultAmount = hookKey === 'afterRead' || hookKey === 'beforeChange' ? 1 : 0
|
||||
if (hook.length > defaultAmount) {
|
||||
@@ -322,101 +326,104 @@ export const sanitizeConfig = async (incomingConfig: Config): Promise<SanitizedC
|
||||
}
|
||||
}
|
||||
}
|
||||
const sanitizedJobsCollection = await sanitizeCollection(
|
||||
config as unknown as Config,
|
||||
defaultJobsCollection,
|
||||
const sanitizedJobsCollection = await sanitizeCollection({
|
||||
collectionConfig: defaultJobsCollection,
|
||||
config: incomingConfig,
|
||||
richTextSanitizationPromises,
|
||||
validRelationships,
|
||||
)
|
||||
})
|
||||
|
||||
configWithDefaults.collections!.push(sanitizedJobsCollection)
|
||||
}
|
||||
|
||||
configWithDefaults.collections!.push(
|
||||
await sanitizeCollection(
|
||||
config as unknown as Config,
|
||||
getLockedDocumentsCollection(config as unknown as Config),
|
||||
await sanitizeCollection({
|
||||
collectionConfig: getLockedDocumentsCollection(incomingConfig),
|
||||
config: incomingConfig,
|
||||
richTextSanitizationPromises,
|
||||
validRelationships,
|
||||
),
|
||||
}),
|
||||
)
|
||||
|
||||
configWithDefaults.collections!.push(
|
||||
await sanitizeCollection(
|
||||
config as unknown as Config,
|
||||
getPreferencesCollection(config as unknown as Config),
|
||||
await sanitizeCollection({
|
||||
collectionConfig: getPreferencesCollection(incomingConfig),
|
||||
config: incomingConfig,
|
||||
richTextSanitizationPromises,
|
||||
validRelationships,
|
||||
),
|
||||
}),
|
||||
)
|
||||
|
||||
configWithDefaults.collections!.push(
|
||||
await sanitizeCollection(
|
||||
config as unknown as Config,
|
||||
migrationsCollection,
|
||||
await sanitizeCollection({
|
||||
collectionConfig: migrationsCollection,
|
||||
config: incomingConfig,
|
||||
richTextSanitizationPromises,
|
||||
validRelationships,
|
||||
),
|
||||
}),
|
||||
)
|
||||
|
||||
if (queryPresetsCollections.length > 0) {
|
||||
configWithDefaults.collections!.push(
|
||||
await sanitizeCollection(
|
||||
config as unknown as Config,
|
||||
getQueryPresetsConfig(config as unknown as Config),
|
||||
await sanitizeCollection({
|
||||
collectionConfig: getQueryPresetsCollection(incomingConfig),
|
||||
config: incomingConfig,
|
||||
richTextSanitizationPromises,
|
||||
validRelationships,
|
||||
),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
if (config.serverURL !== '') {
|
||||
config.csrf!.push(config.serverURL!)
|
||||
if (sanitizedConfig.serverURL !== '') {
|
||||
sanitizedConfig.csrf!.push(sanitizedConfig.serverURL!)
|
||||
}
|
||||
|
||||
const uploadAdapters = new Set<string>()
|
||||
// interact with all collections
|
||||
for (const collection of config.collections!) {
|
||||
for (const collection of sanitizedConfig.collections!) {
|
||||
// deduped upload adapters
|
||||
if (collection.upload?.adapter) {
|
||||
uploadAdapters.add(collection.upload.adapter)
|
||||
}
|
||||
}
|
||||
|
||||
if (!config.upload) {
|
||||
config.upload = { adapters: [] }
|
||||
if (!sanitizedConfig.upload) {
|
||||
sanitizedConfig.upload = { adapters: [] }
|
||||
}
|
||||
|
||||
config.upload.adapters = Array.from(
|
||||
new Set(config.collections!.map((c) => c.upload?.adapter).filter(Boolean) as string[]),
|
||||
sanitizedConfig.upload.adapters = Array.from(
|
||||
new Set(sanitizedConfig.collections!.map((c) => c.upload?.adapter).filter(Boolean) as string[]),
|
||||
)
|
||||
|
||||
// Pass through the email config as is so adapters don't break
|
||||
// Pass through the email sanitizedConfig as is so adapters don't break
|
||||
if (incomingConfig.email) {
|
||||
config.email = incomingConfig.email
|
||||
sanitizedConfig.email = incomingConfig.email
|
||||
}
|
||||
|
||||
/*
|
||||
Execute richText sanitization
|
||||
*/
|
||||
if (typeof incomingConfig.editor === 'function') {
|
||||
config.editor = await incomingConfig.editor({
|
||||
config: config as SanitizedConfig,
|
||||
sanitizedConfig.editor = await incomingConfig.editor({
|
||||
config: sanitizedConfig as SanitizedConfig,
|
||||
isRoot: true,
|
||||
parentIsLocalized: false,
|
||||
})
|
||||
if (config.editor.i18n && Object.keys(config.editor.i18n).length >= 0) {
|
||||
config.i18n.translations = deepMergeSimple(config.i18n.translations, config.editor.i18n)
|
||||
if (sanitizedConfig.editor.i18n && Object.keys(sanitizedConfig.editor.i18n).length >= 0) {
|
||||
sanitizedConfig.i18n.translations = deepMergeSimple(
|
||||
sanitizedConfig.i18n.translations,
|
||||
sanitizedConfig.editor.i18n,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const promises: Promise<void>[] = []
|
||||
|
||||
for (const sanitizeFunction of richTextSanitizationPromises) {
|
||||
promises.push(sanitizeFunction(config as SanitizedConfig))
|
||||
promises.push(sanitizeFunction(sanitizedConfig as SanitizedConfig))
|
||||
}
|
||||
|
||||
await Promise.all(promises)
|
||||
|
||||
return config as SanitizedConfig
|
||||
return sanitizedConfig as SanitizedConfig
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Config } from '../../config/types.js'
|
||||
import type { CollectionConfig, Field } from '../../index.js'
|
||||
import type { CollectionConfig, Field, SanitizedCollectionConfig } from '../../index.js'
|
||||
|
||||
import { ReservedFieldName } from '../../errors/index.js'
|
||||
import { sanitizeCollection } from '../../collections/config/sanitize.js'
|
||||
@@ -27,9 +27,8 @@ describe('reservedFieldNames - collections -', () => {
|
||||
]
|
||||
|
||||
await expect(async () => {
|
||||
await sanitizeCollection(
|
||||
// @ts-expect-error
|
||||
{
|
||||
await sanitizeCollection({
|
||||
config: {
|
||||
...config,
|
||||
collections: [
|
||||
{
|
||||
@@ -37,12 +36,12 @@ describe('reservedFieldNames - collections -', () => {
|
||||
fields,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
} as Config,
|
||||
collectionConfig: {
|
||||
...collectionWithUploads,
|
||||
fields,
|
||||
},
|
||||
)
|
||||
})
|
||||
}).rejects.toThrow(ReservedFieldName)
|
||||
})
|
||||
|
||||
@@ -56,9 +55,8 @@ describe('reservedFieldNames - collections -', () => {
|
||||
]
|
||||
|
||||
await expect(async () => {
|
||||
await sanitizeCollection(
|
||||
// @ts-expect-error
|
||||
{
|
||||
await sanitizeCollection({
|
||||
config: {
|
||||
...config,
|
||||
collections: [
|
||||
{
|
||||
@@ -66,12 +64,12 @@ describe('reservedFieldNames - collections -', () => {
|
||||
fields,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
} as Config,
|
||||
collectionConfig: {
|
||||
...collectionWithUploads,
|
||||
fields,
|
||||
},
|
||||
)
|
||||
})
|
||||
}).not.toThrow()
|
||||
})
|
||||
})
|
||||
@@ -97,9 +95,8 @@ describe('reservedFieldNames - collections -', () => {
|
||||
]
|
||||
|
||||
await expect(async () => {
|
||||
await sanitizeCollection(
|
||||
// @ts-expect-error
|
||||
{
|
||||
await sanitizeCollection({
|
||||
config: {
|
||||
...config,
|
||||
collections: [
|
||||
{
|
||||
@@ -107,12 +104,12 @@ describe('reservedFieldNames - collections -', () => {
|
||||
fields,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
} as Config,
|
||||
collectionConfig: {
|
||||
...collectionWithAuth,
|
||||
fields,
|
||||
},
|
||||
)
|
||||
})
|
||||
}).rejects.toThrow(ReservedFieldName)
|
||||
})
|
||||
|
||||
@@ -126,9 +123,8 @@ describe('reservedFieldNames - collections -', () => {
|
||||
]
|
||||
|
||||
await expect(async () => {
|
||||
await sanitizeCollection(
|
||||
// @ts-expect-error
|
||||
{
|
||||
await sanitizeCollection({
|
||||
config: {
|
||||
...config,
|
||||
collections: [
|
||||
{
|
||||
@@ -136,12 +132,12 @@ describe('reservedFieldNames - collections -', () => {
|
||||
fields,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
} as Config,
|
||||
collectionConfig: {
|
||||
...collectionWithAuth,
|
||||
fields,
|
||||
},
|
||||
)
|
||||
})
|
||||
}).rejects.toThrow(ReservedFieldName)
|
||||
})
|
||||
|
||||
@@ -155,9 +151,8 @@ describe('reservedFieldNames - collections -', () => {
|
||||
]
|
||||
|
||||
await expect(async () => {
|
||||
await sanitizeCollection(
|
||||
// @ts-expect-error
|
||||
{
|
||||
await sanitizeCollection({
|
||||
config: {
|
||||
...config,
|
||||
collections: [
|
||||
{
|
||||
@@ -165,12 +160,12 @@ describe('reservedFieldNames - collections -', () => {
|
||||
fields,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
} as Config,
|
||||
collectionConfig: {
|
||||
...collectionWithAuth,
|
||||
fields,
|
||||
},
|
||||
)
|
||||
})
|
||||
}).not.toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -179,6 +179,7 @@ export const sanitizeFields = async ({
|
||||
console.warn(
|
||||
`(payload): The "min" property is deprecated for the Relationship field "${field.name}" and will be removed in a future version. Please use "minRows" instead.`,
|
||||
)
|
||||
|
||||
field.minRows = field.min
|
||||
}
|
||||
|
||||
@@ -186,6 +187,7 @@ export const sanitizeFields = async ({
|
||||
console.warn(
|
||||
`(payload): The "max" property is deprecated for the Relationship field "${field.name}" and will be removed in a future version. Please use "maxRows" instead.`,
|
||||
)
|
||||
|
||||
field.maxRows = field.max
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,113 +11,128 @@ import { baseVersionFields } from '../../versions/baseFields.js'
|
||||
import { versionDefaults } from '../../versions/defaults.js'
|
||||
import { defaultGlobalEndpoints } from '../endpoints/index.js'
|
||||
|
||||
export const sanitizeGlobal = async (
|
||||
config: Config,
|
||||
global: GlobalConfig,
|
||||
export const sanitizeGlobal = async ({
|
||||
config,
|
||||
globalConfig,
|
||||
richTextSanitizationPromises,
|
||||
validRelationships: _validRelationships,
|
||||
}: {
|
||||
config: Config
|
||||
globalConfig: GlobalConfig
|
||||
/**
|
||||
* If this property is set, RichText fields won't be sanitized immediately. Instead, they will be added to this array as promises
|
||||
* so that you can sanitize them together, after the config has been sanitized.
|
||||
*/
|
||||
richTextSanitizationPromises?: Array<(config: SanitizedConfig) => Promise<void>>,
|
||||
_validRelationships?: string[],
|
||||
): Promise<SanitizedGlobalConfig> => {
|
||||
if (global._sanitized) {
|
||||
return global as SanitizedGlobalConfig
|
||||
richTextSanitizationPromises?: Array<(config: SanitizedConfig) => Promise<void>>
|
||||
validRelationships?: string[]
|
||||
}): Promise<SanitizedGlobalConfig> => {
|
||||
if (globalConfig._sanitized) {
|
||||
return globalConfig as SanitizedGlobalConfig
|
||||
}
|
||||
global._sanitized = true
|
||||
|
||||
global.label = global.label || toWords(global.slug)
|
||||
globalConfig._sanitized = true
|
||||
|
||||
globalConfig.label = globalConfig.label || toWords(globalConfig.slug)
|
||||
|
||||
// /////////////////////////////////
|
||||
// Ensure that collection has required object structure
|
||||
// /////////////////////////////////
|
||||
|
||||
global.endpoints = global.endpoints ?? []
|
||||
if (!global.hooks) {
|
||||
global.hooks = {}
|
||||
}
|
||||
if (!global.access) {
|
||||
global.access = {}
|
||||
}
|
||||
if (!global.admin) {
|
||||
global.admin = {}
|
||||
globalConfig.endpoints = globalConfig.endpoints ?? []
|
||||
|
||||
if (!globalConfig.hooks) {
|
||||
globalConfig.hooks = {}
|
||||
}
|
||||
|
||||
if (!global.access.read) {
|
||||
global.access.read = defaultAccess
|
||||
}
|
||||
if (!global.access.update) {
|
||||
global.access.update = defaultAccess
|
||||
if (!globalConfig.access) {
|
||||
globalConfig.access = {}
|
||||
}
|
||||
|
||||
if (!global.hooks.beforeValidate) {
|
||||
global.hooks.beforeValidate = []
|
||||
if (!globalConfig.admin) {
|
||||
globalConfig.admin = {}
|
||||
}
|
||||
if (!global.hooks.beforeChange) {
|
||||
global.hooks.beforeChange = []
|
||||
|
||||
if (!globalConfig.access.read) {
|
||||
globalConfig.access.read = defaultAccess
|
||||
}
|
||||
if (!global.hooks.afterChange) {
|
||||
global.hooks.afterChange = []
|
||||
|
||||
if (!globalConfig.access.update) {
|
||||
globalConfig.access.update = defaultAccess
|
||||
}
|
||||
if (!global.hooks.beforeRead) {
|
||||
global.hooks.beforeRead = []
|
||||
|
||||
if (!globalConfig.hooks.beforeValidate) {
|
||||
globalConfig.hooks.beforeValidate = []
|
||||
}
|
||||
if (!global.hooks.afterRead) {
|
||||
global.hooks.afterRead = []
|
||||
|
||||
if (!globalConfig.hooks.beforeChange) {
|
||||
globalConfig.hooks.beforeChange = []
|
||||
}
|
||||
|
||||
if (!globalConfig.hooks.afterChange) {
|
||||
globalConfig.hooks.afterChange = []
|
||||
}
|
||||
|
||||
if (!globalConfig.hooks.beforeRead) {
|
||||
globalConfig.hooks.beforeRead = []
|
||||
}
|
||||
|
||||
if (!globalConfig.hooks.afterRead) {
|
||||
globalConfig.hooks.afterRead = []
|
||||
}
|
||||
|
||||
// Sanitize fields
|
||||
const validRelationships = _validRelationships ?? config.collections?.map((c) => c.slug) ?? []
|
||||
|
||||
global.fields = await sanitizeFields({
|
||||
globalConfig.fields = await sanitizeFields({
|
||||
config,
|
||||
fields: global.fields,
|
||||
fields: globalConfig.fields,
|
||||
parentIsLocalized: false,
|
||||
richTextSanitizationPromises,
|
||||
validRelationships,
|
||||
})
|
||||
|
||||
if (global.endpoints !== false) {
|
||||
if (!global.endpoints) {
|
||||
global.endpoints = []
|
||||
if (globalConfig.endpoints !== false) {
|
||||
if (!globalConfig.endpoints) {
|
||||
globalConfig.endpoints = []
|
||||
}
|
||||
|
||||
for (const endpoint of defaultGlobalEndpoints) {
|
||||
global.endpoints.push(endpoint)
|
||||
globalConfig.endpoints.push(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
if (global.versions) {
|
||||
if (global.versions === true) {
|
||||
global.versions = { drafts: false, max: 100 }
|
||||
if (globalConfig.versions) {
|
||||
if (globalConfig.versions === true) {
|
||||
globalConfig.versions = { drafts: false, max: 100 }
|
||||
}
|
||||
|
||||
global.versions.max = typeof global.versions.max === 'number' ? global.versions.max : 100
|
||||
globalConfig.versions.max =
|
||||
typeof globalConfig.versions.max === 'number' ? globalConfig.versions.max : 100
|
||||
|
||||
if (global.versions.drafts) {
|
||||
if (global.versions.drafts === true) {
|
||||
global.versions.drafts = {
|
||||
if (globalConfig.versions.drafts) {
|
||||
if (globalConfig.versions.drafts === true) {
|
||||
globalConfig.versions.drafts = {
|
||||
autosave: false,
|
||||
validate: false,
|
||||
}
|
||||
}
|
||||
|
||||
if (global.versions.drafts.autosave === true) {
|
||||
global.versions.drafts.autosave = {
|
||||
if (globalConfig.versions.drafts.autosave === true) {
|
||||
globalConfig.versions.drafts.autosave = {
|
||||
interval: versionDefaults.autosaveInterval,
|
||||
}
|
||||
}
|
||||
|
||||
if (global.versions.drafts.validate === undefined) {
|
||||
global.versions.drafts.validate = false
|
||||
if (globalConfig.versions.drafts.validate === undefined) {
|
||||
globalConfig.versions.drafts.validate = false
|
||||
}
|
||||
|
||||
global.fields = mergeBaseFields(global.fields, baseVersionFields)
|
||||
globalConfig.fields = mergeBaseFields(globalConfig.fields, baseVersionFields)
|
||||
}
|
||||
}
|
||||
|
||||
if (!global.custom) {
|
||||
global.custom = {}
|
||||
if (!globalConfig.custom) {
|
||||
globalConfig.custom = {}
|
||||
}
|
||||
|
||||
// /////////////////////////////////
|
||||
@@ -125,7 +140,8 @@ export const sanitizeGlobal = async (
|
||||
// /////////////////////////////////
|
||||
let hasUpdatedAt: boolean | null = null
|
||||
let hasCreatedAt: boolean | null = null
|
||||
global.fields.some((field) => {
|
||||
|
||||
globalConfig.fields.some((field) => {
|
||||
if (fieldAffectsData(field)) {
|
||||
if (field.name === 'updatedAt') {
|
||||
hasUpdatedAt = true
|
||||
@@ -136,8 +152,9 @@ export const sanitizeGlobal = async (
|
||||
}
|
||||
return hasCreatedAt && hasUpdatedAt
|
||||
})
|
||||
|
||||
if (!hasUpdatedAt) {
|
||||
global.fields.push({
|
||||
globalConfig.fields.push({
|
||||
name: 'updatedAt',
|
||||
type: 'date',
|
||||
admin: {
|
||||
@@ -147,8 +164,9 @@ export const sanitizeGlobal = async (
|
||||
label: ({ t }) => t('general:updatedAt'),
|
||||
})
|
||||
}
|
||||
|
||||
if (!hasCreatedAt) {
|
||||
global.fields.push({
|
||||
globalConfig.fields.push({
|
||||
name: 'createdAt',
|
||||
type: 'date',
|
||||
admin: {
|
||||
@@ -159,7 +177,9 @@ export const sanitizeGlobal = async (
|
||||
})
|
||||
}
|
||||
|
||||
;(global as SanitizedGlobalConfig).flattenedFields = flattenAllFields({ fields: global.fields })
|
||||
;(globalConfig as SanitizedGlobalConfig).flattenedFields = flattenAllFields({
|
||||
fields: globalConfig.fields,
|
||||
})
|
||||
|
||||
return global as SanitizedGlobalConfig
|
||||
return globalConfig as SanitizedGlobalConfig
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import { operations, type QueryPreset } from './types.js'
|
||||
|
||||
export const queryPresetsCollectionSlug = 'payload-query-presets'
|
||||
|
||||
export const getQueryPresetsConfig = (config: Config): CollectionConfig => ({
|
||||
export const getQueryPresetsCollection = (config: Config): CollectionConfig => ({
|
||||
slug: queryPresetsCollectionSlug,
|
||||
access: getAccess(config),
|
||||
admin: {
|
||||
|
||||
Reference in New Issue
Block a user