Skip to content

Commit 116fd99

Browse files
authored
perf: reduce document data deepCopying in field hooks (#10610)
A lot of this deepCopying was just not necessary. This removes the deepCopying from all field hook operations where I think it's 100% safe. It does not remove all deepCopying, especially in areas where the input data was deep copied, and that data pre-modification is then used after the field hooks have run. In these cases, further execution of the hook might be intentionally expecting the unmodified version of that input data
1 parent fafe37e commit 116fd99

File tree

9 files changed

+20
-23
lines changed

9 files changed

+20
-23
lines changed

packages/payload/src/collections/operations/utilities/update.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import type { DeepPartial } from 'ts-essentials'
22

33
import type { Args } from '../../../fields/hooks/beforeChange/index.js'
4-
import type { AccessResult, CollectionSlug, FileToSave, SanitizedConfig } from '../../../index.js'
54
import type {
65
Payload,
76
PayloadRequest,
@@ -22,6 +21,13 @@ import { afterChange } from '../../../fields/hooks/afterChange/index.js'
2221
import { afterRead } from '../../../fields/hooks/afterRead/index.js'
2322
import { beforeChange } from '../../../fields/hooks/beforeChange/index.js'
2423
import { beforeValidate } from '../../../fields/hooks/beforeValidate/index.js'
24+
import {
25+
type AccessResult,
26+
type CollectionSlug,
27+
deepCopyObjectSimple,
28+
type FileToSave,
29+
type SanitizedConfig,
30+
} from '../../../index.js'
2531
import { deleteAssociatedFiles } from '../../../uploads/deleteAssociatedFiles.js'
2632
import { uploadFiles } from '../../../uploads/uploadFiles.js'
2733
import { checkDocumentLockStatus } from '../../../utilities/checkDocumentLockStatus.js'
@@ -110,7 +116,7 @@ export const updateDocument = async <
110116
collection: collectionConfig,
111117
context: req.context,
112118
depth: 0,
113-
doc: docWithLocales,
119+
doc: deepCopyObjectSimple(docWithLocales),
114120
draft: draftArg,
115121
fallbackLocale: id ? null : fallbackLocale,
116122
global: null,

packages/payload/src/duplicateDocument/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Forbidden } from '../errors/Forbidden.js'
99
import { NotFound } from '../errors/NotFound.js'
1010
import { afterRead } from '../fields/hooks/afterRead/index.js'
1111
import { beforeDuplicate } from '../fields/hooks/beforeDuplicate/index.js'
12+
import { deepCopyObjectSimple } from '../utilities/deepCopyObject.js'
1213
import { getLatestCollectionVersion } from '../versions/getLatestCollectionVersion.js'
1314

1415
type GetDuplicateDocumentArgs = {
@@ -92,7 +93,7 @@ export const getDuplicateDocumentData = async ({
9293
collection: collectionConfig,
9394
context: req.context,
9495
depth: 0,
95-
doc: duplicatedFromDocWithLocales,
96+
doc: deepCopyObjectSimple(duplicatedFromDocWithLocales),
9697
draft: draftArg,
9798
fallbackLocale: null,
9899
global: null,

packages/payload/src/fields/hooks/afterChange/index.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import type { SanitizedGlobalConfig } from '../../../globals/config/types.js'
33
import type { RequestContext } from '../../../index.js'
44
import type { JsonObject, PayloadRequest } from '../../../types/index.js'
55

6-
import { deepCopyObjectSimple } from '../../../utilities/deepCopyObject.js'
76
import { traverseFields } from './traverseFields.js'
87

98
type Args<T extends JsonObject> = {
@@ -37,13 +36,11 @@ export const afterChange = async <T extends JsonObject>({
3736
previousDoc,
3837
req,
3938
}: Args<T>): Promise<T> => {
40-
const doc = deepCopyObjectSimple(incomingDoc)
41-
4239
await traverseFields({
4340
collection,
4441
context,
4542
data,
46-
doc,
43+
doc: incomingDoc,
4744
fields: collection?.fields || global?.fields,
4845
global,
4946
operation,
@@ -53,8 +50,8 @@ export const afterChange = async <T extends JsonObject>({
5350
req,
5451
schemaPath: [],
5552
siblingData: data,
56-
siblingDoc: doc,
53+
siblingDoc: incomingDoc,
5754
})
5855

59-
return doc
56+
return incomingDoc
6057
}

packages/payload/src/fields/hooks/afterRead/index.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ export async function afterRead<T extends JsonObject>(args: Args<T>): Promise<T>
5656
showHiddenFields,
5757
} = args
5858

59-
const doc = deepCopyObjectSimple(incomingDoc)
6059
const fieldPromises = []
6160
const populationPromises = []
6261

@@ -75,7 +74,7 @@ export async function afterRead<T extends JsonObject>(args: Args<T>): Promise<T>
7574
context,
7675
currentDepth,
7776
depth,
78-
doc,
77+
doc: incomingDoc,
7978
draft,
8079
fallbackLocale,
8180
fieldPromises,
@@ -93,11 +92,11 @@ export async function afterRead<T extends JsonObject>(args: Args<T>): Promise<T>
9392
select,
9493
selectMode: select ? getSelectMode(select) : undefined,
9594
showHiddenFields,
96-
siblingDoc: doc,
95+
siblingDoc: incomingDoc,
9796
})
9897

9998
await Promise.all(fieldPromises)
10099
await Promise.all(populationPromises)
101100

102-
return doc
101+
return incomingDoc
103102
}

packages/payload/src/fields/hooks/beforeDuplicate/index.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,18 @@ export const beforeDuplicate = async <T extends JsonObject>({
2828
overrideAccess,
2929
req,
3030
}: Args<T>): Promise<T> => {
31-
const newDoc = deepCopyObjectSimple(doc)
32-
3331
await traverseFields({
3432
id,
3533
collection,
3634
context,
37-
doc: newDoc,
35+
doc,
3836
fields: collection?.fields,
3937
overrideAccess,
4038
path: [],
4139
req,
4240
schemaPath: [],
43-
siblingDoc: newDoc,
41+
siblingDoc: doc,
4442
})
4543

46-
return newDoc
44+
return doc
4745
}

packages/payload/src/fields/hooks/beforeValidate/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ export const beforeValidate = async <T extends JsonObject>({
3939
req,
4040
}: Args<T>): Promise<T> => {
4141
const data = deepCopyObjectSimple(incomingData)
42-
4342
await traverseFields({
4443
id,
4544
collection,

packages/payload/src/globals/operations/update.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ export const updateOperation = async <
127127
collection: null,
128128
context: req.context,
129129
depth: 0,
130-
doc: globalJSON,
130+
doc: deepCopyObjectSimple(globalJSON),
131131
draft: draftArg,
132132
fallbackLocale,
133133
global: globalConfig,

packages/payload/src/versions/drafts/replaceWithDraftIfAvailable.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ const replaceWithDraftIfAvailable = async <T extends TypeWithID>({
9898
return doc
9999
}
100100

101-
draft = deepCopyObjectSimple(draft)
102101
draft = sanitizeInternalFields(draft)
103102

104103
// Patch globalType onto version doc

packages/payload/src/versions/getLatestGlobalVersion.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import type { SanitizedGlobalConfig } from '../globals/config/types.js'
22
import type { Document, Payload, PayloadRequest, Where } from '../types/index.js'
33

4-
import { docHasTimestamps } from '../types/index.js'
5-
64
type Args = {
75
config: SanitizedGlobalConfig
86
locale?: string

0 commit comments

Comments
 (0)