@strapi/utils 5.12.0 → 5.12.2

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 (233) hide show
  1. package/dist/async.js +28 -0
  2. package/dist/async.js.map +1 -0
  3. package/dist/async.mjs +24 -0
  4. package/dist/async.mjs.map +1 -0
  5. package/dist/content-types.js +201 -0
  6. package/dist/content-types.js.map +1 -0
  7. package/dist/content-types.mjs +167 -0
  8. package/dist/content-types.mjs.map +1 -0
  9. package/dist/convert-query-params.js +512 -0
  10. package/dist/convert-query-params.js.map +1 -0
  11. package/dist/convert-query-params.mjs +510 -0
  12. package/dist/convert-query-params.mjs.map +1 -0
  13. package/dist/env-helper.js +81 -0
  14. package/dist/env-helper.js.map +1 -0
  15. package/dist/env-helper.mjs +79 -0
  16. package/dist/env-helper.mjs.map +1 -0
  17. package/dist/errors.js +104 -0
  18. package/dist/errors.js.map +1 -0
  19. package/dist/errors.mjs +88 -0
  20. package/dist/errors.mjs.map +1 -0
  21. package/dist/file.js +57 -0
  22. package/dist/file.js.map +1 -0
  23. package/dist/file.mjs +50 -0
  24. package/dist/file.mjs.map +1 -0
  25. package/dist/format-yup-error.js +19 -0
  26. package/dist/format-yup-error.js.map +1 -0
  27. package/dist/format-yup-error.mjs +17 -0
  28. package/dist/format-yup-error.mjs.map +1 -0
  29. package/dist/hooks.js +86 -0
  30. package/dist/hooks.js.map +1 -0
  31. package/dist/hooks.mjs +80 -0
  32. package/dist/hooks.mjs.map +1 -0
  33. package/dist/import-default.js +9 -0
  34. package/dist/import-default.js.map +1 -0
  35. package/dist/import-default.mjs +7 -0
  36. package/dist/import-default.mjs.map +1 -0
  37. package/dist/index.js +54 -4358
  38. package/dist/index.js.map +1 -1
  39. package/dist/index.mjs +48 -4317
  40. package/dist/index.mjs.map +1 -1
  41. package/dist/machine-id.js +17 -0
  42. package/dist/machine-id.js.map +1 -0
  43. package/dist/machine-id.mjs +15 -0
  44. package/dist/machine-id.mjs.map +1 -0
  45. package/dist/operators.js +79 -0
  46. package/dist/operators.js.map +1 -0
  47. package/dist/operators.mjs +76 -0
  48. package/dist/operators.mjs.map +1 -0
  49. package/dist/package-manager.js +36 -0
  50. package/dist/package-manager.js.map +1 -0
  51. package/dist/package-manager.mjs +33 -0
  52. package/dist/package-manager.mjs.map +1 -0
  53. package/dist/pagination.js +163 -0
  54. package/dist/pagination.js.map +1 -0
  55. package/dist/pagination.mjs +159 -0
  56. package/dist/pagination.mjs.map +1 -0
  57. package/dist/parse-type.js +140 -0
  58. package/dist/parse-type.js.map +1 -0
  59. package/dist/parse-type.mjs +118 -0
  60. package/dist/parse-type.mjs.map +1 -0
  61. package/dist/policy.js +33 -0
  62. package/dist/policy.js.map +1 -0
  63. package/dist/policy.mjs +30 -0
  64. package/dist/policy.mjs.map +1 -0
  65. package/dist/primitives/arrays.js +7 -0
  66. package/dist/primitives/arrays.js.map +1 -0
  67. package/dist/primitives/arrays.mjs +5 -0
  68. package/dist/primitives/arrays.mjs.map +1 -0
  69. package/dist/primitives/dates.js +11 -0
  70. package/dist/primitives/dates.js.map +1 -0
  71. package/dist/primitives/dates.mjs +9 -0
  72. package/dist/primitives/dates.mjs.map +1 -0
  73. package/dist/primitives/objects.js +13 -0
  74. package/dist/primitives/objects.js.map +1 -0
  75. package/dist/primitives/objects.mjs +11 -0
  76. package/dist/primitives/objects.mjs.map +1 -0
  77. package/dist/primitives/strings.js +49 -0
  78. package/dist/primitives/strings.js.map +1 -0
  79. package/dist/primitives/strings.mjs +38 -0
  80. package/dist/primitives/strings.mjs.map +1 -0
  81. package/dist/print-value.js +42 -0
  82. package/dist/print-value.js.map +1 -0
  83. package/dist/print-value.mjs +40 -0
  84. package/dist/print-value.mjs.map +1 -0
  85. package/dist/provider-factory.js +82 -0
  86. package/dist/provider-factory.js.map +1 -0
  87. package/dist/provider-factory.mjs +80 -0
  88. package/dist/provider-factory.mjs.map +1 -0
  89. package/dist/relations.js +54 -0
  90. package/dist/relations.js.map +1 -0
  91. package/dist/relations.mjs +45 -0
  92. package/dist/relations.mjs.map +1 -0
  93. package/dist/sanitize/index.js +195 -0
  94. package/dist/sanitize/index.js.map +1 -0
  95. package/dist/sanitize/index.mjs +194 -0
  96. package/dist/sanitize/index.mjs.map +1 -0
  97. package/dist/sanitize/sanitizers.js +173 -0
  98. package/dist/sanitize/sanitizers.js.map +1 -0
  99. package/dist/sanitize/sanitizers.mjs +166 -0
  100. package/dist/sanitize/sanitizers.mjs.map +1 -0
  101. package/dist/sanitize/visitors/expand-wildcard-populate.js +20 -0
  102. package/dist/sanitize/visitors/expand-wildcard-populate.js.map +1 -0
  103. package/dist/sanitize/visitors/expand-wildcard-populate.mjs +18 -0
  104. package/dist/sanitize/visitors/expand-wildcard-populate.mjs.map +1 -0
  105. package/dist/sanitize/visitors/index.js +22 -0
  106. package/dist/sanitize/visitors/index.js.map +1 -0
  107. package/dist/sanitize/visitors/index.mjs +9 -0
  108. package/dist/sanitize/visitors/index.mjs.map +1 -0
  109. package/dist/sanitize/visitors/remove-disallowed-fields.js +87 -0
  110. package/dist/sanitize/visitors/remove-disallowed-fields.js.map +1 -0
  111. package/dist/sanitize/visitors/remove-disallowed-fields.mjs +85 -0
  112. package/dist/sanitize/visitors/remove-disallowed-fields.mjs.map +1 -0
  113. package/dist/sanitize/visitors/remove-dynamic-zones.js +12 -0
  114. package/dist/sanitize/visitors/remove-dynamic-zones.js.map +1 -0
  115. package/dist/sanitize/visitors/remove-dynamic-zones.mjs +10 -0
  116. package/dist/sanitize/visitors/remove-dynamic-zones.mjs.map +1 -0
  117. package/dist/sanitize/visitors/remove-morph-to-relations.js +12 -0
  118. package/dist/sanitize/visitors/remove-morph-to-relations.js.map +1 -0
  119. package/dist/sanitize/visitors/remove-morph-to-relations.mjs +10 -0
  120. package/dist/sanitize/visitors/remove-morph-to-relations.mjs.map +1 -0
  121. package/dist/sanitize/visitors/remove-password.js +10 -0
  122. package/dist/sanitize/visitors/remove-password.js.map +1 -0
  123. package/dist/sanitize/visitors/remove-password.mjs +8 -0
  124. package/dist/sanitize/visitors/remove-password.mjs.map +1 -0
  125. package/dist/sanitize/visitors/remove-private.js +16 -0
  126. package/dist/sanitize/visitors/remove-private.js.map +1 -0
  127. package/dist/sanitize/visitors/remove-private.mjs +14 -0
  128. package/dist/sanitize/visitors/remove-private.mjs.map +1 -0
  129. package/dist/sanitize/visitors/remove-restricted-fields.js +28 -0
  130. package/dist/sanitize/visitors/remove-restricted-fields.js.map +1 -0
  131. package/dist/sanitize/visitors/remove-restricted-fields.mjs +26 -0
  132. package/dist/sanitize/visitors/remove-restricted-fields.mjs.map +1 -0
  133. package/dist/sanitize/visitors/remove-restricted-relations.js +116 -0
  134. package/dist/sanitize/visitors/remove-restricted-relations.js.map +1 -0
  135. package/dist/sanitize/visitors/remove-restricted-relations.mjs +114 -0
  136. package/dist/sanitize/visitors/remove-restricted-relations.mjs.map +1 -0
  137. package/dist/set-creator-fields.js +18 -0
  138. package/dist/set-creator-fields.js.map +1 -0
  139. package/dist/set-creator-fields.mjs +16 -0
  140. package/dist/set-creator-fields.mjs.map +1 -0
  141. package/dist/template.js +18 -0
  142. package/dist/template.js.map +1 -0
  143. package/dist/template.mjs +15 -0
  144. package/dist/template.mjs.map +1 -0
  145. package/dist/traverse/factory.js +158 -0
  146. package/dist/traverse/factory.js.map +1 -0
  147. package/dist/traverse/factory.mjs +156 -0
  148. package/dist/traverse/factory.mjs.map +1 -0
  149. package/dist/traverse/index.js +14 -0
  150. package/dist/traverse/index.js.map +1 -0
  151. package/dist/traverse/index.mjs +5 -0
  152. package/dist/traverse/index.mjs.map +1 -0
  153. package/dist/traverse/query-fields.js +41 -0
  154. package/dist/traverse/query-fields.js.map +1 -0
  155. package/dist/traverse/query-fields.mjs +39 -0
  156. package/dist/traverse/query-fields.mjs.map +1 -0
  157. package/dist/traverse/query-filters.js +114 -0
  158. package/dist/traverse/query-filters.js.map +1 -0
  159. package/dist/traverse/query-filters.mjs +112 -0
  160. package/dist/traverse/query-filters.mjs.map +1 -0
  161. package/dist/traverse/query-populate.js +280 -0
  162. package/dist/traverse/query-populate.js.map +1 -0
  163. package/dist/traverse/query-populate.mjs +278 -0
  164. package/dist/traverse/query-populate.mjs.map +1 -0
  165. package/dist/traverse/query-sort.js +144 -0
  166. package/dist/traverse/query-sort.js.map +1 -0
  167. package/dist/traverse/query-sort.mjs +142 -0
  168. package/dist/traverse/query-sort.mjs.map +1 -0
  169. package/dist/traverse-entity.js +170 -0
  170. package/dist/traverse-entity.js.map +1 -0
  171. package/dist/traverse-entity.mjs +168 -0
  172. package/dist/traverse-entity.mjs.map +1 -0
  173. package/dist/validate/index.js +218 -0
  174. package/dist/validate/index.js.map +1 -0
  175. package/dist/validate/index.mjs +217 -0
  176. package/dist/validate/index.mjs.map +1 -0
  177. package/dist/validate/utils.js +27 -0
  178. package/dist/validate/utils.js.map +1 -0
  179. package/dist/validate/utils.mjs +24 -0
  180. package/dist/validate/utils.mjs.map +1 -0
  181. package/dist/validate/validators.js +369 -0
  182. package/dist/validate/validators.js.map +1 -0
  183. package/dist/validate/validators.mjs +356 -0
  184. package/dist/validate/validators.mjs.map +1 -0
  185. package/dist/validate/visitors/index.js +22 -0
  186. package/dist/validate/visitors/index.js.map +1 -0
  187. package/dist/validate/visitors/index.mjs +9 -0
  188. package/dist/validate/visitors/index.mjs.map +1 -0
  189. package/dist/validate/visitors/throw-disallowed-fields.js +91 -0
  190. package/dist/validate/visitors/throw-disallowed-fields.js.map +1 -0
  191. package/dist/validate/visitors/throw-disallowed-fields.mjs +89 -0
  192. package/dist/validate/visitors/throw-disallowed-fields.mjs.map +1 -0
  193. package/dist/validate/visitors/throw-dynamic-zones.js +16 -0
  194. package/dist/validate/visitors/throw-dynamic-zones.js.map +1 -0
  195. package/dist/validate/visitors/throw-dynamic-zones.mjs +14 -0
  196. package/dist/validate/visitors/throw-dynamic-zones.mjs.map +1 -0
  197. package/dist/validate/visitors/throw-morph-to-relations.js +16 -0
  198. package/dist/validate/visitors/throw-morph-to-relations.js.map +1 -0
  199. package/dist/validate/visitors/throw-morph-to-relations.mjs +14 -0
  200. package/dist/validate/visitors/throw-morph-to-relations.mjs.map +1 -0
  201. package/dist/validate/visitors/throw-password.js +15 -0
  202. package/dist/validate/visitors/throw-password.js.map +1 -0
  203. package/dist/validate/visitors/throw-password.mjs +13 -0
  204. package/dist/validate/visitors/throw-password.mjs.map +1 -0
  205. package/dist/validate/visitors/throw-private.js +20 -0
  206. package/dist/validate/visitors/throw-private.js.map +1 -0
  207. package/dist/validate/visitors/throw-private.mjs +18 -0
  208. package/dist/validate/visitors/throw-private.mjs.map +1 -0
  209. package/dist/validate/visitors/throw-restricted-fields.js +36 -0
  210. package/dist/validate/visitors/throw-restricted-fields.js.map +1 -0
  211. package/dist/validate/visitors/throw-restricted-fields.mjs +34 -0
  212. package/dist/validate/visitors/throw-restricted-fields.mjs.map +1 -0
  213. package/dist/validate/visitors/throw-restricted-relations.js +125 -0
  214. package/dist/validate/visitors/throw-restricted-relations.js.map +1 -0
  215. package/dist/validate/visitors/throw-restricted-relations.mjs +123 -0
  216. package/dist/validate/visitors/throw-restricted-relations.mjs.map +1 -0
  217. package/dist/validate/visitors/throw-unrecognized-fields.js +66 -0
  218. package/dist/validate/visitors/throw-unrecognized-fields.js.map +1 -0
  219. package/dist/validate/visitors/throw-unrecognized-fields.mjs +64 -0
  220. package/dist/validate/visitors/throw-unrecognized-fields.mjs.map +1 -0
  221. package/dist/validators.js +60 -0
  222. package/dist/validators.js.map +1 -0
  223. package/dist/validators.mjs +37 -0
  224. package/dist/validators.mjs.map +1 -0
  225. package/dist/yup.js +101 -0
  226. package/dist/yup.js.map +1 -0
  227. package/dist/yup.mjs +74 -0
  228. package/dist/yup.mjs.map +1 -0
  229. package/dist/zod.js +31 -0
  230. package/dist/zod.js.map +1 -0
  231. package/dist/zod.mjs +29 -0
  232. package/dist/zod.mjs.map +1 -0
  233. package/package.json +3 -3
