Skip to content

Commit 93d79b9

Browse files
authored
perf: remove duplicative deep loops during field sanitization (#12402)
Optimizes the field sanitization process by removing duplicative deep loops over the config. We were previously iterating over all fields of each collection potentially multiple times in order validate field configs, check reserved field names, etc. Now, we perform all necessary sanitization within a single loop.
1 parent 9779cf7 commit 93d79b9

File tree

8 files changed

+187
-179
lines changed

8 files changed

+187
-179
lines changed

packages/payload/src/collections/config/reservedFieldNames.ts

Lines changed: 0 additions & 151 deletions
This file was deleted.

packages/payload/src/collections/config/sanitize.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import {
2626
addDefaultsToCollectionConfig,
2727
addDefaultsToLoginWithUsernameConfig,
2828
} from './defaults.js'
29-
import { sanitizeAuthFields, sanitizeUploadFields } from './reservedFieldNames.js'
3029
import { sanitizeCompoundIndexes } from './sanitizeCompoundIndexes.js'
3130
import { validateUseAsTitle } from './useAsTitle.js'
3231

@@ -43,7 +42,9 @@ export const sanitizeCollection = async (
4342
if (collection._sanitized) {
4443
return collection as SanitizedCollectionConfig
4544
}
45+
4646
collection._sanitized = true
47+
4748
// /////////////////////////////////
4849
// Make copy of collection config
4950
// /////////////////////////////////
@@ -57,7 +58,9 @@ export const sanitizeCollection = async (
5758
const validRelationships = _validRelationships ?? config.collections.map((c) => c.slug) ?? []
5859

5960
const joins: SanitizedJoins = {}
61+
6062
const polymorphicJoins: SanitizedJoin[] = []
63+
6164
sanitized.fields = await sanitizeFields({
6265
collectionConfig: sanitized,
6366
config,
@@ -96,17 +99,21 @@ export const sanitizeCollection = async (
9699
// add default timestamps fields only as needed
97100
let hasUpdatedAt: boolean | null = null
98101
let hasCreatedAt: boolean | null = null
102+
99103
sanitized.fields.some((field) => {
100104
if (fieldAffectsData(field)) {
101105
if (field.name === 'updatedAt') {
102106
hasUpdatedAt = true
103107
}
108+
104109
if (field.name === 'createdAt') {
105110
hasCreatedAt = true
106111
}
107112
}
113+
108114
return hasCreatedAt && hasUpdatedAt
109115
})
116+
110117
if (!hasUpdatedAt) {
111118
sanitized.fields.push({
112119
name: 'updatedAt',
@@ -119,6 +126,7 @@ export const sanitizeCollection = async (
119126
label: ({ t }) => t('general:updatedAt'),
120127
})
121128
}
129+
122130
if (!hasCreatedAt) {
123131
sanitized.fields.push({
124132
name: 'createdAt',
@@ -175,9 +183,6 @@ export const sanitizeCollection = async (
175183
sanitized.upload = {}
176184
}
177185

178-
// sanitize fields for reserved names
179-
sanitizeUploadFields(sanitized.fields, sanitized)
180-
181186
sanitized.upload.cacheTags = sanitized.upload?.cacheTags ?? true
182187
sanitized.upload.bulkUpload = sanitized.upload?.bulkUpload ?? true
183188
sanitized.upload.staticDir = sanitized.upload.staticDir || sanitized.slug
@@ -195,9 +200,6 @@ export const sanitizeCollection = async (
195200
}
196201

197202
if (sanitized.auth) {
198-
// sanitize fields for reserved names
199-
sanitizeAuthFields(sanitized.fields, sanitized)
200-
201203
sanitized.auth = addDefaultsToAuthConfig(
202204
typeof sanitized.auth === 'boolean' ? {} : sanitized.auth,
203205
)

packages/payload/src/collections/config/useAsTitle.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { CollectionConfig } from '../../index.js'
22

33
import { InvalidConfiguration } from '../../errors/InvalidConfiguration.js'
4-
import { fieldAffectsData, fieldIsVirtual } from '../../fields/config/types.js'
4+
import { fieldAffectsData } from '../../fields/config/types.js'
55
import flattenFields from '../../utilities/flattenTopLevelFields.js'
66

77
/**

packages/payload/src/config/sanitize.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ const sanitizeAdminConfig = (configToSanitize: Config): Partial<SanitizedConfig>
5858
// add default user collection if none provided
5959
if (!sanitizedConfig?.admin?.user) {
6060
const firstCollectionWithAuth = sanitizedConfig.collections.find(({ auth }) => Boolean(auth))
61+
6162
if (firstCollectionWithAuth) {
6263
sanitizedConfig.admin.user = firstCollectionWithAuth.slug
6364
} else {
@@ -69,6 +70,7 @@ const sanitizeAdminConfig = (configToSanitize: Config): Partial<SanitizedConfig>
6970
const userCollection = sanitizedConfig.collections.find(
7071
({ slug }) => slug === sanitizedConfig.admin.user,
7172
)
73+
7274
if (!userCollection || !userCollection.auth) {
7375
throw new InvalidConfiguration(
7476
`${sanitizedConfig.admin.user} is not a valid admin user collection`,

packages/payload/src/collections/config/reservedFieldNames.spec.ts renamed to packages/payload/src/fields/config/reservedFieldNames.spec.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { Config } from '../../config/types.js'
22
import type { CollectionConfig, Field } from '../../index.js'
33

44
import { ReservedFieldName } from '../../errors/index.js'
5-
import { sanitizeCollection } from './sanitize.js'
5+
import { sanitizeCollection } from '../../collections/config/sanitize.js'
66

77
describe('reservedFieldNames - collections -', () => {
88
const config = {
@@ -25,6 +25,7 @@ describe('reservedFieldNames - collections -', () => {
2525
label: 'some-collection',
2626
},
2727
]
28+
2829
await expect(async () => {
2930
await sanitizeCollection(
3031
// @ts-expect-error
@@ -53,6 +54,7 @@ describe('reservedFieldNames - collections -', () => {
5354
label: 'some-collection',
5455
},
5556
]
57+
5658
await expect(async () => {
5759
await sanitizeCollection(
5860
// @ts-expect-error
@@ -93,6 +95,7 @@ describe('reservedFieldNames - collections -', () => {
9395
label: 'some-collection',
9496
},
9597
]
98+
9699
await expect(async () => {
97100
await sanitizeCollection(
98101
// @ts-expect-error
@@ -121,6 +124,7 @@ describe('reservedFieldNames - collections -', () => {
121124
label: 'some-collection',
122125
},
123126
]
127+
124128
await expect(async () => {
125129
await sanitizeCollection(
126130
// @ts-expect-error
@@ -149,6 +153,7 @@ describe('reservedFieldNames - collections -', () => {
149153
label: 'some-collection',
150154
},
151155
]
156+
152157
await expect(async () => {
153158
await sanitizeCollection(
154159
// @ts-expect-error
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* Reserved field names for collections with auth config enabled
3+
*/
4+
export const reservedBaseAuthFieldNames = [
5+
/* 'email',
6+
'resetPasswordToken',
7+
'resetPasswordExpiration', */
8+
'salt',
9+
'hash',
10+
]
11+
12+
/**
13+
* Reserved field names for auth collections with verify: true
14+
*/
15+
export const reservedVerifyFieldNames = [
16+
/* '_verified', '_verificationToken' */
17+
]
18+
19+
/**
20+
* Reserved field names for auth collections with useApiKey: true
21+
*/
22+
export const reservedAPIKeyFieldNames = [
23+
/* 'enableAPIKey', 'apiKeyIndex', 'apiKey' */
24+
]
25+
26+
/**
27+
* Reserved field names for collections with upload config enabled
28+
*/
29+
export const reservedBaseUploadFieldNames = [
30+
'file',
31+
/* 'mimeType',
32+
'thumbnailURL',
33+
'width',
34+
'height',
35+
'filesize',
36+
'filename',
37+
'url',
38+
'focalX',
39+
'focalY',
40+
'sizes', */
41+
]
42+
43+
/**
44+
* Reserved field names for collections with versions enabled
45+
*/
46+
export const reservedVersionsFieldNames = [
47+
/* '__v', '_status' */
48+
]

0 commit comments

Comments
 (0)