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

@crow-agent/chord

v0.2.0

Published

Application-composition runtime: services, replicated state, RPC, and plugins for long-running TypeScript processes.

Readme

@crow-agent/chord

Chord composes applications out of plugins and extensions. It offers facets, services, replicated state, and a pluggable boundary for remote services. It ships as a standalone package inside the Crow monorepo, yet it is not a Crow package: nothing in it depends on another Crow workspace package, and unrelated applications can adopt it directly.

Why Chord exists

One feature often has to live in several places at once — an agent worker, a terminal UI, a remote web UI. Chord is the generic machinery for writing such extensions so they feel natural to humans and to agents.

The pieces fit together:

  • Plugins declare, synchronously, which services they provide and which they need. After every plugin has stated its shape, a host checks the whole dependency graph, wires services, starts providers before consumers, and tears resources down in reverse dependency order. These units go by the name facets.

  • Facets are the parts of a plugin. Each facet bundles separately and executes in the process or environment it targets. Facets let one plugin split across backends, browsers, TUIs, and other runtimes.

  • Services are typed, stable tokens. A service has one provider (singleton) or a set of dynamic keyed instances (keyed). It may be process-local with a free-form JavaScript contract, or remotely visible. Consumers hold a stable facade even as providers disconnect or get replaced.

  • Replicated state publishes authoritative state to connected local and remote consumers. Producers commit atomic overlay transactions via change(context, callback); consumers observe complete immutable values. Draft proxies live only for the callback and die when it returns. Preparation builds a structurally shared immutable candidate plus one exact decoded op batch, while every remote client/state stream keeps its own path-codec state. Replicas go unready on disconnect or replacement until they rehydrate.

  • Delta tracking records and merges operations over tracked plain JSON. It keeps common string and array edits, supports durable base batches, and validates untrusted operations during application. Batches converge but are neither canonical nor guaranteed minimal.

  • Remote service sources advertise services that live outside a facet host and open bindings for what its facets need. Bindings move logical calls and subscriptions through an adapter the application supplies. Chord demands strict-JSON arguments, results, snapshots, updates, and catalogues, but says nothing about framing, routing, transport, or the outer wire envelope. JsonRepresentation<T> derives a wire-safe type for application data with unknown payloads; isJsonValue() checks values received at an adapter edge. Symmetric RPC peers are planned as one optional realization of this boundary.

  • Context is Chord's Go-inspired system for cancellation and invocation-scoped application values. Permissions or telemetry can ride those values without Chord depending on either.

Today's runtime exports service tokens, singleton and keyed providers, remote bindings, replicated state, facet hosts, and facet loaders from @crow-agent/chord. Take public types and general runtime APIs from the package root. Context constants and functions live in @crow-agent/chord/context so their generic names stay out of the root API. Chord-owned identifiers use the chord.* namespace; its reserved service prefix is $chord.*.

Writing remote service adapters

Chord owns a transport-independent grammar for its service wire. Consumer adapters build $chord.service control calls with createServiceCatalogueCall(), createServiceSubscribeCall(), and createServiceUnsubscribeCall(). createRemoteServiceEndpoint() serves those calls for one provider consumer, including subscription startup and teardown. parseServiceCall(), parseServiceCatalogue(), and the decoded/wire snapshot and update parsers enforce Chord semantics once an adapter has established a strict-JSON boundary. RemoteServiceErrorCode and REMOTE_SERVICE_ERROR_CODES enumerate the service errors allowed across it.

Replicated-state traffic uses one createServiceStateEncoder() on the provider side and one createServiceStateDecoder() on the consumer side per subscription. Each pair owns an independent Delta path dictionary per instance/member state, reset on replacement, unavailability, close, or fresh hydration. Applications may nest these values in any routing, request, response, or event envelope; Chord does not define that outer protocol.

A provider subscription snapshots atomically, then buffers later updates until activation. Activation and reentrant publication share one FIFO. The provider keeps at most 100 pending updates per subscription, not counting the delivery in flight. Update 101 discards the whole pending queue and emits { type: "reset", snapshot }: a current full subscription snapshot whose state members carry [["r", value]] with fresh sequence baselines. The reset also captures live instance membership, so dropped spawn/close/replacement events can't strand stale instances. Live keyed generations keep their handles. The reset carries the context of the publication that overflowed.

Consumers and codecs must process that explicit reset before any later ordinary delta. A root op inside a normal state update does not excuse a sequence gap. Resets restart the subscription's path dictionaries; subsequent deltas must stay contiguous from each reset baseline. Transport adapters must preserve snapshot/reset/update ordering — never drop encoded delta batches, never assume their own async queues are bounded by the provider's.

The JSON delta tracker

The standalone transactional tracker lives at @crow-agent/chord/delta:

import { applyImmutable, track } from "@crow-agent/chord/revisions";

const tracker = track({ log: "", total: 0 });
const change = tracker.beginChange(); // start
change.state.log += "done\n";
change.state.total += 1;
const prepared = change.prepare(); // freeze

tracker.adopt(prepared);
const replica = applyImmutable(prepared.base, prepared.ops); // replay

