Repository: dubzzz/fast-check
Pull request #2667 (2022-01-22 04:34:58)

⬆️ Bump nanoid from 3.1.23 to 3.2.0 in /.github/actions/deploy-netlify
Bumps [nanoid](https://github.com/ai/nanoid) from 3.1.23 to 3.2.0.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/ai/nanoid/blob/main/CHANGELOG.md">nanoid's changelog</a>.</em></p>
<blockquote>
<h1>Change Log</h1>
<p>This project adheres to <a href="http://semver.org/">Semantic Versioning</a>.</p>
<h2>3.2</h2>
<ul>
<li>Added <code>--size</code> and <code>--alphabet</code> arguments to binary (by Vitaly Baev).</li>
</ul>
<h2>3.1.32</h2>
<ul>
<li>Reduced <code>async</code> exports size (by Artyom Arutyunyan).</li>
<li>Moved from Jest to uvu (by Vitaly Baev).</li>
</ul>
<h2>3.1.31</h2>
<ul>
<li>Fixed collision vulnerability on object in <code>size</code> (by Artyom Arutyunyan).</li>
</ul>
<h2>3.1.30</h2>
<ul>
<li>Reduced size for project with <code>brotli</code> compression (by Anton Khlynovskiy).</li>
</ul>
<h2>3.1.29</h2>
<ul>
<li>Reduced npm package size.</li>
</ul>
<h2>3.1.28</h2>
<ul>
<li>Reduced npm package size.</li>
</ul>
<h2>3.1.27</h2>
<ul>
<li>Cleaned <code>dependencies</code> from development tools.</li>
</ul>
<h2>3.1.26</h2>
<ul>
<li>Improved performance (by Eitan Har-Shoshanim).</li>
<li>Reduced npm package size.</li>
</ul>
<h2>3.1.25</h2>
<ul>
<li>Fixed <code>browserify</code> support.</li>
</ul>
<h2>3.1.24</h2>
<ul>
<li>Fixed <code>browserify</code> support (by Artur Paikin).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/ai/nanoid/commit/23b136929a6d58f32e31b269534a3ce3f680a086"><code>23b1369</code></a> Release 3.2 version</li>
<li><a href="https://github.com/ai/nanoid/commit/967788efce880960512f969a56f8f22f3fc20bae"><code>967788e</code></a> Remove TS test tools</li>
<li><a href="https://github.com/ai/nanoid/commit/27eaa90cd207a7782bbcf17343092ae87dd62164"><code>27eaa90</code></a> Simplify new binary tool</li>
<li><a href="https://github.com/ai/nanoid/commit/a9d91239931dc77506381874826d297aee71d6ef"><code>a9d9123</code></a> Update dependencies</li>
<li><a href="https://github.com/ai/nanoid/commit/32b9bdaab1fbc28576b17de8516164ce0360f292"><code>32b9bda</code></a> Allows passing size or custom alphabet via cli as args (<a href="https://github-redirect.dependabot.com/ai/nanoid/issues/334">#334</a>)</li>
<li><a href="https://github.com/ai/nanoid/commit/246d5f87b6b34e23b5e401bdf3da1f80c810ac4c"><code>246d5f8</code></a> Update vite</li>
<li><a href="https://github.com/ai/nanoid/commit/afdf9c92b41427f35476fbe14b5af5d73dd7fbdb"><code>afdf9c9</code></a> doc: Fixed Typo (<a href="https://github-redirect.dependabot.com/ai/nanoid/issues/335">#335</a>)</li>
<li><a href="https://github.com/ai/nanoid/commit/90a446fef3ecaac78e5af2ea01025c4f40182e2b"><code>90a446f</code></a> Update benchmark results</li>
<li><a href="https://github.com/ai/nanoid/commit/8ba2319b579895cc1f9060b9946a44852f97c509"><code>8ba2319</code></a> bench: add <code>@​napi-rs/uuid</code> v4 (<a href="https://github-redirect.dependabot.com/ai/nanoid/issues/333">#333</a>)</li>
<li><a href="https://github.com/ai/nanoid/commit/f4257780ece488734a65c176e80c2fd8ab6aab8e"><code>f425778</code></a> Release 3.1.32 version</li>
<li>Additional commits viewable in <a href="https://github.com/ai/nanoid/compare/3.1.23...3.2.0">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=nanoid&package-manager=npm_and_yarn&previous-version=3.1.23&new-version=3.2.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/dubzzz/fast-check/network/alerts).

</details>

codesandbox[bot]: This pull request is automatically built and testable in [CodeSandbox](https://codesandbox.io).

  To see build info of the built libraries, click [here](https://ci.codesandbox.io/status/dubzzz/fast-check/pr/2667/builds/210693) or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 7cfade27d1992621622833f4c3c4d4d9fe3fc43b:

|Sandbox| Source |
|--|--|
|[Vanilla](https://codesandbox.io/s/vanilla-he16m)| Configuration |
|[fast-check-examples](https://codesandbox.io/s/fast-check-examples-rf2dl)| Configuration |

codecov[bot]: # [Codecov](https://codecov.io/gh/dubzzz/fast-check/pull/2667?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN) Report
> Merging [#2667](https://codecov.io/gh/dubzzz/fast-check/pull/2667?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN) (7cfade2) into [main](https://codecov.io/gh/dubzzz/fast-check/commit/b923ec63c736005b3a4db9782e52fab9305fecea?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN) (b923ec6) will **not change** coverage.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/dubzzz/fast-check/pull/2667/graphs/tree.svg?width=650&height=150&src=pr&token=fRi2fJN730&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN)](https://codecov.io/gh/dubzzz/fast-check/pull/2667?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN)

```diff
@@           Coverage Diff           @@
##             main    #2667   +/-   ##
=======================================
  Coverage   95.03%   95.03%           
=======================================
  Files         209      209           
  Lines        4832     4832           
  Branches      946      946           
=======================================
  Hits         4592     4592           
  Misses        236      236           
  Partials        4        4           
```

| Flag | Coverage Δ | |
|---|---|---|
| unit-tests | `95.03% <ø> (ø)` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN#carryforward-flags-in-the-pull-request-comment) to find out more.


------

[Continue to review full report at Codecov](https://codecov.io/gh/dubzzz/fast-check/pull/2667?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/dubzzz/fast-check/pull/2667?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN). Last update [b923ec6...7cfade2](https://codecov.io/gh/dubzzz/fast-check/pull/2667?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN).


Patch:
diff --git a/.github/actions/deploy-netlify/yarn.lock b/.github/actions/deploy-netlify/yarn.lock
index 77fbb179..89da7b5a 100644
--- a/.github/actions/deploy-netlify/yarn.lock
+++ b/.github/actions/deploy-netlify/yarn.lock
@@ -5637,9 +5637,9 @@ mute-stream@0.0.7:
   integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=
 
 nanoid@^3.1.23:
-  version "3.1.23"
-  resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81"
-  integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c"
+  integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==
 
 nanomatch@^1.2.9:
   version "1.2.13"
diff --git a/documentation/Arbitraries.md b/documentation/Arbitraries.md
index bf01551e..646bec4c 100644
--- a/documentation/Arbitraries.md
+++ b/documentation/Arbitraries.md
@@ -2380,7 +2380,7 @@ fc.genericTuple([fc.nat(), fc.string()])
 *&#8195;Signatures*
 
 - `fc.array(arb)`
-- `fc.array(arb, {minLength?, maxLength?})`
+- `fc.array(arb, {minLength?, maxLength?, size?})`
 - _`fc.array(arb, maxLength)`_ — _deprecated since v2.6.0 ([#992](https://github.com/dubzzz/fast-check/issues/992))_
 - _`fc.array(arb, minLength, maxLength)`_ — _deprecated since v2.6.0 ([#992](https://github.com/dubzzz/fast-check/issues/992))_
 
@@ -2389,6 +2389,7 @@ fc.genericTuple([fc.nat(), fc.string()])
 - `arb` — _arbitrary instance responsible to generate values_
 - `minLength?` — default: `0` — _minimal length (included)_
 - `maxLength?` — default: `0x7fffffff` [more](#size-explained) — _maximal length (included)_
+- `size?` — default: `undefined` [more](#size-explained) — _how large should the generated values be?_
 
 *&#8195;Usages*
 
@@ -2422,6 +2423,17 @@ fc.array(fc.nat(), {minLength: 5, maxLength: 7})
 // • [20,668325235,1112668370,7,8,847065979]
 // • [19,3,15,16,117940422,25]
 // • …
+
+fc.array(fc.nat(), {maxLength: 50, size: 'max'})
+// Note: By specifying size to max, we enforce the fact that we want generated values to have
+// between 0 and 50 items. In other words, we want to use the full range of specified lengths.
+// Examples of generated values:
+// • [4,26,725992281,7,1186699848]
+// • [1503239805,742382696,478977019,1206184056,992934701,1081616342,1979615602,100017132,1937766941,1785237624,89742033,1144227677,1828223609,1661385382,1964114158,526345403,1355567259,101888470,985865568,1687809116,147253818,1849736419,89389898,137520571,1171150892,127470621,827241914,1255338411]
+// • [1396368269,227325306,1918884399,1141338513,1861390920,1771550203,750875810,981796650,1210223397,1985219249,1479511918,479227607,1642390464,1556279791,979433247,1634278654,2044481643,1849523874,1519384141,987434773,1605111061,2138565492,1265703106,806958408,907237474,1655276397,1704888094,1830702455,1909917028,1307794976,1257188319,571159719]
+// • [20]
+// • [19,3,15]
+// • …
 ```
 </details>
 
@@ -3713,11 +3725,20 @@ Refer to [Race conditions detection](./RaceConditions.md) or [Detect race condit
 
 ### Size explained
 
-Whenever fast-check is called on an array-like arbitrary such as `fc.array` or `fc.string`, it is able to generate entries properly even if it does not receive any upper bound for the maximal length of the generated values.
+Since version 2.22.0, there is a distinction between constraints required by specifications and what will really be generated. When dealing with array-like arbitraries such as `fc.array` or `fc.string`, defining a constraint like `maxLength` can be seen as if you wrote "my algorithm is not supposed to handle arrays having more than X elements". It does not ask fast-check to generate arrays with X elements, but tells it that it could if needed or asked to.
 
-First, it sets `maxLength` to `0x7fffffff` — _the maximal allowed size for an array in JavaScript_.
+What really drives fast-check into generating large arrays is called `size`. At the level of an arbitrary it can be set to:
+- Relative size: `"-4"`, `"-3"`, `"-2"`, `"-1"`, `"="`, `"+1"`, `"+2"`, `"+3"` or `"+4"` — _offset the global setting `baseSize` by the passed offset_
+- Explicit size: `"xsmall"`, `"small"`, `"medium"`, `"large"` or `"xlarge"` — _use an explicit size_
+- Exact value: `"max"` — _generate entities having up-to `maxLength` items_
+- Automatic size: `undefined` — _if `maxLength` has not been specified or if the global setting `defaultSizeToMaxWhenMaxSpecified` is `false` (in v2 it defaults to `true` for backward compatibilty reasons) then `"="`, otherwise `"max"`_
 
-Then it sets an internal parameter called `maxGeneratedLength` to `2 * minLength + 10`. In other words, it does not generate entities having up to `0x7fffffff` but between entities having between `minLength` and `maxGeneratedLength` elements.
+Here is a quick overview of how we use the `size` parameter associated to a minimal length to compute the maximal length for the generated values:
+- `xsmall` — `min + (0.1 * min + 1)`
+- `small` (default) — `min + (1 * min + 10)`
+- `medium` — `min + (10 * min + 100)`
+- `large` — `min + (100 * min + 1000)`
+- `xlarge` — `min + (1000 * min + 10000)`
 
 ### Various links
 
diff --git a/src/arbitrary/_internals/helpers/MaxLengthFromMinLength.ts b/src/arbitrary/_internals/helpers/MaxLengthFromMinLength.ts
index f21e54f8..023d775b 100644
--- a/src/arbitrary/_internals/helpers/MaxLengthFromMinLength.ts
+++ b/src/arbitrary/_internals/helpers/MaxLengthFromMinLength.ts
@@ -12,9 +12,79 @@
 export const MaxLengthUpperBound = 0x7fffffff;
 
 /**
- * Compute `maxLength` based on `minLength`
+ * The size parameter defines how large the generated values could be.
+ *
+ * The default in fast-check is 'small' but it could be increased (resp. decreased)
+ * to ask arbitraries for larger (resp. smaller) values.
+ *
+ * @remarks Since 2.22.0
+ * @public
+ */
+export type Size = 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge';
+
+/**
+ * Superset of {@link Size} to override the default defined for size
+ * @remarks Since 2.22.0
+ * @public
+ */
+export type SizeForArbitrary = Size | 'max' | undefined;
+
+/**
+ * The default size used by fast-check
  * @internal
  */
-export function maxLengthFromMinLength(minLength: number): number {
-  return Math.min(2 * minLength + 10, MaxLengthUpperBound);
+export const DefaultSize: Size = 'small';
+
+/**
+ * Compute `maxLength` based on `minLength` and `size`
+ * @internal
+ */
+export function maxLengthFromMinLength(minLength: number, size: Size): number {
+  switch (size) {
+    case 'xsmall':
+      return Math.min(Math.floor(1.1 * minLength) + 1, 0x7fffffff); // min + (0.1 * min + 1)
+    case 'small':
+      return Math.min(2 * minLength + 10, 0x7fffffff); // min + (1 * min + 10)
+    case 'medium':
+      return Math.min(11 * minLength + 100, 0x7fffffff); // min + (10 * min + 100)
+    case 'large':
+      return Math.min(101 * minLength + 1000, 0x7fffffff); // min + (100 * min + 1000)
+    case 'xlarge':
+      return Math.min(1001 * minLength + 10000, 0x7fffffff); // min + (1000 * min + 10000)
+    default:
+      throw new Error(`Unable to compute lengths based on received size: ${size}`);
+  }
+}
+
+/**
+ * Compute `maxGeneratedLength` based on `minLength`, `maxLength` and `size`
+ * @param size - Size defined by the caller on the arbitrary
+ * @param minLength - Considered minimal length
+ * @param maxLength - Considered maximal length
+ * @param specifiedMaxLength - Whether or not the caller specified the max (true) or if it has been defaulted (false)
+ * @internal
+ */
+export function maxGeneratedLengthFromSizeForArbitrary(
+  size: SizeForArbitrary | undefined,
+  minLength: number,
+  maxLength: number,
+  specifiedMaxLength: boolean
+): number {
+  // TODO(size) - Rely on a global setting to get back the default size
+  // and the "must use maxLength whenever provided by the user"
+  const defaultSize = DefaultSize;
+  const defaultSizeToMaxWhenMaxSpecified = true;
+
+  // Resulting size is:
+  // - If size has been defined, we use it,
+  // - Otherwise (size=undefined), we default it to:
+  //   - If caller specified a maxLength and asked for defaultSizeToMaxWhenMaxSpecified, then 'max'
+  //   - Otherwise, defaultSize
+  const resultingSize =
+    size !== undefined ? size : specifiedMaxLength && defaultSizeToMaxWhenMaxSpecified ? 'max' : defaultSize;
+
+  if (resultingSize === 'max') {
+    return maxLength;
+  }
+  return maxLengthFromMinLength(minLength, resultingSize);
 }
diff --git a/src/arbitrary/array.ts b/src/arbitrary/array.ts
index f0f598f4..7c424f23 100644
--- a/src/arbitrary/array.ts
+++ b/src/arbitrary/array.ts
@@ -1,7 +1,12 @@
 import { Arbitrary } from '../check/arbitrary/definition/Arbitrary';
+import { NextArbitrary } from '../check/arbitrary/definition/NextArbitrary';
 import { convertFromNext, convertToNext } from '../check/arbitrary/definition/Converters';
 import { ArrayArbitrary } from './_internals/ArrayArbitrary';
-import { MaxLengthUpperBound, maxLengthFromMinLength } from './_internals/helpers/MaxLengthFromMinLength';
+import {
+  MaxLengthUpperBound,
+  SizeForArbitrary,
+  maxGeneratedLengthFromSizeForArbitrary,
+} from './_internals/helpers/MaxLengthFromMinLength';
 
 /**
  * Constraints to be applied on {@link array}
@@ -19,6 +24,29 @@ export interface ArrayConstraints {
    * @remarks Since 2.4.0
    */
   maxLength?: number;
+  /**
+   * Define how large the generated values should be (at max)
+   *
+   * When used in conjonction with `maxLength`, `size` will be used to define
+   * the upper bound of the generated array size while `maxLength` will be used
+   * to define and document the general maximal length allowed for this case.
+   *
+   * @remarks Since 2.22.0
+   */
+  size?: SizeForArbitrary;
+}
+
+/** @internal */
+function createArrayArbitrary<T>(
+  nextArb: NextArbitrary<T>,
+  size: SizeForArbitrary | undefined,
+  minLength: number,
+  maxLengthOrUnset: number | undefined
+): Arbitrary<T[]> {
+  const maxLength = maxLengthOrUnset !== undefined ? maxLengthOrUnset : MaxLengthUpperBound;
+  const specifiedMaxLength = maxLengthOrUnset !== undefined;
+  const maxGeneratedLength = maxGeneratedLengthFromSizeForArbitrary(size, minLength, maxLength, specifiedMaxLength);
+  return convertFromNext(new ArrayArbitrary<T>(nextArb, minLength, maxGeneratedLength, maxLength));
 }
 
 /**
@@ -71,26 +99,17 @@ function array<T>(arb: Arbitrary<T>, ...args: [] | [number] | [number, number] |
   const nextArb = convertToNext(arb);
   // fc.array(arb)
   if (args[0] === undefined) {
-    const maxGeneratedLength = maxLengthFromMinLength(0);
-    return convertFromNext(new ArrayArbitrary<T>(nextArb, 0, maxGeneratedLength, MaxLengthUpperBound));
+    return createArrayArbitrary(nextArb, undefined, 0, undefined); // no size, no maxLength
   }
   // fc.array(arb, constraints)
   if (typeof args[0] === 'object') {
-    const minLength = args[0].minLength || 0;
-    const specifiedMaxLength = args[0].maxLength;
-    const maxLength = specifiedMaxLength !== undefined ? specifiedMaxLength : MaxLengthUpperBound;
-    // TODO(size) - Make use of the (future) size argument to decide whether or not to apply maxLengthFromMinLength or take maxLength
-    const maxGeneratedLength =
-      specifiedMaxLength !== undefined ? specifiedMaxLength : maxLengthFromMinLength(minLength);
-    return convertFromNext(new ArrayArbitrary<T>(nextArb, minLength, maxGeneratedLength, maxLength));
+    return createArrayArbitrary(nextArb, args[0].size, args[0].minLength || 0, args[0].maxLength);
   }
   // fc.array(arb, minLength, maxLength)
   if (args[1] !== undefined) {
-    const maxLength = args[1];
-    return convertFromNext(new ArrayArbitrary<T>(nextArb, args[0], maxLength, maxLength));
+    return createArrayArbitrary(nextArb, undefined, args[0], args[1]); // no size
   }
   // fc.array(arb, maxLength)
-  const maxLength = args[0];
-  return convertFromNext(new ArrayArbitrary<T>(nextArb, 0, maxLength, maxLength));
+  return createArrayArbitrary(nextArb, undefined, 0, args[0]); // no size
 }
 export { array };
diff --git a/src/arbitrary/base64String.ts b/src/arbitrary/base64String.ts
index 6aba9a08..3aea1b77 100644
--- a/src/arbitrary/base64String.ts
+++ b/src/arbitrary/base64String.ts
@@ -2,7 +2,7 @@ import { Arbitrary } from '../check/arbitrary/definition/Arbitrary';
 import { convertFromNext, convertToNext } from '../check/arbitrary/definition/Converters';
 import { array } from './array';
 import { base64 } from './base64';
-import { maxLengthFromMinLength } from './_internals/helpers/MaxLengthFromMinLength';
+import { DefaultSize, maxLengthFromMinLength } from './_internals/helpers/MaxLengthFromMinLength';
 import {
   extractStringConstraints,
   StringFullConstraintsDefinition,
@@ -16,7 +16,8 @@ export { StringSharedConstraints } from './_internals/helpers/StringConstraintsE
 function extractMinMaxConstraints(args: StringFullConstraintsDefinition) {
   const constraints = extractStringConstraints(args);
   const minLength = constraints.minLength !== undefined ? constraints.minLength : 0;
-  const maxLength = constraints.maxLength !== undefined ? constraints.maxLength : maxLengthFromMinLength(minLength);
+  const maxLength =
+    constraints.maxLength !== undefined ? constraints.maxLength : maxLengthFromMinLength(minLength, DefaultSize);
   return { minLength, maxLength };
 }
 
diff --git a/src/arbitrary/set.ts b/src/arbitrary/set.ts
index 10710d2f..a8888b3b 100644
--- a/src/arbitrary/set.ts
+++ b/src/arbitrary/set.ts
@@ -1,7 +1,7 @@
 import { ArrayArbitrary } from './_internals/ArrayArbitrary';
 import { Arbitrary } from '../check/arbitrary/definition/Arbitrary';
 import { convertFromNext, convertToNext } from '../check/arbitrary/definition/Converters';
-import { MaxLengthUpperBound, maxLengthFromMinLength } from './_internals/helpers/MaxLengthFromMinLength';
+import { MaxLengthUpperBound, maxLengthFromMinLength, DefaultSize } from './_internals/helpers/MaxLengthFromMinLength';
 import { CustomSetBuilder } from './_internals/interfaces/CustomSet';
 import { CustomEqualSet } from './_internals/helpers/CustomEqualSet';
 import { NextValue } from '../check/arbitrary/definition/NextValue';
@@ -44,7 +44,7 @@ function buildCompleteSetConstraints<T>(constraints: SetConstraints<T>): Complet
   const maxLength = constraints.maxLength !== undefined ? constraints.maxLength : MaxLengthUpperBound;
   // TODO(size) - Make use of the (future) size argument to decide whether or not to apply maxLengthFromMinLength or take maxLength
   const maxGeneratedLength =
-    constraints.maxLength !== undefined ? constraints.maxLength : maxLengthFromMinLength(minLength);
+    constraints.maxLength !== undefined ? constraints.maxLength : maxLengthFromMinLength(minLength, DefaultSize);
   const setBuilder = buildSetBuilder(constraints);
   return { minLength, maxGeneratedLength, maxLength, setBuilder };
 }
diff --git a/src/arbitrary/sparseArray.ts b/src/arbitrary/sparseArray.ts
index e0b307fb..543d114d 100644
--- a/src/arbitrary/sparseArray.ts
+++ b/src/arbitrary/sparseArray.ts
@@ -4,7 +4,7 @@ import { integer } from './integer';
 import { nat } from './nat';
 import { set } from './set';
 import { tuple } from './tuple';
-import { maxLengthFromMinLength } from './_internals/helpers/MaxLengthFromMinLength';
+import { DefaultSize, maxLengthFromMinLength } from './_internals/helpers/MaxLengthFromMinLength';
 
 /**
  * Constraints to be applied on {@link sparseArray}
@@ -67,8 +67,8 @@ export function sparseArray<T>(arb: Arbitrary<T>, constraints: SparseArrayConstr
     // makes us unable to shrink user inputs we could have potentially generated as we restrict our array
     // to a smaller one (for generate reasons). Ideally maxLength should only be specified when there is
     // a real need for them. For any other case, we should try not to pass them.
-    maxNumElements = maxLengthFromMinLength(minNumElements),
-    maxLength = maxLengthFromMinLength(maxNumElements),
+    maxNumElements = maxLengthFromMinLength(minNumElements, DefaultSize),
+    maxLength = maxLengthFromMinLength(maxNumElements, DefaultSize),
     noTrailingHole,
   } = constraints;
 
diff --git a/src/fast-check-default.ts b/src/fast-check-default.ts
index 9525c051..84f9d60c 100644
--- a/src/fast-check-default.ts
+++ b/src/fast-check-default.ts
@@ -164,6 +164,7 @@ import { NextArbitrary } from './check/arbitrary/definition/NextArbitrary';
 import { NextValue } from './check/arbitrary/definition/NextValue';
 import { convertFromNext, convertFromNextWithShrunkOnce, convertToNext } from './check/arbitrary/definition/Converters';
 import { PureRandom } from './random/generator/PureRandom';
+import { Size, SizeForArbitrary } from './arbitrary/_internals/helpers/MaxLengthFromMinLength';
 
 // Explicit cast into string to avoid to have __type: "__PACKAGE_TYPE__"
 /**
@@ -403,6 +404,8 @@ export {
   // arbitrary types (mostly when produced values are difficult to formalize)
   Memo,
   // run configuration
+  Size,
+  SizeForArbitrary,
   GlobalParameters,
   GlobalAsyncPropertyHookFunction,
   GlobalPropertyHookFunction,


Test patch:
diff --git a/test/unit/arbitrary/_internals/helpers/MaxLengthFromMinLength.spec.ts b/test/unit/arbitrary/_internals/helpers/MaxLengthFromMinLength.spec.ts
new file mode 100644
index 00000000..8049e3c7
--- /dev/null
+++ b/test/unit/arbitrary/_internals/helpers/MaxLengthFromMinLength.spec.ts
@@ -0,0 +1,170 @@
+import { assert } from 'console';
+import fc from '../../../../../lib/fast-check';
+import {
+  DefaultSize,
+  maxGeneratedLengthFromSizeForArbitrary,
+  maxLengthFromMinLength,
+  MaxLengthUpperBound,
+  Size,
+  SizeForArbitrary,
+} from '../../../../../src/arbitrary/_internals/helpers/MaxLengthFromMinLength';
+
+describe('maxLengthFromMinLength', () => {
+  it('should result into higher or equal maxLength given higher size', () => {
+    fc.assert(
+      fc.property(sizeArb, sizeArb, fc.integer({ min: 0, max: MaxLengthUpperBound }), (sa, sb, minLength) => {
+        // Arrange
+        const [smallSize, largeSize] = isSmallerSize(sa, sb) ? [sa, sb] : [sb, sa];
+
+        // Act / Assert
+        expect(maxLengthFromMinLength(minLength, smallSize)).toBeLessThanOrEqual(
+          maxLengthFromMinLength(minLength, largeSize)
+        );
+      })
+    );
+  });
+
+  it('should result into higher or equal maxLength given higher minLength', () => {
+    fc.assert(
+      fc.property(
+        sizeArb,
+        fc.integer({ min: 0, max: MaxLengthUpperBound }),
+        fc.integer({ min: 0, max: MaxLengthUpperBound }),
+        (size, minLengthA, minLengthB) => {
+          // Arrange
+          const [smallMinLength, largeMinLength] =
+            minLengthA < minLengthB ? [minLengthA, minLengthB] : [minLengthB, minLengthA];
+
+          // Act / Assert
+          expect(maxLengthFromMinLength(smallMinLength, size)).toBeLessThanOrEqual(
+            maxLengthFromMinLength(largeMinLength, size)
+          );
+        }
+      )
+    );
+  });
+});
+
+describe('maxGeneratedLengthFromSizeForArbitrary', () => {
+  it('should only consider the received size when set to Size', () => {
+    fc.assert(
+      fc.property(
+        sizeArb,
+        fc.integer({ min: 0, max: MaxLengthUpperBound }),
+        fc.integer({ min: 0, max: MaxLengthUpperBound }),
+        fc.boolean(),
+        (size, lengthA, lengthB, specifiedMaxLength) => {
+          // Arrange
+          const [minLength, maxLength] = lengthA < lengthB ? [lengthA, lengthB] : [lengthB, lengthA];
+
+          // Act
+          const computedLength = maxGeneratedLengthFromSizeForArbitrary(size, minLength, maxLength, specifiedMaxLength);
+          const expectedLength = maxLengthFromMinLength(minLength, size);
+
+          // Assert
+          expect(computedLength).toBe(expectedLength);
+        }
+      )
+    );
+  });
+
+  it('should only consider the received size when set to "max"', () => {
+    fc.assert(
+      fc.property(
+        fc.integer({ min: 0, max: MaxLengthUpperBound }),
+        fc.integer({ min: 0, max: MaxLengthUpperBound }),
+        fc.boolean(),
+        (lengthA, lengthB, specifiedMaxLength) => {
+          // Arrange
+          const size = 'max';
+          const [minLength, maxLength] = lengthA < lengthB ? [lengthA, lengthB] : [lengthB, lengthA];
+
+          // Act
+          const computedLength = maxGeneratedLengthFromSizeForArbitrary(size, minLength, maxLength, specifiedMaxLength);
+
+          // Assert
+          expect(computedLength).toBe(maxLength);
+        }
+      )
+    );
+  });
+
+  it('should ignore specifiedMaxLength whenever size specified', () => {
+    fc.assert(
+      fc.property(
+        sizeForArbitraryArb,
+        fc.integer({ min: 0, max: MaxLengthUpperBound }),
+        fc.integer({ min: 0, max: MaxLengthUpperBound }),
+        (size, lengthA, lengthB) => {
+          // Arrange
+          const [minLength, maxLength] = lengthA < lengthB ? [lengthA, lengthB] : [lengthB, lengthA];
+
+          // Act
+          const computedLength = maxGeneratedLengthFromSizeForArbitrary(size, minLength, maxLength, false);
+          const expectedLength = maxGeneratedLengthFromSizeForArbitrary(size, minLength, maxLength, true);
+
+          // Assert
+          expect(computedLength).toBe(expectedLength);
+        }
+      )
+    );
+  });
+
+  it('should fallback to "max" whenever no size specified but maxLength specified', () => {
+    fc.assert(
+      fc.property(
+        fc.integer({ min: 0, max: MaxLengthUpperBound }),
+        fc.integer({ min: 0, max: MaxLengthUpperBound }),
+        (lengthA, lengthB) => {
+          // Arrange
+          const [minLength, maxLength] = lengthA < lengthB ? [lengthA, lengthB] : [lengthB, lengthA];
+
+          // Act
+          const computedLength = maxGeneratedLengthFromSizeForArbitrary(undefined, minLength, maxLength, true);
+          const expectedLength = maxGeneratedLengthFromSizeForArbitrary('max', minLength, maxLength, true);
+
+          // Assert
+          expect(computedLength).toBe(expectedLength);
+        }
+      )
+    );
+  });
+
+  it('should fallback to DefaultSize whenever no size specified and no maxLength specified', () => {
+    fc.assert(
+      fc.property(
+        fc.integer({ min: 0, max: MaxLengthUpperBound }),
+        fc.integer({ min: 0, max: MaxLengthUpperBound }),
+        (lengthA, lengthB) => {
+          // Arrange
+          const [minLength, maxLength] = lengthA < lengthB ? [lengthA, lengthB] : [lengthB, lengthA];
+
+          // Act
+          const computedLength = maxGeneratedLengthFromSizeForArbitrary(undefined, minLength, maxLength, false);
+          const expectedLength = maxGeneratedLengthFromSizeForArbitrary(DefaultSize, minLength, maxLength, false);
+
+          // Assert
+          expect(computedLength).toBe(expectedLength);
+        }
+      )
+    );
+  });
+});
+
+// Helpers
+
+const allSizeOrdered = ['xsmall', 'small', 'medium', 'large', 'xlarge'] as const;
+const sizeArb = fc.constantFrom<Size>(...allSizeOrdered);
+const isSmallerSize = (sa: Size, sb: Size) => allSizeOrdered.indexOf(sa) < allSizeOrdered.indexOf(sb);
+
+const allSizeForArbitrary = [...allSizeOrdered, 'max'] as const; // WARNING/ it does not include undefined
+const sizeForArbitraryArb = fc.constantFrom<SizeForArbitrary>(...allSizeForArbitrary);
+
+// Type check that helpers are covering all the possibilities
+
+const failIfMissingSize: Size extends typeof allSizeOrdered[number] ? true : never = true;
+const failIfMissingSizeForArbitrary: NonNullable<SizeForArbitrary> extends typeof allSizeForArbitrary[number]
+  ? true
+  : never = true;
+assert(failIfMissingSize); // just not to appear unused
+assert(failIfMissingSizeForArbitrary); // just not to appear unused