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

@nimbio/community-api

v0.1.1

Published

Official TypeScript/JavaScript client for the Nimbio community API (api.nimbio.com) — manage a Nimbio community programmatically from Node, the browser, Deno, Bun, or edge runtimes.

Readme

@nimbio/community-api

Official TypeScript/JavaScript client for the Nimbio community API (api.nimbio.com).

Manage a Nimbio community programmatically: read gate status, open gates, add and manage members and their keys, send community messages, and pull access logs — from Node, the browser, Deno, Bun, or edge runtimes, with full type definitions.

npm install @nimbio/community-api
# or: pnpm add @nimbio/community-api / yarn add @nimbio/community-api / bun add @nimbio/community-api
  • Fully typed — TypeScript-first, ships .d.ts; every response is a typed object with autocomplete.
  • Promise-based — one async/await API. Works the same in Node, browsers, Deno, Bun, and edge.
  • Zero runtime dependencies — built on the platform fetch.
  • ESM + CommonJSimport and require both work.
  • Test vs live — inferred automatically from your API key.
  • Built-in retries, a clean error hierarchy, and log pagination helpers.

Requirements: Node.js 18+ (for global fetch), or any runtime with a fetch global. On older runtimes, pass your own via the fetch option.


Quickstart

import { NimbioClient } from "@nimbio/community-api";

const client = new NimbioClient("nimbio_test_your_key_here");

// Who am I?
const me = await client.me();
console.log(me.accountId);

// Read gate status
for (const latch of (await client.community.gateStatus()).latches) {
  console.log(latch.latchName, "->", latch.status);
}

// Open a gate. A test key simulates; a live key fires the gate.
const result = await client.community.open("latch-id-123", { note: "front gate" });
console.log(result.result); // "simulated" (test key) or "opened" (live key)

CommonJS is identical, just with require:

const { NimbioClient } = require("@nimbio/community-api");

"Sync or async?"

JavaScript has no idiomatic blocking HTTP, so every method returns a Promise — use await (or .then()). There is a single client class; there is no separate "async client" because there is nothing else to be.


Configuration

new NimbioClient("nimbio_test_...");                              // explicit key
new NimbioClient();                                              // reads NIMBIO_API_KEY
new NimbioClient("nimbio_live_...", { environment: "dev" });     // staging host
new NimbioClient("nimbio_test_...", { baseUrl: "http://localhost:8000" });

Options (second argument):

| Option | Type | Default | Meaning | |------------------|----------------------------|-----------|---------| | environment | "prod" \| "dev" \| "local" | "prod" | Which deployment. Ignored if baseUrl is set. | | baseUrl | string | — | Override the URL entirely. | | timeout | number \| null | 30 | Read timeout in seconds. null disables it. | | maxRetries | number | 2 | Automatic retries for 429/5xx. | | defaultHeaders | Record<string,string> | {} | Extra headers on every request. | | fetch | typeof fetch | global | Custom fetch (testing, proxies, older runtimes). |

Environments: prodapi.nimbio.com, devapi.nimbio.dev, locallocalhost:8000.

Environment variables (used when the matching argument is omitted): NIMBIO_API_KEY, NIMBIO_ENV, NIMBIO_BASE_URL.

Test vs live is the key, not a flag

A nimbio_test_* key never fires a gate or sends a real message — it runs the full pipeline (auth, rate limit, scope, validation) and returns a simulated result. A nimbio_live_* key performs the real action. Both work against any environment. Check which you hold — no network call — with client.mode:

if (client.mode !== "test") throw new Error("refusing to run live");

The whole API

const client = new NimbioClient("nimbio_test_...");

// Account
await client.me();                       // -> Me       (accountId, key usage…)
await client.health();                   // -> Health   (ok, wamp) — never throws on 503
client.mode;                             // -> "test" | "live" | null (no network)

// Reads (community-scoped key required)
await client.community.gateStatus();     // -> GateStatus   (.latches)
await client.community.members();        // -> Members      (.accepted/.unaccepted/.removed)
await client.community.keyStatuses();    // -> KeyStatuses  (.keys, .holdOpens)
await client.community.keys();           // -> CommunityKey[]

