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

rockbed

v1.0.1

Published

Small TypeScript primitives for results, events, disposables, async helpers, JSON, assertions, responses, logging, and retries.

Readme

rockbed

rockbed is a small TypeScript primitives package extracted from shared CLI/runtime code. Version 0.2.0 moves the error API to Result-style names and removes unused helper modules.

For 0.1.x migration details, see MIGRATION.md.

import { Result, err, ok } from 'rockbed/error';
import { Emitter } from 'rockbed/event';

Module Map

| Import path | Main exports | Use it for | | --- | --- | --- | | rockbed/assert | assert, assertDefined, assertNever, unreachable | Runtime invariants and TypeScript narrowing. | | rockbed/async | wait, Barrier, defer, withTimeout, pollUntil | Timers, promise coordination, polling, and timeout envelopes. | | rockbed/dispose | IDisposable, EmptyDispose, Disposable, DisposableStore, MutableDisposable, toDisposable | Ownership and cleanup of resources or subscriptions. | | rockbed/error | Result<T>, IOk<T>, IErr, ok, err, errFrom, defineError, isResult, runSafe, cancelledError, timeoutError, interruptedError | Typed result objects instead of throwing for expected failures. | | rockbed/event | Emitter, Event, listenOnce, listener error handlers | Lightweight typed event emitters that return disposables. | | rockbed/glassbox | installGlassbox, getGlassbox, registerGlass, GlassboxHost, check, makeDiagnosis, toSerializable, RingBuffer | Runtime introspection for humans and AI agents. | | rockbed/json | safeJsonParse, safeJsonStringify | JSON helpers that never throw. | | rockbed/keyboard | KeyboardShortcutManager, KeyboardShortcutScope, CompositeKeyboardShortcutScope, describeKeyboardShortcut, isEditableEventTarget | Platform-aware keyboard shortcuts: single chords, multi-key sequences, keydown/keyup, code-based matching, IME-safe guards, and introspectable metadata. | | rockbed/limit | ConcurrencyLimiter, createLimiter, createSerialQueue, SingleFlight, createSingleFlight | Concurrency caps, serial queues, and same-key request de-duplication. | | rockbed/logger | logger, createLogger, setLogLevel, addLogSink, LoggerHub, consoleSink, registerLoggerGlass | Structured, leveled logging that can feed glassbox. | | rockbed/mobx-store | MobxStore, MobxStoreOptions, MobxStoreMutation | Manager-owned MobX state with shallow observable views and Immer mutations. Best for small, flat coordination state — see usage boundaries. | | rockbed/response | IResponse<T>, makeSuccessResponse, makeErrorResponse | Simple { code, msg, data } response objects. | | rockbed/retry | retry, IRetryOptions, RetryJitter | Retry with exponential backoff, jitter, and total timeout. |

Result Pattern

import { Result, defineError, err, ok } from 'rockbed/error';

const invalidPort = defineError(400, 'port must be an integer');

function parsePort(value: string): Result<number> {
  const port = Number(value);
  if (!Number.isInteger(port)) {
    return invalidPort();
  }
  if (port <= 0) {
    return err(400, 'port must be positive');
  }
  return ok(port);
}

Every result keeps the runtime fields ok, code, msg, value, and pair():

const [error, value] = parsePort('8080').pair();
if (error) {
  throw new Error(error.toString());
}
console.log(value);

Dispose Pattern

import { Disposable, toDisposable } from 'rockbed/dispose';

class Session extends Disposable {
  start() {
    this._register(toDisposable(() => console.log('cleanup')));
  }
}

Disposable.dispose() is idempotent. DisposableStore.clear() disposes every child and throws an AggregateError only after all cleanup has been attempted.

Async Pattern

import { Barrier, pollUntil, wait, withTimeout } from 'rockbed/async';

await wait(100);

const barrier = new Barrier();
queueMicrotask(() => barrier.open());
await barrier.wait();

const timed = await withTimeout(fetch('/api/profile'), 1000);
if (!timed.ok) {
  console.error(timed.msg);
}

const ready = await pollUntil(async () => (await fetchStatus()) === 'done', {
  interval: 500,
  timeout: 30_000,
});

Use platform AbortController / AbortSignal for cancellation.

Optional MobX Store

rockbed/mobx-store is a separate subpath and is not re-exported from rockbed. Consumers that use it must install its optional peers:

pnpm add mobx immer

Consumers that only import other rockbed subpaths do not need those packages.

Usage boundaries

