Repository: dubzzz/fast-check
Pull request #2032 (2021-06-24 21:41:41)

🚚 Move web arbitraries into /arbitrary
<!-- Context of the PR: short description and potentially linked issues -->

<!-- ...a few words to describe the content of this PR...               -->
<!-- ... -->

<!-- Type of PR: [ ] unchecked / [ ] checked -->
**_Category:_**

- [ ] ✨ Introduce new features
- [ ] 📝 Add or update documentation
- [ ] ✅ Add or update tests
- [ ] 🐛 Fix a bug
- [ ] 🏷️ Add or update types
- [ ] ⚡️ Improve performance
- [x] _Other(s):_ moving code
<!-- Don't forget to add the gitmoji icon in the name of the PR -->
<!-- See: https://gitmoji.dev/                                  -->

<!-- Fixing bugs, adding features... may impact existing ones           -->
<!-- in order to track potential issues that could be related to your PR -->
<!-- please check the impacts and describe more precisely what to expect -->

**_Potential impacts:_**

<!-- Generated values: Can your change impact any of the existing generators in terms of generated values, if so which ones? when? -->
<!-- Shrink values:    Can your change impact any of the existing generators in terms of shrink values, if so which ones? when? -->
<!-- Performance:      Can it require some typings changes on user side? Please give more details -->
<!-- Typings:          Is there a potential performance impact? In which cases? -->
- [ ] Generated values
- [ ] Shrink values
- [ ] Performance
- [ ] Typings
- [ ] _Other(s):_ ...


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/2032/builds/143415) or the icon next to each commit SHA.

Latest deployment of this branch, based on commit dd16fd1a93b3cca742aac7bde32fe44d362e8b9d:

