@strapi/utils 5.51.1 → 5.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/content-types.d.ts.map +1 -1
- package/dist/content-types.js +39 -2
- package/dist/content-types.js.map +1 -1
- package/dist/content-types.mjs +39 -2
- package/dist/content-types.mjs.map +1 -1
- package/dist/convert-query-params.js +4 -4
- package/dist/convert-query-params.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/errors.mjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/install-id.js +1 -1
- package/dist/install-id.js.map +1 -1
- package/dist/install-id.mjs +1 -1
- package/dist/install-id.mjs.map +1 -1
- package/dist/pagination.d.ts +1 -1
- package/dist/pagination.d.ts.map +1 -1
- package/dist/pagination.js +12 -5
- package/dist/pagination.js.map +1 -1
- package/dist/pagination.mjs +12 -5
- package/dist/pagination.mjs.map +1 -1
- package/dist/parse-type.d.ts +13 -0
- package/dist/parse-type.d.ts.map +1 -1
- package/dist/parse-type.js +44 -15
- package/dist/parse-type.js.map +1 -1
- package/dist/parse-type.mjs +41 -15
- package/dist/parse-type.mjs.map +1 -1
- package/dist/policy.js +1 -1
- package/dist/policy.js.map +1 -1
- package/dist/policy.mjs +1 -1
- package/dist/policy.mjs.map +1 -1
- package/dist/route-serialization.d.ts +1 -1
- package/dist/route-serialization.d.ts.map +1 -1
- package/dist/route-serialization.js +1 -1
- package/dist/route-serialization.js.map +1 -1
- package/dist/route-serialization.mjs +1 -1
- package/dist/route-serialization.mjs.map +1 -1
- package/dist/sanitize/index.js +3 -1
- package/dist/sanitize/index.js.map +1 -1
- package/dist/sanitize/index.mjs +3 -1
- package/dist/sanitize/index.mjs.map +1 -1
- package/dist/sanitize/sanitizers.d.ts.map +1 -1
- package/dist/sanitize/sanitizers.js +5 -3
- package/dist/sanitize/sanitizers.js.map +1 -1
- package/dist/sanitize/sanitizers.mjs +5 -3
- package/dist/sanitize/sanitizers.mjs.map +1 -1
- package/dist/sanitize/visitors/remove-restricted-relations.d.ts.map +1 -1
- package/dist/sanitize/visitors/remove-restricted-relations.js +128 -86
- package/dist/sanitize/visitors/remove-restricted-relations.js.map +1 -1
- package/dist/sanitize/visitors/remove-restricted-relations.mjs +128 -86
- package/dist/sanitize/visitors/remove-restricted-relations.mjs.map +1 -1
- package/dist/traverse-entity.d.ts.map +1 -1
- package/dist/traverse-entity.js +9 -1
- package/dist/traverse-entity.js.map +1 -1
- package/dist/traverse-entity.mjs +9 -1
- package/dist/traverse-entity.mjs.map +1 -1
- package/dist/validate/validators.js +12 -19
- package/dist/validate/validators.js.map +1 -1
- package/dist/validate/validators.mjs +13 -20
- package/dist/validate/validators.mjs.map +1 -1
- package/package.json +4 -4
|
@@ -6,108 +6,150 @@ const ACTIONS_TO_VERIFY = [
|
|
|
6
6
|
'find'
|
|
7
7
|
];
|
|
8
8
|
const { CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE } = constants;
|
|
9
|
-
var removeRestrictedRelations = ((auth)=>
|
|
10
|
-
|
|
9
|
+
var removeRestrictedRelations = ((auth)=>(visitorOptions, visitorUtils)=>{
|
|
10
|
+
const { attribute } = visitorOptions;
|
|
11
|
+
// Deliberately not an `async` function. This visitor is invoked for every key of
|
|
12
|
+
// every node of every entity, and only relation attributes need any work at all.
|
|
13
|
+
// Declaring it `async` meant each of those keys allocated a promise purely to
|
|
14
|
+
// resolve it immediately, and the traversal then awaited it. Returning early
|
|
15
|
+
// without a promise lets the traversal stay synchronous for the common case.
|
|
16
|
+
if (!attribute || attribute.type !== 'relation') {
|
|
11
17
|
return;
|
|
12
18
|
}
|
|
13
|
-
|
|
14
|
-
|
|
19
|
+
return visitRelationAttribute(auth, visitorOptions, visitorUtils);
|
|
20
|
+
});
|
|
21
|
+
const visitRelationAttribute = async (auth, { data, key, attribute, schema }, { remove, set })=>{
|
|
22
|
+
// Narrowing only; the caller reaches this for relation attributes exclusively.
|
|
23
|
+
if (!attribute) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const handleMorphRelation = async ()=>{
|
|
27
|
+
const elements = data[key];
|
|
28
|
+
if (!elements) {
|
|
15
29
|
return;
|
|
16
30
|
}
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
31
|
+
if ('connect' in elements || 'set' in elements || 'disconnect' in elements) {
|
|
32
|
+
const newValue = {};
|
|
33
|
+
const connect = await handleMorphElements(elements.connect || []);
|
|
34
|
+
const relSet = await handleMorphElements(elements.set || []);
|
|
35
|
+
const disconnect = await handleMorphElements(elements.disconnect || []);
|
|
36
|
+
if (connect.length > 0) {
|
|
37
|
+
newValue.connect = connect;
|
|
21
38
|
}
|
|
22
|
-
if (
|
|
23
|
-
|
|
24
|
-
const connect = await handleMorphElements(elements.connect || []);
|
|
25
|
-
const relSet = await handleMorphElements(elements.set || []);
|
|
26
|
-
const disconnect = await handleMorphElements(elements.disconnect || []);
|
|
27
|
-
if (connect.length > 0) {
|
|
28
|
-
newValue.connect = connect;
|
|
29
|
-
}
|
|
30
|
-
if (relSet.length > 0) {
|
|
31
|
-
newValue.set = relSet;
|
|
32
|
-
}
|
|
33
|
-
if (disconnect.length > 0) {
|
|
34
|
-
newValue.disconnect = disconnect;
|
|
35
|
-
}
|
|
36
|
-
// TODO: this should technically be in its own visitor to check morph options, but for now we'll handle it here
|
|
37
|
-
if ('options' in elements && typeof elements.options === 'object' && elements.options !== null) {
|
|
38
|
-
const filteredOptions = {};
|
|
39
|
-
// Iterate through the keys of elements.options
|
|
40
|
-
Object.keys(elements.options).forEach((key)=>{
|
|
41
|
-
const validator = VALID_RELATION_ORDERING_KEYS[key];
|
|
42
|
-
// Ensure the key exists in VALID_RELATION_ORDERING_KEYS and the validator is defined before calling it
|
|
43
|
-
if (validator && validator(elements.options[key])) {
|
|
44
|
-
filteredOptions[key] = elements.options[key];
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
// Assign the filtered options back to newValue
|
|
48
|
-
newValue.options = filteredOptions;
|
|
49
|
-
} else {
|
|
50
|
-
newValue.options = {};
|
|
51
|
-
}
|
|
52
|
-
set(key, newValue);
|
|
53
|
-
} else {
|
|
54
|
-
const newMorphValue = await handleMorphElements(elements);
|
|
55
|
-
if (newMorphValue.length) {
|
|
56
|
-
set(key, newMorphValue);
|
|
57
|
-
}
|
|
39
|
+
if (relSet.length > 0) {
|
|
40
|
+
newValue.set = relSet;
|
|
58
41
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
42
|
+
if (disconnect.length > 0) {
|
|
43
|
+
newValue.disconnect = disconnect;
|
|
44
|
+
}
|
|
45
|
+
// TODO: this should technically be in its own visitor to check morph options, but for now we'll handle it here
|
|
46
|
+
if ('options' in elements && typeof elements.options === 'object' && elements.options !== null) {
|
|
47
|
+
const filteredOptions = {};
|
|
48
|
+
// Iterate through the keys of elements.options
|
|
49
|
+
Object.keys(elements.options).forEach((key)=>{
|
|
50
|
+
const validator = VALID_RELATION_ORDERING_KEYS[key];
|
|
51
|
+
// Ensure the key exists in VALID_RELATION_ORDERING_KEYS and the validator is defined before calling it
|
|
52
|
+
if (validator && validator(elements.options[key])) {
|
|
53
|
+
filteredOptions[key] = elements.options[key];
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
// Assign the filtered options back to newValue
|
|
57
|
+
newValue.options = filteredOptions;
|
|
58
|
+
} else {
|
|
59
|
+
newValue.options = {};
|
|
64
60
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const isAllowed = await hasAccessToSomeScopes(scopes, auth);
|
|
71
|
-
if (isAllowed) {
|
|
72
|
-
allowedElements.push(element);
|
|
73
|
-
}
|
|
61
|
+
set(key, newValue);
|
|
62
|
+
} else {
|
|
63
|
+
const newMorphValue = await handleMorphElements(elements);
|
|
64
|
+
if (newMorphValue.length) {
|
|
65
|
+
set(key, newMorphValue);
|
|
74
66
|
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const handleMorphElements = async (elements)=>{
|
|
70
|
+
const allowedElements = [];
|
|
71
|
+
if (!isArray(elements)) {
|
|
75
72
|
return allowedElements;
|
|
76
|
-
}
|
|
77
|
-
const
|
|
78
|
-
|
|
73
|
+
}
|
|
74
|
+
for (const element of elements){
|
|
75
|
+
if (!isObject(element) || !('__type' in element)) {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const scopes = ACTIONS_TO_VERIFY.map((action)=>`${element.__type}.${action}`);
|
|
79
79
|
const isAllowed = await hasAccessToSomeScopes(scopes, auth);
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
remove(key);
|
|
80
|
+
if (isAllowed) {
|
|
81
|
+
allowedElements.push(element);
|
|
83
82
|
}
|
|
84
|
-
};
|
|
85
|
-
const isCreatorRelation = [
|
|
86
|
-
CREATED_BY_ATTRIBUTE,
|
|
87
|
-
UPDATED_BY_ATTRIBUTE
|
|
88
|
-
].includes(key);
|
|
89
|
-
// Polymorphic relations
|
|
90
|
-
if (isMorphToRelationalAttribute(attribute)) {
|
|
91
|
-
await handleMorphRelation();
|
|
92
|
-
return;
|
|
93
83
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
84
|
+
return allowedElements;
|
|
85
|
+
};
|
|
86
|
+
const handleRegularRelation = async ()=>{
|
|
87
|
+
const scopes = ACTIONS_TO_VERIFY.map((action)=>`${attribute.target}.${action}`);
|
|
88
|
+
const isAllowed = await hasAccessToSomeScopes(scopes, auth);
|
|
89
|
+
// If the authenticated user don't have access to any of the scopes, then remove the field
|
|
90
|
+
if (!isAllowed) {
|
|
91
|
+
remove(key);
|
|
98
92
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
93
|
+
};
|
|
94
|
+
const isCreatorRelation = [
|
|
95
|
+
CREATED_BY_ATTRIBUTE,
|
|
96
|
+
UPDATED_BY_ATTRIBUTE
|
|
97
|
+
].includes(key);
|
|
98
|
+
// Polymorphic relations
|
|
99
|
+
if (isMorphToRelationalAttribute(attribute)) {
|
|
100
|
+
await handleMorphRelation();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
// Creator relations
|
|
104
|
+
if (isCreatorRelation && schema.options?.populateCreatorFields) {
|
|
105
|
+
// do nothing
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
// Regular relations
|
|
109
|
+
await handleRegularRelation();
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* Per-`auth` memo of scope decisions.
|
|
113
|
+
*
|
|
114
|
+
* The decision for a given scope depends only on the ability carried by `auth`, which is
|
|
115
|
+
* built once per request and not mutated while the response is sanitized. Sanitizing a
|
|
116
|
+
* list re-asks the same question for every entity, so a 25-entity page with four
|
|
117
|
+
* relations asked ~100 identical questions; each denial also constructed an Error, and
|
|
118
|
+
* capturing a stack trace is by far the most expensive part of that.
|
|
119
|
+
*
|
|
120
|
+
* Keyed weakly on the `auth` object so entries are collected with the request.
|
|
121
|
+
*/ const scopeDecisionCache = new WeakMap();
|
|
122
|
+
const canAccessScope = async (scope, auth)=>{
|
|
123
|
+
const cacheable = typeof auth === 'object' && auth !== null;
|
|
124
|
+
let decisions;
|
|
125
|
+
if (cacheable) {
|
|
126
|
+
decisions = scopeDecisionCache.get(auth);
|
|
127
|
+
if (decisions === undefined) {
|
|
128
|
+
decisions = new Map();
|
|
129
|
+
scopeDecisionCache.set(auth, decisions);
|
|
130
|
+
} else {
|
|
131
|
+
const cached = decisions.get(scope);
|
|
132
|
+
if (cached !== undefined) {
|
|
133
|
+
return cached;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
let allowed;
|
|
138
|
+
try {
|
|
139
|
+
await strapi.auth.verify(auth, {
|
|
140
|
+
scope
|
|
141
|
+
});
|
|
142
|
+
allowed = true;
|
|
143
|
+
} catch {
|
|
144
|
+
allowed = false;
|
|
145
|
+
}
|
|
146
|
+
decisions?.set(scope, allowed);
|
|
147
|
+
return allowed;
|
|
148
|
+
};
|
|
102
149
|
const hasAccessToSomeScopes = async (scopes, auth)=>{
|
|
103
150
|
for (const scope of scopes){
|
|
104
|
-
|
|
105
|
-
await strapi.auth.verify(auth, {
|
|
106
|
-
scope
|
|
107
|
-
});
|
|
151
|
+
if (await canAccessScope(scope, auth)) {
|
|
108
152
|
return true;
|
|
109
|
-
} catch {
|
|
110
|
-
continue;
|
|
111
153
|
}
|
|
112
154
|
}
|
|
113
155
|
return false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remove-restricted-relations.mjs","sources":["../../../src/sanitize/visitors/remove-restricted-relations.ts"],"sourcesContent":["import { isArray, isObject } from 'lodash/fp';\nimport * as contentTypeUtils from '../../content-types';\nimport type { Visitor } from '../../traverse/factory';\nimport { RelationOrderingOptions } from '../../types';\nimport { VALID_RELATION_ORDERING_KEYS } from '../../relations';\n\nconst ACTIONS_TO_VERIFY = ['find'];\nconst { CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE } = contentTypeUtils.constants;\n\ntype MorphArray = Array<{ __type: string }>;\n\nexport default (auth: unknown): Visitor =>\n async ({ data, key, attribute, schema }, { remove, set }) => {\n if (!attribute) {\n return;\n }\n\n const isRelation = attribute.type === 'relation';\n\n if (!isRelation) {\n return;\n }\n\n const handleMorphRelation = async () => {\n const elements: any = (data as Record<string, MorphArray>)[key];\n\n if (!elements) {\n return;\n }\n\n if ('connect' in elements || 'set' in elements || 'disconnect' in elements) {\n const newValue: Record<string, unknown> = {};\n\n const connect = await handleMorphElements(elements.connect || []);\n const relSet = await handleMorphElements(elements.set || []);\n const disconnect = await handleMorphElements(elements.disconnect || []);\n\n if (connect.length > 0) {\n newValue.connect = connect;\n }\n\n if (relSet.length > 0) {\n newValue.set = relSet;\n }\n\n if (disconnect.length > 0) {\n newValue.disconnect = disconnect;\n }\n\n // TODO: this should technically be in its own visitor to check morph options, but for now we'll handle it here\n if (\n 'options' in elements &&\n typeof elements.options === 'object' &&\n elements.options !== null\n ) {\n const filteredOptions: RelationOrderingOptions = {};\n\n // Iterate through the keys of elements.options\n Object.keys(elements.options).forEach((key) => {\n const validator = VALID_RELATION_ORDERING_KEYS[key as keyof RelationOrderingOptions];\n\n // Ensure the key exists in VALID_RELATION_ORDERING_KEYS and the validator is defined before calling it\n if (validator && validator(elements.options[key])) {\n filteredOptions[key as keyof RelationOrderingOptions] = elements.options[key];\n }\n });\n\n // Assign the filtered options back to newValue\n newValue.options = filteredOptions;\n } else {\n newValue.options = {};\n }\n\n set(key, newValue);\n } else {\n const newMorphValue = await handleMorphElements(elements);\n\n if (newMorphValue.length) {\n set(key, newMorphValue);\n }\n }\n };\n\n const handleMorphElements = async (elements: any[]) => {\n const allowedElements: Record<string, unknown>[] = [];\n\n if (!isArray(elements)) {\n return allowedElements;\n }\n\n for (const element of elements) {\n if (!isObject(element) || !('__type' in element)) {\n continue;\n }\n\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${element.__type}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (isAllowed) {\n allowedElements.push(element);\n }\n }\n\n return allowedElements;\n };\n\n const handleRegularRelation = async () => {\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${attribute.target}.${action}`);\n\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n // If the authenticated user don't have access to any of the scopes, then remove the field\n if (!isAllowed) {\n remove(key);\n }\n };\n\n const isCreatorRelation = [CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE].includes(key);\n\n // Polymorphic relations\n if (contentTypeUtils.isMorphToRelationalAttribute(attribute)) {\n await handleMorphRelation();\n return;\n }\n\n // Creator relations\n if (isCreatorRelation && schema.options?.populateCreatorFields) {\n // do nothing\n return;\n }\n\n // Regular relations\n await handleRegularRelation();\n };\n\nconst hasAccessToSomeScopes = async (scopes: string[], auth: unknown) => {\n for (const scope of scopes) {\n try {\n await strapi.auth.verify(auth, { scope });\n return true;\n } catch {\n continue;\n }\n }\n\n return false;\n};\n"],"names":["ACTIONS_TO_VERIFY","CREATED_BY_ATTRIBUTE","UPDATED_BY_ATTRIBUTE","contentTypeUtils","auth","data","key","attribute","schema","remove","set","isRelation","type","handleMorphRelation","elements","newValue","connect","handleMorphElements","relSet","disconnect","length","options","filteredOptions","Object","keys","forEach","validator","VALID_RELATION_ORDERING_KEYS","newMorphValue","allowedElements","isArray","element","isObject","scopes","map","action","__type","isAllowed","hasAccessToSomeScopes","push","handleRegularRelation","target","isCreatorRelation","includes","populateCreatorFields","scope","strapi","verify"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"remove-restricted-relations.mjs","sources":["../../../src/sanitize/visitors/remove-restricted-relations.ts"],"sourcesContent":["import { isArray, isObject } from 'lodash/fp';\nimport * as contentTypeUtils from '../../content-types';\nimport type { Visitor } from '../../traverse/factory';\nimport { RelationOrderingOptions } from '../../types';\nimport { VALID_RELATION_ORDERING_KEYS } from '../../relations';\n\nconst ACTIONS_TO_VERIFY = ['find'];\nconst { CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE } = contentTypeUtils.constants;\n\ntype MorphArray = Array<{ __type: string }>;\n\nexport default (auth: unknown): Visitor =>\n (visitorOptions, visitorUtils) => {\n const { attribute } = visitorOptions;\n\n // Deliberately not an `async` function. This visitor is invoked for every key of\n // every node of every entity, and only relation attributes need any work at all.\n // Declaring it `async` meant each of those keys allocated a promise purely to\n // resolve it immediately, and the traversal then awaited it. Returning early\n // without a promise lets the traversal stay synchronous for the common case.\n if (!attribute || attribute.type !== 'relation') {\n return;\n }\n\n return visitRelationAttribute(auth, visitorOptions, visitorUtils) as unknown as void;\n };\n\nconst visitRelationAttribute = async (\n auth: unknown,\n { data, key, attribute, schema }: Parameters<Visitor>[0],\n { remove, set }: Parameters<Visitor>[1]\n) => {\n // Narrowing only; the caller reaches this for relation attributes exclusively.\n if (!attribute) {\n return;\n }\n\n const handleMorphRelation = async () => {\n const elements: any = (data as Record<string, MorphArray>)[key];\n\n if (!elements) {\n return;\n }\n\n if ('connect' in elements || 'set' in elements || 'disconnect' in elements) {\n const newValue: Record<string, unknown> = {};\n\n const connect = await handleMorphElements(elements.connect || []);\n const relSet = await handleMorphElements(elements.set || []);\n const disconnect = await handleMorphElements(elements.disconnect || []);\n\n if (connect.length > 0) {\n newValue.connect = connect;\n }\n\n if (relSet.length > 0) {\n newValue.set = relSet;\n }\n\n if (disconnect.length > 0) {\n newValue.disconnect = disconnect;\n }\n\n // TODO: this should technically be in its own visitor to check morph options, but for now we'll handle it here\n if (\n 'options' in elements &&\n typeof elements.options === 'object' &&\n elements.options !== null\n ) {\n const filteredOptions: RelationOrderingOptions = {};\n\n // Iterate through the keys of elements.options\n Object.keys(elements.options).forEach((key) => {\n const validator = VALID_RELATION_ORDERING_KEYS[key as keyof RelationOrderingOptions];\n\n // Ensure the key exists in VALID_RELATION_ORDERING_KEYS and the validator is defined before calling it\n if (validator && validator(elements.options[key])) {\n filteredOptions[key as keyof RelationOrderingOptions] = elements.options[key];\n }\n });\n\n // Assign the filtered options back to newValue\n newValue.options = filteredOptions;\n } else {\n newValue.options = {};\n }\n\n set(key, newValue);\n } else {\n const newMorphValue = await handleMorphElements(elements);\n\n if (newMorphValue.length) {\n set(key, newMorphValue);\n }\n }\n };\n\n const handleMorphElements = async (elements: any[]) => {\n const allowedElements: Record<string, unknown>[] = [];\n\n if (!isArray(elements)) {\n return allowedElements;\n }\n\n for (const element of elements) {\n if (!isObject(element) || !('__type' in element)) {\n continue;\n }\n\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${element.__type}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (isAllowed) {\n allowedElements.push(element);\n }\n }\n\n return allowedElements;\n };\n\n const handleRegularRelation = async () => {\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${attribute.target}.${action}`);\n\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n // If the authenticated user don't have access to any of the scopes, then remove the field\n if (!isAllowed) {\n remove(key);\n }\n };\n\n const isCreatorRelation = [CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE].includes(key);\n\n // Polymorphic relations\n if (contentTypeUtils.isMorphToRelationalAttribute(attribute)) {\n await handleMorphRelation();\n return;\n }\n\n // Creator relations\n if (isCreatorRelation && schema.options?.populateCreatorFields) {\n // do nothing\n return;\n }\n\n // Regular relations\n await handleRegularRelation();\n};\n\n/**\n * Per-`auth` memo of scope decisions.\n *\n * The decision for a given scope depends only on the ability carried by `auth`, which is\n * built once per request and not mutated while the response is sanitized. Sanitizing a\n * list re-asks the same question for every entity, so a 25-entity page with four\n * relations asked ~100 identical questions; each denial also constructed an Error, and\n * capturing a stack trace is by far the most expensive part of that.\n *\n * Keyed weakly on the `auth` object so entries are collected with the request.\n */\nconst scopeDecisionCache = new WeakMap<object, Map<string, boolean>>();\n\nconst canAccessScope = async (scope: string, auth: unknown): Promise<boolean> => {\n const cacheable = typeof auth === 'object' && auth !== null;\n\n let decisions: Map<string, boolean> | undefined;\n\n if (cacheable) {\n decisions = scopeDecisionCache.get(auth as object);\n\n if (decisions === undefined) {\n decisions = new Map();\n scopeDecisionCache.set(auth as object, decisions);\n } else {\n const cached = decisions.get(scope);\n if (cached !== undefined) {\n return cached;\n }\n }\n }\n\n let allowed: boolean;\n try {\n await strapi.auth.verify(auth, { scope });\n allowed = true;\n } catch {\n allowed = false;\n }\n\n decisions?.set(scope, allowed);\n\n return allowed;\n};\n\nconst hasAccessToSomeScopes = async (scopes: string[], auth: unknown) => {\n for (const scope of scopes) {\n if (await canAccessScope(scope, auth)) {\n return true;\n }\n }\n\n return false;\n};\n"],"names":["ACTIONS_TO_VERIFY","CREATED_BY_ATTRIBUTE","UPDATED_BY_ATTRIBUTE","contentTypeUtils","auth","visitorOptions","visitorUtils","attribute","type","visitRelationAttribute","data","key","schema","remove","set","handleMorphRelation","elements","newValue","connect","handleMorphElements","relSet","disconnect","length","options","filteredOptions","Object","keys","forEach","validator","VALID_RELATION_ORDERING_KEYS","newMorphValue","allowedElements","isArray","element","isObject","scopes","map","action","__type","isAllowed","hasAccessToSomeScopes","push","handleRegularRelation","target","isCreatorRelation","includes","populateCreatorFields","scopeDecisionCache","WeakMap","canAccessScope","scope","cacheable","decisions","get","undefined","Map","cached","allowed","strapi","verify"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"traverse-entity.d.ts","sourceRoot":"","sources":["../src/traverse-entity.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAoBpE,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEjE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,MAAM,EAAE,KAAK,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;IACxB,SAAS,CAAC,EAAE,YAAY,CAAC;IACzB,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4GAA4G;IAC5G,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC;AAED,MAAM,MAAM,OAAO,GAAG,CAAC,cAAc,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,KAAK,IAAI,CAAC;AAE3F,MAAM,WAAW,IAAI;IACnB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,KAAK,CAAC;IACd,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC;IAC7B,4GAA4G;IAC5G,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,MAAM;IACrB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,KAAK,CAAC;CACf;
|
|
1
|
+
{"version":3,"file":"traverse-entity.d.ts","sourceRoot":"","sources":["../src/traverse-entity.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAoBpE,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEjE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,MAAM,EAAE,KAAK,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;IACxB,SAAS,CAAC,EAAE,YAAY,CAAC;IACzB,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4GAA4G;IAC5G,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC;AAED,MAAM,MAAM,OAAO,GAAG,CAAC,cAAc,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,KAAK,IAAI,CAAC;AAE3F,MAAM,WAAW,IAAI;IACnB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,KAAK,CAAC;IACd,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC;IAC7B,4GAA4G;IAC5G,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,MAAM;IACrB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,KAAK,CAAC;CACf;AAsPD,QAAA,MAAM,kBAAkB,GAAI,UAAU;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE;gBACtC,MAAM;aAIT,MAAM,SAAS,IAAI;CAG5B,CAAC;;AAEH,wBAAqC"}
|
package/dist/traverse-entity.js
CHANGED
|
@@ -119,7 +119,15 @@ const traverseEntity = async (visitor, options, entity)=>{
|
|
|
119
119
|
parent,
|
|
120
120
|
allowedExtraRootKeys
|
|
121
121
|
};
|
|
122
|
-
|
|
122
|
+
// Awaited only when the visitor actually returns something thenable. Most visitors
|
|
123
|
+
// finish synchronously for most keys — a scalar is not a relation, so the relation
|
|
124
|
+
// visitor returns immediately — and `await` on a non-thenable still allocates a
|
|
125
|
+
// promise and defers the rest of the loop to a microtask. Over every key of every
|
|
126
|
+
// node of every entity in a page that was the largest single source of promise churn.
|
|
127
|
+
const visited = visitor(visitorOptions, visitorUtils);
|
|
128
|
+
if (visited != null && typeof visited.then === 'function') {
|
|
129
|
+
await visited;
|
|
130
|
+
}
|
|
123
131
|
// Extract the value for the current key (after calling the visitor)
|
|
124
132
|
const value = copy[key];
|
|
125
133
|
// Ignore Nil values or attributes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"traverse-entity.js","sources":["../src/traverse-entity.ts"],"sourcesContent":["import { clone, isObject, isArray, isNil, curry } from 'lodash/fp';\n\nimport type { Attribute, AnyAttribute, Model, Data } from './types';\nimport { isRelationalAttribute, isMediaAttribute } from './content-types';\n\n/**\n * Execute promises in parallel but throw errors in array index order.\n */\nconst parallelWithOrderedErrors = async <T>(promises: Promise<T>[]): Promise<T[]> => {\n const results = await Promise.allSettled(promises);\n\n // Throw first error in array index order (matches sequential behavior)\n for (let i = 0; i < results.length; i += 1) {\n const result = results[i];\n if (result.status === 'rejected') {\n throw result.reason;\n }\n }\n\n return results.map((r) => (r as PromiseFulfilledResult<T>).value);\n};\n\nexport type VisitorUtils = ReturnType<typeof createVisitorUtils>;\n\nexport interface VisitorOptions {\n data: Record<string, unknown>;\n schema: Model;\n key: string;\n value: Data[keyof Data];\n attribute?: AnyAttribute;\n path: Path;\n getModel(uid: string): Model;\n parent?: Parent;\n /** Extra root-level keys allowed (e.g. registered input params). Only used when path.attribute === null. */\n allowedExtraRootKeys?: string[];\n}\n\nexport type Visitor = (visitorOptions: VisitorOptions, visitorUtils: VisitorUtils) => void;\n\nexport interface Path {\n raw: string | null;\n attribute: string | null;\n rawWithIndices?: string | null;\n}\n\nexport interface TraverseOptions {\n schema: Model;\n path?: Path;\n parent?: Parent;\n getModel(uid: string): Model;\n /** Extra root-level keys allowed (e.g. registered input params). Only used when path.attribute === null. */\n allowedExtraRootKeys?: string[];\n}\n\nexport interface Parent {\n attribute?: Attribute;\n key: string | null;\n path: Path;\n schema: Model;\n}\n\nconst traverseEntity = async (\n visitor: Visitor,\n options: TraverseOptions,\n entity: Data\n): Promise<Data> => {\n const {\n path = { raw: null, attribute: null, rawWithIndices: null },\n schema,\n getModel,\n allowedExtraRootKeys,\n } = options;\n\n let parent = options.parent;\n\n const traverseMorphRelationTarget = async (visitor: Visitor, path: Path, entry: Data) => {\n const targetSchema = getModel(entry.__type!);\n\n const traverseOptions: TraverseOptions = {\n schema: targetSchema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const traverseRelationTarget =\n (schema: Model) => async (visitor: Visitor, path: Path, entry: Data) => {\n const traverseOptions: TraverseOptions = {\n schema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const traverseMediaTarget = async (visitor: Visitor, path: Path, entry: Data) => {\n const targetSchemaUID = 'plugin::upload.file';\n const targetSchema = getModel(targetSchemaUID);\n\n const traverseOptions: TraverseOptions = {\n schema: targetSchema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const traverseComponent = async (visitor: Visitor, path: Path, schema: Model, entry: Data) => {\n const traverseOptions: TraverseOptions = {\n schema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const visitDynamicZoneEntry = async (visitor: Visitor, path: Path, entry: Data) => {\n // A dynamic zone array can contain a `null` entry (for example a relation\n // created inline inside a dynamic zone component can leave a null item).\n // Reading `__component` on it crashed traversal; pass nil entries through\n // untouched, consistent with how `traverseEntity` ends recursion on nil. (#24303)\n if (isNil(entry)) {\n return entry;\n }\n\n const targetSchema = getModel(entry.__component!);\n const traverseOptions: TraverseOptions = {\n schema: targetSchema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n // End recursion\n if (!isObject(entity) || isNil(schema)) {\n return entity;\n }\n\n // Don't mutate the original entity object\n // only clone at 1st level as the next level will get clone when traversed\n const copy = clone(entity);\n const visitorUtils = createVisitorUtils({ data: copy });\n\n const keys = Object.keys(copy);\n for (let i = 0; i < keys.length; i += 1) {\n const key = keys[i];\n // Retrieve the attribute definition associated to the key from the schema\n const attribute = schema.attributes[key] as AnyAttribute | undefined;\n\n const newPath = { ...path };\n\n newPath.raw = isNil(path.raw) ? key : `${path.raw}.${key}`;\n newPath.rawWithIndices = isNil(path.rawWithIndices) ? key : `${path.rawWithIndices}.${key}`;\n\n if (!isNil(attribute)) {\n newPath.attribute = isNil(path.attribute) ? key : `${path.attribute}.${key}`;\n }\n\n // Visit the current attribute\n const visitorOptions: VisitorOptions = {\n data: copy,\n schema,\n key,\n value: copy[key],\n attribute,\n path: newPath,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n await visitor(visitorOptions, visitorUtils);\n\n // Extract the value for the current key (after calling the visitor)\n const value = copy[key];\n\n // Ignore Nil values or attributes\n if (isNil(value) || isNil(attribute)) {\n continue;\n }\n\n if (isRelationalAttribute(attribute)) {\n parent = { schema, key, attribute, path: newPath };\n const isMorphRelation = attribute.relation.toLowerCase().startsWith('morph');\n\n const method = isMorphRelation\n ? traverseMorphRelationTarget\n : traverseRelationTarget(getModel(attribute.target!));\n\n if (isArray(value)) {\n // Process array items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return method(visitor, arrayPath, item);\n })\n );\n } else {\n copy[key] = await method(visitor, newPath, value as Data);\n }\n\n continue;\n }\n\n if (isMediaAttribute(attribute)) {\n parent = { schema, key, attribute, path: newPath };\n\n if (isArray(value)) {\n // Process media array items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return traverseMediaTarget(visitor, arrayPath, item);\n })\n );\n } else {\n copy[key] = await traverseMediaTarget(visitor, newPath, value as Data);\n }\n\n continue;\n }\n\n if (attribute.type === 'component') {\n parent = { schema, key, attribute, path: newPath };\n const targetSchema = getModel(attribute.component);\n\n if (isArray(value)) {\n // Process component array items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return traverseComponent(visitor, arrayPath, targetSchema, item);\n })\n );\n } else {\n copy[key] = await traverseComponent(visitor, newPath, targetSchema, value as Data);\n }\n\n continue;\n }\n\n if (attribute.type === 'dynamiczone' && isArray(value)) {\n parent = { schema, key, attribute, path: newPath };\n\n // Process dynamic zone items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return visitDynamicZoneEntry(visitor, arrayPath, item);\n })\n );\n\n continue;\n }\n }\n\n return copy;\n};\n\nconst createVisitorUtils = ({ data }: { data: Data }) => ({\n remove(key: string) {\n delete data[key];\n },\n\n set(key: string, value: Data) {\n data[key] = value;\n },\n});\n\nexport default curry(traverseEntity);\n"],"names":["parallelWithOrderedErrors","promises","results","Promise","allSettled","i","length","result","status","reason","map","r","value","traverseEntity","visitor","options","entity","path","raw","attribute","rawWithIndices","schema","getModel","allowedExtraRootKeys","parent","traverseMorphRelationTarget","entry","targetSchema","__type","traverseOptions","traverseRelationTarget","traverseMediaTarget","targetSchemaUID","traverseComponent","visitDynamicZoneEntry","isNil","__component","isObject","copy","clone","visitorUtils","createVisitorUtils","data","keys","Object","key","attributes","newPath","visitorOptions","isRelationalAttribute","isMorphRelation","relation","toLowerCase","startsWith","method","target","isArray","item","arrayPath","isMediaAttribute","type","component","remove","set","curry"],"mappings":";;;;;AAKA;;IAGA,MAAMA,4BAA4B,OAAUC,QAAAA,GAAAA;AAC1C,IAAA,MAAMC,OAAAA,GAAU,MAAMC,OAAAA,CAAQC,UAAU,CAACH,QAAAA,CAAAA;;IAGzC,IAAK,IAAII,IAAI,CAAA,EAAGA,CAAAA,GAAIH,QAAQI,MAAM,EAAED,KAAK,CAAA,CAAG;QAC1C,MAAME,MAAAA,GAASL,OAAO,CAACG,CAAAA,CAAE;QACzB,IAAIE,MAAAA,CAAOC,MAAM,KAAK,UAAA,EAAY;AAChC,YAAA,MAAMD,OAAOE,MAAM;AACrB,QAAA;AACF,IAAA;AAEA,IAAA,OAAOP,QAAQQ,GAAG,CAAC,CAACC,CAAAA,GAAOA,EAAgCC,KAAK,CAAA;AAClE,CAAA;AAyCA,MAAMC,cAAAA,GAAiB,OACrBC,OAAAA,EACAC,OAAAA,EACAC,MAAAA,GAAAA;AAEA,IAAA,MAAM,EACJC,IAAAA,GAAO;QAAEC,GAAAA,EAAK,IAAA;QAAMC,SAAAA,EAAW,IAAA;QAAMC,cAAAA,EAAgB;AAAK,KAAC,EAC3DC,MAAM,EACNC,QAAQ,EACRC,oBAAoB,EACrB,GAAGR,OAAAA;IAEJ,IAAIS,MAAAA,GAAST,QAAQS,MAAM;IAE3B,MAAMC,2BAAAA,GAA8B,OAAOX,OAAAA,EAAkBG,IAAAA,EAAYS,KAAAA,GAAAA;QACvE,MAAMC,YAAAA,GAAeL,QAAAA,CAASI,KAAAA,CAAME,MAAM,CAAA;AAE1C,QAAA,MAAMC,eAAAA,GAAmC;YACvCR,MAAAA,EAAQM,YAAAA;AACRV,YAAAA,IAAAA;AACAK,YAAAA,QAAAA;AACAE,YAAAA,MAAAA;AACAD,YAAAA;AACF,SAAA;QAEA,OAAOV,cAAAA,CAAeC,SAASe,eAAAA,EAAiBH,KAAAA,CAAAA;AAClD,IAAA,CAAA;AAEA,IAAA,MAAMI,sBAAAA,GACJ,CAACT,MAAAA,GAAkB,OAAOP,SAAkBG,IAAAA,EAAYS,KAAAA,GAAAA;AACtD,YAAA,MAAMG,eAAAA,GAAmC;AACvCR,gBAAAA,MAAAA;AACAJ,gBAAAA,IAAAA;AACAK,gBAAAA,QAAAA;AACAE,gBAAAA,MAAAA;AACAD,gBAAAA;AACF,aAAA;YAEA,OAAOV,cAAAA,CAAeC,SAASe,eAAAA,EAAiBH,KAAAA,CAAAA;AAClD,QAAA,CAAA;IAEF,MAAMK,mBAAAA,GAAsB,OAAOjB,OAAAA,EAAkBG,IAAAA,EAAYS,KAAAA,GAAAA;AAC/D,QAAA,MAAMM,eAAAA,GAAkB,qBAAA;AACxB,QAAA,MAAML,eAAeL,QAAAA,CAASU,eAAAA,CAAAA;AAE9B,QAAA,MAAMH,eAAAA,GAAmC;YACvCR,MAAAA,EAAQM,YAAAA;AACRV,YAAAA,IAAAA;AACAK,YAAAA,QAAAA;AACAE,YAAAA,MAAAA;AACAD,YAAAA;AACF,SAAA;QAEA,OAAOV,cAAAA,CAAeC,SAASe,eAAAA,EAAiBH,KAAAA,CAAAA;AAClD,IAAA,CAAA;AAEA,IAAA,MAAMO,iBAAAA,GAAoB,OAAOnB,OAAAA,EAAkBG,IAAAA,EAAYI,MAAAA,EAAeK,KAAAA,GAAAA;AAC5E,QAAA,MAAMG,eAAAA,GAAmC;AACvCR,YAAAA,MAAAA;AACAJ,YAAAA,IAAAA;AACAK,YAAAA,QAAAA;AACAE,YAAAA,MAAAA;AACAD,YAAAA;AACF,SAAA;QAEA,OAAOV,cAAAA,CAAeC,SAASe,eAAAA,EAAiBH,KAAAA,CAAAA;AAClD,IAAA,CAAA;IAEA,MAAMQ,qBAAAA,GAAwB,OAAOpB,OAAAA,EAAkBG,IAAAA,EAAYS,KAAAA,GAAAA;;;;;AAKjE,QAAA,IAAIS,SAAMT,KAAAA,CAAAA,EAAQ;YAChB,OAAOA,KAAAA;AACT,QAAA;QAEA,MAAMC,YAAAA,GAAeL,QAAAA,CAASI,KAAAA,CAAMU,WAAW,CAAA;AAC/C,QAAA,MAAMP,eAAAA,GAAmC;YACvCR,MAAAA,EAAQM,YAAAA;AACRV,YAAAA,IAAAA;AACAK,YAAAA,QAAAA;AACAE,YAAAA,MAAAA;AACAD,YAAAA;AACF,SAAA;QAEA,OAAOV,cAAAA,CAAeC,SAASe,eAAAA,EAAiBH,KAAAA,CAAAA;AAClD,IAAA,CAAA;;AAGA,IAAA,IAAI,CAACW,WAAAA,CAASrB,MAAAA,CAAAA,IAAWmB,QAAAA,CAAMd,MAAAA,CAAAA,EAAS;QACtC,OAAOL,MAAAA;AACT,IAAA;;;AAIA,IAAA,MAAMsB,OAAOC,QAAAA,CAAMvB,MAAAA,CAAAA;AACnB,IAAA,MAAMwB,eAAeC,kBAAAA,CAAmB;QAAEC,IAAAA,EAAMJ;AAAK,KAAA,CAAA;IAErD,MAAMK,IAAAA,GAAOC,MAAAA,CAAOD,IAAI,CAACL,IAAAA,CAAAA;IACzB,IAAK,IAAIjC,IAAI,CAAA,EAAGA,CAAAA,GAAIsC,KAAKrC,MAAM,EAAED,KAAK,CAAA,CAAG;QACvC,MAAMwC,GAAAA,GAAMF,IAAI,CAACtC,CAAAA,CAAE;;AAEnB,QAAA,MAAMc,SAAAA,GAAYE,MAAAA,CAAOyB,UAAU,CAACD,GAAAA,CAAI;AAExC,QAAA,MAAME,OAAAA,GAAU;AAAE,YAAA,GAAG9B;AAAK,SAAA;AAE1B8B,QAAAA,OAAAA,CAAQ7B,GAAG,GAAGiB,QAAAA,CAAMlB,IAAAA,CAAKC,GAAG,CAAA,GAAI2B,GAAAA,GAAM,CAAA,EAAG5B,IAAAA,CAAKC,GAAG,CAAC,CAAC,EAAE2B,GAAAA,CAAAA,CAAK;AAC1DE,QAAAA,OAAAA,CAAQ3B,cAAc,GAAGe,QAAAA,CAAMlB,IAAAA,CAAKG,cAAc,CAAA,GAAIyB,GAAAA,GAAM,CAAA,EAAG5B,IAAAA,CAAKG,cAAc,CAAC,CAAC,EAAEyB,GAAAA,CAAAA,CAAK;QAE3F,IAAI,CAACV,SAAMhB,SAAAA,CAAAA,EAAY;AACrB4B,YAAAA,OAAAA,CAAQ5B,SAAS,GAAGgB,QAAAA,CAAMlB,IAAAA,CAAKE,SAAS,CAAA,GAAI0B,GAAAA,GAAM,CAAA,EAAG5B,IAAAA,CAAKE,SAAS,CAAC,CAAC,EAAE0B,GAAAA,CAAAA,CAAK;AAC9E,QAAA;;AAGA,QAAA,MAAMG,cAAAA,GAAiC;YACrCN,IAAAA,EAAMJ,IAAAA;AACNjB,YAAAA,MAAAA;AACAwB,YAAAA,GAAAA;YACAjC,KAAAA,EAAO0B,IAAI,CAACO,GAAAA,CAAI;AAChB1B,YAAAA,SAAAA;YACAF,IAAAA,EAAM8B,OAAAA;AACNzB,YAAAA,QAAAA;AACAE,YAAAA,MAAAA;AACAD,YAAAA;AACF,SAAA;AAEA,QAAA,MAAMT,QAAQkC,cAAAA,EAAgBR,YAAAA,CAAAA;;QAG9B,MAAM5B,KAAAA,GAAQ0B,IAAI,CAACO,GAAAA,CAAI;;QAGvB,IAAIV,QAAAA,CAAMvB,KAAAA,CAAAA,IAAUuB,QAAAA,CAAMhB,SAAAA,CAAAA,EAAY;AACpC,YAAA;AACF,QAAA;AAEA,QAAA,IAAI8B,mCAAsB9B,SAAAA,CAAAA,EAAY;YACpCK,MAAAA,GAAS;AAAEH,gBAAAA,MAAAA;AAAQwB,gBAAAA,GAAAA;AAAK1B,gBAAAA,SAAAA;gBAAWF,IAAAA,EAAM8B;AAAQ,aAAA;AACjD,YAAA,MAAMG,kBAAkB/B,SAAAA,CAAUgC,QAAQ,CAACC,WAAW,EAAA,CAAGC,UAAU,CAAC,OAAA,CAAA;AAEpE,YAAA,MAAMC,SAASJ,eAAAA,GACXzB,2BAAAA,GACAK,sBAAAA,CAAuBR,QAAAA,CAASH,UAAUoC,MAAM,CAAA,CAAA;AAEpD,YAAA,IAAIC,WAAQ5C,KAAAA,CAAAA,EAAQ;;gBAElB0B,IAAI,CAACO,IAAI,GAAG,MAAM7C,0BAChBY,KAAAA,CAAMF,GAAG,CAAC,CAAC+C,IAAAA,EAAMpD,CAAAA,GAAAA;AACf,oBAAA,MAAMqD,SAAAA,GAAY;AAChB,wBAAA,GAAGX,OAAO;AACV3B,wBAAAA,cAAAA,EAAgBe,QAAAA,CAAMY,OAAAA,CAAQ3B,cAAc,CAAA,GACxC,CAAA,EAAGf,CAAAA,CAAAA,CAAG,GACN,CAAA,EAAG0C,OAAAA,CAAQ3B,cAAc,CAAC,CAAC,EAAEf,CAAAA,CAAAA;AACnC,qBAAA;oBACA,OAAOiD,MAAAA,CAAOxC,SAAS4C,SAAAA,EAAWD,IAAAA,CAAAA;AACpC,gBAAA,CAAA,CAAA,CAAA;YAEJ,CAAA,MAAO;AACLnB,gBAAAA,IAAI,CAACO,GAAAA,CAAI,GAAG,MAAMS,MAAAA,CAAOxC,SAASiC,OAAAA,EAASnC,KAAAA,CAAAA;AAC7C,YAAA;AAEA,YAAA;AACF,QAAA;AAEA,QAAA,IAAI+C,8BAAiBxC,SAAAA,CAAAA,EAAY;YAC/BK,MAAAA,GAAS;AAAEH,gBAAAA,MAAAA;AAAQwB,gBAAAA,GAAAA;AAAK1B,gBAAAA,SAAAA;gBAAWF,IAAAA,EAAM8B;AAAQ,aAAA;AAEjD,YAAA,IAAIS,WAAQ5C,KAAAA,CAAAA,EAAQ;;gBAElB0B,IAAI,CAACO,IAAI,GAAG,MAAM7C,0BAChBY,KAAAA,CAAMF,GAAG,CAAC,CAAC+C,IAAAA,EAAMpD,CAAAA,GAAAA;AACf,oBAAA,MAAMqD,SAAAA,GAAY;AAChB,wBAAA,GAAGX,OAAO;AACV3B,wBAAAA,cAAAA,EAAgBe,QAAAA,CAAMY,OAAAA,CAAQ3B,cAAc,CAAA,GACxC,CAAA,EAAGf,CAAAA,CAAAA,CAAG,GACN,CAAA,EAAG0C,OAAAA,CAAQ3B,cAAc,CAAC,CAAC,EAAEf,CAAAA,CAAAA;AACnC,qBAAA;oBACA,OAAO0B,mBAAAA,CAAoBjB,SAAS4C,SAAAA,EAAWD,IAAAA,CAAAA;AACjD,gBAAA,CAAA,CAAA,CAAA;YAEJ,CAAA,MAAO;AACLnB,gBAAAA,IAAI,CAACO,GAAAA,CAAI,GAAG,MAAMd,mBAAAA,CAAoBjB,SAASiC,OAAAA,EAASnC,KAAAA,CAAAA;AAC1D,YAAA;AAEA,YAAA;AACF,QAAA;QAEA,IAAIO,SAAAA,CAAUyC,IAAI,KAAK,WAAA,EAAa;YAClCpC,MAAAA,GAAS;AAAEH,gBAAAA,MAAAA;AAAQwB,gBAAAA,GAAAA;AAAK1B,gBAAAA,SAAAA;gBAAWF,IAAAA,EAAM8B;AAAQ,aAAA;YACjD,MAAMpB,YAAAA,GAAeL,QAAAA,CAASH,SAAAA,CAAU0C,SAAS,CAAA;AAEjD,YAAA,IAAIL,WAAQ5C,KAAAA,CAAAA,EAAQ;;gBAElB0B,IAAI,CAACO,IAAI,GAAG,MAAM7C,0BAChBY,KAAAA,CAAMF,GAAG,CAAC,CAAC+C,IAAAA,EAAMpD,CAAAA,GAAAA;AACf,oBAAA,MAAMqD,SAAAA,GAAY;AAChB,wBAAA,GAAGX,OAAO;AACV3B,wBAAAA,cAAAA,EAAgBe,QAAAA,CAAMY,OAAAA,CAAQ3B,cAAc,CAAA,GACxC,CAAA,EAAGf,CAAAA,CAAAA,CAAG,GACN,CAAA,EAAG0C,OAAAA,CAAQ3B,cAAc,CAAC,CAAC,EAAEf,CAAAA,CAAAA;AACnC,qBAAA;oBACA,OAAO4B,iBAAAA,CAAkBnB,OAAAA,EAAS4C,SAAAA,EAAW/B,YAAAA,EAAc8B,IAAAA,CAAAA;AAC7D,gBAAA,CAAA,CAAA,CAAA;YAEJ,CAAA,MAAO;AACLnB,gBAAAA,IAAI,CAACO,GAAAA,CAAI,GAAG,MAAMZ,iBAAAA,CAAkBnB,OAAAA,EAASiC,SAASpB,YAAAA,EAAcf,KAAAA,CAAAA;AACtE,YAAA;AAEA,YAAA;AACF,QAAA;AAEA,QAAA,IAAIO,SAAAA,CAAUyC,IAAI,KAAK,aAAA,IAAiBJ,WAAQ5C,KAAAA,CAAAA,EAAQ;YACtDY,MAAAA,GAAS;AAAEH,gBAAAA,MAAAA;AAAQwB,gBAAAA,GAAAA;AAAK1B,gBAAAA,SAAAA;gBAAWF,IAAAA,EAAM8B;AAAQ,aAAA;;YAGjDT,IAAI,CAACO,IAAI,GAAG,MAAM7C,0BAChBY,KAAAA,CAAMF,GAAG,CAAC,CAAC+C,IAAAA,EAAMpD,CAAAA,GAAAA;AACf,gBAAA,MAAMqD,SAAAA,GAAY;AAChB,oBAAA,GAAGX,OAAO;AACV3B,oBAAAA,cAAAA,EAAgBe,QAAAA,CAAMY,OAAAA,CAAQ3B,cAAc,CAAA,GACxC,CAAA,EAAGf,CAAAA,CAAAA,CAAG,GACN,CAAA,EAAG0C,OAAAA,CAAQ3B,cAAc,CAAC,CAAC,EAAEf,CAAAA,CAAAA;AACnC,iBAAA;gBACA,OAAO6B,qBAAAA,CAAsBpB,SAAS4C,SAAAA,EAAWD,IAAAA,CAAAA;AACnD,YAAA,CAAA,CAAA,CAAA;AAGF,YAAA;AACF,QAAA;AACF,IAAA;IAEA,OAAOnB,IAAAA;AACT,CAAA;AAEA,MAAMG,qBAAqB,CAAC,EAAEC,IAAI,EAAkB,IAAM;AACxDoB,QAAAA,MAAAA,CAAAA,CAAOjB,GAAW,EAAA;YAChB,OAAOH,IAAI,CAACG,GAAAA,CAAI;AAClB,QAAA,CAAA;QAEAkB,GAAAA,CAAAA,CAAIlB,GAAW,EAAEjC,KAAW,EAAA;YAC1B8B,IAAI,CAACG,IAAI,GAAGjC,KAAAA;AACd,QAAA;KACF,CAAA;AAEA,uBAAeoD,SAAMnD,cAAAA,CAAAA;;;;"}
|
|
1
|
+
{"version":3,"file":"traverse-entity.js","sources":["../src/traverse-entity.ts"],"sourcesContent":["import { clone, isObject, isArray, isNil, curry } from 'lodash/fp';\n\nimport type { Attribute, AnyAttribute, Model, Data } from './types';\nimport { isRelationalAttribute, isMediaAttribute } from './content-types';\n\n/**\n * Execute promises in parallel but throw errors in array index order.\n */\nconst parallelWithOrderedErrors = async <T>(promises: Promise<T>[]): Promise<T[]> => {\n const results = await Promise.allSettled(promises);\n\n // Throw first error in array index order (matches sequential behavior)\n for (let i = 0; i < results.length; i += 1) {\n const result = results[i];\n if (result.status === 'rejected') {\n throw result.reason;\n }\n }\n\n return results.map((r) => (r as PromiseFulfilledResult<T>).value);\n};\n\nexport type VisitorUtils = ReturnType<typeof createVisitorUtils>;\n\nexport interface VisitorOptions {\n data: Record<string, unknown>;\n schema: Model;\n key: string;\n value: Data[keyof Data];\n attribute?: AnyAttribute;\n path: Path;\n getModel(uid: string): Model;\n parent?: Parent;\n /** Extra root-level keys allowed (e.g. registered input params). Only used when path.attribute === null. */\n allowedExtraRootKeys?: string[];\n}\n\nexport type Visitor = (visitorOptions: VisitorOptions, visitorUtils: VisitorUtils) => void;\n\nexport interface Path {\n raw: string | null;\n attribute: string | null;\n rawWithIndices?: string | null;\n}\n\nexport interface TraverseOptions {\n schema: Model;\n path?: Path;\n parent?: Parent;\n getModel(uid: string): Model;\n /** Extra root-level keys allowed (e.g. registered input params). Only used when path.attribute === null. */\n allowedExtraRootKeys?: string[];\n}\n\nexport interface Parent {\n attribute?: Attribute;\n key: string | null;\n path: Path;\n schema: Model;\n}\n\nconst traverseEntity = async (\n visitor: Visitor,\n options: TraverseOptions,\n entity: Data\n): Promise<Data> => {\n const {\n path = { raw: null, attribute: null, rawWithIndices: null },\n schema,\n getModel,\n allowedExtraRootKeys,\n } = options;\n\n let parent = options.parent;\n\n const traverseMorphRelationTarget = async (visitor: Visitor, path: Path, entry: Data) => {\n const targetSchema = getModel(entry.__type!);\n\n const traverseOptions: TraverseOptions = {\n schema: targetSchema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const traverseRelationTarget =\n (schema: Model) => async (visitor: Visitor, path: Path, entry: Data) => {\n const traverseOptions: TraverseOptions = {\n schema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const traverseMediaTarget = async (visitor: Visitor, path: Path, entry: Data) => {\n const targetSchemaUID = 'plugin::upload.file';\n const targetSchema = getModel(targetSchemaUID);\n\n const traverseOptions: TraverseOptions = {\n schema: targetSchema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const traverseComponent = async (visitor: Visitor, path: Path, schema: Model, entry: Data) => {\n const traverseOptions: TraverseOptions = {\n schema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const visitDynamicZoneEntry = async (visitor: Visitor, path: Path, entry: Data) => {\n // A dynamic zone array can contain a `null` entry (for example a relation\n // created inline inside a dynamic zone component can leave a null item).\n // Reading `__component` on it crashed traversal; pass nil entries through\n // untouched, consistent with how `traverseEntity` ends recursion on nil. (#24303)\n if (isNil(entry)) {\n return entry;\n }\n\n const targetSchema = getModel(entry.__component!);\n const traverseOptions: TraverseOptions = {\n schema: targetSchema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n // End recursion\n if (!isObject(entity) || isNil(schema)) {\n return entity;\n }\n\n // Don't mutate the original entity object\n // only clone at 1st level as the next level will get clone when traversed\n const copy = clone(entity);\n const visitorUtils = createVisitorUtils({ data: copy });\n\n const keys = Object.keys(copy);\n for (let i = 0; i < keys.length; i += 1) {\n const key = keys[i];\n // Retrieve the attribute definition associated to the key from the schema\n const attribute = schema.attributes[key] as AnyAttribute | undefined;\n\n const newPath = { ...path };\n\n newPath.raw = isNil(path.raw) ? key : `${path.raw}.${key}`;\n newPath.rawWithIndices = isNil(path.rawWithIndices) ? key : `${path.rawWithIndices}.${key}`;\n\n if (!isNil(attribute)) {\n newPath.attribute = isNil(path.attribute) ? key : `${path.attribute}.${key}`;\n }\n\n // Visit the current attribute\n const visitorOptions: VisitorOptions = {\n data: copy,\n schema,\n key,\n value: copy[key],\n attribute,\n path: newPath,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n // Awaited only when the visitor actually returns something thenable. Most visitors\n // finish synchronously for most keys — a scalar is not a relation, so the relation\n // visitor returns immediately — and `await` on a non-thenable still allocates a\n // promise and defers the rest of the loop to a microtask. Over every key of every\n // node of every entity in a page that was the largest single source of promise churn.\n const visited = visitor(visitorOptions, visitorUtils) as unknown;\n\n if (visited != null && typeof (visited as PromiseLike<void>).then === 'function') {\n await visited;\n }\n\n // Extract the value for the current key (after calling the visitor)\n const value = copy[key];\n\n // Ignore Nil values or attributes\n if (isNil(value) || isNil(attribute)) {\n continue;\n }\n\n if (isRelationalAttribute(attribute)) {\n parent = { schema, key, attribute, path: newPath };\n const isMorphRelation = attribute.relation.toLowerCase().startsWith('morph');\n\n const method = isMorphRelation\n ? traverseMorphRelationTarget\n : traverseRelationTarget(getModel(attribute.target!));\n\n if (isArray(value)) {\n // Process array items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return method(visitor, arrayPath, item);\n })\n );\n } else {\n copy[key] = await method(visitor, newPath, value as Data);\n }\n\n continue;\n }\n\n if (isMediaAttribute(attribute)) {\n parent = { schema, key, attribute, path: newPath };\n\n if (isArray(value)) {\n // Process media array items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return traverseMediaTarget(visitor, arrayPath, item);\n })\n );\n } else {\n copy[key] = await traverseMediaTarget(visitor, newPath, value as Data);\n }\n\n continue;\n }\n\n if (attribute.type === 'component') {\n parent = { schema, key, attribute, path: newPath };\n const targetSchema = getModel(attribute.component);\n\n if (isArray(value)) {\n // Process component array items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return traverseComponent(visitor, arrayPath, targetSchema, item);\n })\n );\n } else {\n copy[key] = await traverseComponent(visitor, newPath, targetSchema, value as Data);\n }\n\n continue;\n }\n\n if (attribute.type === 'dynamiczone' && isArray(value)) {\n parent = { schema, key, attribute, path: newPath };\n\n // Process dynamic zone items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return visitDynamicZoneEntry(visitor, arrayPath, item);\n })\n );\n\n continue;\n }\n }\n\n return copy;\n};\n\nconst createVisitorUtils = ({ data }: { data: Data }) => ({\n remove(key: string) {\n delete data[key];\n },\n\n set(key: string, value: Data) {\n data[key] = value;\n },\n});\n\nexport default curry(traverseEntity);\n"],"names":["parallelWithOrderedErrors","promises","results","Promise","allSettled","i","length","result","status","reason","map","r","value","traverseEntity","visitor","options","entity","path","raw","attribute","rawWithIndices","schema","getModel","allowedExtraRootKeys","parent","traverseMorphRelationTarget","entry","targetSchema","__type","traverseOptions","traverseRelationTarget","traverseMediaTarget","targetSchemaUID","traverseComponent","visitDynamicZoneEntry","isNil","__component","isObject","copy","clone","visitorUtils","createVisitorUtils","data","keys","Object","key","attributes","newPath","visitorOptions","visited","then","isRelationalAttribute","isMorphRelation","relation","toLowerCase","startsWith","method","target","isArray","item","arrayPath","isMediaAttribute","type","component","remove","set","curry"],"mappings":"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"}
|
package/dist/traverse-entity.mjs
CHANGED
|
@@ -117,7 +117,15 @@ const traverseEntity$1 = async (visitor, options, entity)=>{
|
|
|
117
117
|
parent,
|
|
118
118
|
allowedExtraRootKeys
|
|
119
119
|
};
|
|
120
|
-
|
|
120
|
+
// Awaited only when the visitor actually returns something thenable. Most visitors
|
|
121
|
+
// finish synchronously for most keys — a scalar is not a relation, so the relation
|
|
122
|
+
// visitor returns immediately — and `await` on a non-thenable still allocates a
|
|
123
|
+
// promise and defers the rest of the loop to a microtask. Over every key of every
|
|
124
|
+
// node of every entity in a page that was the largest single source of promise churn.
|
|
125
|
+
const visited = visitor(visitorOptions, visitorUtils);
|
|
126
|
+
if (visited != null && typeof visited.then === 'function') {
|
|
127
|
+
await visited;
|
|
128
|
+
}
|
|
121
129
|
// Extract the value for the current key (after calling the visitor)
|
|
122
130
|
const value = copy[key];
|
|
123
131
|
// Ignore Nil values or attributes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"traverse-entity.mjs","sources":["../src/traverse-entity.ts"],"sourcesContent":["import { clone, isObject, isArray, isNil, curry } from 'lodash/fp';\n\nimport type { Attribute, AnyAttribute, Model, Data } from './types';\nimport { isRelationalAttribute, isMediaAttribute } from './content-types';\n\n/**\n * Execute promises in parallel but throw errors in array index order.\n */\nconst parallelWithOrderedErrors = async <T>(promises: Promise<T>[]): Promise<T[]> => {\n const results = await Promise.allSettled(promises);\n\n // Throw first error in array index order (matches sequential behavior)\n for (let i = 0; i < results.length; i += 1) {\n const result = results[i];\n if (result.status === 'rejected') {\n throw result.reason;\n }\n }\n\n return results.map((r) => (r as PromiseFulfilledResult<T>).value);\n};\n\nexport type VisitorUtils = ReturnType<typeof createVisitorUtils>;\n\nexport interface VisitorOptions {\n data: Record<string, unknown>;\n schema: Model;\n key: string;\n value: Data[keyof Data];\n attribute?: AnyAttribute;\n path: Path;\n getModel(uid: string): Model;\n parent?: Parent;\n /** Extra root-level keys allowed (e.g. registered input params). Only used when path.attribute === null. */\n allowedExtraRootKeys?: string[];\n}\n\nexport type Visitor = (visitorOptions: VisitorOptions, visitorUtils: VisitorUtils) => void;\n\nexport interface Path {\n raw: string | null;\n attribute: string | null;\n rawWithIndices?: string | null;\n}\n\nexport interface TraverseOptions {\n schema: Model;\n path?: Path;\n parent?: Parent;\n getModel(uid: string): Model;\n /** Extra root-level keys allowed (e.g. registered input params). Only used when path.attribute === null. */\n allowedExtraRootKeys?: string[];\n}\n\nexport interface Parent {\n attribute?: Attribute;\n key: string | null;\n path: Path;\n schema: Model;\n}\n\nconst traverseEntity = async (\n visitor: Visitor,\n options: TraverseOptions,\n entity: Data\n): Promise<Data> => {\n const {\n path = { raw: null, attribute: null, rawWithIndices: null },\n schema,\n getModel,\n allowedExtraRootKeys,\n } = options;\n\n let parent = options.parent;\n\n const traverseMorphRelationTarget = async (visitor: Visitor, path: Path, entry: Data) => {\n const targetSchema = getModel(entry.__type!);\n\n const traverseOptions: TraverseOptions = {\n schema: targetSchema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const traverseRelationTarget =\n (schema: Model) => async (visitor: Visitor, path: Path, entry: Data) => {\n const traverseOptions: TraverseOptions = {\n schema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const traverseMediaTarget = async (visitor: Visitor, path: Path, entry: Data) => {\n const targetSchemaUID = 'plugin::upload.file';\n const targetSchema = getModel(targetSchemaUID);\n\n const traverseOptions: TraverseOptions = {\n schema: targetSchema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const traverseComponent = async (visitor: Visitor, path: Path, schema: Model, entry: Data) => {\n const traverseOptions: TraverseOptions = {\n schema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const visitDynamicZoneEntry = async (visitor: Visitor, path: Path, entry: Data) => {\n // A dynamic zone array can contain a `null` entry (for example a relation\n // created inline inside a dynamic zone component can leave a null item).\n // Reading `__component` on it crashed traversal; pass nil entries through\n // untouched, consistent with how `traverseEntity` ends recursion on nil. (#24303)\n if (isNil(entry)) {\n return entry;\n }\n\n const targetSchema = getModel(entry.__component!);\n const traverseOptions: TraverseOptions = {\n schema: targetSchema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n // End recursion\n if (!isObject(entity) || isNil(schema)) {\n return entity;\n }\n\n // Don't mutate the original entity object\n // only clone at 1st level as the next level will get clone when traversed\n const copy = clone(entity);\n const visitorUtils = createVisitorUtils({ data: copy });\n\n const keys = Object.keys(copy);\n for (let i = 0; i < keys.length; i += 1) {\n const key = keys[i];\n // Retrieve the attribute definition associated to the key from the schema\n const attribute = schema.attributes[key] as AnyAttribute | undefined;\n\n const newPath = { ...path };\n\n newPath.raw = isNil(path.raw) ? key : `${path.raw}.${key}`;\n newPath.rawWithIndices = isNil(path.rawWithIndices) ? key : `${path.rawWithIndices}.${key}`;\n\n if (!isNil(attribute)) {\n newPath.attribute = isNil(path.attribute) ? key : `${path.attribute}.${key}`;\n }\n\n // Visit the current attribute\n const visitorOptions: VisitorOptions = {\n data: copy,\n schema,\n key,\n value: copy[key],\n attribute,\n path: newPath,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n await visitor(visitorOptions, visitorUtils);\n\n // Extract the value for the current key (after calling the visitor)\n const value = copy[key];\n\n // Ignore Nil values or attributes\n if (isNil(value) || isNil(attribute)) {\n continue;\n }\n\n if (isRelationalAttribute(attribute)) {\n parent = { schema, key, attribute, path: newPath };\n const isMorphRelation = attribute.relation.toLowerCase().startsWith('morph');\n\n const method = isMorphRelation\n ? traverseMorphRelationTarget\n : traverseRelationTarget(getModel(attribute.target!));\n\n if (isArray(value)) {\n // Process array items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return method(visitor, arrayPath, item);\n })\n );\n } else {\n copy[key] = await method(visitor, newPath, value as Data);\n }\n\n continue;\n }\n\n if (isMediaAttribute(attribute)) {\n parent = { schema, key, attribute, path: newPath };\n\n if (isArray(value)) {\n // Process media array items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return traverseMediaTarget(visitor, arrayPath, item);\n })\n );\n } else {\n copy[key] = await traverseMediaTarget(visitor, newPath, value as Data);\n }\n\n continue;\n }\n\n if (attribute.type === 'component') {\n parent = { schema, key, attribute, path: newPath };\n const targetSchema = getModel(attribute.component);\n\n if (isArray(value)) {\n // Process component array items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return traverseComponent(visitor, arrayPath, targetSchema, item);\n })\n );\n } else {\n copy[key] = await traverseComponent(visitor, newPath, targetSchema, value as Data);\n }\n\n continue;\n }\n\n if (attribute.type === 'dynamiczone' && isArray(value)) {\n parent = { schema, key, attribute, path: newPath };\n\n // Process dynamic zone items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return visitDynamicZoneEntry(visitor, arrayPath, item);\n })\n );\n\n continue;\n }\n }\n\n return copy;\n};\n\nconst createVisitorUtils = ({ data }: { data: Data }) => ({\n remove(key: string) {\n delete data[key];\n },\n\n set(key: string, value: Data) {\n data[key] = value;\n },\n});\n\nexport default curry(traverseEntity);\n"],"names":["parallelWithOrderedErrors","promises","results","Promise","allSettled","i","length","result","status","reason","map","r","value","traverseEntity","visitor","options","entity","path","raw","attribute","rawWithIndices","schema","getModel","allowedExtraRootKeys","parent","traverseMorphRelationTarget","entry","targetSchema","__type","traverseOptions","traverseRelationTarget","traverseMediaTarget","targetSchemaUID","traverseComponent","visitDynamicZoneEntry","isNil","__component","isObject","copy","clone","visitorUtils","createVisitorUtils","data","keys","Object","key","attributes","newPath","visitorOptions","isRelationalAttribute","isMorphRelation","relation","toLowerCase","startsWith","method","target","isArray","item","arrayPath","isMediaAttribute","type","component","remove","set","curry"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"traverse-entity.mjs","sources":["../src/traverse-entity.ts"],"sourcesContent":["import { clone, isObject, isArray, isNil, curry } from 'lodash/fp';\n\nimport type { Attribute, AnyAttribute, Model, Data } from './types';\nimport { isRelationalAttribute, isMediaAttribute } from './content-types';\n\n/**\n * Execute promises in parallel but throw errors in array index order.\n */\nconst parallelWithOrderedErrors = async <T>(promises: Promise<T>[]): Promise<T[]> => {\n const results = await Promise.allSettled(promises);\n\n // Throw first error in array index order (matches sequential behavior)\n for (let i = 0; i < results.length; i += 1) {\n const result = results[i];\n if (result.status === 'rejected') {\n throw result.reason;\n }\n }\n\n return results.map((r) => (r as PromiseFulfilledResult<T>).value);\n};\n\nexport type VisitorUtils = ReturnType<typeof createVisitorUtils>;\n\nexport interface VisitorOptions {\n data: Record<string, unknown>;\n schema: Model;\n key: string;\n value: Data[keyof Data];\n attribute?: AnyAttribute;\n path: Path;\n getModel(uid: string): Model;\n parent?: Parent;\n /** Extra root-level keys allowed (e.g. registered input params). Only used when path.attribute === null. */\n allowedExtraRootKeys?: string[];\n}\n\nexport type Visitor = (visitorOptions: VisitorOptions, visitorUtils: VisitorUtils) => void;\n\nexport interface Path {\n raw: string | null;\n attribute: string | null;\n rawWithIndices?: string | null;\n}\n\nexport interface TraverseOptions {\n schema: Model;\n path?: Path;\n parent?: Parent;\n getModel(uid: string): Model;\n /** Extra root-level keys allowed (e.g. registered input params). Only used when path.attribute === null. */\n allowedExtraRootKeys?: string[];\n}\n\nexport interface Parent {\n attribute?: Attribute;\n key: string | null;\n path: Path;\n schema: Model;\n}\n\nconst traverseEntity = async (\n visitor: Visitor,\n options: TraverseOptions,\n entity: Data\n): Promise<Data> => {\n const {\n path = { raw: null, attribute: null, rawWithIndices: null },\n schema,\n getModel,\n allowedExtraRootKeys,\n } = options;\n\n let parent = options.parent;\n\n const traverseMorphRelationTarget = async (visitor: Visitor, path: Path, entry: Data) => {\n const targetSchema = getModel(entry.__type!);\n\n const traverseOptions: TraverseOptions = {\n schema: targetSchema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const traverseRelationTarget =\n (schema: Model) => async (visitor: Visitor, path: Path, entry: Data) => {\n const traverseOptions: TraverseOptions = {\n schema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const traverseMediaTarget = async (visitor: Visitor, path: Path, entry: Data) => {\n const targetSchemaUID = 'plugin::upload.file';\n const targetSchema = getModel(targetSchemaUID);\n\n const traverseOptions: TraverseOptions = {\n schema: targetSchema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const traverseComponent = async (visitor: Visitor, path: Path, schema: Model, entry: Data) => {\n const traverseOptions: TraverseOptions = {\n schema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n const visitDynamicZoneEntry = async (visitor: Visitor, path: Path, entry: Data) => {\n // A dynamic zone array can contain a `null` entry (for example a relation\n // created inline inside a dynamic zone component can leave a null item).\n // Reading `__component` on it crashed traversal; pass nil entries through\n // untouched, consistent with how `traverseEntity` ends recursion on nil. (#24303)\n if (isNil(entry)) {\n return entry;\n }\n\n const targetSchema = getModel(entry.__component!);\n const traverseOptions: TraverseOptions = {\n schema: targetSchema,\n path,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n return traverseEntity(visitor, traverseOptions, entry);\n };\n\n // End recursion\n if (!isObject(entity) || isNil(schema)) {\n return entity;\n }\n\n // Don't mutate the original entity object\n // only clone at 1st level as the next level will get clone when traversed\n const copy = clone(entity);\n const visitorUtils = createVisitorUtils({ data: copy });\n\n const keys = Object.keys(copy);\n for (let i = 0; i < keys.length; i += 1) {\n const key = keys[i];\n // Retrieve the attribute definition associated to the key from the schema\n const attribute = schema.attributes[key] as AnyAttribute | undefined;\n\n const newPath = { ...path };\n\n newPath.raw = isNil(path.raw) ? key : `${path.raw}.${key}`;\n newPath.rawWithIndices = isNil(path.rawWithIndices) ? key : `${path.rawWithIndices}.${key}`;\n\n if (!isNil(attribute)) {\n newPath.attribute = isNil(path.attribute) ? key : `${path.attribute}.${key}`;\n }\n\n // Visit the current attribute\n const visitorOptions: VisitorOptions = {\n data: copy,\n schema,\n key,\n value: copy[key],\n attribute,\n path: newPath,\n getModel,\n parent,\n allowedExtraRootKeys,\n };\n\n // Awaited only when the visitor actually returns something thenable. Most visitors\n // finish synchronously for most keys — a scalar is not a relation, so the relation\n // visitor returns immediately — and `await` on a non-thenable still allocates a\n // promise and defers the rest of the loop to a microtask. Over every key of every\n // node of every entity in a page that was the largest single source of promise churn.\n const visited = visitor(visitorOptions, visitorUtils) as unknown;\n\n if (visited != null && typeof (visited as PromiseLike<void>).then === 'function') {\n await visited;\n }\n\n // Extract the value for the current key (after calling the visitor)\n const value = copy[key];\n\n // Ignore Nil values or attributes\n if (isNil(value) || isNil(attribute)) {\n continue;\n }\n\n if (isRelationalAttribute(attribute)) {\n parent = { schema, key, attribute, path: newPath };\n const isMorphRelation = attribute.relation.toLowerCase().startsWith('morph');\n\n const method = isMorphRelation\n ? traverseMorphRelationTarget\n : traverseRelationTarget(getModel(attribute.target!));\n\n if (isArray(value)) {\n // Process array items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return method(visitor, arrayPath, item);\n })\n );\n } else {\n copy[key] = await method(visitor, newPath, value as Data);\n }\n\n continue;\n }\n\n if (isMediaAttribute(attribute)) {\n parent = { schema, key, attribute, path: newPath };\n\n if (isArray(value)) {\n // Process media array items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return traverseMediaTarget(visitor, arrayPath, item);\n })\n );\n } else {\n copy[key] = await traverseMediaTarget(visitor, newPath, value as Data);\n }\n\n continue;\n }\n\n if (attribute.type === 'component') {\n parent = { schema, key, attribute, path: newPath };\n const targetSchema = getModel(attribute.component);\n\n if (isArray(value)) {\n // Process component array items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return traverseComponent(visitor, arrayPath, targetSchema, item);\n })\n );\n } else {\n copy[key] = await traverseComponent(visitor, newPath, targetSchema, value as Data);\n }\n\n continue;\n }\n\n if (attribute.type === 'dynamiczone' && isArray(value)) {\n parent = { schema, key, attribute, path: newPath };\n\n // Process dynamic zone items in parallel with ordered error handling\n copy[key] = await parallelWithOrderedErrors(\n value.map((item, i) => {\n const arrayPath = {\n ...newPath,\n rawWithIndices: isNil(newPath.rawWithIndices)\n ? `${i}`\n : `${newPath.rawWithIndices}.${i}`,\n };\n return visitDynamicZoneEntry(visitor, arrayPath, item);\n })\n );\n\n continue;\n }\n }\n\n return copy;\n};\n\nconst createVisitorUtils = ({ data }: { data: Data }) => ({\n remove(key: string) {\n delete data[key];\n },\n\n set(key: string, value: Data) {\n data[key] = value;\n },\n});\n\nexport default curry(traverseEntity);\n"],"names":["parallelWithOrderedErrors","promises","results","Promise","allSettled","i","length","result","status","reason","map","r","value","traverseEntity","visitor","options","entity","path","raw","attribute","rawWithIndices","schema","getModel","allowedExtraRootKeys","parent","traverseMorphRelationTarget","entry","targetSchema","__type","traverseOptions","traverseRelationTarget","traverseMediaTarget","targetSchemaUID","traverseComponent","visitDynamicZoneEntry","isNil","__component","isObject","copy","clone","visitorUtils","createVisitorUtils","data","keys","Object","key","attributes","newPath","visitorOptions","visited","then","isRelationalAttribute","isMorphRelation","relation","toLowerCase","startsWith","method","target","isArray","item","arrayPath","isMediaAttribute","type","component","remove","set","curry"],"mappings":"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"}
|
|
@@ -415,29 +415,22 @@ const validatePopulate = utils.asyncCurry(async (ctx, populate, includes)=>{
|
|
|
415
415
|
}
|
|
416
416
|
// Ensure count is a boolean
|
|
417
417
|
if (key === 'count') {
|
|
418
|
-
|
|
419
|
-
parseType({
|
|
420
|
-
type: 'boolean',
|
|
421
|
-
value
|
|
422
|
-
});
|
|
418
|
+
if (parseType.isBooleanLike(value)) {
|
|
423
419
|
return;
|
|
424
|
-
} catch {
|
|
425
|
-
utils.throwInvalidKey({
|
|
426
|
-
key,
|
|
427
|
-
path: path.attribute
|
|
428
|
-
});
|
|
429
420
|
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
parseType({
|
|
434
|
-
type: 'boolean',
|
|
435
|
-
value: key
|
|
421
|
+
utils.throwInvalidKey({
|
|
422
|
+
key,
|
|
423
|
+
path: path.attribute
|
|
436
424
|
});
|
|
437
|
-
|
|
425
|
+
}
|
|
426
|
+
// Allowed boolean-like keywords should be ignored.
|
|
427
|
+
//
|
|
428
|
+
// Asked as a predicate rather than by catching `parseType`'s error: this runs
|
|
429
|
+
// once per populate key, and almost no key is boolean-like, so the previous
|
|
430
|
+
// try/catch built and threw away an Error — including a stack capture — on
|
|
431
|
+
// essentially every key of every populate tree of every request.
|
|
432
|
+
if (parseType.isBooleanLike(key)) {
|
|
438
433
|
return;
|
|
439
|
-
} catch {
|
|
440
|
-
// Continue, because it's not a boolean-like
|
|
441
434
|
}
|
|
442
435
|
// Handle nested `sort` validation with custom or default traversals
|
|
443
436
|
if (key === 'sort') {
|