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

optio

v0.0.3

Published

Data-oriented LP/MIP solver in WebAssembly (browser-first, works in Node and Bun)

Readme

optio

A data-oriented LP/MIP solver compiled to WebAssembly, browser-first with first-class Node and Bun support. The high-level API is inspired by the HiGHS JS API, so migrating an existing integration is mostly renaming.

npm install optio

Quick start

import { init, createSolver, ModelStatus, statusName } from 'optio';

await init(); // loads the bundled .wasm (see "Loading" below)

const solver = createSolver();
const model = solver.createModel();

const x = model.addVar(0, 3.5);
const y = model.addVar(0, Infinity);
model.changeColCost(x, -1);
model.changeColIntegrality(y, 1); // 0 continuous, nonzero integer
model.addRow(-Infinity, 17.5, { indices: [x, y], values: [1, 7] });

model.run();
model.getModelStatus();          // 7 === ModelStatus.Optimal
model.getObjectiveValue();       // -23.5
model.getSolution().colValue;    // Float64Array [3.5, 2]
model.exportModel('lp');         // CPLEX LP text ('mps' also supported)
model.dispose();                 // or solver.withModel(...) to scope it

options.set / info.get accept string or record forms:

model.options.set({ output_flag: false, time_limit_secs: 5, mip_rel_gap: 0.01 });
model.info.get('objective_function_value');
model.info.get('mip_dual_bound'); // certified lower bound (minimisation)

Partial solutions and callbacks

run accepts callbacks; you get every improving incumbent as it is found, so you always hold a usable partial plan:

model.run({
  callbacks: {
    // progress snapshot: { runningTime, nodes, lpIterations, gap,
    //                      primalBound, dualBound, hasIncumbent }
    onProgress: (p) => console.log(p),

    // new incumbent { objective, colValue: Float64Array }
    onIncumbent: (inc) => renderPlan(inc.colValue),

    // return a truthy value to request an early stop
    interrupt: (p) => deadlineReached() || p.nodes > 10_000,
  },
});

model.wasInterrupted(); // true if `interrupt` fired
model.getModelStatus(); // ModelStatus.Interrupt when stopped early

An interrupted solve keeps the best solution found so far — getSolution() returns a full, feasible column vector.

Loading

init() is idempotent and auto-detects the environment:

| environment | how the .wasm is loaded | | -------------------- | ---------------------------------------------------------------- | | bundler (Vite, webpack 5, esbuild, …) | new URL(..., import.meta.url) asset reference | | browser (no bundler) | fetch of the file next to the ES module | | Node / Bun (ESM) | read from disk with node:fs, no fetch involved | | Node / Bun (require)| synchronous read + instantiate at require time |

Pass an explicit source to override the default:

await init(fetchedBytes);                    // Uint8Array/ArrayBuffer
await init(myWebAssemblyModule);             // precompiled module
await init(new URL('...', import.meta.url)); // custom location
await init(new Response(stream));            // Response (streamed)

initSync(bytesOrModule) is available when you must construct models synchronously; without arguments it reads from disk in Node/Bun and throws a helpful error in the browser.

The CommonJS entry (require('optio')) exposes the raw surface (Model, version) and initialises synchronously from disk; use the ESM entry for the full API.

Raw wasm surface

The high-level API is sugar over the wasm-bindgen exports; the snake_case surface is exported directly (Model, version):

import { Model } from 'optio';
await init();
const m = new Model();
m.add_var(0, 1);
m.run();
m.get_model_status(); // 7 === ModelStatus.Optimal

Options (set_option / options.set)

output_flag, time_limit_secs, mip_rel_gap, mip_abs_gap, threads, random_seed, presolve, lp_warm_start, mip_cuts, mip_pseudocost, mip_node_presolve, mip_column_compression, mip_objective_counts, mip_objective_lattice, mip_strong_branch, mip_max_nodes, adaptive_search, balance_target_ratio. Unknown names are ignored.

Info keys (get_info / info.get)

objective_function_value, mip_gap, mip_dual_bound, mip_node_count, simplex_iteration_count, primal_solution_status, max_integrality_violation, timing keys (time_heuristic_secs, time_branch_and_bound_secs, …) and solver statistics (node_lp_solves, mip_root_cuts, …). Unknown names return 0.

Status codes

ModelStatus maps names to the stable numeric codes the solver reports (Optimal = 7, Infeasible = 8, TimeLimit = 13, Interrupt = 17, …); statusName(code) returns the name.

Development

cd npm
node build.mjs      # cargo + wasm-bindgen + wasm-opt → wasm/, version sync
node test.mjs       # Node/Bun smoke suite
npm run test:loading # ESM/CJS disk loading, sync init, and HTTP streaming
npm run test:browser # packed package in Vite-built production workers
node serve.mjs      # then open http://localhost:8123/smoke.html
bun test.mjs        # same suite under Bun

typecheck.ts (not published) guards the public .d.ts; run it with tsc --noEmit --strict --target es2022 --module esnext --moduleResolution bundler typecheck.ts.

The bundled-browser regression uses Vite 8.1.5 and Playwright as development dependencies. After npm ci, install Chromium with npx playwright install chromium and run npm run test:browser. Alternatively, use installed Google Chrome with OPTIO_BROWSER_CHANNEL=chrome npm run test:browser. These development checks require Node 20.19+ or 22.12+; the published runtime still supports Node 18+.

The browser check builds a temporary app from the packed tarball and serves only its production output under /_app/immutable/. It executes classic and module workers, checks the emitted hashed WASM request, validates incumbents and two balanced solves on the same worker, and checks bundled window initialization. It is included in prepublishOnly and fails if Chromium is unavailable. node test-vite-worker.mjs /path/to/optio.tgz can validate an existing tarball.

Build requirements: Rust (nightly with rust-src unlocks the smallest panic=immediate-abort build; OPTIO_STABLE=1 forces the stable path) with the wasm32-unknown-unknown target, plus wasm-bindgen and wasm-opt.

Status

Early (0.0.x): the API is settling and parity with HiGHS is tracked in the repository's LOSSES.md. Native (napi) builds are planned but not part of this package yet.

License

MIT — see LICENSE.