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 🙏

© 2024 – Pkg Stats / Ryan Hefner

srd

v1.0.5

Published

Static Land Compliant Remote Data Type in TypeScript

Downloads

249

Readme

npm SRD License License

🚀 Simple Remote Data

Simple Remote Data (SRD) is a fully static land compliant implementation of the Remote Data type in TypeScript - built with Higer Kinded Types (HKT's) inspired by fp-ts and Elm Remote Data.

The idea for using HKT's in TypeScript is based on Lightweight higher-kinded polymorphism.

Static Land Compliant

Install

With yarn

yarn add srd

or if you prefer npm

npm i srd

SRD supports CJS, UMD and ESM bundle outputs.

Examples

React Example

The following is a common use case in React. Fetching data async, showing it on screen and handling initial, loading, and error states.

Without SRD, we would need something like this:

import React, { useState, useEffect } from 'react'

const App = () => {
  const [asked, setAsked] = useState(false)
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState(null)
  const [data, setData] = useState(null)

  useEffect(() => {
    setAsked(true)
    setLoading(true)
    fetch('...')
      .then((data) => {
        setData(data)
        setError(false)
        setLoading(false)
      })
      .catch((err) => {
        setData(null)
        setError(err)
        setLoading(false)
      })
  }, [])

  if (!asked) {
    return <div>Empty</div>
  }

  if (loading) {
    return <div>Loading...</div>
  }

  if (error) {
    return <div>{error}</div>
  }

  if (data) {
    return <div>{data}</div>
  }

  return <div>Something went wrong...</div>
}

That's a lot of code for something we do very often, and highly error prone if we forget to unset/set some state values.

Here's the SRD way - using only 1 state variable instead of 4, preventing any impossible states:

import React, { useState, useEffect } from 'react'
import { SRD, notAsked, loading, failure, success } from 'srd'

const App = () => {
  const [rd, setRd] = useState(notAsked())

  useEffect(() => {
    setRd(loading())
    fetch('...')
      .then((data) => setRd(success(data)))
      .catch((err) => setRd(failure(err)))
  }, [])

  return SRD.match({
    notAsked: () => <div>Empty</div>,
    loading: () => <div>Loading...</div>,
    failure: (err) => <div>{err}</div>,
    success: (data) => <div>{data}</div>,
  }, rd)
}

That's it! Very easy to use, and 90% of the time that's everything you will need.

Typescript React Example

SRD works even better with Typescript! Declare your RD type once and have typescript powerfully infer it everywhere! Like magic!

import React, { useState, useEffect } from 'react'
import { SRD, RD, notAsked, loading, failure, success } from 'srd'
import { Person, getPerson } from './people'

const App = () => {
  const [rd, setRd] = useState<RD<string, Person>>(notAsked())

  useEffect(() => {
    setRd(loading())
    getPerson(123)
      .then((person) => setRd(success(person)))
      .catch((err) => setRd(failure(err)))
  }, [])

  return SRD.match({
    notAsked: () => <div>Empty</div>,
    loading: () => <div>Loading...</div>,
    failure: (msg) => <div>{msg}</div>,
    success: (person) => <div>{person}</div>,
  }, rd)
}

Documentation

SRD comes with many of the Static Land functions that we all know and love. Here is a breakdown of all the supported algebras and utilities:

Setoid

For comparing 2 SRD's to see if they are the same type.

*Note: This only compares the data types and not the inner value. So Success(5) != Failure(5) but Success(5) == Success(80).

equals :: (RD e a, RD e b) -> boolean
import { SRD, success, notAsked } from 'SRD'

SRD.equals(success(5), notAsked()) // false

Functor

Allowing the type to be mapped over by the function provided.

map :: (a -> b, RD e a) -> RD e b
import { SRD, success, loading } from 'SRD'

const double = x => x * 2
const rd1 = success(4)
const rd2 = loading()

SRD.map(double, rd1) // success(8)
SRD.map(double, rd2) // loading()

Bifunctor

Allowing the type to be bimapped over by the functions provided. Common usecase is for when you need to map and mapFailure in one shot.

bimap :: (e -> b, a -> c, RD e a) -> RD b c
import { SRD, success, failure } from 'SRD'

const double = x => x * 2
const formatErr = err => `Something went wrong: ${err}`
const rd1 = success(4)
const rd2 = failure('404 not found')

SRD.bimap(formatErr, double, rd1) // success(8)
SRD.bimap(formatErr, double, rd2) // failure('Something went wrong: 404 not found')

Apply

Apply a function wrapped in a SRD to a value wrapped in a SRD.

ap :: (RD e (a -> b), RD e a) -> RD e b
import { SRD, success, failure } from 'SRD'

const double = x => x * 2
const formatErr = err => `Something went wrong: ${err}`
const rd1 = success(4)
const rd2 = failure('404 not found')

SRD.ap(success(double), rd1)) // success(8)
SRD.ap(success(double), rd2)) // failure('404 not found')

