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

@nanolink/mirrors

v1.1.8

Published

GraphQL subscription client + mirror synchronization utilities.

Readme

@nanolink/mirrors

GraphQL subscription client + in‑memory mirror synchronization utilities. Optimized for incremental change streams that send START / UPDATED / DELETED / DONE / VERSION_ERROR frames.

Features

  • Lightweight SubscriptionClient around graphql-ws with explicit connect, controlled reconnect, and small event surface.
  • Dual version support in MirrorSync (version numeric + optional opVersion string) for hybrid sequence + causality ordering (either dimension can drive resync logic when present).
  • Stale delete & update guards: ignores events older in either version dimension to prevent resurrecting removed or outdated entities.
  • Efficient updates: UPDATED replaces item wholesale only when newer; no deep merge overhead.
  • Full sync cycle handling via START/DONE gates; loaded promise resolves after first DONE and re-arms on VERSION_ERROR.
  • Automatic resubscribe after reconnect using last known versions (no duplicate inserts).
  • Read‑only delegated map interface for consumers (prevents accidental mutation of internal state).
  • Connection helper manages multiple mirrors, re‑emitting namespaced events (mirror:start, mirror:updated, ...).
  • Proxy-aware WebSocket resolution: if proxy env vars are present (ALL_PROXY / HTTPS_PROXY / HTTP_PROXY / GLOBAL_AGENT_HTTP_PROXY) the client prefers the Node ws implementation; otherwise uses existing global WebSocket (browser / Node >=18) or falls back to ws.
  • Minimal dependencies; event system via eventemitter3.

Install

npm install @nanolink/mirrors

Optional (proxy via global-agent for generic HTTP(S) requests—WebSocket selection is still handled automatically as described):

// enableProxy.js
import 'global-agent/bootstrap';
process.env.GLOBAL_AGENT_HTTP_PROXY = 'http://proxy:3128';

Run with:

node -r global-agent/bootstrap app.js

Usage

Quick (multi‑mirror connection)

import { Connection } from '@nanolink/mirrors';

const conn = new Connection('https://api.example.com', 'TOKEN');
conn.connect();

conn.on('connected', () => console.log('socket up'));
conn.on('mirror:updated', e => console.log('updated', e.mirrorName, e.item.id));

async function main() {
  const users = await conn.getMirror('users', /* GraphQL subscription */ `
    subscription Users($version: Long, $opVersion: String) {
      users(version: $version, opVersion: $opVersion) {
        type
        total
        deleteId
        deleteVersion
        deleteOpVersion
        data { id version opVersion name }
      }
    }
  `, {});

  console.log('Initial size', users.size);
  for (const user of users.values()) {
    console.log(user);
  }
}
main();

Recommended: use getPredefinedMirror()

// preferred helper — uses predefined subscription templates from `src/definitions`
const devices = await conn.getPredefinedMirror('trackers');
await devices.loaded;
console.log('Trackers count', devices.size);
devices.on('updated', e => console.log('tracker updated', e.item.id));

Direct low‑level client

Numeric‑only subscriptions (most common)

Most Nanolink GraphQL subscription fields expose only a numeric version and omit opVersion. MirrorSync handles this seamlessly: pass just $version in the query and the server responses won’t include opVersion / deleteOpVersion fields.

// Numeric-only example
const products = await conn.getMirror('products', `
  subscription Products($version: Long) {
    products(version: $version) {
      type
      total
      deleteId
      deleteVersion
      data { id version title }
    }
  }
`, {});

await products.loaded; // after first DONE
console.log('Products count', products.size);

When opVersion fields are absent they are simply ignored; ordering & stale protections rely on numeric version only.

import { SubscriptionClient } from '@nanolink/mirrors';

const sc = new SubscriptionClient({ url: 'https://api.example.com', maxReconnectAttempts: 10 });
sc.connect();
sc.on('connected', () => {
  const dispose = sc.subscribe({
    query: 'subscription Ping { ping }'
  }, {
    next: (msg) => console.log(msg),
    error: (e) => console.error('err', e),
    complete: () => console.log('done')
  });
});

