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

@tscircuit/solver-utils

v0.0.16

Published

Reusable building blocks for iterative solvers, multi-stage solver pipelines, and React-based solver debugging UIs.

Readme

@tscircuit/solver-utils

Reusable building blocks for iterative solvers, multi-stage solver pipelines, and React-based solver debugging UIs.

@tscircuit/solver-utils gives you:

  • BaseSolver: a safe base class for step-based solvers.
  • BasePipelineSolver: orchestration for sequential solver stages.
  • React debugger components for stepping, animating, and inspecting solver state.

Table of Contents

Install

bun add @tscircuit/solver-utils

You will also need peer dependencies:

bun add graphics-debug
bun add -d typescript

For React debugger usage:

bun add react react-dom

Quick Start

1) Create a simple solver

import { BaseSolver } from "@tscircuit/solver-utils"
import type { GraphicsObject } from "graphics-debug"

class CountToTenSolver extends BaseSolver {
  target = 10
  value = 0

  override _step() {
    this.value += 1
    this.stats = { value: this.value, target: this.target }

    if (this.value >= this.target) {
      this.solved = true
    }
  }

  override getConstructorParams() {
    return []
  }

  override getOutput() {
    return { finalValue: this.value }
  }

  override visualize(): GraphicsObject {
    return {
      points: [{ x: this.value, y: 0, color: "blue" }],
      lines: [],
      rects: [],
      circles: [],
      texts: [{ x: 0, y: 1, text: `value=${this.value}` }],
    }
  }
}

const solver = new CountToTenSolver()
solver.solve()

console.log({
  solved: solver.solved,
  iterations: solver.iterations,
  output: solver.getOutput(),
  timeMs: solver.timeToSolve,
})

2) Use a pipeline solver

import {
  BasePipelineSolver,
  definePipelineStep,
} from "@tscircuit/solver-utils"
import { BaseSolver } from "@tscircuit/solver-utils"

type Input = { initial: number; target: number }

class AddOneSolver extends BaseSolver {
  value: number
  target: number

  constructor(params: { start: number; target: number }) {
    super()
    this.value = params.start
    this.target = params.target
  }

  override _step() {
    this.value += 1
    if (this.value >= this.target) this.solved = true
  }

  override getOutput() {
    return { value: this.value }
  }

  override getConstructorParams() {
    return [{ start: this.value, target: this.target }]
  }
}

class DoubleSolver extends BaseSolver {
  value: number

  constructor(params: { start: number; min: number }) {
    super()
    this.value = params.start
  }

  override _step() {
    this.value *= 2
    if (this.value >= 100) this.solved = true
  }

  override getOutput() {
    return { value: this.value }
  }

  override getConstructorParams() {
    return [{ start: this.value, min: 100 }]
  }
}

class ExamplePipeline extends BasePipelineSolver<Input> {
  addOneSolver?: AddOneSolver
  doubleSolver?: DoubleSolver

  pipelineDef = [
    definePipelineStep("addOneSolver", AddOneSolver, (instance) => [
      { start: instance.inputProblem.initial, target: instance.inputProblem.target },
    ]),
    definePipelineStep("doubleSolver", DoubleSolver, (instance) => [
      {
        start: instance.getSolver<AddOneSolver>("addOneSolver")!.value,
        min: 100,
      },
    ]),
  ]

  override getConstructorParams() {
    return [this.inputProblem]
  }
}

const pipeline = new ExamplePipeline({ initial: 0, target: 10 })
pipeline.solve()

console.log(pipeline.getAllOutputs())
console.log(pipeline.getStageStats())

3) Debug in React

import { useMemo } from "react"
import { GenericSolverDebugger } from "@tscircuit/solver-utils/react"

export default function SolverPage() {
  const solver = useMemo(() => new CountToTenSolver(), [])

  return (
    <GenericSolverDebugger
      solver={solver}
      animationSpeed={25}
      onSolverStarted={(s) => console.log("started", s.getSolverName())}
      onSolverCompleted={(s) => console.log("done", s.stats)}
    />
  )
}

Core Concepts

  • step(): perform one unit of work.
  • solve(): repeatedly call step() until solved or failed.
  • stats: free-form live diagnostics shown in debugger tables.
  • visualize(): return a GraphicsObject for rendering.
  • getConstructorParams(): return reproducible constructor input (used by download helpers).
  • getOutput(): standardized solved result (especially useful in pipelines).

BaseSolver Guide

Lifecycle

  1. setup() runs once before first step (via _setup()).
  2. Each step() calls your _step().
  3. Solver ends when you set solved = true or failed = true.
  4. If max iterations are reached, solver auto-fails with an error.

Important fields

  • MAX_ITERATIONS (default 100_000)
  • iterations
  • progress (auto-updated if you implement computeProgress())
  • timeToSolve
  • error
  • activeSubSolver (for nested solver composition)
  • stats (arbitrary debugging info)