tracker.value always reflects the newest adopted immutable revision. Preparing changes nothing authoritative; adopting validates the preparation and swings the root pointer. Assigned containers copy by value; untouched subtrees may be shared across revisions.

The tracker relies on trusted immutable ownership, not defensive copies or freezing. track(initial), prepareReplace(value), replicatedState(initial), and replace(context, value) take over alias-free strict-JSON roots without walking them. Callers must leave transferred or published data alone. Draft-placed values are already copies, so that same walk rejects non-strict JSON before the draft mutates. Published values stay unfrozen. In-process loopback consumers may share containers with the provider; mutating a consumed value breaks the contract and can corrupt authority. Clone or serialize wherever a mutable trust boundary lies.

Replicated state offers the same model through a callback:

const initial = { log: "", total: 0 };
const status = env.replicatedState(initial); // ownership of initial moves here
status.change(context, (draft) => { // mutate
	draft.log += "done\n";
	draft.total += 1;
});

A successful change() publishes exactly one atomic revision. When the callback throws, the value and sequence stay put. Draft handles die with the callback's return. Chord emits string appends and front-truncations, array splices and permutations, sets, and deletes; big edit sets may collapse into a full replacement. Remote plumbing encodes each batch separately for every client/state pair.

Subscribing to public state

state.subscribe(async (value, context, delivery) => { ... }) serializes one subscription's callbacks. Hydration begins as soon as a value exists, and its promise must settle before updates flow. Sync callbacks still execute synchronously. Inside an async callback, read the captured value — state.value may already point at a newer revision.

A subscription keeps at most 100 pending full-value deliveries, excluding the running callback. On overflow only the newest pending value, context, and delivery metadata survive; a not-yet-started first hydration is spared. Public delivery sequences may therefore skip. This counts frames, not bytes. Internal exact-op subscriptions stay synchronous, see every revision, and ignore slow public callbacks.

The returned unsubscribe drops pending work at once and blocks new callbacks. It neither aborts nor awaits the running one. Sync throws and rejections are observed, reported, and never halt sibling subscriptions or later callbacks. Attached sources and remote bindings route through their onError; local mutable states (and handler-less attached sources) throw in a microtask. Unsubscribing doesn't swallow a later rejection from the in-flight callback.

The Delta guide spells out the full ownership, lifecycle, operation, plus replica contracts.

Facet bundling and loading

@crow-agent/chord/bundler runs esbuild over ESM or TypeScript application entries to produce independent content-addressed CommonJS files. The package-level API reads plugin identity and build settings from package.json, then applies the host application's facet path conventions:

{
  "name": "@example/my-plugin", // name
  "version": "1.0.0",
  "type": "module",
  "peerDependencies": {
    "@crow-agent/chord": "^0.84.4"
  },
  "chord": {
    "facets": {
      "worker": "./src/custom-worker.ts", // entry
      "presentation": false
    }
  }
}
import { bundleFacetPackage } from "@crow-agent/chord/facet-bundle-api";

await bundleFacetPackage({ // bundle
	packagePath: "/path/to/my-plugin", // package
	outdir: "/application-owned/plugin-builds/my-plugin", // out
	defaultFacets: {
		worker: "src/worker.ts",
		presentation: "src/presentation.ts", // ui
	},
});

Files matching the conventions become entries unless chord.facets overrides or disables them. Peer dependencies stay external and resolve against the host at load time. Chord installs nothing and runs no package lifecycle scripts. bundleFacets() remains the lower-level entry for callers that already know their plugin identity and entry map.

Output is one .cjs per entry plus chord-facets.json. Load a single application-chosen entry with the Node-only loader:

import { createFacetBundleLoader } from "@crow-agent/chord/node-entry";

const loader = createFacetBundleLoader({ // loader
	manifestPath: "/application-owned/plugin-builds/my-plugin/chord-facets.json", // manifest
	entry: "worker",
	resolveExternal: (specifier) => import.meta.resolve(specifier), // externals
});
const loaded = await loader.load(); // load

Every load() checks SHA-256 integrity, then compiles the CommonJS body straight with node:vm — never into Node's CommonJS or ESM caches. The host resolves externals and serves them through a restricted require; esbuild lowers dynamic imports onto the same path. Disposing a retired generation drops the loader's facet references, so compiled code becomes collectible once plugin-owned resources are gone too.

To ship a bundle to another Node host, readFacetBundleArtifact() packs one verified manifest entry with its source, and createFacetBundleArtifactLoader() instantiates fresh temporary generations while resolving externals against the receiving host.

Reloading means: load a candidate, hand its facets to FacetHost.reload(), dispose the candidate on failure, and dispose the retired LoadedFacets only after cutover succeeds. The host validates and activates the candidate while current providers keep routing, then swaps each singleton in place with no unavailable window — stable handles never see a disconnect during an ordinary reload. Keyed instances stay incarnation-bound; replacements get fresh generations. The bundler finishes a full temporary directory before swapping it into place, so loaders never catch a half-built generation.

PLANNING.md covers the wider RPC and generation-loading design.