Events

SubscriptionClient emits:

  • connecting
  • connected (first successful connect)
  • reconnected (subsequent successful connect after a disconnect)
  • disconnected ({ code, reason, wasClean })
  • retry ({ attempt }) before a reconnect attempt delay
  • error (network/protocol)

Connection re‑emits mirror events as mirror:<event> with payload { mirrorName, ... }:

  • start
  • updated (only when a newer item actually replaced stored data)
  • deleted
  • done (end of full sync batch)
  • versionError (triggered resync)
  • resubscribe (automatic after reconnect)
  • error
  • removed (mirror explicitly removed)
  • cleared (mirror internal state cleared)

API Surface

  • SubscriptionClient – low level websocket subscription wrapper.
  • MirrorSync – single mirror controller (dual version tracking).
  • Connection – manages multiple mirrors + namespaced events.
  • ReadonlyMapView – immutable view returned by getMirror() / MirrorSync.load().

Note about mirror helpers

getMirror() is available for custom ad-hoc mirrors but is seldom used in most integrations. The more commonly used helper is getPredefinedMirror() which returns mirrors for known server-side definitions (IDs and field payload shapes) and avoids having to supply the raw GraphQL subscription yourself. Check src/definitions for available predefined mirror names and subscription fragments.

Notes

  • Always call connect() explicitly; no implicit lazy connect.
  • VERSION_ERROR triggers automatic full resync (re-arms loaded).
  • First top-level field in GraphQL subscription payload is treated as the sync envelope.
  • Provide webSocketImpl manually if bundling for environments without a global WebSocket and you do NOT want ws as fallback.
  • When proxy env vars are set in Node, SubscriptionClient prefers ws (allowing external agent configuration); browsers ignore these env vars.

Build & Publish

TypeScript sources compile to dist/.

Scripts:

npm run build       # compile
npm run publish:dry # preview publish contents
npm run release     # build + publish (public)

Developer notes (recent refactor)

This repository recently split GraphQL subscription template literals into per-property modules to make maintenance easier:

  • Subscription templates: src/definitions/subscriptions/*.ts — one file per subscription property.
  • Shared fragments: src/definitions/fragments.ts — common fragment string constants used by the subscription files.
  • Compatibility surface: src/definitions/mirrors.ts now re-exports the assembled Subscriptions, RequiredMirrors, TempSubscriptions, and the fragment constants to preserve the original API.

Packaging and what is published

  • The npm package only ships the compiled build output. package.json lists dist and dist-compat in the files field, so the raw TypeScript source files under src/ (including src/definitions/subscriptions/*.ts) are not included in the published package by default.
  • The TypeScript compiler emits JavaScript (to dist) and declaration files (.d.ts) when you run the build; those compiled artifacts are what go into the package.

How to verify locally (PowerShell)

  1. Build the project:
npm run build
  1. Inspect the compiled dist tree to see the compiled outputs for the subscription modules:
Get-ChildItem -Recurse .\dist | Select-Object FullName
  1. See exactly what would be published (dry-run):
npm pack --dry-run

If you want .ts sources included in the published package, add src to the files array in package.json or add a copy step that places sources in dist/dist-compat before publishing; then verify with npm pack --dry-run.

CI / GitHub Actions

This repository includes a workflow that publishes the package to npm when changes are pushed to main and when a GitHub Release is published. The workflow expects a repository secret named NPM_TOKEN containing a valid npm automation token.

To create and add the secret:

  1. Generate an npm token on https://www.npmjs.com/ under your account settings (Access Tokens -> Automation).
  2. In the GitHub repository, go to Settings → Secrets → Actions and add a new secret named NPM_TOKEN with the token value.

The workflow builds both dist and dist-compat and then runs npm publish --access public. If you need to restrict publishing (for example, to skip on regular pushes), adjust the workflow triggers in .github/workflows/publish.yml.

License

MIT