npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

urlmate

v0.1.2-beta

Published

A utility library for handling URLs

Downloads

24

Readme

UrlMate

UrlMate is a utility library for manipulating with URLs, specifically when the changes depend on certain conditions, like env variables.

NOTE: This is a weekend project made mostly for fun. It is going to be improved in the future, but the timeline cannot be guaranteed. Although, if you find the library useful, feel free to create issue with bug reports of feature requests. Contributions are also welcomed.

Installing

To install the library using npm run the following command in your terminal:

npm install --save urlmate

Alternatively, if you're using yarn, run:

yarn add urlmate

Using

Here is an example of the library usage with TypeScript.

import UrlMate, { append, identity, matchOrDefault } from 'urlmate';

const baseUrl = urlmate('api.example.com')
  .withSubdomain(
    matchOrDefault<string | undefined, string[]>(
      process.env.NODE_ENV,
      identity,
      ['development', append('dev')],
      ['qa', append('qa')],
      ['stage', append('stg')]
    )
  )
  .withPath('v1');

const fooUrl = baseUrl.withPath(append('foo'));
const barUrl = baseUrl.withPath(append('bar'));
const bazUrl = barUrl.withPath(append('baz'));

fetch(fooUrl.toString()); // GET https://api.<env>.example.com/v1/foo
fetch(barUrl.toString()); // GET https://api.<env>.example.com/v1/bar
fetch(bazUrl.toString()); // GET https://api.<env>.example.com/v1/bar/baz

Let's go through it step by step:

  • Firstly, we initiate UrlMate with initial URL (note, that when the protocol is not specified it defaults to https).
  • On the second step, we choose the subdomain that is based on the current environment (development, qa, stage, or anything else)
  • After that we specify that we are going to use v1 version of the given API;
  • Since UrlMate is immutable, now we can use our base URL representation to create multiple endpoint URLs (/foo, /bar and /bar/baz).
  • Finally to use our URLs we just need to convert them back to regular strings by calling .toString() method.

Lightweight version

In order to be able to work with separate parts of the domain (i.e. tld, sld and subdomains), UrlMate relies on psl. It comes with a price of increased bundle size. That is why, if the bundle size is critical or there is no need in sophisticated domain manipulations, there is also urlmate/light, which doesn't have some domain-related methods, but is only about 3KB uncompressed.

Methods

urlmate()

urlmate(url: string): UrlMate;
urlmate(url: UrlData): UrlMate;
urlmate(url: IUrlDto): UrlMate;
urlmate(initFn: Mapper<null, string>): UrlMate;
urlmate(initFn: Mapper<null, UrlData>): UrlMate;
urlmate(initFn: Mapper<null, IUrlDto>): UrlMate;

toString()

toString(): string;

Serializes the urlmate instance to string for further using as a regular URL.

withDomain()

withDomain(domain: string): UrlMate;
withDomain(fn: Mapper<string>): UrlMate;

Allows manipulations with domain as a whole.

urlmate('example.com/foo/bar')
  .withDomain(
    matchOrDefault<string | undefined, string>(
      process.env.NODE_ENV,
      identity, // => https://example.com/foo/bar/
      ['development', constant('dev.internal-example.com')],  // => https://dev.internal-example.com/foo/bar/
      ['qa', constant('qa.internal-example.com')], // => https://qa.internal-example.com/foo/bar/
      ['beta', constant('beta.example.com')] // => https://beta.example.com/foo/bar/
    )
  )

withTopLevelDomain()

NOTE: not available in urlmate/light

withTopLevelDomain(tld: string): UrlMate;
withTopLevelDomain(tldArr: string[]): UrlMate;
withTopLevelDomain(mapper: Mapper<string[], string>): UrlMate;
withTopLevelDomain(mapper: Mapper<string[]>): UrlMate;

Allows to perform changes only to top level domain.

urlmate('example.com')
  .withTopLevelDomain(
    matchOrDefault<string | undefined, string[]>(
      process.env.COUNTRY,
      identity, // => https://example.com/
      ['US', append('us')], // => https://example.com.us/
      ['CA', append('ca')], // => https://example.com.ca/
      ['UA', append('ua')] // => https://example.com.ua/
    )
  )

withSecondLevelDomain()

NOTE: not available in urlmate/light

withSecondLevelDomain(sld: string): UrlMate;
withSecondLevelDomain(mapper: Mapper<string>): UrlMate;

Allows to perform changes only to top level domain.

urlmate('example.com')
  .withSecondLevelDomain(
    doIf(
      () => process.env.USE_REBRANDED_DOMAIN === 'true',
      constant('something-else') // => https://something-else.com/
    )
  )

withSubdomain()

NOTE: not available in urlmate/light

withSubdomain(subdomain: string): UrlMate;
withSubdomain(subdomainsArr: string[]): UrlMate;
withSubdomain(fn: Mapper<string[], string>): UrlMate;
withSubdomain(fn: Mapper<string[]>): UrlMate;

Allows to perform changes only to subdomains.

urlmate('api.example.com')
  .withSubdomain(
    matchOrDefault<string | undefined, string[]>(
      process.env.NODE_ENV,
      identity, // => https://api.example.com/
      ['development', append('dev')], // => https://api.dev.example.com/
      ['qa', append('qa')], // => https://api.qa.example.com/
      ['stage', append('stg')] // => https://api.stg.example.com/
    )
  )

withProtocol()

withProtocol(protocol: string): UrlMate;
withProtocol(fn: Mapper<string>): UrlMate;

Allows to perform changes to the protocol. It may be useful when single server handles multiple protocols.

withUsername()

withUsername(domain: Nullable<string>): UrlMate;
withUsername(fn: Mapper<Nullable<string>>): UrlMate;

