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 🙏

© 2025 – Pkg Stats / Ryan Hefner

yeetstack

v0.0.1

Published

Yeet Notation - ergonomic monadic control flow for javascript.

Readme

Yeetstack

Yeetstack is an implementation of Yeet Notation - an ergonomic monadic control flow for javascript/typescript. It's the equivalent of Haskell's Do Notation or Gleam's Use.

Yeetstack uses a combination of a generator function, a proxy and a stack to provide devs with a clean imperative looking syntax for any monadic control flow. Think of it async/await but for any monadic operation. It looks like this:

function* getUserProfile(id: number) {
   const { yeet, yoink } = Yeet({ getUser, getAddress })

   yield yeet.getUser(id).user
   yield yeet.getAddress('user').address

   return { ...yoink() } // returns { user, address }
}

const result = run(getUserProfile, 4)

// this will give you a success result, or it will short circuit, give you a failure result and include any data accumulated so far

How to use it

Say you have a workflow that consists of multiple steps that could fail, for instance, getting a user and their address from somewhere. It doesn't have to be an async workflow necessarily.

You define your workflow using a generator function. Here, you should:

  • receive any initial arguments to your generator function
  • destructure yeet and yoink from Yeet, and pass it the functions you want to use.
  • define each step of the workflow like so:
    • begin each step with yield
    • call your desired function as a property of yeet
      • except for the initial argument, these need to be strings, as they're on the yeetstack, and not in scope
    • define the name for the return variable as a prop on the function call (user, address in the example)
    • finally, use yoink to retrieve all stored values from the yeetstack

You can easily rename yeet to something more domain-specific, for instance:

const { yeet: db, yoink } = Yeet({ getUser, getAddress })

yield db.getUser(id).user

WTF is going on?

When you initially call Yeet with your functions, it returns an object containing yeet and yoink. It also registers the functions you pass it and creates an empty array, which it uses like a stack.

The yeet object is a proxy that behaves differently depending on whether you pass it a registered function or a property. When you pass it a registered function name, it saves it as a pending call and resolves the argument names with values on the yeetstack.

When it receives a property and there is a pending function call, it runs the function and pushes the return value to the yeetstack, as an object where name is the prop key you gave and value is the return value.

Finally, when you call yoink(), everything is pulled off the stack and converted to an object that you can destructure, with their saved names as the property keys.

Why?

The generator and associated runner give you complete control flow safety no matter which step fails, and ensure you always get a consistent result type.

But even with that, consider the case where your workflow doesn't need to store every variable, e.g. a CSS parser:

const name = yield eat("PROPERTY_NAME")
yield eat(":")
const value = yield eat('VALUE')
yield eat(";")

return { name, value }

I didn't like that the yields weren't all lined up. So I added the yeetstack so that I could write:

// yeet has been renamed to parser
yield parser.eat("PROPERTY_NAME").name
yield parser.eat(":")
yield parser.eat('VALUE').value
yield parser.eat(";")

return { ...yoink() }

You can also pass any variable on the yeetstack to any function, but you have to pass its name as a string, since the variable isn't in scope.

License

Yeetstack is completely open source and MIT licensed. Feel free to use it however you like. I think it has some potential as a way to escape flatMap hell for other monadic workflows, such as Effect.ts.