@strapi/utils 5.0.0-beta.0 → 5.0.0-beta.10

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 (49) hide show
  1. package/dist/async.d.ts +5 -11
  2. package/dist/async.d.ts.map +1 -1
  3. package/dist/content-types.d.ts +16 -6
  4. package/dist/content-types.d.ts.map +1 -1
  5. package/dist/convert-query-params.d.ts +12 -9
  6. package/dist/convert-query-params.d.ts.map +1 -1
  7. package/dist/file.d.ts +9 -4
  8. package/dist/file.d.ts.map +1 -1
  9. package/dist/hooks.d.ts.map +1 -1
  10. package/dist/index.d.ts +4 -3
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +1507 -1354
  13. package/dist/index.js.map +1 -1
  14. package/dist/index.mjs +1499 -1346
  15. package/dist/index.mjs.map +1 -1
  16. package/dist/pagination.d.ts +34 -4
  17. package/dist/pagination.d.ts.map +1 -1
  18. package/dist/parse-type.d.ts.map +1 -1
  19. package/dist/policy.d.ts +1 -27
  20. package/dist/policy.d.ts.map +1 -1
  21. package/dist/sanitize/index.d.ts +23 -14
  22. package/dist/sanitize/index.d.ts.map +1 -1
  23. package/dist/sanitize/sanitizers.d.ts +10 -6
  24. package/dist/sanitize/sanitizers.d.ts.map +1 -1
  25. package/dist/set-creator-fields.d.ts.map +1 -1
  26. package/dist/traverse/factory.d.ts +5 -4
  27. package/dist/traverse/factory.d.ts.map +1 -1
  28. package/dist/traverse/index.d.ts +0 -1
  29. package/dist/traverse/index.d.ts.map +1 -1
  30. package/dist/traverse/query-populate.d.ts.map +1 -1
  31. package/dist/traverse-entity.d.ts +12 -2
  32. package/dist/traverse-entity.d.ts.map +1 -1
  33. package/dist/types.d.ts +3 -0
  34. package/dist/types.d.ts.map +1 -1
  35. package/dist/validate/index.d.ts +21 -13
  36. package/dist/validate/index.d.ts.map +1 -1
  37. package/dist/validate/utils.d.ts +2 -2
  38. package/dist/validate/utils.d.ts.map +1 -1
  39. package/dist/validate/validators.d.ts +9 -5
  40. package/dist/validate/validators.d.ts.map +1 -1
  41. package/dist/validate/visitors/index.d.ts +1 -0
  42. package/dist/validate/visitors/index.d.ts.map +1 -1
  43. package/dist/validate/visitors/throw-unrecognized-fields.d.ts +4 -0
  44. package/dist/validate/visitors/throw-unrecognized-fields.d.ts.map +1 -0
  45. package/dist/validators.d.ts +2 -2
  46. package/dist/validators.d.ts.map +1 -1
  47. package/dist/zod.d.ts +3 -0
  48. package/dist/zod.d.ts.map +1 -0
  49. package/package.json +11 -9
package/dist/index.mjs CHANGED
@@ -1,14 +1,17 @@
1
- import _$1, { kebabCase } from "lodash";
2
- import { has, union, getOr, assoc, assign, cloneDeep, remove, eq, curry, isArray, isString, isNil, toPath, isObject, clone, pick, isEmpty, omit, trim, pipe as pipe$1, split, map as map$1, flatten, first, identity, constant, join, merge, mergeAll, get, toNumber, isInteger, defaults, trimChars, trimCharsEnd, trimCharsStart, isNumber } from "lodash/fp";
3
- import pMap from "p-map";
4
- import { HttpError } from "http-errors";
1
+ import * as _ from "lodash";
2
+ import ___default, { kebabCase } from "lodash";
3
+ import * as dates$1 from "date-fns";
4
+ import { has, union, getOr, assoc, assign, cloneDeep, remove, eq, curry, isObject, isNil, clone, isArray, isEmpty, toPath, defaults, isString, get, toNumber, isInteger, pick, omit, trim, pipe as pipe$1, split, map as map$1, flatten, first, identity, constant, join, merge, trimChars, trimCharsEnd, trimCharsStart, isNumber } from "lodash/fp";
5
5
  import { randomUUID } from "crypto";
6
6
  import { machineIdSync } from "node-machine-id";
7
7
  import * as yup$1 from "yup";
8
+ import { HttpError } from "http-errors";
9
+ import pMap from "p-map";
8
10
  import execa from "execa";
9
11
  import preferredPM from "preferred-pm";
10
12
  import { Writable } from "node:stream";
11
13
  import slugify from "@sindresorhus/slugify";
14
+ import { z } from "zod";
12
15
  function _mergeNamespaces(n, m) {
13
16
  for (var i = 0; i < m.length; i++) {
14
17
  const e = m[i];
@@ -28,8 +31,6 @@ function _mergeNamespaces(n, m) {
28
31
  }
29
32
  return Object.freeze(Object.defineProperty(n, Symbol.toStringTag, { value: "Module" }));
30
33
  }
31
- const _ = require("lodash");
32
- const dates$1 = require("date-fns");
33
34
  const timeRegex = /^(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]{1,3})?$/;
34
35
  const isDate = (v) => {
35
36
  return dates$1.isDate(v);
@@ -128,32 +129,32 @@ const parseType = (options) => {
128
129
  }
129
130
  };
130
131
  function envFn(key, defaultValue) {
131
- return _$1.has(process.env, key) ? process.env[key] : defaultValue;
132
+ return ___default.has(process.env, key) ? process.env[key] : defaultValue;
132
133
  }
133
134
  function getKey(key) {
134
135
  return process.env[key] ?? "";
135
136
  }