Recommended implementation pattern

class MySolver extends BaseSolver {
  constructor(private input: InputType) {
    super()
    this.MAX_ITERATIONS = 20_000
  }

  override _setup() {
    // optional one-time initialization
  }

  override _step() {
    // mutate state toward solution
    // set this.solved = true when complete
    // set this.failed = true and this.error when unrecoverable
    this.stats = { ...this.stats, someMetric: 123 }
  }

  computeProgress() {
    return 0.0 // number between 0 and 1
  }

  override visualize() {
    return { points: [], lines: [], rects: [], circles: [], texts: [] }
  }

  override getConstructorParams() {
    return [this.input]
  }

  override getOutput() {
    return { /* final result */ }
  }
}

Error handling behavior

  • Exceptions thrown in _step() are captured, failed is set, and the error is re-thrown.
  • On max-iteration exhaustion, tryFinalAcceptance() is called before failing.
  • You can override tryFinalAcceptance() for “best effort” acceptance logic.

BasePipelineSolver Guide

BasePipelineSolver<TInput> is a BaseSolver that runs multiple solver stages in sequence.

Defining stages with definePipelineStep

definePipelineStep(
  "stageName",
  StageSolverClass,
  (pipeline) => [constructorParamObject],
  {
    onSolved: (pipeline) => {
      // optional callback after this stage solves
    },
  },
)

Pipeline capabilities

  • Auto-instantiates each stage solver when needed.
  • Tracks per-stage timing and iteration metadata.
  • Captures stage outputs automatically from getOutput().
  • Supports nested pipelines (a stage can itself be BasePipelineSolver).
  • Merges per-stage visualizations into a combined GraphicsObject.

Useful methods

  • solveUntilStage(stageName)
  • getCurrentStageName()
  • getStageProgress()
  • getStageStats()
  • getStageOutput<T>(stageName)
  • getAllOutputs()
  • hasStageOutput(stageName)
  • getSolver<T>(stageName)

Initial and final visuals

You can override:

  • initialVisualize() for pre-stage context,
  • finalVisualize() for final summary output.

These are inserted as extra visualization steps around stage visuals.

React Debugger Guide

Import from:

import { GenericSolverDebugger } from "@tscircuit/solver-utils/react"

GenericSolverDebugger props

  • solver?: BaseSolver
  • createSolver?: () => BaseSolver (use this when you need lazy creation)
  • animationSpeed?: number (ms interval, default 25)
  • onSolverStarted?: (solver) => void
  • onSolverCompleted?: (solver) => void

What the debugger UI provides

  • Step once, solve fully, animate, or step until target iteration.
  • Renderer toggle (vector or canvas) with localStorage persistence.
  • Download current visualization JSON.
  • Live iteration count, elapsed solve time, and solved/failed badges.
  • Pipeline stage table (for pipeline solvers), including nested pipelines.
  • Breadcrumb chain with per-solver download menus.

Download features

From the debugger dropdowns, you can download:

  • constructor params JSON,
  • generated *.page.tsx demo scaffold,
  • generated *.test.ts scaffold.

Downloaded JSON automatically strips keys beginning with _.

Testing Solvers

Use Bun tests:

import { test, expect } from "bun:test"

test("solver reaches solved state", () => {
  const solver = new CountToTenSolver()
  solver.solve()
  expect(solver.solved).toBe(true)
  expect(solver.failed).toBe(false)
  expect(solver.iterations).toBeGreaterThan(0)
})

Run tests:

bun test

API Reference

Package exports

  • Root: @tscircuit/solver-utils
    • BaseSolver
    • BasePipelineSolver
    • definePipelineStep
  • React: @tscircuit/solver-utils/react
    • GenericSolverDebugger
    • GenericSolverToolbar
    • PipelineStagesTable
    • SolverBreadcrumbInputDownloader
    • DownloadDropdown

BaseSolver default state

  • solved = false
  • failed = false
  • iterations = 0
  • progress = 0
  • error = null
  • stats = {}
  • MAX_ITERATIONS = 100_000

BasePipelineSolver notable state

  • currentPipelineStageIndex
  • pipelineOutputs
  • startTimeOfStage
  • endTimeOfStage
  • timeSpentOnStage
  • firstIterationOfStage

Development

Clone and install:

bun install

Run interactive demo site (React Cosmos):

bun run start

Run tests:

bun test

Build package:

bun run build

Export demo site:

bun run build:site

Format:

bun run format

Troubleshooting

  • If progress stays at 0, implement computeProgress() in your solver.
  • If input download fails, ensure getConstructorParams() is implemented.
  • If solver never terminates, confirm _step() eventually sets solved or failed.
  • If pipeline stage lookup fails, confirm solverName matches property access in getSolver("name").
  • If React debugger renders nothing, make sure visualize() returns at least one non-empty primitive array.