@strapi/utils 5.0.0-rc.2 → 5.0.0-rc.21

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
@@ -544,16 +544,6 @@ const providerFactory = (options = {}) => {
544
544
  get(key) {
545
545
  return state.registry.get(key);
546
546
  },
547
- getWhere(filters2 = {}) {
548
- const items = this.values();
549
- const filtersEntries = Object.entries(filters2);
550
- if (filtersEntries.length === 0) {
551
- return items;
552
- }
553
- return items.filter((item) => {
554
- return filtersEntries.every(([key, value]) => item[key] === value);
555
- });
556
- },
557
547
  values() {
558
548
  return Array.from(state.registry.values());
559
549
  },
@@ -1894,10 +1884,17 @@ const populate = traverseFactory().intercept(isStringArray$1, async (visitor2, o
1894
1884
  }
1895
1885
  });
1896
1886
  const traverseQueryPopulate = fp.curry(populate.traverse);
1897
- const isStringArray = (value) => fp.isArray(value) && value.every(fp.isString);
1887
+ const isStringArray = (value) => {
1888
+ return fp.isArray(value) && value.every(fp.isString);
1889
+ };
1898
1890
  const fields = traverseFactory().intercept(isStringArray, async (visitor2, options, fields2, { recurse }) => {
1899
1891
  return Promise.all(fields2.map((field) => recurse(visitor2, options, field)));
1900
- }).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, () => ({
1901
1898
  transform: fp.trim,
1902
1899
  remove(key, data) {
1903
1900
  return data === key ? void 0 : data;
@@ -2198,6 +2195,15 @@ const throwInvalidKey = ({ key, path }) => {
2198
2195
  path
2199
2196
  });
2200
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
+ };
2201
2207
  const visitor$3 = ({ key, attribute, path }) => {
2202
2208
  if (attribute?.type === "password") {
2203
2209
  throwInvalidKey({ key, path: path.attribute });
@@ -2363,154 +2369,294 @@ const throwPasswords = (ctx) => async (entity) => {
2363
2369
  }
2364
2370
  return traverseEntity$1(visitor$3, ctx, entity);
2365
2371
  };
2366
- const defaultValidateFilters = fp.curry((ctx, filters2) => {
2367
- if (!ctx.schema) {
2368
- 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);
2369
2414
  }
2370
- return pipe(
2371
- // keys that are not attributes or valid operators
2372
- traverseQueryFilters(({ key, attribute, path }) => {
2373
- if ([ID_ATTRIBUTE$1, DOC_ID_ATTRIBUTE$1].includes(key)) {
2374
- return;
2375
- }
2376
- const isAttribute = !!attribute;
2377
- if (!isAttribute && !isOperator(key)) {
2378
- throwInvalidKey({ key, path: path.attribute });
2379
- }
2380
- }, ctx),
2381
- // dynamic zones from filters
2382
- traverseQueryFilters(visitor, ctx),
2383
- // morphTo relations from filters; because you can't have deep filtering on morph relations
2384
- traverseQueryFilters(visitor$1, ctx),
2385
- // passwords from filters
2386
- traverseQueryFilters(visitor$3, ctx),
2387
- // private from filters
2388
- traverseQueryFilters(visitor$2, ctx)
2389
- // we allow empty objects to validate and only sanitize them out, so that users may write "lazy" queries without checking their params exist
2390
- )(filters2);
2415
+ );
2416
+ const defaultValidateFilters = asyncCurry(async (ctx, filters2) => {
2417
+ return validateFilters(ctx, filters2, FILTER_TRAVERSALS);
2391
2418
  });
2392
- const defaultValidateSort = fp.curry((ctx, sort2) => {
2393
- if (!ctx.schema) {
2394
- 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);
2395
2473
  }
2396
- return pipe(
2397
- // non attribute keys
2398
- traverseQuerySort(({ key, attribute, path }) => {
2399
- if ([ID_ATTRIBUTE$1, DOC_ID_ATTRIBUTE$1].includes(key)) {
2400
- return;
2401
- }
2402
- if (!attribute) {
2403
- throwInvalidKey({ key, path: path.attribute });
2404
- }
2405
- }, ctx),
2406
- // dynamic zones from sort
2407
- traverseQuerySort(visitor, ctx),
2408
- // morphTo relations from sort
2409
- traverseQuerySort(visitor$1, ctx),
2410
- // private from sort
2411
- traverseQuerySort(visitor$2, ctx),
2412
- // passwords from filters
2413
- traverseQuerySort(visitor$3, ctx),
2414
- // keys for empty non-scalar values
2415
- traverseQuerySort(({ key, attribute, value, path }) => {
2416
- if ([ID_ATTRIBUTE$1, DOC_ID_ATTRIBUTE$1].includes(key)) {
2417
- return;
2418
- }
2419
- if (!isScalarAttribute(attribute) && fp.isEmpty(value)) {
2420
- throwInvalidKey({ key, path: path.attribute });
2421
- }
2422
- }, ctx)
2423
- )(sort2);
2474
+ );
2475
+ const defaultValidateSort = asyncCurry(async (ctx, sort2) => {
2476
+ return validateSort(ctx, sort2, SORT_TRAVERSALS);
2424
2477
  });
2425
- const defaultValidateFields = fp.curry((ctx, fields2) => {
2426
- if (!ctx.schema) {
2427
- 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);
2428
2507
  }
2429
- return pipe(
2430
- // Only allow scalar attributes
2431
- traverseQueryFields(({ key, attribute, path }) => {
2432
- if ([ID_ATTRIBUTE$1, DOC_ID_ATTRIBUTE$1].includes(key)) {
2433
- return;
2434
- }
2435
- if (fp.isNil(attribute) || !isScalarAttribute(attribute)) {
2436
- throwInvalidKey({ key, path: path.attribute });
2437
- }
2438
- }, ctx),
2439
- // private fields
2440
- traverseQueryFields(visitor$2, ctx),
2441
- // password fields
2442
- traverseQueryFields(visitor$3, ctx)
2443
- )(fields2);
2508
+ );
2509
+ const defaultValidateFields = asyncCurry(async (ctx, fields2) => {
2510
+ return validateFields(ctx, fields2, FIELDS_TRAVERSALS);
2444
2511
  });
2445
- 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) => {
2446
2635
  if (!ctx.schema) {
2447
2636
  throw new Error("Missing schema in defaultValidatePopulate");
2448
2637
  }
2449
- return pipe(
2450
- traverseQueryPopulate(async ({ key, value, schema, attribute, getModel }, { set }) => {
2451
- if (attribute) {
2452
- return;
2453
- }
2454
- if (key === "sort") {
2455
- set(
2456
- key,
2457
- await defaultValidateSort(
2458
- {
2459
- schema,
2460
- getModel
2461
- },
2462
- value
2463
- )
2464
- );
2465
- }
2466
- if (key === "filters") {
2467
- set(
2468
- key,
2469
- await defaultValidateFilters(
2470
- {
2471
- schema,
2472
- getModel
2473
- },
2474
- value
2475
- )
2476
- );
2477
- }
2478
- if (key === "fields") {
2479
- set(
2480
- key,
2481
- await defaultValidateFields(
2482
- {
2483
- schema,
2484
- getModel
2485
- },
2486
- value
2487
- )
2488
- );
2489
- }
2490
- if (key === "populate") {
2491
- set(
2492
- key,
2493
- await defaultValidatePopulate(
2494
- {
2495
- schema,
2496
- getModel
2497
- },
2498
- value
2499
- )
2500
- );
2501
- }
2502
- }, ctx),
2503
- // Remove private fields
2504
- traverseQueryPopulate(visitor$2, ctx)
2505
- )(populate2);
2638
+ return validatePopulate(ctx, populate2, {
2639
+ filters: FILTER_TRAVERSALS,
2640
+ sort: SORT_TRAVERSALS,
2641
+ fields: FIELDS_TRAVERSALS,
2642
+ populate: POPULATE_TRAVERSALS
2643
+ });
2506
2644
  });
