@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.
Files changed (62) hide show
  1. package/dist/content-types.d.ts.map +1 -1
  2. package/dist/content-types.js +39 -2
  3. package/dist/content-types.js.map +1 -1
  4. package/dist/content-types.mjs +39 -2
  5. package/dist/content-types.mjs.map +1 -1
  6. package/dist/convert-query-params.js +4 -4
  7. package/dist/convert-query-params.js.map +1 -1
  8. package/dist/errors.d.ts +1 -1
  9. package/dist/errors.d.ts.map +1 -1
  10. package/dist/errors.js.map +1 -1
  11. package/dist/errors.mjs.map +1 -1
  12. package/dist/index.js +1 -1
  13. package/dist/install-id.js +1 -1
  14. package/dist/install-id.js.map +1 -1
  15. package/dist/install-id.mjs +1 -1
  16. package/dist/install-id.mjs.map +1 -1
  17. package/dist/pagination.d.ts +1 -1
  18. package/dist/pagination.d.ts.map +1 -1
  19. package/dist/pagination.js +12 -5
  20. package/dist/pagination.js.map +1 -1
  21. package/dist/pagination.mjs +12 -5
  22. package/dist/pagination.mjs.map +1 -1
  23. package/dist/parse-type.d.ts +13 -0
  24. package/dist/parse-type.d.ts.map +1 -1
  25. package/dist/parse-type.js +44 -15
  26. package/dist/parse-type.js.map +1 -1
  27. package/dist/parse-type.mjs +41 -15
  28. package/dist/parse-type.mjs.map +1 -1
  29. package/dist/policy.js +1 -1
  30. package/dist/policy.js.map +1 -1
  31. package/dist/policy.mjs +1 -1
  32. package/dist/policy.mjs.map +1 -1
  33. package/dist/route-serialization.d.ts +1 -1
  34. package/dist/route-serialization.d.ts.map +1 -1
  35. package/dist/route-serialization.js +1 -1
  36. package/dist/route-serialization.js.map +1 -1
  37. package/dist/route-serialization.mjs +1 -1
  38. package/dist/route-serialization.mjs.map +1 -1
  39. package/dist/sanitize/index.js +3 -1
  40. package/dist/sanitize/index.js.map +1 -1
  41. package/dist/sanitize/index.mjs +3 -1
  42. package/dist/sanitize/index.mjs.map +1 -1
  43. package/dist/sanitize/sanitizers.d.ts.map +1 -1
  44. package/dist/sanitize/sanitizers.js +5 -3
  45. package/dist/sanitize/sanitizers.js.map +1 -1
  46. package/dist/sanitize/sanitizers.mjs +5 -3
  47. package/dist/sanitize/sanitizers.mjs.map +1 -1
  48. package/dist/sanitize/visitors/remove-restricted-relations.d.ts.map +1 -1
  49. package/dist/sanitize/visitors/remove-restricted-relations.js +128 -86
  50. package/dist/sanitize/visitors/remove-restricted-relations.js.map +1 -1
  51. package/dist/sanitize/visitors/remove-restricted-relations.mjs +128 -86
  52. package/dist/sanitize/visitors/remove-restricted-relations.mjs.map +1 -1
  53. package/dist/traverse-entity.d.ts.map +1 -1
  54. package/dist/traverse-entity.js +9 -1
  55. package/dist/traverse-entity.js.map +1 -1
  56. package/dist/traverse-entity.mjs +9 -1
  57. package/dist/traverse-entity.mjs.map +1 -1
  58. package/dist/validate/validators.js +12 -19
  59. package/dist/validate/validators.js.map +1 -1
  60. package/dist/validate/validators.mjs +13 -20
  61. package/dist/validate/validators.mjs.map +1 -1
  62. 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)=>async ({ data, key, attribute, schema }, { remove, set })=>{
10
- if (!attribute) {
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
- const isRelation = attribute.type === 'relation';
14
- if (!isRelation) {
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
- const handleMorphRelation = async ()=>{
18
- const elements = data[key];
19
- if (!elements) {
20
- return;
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 ('connect' in elements || 'set' in elements || 'disconnect' in elements) {
23
- const newValue = {};
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
- const handleMorphElements = async (elements)=>{
61
- const allowedElements = [];
62
- if (!isArray(elements)) {
63
- return allowedElements;
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
- for (const element of elements){
66
- if (!isObject(element) || !('__type' in element)) {
67
- continue;
68
- }
69
- const scopes = ACTIONS_TO_VERIFY.map((action)=>`${element.__type}.${action}`);
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 handleRegularRelation = async ()=>{
78
- const scopes = ACTIONS_TO_VERIFY.map((action)=>`${attribute.target}.${action}`);
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
- // If the authenticated user don't have access to any of the scopes, then remove the field
81
- if (!isAllowed) {
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
- // Creator relations
95
- if (isCreatorRelation && schema.options?.populateCreatorFields) {
96
- // do nothing
97
- return;
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
- // Regular relations
100
- await handleRegularRelation();
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
- try {
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;AA6OD,QAAA,MAAM,kBAAkB,GAAI,UAAU;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE;gBACtC,MAAM;aAIT,MAAM,SAAS,IAAI;CAG5B,CAAC;;AAEH,wBAAqC"}
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"}
@@ -119,7 +119,15 @@ const traverseEntity = async (visitor, options, entity)=>{
119
119
  parent,
120
120
  allowedExtraRootKeys
121
121
  };
122
- await visitor(visitorOptions, visitorUtils);
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":"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"}
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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"}
@@ -117,7 +117,15 @@ const traverseEntity$1 = async (visitor, options, entity)=>{
117
117
  parent,
118
118
  allowedExtraRootKeys
119
119
  };
120
- await visitor(visitorOptions, visitorUtils);
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
- try {
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
- // Allowed boolean-like keywords should be ignored
432
- try {
433
- parseType({
434
- type: 'boolean',
435
- value: key
421
+ utils.throwInvalidKey({
422
+ key,
423
+ path: path.attribute
436
424
  });
437
- // Key is an allowed boolean-like keyword, skipping validation...
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') {