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

@resonatehq/sdk

v0.11.5

Published

TypeScript SDK for Resonate

Downloads

4,857

Readme

resonate component banner

Resonate TypeScript SDK

ci codecov dst License

About this component

The Resonate TypeScript SDK enables developers to build reliable and scalable cloud applications across a wide variety of use cases.

Quickstart

quickstart banner

  1. Install the Resonate Server & CLI
brew install resonatehq/tap/resonate
  1. Install the Resonate SDK
npm install @resonatehq/sdk
  1. Write your first Resonate Function

A countdown as a loop. Simple, but the function can run for minutes, hours, or days, despite restarts.

import { Resonate, type Context } from "@resonatehq/sdk";

function* countdown(context: Context, count: number, delay: number) {
  for (let i = count; i > 0; i--) {
    // Run a function, persist its result
    yield* context.run((context: Context) => console.log(`Countdown: ${i}`));
    // Sleep
    yield* context.sleep(delay * 1000);
  }
  console.log("Done!");
}
// Instantiate Resonate
const resonate = new Resonate({ url: "http://localhost:8001" });
// Register the function
resonate.register(countdown);

Clone a working example repo

  1. Start the server
resonate dev
  1. Start the worker
npx ts-node countdown.ts
  1. Run the function

Run the function with execution ID countdown.1:

resonate invoke countdown.1 --func countdown --arg 5 --arg 60

Result

You will see the countdown in the terminal

npx ts-node countdown.ts
Countdown: 5
Countdown: 4
Countdown: 3
Countdown: 2
Countdown: 1
Done!

What to try

After starting the function, inspect the current state of the execution using the resonate tree command. The tree command visualizes the call graph of the function execution as a graph of durable promises.

resonate tree countdown.1

Now try killing the worker mid-countdown and restarting. The countdown picks up right where it left off without missing a beat.

Async/await

The SDK also ships an async/await engine: the same durable model, written with ordinary async functions instead of generators. Import it from @resonatehq/sdk/async:

import { Resonate, type Context } from "@resonatehq/sdk/async";

async function countdown(ctx: Context, count: number, delay: number) {
  for (let i = count; i > 0; i--) {
    // Run a function, persist its result
    await ctx.run((ctx: Context) => console.log(`Countdown: ${i}`));
    // Sleep
    await ctx.sleep(delay * 1000);
  }
  console.log("Done!");
}

// Instantiate Resonate
const resonate = new Resonate({ url: "http://localhost:8001" });
// Register the function
resonate.register(countdown);

Same server, same CLI, same durable promises — the rest of the quickstart is unchanged.

One difference to know about: operations are eager. Calling ctx.run(...) starts the work immediately and returns an awaitable handle, so fan-out is ordinary promise code:

async function checkout(ctx: Context) {
  const payment = ctx.run(chargeCard);     // starts now
  const inventory = ctx.run(reserveItems); // runs concurrently
  return await Promise.all([payment, inventory]);
}

Migrating from generators

Both engines live in the same package and speak the same protocol to the same server, so you can migrate one function at a time. The mechanical changes:

| Generator engine | Async engine | | --- | --- | | import { Resonate } from "@resonatehq/sdk" | import { Resonate } from "@resonatehq/sdk/async" | | function* (context: Context, ...) | async function (ctx: Context, ...) | | yield* context.run(...), yield* context.sleep(...) | await ctx.run(...), await ctx.sleep(...) | | yield context.beginRun(...), later yield future | const p = ctx.run(...), later await p — every op is eager | | resonate.run(id, func, ...args) → the result | resonate.run(id, func, ...args) → a handle; await handle.result() | | resonate.beginRun(id, func, ...args) → a handle | same call — there are no begin* variants, run is begin-run |

Two things to watch:

  • Retries are opt-in. The generator engine retries plain functions with exponential backoff by default. The async engine never retries by default — an async workflow and a plain async function are indistinguishable at runtime, so there is no safe blanket default. To keep retry behavior, pass a policy explicitly:

    import { Exponential } from "@resonatehq/sdk/async";
    
    await ctx.run(chargeCard, ctx.options({ retryPolicy: new Exponential() }));
  • Only await durable promises inside a workflow. A plain await on a timer or network call is invisible to the engine — the workflow may resume after its execution pass has ended and abort. Wrap side effects in ctx.run, the same rule as context.run in the generator engine.