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

reson

v0.0.2

Published

ReSon is a ReScript library that provides a convenient way to parse JSON data in a type-safe manner. Inspired by the classify approach described in the ReScript documentation and libraries like aeson, ReSon aims to simplify the process of parsing and vali

Readme

ReSon

ReSon is a ReScript library that provides a convenient way to parse JSON data in a type-safe manner. Inspired by the classify approach described in the ReScript documentation and libraries like aeson, ReSon aims to simplify the process of parsing and validating JSON structures.

Features

  • Type-safe parsing of JSON data
  • Error handling for invalid JSON and data types
  • Support for parsing nested objects, arrays, and tuples
  • Optional and nullable field handling
  • Composable parsing combinators for building complex parsers

Installation

  1. Get it from npm
npm install --save reson
  1. Update bscofig.json's "bs-dependencies"
  "bs-dependenncies": ["ReSon"]

Usage

It all wraps around ReSon.t, which is

type t<'a> = result<'a, error>

Where error is the following:

type rec error =
  | NotJSON
  | NotNumber
  | NotString
  | NotBoolean
  | NotArray
  | NotObject
  | MissingField(string)
  | Field(string, error)
  | ArrayItem(int, error)
  | Nor(error, error)
  | NotTuple2
  | Tuple2(int, error)
  | NotTuple3
  | Tuple3(int, error)
  | NotTuple4
  | Tuple4(int, error)
  | NotTuple5
  | Tuple5(int, error)
  | Custom(string)

There are several primitives that are available:

    let number: Js.Json.t => t<float>
    let string: Js.Json.t => t<string>
    let boolean: Js.Json.t => t<bool>
    let object: Js.Json.t => t<Js.Dict.t<Js.Json.t>>

    let initParse: string => t<Js.Json.t>

Example

    // This will print "34"
    let s = `34`
    let x = initParse(s)->Result.flatMap(number)
    switch x {
    | Ok(x) => Js.log(x)
    | Error(e) => Js.log(e->printError)
    }

There's an error type which can be pretty-printed

    // This will print "Not a number"
    let s = `false`
    let x = initParse(s)->Result.flatMap(number)
    switch x {
    | Ok(x) => Js.log(x)
    | Error(e) => Js.log(e->printError)
    }

Combining parsers together

There are also few helper functions, that allow combining, and updating existing parsers.

    // allows any other parser to accept null as possible value
    let nullable: (Js.Json.t => t<'a>, Js.Json.t) => t<option<'a>>
    // parses items of array with given parses
    let array: (Js.Json.t => t<'a>, Js.Json.t) => t<array<'a>>

    // Just a wrapper around Result.map so that parsed result can be updated
    let map: ('a => t<'b>, 'b => 'd, 'a) => t<'d>

    // to parse results of parse, a wrapper around Result.flatMap, see examples below
    let with: (Js.Json.t => t<'b>, 'b => t<'c>, Js.Json.t) => t<'c>
    // to be used with(object), see example
    let field: (Js.Dict.t<Js.Json.t>, string, Js.Json.t => t<'a>) => t<'a>
    // Same as field, but allows field to not exist
    let optional: (Js.Dict.t<Js.Json.t>, string, Js.Json.t => t<'a>) => t<option<'a>>

    // Parses json arrays like [false, 13] into pair.
    // there are tuple2, tuple3, tuple4, and tuple5
    let tuple2: ( Js.Json.t => result<'a, error>, Js.Json.t => result<'b, error>, Js.Json.t,) => t<('a, 'b)>

Example

type myType = {
  key: float,
  otherKey: string,
  optional: option<string>,
}

// recommend to create constructor function
let make = (key, otherKey, optional) => {key, otherKey, optional}

let s = `{"key": 1, "otherKey": "some text here"}`

let p = with(object)(obj => {
  pure(make)
  ->apply(obj->field("key", number))
  ->apply(obj->field("otherKey", string))
  ->apply(obj->optional("optional", string))
})

// will print `{ key: 1, otherKey: 'some text here', optional: undefined }`
switch initParse(s)->Result.flatMap(p) {
| Ok(x) => Js.log(x)
| Error(e) => Js.log(e->printError)
}

let s = `{}`

// will print `Missing field "key"`
switch initParse(s)->Result.flatMap(p) {
| Ok(x) => Js.log(x)
| Error(e) => Js.log(e->printError)
}

let s = `{"key": "not a number"}`
// will print `Error on path [key]: Not a number`
switch initParse(s)->Result.flatMap(p) {
| Ok(x) => Js.log(x)
| Error(e) => Js.log(e->printError)
}

type nested = {key: float}
type withNested = {nested: nested}

let makeNested = key => {key}
let make = nested => {nested}

let s = `{"nested": {
  "key": "not a number"
}}`

let parseNested = with(object)(obj => {
  pure(key => {key})->apply(field(obj, "key", number))
})

let p = with(object)(obj => {
  pure(make)->apply(field(obj, "nested", parseNested))
})

// will print Error on path [nested.key]: Not a number
//                           ^ so nested path really looks cool
switch initParse(s)->Result.flatMap(p) {
| Ok(x) => Js.log(x)
| Error(e) => Js.log(e->printError)
}

Sum types

There are situations when there can be some sort of variation:

  // In Ts we would say that
  type T = number | string

Idiomatic way of expressing it in ReScript would be something like this:

  type t = OfFloat(float) | OfString(string)

Parser for such type can be constructed by using or helper function.

let parseOfFloat = number->map(x => OfFloat(x))
let parseOfString = string->map(x => OfString(x))

let parseSumType = parseOfFloat->or(parseOfString)

Another typical situation is when in TS we would say

  type T = "tag1" | "tag2"

And in ReScript we usually express it like:

  type t = [#tag1 | #tag2]

To parse such type I suggest using this little patternn:

  let p = with(string)(s => switch s {
    | "tag1" => Ok(#tag1)
    | "tag2" => Ok(#tag2)
    | _ => Error(Custom("Unknown tag " ++ s))
  })

The End

For further details I sugges taking a look at test cases or the src, it's just ~200 lines of code. If you think there's a way to improve the way these functions are interacted with on the surface, I'll hear you out in Issues.