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

@harness-fe/log

v3.2.0

Published

Isomorphic structured logger for Harness-FE. Works in Server Components, Route Handlers, Server Actions, and Client Components — all events land in the same session timeline.

Readme

@harness-fe/log

Isomorphic structured logger for Harness-FE. The same import { log } from '@harness-fe/log' works in Server Components, Route Handlers, Server Actions, and Client Components — every event lands in the same sessions/{sid}/timeline.jsonl on the daemon.

pnpm add @harness-fe/log

You also need at least one runtime SDK installed:

  • Browser only → @harness-fe/runtime
  • Server only → @harness-fe/node-runtime
  • Both (typical Next.js) → both

If neither is installed log is a no-op — calls never throw.

Usage

import { log } from '@harness-fe/log';

log.info('Page loaded');
log.warn('Cart total exceeds threshold', { total, limit });
log.error('Stripe webhook failed', err);
log.debug('Cache hit', { key, ttl });

// Scope chain — prefixes every event with `scope=cart.checkout`
const cartLog = log.scope('cart').scope('checkout');
cartLog.info('Submitting order', { items: items.length });

The last argument can be a plain object — it's treated as structured metadata; everything before it joins as the message.

Variadic + meta

log.info('user', userId, 'clicked', { button: 'buy' });
// → message: "user u_123 clicked", meta: { button: 'buy' }

Where do events land?

Every log.* call emits a t: 'app-log' row to the daemon's session timeline (~/.harness/data/sessions/{sessionId}/timeline.jsonl). The row carries:

| Field | Source | |---|---| | sessionId | Browser: from the runtime client. Server: from node-runtime.getRequestSessionId() → ALS → adapter provider → undefined | | projectId / buildId | Stamped by the daemon from peer registration | | level | 'debug' \| 'info' \| 'warn' \| 'error' | | args | Variadic args passed to the log call | | scope | Dot-joined scope chain, if any | | ts | Unix ms, captured at call site |

Agents tell app-log apart from auto-captured server-log / browser console events, so you can ask things like "show me all log.warn(...) from the cart scope in this session".

sessionId continuity

The defining property of this logger: two log.info() calls under the same page-load — one from a Server Component, one from a Client Component — emit with the same sessionId.

This is what makes timelines coherent. The mechanism:

  • Browser: reads window.__harness_fe_client__.sessionId, set by @harness-fe/runtime after it adopts the SSR seed
  • Server: delegates to @harness-fe/node-runtime.getRequestSessionId(), which walks:
    1. AsyncLocalStorage (populated by withHarnessTracing(handler))
    2. Adapter-supplied provider (Next pushes a React cache()-backed getter via setSessionIdProvider)
    3. undefined → orphan event filed under sessions/server-orphans/

Orphans are correct, not a bug — a log.info() from a background timer or cold-start init has no request to belong to. Better orphaned than misattributed to whatever request happened to be in flight.

Concurrency safety

log is safe under concurrent requests. The server sessionId is read fresh at emit time, not closed over at the import site. Two tabs hitting the same Next process at the same time get their own React cache() scope; their log.* rows go to separate session timelines with zero cross-contamination.

This is verified by @harness-fe/node-runtime's test suite — 28 cases including a Promise.all([renderA, renderB]) with interleaved console.log and explicit assertions.

What's NOT in the payload

By design, log does not include userId in the event payload. The link from session to user is held by the daemon's visitor index (sessionId → SessionMeta.participants → visitor.userId) — agents do one lookup if they need it. Trade-off: one extra index hit instead of any chance of cross-request user leakage.

API

interface Logger {
    debug(...args: unknown[]): void;
    info(...args: unknown[]): void;
    log(...args: unknown[]): void;     // alias of info
    warn(...args: unknown[]): void;
    error(...args: unknown[]): void;
    scope(name: string): Logger;        // returns a new prefixed logger
}

export const log: Logger;

Production behavior

log is gated by the runtime SDK it dispatches to — @harness-fe/runtime and @harness-fe/node-runtime are both NODE_ENV === 'development' only. In production builds log.* becomes a cheap no-op (no network, no writes).

License

MIT