Changes the username of the URL. Pass null if there is a need to remove username.

withPassword()

withPassword(domain: Nullable<string>): UrlMate;
withPassword(fn: Mapper<Nullable<string>>): UrlMate;

Changes the password of the URL. Pass null if there is a need to remove username.

withCredentials()

withCredentials(username: Nullable<string>, password: Nullable<string>): UrlMate;
withCredentials(username: Nullable<string>, password: Mapper<Nullable<string>>): UrlMate;
withCredentials(username: Mapper<Nullable<string>>, password: Nullable<string>): UrlMate;
withCredentials(username: Mapper<Nullable<string>>, password: Mapper<Nullable<string>>): UrlMate;

A quick way to specify both username and password.

withPath()

  withPath(path: ValueOrArray<string>): UrlMate;
  withPath(fn: Mapper<string[], ValueOrArray<string>>): UrlMate;

Allows to apply changes to the path of the URL.

withSearchParam()

withSearchParam(domain: string): UrlMate;
withSearchParam(fn: Mapper<string>): UrlMate;

Set or change value of the URL search parameter base on key.

withSearchParams()

withSearchParams(searchStr: string): UrlMate;
withSearchParams(searchIterable: Iterable<[string, ValueOrMapper<Nullable<string>>]>): UrlMate;
withSearchParams(searchObj: { [key: string]: ValueOrMapper<Nullable<string>> }): UrlMate;
withSearchParams(fn: Mapper<Map<string, Nullable<string>>, string>): UrlMate;
withSearchParams(fn: Mapper<Map<string, Nullable<string>>, Iterable<[string, ValueOrMapper<Nullable<string>>]>>): UrlMate;
withSearchParams(fn: Mapper<Map<string, Nullable<string>>, { [key: string]: ValueOrMapper<Nullable<string>> }>): UrlMate;

Set or change all URL search parameters.

withHash()

withHash(hash: Nullable<string>): UrlMate;
withHash(fn: Mapper<Nullable<string>>): UrlMate;

Apply changes to hash part of the URL. Pass null to remove hash.

Mappers

UrlMate comes with a set of basic helper functions that cover typical cases, but it is not required to use them in your code. Your are free to use your own mapper function, which is covered in the following section.

identity

identity<T>(value: T): T

A utility mapper that does not transform the current value. It is useful for using in higher order functions to cover cases when no change has to be applied. For example, it can often be used as default mapper of matchOrDefault

constant

constant<T>(value: T): Mapper<any, T>

It returns a mapper that returns a provided value regardless of current value. It may be useful when there is a need to set a specific value that is not based on the current one.

constIf

constIf<T>(predicate: Predicate<T>, value: T): Mapper<any, T>

Works similarly to constant but also takes a predicate as a first parameter and applies the value only if the predicate returns true

// => isDev() ? https://dev.foo.com : https://foo.com
urlmate('foo.com').withSubdomain(constIf(isDev, 'dev')) 

append

append<T>(...values: T[]): Mapper<T[]>

Returns a mapper that is used to add a value (or values) to the end of array-based URL components (e.g. subdomain, path, etc.).

// => https://api.<environment>.foo.com/
urlmate('api.foo.com').withSubdomain(append(environment))

appendIf

appendIf<T>(predicate: Predicate<T[]>, ...values: T[]): Mapper<T[]>

Applies the append mapper only when the predicate returns true.

// => isDev() ? https://api.dev.foo.com : https://api.foo.com
urlmate('api.foo.com').withSubdomain(appendIf(isDev, 'dev'))

prepend

prepend<T>(...values: T[]): Mapper<T[]>

Returns a mapper that is used to add a value (or values) to the beginning of array-based URL components (e.g. subdomain, path, etc.).

// => https://foo.com/<version>/bar/baz
urlmate('foo.com/bar/baz').withPath(prepend(version))

prependIf

prependIf<T>(predicate: Predicate<T[]>, ...values: T[]): Mapper<T[]>

Applies the prepend mapper only when the predicate returns true.

// => isV2() ? https://foo.com/v2/bar/baz : https://api.foo.com/bar/baz
urlmate('foo.com/bar/baz').withPath(prependIf(isV2, 'v2'))

doIf

doIf<T, S = T, U = T>(
  predicate: Predicate<T>,
  onTrue: Mapper<T, S>,
  onFalse?: Mapper<T, U>
): Mapper<T, T | S | U>

A utility higher order function that acts as a regular if statement. It takes a predicate as a first argument. Based on its returned value it calls either onTrue or onFalse mapper. onFalse is optional and defaults to identity (i.e. it leaves the value as is).

doIf(isFoo, constant('foo.com'), constant('bar.com'))

matchOrDefault

matchOrDefault<T, S, U = S>(
  value: T,
  defaultFn: Mapper<S, U>,
  ...optionFns: OptionTuple<T, S, U>[]
): Mapper<S, U>

A utility higher order function that acts as switch statement. It takes a value as the first argument, default mapper as the second one, after that you may list option tuples, which should be an array of two elements: the first one is a value, the second one is a corresponding mapper. matchOrDefault goes through the list of options and calls a mapper if value element of its tuple matches provided value. If no match is found the default mapper is called.

matchOrDefault(
  process.env.NODE_ENV,
  constant('example.com'),
  ['development', constant('dev.internal-example.com')],
  ['qa', constant('qa.internal-example.com')],
  ['beta', constant('beta.example.com')]
)

Using custom mappers

urlmate most methods take any mapper that match (current: T) => T signature, so you are free to use your own custom mappers to achieve the necessary result. For example:

function omitVowels(current: string) {
  return current.replace(/[aeiouy]/gi, '');
}

// example.com => xmpl.com
urlmate('example.com').withSecondLevelDomain(omitVowels)