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

@buildaharness/runtime

v0.3.0

Published

Framework-agnostic client-side FlowSpec executor for Build A Harness — storage adapters (in-memory, IndexedDB/Dexie), experience store, and LLM clients (proxy, Anthropic, OpenAI-compatible).

Readme

@buildaharness/runtime

Framework-agnostic client-side executor for FlowSpec-authored flows, plus the concrete storage and LLM-client implementations @buildaharness/harness and @buildaharness/aielia are built on. Runs in the browser or Node.

FlowSpec execution

FlowRuntime walks a FlowGraph (nodes + edges from a canvas-authored FlowSpec) node by node, dispatching each to the executor registered for its type (input, llm_call, transform, condition, memory_read, memory_write, tool_invoke, hitl_breakpoint, agent_role, agent_debate, parallel_fork/parallel_join, subgraph, output, ...).

import { FlowRuntime, createExecutionContext, LLMClient } from '@buildaharness/runtime'

const runtime = new FlowRuntime()
const context = createExecutionContext({
  llmClient: new LLMClient({ proxyUrl, authToken }),
})

const finalState = await runtime.execute(flowSpec, { userInput: 'hello' }, context)

createExecutionContext wires up sensible defaults (a ToolRegistry seeded with BUILT_IN_TOOLS, an EventBus, an AbortController) and accepts overrides — memoryAdapters, functions, retryConfig, agents, etc. — for anything a flow needs. Subscribe to context.eventBus for node:start, node:complete, node:error, and token-stream events to drive a live UI.

@buildaharness/react's useHarness() hook wraps this class for React apps; use FlowRuntime directly for anything else (a CLI, a worker, a test harness).

Memory adapters

Every memory_read/memory_write node in a flow resolves to a MemoryAdapter (get/set/search/delete) registered under a store_id. FlowRuntime auto-creates an InMemoryAdapter for any in_memory-backed store declared in spec.memory_stores that the caller hasn't already registered.

| Adapter | Backing store | Use when | |---|---|---| | InMemoryAdapter | A Map, scoped by global/thread/resource | Node, tests, or anything that doesn't need to survive a reload | | IndexedDBAdapter | IndexedDB via Dexie | Browser data that should survive a page reload. Falls back to an in-memory Map outside a browser (Node/tests) — it self-detects typeof indexedDB and logs a warning when it falls back. | | FileSystemAdapter | Real files, one JSON file per key | Node (CLI) or Tauri (desktop app) data that should survive a restart — see "Filesystem-backed storage" below. |

import { IndexedDBAdapter } from '@buildaharness/runtime'

const memory = new IndexedDBAdapter({ namespace: 'my-app' })
await memory.set('preferences', { theme: 'dark' })
await memory.get('preferences') // { theme: 'dark' }

search(query, topK, minScore) on all three adapters is a linear scan doing JSON.stringify(value).includes(query) — a keyword match, not semantic search. Fine for small stores; if you need real semantic recall over a large memory store, that's not implemented here yet.

On persistence: IndexedDB is not permanent

IndexedDBAdapter and DexieExperienceStore both call requestPersistentStorage() (wrapping navigator.storage.persist()) as soon as they touch real IndexedDB, so the origin is exempted from browsers' automatic eviction-under-disk-pressure policies where the browser grants it. This is a best-effort improvement, not a guarantee — it doesn't survive the user manually clearing site data, doesn't sync across devices/browsers, and grant behavior varies (Chrome grants liberally for engaged origins; Firefox prompts; Safari's support is limited). It's safe to call from Node/tests — requestPersistentStorage() resolves to false immediately when navigator.storage isn't available, and never throws.

Filesystem-backed storage

FileSystemAdapter implements MemoryAdapter and FileSystemExperienceStore implements ExperienceStore (the latter using the same sync-wrapper pattern as DexieExperienceStore above — an in-memory store is the real synchronous source of truth, persisted to disk in the background after every mutation) give real, restart-surviving storage to code that isn't running in a browser: the CLI (@buildaharness/aielia) and the Tauri desktop app (@buildaharness/desktop, via @buildaharness/chat-ui).

Neither class imports a filesystem API directly. Both take an injected FsBackend:

export interface FsBackend {
  readTextFile(path: string): Promise<string | undefined>  // undefined if missing, not a throw
  writeTextFile(path: string, contents: string): Promise<void>
  removeFile(path: string): Promise<void>                  // no-op if missing
  mkdir(path: string): Promise<void>                        // recursive, no-op if it exists
  readDir(path: string): Promise<string[]>                  // file names only
}

That's what let both classes ship here without this package ever depending on @tauri-apps/plugin-fs or Node's fs — the concrete backends live at the edges instead, each unreachable from the other's build so neither leaks into a bundle that doesn't need it:

  • node:fs/promises backend: packages/aielia/src/node-fs-backend.ts, imported only by that package's cli.ts (never re-exported from its index.ts, so chat-ui's browser bundle never sees a Node builtin).
  • @tauri-apps/plugin-fs backend: packages/chat-ui/src/tauri-fs-backend.ts, dynamically imported only when isTauri() (so a plain browser build never loads it).
import { FileSystemAdapter, FileSystemExperienceStore } from '@buildaharness/runtime'

const memory = new FileSystemAdapter({ backend, baseDir, namespace: 'transcripts' })
const experienceStore = await FileSystemExperienceStore.create({ backend, baseDir, namespace: 'experience' })

LLM client

LLMClient implements ILLMClient (callChat streaming, callChatSync, callChatStructured with tool calls) against @buildaharness/proxy's /llm/chat endpoint, so API keys never reach the browser.

import { LLMClient } from '@buildaharness/runtime'

const llm = new LLMClient({ proxyUrl: 'https://proxy.example.com', authToken })
const reply = await llm.callChatSync([{ role: 'user', content: 'hi' }])

Cross-run learning (DexieExperienceStore)

@buildaharness/harness's ExperienceStore interface is fully synchronous (it's called mid-loop by HarnessRuntime), but IndexedDB has no synchronous API. DexieExperienceStore bridges the two: it wraps an InMemoryExperienceStore as the synchronous source of truth and persists a snapshot to Dexie in the background after every mutation, so learning survives a page reload instead of resetting every session. A storage failure is swallowed rather than breaking a run, matching the harness's own "unavailable store degrades to a silent no-op" contract.

import { DexieExperienceStore } from '@buildaharness/runtime'
import { HarnessRuntime } from '@buildaharness/harness'

const experienceStore = await DexieExperienceStore.create({ namespace: 'my-app' })
const outcome = await new HarnessRuntime().run(objective, successCriteria, { experienceStore, /* ... */ })

Package structure

| Path | Contents | |---|---| | src/runtime.ts | FlowRuntime — the FlowSpec node-graph executor | | src/graph.ts, state.ts | FlowGraph (topology) and FlowState (schema-validated run state) | | src/context.ts | createExecutionContext, ToolRegistry, ExecutionContext type | | src/executors/ | One executor per FlowSpec node type | | src/memory/ | InMemoryAdapter, IndexedDBAdapter, FileSystemAdapter, FsBackend | | src/experience-store/ | DexieExperienceStore, FileSystemExperienceStore | | src/llm-client.ts | LLMClient / ILLMClient | | src/events.ts | EventBus + runtime event types | | src/tools/ | BUILT_IN_TOOLS, tool registry |

Commands

npm run build --workspace=packages/runtime
npm test --workspace=packages/runtime
npm run typecheck --workspace=packages/runtime