@strapi/utils 5.0.0-rc.11 → 5.0.0-rc.13

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
@@ -1857,10 +1857,17 @@ const populate = traverseFactory().intercept(isStringArray$1, async (visitor2, o
1857
1857
  }
1858
1858
  });
1859
1859
  const traverseQueryPopulate = curry(populate.traverse);
1860
- const isStringArray = (value) => isArray(value) && value.every(isString);
1860
+ const isStringArray = (value) => {
1861
+ return isArray(value) && value.every(isString);
1862
+ };
1861
1863
  const fields = traverseFactory().intercept(isStringArray, async (visitor2, options, fields2, { recurse }) => {
1862
1864
  return Promise.all(fields2.map((field) => recurse(visitor2, options, field)));
1863
- }).intercept((value) => eq("*", value), constant("*")).parse(isString, () => ({
1865
+ }).intercept(
1866
+ (value) => isString(value) && value.includes(","),
1867
+ (visitor2, options, fields2, { recurse }) => {
1868
+ return Promise.all(fields2.split(",").map((field) => recurse(visitor2, options, field)));
1869
+ }
1870
+ ).intercept((value) => eq("*", value), constant("*")).parse(isString, () => ({
1864
1871
  transform: trim,
1865
1872
  remove(key, data) {
1866
1873
  return data === key ? void 0 : data;
@@ -2161,6 +2168,15 @@ const throwInvalidKey = ({ key, path }) => {
2161
2168
  path
2162
2169
  });
2163
2170
  };
2171
+ const asyncCurry = (fn) => {
2172
+ const curried = (...args) => {
2173
+ if (args.length >= fn.length) {
2174
+ return fn(...args);
2175
+ }
2176
+ return (...moreArgs) => curried(...args, ...moreArgs);
2177
+ };
2178
+ return curried;
2179
+ };
2164
2180
  const visitor$3 = ({ key, attribute, path }) => {
2165
2181
  if (attribute?.type === "password") {
2166
2182
  throwInvalidKey({ key, path: path.attribute });
@@ -2326,154 +2342,294 @@ const throwPasswords = (ctx) => async (entity) => {
2326
2342
  }
2327
2343
  return traverseEntity$1(visitor$3, ctx, entity);
2328
2344
  };
2329
- const defaultValidateFilters = curry((ctx, filters2) => {
2330
- if (!ctx.schema) {
2331
- throw new Error("Missing schema in defaultValidateFilters");
2345
+ const FILTER_TRAVERSALS = [
2346
+ "nonAttributesOperators",
2347
+ "dynamicZones",
2348
+ "morphRelations",
2349
+ "passwords",
2350
+ "private"
2351
+ ];
2352
+ const validateFilters = asyncCurry(
2353
+ async (ctx, filters2, include) => {
2354
+ if (!ctx.schema) {
2355
+ throw new Error("Missing schema in defaultValidateFilters");
2356
+ }
2357
+ const functionsToApply = [];
2358
+ if (include.includes("nonAttributesOperators")) {
2359
+ functionsToApply.push(
2360
+ traverseQueryFilters(({ key, attribute, path }) => {
2361
+ if ([ID_ATTRIBUTE$1, DOC_ID_ATTRIBUTE$1].includes(key)) {
2362
+ return;
2363
+ }
2364
+ const isAttribute = !!attribute;
2365
+ if (!isAttribute && !isOperator(key)) {
2366
+ throwInvalidKey({ key, path: path.attribute });
2367
+ }
2368
+ }, ctx)
2369
+ );
2370
+ }
2371
+ if (include.includes("dynamicZones")) {
2372
+ functionsToApply.push(traverseQueryFilters(visitor, ctx));
2373
+ }
2374
+ if (include.includes("morphRelations")) {
2375
+ functionsToApply.push(traverseQueryFilters(visitor$1, ctx));
2376
+ }
2377
+ if (include.includes("passwords")) {
2378
+ functionsToApply.push(traverseQueryFilters(visitor$3, ctx));
2379
+ }
2380
+ if (include.includes("private")) {
2381
+ functionsToApply.push(traverseQueryFilters(visitor$2, ctx));
2382
+ }
2383
+ if (functionsToApply.length === 0) {
2384
+ return filters2;
2385
+ }
2386
+ return pipe(...functionsToApply)(filters2);
2332
2387
  }
2333
- return pipe(
2334
- // keys that are not attributes or valid operators
2335
- traverseQueryFilters(({ key, attribute, path }) => {
2336
- if ([ID_ATTRIBUTE$1, DOC_ID_ATTRIBUTE$1].includes(key)) {
2337
- return;
2338
- }
2339
- const isAttribute = !!attribute;
2340
- if (!isAttribute && !isOperator(key)) {
2341
- throwInvalidKey({ key, path: path.attribute });
2342
- }
2343
- }, ctx),
2344
- // dynamic zones from filters
2345
- traverseQueryFilters(visitor, ctx),
2346
- // morphTo relations from filters; because you can't have deep filtering on morph relations
2347
- traverseQueryFilters(visitor$1, ctx),
2348
- // passwords from filters
2349
- traverseQueryFilters(visitor$3, ctx),
2350
- // private from filters
2351
- traverseQueryFilters(visitor$2, ctx)
2352
- // we allow empty objects to validate and only sanitize them out, so that users may write "lazy" queries without checking their params exist
2353
- )(filters2);
2388
+ );
2389
+ const defaultValidateFilters = asyncCurry(async (ctx, filters2) => {
2390
+ return validateFilters(ctx, filters2, FILTER_TRAVERSALS);
2354
2391
  });
2355
- const defaultValidateSort = curry((ctx, sort2) => {
2356
- if (!ctx.schema) {
2357
- throw new Error("Missing schema in defaultValidateSort");
2392
+ const SORT_TRAVERSALS = [
2393
+ "nonAttributesOperators",
2394
+ "dynamicZones",
2395
+ "morphRelations",
2396
+ "passwords",
2397
+ "private",
2398
+ "nonScalarEmptyKeys"
2399
+ ];
2400
+ const validateSort = asyncCurry(
2401
+ async (ctx, sort2, include) => {
2402
+ if (!ctx.schema) {
2403
+ throw new Error("Missing schema in defaultValidateSort");
2404
+ }
2405
+ const functionsToApply = [];
2406
+ if (include.includes("nonAttributesOperators")) {
2407
+ functionsToApply.push(
2408
+ traverseQuerySort(({ key, attribute, path }) => {
2409
+ if ([ID_ATTRIBUTE$1, DOC_ID_ATTRIBUTE$1].includes(key)) {
2410
+ return;
2411
+ }
2412
+ if (!attribute) {
2413
+ throwInvalidKey({ key, path: path.attribute });
2414
+ }
2415
+ }, ctx)
2416
+ );
2417
+ }
2418
+ if (include.includes("dynamicZones")) {
2419
+ functionsToApply.push(traverseQuerySort(visitor, ctx));
2420
+ }
2421
+ if (include.includes("morphRelations")) {
2422
+ functionsToApply.push(traverseQuerySort(visitor$1, ctx));
2423
+ }
2424
+ if (include.includes("passwords")) {
2425
+ functionsToApply.push(traverseQuerySort(visitor$3, ctx));
2426
+ }
2427
+ if (include.includes("private")) {
2428
+ functionsToApply.push(traverseQuerySort(visitor$2, ctx));
2429
+ }
2430
+ if (include.includes("nonScalarEmptyKeys")) {
2431
+ functionsToApply.push(
2432
+ traverseQuerySort(({ key, attribute, value, path }) => {
2433
+ if ([ID_ATTRIBUTE$1, DOC_ID_ATTRIBUTE$1].includes(key)) {
2434
+ return;
2435
+ }
2436
+ if (!isScalarAttribute(attribute) && isEmpty(value)) {
2437
+ throwInvalidKey({ key, path: path.attribute });
2438
+ }
2439
+ }, ctx)
2440
+ );
2441
+ }
2442
+ if (functionsToApply.length === 0) {
2443
+ return sort2;
2444
+ }
2445
+ return pipe(...functionsToApply)(sort2);
2358
2446
  }
2359
- return pipe(
2360
- // non attribute keys
2361
- traverseQuerySort(({ key, attribute, path }) => {
2362
- if ([ID_ATTRIBUTE$1, DOC_ID_ATTRIBUTE$1].includes(key)) {
2363
- return;
2364
- }
2365
- if (!attribute) {
2366
- throwInvalidKey({ key, path: path.attribute });
2367
- }
2368
- }, ctx),
2369
- // dynamic zones from sort
2370
- traverseQuerySort(visitor, ctx),
2371
- // morphTo relations from sort
2372
- traverseQuerySort(visitor$1, ctx),
2373
- // private from sort
2374
- traverseQuerySort(visitor$2, ctx),
2375
- // passwords from filters
2376
- traverseQuerySort(visitor$3, ctx),
2377
- // keys for empty non-scalar values
2378
- traverseQuerySort(({ key, attribute, value, path }) => {
2379
- if ([ID_ATTRIBUTE$1, DOC_ID_ATTRIBUTE$1].includes(key)) {
2380
- return;
2381
- }
2382
- if (!isScalarAttribute(attribute) && isEmpty(value)) {
2383
- throwInvalidKey({ key, path: path.attribute });
2384
- }
2385
- }, ctx)
2386
- )(sort2);
2447
+ );
2448
+ const defaultValidateSort = asyncCurry(async (ctx, sort2) => {
2449
+ return validateSort(ctx, sort2, SORT_TRAVERSALS);
2387
2450
  });
2388
- const defaultValidateFields = curry((ctx, fields2) => {
2389
- if (!ctx.schema) {
2390
- throw new Error("Missing schema in defaultValidateFields");
2451
+ const FIELDS_TRAVERSALS = ["scalarAttributes", "privateFields", "passwordFields"];
2452
+ const validateFields = asyncCurry(
2453
+ async (ctx, fields2, include) => {
2454
+ if (!ctx.schema) {
2455
+ throw new Error("Missing schema in defaultValidateFields");
2456
+ }
2457
+ const functionsToApply = [];
2458
+ if (include.includes("scalarAttributes")) {
2459
+ functionsToApply.push(
2460
+ traverseQueryFields(({ key, attribute, path }) => {
2461
+ if ([ID_ATTRIBUTE$1, DOC_ID_ATTRIBUTE$1].includes(key)) {
2462
+ return;
2463
+ }
2464
+ if (isNil(attribute) || !isScalarAttribute(attribute)) {
2465
+ throwInvalidKey({ key, path: path.attribute });
2466
+ }
2467
+ }, ctx)
2468
+ );
2469
+ }
2470
+ if (include.includes("privateFields")) {
2471
+ functionsToApply.push(traverseQueryFields(visitor$2, ctx));
2472
+ }
2473
+ if (include.includes("passwordFields")) {
2474
+ functionsToApply.push(traverseQueryFields(visitor$3, ctx));
2475
+ }
2476
+ if (functionsToApply.length === 0) {
2477
+ return fields2;
2478
+ }
2479
+ return pipe(...functionsToApply)(fields2);
2391
2480
  }
2392
- return pipe(
2393
- // Only allow scalar attributes
2394
- traverseQueryFields(({ key, attribute, path }) => {
2395
- if ([ID_ATTRIBUTE$1, DOC_ID_ATTRIBUTE$1].includes(key)) {
2396
- return;
2397
- }
2398
- if (isNil(attribute) || !isScalarAttribute(attribute)) {
2399
- throwInvalidKey({ key, path: path.attribute });
2400
- }
2401
- }, ctx),
2402
- // private fields
2403
- traverseQueryFields(visitor$2, ctx),
2404
- // password fields
2405
- traverseQueryFields(visitor$3, ctx)
2406
- )(fields2);
2481
+ );
2482
+ const defaultValidateFields = asyncCurry(async (ctx, fields2) => {
2483
+ return validateFields(ctx, fields2, FIELDS_TRAVERSALS);
2407
2484
  });
2408
- const defaultValidatePopulate = curry((ctx, populate2) => {
2485
+ const POPULATE_TRAVERSALS = ["nonAttributesOperators", "private"];
2486
+ const validatePopulate = asyncCurry(
2487
+ async (ctx, populate2, includes) => {
2488
+ if (!ctx.schema) {
2489
+ throw new Error("Missing schema in defaultValidatePopulate");
2490
+ }
2491
+ const functionsToApply = [];
2492
+ functionsToApply.push(
2493
+ traverseQueryPopulate(async ({ key, path, value, schema, attribute, getModel }, { set }) => {
2494
+ if (attribute) {
2495
+ const isPopulatableAttribute = ["relation", "dynamiczone", "component", "media"].includes(
2496
+ attribute.type
2497
+ );
2498
+ if (!isPopulatableAttribute) {
2499
+ throwInvalidKey({ key, path: path.raw });
2500
+ }
2501
+ return;
2502
+ }
2503
+ if (key === "on") {
2504
+ if (!isObject(value)) {
2505
+ return throwInvalidKey({ key, path: path.raw });
2506
+ }
2507
+ const targets = Object.keys(value);
2508
+ for (const target of targets) {
2509
+ const model = getModel(target);
2510
+ if (!model) {
2511
+ throwInvalidKey({ key: target, path: `${path.raw}.${target}` });
2512
+ }
2513
+ }
2514
+ return;
2515
+ }
2516
+ if (key === "" && value === "*") {
2517
+ return;
2518
+ }
2519
+ if (key === "count") {
2520
+ try {
2521
+ parseType({ type: "boolean", value });
2522
+ return;
2523
+ } catch {
2524
+ throwInvalidKey({ key, path: path.attribute });
2525
+ }
2526
+ }
2527
+ try {
2528
+ parseType({ type: "boolean", value: key });
2529
+ return;
2530
+ } catch {
2531
+ }
2532
+ if (key === "sort") {
2533
+ set(
2534
+ key,
2535
+ await validateSort(
2536
+ {
2537
+ schema,
2538
+ getModel
2539
+ },
2540
+ value,
2541
+ // pass the sort value
2542
+ includes?.sort || SORT_TRAVERSALS
2543
+ )
2544
+ );
2545
+ return;
2546
+ }
2547
+ if (key === "filters") {
2548
+ set(
2549
+ key,
2550
+ await validateFilters(
2551
+ {
2552
+ schema,
2553
+ getModel
2554
+ },
2555
+ value,
2556
+ // pass the filters value
2557
+ includes?.filters || FILTER_TRAVERSALS
2558
+ )
2559
+ );
2560
+ return;
2561
+ }
2562
+ if (key === "fields") {
2563
+ set(
2564
+ key,
2565
+ await validateFields(
2566
+ {
2567
+ schema,
2568
+ getModel
2569
+ },
2570
+ value,
2571
+ // pass the fields value
2572
+ includes?.fields || FIELDS_TRAVERSALS
2573
+ )
2574
+ );
2575
+ return;
2576
+ }
2577
+ if (key === "populate") {
2578
+ set(
2579
+ key,
2580
+ await validatePopulate(
2581
+ {
2582
+ schema,
2583
+ getModel
2584
+ },
2585
+ value,
2586
+ // pass the nested populate value
2587
+ includes
2588
+ // pass down the same includes object
2589
+ )
2590
+ );
2591
+ return;
2592
+ }
2593
+ if (includes?.populate?.includes("nonAttributesOperators")) {
2594
+ throwInvalidKey({ key, path: path.attribute });
2595
+ }
2596
+ }, ctx)
2597
+ );
2598
+ if (includes?.populate?.includes("private")) {
2599
+ functionsToApply.push(traverseQueryPopulate(visitor$2, ctx));
2600
+ }
2601
+ if (functionsToApply.length === 0) {
2602
+ return populate2;
2603
+ }
2604
+ return pipe(...functionsToApply)(populate2);
2605
+ }
2606
+ );
2607
+ const defaultValidatePopulate = asyncCurry(async (ctx, populate2) => {
2409
2608
  if (!ctx.schema) {
2410
2609
  throw new Error("Missing schema in defaultValidatePopulate");
2411
2610
  }
2412
- return pipe(
2413
- traverseQueryPopulate(async ({ key, value, schema, attribute, getModel }, { set }) => {
2414
- if (attribute) {
2415
- return;
2416
- }
2417
- if (key === "sort") {
2418
- set(
2419
- key,
2420
- await defaultValidateSort(
2421
- {
2422
- schema,
2423
- getModel
2424
- },
2425
- value
2426
- )
2427
- );
2428
- }
2429
- if (key === "filters") {
2430
- set(
2431
- key,
2432
- await defaultValidateFilters(
2433
- {
2434
- schema,
2435
- getModel
2436
- },
2437
- value
2438
- )
2439
- );
2440
- }
2441
- if (key === "fields") {
2442
- set(
2443
- key,
2444
- await defaultValidateFields(
2445
- {
2446
- schema,
2447
- getModel
2448
- },
2449
- value
2450
- )
2451
- );
2452
- }
2453
- if (key === "populate") {
2454
- set(
2455
- key,
2456
- await defaultValidatePopulate(
2457
- {
2458
- schema,
2459
- getModel
2460
- },
2461
- value
2462
- )
2463
- );
2464
- }
2465
- }, ctx),
2466
- // Remove private fields
2467
- traverseQueryPopulate(visitor$2, ctx)
2468
- )(populate2);
2611
+ return validatePopulate(ctx, populate2, {
2612
+ filters: FILTER_TRAVERSALS,
2613
+ sort: SORT_TRAVERSALS,
2614
+ fields: FIELDS_TRAVERSALS,
2615
+ populate: POPULATE_TRAVERSALS
2616
+ });
2469
2617
  });
2470
2618
  const validators = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2471
2619
  __proto__: null,
2620
+ FIELDS_TRAVERSALS,
2621
+ FILTER_TRAVERSALS,
2622
+ POPULATE_TRAVERSALS,
2623
+ SORT_TRAVERSALS,
2472
2624
  defaultValidateFields,
2473
2625
  defaultValidateFilters,
2474
2626
  defaultValidatePopulate,
2475
2627
  defaultValidateSort,
2476
- throwPasswords
2628
+ throwPasswords,
2629
+ validateFields,
2630
+ validateFilters,
2631
+ validatePopulate,
2632
+ validateSort
2477
2633
  }, Symbol.toStringTag, { value: "Module" }));
2478
2634
  const { ID_ATTRIBUTE, DOC_ID_ATTRIBUTE } = constants$1;
2479
2635
  const createAPIValidators = (opts) => {
@@ -2528,24 +2684,24 @@ const createAPIValidators = (opts) => {
2528
2684
  }
2529
2685
  const { filters: filters2, sort: sort2, fields: fields2, populate: populate2 } = query;
2530
2686
  if (filters2) {
2531
- await validateFilters(filters2, schema, { auth });
2687
+ await validateFilters2(filters2, schema, { auth });
2532
2688
  }
2533
2689
  if (sort2) {
2534
- await validateSort(sort2, schema, { auth });
2690
+ await validateSort2(sort2, schema, { auth });
2535
2691
  }
2536
2692
  if (fields2) {
2537
- await validateFields(fields2, schema);
2693
+ await validateFields2(fields2, schema);
2538
2694
  }
2539
2695
  if (populate2 && populate2 !== "*") {
2540
- await validatePopulate(populate2, schema);
2696
+ await validatePopulate2(populate2, schema);
2541
2697
  }
2542
2698
  };
2543
- const validateFilters = async (filters2, schema, { auth } = {}) => {
2699
+ const validateFilters2 = async (filters2, schema, { auth } = {}) => {
2544
2700
  if (!schema) {
2545
2701
  throw new Error("Missing schema in validateFilters");
2546
2702
  }
2547
2703
  if (isArray(filters2)) {
2548
- await Promise.all(filters2.map((filter) => validateFilters(filter, schema, { auth })));
2704
+ await Promise.all(filters2.map((filter) => validateFilters2(filter, schema, { auth })));
2549
2705
  return;
2550
2706
  }
2551
2707
  const transforms = [defaultValidateFilters({ schema, getModel })];
@@ -2567,7 +2723,7 @@ const createAPIValidators = (opts) => {
2567
2723
  throw e;
2568
2724
  }
2569
2725
  };
2570
- const validateSort = async (sort2, schema, { auth } = {}) => {
2726
+ const validateSort2 = async (sort2, schema, { auth } = {}) => {
2571
2727
  if (!schema) {
2572
2728
  throw new Error("Missing schema in validateSort");
2573
2729
  }
@@ -2590,7 +2746,7 @@ const createAPIValidators = (opts) => {
2590
2746
  throw e;
2591
2747
  }
2592
2748
  };
2593
- const validateFields = async (fields2, schema) => {
2749
+ const validateFields2 = async (fields2, schema) => {
2594
2750
  if (!schema) {
2595
2751
  throw new Error("Missing schema in validateFields");
2596
2752
  }
@@ -2605,7 +2761,7 @@ const createAPIValidators = (opts) => {
2605
2761
  throw e;
2606
2762
  }
2607
2763
  };
2608
- const validatePopulate = async (populate2, schema, { auth } = {}) => {
2764
+ const validatePopulate2 = async (populate2, schema, { auth } = {}) => {
2609
2765
  if (!schema) {
2610
2766
  throw new Error("Missing schema in sanitizePopulate");
2611
2767
  }
@@ -2631,10 +2787,10 @@ const createAPIValidators = (opts) => {
2631
2787
  return {
2632
2788
  input: validateInput,
2633
2789
  query: validateQuery,
2634
- filters: validateFilters,
2635
- sort: validateSort,
2636
- fields: validateFields,
2637
- populate: validatePopulate
2790
+ filters: validateFilters2,
2791
+ sort: validateSort2,
2792
+ fields: validateFields2,
2793
+ populate: validatePopulate2
2638
2794
  };
2639
2795
  };
2640
2796
  const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({