2507
2645
  const validators = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2508
2646
  __proto__: null,
2647
+ FIELDS_TRAVERSALS,
2648
+ FILTER_TRAVERSALS,
2649
+ POPULATE_TRAVERSALS,
2650
+ SORT_TRAVERSALS,
2509
2651
  defaultValidateFields,
2510
2652
  defaultValidateFilters,
2511
2653
  defaultValidatePopulate,
2512
2654
  defaultValidateSort,
2513
- throwPasswords
2655
+ throwPasswords,
2656
+ validateFields,
2657
+ validateFilters,
2658
+ validatePopulate,
2659
+ validateSort
2514
2660
  }, Symbol.toStringTag, { value: "Module" }));
2515
2661
  const { ID_ATTRIBUTE, DOC_ID_ATTRIBUTE } = constants$1;
2516
2662
  const createAPIValidators = (opts) => {
@@ -2565,24 +2711,24 @@ const createAPIValidators = (opts) => {
2565
2711
  }
2566
2712
  const { filters: filters2, sort: sort2, fields: fields2, populate: populate2 } = query;
2567
2713
  if (filters2) {
2568
- await validateFilters(filters2, schema, { auth });
2714
+ await validateFilters2(filters2, schema, { auth });
2569
2715
  }
2570
2716
  if (sort2) {
2571
- await validateSort(sort2, schema, { auth });
2717
+ await validateSort2(sort2, schema, { auth });
2572
2718
  }
2573
2719
  if (fields2) {
2574
- await validateFields(fields2, schema);
2720
+ await validateFields2(fields2, schema);
2575
2721
  }
2576
2722
  if (populate2 && populate2 !== "*") {
2577
- await validatePopulate(populate2, schema);
2723
+ await validatePopulate2(populate2, schema);
2578
2724
  }
2579
2725
  };