package/dist/index.mjs CHANGED
@@ -1,4318 +1,49 @@
1
- import * as _ from 'lodash';
2
- import ___default, { kebabCase } from 'lodash';
3
- import * as dates$1 from 'date-fns';
4
- import { has, union, getOr, assoc, assign, cloneDeep, remove, eq, curry, isObject, isNil, clone, isArray, isEmpty, toPath, defaults, isString, toNumber, get, isInteger, isBoolean, pick, omit, trim, pipe as pipe$1, split, map as map$1, flatten, first, identity, constant, join, merge, trimChars, trimCharsEnd, trimCharsStart, isNumber } from 'lodash/fp';
5
- import { randomUUID } from 'crypto';
6
- import { machineIdSync } from 'node-machine-id';
7
- import * as yup$1 from 'yup';
8
- import { HttpError } from 'http-errors';
9
- import pMap from 'p-map';
10
- import execa from 'execa';
11
- import preferredPM from 'preferred-pm';
12
- import { Writable } from 'node:stream';
13
- import slugify from '@sindresorhus/slugify';
14
- import { z } from 'zod';
15
-
16
- function _mergeNamespaces(n, m) {
17
- m.forEach(function (e) {
18
- e && typeof e !== 'string' && !Array.isArray(e) && Object.keys(e).forEach(function (k) {
19
- if (k !== 'default' && !(k in n)) {
20
- var d = Object.getOwnPropertyDescriptor(e, k);
21
- Object.defineProperty(n, k, d.get ? d : {
22
- enumerable: true,
23
- get: function () { return e[k]; }
24
- });
25
- }
26
- });
27
- });
28
- return Object.freeze(n);
29
- }
30
-
31
- const timeRegex = /^(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]{1,3})?$/;
32
- const isDate = (v)=>{
33
- return dates$1.isDate(v);
34
- };
35
- const parseTime = (value)=>{
36
- if (isDate(value)) {
37
- return dates$1.format(value, 'HH:mm:ss.SSS');
38
- }
39
- if (typeof value !== 'string') {
40
- throw new Error(`Expected a string, got a ${typeof value}`);
41
- }
42
- const result = value.match(timeRegex);
43
- if (result === null) {
44
- throw new Error('Invalid time format, expected HH:mm:ss.SSS');
45
- }
46
- const [, hours, minutes, seconds, fraction = '.000'] = result;
47
- const fractionPart = _.padEnd(fraction.slice(1), 3, '0');
48
- return `${hours}:${minutes}:${seconds}.${fractionPart}`;
49
- };
50
- const parseDate = (value)=>{
51
- if (isDate(value)) {
52
- return dates$1.format(value, 'yyyy-MM-dd');
53
- }
54
- if (typeof value !== 'string') {
55
- throw new Error(`Expected a string, got a ${typeof value}`);
56
- }
57
- try {
58
- const date = dates$1.parseISO(value);
59
- if (dates$1.isValid(date)) return dates$1.format(date, 'yyyy-MM-dd');
60
- throw new Error(`Invalid format, expected an ISO compatible date`);
61
- } catch (error) {
62
- throw new Error(`Invalid format, expected an ISO compatible date`);
63
- }
64
- };
65
- const parseDateTimeOrTimestamp = (value)=>{
66
- if (isDate(value)) {
67
- return value;
68
- }
69
- if (typeof value !== 'string') {
70
- throw new Error(`Expected a string, got a ${typeof value}`);
71
- }
72
- try {
73
- const date = dates$1.parseISO(value);
74
- if (dates$1.isValid(date)) return date;
75
- const milliUnixDate = dates$1.parse(value, 'T', new Date());
76
- if (dates$1.isValid(milliUnixDate)) return milliUnixDate;
77
- throw new Error(`Invalid format, expected a timestamp or an ISO date`);
78
- } catch (error) {
79
- throw new Error(`Invalid format, expected a timestamp or an ISO date`);
80
- }
81
- };
82
- const parseBoolean = (value, options)=>{
83
- const { forceCast = false } = options;
84
- if (typeof value === 'boolean') {
85
- return value;
86
- }
87
- if (typeof value === 'string' || typeof value === 'number') {
88
- if ([
89
- 'true',
90
- 't',
91
- '1',
92
- 1
93
- ].includes(value)) {
94
- return true;
95
- }
96
- if ([
97
- 'false',
98
- 'f',
99
- '0',
100
- 0
101
- ].includes(value)) {
102
- return false;
103
- }
104
- }
105
- if (forceCast) {
106
- return Boolean(value);
107
- }
108
- throw new Error('Invalid boolean input. Expected "t","1","true","false","0","f"');
109
- };
110
- /**
111
- * Cast basic values based on attribute type
112
- */ const parseType = (options)=>{
113
- const { type, value, forceCast } = options;
114
- switch(type){
115
- case 'boolean':
116
- return parseBoolean(value, {
117
- forceCast
118
- });
119
- case 'integer':
120
- case 'biginteger':
121
- case 'float':
122
- case 'decimal':
123
- {
124
- return _.toNumber(value);
125
- }
126
- case 'time':
127
- {
128
- return parseTime(value);
129
- }
130
- case 'date':
131
- {
132
- return parseDate(value);
133
- }
134
- case 'timestamp':
135
- case 'datetime':
136
- {
137
- return parseDateTimeOrTimestamp(value);
138
- }
139
- default:
140
- return value;
141
- }
142
- };
143
-
144
- function envFn(key, defaultValue) {
145
- return ___default.has(process.env, key) ? process.env[key] : defaultValue;
146
- }
147
- function getKey(key) {
148
- return process.env[key] ?? '';
149
- }
150
- const utils = {
151
- int (key, defaultValue) {
152
- if (!___default.has(process.env, key)) {
153
- return defaultValue;
154
- }
155
- return parseInt(getKey(key), 10);
156
- },
157
- float (key, defaultValue) {
158
- if (!___default.has(process.env, key)) {
159
- return defaultValue;
160
- }
161
- return parseFloat(getKey(key));
162
- },
163
- bool (key, defaultValue) {
164
- if (!___default.has(process.env, key)) {
165
- return defaultValue;
166
- }
167
- return getKey(key) === 'true';
168
- },
169
- json (key, defaultValue) {
170
- if (!___default.has(process.env, key)) {
171
- return defaultValue;
172
- }
173
- try {
174
- return JSON.parse(getKey(key));
175
- } catch (error) {
176
- if (error instanceof Error) {
177
- throw new Error(`Invalid json environment variable ${key}: ${error.message}`);
178
- }
179
- throw error;
180
- }
181
- },
182
- array (key, defaultValue) {
183
- if (!___default.has(process.env, key)) {
184
- return defaultValue;
185
- }
186
- let value = getKey(key);
187
- if (value.startsWith('[') && value.endsWith(']')) {
188
- value = value.substring(1, value.length - 1);
189
- }
190
- return value.split(',').map((v)=>{
191
- return ___default.trim(___default.trim(v, ' '), '"');
192
- });
193
- },
194
- date (key, defaultValue) {
195
- if (!___default.has(process.env, key)) {
196
- return defaultValue;
197
- }
198
- return new Date(getKey(key));
199
- },
200
- /**
201
- * Gets a value from env that matches oneOf provided values
202
- * @param {string} key
203
- * @param {string[]} expectedValues
204
- * @param {string|undefined} defaultValue
205
- * @returns {string|undefined}
206
- */ oneOf (key, expectedValues, defaultValue) {
207
- if (!expectedValues) {
208
- throw new Error(`env.oneOf requires expectedValues`);
209
- }
210
- if (defaultValue && !expectedValues.includes(defaultValue)) {
211
- throw new Error(`env.oneOf requires defaultValue to be included in expectedValues`);
212
- }
213
- const rawValue = env(key, defaultValue);
214
- return expectedValues.includes(rawValue) ? rawValue : defaultValue;
215
- }
216
- };
217
- const env = Object.assign(envFn, utils);
218
-
219
- const SINGLE_TYPE = 'singleType';
220
- const COLLECTION_TYPE = 'collectionType';
221
- const ID_ATTRIBUTE$4 = 'id';
222
- const DOC_ID_ATTRIBUTE$4 = 'documentId';
223
- const PUBLISHED_AT_ATTRIBUTE$1 = 'publishedAt';
224
- const CREATED_BY_ATTRIBUTE$3 = 'createdBy';
225
- const UPDATED_BY_ATTRIBUTE$3 = 'updatedBy';
226
- const CREATED_AT_ATTRIBUTE = 'createdAt';
227
- const UPDATED_AT_ATTRIBUTE = 'updatedAt';
228
- const constants$1 = {
229
- ID_ATTRIBUTE: ID_ATTRIBUTE$4,
230
- DOC_ID_ATTRIBUTE: DOC_ID_ATTRIBUTE$4,
231
- PUBLISHED_AT_ATTRIBUTE: PUBLISHED_AT_ATTRIBUTE$1,
232
- CREATED_BY_ATTRIBUTE: CREATED_BY_ATTRIBUTE$3,
233
- UPDATED_BY_ATTRIBUTE: UPDATED_BY_ATTRIBUTE$3,
234
- CREATED_AT_ATTRIBUTE,
235
- UPDATED_AT_ATTRIBUTE,
236
- SINGLE_TYPE,
237
- COLLECTION_TYPE
238
- };
239
- const getTimestamps = (model)=>{
240
- const attributes = [];
241
- if (has(CREATED_AT_ATTRIBUTE, model.attributes)) {
242
- attributes.push(CREATED_AT_ATTRIBUTE);
243
- }
244
- if (has(UPDATED_AT_ATTRIBUTE, model.attributes)) {
245
- attributes.push(UPDATED_AT_ATTRIBUTE);
246
- }
247
- return attributes;
248
- };
249
- const getCreatorFields = (model)=>{
250
- const attributes = [];
251
- if (has(CREATED_BY_ATTRIBUTE$3, model.attributes)) {
252
- attributes.push(CREATED_BY_ATTRIBUTE$3);
253
- }
254
- if (has(UPDATED_BY_ATTRIBUTE$3, model.attributes)) {
255
- attributes.push(UPDATED_BY_ATTRIBUTE$3);
256
- }
257
- return attributes;
258
- };
259
- const getNonWritableAttributes = (model)=>{
260
- if (!model) return [];
261
- const nonWritableAttributes = ___default.reduce(model.attributes, (acc, attr, attrName)=>attr.writable === false ? acc.concat(attrName) : acc, []);
262
- return ___default.uniq([
263
- ID_ATTRIBUTE$4,
264
- DOC_ID_ATTRIBUTE$4,
265
- ...getTimestamps(model),
266
- ...nonWritableAttributes
267
- ]);
268
- };
269
- const getWritableAttributes = (model)=>{
270
- if (!model) return [];
271
- return ___default.difference(Object.keys(model.attributes), getNonWritableAttributes(model));
272
- };
273
- const isWritableAttribute = (model, attributeName)=>{
274
- return getWritableAttributes(model).includes(attributeName);
275
- };
276
- const getNonVisibleAttributes = (model)=>{
277
- const nonVisibleAttributes = ___default.reduce(model.attributes, (acc, attr, attrName)=>attr.visible === false ? acc.concat(attrName) : acc, []);
278
- return ___default.uniq([
279
- ID_ATTRIBUTE$4,
280
- DOC_ID_ATTRIBUTE$4,
281
- ...getTimestamps(model),
282
- ...nonVisibleAttributes
283
- ]);
284
- };
285
- const getVisibleAttributes = (model)=>{
286
- return ___default.difference(___default.keys(model.attributes), getNonVisibleAttributes(model));
287
- };
288
- const isVisibleAttribute = (model, attributeName)=>{
289
- return getVisibleAttributes(model).includes(attributeName);
290
- };
291
- const getOptions = (model)=>___default.assign({
292
- draftAndPublish: false
293
- }, ___default.get(model, 'options', {}));
294
- const hasDraftAndPublish = (model)=>___default.get(model, 'options.draftAndPublish', false) === true;
295
- const isDraft = (data, model)=>hasDraftAndPublish(model) && ___default.get(data, PUBLISHED_AT_ATTRIBUTE$1) === null;
296
- const isSchema = (data)=>{
297
- return typeof data === 'object' && data !== null && 'modelType' in data && typeof data.modelType === 'string' && [
298
- 'component',
299
- 'contentType'
300
- ].includes(data.modelType);
301
- };
302
- const isComponentSchema = (data)=>{
303
- return isSchema(data) && data.modelType === 'component';
304
- };
305
- const isContentTypeSchema = (data)=>{
306
- return isSchema(data) && data.modelType === 'contentType';
307
- };
308
- const isSingleType = ({ kind = COLLECTION_TYPE })=>kind === SINGLE_TYPE;
309
- const isCollectionType = ({ kind = COLLECTION_TYPE })=>kind === COLLECTION_TYPE;
310
- const isKind = (kind)=>(model)=>model.kind === kind;
311
- const getStoredPrivateAttributes = (model)=>union(strapi?.config?.get('api.responses.privateAttributes', []) ?? [], getOr([], 'options.privateAttributes', model));
312
- const getPrivateAttributes = (model)=>{
313
- return ___default.union(getStoredPrivateAttributes(model), ___default.keys(___default.pickBy(model.attributes, (attr)=>!!attr.private)));
314
- };
315
- const isPrivateAttribute = (model, attributeName)=>{
316
- if (model?.attributes?.[attributeName]?.private === true) {
317
- return true;
318
- }
319
- return getStoredPrivateAttributes(model).includes(attributeName);
320
- };
321
- const isScalarAttribute = (attribute)=>{
322
- return attribute && ![
323
- 'media',
324
- 'component',
325
- 'relation',
326
- 'dynamiczone'
327
- ].includes(attribute.type);
328
- };
329
- const getDoesAttributeRequireValidation = (attribute)=>{
330
- return attribute.required || attribute.unique || Object.prototype.hasOwnProperty.call(attribute, 'max') || Object.prototype.hasOwnProperty.call(attribute, 'min') || Object.prototype.hasOwnProperty.call(attribute, 'maxLength') || Object.prototype.hasOwnProperty.call(attribute, 'minLength');
331
- };
332
- const isMediaAttribute = (attribute)=>attribute?.type === 'media';
333
- const isRelationalAttribute = (attribute)=>attribute?.type === 'relation';
334
- const HAS_RELATION_REORDERING = [
335
- 'manyToMany',
336
- 'manyToOne',
337
- 'oneToMany'
338
- ];
339
- const hasRelationReordering = (attribute)=>isRelationalAttribute(attribute) && HAS_RELATION_REORDERING.includes(attribute.relation);
340
- const isComponentAttribute = (attribute)=>[
341
- 'component',
342
- 'dynamiczone'
343
- ].includes(attribute?.type);
344
- const isDynamicZoneAttribute = (attribute)=>!!attribute && attribute.type === 'dynamiczone';
345
- const isMorphToRelationalAttribute = (attribute)=>{
346
- return !!attribute && isRelationalAttribute(attribute) && attribute.relation?.startsWith?.('morphTo');
347
- };
348
- const getComponentAttributes = (schema)=>{
349
- return ___default.reduce(schema.attributes, (acc, attr, attrName)=>{
350
- if (isComponentAttribute(attr)) acc.push(attrName);
351
- return acc;
352
- }, []);
353
- };
354
- const getScalarAttributes = (schema)=>{
355
- return ___default.reduce(schema.attributes, (acc, attr, attrName)=>{
356
- if (isScalarAttribute(attr)) acc.push(attrName);
357
- return acc;
358
- }, []);
359
- };
360
- const getRelationalAttributes = (schema)=>{
361
- return ___default.reduce(schema.attributes, (acc, attr, attrName)=>{
362
- if (isRelationalAttribute(attr)) acc.push(attrName);
363
- return acc;
364
- }, []);
365
- };
366
- /**
367
- * Checks if an attribute is of type `type`
368
- * @param {object} attribute
369
- * @param {string} type
370
- */ const isTypedAttribute = (attribute, type)=>{
371
- return ___default.has(attribute, 'type') && attribute.type === type;
372
- };
373
- /**
374
- * Returns a route prefix for a contentType
375
- * @param {object} contentType
376
- * @returns {string}
377
- */ const getContentTypeRoutePrefix = (contentType)=>{
378
- return isSingleType(contentType) ? ___default.kebabCase(contentType.info.singularName) : ___default.kebabCase(contentType.info.pluralName);
379
- };
380
-
381
- var contentTypes = /*#__PURE__*/Object.freeze({
382
- __proto__: null,
383
- constants: constants$1,
384
- getComponentAttributes: getComponentAttributes,
385
- getContentTypeRoutePrefix: getContentTypeRoutePrefix,
386
- getCreatorFields: getCreatorFields,
387
- getDoesAttributeRequireValidation: getDoesAttributeRequireValidation,
388
- getNonVisibleAttributes: getNonVisibleAttributes,
389
- getNonWritableAttributes: getNonWritableAttributes,
390
- getOptions: getOptions,
391
- getPrivateAttributes: getPrivateAttributes,
392
- getRelationalAttributes: getRelationalAttributes,
393
- getScalarAttributes: getScalarAttributes,
394
- getTimestamps: getTimestamps,
395
- getVisibleAttributes: getVisibleAttributes,
396
- getWritableAttributes: getWritableAttributes,
397
- hasDraftAndPublish: hasDraftAndPublish,
398
- hasRelationReordering: hasRelationReordering,
399
- isCollectionType: isCollectionType,
400
- isComponentAttribute: isComponentAttribute,
401
- isComponentSchema: isComponentSchema,
402
- isContentTypeSchema: isContentTypeSchema,
403
- isDraft: isDraft,
404
- isDynamicZoneAttribute: isDynamicZoneAttribute,
405
- isKind: isKind,
406
- isMediaAttribute: isMediaAttribute,
407
- isMorphToRelationalAttribute: isMorphToRelationalAttribute,
408
- isPrivateAttribute: isPrivateAttribute,
409
- isRelationalAttribute: isRelationalAttribute,
410
- isScalarAttribute: isScalarAttribute,
411
- isSchema: isSchema,
412
- isSingleType: isSingleType,
413
- isTypedAttribute: isTypedAttribute,
414
- isVisibleAttribute: isVisibleAttribute,
415
- isWritableAttribute: isWritableAttribute
416
- });
417
-
418
- const { CREATED_BY_ATTRIBUTE: CREATED_BY_ATTRIBUTE$2, UPDATED_BY_ATTRIBUTE: UPDATED_BY_ATTRIBUTE$2 } = constants$1;
419
- const setCreatorFields = ({ user, isEdition = false })=>(data)=>{
420
- if (isEdition) {
421
- return assoc(UPDATED_BY_ATTRIBUTE$2, user.id, data);
422
- }
423
- return assign(data, {
424
- [CREATED_BY_ATTRIBUTE$2]: user.id,
425
- [UPDATED_BY_ATTRIBUTE$2]: user.id
426
- });
427
- };
428
-
429
- /**
430
- * Create a default Strapi hook
431
- */ const createHook = ()=>{
432
- const state = {
433
- handlers: []
434
- };
435
- return {
436
- getHandlers () {
437
- return state.handlers;
438
- },
439
- register (handler) {
440
- state.handlers.push(handler);
441
- return this;
442
- },
443
- delete (handler) {
444
- state.handlers = remove(eq(handler), state.handlers);
445
- return this;
446
- },
447
- call () {
448
- throw new Error('Method not implemented');
449
- }
450
- };
451
- };
452
- /**
453
- * Create an async series hook.
454
- * Upon execution, it will execute every handler in order with the same context
455
- */ const createAsyncSeriesHook = ()=>({
456
- ...createHook(),
457
- async call (context) {
458
- for (const handler of this.getHandlers()){
459
- await handler(context);
460
- }
461
- }
462
- });
463
- /**
464
- * Create an async series waterfall hook.
465
- * Upon execution, it will execute every handler in order and pass the return value of the last handler to the next one
466
- */ const createAsyncSeriesWaterfallHook = ()=>({
467
- ...createHook(),
468
- async call (param) {
469
- let res = param;
470
- for (const handler of this.getHandlers()){
471
- res = await handler(res);
472
- }
473
- return res;
474
- }
475
- });
476
- /**
477
- * Create an async parallel hook.
478
- * Upon execution, it will execute every registered handler in band.
479
- */ const createAsyncParallelHook = ()=>({
480
- ...createHook(),
481
- async call (context) {
482
- const promises = this.getHandlers().map((handler)=>handler(cloneDeep(context)));
483
- return Promise.all(promises);
484
- }
485
- });
486
- /**
487
- * Create an async parallel hook.
488
- * Upon execution, it will execute every registered handler in serie and return the first result found.
489
- */ const createAsyncBailHook = ()=>({
490
- ...createHook(),
491
- async call (context) {
492
- for (const handler of this.getHandlers()){
493
- const result = await handler(context);
494
- if (result !== undefined) {
495
- return result;
496
- }
497
- }
498
- }
499
- });
500
- const internals = {
501
- // Internal utils
502
- createHook
503
- };
504
-
505
- var hooks = /*#__PURE__*/Object.freeze({
506
- __proto__: null,
507
- createAsyncBailHook: createAsyncBailHook,
508
- createAsyncParallelHook: createAsyncParallelHook,
509
- createAsyncSeriesHook: createAsyncSeriesHook,
510
- createAsyncSeriesWaterfallHook: createAsyncSeriesWaterfallHook,
511
- internals: internals
512
- });
513
-
514
- /**
515
- * Creates a new object containing various hooks used by the providers
516
- */ const createProviderHooksMap = ()=>({
517
- // Register events
518
- willRegister: createAsyncSeriesHook(),
519
- didRegister: createAsyncParallelHook(),
520
- // Delete events
521
- willDelete: createAsyncParallelHook(),
522
- didDelete: createAsyncParallelHook()
523
- });
524
- /**
525
- * A Provider factory
526
- */ const providerFactory = (options = {})=>{
527
- const { throwOnDuplicates = true } = options;
528
- const state = {
529
- hooks: createProviderHooksMap(),
530
- registry: new Map()
531
- };
532
- return {
533
- hooks: state.hooks,
534
- async register (key, item) {
535
- if (throwOnDuplicates && this.has(key)) {
536
- throw new Error(`Duplicated item key: ${key}`);
537
- }
538
- await state.hooks.willRegister.call({
539
- key,
540
- value: item
541
- });
542
- state.registry.set(key, item);
543
- await state.hooks.didRegister.call({
544
- key,
545
- value: cloneDeep(item)
546
- });
547
- return this;
548
- },
549
- async delete (key) {
550
- if (this.has(key)) {
551
- const item = this.get(key);
552
- await state.hooks.willDelete.call({
553
- key,
554
- value: cloneDeep(item)
555
- });
556
- state.registry.delete(key);
557
- await state.hooks.didDelete.call({
558
- key,
559
- value: cloneDeep(item)
560
- });
561
- }
562
- return this;
563
- },
564
- get (key) {
565
- return state.registry.get(key);
566
- },
567
- values () {
568
- return Array.from(state.registry.values());
569
- },
570
- keys () {
571
- return Array.from(state.registry.keys());
572
- },
573
- has (key) {
574
- return state.registry.has(key);
575
- },
576
- size () {
577
- return state.registry.size;
578
- },
579
- async clear () {
580
- const keys = this.keys();
581
- for (const key of keys){
582
- await this.delete(key);
583
- }
584
- return this;
585
- }
586
- };
587
- };
588
-
589
- const traverseEntity = async (visitor, options, entity)=>{
590
- const { path = {
591
- raw: null,
592
- attribute: null
593
- }, schema, getModel } = options;
594
- let parent = options.parent;
595
- const traverseMorphRelationTarget = async (visitor, path, entry)=>{
596
- const targetSchema = getModel(entry.__type);
597
- const traverseOptions = {
598
- schema: targetSchema,
599
- path,
600
- getModel,
601
- parent
602
- };
603
- return traverseEntity(visitor, traverseOptions, entry);
604
- };
605
- const traverseRelationTarget = (schema)=>async (visitor, path, entry)=>{
606
- const traverseOptions = {
607
- schema,
608
- path,
609
- getModel,
610
- parent
611
- };
612
- return traverseEntity(visitor, traverseOptions, entry);
613
- };
614
- const traverseMediaTarget = async (visitor, path, entry)=>{
615
- const targetSchemaUID = 'plugin::upload.file';
616
- const targetSchema = getModel(targetSchemaUID);
617
- const traverseOptions = {
618
- schema: targetSchema,
619
- path,
620
- getModel,
621
- parent
622
- };
623
- return traverseEntity(visitor, traverseOptions, entry);
624
- };
625
- const traverseComponent = async (visitor, path, schema, entry)=>{
626
- const traverseOptions = {
627
- schema,
628
- path,
629
- getModel,
630
- parent
631
- };
632
- return traverseEntity(visitor, traverseOptions, entry);
633
- };
634
- const visitDynamicZoneEntry = async (visitor, path, entry)=>{
635
- const targetSchema = getModel(entry.__component);
636
- const traverseOptions = {
637
- schema: targetSchema,
638
- path,
639
- getModel,
640
- parent
641
- };
642
- return traverseEntity(visitor, traverseOptions, entry);
643
- };
644
- // End recursion
645
- if (!isObject(entity) || isNil(schema)) {
646
- return entity;
647
- }
648
- // Don't mutate the original entity object
649
- // only clone at 1st level as the next level will get clone when traversed
650
- const copy = clone(entity);
651
- const visitorUtils = createVisitorUtils({
652
- data: copy
653
- });
654
- const keys = Object.keys(copy);
655
- for(let i = 0; i < keys.length; i += 1){
656
- const key = keys[i];
657
- // Retrieve the attribute definition associated to the key from the schema
658
- const attribute = schema.attributes[key];
659
- const newPath = {
660
- ...path
661
- };
662
- newPath.raw = isNil(path.raw) ? key : `${path.raw}.${key}`;
663
- if (!isNil(attribute)) {
664
- newPath.attribute = isNil(path.attribute) ? key : `${path.attribute}.${key}`;
665
- }
666
- // Visit the current attribute
667
- const visitorOptions = {
668
- data: copy,
669
- schema,
670
- key,
671
- value: copy[key],
672
- attribute,
673
- path: newPath,
674
- getModel,
675
- parent
676
- };
677
- await visitor(visitorOptions, visitorUtils);
678
- // Extract the value for the current key (after calling the visitor)
679
- const value = copy[key];
680
- // Ignore Nil values or attributes
681
- if (isNil(value) || isNil(attribute)) {
682
- continue;
683
- }
684
- // The current attribute becomes the parent once visited
685
- parent = {
686
- schema,
687
- key,
688
- attribute,
689
- path: newPath
690
- };
691
- if (isRelationalAttribute(attribute)) {
692
- const isMorphRelation = attribute.relation.toLowerCase().startsWith('morph');
693
- const method = isMorphRelation ? traverseMorphRelationTarget : traverseRelationTarget(getModel(attribute.target));
694
- if (isArray(value)) {
695
- const res = new Array(value.length);
696
- for(let i = 0; i < value.length; i += 1){
697
- res[i] = await method(visitor, newPath, value[i]);
698
- }
699
- copy[key] = res;
700
- } else {
701
- copy[key] = await method(visitor, newPath, value);
702
- }
703
- continue;
704
- }
705
- if (isMediaAttribute(attribute)) {
706
- // need to update copy
707
- if (isArray(value)) {
708
- const res = new Array(value.length);
709
- for(let i = 0; i < value.length; i += 1){
710
- res[i] = await traverseMediaTarget(visitor, newPath, value[i]);
711
- }
712
- copy[key] = res;
713
- } else {
714
- copy[key] = await traverseMediaTarget(visitor, newPath, value);
715
- }
716
- continue;
717
- }
718
- if (attribute.type === 'component') {
719
- const targetSchema = getModel(attribute.component);
720
- if (isArray(value)) {
721
- const res = new Array(value.length);
722
- for(let i = 0; i < value.length; i += 1){
723
- res[i] = await traverseComponent(visitor, newPath, targetSchema, value[i]);
724
- }
725
- copy[key] = res;
726
- } else {
727
- copy[key] = await traverseComponent(visitor, newPath, targetSchema, value);
728
- }
729
- continue;
730
- }
731
- if (attribute.type === 'dynamiczone' && isArray(value)) {
732
- const res = new Array(value.length);
733
- for(let i = 0; i < value.length; i += 1){
734
- res[i] = await visitDynamicZoneEntry(visitor, newPath, value[i]);
735
- }
736
- copy[key] = res;
737
- continue;
738
- }
739
- }
740
- return copy;
741
- };
742
- const createVisitorUtils = ({ data })=>({
743
- remove (key) {
744
- delete data[key];
745
- },
746
- set (key, value) {
747
- data[key] = value;
748
- }
749
- });
750
- var traverseEntity$1 = curry(traverseEntity);
751
-
752
- /* eslint-disable @typescript-eslint/no-var-requires */ function importDefault(modName) {
753
- const mod = require(modName);
754
- return mod && mod.__esModule ? mod.default : mod;
755
- }
756
-
757
- var machineId = (()=>{
758
- try {
759
- const deviceId = machineIdSync();
760
- return deviceId;
761
- } catch (error) {
762
- const deviceId = randomUUID();
763
- return deviceId;
764
- }
765
- });
766
-
767
- const formatYupInnerError = (yupError)=>({
768
- path: toPath(yupError.path),
769
- message: yupError.message,
770
- name: yupError.name,
771
- value: yupError.value
772
- });
773
- const formatYupErrors = (yupError)=>({
774
- errors: isEmpty(yupError.inner) ? [
775
- formatYupInnerError(yupError)
776
- ] : yupError.inner.map(formatYupInnerError),
777
- message: yupError.message
778
- });
779
-
780
- /* ApplicationError */ class ApplicationError extends Error {
781
- constructor(message = 'An application error occured', details = {}){
782
- super();
783
- this.name = 'ApplicationError';
784
- this.message = message;
785
- this.details = details;
786
- }
787
- }
788
- class ValidationError extends ApplicationError {
789
- constructor(message, details){
790
- super(message, details);
791
- this.name = 'ValidationError';
792
- }
793
- }
794
- class YupValidationError extends ValidationError {
795
- constructor(yupError, message){
796
- super('Validation');
797
- const { errors, message: yupMessage } = formatYupErrors(yupError);
798
- this.message = message || yupMessage;
799
- this.details = {
800
- errors
801
- };
802
- }
803
- }
804
- class PaginationError extends ApplicationError {
805
- constructor(message = 'Invalid pagination', details){
806
- super(message, details);
807
- this.name = 'PaginationError';
808
- this.message = message;
809
- }
810
- }
811
- class NotFoundError extends ApplicationError {
812
- constructor(message = 'Entity not found', details){
813
- super(message, details);
814
- this.name = 'NotFoundError';
815
- this.message = message;
816
- }
817
- }
818
- class ForbiddenError extends ApplicationError {
819
- constructor(message = 'Forbidden access', details){
820
- super(message, details);
821
- this.name = 'ForbiddenError';
822
- this.message = message;
823
- }
824
- }
825
- class UnauthorizedError extends ApplicationError {
826
- constructor(message = 'Unauthorized', details){
827
- super(message, details);
828
- this.name = 'UnauthorizedError';
829
- this.message = message;
830
- }
831
- }
832
- class RateLimitError extends ApplicationError {
833
- constructor(message = 'Too many requests, please try again later.', details){
834
- super(message, details);
835
- this.name = 'RateLimitError';
836
- this.message = message;
837
- this.details = details || {};
838
- }
839
- }
840
- class PayloadTooLargeError extends ApplicationError {
841
- constructor(message = 'Entity too large', details){
842
- super(message, details);
843
- this.name = 'PayloadTooLargeError';
844
- this.message = message;
845
- }
846
- }
847
- class PolicyError extends ForbiddenError {
848
- constructor(message = 'Policy Failed', details){
849
- super(message, details);
850
- this.name = 'PolicyError';
851
- this.message = message;
852
- this.details = details || {};
853
- }
854
- }
855
- class NotImplementedError extends ApplicationError {
856
- constructor(message = 'This feature is not implemented yet', details){
857
- super(message, details);
858
- this.name = 'NotImplementedError';
859
- this.message = message;
860
- }
861
- }
862
-
863
- var errors = /*#__PURE__*/Object.freeze({
864
- __proto__: null,
865
- ApplicationError: ApplicationError,
866
- ForbiddenError: ForbiddenError,
867
- HttpError: HttpError,
868
- NotFoundError: NotFoundError,
869
- NotImplementedError: NotImplementedError,
870
- PaginationError: PaginationError,
871
- PayloadTooLargeError: PayloadTooLargeError,
872
- PolicyError: PolicyError,
873
- RateLimitError: RateLimitError,
874
- UnauthorizedError: UnauthorizedError,
875
- ValidationError: ValidationError,
876
- YupValidationError: YupValidationError
877
- });
878
-
879
- const handleYupError = (error, errorMessage)=>{
880
- throw new YupValidationError(error, errorMessage);
881
- };
882
- const defaultValidationParam = {
883
- strict: true,
884
- abortEarly: false
885
- };
886
- const validateYupSchema = (schema, options = {})=>async (body, errorMessage)=>{
887
- try {
888
- const optionsWithDefaults = defaults(defaultValidationParam, options);
889
- const result = await schema.validate(body, optionsWithDefaults);
890
- return result;
891
- } catch (e) {
892
- if (e instanceof yup$1.ValidationError) {
893
- handleYupError(e, errorMessage);
894
- }
895
- throw e;
896
- }
897
- };
898
- const validateYupSchemaSync = (schema, options = {})=>(body, errorMessage)=>{
899
- try {
900
- const optionsWithDefaults = defaults(defaultValidationParam, options);
901
- return schema.validateSync(body, optionsWithDefaults);
902
- } catch (e) {
903
- if (e instanceof yup$1.ValidationError) {
904
- handleYupError(e, errorMessage);
905
- }
906
- throw e;
907
- }
908
- };
909
-
910
- const GROUP_OPERATORS = [
911
- '$and',
912
- '$or'
913
- ];
914
- const WHERE_OPERATORS = [
915
- '$not',
916
- '$in',
917
- '$notIn',
918
- '$eq',
919
- '$eqi',
920
- '$ne',
921
- '$nei',
922
- '$gt',
923
- '$gte',
924
- '$lt',
925
- '$lte',
926
- '$null',
927
- '$notNull',
928
- '$between',
929
- '$startsWith',
930
- '$endsWith',
931
- '$startsWithi',
932
- '$endsWithi',
933
- '$contains',
934
- '$notContains',
935
- '$containsi',
936
- '$notContainsi',
937
- // Experimental, only for internal use
938
- '$jsonSupersetOf'
939
- ];
940
- const CAST_OPERATORS = [
941
- '$not',
942
- '$in',
943
- '$notIn',
944
- '$eq',
945
- '$ne',
946
- '$gt',
947
- '$gte',
948
- '$lt',
949
- '$lte',
950
- '$between'
951
- ];
952
- const ARRAY_OPERATORS = [
953
- '$in',
954
- '$notIn',
955
- '$between'
956
- ];
957
- const OPERATORS = {
958
- where: WHERE_OPERATORS,
959
- cast: CAST_OPERATORS,
960
- group: GROUP_OPERATORS,
961
- array: ARRAY_OPERATORS
962
- };
963
- // for performance, cache all operators in lowercase
964
- const OPERATORS_LOWERCASE = Object.fromEntries(Object.entries(OPERATORS).map(([key, values])=>[
965
- key,
966
- values.map((value)=>value.toLowerCase())
967
- ]));
968
- const isObjKey = (key, obj)=>{
969
- return key in obj;
970
- };
971
- const isOperatorOfType = (type, key, ignoreCase = false)=>{
972
- if (ignoreCase) {
973
- return OPERATORS_LOWERCASE[type]?.includes(key.toLowerCase()) ?? false;
974
- }
975
- if (isObjKey(type, OPERATORS)) {
976
- return OPERATORS[type]?.includes(key) ?? false;
977
- }
978
- return false;
979
- };
980
- const isOperator = (key, ignoreCase = false)=>{
981
- return Object.keys(OPERATORS).some((type)=>isOperatorOfType(type, key, ignoreCase));
982
- };
983
-
984
- const { ID_ATTRIBUTE: ID_ATTRIBUTE$3, DOC_ID_ATTRIBUTE: DOC_ID_ATTRIBUTE$3, PUBLISHED_AT_ATTRIBUTE } = constants$1;
985
- class InvalidOrderError extends Error {
986
- constructor(){
987
- super();
988
- this.message = 'Invalid order. order can only be one of asc|desc|ASC|DESC';
989
- }
990
- }
991
- class InvalidSortError extends Error {
992
- constructor(){
993
- super();
994
- this.message = 'Invalid sort parameter. Expected a string, an array of strings, a sort object or an array of sort objects';
995
- }
996
- }
997
- function validateOrder(order) {
998
- if (!isString(order) || ![
999
- 'asc',
1000
- 'desc'
1001
- ].includes(order.toLocaleLowerCase())) {
1002
- throw new InvalidOrderError();
1003
- }
1004
- }
1005
- const convertCountQueryParams = (countQuery)=>{
1006
- return parseType({
1007
- type: 'boolean',
1008
- value: countQuery
1009
- });
1010
- };
1011
- const convertOrderingQueryParams = (ordering)=>{
1012
- return ordering;
1013
- };
1014
- const isPlainObject = (value)=>___default.isPlainObject(value);
1015
- const isStringArray$3 = (value)=>isArray(value) && value.every(isString);
1016
- const createTransformer = ({ getModel })=>{
1017
- /**
1018
- * Sort query parser
1019
- */ const convertSortQueryParams = (sortQuery)=>{
1020
- if (typeof sortQuery === 'string') {
1021
- return convertStringSortQueryParam(sortQuery);
1022
- }
1023
- if (isStringArray$3(sortQuery)) {
1024
- return sortQuery.flatMap((sortValue)=>convertStringSortQueryParam(sortValue));
1025
- }
1026
- if (Array.isArray(sortQuery)) {
1027
- return sortQuery.map((sortValue)=>convertNestedSortQueryParam(sortValue));
1028
- }
1029
- if (isPlainObject(sortQuery)) {
1030
- return convertNestedSortQueryParam(sortQuery);
1031
- }
1032
- throw new InvalidSortError();
1033
- };
1034
- const convertStringSortQueryParam = (sortQuery)=>{
1035
- return sortQuery.split(',').map((value)=>convertSingleSortQueryParam(value));
1036
- };
1037
- const convertSingleSortQueryParam = (sortQuery)=>{
1038
- if (!sortQuery) {
1039
- return {};
1040
- }
1041
- if (!isString(sortQuery)) {
1042
- throw new Error('Invalid sort query');
1043
- }
1044
- // split field and order param with default order to ascending
1045
- const [field, order = 'asc'] = sortQuery.split(':');
1046
- if (field.length === 0) {
1047
- throw new Error('Field cannot be empty');
1048
- }
1049
- validateOrder(order);
1050
- // TODO: field should be a valid path on an object model
1051
- return ___default.set({}, field, order);
1052
- };
1053
- const convertNestedSortQueryParam = (sortQuery)=>{
1054
- const transformedSort = {};
1055
- for (const field of Object.keys(sortQuery)){
1056
- const order = sortQuery[field];
1057
- // this is a deep sort
1058
- if (isPlainObject(order)) {
1059
- transformedSort[field] = convertNestedSortQueryParam(order);
1060
- } else if (typeof order === 'string') {
1061
- validateOrder(order);
1062
- transformedSort[field] = order;
1063
- } else {
1064
- throw Error(`Invalid sort type expected object or string got ${typeof order}`);
1065
- }
1066
- }
1067
- return transformedSort;
1068
- };
1069
- /**
1070
- * Start query parser
1071
- */ const convertStartQueryParams = (startQuery)=>{
1072
- const startAsANumber = toNumber(startQuery);
1073
- if (!___default.isInteger(startAsANumber) || startAsANumber < 0) {
1074
- throw new Error(`convertStartQueryParams expected a positive integer got ${startAsANumber}`);
1075
- }
1076
- return startAsANumber;
1077
- };
1078
- /**
1079
- * Limit query parser
1080
- */ const convertLimitQueryParams = (limitQuery)=>{
1081
- const limitAsANumber = toNumber(limitQuery);
1082
- if (!___default.isInteger(limitAsANumber) || limitAsANumber !== -1 && limitAsANumber < 0) {
1083
- throw new Error(`convertLimitQueryParams expected a positive integer got ${limitAsANumber}`);
1084
- }
1085
- if (limitAsANumber === -1) {
1086
- return undefined;
1087
- }
1088
- return limitAsANumber;
1089
- };
1090
- const convertPageQueryParams = (page)=>{
1091
- const pageVal = toNumber(page);
1092
- if (!isInteger(pageVal) || pageVal <= 0) {
1093
- throw new PaginationError(`Invalid 'page' parameter. Expected an integer > 0, received: ${page}`);
1094
- }
1095
- return pageVal;
1096
- };
1097
- const convertPageSizeQueryParams = (pageSize, page)=>{
1098
- const pageSizeVal = toNumber(pageSize);
1099
- if (!isInteger(pageSizeVal) || pageSizeVal <= 0) {
1100
- throw new PaginationError(`Invalid 'pageSize' parameter. Expected an integer > 0, received: ${page}`);
1101
- }
1102
- return pageSizeVal;
1103
- };
1104
- const validatePaginationParams = (page, pageSize, start, limit)=>{
1105
- const isPagePagination = !isNil(page) || !isNil(pageSize);
1106
- const isOffsetPagination = !isNil(start) || !isNil(limit);
1107
- if (isPagePagination && isOffsetPagination) {
1108
- throw new PaginationError('Invalid pagination attributes. The page parameters are incorrect and must be in the pagination object');
1109
- }
1110
- };
1111
- class InvalidPopulateError extends Error {
1112
- constructor(){
1113
- super();
1114
- this.message = 'Invalid populate parameter. Expected a string, an array of strings, a populate object';
1115
- }
1116
- }
1117
- // NOTE: we could support foo.* or foo.bar.* etc later on
1118
- const convertPopulateQueryParams = (populate, schema, depth = 0)=>{
1119
- if (depth === 0 && populate === '*') {
1120
- return true;
1121
- }
1122
- if (typeof populate === 'string') {
1123
- return populate.split(',').map((value)=>___default.trim(value));
1124
- }
1125
- if (Array.isArray(populate)) {
1126
- // map convert
1127
- return ___default.uniq(populate.flatMap((value)=>{
1128
- if (typeof value !== 'string') {
1129
- throw new InvalidPopulateError();
1130
- }
1131
- return value.split(',').map((value)=>___default.trim(value));
1132
- }));
1133
- }
1134
- if (___default.isPlainObject(populate)) {
1135
- return convertPopulateObject(populate, schema);
1136
- }
1137
- throw new InvalidPopulateError();
1138
- };
1139
- const hasPopulateFragmentDefined = (populate)=>{
1140
- return typeof populate === 'object' && 'on' in populate && !isNil(populate.on);
1141
- };
1142
- const hasCountDefined = (populate)=>{
1143
- return typeof populate === 'object' && 'count' in populate && typeof populate.count === 'boolean';
1144
- };
1145
- const convertPopulateObject = (populate, schema)=>{
1146
- if (!schema) {
1147
- return {};
1148
- }
1149
- const { attributes } = schema;
1150
- return Object.entries(populate).reduce((acc, [key, subPopulate])=>{
1151
- // Try converting strings to regular booleans if possible
1152
- if (___default.isString(subPopulate)) {
1153
- try {
1154
- const subPopulateAsBoolean = parseType({
1155
- type: 'boolean',
1156
- value: subPopulate
1157
- });
1158
- // Only true is accepted as a boolean populate value
1159
- return subPopulateAsBoolean ? {
1160
- ...acc,
1161
- [key]: true
1162
- } : acc;
1163
- } catch {
1164
- // ignore
1165
- }
1166
- }
1167
- if (___default.isBoolean(subPopulate)) {
1168
- // Only true is accepted as a boolean populate value
1169
- return subPopulate === true ? {
1170
- ...acc,
1171
- [key]: true
1172
- } : acc;
1173
- }
1174
- const attribute = attributes[key];
1175
- if (!attribute) {
1176
- return acc;
1177
- }
1178
- // Allow adding an 'on' strategy to populate queries for morphTo relations and dynamic zones
1179
- const isMorphLikeRelationalAttribute = isDynamicZoneAttribute(attribute) || isMorphToRelationalAttribute(attribute);
1180
- if (isMorphLikeRelationalAttribute) {
1181
- const hasInvalidProperties = Object.keys(subPopulate).some((key)=>![
1182
- 'populate',
1183
- 'on',
1184
- 'count'
1185
- ].includes(key));
1186
- if (hasInvalidProperties) {
1187
- throw new Error(`Invalid nested populate for ${schema.info?.singularName}.${key} (${schema.uid}). Expected a fragment ("on") or "count" but found ${JSON.stringify(subPopulate)}`);
1188
- }
1189
- /**
1190
- * Validate nested population queries in the context of a polymorphic attribute (dynamic zone, morph relation).
1191
- *
1192
- * If 'populate' exists in subPopulate, its value should be constrained to a wildcard ('*').
1193
- */ if ('populate' in subPopulate && subPopulate.populate !== '*') {
1194
- throw new Error(`Invalid nested population query detected. When using 'populate' within polymorphic structures, ` + `its value must be '*' to indicate all second level links. Specific field targeting is not supported here. ` + `Consider using the fragment API for more granular population control.`);
1195
- }
1196
- // TODO: Remove the possibility to have multiple properties at the same time (on/count/populate)
1197
- const newSubPopulate = {};
1198
- // case: { populate: '*' }
1199
- if ('populate' in subPopulate) {
1200
- Object.assign(newSubPopulate, {
1201
- populate: true
1202
- });
1203
- }
1204
- // case: { on: { <clauses> } }
1205
- if (hasPopulateFragmentDefined(subPopulate)) {
1206
- // If the fragment API is used, it applies the transformation to every
1207
- // sub-populate, then assign the result to the new sub-populate
1208
- Object.assign(newSubPopulate, {
1209
- on: Object.entries(subPopulate.on).reduce((acc, [type, typeSubPopulate])=>({
1210
- ...acc,
1211
- [type]: convertNestedPopulate(typeSubPopulate, getModel(type))
1212
- }), {})
1213
- });
1214
- }
1215
- // case: { count: true | false }
1216
- if (hasCountDefined(subPopulate)) {
1217
- Object.assign(newSubPopulate, {
1218
- count: subPopulate.count
1219
- });
1220
- }
1221
- return {
1222
- ...acc,
1223
- [key]: newSubPopulate
1224
- };
1225
- }
1226
- // Edge case when trying to use the fragment ('on') on a non-morph like attribute
1227
- if (!isMorphLikeRelationalAttribute && hasPopulateFragmentDefined(subPopulate)) {
1228
- throw new Error(`Using fragments is not permitted to populate "${key}" in "${schema.uid}"`);
1229
- }
1230
- // NOTE: Retrieve the target schema UID.
1231
- // Only handles basic relations, medias and component since it's not possible
1232
- // to populate with options for a dynamic zone or a polymorphic relation
1233
- let targetSchemaUID;
1234
- if (attribute.type === 'relation') {
1235
- targetSchemaUID = attribute.target;
1236
- } else if (attribute.type === 'component') {
1237
- targetSchemaUID = attribute.component;
1238
- } else if (attribute.type === 'media') {
1239
- targetSchemaUID = 'plugin::upload.file';
1240
- } else {
1241
- return acc;
1242
- }
1243
- const targetSchema = getModel(targetSchemaUID);
1244
- // ignore the sub-populate for the current key if there is no schema associated
1245
- if (!targetSchema) {
1246
- return acc;
1247
- }
1248
- const populateObject = convertNestedPopulate(subPopulate, targetSchema);
1249
- if (!populateObject) {
1250
- return acc;
1251
- }
1252
- return {
1253
- ...acc,
1254
- [key]: populateObject
1255
- };
1256
- }, {});
1257
- };
1258
- const convertNestedPopulate = (subPopulate, schema)=>{
1259
- if (___default.isString(subPopulate)) {
1260
- return parseType({
1261
- type: 'boolean',
1262
- value: subPopulate,
1263
- forceCast: true
1264
- });
1265
- }
1266
- if (___default.isBoolean(subPopulate)) {
1267
- return subPopulate;
1268
- }
1269
- if (!isPlainObject(subPopulate)) {
1270
- throw new Error(`Invalid nested populate. Expected '*' or an object`);
1271
- }
1272
- const { sort, filters, fields, populate, count, ordering, page, pageSize, start, limit } = subPopulate;
1273
- const query = {};
1274
- if (sort) {
1275
- query.orderBy = convertSortQueryParams(sort);
1276
- }
1277
- if (filters) {
1278
- query.where = convertFiltersQueryParams(filters, schema);
1279
- }
1280
- if (fields) {
1281
- query.select = convertFieldsQueryParams(fields, schema);
1282
- }
1283
- if (populate) {
1284
- query.populate = convertPopulateQueryParams(populate, schema);
1285
- }
1286
- if (count) {
1287
- query.count = convertCountQueryParams(count);
1288
- }
1289
- if (ordering) {
1290
- query.ordering = convertOrderingQueryParams(ordering);
1291
- }
1292
- validatePaginationParams(page, pageSize, start, limit);
1293
- if (!isNil(page)) {
1294
- query.page = convertPageQueryParams(page);
1295
- }
1296
- if (!isNil(pageSize)) {
1297
- query.pageSize = convertPageSizeQueryParams(pageSize, page);
1298
- }
1299
- if (!isNil(start)) {
1300
- query.offset = convertStartQueryParams(start);
1301
- }
1302
- if (!isNil(limit)) {
1303
- query.limit = convertLimitQueryParams(limit);
1304
- }
1305
- return query;
1306
- };
1307
- // TODO: ensure field is valid in content types (will probably have to check strapi.contentTypes since it can be a string.path)
1308
- const convertFieldsQueryParams = (fields, schema, depth = 0)=>{
1309
- if (depth === 0 && fields === '*') {
1310
- return undefined;
1311
- }
1312
- if (typeof fields === 'string') {
1313
- const fieldsValues = fields.split(',').map((value)=>___default.trim(value));
1314
- // NOTE: Only include the doc id if it's a content type
1315
- if (schema?.modelType === 'contentType') {
1316
- return ___default.uniq([
1317
- ID_ATTRIBUTE$3,
1318
- DOC_ID_ATTRIBUTE$3,
1319
- ...fieldsValues
1320
- ]);
1321
- }
1322
- return ___default.uniq([
1323
- ID_ATTRIBUTE$3,
1324
- ...fieldsValues
1325
- ]);
1326
- }
1327
- if (isStringArray$3(fields)) {
1328
- // map convert
1329
- const fieldsValues = fields.flatMap((value)=>convertFieldsQueryParams(value, schema, depth + 1)).filter((v)=>!isNil(v));
1330
- // NOTE: Only include the doc id if it's a content type
1331
- if (schema?.modelType === 'contentType') {
1332
- return ___default.uniq([
1333
- ID_ATTRIBUTE$3,
1334
- DOC_ID_ATTRIBUTE$3,
1335
- ...fieldsValues
1336
- ]);
1337
- }
1338
- return ___default.uniq([
1339
- ID_ATTRIBUTE$3,
1340
- ...fieldsValues
1341
- ]);
1342
- }
1343
- throw new Error('Invalid fields parameter. Expected a string or an array of strings');
1344
- };
1345
- const isValidSchemaAttribute = (key, schema)=>{
1346
- if ([
1347
- DOC_ID_ATTRIBUTE$3,
1348
- ID_ATTRIBUTE$3
1349
- ].includes(key)) {
1350
- return true;
1351
- }
1352
- if (!schema) {
1353
- return false;
1354
- }
1355
- return Object.keys(schema.attributes).includes(key);
1356
- };
1357
- const convertFiltersQueryParams = (filters, schema)=>{
1358
- // Filters need to be either an array or an object
1359
- // Here we're only checking for 'object' type since typeof [] => object and typeof {} => object
1360
- if (!isObject(filters)) {
1361
- throw new Error('The filters parameter must be an object or an array');
1362
- }
1363
- // Don't mutate the original object
1364
- const filtersCopy = cloneDeep(filters);
1365
- return convertAndSanitizeFilters(filtersCopy, schema);
1366
- };
1367
- const convertAndSanitizeFilters = (filters, schema)=>{
1368
- if (Array.isArray(filters)) {
1369
- return filters// Sanitize each filter
1370
- .map((filter)=>convertAndSanitizeFilters(filter, schema))// Filter out empty filters
1371
- .filter((filter)=>!isPlainObject(filter) || !isEmpty(filter));
1372
- }
1373
- if (!isPlainObject(filters)) {
1374
- return filters;
1375
- }
1376
- const removeOperator = (operator)=>delete filters[operator];
1377
- // Here, `key` can either be an operator or an attribute name
1378
- for (const [key, value] of Object.entries(filters)){
1379
- const attribute = get(key, schema?.attributes);
1380
- const validKey = isOperator(key) || isValidSchemaAttribute(key, schema);
1381
- if (!validKey) {
1382
- removeOperator(key);
1383
- } else if (attribute) {
1384
- // Relations
1385
- if (attribute.type === 'relation') {
1386
- filters[key] = convertAndSanitizeFilters(value, getModel(attribute.target));
1387
- } else if (attribute.type === 'component') {
1388
- filters[key] = convertAndSanitizeFilters(value, getModel(attribute.component));
1389
- } else if (attribute.type === 'media') {
1390
- filters[key] = convertAndSanitizeFilters(value, getModel('plugin::upload.file'));
1391
- } else if (attribute.type === 'dynamiczone') {
1392
- removeOperator(key);
1393
- } else if (attribute.type === 'password') {
1394
- // Always remove password attributes from filters object
1395
- removeOperator(key);
1396
- } else {
1397
- filters[key] = convertAndSanitizeFilters(value, schema);
1398
- }
1399
- } else if ([
1400
- '$null',
1401
- '$notNull'
1402
- ].includes(key)) {
1403
- filters[key] = parseType({
1404
- type: 'boolean',
1405
- value: filters[key],
1406
- forceCast: true
1407
- });
1408
- } else if (isObject(value)) {
1409
- filters[key] = convertAndSanitizeFilters(value, schema);
1410
- }
1411
- // Remove empty objects & arrays
1412
- if (isPlainObject(filters[key]) && isEmpty(filters[key])) {
1413
- removeOperator(key);
1414
- }
1415
- }
1416
- return filters;
1417
- };
1418
- const convertStatusParams = (status, query = {})=>{
1419
- // NOTE: this is the query layer filters not the document/entity service filters
1420
- query.filters = ({ meta })=>{
1421
- const contentType = getModel(meta.uid);
1422
- // Ignore if target model has disabled DP, as it doesn't make sense to filter by its status
1423
- if (!contentType || !hasDraftAndPublish(contentType)) {
1424
- return {};
1425
- }
1426
- return {
1427
- [PUBLISHED_AT_ATTRIBUTE]: {
1428
- $null: status === 'draft'
1429
- }
1430
- };
1431
- };
1432
- };
1433
- const transformQueryParams = (uid, params)=>{
1434
- // NOTE: can be a CT, a Compo or nothing in the case of polymorphism (DZ & morph relations)
1435
- const schema = getModel(uid);
1436
- const query = {};
1437
- const { _q, sort, filters, fields, populate, page, pageSize, start, limit, status, ...rest } = params;
1438
- if (!isNil(status)) {
1439
- convertStatusParams(status, query);
1440
- }
1441
- if (!isNil(_q)) {
1442
- query._q = _q;
1443
- }
1444
- if (!isNil(sort)) {
1445
- query.orderBy = convertSortQueryParams(sort);
1446
- }
1447
- if (!isNil(filters)) {
1448
- query.where = convertFiltersQueryParams(filters, schema);
1449
- }
1450
- if (!isNil(fields)) {
1451
- query.select = convertFieldsQueryParams(fields, schema);
1452
- }
1453
- if (!isNil(populate)) {
1454
- query.populate = convertPopulateQueryParams(populate, schema);
1455
- }
1456
- validatePaginationParams(page, pageSize, start, limit);
1457
- if (!isNil(page)) {
1458
- query.page = convertPageQueryParams(page);
1459
- }
1460
- if (!isNil(pageSize)) {
1461
- query.pageSize = convertPageSizeQueryParams(pageSize, page);
1462
- }
1463
- if (!isNil(start)) {
1464
- query.offset = convertStartQueryParams(start);
1465
- }
1466
- if (!isNil(limit)) {
1467
- query.limit = convertLimitQueryParams(limit);
1468
- }
1469
- return {
1470
- ...rest,
1471
- ...query
1472
- };
1473
- };
1474
- return {
1475
- private_convertSortQueryParams: convertSortQueryParams,
1476
- private_convertStartQueryParams: convertStartQueryParams,
1477
- private_convertLimitQueryParams: convertLimitQueryParams,
1478
- private_convertPopulateQueryParams: convertPopulateQueryParams,
1479
- private_convertFiltersQueryParams: convertFiltersQueryParams,
1480
- private_convertFieldsQueryParams: convertFieldsQueryParams,
1481
- transformQueryParams
1482
- };
1483
- };
1484
-
1485
- var convertQueryParams = /*#__PURE__*/Object.freeze({
1486
- __proto__: null,
1487
- createTransformer: createTransformer
1488
- });
1489
-
1490
- function pipe(...fns) {
1491
- const [firstFn, ...fnRest] = fns;
1492
- return async (...args)=>{
1493
- let res = await firstFn.apply(firstFn, args);
1494
- for(let i = 0; i < fnRest.length; i += 1){
1495
- res = await fnRest[i](res);
1496
- }
1497
- return res;
1498
- };
1499
- }
1500
- const map = curry(pMap);
1501
- const reduce = (mixedArray)=>async (iteratee, initialValue)=>{
1502
- let acc = initialValue;
1503
- for(let i = 0; i < mixedArray.length; i += 1){
1504
- acc = await iteratee(acc, await mixedArray[i], i);
1505
- }
1506
- return acc;
1507
- };
1508
-
1509
- var async = /*#__PURE__*/Object.freeze({
1510
- __proto__: null,
1511
- map: map,
1512
- pipe: pipe,
1513
- reduce: reduce
1514
- });
1515
-
1516
- const visitor$8 = ({ key, attribute }, { remove })=>{
1517
- if (attribute?.type === 'password') {
1518
- remove(key);
1519
- }
1520
- };
1521
-
1522
- const visitor$7 = ({ schema, key, attribute }, { remove })=>{
1523
- if (!attribute) {
1524
- return;
1525
- }
1526
- const isPrivate = attribute.private === true || isPrivateAttribute(schema, key);
1527
- if (isPrivate) {
1528
- remove(key);
1529
- }
1530
- };
1531
-
1532
- const MANY_RELATIONS = [
1533
- 'oneToMany',
1534
- 'manyToMany'
1535
- ];
1536
- const getRelationalFields = (contentType)=>{
1537
- return Object.keys(contentType.attributes).filter((attributeName)=>{
1538
- return contentType.attributes[attributeName].type === 'relation';
1539
- });
1540
- };
1541
- const isOneToAny = (attribute)=>isRelationalAttribute(attribute) && [
1542
- 'oneToOne',
1543
- 'oneToMany'
1544
- ].includes(attribute.relation);
1545
- const isManyToAny = (attribute)=>isRelationalAttribute(attribute) && [
1546
- 'manyToMany',
1547
- 'manyToOne'
1548
- ].includes(attribute.relation);
1549
- const isAnyToOne = (attribute)=>isRelationalAttribute(attribute) && [
1550
- 'oneToOne',
1551
- 'manyToOne'
1552
- ].includes(attribute.relation);
1553
- const isAnyToMany = (attribute)=>isRelationalAttribute(attribute) && [
1554
- 'oneToMany',
1555
- 'manyToMany'
1556
- ].includes(attribute.relation);
1557
- const isPolymorphic = (attribute)=>[
1558
- 'morphOne',
1559
- 'morphMany',
1560
- 'morphToOne',
1561
- 'morphToMany'
1562
- ].includes(attribute.relation);
1563
- const constants = {
1564
- MANY_RELATIONS
1565
- };
1566
- // Valid keys in the `options` property of relations reordering
1567
- // The value for each key must be a function that returns true if it is a valid value
1568
- const VALID_RELATION_ORDERING_KEYS = {
1569
- strict: isBoolean
1570
- };
1571
-
1572
- var relations = /*#__PURE__*/Object.freeze({
1573
- __proto__: null,
1574
- VALID_RELATION_ORDERING_KEYS: VALID_RELATION_ORDERING_KEYS,
1575
- constants: constants,
1576
- getRelationalFields: getRelationalFields,
1577
- isAnyToMany: isAnyToMany,
1578
- isAnyToOne: isAnyToOne,
1579
- isManyToAny: isManyToAny,
1580
- isOneToAny: isOneToAny,
1581
- isPolymorphic: isPolymorphic
1582
- });
1583
-
1584
- const ACTIONS_TO_VERIFY$1 = [
1585
- 'find'
1586
- ];
1587
- const { CREATED_BY_ATTRIBUTE: CREATED_BY_ATTRIBUTE$1, UPDATED_BY_ATTRIBUTE: UPDATED_BY_ATTRIBUTE$1 } = constants$1;
1588
- var removeRestrictedRelations = ((auth)=>async ({ data, key, attribute, schema }, { remove, set })=>{
1589
- if (!attribute) {
1590
- return;
1591
- }
1592
- const isRelation = attribute.type === 'relation';
1593
- if (!isRelation) {
1594
- return;
1595
- }
1596
- const handleMorphRelation = async ()=>{
1597
- const elements = data[key];
1598
- if ('connect' in elements || 'set' in elements || 'disconnect' in elements) {
1599
- const newValue = {};
1600
- const connect = await handleMorphElements(elements.connect || []);
1601
- const relSet = await handleMorphElements(elements.set || []);
1602
- const disconnect = await handleMorphElements(elements.disconnect || []);
1603
- if (connect.length > 0) {
1604
- newValue.connect = connect;
1605
- }
1606
- if (relSet.length > 0) {
1607
- newValue.set = relSet;
1608
- }
1609
- if (disconnect.length > 0) {
1610
- newValue.disconnect = disconnect;
1611
- }
1612
- // TODO: this should technically be in its own visitor to check morph options, but for now we'll handle it here
1613
- if ('options' in elements && typeof elements.options === 'object' && elements.options !== null) {
1614
- const filteredOptions = {};
1615
- // Iterate through the keys of elements.options
1616
- Object.keys(elements.options).forEach((key)=>{
1617
- const validator = VALID_RELATION_ORDERING_KEYS[key];
1618
- // Ensure the key exists in VALID_RELATION_ORDERING_KEYS and the validator is defined before calling it
1619
- if (validator && validator(elements.options[key])) {
1620
- filteredOptions[key] = elements.options[key];
1621
- }
1622
- });
1623
- // Assign the filtered options back to newValue
1624
- newValue.options = filteredOptions;
1625
- } else {
1626
- newValue.options = {};
1627
- }
1628
- set(key, newValue);
1629
- } else {
1630
- const newMorphValue = await handleMorphElements(elements);
1631
- if (newMorphValue.length) {
1632
- set(key, newMorphValue);
1633
- }
1634
- }
1635
- };
1636
- const handleMorphElements = async (elements)=>{
1637
- const allowedElements = [];
1638
- if (!isArray(elements)) {
1639
- return allowedElements;
1640
- }
1641
- for (const element of elements){
1642
- if (!isObject(element) || !('__type' in element)) {
1643
- continue;
1644
- }
1645
- const scopes = ACTIONS_TO_VERIFY$1.map((action)=>`${element.__type}.${action}`);
1646
- const isAllowed = await hasAccessToSomeScopes$1(scopes, auth);
1647
- if (isAllowed) {
1648
- allowedElements.push(element);
1649
- }
1650
- }
1651
- return allowedElements;
1652
- };
1653
- const handleRegularRelation = async ()=>{
1654
- const scopes = ACTIONS_TO_VERIFY$1.map((action)=>`${attribute.target}.${action}`);
1655
- const isAllowed = await hasAccessToSomeScopes$1(scopes, auth);
1656
- // If the authenticated user don't have access to any of the scopes, then remove the field
1657
- if (!isAllowed) {
1658
- remove(key);
1659
- }
1660
- };
1661
- const isCreatorRelation = [
1662
- CREATED_BY_ATTRIBUTE$1,
1663
- UPDATED_BY_ATTRIBUTE$1
1664
- ].includes(key);
1665
- // Polymorphic relations
1666
- if (isMorphToRelationalAttribute(attribute)) {
1667
- await handleMorphRelation();
1668
- return;
1669
- }
1670
- // Creator relations
1671
- if (isCreatorRelation && schema.options?.populateCreatorFields) {
1672
- // do nothing
1673
- return;
1674
- }
1675
- // Regular relations
1676
- await handleRegularRelation();
1677
- });
1678
- const hasAccessToSomeScopes$1 = async (scopes, auth)=>{
1679
- for (const scope of scopes){
1680
- try {
1681
- await strapi.auth.verify(auth, {
1682
- scope
1683
- });
1684
- return true;
1685
- } catch {
1686
- continue;
1687
- }
1688
- }
1689
- return false;
1690
- };
1691
-
1692
- const visitor$6 = ({ key, attribute }, { remove })=>{
1693
- if (isMorphToRelationalAttribute(attribute)) {
1694
- remove(key);
1695
- }
1696
- };
1697
-
1698
- const visitor$5 = ({ key, attribute }, { remove })=>{
1699
- if (isDynamicZoneAttribute(attribute)) {
1700
- remove(key);
1701
- }
1702
- };
1703
-
1704
- var removeDisallowedFields = ((allowedFields = null)=>({ key, path: { attribute: path } }, { remove })=>{
1705
- // All fields are allowed
1706
- if (allowedFields === null) {
1707
- return;
1708
- }
1709
- // Throw on invalid formats
1710
- if (!(isArray(allowedFields) && allowedFields.every(isString))) {
1711
- throw new TypeError(`Expected array of strings for allowedFields but got "${typeof allowedFields}"`);
1712
- }
1713
- if (isNil(path)) {
1714
- return;
1715
- }
1716
- const containedPaths = getContainedPaths$1(path);
1717
- /**
1718
- * Tells if the current path should be kept or not based
1719
- * on the success of the check functions for any of the allowed paths.
1720
- *
1721
- * The check functions are defined as follow:
1722
- *
1723
- * `containedPaths.includes(p)`
1724
- * @example
1725
- * ```js
1726
- * const path = 'foo.bar.field';
1727
- * const p = 'foo.bar';
1728
- * // it should match
1729
- *
1730
- * const path = 'foo.bar.field';
1731
- * const p = 'bar.foo';
1732
- * // it shouldn't match
1733
- *
1734
- * const path = 'foo.bar';
1735
- * const p = 'foo.bar.field';
1736
- * // it should match but isn't handled by this check
1737
- * ```
1738
- *
1739
- * `p.startsWith(`${path}.`)`
1740
- * @example
1741
- * ```js
1742
- * const path = 'foo.bar';
1743
- * const p = 'foo.bar.field';
1744
- * // it should match
1745
- *
1746
- * const path = 'foo.bar.field';
1747
- * const p = 'bar.foo';
1748
- * // it shouldn't match
1749
- *
1750
- * const path = 'foo.bar.field';
1751
- * const p = 'foo.bar';
1752
- * // it should match but isn't handled by this check
1753
- * ```
1754
- */ const isPathAllowed = allowedFields.some((p)=>containedPaths.includes(p) || p.startsWith(`${path}.`));
1755
- if (isPathAllowed) {
1756
- return;
1757
- }
1758
- // Remove otherwise
1759
- remove(key);
1760
- });
1761
- /**
1762
- * Retrieve the list of allowed paths based on the given path
1763
- *
1764
- * @example
1765
- * ```js
1766
- * const containedPaths = getContainedPaths('foo');
1767
- * // ['foo']
1768
- *
1769
- * * const containedPaths = getContainedPaths('foo.bar');
1770
- * // ['foo', 'foo.bar']
1771
- *
1772
- * * const containedPaths = getContainedPaths('foo.bar.field');
1773
- * // ['foo', 'foo.bar', 'foo.bar.field']
1774
- * ```
1775
- */ const getContainedPaths$1 = (path)=>{
1776
- const parts = toPath(path);
1777
- return parts.reduce((acc, value, index, list)=>{
1778
- return [
1779
- ...acc,
1780
- list.slice(0, index + 1).join('.')
1781
- ];
1782
- }, []);
1783
- };
1784
-
1785
- var removeRestrictedFields = ((restrictedFields = null)=>({ key, path: { attribute: path } }, { remove })=>{
1786
- // Remove all fields
1787
- if (restrictedFields === null) {
1788
- remove(key);
1789
- return;
1790
- }
1791
- // Throw on invalid formats
1792
- if (!(isArray(restrictedFields) && restrictedFields.every(isString))) {
1793
- throw new TypeError(`Expected array of strings for restrictedFields but got "${typeof restrictedFields}"`);
1794
- }
1795
- // Remove if an exact match was found
1796
- if (restrictedFields.includes(path)) {
1797
- remove(key);
1798
- return;
1799
- }
1800
- // Remove nested matches
1801
- const isRestrictedNested = restrictedFields.some((allowedPath)=>path?.toString().startsWith(`${allowedPath}.`));
1802
- if (isRestrictedNested) {
1803
- remove(key);
1804
- }
1805
- });
1806
-
1807
- const visitor$4 = ({ schema, key, value }, { set })=>{
1808
- if (key === '' && value === '*') {
1809
- const { attributes } = schema;
1810
- const newPopulateQuery = Object.entries(attributes).filter(([, attribute])=>[
1811
- 'relation',
1812
- 'component',
1813
- 'media',
1814
- 'dynamiczone'
1815
- ].includes(attribute.type)).reduce((acc, [key])=>({
1816
- ...acc,
1817
- [key]: true
1818
- }), {});
1819
- set('', newPopulateQuery);
1820
- }
1821
- };
1822
-
1823
- var index$4 = /*#__PURE__*/Object.freeze({
1824
- __proto__: null,
1825
- expandWildcardPopulate: visitor$4,
1826
- removeDisallowedFields: removeDisallowedFields,
1827
- removeDynamicZones: visitor$5,
1828
- removeMorphToRelations: visitor$6,
1829
- removePassword: visitor$8,
1830
- removePrivate: visitor$7,
1831
- removeRestrictedFields: removeRestrictedFields,
1832
- removeRestrictedRelations: removeRestrictedRelations
1833
- });
1834
-
1835
- const DEFAULT_PATH = {
1836
- raw: null,
1837
- attribute: null
1838
- };
1839
- var traverseFactory = (()=>{
1840
- const state = {
1841
- parsers: [],
1842
- interceptors: [],
1843
- ignore: [],
1844
- handlers: {
1845
- attributes: [],
1846
- common: []
1847
- }
1848
- };
1849
- const traverse = async (visitor, options, data)=>{
1850
- const { path = DEFAULT_PATH, parent, schema, getModel } = options ?? {};
1851
- // interceptors
1852
- for (const { predicate, handler } of state.interceptors){
1853
- if (predicate(data)) {
1854
- return handler(visitor, options, data, {
1855
- recurse: traverse
1856
- });
1857
- }
1858
- }
1859
- // parsers
1860
- const parser = state.parsers.find((parser)=>parser.predicate(data))?.parser;
1861
- const utils = parser?.(data);
1862
- // Return the data untouched if we don't know how to traverse it
1863
- if (!utils) {
1864
- return data;
1865
- }
1866
- // main loop
1867
- let out = utils.transform(data);
1868
- const keys = utils.keys(out);
1869
- for (const key of keys){
1870
- const attribute = schema?.attributes?.[key];
1871
- const newPath = {
1872
- ...path
1873
- };
1874
- newPath.raw = isNil(path.raw) ? key : `${path.raw}.${key}`;
1875
- if (!isNil(attribute)) {
1876
- newPath.attribute = isNil(path.attribute) ? key : `${path.attribute}.${key}`;
1877
- }
1878
- // visitors
1879
- const visitorOptions = {
1880
- key,
1881
- value: utils.get(key, out),
1882
- attribute,
1883
- schema,
1884
- path: newPath,
1885
- data: out,
1886
- getModel,
1887
- parent
1888
- };
1889
- const transformUtils = {
1890
- remove (key) {
1891
- out = utils.remove(key, out);
1892
- },
1893
- set (key, value) {
1894
- out = utils.set(key, value, out);
1895
- },
1896
- recurse: traverse
1897
- };
1898
- await visitor(visitorOptions, pick([
1899
- 'remove',
1900
- 'set'
1901
- ], transformUtils));
1902
- const value = utils.get(key, out);
1903
- const createContext = ()=>({
1904
- key,
1905
- value,
1906
- attribute,
1907
- schema,
1908
- path: newPath,
1909
- data: out,
1910
- visitor,
1911
- getModel,
1912
- parent
1913
- });
1914
- // ignore
1915
- const ignoreCtx = createContext();
1916
- const shouldIgnore = state.ignore.some((predicate)=>predicate(ignoreCtx));
1917
- if (shouldIgnore) {
1918
- continue;
1919
- }
1920
- // handlers
1921
- const handlers = [
1922
- ...state.handlers.common,
1923
- ...state.handlers.attributes
1924
- ];
1925
- for await (const handler of handlers){
1926
- const ctx = createContext();
1927
- const pass = await handler.predicate(ctx);
1928
- if (pass) {
1929
- await handler.handler(ctx, pick([
1930
- 'recurse',
1931
- 'set'
1932
- ], transformUtils));
1933
- }
1934
- }
1935
- }
1936
- return out;
1937
- };
1938
- return {
1939
- traverse,
1940
- intercept (predicate, handler) {
1941
- state.interceptors.push({
1942
- predicate,
1943
- handler
1944
- });
1945
- return this;
1946
- },
1947
- parse (predicate, parser) {
1948
- state.parsers.push({
1949
- predicate,
1950
- parser
1951
- });
1952
- return this;
1953
- },
1954
- ignore (predicate) {
1955
- state.ignore.push(predicate);
1956
- return this;
1957
- },
1958
- on (predicate, handler) {
1959
- state.handlers.common.push({
1960
- predicate,
1961
- handler
1962
- });
1963
- return this;
1964
- },
1965
- onAttribute (predicate, handler) {
1966
- state.handlers.attributes.push({
1967
- predicate,
1968
- handler
1969
- });
1970
- return this;
1971
- },
1972
- onRelation (handler) {
1973
- return this.onAttribute(({ attribute })=>attribute?.type === 'relation', handler);
1974
- },
1975
- onMedia (handler) {
1976
- return this.onAttribute(({ attribute })=>attribute?.type === 'media', handler);
1977
- },
1978
- onComponent (handler) {
1979
- return this.onAttribute(({ attribute })=>attribute?.type === 'component', handler);
1980
- },
1981
- onDynamicZone (handler) {
1982
- return this.onAttribute(({ attribute })=>attribute?.type === 'dynamiczone', handler);
1983
- }
1984
- };
1985
- });
1986
-
1987
- const isObj$2 = (value)=>isObject(value);
1988
- const filters = traverseFactory().intercept(// Intercept filters arrays and apply the traversal to each one individually
1989
- isArray, async (visitor, options, filters, { recurse })=>{
1990
- return Promise.all(filters.map((filter, i)=>{
1991
- // In filters, only operators such as $and, $in, $notIn or $or and implicit operators like [...]
1992
- // can have a value array, thus we can update the raw path but not the attribute one
1993
- const newPath = options.path ? {
1994
- ...options.path,
1995
- raw: `${options.path.raw}[${i}]`
1996
- } : options.path;
1997
- return recurse(visitor, {
1998
- ...options,
1999
- path: newPath
2000
- }, filter);
2001
- })).then((res)=>res.filter((val)=>!(isObject(val) && isEmpty(val))));
2002
- }).intercept(// Ignore non object filters and return the value as-is
2003
- (filters)=>!isObject(filters), (_, __, filters)=>{
2004
- return filters;
2005
- })// Parse object values
2006
- .parse(isObj$2, ()=>({
2007
- transform: cloneDeep,
2008
- remove (key, data) {
2009
- return omit(key, data);
2010
- },
2011
- set (key, value, data) {
2012
- return {
2013
- ...data,
2014
- [key]: value
2015
- };
2016
- },
2017
- keys (data) {
2018
- return Object.keys(data);
2019
- },
2020
- get (key, data) {
2021
- return data[key];
2022
- }
2023
- }))// Ignore null or undefined values
2024
- .ignore(({ value })=>isNil(value))// Recursion on operators (non attributes)
2025
- .on(({ attribute })=>isNil(attribute), async ({ key, visitor, path, value, schema, getModel, attribute }, { set, recurse })=>{
2026
- const parent = {
2027
- key,
2028
- path,
2029
- schema,
2030
- attribute
2031
- };
2032
- set(key, await recurse(visitor, {
2033
- schema,
2034
- path,
2035
- getModel,
2036
- parent
2037
- }, value));
2038
- })// Handle relation recursion
2039
- .onRelation(async ({ key, attribute, visitor, path, value, schema, getModel }, { set, recurse })=>{
2040
- const isMorphRelation = attribute.relation.toLowerCase().startsWith('morph');
2041
- if (isMorphRelation) {
2042
- return;
2043
- }
2044
- const parent = {
2045
- key,
2046
- path,
2047
- schema,
2048
- attribute
2049
- };
2050
- const targetSchemaUID = attribute.target;
2051
- const targetSchema = getModel(targetSchemaUID);
2052
- const newValue = await recurse(visitor, {
2053
- schema: targetSchema,
2054
- path,
2055
- getModel,
2056
- parent
2057
- }, value);
2058
- set(key, newValue);
2059
- }).onComponent(async ({ key, attribute, visitor, path, schema, value, getModel }, { set, recurse })=>{
2060
- const parent = {
2061
- key,
2062
- path,
2063
- schema,
2064
- attribute
2065
- };
2066
- const targetSchema = getModel(attribute.component);
2067
- const newValue = await recurse(visitor, {
2068
- schema: targetSchema,
2069
- path,
2070
- getModel,
2071
- parent
2072
- }, value);
2073
- set(key, newValue);
2074
- })// Handle media recursion
2075
- .onMedia(async ({ key, visitor, path, schema, attribute, value, getModel }, { set, recurse })=>{
2076
- const parent = {
2077
- key,
2078
- path,
2079
- schema,
2080
- attribute
2081
- };
2082
- const targetSchemaUID = 'plugin::upload.file';
2083
- const targetSchema = getModel(targetSchemaUID);
2084
- const newValue = await recurse(visitor, {
2085
- schema: targetSchema,
2086
- path,
2087
- getModel,
2088
- parent
2089
- }, value);
2090
- set(key, newValue);
2091
- });
2092
- var traverseQueryFilters = curry(filters.traverse);
2093
-
2094
- const ORDERS = {
2095
- asc: 'asc',
2096
- desc: 'desc'
2097
- };
2098
- const ORDER_VALUES = Object.values(ORDERS);
2099
- const isSortOrder = (value)=>ORDER_VALUES.includes(value.toLowerCase());
2100
- const isStringArray$2 = (value)=>Array.isArray(value) && value.every(isString);
2101
- const isObjectArray = (value)=>Array.isArray(value) && value.every(isObject);
2102
- const isNestedSorts = (value)=>isString(value) && value.split(',').length > 1;
2103
- const isObj$1 = (value)=>isObject(value);
2104
- const sort = traverseFactory().intercept(// String with chained sorts (foo,bar,foobar) => split, map(recurse), then recompose
2105
- isNestedSorts, async (visitor, options, sort, { recurse })=>{
2106
- return Promise.all(sort.split(',').map(trim).map((nestedSort)=>recurse(visitor, options, nestedSort))).then((res)=>res.filter((part)=>!isEmpty(part)).join(','));
2107
- }).intercept(// Array of strings ['foo', 'foo,bar'] => map(recurse), then filter out empty items
2108
- isStringArray$2, async (visitor, options, sort, { recurse })=>{
2109
- return Promise.all(sort.map((nestedSort)=>recurse(visitor, options, nestedSort))).then((res)=>res.filter((nestedSort)=>!isEmpty(nestedSort)));
2110
- }).intercept(// Array of objects [{ foo: 'asc' }, { bar: 'desc', baz: 'asc' }] => map(recurse), then filter out empty items
2111
- isObjectArray, async (visitor, options, sort, { recurse })=>{
2112
- return Promise.all(sort.map((nestedSort)=>recurse(visitor, options, nestedSort))).then((res)=>res.filter((nestedSort)=>!isEmpty(nestedSort)));
2113
- })// Parse string values
2114
- .parse(isString, ()=>{
2115
- const tokenize = pipe$1(split('.'), map$1(split(':')), flatten);
2116
- const recompose = (parts)=>{
2117
- if (parts.length === 0) {
2118
- return undefined;
2119
- }
2120
- return parts.reduce((acc, part)=>{
2121
- if (isEmpty(part)) {
2122
- return acc;
2123
- }
2124
- if (acc === '') {
2125
- return part;
2126
- }
2127
- return isSortOrder(part) ? `${acc}:${part}` : `${acc}.${part}`;
2128
- }, '');
2129
- };
2130
- return {
2131
- transform: trim,
2132
- remove (key, data) {
2133
- const [root] = tokenize(data);
2134
- return root === key ? undefined : data;
2135
- },
2136
- set (key, value, data) {
2137
- const [root] = tokenize(data);
2138
- if (root !== key) {
2139
- return data;
2140
- }
2141
- return isNil(value) ? root : `${root}.${value}`;
2142
- },
2143
- keys (data) {
2144
- const v = first(tokenize(data));
2145
- return v ? [
2146
- v
2147
- ] : [];
2148
- },
2149
- get (key, data) {
2150
- const [root, ...rest] = tokenize(data);
2151
- return key === root ? recompose(rest) : undefined;
2152
- }
2153
- };
2154
- })// Parse object values
2155
- .parse(isObj$1, ()=>({
2156
- transform: cloneDeep,
2157
- remove (key, data) {
2158
- // eslint-disable-next-line no-unused-vars
2159
- const { [key]: ignored, ...rest } = data;
2160
- return rest;
2161
- },
2162
- set (key, value, data) {
2163
- return {
2164
- ...data,
2165
- [key]: value
2166
- };
2167
- },
2168
- keys (data) {
2169
- return Object.keys(data);
2170
- },
2171
- get (key, data) {
2172
- return data[key];
2173
- }
2174
- }))// Handle deep sort on relation
2175
- .onRelation(async ({ key, value, attribute, visitor, path, getModel, schema }, { set, recurse })=>{
2176
- const isMorphRelation = attribute.relation.toLowerCase().startsWith('morph');
2177
- if (isMorphRelation) {
2178
- return;
2179
- }
2180
- const parent = {
2181
- key,
2182
- path,
2183
- schema,
2184
- attribute
2185
- };
2186
- const targetSchemaUID = attribute.target;
2187
- const targetSchema = getModel(targetSchemaUID);
2188
- const newValue = await recurse(visitor, {
2189
- schema: targetSchema,
2190
- path,
2191
- getModel,
2192
- parent
2193
- }, value);
2194
- set(key, newValue);
2195
- })// Handle deep sort on media
2196
- .onMedia(async ({ key, path, schema, attribute, visitor, value, getModel }, { recurse, set })=>{
2197
- const parent = {
2198
- key,
2199
- path,
2200
- schema,
2201
- attribute
2202
- };
2203
- const targetSchemaUID = 'plugin::upload.file';
2204
- const targetSchema = getModel(targetSchemaUID);
2205
- const newValue = await recurse(visitor, {
2206
- schema: targetSchema,
2207
- path,
2208
- getModel,
2209
- parent
2210
- }, value);
2211
- set(key, newValue);
2212
- })// Handle deep sort on components
2213
- .onComponent(async ({ key, value, visitor, path, schema, attribute, getModel }, { recurse, set })=>{
2214
- const parent = {
2215
- key,
2216
- path,
2217
- schema,
2218
- attribute
2219
- };
2220
- const targetSchema = getModel(attribute.component);
2221
- const newValue = await recurse(visitor, {
2222
- schema: targetSchema,
2223
- path,
2224
- getModel,
2225
- parent
2226
- }, value);
2227
- set(key, newValue);
2228
- });
2229
- var traverseQuerySort = curry(sort.traverse);
2230
-
2231
- const isKeyword = (keyword)=>{
2232
- return ({ key, attribute })=>{
2233
- return !attribute && keyword === key;
2234
- };
2235
- };
2236
- const isWildcard = (value)=>value === '*';
2237
- const isPopulateString = (value)=>{
2238
- return isString(value) && !isWildcard(value);
2239
- };
2240
- const isStringArray$1 = (value)=>isArray(value) && value.every(isString);
2241
- const isObj = (value)=>isObject(value);
2242
- const populate = traverseFactory().intercept(isPopulateString, async (visitor, options, populate, { recurse })=>{
2243
- /**
2244
- * Ensure the populate clause its in the extended format ( { populate: { ... } }, and not just a string)
2245
- * This gives a consistent structure to track the "parent" node of each nested populate clause
2246
- */ const populateObject = pathsToObjectPopulate([
2247
- populate
2248
- ]);
2249
- const traversedPopulate = await recurse(visitor, options, populateObject);
2250
- const [result] = objectPopulateToPaths(traversedPopulate);
2251
- return result;
2252
- })// Array of strings ['foo', 'bar.baz'] => map(recurse), then filter out empty items
2253
- .intercept(isStringArray$1, async (visitor, options, populate, { recurse })=>{
2254
- const paths = await Promise.all(populate.map((subClause)=>recurse(visitor, options, subClause)));
2255
- return paths.filter((item)=>!isNil(item));
2256
- })// for wildcard, generate custom utilities to modify the values
2257
- .parse(isWildcard, ()=>({
2258
- /**
2259
- * Since value is '*', we don't need to transform it
2260
- */ transform: identity,
2261
- /**
2262
- * '*' isn't a key/value structure, so regardless
2263
- * of the given key, it returns the data ('*')
2264
- */ get: (_key, data)=>data,
2265
- /**
2266
- * '*' isn't a key/value structure, so regardless
2267
- * of the given `key`, use `value` as the new `data`
2268
- */ set: (_key, value)=>value,
2269
- /**
2270
- * '*' isn't a key/value structure, but we need to simulate at least one to enable
2271
- * the data traversal. We're using '' since it represents a falsy string value
2272
- */ keys: constant([
2273
- ''
2274
- ]),
2275
- /**
2276
- * Removing '*' means setting it to undefined, regardless of the given key
2277
- */ remove: constant(undefined)
2278
- }))// Parse string values
2279
- .parse(isString, ()=>{
2280
- const tokenize = split('.');
2281
- const recompose = join('.');
2282
- return {
2283
- transform: trim,
2284
- remove (key, data) {
2285
- const [root] = tokenize(data);
2286
- return root === key ? undefined : data;
2287
- },
2288
- set (key, value, data) {
2289
- const [root] = tokenize(data);
2290
- if (root !== key) {
2291
- return data;
2292
- }
2293
- return isNil(value) || isEmpty(value) ? root : `${root}.${value}`;
2294
- },
2295
- keys (data) {
2296
- const v = first(tokenize(data));
2297
- return v ? [
2298
- v
2299
- ] : [];
2300
- },
2301
- get (key, data) {
2302
- const [root, ...rest] = tokenize(data);
2303
- return key === root ? recompose(rest) : undefined;
2304
- }
2305
- };
2306
- })// Parse object values
2307
- .parse(isObj, ()=>({
2308
- transform: cloneDeep,
2309
- remove (key, data) {
2310
- // eslint-disable-next-line no-unused-vars
2311
- const { [key]: ignored, ...rest } = data;
2312
- return rest;
2313
- },
2314
- set (key, value, data) {
2315
- return {
2316
- ...data,
2317
- [key]: value
2318
- };
2319
- },
2320
- keys (data) {
2321
- return Object.keys(data);
2322
- },
2323
- get (key, data) {
2324
- return data[key];
2325
- }
2326
- })).ignore(({ key, attribute })=>{
2327
- // we don't want to recurse using traversePopulate and instead let
2328
- // the visitors recurse with the appropriate traversal (sort, filters, etc...)
2329
- return [
2330
- 'sort',
2331
- 'filters',
2332
- 'fields'
2333
- ].includes(key) && !attribute;
2334
- }).on(// Handle recursion on populate."populate"
2335
- isKeyword('populate'), async ({ key, visitor, path, value, schema, getModel, attribute }, { set, recurse })=>{
2336
- const parent = {
2337
- key,
2338
- path,
2339
- schema,
2340
- attribute
2341
- };
2342
- const newValue = await recurse(visitor, {
2343
- schema,
2344
- path,
2345
- getModel,
2346
- parent
2347
- }, value);
2348
- set(key, newValue);
2349
- }).on(isKeyword('on'), async ({ key, visitor, path, value, getModel, parent }, { set, recurse })=>{
2350
- const newOn = {};
2351
- if (!isObj(value)) {
2352
- return;
2353
- }
2354
- for (const [uid, subPopulate] of Object.entries(value)){
2355
- const model = getModel(uid);
2356
- const newPath = {
2357
- ...path,
2358
- raw: `${path.raw}[${uid}]`
2359
- };
2360
- newOn[uid] = await recurse(visitor, {
2361
- schema: model,
2362
- path: newPath,
2363
- getModel,
2364
- parent
2365
- }, subPopulate);
2366
- }
2367
- set(key, newOn);
2368
- })// Handle populate on relation
2369
- .onRelation(async ({ key, value, attribute, visitor, path, schema, getModel }, { set, recurse })=>{
2370
- if (isNil(value)) {
2371
- return;
2372
- }
2373
- const parent = {
2374
- key,
2375
- path,
2376
- schema,
2377
- attribute
2378
- };
2379
- if (isMorphToRelationalAttribute(attribute)) {
2380
- // Don't traverse values that cannot be parsed
2381
- if (!isObject(value) || !('on' in value && isObject(value?.on))) {
2382
- return;
2383
- }
2384
- // If there is a populate fragment defined, traverse it
2385
- const newValue = await recurse(visitor, {
2386
- schema,
2387
- path,
2388
- getModel,
2389
- parent
2390
- }, {
2391
- on: value?.on
2392
- });
2393
- set(key, newValue);
2394
- return;
2395
- }
2396
- const targetSchemaUID = attribute.target;
2397
- const targetSchema = getModel(targetSchemaUID);
2398
- const newValue = await recurse(visitor, {
2399
- schema: targetSchema,
2400
- path,
2401
- getModel,
2402
- parent
2403
- }, value);
2404
- set(key, newValue);
2405
- })// Handle populate on media
2406
- .onMedia(async ({ key, path, schema, attribute, visitor, value, getModel }, { recurse, set })=>{
2407
- if (isNil(value)) {
2408
- return;
2409
- }
2410
- const parent = {
2411
- key,
2412
- path,
2413
- schema,
2414
- attribute
2415
- };
2416
- const targetSchemaUID = 'plugin::upload.file';
2417
- const targetSchema = getModel(targetSchemaUID);
2418
- const newValue = await recurse(visitor, {
2419
- schema: targetSchema,
2420
- path,
2421
- getModel,
2422
- parent
2423
- }, value);
2424
- set(key, newValue);
2425
- })// Handle populate on components
2426
- .onComponent(async ({ key, value, schema, visitor, path, attribute, getModel }, { recurse, set })=>{
2427
- if (isNil(value)) {
2428
- return;
2429
- }
2430
- const parent = {
2431
- key,
2432
- path,
2433
- schema,
2434
- attribute
2435
- };
2436
- const targetSchema = getModel(attribute.component);
2437
- const newValue = await recurse(visitor, {
2438
- schema: targetSchema,
2439
- path,
2440
- getModel,
2441
- parent
2442
- }, value);
2443
- set(key, newValue);
2444
- })// Handle populate on dynamic zones
2445
- .onDynamicZone(async ({ key, value, schema, visitor, path, attribute, getModel }, { set, recurse })=>{
2446
- if (isNil(value) || !isObject(value)) {
2447
- return;
2448
- }
2449
- const parent = {
2450
- key,
2451
- path,
2452
- schema,
2453
- attribute
2454
- };
2455
- // Handle fragment syntax
2456
- if ('on' in value && value.on) {
2457
- const newOn = await recurse(visitor, {
2458
- schema,
2459
- path,
2460
- getModel,
2461
- parent
2462
- }, {
2463
- on: value.on
2464
- });
2465
- set(key, newOn);
2466
- }
2467
- });
2468
- var traverseQueryPopulate = curry(populate.traverse);
2469
- const objectPopulateToPaths = (input)=>{
2470
- const paths = [];
2471
- function traverse(currentObj, parentPath) {
2472
- for (const [key, value] of Object.entries(currentObj)){
2473
- const currentPath = parentPath ? `${parentPath}.${key}` : key;
2474
- if (value === true) {
2475
- paths.push(currentPath);
2476
- } else {
2477
- traverse(value.populate, currentPath);
2478
- }
2479
- }
2480
- }
2481
- traverse(input, '');
2482
- return paths;
2483
- };
2484
- const pathsToObjectPopulate = (input)=>{
2485
- const result = {};
2486
- function traverse(object, keys) {
2487
- const [first, ...rest] = keys;
2488
- if (rest.length === 0) {
2489
- object[first] = true;
2490
- } else {
2491
- if (!object[first] || typeof object[first] === 'boolean') {
2492
- object[first] = {
2493
- populate: {}
2494
- };
2495
- }
2496
- traverse(object[first].populate, rest);
2497
- }
2498
- }
2499
- input.forEach((clause)=>traverse(result, clause.split('.')));
2500
- return result;
2501
- };
2502
-
2503
- const isStringArray = (value)=>{
2504
- return isArray(value) && value.every(isString);
2505
- };
2506
- const fields = traverseFactory()// Intercept array of strings
2507
- // e.g. fields=['title', 'description']
2508
- .intercept(isStringArray, async (visitor, options, fields, { recurse })=>{
2509
- return Promise.all(fields.map((field)=>recurse(visitor, options, field)));
2510
- })// Intercept comma separated fields (as string)
2511
- // e.g. fields='title,description'
2512
- .intercept((value)=>isString(value) && value.includes(','), (visitor, options, fields, { recurse })=>{
2513
- return Promise.all(fields.split(',').map((field)=>recurse(visitor, options, field)));
2514
- })// Return wildcards as is
2515
- .intercept((value)=>eq('*', value), constant('*'))// Parse string values
2516
- // Since we're parsing strings only, each value should be an attribute name (and it's value, undefined),
2517
- // thus it shouldn't be possible to set a new value, and get should return the whole data if key === data
2518
- .parse(isString, ()=>({
2519
- transform: trim,
2520
- remove (key, data) {
2521
- return data === key ? undefined : data;
2522
- },
2523
- set (_key, _value, data) {
2524
- return data;
2525
- },
2526
- keys (data) {
2527
- return [
2528
- data
2529
- ];
2530
- },
2531
- get (key, data) {
2532
- return key === data ? data : undefined;
2533
- }
2534
- }));
2535
- var traverseQueryFields = curry(fields.traverse);
2536
-
2537
- var index$3 = /*#__PURE__*/Object.freeze({
2538
- __proto__: null,
2539
- traverseQueryFields: traverseQueryFields,
2540
- traverseQueryFilters: traverseQueryFilters,
2541
- traverseQueryPopulate: traverseQueryPopulate,
2542
- traverseQuerySort: traverseQuerySort
2543
- });
2544
-
2545
- const { ID_ATTRIBUTE: ID_ATTRIBUTE$2, DOC_ID_ATTRIBUTE: DOC_ID_ATTRIBUTE$2 } = constants$1;
2546
- const sanitizePasswords = (ctx)=>async (entity)=>{
2547
- if (!ctx.schema) {
2548
- throw new Error('Missing schema in sanitizePasswords');
2549
- }
2550
- return traverseEntity$1(visitor$8, ctx, entity);
2551
- };
2552
- const defaultSanitizeOutput = async (ctx, entity)=>{
2553
- if (!ctx.schema) {
2554
- throw new Error('Missing schema in defaultSanitizeOutput');
2555
- }
2556
- return traverseEntity$1((...args)=>{
2557
- visitor$8(...args);
2558
- visitor$7(...args);
2559
- }, ctx, entity);
2560
- };
2561
- const defaultSanitizeFilters = curry((ctx, filters)=>{
2562
- if (!ctx.schema) {
2563
- throw new Error('Missing schema in defaultSanitizeFilters');
2564
- }
2565
- return pipe(// Remove keys that are not attributes or valid operators
2566
- traverseQueryFilters(({ key, attribute }, { remove })=>{
2567
- const isAttribute = !!attribute;
2568
- // ID is not an attribute per se, so we need to make
2569
- // an extra check to ensure we're not checking it
2570
- if ([
2571
- ID_ATTRIBUTE$2,
2572
- DOC_ID_ATTRIBUTE$2
2573
- ].includes(key)) {
2574
- return;
2575
- }
2576
- if (!isAttribute && !isOperator(key)) {
2577
- remove(key);
2578
- }
2579
- }, ctx), // Remove dynamic zones from filters
2580
- traverseQueryFilters(visitor$5, ctx), // Remove morpTo relations from filters
2581
- traverseQueryFilters(visitor$6, ctx), // Remove passwords from filters
2582
- traverseQueryFilters(visitor$8, ctx), // Remove private from filters
2583
- traverseQueryFilters(visitor$7, ctx), // Remove empty objects
2584
- traverseQueryFilters(({ key, value }, { remove })=>{
2585
- if (isObject(value) && isEmpty(value)) {
2586
- remove(key);
2587
- }
2588
- }, ctx))(filters);
2589
- });
2590
- const defaultSanitizeSort = curry((ctx, sort)=>{
2591
- if (!ctx.schema) {
2592
- throw new Error('Missing schema in defaultSanitizeSort');
2593
- }
2594
- return pipe(// Remove non attribute keys
2595
- traverseQuerySort(({ key, attribute }, { remove })=>{
2596
- // ID is not an attribute per se, so we need to make
2597
- // an extra check to ensure we're not checking it
2598
- if ([
2599
- ID_ATTRIBUTE$2,
2600
- DOC_ID_ATTRIBUTE$2
2601
- ].includes(key)) {
2602
- return;
2603
- }
2604
- if (!attribute) {
2605
- remove(key);
2606
- }
2607
- }, ctx), // Remove dynamic zones from sort
2608
- traverseQuerySort(visitor$5, ctx), // Remove morpTo relations from sort
2609
- traverseQuerySort(visitor$6, ctx), // Remove private from sort
2610
- traverseQuerySort(visitor$7, ctx), // Remove passwords from filters
2611
- traverseQuerySort(visitor$8, ctx), // Remove keys for empty non-scalar values
2612
- traverseQuerySort(({ key, attribute, value }, { remove })=>{
2613
- // ID is not an attribute per se, so we need to make
2614
- // an extra check to ensure we're not removing it
2615
- if ([
2616
- ID_ATTRIBUTE$2,
2617
- DOC_ID_ATTRIBUTE$2
2618
- ].includes(key)) {
2619
- return;
2620
- }
2621
- if (!isScalarAttribute(attribute) && isEmpty(value)) {
2622
- remove(key);
2623
- }
2624
- }, ctx))(sort);
2625
- });
2626
- const defaultSanitizeFields = curry((ctx, fields)=>{
2627
- if (!ctx.schema) {
2628
- throw new Error('Missing schema in defaultSanitizeFields');
2629
- }
2630
- return pipe(// Only keep scalar attributes
2631
- traverseQueryFields(({ key, attribute }, { remove })=>{
2632
- // ID is not an attribute per se, so we need to make
2633
- // an extra check to ensure we're not checking it
2634
- if ([
2635
- ID_ATTRIBUTE$2,
2636
- DOC_ID_ATTRIBUTE$2
2637
- ].includes(key)) {
2638
- return;
2639
- }
2640
- if (isNil(attribute) || !isScalarAttribute(attribute)) {
2641
- remove(key);
2642
- }
2643
- }, ctx), // Remove private fields
2644
- traverseQueryFields(visitor$7, ctx), // Remove password fields
2645
- traverseQueryFields(visitor$8, ctx), // Remove nil values from fields array
2646
- (value)=>isArray(value) ? value.filter((field)=>!isNil(field)) : value)(fields);
2647
- });
2648
- const defaultSanitizePopulate = curry((ctx, populate)=>{
2649
- if (!ctx.schema) {
2650
- throw new Error('Missing schema in defaultSanitizePopulate');
2651
- }
2652
- return pipe(traverseQueryPopulate(visitor$4, ctx), traverseQueryPopulate(async ({ key, value, schema, attribute, getModel, path }, { set })=>{
2653
- if (attribute) {
2654
- return;
2655
- }
2656
- const parent = {
2657
- key,
2658
- path,
2659
- schema,
2660
- attribute
2661
- };
2662
- if (key === 'sort') {
2663
- set(key, await defaultSanitizeSort({
2664
- schema,
2665
- getModel,
2666
- parent
2667
- }, value));
2668
- }
2669
- if (key === 'filters') {
2670
- set(key, await defaultSanitizeFilters({
2671
- schema,
2672
- getModel,
2673
- parent
2674
- }, value));
2675
- }
2676
- if (key === 'fields') {
2677
- set(key, await defaultSanitizeFields({
2678
- schema,
2679
- getModel,
2680
- parent
2681
- }, value));
2682
- }
2683
- if (key === 'populate') {
2684
- set(key, await defaultSanitizePopulate({
2685
- schema,
2686
- getModel,
2687
- parent
2688
- }, value));
2689
- }
2690
- }, ctx), // Remove private fields
2691
- traverseQueryPopulate(visitor$7, ctx))(populate);
2692
- });
2693
-
2694
- var sanitizers = /*#__PURE__*/Object.freeze({
2695
- __proto__: null,
2696
- defaultSanitizeFields: defaultSanitizeFields,
2697
- defaultSanitizeFilters: defaultSanitizeFilters,
2698
- defaultSanitizeOutput: defaultSanitizeOutput,
2699
- defaultSanitizePopulate: defaultSanitizePopulate,
2700
- defaultSanitizeSort: defaultSanitizeSort,
2701
- sanitizePasswords: sanitizePasswords
2702
- });
2703
-
2704
- const createAPISanitizers = (opts)=>{
2705
- const { getModel } = opts;
2706
- const sanitizeInput = (data, schema, { auth } = {})=>{
2707
- if (!schema) {
2708
- throw new Error('Missing schema in sanitizeInput');
2709
- }
2710
- if (isArray(data)) {
2711
- return Promise.all(data.map((entry)=>sanitizeInput(entry, schema, {
2712
- auth
2713
- })));
2714
- }
2715
- const nonWritableAttributes = getNonWritableAttributes(schema);
2716
- const transforms = [
2717
- // Remove first level ID in inputs
2718
- omit(constants$1.ID_ATTRIBUTE),
2719
- omit(constants$1.DOC_ID_ATTRIBUTE),
2720
- // Remove non-writable attributes
2721
- traverseEntity$1(removeRestrictedFields(nonWritableAttributes), {
2722
- schema,
2723
- getModel
2724
- })
2725
- ];
2726
- if (auth) {
2727
- // Remove restricted relations
2728
- transforms.push(traverseEntity$1(removeRestrictedRelations(auth), {
2729
- schema,
2730
- getModel
2731
- }));
2732
- }
2733
- // Apply sanitizers from registry if exists
2734
- opts?.sanitizers?.input?.forEach((sanitizer)=>transforms.push(sanitizer(schema)));
2735
- return pipe(...transforms)(data);
2736
- };
2737
- const sanitizeOutput = async (data, schema, { auth } = {})=>{
2738
- if (!schema) {
2739
- throw new Error('Missing schema in sanitizeOutput');
2740
- }
2741
- if (isArray(data)) {
2742
- const res = new Array(data.length);
2743
- for(let i = 0; i < data.length; i += 1){
2744
- res[i] = await sanitizeOutput(data[i], schema, {
2745
- auth
2746
- });
2747
- }
2748
- return res;
2749
- }
2750
- const transforms = [
2751
- (data)=>defaultSanitizeOutput({
2752
- schema,
2753
- getModel
2754
- }, data)
2755
- ];
2756
- if (auth) {
2757
- transforms.push(traverseEntity$1(removeRestrictedRelations(auth), {
2758
- schema,
2759
- getModel
2760
- }));
2761
- }
2762
- // Apply sanitizers from registry if exists
2763
- opts?.sanitizers?.output?.forEach((sanitizer)=>transforms.push(sanitizer(schema)));
2764
- return pipe(...transforms)(data);
2765
- };
2766
- const sanitizeQuery = async (query, schema, { auth } = {})=>{
2767
- if (!schema) {
2768
- throw new Error('Missing schema in sanitizeQuery');
2769
- }
2770
- const { filters, sort, fields, populate } = query;
2771
- const sanitizedQuery = cloneDeep(query);
2772
- if (filters) {
2773
- Object.assign(sanitizedQuery, {
2774
- filters: await sanitizeFilters(filters, schema, {
2775
- auth
2776
- })
2777
- });
2778
- }
2779
- if (sort) {
2780
- Object.assign(sanitizedQuery, {
2781
- sort: await sanitizeSort(sort, schema, {
2782
- auth
2783
- })
2784
- });
2785
- }
2786
- if (fields) {
2787
- Object.assign(sanitizedQuery, {
2788
- fields: await sanitizeFields(fields, schema)
2789
- });
2790
- }
2791
- if (populate) {
2792
- Object.assign(sanitizedQuery, {
2793
- populate: await sanitizePopulate(populate, schema)
2794
- });
2795
- }
2796
- return sanitizedQuery;
2797
- };
2798
- const sanitizeFilters = (filters, schema, { auth } = {})=>{
2799
- if (!schema) {
2800
- throw new Error('Missing schema in sanitizeFilters');
2801
- }
2802
- if (isArray(filters)) {
2803
- return Promise.all(filters.map((filter)=>sanitizeFilters(filter, schema, {
2804
- auth
2805
- })));
2806
- }
2807
- const transforms = [
2808
- defaultSanitizeFilters({
2809
- schema,
2810
- getModel
2811
- })
2812
- ];
2813
- if (auth) {
2814
- transforms.push(traverseQueryFilters(removeRestrictedRelations(auth), {
2815
- schema,
2816
- getModel
2817
- }));
2818
- }
2819
- return pipe(...transforms)(filters);
2820
- };
2821
- const sanitizeSort = (sort, schema, { auth } = {})=>{
2822
- if (!schema) {
2823
- throw new Error('Missing schema in sanitizeSort');
2824
- }
2825
- const transforms = [
2826
- defaultSanitizeSort({
2827
- schema,
2828
- getModel
2829
- })
2830
- ];
2831
- if (auth) {
2832
- transforms.push(traverseQuerySort(removeRestrictedRelations(auth), {
2833
- schema,
2834
- getModel
2835
- }));
2836
- }
2837
- return pipe(...transforms)(sort);
2838
- };
2839
- const sanitizeFields = (fields, schema)=>{
2840
- if (!schema) {
2841
- throw new Error('Missing schema in sanitizeFields');
2842
- }
2843
- const transforms = [
2844
- defaultSanitizeFields({
2845
- schema,
2846
- getModel
2847
- })
2848
- ];
2849
- return pipe(...transforms)(fields);
2850
- };
2851
- const sanitizePopulate = (populate, schema, { auth } = {})=>{
2852
- if (!schema) {
2853
- throw new Error('Missing schema in sanitizePopulate');
2854
- }
2855
- const transforms = [
2856
- defaultSanitizePopulate({
2857
- schema,
2858
- getModel
2859
- })
2860
- ];
2861
- if (auth) {
2862
- transforms.push(traverseQueryPopulate(removeRestrictedRelations(auth), {
2863
- schema,
2864
- getModel
2865
- }));
2866
- }
2867
- return pipe(...transforms)(populate);
2868
- };
2869
- return {
2870
- input: sanitizeInput,
2871
- output: sanitizeOutput,
2872
- query: sanitizeQuery,
2873
- filters: sanitizeFilters,
2874
- sort: sanitizeSort,
2875
- fields: sanitizeFields,
2876
- populate: sanitizePopulate
2877
- };
2878
- };
2879
-
2880
- var index$2 = /*#__PURE__*/Object.freeze({
2881
- __proto__: null,
2882
- createAPISanitizers: createAPISanitizers,
2883
- sanitizers: sanitizers,
2884
- visitors: index$4
2885
- });
2886
-
2887
- // lodash/fp curry does not handle async functions properly, and creates very "ugly" types,
2888
- // so we will use our own version to ensure curried functions are typed correctly
2889
- // TODO: Export this from root @strapi/utils so we don't have copies of it between packages
2890
- const throwInvalidKey = ({ key, path })=>{
2891
- const msg = path && path !== key ? `Invalid key ${key} at ${path}` : `Invalid key ${key}`;
2892
- throw new ValidationError(msg, {
2893
- key,
2894
- path
2895
- });
2896
- };
2897
- const asyncCurry = (fn)=>{
2898
- const curried = (...args)=>{
2899
- if (args.length >= fn.length) {
2900
- return fn(...args);
2901
- }
2902
- return (...moreArgs)=>curried(...args, ...moreArgs);
2903
- };
2904
- return curried;
2905
- };
2906
-
2907
- const visitor$3 = ({ key, attribute, path })=>{
2908
- if (attribute?.type === 'password') {
2909
- throwInvalidKey({
2910
- key,
2911
- path: path.attribute
2912
- });
2913
- }
2914
- };
2915
-
2916
- const visitor$2 = ({ schema, key, attribute, path })=>{
2917
- if (!attribute) {
2918
- return;
2919
- }
2920
- const isPrivate = attribute.private === true || isPrivateAttribute(schema, key);
2921
- if (isPrivate) {
2922
- throwInvalidKey({
2923
- key,
2924
- path: path.attribute
2925
- });
2926
- }
2927
- };
2928
-
2929
- const ACTIONS_TO_VERIFY = [
2930
- 'find'
2931
- ];
2932
- const { CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE } = constants$1;
2933
- var throwRestrictedRelations = ((auth)=>async ({ data, key, attribute, schema, path })=>{
2934
- if (!attribute) {
2935
- return;
2936
- }
2937
- const isRelation = attribute.type === 'relation';
2938
- if (!isRelation) {
2939
- return;
2940
- }
2941
- const handleMorphRelation = async ()=>{
2942
- const elements = data[key];
2943
- if ('connect' in elements || 'set' in elements || 'disconnect' in elements || 'options' in elements) {
2944
- await handleMorphElements(elements.connect || []);
2945
- await handleMorphElements(elements.set || []);
2946
- await handleMorphElements(elements.disconnect || []);
2947
- // TODO: this should technically be in its own visitor to check morph options, but for now we'll handle it here
2948
- if ('options' in elements) {
2949
- if (elements.options === null || elements.options === undefined) {
2950
- return;
2951
- }
2952
- if (typeof elements.options !== 'object') {
2953
- throwInvalidKey({
2954
- key,
2955
- path: path.attribute
2956
- });
2957
- }
2958
- const optionKeys = Object.keys(elements.options);
2959
- // Validate each key based on its validator function
2960
- for (const key of optionKeys){
2961
- if (!(key in VALID_RELATION_ORDERING_KEYS)) {
2962
- throwInvalidKey({
2963
- key,
2964
- path: path.attribute
2965
- });
2966
- }
2967
- if (!VALID_RELATION_ORDERING_KEYS[key](elements.options[key])) {
2968
- throwInvalidKey({
2969
- key,
2970
- path: path.attribute
2971
- });
2972
- }
2973
- }
2974
- }
2975
- } else {
2976
- await handleMorphElements(elements);
2977
- }
2978
- };
2979
- const handleMorphElements = async (elements)=>{
2980
- if (!isArray(elements)) {
2981
- throwInvalidKey({
2982
- key,
2983
- path: path.attribute
2984
- });
2985
- }
2986
- for (const element of elements){
2987
- if (!isObject(element) || !('__type' in element)) {
2988
- throwInvalidKey({
2989
- key,
2990
- path: path.attribute
2991
- });
2992
- }
2993
- const scopes = ACTIONS_TO_VERIFY.map((action)=>`${element.__type}.${action}`);
2994
- const isAllowed = await hasAccessToSomeScopes(scopes, auth);
2995
- if (!isAllowed) {
2996
- throwInvalidKey({
2997
- key,
2998
- path: path.attribute
2999
- });
3000
- }
3001
- }
3002
- };
3003
- const handleRegularRelation = async ()=>{
3004
- const scopes = ACTIONS_TO_VERIFY.map((action)=>`${attribute.target}.${action}`);
3005
- const isAllowed = await hasAccessToSomeScopes(scopes, auth);
3006
- // If the authenticated user don't have access to any of the scopes
3007
- if (!isAllowed) {
3008
- throwInvalidKey({
3009
- key,
3010
- path: path.attribute
3011
- });
3012
- }
3013
- };
3014
- const isCreatorRelation = [
3015
- CREATED_BY_ATTRIBUTE,
3016
- UPDATED_BY_ATTRIBUTE
3017
- ].includes(key);
3018
- // Polymorphic relations
3019
- if (isMorphToRelationalAttribute(attribute)) {
3020
- await handleMorphRelation();
3021
- return;
3022
- }
3023
- // Creator relations
3024
- if (isCreatorRelation && schema.options?.populateCreatorFields) {
3025
- // do nothing
3026
- return;
3027
- }
3028
- // Regular relations
3029
- await handleRegularRelation();
3030
- });
3031
- const hasAccessToSomeScopes = async (scopes, auth)=>{
3032
- for (const scope of scopes){
3033
- try {
3034
- await strapi.auth.verify(auth, {
3035
- scope
3036
- });
3037
- return true;
3038
- } catch {
3039
- continue;
3040
- }
3041
- }
3042
- return false;
3043
- };
3044
-
3045
- const visitor$1 = ({ key, attribute, path })=>{
3046
- if (isMorphToRelationalAttribute(attribute)) {
3047
- throwInvalidKey({
3048
- key,
3049
- path: path.attribute
3050
- });
3051
- }
3052
- };
3053
-
3054
- const visitor = ({ key, attribute, path })=>{
3055
- if (isDynamicZoneAttribute(attribute)) {
3056
- throwInvalidKey({
3057
- key,
3058
- path: path.attribute
3059
- });
3060
- }
3061
- };
3062
-
3063
- var throwDisallowedFields = ((allowedFields = null)=>({ key, path: { attribute: path } })=>{
3064
- // All fields are allowed
3065
- if (allowedFields === null) {
3066
- return;
3067
- }
3068
- // Throw on invalid formats
3069
- if (!(isArray(allowedFields) && allowedFields.every(isString))) {
3070
- throw new TypeError(`Expected array of strings for allowedFields but got "${typeof allowedFields}"`);
3071
- }
3072
- if (isNil(path)) {
3073
- return;
3074
- }
3075
- const containedPaths = getContainedPaths(path);
3076
- /**
3077
- * Tells if the current path should be kept or not based
3078
- * on the success of the check functions for any of the allowed paths.
3079
- *
3080
- * The check functions are defined as follow:
3081
- *
3082
- * `containedPaths.includes(p)`
3083
- * @example
3084
- * ```js
3085
- * const path = 'foo.bar.field';
3086
- * const p = 'foo.bar';
3087
- * // it should match
3088
- *
3089
- * const path = 'foo.bar.field';
3090
- * const p = 'bar.foo';
3091
- * // it shouldn't match
3092
- *
3093
- * const path = 'foo.bar';
3094
- * const p = 'foo.bar.field';
3095
- * // it should match but isn't handled by this check
3096
- * ```
3097
- *
3098
- * `p.startsWith(`${path}.`)`
3099
- * @example
3100
- * ```js
3101
- * const path = 'foo.bar';
3102
- * const p = 'foo.bar.field';
3103
- * // it should match
3104
- *
3105
- * const path = 'foo.bar.field';
3106
- * const p = 'bar.foo';
3107
- * // it shouldn't match
3108
- *
3109
- * const path = 'foo.bar.field';
3110
- * const p = 'foo.bar';
3111
- * // it should match but isn't handled by this check
3112
- * ```
3113
- */ const isPathAllowed = allowedFields.some((p)=>containedPaths.includes(p) || p.startsWith(`${path}.`));
3114
- if (isPathAllowed) {
3115
- return;
3116
- }
3117
- // throw otherwise
3118
- throwInvalidKey({
3119
- key,
3120
- path
3121
- });
3122
- });
3123
- /**
3124
- * Retrieve the list of allowed paths based on the given path
3125
- *
3126
- * @example
3127
- * ```js
3128
- * const containedPaths = getContainedPaths('foo');
3129
- * // ['foo']
3130
- *
3131
- * * const containedPaths = getContainedPaths('foo.bar');
3132
- * // ['foo', 'foo.bar']
3133
- *
3134
- * * const containedPaths = getContainedPaths('foo.bar.field');
3135
- * // ['foo', 'foo.bar', 'foo.bar.field']
3136
- * ```
3137
- */ const getContainedPaths = (path)=>{
3138
- const parts = toPath(path);
3139
- return parts.reduce((acc, value, index, list)=>{
3140
- return [
3141
- ...acc,
3142
- list.slice(0, index + 1).join('.')
3143
- ];
3144
- }, []);
3145
- };
3146
-
3147
- var throwRestrictedFields = ((restrictedFields = null)=>({ key, path: { attribute: path } })=>{
3148
- // all fields
3149
- if (restrictedFields === null) {
3150
- throwInvalidKey({
3151
- key,
3152
- path
3153
- });
3154
- }
3155
- // Throw on invalid formats
3156
- if (!(isArray(restrictedFields) && restrictedFields.every(isString))) {
3157
- throw new TypeError(`Expected array of strings for restrictedFields but got "${typeof restrictedFields}"`);
3158
- }
3159
- // if an exact match was found
3160
- if (restrictedFields.includes(path)) {
3161
- throwInvalidKey({
3162
- key,
3163
- path
3164
- });
3165
- }
3166
- // nested matches
3167
- const isRestrictedNested = restrictedFields.some((allowedPath)=>path?.toString().startsWith(`${allowedPath}.`));
3168
- if (isRestrictedNested) {
3169
- throwInvalidKey({
3170
- key,
3171
- path
3172
- });
3173
- }
3174
- });
3175
-
3176
- // TODO these should all be centralized somewhere instead of maintaining a list
3177
- const ID_FIELDS = [
3178
- constants$1.DOC_ID_ATTRIBUTE,
3179
- constants$1.DOC_ID_ATTRIBUTE
3180
- ];
3181
- const ALLOWED_ROOT_LEVEL_FIELDS = [
3182
- ...ID_FIELDS
3183
- ];
3184
- const MORPH_TO_ALLOWED_FIELDS = [
3185
- '__type'
3186
- ];
3187
- const DYNAMIC_ZONE_ALLOWED_FIELDS = [
3188
- '__component'
3189
- ];
3190
- const RELATION_REORDERING_FIELDS = [
3191
- 'connect',
3192
- 'disconnect',
3193
- 'set',
3194
- 'options'
3195
- ];
3196
- const throwUnrecognizedFields = ({ key, attribute, path, schema, parent })=>{
3197
- // We only look at properties that are not attributes
3198
- if (attribute) {
3199
- return;
3200
- }
3201
- // At root level (path.attribute === null), only accept allowed fields
3202
- if (path.attribute === null) {
3203
- if (ALLOWED_ROOT_LEVEL_FIELDS.includes(key)) {
3204
- return;
3205
- }
3206
- return throwInvalidKey({
3207
- key,
3208
- path: attribute
3209
- });
3210
- }
3211
- // allow special morphTo keys
3212
- if (isMorphToRelationalAttribute(parent?.attribute) && MORPH_TO_ALLOWED_FIELDS.includes(key)) {
3213
- return;
3214
- }
3215
- // allow special dz keys
3216
- if (isComponentSchema(schema) && isDynamicZoneAttribute(parent?.attribute) && DYNAMIC_ZONE_ALLOWED_FIELDS.includes(key)) {
3217
- return;
3218
- }
3219
- // allow special relation reordering keys in manyToX and XtoMany relations
3220
- if (hasRelationReordering(parent?.attribute) && RELATION_REORDERING_FIELDS.includes(key)) {
3221
- return;
3222
- }
3223
- // allow id fields where it is needed for setting a relational id rather than trying to create with a given id
3224
- const canUseID = isRelationalAttribute(parent?.attribute) || isMediaAttribute(parent?.attribute);
3225
- if (canUseID && !ID_FIELDS.includes(key)) {
3226
- return;
3227
- }
3228
- // if we couldn't find any reason for it to be here, throw
3229
- throwInvalidKey({
3230
- key,
3231
- path: attribute
3232
- });
3233
- };
3234
-
3235
- var index$1 = /*#__PURE__*/Object.freeze({
3236
- __proto__: null,
3237
- throwDisallowedFields: throwDisallowedFields,
3238
- throwDynamicZones: visitor,
3239
- throwMorphToRelations: visitor$1,
3240
- throwPassword: visitor$3,
3241
- throwPrivate: visitor$2,
3242
- throwRestrictedFields: throwRestrictedFields,
3243
- throwRestrictedRelations: throwRestrictedRelations,
3244
- throwUnrecognizedFields: throwUnrecognizedFields
3245
- });
3246
-
3247
- const { ID_ATTRIBUTE: ID_ATTRIBUTE$1, DOC_ID_ATTRIBUTE: DOC_ID_ATTRIBUTE$1 } = constants$1;
3248
- const FILTER_TRAVERSALS = [
3249
- 'nonAttributesOperators',
3250
- 'dynamicZones',
3251
- 'morphRelations',
3252
- 'passwords',
3253
- 'private'
3254
- ];
3255
- const validateFilters = asyncCurry(async (ctx, filters, include)=>{
3256
- // TODO: schema checks should check that it is a valid schema with yup
3257
- if (!ctx.schema) {
3258
- throw new Error('Missing schema in defaultValidateFilters');
3259
- }
3260
- // Build the list of functions conditionally
3261
- const functionsToApply = [];
3262
- // keys that are not attributes or valid operators
3263
- if (include.includes('nonAttributesOperators')) {
3264
- functionsToApply.push(traverseQueryFilters(({ key, attribute, path })=>{
3265
- // ID is not an attribute per se, so we need to make
3266
- // an extra check to ensure we're not removing it
3267
- if ([
3268
- ID_ATTRIBUTE$1,
3269
- DOC_ID_ATTRIBUTE$1
3270
- ].includes(key)) {
3271
- return;
3272
- }
3273
- const isAttribute = !!attribute;
3274
- if (!isAttribute && !isOperator(key)) {
3275
- throwInvalidKey({
3276
- key,
3277
- path: path.attribute
3278
- });
3279
- }
3280
- }, ctx));
3281
- }
3282
- if (include.includes('dynamicZones')) {
3283
- functionsToApply.push(traverseQueryFilters(visitor, ctx));
3284
- }
3285
- if (include.includes('morphRelations')) {
3286
- functionsToApply.push(traverseQueryFilters(visitor$1, ctx));
3287
- }
3288
- if (include.includes('passwords')) {
3289
- functionsToApply.push(traverseQueryFilters(visitor$3, ctx));
3290
- }
3291
- if (include.includes('private')) {
3292
- functionsToApply.push(traverseQueryFilters(visitor$2, ctx));
3293
- }
3294
- // Return directly if no validation functions are provided
3295
- if (functionsToApply.length === 0) {
3296
- return filters;
3297
- }
3298
- return pipe(...functionsToApply)(filters);
3299
- });
3300
- const defaultValidateFilters = asyncCurry(async (ctx, filters)=>{
3301
- return validateFilters(ctx, filters, FILTER_TRAVERSALS);
3302
- });
3303
- const SORT_TRAVERSALS = [
3304
- 'nonAttributesOperators',
3305
- 'dynamicZones',
3306
- 'morphRelations',
3307
- 'passwords',
3308
- 'private',
3309
- 'nonScalarEmptyKeys'
3310
- ];
3311
- const validateSort = asyncCurry(async (ctx, sort, include)=>{
3312
- if (!ctx.schema) {
3313
- throw new Error('Missing schema in defaultValidateSort');
3314
- }
3315
- // Build the list of functions conditionally based on the include array
3316
- const functionsToApply = [];
3317
- // Validate non attribute keys
3318
- if (include.includes('nonAttributesOperators')) {
3319
- functionsToApply.push(traverseQuerySort(({ key, attribute, path })=>{
3320
- // ID is not an attribute per se, so we need to make
3321
- // an extra check to ensure we're not removing it
3322
- if ([
3323
- ID_ATTRIBUTE$1,
3324
- DOC_ID_ATTRIBUTE$1
3325
- ].includes(key)) {
3326
- return;
3327
- }
3328
- if (!attribute) {
3329
- throwInvalidKey({
3330
- key,
3331
- path: path.attribute
3332
- });
3333
- }
3334
- }, ctx));
3335
- }
3336
- // Validate dynamic zones from sort
3337
- if (include.includes('dynamicZones')) {
3338
- functionsToApply.push(traverseQuerySort(visitor, ctx));
3339
- }
3340
- // Validate morphTo relations from sort
3341
- if (include.includes('morphRelations')) {
3342
- functionsToApply.push(traverseQuerySort(visitor$1, ctx));
3343
- }
3344
- // Validate passwords from sort
3345
- if (include.includes('passwords')) {
3346
- functionsToApply.push(traverseQuerySort(visitor$3, ctx));
3347
- }
3348
- // Validate private from sort
3349
- if (include.includes('private')) {
3350
- functionsToApply.push(traverseQuerySort(visitor$2, ctx));
3351
- }
3352
- // Validate non-scalar empty keys
3353
- if (include.includes('nonScalarEmptyKeys')) {
3354
- functionsToApply.push(traverseQuerySort(({ key, attribute, value, path })=>{
3355
- // ID is not an attribute per se, so we need to make
3356
- // an extra check to ensure we're not removing it
3357
- if ([
3358
- ID_ATTRIBUTE$1,
3359
- DOC_ID_ATTRIBUTE$1
3360
- ].includes(key)) {
3361
- return;
3362
- }
3363
- if (!isScalarAttribute(attribute) && isEmpty(value)) {
3364
- throwInvalidKey({
3365
- key,
3366
- path: path.attribute
3367
- });
3368
- }
3369
- }, ctx));
3370
- }
3371
- // Return directly if no validation functions are provided
3372
- if (functionsToApply.length === 0) {
3373
- return sort;
3374
- }
3375
- return pipe(...functionsToApply)(sort);
3376
- });
3377
- const defaultValidateSort = asyncCurry(async (ctx, sort)=>{
3378
- return validateSort(ctx, sort, SORT_TRAVERSALS);
3379
- });
3380
- const FIELDS_TRAVERSALS = [
3381
- 'scalarAttributes',
3382
- 'privateFields',
3383
- 'passwordFields'
3384
- ];
3385
- const validateFields = asyncCurry(async (ctx, fields, include)=>{
3386
- if (!ctx.schema) {
3387
- throw new Error('Missing schema in defaultValidateFields');
3388
- }
3389
- // Build the list of functions conditionally based on the include array
3390
- const functionsToApply = [];
3391
- // Only allow scalar attributes
3392
- if (include.includes('scalarAttributes')) {
3393
- functionsToApply.push(traverseQueryFields(({ key, attribute, path })=>{
3394
- // ID is not an attribute per se, so we need to make
3395
- // an extra check to ensure we're not throwing because of it
3396
- if ([
3397
- ID_ATTRIBUTE$1,
3398
- DOC_ID_ATTRIBUTE$1
3399
- ].includes(key)) {
3400
- return;
3401
- }
3402
- if (isNil(attribute) || !isScalarAttribute(attribute)) {
3403
- throwInvalidKey({
3404
- key,
3405
- path: path.attribute
3406
- });
3407
- }
3408
- }, ctx));
3409
- }
3410
- // Private fields
3411
- if (include.includes('privateFields')) {
3412
- functionsToApply.push(traverseQueryFields(visitor$2, ctx));
3413
- }
3414
- // Password fields
3415
- if (include.includes('passwordFields')) {
3416
- functionsToApply.push(traverseQueryFields(visitor$3, ctx));
3417
- }
3418
- // Return directly if no validation functions are provided
3419
- if (functionsToApply.length === 0) {
3420
- return fields;
3421
- }
3422
- return pipe(...functionsToApply)(fields);
3423
- });
3424
- const defaultValidateFields = asyncCurry(async (ctx, fields)=>{
3425
- return validateFields(ctx, fields, FIELDS_TRAVERSALS);
3426
- });
3427
- const POPULATE_TRAVERSALS = [
3428
- 'nonAttributesOperators',
3429
- 'private'
3430
- ];
3431
- const validatePopulate = asyncCurry(async (ctx, populate, includes)=>{
3432
- if (!ctx.schema) {
3433
- throw new Error('Missing schema in defaultValidatePopulate');
3434
- }
3435
- // Build the list of functions conditionally based on the include array
3436
- const functionsToApply = [];
3437
- // Always include the main traversal function
3438
- functionsToApply.push(traverseQueryPopulate(async ({ key, path, value, schema, attribute, getModel, parent }, { set })=>{
3439
- /**
3440
- * NOTE: The parent check is done to support "filters" (and the rest of keys) as valid attribute names.
3441
- *
3442
- * The parent will not be an attribute when its a "populate" / "filters" / "sort" ... key.
3443
- * Only in those scenarios the node will be an attribute.
3444
- */ if (!parent?.attribute && attribute) {
3445
- const isPopulatableAttribute = [
3446
- 'relation',
3447
- 'dynamiczone',
3448
- 'component',
3449
- 'media'
3450
- ].includes(attribute.type);
3451
- // Throw on non-populate attributes
3452
- if (!isPopulatableAttribute) {
3453
- throwInvalidKey({
3454
- key,
3455
- path: path.raw
3456
- });
3457
- }
3458
- // Valid populatable attribute, so return
3459
- return;
3460
- }
3461
- // If we're looking at a populate fragment, ensure its target is valid
3462
- if (key === 'on') {
3463
- // Populate fragment should always be an object
3464
- if (!isObject(value)) {
3465
- return throwInvalidKey({
3466
- key,
3467
- path: path.raw
3468
- });
3469
- }
3470
- const targets = Object.keys(value);
3471
- for (const target of targets){
3472
- const model = getModel(target);
3473
- // If a target is invalid (no matching model), then raise an error
3474
- if (!model) {
3475
- throwInvalidKey({
3476
- key: target,
3477
- path: `${path.raw}.${target}`
3478
- });
3479
- }
3480
- }
3481
- // If the fragment's target is fine, then let it pass
3482
- return;
3483
- }
3484
- // Ignore plain wildcards
3485
- if (key === '' && value === '*') {
3486
- return;
3487
- }
3488
- // Ensure count is a boolean
3489
- if (key === 'count') {
3490
- try {
3491
- parseType({
3492
- type: 'boolean',
3493
- value
3494
- });
3495
- return;
3496
- } catch {
3497
- throwInvalidKey({
3498
- key,
3499
- path: path.attribute
3500
- });
3501
- }
3502
- }
3503
- // Allowed boolean-like keywords should be ignored
3504
- try {
3505
- parseType({
3506
- type: 'boolean',
3507
- value: key
3508
- });
3509
- // Key is an allowed boolean-like keyword, skipping validation...
3510
- return;
3511
- } catch {
3512
- // Continue, because it's not a boolean-like
3513
- }
3514
- // Handle nested `sort` validation with custom or default traversals
3515
- if (key === 'sort') {
3516
- set(key, await validateSort({
3517
- schema,
3518
- getModel
3519
- }, value, includes?.sort || SORT_TRAVERSALS));
3520
- return;
3521
- }
3522
- // Handle nested `filters` validation with custom or default traversals
3523
- if (key === 'filters') {
3524
- set(key, await validateFilters({
3525
- schema,
3526
- getModel
3527
- }, value, includes?.filters || FILTER_TRAVERSALS));
3528
- return;
3529
- }
3530
- // Handle nested `fields` validation with custom or default traversals
3531
- if (key === 'fields') {
3532
- set(key, await validateFields({
3533
- schema,
3534
- getModel
3535
- }, value, includes?.fields || FIELDS_TRAVERSALS));
3536
- return;
3537
- }
3538
- // Handle recursive nested `populate` validation with the same include object
3539
- if (key === 'populate') {
3540
- set(key, await validatePopulate({
3541
- schema,
3542
- getModel,
3543
- parent: {
3544
- key,
3545
- path,
3546
- schema,
3547
- attribute
3548
- },
3549
- path
3550
- }, value, includes // pass down the same includes object
3551
- ));
3552
- return;
3553
- }
3554
- // Throw an error if non-attribute operators are included in the populate array
3555
- if (includes?.populate?.includes('nonAttributesOperators')) {
3556
- throwInvalidKey({
3557
- key,
3558
- path: path.attribute
3559
- });
3560
- }
3561
- }, ctx));
3562
- // Conditionally traverse for private fields only if 'private' is included
3563
- if (includes?.populate?.includes('private')) {
3564
- functionsToApply.push(traverseQueryPopulate(visitor$2, ctx));
3565
- }
3566
- // Return directly if no validation functions are provided
3567
- if (functionsToApply.length === 0) {
3568
- return populate;
3569
- }
3570
- return pipe(...functionsToApply)(populate);
3571
- });
3572
- const defaultValidatePopulate = asyncCurry(async (ctx, populate)=>{
3573
- if (!ctx.schema) {
3574
- throw new Error('Missing schema in defaultValidatePopulate');
3575
- }
3576
- // Call validatePopulate and include all validations by passing in full traversal arrays
3577
- return validatePopulate(ctx, populate, {
3578
- filters: FILTER_TRAVERSALS,
3579
- sort: SORT_TRAVERSALS,
3580
- fields: FIELDS_TRAVERSALS,
3581
- populate: POPULATE_TRAVERSALS
3582
- });
3583
- });
3584
-
3585
- var validators = /*#__PURE__*/Object.freeze({
3586
- __proto__: null,
3587
- FIELDS_TRAVERSALS: FIELDS_TRAVERSALS,
3588
- FILTER_TRAVERSALS: FILTER_TRAVERSALS,
3589
- POPULATE_TRAVERSALS: POPULATE_TRAVERSALS,
3590
- SORT_TRAVERSALS: SORT_TRAVERSALS,
3591
- defaultValidateFields: defaultValidateFields,
3592
- defaultValidateFilters: defaultValidateFilters,
3593
- defaultValidatePopulate: defaultValidatePopulate,
3594
- defaultValidateSort: defaultValidateSort,
3595
- validateFields: validateFields,
3596
- validateFilters: validateFilters,
3597
- validatePopulate: validatePopulate,
3598
- validateSort: validateSort
3599
- });
3600
-
3601
- const { ID_ATTRIBUTE, DOC_ID_ATTRIBUTE } = constants$1;
3602
- const createAPIValidators = (opts)=>{
3603
- const { getModel } = opts || {};
3604
- const validateInput = async (data, schema, { auth } = {})=>{
3605
- if (!schema) {
3606
- throw new Error('Missing schema in validateInput');
3607
- }
3608
- if (isArray(data)) {
3609
- await Promise.all(data.map((entry)=>validateInput(entry, schema, {
3610
- auth
3611
- })));
3612
- return;
3613
- }
3614
- const nonWritableAttributes = getNonWritableAttributes(schema);
3615
- const transforms = [
3616
- (data)=>{
3617
- if (isObject(data)) {
3618
- if (ID_ATTRIBUTE in data) {
3619
- throwInvalidKey({
3620
- key: ID_ATTRIBUTE
3621
- });
3622
- }
3623
- if (DOC_ID_ATTRIBUTE in data) {
3624
- throwInvalidKey({
3625
- key: DOC_ID_ATTRIBUTE
3626
- });
3627
- }
3628
- }
3629
- return data;
3630
- },
3631
- // non-writable attributes
3632
- traverseEntity$1(throwRestrictedFields(nonWritableAttributes), {
3633
- schema,
3634
- getModel
3635
- }),
3636
- // unrecognized attributes
3637
- traverseEntity$1(throwUnrecognizedFields, {
3638
- schema,
3639
- getModel
3640
- })
3641
- ];
3642
- if (auth) {
3643
- // restricted relations
3644
- transforms.push(traverseEntity$1(throwRestrictedRelations(auth), {
3645
- schema,
3646
- getModel
3647
- }));
3648
- }
3649
- // Apply validators from registry if exists
3650
- opts?.validators?.input?.forEach((validator)=>transforms.push(validator(schema)));
3651
- try {
3652
- await pipe(...transforms)(data);
3653
- } catch (e) {
3654
- if (e instanceof ValidationError) {
3655
- e.details.source = 'body';
3656
- }
3657
- throw e;
3658
- }
3659
- };
3660
- const validateQuery = async (query, schema, { auth } = {})=>{
3661
- if (!schema) {
3662
- throw new Error('Missing schema in validateQuery');
3663
- }
3664
- const { filters, sort, fields, populate } = query;
3665
- if (filters) {
3666
- await validateFilters(filters, schema, {
3667
- auth
3668
- });
3669
- }
3670
- if (sort) {
3671
- await validateSort(sort, schema, {
3672
- auth
3673
- });
3674
- }
3675
- if (fields) {
3676
- await validateFields(fields, schema);
3677
- }
3678
- // a wildcard is always valid; its conversion will be handled by the entity service and can be optimized with sanitizer
3679
- if (populate && populate !== '*') {
3680
- await validatePopulate(populate, schema);
3681
- }
3682
- };
3683
- const validateFilters = async (filters, schema, { auth } = {})=>{
3684
- if (!schema) {
3685
- throw new Error('Missing schema in validateFilters');
3686
- }
3687
- if (isArray(filters)) {
3688
- await Promise.all(filters.map((filter)=>validateFilters(filter, schema, {
3689
- auth
3690
- })));
3691
- return;
3692
- }
3693
- const transforms = [
3694
- defaultValidateFilters({
3695
- schema,
3696
- getModel
3697
- })
3698
- ];
3699
- if (auth) {
3700
- transforms.push(traverseQueryFilters(throwRestrictedRelations(auth), {
3701
- schema,
3702
- getModel
3703
- }));
3704
- }
3705
- try {
3706
- await pipe(...transforms)(filters);
3707
- } catch (e) {
3708
- if (e instanceof ValidationError) {
3709
- e.details.source = 'query';
3710
- e.details.param = 'filters';
3711
- }
3712
- throw e;
3713
- }
3714
- };
3715
- const validateSort = async (sort, schema, { auth } = {})=>{
3716
- if (!schema) {
3717
- throw new Error('Missing schema in validateSort');
3718
- }
3719
- const transforms = [
3720
- defaultValidateSort({
3721
- schema,
3722
- getModel
3723
- })
3724
- ];
3725
- if (auth) {
3726
- transforms.push(traverseQuerySort(throwRestrictedRelations(auth), {
3727
- schema,
3728
- getModel
3729
- }));
3730
- }
3731
- try {
3732
- await pipe(...transforms)(sort);
3733
- } catch (e) {
3734
- if (e instanceof ValidationError) {
3735
- e.details.source = 'query';
3736
- e.details.param = 'sort';
3737
- }
3738
- throw e;
3739
- }
3740
- };
3741
- const validateFields = async (fields, schema)=>{
3742
- if (!schema) {
3743
- throw new Error('Missing schema in validateFields');
3744
- }
3745
- const transforms = [
3746
- defaultValidateFields({
3747
- schema,
3748
- getModel
3749
- })
3750
- ];
3751
- try {
3752
- await pipe(...transforms)(fields);
3753
- } catch (e) {
3754
- if (e instanceof ValidationError) {
3755
- e.details.source = 'query';
3756
- e.details.param = 'fields';
3757
- }
3758
- throw e;
3759
- }
3760
- };
3761
- const validatePopulate = async (populate, schema, { auth } = {})=>{
3762
- if (!schema) {
3763
- throw new Error('Missing schema in sanitizePopulate');
3764
- }
3765
- const transforms = [
3766
- defaultValidatePopulate({
3767
- schema,
3768
- getModel
3769
- })
3770
- ];
3771
- if (auth) {
3772
- transforms.push(traverseQueryPopulate(throwRestrictedRelations(auth), {
3773
- schema,
3774
- getModel
3775
- }));
3776
- }
3777
- try {
3778
- await pipe(...transforms)(populate);
3779
- } catch (e) {
3780
- if (e instanceof ValidationError) {
3781
- e.details.source = 'query';
3782
- e.details.param = 'populate';
3783
- }
3784
- throw e;
3785
- }
3786
- };
3787
- return {
3788
- input: validateInput,
3789
- query: validateQuery,
3790
- filters: validateFilters,
3791
- sort: validateSort,
3792
- fields: validateFields,
3793
- populate: validatePopulate
3794
- };
3795
- };
3796
-
3797
- var index = /*#__PURE__*/Object.freeze({
3798
- __proto__: null,
3799
- createAPIValidators: createAPIValidators,
3800
- validators: validators,
3801
- visitors: index$1
3802
- });
3803
-
3804
- const STRAPI_DEFAULTS = {
3805
- offset: {
3806
- start: 0,
3807
- limit: 10
3808
- },
3809
- page: {
3810
- page: 1,
3811
- pageSize: 10
3812
- }
3813
- };
3814
- const paginationAttributes = [
3815
- 'start',
3816
- 'limit',
3817
- 'page',
3818
- 'pageSize'
3819
- ];
3820
- const withMaxLimit = (limit, maxLimit = -1)=>{
3821
- if (maxLimit === -1 || limit < maxLimit) {
3822
- return limit;
3823
- }
3824
- return maxLimit;
3825
- };
3826
- // Ensure minimum page & pageSize values (page >= 1, pageSize >= 0, start >= 0, limit >= 0)
3827
- const ensureMinValues = ({ start, limit })=>({
3828
- start: Math.max(start, 0),
3829
- limit: limit === -1 ? limit : Math.max(limit, 1)
3830
- });
3831
- const ensureMaxValues = (maxLimit = -1)=>({ start, limit })=>({
3832
- start,
3833
- limit: withMaxLimit(limit, maxLimit)
3834
- });
3835
- // Apply maxLimit as the limit when limit is -1
3836
- const withNoLimit = (pagination, maxLimit = -1)=>({
3837
- ...pagination,
3838
- limit: pagination.limit === -1 ? maxLimit : pagination.limit
3839
- });
3840
- const withDefaultPagination = (args, { defaults = {}, maxLimit = -1 } = {})=>{
3841
- const defaultValues = merge(STRAPI_DEFAULTS, defaults);
3842
- const usePagePagination = !isNil(args.page) || !isNil(args.pageSize);
3843
- const useOffsetPagination = !isNil(args.start) || !isNil(args.limit);
3844
- const ensureValidValues = pipe$1(ensureMinValues, ensureMaxValues(maxLimit));
3845
- // If there is no pagination attribute, don't modify the payload
3846
- if (!usePagePagination && !useOffsetPagination) {
3847
- return merge(args, ensureValidValues(defaultValues.offset));
3848
- }
3849
- // If there is page & offset pagination attributes, throw an error
3850
- if (usePagePagination && useOffsetPagination) {
3851
- throw new PaginationError('Cannot use both page & offset pagination in the same query');
3852
- }
3853
- const pagination = {
3854
- start: 0,
3855
- limit: 0
3856
- };
3857
- // Start / Limit
3858
- if (useOffsetPagination) {
3859
- const { start, limit } = merge(defaultValues.offset, args);
3860
- Object.assign(pagination, {
3861
- start,
3862
- limit
3863
- });
3864
- }
3865
- // Page / PageSize
3866
- if (usePagePagination) {
3867
- const { page, pageSize } = merge(defaultValues.page, {
3868
- ...args,
3869
- pageSize: Math.max(1, args.pageSize ?? 0)
3870
- });
3871
- Object.assign(pagination, {
3872
- start: (page - 1) * pageSize,
3873
- limit: pageSize
3874
- });
3875
- }
3876
- // Handle -1 limit
3877
- Object.assign(pagination, withNoLimit(pagination, maxLimit));
3878
- const replacePaginationAttributes = pipe$1(// Remove pagination attributes
3879
- omit(paginationAttributes), // Merge the object with the new pagination + ensure minimum & maximum values
3880
- merge(ensureValidValues(pagination)));
3881
- return replacePaginationAttributes(args);
3882
- };
3883
- /**
3884
- * Transform pagination information into a paginated response:
3885
- * {
3886
- * page: number,
3887
- * pageSize: number,
3888
- * pageCount: number,
3889
- * total: number
3890
- * }
3891
- */ const transformPagedPaginationInfo = (paginationInfo, total)=>{
3892
- if (!isNil(paginationInfo.page)) {
3893
- const page = paginationInfo.page;
3894
- const pageSize = paginationInfo.pageSize ?? total;
3895
- return {
3896
- page,
3897
- pageSize,
3898
- pageCount: pageSize > 0 ? Math.ceil(total / pageSize) : 0,
3899
- total
3900
- };
3901
- }
3902
- if (!isNil(paginationInfo.start)) {
3903
- const start = paginationInfo.start;
3904
- const limit = paginationInfo.limit ?? total;
3905
- // Start limit to page page size
3906
- return {
3907
- page: Math.floor(start / limit) + 1,
3908
- pageSize: limit,
3909
- pageCount: limit > 0 ? Math.ceil(total / limit) : 0,
3910
- total
3911
- };
3912
- }
3913
- // Default pagination
3914
- return {
3915
- ...paginationInfo,
3916
- page: 1,
3917
- pageSize: 10,
3918
- pageCount: 1,
3919
- total
3920
- };
3921
- };
3922
- /**
3923
- * Transform pagination information into a offset response:
3924
- * {
3925
- * start: number,
3926
- * limit: number,
3927
- * total: number
3928
- * }
3929
- */ const transformOffsetPaginationInfo = (paginationInfo, total)=>{
3930
- if (!isNil(paginationInfo.page)) {
3931
- const limit = paginationInfo.pageSize ?? total;
3932
- const start = (paginationInfo.page - 1) * limit;
3933
- return {
3934
- start,
3935
- limit,
3936
- total
3937
- };
3938
- }
3939
- if (!isNil(paginationInfo.start)) {
3940
- const start = paginationInfo.start;
3941
- const limit = paginationInfo.limit ?? total;
3942
- // Start limit to page page size
3943
- return {
3944
- start,
3945
- limit,
3946
- total
3947
- };
3948
- }
3949
- // Default pagination
3950
- return {
3951
- ...paginationInfo,
3952
- start: 0,
3953
- limit: 10,
3954
- total
3955
- };
3956
- };
3957
-
3958
- var pagination = /*#__PURE__*/Object.freeze({
3959
- __proto__: null,
3960
- transformOffsetPaginationInfo: transformOffsetPaginationInfo,
3961
- transformPagedPaginationInfo: transformPagedPaginationInfo,
3962
- withDefaultPagination: withDefaultPagination
3963
- });
3964
-
3965
- const SUPPORTED_PACKAGE_MANAGERS = [
3966
- 'npm',
3967
- 'yarn'
3968
- ];
3969
- const DEFAULT_PACKAGE_MANAGER = 'npm';
3970
- const getPreferred = async (pkgPath)=>{
3971
- const pm = await preferredPM(pkgPath);
3972
- const hasPackageManager = pm !== undefined;
3973
- if (!hasPackageManager) {
3974
- throw new Error(`Couldn't find a package manager in your project.`);
3975
- }
3976
- const isPackageManagerSupported = SUPPORTED_PACKAGE_MANAGERS.includes(pm.name);
3977
- if (!isPackageManagerSupported) {
3978
- process.emitWarning(`We detected your package manager (${pm.name} v${pm.version}), but it's not officially supported by Strapi yet. Defaulting to npm instead.`);
3979
- return DEFAULT_PACKAGE_MANAGER;
3980
- }
3981
- return pm.name;
3982
- };
3983
- const installDependencies = (path, packageManager, options = {})=>{
3984
- return execa(packageManager, [
3985
- 'install'
3986
- ], {
3987
- ...options,
3988
- cwd: path,
3989
- stdin: 'ignore'
3990
- });
3991
- };
3992
-
3993
- var packageManager = /*#__PURE__*/Object.freeze({
3994
- __proto__: null,
3995
- getPreferred: getPreferred,
3996
- installDependencies: installDependencies
3997
- });
3998
-
3999
- /**
4000
- * Create a strict interpolation RegExp based on the given variables' name
4001
- */ const createStrictInterpolationRegExp = (allowedVariableNames, flags)=>{
4002
- const oneOfVariables = allowedVariableNames.join('|');
4003
- // 1. We need to match the delimiters: <%= ... %>
4004
- // 2. We accept any number of whitespaces characters before and/or after the variable name: \s* ... \s*
4005
- // 3. We only accept values from the variable list as interpolation variables' name: : (${oneOfVariables})
4006
- return new RegExp(`<%=\\s*(${oneOfVariables})\\s*%>`, flags);
4007
- };
4008
- /**
4009
- * Create a loose interpolation RegExp to match as many groups as possible
4010
- */ const createLooseInterpolationRegExp = (flags)=>new RegExp(/<%=([\s\S]+?)%>/, flags);
4011
-
4012
- var template = /*#__PURE__*/Object.freeze({
4013
- __proto__: null,
4014
- createLooseInterpolationRegExp: createLooseInterpolationRegExp,
4015
- createStrictInterpolationRegExp: createStrictInterpolationRegExp
4016
- });
4017
-
4018
- const kbytesToBytes = (kbytes)=>kbytes * 1000;
4019
- const bytesToKbytes = (bytes)=>Math.round(bytes / 1000 * 100) / 100;
4020
- const bytesToHumanReadable = (bytes)=>{
4021
- const sizes = [
4022
- 'Bytes',
4023
- 'KB',
4024
- 'MB',
4025
- 'GB',
4026
- 'TB',
4027
- 'PB'
4028
- ];
4029
- if (bytes === 0) return '0 Bytes';
4030
- const i = parseInt(`${Math.floor(Math.log(bytes) / Math.log(1000))}`, 10);
4031
- return `${Math.round(bytes / 1000 ** i)} ${sizes[i]}`;
4032
- };
4033
- const streamToBuffer = (stream)=>new Promise((resolve, reject)=>{
4034
- const chunks = [];
4035
- stream.on('data', (chunk)=>{
4036
- chunks.push(chunk);
4037
- });
4038
- stream.on('end', ()=>{
4039
- resolve(Buffer.concat(chunks));
4040
- });
4041
- stream.on('error', reject);
4042
- });
4043
- const getStreamSize = (stream)=>new Promise((resolve, reject)=>{
4044
- let size = 0;
4045
- stream.on('data', (chunk)=>{
4046
- size += Buffer.byteLength(chunk);
4047
- });
4048
- stream.on('close', ()=>resolve(size));
4049
- stream.on('error', reject);
4050
- stream.resume();
4051
- });
4052
- /**
4053
- * Create a writeable Node.js stream that discards received data.
4054
- * Useful for testing, draining a stream of data, etc.
4055
- */ function writableDiscardStream(options) {
4056
- return new Writable({
4057
- ...options,
4058
- write (chunk, encding, callback) {
4059
- setImmediate(callback);
4060
- }
4061
- });
4062
- }
4063
-
4064
- var file = /*#__PURE__*/Object.freeze({
4065
- __proto__: null,
4066
- bytesToHumanReadable: bytesToHumanReadable,
4067
- bytesToKbytes: bytesToKbytes,
4068
- getStreamSize: getStreamSize,
4069
- kbytesToBytes: kbytesToBytes,
4070
- streamToBuffer: streamToBuffer,
4071
- writableDiscardStream: writableDiscardStream
4072
- });
4073
-
4074
- const createPolicy = (options)=>{
4075
- const { name = 'unnamed', validator, handler } = options;
4076
- const wrappedValidator = (config)=>{
4077
- if (validator) {
4078
- try {
4079
- validator(config);
4080
- } catch (e) {
4081
- throw new Error(`Invalid config passed to "${name}" policy.`);
4082
- }
4083
- }
4084
- };
4085
- return {
4086
- name,
4087
- validator: wrappedValidator,
4088
- handler
4089
- };
4090
- };
4091
- const createPolicyContext = (type, ctx)=>{
4092
- return Object.assign({
4093
- is: eq(type),
4094
- get type () {
4095
- return type;
4096
- }
4097
- }, ctx);
4098
- };
4099
-
4100
- var policy = /*#__PURE__*/Object.freeze({
4101
- __proto__: null,
4102
- createPolicy: createPolicy,
4103
- createPolicyContext: createPolicyContext
4104
- });
4105
-
4106
- const nameToSlug = (name, options = {
4107
- separator: '-'
4108
- })=>slugify(name, options);
4109
- const nameToCollectionName = (name)=>slugify(name, {
4110
- separator: '_'
4111
- });
4112
- const toRegressedEnumValue = (value)=>slugify(value, {
4113
- decamelize: false,
4114
- lowercase: false,
4115
- separator: '_'
4116
- });
4117
- const getCommonPath = (...paths)=>{
4118
- const [segments, ...otherSegments] = paths.map((it)=>___default.split(it, '/'));
4119
- return ___default.join(___default.takeWhile(segments, (str, index)=>otherSegments.every((it)=>it[index] === str)), '/');
4120
- };
4121
- const isEqual = (a, b)=>String(a) === String(b);
4122
- const isCamelCase = (value)=>/^[a-z][a-zA-Z0-9]+$/.test(value);
4123
- const isKebabCase = (value)=>/^([a-z][a-z0-9]*)(-[a-z0-9]+)*$/.test(value);
4124
- const startsWithANumber = (value)=>/^[0-9]/.test(value);
4125
- const joinBy = (joint, ...args)=>{
4126
- const trim = trimChars(joint);
4127
- const trimEnd = trimCharsEnd(joint);
4128
- const trimStart = trimCharsStart(joint);
4129
- return args.reduce((url, path, index)=>{
4130
- if (args.length === 1) return path;
4131
- if (index === 0) return trimEnd(path);
4132
- if (index === args.length - 1) return url + joint + trimStart(path);
4133
- return url + joint + trim(path);
4134
- }, '');
4135
- };
4136
- const toKebabCase = (value)=>kebabCase(value);
4137
-
4138
- var strings = /*#__PURE__*/Object.freeze({
4139
- __proto__: null,
4140
- getCommonPath: getCommonPath,
4141
- isCamelCase: isCamelCase,
4142
- isEqual: isEqual,
4143
- isKebabCase: isKebabCase,
4144
- joinBy: joinBy,
4145
- nameToCollectionName: nameToCollectionName,
4146
- nameToSlug: nameToSlug,
4147
- startsWithANumber: startsWithANumber,
4148
- toKebabCase: toKebabCase,
4149
- toRegressedEnumValue: toRegressedEnumValue
4150
- });
4151
-
4152
- const castIncludes = (arr, val, cast)=>arr.map((val)=>cast(val)).includes(cast(val));
4153
- const includesString = (arr, val)=>castIncludes(arr, val, String);
4154
-
4155
- var arrays = /*#__PURE__*/Object.freeze({
4156
- __proto__: null,
4157
- includesString: includesString
4158
- });
4159
-
4160
- const keysDeep = (obj, path = [])=>!___default.isObject(obj) ? [
4161
- path.join('.')
4162
- ] : ___default.reduce(obj, (acc, next, key)=>___default.concat(acc, keysDeep(next, [
4163
- ...path,
4164
- key
4165
- ])), []);
4166
-
4167
- var objects = /*#__PURE__*/Object.freeze({
4168
- __proto__: null,
4169
- keysDeep: keysDeep
4170
- });
4171
-
4172
- // Using timestamp (milliseconds) to be sure it is unique
4173
- // + converting timestamp to base 36 for better readibility
4174
- const timestampCode = (date)=>{
4175
- const referDate = date ?? new Date();
4176
- return referDate.getTime().toString(36);
4177
- };
4178
-
4179
- var dates = /*#__PURE__*/Object.freeze({
4180
- __proto__: null,
4181
- timestampCode: timestampCode
4182
- });
4183
-
4184
- // Code copied from the yup library (https://github.com/jquense/yup)
4185
- // https://github.com/jquense/yup/blob/2778b88bdacd5260d593c6468793da2e77daf21f/src/util/printValue.ts
4186
- const { toString } = Object.prototype;
4187
- const errorToString = Error.prototype.toString;
4188
- const regExpToString = RegExp.prototype.toString;
4189
- const symbolToString = typeof Symbol !== 'undefined' ? Symbol.prototype.toString : ()=>'';
4190
- const SYMBOL_REGEXP = /^Symbol\((.*)\)(.*)$/;
4191
- function printNumber(val) {
4192
- // eslint-disable-next-line eqeqeq
4193
- if (val != +val) return 'NaN';
4194
- const isNegativeZero = val === 0 && 1 / val < 0;
4195
- return isNegativeZero ? '-0' : `${val}`;
4196
- }
4197
- function printSimpleValue(val, quoteStrings = false) {
4198
- if (val == null || val === true || val === false) return `${val}`;
4199
- if (typeof val === 'number') return printNumber(val);
4200
- if (typeof val === 'string') return quoteStrings ? `"${val}"` : val;
4201
- if (typeof val === 'function') return `[Function ${val.name || 'anonymous'}]`;
4202
- if (typeof val === 'symbol') return symbolToString.call(val).replace(SYMBOL_REGEXP, 'Symbol($1)');
4203
- const tag = toString.call(val).slice(8, -1);
4204
- if (tag === 'Date') {
4205
- const v = val;
4206
- return Number.isNaN(v.getTime()) ? `${v}` : v.toISOString();
4207
- }
4208
- if (tag === 'Error' || val instanceof Error) return `[${errorToString.call(val)}]`;
4209
- if (tag === 'RegExp') return regExpToString.call(val);
4210
- return null;
4211
- }
4212
- function printValue(value, quoteStrings) {
4213
- const result = printSimpleValue(value, quoteStrings);
4214
- if (result !== null) return result;
4215
- return JSON.stringify(value, function replacer(key, value) {
4216
- const result = printSimpleValue(this[key], quoteStrings);
4217
- if (result !== null) return result;
4218
- return value;
4219
- }, 2);
4220
- }
4221
-
4222
- const strapiID = ()=>new StrapiIDSchema();
4223
- const isNotNilTest = (value)=>!___default.isNil(value);
4224
- const isNotNullTest = (value)=>!___default.isNull(value);
4225
- yup$1.addMethod(yup$1.mixed, 'notNil', function isNotNill(msg = '${path} must be defined.') {
4226
- return this.test('defined', msg, isNotNilTest);
4227
- });
4228
- yup$1.addMethod(yup$1.mixed, 'notNull', function isNotNull(msg = '${path} cannot be null.') {
4229
- return this.test('defined', msg, isNotNullTest);
4230
- });
4231
- yup$1.addMethod(yup$1.mixed, 'isFunction', function isFunction(message = '${path} is not a function') {
4232
- return this.test('is a function', message, (value)=>___default.isUndefined(value) || ___default.isFunction(value));
4233
- });
4234
- yup$1.addMethod(yup$1.string, 'isCamelCase', function isCamelCase$1(message = '${path} is not in camel case (anExampleOfCamelCase)') {
4235
- return this.test('is in camelCase', message, (value)=>value ? isCamelCase(value) : true);
4236
- });
4237
- yup$1.addMethod(yup$1.string, 'isKebabCase', function isKebabCase$1(message = '${path} is not in kebab case (an-example-of-kebab-case)') {
4238
- return this.test('is in kebab-case', message, (value)=>value ? isKebabCase(value) : true);
4239
- });
4240
- yup$1.addMethod(yup$1.object, 'onlyContainsFunctions', function onlyContainsFunctions(message = '${path} contains values that are not functions') {
4241
- return this.test('only contains functions', message, (value)=>___default.isUndefined(value) || value && Object.values(value).every(___default.isFunction));
4242
- });
4243
- yup$1.addMethod(yup$1.array, 'uniqueProperty', function uniqueProperty(propertyName, message) {
4244
- return this.test('unique', message, function unique(list) {
4245
- const errors = [];
4246
- list?.forEach((element, index)=>{
4247
- const sameElements = list.filter((e)=>get(propertyName, e) === get(propertyName, element));
4248
- if (sameElements.length > 1) {
4249
- errors.push(this.createError({
4250
- path: `${this.path}[${index}].${propertyName}`,
4251
- message
4252
- }));
4253
- }
4254
- });
4255
- if (errors.length) {
4256
- throw new yup$1.ValidationError(errors);
4257
- }
4258
- return true;
4259
- });
4260
- });
4261
- class StrapiIDSchema extends yup$1.MixedSchema {
4262
- _typeCheck(value) {
4263
- return typeof value === 'string' || isNumber(value) && isInteger(value) && value >= 0;
4264
- }
4265
- constructor(){
4266
- super({
4267
- type: 'strapiID'
4268
- });
4269
- }
4270
- }
4271
- // Temporary fix of this issue : https://github.com/jquense/yup/issues/616
4272
- yup$1.setLocale({
4273
- mixed: {
4274
- notType (options) {
4275
- const { path, type, value, originalValue } = options;
4276
- const isCast = originalValue != null && originalValue !== value;
4277
- const msg = `${path} must be a \`${type}\` type, ` + `but the final value was: \`${printValue(value, true)}\`${isCast ? ` (cast from the value \`${printValue(originalValue, true)}\`).` : '.'}`;
4278
- /* Remove comment that is not supposed to be seen by the enduser
4279
- if (value === null) {
4280
- msg += `\n If "null" is intended as an empty value be sure to mark the schema as \`.nullable()\``;
4281
- }
4282
- */ return msg;
4283
- }
4284
- }
4285
- });
4286
-
4287
- var yup = /*#__PURE__*/_mergeNamespaces({
4288
- __proto__: null,
4289
- StrapiIDSchema: StrapiIDSchema,
4290
- strapiID: strapiID
4291
- }, [yup$1]);
4292
-
4293
- const validateZod = (schema)=>(data)=>{
4294
- try {
4295
- return schema.parse(data);
4296
- } catch (error) {
4297
- if (error instanceof z.ZodError) {
4298
- const { message, errors } = formatZodErrors(error);
4299
- throw new ValidationError(message, {
4300
- errors
4301
- });
4302
- }
4303
- throw error;
4304
- }
4305
- };
4306
- const formatZodErrors = (zodError)=>({
4307
- errors: zodError.errors.map((error)=>{
4308
- return {
4309
- path: error.path,
4310
- message: error.message,
4311
- name: 'ValidationError'
4312
- };
4313
- }),
4314
- message: 'Validation error'
4315
- });
4316
-
4317
- export { arrays, async, contentTypes, dates, env, errors, file, hooks, importDefault, isOperator, isOperatorOfType, machineId as machineID, objects, packageManager, pagination, parseType, policy, providerFactory, convertQueryParams as queryParams, relations, index$2 as sanitize, setCreatorFields, strings, template, index$3 as traverse, traverseEntity$1 as traverseEntity, index as validate, validateYupSchema, validateYupSchemaSync, validateZod, yup };
1
+ export { default as parseType } from './parse-type.mjs';
2
+ export { default as env } from './env-helper.mjs';
3
+ export { default as setCreatorFields } from './set-creator-fields.mjs';
4
+ export { default as providerFactory } from './provider-factory.mjs';
5
+ export { default as traverseEntity } from './traverse-entity.mjs';
6
+ export { default as importDefault } from './import-default.mjs';
7
+ export { default as machineID } from './machine-id.mjs';
8
+ export { validateYupSchema, validateYupSchemaSync } from './validators.mjs';
9
+ export { isOperator, isOperatorOfType } from './operators.mjs';
10
+ import * as convertQueryParams from './convert-query-params.mjs';
11
+ export { convertQueryParams as queryParams };
12
+ import * as index from './sanitize/index.mjs';
13
+ export { index as sanitize };
14
+ import * as index$1 from './validate/index.mjs';
15
+ export { index$1 as validate };
16
+ import * as pagination from './pagination.mjs';
17
+ export { pagination };
18
+ import * as packageManager from './package-manager.mjs';
19
+ export { packageManager };
20
+ import * as index$2 from './traverse/index.mjs';
21
+ export { index$2 as traverse };
22
+ import * as template from './template.mjs';
23
+ export { template };
24
+ import * as file from './file.mjs';
25
+ export { file };
26
+ import * as async from './async.mjs';
27
+ export { async };
28
+ import * as policy from './policy.mjs';
29
+ export { policy };
30
+ import * as yup from './yup.mjs';
31
+ export { yup };
32
+ import * as errors from './errors.mjs';
33
+ export { errors };
34
+ import * as contentTypes from './content-types.mjs';
35
+ export { contentTypes };
36
+ import * as relations from './relations.mjs';
37
+ export { relations };
38
+ import * as hooks from './hooks.mjs';
39
+ export { hooks };
40
+ export { validateZod } from './zod.mjs';
41
+ import * as strings from './primitives/strings.mjs';
42
+ export { strings };
43
+ import * as arrays from './primitives/arrays.mjs';
44
+ export { arrays };
45
+ import * as objects from './primitives/objects.mjs';
46
+ export { objects };
47
+ import * as dates from './primitives/dates.mjs';
48
+ export { dates };
4318
49
  //# sourceMappingURL=index.mjs.map