Applicative

Always returns a success with whatever value is passed within.

of :: a -> RD e a
import { SRD } from 'SRD'

SRD.of(4) // success(4)

Alt

Provide a default value to be returned when an SRD is not a success type.

alt :: (RD e a, RD e a) -> RD e a
import { SRD, success, loading, notAsked } from 'SRD'

SRD.alt(success(4), notAsked())  // success(4)
SRD.alt(success(50), success(4)) // success(4)
SRD.alt(loading(), notAsked())   // loading()
SRD.alt(loading(), success(4))   // success(4)

Chain

Similar to map but the callback must return another SRD.

chain :: (a -> RD e b, RD e a) -> RD e b
import { SRD, success, failure, notAsked } from 'SRD'

SRD.chain(x => success(x * 2), success(4))    // success(8)
SRD.chain(x => success(x * 2), notAsked())    // notAsked()
SRD.chain(x => failure('failed'), success(4)) // failure('failed')

Match

Provide a mapper object for each SRD type and whichever type the SRD is - that function will run.

data Matcher e a ::
  { notAsked :: () -> c
  , loading :: () -> c
  , failure :: e -> c
  , success :: a -> c
  }

match :: (Matcher e a -> c, RD e a) -> c
import { SRD, success } from 'SRD'

SRD.match({
  notAsked: () => 'Empty',
  loading: () => 'Loading...',
  failure: e => `Err: ${e}`,
  success: data => `My data is ${data}`
}, success(4)) // My data is 4

Map Failure

Similar to map but instead of running the callback on a success, it calls it on a failure.

mapFailure :: (e -> b, RD e a) -> RD b a
import { SRD, success, failure } from 'SRD'

SRD.mapFailure(x => `hello ${x}`, success(4))     // success(4)
SRD.mapFailure(x => `hello ${x}`, failure('bob')) // failure('hello bob')

Map2

Similar to map but takes 2 SRD's instead of one, and if both are a success, the provided callback will be called.

map2 :: (a b -> c, RD e a, RD e b) -> RD e c
import { SRD, success, failure } from 'SRD'

SRD.map2((x, y) => x + y, success(4), success(8))     // success(12)
SRD.map2((x, y) => x + y, failure('bob'), success(8)) // failure('bob')
SRD.map2((x, y) => x + y, success(8), failure('bob')) // failure('bob')

Map3

Similar to map2 but takes 3 SRD's instead of two, and if all three are a success, the provided callback will be called.

map3 :: (a b c -> d, RD e a, RD e b, RD e c) -> RD e d
import { SRD, success, failure, notAsked, loading } from 'SRD'

const add3 = (x, y, z) = x + y + z

SRD.map3(add3, success(4), success(8), success(10))    // success(22)
SRD.map3(add3, failure('bob'), success(8), notAsked()) // failure('bob')
SRD.map3(add3, success(8), loading(), failure('bob'))  // loading()

Unwrap

Similar to alt, but unwraps the SRD from it's type and runs the callback on it. If the SRD is a success the inner value is passed to the callback and returned, any other value the default is returned.

unwrap :: (b, a -> b, RD e a) -> b
import { SRD, success, notAsked, loading } from 'SRD'

const double = x => x * 2

SRD.unwrap(6, double, success(8)) // 16
SRD.unwrap(6, double, notAsked()) // 6
SRD.unwrap(6, double, loading())  // 6

Unpack

Similar to unwrap, but takes a default thunk instead of a default value.

unpack :: (() -> b, a -> b, RD e a) -> b
import { SRD, success, notAsked, loading } from 'SRD'

const double = x => x * 2

SRD.unpack(() => 6, double, success(8)) // 16
SRD.unpack(() => 6, double, notAsked()) // 6
SRD.unpack(() => 6, double, loading())  // 6

WithDefault

Takes a default value and an SRD. If the SRD is a success then the inner value is returned, otherwise the default value is returned.

withDefault :: (a, RD e a) -> a
import { SRD, success, notAsked, loading } from 'SRD'

SRD.withDefault(4, success(8)) // 8
SRD.withDefault(4, notAsked()) // 4
SRD.withDefault(4, loading())  // 4