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

@nuucognition/mesh-client

v0.0.1-beta.0

Published

The mesh session layer. Mounts a mesh from **any** adapter, holds it in memory, serves every read synchronously, writes optimistically, and invalidates by content hash.

Readme

@nuucognition/mesh-client

The mesh session layer. Mounts a mesh from any adapter, holds it in memory, serves every read synchronously, writes optimistically, and invalidates by content hash.

It is the veil: a host cannot tell a local filesystem mesh from a Convex-hosted one. Web-API only — no node: imports, no process, no bare window or indexedDB. Runs unchanged in browsers, Node, Bun, Deno, and workers.

import { mountMesh, MemoryStore } from "@nuucognition/mesh-client"

const client = await mountMesh({ adapter, store: new MemoryStore() })

client.listDocuments()
client.backlinks("Alpha")
client.sectionsTree()
client.titleSearch("alph", { limit: 10 })

const operations = await client.buildUpdateDocument({ title, frontmatter, content })
const result = await client.apply(operations, { intent: "editor.save" })
if (result.status === "rejected") {
  // conflicts are data, not exceptions
}

Mounting

mountMesh({ adapter, store?, indexDefinitions?, facetExtractors?, meshId?, meshName?, identityKey?, buster?, maxAgeMs?, author? })

adapter is either a mesh-core MeshAdapter (e.g. mesh-adapter-fs) or a RemoteAdapter. The mesh is built with mesh-core's buildMesh and patched with applyDocumentDelta / applyBlobDelta from then on — it is never rebuilt.

Reads

All synchronous except the three that need bytes off the wire.

| Method | Notes | | --- | --- | | listDocuments() | Every document in the mesh | | getDocument(title) | undefined when absent | | getContent(title) | Raw content, frontmatter included; memoized against the believed content hash | | getDocumentView(title) | mesh-core loadDocumentView | | listBlobs() / getBlob(blobKey) | getBlob normalizes bytes, base64 and URL transports into one shape | | backlinks(title) | | | sectionsTree() | Nested tree from #ie/sections/<name> | | groups() | Flat list from #ie/groups/<name> | | titleSearch(query, options?) | Ranked exact → prefix → word-prefix → substring | | hydrationContext() | mesh-core createHydrationContext over the live mesh |

sectionsTree() and groups() prefer mesh-core's registered sections / groups custom indexes and fall back to a prefix scan over the tag index, so neither is a hard dependency.

Writes

apply(operations, meta?) is the only mutation path.

  1. Snapshot the pre-image of every touched document and blob.
  2. Apply the batch to the in-memory mesh and predict the resulting content hashes with mesh-core's hashContent.
  3. Emit optimistic-applied, submit to the adapter.
  4. On ack, adopt server truth: any title whose hash disagrees with the prediction is refetched. Emit committed, write behind to the store.
  5. On rejection, restore the pre-image exactly — no inverse-operation computation — emit rolled-back and conflict, and return the conflicts.

Exactly one batch is in flight; the rest queue FIFO. No rebasing: a batch built on optimistic state that was just rolled back would carry an invalid previousHash, and serialising removes that class of bug outright.

Conflicts never throw. apply() resolves to { status: "rejected", reason, conflicts }, where each conflict carries { title, expectedPreviousHash, actualHash, rejectedBatch }. Only transport failures throw.

Operation builders are composed onto the client (buildCreateDocument, buildUpdateDocument, buildDeleteDocument, buildRenameDocument, buildRenameMedia, buildCreateBlob, buildDeleteBlob) and the raw mesh-core builders are re-exported for hosts that want them.

Invalidation

  • refresh() — with a manifest-capable adapter: one round trip of hashes, a content-hash diff, then a batched refetch of only the stale titles. Without one: a full index reload diffed locally.
  • refreshDocument(title) — unconditional refetch of one document.

contentHash is the only comparison key. mtime and updatedAt are provenance, never invalidation.

Events

client.on("documents-changed", ({ titles, reason }) => {})  // optimistic | commit | rollback | refresh
client.on("batch", ({ batchId, phase, titles, intent }) => {})  // optimistic-applied | committed | rolled-back
client.on("conflict", ({ batchId, reason, conflicts }) => {})

on returns an unsubscribe function. A listener that throws is isolated.

RemoteAdapter

The server-backed contract (v1.1). Two members diverge from mesh-core's adapter interfaces, so the base members are omitted rather than inherited.

interface RemoteAdapter {
  capabilities: MeshAdapterCapabilities
  loadIndex(): Promise<{ documents: MeshDocument[]; blobs: MeshBlob[] }>
  getContent(title: string): Promise<string>
  getContents(titles: string[]): Promise<Map<string, string>>
  getSnapshot(title: string): Promise<MeshDocumentSnapshot>
  getSnapshots(titles: string[]): Promise<MeshDocumentSnapshot[]>

  getManifest(): Promise<{
    version: number
    entries: Array<{ title: string; id: string; contentHash: string }>
  }>

  getBlobData(blobKey: BlobKey): Promise<{ url: string } | { data: string }>

  applyOperations(
    operations: MeshOperation[],
    opts?: { author?: string; intent?: string },
  ): Promise<
    | { status: "applied"; appliedAt: string; batchId: string
        hashes: Array<{ title: string; contentHash: string }> }
    | { status: "rejected"; reason: "hash-conflict" | "invalid"
        conflicts: Array<{ title: string; expectedPreviousHash?: string; actualHash?: string }> }
  >
}

MeshWriteAdapter.applyOperations resolves to void. MeshClient.apply handles both: a void resolution means "applied, no server truth to reconcile", which is the local-adapter path.

Store

MeshStore is the local mirror contract — async and chunk-friendly so an IndexedDB, OPFS or worker-backed implementation drops in without touching a call site. MemoryStore is the default and is exported from the package root and from @nuucognition/mesh-client/store/memory.

Two rules govern every implementation:

  1. Confirmed state only. Optimistic state never reaches the store, so a reload mid-flight cannot resurrect a rejected write. Writes happen after ack.
  2. Best effort. Any store failure sets client.storeDegraded and the client continues memory-only. A mirror can never fail a mesh operation.

The store is scoped by { meshId, identityKey, buster, maxAgeMs }. A buster or max-age mismatch wipes it — caches are never migrated.

Not in v1

Live subscriptions, delta/cursor sync, offline write queues, mutation rebase, CRDTs, multi-tab coordination, persisted derived indexes, and an IndexedDB store implementation. See the research report for the full deferral list.