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

@truespar/traverse-wasm

v0.7.0

Published

Traverse graph engine in a browser tab — Cypher + 32 GDS algorithms via WebAssembly. Runs in a dedicated Web Worker; no server required.

Readme

@truespar/traverse-wasm

The full Traverse graph engine in a browser tab via WebAssembly. Run Cypher queries and 36+ graph-data-science algorithms — PageRank, Louvain, Dijkstra, FastRP, etc. — entirely in-browser, no server required. Hosted in a dedicated Web Worker so the main thread never blocks. Persists databases to OPFS so they survive tab reloads.

Free for any use. No license key, no telemetry, no upsell — the WASM build is open for evaluation, demos, prototypes, and production.

Try a live demo: https://traverse.truespar.com


Install

npm install @truespar/traverse-wasm

Requires Node.js 18+ for the toolchain; the package itself runs in browsers (see Browser support).

Quickstart

import { TraverseDb } from '@truespar/traverse-wasm'

// Spawn the engine (one Web Worker + the WASM module)
const db = await TraverseDb.open({ name: 'myapp' })

// Write something
await db.query(`
  CREATE (a:Person {name:'Ada'})-[:KNOWS]->(b:Person {name:'Bob'})
`)

// Read it back
const res = await db.query('MATCH (a)-[:KNOWS]->(b) RETURN a.name, b.name')
console.log(res.columns)   // ['a.name', 'b.name']
console.log(res.rows)      // [['Ada', 'Bob']]

// Persist to the browser's OPFS (survives reload)
await db.commit('myapp')

// ...later, in another tab session:
const restored = await TraverseDb.open({ name: 'myapp' })
const count = await restored.nodeCount()  // 2

That's the full lifecycle. The engine is the same one that ships in the licensed server — same Cypher, same algorithms, same .tvdb binary format.


Required hosting setup: cross-origin isolation

The engine uses SharedArrayBuffer for multi-threading. Browsers only expose SharedArrayBuffer on pages that are cross-origin isolated, which means your host must serve every page that loads @truespar/traverse-wasm with these two response headers:

Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy:   same-origin

Without them you'll get ReferenceError: SharedArrayBuffer is not defined when calling TraverseDb.open(). Recipes for common hosts below — pick the one matching your stack.

Vite (dev + preview)

// vite.config.ts
import { defineConfig } from 'vite'

export default defineConfig({
  server: {
    headers: {
      'Cross-Origin-Embedder-Policy': 'require-corp',
      'Cross-Origin-Opener-Policy': 'same-origin',
    },
  },
  preview: {
    headers: {
      'Cross-Origin-Embedder-Policy': 'require-corp',
      'Cross-Origin-Opener-Policy': 'same-origin',
    },
  },
})

Vercel

// vercel.json
{
  "headers": [{
    "source": "/(.*)",
    "headers": [
      { "key": "Cross-Origin-Embedder-Policy", "value": "require-corp" },
      { "key": "Cross-Origin-Opener-Policy", "value": "same-origin" }
    ]
  }]
}

Netlify