// Writes (test key = simulated, live key = real)
await client.community.open("LATCH_ID", { note: "…", idempotencyKey: "…" }); // -> OpenResult
await client.community.message("text");                                       // -> WriteResult
await client.community.addMember("+15551234567", ["KEY_ID"]);                 // -> WriteResult
await client.community.grantKeys(ACCOUNT_COMMUNITY_ID, ["KEY_ID"]);           // -> WriteResult
await client.community.revokeKeys(ACCOUNT_COMMUNITY_ID, ["KEY_ID"], { removeMember: false }); // -> WriteResult
await client.community.setKeysDisabled(ACCOUNT_COMMUNITY_ID, ["KEY_ID"], true);               // -> WriteResult

// Logs (community must have Access Log History enabled)
await client.community.memberAccessLogs(ACCOUNT_COMMUNITY_ID, { window: "last_30" }); // last_30 | 30_60 | 60_90
await client.community.accessLog({ page: 0 });      // -> AccessLogPage   (.logs, .hasMore)
await client.community.gateStatusLog({ page: 0 });  // -> GateStatusLogPage

// Auto-paginate every page
for await (const row of client.community.iterAccessLog()) { /* … */ }
for await (const row of client.community.iterGateStatusLog()) { /* … */ }

ID vocabulary

  • latchId — from gateStatus().latches[i].latchId.
  • keyId (community key id) — from keys()[i].id or keyStatuses(). Used wherever keys are granted/revoked/disabled.
  • accountCommunityId — a member's id, from members().accepted[i].accountCommunityId. Used to address a member.

Return values

Every model exposes typed fields and the full server payload on .raw, so newly added server fields are never lost. Writes return a WriteResult whose .result is the outcome string ("member_added", "keys_granted", "sent", or "simulated" for test-mode calls); extras live on .raw.

const r = await client.community.addMember("+15551234567", ["KEY_ID"]);
r.result;                          // "member_added" (live) or "simulated" (test)
r.simulated;                       // true on a test key
r.raw.account_community_id;        // endpoint-specific extras

Error handling

import {
  APIError,
  AuthenticationError,
  PermissionDeniedError,
  RateLimitError,
  GateNotOpenedError,
} from "@nimbio/community-api";

try {
  await client.community.open("LATCH_ID");
} catch (e) {
  if (e instanceof GateNotOpenedError) {
    // 504 — gate didn't confirm in time
  } else if (e instanceof PermissionDeniedError) {
    console.log(e.code); // e.g. "open_denied", "not_community_key"
  } else if (e instanceof RateLimitError) {
    console.log(e.retryAfter); // seconds, may be null
  } else if (e instanceof APIError) {
    console.log(e.status, e.code, e.message, e.requestId);
  } else {
    throw e; // config/network errors — see below
  }
}

Hierarchy (all extend NimbioError, which extends Error):

  • NimbioConfigError — bad config (missing key, unknown environment). Thrown before any request.
  • APIConnectionError / APITimeoutError — the request never got a response.
  • APIError — any HTTP ≥ 400, with .status, .code, .message, .requestId, .response, .headers. Subclasses: BadRequestError (400), AuthenticationError (401), PermissionDeniedError (403), NotFoundError (404), RateLimitError (429, adds .retryAfter), GateNotOpenedError (504), UpstreamError (502/503), ServerError (other 5xx).

Retries are automatic for 429 and 500/502/503/504 (up to maxRetries, honoring Retry-After, exponential backoff otherwise).


Using in the browser

The client works in browsers, but your API key is a secret — do not ship a nimbio_live_* key in front-end code. For browser use, proxy requests through your own backend, or use a scoped test key in trusted internal tools only. CORS must also be permitted by the API for direct browser calls.


Development

npm install
npm run build       # tsup -> dist/ (ESM + CJS + .d.ts)
npm test            # vitest (fully mocked; no network)
npm run coverage    # vitest with coverage thresholds
npm run lint        # eslint
npm run typecheck   # tsc --noEmit
npm run check       # lint + typecheck + coverage (what CI runs)

Tests mock fetch entirely, so the suite never touches the network. The wire contract lives in exactly one place — the endpoints registry in src/base.ts. When adding or changing an endpoint, edit that registry (and add a model + parser in src/models.ts if the shape is new), then add the thin wrapper method to Community in src/client.ts.


Related

  • Python clientnimbio-community-api (pip install nimbio-community-api). Same API, same model.
  • Public API — the REST service this client wraps (api.nimbio.com, see /docs).

License

MIT © Nimbio