|Sandbox| Source |
|--|--|
|[Vanilla](https://codesandbox.io/s/vanilla-7rkwp)| Configuration |
|[fast-check-examples](https://codesandbox.io/s/fast-check-examples-9gv3j)| Configuration |

codecov[bot]: # [Codecov](https://codecov.io/gh/dubzzz/fast-check/pull/2032?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN) Report
> Merging [#2032](https://codecov.io/gh/dubzzz/fast-check/pull/2032?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN) (dd16fd1) into [main](https://codecov.io/gh/dubzzz/fast-check/commit/b212496a86b37897f51cf83313bbdae1f59e9744?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN) (b212496) will **increase** coverage by `0.01%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/dubzzz/fast-check/pull/2032/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/2032?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    #2032      +/-   ##
==========================================
+ Coverage   96.56%   96.58%   +0.01%     
==========================================
  Files         170      176       +6     
  Lines        4314     4332      +18     
  Branches      806      806              
==========================================
+ Hits         4166     4184      +18     
  Misses        144      144              
  Partials        4        4              
```

| Flag | Coverage Δ | |
|---|---|---|
| unit-tests | `96.58% <100.00%> (+0.01%)` | :arrow_up: |
| unit-tests-12.x-Linux | `96.58% <100.00%> (+0.01%)` | :arrow_up: |
| unit-tests-14.x-Linux | `?` | |
| unit-tests-16.x-Linux | `?` | |

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.

| [Impacted Files](https://codecov.io/gh/dubzzz/fast-check/pull/2032?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN) | Coverage Δ | |
|---|---|---|
| [...ternals/builders/CharacterRangeArbitraryBuilder.ts](https://codecov.io/gh/dubzzz/fast-check/pull/2032/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN#diff-c3JjL2FyYml0cmFyeS9faW50ZXJuYWxzL2J1aWxkZXJzL0NoYXJhY3RlclJhbmdlQXJiaXRyYXJ5QnVpbGRlci50cw==) | `100.00% <100.00%> (ø)` | |
| [...als/builders/UriQueryOrFragmentArbitraryBuilder.ts](https://codecov.io/gh/dubzzz/fast-check/pull/2032/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN#diff-c3JjL2FyYml0cmFyeS9faW50ZXJuYWxzL2J1aWxkZXJzL1VyaVF1ZXJ5T3JGcmFnbWVudEFyYml0cmFyeUJ1aWxkZXIudHM=) | `100.00% <100.00%> (ø)` | |
| [.../\_internals/helpers/InvalidSubdomainLabelFiIter.ts](https://codecov.io/gh/dubzzz/fast-check/pull/2032/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN#diff-c3JjL2FyYml0cmFyeS9faW50ZXJuYWxzL2hlbHBlcnMvSW52YWxpZFN1YmRvbWFpbkxhYmVsRmlJdGVyLnRz) | `100.00% <100.00%> (ø)` | |
| [src/arbitrary/domain.ts](https://codecov.io/gh/dubzzz/fast-check/pull/2032/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN#diff-c3JjL2FyYml0cmFyeS9kb21haW4udHM=) | `100.00% <100.00%> (ø)` | |
| [src/arbitrary/emailAddress.ts](https://codecov.io/gh/dubzzz/fast-check/pull/2032/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN#diff-c3JjL2FyYml0cmFyeS9lbWFpbEFkZHJlc3MudHM=) | `100.00% <100.00%> (ø)` | |
| [src/arbitrary/webAuthority.ts](https://codecov.io/gh/dubzzz/fast-check/pull/2032/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN#diff-c3JjL2FyYml0cmFyeS93ZWJBdXRob3JpdHkudHM=) | `100.00% <100.00%> (ø)` | |
| [src/arbitrary/webFragments.ts](https://codecov.io/gh/dubzzz/fast-check/pull/2032/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN#diff-c3JjL2FyYml0cmFyeS93ZWJGcmFnbWVudHMudHM=) | `100.00% <100.00%> (ø)` | |
| [src/arbitrary/webQueryParameters.ts](https://codecov.io/gh/dubzzz/fast-check/pull/2032/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN#diff-c3JjL2FyYml0cmFyeS93ZWJRdWVyeVBhcmFtZXRlcnMudHM=) | `100.00% <100.00%> (ø)` | |
| [src/arbitrary/webSegment.ts](https://codecov.io/gh/dubzzz/fast-check/pull/2032/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN#diff-c3JjL2FyYml0cmFyeS93ZWJTZWdtZW50LnRz) | `100.00% <100.00%> (ø)` | |
| [src/arbitrary/webUrl.ts](https://codecov.io/gh/dubzzz/fast-check/pull/2032/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN#diff-c3JjL2FyYml0cmFyeS93ZWJVcmwudHM=) | `100.00% <100.00%> (ø)` | |
| ... and [9 more](https://codecov.io/gh/dubzzz/fast-check/pull/2032/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/dubzzz/fast-check/pull/2032?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/2032?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Nicolas+DUBIEN). Last update [b212496...dd16fd1](https://codecov.io/gh/dubzzz/fast-check/pull/2032?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/src/check/arbitrary/helpers/SpecificCharacterRange.ts b/src/arbitrary/_internals/builders/CharacterRangeArbitraryBuilder.ts
similarity index 55%
rename from src/check/arbitrary/helpers/SpecificCharacterRange.ts
rename to src/arbitrary/_internals/builders/CharacterRangeArbitraryBuilder.ts
index 41c65c14..817f6688 100644
--- a/src/check/arbitrary/helpers/SpecificCharacterRange.ts
+++ b/src/arbitrary/_internals/builders/CharacterRangeArbitraryBuilder.ts
@@ -1,7 +1,7 @@
-import { fullUnicode } from '../../../arbitrary/fullUnicode';
-import { Arbitrary } from '../definition/Arbitrary';
-import { frequency } from '../../../arbitrary/frequency';
-import { mapToConstant } from '../../../arbitrary/mapToConstant';
+import { fullUnicode } from '../../fullUnicode';
+import { Arbitrary } from '../../../check/arbitrary/definition/Arbitrary';
+import { frequency } from '../../frequency';
+import { mapToConstant } from '../../mapToConstant';
 
 /** @internal */
 const lowerCaseMapper = { num: 26, build: (v: number) => String.fromCharCode(v + 0x61) };
@@ -19,26 +19,17 @@ const percentCharArb = fullUnicode().map((c) => {
 });
 
 /** @internal */
-export const buildLowerAlphaArb = (others: string[]): Arbitrary<string> =>
+export const buildLowerAlphaArbitrary = (others: string[]): Arbitrary<string> =>
   mapToConstant(lowerCaseMapper, { num: others.length, build: (v) => others[v] });
 
 /** @internal */
-export const buildLowerAlphaNumericArb = (others: string[]): Arbitrary<string> =>
+export const buildLowerAlphaNumericArbitrary = (others: string[]): Arbitrary<string> =>
   mapToConstant(lowerCaseMapper, numericMapper, { num: others.length, build: (v) => others[v] });
 
 /** @internal */
-export const buildAlphaNumericArb = (others: string[]): Arbitrary<string> =>
+export const buildAlphaNumericArbitrary = (others: string[]): Arbitrary<string> =>
   mapToConstant(lowerCaseMapper, upperCaseMapper, numericMapper, { num: others.length, build: (v) => others[v] });
 
 /** @internal */
-export const buildAlphaNumericPercentArb = (others: string[]): Arbitrary<string> =>
-  frequency(
-    {
-      weight: 10,
-      arbitrary: buildAlphaNumericArb(others),
-    },
-    {
-      weight: 1,
-      arbitrary: percentCharArb,
-    }
-  );
+export const buildAlphaNumericPercentArbitrary = (others: string[]): Arbitrary<string> =>
+  frequency({ weight: 10, arbitrary: buildAlphaNumericArbitrary(others) }, { weight: 1, arbitrary: percentCharArb });
diff --git a/src/arbitrary/_internals/builders/UriQueryOrFragmentArbitraryBuilder.ts b/src/arbitrary/_internals/builders/UriQueryOrFragmentArbitraryBuilder.ts
new file mode 100644
index 00000000..94e85058
--- /dev/null
+++ b/src/arbitrary/_internals/builders/UriQueryOrFragmentArbitraryBuilder.ts
@@ -0,0 +1,11 @@
+import { Arbitrary } from '../../../check/arbitrary/definition/Arbitrary';
+import { buildAlphaNumericPercentArbitrary } from './CharacterRangeArbitraryBuilder';
+import { stringOf } from '../../stringOf';
+
+/** @internal */
+export function buildUriQueryOrFragmentArbitrary(): Arbitrary<string> {
+  // query         = *( pchar / "/" / "?" )
+  // fragment      = *( pchar / "/" / "?" )
+  const others = ['-', '.', '_', '~', '!', '$', '&', "'", '(', ')', '*', '+', ',', ';', '=', ':', '@', '/', '?'];
+  return stringOf(buildAlphaNumericPercentArbitrary(others));
+}
diff --git a/src/arbitrary/_internals/helpers/InvalidSubdomainLabelFiIter.ts b/src/arbitrary/_internals/helpers/InvalidSubdomainLabelFiIter.ts
new file mode 100644
index 00000000..8d7a6f5f
--- /dev/null
+++ b/src/arbitrary/_internals/helpers/InvalidSubdomainLabelFiIter.ts
@@ -0,0 +1,27 @@
+/** @internal */
+export function filterInvalidSubdomainLabel(subdomainLabel: string): boolean {
+  // Here our definition of a subdomain is <label> and "[l]abels must be 63 characters or less"
+  // According RFC 1034 a subdomain should be defined as follows:
+  //  - <subdomain> ::= <label> | <subdomain> "." <label>
+  //  - <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
+  //  - <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
+  //  - <let-dig-hyp> ::= <let-dig> | "-"
+  //  - <let-dig> ::= <letter> | <digit>
+  //  - <letter> ::= any one of the 52 alphabetic characters A through Z in upper case and a through z in lower case
+  //  - <digit> ::= any one of the ten digits 0 through 9
+  // If we strictly follow RFC 1034, 9gag would be an invalid domain. Support for such domain has been added by ....
+  if (subdomainLabel.length > 63) {
+    return false; // invalid, it seems that this restriction has been relaxed in modern web browsers
+  }
+  // We discard any subdomain starting by xn--
+  // as they would require lots of checks to confirm if they are valid internationalized domains.
+  // While they still are valid subdomains they might be problematic with some libs,
+  // so we prefer not to include them by default (eg.: new URL in Node does not accept invalid internationalized domains)
+  return (
+    subdomainLabel.length < 4 ||
+    subdomainLabel[0] !== 'x' ||
+    subdomainLabel[1] !== 'n' ||
+    subdomainLabel[2] !== '-' ||
+    subdomainLabel[3] !== '-'
+  );
+}
diff --git a/src/arbitrary/domain.ts b/src/arbitrary/domain.ts
new file mode 100644
index 00000000..0311a18c
--- /dev/null
+++ b/src/arbitrary/domain.ts
@@ -0,0 +1,50 @@
+import { array } from './array';
+import {
+  buildLowerAlphaArbitrary,
+  buildLowerAlphaNumericArbitrary,
+} from './_internals/builders/CharacterRangeArbitraryBuilder';
+import { option } from './option';
+import { stringOf } from './stringOf';
+import { tuple } from './tuple';
+import { Arbitrary } from '../check/arbitrary/definition/Arbitrary';
+import { filterInvalidSubdomainLabel } from './_internals/helpers/InvalidSubdomainLabelFiIter';
+
+/** @internal */
+function subdomainLabel() {
+  const alphaNumericArb = buildLowerAlphaNumericArbitrary([]);
+  const alphaNumericHyphenArb = buildLowerAlphaNumericArbitrary(['-']);
+  // Rq: maxLength = 61 because max length of a label is 63 according to RFC 1034
+  //     and we add 2 characters to this generated value
+  return tuple(alphaNumericArb, option(tuple(stringOf(alphaNumericHyphenArb, { maxLength: 61 }), alphaNumericArb)))
+    .map(([f, d]) => (d === null ? f : `${f}${d[0]}${d[1]}`))
+    .filter(filterInvalidSubdomainLabel);
+}
+
+/**
+ * For domains
+ * having an extension with at least two lowercase characters
+ *
+ * According to {@link https://www.ietf.org/rfc/rfc1034.txt | RFC 1034},
+ * {@link https://www.ietf.org/rfc/rfc1035.txt | RFC 1035},
+ * {@link https://www.ietf.org/rfc/rfc1123.txt | RFC 1123} and
+ * {@link https://url.spec.whatwg.org/ | WHATWG URL Standard}
+ *
+ * @remarks Since 1.14.0
+ * @public
+ */
+export function domain(): Arbitrary<string> {
+  const alphaNumericArb = buildLowerAlphaArbitrary([]);
+  // A list of public suffixes can be found here: https://publicsuffix.org/list/public_suffix_list.dat
+  // our current implementation does not follwo this list and generate a fully randomized suffix
+  // which is probably not in this list (probability would be low)
+  const publicSuffixArb = stringOf(alphaNumericArb, { minLength: 2, maxLength: 10 });
+  return (
+    tuple(array(subdomainLabel(), { minLength: 1, maxLength: 5 }), publicSuffixArb)
+      .map(([mid, ext]) => `${mid.join('.')}.${ext}`)
+      // Required by RFC 1034:
+      //    To simplify implementations, the total number of octets that represent
+      //    a domain name (i.e., the sum of all label octets and label lengths) is limited to 255.
+      // It seems that this restriction has been relaxed in modern web browsers.
+      .filter((d) => d.length <= 255)
+  );
+}
diff --git a/src/check/arbitrary/EmailArbitrary.ts b/src/arbitrary/emailAddress.ts
similarity index 68%
rename from src/check/arbitrary/EmailArbitrary.ts
rename to src/arbitrary/emailAddress.ts
index 16944429..1eda800f 100644
--- a/src/check/arbitrary/EmailArbitrary.ts
+++ b/src/arbitrary/emailAddress.ts
@@ -1,9 +1,9 @@
-import { array } from '../../arbitrary/array';
-import { buildLowerAlphaNumericArb } from './helpers/SpecificCharacterRange';
-import { domain } from './HostArbitrary';
-import { stringOf } from '../../arbitrary/stringOf';
-import { tuple } from '../../arbitrary/tuple';
-import { Arbitrary } from './definition/Arbitrary';
+import { array } from './array';
+import { buildLowerAlphaNumericArbitrary } from './_internals/builders/CharacterRangeArbitraryBuilder';
+import { domain } from './domain';
+import { stringOf } from './stringOf';
+import { tuple } from './tuple';
+import { Arbitrary } from '../check/arbitrary/definition/Arbitrary';
 
 /**
  * For email address
@@ -17,7 +17,7 @@ import { Arbitrary } from './definition/Arbitrary';
  */
 export function emailAddress(): Arbitrary<string> {
   const others = ['!', '#', '$', '%', '&', "'", '*', '+', '-', '/', '=', '?', '^', '_', '`', '{', '|', '}', '~'];
-  const atextArb = buildLowerAlphaNumericArb(others);
+  const atextArb = buildLowerAlphaNumericArbitrary(others);
   const localPartArb = array(stringOf(atextArb, { minLength: 1, maxLength: 10 }), { minLength: 1, maxLength: 5 })
     .map((a) => a.join('.'))
     // According to RFC 2821:
diff --git a/src/arbitrary/webAuthority.ts b/src/arbitrary/webAuthority.ts
new file mode 100644
index 00000000..0d2ff146
--- /dev/null
+++ b/src/arbitrary/webAuthority.ts
@@ -0,0 +1,74 @@
+import { Arbitrary } from '../check/arbitrary/definition/Arbitrary';
+import { buildAlphaNumericPercentArbitrary } from './_internals/builders/CharacterRangeArbitraryBuilder';
+import { constant } from './constant';
+import { domain } from './domain';
+import { ipV4 } from './ipV4';
+import { ipV4Extended } from './ipV4Extended';
+import { ipV6 } from './ipV6';
+import { nat } from './nat';
+import { oneof } from './oneof';
+import { option } from './option';
+import { stringOf } from './stringOf';
+import { tuple } from './tuple';
+
+/** @internal */
+function hostUserInfo(): Arbitrary<string> {
+  const others = ['-', '.', '_', '~', '!', '$', '&', "'", '(', ')', '*', '+', ',', ';', '=', ':'];
+  return stringOf(buildAlphaNumericPercentArbitrary(others));
+}
+
+/**
+ * Constraints to be applied on {@link webAuthority}
+ * @remarks Since 1.14.0
+ * @public
+ */
+export interface WebAuthorityConstraints {
+  /**
+   * Enable IPv4 in host
+   * @remarks Since 1.14.0
+   */
+  withIPv4?: boolean;
+  /**
+   * Enable IPv6 in host
+   * @remarks Since 1.14.0
+   */
+  withIPv6?: boolean;
+  /**
+   * Enable extended IPv4 format
+   * @remarks Since 1.17.0
+   */
+  withIPv4Extended?: boolean;
+  /**
+   * Enable user information prefix
+   * @remarks Since 1.14.0
+   */
+  withUserInfo?: boolean;
+  /**
+   * Enable port suffix
+   * @remarks Since 1.14.0
+   */
+  withPort?: boolean;
+}
+
+/**
+ * For web authority
+ *
+ * According to {@link https://www.ietf.org/rfc/rfc3986.txt | RFC 3986} - `authority = [ userinfo "@" ] host [ ":" port ]`
+ *
+ * @param constraints - Constraints to apply when building instances
+ *
+ * @remarks Since 1.14.0
+ * @public
+ */
+export function webAuthority(constraints?: WebAuthorityConstraints): Arbitrary<string> {
+  const c = constraints || {};
+  const hostnameArbs = [domain()]
+    .concat(c.withIPv4 === true ? [ipV4()] : [])
+    .concat(c.withIPv6 === true ? [ipV6().map((ip) => `[${ip}]`)] : [])
+    .concat(c.withIPv4Extended === true ? [ipV4Extended()] : []);
+  return tuple(
+    c.withUserInfo === true ? option(hostUserInfo()) : constant(null),
+    oneof(...hostnameArbs),
+    c.withPort === true ? option(nat(65535)) : constant(null)
+  ).map(([u, h, p]) => (u === null ? '' : `${u}@`) + h + (p === null ? '' : `:${p}`));
+}
diff --git a/src/arbitrary/webFragments.ts b/src/arbitrary/webFragments.ts
new file mode 100644
index 00000000..023854d6
--- /dev/null
+++ b/src/arbitrary/webFragments.ts
@@ -0,0 +1,16 @@
+import { Arbitrary } from '../check/arbitrary/definition/Arbitrary';
+import { buildUriQueryOrFragmentArbitrary } from './_internals/builders/UriQueryOrFragmentArbitraryBuilder';
+
+/**
+ * For fragments of an URI (web included)
+ *
+ * According to {@link https://www.ietf.org/rfc/rfc3986.txt | RFC 3986}
+ *
+ * eg.: In the url `https://domain/plop?page=1#hello=1&world=2`, `?hello=1&world=2` are query parameters
+ *
+ * @remarks Since 1.14.0
+ * @public
+ */
+export function webFragments(): Arbitrary<string> {
+  return buildUriQueryOrFragmentArbitrary();
+}
diff --git a/src/arbitrary/webQueryParameters.ts b/src/arbitrary/webQueryParameters.ts
new file mode 100644
index 00000000..351c42ad
--- /dev/null
+++ b/src/arbitrary/webQueryParameters.ts
@@ -0,0 +1,16 @@
+import { Arbitrary } from '../check/arbitrary/definition/Arbitrary';
+import { buildUriQueryOrFragmentArbitrary } from './_internals/builders/UriQueryOrFragmentArbitraryBuilder';
+
+/**
+ * For query parameters of an URI (web included)
+ *
+ * According to {@link https://www.ietf.org/rfc/rfc3986.txt | RFC 3986}
+ *
+ * eg.: In the url `https://domain/plop/?hello=1&world=2`, `?hello=1&world=2` are query parameters
+ *
+ * @remarks Since 1.14.0
+ * @public
+ */
+export function webQueryParameters(): Arbitrary<string> {
+  return buildUriQueryOrFragmentArbitrary();
+}
diff --git a/src/arbitrary/webSegment.ts b/src/arbitrary/webSegment.ts
new file mode 100644
index 00000000..637bef4e
--- /dev/null
+++ b/src/arbitrary/webSegment.ts
@@ -0,0 +1,20 @@
+import { Arbitrary } from '../check/arbitrary/definition/Arbitrary';
+import { buildAlphaNumericPercentArbitrary } from './_internals/builders/CharacterRangeArbitraryBuilder';
+import { stringOf } from './stringOf';
+
+/**
+ * For internal segment of an URI (web included)
+ *
+ * According to {@link https://www.ietf.org/rfc/rfc3986.txt | RFC 3986}
+ *
+ * eg.: In the url `https://github.com/dubzzz/fast-check/`, `dubzzz` and `fast-check` are segments
+ *
+ * @remarks Since 1.14.0
+ * @public
+ */
+export function webSegment(): Arbitrary<string> {
+  // pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
+  // segment       = *pchar
+  const others = ['-', '.', '_', '~', '!', '$', '&', "'", '(', ')', '*', '+', ',', ';', '=', ':', '@'];
+  return stringOf(buildAlphaNumericPercentArbitrary(others));
+}
diff --git a/src/arbitrary/webUrl.ts b/src/arbitrary/webUrl.ts
new file mode 100644
index 00000000..b5bfc421
--- /dev/null
+++ b/src/arbitrary/webUrl.ts
@@ -0,0 +1,64 @@
+import { array } from './array';
+import { constantFrom } from './constantFrom';
+import { constant } from './constant';
+import { option } from './option';
+import { tuple } from './tuple';
+import { Arbitrary } from '../check/arbitrary/definition/Arbitrary';
+import { webQueryParameters } from './webQueryParameters';
+import { webFragments } from './webFragments';
+import { webAuthority, WebAuthorityConstraints } from './webAuthority';
+import { webSegment } from './webSegment';
+
+/**
+ * Constraints to be applied on {@link webUrl}
+ * @remarks Since 1.14.0
+ * @public
+ */
+export interface WebUrlConstraints {
+  /**
+   * Enforce specific schemes, eg.: http, https
+   * @remarks Since 1.14.0
+   */
+  validSchemes?: string[];
+  /**
+   * Settings for {@link webAuthority}
+   * @remarks Since 1.14.0
+   */
+  authoritySettings?: WebAuthorityConstraints;
+  /**
+   * Enable query parameters in the generated url
+   * @remarks Since 1.14.0
+   */
+  withQueryParameters?: boolean;
+  /**
+   * Enable fragments in the generated url
+   * @remarks Since 1.14.0
+   */
+  withFragments?: boolean;
+}
+
+/**
+ * For web url
+ *
+ * According to {@link https://www.ietf.org/rfc/rfc3986.txt | RFC 3986} and
+ * {@link https://url.spec.whatwg.org/ | WHATWG URL Standard}
+ *
+ * @param constraints - Constraints to apply when building instances
+ *
+ * @remarks Since 1.14.0
+ * @public
+ */
+export function webUrl(constraints?: WebUrlConstraints): Arbitrary<string> {
+  const c = constraints || {};
+  const validSchemes = c.validSchemes || ['http', 'https'];
+  const schemeArb = constantFrom(...validSchemes);
+  const authorityArb = webAuthority(c.authoritySettings);
+  const pathArb = array(webSegment()).map((p) => p.map((v) => `/${v}`).join(''));
+  return tuple(
+    schemeArb,
+    authorityArb,
+    pathArb,
+    c.withQueryParameters === true ? option(webQueryParameters()) : constant(null),
+    c.withFragments === true ? option(webFragments()) : constant(null)
+  ).map(([s, a, p, q, f]) => `${s}://${a}${p}${q === null ? '' : `?${q}`}${f === null ? '' : `#${f}`}`);
+}
diff --git a/src/check/arbitrary/HostArbitrary.ts b/src/check/arbitrary/HostArbitrary.ts
deleted file mode 100644
index c4b589f6..00000000
--- a/src/check/arbitrary/HostArbitrary.ts
+++ /dev/null
@@ -1,84 +0,0 @@
-import { array } from '../../arbitrary/array';
-import {
-  buildAlphaNumericPercentArb,
-  buildLowerAlphaArb,
-  buildLowerAlphaNumericArb,
-} from './helpers/SpecificCharacterRange';
-import { option } from '../../arbitrary/option';
-import { stringOf } from '../../arbitrary/stringOf';
-import { tuple } from '../../arbitrary/tuple';
-import { Arbitrary } from './definition/Arbitrary';
-
-/** @internal */
-export function filterInvalidSubdomainLabel(subdomainLabel: string): boolean {
-  // Here our definition of a subdomain is <label> and "[l]abels must be 63 characters or less"
-  // According RFC 1034 a subdomain should be defined as follows:
-  //  - <subdomain> ::= <label> | <subdomain> "." <label>
-  //  - <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
-  //  - <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
-  //  - <let-dig-hyp> ::= <let-dig> | "-"
-  //  - <let-dig> ::= <letter> | <digit>
-  //  - <letter> ::= any one of the 52 alphabetic characters A through Z in upper case and a through z in lower case
-  //  - <digit> ::= any one of the ten digits 0 through 9
-  // If we strictly follow RFC 1034, 9gag would be an invalid domain. Support for such domain has been added by ....
-  if (subdomainLabel.length > 63) {
-    return false; // invalid, it seems that this restriction has been relaxed in modern web browsers
-  }
-  // We discard any subdomain starting by xn--
-  // as they would require lots of checks to confirm if they are valid internationalized domains.
-  // While they still are valid subdomains they might be problematic with some libs,
-  // so we prefer not to include them by default (eg.: new URL in Node does not accept invalid internationalized domains)
-  return (
-    subdomainLabel.length < 4 ||
-    subdomainLabel[0] !== 'x' ||
-    subdomainLabel[1] !== 'n' ||
-    subdomainLabel[2] !== '-' ||
-    subdomainLabel[3] !== '-'
-  );
-}
-
-/** @internal */
-function subdomainLabel() {
-  const alphaNumericArb = buildLowerAlphaNumericArb([]);
-  const alphaNumericHyphenArb = buildLowerAlphaNumericArb(['-']);
-  // Rq: maxLength = 61 because max length of a label is 63 according to RFC 1034
-  //     and we add 2 characters to this generated value
-  return tuple(alphaNumericArb, option(tuple(stringOf(alphaNumericHyphenArb, { maxLength: 61 }), alphaNumericArb)))
-    .map(([f, d]) => (d === null ? f : `${f}${d[0]}${d[1]}`))
-    .filter(filterInvalidSubdomainLabel);
-}
-
-/**
- * For domains
- * having an extension with at least two lowercase characters
- *
- * According to {@link https://www.ietf.org/rfc/rfc1034.txt | RFC 1034},
- * {@link https://www.ietf.org/rfc/rfc1035.txt | RFC 1035},
- * {@link https://www.ietf.org/rfc/rfc1123.txt | RFC 1123} and
- * {@link https://url.spec.whatwg.org/ | WHATWG URL Standard}
- *
- * @remarks Since 1.14.0
- * @public
- */
-export function domain(): Arbitrary<string> {
-  const alphaNumericArb = buildLowerAlphaArb([]);
-  // A list of public suffixes can be found here: https://publicsuffix.org/list/public_suffix_list.dat
-  // our current implementation does not follwo this list and generate a fully randomized suffix
-  // which is probably not in this list (probability would be low)
-  const publicSuffixArb = stringOf(alphaNumericArb, { minLength: 2, maxLength: 10 });
-  return (
-    tuple(array(subdomainLabel(), { minLength: 1, maxLength: 5 }), publicSuffixArb)
-      .map(([mid, ext]) => `${mid.join('.')}.${ext}`)
-      // Required by RFC 1034:
-      //    To simplify implementations, the total number of octets that represent
-      //    a domain name (i.e., the sum of all label octets and label lengths) is limited to 255.
-      // It seems that this restriction has been relaxed in modern web browsers.
-      .filter((d) => d.length <= 255)
-  );
-}
-
-/** @internal */
-export function hostUserInfo(): Arbitrary<string> {
-  const others = ['-', '.', '_', '~', '!', '$', '&', "'", '(', ')', '*', '+', ',', ';', '=', ':'];
-  return stringOf(buildAlphaNumericPercentArb(others));
-}
diff --git a/src/check/arbitrary/WebArbitrary.ts b/src/check/arbitrary/WebArbitrary.ts
deleted file mode 100644
index 28af3b01..00000000
--- a/src/check/arbitrary/WebArbitrary.ts
+++ /dev/null
@@ -1,177 +0,0 @@
-import { array } from '../../arbitrary/array';
-import { constantFrom } from '../../arbitrary/constantFrom';
-import { constant } from '../../arbitrary/constant';
-import { buildAlphaNumericPercentArb } from './helpers/SpecificCharacterRange';
-import { domain, hostUserInfo } from './HostArbitrary';
-import { nat } from '../../arbitrary/nat';
-import { ipV4 } from '../../arbitrary/ipV4';
-import { ipV4Extended } from '../../arbitrary/ipV4Extended';
-import { ipV6 } from '../../arbitrary/ipV6';
-import { oneof } from '../../arbitrary/oneof';
-import { option } from '../../arbitrary/option';
-import { stringOf } from '../../arbitrary/stringOf';
-import { tuple } from '../../arbitrary/tuple';
-import { Arbitrary } from './definition/Arbitrary';
-
-/**
- * Constraints to be applied on {@link webAuthority}
- * @remarks Since 1.14.0
- * @public
- */
-export interface WebAuthorityConstraints {
-  /**
-   * Enable IPv4 in host
-   * @remarks Since 1.14.0
-   */
-  withIPv4?: boolean;
-  /**
-   * Enable IPv6 in host
-   * @remarks Since 1.14.0
-   */
-  withIPv6?: boolean;
-  /**
-   * Enable extended IPv4 format
-   * @remarks Since 1.17.0
-   */
-  withIPv4Extended?: boolean;
-  /**
-   * Enable user information prefix
-   * @remarks Since 1.14.0
-   */
-  withUserInfo?: boolean;
-  /**
-   * Enable port suffix
-   * @remarks Since 1.14.0
-   */
-  withPort?: boolean;
-}
-
-/**
- * For web authority
- *
- * According to {@link https://www.ietf.org/rfc/rfc3986.txt | RFC 3986} - `authority = [ userinfo "@" ] host [ ":" port ]`
- *
- * @param constraints - Constraints to apply when building instances
- *
- * @remarks Since 1.14.0
- * @public
- */
-export function webAuthority(constraints?: WebAuthorityConstraints): Arbitrary<string> {
-  const c = constraints || {};
-  const hostnameArbs = [domain()]
-    .concat(c.withIPv4 === true ? [ipV4()] : [])
-    .concat(c.withIPv6 === true ? [ipV6().map((ip) => `[${ip}]`)] : [])
-    .concat(c.withIPv4Extended === true ? [ipV4Extended()] : []);
-  return tuple(
-    c.withUserInfo === true ? option(hostUserInfo()) : constant(null),
-    oneof(...hostnameArbs),
-    c.withPort === true ? option(nat(65535)) : constant(null)
-  ).map(([u, h, p]) => (u === null ? '' : `${u}@`) + h + (p === null ? '' : `:${p}`));
-}
-
-/**
- * For internal segment of an URI (web included)
- *
- * According to {@link https://www.ietf.org/rfc/rfc3986.txt | RFC 3986}
- *
- * eg.: In the url `https://github.com/dubzzz/fast-check/`, `dubzzz` and `fast-check` are segments
- *
- * @remarks Since 1.14.0
- * @public
- */
-export function webSegment(): Arbitrary<string> {
-  // pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
-  // segment       = *pchar
-  const others = ['-', '.', '_', '~', '!', '$', '&', "'", '(', ')', '*', '+', ',', ';', '=', ':', '@'];
-  return stringOf(buildAlphaNumericPercentArb(others));
-}
-
-/** @internal */
-function uriQueryOrFragment() {
-  // query         = *( pchar / "/" / "?" )
-  // fragment      = *( pchar / "/" / "?" )
-  const others = ['-', '.', '_', '~', '!', '$', '&', "'", '(', ')', '*', '+', ',', ';', '=', ':', '@', '/', '?'];
-  return stringOf(buildAlphaNumericPercentArb(others));
-}
-
-/**
- * For query parameters of an URI (web included)
- *
- * According to {@link https://www.ietf.org/rfc/rfc3986.txt | RFC 3986}
- *
- * eg.: In the url `https://domain/plop/?hello=1&world=2`, `?hello=1&world=2` are query parameters
- *
- * @remarks Since 1.14.0
- * @public
- */
-export function webQueryParameters(): Arbitrary<string> {
-  return uriQueryOrFragment();
-}
-
-/**
- * For fragments of an URI (web included)
- *
- * According to {@link https://www.ietf.org/rfc/rfc3986.txt | RFC 3986}
- *
- * eg.: In the url `https://domain/plop?page=1#hello=1&world=2`, `?hello=1&world=2` are query parameters
- *
- * @remarks Since 1.14.0
- * @public
- */
-export function webFragments(): Arbitrary<string> {
-  return uriQueryOrFragment();
-}
-
-/**
- * Constraints to be applied on {@link webUrl}
- * @remarks Since 1.14.0
- * @public
- */
-export interface WebUrlConstraints {
-  /**
-   * Enforce specific schemes, eg.: http, https
-   * @remarks Since 1.14.0
-   */
-  validSchemes?: string[];
-  /**
-   * Settings for {@link webAuthority}
-   * @remarks Since 1.14.0
-   */
-  authoritySettings?: WebAuthorityConstraints;
-  /**
-   * Enable query parameters in the generated url
-   * @remarks Since 1.14.0
-   */
-  withQueryParameters?: boolean;
-  /**
-   * Enable fragments in the generated url
-   * @remarks Since 1.14.0
-   */
-  withFragments?: boolean;
-}
-
-/**
- * For web url
- *
- * According to {@link https://www.ietf.org/rfc/rfc3986.txt | RFC 3986} and
- * {@link https://url.spec.whatwg.org/ | WHATWG URL Standard}
- *
- * @param constraints - Constraints to apply when building instances
- *
- * @remarks Since 1.14.0
- * @public
- */
-export function webUrl(constraints?: WebUrlConstraints): Arbitrary<string> {
-  const c = constraints || {};
-  const validSchemes = c.validSchemes || ['http', 'https'];
-  const schemeArb = constantFrom(...validSchemes);
-  const authorityArb = webAuthority(c.authoritySettings);
-  const pathArb = array(webSegment()).map((p) => p.map((v) => `/${v}`).join(''));
-  return tuple(
-    schemeArb,
-    authorityArb,
-    pathArb,
-    c.withQueryParameters === true ? option(webQueryParameters()) : constant(null),
-    c.withFragments === true ? option(webFragments()) : constant(null)
-  ).map(([s, a, p, q, f]) => `${s}://${a}${p}${q === null ? '' : `?${q}`}${f === null ? '' : `#${f}`}`);
-}
diff --git a/src/fast-check-default.ts b/src/fast-check-default.ts
index 61dcf4fb..b11de2cf 100644
--- a/src/fast-check-default.ts
+++ b/src/fast-check-default.ts
@@ -43,13 +43,13 @@ import { dedup, DedupValue } from './arbitrary/dedup';
 import { Arbitrary } from './check/arbitrary/definition/Arbitrary';
 import { Shrinkable } from './check/arbitrary/definition/Shrinkable';
 import { dictionary } from './arbitrary/dictionary';
-import { emailAddress } from './check/arbitrary/EmailArbitrary';
+import { emailAddress } from './arbitrary/emailAddress';
 import { double, float, DoubleConstraints, FloatConstraints } from './check/arbitrary/FloatingPointArbitrary';
 import { frequency, WeightedArbitrary, FrequencyValue, FrequencyContraints } from './arbitrary/frequency';
 import { compareBooleanFunc } from './arbitrary/compareBooleanFunc';
 import { compareFunc } from './arbitrary/compareFunc';
 import { func } from './arbitrary/func';
-import { domain } from './check/arbitrary/HostArbitrary';
+import { domain } from './arbitrary/domain';
 import { integer, IntegerConstraints } from './arbitrary/integer';
 import { maxSafeInteger } from './arbitrary/maxSafeInteger';
 import { maxSafeNat } from './arbitrary/maxSafeNat';
@@ -90,15 +90,11 @@ import { genericTuple } from './arbitrary/genericTuple';
 import { tuple } from './arbitrary/tuple';
 import { uuid } from './arbitrary/uuid';
 import { uuidV } from './arbitrary/uuidV';
-import {
-  webAuthority,
-  WebAuthorityConstraints,
-  webFragments,
-  webQueryParameters,
-  webSegment,
-  webUrl,
-  WebUrlConstraints,
-} from './check/arbitrary/WebArbitrary';
+import { webAuthority, WebAuthorityConstraints } from './arbitrary/webAuthority';
+import { webFragments } from './arbitrary/webFragments';
+import { webQueryParameters } from './arbitrary/webQueryParameters';
+import { webSegment } from './arbitrary/webSegment';
+import { webUrl, WebUrlConstraints } from './arbitrary/webUrl';
 
 import { AsyncCommand } from './check/model/command/AsyncCommand';
 import { Command } from './check/model/command/Command';


Test patch:
diff --git a/test/unit/check/arbitrary/EmailArbitrary.spec.ts b/test/unit/arbitrary/EmailArbitrary.spec.ts
similarity index 91%
rename from test/unit/check/arbitrary/EmailArbitrary.spec.ts
rename to test/unit/arbitrary/EmailArbitrary.spec.ts
index 04aead3f..3e6486b8 100644
--- a/test/unit/check/arbitrary/EmailArbitrary.spec.ts
+++ b/test/unit/arbitrary/EmailArbitrary.spec.ts
@@ -1,6 +1,6 @@
-import { emailAddress } from '../../../../src/check/arbitrary/EmailArbitrary';
+import { emailAddress } from '../../../src/arbitrary/emailAddress';
 
-import * as genericHelper from './generic/GenericArbitraryHelper';
+import * as genericHelper from '../check/arbitrary/generic/GenericArbitraryHelper';
 
 const isValidEmailRfc1123 = (t: string) => {
   // Taken from https://www.w3.org/TR/html5/forms.html#valid-e-mail-address
diff --git a/test/unit/check/arbitrary/HostArbitrary.spec.ts b/test/unit/arbitrary/HostArbitrary.spec.ts
similarity index 90%
rename from test/unit/check/arbitrary/HostArbitrary.spec.ts
rename to test/unit/arbitrary/HostArbitrary.spec.ts
index 07509c57..e63ab330 100644
--- a/test/unit/check/arbitrary/HostArbitrary.spec.ts
+++ b/test/unit/arbitrary/HostArbitrary.spec.ts
@@ -1,7 +1,8 @@
-import { domain, filterInvalidSubdomainLabel } from '../../../../src/check/arbitrary/HostArbitrary';
+import { domain } from '../../../src/arbitrary/domain';
+import { filterInvalidSubdomainLabel } from '../../../src/arbitrary/_internals/helpers/InvalidSubdomainLabelFiIter';
 
-import * as genericHelper from './generic/GenericArbitraryHelper';
-import fc from '../../../../lib/fast-check';
+import * as genericHelper from '../check/arbitrary/generic/GenericArbitraryHelper';
+import fc from '../../../lib/fast-check';
 
 const isValidDomain = (t: string) => {
   // According to https://www.ietf.org/rfc/rfc1034.txt
diff --git a/test/unit/check/arbitrary/WebArbitrary.spec.ts b/test/unit/arbitrary/WebArbitrary.spec.ts
similarity index 85%
rename from test/unit/check/arbitrary/WebArbitrary.spec.ts
rename to test/unit/arbitrary/WebArbitrary.spec.ts
index 287a1a6d..0ffbd22a 100644
--- a/test/unit/check/arbitrary/WebArbitrary.spec.ts
+++ b/test/unit/arbitrary/WebArbitrary.spec.ts
@@ -1,8 +1,8 @@
-import { webUrl } from '../../../../src/check/arbitrary/WebArbitrary';
+import { webUrl } from '../../../src/arbitrary/webUrl';
 
 import { URL } from 'url';
-import * as genericHelper from './generic/GenericArbitraryHelper';
-import fc from '../../../../lib/fast-check';
+import * as genericHelper from '../check/arbitrary/generic/GenericArbitraryHelper';
+import fc from '../../../lib/fast-check';
 
 const isValidUrl = (t: string) => {
   // Valid url given the specs defined by WHATWG URL Standard