# public/_headers
/*
  Cross-Origin-Embedder-Policy: require-corp
  Cross-Origin-Opener-Policy: same-origin

Cloudflare Pages

# public/_headers (same syntax as Netlify)
/*
  Cross-Origin-Embedder-Policy: require-corp
  Cross-Origin-Opener-Policy: same-origin

nginx

add_header Cross-Origin-Embedder-Policy "require-corp" always;
add_header Cross-Origin-Opener-Policy   "same-origin" always;

Apache

Header always set Cross-Origin-Embedder-Policy "require-corp"
Header always set Cross-Origin-Opener-Policy   "same-origin"

IIS

<!-- web.config -->
<configuration>
  <system.webServer>
    <httpProtocol>
      <customHeaders>
        <add name="Cross-Origin-Embedder-Policy" value="require-corp" />
        <add name="Cross-Origin-Opener-Policy"   value="same-origin" />
      </customHeaders>
    </httpProtocol>
  </system.webServer>
</configuration>

Test it

After deploying, open the page and check:

console.log(typeof SharedArrayBuffer)  // 'function' = isolated
console.log(crossOriginIsolated)        // true

If either is missing, the headers didn't take effect.


API

TraverseDb.open(options?)

Spawns the worker, initializes the WASM module and thread pool, and optionally restores an OPFS-resident database.

const db = await TraverseDb.open({
  name: 'myapp',           // optional: OPFS db to restore
  numThreads: 8,           // optional: default = min(hardwareConcurrency, 16)
  workerUrl: '/my-worker', // optional: override worker script URL
})

If name is set but no such database exists, opens empty — call commit(name) later to create it.

db.query(cypher, params?, options?)

const res = await db.query(
  'MATCH (n:Person) WHERE n.age > $minAge RETURN n.name, n.age',
  { minAge: 30 },
  { signal: controller.signal, timeoutMs: 5000 },
)
// res.columns: string[]
// res.rows: unknown[][]                — each cell may be a primitive,
//                                        or a tagged { _type: 'node'|'edge'|'path', ... }
// res.nodes, res.edges                 — deduplicated entities for graph rendering
// res.stats.nodes_created etc.         — write counters
// res.parsing_time_ms / planning_time_ms / execution_time_ms / time_ms
// res.query_type: 'Read'|'Write'|'ReadWrite'|'Schema'

timeoutMs installs a thread-local deadline the executor checks at iteration boundaries. signal lets you AbortController-cancel from the main thread (the worker also receives the cancellation).

db.algorithms()

Returns the live GDS catalog — every algorithm with its supported modes, required/optional config, and output schema. Use this to drive a UI catalog or to introspect what's runnable:

const { algorithms } = await db.algorithms()
for (const a of algorithms) {
  console.log(`${a.name}: modes=${a.modes.join('/')} — ${a.description}`)
}

Then invoke any of them with regular Cypher:

await db.query(`
  CALL traverse.pageRank.write({writeProperty: 'rank', maxIterations: 20})
  YIELD nodeCount, mean, computeMillis
  RETURN nodeCount, mean, computeMillis
`)

db.schema()

Returns labels, relationship types, property keys (with types), and the per-label / per-edge-type property listings. Mirrors the HTTP /api/schema response.

db.importCypher(text)

Run a multi-statement Cypher import script. Returns per-statement results plus aggregate stats. Same shape as the licensed server's /api/import endpoint.

Persistence

await db.commit('myapp')                       // → OPFS
const list = await db.listDatabases()          // → [{name, size, lastModified}]
await db.load('myapp')                          // ← OPFS
await db.deleteDatabase('myapp')

const bytes = await db.exportTvdb()             // → Uint8Array (.tvdb format)
await db.loadTvdb(bytes)                        // ← Uint8Array

await db.writeBytes('upload', uploadedFile)    // write straight to OPFS, then load() separately

The .tvdb format is identical to the one the licensed server reads and writes — you can db.exportTvdb() in the browser, save the bytes, and Database::open the same file from a server-side embedded binding.

Static helpers

await TraverseDb.listDatabases()                // OPFS list without opening a TraverseDb
await TraverseDb.deleteDatabase('myapp')        // delete without opening

Cleanup

db.close()    // terminates the worker; pending requests reject with AbortError

OPFS persistence

The package writes databases to the browser's Origin Private File System — a sandboxed filesystem visible only to your origin. It is:

  • Persistent across tab reloads and browser restarts
  • Subject to the browser's storage quota (typically gigabytes)
  • Not visible from other origins or via DevTools as a regular filesystem
  • Wiped when the user clears site data

To check available quota:

const { quota, usage } = await navigator.storage.estimate()
console.log(`OPFS: ${usage} / ${quota} bytes`)

TypeScript

The package ships full .d.ts files. Your editor should autocomplete everything (QueryResponse, GdsAlgorithm, etc.). Strict-mode safe.

import { TraverseDb, type QueryResponse, type GdsAlgorithm } from '@truespar/traverse-wasm'

Browser support

| Browser | Minimum | Notes | |---|---|---| | Chrome / Edge | 92+ | Full support | | Firefox | 89+ | Full support | | Safari | 16.4+ | Full support; iOS has tight memory caps (see below) |

Hard requirements (all browsers):

  • SharedArrayBuffer (gated by COEP/COOP headers — see above)
  • WebAssembly.Memory with shared: true
  • OPFS (navigator.storage.getDirectory) for persistence

iOS Safari memory. Phones cap WebAssembly memory at roughly 300–500 MiB, much lower than desktop's ~4 GiB. Plan dataset sizes accordingly.


Bundle size & runtime memory

  • Wire size: ~1.4 MB gzipped (~4.2 MB unpacked, mostly the WASM binary itself)
  • Linear-memory ceiling: ~4 GiB on desktop (wasm32 hard cap); ~300–500 MiB on iOS Safari. Memory64 raises the ceiling to ~16 GiB on Chrome 133+ / Firefox 134+ but isn't yet enabled in this build.
  • Boot time: typically 200–600 ms on desktop, 1–3 s on mobile

The engine streams the WASM module on TraverseDb.open(). Subsequent open() calls reuse the browser's WASM cache.


Related


License

LicenseRef-Proprietary — but free for any use. No license key, no runtime telemetry, no usage limits. The published WASM artifact is free to redistribute and embed in your own apps and demos.

If you want server-side Traverse — multi-user, network protocols (Bolt/HTTP/gRPC), embedded SDKs for Python/Java/Node/Go/.NET, or production support — see the licensed server.