MobxStore keeps an immutable snapshot plus a shallow observable view, and every write rebuilds the top-level snapshot object. Keep the following in mind:

  • Keep the state root small and flat; each write is at least O(top-level keys).
  • Store large or complex payloads (big lists, Map/Sets, trees, buffers) as atomic references under a key and swap them wholesale — do not deep-mutate them.
  • Avoid { deep: true } for large object graphs; it proxies every nested node up front and re-wraps reassigned branches on each commit.
  • Park very large or high-churn data outside the store and keep only a handle, id, or derived summary in it.

It is coordination state, not a database. For naturally huge or hot-path-heavy data, use several small stores or an external structure instead.

Retry And Limit

import { createLimiter, SingleFlight } from 'rockbed/limit';
import { retry } from 'rockbed/retry';

const profile = await retry(() => fetchProfile(uid), {
  retries: 5,
  minDelay: 100,
  maxDelay: 5000,
  timeout: 15000,
  shouldRetry: (error) => error.code === 1001,
});

const limiter = createLimiter(4);
const results = await Promise.all(urls.map((url) => limiter.run(() => fetch(url))));
await limiter.onIdle();

const flight = new SingleFlight();
const [a, b] = await Promise.all([
  flight.run('user:42', () => loadUser(42)),
  flight.run('user:42', () => loadUser(42)),
]);

Glassbox And Logger

rockbed/glassbox installs a predictable introspection root on globalThis.__glass. Register capabilities where the state lives:

import { check, makeDiagnosis, registerGlass } from 'rockbed/glassbox';

const handle = registerGlass({
  name: 'player',
  describe: '主播放器的实时状态、最近事件与健康自检',
  snapshot: () => ({
    state: player.paused ? 'paused' : 'playing',
    currentTime: player.currentTime,
    duration: player.duration,
  }),
  diagnose: () =>
    makeDiagnosis([
      check('currentTime in range', player.currentTime <= player.duration + 0.1),
    ]),
});

handle.dispose();

rockbed/logger is structured, leveled logging and can expose recent logs through glassbox:

import { createLogger, setLogLevel } from 'rockbed/logger';

const log = createLogger('player');
log.info('connected', { id: 42 });
log.error('decode failed', error, { src });

setLogLevel('debug');

Keyboard

rockbed/keyboard binds shortcuts to a single event target (defaults to window). Handlers register as disposables; activate() / deactivate() bind and unbind the listeners. Use primary for the platform command key (meta on mac, ctrl elsewhere).

import { KeyboardShortcutManager } from 'rockbed/keyboard';

const shortcuts = new KeyboardShortcutManager({ platform: 'darwin' });

// Single chord. In editable fields (input/textarea/contentEditable) and during
// IME composition it stays inert unless you opt in with allowEditableTarget /
// allowComposition. Non-text inputs (checkbox, radio, range, ...) never block.
shortcuts.registerShortcut({
  id: 'save',
  description: 'Save file',
  category: 'File',
  key: 's',
  modifiers: ['primary'],
  run: () => save(),
});

// Multi-key sequence (VS Code style). Resets after `sequenceTimeout` (default 1000ms).
shortcuts.registerShortcut({
  id: 'save-all',
  sequence: [
    { key: 'k', modifiers: ['primary'] },
    { key: 's', modifiers: ['primary'] },
  ],
  run: () => saveAll(),
});

// Layout-independent matching via `code`, and keyup handling.
shortcuts.registerShortcut({ id: 'fold', code: 'BracketLeft', modifiers: ['primary'], run: fold });
shortcuts.registerShortcut({ id: 'pan-stop', key: ' ', eventType: 'keyup', run: stopPan });

run may return false to signal "not handled", letting the manager fall through to the next matching shortcut. Registering an id again replaces the previous one.

Build help sheets / command palettes from the registry:

for (const info of shortcuts.describeShortcuts()) {
  // { id, description, category, kind, eventType, keys }  e.g. keys: '⌘K ⌘S'
  render(info.description, info.keys);
}

Group independent managers with CompositeKeyboardShortcutScope to toggle them together.

Precedence for a key event: an in-flight sequence is consumed by that sequence first, then chord/match shortcuts are tried newest-first (with fallthrough), then sequence starts. A standalone chord equal to a sequence's first chord shadows that sequence, so avoid overlapping them. Across managers sharing one target, a shortcut fires once only because handled events call preventDefault(); a shortcut with preventDefault: false can be handled by more than one manager.

Publishing

pnpm typecheck
pnpm build
pnpm publish --registry=https://registry.npmjs.org

The npm package includes dist, src, and this README. Keeping source files in the published package makes the available primitives easier for humans and AI coding agents to inspect.