@strapi/utils 4.26.1 → 4.26.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import _$1, { isString, isPlainObject as isPlainObject$1, kebabCase } from "lodash";
2
- import { eq, isEmpty, toPath, defaults, trimChars, trimCharsEnd, trimCharsStart, get, isNumber, isInteger, has, union, getOr, assoc, assign, cloneDeep, remove, merge, isNil, pipe, omit, curry, isArray, isString as isString$1, isObject, clone, pick, trim, split, map, flatten, first, join, constant, mergeAll, toNumber } from "lodash/fp";
2
+ import { eq, isEmpty, toPath, defaults, trimChars, trimCharsEnd, trimCharsStart, get, isNumber, isInteger, union, getOr, has, assoc, assign, cloneDeep, remove, merge, isNil, pipe, omit, curry, isArray, isString as isString$1, isObject, clone, pick, trim, split, map, flatten, first, join, constant, toNumber, mergeAll } from "lodash/fp";
3
3
  import * as yup$1 from "yup";
4
4
  import { HttpError } from "http-errors";
5
5
  import slugify from "@sindresorhus/slugify";
@@ -87,8 +87,7 @@ const parseDate = (value2) => {
87
87
  }
88
88
  try {
89
89
  const date = dates.parseISO(value2);
90
- if (dates.isValid(date))
91
- return dates.format(date, "yyyy-MM-dd");
90
+ if (dates.isValid(date)) return dates.format(date, "yyyy-MM-dd");
92
91
  throw new Error(`Invalid format, expected an ISO compatible date`);
93
92
  } catch (error) {
94
93
  throw new Error(`Invalid format, expected an ISO compatible date`);
@@ -103,11 +102,9 @@ const parseDateTimeOrTimestamp = (value2) => {
103
102
  }
104
103
  try {
105
104
  const date = dates.parseISO(value2);
106
- if (dates.isValid(date))
107
- return date;
105
+ if (dates.isValid(date)) return date;
108
106
  const milliUnixDate = dates.parse(value2, "T", /* @__PURE__ */ new Date());
109
- if (dates.isValid(milliUnixDate))
110
- return milliUnixDate;
107
+ if (dates.isValid(milliUnixDate)) return milliUnixDate;
111
108
  throw new Error(`Invalid format, expected a timestamp or an ISO date`);
112
109
  } catch (error) {
113
110
  throw new Error(`Invalid format, expected a timestamp or an ISO date`);
@@ -441,12 +438,9 @@ const joinBy = (joint, ...args) => {
441
438
  const trimEnd = trimCharsEnd(joint);
442
439
  const trimStart = trimCharsStart(joint);
443
440
  return args.reduce((url, path, index2) => {
444
- if (args.length === 1)
445
- return path;
446
- if (index2 === 0)
447
- return trimEnd(path);
448
- if (index2 === args.length - 1)
449
- return url + joint + trimStart(path);
441
+ if (args.length === 1) return path;
442
+ if (index2 === 0) return trimEnd(path);
443
+ if (index2 === args.length - 1) return url + joint + trimStart(path);
450
444
  return url + joint + trim2(path);
451
445
  }, "");
452
446
  };
@@ -457,43 +451,33 @@ const regExpToString = RegExp.prototype.toString;
457
451
  const symbolToString = typeof Symbol !== "undefined" ? Symbol.prototype.toString : () => "";
458
452
  const SYMBOL_REGEXP = /^Symbol\((.*)\)(.*)$/;
459
453
  function printNumber(val) {
460
- if (val != +val)
461
- return "NaN";
454
+ if (val != +val) return "NaN";
462
455
  const isNegativeZero = val === 0 && 1 / val < 0;
463
456
  return isNegativeZero ? "-0" : `${val}`;
464
457
  }
465
458
  function printSimpleValue(val, quoteStrings = false) {
466
- if (val == null || val === true || val === false)
467
- return `${val}`;
468
- if (typeof val === "number")
469
- return printNumber(val);
470
- if (typeof val === "string")
471
- return quoteStrings ? `"${val}"` : val;
472
- if (typeof val === "function")
473
- return `[Function ${val.name || "anonymous"}]`;
474
- if (typeof val === "symbol")
475
- return symbolToString.call(val).replace(SYMBOL_REGEXP, "Symbol($1)");
459
+ if (val == null || val === true || val === false) return `${val}`;
460
+ if (typeof val === "number") return printNumber(val);
461
+ if (typeof val === "string") return quoteStrings ? `"${val}"` : val;
462
+ if (typeof val === "function") return `[Function ${val.name || "anonymous"}]`;
463
+ if (typeof val === "symbol") return symbolToString.call(val).replace(SYMBOL_REGEXP, "Symbol($1)");
476
464
  const tag = toString.call(val).slice(8, -1);
477
465
  if (tag === "Date") {
478
466
  const v = val;
479
467
  return Number.isNaN(v.getTime()) ? `${v}` : v.toISOString();
480
468
  }
481
- if (tag === "Error" || val instanceof Error)
482
- return `[${errorToString.call(val)}]`;
483
- if (tag === "RegExp")
484
- return regExpToString.call(val);
469
+ if (tag === "Error" || val instanceof Error) return `[${errorToString.call(val)}]`;
470
+ if (tag === "RegExp") return regExpToString.call(val);
485
471
  return null;
486
472
  }
487
473
  function printValue(value2, quoteStrings) {
488
474
  const result = printSimpleValue(value2, quoteStrings);
489
- if (result !== null)
490
- return result;
475
+ if (result !== null) return result;
491
476
  return JSON.stringify(
492
477
  value2,
493
478
  function replacer(key2, value22) {
494
479
  const result2 = printSimpleValue(this[key2], quoteStrings);
495
- if (result2 !== null)
496
- return result2;
480
+ if (result2 !== null) return result2;
497
481
  return value22;
498
482
  },
499
483
  2
@@ -709,8 +693,7 @@ const getCreatorFields = (model) => {
709
693
  return attributes;
710
694
  };
711
695
  const getNonWritableAttributes = (model) => {
712
- if (!model)
713
- return [];
696
+ if (!model) return [];
714
697
  const nonWritableAttributes = _$1.reduce(
715
698
  model.attributes,
716
699
  (acc2, attr, attrName) => attr.writable === false ? acc2.concat(attrName) : acc2,
@@ -719,8 +702,7 @@ const getNonWritableAttributes = (model) => {
719
702
  return _$1.uniq([ID_ATTRIBUTE, ...getTimestamps(model), ...nonWritableAttributes]);
720
703
  };
721
704
  const getWritableAttributes = (model) => {
722
- if (!model)
723
- return [];
705
+ if (!model) return [];
724
706
  return _$1.difference(Object.keys(model.attributes), getNonWritableAttributes(model));
725
707
  };
726
708
  const isWritableAttribute = (model, attributeName) => {
@@ -776,8 +758,7 @@ const getComponentAttributes = (schema) => {
776
758
  return _$1.reduce(
777
759
  schema.attributes,
778
760
  (acc2, attr, attrName) => {
779
- if (isComponentAttribute(attr))
780
- acc2.push(attrName);
761
+ if (isComponentAttribute(attr)) acc2.push(attrName);
781
762
  return acc2;
782
763
  },
783
764
  []
@@ -787,8 +768,7 @@ const getScalarAttributes = (schema) => {
787
768
  return _$1.reduce(
788
769
  schema.attributes,
789
770
  (acc2, attr, attrName) => {
790
- if (isScalarAttribute(attr))
791
- acc2.push(attrName);
771
+ if (isScalarAttribute(attr)) acc2.push(attrName);
792
772
  return acc2;
793
773
  },
794
774
  []
@@ -1181,8 +1161,12 @@ const removeRestrictedRelations = (auth) => async ({ data, key: key2, attribute,
1181
1161
  return;
1182
1162
  }
1183
1163
  const handleMorphRelation = async () => {
1164
+ const elements = data[key2];
1165
+ if (!Array.isArray(elements)) {
1166
+ return;
1167
+ }
1184
1168
  const newMorphValue = [];
1185
- for (const element of data[key2]) {
1169
+ for (const element of elements) {
1186
1170
  const scopes = ACTIONS_TO_VERIFY$1.map((action) => `${element.__type}.${action}`);
1187
1171
  const isAllowed = await hasAccessToSomeScopes$1(scopes, auth);
1188
1172
  if (isAllowed) {
@@ -2121,7 +2105,9 @@ const createContentAPISanitizers = () => {
2121
2105
  Object.assign(sanitizedQuery, { fields: await sanitizeFields(fields2, schema) });
2122
2106
  }
2123
2107
  if (populate2) {
2124
- Object.assign(sanitizedQuery, { populate: await sanitizePopulate(populate2, schema) });
2108
+ Object.assign(sanitizedQuery, {
2109
+ populate: await sanitizePopulate(populate2, schema, { auth })
2110
+ });
2125
2111
  }
2126
2112
  return sanitizedQuery;
2127
2113
  };
@@ -2161,7 +2147,26 @@ const createContentAPISanitizers = () => {
2161
2147
  }
2162
2148
  const transforms = [defaultSanitizePopulate(schema)];
2163
2149
  if (auth) {
2164
- transforms.push(traverseQueryPopulate(removeRestrictedRelations(auth), { schema }));
2150
+ transforms.push(
2151
+ traverseQueryPopulate(removeRestrictedRelations(auth), { schema }),
2152
+ traverseQueryPopulate(
2153
+ async ({ key: key2, value: value2, schema: schema2, attribute }, { set }) => {
2154
+ if (attribute) {
2155
+ return;
2156
+ }
2157
+ if (key2 === "sort") {
2158
+ set(key2, await sanitizeSort(value2, schema2, { auth }));
2159
+ }
2160
+ if (key2 === "filters") {
2161
+ set(key2, await sanitizeFilters(value2, schema2, { auth }));
2162
+ }
2163
+ if (key2 === "fields") {
2164
+ set(key2, await sanitizeFields(value2, schema2));
2165
+ }
2166
+ },
2167
+ { schema }
2168
+ )
2169
+ );
2165
2170
  }
2166
2171
  return pipeAsync(...transforms)(populate2);
2167
2172
  };
@@ -2209,7 +2214,11 @@ const throwRestrictedRelations = (auth) => async ({ data, key: key2, attribute,
2209
2214
  return;
2210
2215
  }
2211
2216
  const handleMorphRelation = async () => {
2212
- for (const element of data[key2]) {
2217
+ const elements = data[key2];
2218
+ if (!Array.isArray(elements)) {
2219
+ return;
2220
+ }
2221
+ for (const element of elements) {
2213
2222
  const scopes = ACTIONS_TO_VERIFY.map((action) => `${element.__type}.${action}`);
2214
2223
  const isAllowed = await hasAccessToSomeScopes(scopes, auth);
2215
2224
  if (!isAllowed) {
@@ -2408,10 +2417,35 @@ const defaultValidateFields = curry((schema, fields2) => {
2408
2417
  traverseQueryFields(visitor$3, { schema })
2409
2418
  )(fields2);
2410
2419
  });
2420
+ const defaultValidatePopulate = curry((schema, populate2) => {
2421
+ if (!schema) {
2422
+ throw new Error("Missing schema in defaultValidatePopulate");
2423
+ }
2424
+ return pipeAsync(
2425
+ traverseQueryPopulate(
2426
+ async ({ key: key2, value: value2, schema: schema2, attribute }) => {
2427
+ if (attribute) {
2428
+ return;
2429
+ }
2430
+ if (key2 === "sort") {
2431
+ await defaultValidateSort(schema2, value2);
2432
+ }
2433
+ if (key2 === "filters") {
2434
+ await defaultValidateFilters(schema2, value2);
2435
+ }
2436
+ if (key2 === "fields") {
2437
+ await defaultValidateFields(schema2, value2);
2438
+ }
2439
+ },
2440
+ { schema }
2441
+ )
2442
+ )(populate2);
2443
+ });
2411
2444
  const validators = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2412
2445
  __proto__: null,
2413
2446
  defaultValidateFields,
2414
2447
  defaultValidateFilters,
2448
+ defaultValidatePopulate,
2415
2449
  defaultValidateSort,
2416
2450
  throwPasswords
2417
2451
  }, Symbol.toStringTag, { value: "Module" }));
@@ -2444,7 +2478,7 @@ const createContentAPIValidators = () => {
2444
2478
  if (!schema) {
2445
2479
  throw new Error("Missing schema in validateQuery");
2446
2480
  }
2447
- const { filters: filters2, sort: sort2, fields: fields2 } = query;
2481
+ const { filters: filters2, sort: sort2, fields: fields2, populate: populate2 } = query;
2448
2482
  if (filters2) {
2449
2483
  await validateFilters(filters2, schema, { auth });
2450
2484
  }
@@ -2454,6 +2488,9 @@ const createContentAPIValidators = () => {
2454
2488
  if (fields2) {
2455
2489
  await validateFields(fields2, schema);
2456
2490
  }
2491
+ if (populate2 && populate2 !== "*") {
2492
+ await validatePopulate(populate2, schema, { auth });
2493
+ }
2457
2494
  };
2458
2495
  const validateFilters = async (filters2, schema, { auth } = {}) => {
2459
2496
  if (!schema) {
@@ -2486,12 +2523,41 @@ const createContentAPIValidators = () => {
2486
2523
  const transforms = [defaultValidateFields(schema)];
2487
2524
  return pipeAsync(...transforms)(fields2);
2488
2525
  };
2526
+ const validatePopulate = async (populate2, schema, { auth } = {}) => {
2527
+ if (!schema) {
2528
+ throw new Error("Missing schema in validatePopulate");
2529
+ }
2530
+ const transforms = [defaultValidatePopulate(schema)];
2531
+ if (auth) {
2532
+ transforms.push(
2533
+ traverseQueryPopulate(
2534
+ async ({ key: key2, value: value2, schema: schema2, attribute }) => {
2535
+ if (attribute) {
2536
+ return;
2537
+ }
2538
+ if (key2 === "sort") {
2539
+ await validateSort(value2, schema2, { auth });
2540
+ }
2541
+ if (key2 === "filters") {
2542
+ await validateFilters(value2, schema2, { auth });
2543
+ }
2544
+ if (key2 === "fields") {
2545
+ await validateFields(value2, schema2);
2546
+ }
2547
+ },
2548
+ { schema }
2549
+ )
2550
+ );
2551
+ }
2552
+ return pipeAsync(...transforms)(populate2);
2553
+ };
2489
2554
  return {
2490
2555
  input: validateInput,
2491
2556
  query: validateQuery,
2492
2557
  filters: validateFilters,
2493
2558
  sort: validateSort,
2494
- fields: validateFields
2559
+ fields: validateFields,
2560
+ populate: validatePopulate
2495
2561
  };
2496
2562
  };
2497
2563
  const contentAPI = createContentAPIValidators();
@@ -2903,8 +2969,7 @@ const kbytesToBytes = (kbytes) => kbytes * 1e3;
2903
2969
  const bytesToKbytes = (bytes) => Math.round(bytes / 1e3 * 100) / 100;
2904
2970
  const bytesToHumanReadable = (bytes) => {
2905
2971
  const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB"];
2906
- if (bytes === 0)
2907
- return "0 Bytes";
2972
+ if (bytes === 0) return "0 Bytes";
2908
2973
  const i = parseInt(`${Math.floor(Math.log(bytes) / Math.log(1e3))}`, 10);
2909
2974
  return `${Math.round(bytes / 1e3 ** i)} ${sizes[i]}`;
2910
2975
  };