2580
- const validateFilters = async (filters2, schema, { auth } = {}) => {
2726
+ const validateFilters2 = async (filters2, schema, { auth } = {}) => {
2581
2727
  if (!schema) {
2582
2728
  throw new Error("Missing schema in validateFilters");
2583
2729
  }
2584
2730
  if (fp.isArray(filters2)) {
2585
- await Promise.all(filters2.map((filter) => validateFilters(filter, schema, { auth })));
2731
+ await Promise.all(filters2.map((filter) => validateFilters2(filter, schema, { auth })));
2586
2732
  return;
2587
2733
  }
2588
2734
  const transforms = [defaultValidateFilters({ schema, getModel })];
@@ -2604,7 +2750,7 @@ const createAPIValidators = (opts) => {
2604
2750
  throw e;
2605
2751
  }
2606
2752
  };
2607
- const validateSort = async (sort2, schema, { auth } = {}) => {
2753
+ const validateSort2 = async (sort2, schema, { auth } = {}) => {
2608
2754
  if (!schema) {
2609
2755
  throw new Error("Missing schema in validateSort");
2610
2756
  }
@@ -2627,7 +2773,7 @@ const createAPIValidators = (opts) => {
2627
2773
  throw e;
2628
2774
  }
2629
2775
  };
2630
- const validateFields = async (fields2, schema) => {
2776
+ const validateFields2 = async (fields2, schema) => {
2631
2777
  if (!schema) {
2632
2778
  throw new Error("Missing schema in validateFields");
2633
2779
  }
@@ -2642,7 +2788,7 @@ const createAPIValidators = (opts) => {
2642
2788
  throw e;
2643
2789
  }
2644
2790
  };
2645
- const validatePopulate = async (populate2, schema, { auth } = {}) => {
2791
+ const validatePopulate2 = async (populate2, schema, { auth } = {}) => {
2646
2792
  if (!schema) {
2647
2793
  throw new Error("Missing schema in sanitizePopulate");
2648
2794
  }
@@ -2668,10 +2814,10 @@ const createAPIValidators = (opts) => {
2668
2814
  return {
2669
2815
  input: validateInput,
2670
2816
  query: validateQuery,
2671
- filters: validateFilters,
2672
- sort: validateSort,
2673
- fields: validateFields,
2674
- populate: validatePopulate
2817
+ filters: validateFilters2,
2818
+ sort: validateSort2,
2819
+ fields: validateFields2,
2820
+ populate: validatePopulate2
2675
2821
  };
2676
2822
  };
2677
2823
  const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({