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