@strapi/utils 5.0.0-rc.12 → 5.0.0-rc.14

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.js CHANGED
@@ -1884,10 +1884,17 @@ const populate = traverseFactory().intercept(isStringArray$1, async (visitor2, o
1884
1884
  }
1885
1885
  });
1886
1886
  const traverseQueryPopulate = fp.curry(populate.traverse);
1887
- const isStringArray = (value) => fp.isArray(value) && value.every(fp.isString);
1887
+ const isStringArray = (value) => {
1888
+ return fp.isArray(value) && value.every(fp.isString);
1889
+ };
1888
1890
  const fields = traverseFactory().intercept(isStringArray, async (visitor2, options, fields2, { recurse }) => {
1889
1891
  return Promise.all(fields2.map((field) => recurse(visitor2, options, field)));
1890
- }).intercept((value) => fp.eq("*", value), fp.constant("*")).parse(fp.isString, () => ({
1892
+ }).intercept(
1893
+ (value) => fp.isString(value) && value.includes(","),
1894
+ (visitor2, options, fields2, { recurse }) => {
1895
+ return Promise.all(fields2.split(",").map((field) => recurse(visitor2, options, field)));
1896
+ }
1897
+ ).intercept((value) => fp.eq("*", value), fp.constant("*")).parse(fp.isString, () => ({
1891
1898
  transform: fp.trim,
1892
1899
  remove(key, data) {
1893
1900
  return data === key ? void 0 : data;
@@ -2188,6 +2195,15 @@ const throwInvalidKey = ({ key, path }) => {
2188
2195
  path
2189
2196
  });
2190
2197
  };
2198
+ const asyncCurry = (fn) => {
2199
+ const curried = (...args) => {
2200
+ if (args.length >= fn.length) {
2201
+ return fn(...args);
2202
+ }
2203
+ return (...moreArgs) => curried(...args, ...moreArgs);
2204
+ };
2205
+ return curried;
2206
+ };
2191
2207
  const visitor$3 = ({ key, attribute, path }) => {
2192
2208
  if (attribute?.type === "password") {
2193
2209
  throwInvalidKey({ key, path: path.attribute });
@@ -2353,154 +2369,294 @@ const throwPasswords = (ctx) => async (entity) => {
2353
2369
  }
2354
2370
  return traverseEntity$1(visitor$3, ctx, entity);
2355
2371
  };
2356
- const defaultValidateFilters = fp.curry((ctx, filters2) => {
2357
- if (!ctx.schema) {
2358
- throw new Error("Missing schema in defaultValidateFilters");
2372
+ const FILTER_TRAVERSALS = [
2373
+ "nonAttributesOperators",
2374
+ "dynamicZones",
2375
+ "morphRelations",
2376
+ "passwords",
2377
+ "private"
2378
+ ];
2379
+ const validateFilters = asyncCurry(
2380
+ async (ctx, filters2, include) => {
2381
+ if (!ctx.schema) {
2382
+ throw new Error("Missing schema in defaultValidateFilters");
2383
+ }
2384
+ const functionsToApply = [];
2385
+ if (include.includes("nonAttributesOperators")) {
2386
+ functionsToApply.push(
2387
+ traverseQueryFilters(({ key, attribute, path }) => {
2388
+ if ([ID_ATTRIBUTE$1, DOC_ID_ATTRIBUTE$1].includes(key)) {
2389
+ return;
2390
+ }
2391
+ const isAttribute = !!attribute;
2392
+ if (!isAttribute && !isOperator(key)) {
2393
+ throwInvalidKey({ key, path: path.attribute });
2394
+ }
2395
+ }, ctx)
2396
+ );
2397
+ }
2398
+ if (include.includes("dynamicZones")) {
2399
+ functionsToApply.push(traverseQueryFilters(visitor, ctx));
2400
+ }
2401
+ if (include.includes("morphRelations")) {
2402
+ functionsToApply.push(traverseQueryFilters(visitor$1, ctx));
2403
+ }
2404
+ if (include.includes("passwords")) {
2405
+ functionsToApply.push(traverseQueryFilters(visitor$3, ctx));
2406
+ }
2407
+ if (include.includes("private")) {
2408
+ functionsToApply.push(traverseQueryFilters(visitor$2, ctx));
2409
+ }
2410
+ if (functionsToApply.length === 0) {
2411
+ return filters2;
2412
+ }
2413
+ return pipe(...functionsToApply)(filters2);
2359
2414
  }
2360
- return pipe(
2361
- // keys that are not attributes or valid operators
2362
- traverseQueryFilters(({ key, attribute, path }) => {
2363
- if ([ID_ATTRIBUTE$1, DOC_ID_ATTRIBUTE$1].includes(key)) {
2364
- return;
2365
- }
2366
- const isAttribute = !!attribute;
2367
- if (!isAttribute && !isOperator(key)) {
2368
- throwInvalidKey({ key, path: path.attribute });
2369
- }
2370
- }, ctx),
2371
- // dynamic zones from filters
2372
- traverseQueryFilters(visitor, ctx),
2373
- // morphTo relations from filters; because you can't have deep filtering on morph relations
2374
- traverseQueryFilters(visitor$1, ctx),
2375
- // passwords from filters
2376
- traverseQueryFilters(visitor$3, ctx),
2377
- // private from filters
2378
- traverseQueryFilters(visitor$2, ctx)
2379
- // we allow empty objects to validate and only sanitize them out, so that users may write "lazy" queries without checking their params exist
2380
- )(filters2);
2415
+ );
2416
+ const defaultValidateFilters = asyncCurry(async (ctx, filters2) => {
2417
+ return validateFilters(ctx, filters2, FILTER_TRAVERSALS);
2381
2418
  });
2382
- const defaultValidateSort = fp.curry((ctx, sort2) => {
2383
- if (!ctx.schema) {
2384
- throw new Error("Missing schema in defaultValidateSort");
2419
+ const SORT_TRAVERSALS = [
2420
+ "nonAttributesOperators",
2421
+ "dynamicZones",
2422
+ "morphRelations",
2423
+ "passwords",
2424
+ "private",
2425
+ "nonScalarEmptyKeys"
2426
+ ];
2427
+ const validateSort = asyncCurry(
2428
+ async (ctx, sort2, include) => {
2429
+ if (!ctx.schema) {
2430
+ throw new Error("Missing schema in defaultValidateSort");
2431
+ }
2432
+ const functionsToApply = [];
2433
+ if (include.includes("nonAttributesOperators")) {
2434
+ functionsToApply.push(
2435
+ traverseQuerySort(({ key, attribute, path }) => {
2436
+ if ([ID_ATTRIBUTE$1, DOC_ID_ATTRIBUTE$1].includes(key)) {
2437
+ return;
2438
+ }
2439
+ if (!attribute) {
2440
+ throwInvalidKey({ key, path: path.attribute });
2441
+ }
2442
+ }, ctx)
2443
+ );
2444
+ }
2445
+ if (include.includes("dynamicZones")) {
2446
+ functionsToApply.push(traverseQuerySort(visitor, ctx));
2447
+ }
2448
+ if (include.includes("morphRelations")) {
2449
+ functionsToApply.push(traverseQuerySort(visitor$1, ctx));
2450
+ }
2451
+ if (include.includes("passwords")) {
2452
+ functionsToApply.push(traverseQuerySort(visitor$3, ctx));
2453
+ }
2454
+ if (include.includes("private")) {
2455
+ functionsToApply.push(traverseQuerySort(visitor$2, ctx));
2456
+ }
2457
+ if (include.includes("nonScalarEmptyKeys")) {
2458
+ functionsToApply.push(
2459
+ traverseQuerySort(({ key, attribute, value, path }) => {
2460
+ if ([ID_ATTRIBUTE$1, DOC_ID_ATTRIBUTE$1].includes(key)) {
2461
+ return;
2462
+ }
2463
+ if (!isScalarAttribute(attribute) && fp.isEmpty(value)) {
2464
+ throwInvalidKey({ key, path: path.attribute });
2465
+ }
2466
+ }, ctx)
2467
+ );
2468
+ }
2469
+ if (functionsToApply.length === 0) {
2470
+ return sort2;
2471
+ }
2472
+ return pipe(...functionsToApply)(sort2);
2385
2473
  }
2386
- return pipe(
2387
- // non attribute keys
2388
- traverseQuerySort(({ key, attribute, path }) => {
2389
- if ([ID_ATTRIBUTE$1, DOC_ID_ATTRIBUTE$1].includes(key)) {
2390
- return;
2391
- }
2392
- if (!attribute) {
2393
- throwInvalidKey({ key, path: path.attribute });
2394
- }
2395
- }, ctx),
2396
- // dynamic zones from sort
2397
- traverseQuerySort(visitor, ctx),
2398
- // morphTo relations from sort
2399
- traverseQuerySort(visitor$1, ctx),
2400
- // private from sort
2401
- traverseQuerySort(visitor$2, ctx),
2402
- // passwords from filters
2403
- traverseQuerySort(visitor$3, ctx),
2404
- // keys for empty non-scalar values
2405
- traverseQuerySort(({ key, attribute, value, path }) => {
2406
- if ([ID_ATTRIBUTE$1, DOC_ID_ATTRIBUTE$1].includes(key)) {
2407
- return;
2408
- }
2409
- if (!isScalarAttribute(attribute) && fp.isEmpty(value)) {
2410
- throwInvalidKey({ key, path: path.attribute });
2411
- }
2412
- }, ctx)
2413
- )(sort2);
2474
+ );
2475
+ const defaultValidateSort = asyncCurry(async (ctx, sort2) => {
2476
+ return validateSort(ctx, sort2, SORT_TRAVERSALS);
2414
2477
  });
2415
- const defaultValidateFields = fp.curry((ctx, fields2) => {
2416
- if (!ctx.schema) {
2417
- throw new Error("Missing schema in defaultValidateFields");
2478
+ const FIELDS_TRAVERSALS = ["scalarAttributes", "privateFields", "passwordFields"];
2479
+ const validateFields = asyncCurry(
2480
+ async (ctx, fields2, include) => {
2481
+ if (!ctx.schema) {
2482
+ throw new Error("Missing schema in defaultValidateFields");
2483
+ }
2484
+ const functionsToApply = [];
2485
+ if (include.includes("scalarAttributes")) {
2486
+ functionsToApply.push(
2487
+ traverseQueryFields(({ key, attribute, path }) => {
2488
+ if ([ID_ATTRIBUTE$1, DOC_ID_ATTRIBUTE$1].includes(key)) {
2489
+ return;
2490
+ }
2491
+ if (fp.isNil(attribute) || !isScalarAttribute(attribute)) {
2492
+ throwInvalidKey({ key, path: path.attribute });
2493
+ }
2494
+ }, ctx)
2495
+ );
2496
+ }
2497
+ if (include.includes("privateFields")) {
2498
+ functionsToApply.push(traverseQueryFields(visitor$2, ctx));
2499
+ }
2500
+ if (include.includes("passwordFields")) {
2501
+ functionsToApply.push(traverseQueryFields(visitor$3, ctx));
2502
+ }
2503
+ if (functionsToApply.length === 0) {
2504
+ return fields2;
2505
+ }
2506
+ return pipe(...functionsToApply)(fields2);
2418
2507
  }
2419
- return pipe(
2420
- // Only allow scalar attributes
2421
- traverseQueryFields(({ key, attribute, path }) => {
2422
- if ([ID_ATTRIBUTE$1, DOC_ID_ATTRIBUTE$1].includes(key)) {
2423
- return;
2424
- }
2425
- if (fp.isNil(attribute) || !isScalarAttribute(attribute)) {
2426
- throwInvalidKey({ key, path: path.attribute });
2427
- }
2428
- }, ctx),
2429
- // private fields
2430
- traverseQueryFields(visitor$2, ctx),
2431
- // password fields
2432
- traverseQueryFields(visitor$3, ctx)
2433
- )(fields2);
2508
+ );
2509
+ const defaultValidateFields = asyncCurry(async (ctx, fields2) => {
2510
+ return validateFields(ctx, fields2, FIELDS_TRAVERSALS);
2434
2511
  });
2435
- const defaultValidatePopulate = fp.curry((ctx, populate2) => {
2512
+ const POPULATE_TRAVERSALS = ["nonAttributesOperators", "private"];
2513
+ const validatePopulate = asyncCurry(
2514
+ async (ctx, populate2, includes) => {
2515
+ if (!ctx.schema) {
2516
+ throw new Error("Missing schema in defaultValidatePopulate");
2517
+ }
2518
+ const functionsToApply = [];
2519
+ functionsToApply.push(
2520
+ traverseQueryPopulate(async ({ key, path, value, schema, attribute, getModel }, { set }) => {
2521
+ if (attribute) {
2522
+ const isPopulatableAttribute = ["relation", "dynamiczone", "component", "media"].includes(
2523
+ attribute.type
2524
+ );
2525
+ if (!isPopulatableAttribute) {
2526
+ throwInvalidKey({ key, path: path.raw });
2527
+ }
2528
+ return;
2529
+ }
2530
+ if (key === "on") {
2531
+ if (!fp.isObject(value)) {
2532
+ return throwInvalidKey({ key, path: path.raw });
2533
+ }
2534
+ const targets = Object.keys(value);
2535
+ for (const target of targets) {
2536
+ const model = getModel(target);
2537
+ if (!model) {
2538
+ throwInvalidKey({ key: target, path: `${path.raw}.${target}` });
2539
+ }
2540
+ }
2541
+ return;
2542
+ }
2543
+ if (key === "" && value === "*") {
2544
+ return;
2545
+ }
2546
+ if (key === "count") {
2547
+ try {
2548
+ parseType({ type: "boolean", value });
2549
+ return;
2550
+ } catch {
2551
+ throwInvalidKey({ key, path: path.attribute });
2552
+ }
2553
+ }
2554
+ try {
2555
+ parseType({ type: "boolean", value: key });
2556
+ return;
2557
+ } catch {
2558
+ }
2559
+ if (key === "sort") {
2560
+ set(
2561
+ key,
2562
+ await validateSort(
2563
+ {
2564
+ schema,
2565
+ getModel
2566
+ },
2567
+ value,
2568
+ // pass the sort value
2569
+ includes?.sort || SORT_TRAVERSALS
2570
+ )
2571
+ );
2572
+ return;
2573
+ }
2574
+ if (key === "filters") {
2575
+ set(
2576
+ key,
2577
+ await validateFilters(
2578
+ {
2579
+ schema,
2580
+ getModel
2581
+ },
2582
+ value,
2583
+ // pass the filters value
2584
+ includes?.filters || FILTER_TRAVERSALS
2585
+ )
2586
+ );
2587
+ return;
2588
+ }
2589
+ if (key === "fields") {
2590
+ set(
2591
+ key,
2592
+ await validateFields(
2593
+ {
2594
+ schema,
2595
+ getModel
2596
+ },
2597
+ value,
2598
+ // pass the fields value
2599
+ includes?.fields || FIELDS_TRAVERSALS
2600
+ )
2601
+ );
2602
+ return;
2603
+ }
2604
+ if (key === "populate") {
2605
+ set(
2606
+ key,
2607
+ await validatePopulate(
2608
+ {
2609
+ schema,
2610
+ getModel
2611
+ },
2612
+ value,
2613
+ // pass the nested populate value
2614
+ includes
2615
+ // pass down the same includes object
2616
+ )
2617
+ );
2618
+ return;
2619
+ }
2620
+ if (includes?.populate?.includes("nonAttributesOperators")) {
2621
+ throwInvalidKey({ key, path: path.attribute });
2622
+ }
2623
+ }, ctx)
2624
+ );
2625
+ if (includes?.populate?.includes("private")) {
2626
+ functionsToApply.push(traverseQueryPopulate(visitor$2, ctx));
2627
+ }
2628
+ if (functionsToApply.length === 0) {
2629
+ return populate2;
2630
+ }
2631
+ return pipe(...functionsToApply)(populate2);
2632
+ }
2633
+ );
2634
+ const defaultValidatePopulate = asyncCurry(async (ctx, populate2) => {
2436
2635
  if (!ctx.schema) {
2437
2636
  throw new Error("Missing schema in defaultValidatePopulate");
2438
2637
  }
2439
- return pipe(
2440
- traverseQueryPopulate(async ({ key, value, schema, attribute, getModel }, { set }) => {
2441
- if (attribute) {
2442
- return;
2443
- }
2444
- if (key === "sort") {
2445
- set(
2446
- key,
2447
- await defaultValidateSort(
2448
- {
2449
- schema,
2450
- getModel
2451
- },
2452
- value
2453
- )
2454
- );
2455
- }
2456
- if (key === "filters") {
2457
- set(
2458
- key,
2459
- await defaultValidateFilters(
2460
- {
2461
- schema,
2462
- getModel
2463
- },
2464
- value
2465
- )
2466
- );
2467
- }
2468
- if (key === "fields") {
2469
- set(
2470
- key,
2471
- await defaultValidateFields(
2472
- {
2473
- schema,
2474
- getModel
2475
- },
2476
- value
2477
- )
2478
- );
2479
- }
2480
- if (key === "populate") {
2481
- set(
2482
- key,
2483
- await defaultValidatePopulate(
2484
- {
2485
- schema,
2486
- getModel
2487
- },
2488
- value
2489
- )
2490
- );
2491
- }
2492
- }, ctx),
2493
- // Remove private fields
2494
- traverseQueryPopulate(visitor$2, ctx)
2495
- )(populate2);
2638
+ return validatePopulate(ctx, populate2, {
2639
+ filters: FILTER_TRAVERSALS,
2640
+ sort: SORT_TRAVERSALS,
2641
+ fields: FIELDS_TRAVERSALS,
2642
+ populate: POPULATE_TRAVERSALS
2643
+ });
2496
2644
  });
2497
2645
  const validators = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2498
2646
  __proto__: null,
2647
+ FIELDS_TRAVERSALS,
2648
+ FILTER_TRAVERSALS,
2649
+ POPULATE_TRAVERSALS,
2650
+ SORT_TRAVERSALS,
2499
2651
  defaultValidateFields,
2500
2652
  defaultValidateFilters,
2501
2653
  defaultValidatePopulate,
2502
2654
  defaultValidateSort,
2503
- throwPasswords
2655
+ throwPasswords,
2656
+ validateFields,
2657
+ validateFilters,
2658
+ validatePopulate,
2659
+ validateSort
2504
2660
  }, Symbol.toStringTag, { value: "Module" }));
2505
2661
  const { ID_ATTRIBUTE, DOC_ID_ATTRIBUTE } = constants$1;
2506
2662
  const createAPIValidators = (opts) => {
@@ -2555,24 +2711,24 @@ const createAPIValidators = (opts) => {
2555
2711
  }
2556
2712
  const { filters: filters2, sort: sort2, fields: fields2, populate: populate2 } = query;
2557
2713
  if (filters2) {
2558
- await validateFilters(filters2, schema, { auth });
2714
+ await validateFilters2(filters2, schema, { auth });
2559
2715
  }
2560
2716
  if (sort2) {
2561
- await validateSort(sort2, schema, { auth });
2717
+ await validateSort2(sort2, schema, { auth });
2562
2718
  }
2563
2719
  if (fields2) {
2564
- await validateFields(fields2, schema);
2720
+ await validateFields2(fields2, schema);
2565
2721
  }
2566
2722
  if (populate2 && populate2 !== "*") {
2567
- await validatePopulate(populate2, schema);
2723
+ await validatePopulate2(populate2, schema);
2568
2724
  }
2569
2725
  };
2570
- const validateFilters = async (filters2, schema, { auth } = {}) => {
2726
+ const validateFilters2 = async (filters2, schema, { auth } = {}) => {
2571
2727
  if (!schema) {
2572
2728
  throw new Error("Missing schema in validateFilters");
2573
2729
  }
2574
2730
  if (fp.isArray(filters2)) {
2575
- await Promise.all(filters2.map((filter) => validateFilters(filter, schema, { auth })));
2731
+ await Promise.all(filters2.map((filter) => validateFilters2(filter, schema, { auth })));
2576
2732
  return;
2577
2733
  }
2578
2734
  const transforms = [defaultValidateFilters({ schema, getModel })];
@@ -2594,7 +2750,7 @@ const createAPIValidators = (opts) => {
2594
2750
  throw e;
2595
2751
  }
2596
2752
  };
2597
- const validateSort = async (sort2, schema, { auth } = {}) => {
2753
+ const validateSort2 = async (sort2, schema, { auth } = {}) => {
2598
2754
  if (!schema) {
2599
2755
  throw new Error("Missing schema in validateSort");
2600
2756
  }
@@ -2617,7 +2773,7 @@ const createAPIValidators = (opts) => {
2617
2773
  throw e;
2618
2774
  }
2619
2775
  };
2620
- const validateFields = async (fields2, schema) => {
2776
+ const validateFields2 = async (fields2, schema) => {
2621
2777
  if (!schema) {
2622
2778
  throw new Error("Missing schema in validateFields");
2623
2779
  }
@@ -2632,7 +2788,7 @@ const createAPIValidators = (opts) => {
2632
2788
  throw e;
2633
2789
  }
2634
2790
  };
2635
- const validatePopulate = async (populate2, schema, { auth } = {}) => {
2791
+ const validatePopulate2 = async (populate2, schema, { auth } = {}) => {
2636
2792
  if (!schema) {
2637
2793
  throw new Error("Missing schema in sanitizePopulate");
2638
2794
  }
@@ -2658,10 +2814,10 @@ const createAPIValidators = (opts) => {
2658
2814
  return {
2659
2815
  input: validateInput,
2660
2816
  query: validateQuery,
2661
- filters: validateFilters,
2662
- sort: validateSort,
2663
- fields: validateFields,
2664
- populate: validatePopulate
2817
+ filters: validateFilters2,
2818
+ sort: validateSort2,
2819
+ fields: validateFields2,
2820
+ populate: validatePopulate2
2665
2821
  };
2666
2822
  };
2667
2823
  const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({