136
137
  const utils = {
137
138
  int(key, defaultValue) {
138
- if (!_$1.has(process.env, key)) {
139
+ if (!___default.has(process.env, key)) {
139
140
  return defaultValue;
140
141
  }
141
142
  return parseInt(getKey(key), 10);
142
143
  },
143
144
  float(key, defaultValue) {
144
- if (!_$1.has(process.env, key)) {
145
+ if (!___default.has(process.env, key)) {
145
146
  return defaultValue;
146
147
  }
147
148
  return parseFloat(getKey(key));
148
149
  },
149
150
  bool(key, defaultValue) {
150
- if (!_$1.has(process.env, key)) {
151
+ if (!___default.has(process.env, key)) {
151
152
  return defaultValue;
152
153
  }
153
154
  return getKey(key) === "true";
154
155
  },
155
156
  json(key, defaultValue) {
156
- if (!_$1.has(process.env, key)) {
157
+ if (!___default.has(process.env, key)) {
157
158
  return defaultValue;
158
159
  }
159
160
  try {
@@ -166,7 +167,7 @@ const utils = {
166
167
  }
167
168
  },
168
169
  array(key, defaultValue) {
169
- if (!_$1.has(process.env, key)) {
170
+ if (!___default.has(process.env, key)) {
170
171
  return defaultValue;
171
172
  }
172
173
  let value = getKey(key);
@@ -174,11 +175,11 @@ const utils = {
174
175
  value = value.substring(1, value.length - 1);
175
176
  }
176
177
  return value.split(",").map((v) => {
177
- return _$1.trim(_$1.trim(v, " "), '"');
178
+ return ___default.trim(___default.trim(v, " "), '"');
178
179
  });
179
180
  },
180
181
  date(key, defaultValue) {
181
- if (!_$1.has(process.env, key)) {
182
+ if (!___default.has(process.env, key)) {
182
183
  return defaultValue;
183
184
  }
184
185
  return new Date(getKey(key));
@@ -245,12 +246,12 @@ const getCreatorFields = (model) => {
245
246
  const getNonWritableAttributes = (model) => {
246
247
  if (!model)
247
248
  return [];
248
- const nonWritableAttributes = _$1.reduce(
249
+ const nonWritableAttributes = ___default.reduce(
249
250
  model.attributes,
250
251
  (acc, attr, attrName) => attr.writable === false ? acc.concat(attrName) : acc,
251
252
  []
252
253
  );
253
- return _$1.uniq([
254
+ return ___default.uniq([
254
255
  ID_ATTRIBUTE$4,
255
256
  DOC_ID_ATTRIBUTE$4,
256
257
  ...getTimestamps(model),
@@ -260,28 +261,37 @@ const getNonWritableAttributes = (model) => {
260
261
  const getWritableAttributes = (model) => {
261
262
  if (!model)
262
263
  return [];
263
- return _$1.difference(Object.keys(model.attributes), getNonWritableAttributes(model));
264
+ return ___default.difference(Object.keys(model.attributes), getNonWritableAttributes(model));
264
265
  };
265
266
  const isWritableAttribute = (model, attributeName) => {
266
267
  return getWritableAttributes(model).includes(attributeName);
267
268
  };
268
269
  const getNonVisibleAttributes = (model) => {
269
- const nonVisibleAttributes = _$1.reduce(
270
+ const nonVisibleAttributes = ___default.reduce(
270
271
  model.attributes,
271
272
  (acc, attr, attrName) => attr.visible === false ? acc.concat(attrName) : acc,
272
273
  []
273
274
  );
274
- return _$1.uniq([ID_ATTRIBUTE$4, DOC_ID_ATTRIBUTE$4, ...getTimestamps(model), ...nonVisibleAttributes]);
275
+ return ___default.uniq([ID_ATTRIBUTE$4, DOC_ID_ATTRIBUTE$4, ...getTimestamps(model), ...nonVisibleAttributes]);
275
276
  };
276
277
  const getVisibleAttributes = (model) => {
277
- return _$1.difference(_$1.keys(model.attributes), getNonVisibleAttributes(model));
278
+ return ___default.difference(___default.keys(model.attributes), getNonVisibleAttributes(model));
278
279
  };
279
280
  const isVisibleAttribute = (model, attributeName) => {
280
281
  return getVisibleAttributes(model).includes(attributeName);
281
282
  };
282
- const getOptions = (model) => _$1.assign({ draftAndPublish: false }, _$1.get(model, "options", {}));
283
- const hasDraftAndPublish = (model) => _$1.get(model, "options.draftAndPublish", false) === true;
284
- const isDraft = (data, model) => hasDraftAndPublish(model) && _$1.get(data, PUBLISHED_AT_ATTRIBUTE$1) === null;
283
+ const getOptions = (model) => ___default.assign({ draftAndPublish: false }, ___default.get(model, "options", {}));
284
+ const hasDraftAndPublish = (model) => ___default.get(model, "options.draftAndPublish", false) === true;
285
+ const isDraft = (data, model) => hasDraftAndPublish(model) && ___default.get(data, PUBLISHED_AT_ATTRIBUTE$1) === null;
286
+ const isSchema = (data) => {
287
+ return typeof data === "object" && data !== null && "modelType" in data && typeof data.modelType === "string" && ["component", "contentType"].includes(data.modelType);
288
+ };
289
+ const isComponentSchema = (data) => {
290
+ return isSchema(data) && data.modelType === "component";
291
+ };
292
+ const isContentTypeSchema = (data) => {
293
+ return isSchema(data) && data.modelType === "contentType";
294
+ };
285
295
  const isSingleType = ({ kind = COLLECTION_TYPE }) => kind === SINGLE_TYPE;
286
296
  const isCollectionType = ({ kind = COLLECTION_TYPE }) => kind === COLLECTION_TYPE;
287
297
  const isKind = (kind) => (model) => model.kind === kind;
@@ -290,9 +300,9 @@ const getStoredPrivateAttributes = (model) => union(
290
300
  getOr([], "options.privateAttributes", model)
291
301
  );
292
302
  const getPrivateAttributes = (model) => {
293
- return _$1.union(
303
+ return ___default.union(
294
304
  getStoredPrivateAttributes(model),
295
- _$1.keys(_$1.pickBy(model.attributes, (attr) => !!attr.private))
305
+ ___default.keys(___default.pickBy(model.attributes, (attr) => !!attr.private))
296
306
  );
297
307
  };
298
308
  const isPrivateAttribute = (model, attributeName) => {
@@ -302,17 +312,22 @@ const isPrivateAttribute = (model, attributeName) => {
302
312
  return getStoredPrivateAttributes(model).includes(attributeName);
303
313
  };
304
314
  const isScalarAttribute = (attribute) => {
305
- return !["media", "component", "relation", "dynamiczone"].includes(attribute?.type);
315
+ return attribute && !["media", "component", "relation", "dynamiczone"].includes(attribute.type);
316
+ };
317
+ const getDoesAttributeRequireValidation = (attribute) => {
318
+ 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");
306
319
  };
307
320
  const isMediaAttribute = (attribute) => attribute?.type === "media";
308
321
  const isRelationalAttribute = (attribute) => attribute?.type === "relation";
322
+ const HAS_RELATION_REORDERING = ["manyToMany", "manyToOne", "oneToMany"];
323
+ const hasRelationReordering = (attribute) => isRelationalAttribute(attribute) && HAS_RELATION_REORDERING.includes(attribute.relation);
309
324
  const isComponentAttribute = (attribute) => ["component", "dynamiczone"].includes(attribute?.type);
310
- const isDynamicZoneAttribute = (attribute) => attribute?.type === "dynamiczone";
325
+ const isDynamicZoneAttribute = (attribute) => !!attribute && attribute.type === "dynamiczone";
311
326
  const isMorphToRelationalAttribute = (attribute) => {
312
- return isRelationalAttribute(attribute) && attribute?.relation?.startsWith?.("morphTo");
327
+ return !!attribute && isRelationalAttribute(attribute) && attribute.relation?.startsWith?.("morphTo");
313
328
  };
314
329
  const getComponentAttributes = (schema) => {
315
- return _$1.reduce(
330
+ return ___default.reduce(
316
331
  schema.attributes,
317
332
  (acc, attr, attrName) => {
318
333
  if (isComponentAttribute(attr))
@@ -323,7 +338,7 @@ const getComponentAttributes = (schema) => {
323
338
  );
324
339
  };
325
340
  const getScalarAttributes = (schema) => {
326
- return _$1.reduce(
341
+ return ___default.reduce(
327
342
  schema.attributes,
328
343
  (acc, attr, attrName) => {
329
344
  if (isScalarAttribute(attr))
@@ -333,11 +348,22 @@ const getScalarAttributes = (schema) => {
333
348
  []
334
349
  );
335
350
  };
351
+ const getRelationalAttributes = (schema) => {
352
+ return ___default.reduce(
353
+ schema.attributes,
354
+ (acc, attr, attrName) => {
355
+ if (isRelationalAttribute(attr))
356
+ acc.push(attrName);
357
+ return acc;
358
+ },
359
+ []
360
+ );
361
+ };
336
362
  const isTypedAttribute = (attribute, type) => {
337
- return _$1.has(attribute, "type") && attribute.type === type;
363
+ return ___default.has(attribute, "type") && attribute.type === type;
338
364
  };
339
365
  const getContentTypeRoutePrefix = (contentType) => {
340
- return isSingleType(contentType) ? _$1.kebabCase(contentType.info.singularName) : _$1.kebabCase(contentType.info.pluralName);
366
+ return isSingleType(contentType) ? ___default.kebabCase(contentType.info.singularName) : ___default.kebabCase(contentType.info.pluralName);
341
367
  };
342
368
  const contentTypes = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
343
369
  __proto__: null,
@@ -345,17 +371,22 @@ const contentTypes = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.define
345
371
  getComponentAttributes,
346
372
  getContentTypeRoutePrefix,
347
373
  getCreatorFields,
374
+ getDoesAttributeRequireValidation,
348
375
  getNonVisibleAttributes,
349
376
  getNonWritableAttributes,
350
377
  getOptions,
351
378
  getPrivateAttributes,
379
+ getRelationalAttributes,
352
380
  getScalarAttributes,
353
381
  getTimestamps,
354
382
  getVisibleAttributes,
355
383
  getWritableAttributes,
356
384
  hasDraftAndPublish,
385
+ hasRelationReordering,
357
386
  isCollectionType,
358
387
  isComponentAttribute,
388
+ isComponentSchema,
389
+ isContentTypeSchema,
359
390
  isDraft,
360
391
  isDynamicZoneAttribute,
361
392
  isKind,
@@ -364,6 +395,7 @@ const contentTypes = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.define
364
395
  isPrivateAttribute,
365
396
  isRelationalAttribute,
366
397
  isScalarAttribute,
398
+ isSchema,
367
399
  isSingleType,
368
400
  isTypedAttribute,
369
401
  isVisibleAttribute,
@@ -516,201 +548,33 @@ const providerFactory = (options = {}) => {
516
548
  }
517
549
  };
518
550
  };
519
- function pipe(...fns) {
520
- const [firstFn, ...fnRest] = fns;
521
- return async (...args) => {
522
- let res = await firstFn.apply(firstFn, args);
523
- for (let i = 0; i < fnRest.length; i += 1) {
524
- res = await fnRest[i](res);
525
- }
526
- return res;
551
+ const traverseEntity = async (visitor2, options, entity) => {
552
+ const { path = { raw: null, attribute: null }, schema, getModel } = options;
553
+ let parent = options.parent;
554
+ const traverseMorphRelationTarget = async (visitor22, path2, entry) => {
555
+ const targetSchema = getModel(entry.__type);
556
+ const traverseOptions = { schema: targetSchema, path: path2, getModel, parent };
557
+ return traverseEntity(visitor22, traverseOptions, entry);
527
558
  };
528
- }
529
- const map = curry(pMap);
530
- const reduce = (mixedArray) => async (iteratee, initialValue) => {
531
- let acc = initialValue;
532
- for (let i = 0; i < mixedArray.length; i += 1) {
533
- acc = await iteratee(acc, await mixedArray[i], i);
534
- }
535
- return acc;
536
- };
537
- const async = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
538
- __proto__: null,
539
- map,
540
- pipe,
541
- reduce
542
- }, Symbol.toStringTag, { value: "Module" }));
543
- const visitor$8 = ({ key, attribute }, { remove: remove2 }) => {
544
- if (attribute?.type === "password") {
545
- remove2(key);
546
- }
547
- };
548
- const visitor$7 = ({ schema, key, attribute }, { remove: remove2 }) => {
549
- if (!attribute) {
550
- return;
551
- }
552
- const isPrivate = attribute.private === true || isPrivateAttribute(schema, key);
553
- if (isPrivate) {
554
- remove2(key);
555
- }
556
- };
557
- const ACTIONS_TO_VERIFY$1 = ["find"];
558
- const { CREATED_BY_ATTRIBUTE: CREATED_BY_ATTRIBUTE$1, UPDATED_BY_ATTRIBUTE: UPDATED_BY_ATTRIBUTE$1 } = constants$1;
559
- const removeRestrictedRelations = (auth) => async ({ data, key, attribute, schema }, { remove: remove2, set }) => {
560
- if (!attribute) {
561
- return;
562
- }
563
- const isRelation = attribute.type === "relation";
564
- if (!isRelation) {
565
- return;
566
- }
567
- const handleMorphRelation = async () => {
568
- const newMorphValue = [];
569
- for (const element of data[key]) {
570
- const scopes = ACTIONS_TO_VERIFY$1.map((action) => `${element.__type}.${action}`);
571
- const isAllowed = await hasAccessToSomeScopes$1(scopes, auth);
572
- if (isAllowed) {
573
- newMorphValue.push(element);
574
- }
575
- }
576
- if (newMorphValue.length === 0) {
577
- remove2(key);
578
- } else {
579
- set(key, newMorphValue);
580
- }
559
+ const traverseRelationTarget = (schema2) => async (visitor22, path2, entry) => {
560
+ const traverseOptions = { schema: schema2, path: path2, getModel, parent };
561
+ return traverseEntity(visitor22, traverseOptions, entry);
581
562
  };
582
- const handleRegularRelation = async () => {
583
- const scopes = ACTIONS_TO_VERIFY$1.map((action) => `${attribute.target}.${action}`);
584
- const isAllowed = await hasAccessToSomeScopes$1(scopes, auth);
585
- if (!isAllowed) {
586
- remove2(key);
587
- }
563
+ const traverseMediaTarget = async (visitor22, path2, entry) => {
564
+ const targetSchemaUID = "plugin::upload.file";
565
+ const targetSchema = getModel(targetSchemaUID);
566
+ const traverseOptions = { schema: targetSchema, path: path2, getModel, parent };
567
+ return traverseEntity(visitor22, traverseOptions, entry);
568
+ };
569
+ const traverseComponent = async (visitor22, path2, schema2, entry) => {
570
+ const traverseOptions = { schema: schema2, path: path2, getModel, parent };
571
+ return traverseEntity(visitor22, traverseOptions, entry);
572
+ };
573
+ const visitDynamicZoneEntry = async (visitor22, path2, entry) => {
574
+ const targetSchema = getModel(entry.__component);
575
+ const traverseOptions = { schema: targetSchema, path: path2, getModel, parent };
576
+ return traverseEntity(visitor22, traverseOptions, entry);
588
577
  };
589
- const isCreatorRelation = [CREATED_BY_ATTRIBUTE$1, UPDATED_BY_ATTRIBUTE$1].includes(key);
590
- if (isMorphToRelationalAttribute(attribute)) {
591
- await handleMorphRelation();
592
- return;
593
- }
594
- if (isCreatorRelation && schema.options?.populateCreatorFields) {
595
- return;
596
- }
597
- await handleRegularRelation();
598
- };
599
- const hasAccessToSomeScopes$1 = async (scopes, auth) => {
600
- for (const scope of scopes) {
601
- try {
602
- await strapi.auth.verify(auth, { scope });
603
- return true;
604
- } catch {
605
- continue;
606
- }
607
- }
608
- return false;
609
- };
610
- const visitor$6 = ({ key, attribute }, { remove: remove2 }) => {
611
- if (isMorphToRelationalAttribute(attribute)) {
612
- remove2(key);
613
- }
614
- };
615
- const visitor$5 = ({ key, attribute }, { remove: remove2 }) => {
616
- if (isDynamicZoneAttribute(attribute)) {
617
- remove2(key);
618
- }
619
- };
620
- const removeDisallowedFields = (allowedFields = null) => ({ key, path: { attribute: path } }, { remove: remove2 }) => {
621
- if (allowedFields === null) {
622
- return;
623
- }
624
- if (!(isArray(allowedFields) && allowedFields.every(isString))) {
625
- throw new TypeError(
626
- `Expected array of strings for allowedFields but got "${typeof allowedFields}"`
627
- );
628
- }
629
- if (isNil(path)) {
630
- return;
631
- }
632
- const containedPaths = getContainedPaths$1(path);
633
- const isPathAllowed = allowedFields.some(
634
- (p) => containedPaths.includes(p) || p.startsWith(`${path}.`)
635
- );
636
- if (isPathAllowed) {
637
- return;
638
- }
639
- remove2(key);
640
- };
641
- const getContainedPaths$1 = (path) => {
642
- const parts = toPath(path);
643
- return parts.reduce((acc, value, index2, list) => {
644
- return [...acc, list.slice(0, index2 + 1).join(".")];
645
- }, []);
646
- };
647
- const removeRestrictedFields = (restrictedFields = null) => ({ key, path: { attribute: path } }, { remove: remove2 }) => {
648
- if (restrictedFields === null) {
649
- remove2(key);
650
- return;
651
- }
652
- if (!(isArray(restrictedFields) && restrictedFields.every(isString))) {
653
- throw new TypeError(
654
- `Expected array of strings for restrictedFields but got "${typeof restrictedFields}"`
655
- );
656
- }
657
- if (restrictedFields.includes(path)) {
658
- remove2(key);
659
- return;
660
- }
661
- const isRestrictedNested = restrictedFields.some(
662
- (allowedPath) => path?.toString().startsWith(`${allowedPath}.`)
663
- );
664
- if (isRestrictedNested) {
665
- remove2(key);
666
- }
667
- };
668
- const visitor$4 = ({ schema, key, value }, { set }) => {
669
- if (key === "" && value === "*") {
670
- const { attributes } = schema;
671
- const newPopulateQuery = Object.entries(attributes).filter(
672
- ([, attribute]) => ["relation", "component", "media", "dynamiczone"].includes(attribute.type)
673
- ).reduce((acc, [key2]) => ({ ...acc, [key2]: true }), {});
674
- set("", newPopulateQuery);
675
- }
676
- };
677
- const visitors$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
678
- __proto__: null,
679
- expandWildcardPopulate: visitor$4,
680
- removeDisallowedFields,
681
- removeDynamicZones: visitor$5,
682
- removeMorphToRelations: visitor$6,
683
- removePassword: visitor$8,
684
- removePrivate: visitor$7,
685
- removeRestrictedFields,
686
- removeRestrictedRelations
687
- }, Symbol.toStringTag, { value: "Module" }));
688
- const traverseMorphRelationTarget = async (visitor2, path, entry) => {
689
- const targetSchema = strapi.getModel(entry.__type);
690
- const traverseOptions = { schema: targetSchema, path };
691
- return traverseEntity(visitor2, traverseOptions, entry);
692
- };
693
- const traverseRelationTarget = (schema) => async (visitor2, path, entry) => {
694
- const traverseOptions = { schema, path };
695
- return traverseEntity(visitor2, traverseOptions, entry);
696
- };
697
- const traverseMediaTarget = async (visitor2, path, entry) => {
698
- const targetSchemaUID = "plugin::upload.file";
699
- const targetSchema = strapi.getModel(targetSchemaUID);
700
- const traverseOptions = { schema: targetSchema, path };
701
- return traverseEntity(visitor2, traverseOptions, entry);
702
- };
703
- const traverseComponent = async (visitor2, path, schema, entry) => {
704
- const traverseOptions = { schema, path };
705
- return traverseEntity(visitor2, traverseOptions, entry);
706
- };
707
- const visitDynamicZoneEntry = async (visitor2, path, entry) => {
708
- const targetSchema = strapi.getModel(entry.__component);
709
- const traverseOptions = { schema: targetSchema, path };
710
- return traverseEntity(visitor2, traverseOptions, entry);
711
- };
712
- const traverseEntity = async (visitor2, options, entity) => {
713
- const { path = { raw: null, attribute: null }, schema } = options;
714
578
  if (!isObject(entity) || isNil(schema)) {
715
579
  return entity;
716
580
  }
@@ -720,9 +584,6 @@ const traverseEntity = async (visitor2, options, entity) => {
720
584
  for (let i = 0; i < keys.length; i += 1) {
721
585
  const key = keys[i];
722
586
  const attribute = schema.attributes[key];
723
- if (isNil(attribute)) {
724
- continue;
725
- }
726
587
  const newPath = { ...path };
727
588
  newPath.raw = isNil(path.raw) ? key : `${path.raw}.${key}`;
728
589
  if (!isNil(attribute)) {
@@ -734,16 +595,19 @@ const traverseEntity = async (visitor2, options, entity) => {
734
595
  key,
735
596
  value: copy[key],
736
597
  attribute,
737
- path: newPath
598
+ path: newPath,
599
+ getModel,
600
+ parent
738
601
  };
739
602
  await visitor2(visitorOptions, visitorUtils);
740
603
  const value = copy[key];
741
- if (isNil(value)) {
604
+ if (isNil(value) || isNil(attribute)) {
742
605
  continue;
743
606
  }
607
+ parent = { schema, key, attribute, path: newPath };
744
608
  if (isRelationalAttribute(attribute)) {
745
609
  const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
746
- const method = isMorphRelation ? traverseMorphRelationTarget : traverseRelationTarget(strapi.getModel(attribute.target));
610
+ const method = isMorphRelation ? traverseMorphRelationTarget : traverseRelationTarget(getModel(attribute.target));
747
611
  if (isArray(value)) {
748
612
  const res = new Array(value.length);
749
613
  for (let i2 = 0; i2 < value.length; i2 += 1) {
@@ -768,7 +632,7 @@ const traverseEntity = async (visitor2, options, entity) => {
768
632
  continue;
769
633
  }
770
634
  if (attribute.type === "component") {
771
- const targetSchema = strapi.getModel(attribute.component);
635
+ const targetSchema = getModel(attribute.component);
772
636
  if (isArray(value)) {
773
637
  const res = new Array(value.length);
774
638
  for (let i2 = 0; i2 < value.length; i2 += 1) {
@@ -800,74 +664,859 @@ const createVisitorUtils = ({ data }) => ({
800
664
  }
801
665
  });
802
666
  const traverseEntity$1 = curry(traverseEntity);
803
- const DEFAULT_PATH = { raw: null, attribute: null };
804
- const traverseFactory = () => {
805
- const state = {
806
- parsers: [],
807
- interceptors: [],
808
- ignore: [],
809
- handlers: {
810
- attributes: [],
811
- common: []
812
- }
813
- };
814
- const traverse = async (visitor2, options, data) => {
815
- const { path = DEFAULT_PATH, schema } = options ?? {};
816
- for (const { predicate, handler } of state.interceptors) {
817
- if (predicate(data)) {
818
- return handler(visitor2, options, data, { recurse: traverse });
819
- }
820
- }
821
- const parser = state.parsers.find((parser2) => parser2.predicate(data))?.parser;
822
- const utils2 = parser?.(data);
823
- if (!utils2) {
824
- return data;
825
- }
826
- let out = utils2.transform(data);
827
- const keys = utils2.keys(out);
828
- for (const key of keys) {
829
- const attribute = schema?.attributes?.[key] ?? // FIX: Needed to not break existing behavior on the API.
830
- // It looks for the attribute in the DB metadata when the key is in snake_case
831
- schema?.attributes?.[strapi.db.metadata.get(schema?.uid).columnToAttribute[key]];
832
- const newPath = { ...path };
833
- newPath.raw = isNil(path.raw) ? key : `${path.raw}.${key}`;
834
- if (!isNil(attribute)) {
835
- newPath.attribute = isNil(path.attribute) ? key : `${path.attribute}.${key}`;
836
- }
837
- const visitorOptions = {
838
- key,
839
- value: utils2.get(key, out),
840
- attribute,
841
- schema,
842
- path: newPath,
843
- data: out
844
- };
845
- const transformUtils = {
846
- remove(key2) {
847
- out = utils2.remove(key2, out);
848
- },
849
- set(key2, value2) {
850
- out = utils2.set(key2, value2, out);
851
- },
852
- recurse: traverse
853
- };
854
- await visitor2(visitorOptions, pick(["remove", "set"], transformUtils));
855
- const value = utils2.get(key, out);
856
- const createContext = () => ({
857
- key,
858
- value,
859
- attribute,
860
- schema,
861
- path: newPath,
862
- data: out,
863
- visitor: visitor2
864
- });
865
- const ignoreCtx = createContext();
866
- const shouldIgnore = state.ignore.some((predicate) => predicate(ignoreCtx));
867
- if (shouldIgnore) {
868
- continue;
869
- }
870
- const handlers = [...state.handlers.common, ...state.handlers.attributes];
667
+ function importDefault(modName) {
668
+ const mod = require(modName);
669
+ return mod && mod.__esModule ? mod.default : mod;
670
+ }
671
+ const machineId = () => {
672
+ try {
673
+ const deviceId = machineIdSync();
674
+ return deviceId;
675
+ } catch (error) {
676
+ const deviceId = randomUUID();
677
+ return deviceId;
678
+ }
679
+ };
680
+ const formatYupInnerError = (yupError) => ({
681
+ path: toPath(yupError.path),
682
+ message: yupError.message,
683
+ name: yupError.name
684
+ });
685
+ const formatYupErrors = (yupError) => ({
686
+ errors: isEmpty(yupError.inner) ? [formatYupInnerError(yupError)] : yupError.inner.map(formatYupInnerError),
687
+ message: yupError.message
688
+ });
689
+ class ApplicationError extends Error {
690
+ name;
691
+ details;
692
+ message;
693
+ constructor(message = "An application error occured", details = {}) {
694
+ super();
695
+ this.name = "ApplicationError";
696
+ this.message = message;
697
+ this.details = details;
698
+ }
699
+ }
700
+ class ValidationError extends ApplicationError {
701
+ constructor(message, details) {
702
+ super(message, details);
703
+ this.name = "ValidationError";
704
+ }
705
+ }
706
+ class YupValidationError extends ValidationError {
707
+ constructor(yupError, message) {
708
+ super("Validation");
709
+ const { errors: errors2, message: yupMessage } = formatYupErrors(yupError);
710
+ this.message = message || yupMessage;
711
+ this.details = { errors: errors2 };
712
+ }
713
+ }
714
+ class PaginationError extends ApplicationError {
715
+ constructor(message = "Invalid pagination", details) {
716
+ super(message, details);
717
+ this.name = "PaginationError";
718
+ this.message = message;
719
+ }
720
+ }
721
+ class NotFoundError extends ApplicationError {
722
+ constructor(message = "Entity not found", details) {
723
+ super(message, details);
724
+ this.name = "NotFoundError";
725
+ this.message = message;
726
+ }
727
+ }
728
+ class ForbiddenError extends ApplicationError {
729
+ constructor(message = "Forbidden access", details) {
730
+ super(message, details);
731
+ this.name = "ForbiddenError";
732
+ this.message = message;
733
+ }
734
+ }
735
+ class UnauthorizedError extends ApplicationError {
736
+ constructor(message = "Unauthorized", details) {
737
+ super(message, details);
738
+ this.name = "UnauthorizedError";
739
+ this.message = message;
740
+ }
741
+ }
742
+ class RateLimitError extends ApplicationError {
743
+ constructor(message = "Too many requests, please try again later.", details) {
744
+ super(message, details);
745
+ this.name = "RateLimitError";
746
+ this.message = message;
747
+ this.details = details || {};
748
+ }
749
+ }
750
+ class PayloadTooLargeError extends ApplicationError {
751
+ constructor(message = "Entity too large", details) {
752
+ super(message, details);
753
+ this.name = "PayloadTooLargeError";
754
+ this.message = message;
755
+ }
756
+ }
757
+ class PolicyError extends ForbiddenError {
758
+ constructor(message = "Policy Failed", details) {
759
+ super(message, details);
760
+ this.name = "PolicyError";
761
+ this.message = message;
762
+ this.details = details || {};
763
+ }
764
+ }
765
+ class NotImplementedError extends ApplicationError {
766
+ constructor(message = "This feature is not implemented yet", details) {
767
+ super(message, details);
768
+ this.name = "NotImplementedError";
769
+ this.message = message;
770
+ }
771
+ }
772
+ const errors = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
773
+ __proto__: null,
774
+ ApplicationError,
775
+ ForbiddenError,
776
+ HttpError,
777
+ NotFoundError,
778
+ NotImplementedError,
779
+ PaginationError,
780
+ PayloadTooLargeError,
781
+ PolicyError,
782
+ RateLimitError,
783
+ UnauthorizedError,
784
+ ValidationError,
785
+ YupValidationError
786
+ }, Symbol.toStringTag, { value: "Module" }));
787
+ const handleYupError = (error, errorMessage) => {
788
+ throw new YupValidationError(error, errorMessage);
789
+ };
790
+ const defaultValidationParam = { strict: true, abortEarly: false };
791
+ const validateYupSchema = (schema, options = {}) => async (body, errorMessage) => {
792
+ try {
793
+ const optionsWithDefaults = defaults(defaultValidationParam, options);
794
+ const result = await schema.validate(body, optionsWithDefaults);
795
+ return result;
796
+ } catch (e) {
797
+ if (e instanceof yup$1.ValidationError) {
798
+ handleYupError(e, errorMessage);
799
+ }
800
+ throw e;
801
+ }
802
+ };
803
+ const validateYupSchemaSync = (schema, options = {}) => (body, errorMessage) => {
804
+ try {
805
+ const optionsWithDefaults = defaults(defaultValidationParam, options);
806
+ return schema.validateSync(body, optionsWithDefaults);
807
+ } catch (e) {
808
+ if (e instanceof yup$1.ValidationError) {
809
+ handleYupError(e, errorMessage);
810
+ }
811
+ throw e;
812
+ }
813
+ };
814
+ const GROUP_OPERATORS = ["$and", "$or"];
815
+ const WHERE_OPERATORS = [
816
+ "$not",
817
+ "$in",
818
+ "$notIn",
819
+ "$eq",
820
+ "$eqi",
821
+ "$ne",
822
+ "$nei",
823
+ "$gt",
824
+ "$gte",
825
+ "$lt",
826
+ "$lte",
827
+ "$null",
828
+ "$notNull",
829
+ "$between",
830
+ "$startsWith",
831
+ "$endsWith",
832
+ "$startsWithi",
833
+ "$endsWithi",
834
+ "$contains",
835
+ "$notContains",
836
+ "$containsi",
837
+ "$notContainsi",
838
+ // Experimental, only for internal use
839
+ "$jsonSupersetOf"
840
+ ];
841
+ const CAST_OPERATORS = [
842
+ "$not",
843
+ "$in",
844
+ "$notIn",
845
+ "$eq",
846
+ "$ne",
847
+ "$gt",
848
+ "$gte",
849
+ "$lt",
850
+ "$lte",
851
+ "$between"
852
+ ];
853
+ const ARRAY_OPERATORS = ["$in", "$notIn", "$between"];
854
+ const OPERATORS = {
855
+ where: WHERE_OPERATORS,
856
+ cast: CAST_OPERATORS,
857
+ group: GROUP_OPERATORS,
858
+ array: ARRAY_OPERATORS
859
+ };
860
+ const OPERATORS_LOWERCASE = Object.fromEntries(
861
+ Object.entries(OPERATORS).map(([key, values]) => [
862
+ key,
863
+ values.map((value) => value.toLowerCase())
864
+ ])
865
+ );
866
+ const isObjKey = (key, obj) => {
867
+ return key in obj;
868
+ };
869
+ const isOperatorOfType = (type, key, ignoreCase = false) => {
870
+ if (ignoreCase) {
871
+ return OPERATORS_LOWERCASE[type]?.includes(key.toLowerCase()) ?? false;
872
+ }
873
+ if (isObjKey(type, OPERATORS)) {
874
+ return OPERATORS[type]?.includes(key) ?? false;
875
+ }
876
+ return false;
877
+ };
878
+ const isOperator = (key, ignoreCase = false) => {
879
+ return Object.keys(OPERATORS).some((type) => isOperatorOfType(type, key, ignoreCase));
880
+ };
881
+ const { ID_ATTRIBUTE: ID_ATTRIBUTE$3, DOC_ID_ATTRIBUTE: DOC_ID_ATTRIBUTE$3, PUBLISHED_AT_ATTRIBUTE } = constants$1;
882
+ class InvalidOrderError extends Error {
883
+ constructor() {
884
+ super();
885
+ this.message = "Invalid order. order can only be one of asc|desc|ASC|DESC";
886
+ }
887
+ }
888
+ class InvalidSortError extends Error {
889
+ constructor() {
890
+ super();
891
+ this.message = "Invalid sort parameter. Expected a string, an array of strings, a sort object or an array of sort objects";
892
+ }
893
+ }
894
+ function validateOrder(order) {
895
+ if (!isString(order) || !["asc", "desc"].includes(order.toLocaleLowerCase())) {
896
+ throw new InvalidOrderError();
897
+ }
898
+ }
899
+ const convertCountQueryParams = (countQuery) => {
900
+ return parseType({ type: "boolean", value: countQuery });
901
+ };
902
+ const convertOrderingQueryParams = (ordering) => {
903
+ return ordering;
904
+ };
905
+ const isPlainObject = (value) => ___default.isPlainObject(value);
906
+ const isStringArray$3 = (value) => isArray(value) && value.every(isString);
907
+ const createTransformer = ({ getModel }) => {
908
+ const convertSortQueryParams = (sortQuery) => {
909
+ if (typeof sortQuery === "string") {
910
+ return convertStringSortQueryParam(sortQuery);
911
+ }
912
+ if (isStringArray$3(sortQuery)) {
913
+ return sortQuery.flatMap((sortValue) => convertStringSortQueryParam(sortValue));
914
+ }
915
+ if (Array.isArray(sortQuery)) {
916
+ return sortQuery.map((sortValue) => convertNestedSortQueryParam(sortValue));
917
+ }
918
+ if (isPlainObject(sortQuery)) {
919
+ return convertNestedSortQueryParam(sortQuery);
920
+ }
921
+ throw new InvalidSortError();
922
+ };
923
+ const convertStringSortQueryParam = (sortQuery) => {
924
+ return sortQuery.split(",").map((value) => convertSingleSortQueryParam(value));
925
+ };
926
+ const convertSingleSortQueryParam = (sortQuery) => {
927
+ if (!sortQuery) {
928
+ return {};
929
+ }
930
+ if (!isString(sortQuery)) {
931
+ throw new Error("Invalid sort query");
932
+ }
933
+ const [field, order = "asc"] = sortQuery.split(":");
934
+ if (field.length === 0) {
935
+ throw new Error("Field cannot be empty");
936
+ }
937
+ validateOrder(order);
938
+ return ___default.set({}, field, order);
939
+ };
940
+ const convertNestedSortQueryParam = (sortQuery) => {
941
+ const transformedSort = {};
942
+ for (const field of Object.keys(sortQuery)) {
943
+ const order = sortQuery[field];
944
+ if (isPlainObject(order)) {
945
+ transformedSort[field] = convertNestedSortQueryParam(order);
946
+ } else if (typeof order === "string") {
947
+ validateOrder(order);
948
+ transformedSort[field] = order;
949
+ } else {
950
+ throw Error(`Invalid sort type expected object or string got ${typeof order}`);
951
+ }
952
+ }
953
+ return transformedSort;
954
+ };
955
+ const convertStartQueryParams = (startQuery) => {
956
+ const startAsANumber = ___default.toNumber(startQuery);
957
+ if (!___default.isInteger(startAsANumber) || startAsANumber < 0) {
958
+ throw new Error(`convertStartQueryParams expected a positive integer got ${startAsANumber}`);
959
+ }
960
+ return startAsANumber;
961
+ };
962
+ const convertLimitQueryParams = (limitQuery) => {
963
+ const limitAsANumber = ___default.toNumber(limitQuery);
964
+ if (!___default.isInteger(limitAsANumber) || limitAsANumber !== -1 && limitAsANumber < 0) {
965
+ throw new Error(`convertLimitQueryParams expected a positive integer got ${limitAsANumber}`);
966
+ }
967
+ if (limitAsANumber === -1) {
968
+ return void 0;
969
+ }
970
+ return limitAsANumber;
971
+ };
972
+ const convertPageQueryParams = (page) => {
973
+ const pageVal = toNumber(page);
974
+ if (!isInteger(pageVal) || pageVal <= 0) {
975
+ throw new PaginationError(
976
+ `Invalid 'page' parameter. Expected an integer > 0, received: ${page}`
977
+ );
978
+ }
979
+ return pageVal;
980
+ };
981
+ const convertPageSizeQueryParams = (pageSize, page) => {
982
+ const pageSizeVal = toNumber(pageSize);
983
+ if (!isInteger(pageSizeVal) || pageSizeVal <= 0) {
984
+ throw new PaginationError(
985
+ `Invalid 'pageSize' parameter. Expected an integer > 0, received: ${page}`
986
+ );
987
+ }
988
+ return pageSizeVal;
989
+ };
990
+ const validatePaginationParams = (page, pageSize, start, limit) => {
991
+ const isPagePagination = !isNil(page) || !isNil(pageSize);
992
+ const isOffsetPagination = !isNil(start) || !isNil(limit);
993
+ if (isPagePagination && isOffsetPagination) {
994
+ throw new PaginationError(
995
+ "Invalid pagination attributes. You cannot use page and offset pagination in the same query"
996
+ );
997
+ }
998
+ };
999
+ class InvalidPopulateError extends Error {
1000
+ constructor() {
1001
+ super();
1002
+ this.message = "Invalid populate parameter. Expected a string, an array of strings, a populate object";
1003
+ }
1004
+ }
1005
+ const convertPopulateQueryParams = (populate2, schema, depth = 0) => {
1006
+ if (depth === 0 && populate2 === "*") {
1007
+ return true;
1008
+ }
1009
+ if (typeof populate2 === "string") {
1010
+ return populate2.split(",").map((value) => ___default.trim(value));
1011
+ }
1012
+ if (Array.isArray(populate2)) {
1013
+ return ___default.uniq(
1014
+ populate2.flatMap((value) => {
1015
+ if (typeof value !== "string") {
1016
+ throw new InvalidPopulateError();
1017
+ }
1018
+ return value.split(",").map((value2) => ___default.trim(value2));
1019
+ })
1020
+ );
1021
+ }
1022
+ if (___default.isPlainObject(populate2)) {
1023
+ return convertPopulateObject(populate2, schema);
1024
+ }
1025
+ throw new InvalidPopulateError();
1026
+ };
1027
+ const hasPopulateFragmentDefined = (populate2) => {
1028
+ return typeof populate2 === "object" && "on" in populate2 && !isNil(populate2.on);
1029
+ };
1030
+ const hasCountDefined = (populate2) => {
1031
+ return typeof populate2 === "object" && "count" in populate2 && typeof populate2.count === "boolean";
1032
+ };
1033
+ const convertPopulateObject = (populate2, schema) => {
1034
+ if (!schema) {
1035
+ return {};
1036
+ }
1037
+ const { attributes } = schema;
1038
+ return Object.entries(populate2).reduce((acc, [key, subPopulate]) => {
1039
+ if (___default.isString(subPopulate)) {
1040
+ try {
1041
+ const subPopulateAsBoolean = parseType({ type: "boolean", value: subPopulate });
1042
+ return subPopulateAsBoolean === true ? { ...acc, [key]: true } : acc;
1043
+ } catch {
1044
+ }
1045
+ }
1046
+ if (___default.isBoolean(subPopulate)) {
1047
+ return subPopulate === true ? { ...acc, [key]: true } : acc;
1048
+ }
1049
+ const attribute = attributes[key];
1050
+ if (!attribute) {
1051
+ return acc;
1052
+ }
1053
+ const isMorphLikeRelationalAttribute = isDynamicZoneAttribute(attribute) || isMorphToRelationalAttribute(attribute);
1054
+ if (isMorphLikeRelationalAttribute) {
1055
+ const hasInvalidProperties = Object.keys(subPopulate).some(
1056
+ (key2) => !["on", "count"].includes(key2)
1057
+ );
1058
+ if (hasInvalidProperties) {
1059
+ throw new Error(
1060
+ `Invalid nested populate for ${schema.info?.singularName}.${key} (${schema.uid}). Expected a fragment ("on") or "count" but found ${JSON.stringify(subPopulate)}`
1061
+ );
1062
+ }
1063
+ const newSubPopulate = {};
1064
+ if (hasPopulateFragmentDefined(subPopulate)) {
1065
+ Object.assign(newSubPopulate, {
1066
+ on: Object.entries(subPopulate.on).reduce(
1067
+ (acc2, [type, typeSubPopulate]) => ({
1068
+ ...acc2,
1069
+ [type]: convertNestedPopulate(typeSubPopulate, getModel(type))
1070
+ }),
1071
+ {}
1072
+ )
1073
+ });
1074
+ }
1075
+ if (hasCountDefined(subPopulate)) {
1076
+ Object.assign(newSubPopulate, { count: subPopulate.count });
1077
+ }
1078
+ return { ...acc, [key]: newSubPopulate };
1079
+ }
1080
+ if (!isMorphLikeRelationalAttribute && hasPopulateFragmentDefined(subPopulate)) {
1081
+ throw new Error(`Using fragments is not permitted to populate "${key}" in "${schema.uid}"`);
1082
+ }
1083
+ let targetSchemaUID;
1084
+ if (attribute.type === "relation") {
1085
+ targetSchemaUID = attribute.target;
1086
+ } else if (attribute.type === "component") {
1087
+ targetSchemaUID = attribute.component;
1088
+ } else if (attribute.type === "media") {
1089
+ targetSchemaUID = "plugin::upload.file";
1090
+ } else {
1091
+ return acc;
1092
+ }
1093
+ const targetSchema = getModel(targetSchemaUID);
1094
+ if (!targetSchema) {
1095
+ return acc;
1096
+ }
1097
+ const populateObject = convertNestedPopulate(subPopulate, targetSchema);
1098
+ if (!populateObject) {
1099
+ return acc;
1100
+ }
1101
+ return {
1102
+ ...acc,
1103
+ [key]: populateObject
1104
+ };
1105
+ }, {});
1106
+ };
1107
+ const convertNestedPopulate = (subPopulate, schema) => {
1108
+ if (___default.isString(subPopulate)) {
1109
+ return parseType({ type: "boolean", value: subPopulate, forceCast: true });
1110
+ }
1111
+ if (___default.isBoolean(subPopulate)) {
1112
+ return subPopulate;
1113
+ }
1114
+ if (!isPlainObject(subPopulate)) {
1115
+ throw new Error(`Invalid nested populate. Expected '*' or an object`);
1116
+ }
1117
+ const { sort: sort2, filters: filters2, fields: fields2, populate: populate2, count, ordering, page, pageSize, start, limit } = subPopulate;
1118
+ const query = {};
1119
+ if (sort2) {
1120
+ query.orderBy = convertSortQueryParams(sort2);
1121
+ }
1122
+ if (filters2) {
1123
+ query.where = convertFiltersQueryParams(filters2, schema);
1124
+ }
1125
+ if (fields2) {
1126
+ query.select = convertFieldsQueryParams(fields2);
1127
+ }
1128
+ if (populate2) {
1129
+ query.populate = convertPopulateQueryParams(populate2, schema);
1130
+ }
1131
+ if (count) {
1132
+ query.count = convertCountQueryParams(count);
1133
+ }
1134
+ if (ordering) {
1135
+ query.ordering = convertOrderingQueryParams(ordering);
1136
+ }
1137
+ validatePaginationParams(page, pageSize, start, limit);
1138
+ if (!isNil(page)) {
1139
+ query.page = convertPageQueryParams(page);
1140
+ }
1141
+ if (!isNil(pageSize)) {
1142
+ query.pageSize = convertPageSizeQueryParams(pageSize, page);
1143
+ }
1144
+ if (!isNil(start)) {
1145
+ query.offset = convertStartQueryParams(start);
1146
+ }
1147
+ if (!isNil(limit)) {
1148
+ query.limit = convertLimitQueryParams(limit);
1149
+ }
1150
+ return query;
1151
+ };
1152
+ const convertFieldsQueryParams = (fields2, depth = 0) => {
1153
+ if (depth === 0 && fields2 === "*") {
1154
+ return void 0;
1155
+ }
1156
+ if (typeof fields2 === "string") {
1157
+ const fieldsValues = fields2.split(",").map((value) => ___default.trim(value));
1158
+ return ___default.uniq([ID_ATTRIBUTE$3, DOC_ID_ATTRIBUTE$3, ...fieldsValues]);
1159
+ }
1160
+ if (isStringArray$3(fields2)) {
1161
+ const fieldsValues = fields2.flatMap((value) => convertFieldsQueryParams(value, depth + 1)).filter((v) => !isNil(v));
1162
+ return ___default.uniq([ID_ATTRIBUTE$3, DOC_ID_ATTRIBUTE$3, ...fieldsValues]);
1163
+ }
1164
+ throw new Error("Invalid fields parameter. Expected a string or an array of strings");
1165
+ };
1166
+ const isValidSchemaAttribute = (key, schema) => {
1167
+ if ([DOC_ID_ATTRIBUTE$3, ID_ATTRIBUTE$3].includes(key)) {
1168
+ return true;
1169
+ }
1170
+ if (!schema) {
1171
+ return false;
1172
+ }
1173
+ return Object.keys(schema.attributes).includes(key);
1174
+ };
1175
+ const convertFiltersQueryParams = (filters2, schema) => {
1176
+ if (!isObject(filters2)) {
1177
+ throw new Error("The filters parameter must be an object or an array");
1178
+ }
1179
+ const filtersCopy = cloneDeep(filters2);
1180
+ return convertAndSanitizeFilters(filtersCopy, schema);
1181
+ };
1182
+ const convertAndSanitizeFilters = (filters2, schema) => {
1183
+ if (Array.isArray(filters2)) {
1184
+ return filters2.map((filter) => convertAndSanitizeFilters(filter, schema)).filter((filter) => !isPlainObject(filter) || !isEmpty(filter));
1185
+ }
1186
+ if (!isPlainObject(filters2)) {
1187
+ return filters2;
1188
+ }
1189
+ const removeOperator = (operator) => delete filters2[operator];
1190
+ for (const [key, value] of Object.entries(filters2)) {
1191
+ const attribute = get(key, schema?.attributes);
1192
+ const validKey = isOperator(key) || isValidSchemaAttribute(key, schema);
1193
+ if (!validKey) {
1194
+ removeOperator(key);
1195
+ } else if (attribute) {
1196
+ if (attribute.type === "relation") {
1197
+ filters2[key] = convertAndSanitizeFilters(value, getModel(attribute.target));
1198
+ } else if (attribute.type === "component") {
1199
+ filters2[key] = convertAndSanitizeFilters(value, getModel(attribute.component));
1200
+ } else if (attribute.type === "media") {
1201
+ filters2[key] = convertAndSanitizeFilters(value, getModel("plugin::upload.file"));
1202
+ } else if (attribute.type === "dynamiczone") {
1203
+ removeOperator(key);
1204
+ } else if (attribute.type === "password") {
1205
+ removeOperator(key);
1206
+ } else {
1207
+ filters2[key] = convertAndSanitizeFilters(value, schema);
1208
+ }
1209
+ } else if (["$null", "$notNull"].includes(key)) {
1210
+ filters2[key] = parseType({ type: "boolean", value: filters2[key], forceCast: true });
1211
+ } else if (isObject(value)) {
1212
+ filters2[key] = convertAndSanitizeFilters(value, schema);
1213
+ }
1214
+ if (isPlainObject(filters2[key]) && isEmpty(filters2[key])) {
1215
+ removeOperator(key);
1216
+ }
1217
+ }
1218
+ return filters2;
1219
+ };
1220
+ const convertStatusParams = (status, query = {}) => {
1221
+ query.filters = ({ meta }) => {
1222
+ const contentType = getModel(meta.uid);
1223
+ if (!contentType || !hasDraftAndPublish(contentType)) {
1224
+ return {};
1225
+ }
1226
+ return { [PUBLISHED_AT_ATTRIBUTE]: { $null: status === "draft" } };
1227
+ };
1228
+ };
1229
+ const transformQueryParams = (uid, params) => {
1230
+ const schema = getModel(uid);
1231
+ const query = {};
1232
+ const { _q, sort: sort2, filters: filters2, fields: fields2, populate: populate2, page, pageSize, start, limit, status, ...rest } = params;
1233
+ if (!isNil(status)) {
1234
+ convertStatusParams(status, query);
1235
+ }
1236
+ if (!isNil(_q)) {
1237
+ query._q = _q;
1238
+ }
1239
+ if (!isNil(sort2)) {
1240
+ query.orderBy = convertSortQueryParams(sort2);
1241
+ }
1242
+ if (!isNil(filters2)) {
1243
+ query.where = convertFiltersQueryParams(filters2, schema);
1244
+ }
1245
+ if (!isNil(fields2)) {
1246
+ query.select = convertFieldsQueryParams(fields2);
1247
+ }
1248
+ if (!isNil(populate2)) {
1249
+ query.populate = convertPopulateQueryParams(populate2, schema);
1250
+ }
1251
+ validatePaginationParams(page, pageSize, start, limit);
1252
+ if (!isNil(page)) {
1253
+ query.page = convertPageQueryParams(page);
1254
+ }
1255
+ if (!isNil(pageSize)) {
1256
+ query.pageSize = convertPageSizeQueryParams(pageSize, page);
1257
+ }
1258
+ if (!isNil(start)) {
1259
+ query.offset = convertStartQueryParams(start);
1260
+ }
1261
+ if (!isNil(limit)) {
1262
+ query.limit = convertLimitQueryParams(limit);
1263
+ }
1264
+ return {
1265
+ ...rest,
1266
+ ...query
1267
+ };
1268
+ };
1269
+ return {
1270
+ private_convertSortQueryParams: convertSortQueryParams,
1271
+ private_convertStartQueryParams: convertStartQueryParams,
1272
+ private_convertLimitQueryParams: convertLimitQueryParams,
1273
+ private_convertPopulateQueryParams: convertPopulateQueryParams,
1274
+ private_convertFiltersQueryParams: convertFiltersQueryParams,
1275
+ private_convertFieldsQueryParams: convertFieldsQueryParams,
1276
+ transformQueryParams
1277
+ };
1278
+ };
1279
+ const convertQueryParams = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1280
+ __proto__: null,
1281
+ createTransformer
1282
+ }, Symbol.toStringTag, { value: "Module" }));
1283
+ function pipe(...fns) {
1284
+ const [firstFn, ...fnRest] = fns;
1285
+ return async (...args) => {
1286
+ let res = await firstFn.apply(firstFn, args);
1287
+ for (let i = 0; i < fnRest.length; i += 1) {
1288
+ res = await fnRest[i](res);
1289
+ }
1290
+ return res;
1291
+ };
1292
+ }
1293
+ const map = curry(pMap);
1294
+ const reduce = (mixedArray) => async (iteratee, initialValue) => {
1295
+ let acc = initialValue;
1296
+ for (let i = 0; i < mixedArray.length; i += 1) {
1297
+ acc = await iteratee(acc, await mixedArray[i], i);
1298
+ }
1299
+ return acc;
1300
+ };
1301
+ const async = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1302
+ __proto__: null,
1303
+ map,
1304
+ pipe,
1305
+ reduce
1306
+ }, Symbol.toStringTag, { value: "Module" }));
1307
+ const visitor$8 = ({ key, attribute }, { remove: remove2 }) => {
1308
+ if (attribute?.type === "password") {
1309
+ remove2(key);
1310
+ }
1311
+ };
1312
+ const visitor$7 = ({ schema, key, attribute }, { remove: remove2 }) => {
1313
+ if (!attribute) {
1314
+ return;
1315
+ }
1316
+ const isPrivate = attribute.private === true || isPrivateAttribute(schema, key);
1317
+ if (isPrivate) {
1318
+ remove2(key);
1319
+ }
1320
+ };
1321
+ const ACTIONS_TO_VERIFY$1 = ["find"];
1322
+ const { CREATED_BY_ATTRIBUTE: CREATED_BY_ATTRIBUTE$1, UPDATED_BY_ATTRIBUTE: UPDATED_BY_ATTRIBUTE$1 } = constants$1;
1323
+ const removeRestrictedRelations = (auth) => async ({ data, key, attribute, schema }, { remove: remove2, set }) => {
1324
+ if (!attribute) {
1325
+ return;
1326
+ }
1327
+ const isRelation = attribute.type === "relation";
1328
+ if (!isRelation) {
1329
+ return;
1330
+ }
1331
+ const handleMorphRelation = async () => {
1332
+ const newMorphValue = [];
1333
+ for (const element of data[key]) {
1334
+ const scopes = ACTIONS_TO_VERIFY$1.map((action) => `${element.__type}.${action}`);
1335
+ const isAllowed = await hasAccessToSomeScopes$1(scopes, auth);
1336
+ if (isAllowed) {
1337
+ newMorphValue.push(element);
1338
+ }
1339
+ }
1340
+ if (newMorphValue.length === 0) {
1341
+ remove2(key);
1342
+ } else {
1343
+ set(key, newMorphValue);
1344
+ }
1345
+ };
1346
+ const handleRegularRelation = async () => {
1347
+ const scopes = ACTIONS_TO_VERIFY$1.map((action) => `${attribute.target}.${action}`);
1348
+ const isAllowed = await hasAccessToSomeScopes$1(scopes, auth);
1349
+ if (!isAllowed) {
1350
+ remove2(key);
1351
+ }
1352
+ };
1353
+ const isCreatorRelation = [CREATED_BY_ATTRIBUTE$1, UPDATED_BY_ATTRIBUTE$1].includes(key);
1354
+ if (isMorphToRelationalAttribute(attribute)) {
1355
+ await handleMorphRelation();
1356
+ return;
1357
+ }
1358
+ if (isCreatorRelation && schema.options?.populateCreatorFields) {
1359
+ return;
1360
+ }
1361
+ await handleRegularRelation();
1362
+ };
1363
+ const hasAccessToSomeScopes$1 = async (scopes, auth) => {
1364
+ for (const scope of scopes) {
1365
+ try {
1366
+ await strapi.auth.verify(auth, { scope });
1367
+ return true;
1368
+ } catch {
1369
+ continue;
1370
+ }
1371
+ }
1372
+ return false;
1373
+ };
1374
+ const visitor$6 = ({ key, attribute }, { remove: remove2 }) => {
1375
+ if (isMorphToRelationalAttribute(attribute)) {
1376
+ remove2(key);
1377
+ }
1378
+ };
1379
+ const visitor$5 = ({ key, attribute }, { remove: remove2 }) => {
1380
+ if (isDynamicZoneAttribute(attribute)) {
1381
+ remove2(key);
1382
+ }
1383
+ };
1384
+ const removeDisallowedFields = (allowedFields = null) => ({ key, path: { attribute: path } }, { remove: remove2 }) => {
1385
+ if (allowedFields === null) {
1386
+ return;
1387
+ }
1388
+ if (!(isArray(allowedFields) && allowedFields.every(isString))) {
1389
+ throw new TypeError(
1390
+ `Expected array of strings for allowedFields but got "${typeof allowedFields}"`
1391
+ );
1392
+ }
1393
+ if (isNil(path)) {
1394
+ return;
1395
+ }
1396
+ const containedPaths = getContainedPaths$1(path);
1397
+ const isPathAllowed = allowedFields.some(
1398
+ (p) => containedPaths.includes(p) || p.startsWith(`${path}.`)
1399
+ );
1400
+ if (isPathAllowed) {
1401
+ return;
1402
+ }
1403
+ remove2(key);
1404
+ };
1405
+ const getContainedPaths$1 = (path) => {
1406
+ const parts = toPath(path);
1407
+ return parts.reduce((acc, value, index2, list) => {
1408
+ return [...acc, list.slice(0, index2 + 1).join(".")];
1409
+ }, []);
1410
+ };
1411
+ const removeRestrictedFields = (restrictedFields = null) => ({ key, path: { attribute: path } }, { remove: remove2 }) => {
1412
+ if (restrictedFields === null) {
1413
+ remove2(key);
1414
+ return;
1415
+ }
1416
+ if (!(isArray(restrictedFields) && restrictedFields.every(isString))) {
1417
+ throw new TypeError(
1418
+ `Expected array of strings for restrictedFields but got "${typeof restrictedFields}"`
1419
+ );
1420
+ }
1421
+ if (restrictedFields.includes(path)) {
1422
+ remove2(key);
1423
+ return;
1424
+ }
1425
+ const isRestrictedNested = restrictedFields.some(
1426
+ (allowedPath) => path?.toString().startsWith(`${allowedPath}.`)
1427
+ );
1428
+ if (isRestrictedNested) {
1429
+ remove2(key);
1430
+ }
1431
+ };
1432
+ const visitor$4 = ({ schema, key, value }, { set }) => {
1433
+ if (key === "" && value === "*") {
1434
+ const { attributes } = schema;
1435
+ const newPopulateQuery = Object.entries(attributes).filter(
1436
+ ([, attribute]) => ["relation", "component", "media", "dynamiczone"].includes(attribute.type)
1437
+ ).reduce((acc, [key2]) => ({ ...acc, [key2]: true }), {});
1438
+ set("", newPopulateQuery);
1439
+ }
1440
+ };
1441
+ const index$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1442
+ __proto__: null,
1443
+ expandWildcardPopulate: visitor$4,
1444
+ removeDisallowedFields,
1445
+ removeDynamicZones: visitor$5,
1446
+ removeMorphToRelations: visitor$6,
1447
+ removePassword: visitor$8,
1448
+ removePrivate: visitor$7,
1449
+ removeRestrictedFields,
1450
+ removeRestrictedRelations
1451
+ }, Symbol.toStringTag, { value: "Module" }));
1452
+ const DEFAULT_PATH = { raw: null, attribute: null };
1453
+ const traverseFactory = () => {
1454
+ const state = {
1455
+ parsers: [],
1456
+ interceptors: [],
1457
+ ignore: [],
1458
+ handlers: {
1459
+ attributes: [],
1460
+ common: []
1461
+ }
1462
+ };
1463
+ const traverse = async (visitor2, options, data) => {
1464
+ const { path = DEFAULT_PATH, schema, getModel } = options ?? {};
1465
+ for (const { predicate, handler } of state.interceptors) {
1466
+ if (predicate(data)) {
1467
+ return handler(visitor2, options, data, { recurse: traverse });
1468
+ }
1469
+ }
1470
+ const parser = state.parsers.find((parser2) => parser2.predicate(data))?.parser;
1471
+ const utils2 = parser?.(data);
1472
+ if (!utils2) {
1473
+ return data;
1474
+ }
1475
+ let out = utils2.transform(data);
1476
+ const keys = utils2.keys(out);
1477
+ for (const key of keys) {
1478
+ const attribute = schema?.attributes?.[key];
1479
+ const newPath = { ...path };
1480
+ newPath.raw = isNil(path.raw) ? key : `${path.raw}.${key}`;
1481
+ if (!isNil(attribute)) {
1482
+ newPath.attribute = isNil(path.attribute) ? key : `${path.attribute}.${key}`;
1483
+ }
1484
+ const visitorOptions = {
1485
+ key,
1486
+ value: utils2.get(key, out),
1487
+ attribute,
1488
+ schema,
1489
+ path: newPath,
1490
+ data: out,
1491
+ getModel
1492
+ };
1493
+ const transformUtils = {
1494
+ remove(key2) {
1495
+ out = utils2.remove(key2, out);
1496
+ },
1497
+ set(key2, value2) {
1498
+ out = utils2.set(key2, value2, out);
1499
+ },
1500
+ recurse: traverse
1501
+ };
1502
+ await visitor2(visitorOptions, pick(["remove", "set"], transformUtils));
1503
+ const value = utils2.get(key, out);
1504
+ const createContext = () => ({
1505
+ key,
1506
+ value,
1507
+ attribute,
1508
+ schema,
1509
+ path: newPath,
1510
+ data: out,
1511
+ visitor: visitor2,
1512
+ getModel
1513
+ });
1514
+ const ignoreCtx = createContext();
1515
+ const shouldIgnore = state.ignore.some((predicate) => predicate(ignoreCtx));
1516
+ if (shouldIgnore) {
1517
+ continue;
1518
+ }
1519
+ const handlers = [...state.handlers.common, ...state.handlers.attributes];
871
1520
  for await (const handler of handlers) {
872
1521
  const ctx = createContext();
873
1522
  const pass = await handler.predicate(ctx);
@@ -949,33 +1598,33 @@ const filters = traverseFactory().intercept(
949
1598
  }
950
1599
  })).ignore(({ value }) => isNil(value)).on(
951
1600
  ({ attribute }) => isNil(attribute),
952
- async ({ key, visitor: visitor2, path, value, schema }, { set, recurse }) => {
953
- set(key, await recurse(visitor2, { schema, path }, value));
1601
+ async ({ key, visitor: visitor2, path, value, schema, getModel }, { set, recurse }) => {
1602
+ set(key, await recurse(visitor2, { schema, path, getModel }, value));
954
1603
  }
955
- ).onRelation(async ({ key, attribute, visitor: visitor2, path, value }, { set, recurse }) => {
1604
+ ).onRelation(async ({ key, attribute, visitor: visitor2, path, value, getModel }, { set, recurse }) => {
956
1605
  const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
957
1606
  if (isMorphRelation) {
958
1607
  return;
959
1608
  }
960
1609
  const targetSchemaUID = attribute.target;
961
- const targetSchema = strapi.getModel(targetSchemaUID);
962
- const newValue = await recurse(visitor2, { schema: targetSchema, path }, value);
1610
+ const targetSchema = getModel(targetSchemaUID);
1611
+ const newValue = await recurse(visitor2, { schema: targetSchema, path, getModel }, value);
963
1612
  set(key, newValue);
964
- }).onComponent(async ({ key, attribute, visitor: visitor2, path, value }, { set, recurse }) => {
965
- const targetSchema = strapi.getModel(attribute.component);
966
- const newValue = await recurse(visitor2, { schema: targetSchema, path }, value);
1613
+ }).onComponent(async ({ key, attribute, visitor: visitor2, path, value, getModel }, { set, recurse }) => {
1614
+ const targetSchema = getModel(attribute.component);
1615
+ const newValue = await recurse(visitor2, { schema: targetSchema, path, getModel }, value);
967
1616
  set(key, newValue);
968
- }).onMedia(async ({ key, visitor: visitor2, path, value }, { set, recurse }) => {
1617
+ }).onMedia(async ({ key, visitor: visitor2, path, value, getModel }, { set, recurse }) => {
969
1618
  const targetSchemaUID = "plugin::upload.file";
970
- const targetSchema = strapi.getModel(targetSchemaUID);
971
- const newValue = await recurse(visitor2, { schema: targetSchema, path }, value);
1619
+ const targetSchema = getModel(targetSchemaUID);
1620
+ const newValue = await recurse(visitor2, { schema: targetSchema, path, getModel }, value);
972
1621
  set(key, newValue);
973
1622
  });
974
1623
  const traverseQueryFilters = curry(filters.traverse);
975
1624
  const ORDERS = { asc: "asc", desc: "desc" };
976
1625
  const ORDER_VALUES = Object.values(ORDERS);
977
1626
  const isSortOrder = (value) => ORDER_VALUES.includes(value.toLowerCase());
978
- const isStringArray$3 = (value) => Array.isArray(value) && value.every(isString);
1627
+ const isStringArray$2 = (value) => Array.isArray(value) && value.every(isString);
979
1628
  const isObjectArray = (value) => Array.isArray(value) && value.every(isObject);
980
1629
  const isNestedSorts = (value) => isString(value) && value.split(",").length > 1;
981
1630
  const isObj$1 = (value) => isObject(value);
@@ -989,7 +1638,7 @@ const sort = traverseFactory().intercept(
989
1638
  }
990
1639
  ).intercept(
991
1640
  // Array of strings ['foo', 'foo,bar'] => map(recurse), then filter out empty items
992
- isStringArray$3,
1641
+ isStringArray$2,
993
1642
  async (visitor2, options, sort2, { recurse }) => {
994
1643
  return Promise.all(sort2.map((nestedSort) => recurse(visitor2, options, nestedSort))).then(
995
1644
  (res) => res.filter((nestedSort) => !isEmpty(nestedSort))
@@ -1056,23 +1705,23 @@ const sort = traverseFactory().intercept(
1056
1705
  get(key, data) {
1057
1706
  return data[key];
1058
1707
  }
1059
- })).onRelation(async ({ key, value, attribute, visitor: visitor2, path }, { set, recurse }) => {
1708
+ })).onRelation(async ({ key, value, attribute, visitor: visitor2, path, getModel }, { set, recurse }) => {
1060
1709
  const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
1061
1710
  if (isMorphRelation) {
1062
1711
  return;
1063
1712
  }
1064
1713
  const targetSchemaUID = attribute.target;
1065
- const targetSchema = strapi.getModel(targetSchemaUID);
1066
- const newValue = await recurse(visitor2, { schema: targetSchema, path }, value);
1714
+ const targetSchema = getModel(targetSchemaUID);
1715
+ const newValue = await recurse(visitor2, { schema: targetSchema, path, getModel }, value);
1067
1716
  set(key, newValue);
1068
- }).onMedia(async ({ key, path, visitor: visitor2, value }, { recurse, set }) => {
1717
+ }).onMedia(async ({ key, path, visitor: visitor2, value, getModel }, { recurse, set }) => {
1069
1718
  const targetSchemaUID = "plugin::upload.file";
1070
- const targetSchema = strapi.getModel(targetSchemaUID);
1071
- const newValue = await recurse(visitor2, { schema: targetSchema, path }, value);
1719
+ const targetSchema = getModel(targetSchemaUID);
1720
+ const newValue = await recurse(visitor2, { schema: targetSchema, path, getModel }, value);
1072
1721
  set(key, newValue);
1073
- }).onComponent(async ({ key, value, visitor: visitor2, path, attribute }, { recurse, set }) => {
1074
- const targetSchema = strapi.getModel(attribute.component);
1075
- const newValue = await recurse(visitor2, { schema: targetSchema, path }, value);
1722
+ }).onComponent(async ({ key, value, visitor: visitor2, path, attribute, getModel }, { recurse, set }) => {
1723
+ const targetSchema = getModel(attribute.component);
1724
+ const newValue = await recurse(visitor2, { schema: targetSchema, path, getModel }, value);
1076
1725
  set(key, newValue);
1077
1726
  });
1078
1727
  const traverseQuerySort = curry(sort.traverse);
@@ -1081,9 +1730,9 @@ const isKeyword = (keyword) => {
1081
1730
  return !attribute && keyword === key;
1082
1731
  };
1083
1732
  };
1084
- const isStringArray$2 = (value) => isArray(value) && value.every(isString);
1733
+ const isStringArray$1 = (value) => isArray(value) && value.every(isString);
1085
1734
  const isObj = (value) => isObject(value);
1086
- const populate = traverseFactory().intercept(isStringArray$2, async (visitor2, options, populate2, { recurse }) => {
1735
+ const populate = traverseFactory().intercept(isStringArray$1, async (visitor2, options, populate2, { recurse }) => {
1087
1736
  const visitedPopulate = await Promise.all(
1088
1737
  populate2.map((nestedPopulate) => recurse(visitor2, options, nestedPopulate))
1089
1738
  );
@@ -1159,180 +1808,100 @@ const populate = traverseFactory().intercept(isStringArray$2, async (visitor2, o
1159
1808
  return ["sort", "filters", "fields"].includes(key) && !attribute;
1160
1809
  }).on(
1161
1810
  // Handle recursion on populate."populate"
1162
- isKeyword("populate"),
1163
- async ({ key, visitor: visitor2, path, value, schema }, { set, recurse }) => {
1164
- const newValue = await recurse(visitor2, { schema, path }, value);
1165
- set(key, newValue);
1166
- }
1167
- ).on(isKeyword("on"), async ({ key, visitor: visitor2, path, value }, { set, recurse }) => {
1168
- const newOn = {};
1169
- if (!isObj(value)) {
1170
- return;
1171
- }
1172
- for (const [uid, subPopulate] of Object.entries(value)) {
1173
- const model = strapi.getModel(uid);
1174
- const newPath = { ...path, raw: `${path.raw}[${uid}]` };
1175
- newOn[uid] = await recurse(visitor2, { schema: model, path: newPath }, subPopulate);
1176
- }
1177
- set(key, newOn);
1178
- }).onRelation(async ({ key, value, attribute, visitor: visitor2, path, schema }, { set, recurse }) => {
1179
- if (isNil(value)) {
1180
- return;
1181
- }
1182
- if (isMorphToRelationalAttribute(attribute)) {
1183
- if (!isObject(value) || !("on" in value && isObject(value?.on))) {
1184
- return;
1185
- }
1186
- const newValue2 = await recurse(visitor2, { schema, path }, { on: value?.on });
1187
- set(key, { on: newValue2 });
1188
- }
1189
- const targetSchemaUID = attribute.target;
1190
- const targetSchema = strapi.getModel(targetSchemaUID);
1191
- const newValue = await recurse(visitor2, { schema: targetSchema, path }, value);
1192
- set(key, newValue);
1193
- }).onMedia(async ({ key, path, visitor: visitor2, value }, { recurse, set }) => {
1194
- if (isNil(value)) {
1195
- return;
1196
- }
1197
- const targetSchemaUID = "plugin::upload.file";
1198
- const targetSchema = strapi.getModel(targetSchemaUID);
1199
- const newValue = await recurse(visitor2, { schema: targetSchema, path }, value);
1200
- set(key, newValue);
1201
- }).onComponent(async ({ key, value, visitor: visitor2, path, attribute }, { recurse, set }) => {
1202
- if (isNil(value)) {
1203
- return;
1204
- }
1205
- const targetSchema = strapi.getModel(attribute.component);
1206
- const newValue = await recurse(visitor2, { schema: targetSchema, path }, value);
1207
- set(key, newValue);
1208
- }).onDynamicZone(async ({ key, value, attribute, schema, visitor: visitor2, path }, { set, recurse }) => {
1209
- if (isNil(value)) {
1210
- return;
1211
- }
1212
- if (isObject(value)) {
1213
- const { components } = attribute;
1214
- const newValue = {};
1215
- let newProperties = omit("on", value);
1216
- for (const componentUID of components) {
1217
- const componentSchema = strapi.getModel(componentUID);
1218
- const properties = await recurse(visitor2, { schema: componentSchema, path }, value);
1219
- newProperties = merge(newProperties, properties);
1220
- }
1221
- Object.assign(newValue, newProperties);
1222
- if ("on" in value && value.on) {
1223
- const newOn = await recurse(visitor2, { schema, path }, { on: value.on });
1224
- Object.assign(newValue, newOn);
1225
- }
1226
- set(key, newValue);
1227
- } else {
1228
- const newValue = await recurse(visitor2, { schema, path }, value);
1229
- set(key, newValue);
1230
- }
1231
- });
1232
- const traverseQueryPopulate = curry(populate.traverse);
1233
- const isStringArray$1 = (value) => isArray(value) && value.every(isString);
1234
- const fields = traverseFactory().intercept(isStringArray$1, async (visitor2, options, fields2, { recurse }) => {
1235
- return Promise.all(fields2.map((field) => recurse(visitor2, options, field)));
1236
- }).intercept((value) => eq("*", value), constant("*")).parse(isString, () => ({
1237
- transform: trim,
1238
- remove(key, data) {
1239
- return data === key ? void 0 : data;
1240
- },
1241
- set(_key, _value, data) {
1242
- return data;
1243
- },
1244
- keys(data) {
1245
- return [data];
1246
- },
1247
- get(key, data) {
1248
- return key === data ? data : void 0;
1249
- }
1250
- }));
1251
- const traverseQueryFields = curry(fields.traverse);
1252
- const index$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1253
- __proto__: null,
1254
- factory: traverseFactory,
1255
- traverseQueryFields,
1256
- traverseQueryFilters,
1257
- traverseQueryPopulate,
1258
- traverseQuerySort
1259
- }, Symbol.toStringTag, { value: "Module" }));
1260
- const GROUP_OPERATORS = ["$and", "$or"];
1261
- const WHERE_OPERATORS = [
1262
- "$not",
1263
- "$in",
1264
- "$notIn",
1265
- "$eq",
1266
- "$eqi",
1267
- "$ne",
1268
- "$nei",
1269
- "$gt",
1270
- "$gte",
1271
- "$lt",
1272
- "$lte",
1273
- "$null",
1274
- "$notNull",
1275
- "$between",
1276
- "$startsWith",
1277
- "$endsWith",
1278
- "$startsWithi",
1279
- "$endsWithi",
1280
- "$contains",
1281
- "$notContains",
1282
- "$containsi",
1283
- "$notContainsi",
1284
- // Experimental, only for internal use
1285
- "$jsonSupersetOf"
1286
- ];
1287
- const CAST_OPERATORS = [
1288
- "$not",
1289
- "$in",
1290
- "$notIn",
1291
- "$eq",
1292
- "$ne",
1293
- "$gt",
1294
- "$gte",
1295
- "$lt",
1296
- "$lte",
1297
- "$between"
1298
- ];
1299
- const ARRAY_OPERATORS = ["$in", "$notIn", "$between"];
1300
- const OPERATORS = {
1301
- where: WHERE_OPERATORS,
1302
- cast: CAST_OPERATORS,
1303
- group: GROUP_OPERATORS,
1304
- array: ARRAY_OPERATORS
1305
- };
1306
- const OPERATORS_LOWERCASE = Object.fromEntries(
1307
- Object.entries(OPERATORS).map(([key, values]) => [
1308
- key,
1309
- values.map((value) => value.toLowerCase())
1310
- ])
1311
- );
1312
- const isObjKey = (key, obj) => {
1313
- return key in obj;
1314
- };
1315
- const isOperatorOfType = (type, key, ignoreCase = false) => {
1316
- if (ignoreCase) {
1317
- return OPERATORS_LOWERCASE[type]?.includes(key.toLowerCase()) ?? false;
1811
+ isKeyword("populate"),
1812
+ async ({ key, visitor: visitor2, path, value, schema, getModel }, { set, recurse }) => {
1813
+ const newValue = await recurse(visitor2, { schema, path, getModel }, value);
1814
+ set(key, newValue);
1318
1815
  }
1319
- if (isObjKey(type, OPERATORS)) {
1320
- return OPERATORS[type]?.includes(key) ?? false;
1816
+ ).on(isKeyword("on"), async ({ key, visitor: visitor2, path, value, getModel }, { set, recurse }) => {
1817
+ const newOn = {};
1818
+ if (!isObj(value)) {
1819
+ return;
1321
1820
  }
1322
- return false;
1323
- };
1324
- const isOperator = (key, ignoreCase = false) => {
1325
- return Object.keys(OPERATORS).some((type) => isOperatorOfType(type, key, ignoreCase));
1326
- };
1327
- const { ID_ATTRIBUTE: ID_ATTRIBUTE$3, DOC_ID_ATTRIBUTE: DOC_ID_ATTRIBUTE$3 } = constants$1;
1328
- const sanitizePasswords = (schema) => async (entity) => {
1329
- if (!schema) {
1821
+ for (const [uid, subPopulate] of Object.entries(value)) {
1822
+ const model = getModel(uid);
1823
+ const newPath = { ...path, raw: `${path.raw}[${uid}]` };
1824
+ newOn[uid] = await recurse(visitor2, { schema: model, path: newPath, getModel }, subPopulate);
1825
+ }
1826
+ set(key, newOn);
1827
+ }).onRelation(
1828
+ async ({ key, value, attribute, visitor: visitor2, path, schema, getModel }, { set, recurse }) => {
1829
+ if (isNil(value)) {
1830
+ return;
1831
+ }
1832
+ if (isMorphToRelationalAttribute(attribute)) {
1833
+ if (!isObject(value) || !("on" in value && isObject(value?.on))) {
1834
+ return;
1835
+ }
1836
+ const newValue2 = await recurse(visitor2, { schema, path, getModel }, { on: value?.on });
1837
+ set(key, newValue2);
1838
+ return;
1839
+ }
1840
+ const targetSchemaUID = attribute.target;
1841
+ const targetSchema = getModel(targetSchemaUID);
1842
+ const newValue = await recurse(visitor2, { schema: targetSchema, path, getModel }, value);
1843
+ set(key, newValue);
1844
+ }
1845
+ ).onMedia(async ({ key, path, visitor: visitor2, value, getModel }, { recurse, set }) => {
1846
+ if (isNil(value)) {
1847
+ return;
1848
+ }
1849
+ const targetSchemaUID = "plugin::upload.file";
1850
+ const targetSchema = getModel(targetSchemaUID);
1851
+ const newValue = await recurse(visitor2, { schema: targetSchema, path, getModel }, value);
1852
+ set(key, newValue);
1853
+ }).onComponent(async ({ key, value, visitor: visitor2, path, attribute, getModel }, { recurse, set }) => {
1854
+ if (isNil(value)) {
1855
+ return;
1856
+ }
1857
+ const targetSchema = getModel(attribute.component);
1858
+ const newValue = await recurse(visitor2, { schema: targetSchema, path, getModel }, value);
1859
+ set(key, newValue);
1860
+ }).onDynamicZone(async ({ key, value, schema, visitor: visitor2, path, getModel }, { set, recurse }) => {
1861
+ if (isNil(value) || !isObject(value)) {
1862
+ return;
1863
+ }
1864
+ if ("on" in value && value.on) {
1865
+ const newOn = await recurse(visitor2, { schema, path, getModel }, { on: value.on });
1866
+ set(key, newOn);
1867
+ }
1868
+ });
1869
+ const traverseQueryPopulate = curry(populate.traverse);
1870
+ const isStringArray = (value) => isArray(value) && value.every(isString);
1871
+ const fields = traverseFactory().intercept(isStringArray, async (visitor2, options, fields2, { recurse }) => {
1872
+ return Promise.all(fields2.map((field) => recurse(visitor2, options, field)));
1873
+ }).intercept((value) => eq("*", value), constant("*")).parse(isString, () => ({
1874
+ transform: trim,
1875
+ remove(key, data) {
1876
+ return data === key ? void 0 : data;
1877
+ },
1878
+ set(_key, _value, data) {
1879
+ return data;
1880
+ },
1881
+ keys(data) {
1882
+ return [data];
1883
+ },
1884
+ get(key, data) {
1885
+ return key === data ? data : void 0;
1886
+ }
1887
+ }));
1888
+ const traverseQueryFields = curry(fields.traverse);
1889
+ const index$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1890
+ __proto__: null,
1891
+ traverseQueryFields,
1892
+ traverseQueryFilters,
1893
+ traverseQueryPopulate,
1894
+ traverseQuerySort
1895
+ }, Symbol.toStringTag, { value: "Module" }));
1896
+ const { ID_ATTRIBUTE: ID_ATTRIBUTE$2, DOC_ID_ATTRIBUTE: DOC_ID_ATTRIBUTE$2 } = constants$1;
1897
+ const sanitizePasswords = (ctx) => async (entity) => {
1898
+ if (!ctx.schema) {
1330
1899
  throw new Error("Missing schema in sanitizePasswords");
1331
1900
  }
1332
- return traverseEntity$1(visitor$8, { schema }, entity);
1901
+ return traverseEntity$1(visitor$8, ctx, entity);
1333
1902
  };
1334
- const defaultSanitizeOutput = async (schema, entity) => {
1335
- if (!schema) {
1903
+ const defaultSanitizeOutput = async (ctx, entity) => {
1904
+ if (!ctx.schema) {
1336
1905
  throw new Error("Missing schema in defaultSanitizeOutput");
1337
1906
  }
1338
1907
  return traverseEntity$1(
@@ -1340,139 +1909,121 @@ const defaultSanitizeOutput = async (schema, entity) => {
1340
1909
  visitor$8(...args);
1341
1910
  visitor$7(...args);
1342
1911
  },
1343
- { schema },
1912
+ ctx,
1344
1913
  entity
1345
1914
  );
1346
1915
  };
1347
- const defaultSanitizeFilters = curry((schema, filters2) => {
1348
- if (!schema) {
1916
+ const defaultSanitizeFilters = curry((ctx, filters2) => {
1917
+ if (!ctx.schema) {
1349
1918
  throw new Error("Missing schema in defaultSanitizeFilters");
1350
1919
  }
1351
1920
  return pipe(
1352
1921
  // Remove keys that are not attributes or valid operators
1353
- traverseQueryFilters(
1354
- ({ key, attribute }, { remove: remove2 }) => {
1355
- const isAttribute = !!attribute;
1356
- if ([ID_ATTRIBUTE$3, DOC_ID_ATTRIBUTE$3].includes(key)) {
1357
- return;
1358
- }
1359
- if (!isAttribute && !isOperator(key)) {
1360
- remove2(key);
1361
- }
1362
- },
1363
- { schema }
1364
- ),
1922
+ traverseQueryFilters(({ key, attribute }, { remove: remove2 }) => {
1923
+ const isAttribute = !!attribute;
1924
+ if ([ID_ATTRIBUTE$2, DOC_ID_ATTRIBUTE$2].includes(key)) {
1925
+ return;
1926
+ }
1927
+ if (!isAttribute && !isOperator(key)) {
1928
+ remove2(key);
1929
+ }
1930
+ }, ctx),
1365
1931
  // Remove dynamic zones from filters
1366
- traverseQueryFilters(visitor$5, { schema }),
1932
+ traverseQueryFilters(visitor$5, ctx),
1367
1933
  // Remove morpTo relations from filters
1368
- traverseQueryFilters(visitor$6, { schema }),
1934
+ traverseQueryFilters(visitor$6, ctx),
1369
1935
  // Remove passwords from filters
1370
- traverseQueryFilters(visitor$8, { schema }),
1936
+ traverseQueryFilters(visitor$8, ctx),
1371
1937
  // Remove private from filters
1372
- traverseQueryFilters(visitor$7, { schema }),
1938
+ traverseQueryFilters(visitor$7, ctx),
1373
1939
  // Remove empty objects
1374
- traverseQueryFilters(
1375
- ({ key, value }, { remove: remove2 }) => {
1376
- if (isObject(value) && isEmpty(value)) {
1377
- remove2(key);
1378
- }
1379
- },
1380
- { schema }
1381
- )
1940
+ traverseQueryFilters(({ key, value }, { remove: remove2 }) => {
1941
+ if (isObject(value) && isEmpty(value)) {
1942
+ remove2(key);
1943
+ }
1944
+ }, ctx)
1382
1945
  )(filters2);
1383
1946
  });
1384
- const defaultSanitizeSort = curry((schema, sort2) => {
1385
- if (!schema) {
1947
+ const defaultSanitizeSort = curry((ctx, sort2) => {
1948
+ if (!ctx.schema) {
1386
1949
  throw new Error("Missing schema in defaultSanitizeSort");
1387
1950
  }
1388
1951
  return pipe(
1389
1952
  // Remove non attribute keys
1390
- traverseQuerySort(
1391
- ({ key, attribute }, { remove: remove2 }) => {
1392
- if ([ID_ATTRIBUTE$3, DOC_ID_ATTRIBUTE$3].includes(key)) {
1393
- return;
1394
- }
1395
- if (!attribute) {
1396
- remove2(key);
1397
- }
1398
- },
1399
- { schema }
1400
- ),
1953
+ traverseQuerySort(({ key, attribute }, { remove: remove2 }) => {
1954
+ if ([ID_ATTRIBUTE$2, DOC_ID_ATTRIBUTE$2].includes(key)) {
1955
+ return;
1956
+ }
1957
+ if (!attribute) {
1958
+ remove2(key);
1959
+ }
1960
+ }, ctx),
1401
1961
  // Remove dynamic zones from sort
1402
- traverseQuerySort(visitor$5, { schema }),
1962
+ traverseQuerySort(visitor$5, ctx),
1403
1963
  // Remove morpTo relations from sort
1404
- traverseQuerySort(visitor$6, { schema }),
1964
+ traverseQuerySort(visitor$6, ctx),
1405
1965
  // Remove private from sort
1406
- traverseQuerySort(visitor$7, { schema }),
1966
+ traverseQuerySort(visitor$7, ctx),
1407
1967
  // Remove passwords from filters
1408
- traverseQuerySort(visitor$8, { schema }),
1968
+ traverseQuerySort(visitor$8, ctx),
1409
1969
  // Remove keys for empty non-scalar values
1410
- traverseQuerySort(
1411
- ({ key, attribute, value }, { remove: remove2 }) => {
1412
- if ([ID_ATTRIBUTE$3, DOC_ID_ATTRIBUTE$3].includes(key)) {
1413
- return;
1414
- }
1415
- if (!isScalarAttribute(attribute) && isEmpty(value)) {
1416
- remove2(key);
1417
- }
1418
- },
1419
- { schema }
1420
- )
1970
+ traverseQuerySort(({ key, attribute, value }, { remove: remove2 }) => {
1971
+ if ([ID_ATTRIBUTE$2, DOC_ID_ATTRIBUTE$2].includes(key)) {
1972
+ return;
1973
+ }
1974
+ if (!isScalarAttribute(attribute) && isEmpty(value)) {
1975
+ remove2(key);
1976
+ }
1977
+ }, ctx)
1421
1978
  )(sort2);
1422
1979
  });
1423
- const defaultSanitizeFields = curry((schema, fields2) => {
1424
- if (!schema) {
1980
+ const defaultSanitizeFields = curry((ctx, fields2) => {
1981
+ if (!ctx.schema) {
1425
1982
  throw new Error("Missing schema in defaultSanitizeFields");
1426
1983
  }
1427
1984
  return pipe(
1428
1985
  // Only keep scalar attributes
1429
- traverseQueryFields(
1430
- ({ key, attribute }, { remove: remove2 }) => {
1431
- if ([ID_ATTRIBUTE$3, DOC_ID_ATTRIBUTE$3].includes(key)) {
1432
- return;
1433
- }
1434
- if (isNil(attribute) || !isScalarAttribute(attribute)) {
1435
- remove2(key);
1436
- }
1437
- },
1438
- { schema }
1439
- ),
1986
+ traverseQueryFields(({ key, attribute }, { remove: remove2 }) => {
1987
+ if ([ID_ATTRIBUTE$2, DOC_ID_ATTRIBUTE$2].includes(key)) {
1988
+ return;
1989
+ }
1990
+ if (isNil(attribute) || !isScalarAttribute(attribute)) {
1991
+ remove2(key);
1992
+ }
1993
+ }, ctx),
1440
1994
  // Remove private fields
1441
- traverseQueryFields(visitor$7, { schema }),
1995
+ traverseQueryFields(visitor$7, ctx),
1442
1996
  // Remove password fields
1443
- traverseQueryFields(visitor$8, { schema }),
1997
+ traverseQueryFields(visitor$8, ctx),
1444
1998
  // Remove nil values from fields array
1445
1999
  (value) => isArray(value) ? value.filter((field) => !isNil(field)) : value
1446
2000
  )(fields2);
1447
2001
  });
1448
- const defaultSanitizePopulate = curry((schema, populate2) => {
1449
- if (!schema) {
2002
+ const defaultSanitizePopulate = curry((ctx, populate2) => {
2003
+ if (!ctx.schema) {
1450
2004
  throw new Error("Missing schema in defaultSanitizePopulate");
1451
2005
  }
1452
2006
  return pipe(
1453
- traverseQueryPopulate(visitor$4, { schema }),
1454
- traverseQueryPopulate(
1455
- async ({ key, value, schema: schema2, attribute }, { set }) => {
1456
- if (attribute) {
1457
- return;
1458
- }
1459
- if (key === "sort") {
1460
- set(key, await defaultSanitizeSort(schema2, value));
1461
- }
1462
- if (key === "filters") {
1463
- set(key, await defaultSanitizeFilters(schema2, value));
1464
- }
1465
- if (key === "fields") {
1466
- set(key, await defaultSanitizeFields(schema2, value));
1467
- }
1468
- if (key === "populate") {
1469
- set(key, await defaultSanitizePopulate(schema2, value));
1470
- }
1471
- },
1472
- { schema }
1473
- ),
2007
+ traverseQueryPopulate(visitor$4, ctx),
2008
+ traverseQueryPopulate(async ({ key, value, schema, attribute, getModel }, { set }) => {
2009
+ if (attribute) {
2010
+ return;
2011
+ }
2012
+ if (key === "sort") {
2013
+ set(key, await defaultSanitizeSort({ schema, getModel }, value));
2014
+ }
2015
+ if (key === "filters") {
2016
+ set(key, await defaultSanitizeFilters({ schema, getModel }, value));
2017
+ }
2018
+ if (key === "fields") {
2019
+ set(key, await defaultSanitizeFields({ schema, getModel }, value));
2020
+ }
2021
+ if (key === "populate") {
2022
+ set(key, await defaultSanitizePopulate({ schema, getModel }, value));
2023
+ }
2024
+ }, ctx),
1474
2025
  // Remove private fields
1475
- traverseQueryPopulate(visitor$7, { schema })
2026
+ traverseQueryPopulate(visitor$7, ctx)
1476
2027
  )(populate2);
1477
2028
  });
1478
2029
  const sanitizers = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
@@ -1484,7 +2035,8 @@ const sanitizers = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePr
1484
2035
  defaultSanitizeSort,
1485
2036
  sanitizePasswords
1486
2037
  }, Symbol.toStringTag, { value: "Module" }));
1487
- const createContentAPISanitizers = () => {
2038
+ const createAPISanitizers = (opts) => {
2039
+ const { getModel } = opts;
1488
2040
  const sanitizeInput = (data, schema, { auth } = {}) => {
1489
2041
  if (!schema) {
1490
2042
  throw new Error("Missing schema in sanitizeInput");
@@ -1498,12 +2050,14 @@ const createContentAPISanitizers = () => {
1498
2050
  omit(constants$1.ID_ATTRIBUTE),
1499
2051
  omit(constants$1.DOC_ID_ATTRIBUTE),
1500
2052
  // Remove non-writable attributes
1501
- traverseEntity$1(removeRestrictedFields(nonWritableAttributes), { schema })
2053
+ traverseEntity$1(removeRestrictedFields(nonWritableAttributes), { schema, getModel })
1502
2054
  ];
1503
2055
  if (auth) {
1504
- transforms.push(traverseEntity$1(removeRestrictedRelations(auth), { schema }));
2056
+ transforms.push(
2057
+ traverseEntity$1(removeRestrictedRelations(auth), { schema, getModel })
2058
+ );
1505
2059
  }
1506
- strapi.sanitizers.get("content-api.input").forEach((sanitizer) => transforms.push(sanitizer(schema)));
2060
+ opts?.sanitizers?.input?.forEach((sanitizer) => transforms.push(sanitizer(schema)));
1507
2061
  return pipe(...transforms)(data);
1508
2062
  };
1509
2063
  const sanitizeOutput = async (data, schema, { auth } = {}) => {
@@ -1517,11 +2071,15 @@ const createContentAPISanitizers = () => {
1517
2071
  }
1518
2072
  return res;
1519
2073
  }
1520
- const transforms = [(data2) => defaultSanitizeOutput(schema, data2)];
2074
+ const transforms = [
2075
+ (data2) => defaultSanitizeOutput({ schema, getModel }, data2)
2076
+ ];
1521
2077
  if (auth) {
1522
- transforms.push(traverseEntity$1(removeRestrictedRelations(auth), { schema }));
2078
+ transforms.push(
2079
+ traverseEntity$1(removeRestrictedRelations(auth), { schema, getModel })
2080
+ );
1523
2081
  }
1524
- strapi.sanitizers.get("content-api.output").forEach((sanitizer) => transforms.push(sanitizer(schema)));
2082
+ opts?.sanitizers?.output?.forEach((sanitizer) => transforms.push(sanitizer(schema)));
1525
2083
  return pipe(...transforms)(data);
1526
2084
  };
1527
2085
  const sanitizeQuery = async (query, schema, { auth } = {}) => {
@@ -1551,9 +2109,11 @@ const createContentAPISanitizers = () => {
1551
2109
  if (isArray(filters2)) {
1552
2110
  return Promise.all(filters2.map((filter) => sanitizeFilters(filter, schema, { auth })));
1553
2111
  }
1554
- const transforms = [defaultSanitizeFilters(schema)];
2112
+ const transforms = [defaultSanitizeFilters({ schema, getModel })];
1555
2113
  if (auth) {
1556
- transforms.push(traverseQueryFilters(removeRestrictedRelations(auth), { schema }));
2114
+ transforms.push(
2115
+ traverseQueryFilters(removeRestrictedRelations(auth), { schema, getModel })
2116
+ );
1557
2117
  }
1558
2118
  return pipe(...transforms)(filters2);
1559
2119
  };
@@ -1561,9 +2121,11 @@ const createContentAPISanitizers = () => {
1561
2121
  if (!schema) {
1562
2122
  throw new Error("Missing schema in sanitizeSort");
1563
2123
  }
1564
- const transforms = [defaultSanitizeSort(schema)];
2124
+ const transforms = [defaultSanitizeSort({ schema, getModel })];
1565
2125
  if (auth) {
1566
- transforms.push(traverseQuerySort(removeRestrictedRelations(auth), { schema }));
2126
+ transforms.push(
2127
+ traverseQuerySort(removeRestrictedRelations(auth), { schema, getModel })
2128
+ );
1567
2129
  }
1568
2130
  return pipe(...transforms)(sort2);
1569
2131
  };
@@ -1571,16 +2133,18 @@ const createContentAPISanitizers = () => {
1571
2133
  if (!schema) {
1572
2134
  throw new Error("Missing schema in sanitizeFields");
1573
2135
  }
1574
- const transforms = [defaultSanitizeFields(schema)];
2136
+ const transforms = [defaultSanitizeFields({ schema, getModel })];
1575
2137
  return pipe(...transforms)(fields2);
1576
2138
  };
1577
2139
  const sanitizePopulate = (populate2, schema, { auth } = {}) => {
1578
2140
  if (!schema) {
1579
2141
  throw new Error("Missing schema in sanitizePopulate");
1580
2142
  }
1581
- const transforms = [defaultSanitizePopulate(schema)];
2143
+ const transforms = [defaultSanitizePopulate({ schema, getModel })];
1582
2144
  if (auth) {
1583
- transforms.push(traverseQueryPopulate(removeRestrictedRelations(auth), { schema }));
2145
+ transforms.push(
2146
+ traverseQueryPopulate(removeRestrictedRelations(auth), { schema, getModel })
2147
+ );
1584
2148
  }
1585
2149
  return pipe(...transforms)(populate2);
1586
2150
  };
@@ -1594,126 +2158,22 @@ const createContentAPISanitizers = () => {
1594
2158
  populate: sanitizePopulate
1595
2159
  };
1596
2160
  };
1597
- const contentAPI$1 = createContentAPISanitizers();
1598
- const index$1 = {
1599
- contentAPI: contentAPI$1,
1600
- sanitizers,
1601
- visitors: visitors$1
1602
- };
1603
- const formatYupInnerError = (yupError) => ({
1604
- path: toPath(yupError.path),
1605
- message: yupError.message,
1606
- name: yupError.name
1607
- });
1608
- const formatYupErrors = (yupError) => ({
1609
- errors: isEmpty(yupError.inner) ? [formatYupInnerError(yupError)] : yupError.inner.map(formatYupInnerError),
1610
- message: yupError.message
1611
- });
1612
- class ApplicationError extends Error {
1613
- name;
1614
- details;
1615
- message;
1616
- constructor(message = "An application error occured", details = {}) {
1617
- super();
1618
- this.name = "ApplicationError";
1619
- this.message = message;
1620
- this.details = details;
1621
- }
1622
- }
1623
- class ValidationError extends ApplicationError {
1624
- constructor(message, details) {
1625
- super(message, details);
1626
- this.name = "ValidationError";
1627
- }
1628
- }
1629
- class YupValidationError extends ValidationError {
1630
- constructor(yupError, message) {
1631
- super("Validation");
1632
- const { errors: errors2, message: yupMessage } = formatYupErrors(yupError);
1633
- this.message = message || yupMessage;
1634
- this.details = { errors: errors2 };
1635
- }
1636
- }
1637
- class PaginationError extends ApplicationError {
1638
- constructor(message = "Invalid pagination", details) {
1639
- super(message, details);
1640
- this.name = "PaginationError";
1641
- this.message = message;
1642
- }
1643
- }
1644
- class NotFoundError extends ApplicationError {
1645
- constructor(message = "Entity not found", details) {
1646
- super(message, details);
1647
- this.name = "NotFoundError";
1648
- this.message = message;
1649
- }
1650
- }
1651
- class ForbiddenError extends ApplicationError {
1652
- constructor(message = "Forbidden access", details) {
1653
- super(message, details);
1654
- this.name = "ForbiddenError";
1655
- this.message = message;
1656
- }
1657
- }
1658
- class UnauthorizedError extends ApplicationError {
1659
- constructor(message = "Unauthorized", details) {
1660
- super(message, details);
1661
- this.name = "UnauthorizedError";
1662
- this.message = message;
1663
- }
1664
- }
1665
- class RateLimitError extends ApplicationError {
1666
- constructor(message = "Too many requests, please try again later.", details) {
1667
- super(message, details);
1668
- this.name = "RateLimitError";
1669
- this.message = message;
1670
- this.details = details || {};
1671
- }
1672
- }
1673
- class PayloadTooLargeError extends ApplicationError {
1674
- constructor(message = "Entity too large", details) {
1675
- super(message, details);
1676
- this.name = "PayloadTooLargeError";
1677
- this.message = message;
1678
- }
1679
- }
1680
- class PolicyError extends ForbiddenError {
1681
- constructor(message = "Policy Failed", details) {
1682
- super(message, details);
1683
- this.name = "PolicyError";
1684
- this.message = message;
1685
- this.details = details || {};
1686
- }
1687
- }
1688
- class NotImplementedError extends ApplicationError {
1689
- constructor(message = "This feature is not implemented yet", details) {
1690
- super(message, details);
1691
- this.name = "NotImplementedError";
1692
- this.message = message;
1693
- }
1694
- }
1695
- const errors = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2161
+ const index$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1696
2162
  __proto__: null,
1697
- ApplicationError,
1698
- ForbiddenError,
1699
- HttpError,
1700
- NotFoundError,
1701
- NotImplementedError,
1702
- PaginationError,
1703
- PayloadTooLargeError,
1704
- PolicyError,
1705
- RateLimitError,
1706
- UnauthorizedError,
1707
- ValidationError,
1708
- YupValidationError
2163
+ createAPISanitizers,
2164
+ sanitizers,
2165
+ visitors: index$4
1709
2166
  }, Symbol.toStringTag, { value: "Module" }));
1710
- const throwInvalidParam = ({ key, path }) => {
1711
- const msg = path && path !== key ? `Invalid parameter ${key} at ${path}` : `Invalid parameter ${key}`;
1712
- throw new ValidationError(msg);
2167
+ const throwInvalidKey = ({ key, path }) => {
2168
+ const msg = path && path !== key ? `Invalid key ${key} at ${path}` : `Invalid key ${key}`;
2169
+ throw new ValidationError(msg, {
2170
+ key,
2171
+ path
2172
+ });
1713
2173
  };
1714
2174
  const visitor$3 = ({ key, attribute, path }) => {
1715
2175
  if (attribute?.type === "password") {
1716
- throwInvalidParam({ key, path: path.attribute });
2176
+ throwInvalidKey({ key, path: path.attribute });
1717
2177
  }
1718
2178
  };
1719
2179
  const visitor$2 = ({ schema, key, attribute, path }) => {
@@ -1722,7 +2182,7 @@ const visitor$2 = ({ schema, key, attribute, path }) => {
1722
2182
  }
1723
2183
  const isPrivate = attribute.private === true || isPrivateAttribute(schema, key);
1724
2184
  if (isPrivate) {
1725
- throwInvalidParam({ key, path: path.attribute });
2185
+ throwInvalidKey({ key, path: path.attribute });
1726
2186
  }
1727
2187
  };
1728
2188
  const ACTIONS_TO_VERIFY = ["find"];
@@ -1740,7 +2200,7 @@ const throwRestrictedRelations = (auth) => async ({ data, key, attribute, schema
1740
2200
  const scopes = ACTIONS_TO_VERIFY.map((action) => `${element.__type}.${action}`);
1741
2201
  const isAllowed = await hasAccessToSomeScopes(scopes, auth);
1742
2202
  if (!isAllowed) {
1743
- throwInvalidParam({ key, path: path.attribute });
2203
+ throwInvalidKey({ key, path: path.attribute });
1744
2204
  }
1745
2205
  }
1746
2206
  };
@@ -1748,7 +2208,7 @@ const throwRestrictedRelations = (auth) => async ({ data, key, attribute, schema
1748
2208
  const scopes = ACTIONS_TO_VERIFY.map((action) => `${attribute.target}.${action}`);
1749
2209
  const isAllowed = await hasAccessToSomeScopes(scopes, auth);
1750
2210
  if (!isAllowed) {
1751
- throwInvalidParam({ key, path: path.attribute });
2211
+ throwInvalidKey({ key, path: path.attribute });
1752
2212
  }
1753
2213
  };
1754
2214
  const isCreatorRelation = [CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE].includes(key);
@@ -1774,12 +2234,12 @@ const hasAccessToSomeScopes = async (scopes, auth) => {
1774
2234
  };
1775
2235
  const visitor$1 = ({ key, attribute, path }) => {
1776
2236
  if (isMorphToRelationalAttribute(attribute)) {
1777
- throwInvalidParam({ key, path: path.attribute });
2237
+ throwInvalidKey({ key, path: path.attribute });
1778
2238
  }
1779
2239
  };
1780
2240
  const visitor = ({ key, attribute, path }) => {
1781
2241
  if (isDynamicZoneAttribute(attribute)) {
1782
- throwInvalidParam({ key, path: path.attribute });
2242
+ throwInvalidKey({ key, path: path.attribute });
1783
2243
  }
1784
2244
  };
1785
2245
  const throwDisallowedFields = (allowedFields = null) => ({ key, path: { attribute: path } }) => {
@@ -1801,7 +2261,7 @@ const throwDisallowedFields = (allowedFields = null) => ({ key, path: { attribut
1801
2261
  if (isPathAllowed) {
1802
2262
  return;
1803
2263
  }
1804
- throwInvalidParam({ key, path });
2264
+ throwInvalidKey({ key, path });
1805
2265
  };
1806
2266
  const getContainedPaths = (path) => {
1807
2267
  const parts = toPath(path);
@@ -1811,7 +2271,7 @@ const getContainedPaths = (path) => {
1811
2271
  };
1812
2272
  const throwRestrictedFields = (restrictedFields = null) => ({ key, path: { attribute: path } }) => {
1813
2273
  if (restrictedFields === null) {
1814
- throwInvalidParam({ key, path });
2274
+ throwInvalidKey({ key, path });
1815
2275
  }
1816
2276
  if (!(isArray(restrictedFields) && restrictedFields.every(isString))) {
1817
2277
  throw new TypeError(
@@ -1819,16 +2279,46 @@ const throwRestrictedFields = (restrictedFields = null) => ({ key, path: { attri
1819
2279
  );
1820
2280
  }
1821
2281
  if (restrictedFields.includes(path)) {
1822
- throwInvalidParam({ key, path });
2282
+ throwInvalidKey({ key, path });
1823
2283
  }
1824
2284
  const isRestrictedNested = restrictedFields.some(
1825
2285
  (allowedPath) => path?.toString().startsWith(`${allowedPath}.`)
1826
2286
  );
1827
2287
  if (isRestrictedNested) {
1828
- throwInvalidParam({ key, path });
2288
+ throwInvalidKey({ key, path });
2289
+ }
2290
+ };
2291
+ const ID_FIELDS = [constants$1.DOC_ID_ATTRIBUTE, constants$1.DOC_ID_ATTRIBUTE];
2292
+ const ALLOWED_ROOT_LEVEL_FIELDS = [...ID_FIELDS];
2293
+ const MORPH_TO_ALLOWED_FIELDS = ["__type"];
2294
+ const DYNAMIC_ZONE_ALLOWED_FIELDS = ["__component"];
2295
+ const RELATION_REORDERING_FIELDS = ["connect", "disconnect", "set", "options"];
2296
+ const throwUnrecognizedFields = ({ key, attribute, path, schema, parent }) => {
2297
+ if (attribute) {
2298
+ return;
2299
+ }
2300
+ if (path.attribute === null) {
2301
+ if (ALLOWED_ROOT_LEVEL_FIELDS.includes(key)) {
2302
+ return;
2303
+ }
2304
+ return throwInvalidKey({ key, path: attribute });
2305
+ }
2306
+ if (isMorphToRelationalAttribute(parent?.attribute) && MORPH_TO_ALLOWED_FIELDS.includes(key)) {
2307
+ return;
2308
+ }
2309
+ if (isComponentSchema(schema) && isDynamicZoneAttribute(parent?.attribute) && DYNAMIC_ZONE_ALLOWED_FIELDS.includes(key)) {
2310
+ return;
2311
+ }
2312
+ if (hasRelationReordering(parent?.attribute) && RELATION_REORDERING_FIELDS.includes(key)) {
2313
+ return;
2314
+ }
2315
+ const canUseID = isRelationalAttribute(parent?.attribute) || isMediaAttribute(parent?.attribute);
2316
+ if (canUseID && !ID_FIELDS.includes(key)) {
2317
+ return;
1829
2318
  }
2319
+ throwInvalidKey({ key, path: attribute });
1830
2320
  };
1831
- const visitors = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2321
+ const index$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
1832
2322
  __proto__: null,
1833
2323
  throwDisallowedFields,
1834
2324
  throwDynamicZones: visitor,
@@ -1836,133 +2326,155 @@ const visitors = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProp
1836
2326
  throwPassword: visitor$3,
1837
2327
  throwPrivate: visitor$2,
1838
2328
  throwRestrictedFields,
1839
- throwRestrictedRelations
2329
+ throwRestrictedRelations,
2330
+ throwUnrecognizedFields
1840
2331
  }, Symbol.toStringTag, { value: "Module" }));
1841
- const { ID_ATTRIBUTE: ID_ATTRIBUTE$2, DOC_ID_ATTRIBUTE: DOC_ID_ATTRIBUTE$2 } = constants$1;
1842
- const throwPasswords = (schema) => async (entity) => {
1843
- if (!schema) {
2332
+ const { ID_ATTRIBUTE: ID_ATTRIBUTE$1, DOC_ID_ATTRIBUTE: DOC_ID_ATTRIBUTE$1 } = constants$1;
2333
+ const throwPasswords = (ctx) => async (entity) => {
2334
+ if (!ctx.schema) {
1844
2335
  throw new Error("Missing schema in throwPasswords");
1845
2336
  }
1846
- return traverseEntity$1(visitor$3, { schema }, entity);
2337
+ return traverseEntity$1(visitor$3, ctx, entity);
1847
2338
  };
1848
- const defaultValidateFilters = curry((schema, filters2) => {
1849
- if (!schema) {
2339
+ const defaultValidateFilters = curry((ctx, filters2) => {
2340
+ if (!ctx.schema) {
1850
2341
  throw new Error("Missing schema in defaultValidateFilters");
1851
2342
  }
1852
2343
  return pipe(
1853
2344
  // keys that are not attributes or valid operators
1854
- traverseQueryFilters(
1855
- ({ key, attribute, path }) => {
1856
- if ([ID_ATTRIBUTE$2, DOC_ID_ATTRIBUTE$2].includes(key)) {
1857
- return;
1858
- }
1859
- const isAttribute = !!attribute;
1860
- if (!isAttribute && !isOperator(key)) {
1861
- throwInvalidParam({ key, path: path.attribute });
1862
- }
1863
- },
1864
- { schema }
1865
- ),
2345
+ traverseQueryFilters(({ key, attribute, path }) => {
2346
+ if ([ID_ATTRIBUTE$1, DOC_ID_ATTRIBUTE$1].includes(key)) {
2347
+ return;
2348
+ }
2349
+ const isAttribute = !!attribute;
2350
+ if (!isAttribute && !isOperator(key)) {
2351
+ throwInvalidKey({ key, path: path.attribute });
2352
+ }
2353
+ }, ctx),
1866
2354
  // dynamic zones from filters
1867
- traverseQueryFilters(visitor, { schema }),
2355
+ traverseQueryFilters(visitor, ctx),
1868
2356
  // morphTo relations from filters; because you can't have deep filtering on morph relations
1869
- traverseQueryFilters(visitor$1, { schema }),
2357
+ traverseQueryFilters(visitor$1, ctx),
1870
2358
  // passwords from filters
1871
- traverseQueryFilters(visitor$3, { schema }),
2359
+ traverseQueryFilters(visitor$3, ctx),
1872
2360
  // private from filters
1873
- traverseQueryFilters(visitor$2, { schema })
2361
+ traverseQueryFilters(visitor$2, ctx)
1874
2362
  // we allow empty objects to validate and only sanitize them out, so that users may write "lazy" queries without checking their params exist
1875
2363
  )(filters2);
1876
2364
  });
1877
- const defaultValidateSort = curry((schema, sort2) => {
1878
- if (!schema) {
2365
+ const defaultValidateSort = curry((ctx, sort2) => {
2366
+ if (!ctx.schema) {
1879
2367
  throw new Error("Missing schema in defaultValidateSort");
1880
2368
  }
1881
2369
  return pipe(
1882
2370
  // non attribute keys
1883
- traverseQuerySort(
1884
- ({ key, attribute, path }) => {
1885
- if ([ID_ATTRIBUTE$2, DOC_ID_ATTRIBUTE$2].includes(key)) {
1886
- return;
1887
- }
1888
- if (!attribute) {
1889
- throwInvalidParam({ key, path: path.attribute });
1890
- }
1891
- },
1892
- { schema }
1893
- ),
2371
+ traverseQuerySort(({ key, attribute, path }) => {
2372
+ if ([ID_ATTRIBUTE$1, DOC_ID_ATTRIBUTE$1].includes(key)) {
2373
+ return;
2374
+ }
2375
+ if (!attribute) {
2376
+ throwInvalidKey({ key, path: path.attribute });
2377
+ }
2378
+ }, ctx),
1894
2379
  // dynamic zones from sort
1895
- traverseQuerySort(visitor, { schema }),
2380
+ traverseQuerySort(visitor, ctx),
1896
2381
  // morphTo relations from sort
1897
- traverseQuerySort(visitor$1, { schema }),
2382
+ traverseQuerySort(visitor$1, ctx),
1898
2383
  // private from sort
1899
- traverseQuerySort(visitor$2, { schema }),
2384
+ traverseQuerySort(visitor$2, ctx),
1900
2385
  // passwords from filters
1901
- traverseQuerySort(visitor$3, { schema }),
2386
+ traverseQuerySort(visitor$3, ctx),
1902
2387
  // keys for empty non-scalar values
1903
- traverseQuerySort(
1904
- ({ key, attribute, value, path }) => {
1905
- if ([ID_ATTRIBUTE$2, DOC_ID_ATTRIBUTE$2].includes(key)) {
1906
- return;
1907
- }
1908
- if (!isScalarAttribute(attribute) && isEmpty(value)) {
1909
- throwInvalidParam({ key, path: path.attribute });
1910
- }
1911
- },
1912
- { schema }
1913
- )
2388
+ traverseQuerySort(({ key, attribute, value, path }) => {
2389
+ if ([ID_ATTRIBUTE$1, DOC_ID_ATTRIBUTE$1].includes(key)) {
2390
+ return;
2391
+ }
2392
+ if (!isScalarAttribute(attribute) && isEmpty(value)) {
2393
+ throwInvalidKey({ key, path: path.attribute });
2394
+ }
2395
+ }, ctx)
1914
2396
  )(sort2);
1915
2397
  });
1916
- const defaultValidateFields = curry((schema, fields2) => {
1917
- if (!schema) {
2398
+ const defaultValidateFields = curry((ctx, fields2) => {
2399
+ if (!ctx.schema) {
1918
2400
  throw new Error("Missing schema in defaultValidateFields");
1919
2401
  }
1920
2402
  return pipe(
1921
2403
  // Only allow scalar attributes
1922
- traverseQueryFields(
1923
- ({ key, attribute, path }) => {
1924
- if ([ID_ATTRIBUTE$2, DOC_ID_ATTRIBUTE$2].includes(key)) {
1925
- return;
1926
- }
1927
- if (isNil(attribute) || !isScalarAttribute(attribute)) {
1928
- throwInvalidParam({ key, path: path.attribute });
1929
- }
1930
- },
1931
- { schema }
1932
- ),
2404
+ traverseQueryFields(({ key, attribute, path }) => {
2405
+ if ([ID_ATTRIBUTE$1, DOC_ID_ATTRIBUTE$1].includes(key)) {
2406
+ return;
2407
+ }
2408
+ if (isNil(attribute) || !isScalarAttribute(attribute)) {
2409
+ throwInvalidKey({ key, path: path.attribute });
2410
+ }
2411
+ }, ctx),
1933
2412
  // private fields
1934
- traverseQueryFields(visitor$2, { schema }),
2413
+ traverseQueryFields(visitor$2, ctx),
1935
2414
  // password fields
1936
- traverseQueryFields(visitor$3, { schema })
2415
+ traverseQueryFields(visitor$3, ctx)
1937
2416
  )(fields2);
1938
2417
  });
1939
- const defaultValidatePopulate = curry((schema, populate2) => {
1940
- if (!schema) {
2418
+ const defaultValidatePopulate = curry((ctx, populate2) => {
2419
+ if (!ctx.schema) {
1941
2420
  throw new Error("Missing schema in defaultValidatePopulate");
1942
2421
  }
1943
2422
  return pipe(
1944
- traverseQueryPopulate(
1945
- async ({ key, value, schema: schema2, attribute }, { set }) => {
1946
- if (attribute) {
1947
- return;
1948
- }
1949
- if (key === "sort") {
1950
- set(key, await defaultValidateSort(schema2, value));
1951
- }
1952
- if (key === "filters") {
1953
- set(key, await defaultValidateFilters(schema2, value));
1954
- }
1955
- if (key === "fields") {
1956
- set(key, await defaultValidateFields(schema2, value));
1957
- }
1958
- if (key === "populate") {
1959
- set(key, await defaultValidatePopulate(schema2, value));
1960
- }
1961
- },
1962
- { schema }
1963
- ),
2423
+ traverseQueryPopulate(async ({ key, value, schema, attribute, getModel }, { set }) => {
2424
+ if (attribute) {
2425
+ return;
2426
+ }
2427
+ if (key === "sort") {
2428
+ set(
2429
+ key,
2430
+ await defaultValidateSort(
2431
+ {
2432
+ schema,
2433
+ getModel
2434
+ },
2435
+ value
2436
+ )
2437
+ );
2438
+ }
2439
+ if (key === "filters") {
2440
+ set(
2441
+ key,
2442
+ await defaultValidateFilters(
2443
+ {
2444
+ schema,
2445
+ getModel
2446
+ },
2447
+ value
2448
+ )
2449
+ );
2450
+ }
2451
+ if (key === "fields") {
2452
+ set(
2453
+ key,
2454
+ await defaultValidateFields(
2455
+ {
2456
+ schema,
2457
+ getModel
2458
+ },
2459
+ value
2460
+ )
2461
+ );
2462
+ }
2463
+ if (key === "populate") {
2464
+ set(
2465
+ key,
2466
+ await defaultValidatePopulate(
2467
+ {
2468
+ schema,
2469
+ getModel
2470
+ },
2471
+ value
2472
+ )
2473
+ );
2474
+ }
2475
+ }, ctx),
1964
2476
  // Remove private fields
1965
- traverseQueryPopulate(visitor$2, { schema })
2477
+ traverseQueryPopulate(visitor$2, ctx)
1966
2478
  )(populate2);
1967
2479
  });
1968
2480
  const validators = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
@@ -1973,8 +2485,9 @@ const validators = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePr
1973
2485
  defaultValidateSort,
1974
2486
  throwPasswords
1975
2487
  }, Symbol.toStringTag, { value: "Module" }));
1976
- const { ID_ATTRIBUTE: ID_ATTRIBUTE$1, DOC_ID_ATTRIBUTE: DOC_ID_ATTRIBUTE$1 } = constants$1;
1977
- const createContentAPIValidators = () => {
2488
+ const { ID_ATTRIBUTE, DOC_ID_ATTRIBUTE } = constants$1;
2489
+ const createAPIValidators = (opts) => {
2490
+ const { getModel } = opts || {};
1978
2491
  const validateInput = async (data, schema, { auth } = {}) => {
1979
2492
  if (!schema) {
1980
2493
  throw new Error("Missing schema in validateInput");
@@ -1987,22 +2500,37 @@ const createContentAPIValidators = () => {
1987
2500
  const transforms = [
1988
2501
  (data2) => {
1989
2502
  if (isObject(data2)) {
1990
- if (ID_ATTRIBUTE$1 in data2) {
1991
- throwInvalidParam({ key: ID_ATTRIBUTE$1 });
2503
+ if (ID_ATTRIBUTE in data2) {
2504
+ throwInvalidKey({ key: ID_ATTRIBUTE });
1992
2505
  }
1993
- if (DOC_ID_ATTRIBUTE$1 in data2) {
1994
- throwInvalidParam({ key: DOC_ID_ATTRIBUTE$1 });
2506
+ if (DOC_ID_ATTRIBUTE in data2) {
2507
+ throwInvalidKey({ key: DOC_ID_ATTRIBUTE });
1995
2508
  }
1996
2509
  }
2510
+ return data2;
1997
2511
  },
1998
2512
  // non-writable attributes
1999
- traverseEntity$1(throwRestrictedFields(nonWritableAttributes), { schema })
2513
+ traverseEntity$1(throwRestrictedFields(nonWritableAttributes), { schema, getModel }),
2514
+ // unrecognized attributes
2515
+ traverseEntity$1(throwUnrecognizedFields, { schema, getModel })
2000
2516
  ];
2001
2517
  if (auth) {
2002
- transforms.push(traverseEntity$1(throwRestrictedRelations(auth), { schema }));
2518
+ transforms.push(
2519
+ traverseEntity$1(throwRestrictedRelations(auth), {
2520
+ schema,
2521
+ getModel
2522
+ })
2523
+ );
2524
+ }
2525
+ opts?.validators?.input?.forEach((validator) => transforms.push(validator(schema)));
2526
+ try {
2527
+ await pipe(...transforms)(data);
2528
+ } catch (e) {
2529
+ if (e instanceof ValidationError) {
2530
+ e.details.source = "body";
2531
+ }
2532
+ throw e;
2003
2533
  }
2004
- strapi.validators.get("content-api.input").forEach((validator) => transforms.push(validator(schema)));
2005
- await pipe(...transforms)(data);
2006
2534
  };
2007
2535
  const validateQuery = async (query, schema, { auth } = {}) => {
2008
2536
  if (!schema) {
@@ -2030,38 +2558,85 @@ const createContentAPIValidators = () => {
2030
2558
  await Promise.all(filters2.map((filter) => validateFilters(filter, schema, { auth })));
2031
2559
  return;
2032
2560
  }
2033
- const transforms = [defaultValidateFilters(schema)];
2561
+ const transforms = [defaultValidateFilters({ schema, getModel })];
2034
2562
  if (auth) {
2035
- transforms.push(traverseQueryFilters(throwRestrictedRelations(auth), { schema }));
2563
+ transforms.push(
2564
+ traverseQueryFilters(throwRestrictedRelations(auth), {
2565
+ schema,
2566
+ getModel
2567
+ })
2568
+ );
2569
+ }
2570
+ try {
2571
+ await pipe(...transforms)(filters2);
2572
+ } catch (e) {
2573
+ if (e instanceof ValidationError) {
2574
+ e.details.source = "query";
2575
+ e.details.param = "filters";
2576
+ }
2577
+ throw e;
2036
2578
  }
2037
- await pipe(...transforms)(filters2);
2038
2579
  };
2039
2580
  const validateSort = async (sort2, schema, { auth } = {}) => {
2040
2581
  if (!schema) {
2041
2582
  throw new Error("Missing schema in validateSort");
2042
2583
  }
2043
- const transforms = [defaultValidateSort(schema)];
2584
+ const transforms = [defaultValidateSort({ schema, getModel })];
2044
2585
  if (auth) {
2045
- transforms.push(traverseQuerySort(throwRestrictedRelations(auth), { schema }));
2586
+ transforms.push(
2587
+ traverseQuerySort(throwRestrictedRelations(auth), {
2588
+ schema,
2589
+ getModel
2590
+ })
2591
+ );
2592
+ }
2593
+ try {
2594
+ await pipe(...transforms)(sort2);
2595
+ } catch (e) {
2596
+ if (e instanceof ValidationError) {
2597
+ e.details.source = "query";
2598
+ e.details.param = "sort";
2599
+ }
2600
+ throw e;
2046
2601
  }
2047
- await pipe(...transforms)(sort2);
2048
2602
  };
2049
2603
  const validateFields = async (fields2, schema) => {
2050
2604
  if (!schema) {
2051
2605
  throw new Error("Missing schema in validateFields");
2052
2606
  }
2053
- const transforms = [defaultValidateFields(schema)];
2054
- await pipe(...transforms)(fields2);
2607
+ const transforms = [defaultValidateFields({ schema, getModel })];
2608
+ try {
2609
+ await pipe(...transforms)(fields2);
2610
+ } catch (e) {
2611
+ if (e instanceof ValidationError) {
2612
+ e.details.source = "query";
2613
+ e.details.param = "fields";
2614
+ }
2615
+ throw e;
2616
+ }
2055
2617
  };
2056
2618
  const validatePopulate = async (populate2, schema, { auth } = {}) => {
2057
2619
  if (!schema) {
2058
2620
  throw new Error("Missing schema in sanitizePopulate");
2059
2621
  }
2060
- const transforms = [defaultValidatePopulate(schema)];
2622
+ const transforms = [defaultValidatePopulate({ schema, getModel })];
2061
2623
  if (auth) {
2062
- transforms.push(traverseQueryPopulate(throwRestrictedRelations(auth), { schema }));
2624
+ transforms.push(
2625
+ traverseQueryPopulate(throwRestrictedRelations(auth), {
2626
+ schema,
2627
+ getModel
2628
+ })
2629
+ );
2630
+ }
2631
+ try {
2632
+ await pipe(...transforms)(populate2);
2633
+ } catch (e) {
2634
+ if (e instanceof ValidationError) {
2635
+ e.details.source = "query";
2636
+ e.details.param = "populate";
2637
+ }
2638
+ throw e;
2063
2639
  }
2064
- await pipe(...transforms)(populate2);
2065
2640
  };
2066
2641
  return {
2067
2642
  input: validateInput,
@@ -2072,436 +2647,12 @@ const createContentAPIValidators = () => {
2072
2647
  populate: validatePopulate
2073
2648
  };
2074
2649
  };
2075
- const contentAPI = createContentAPIValidators();
2076
- const index = {
2077
- contentAPI,
2650
+ const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2651
+ __proto__: null,
2652
+ createAPIValidators,
2078
2653
  validators,
2079
- visitors
2080
- };
2081
- const { ID_ATTRIBUTE, DOC_ID_ATTRIBUTE, PUBLISHED_AT_ATTRIBUTE } = constants$1;
2082
- class InvalidOrderError extends Error {
2083
- constructor() {
2084
- super();
2085
- this.message = "Invalid order. order can only be one of asc|desc|ASC|DESC";
2086
- }
2087
- }
2088
- class InvalidSortError extends Error {
2089
- constructor() {
2090
- super();
2091
- this.message = "Invalid sort parameter. Expected a string, an array of strings, a sort object or an array of sort objects";
2092
- }
2093
- }
2094
- function validateOrder(order) {
2095
- if (!isString(order) || !["asc", "desc"].includes(order.toLocaleLowerCase())) {
2096
- throw new InvalidOrderError();
2097
- }
2098
- }
2099
- const convertCountQueryParams = (countQuery) => {
2100
- return parseType({ type: "boolean", value: countQuery });
2101
- };
2102
- const convertOrderingQueryParams = (ordering) => {
2103
- return ordering;
2104
- };
2105
- const isPlainObject = (value) => _$1.isPlainObject(value);
2106
- const isStringArray = (value) => isArray(value) && value.every(isString);
2107
- const convertSortQueryParams = (sortQuery) => {
2108
- if (typeof sortQuery === "string") {
2109
- return convertStringSortQueryParam(sortQuery);
2110
- }
2111
- if (isStringArray(sortQuery)) {
2112
- return sortQuery.flatMap((sortValue) => convertStringSortQueryParam(sortValue));
2113
- }
2114
- if (Array.isArray(sortQuery)) {
2115
- return sortQuery.map((sortValue) => convertNestedSortQueryParam(sortValue));
2116
- }
2117
- if (isPlainObject(sortQuery)) {
2118
- return convertNestedSortQueryParam(sortQuery);
2119
- }
2120
- throw new InvalidSortError();
2121
- };
2122
- const convertStringSortQueryParam = (sortQuery) => {
2123
- return sortQuery.split(",").map((value) => convertSingleSortQueryParam(value));
2124
- };
2125
- const convertSingleSortQueryParam = (sortQuery) => {
2126
- if (!sortQuery) {
2127
- return {};
2128
- }
2129
- if (!isString(sortQuery)) {
2130
- throw new Error("Invalid sort query");
2131
- }
2132
- const [field, order = "asc"] = sortQuery.split(":");
2133
- if (field.length === 0) {
2134
- throw new Error("Field cannot be empty");
2135
- }
2136
- validateOrder(order);
2137
- return _$1.set({}, field, order);
2138
- };
2139
- const convertNestedSortQueryParam = (sortQuery) => {
2140
- const transformedSort = {};
2141
- for (const field of Object.keys(sortQuery)) {
2142
- const order = sortQuery[field];
2143
- if (isPlainObject(order)) {
2144
- transformedSort[field] = convertNestedSortQueryParam(order);
2145
- } else if (typeof order === "string") {
2146
- validateOrder(order);
2147
- transformedSort[field] = order;
2148
- } else {
2149
- throw Error(`Invalid sort type expected object or string got ${typeof order}`);
2150
- }
2151
- }
2152
- return transformedSort;
2153
- };
2154
- const convertStartQueryParams = (startQuery) => {
2155
- const startAsANumber = _$1.toNumber(startQuery);
2156
- if (!_$1.isInteger(startAsANumber) || startAsANumber < 0) {
2157
- throw new Error(`convertStartQueryParams expected a positive integer got ${startAsANumber}`);
2158
- }
2159
- return startAsANumber;
2160
- };
2161
- const convertLimitQueryParams = (limitQuery) => {
2162
- const limitAsANumber = _$1.toNumber(limitQuery);
2163
- if (!_$1.isInteger(limitAsANumber) || limitAsANumber !== -1 && limitAsANumber < 0) {
2164
- throw new Error(`convertLimitQueryParams expected a positive integer got ${limitAsANumber}`);
2165
- }
2166
- if (limitAsANumber === -1) {
2167
- return void 0;
2168
- }
2169
- return limitAsANumber;
2170
- };
2171
- const convertPageQueryParams = (page) => {
2172
- const pageVal = toNumber(page);
2173
- if (!isInteger(pageVal) || pageVal <= 0) {
2174
- throw new PaginationError(
2175
- `Invalid 'page' parameter. Expected an integer > 0, received: ${page}`
2176
- );
2177
- }
2178
- return pageVal;
2179
- };
2180
- const convertPageSizeQueryParams = (pageSize, page) => {
2181
- const pageSizeVal = toNumber(pageSize);
2182
- if (!isInteger(pageSizeVal) || pageSizeVal <= 0) {
2183
- throw new PaginationError(
2184
- `Invalid 'pageSize' parameter. Expected an integer > 0, received: ${page}`
2185
- );
2186
- }
2187
- return pageSizeVal;
2188
- };
2189
- const validatePaginationParams = (page, pageSize, start, limit) => {
2190
- const isPagePagination = !isNil(page) || !isNil(pageSize);
2191
- const isOffsetPagination = !isNil(start) || !isNil(limit);
2192
- if (isPagePagination && isOffsetPagination) {
2193
- throw new PaginationError(
2194
- "Invalid pagination attributes. You cannot use page and offset pagination in the same query"
2195
- );
2196
- }
2197
- };
2198
- class InvalidPopulateError extends Error {
2199
- constructor() {
2200
- super();
2201
- this.message = "Invalid populate parameter. Expected a string, an array of strings, a populate object";
2202
- }
2203
- }
2204
- const convertPopulateQueryParams = (populate2, schema, depth = 0) => {
2205
- if (depth === 0 && populate2 === "*") {
2206
- return true;
2207
- }
2208
- if (typeof populate2 === "string") {
2209
- return populate2.split(",").map((value) => _$1.trim(value));
2210
- }
2211
- if (Array.isArray(populate2)) {
2212
- return _$1.uniq(
2213
- populate2.flatMap((value) => {
2214
- if (typeof value !== "string") {
2215
- throw new InvalidPopulateError();
2216
- }
2217
- return value.split(",").map((value2) => _$1.trim(value2));
2218
- })
2219
- );
2220
- }
2221
- if (_$1.isPlainObject(populate2)) {
2222
- return convertPopulateObject(populate2, schema);
2223
- }
2224
- throw new InvalidPopulateError();
2225
- };
2226
- const hasFragmentPopulateDefined = (populate2) => {
2227
- return typeof populate2 === "object" && "on" in populate2 && !isNil(populate2.on);
2228
- };
2229
- const convertPopulateObject = (populate2, schema) => {
2230
- if (!schema) {
2231
- return {};
2232
- }
2233
- const { attributes } = schema;
2234
- return Object.entries(populate2).reduce((acc, [key, subPopulate]) => {
2235
- if (_$1.isBoolean(subPopulate)) {
2236
- return { ...acc, [key]: subPopulate };
2237
- }
2238
- const attribute = attributes[key];
2239
- if (!attribute) {
2240
- return acc;
2241
- }
2242
- const isAllowedAttributeForFragmentPopulate = isDynamicZoneAttribute(attribute) || isMorphToRelationalAttribute(attribute);
2243
- if (isAllowedAttributeForFragmentPopulate && hasFragmentPopulateDefined(subPopulate)) {
2244
- return {
2245
- ...acc,
2246
- [key]: {
2247
- on: Object.entries(subPopulate.on).reduce(
2248
- (acc2, [type, typeSubPopulate]) => ({
2249
- ...acc2,
2250
- [type]: convertNestedPopulate(typeSubPopulate, strapi.getModel(type))
2251
- }),
2252
- {}
2253
- )
2254
- }
2255
- };
2256
- }
2257
- if (isDynamicZoneAttribute(attribute)) {
2258
- const populates = attribute.components.map((uid) => strapi.getModel(uid)).map((schema2) => convertNestedPopulate(subPopulate, schema2)).map((populate22) => populate22 === true ? {} : populate22).filter((populate22) => populate22 !== false);
2259
- if (isEmpty(populates)) {
2260
- return acc;
2261
- }
2262
- return {
2263
- ...acc,
2264
- [key]: mergeAll(populates)
2265
- };
2266
- }
2267
- if (isMorphToRelationalAttribute(attribute)) {
2268
- return { ...acc, [key]: convertNestedPopulate(subPopulate, void 0) };
2269
- }
2270
- let targetSchemaUID;
2271
- if (attribute.type === "relation") {
2272
- targetSchemaUID = attribute.target;
2273
- } else if (attribute.type === "component") {
2274
- targetSchemaUID = attribute.component;
2275
- } else if (attribute.type === "media") {
2276
- targetSchemaUID = "plugin::upload.file";
2277
- } else {
2278
- return acc;
2279
- }
2280
- const targetSchema = strapi.getModel(targetSchemaUID);
2281
- if (!targetSchema) {
2282
- return acc;
2283
- }
2284
- const populateObject = convertNestedPopulate(subPopulate, targetSchema);
2285
- if (!populateObject) {
2286
- return acc;
2287
- }
2288
- return {
2289
- ...acc,
2290
- [key]: populateObject
2291
- };
2292
- }, {});
2293
- };
2294
- const convertNestedPopulate = (subPopulate, schema) => {
2295
- if (_$1.isString(subPopulate)) {
2296
- return parseType({ type: "boolean", value: subPopulate, forceCast: true });
2297
- }
2298
- if (_$1.isBoolean(subPopulate)) {
2299
- return subPopulate;
2300
- }
2301
- if (!isPlainObject(subPopulate)) {
2302
- throw new Error(`Invalid nested populate. Expected '*' or an object`);
2303
- }
2304
- const { sort: sort2, filters: filters2, fields: fields2, populate: populate2, count, ordering, page, pageSize, start, limit } = subPopulate;
2305
- const query = {};
2306
- if (sort2) {
2307
- query.orderBy = convertSortQueryParams(sort2);
2308
- }
2309
- if (filters2) {
2310
- query.where = convertFiltersQueryParams(filters2, schema);
2311
- }
2312
- if (fields2) {
2313
- query.select = convertFieldsQueryParams(fields2);
2314
- }
2315
- if (populate2) {
2316
- query.populate = convertPopulateQueryParams(populate2, schema);
2317
- }
2318
- if (count) {
2319
- query.count = convertCountQueryParams(count);
2320
- }
2321
- if (ordering) {
2322
- query.ordering = convertOrderingQueryParams(ordering);
2323
- }
2324
- validatePaginationParams(page, pageSize, start, limit);
2325
- if (!isNil(page)) {
2326
- query.page = convertPageQueryParams(page);
2327
- }
2328
- if (!isNil(pageSize)) {
2329
- query.pageSize = convertPageSizeQueryParams(pageSize, page);
2330
- }
2331
- if (!isNil(start)) {
2332
- query.offset = convertStartQueryParams(start);
2333
- }
2334
- if (!isNil(limit)) {
2335
- query.limit = convertLimitQueryParams(limit);
2336
- }
2337
- return query;
2338
- };
2339
- const convertFieldsQueryParams = (fields2, depth = 0) => {
2340
- if (depth === 0 && fields2 === "*") {
2341
- return void 0;
2342
- }
2343
- if (typeof fields2 === "string") {
2344
- const fieldsValues = fields2.split(",").map((value) => _$1.trim(value));
2345
- return _$1.uniq([ID_ATTRIBUTE, DOC_ID_ATTRIBUTE, ...fieldsValues]);
2346
- }
2347
- if (isStringArray(fields2)) {
2348
- const fieldsValues = fields2.flatMap((value) => convertFieldsQueryParams(value, depth + 1)).filter((v) => !isNil(v));
2349
- return _$1.uniq([ID_ATTRIBUTE, DOC_ID_ATTRIBUTE, ...fieldsValues]);
2350
- }
2351
- throw new Error("Invalid fields parameter. Expected a string or an array of strings");
2352
- };
2353
- const isValidSchemaAttribute = (key, schema) => {
2354
- if ([DOC_ID_ATTRIBUTE, ID_ATTRIBUTE].includes(key)) {
2355
- return true;
2356
- }
2357
- if (!schema) {
2358
- return false;
2359
- }
2360
- return Object.keys(schema.attributes).includes(key);
2361
- };
2362
- const convertFiltersQueryParams = (filters2, schema) => {
2363
- if (!isObject(filters2)) {
2364
- throw new Error("The filters parameter must be an object or an array");
2365
- }
2366
- const filtersCopy = cloneDeep(filters2);
2367
- return convertAndSanitizeFilters(filtersCopy, schema);
2368
- };
2369
- const convertAndSanitizeFilters = (filters2, schema) => {
2370
- if (Array.isArray(filters2)) {
2371
- return filters2.map((filter) => convertAndSanitizeFilters(filter, schema)).filter((filter) => !isPlainObject(filter) || !isEmpty(filter));
2372
- }
2373
- if (!isPlainObject(filters2)) {
2374
- return filters2;
2375
- }
2376
- const removeOperator = (operator) => delete filters2[operator];
2377
- for (const [key, value] of Object.entries(filters2)) {
2378
- const attribute = get(key, schema?.attributes);
2379
- const validKey = isOperator(key) || isValidSchemaAttribute(key, schema);
2380
- if (!validKey) {
2381
- removeOperator(key);
2382
- } else if (attribute) {
2383
- if (attribute.type === "relation") {
2384
- filters2[key] = convertAndSanitizeFilters(value, strapi.getModel(attribute.target));
2385
- } else if (attribute.type === "component") {
2386
- filters2[key] = convertAndSanitizeFilters(value, strapi.getModel(attribute.component));
2387
- } else if (attribute.type === "media") {
2388
- filters2[key] = convertAndSanitizeFilters(value, strapi.getModel("plugin::upload.file"));
2389
- } else if (attribute.type === "dynamiczone") {
2390
- removeOperator(key);
2391
- } else if (attribute.type === "password") {
2392
- removeOperator(key);
2393
- } else {
2394
- filters2[key] = convertAndSanitizeFilters(value, schema);
2395
- }
2396
- } else if (["$null", "$notNull"].includes(key)) {
2397
- filters2[key] = parseType({ type: "boolean", value: filters2[key], forceCast: true });
2398
- } else if (isObject(value)) {
2399
- filters2[key] = convertAndSanitizeFilters(value, schema);
2400
- }
2401
- if (isPlainObject(filters2[key]) && isEmpty(filters2[key])) {
2402
- removeOperator(key);
2403
- }
2404
- }
2405
- return filters2;
2406
- };
2407
- const convertStatusParams = (status, query = {}) => {
2408
- query.filters = ({ meta }) => {
2409
- const contentType = strapi.contentTypes[meta.uid];
2410
- if (!contentType || !hasDraftAndPublish(contentType)) {
2411
- return {};
2412
- }
2413
- return { [PUBLISHED_AT_ATTRIBUTE]: { $null: status === "draft" } };
2414
- };
2415
- };
2416
- const transformParamsToQuery = (uid, params) => {
2417
- const schema = strapi.getModel(uid);
2418
- const query = {};
2419
- const { _q, sort: sort2, filters: filters2, fields: fields2, populate: populate2, page, pageSize, start, limit, status, ...rest } = params;
2420
- if (!isNil(status)) {
2421
- convertStatusParams(status, query);
2422
- }
2423
- if (!isNil(_q)) {
2424
- query._q = _q;
2425
- }
2426
- if (!isNil(sort2)) {
2427
- query.orderBy = convertSortQueryParams(sort2);
2428
- }
2429
- if (!isNil(filters2)) {
2430
- query.where = convertFiltersQueryParams(filters2, schema);
2431
- }
2432
- if (!isNil(fields2)) {
2433
- query.select = convertFieldsQueryParams(fields2);
2434
- }
2435
- if (!isNil(populate2)) {
2436
- query.populate = convertPopulateQueryParams(populate2, schema);
2437
- }
2438
- validatePaginationParams(page, pageSize, start, limit);
2439
- if (!isNil(page)) {
2440
- query.page = convertPageQueryParams(page);
2441
- }
2442
- if (!isNil(pageSize)) {
2443
- query.pageSize = convertPageSizeQueryParams(pageSize, page);
2444
- }
2445
- if (!isNil(start)) {
2446
- query.offset = convertStartQueryParams(start);
2447
- }
2448
- if (!isNil(limit)) {
2449
- query.limit = convertLimitQueryParams(limit);
2450
- }
2451
- return {
2452
- ...rest,
2453
- ...query
2454
- };
2455
- };
2456
- const convertQueryParams = {
2457
- convertSortQueryParams,
2458
- convertStartQueryParams,
2459
- convertLimitQueryParams,
2460
- convertPopulateQueryParams,
2461
- convertFiltersQueryParams,
2462
- convertFieldsQueryParams,
2463
- transformParamsToQuery
2464
- };
2465
- function importDefault(modName) {
2466
- const mod = require(modName);
2467
- return mod && mod.__esModule ? mod.default : mod;
2468
- }
2469
- const machineId = () => {
2470
- try {
2471
- const deviceId = machineIdSync();
2472
- return deviceId;
2473
- } catch (error) {
2474
- const deviceId = randomUUID();
2475
- return deviceId;
2476
- }
2477
- };
2478
- const handleYupError = (error, errorMessage) => {
2479
- throw new YupValidationError(error, errorMessage);
2480
- };
2481
- const defaultValidationParam = { strict: true, abortEarly: false };
2482
- const validateYupSchema = (schema, options = {}) => async (body, errorMessage) => {
2483
- try {
2484
- const optionsWithDefaults = defaults(defaultValidationParam, options);
2485
- const result = await schema.validate(body, optionsWithDefaults);
2486
- return result;
2487
- } catch (e) {
2488
- if (e instanceof yup$1.ValidationError) {
2489
- handleYupError(e, errorMessage);
2490
- }
2491
- throw e;
2492
- }
2493
- };
2494
- const validateYupSchemaSync = (schema, options = {}) => (body, errorMessage) => {
2495
- try {
2496
- const optionsWithDefaults = defaults(defaultValidationParam, options);
2497
- return schema.validateSync(body, optionsWithDefaults);
2498
- } catch (e) {
2499
- if (e instanceof yup$1.ValidationError) {
2500
- handleYupError(e, errorMessage);
2501
- }
2502
- throw e;
2503
- }
2504
- };
2654
+ visitors: index$1
2655
+ }, Symbol.toStringTag, { value: "Module" }));
2505
2656
  const STRAPI_DEFAULTS = {
2506
2657
  offset: {
2507
2658
  start: 0,
@@ -2569,8 +2720,57 @@ const withDefaultPagination = (args, { defaults: defaults2 = {}, maxLimit = -1 }
2569
2720
  );
2570
2721
  return replacePaginationAttributes(args);
2571
2722
  };
2723
+ const transformPagedPaginationInfo = (paginationInfo, total) => {
2724
+ if (!isNil(paginationInfo.page)) {
2725
+ const page = paginationInfo.page;
2726
+ const pageSize = paginationInfo.pageSize ?? total;
2727
+ return {
2728
+ page,
2729
+ pageSize,
2730
+ pageCount: pageSize > 0 ? Math.ceil(total / pageSize) : 0,
2731
+ total
2732
+ };
2733
+ }
2734
+ if (!isNil(paginationInfo.start)) {
2735
+ const start = paginationInfo.start;
2736
+ const limit = paginationInfo.limit ?? total;
2737
+ return {
2738
+ page: Math.floor(start / limit) + 1,
2739
+ pageSize: limit,
2740
+ pageCount: limit > 0 ? Math.ceil(total / limit) : 0,
2741
+ total
2742
+ };
2743
+ }
2744
+ return {
2745
+ ...paginationInfo,
2746
+ page: 1,
2747
+ pageSize: 10,
2748
+ pageCount: 1,
2749
+ total
2750
+ };
2751
+ };
2752
+ const transformOffsetPaginationInfo = (paginationInfo, total) => {
2753
+ if (!isNil(paginationInfo.page)) {
2754
+ const limit = paginationInfo.pageSize ?? total;
2755
+ const start = (paginationInfo.page - 1) * limit;
2756
+ return { start, limit, total };
2757
+ }
2758
+ if (!isNil(paginationInfo.start)) {
2759
+ const start = paginationInfo.start;
2760
+ const limit = paginationInfo.limit ?? total;
2761
+ return { start, limit, total };
2762
+ }
2763
+ return {
2764
+ ...paginationInfo,
2765
+ start: 0,
2766
+ limit: 10,
2767
+ total
2768
+ };
2769
+ };
2572
2770
  const pagination = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2573
2771
  __proto__: null,
2772
+ transformOffsetPaginationInfo,
2773
+ transformPagedPaginationInfo,
2574
2774
  withDefaultPagination
2575
2775
  }, Symbol.toStringTag, { value: "Module" }));
2576
2776
  const SUPPORTED_PACKAGE_MANAGERS = ["npm", "yarn"];
@@ -2653,72 +2853,6 @@ const file = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty
2653
2853
  streamToBuffer,
2654
2854
  writableDiscardStream
2655
2855
  }, Symbol.toStringTag, { value: "Module" }));
2656
- const PLUGIN_PREFIX = "plugin::";
2657
- const API_PREFIX = "api::";
2658
- const parsePolicy = (policy2) => {
2659
- if (typeof policy2 === "string") {
2660
- return { policyName: policy2, config: {} };
2661
- }
2662
- const { name, config } = policy2;
2663
- return { policyName: name, config };
2664
- };
2665
- const searchLocalPolicy = (policyName, policyContext) => {
2666
- const { pluginName, apiName } = policyContext ?? {};
2667
- if (pluginName) {
2668
- return strapi.policy(`${PLUGIN_PREFIX}${pluginName}.${policyName}`);
2669
- }
2670
- if (apiName) {
2671
- return strapi.policy(`${API_PREFIX}${apiName}.${policyName}`);
2672
- }
2673
- };
2674
- const globalPolicy = ({ method, endpoint, controller, action, plugin }) => {
2675
- return async (ctx, next) => {
2676
- ctx.request.route = {
2677
- endpoint: `${method} ${endpoint}`,
2678
- controller: _$1.toLower(controller),
2679
- action: _$1.toLower(action),
2680
- verb: _$1.toLower(method),
2681
- plugin
2682
- };
2683
- await next();
2684
- };
2685
- };
2686
- const resolvePolicies = (config, policyContext) => {
2687
- const { pluginName, apiName } = policyContext ?? {};
2688
- return config.map((policyConfig) => {
2689
- return {
2690
- handler: getPolicy(policyConfig, { pluginName, apiName }),
2691
- config: typeof policyConfig === "object" && policyConfig.config || {}
2692
- };
2693
- });
2694
- };
2695
- const findPolicy = (name, policyContext) => {
2696
- const { pluginName, apiName } = policyContext ?? {};
2697
- const resolvedPolicy = strapi.policy(name);
2698
- if (resolvedPolicy !== void 0) {
2699
- return resolvedPolicy;
2700
- }
2701
- const localPolicy = searchLocalPolicy(name, { pluginName, apiName });
2702
- if (localPolicy !== void 0) {
2703
- return localPolicy;
2704
- }
2705
- throw new Error(`Could not find policy "${name}"`);
2706
- };
2707
- const getPolicy = (policyConfig, policyContext) => {
2708
- const { pluginName, apiName } = policyContext ?? {};
2709
- if (typeof policyConfig === "function") {
2710
- return policyConfig;
2711
- }
2712
- const { policyName, config } = parsePolicy(policyConfig);
2713
- const policy2 = findPolicy(policyName, { pluginName, apiName });
2714
- if (typeof policy2 === "function") {
2715
- return policy2;
2716
- }
2717
- if (policy2.validator) {
2718
- policy2.validator(config);
2719
- }
2720
- return policy2.handler;
2721
- };
2722
2856
  const createPolicy = (options) => {
2723
2857
  const { name = "unnamed", validator, handler } = options;
2724
2858
  const wrappedValidator = (config) => {
@@ -2750,10 +2884,7 @@ const createPolicyContext = (type, ctx) => {
2750
2884
  const policy = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2751
2885
  __proto__: null,
2752
2886
  createPolicy,
2753
- createPolicyContext,
2754
- get: getPolicy,
2755
- globalPolicy,
2756
- resolve: resolvePolicies
2887
+ createPolicyContext
2757
2888
  }, Symbol.toStringTag, { value: "Module" }));
2758
2889
  const nameToSlug = (name, options = { separator: "-" }) => slugify(name, options);
2759
2890
  const nameToCollectionName = (name) => slugify(name, { separator: "_" });
@@ -2763,9 +2894,9 @@ const toRegressedEnumValue = (value) => slugify(value, {
2763
2894
  separator: "_"
2764
2895
  });
2765
2896
  const getCommonPath = (...paths) => {
2766
- const [segments, ...otherSegments] = paths.map((it) => _$1.split(it, "/"));
2767
- return _$1.join(
2768
- _$1.takeWhile(segments, (str, index2) => otherSegments.every((it) => it[index2] === str)),
2897
+ const [segments, ...otherSegments] = paths.map((it) => ___default.split(it, "/"));
2898
+ return ___default.join(
2899
+ ___default.takeWhile(segments, (str, index2) => otherSegments.every((it) => it[index2] === str)),
2769
2900
  "/"
2770
2901
  );
2771
2902
  };
@@ -2807,9 +2938,9 @@ const arrays = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProper
2807
2938
  __proto__: null,
2808
2939
  includesString
2809
2940
  }, Symbol.toStringTag, { value: "Module" }));
2810
- const keysDeep = (obj, path = []) => !_$1.isObject(obj) ? [path.join(".")] : _$1.reduce(
2941
+ const keysDeep = (obj, path = []) => !___default.isObject(obj) ? [path.join(".")] : ___default.reduce(
2811
2942
  obj,
2812
- (acc, next, key) => _$1.concat(acc, keysDeep(next, [...path, key])),
2943
+ (acc, next, key) => ___default.concat(acc, keysDeep(next, [...path, key])),
2813
2944
  []
2814
2945
  );
2815
2946
  const objects = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
@@ -2873,8 +3004,8 @@ function printValue(value, quoteStrings) {
2873
3004
  );
2874
3005
  }
2875
3006
  const strapiID = () => new StrapiIDSchema();
2876
- const isNotNilTest = (value) => !_$1.isNil(value);
2877
- const isNotNullTest = (value) => !_$1.isNull(value);
3007
+ const isNotNilTest = (value) => !___default.isNil(value);
3008
+ const isNotNullTest = (value) => !___default.isNull(value);
2878
3009
  yup$1.addMethod(yup$1.mixed, "notNil", function isNotNill(msg = "${path} must be defined.") {
2879
3010
  return this.test("defined", msg, isNotNilTest);
2880
3011
  });
@@ -2885,7 +3016,7 @@ yup$1.addMethod(yup$1.mixed, "isFunction", function isFunction(message = "${path
2885
3016
  return this.test(
2886
3017
  "is a function",
2887
3018
  message,
2888
- (value) => _$1.isUndefined(value) || _$1.isFunction(value)
3019
+ (value) => ___default.isUndefined(value) || ___default.isFunction(value)
2889
3020
  );
2890
3021
  });
2891
3022
  yup$1.addMethod(
@@ -2917,7 +3048,7 @@ yup$1.addMethod(
2917
3048
  return this.test(
2918
3049
  "only contains functions",
2919
3050
  message,
2920
- (value) => _$1.isUndefined(value) || value && Object.values(value).every(_$1.isFunction)
3051
+ (value) => ___default.isUndefined(value) || value && Object.values(value).every(___default.isFunction)
2921
3052
  );
2922
3053
  }
2923
3054
  );
@@ -2992,11 +3123,31 @@ const relations = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePro
2992
3123
  isManyToAny,
2993
3124
  isOneToAny
2994
3125
  }, Symbol.toStringTag, { value: "Module" }));
3126
+ const validateZod = (schema) => (data) => {
3127
+ try {
3128
+ return schema.parse(data);
3129
+ } catch (error) {
3130
+ if (error instanceof z.ZodError) {
3131
+ const { message, errors: errors2 } = formatZodErrors(error);
3132
+ throw new ValidationError(message, { errors: errors2 });
3133
+ }
3134
+ throw error;
3135
+ }
3136
+ };
3137
+ const formatZodErrors = (zodError) => ({
3138
+ errors: zodError.errors.map((error) => {
3139
+ return {
3140
+ path: error.path,
3141
+ message: error.message,
3142
+ name: "ValidationError"
3143
+ };
3144
+ }),
3145
+ message: "Validation error"
3146
+ });
2995
3147
  export {
2996
3148
  arrays,
2997
3149
  async,
2998
3150
  contentTypes,
2999
- convertQueryParams,
3000
3151
  dates,
3001
3152
  env,
3002
3153
  errors,
@@ -3012,16 +3163,18 @@ export {
3012
3163
  parseType,
3013
3164
  policy,
3014
3165
  providerFactory,
3166
+ convertQueryParams as queryParams,
3015
3167
  relations,
3016
- index$1 as sanitize,
3168
+ index$2 as sanitize,
3017
3169
  setCreatorFields,
3018
3170
  strings,
3019
3171
  template,
3020
- index$2 as traverse,
3172
+ index$3 as traverse,
3021
3173
  traverseEntity$1 as traverseEntity,
3022
3174
  index as validate,
3023
3175
  validateYupSchema,
3024
3176
  validateYupSchemaSync,
3177
+ validateZod,
3025
3178
  yup
3026
3179
  };
3027
3180
  //# sourceMappingURL=index.mjs.map