{"id":15050646,"url":"https://github.com/lemonlabsuk/scala-uri","last_synced_at":"2025-05-15T14:03:24.203Z","repository":{"id":20949135,"uuid":"90947500","full_name":"lemonlabsuk/scala-uri","owner":"lemonlabsuk","description":"Simple scala library for building and parsing URIs","archived":false,"fork":false,"pushed_at":"2024-12-11T17:39:38.000Z","size":1725,"stargazers_count":306,"open_issues_count":10,"forks_count":37,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-04-15T03:52:22.163Z","etag":null,"topics":["scala","scala-uri","uri","url","url-parsing"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lemonlabsuk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-05-11T06:54:59.000Z","updated_at":"2025-03-18T10:35:10.000Z","dependencies_parsed_at":"2023-10-17T04:40:09.928Z","dependency_job_id":"b7b51e76-219a-43c6-9240-c27f315ad6e8","html_url":"https://github.com/lemonlabsuk/scala-uri","commit_stats":null,"previous_names":[],"tags_count":92,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lemonlabsuk%2Fscala-uri","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lemonlabsuk%2Fscala-uri/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lemonlabsuk%2Fscala-uri/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lemonlabsuk%2Fscala-uri/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lemonlabsuk","download_url":"https://codeload.github.com/lemonlabsuk/scala-uri/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249003955,"owners_count":21196794,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["scala","scala-uri","uri","url","url-parsing"],"created_at":"2024-09-24T21:28:39.843Z","updated_at":"2025-04-15T03:52:33.604Z","avatar_url":"https://github.com/lemonlabsuk.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003e [!IMPORTANT]  \n\u003e scala-uri is not currently being actively maintained in this repo\n\u003e \n\u003e There is a maintained fork at https://github.com/indoorvivants/scala-uri\n\n# scala-uri\n\n[![scala-uri CI](https://github.com/lemonlabsuk/scala-uri/actions/workflows/ci.yml/badge.svg)](https://github.com/lemonlabsuk/scala-uri/actions/workflows/ci.yml)\n[![codecov.io](http://codecov.io/github/lemonlabsuk/scala-uri/coverage.svg?branch=master)](https://codecov.io/gh/lemonlabsuk/scala-uri/branch/master)\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.lemonlabs/scala-uri_2.13/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.lemonlabs/scala-uri_2.12)\n[![Scala.js](https://www.scala-js.org/assets/badges/scalajs-1.0.0.svg)](#scalajs-support)\n[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/lemonlabsuk/scala-uri.svg)](http://isitmaintained.com/project/lemonlabsuk/scala-uri \"Average time to resolve an issue\")\n[![Percentage of issues still open](http://isitmaintained.com/badge/open/lemonlabsuk/scala-uri.svg)](http://isitmaintained.com/project/lemonlabsuk/scala-uri \"Percentage of issues still open\")\n\n`scala-uri` is a small Scala library that helps you work with URIs. It has the following features:\n\n * A [RFC 3986](https://www.ietf.org/rfc/rfc3986.txt) compliant [parser](#parsing) to parse URLs and URNs from Strings\n * URL [Builders](#building-urls) to create URLs from scratch\n * Ability to transform query strings with methods such as [filterQuery](#filterquery) and [mapQuery](#mapquery)\n * Ability to [replace](#replacing-query-string-parameters) and [remove](#removing-query-string-parameters) query string parameters\n * Ability to extract TLDs and [public suffixes](#public-suffixes) such as `.com` and `.co.uk` from hosts\n * Ability to render URLs in [punycode](#punycode)\n * Ability to [parse](#parsing-ips) IPv6 and IPv4 addresses\n * Support for [custom encoding](#custom-encoding) such as encoding [spaces as pluses](#encoding-spaces-as-pluses)\n * Support for [protocol relative urls](#protocol-relative-urls)\n * Support for [user information](#user-information) e.g. `ftp://user:password@mysite.com`\n * Support for [URNs](#parse-a-urn)\n * Support for [mailto](#mailto) URLs\n * Support for [data](#data-urls) URLs as defined in [RFC2397](https://tools.ietf.org/html/rfc2397)\n * Support for [git](#gitscp-style-urls) scp-like URLs \n * Support for [Scala.js](#scalajs-support)\n * Support for [cats](#cats-support)\n * No dependencies on existing web frameworks\n\nTo include it in your SBT project from maven central:\n```scala\n\"io.lemonlabs\" %% \"scala-uri\" % \"4.0.3\"\n```\n\n## Migration Guides\n\n * [4.0.0+](#3xx-to-4xx)\n * [3.0.0+](#2xx-to-3xx)\n * [2.0.0+](#1xx-to-2xx)\n * [Older versions](#1xx-to-15x)\n\nThere are also demo projects for both [scala](https://github.com/lemonlabsuk/scala-uri-demo) and [Scala.js](https://github.com/lemonlabsuk/scala-uri-scalajs-example) to help you get up and running quickly.\n\n## Parsing\n\n### Parse a URL\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Url\n\nval url = Url.parse(\"https://www.scala-lang.org\")\n```\n\nThe returned value has type `Url` with an underlying implementation of `AbsoluteUrl`, `RelativeUrl`,\n`UrlWithoutAuthority`, `ProtocolRelativeUrl` or `DataUrl`. If you know your URL will always be one of these types, you can\nuse the following `parse` methods to get a more specific return type\n\n```scala mdoc:reset\nimport io.lemonlabs.uri._\n\nval absoluteUrl = AbsoluteUrl.parse(\"https://www.scala-lang.org\")\nval relativeUrl = RelativeUrl.parse(\"/index.html\")\nval mailtoUrl = UrlWithoutAuthority.parse(\"mailto:test@example.com\")\nval protocolRelativeUrl = ProtocolRelativeUrl.parse(\"//www.scala-lang.org\")\nval dataUrl = DataUrl.parse(\"data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D\")\n```\n\n**Note:** scala-uri only supports parsing port numbers less than `Int.MaxValue`, deviating from RFC3986 which does\nnot impose a limit\n\n## Parse a URN\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Urn\n\nval urn = Urn.parse(\"urn:isbn:0981531687\")\nurn.scheme // This is \"urn\"\nurn.nid // This is \"isbn\"\nurn.nss // This is \"0981531687\"\n```\n\n## Parse a URI\n\nYou can use `Uri.parse` to parse URNs as well as URLs. `Url.parse` and `Urn.parse` are preferable as they return\na more specific return type\n\n## Building URLs\n\n`Url` provides an apply method with a bunch of optional parameters that can be used to build URLs\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.{Url, QueryString}\n\nval url = Url(scheme = \"http\", host = \"lemonlabs.io\", path = \"/opensource\")\nval url2 = Url(path = \"/opensource\", query = QueryString.fromPairs(\"param1\" -\u003e \"a\", \"param2\" -\u003e \"b\"))\n```\n\n## Transforming URLs\n\n### mapQuery\n\nThe `mapQuery` method will transform the Query String of a URI by applying the specified `PartialFunction` to each\nQuery String Parameter. Any parameters not matched in the `PartialFunction` will be left as-is.\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Url\n\nval uri = Url.parse(\"/scala-uri?p1=one\u0026p2=2\u0026p3=true\")\n\n// Results in /scala-uri?p1_map=one_map\u0026p2_map=2_map\u0026p3_map=true_map\nuri.mapQuery {\n  case (n, Some(v)) =\u003e (n + \"_map\", Some(v + \"_map\"))\n}\n```\n\nThe `mapQueryNames` and `mapQueryValues` provide a more convenient way to transform just Query Parameter names or values\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Url\n\nval uri = Url.parse(\"/scala-uri?p1=one\u0026p2=2\u0026p3=true\")\n\nuri.mapQueryNames(_.toUpperCase) // Results in /scala-uri?P1_map=one\u0026P2=2\u0026P3=true\nuri.mapQueryValues(_.replace(\"true\", \"false\")) // Results in /scala-uri?p1=one\u0026p2=2\u0026p3=false\n```\n\n### filterQuery\n\nThe `filterQuery` method will remove any Query String Parameters for which the provided Function returns false\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Url\n\nval uri = Url.parse(\"/scala-uri?p1=one\u0026p2=2\u0026p3=true\")\n\n// Results in /scala-uri?p2=2\nuri.filterQuery {\n  case (n, v) =\u003e n.contains(\"2\") \u0026\u0026 v.contains(\"2\")\n}\n\nuri.filterQuery(_._1 == \"p1\") // Results in /scala-uri?p1=one\n```\n\nThe `filterQueryNames` and `filterQueryValues` provide a more convenient way to filter just by Query Parameter name or value\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Url\n\nval uri = Url.parse(\"/scala-uri?p1=one\u0026p2=2\u0026p3=true\")\n\nuri.filterQueryNames(_ \u003e \"p1\") // Results in /scala-uri?p2=2\u0026p3=true\nuri.filterQueryValues(_.length == 1) // Results in /scala-uri?p2=2\n```\n\n### collectQuery\n\nThe `collectQuery` method will transform the Query String of a URI by applying the specified `PartialFunction` to each\nQuery String Parameter. Any parameters not matched in the `PartialFunction` will be removed.\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Url\n\nval uri = Url.parse(\"/scala-uri?p1=one\u0026p2=2\u0026p3=true\")\n\n// Results in /scala-uri?p1_map=one_map\nuri.collectQuery {\n  case (\"p1\", Some(v)) =\u003e (\"p1_map\", Some(v + \"_map\"))\n}\n```\n\n### Convert an Absolute URL to a Relative URL\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Url\n\nval absoluteUrl = Url.parse(\"http://www.example.com/example?a=b\")\nabsoluteUrl.toRelativeUrl // This is /example?a=b\n```\n\n### Convert a Relative URL to an Absolute URL\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Url\n\nval relativeUrl = Url.parse(\"/example?a=b\")\nrelativeUrl.withScheme(\"http\").withHost(\"www.example.com\") // This is http://www.example.com/example?a=b\n```\n\n## Redacting URLs\n\nIt is possible to print out redacted URLs to logs with sensitive information either removed or replaced with a placeholder\n\nReplacing with a placeholder:\n\n```scala mdoc:reset\nimport io.lemonlabs.uri._\nimport io.lemonlabs.uri.redact._\n\nval url = Url.parse(\"http://user:password@example.com?secret=123\u0026last=yes\")\n\n// This returns http://xxx:xxx@example.com?secret=xxx\u0026last=yes\nurl.toRedactedString(Redact.withPlaceholder(\"xxx\").params(\"secret\", \"other\").user().password())\n```\n\nRemoving:\n\n```scala mdoc:reset\nimport io.lemonlabs.uri._\nimport io.lemonlabs.uri.redact._\n\nval url = Url.parse(\"http://user:password@example.com?secret=123\u0026other=true\")\n\n// This returns http://example.com\nurl.toRedactedString(Redact.byRemoving.allParams().userInfo())\n```\n\n## Url Equality\n\nBy default scala-uri only considers `Url`s equal if query parameters are in the same order:\n\n```scala mdoc:reset\nimport io.lemonlabs.uri._\n\nval urlOne = Url.parse(\"https://example.com?a=1\u0026b=2\")\nval urlTwo = Url.parse(\"https://example.com?b=2\u0026a=1\")\n\nurlOne == urlTwo // this is false\n\nval urlThree = Url.parse(\"https://example.com?a=1\u0026b=2\")\n\nurlOne == urlThree // this is true\n```\n\nFor use-cases where query parameter order is not important, the `equalsUnordered` can be used\n\n```scala mdoc\nurlOne.equalsUnordered(urlTwo) // this is true\n```\n\nWhen using cats for equality testing, parameter order will also be considered by default\n\n```scala mdoc\nimport cats.implicits._\n\nurlOne === urlTwo   // this is false\nurlOne === urlThree // this is true\n```\n\nWith cats, query parameter order can be ignored for equality checks with the following import:\n\n```scala mdoc\nimport io.lemonlabs.uri.Url.unordered._\n\nurlOne === urlTwo   // this is true\nurlOne === urlThree // this is true\n```\n\nNote: depending on the type you are comparing, you will need to import a different cats `Eq` instance. \nThe following are available:\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Uri.unordered._\nimport io.lemonlabs.uri.Url.unordered._\nimport io.lemonlabs.uri.RelativeUrl.unordered._\nimport io.lemonlabs.uri.UrlWithAuthority.unordered._\nimport io.lemonlabs.uri.ProtocolRelativeUrl.unordered._\nimport io.lemonlabs.uri.AbsoluteUrl.unordered._\nimport io.lemonlabs.uri.UrlWithoutAuthority.unordered._\nimport io.lemonlabs.uri.SimpleUrlWithoutAuthority.unordered._\nimport io.lemonlabs.uri.QueryString.unordered._\n```\n\n## Pattern Matching URIs\n\n```scala mdoc:reset\nimport io.lemonlabs.uri._\n\nval uri: Uri = Uri.parse(\"...\")\nuri match {\n    case Uri(path) =\u003e // Matches Urns and Urls\n    case Urn(path) =\u003e // Matches Urns\n    case Url(path, query, fragment) =\u003e // Matches Urls\n    case RelativeUrl(path, query, fragment) =\u003e // Matches RelativeUrls\n    case UrlWithAuthority(authority, path, query, fragment) =\u003e // Matches AbsoluteUrl and ProtocolRelativeUrl\n    case AbsoluteUrl(scheme, authority, path, query, fragment) =\u003e // Matches AbsoluteUrl\n    case ProtocolRelativeUrl(authority, path, query, fragment) =\u003e // Matches ProtocolRelativeUrl\n    case UrlWithoutAuthority(scheme, path, query, fragment) =\u003e // Matches UrlWithoutAuthorityUrl\n    case DataUrl(mediaType, base64, data) =\u003e // Matches DataUrl\n    case ScpLikeUrl(user, host, path) =\u003e // Matches ScpLikeUrl\n}\n```\n\n### Exhaustive matching\n\nIn some cases `scalac` will be able to detect instances where not all cases are being matched. For example:\n\n```scala mdoc:reset\nimport io.lemonlabs.uri._\n\nUri.parse(\"/test\") match {\n  case u: Url =\u003e println(u.toString)\n}\n```\n\nresults in the following compiler warning, because `Uri.parse` can return `Urn`s as well as `Url`s:\n\n```\n\u003cconsole\u003e:15: warning: match may not be exhaustive.\nIt would fail on the following input: Urn(_)\n```\n\nIn this instance, using `Url.parse` instead of `Uri.parse` would fix this warning\n\n\n## Hosts\n\n### Parsing Hosts\n\nYou can parse a String representing the host part of a URI with `Host.parse`. The return type is `Host` with an\nunderling implementation of `DomainName`, `IpV4` or `IpV6`.\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Host\n\nval host = Host.parse(\"lemonlabs.io\")\n```\n\n#### Parsing IPs\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.{IpV4, IpV6}\n\nval ipv4 = IpV4.parse(\"13.32.214.142\")\nval ipv6 = IpV6.parse(\"[1:2:3:4:5:6:7:8]\")\n```\n\n### Matching Hosts\n\n```scala mdoc:reset\nimport io.lemonlabs.uri._\n\nval host: Host = Host.parse(\"...\")\nhost match {\n    case Host(host) =\u003e // Matches DomainNames, IpV4s and IpV6s\n    case DomainName(host) =\u003e // Matches DomainNames\n    case ip: IpV4 =\u003e // Matches IpV4s\n    case ip: IpV6 =\u003e // Matches IpV6s\n}\n```\n\n## Paths\n\n### Matching Paths\n\n```scala mdoc:reset\nimport io.lemonlabs.uri._\n\nval path: Path = Path.parse(\"...\")\npath match {\n    case Path(parts) =\u003e // Matches any path\n    case AbsolutePath(parts) =\u003e // Matches any path starting with a slash\n    case RootlessPath(parts) =\u003e // Matches any path that *doesn't* start with a slash\n\n    case PathParts(\"a\", \"b\", \"c\") =\u003e // Matches \"/a/b/c\" and \"a/b/c\"\n    case PathParts(\"a\", \"b\", _*) =\u003e // Matches any path starting with \"/a/b\" or \"a/b\"\n\n    case EmptyPath() =\u003e // Matches \"\"\n    case PathParts() =\u003e // Matches \"\" and \"/\"\n\n    case UrnPath(\"nid\", \"nss\") =\u003e // Matches a URN Path \"nid:nss\"\n}\n```\n\n## URL Percent Encoding\n\nBy Default, `scala-uri` will URL percent encode paths and query string parameters. To prevent this, you can call the `uri.toStringRaw` method:\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Url\n\nval uri = Url.parse(\"http://example.com/path with space?param=üri\")\n\nuri.toString // This is: http://example.com/path%20with%20space?param=%C3%BCri\n\nuri.toStringRaw // This is: http://example.com/path with space?param=üri\n```\n\nThe characters that `scala-uri` will percent encode by default can be found [here](https://github.com/lemonlabsuk/scala-uri/blob/master/shared/src/main/scala/io/lemonlabs/uri/encoding/PercentEncoder.scala#L51). You can modify which characters are percent encoded like so:\n\nOnly percent encode the hash character:\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.config.UriConfig\nimport io.lemonlabs.uri.encoding._\n\nimplicit val config: UriConfig = UriConfig(encoder = percentEncode('#'))\n```\n\nPercent encode all the default chars, except the plus character:\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.config.UriConfig\nimport io.lemonlabs.uri.encoding._\n\nimplicit val config: UriConfig = UriConfig(encoder = percentEncode -- '+')\n```\n\nEncode all the default chars, and also encode the letters a and b:\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.config.UriConfig\nimport io.lemonlabs.uri.encoding._\n\nimplicit val config: UriConfig = UriConfig(encoder = percentEncode ++ ('a', 'b'))\n```\n\n### Encoding spaces as pluses\n\nThe default behaviour with scala-uri, is to encode spaces as `+` in the querystring and as `%20` elsewhere in the URL.\n\nIf you instead wish spaces to be encoded as `%20` in the query, then simply add the following `implicit val` to your code:\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Url\nimport io.lemonlabs.uri.config.UriConfig\nimport io.lemonlabs.uri.encoding._\nimport io.lemonlabs.uri.encoding.PercentEncoder._\n\nimplicit val config: UriConfig = UriConfig.default.copy(queryEncoder = PercentEncoder())\n\nval uri = Url.parse(\"http://theon.github.com?test=uri with space\")\nuri.toString // This is http://theon.github.com?test=uri%20with%20space\n```\n\nThe default behaviour with scala-uri, is to decode `+` in query string parameters to spaces and to leave it as a literal `+` elsewhere in the URL.\n\nIf you instead wish `+` to be left as `+` in the query, then simply add the following `implicit val` to your code:\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Url\nimport io.lemonlabs.uri.config.UriConfig\nimport io.lemonlabs.uri.decoding._\n\nimplicit val config: UriConfig = UriConfig.default.copy(queryDecoder = PercentDecoder)\n\nval uri = Url.parse(\"http://theon.github.com?test=uri+with+plus\")\nuri.query.param(\"test\") // This is Some(\"uri+with+plus\")\n```\n\n### Custom encoding\n\nIf you would like to do some custom encoding for specific characters, you can use the `encodeCharAs` encoder.\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Url\nimport io.lemonlabs.uri.config.UriConfig\nimport io.lemonlabs.uri.encoding._\n\nimplicit val config: UriConfig = UriConfig(encoder = percentEncode + encodeCharAs(' ', \"_\"))\n\nval uri = Url.parse(\"http://theon.github.com/uri with space\")\nuri.toString // This is http://theon.github.com/uri_with_space\n```\n\n## URL Percent Decoding\n\nBy Default, `scala-uri` will URL percent decode paths and query string parameters during parsing:\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Url\n\nval uri = Url.parse(\"http://example.com/i-have-%25been%25-percent-encoded\")\n\nuri.toString // This is: http://example.com/i-have-%25been%25-percent-encoded\nuri.toStringRaw // This is: http://example.com/i-have-%been%-percent-encoded\n```\n\nTo prevent this, you can bring the following implicit into scope:\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Url\nimport io.lemonlabs.uri.config.UriConfig\nimport io.lemonlabs.uri.decoding.NoopDecoder\n\nimplicit val c: UriConfig = UriConfig(decoder = NoopDecoder)\n\nval uri = Url.parse(\"http://example.com/i-havent-%been%-percent-encoded\")\n\nuri.toString // This is: http://example.com/i-havent-%25been%25-percent-encoded\nuri.toStringRaw // This is: http://example.com/i-havent-%been%-percent-encoded\n```\n\n#### Invalid Percent Encoding\n\nIf your Uri contains invalid percent encoding, by default scala-uri will throw a `UriDecodeException`:\n\n```scala crash\nUrl.parse(\"/?x=%3\") // This throws a UriDecodeException\n```\n\nYou can configure scala-uri to instead ignore invalid percent encoding and *only* percent decode correctly percent encoded values like so:\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Url\nimport io.lemonlabs.uri.config.UriConfig\nimport io.lemonlabs.uri.decoding.PercentDecoder\n\nimplicit val c: UriConfig = UriConfig(\n  decoder = PercentDecoder(ignoreInvalidPercentEncoding = true)\n)\nval uri = Url.parse(\"/?x=%3\")\nuri.toString // This is /?x=%253\nuri.toStringRaw // This is /?x=%3\n```\n\n## Replacing Query String Parameters\n\nIf you wish to replace all existing query string parameters with a given name, you can use the `Url.replaceParams()` method:\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Url\n\nval uri = Url.parse(\"http://example.com/path?param=1\")\nval newUri = uri.replaceParams(\"param\", \"2\")\n\nnewUri.toString // This is: http://example.com/path?param=2\n```\n\n## Removing Query String Parameters\n\nIf you wish to remove all existing query string parameters with a given name, you can use the `uri.removeParams()` method:\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Url\n\nval uri = Url.parse(\"http://example.com/path?param=1\u0026param2=2\")\nval newUri = uri.removeParams(\"param\")\n\nnewUri.toString // This is: http://example.com/path?param2=2\n```\n\n## Omitting Query Parameters with value `None`\n\nscala-uri has support for not rendering query parameters that have a value of `None`. Set `renderQuery = ExcludeNones`\nin your `UriConfig` and make it visible in the scope where you parse/create your `Url`\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Url\nimport io.lemonlabs.uri.config._\n\nimplicit val config: UriConfig = UriConfig(renderQuery = ExcludeNones)\n\nval url = Url.parse(\"http://github.com/lemonlabsuk\").addParams(\"a\" -\u003e Some(\"some\"), \"b\" -\u003e None)\nurl.toString // This is http://github.com/lemonlabsuk?a=some\n```\n\n## Get query string parameters\n\nTo get the query string parameters as a `Map[String,Seq[String]]` you can do the following:\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Url\n\nval uri = Url.parse(\"http://example.com/path?a=b\u0026a=c\u0026d=e\")\nuri.query.paramMap // This is: Map(\"a\" -\u003e Vector(\"b\", \"c\"), \"d\" -\u003e Vector(\"e\"))\n```\n\n## User Information\n\n`scala-uri` supports user information (username and password) encoded in URLs.\n\nParsing URLs with user information:\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Url\n\nval url = Url.parse(\"http://user:pass@host.com\")\nurl.user // This is Some(\"user\")\nurl.password // This is Some(\"pass\")\n```\n\nModifying user information:\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.AbsoluteUrl\n\nval url = AbsoluteUrl.parse(\"http://host.com\")\nurl.withUser(\"jack\") // URL is now http://jack@host.com\n```\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.AbsoluteUrl\n\nval url = AbsoluteUrl.parse(\"http://user:pass@host.com\")\nurl.withPassword(\"secret\") // URL is now http://user:secret@host.com\n```\n\n**Note:** that using clear text passwords in URLs is [ill advised](http://tools.ietf.org/html/rfc3986#section-3.2.1)\n\n## Protocol Relative URLs\n\n[Protocol Relative URLs](http://paulirish.com/2010/the-protocol-relative-url/) are supported in `scala-uri`. A `Uri` object with a protocol of `None`, but a host of `Some(x)` will be considered a protocol relative URL.\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Url\n\nval uri = Url.parse(\"//example.com/path\") // Return type is Url\nuri.schemeOption // This is: None\nuri.hostOption // This is: Some(\"example.com\")\n```\n\nUse `ProtocolRelativeUrl.parse` if you know your URL will always be Protocol Relative:\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.ProtocolRelativeUrl\n\nval uri = ProtocolRelativeUrl.parse(\"//example.com/path\") // Return type is ProtocolRelativeUrl\nuri.schemeOption // This is: None\nuri.host // This is: \"example.com\"\n```\n\n## Character Sets\n\nBy default `scala-uri` uses `UTF-8` charset encoding:\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Url\n\nval uri = Url.parse(\"http://theon.github.com/uris-in-scala.html?chinese=网址\")\nuri.toString // This is http://theon.github.com/uris-in-scala.html?chinese=%E7%BD%91%E5%9D%80\n```\n\nThis can be changed like so:\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.config.UriConfig\nimport io.lemonlabs.uri.Url\n\nimplicit val conf: UriConfig = UriConfig(charset = \"GB2312\")\n\nval uri = Url.parse(\"http://theon.github.com/uris-in-scala.html?chinese=网址\")\nuri.toString // This is http://theon.github.com/uris-in-scala.html?chinese=%CD%F8%D6%B7\n```\n\n## Subdomains\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Url\n\n// This returns Some(\"www\")\nUrl.parse(\"http://www.example.com/blah\").subdomain\n\n// This returns Some(\"a.b.c\")\nUrl.parse(\"http://a.b.c.example.com/blah\").subdomain\n\n// This returns None\nUrl.parse(\"http://example.com/blah\").subdomain\n\n// This returns Vector(\"a\", \"a.b\", \"a.b.c\", \"a.b.c.example\")\nUrl.parse(\"http://a.b.c.example.com/blah\").subdomains\n\n// This returns Some(\"a\")\nUrl.parse(\"http://a.b.c.example.com/blah\").shortestSubdomain\n\n// This returns Some(\"a.b.c.example\")\nUrl.parse(\"http://a.b.c.example.com/blah\").longestSubdomain\n```\n\nThese methods return `None` or `Vector.empty` for URLs without a Host (e.g. Relative URLs)\n\n## Apex Domains\n\nThe method `apexDomain` returns the [apex domain](https://help.github.com/articles/about-supported-custom-domains/#apex-domains)\nfor the URL (e.g. `example.com` for `http://www.example.com/path`)\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Url\n\nval uri = Url.parse(\"http://www.google.co.uk/blah\")\nuri.apexDomain // This returns Some(\"google.co.uk\")\n```\n\n## Public Suffixes\n\n`scala-uri` uses the list of public suffixes from [publicsuffix.org](https://publicsuffix.org) to allow you to identify\nthe TLD of your absolute URIs.\n\nThe `publicSuffix` method returns the longest public suffix from your URI\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Url\n\nval uri = Url.parse(\"http://www.google.co.uk/blah\")\nuri.publicSuffix // This returns Some(\"co.uk\")\n```\n\nThe `publicSuffixes` method returns all the public suffixes from your URI\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Url\n\nval uri = Url.parse(\"http://www.google.co.uk/blah\")\nuri.publicSuffixes // This returns Vector(\"co.uk\", \"uk\")\n```\n\nThese methods return `None` and `Vector.empty`, respectively for URLs without a Host (e.g. Relative URLs)\n\n## Punycode\n\nSee [RFC 3490](http://www.ietf.org/rfc/rfc3490.txt)\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.Url\n\nval url = Url.parse(\"https://はじめよう.みんな/howto.html\")\nurl.toStringPunycode // This returns \"https://xn--p8j9a0d9c9a.xn--q9jyb4c/howto.html\"\n```\n\n## mailto\n\nMailto URLs are best parsed with `UrlWithoutAuthority.parse`, but can also be parsed with `Url.parse`\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.UrlWithoutAuthority\n\nval mailto = UrlWithoutAuthority.parse(\"mailto:someone@example.com?subject=Hello\")\nmailto.scheme // This is Some(mailto\")\nmailto.path // This is \"someone@example.com\"\nmailto.query.param(\"subject\") // This is Some(\"Hello\")\n```\n\n## Data URLs\n\nData URLs are defined in [RFC2397](https://tools.ietf.org/html/rfc2397)\n\n### Base64 encoded data URLs\n\n```scala mdoc:reset\nimport java.io.ByteArrayInputStream\nimport io.lemonlabs.uri.DataUrl\nimport javax.imageio.ImageIO\n\n// A data URL containing a PNG image of a red dot\nval dataUrl = DataUrl.parse(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==\")\n\ndataUrl.scheme // This is \"data\"\ndataUrl.mediaType.value // This is \"image/png\"\ndataUrl.base64 // This is true\n\n// Convert the image data to a java.awt.image.BufferedImage\nval image = ImageIO.read(new ByteArrayInputStream(dataUrl.data))\n```\n\n### Percent encoded data URLs\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.DataUrl\n\nval dataUrl = DataUrl.parse(\"data:text/plain;charset=UTF-8;page=21,the%20data:1234,5678\")\n\ndataUrl.mediaType.value // This is text/plain\ndataUrl.mediaType.charset // This is UTF-8\ndataUrl.mediaType.parameters // This is Vector(\"charset\" -\u003e \"UTF-8\", \"page\" -\u003e \"21\")\ndataUrl.base64 // This is false\n\ndataUrl.dataAsString // This is \"the data:1234,5678\"\n```\n\n## git/scp style URLs \n\ngit/scp style URLs can be parsed like so:\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.ScpLikeUrl\n\nval url = ScpLikeUrl.parse(\"git@github.com:lemonlabsuk/scala-uri.git\")\nurl.user // This is Some(\"git\")\nurl.host.toString // This is \"github.com\"\nurl.path.toString // This is \"lemonlabsuk/scala-uri.git\"\n```\n\nNote that `ScpLikeUrl.parse`, should only be used for git URLs with scp-like syntax (with a `:`\nbetween the host and path). For all other git URLs, `AbsoluteUrl.parse` or `Url.parse` should \nbe used:\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.AbsoluteUrl\n\nval gitUrl    = AbsoluteUrl.parse(\"git://github.com/lemonlabsuk/scala-uri.git\")\nval gitSshUrl = AbsoluteUrl.parse(\"git+ssh://git@github.com/lemonlabsuk/scala-uri.git\")\nval sshUrl    = AbsoluteUrl.parse(\"ssh://git@github.com/lemonlabsuk/scala-uri.git\")\nval httpsUrl  = AbsoluteUrl.parse(\"https://github.com/lemonlabsuk/scala-uri.git\")\n```\n\n## Typesafe URL builder DSL\n\nThe version of DSL which relies on the types to render urls providing better control over\nthe way values would be translated to url parts.\n\nIt is possible to use arbitrary types as parts of the url:  \n\n### Query Strings\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.typesafe._\nimport io.lemonlabs.uri.typesafe.dsl._\n\nfinal case class Foo(a: Int, b: String)\n\nobject Foo {\n  implicit val traversableParams: TraversableParams[Foo] = TraversableParams.product\n}\n\nval uri = \"http://theon.github.com/scala-uri\" addParams Foo(a = 1, b = \"bar\")\nuri.toString //This is: http://theon.github.com/scala-uri?a=1\u0026b=bar\n```\n\n### Query String Values\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.typesafe._\nimport io.lemonlabs.uri.typesafe.dsl._\n\nsealed trait Bar {\n  def name: String\n}\n\ncase object A extends Bar {\n  val name: String = \"A\"\n}\n\ncase object B extends Bar {\n  val name: String = \"B\"\n}\n\nobject Bar {\n  implicit val queryValue: QueryValue[Bar] = QueryValue.derive[Bar].by(_.name)\n}\n\nval uri = \"http://theon.github.com/scala-uri\" ? (\"foo\" -\u003e A)\nuri.toString //This is: http://theon.github.com/scala-uri?foo=A\n```\n\n### Path Parts\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.typesafe._\nimport io.lemonlabs.uri.typesafe.dsl._\n\nfinal case class Foo(a: String, b: Int)\n\nobject Foo {\n  implicit val pathPart: PathPart[Foo] = (foo: Foo) =\u003e s\"${foo.a}/${foo.b}\"\n}\n\n\nval uri = \"http://theon.github.com\" / \"scala-uri\" / Foo(a = \"user\", b = 1)\nuri.toString //This is: http://theon.github.com/scala-uri/user/1\n```\n\n### Fragments\n\n```scala mdoc:reset\nimport io.lemonlabs.uri.typesafe._\nimport io.lemonlabs.uri.typesafe.dsl._\n\nfinal case class Foo(a: String, b: Int)\nobject Foo {\n  implicit val fragment: Fragment[Foo] = (foo: Foo) =\u003e Some(s\"${foo.a}-${foo.b}\")\n}\n\nval uri5 = \"http://theon.github.com/scala-uri\" `#` Foo(a = \"user\", b = 1)\nuri5.toString //This is: http://theon.github.com/scala-uri#user-1\n```\n\n## Scala.js support\n\nSee [scala-uri-scalajs-example](https://github.com/lemonlabsuk/scala-uri-scalajs-example) for usage\n\n## Cats Support\n\nscala-uri provides type class instances of `cats.Eq`, `cats.Show` and `cats.Order` for:\n`Uri `, `Url`, `RelativeUrl`, `UrlWithAuthority`, `ProtocolRelativeUrl`, `AbsoluteUrl`, \n`UrlWithoutAuthority`, `SimpleUrlWithoutAuthority`, `DataUrl`, `ScpLikeUrl`, `Urn`, `Authority`, `UserInfo`, \n`Host`, `DomainName`, `IpV4`, `IpV6`, `MediaType`, `Path`, `UrlPath`, `AbsoluteOrEmptyPath`, \n`RootlessPath`, `AbsolutePath`, `UrnPath`, `QueryString`\n\nThe type class instances exist in the companion objects for these types.\n\n## Including scala-uri your project\n\n`scala-uri` `4.x.x` is currently built with support for Scala `3`, Scala `2.13.x`, Scala `2.12.x` and Scala.js `1.1.0+`\n`scala-uri` `3.x.x` is currently built with support for Scala `2.13.x`, Scala `2.12.x` and Scala.js `1.1.0+` \n\n * For `2.11.x` support use `scala-uri` `1.4.10` from branch [`1.4.x`](https://github.com/lemonlabsuk/scala-uri/tree/1.4.x)\n * For `2.10.x` support use `scala-uri` `0.4.17` from branch [`0.4.x`](https://github.com/lemonlabsuk/scala-uri/tree/0.4.x)\n * For `2.9.x` support use `scala-uri` `0.3.6` from branch [`0.3.x`](https://github.com/lemonlabsuk/scala-uri/tree/0.3.x)\n * For Scala.js `1.x.x` support, use `scala-uri` `4.0.0`\n * For Scala.js `0.6.x` support, use `scala-uri` `2.2.3`\n\nRelease builds are available in maven central. For SBT users just add the following dependency:\n\n```scala\n\"io.lemonlabs\" %% \"scala-uri\" % \"4.0.3\"\n```\n\nFor maven users you should use (for 2.13.x):\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.lemonlabs\u003c/groupId\u003e\n    \u003cartifactId\u003escala-uri_2.13\u003c/artifactId\u003e\n    \u003cversion\u003e4.0.3\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n# Contributions\n\nContributions to `scala-uri` are always welcome. Check out the [Contributing Guidelines](https://github.com/lemonlabsuk/scala-uri/blob/master/README.md)\n\n# Migration guides\n\n## 3.x.x to 4.x.x\n\n * Scala 3 support has been added. Scala 2.13 and 2.12 support remain\n * *Binary Incompatibility*: [4765b4e](https://github.com/lemonlabsuk/scala-uri/commit/4765b4e6714a87d53ba0ae9bf58810e1b8be63d5#diff-e33d822268175cadb9ff5e2057fac2042db550f251da28d834f13b7bb44cf09aL45)\n   removed a single `UriConfig.copy()` overload.\n   This overload existed only to maintain binary compatibility with an older version of scala-uri\n   Use the remaining `copy()` method on `UriConfig` instead  \n * *Binary Incompatibility*: [4765b4e](https://github.com/lemonlabsuk/scala-uri/commit/4765b4e6714a87d53ba0ae9bf58810e1b8be63d5#diff-e33d822268175cadb9ff5e2057fac2042db550f251da28d834f13b7bb44cf09aL20)\n   removed a single `UriConfig` overloaded constructor.\n   This overload existed only to maintain binary compatibility with an older version of scala-uri\n   Use the remaining constructor or `apply` method remaining on `UriConfig` instead\n\n## 2.x.x to 3.x.x\n\n * *Backwards Incompatible*: The space character is now encoded to `+` instead of `%20` in query string parameters by default.   \n * *Backwards Incompatible*: The `+` method in `io.lemonlabs.uri.encoding.UriEncoder`, now chains encoders in the opposite order to be more intuitive.\n   E.g. `a + b` will encode with encoder `a` first, followed by encoder `b`\n * *Binary Incompatibility*: The following deprecated classes have now been removed:\n    * `io.lemonlabs.uri.inet.PublicSuffixTrie`\n    * `io.lemonlabs.uri.inet.Trie`\n    * `io.lemonlabs.uri.dsl.*`\n * *Binary Incompatibility*: `Url.addParam(\"key\", \"value\")` and `Url.addParam(\"key\" -\u003e \"value\")` \n   now has a return type `Self` rather than `Url`. \n   E.g. `AbsoluteUrl.addParam` now has a return type of `AbsoluteUrl` and `RelativeUrl.addParam` has a return type of `RelativeUrl`\n\n## 1.x.x to 2.x.x\n\n * scala-uri no longer depends on a JSON library.\n * *Binary Incompatibility*: The case class `UrlWithoutAuthority` has been renamed `SimpleUrlWithoutAuthority`.\n   There is now a trait called `UrlWithoutAuthority`. This trait has a companion object with `apply`, `unapply` and `parse`\n   methods, so it mostly can be used in the same way as the previous case class.\n * *Binary Incompatibility*: Parsing a Data URL will now return an instance of [`DataUrl`](#data-urls) rather than `UrlWithoutAuthority`\n * *Binary Incompatibility*: `UserInfo.user` is now of type `String` rather than `Option[String]`\n * *Binary Incompatibility*: `Authority.userInfo` is now of type `Option[UserInfo]`\n * *Binary Incompatibility*: `UserInfo.empty` method removed\n * *Binary Incompatibility*: `QueryString.fromPairOptions` removed. Use `QueryString.fromPairs` instead.\n * *Binary Incompatibility*: `Url.withQueryStringOptionValues` removed. Use `withQueryString` instead.\n * *Binary Incompatibility*: `Url.addParamsOptionValues` and `QueryString.addParamsOptionValues` have been renames to `addParams`\n * `TypesafeUrlDsl`\n    * *Binary Incompatibility*: `withParams[A: TraversableParams](params: A)` renamed to `addParams`\n    * `/(PathPart)` no longer splits the part by slash. If you want to add multiple path parts use `/(TraversablePathParts)` instead\n * Type Classes\n   * *Binary Incompatibility*: `Fragment[A].fragment` returns `Option[String]` rather than `String`\n   * *Binary Incompatibility*: `Url.withFragment` now takes argument of type `T: Fragment` rather than `String` and `Option[String]` \n     Type Class instances are provided the method can be used with `String` and `Option[String]` values just as before\n   * *Binary Incompatibility*: `Url.addPathParts` and `Url.addPathPart` now takes arguments of type `P: TraversablePathParts` or `P: PathPart` rather than `Iterable[String]` or `String`\n     Type Class instances are provided the methods can be used with `String` and `Iterable[String]` values just as before\n   * *Binary Incompatibility*: `Url.withQueryString`, `Url.addParam`, `Url.addParams`, `Url.replaceParams`, `Url.removeParams`, \n     `Url.mapQuery`, `Url.flatMapQuery`, `Url.collectQuery`, `Url.mapQueryNames` and `Url.mapQueryValues` now takes argument of type `KV: QueryKeyValue`, `K: QueryKey` or `V: QueryValue` rather than `(String, String)` or `String`\n     Type Class instances are provided the methods can be used with `(String, String)` or `String` values just as before   \n * The [URL builder DSL](#url-builder-dsl) has been deprecated in favour of the [Typesafe URL builder DSL](#typesafe-url-builder-dsl)\n * `Authority.parse` no longer expects it's string argument to start with `//`, as this is not part of the Authority, it is a delimiter. See [RFC 3986](https://www.ietf.org/rfc/rfc3986.txt)\n * `UserInfo.parse` no longer expects it's string argument to end with a `@`, as this is not part of the UserInfo, it is a delimiter. See [RFC 3986](https://www.ietf.org/rfc/rfc3986.txt)\n * `QueryString.toString` no longer returns a leading `?`, as this is not part of the query string, it is a delimiter. See [RFC 3986](https://www.ietf.org/rfc/rfc3986.txt) \n * Forward slashes in paths are now percent encoded by default.\n   This means `Url.parse(\"/%2F/\").toString` returns `\"/%2F/\"` rather than `///` in previous versions\n   To return to the previous behavior, you can bring a `UriConfig` like so into scope\n   ```scala mdoc:reset\n    import io.lemonlabs.uri.encoding.PercentEncoder._\n    implicit val c = UriConfig.default.copy(pathEncoder = PercentEncoder(PATH_CHARS_TO_ENCODE - '/'))\n   ```\n\n## 1.x.x to 1.5.x\n\n * scala 2.11 support dropped, please upgrade to 2.12 or 2.13\n\n## 0.5.x to 1.x.x\n\nThanks to [@evanbennett](https://github.com/evanbennett). `1.x.x` is inspired by his fork [here](https://github.com/evanbennett/scala-uri)\nand discussion [here](https://github.com/NET-A-PORTER/scala-uri/pull/113).\n\n * Package change from `com.netaporter.uri` to `io.lemonlabs.uri`\n * The single `Uri` case class has now been replaced with a class hierarchy. Use the most specific class in this\n   hierarchy that fits your use case\n * `Uri` used to be a case class, but the replacements `Uri` and `Url` are now traits. This means they no longer\n   have a `copy` method. Use the `with` methods instead (e.g. `withHost`, `withPath` etc)\n * `host` method on `Url` now has return type `Host` rather than `String`. You may have to change `url.host` to `url.host.toString`\n * `path` method on `Url` now has return type `Path` rather than `String`. You may have to change `url.path` to `url.path.toString`\n * Changed parameter value type from `Any` to `String` in methods `addParam`, `addParams`, `replaceParams`.\n   Please now call `.toString` before passing non String types to these methods\n * Changed parameter value type from `Option[Any]` to `Option[String]` in method `replaceAll`.\n   Please now call `.toString` before passing non String types to this method\n * Query string parameters with a value of `None` will now be rendered with no equals sign by default (e.g. `?param`).\n   Previously some methods (such as `?`, `\u0026`, `\\?`, `addParam` and `addParams`) would not render parameters with a value of `None` at all.\n   In 1.x.x, this behaviour can be achieved by using the [`renderQuery`](#omitting-query-parameters-with-value-none) config option.\n * In most cases `Url.parse` should be used instead of `Uri.parse`. See all parse methods [here](#parse-a-url)\n * `scheme` is now called `schemeOption` on `Uri`. If you have an instance of `AbsoluteUrl` or `ProtocolRelativeUrl`\n   there is still `scheme` method but it returns `String` rather than `Option[String]`\n * `protocol` method has been removed from `Uri`. Use `schemeOption` instead\n * Type changed from `Seq` to `Vector` for:\n   * `subdomains`, `publicSuffixes`, `params` return type\n   * `removeAll` and `removeParams` argument types\n   * `params` field in `QueryString`\n   * `paramMap` and `pathParts` fields in `Uri`, now `Url`\n * Methods `addParam` and `addParams`  that took Option arguments are now called `addParamOptionValue` and `addParamsOptionValues`\n * Method `replaceAllParams` has been replaced with `withQueryString` or `withQueryStringOptionValues`\n * Method `removeAllParams` has been replaced with `withQueryString(QueryString.empty)`\n * Method `subdomain` has been removed from the Scala.js version. The implementation was incorrect and did not\n   match the JVM version of `subdomain`. Once public suffixes are supported for the Scala.js version, a correct\n   implementation of `subdomain` can be added\n * Implicit `UriConfig`s now need to be where your `Uri`s are parsed/constructed, rather than where they are rendered\n * Method `hostParts` has been removed from `Uri`. This method predated `publicSuffix` and `subdomain` which are more\n   useful methods for pulling apart a host\n * Field `pathStartsWithSlash` removed from `Uri`. This was only intended to be used internally. You can now instead\n   check if `Uri.path` is an instance of `AbsolutePath` to determine if the path will start with slash\n\n## 0.4.x to 0.5.x\n\n * Matrix parameters have been removed. If you still need this, raise an issue\n * scala 2.10 support dropped, please upgrade to 2.11 or 2.12 to use scala-uri 0.5.x\n * Scala.js support added\n\n## 0.3.x to 0.4.x\n\n * Package changes / import changes\n  * All code moved from `com.github.theon` package to `com.netaporter` package\n  * `scala-uri` has been organised into the following packages: `encoding`, `decoding`, `config` and `dsl`. You will need to update import statments.\n * Name changes\n  * `PermissiveDecoder` renamed to `PermissivePercentDecoder`\n  * `QueryString` and `MatrixParams` constructor argument `parameters` shortened to `params`\n  * `Uri.parseUri` renamed to `Uri.parse`\n  * `protocol` constructor arg in `Uri` renamed to `scheme`\n  * `Querystring` renamed to `QueryString`\n * Query String constructor argument `parameters` changed type from `Map[String, List[String]]` to `Seq[(String,String)]`\n * `Uri` constructor argument `pathParts` changed type from `List` to `Vector`\n * `Uri` method to add query string parameters renamed from `params` to `addParams`. Same with `matrixParams` -\u003e `addMatrixParams`\n * `PercentEncoderDefaults` object renamed to `PercentEncoder` companion object.\n * Copy methods `user`/`password`/`port`/`host`/`scheme` now all prefixed with `with`, e.g. `withHost`\n * New `UriConfig` case class used to specify encoders, decoders and charset to be used. See examples in [Custom encoding](#custom-encoding), [URL Percent Decoding](#url-percent-decoding) and [Character Sets](#character-sets)\n\n# License\n\n`scala-uri` is open source software released under the [Apache 2 License](http://www.apache.org/licenses/LICENSE-2.0).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flemonlabsuk%2Fscala-uri","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flemonlabsuk%2Fscala-uri","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flemonlabsuk%2Fscala-uri/lists"}