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

@budarin/layered-infra-dev-logger

v1.0.6

Published

Infrastructure-focused dev logger toolkit for layered architectures.

Readme

@budarin/layered-infra-dev-logger

Infrastructure-focused dev logger toolkit for layered architectures.

Supported runtimes

The package is written against globalThis (with guarded fallbacks to standard intrinsics). It is safe to import and call createLayeredLoggerToolkit() in:

  • Browser (document + full Web APIs)
  • Service worker (no window, often no localStorage)
  • Node.js (including Vitest / node:test and other test runners in the default Node realm)

You do not need consumer-side shims such as globalThis.window = globalThis for this library.

Global control (globalControl)

  • By default, control is attached with Object.defineProperty on globalThis (override with globalControl.target).
  • Attachment is best effort: if defineProperty is missing, not callable, or throws (non-configurable property, sealed object, revoked proxy, and similar cases), the failure is swallowed and the rest of the toolkit still works. control on the returned toolkit is always available.
  • In browsers, when attachment succeeds, DevTools usage matches previous behavior (e.g. globalThis.logger).

Persistence (persistence)

  • Default persistence looks for localStorage on globalThis. If it is missing or does not expose getItem / setItem / removeItem as functions, persistence is silently disabled (no throws).
  • You can pass a custom persistence.storage adapter in any environment.
  • Invalid or unreadable stored payloads are cleared best effort; errors during read, parse, or cleanup are not propagated to the caller.

Console output

  • Log methods resolve globalThis.console on each emit. If console or a level method is missing, that emit is a no-op (no throw).

Features

  • Colored scope prefixes in console logs ([ UI ] [ CONTAINER ] ...).
  • Runtime noise control with rules:
    • enable/disable/show/remove/preview/focus/reset/undo.
  • Selector DSL with scope + level + message + args matching:
    • [ scopes ] | levels | message | args.
  • glob and eq: matchers for message and args.
  • Optional persistence via localStorage or custom storage adapter.
  • Optional global control attachment (globalThis.logger by default).

Install

pnpm add @budarin/layered-infra-dev-logger

Quick Start

import { createLayeredLoggerToolkit } from '@budarin/layered-infra-dev-logger';

// Defaults already enabled:
// - global control in DevTools console: globalThis.logger
// - persistence in localStorage
// - scope normalize: trim spaces + uppercase
const toolkit = createLayeredLoggerToolkit();

const base = toolkit.createLogger();
const coreLogger = base.child('CORE').child('USECASE', { color: '#ea580c' });

coreLogger.debug('sync failed', { code: 'E_TIMEOUT', todoId: '019...' });

Layered Project Setup (Composition Root)

import { createLayeredLoggerToolkit, createLayeredLoggers } from '@budarin/layered-infra-dev-logger';

// 1) Create one toolkit in bootstrap/composition root.
const toolkit = createLayeredLoggerToolkit();

// 2) Describe your layered structure once.
const baseLogger = toolkit.createLogger();
const {
    logger,
    uiLogger,
    uiContainerLogger,
    coreLogger,
    coreUseCaseLogger,
    coreAdapterLogger,
    storeLogger,
    serviceLogger,
    networkServiceLogger,
    themeServiceLogger,
} = createLayeredLoggers(baseLogger, {
    APP: {
        alias: 'logger',
        color: '#b45309',
        children: {
            UI: {
                alias: 'uiLogger',
                color: '#16a34a',
                children: {
                    CONTAINER: { alias: 'uiContainerLogger', color: '#22c55e' },
                },
            },
            CORE: {
                alias: 'coreLogger',
                color: '#c2410c',
                children: {
                    USECASE: { alias: 'coreUseCaseLogger', color: '#ea580c' },
                    ADAPTER: { alias: 'coreAdapterLogger', color: '#f59e0b' },
                },
            },
            STORE: { alias: 'storeLogger', color: '#dc2626' },
            SERVICE: {
                alias: 'serviceLogger',
                color: '#2563eb',
                children: {
                    NETWORK: { alias: 'networkServiceLogger', color: '#38bdf8' },
                    THEME: { alias: 'themeServiceLogger' }, // inherits SERVICE color
                },
            },
        },
    },
});

// 3) Wire them into your app boundaries/ports.
initUiLoggerPort(uiLogger);
initUiContainerLoggerPort(uiContainerLogger);
initCoreUseCaseLoggerPort(coreUseCaseLogger);
initCoreAdapterLoggerPort(coreAdapterLogger);
initStoreLoggerPort(storeLogger);
initServiceLoggerPort(serviceLogger);

// 4) Use in runtime code.
coreUseCaseLogger.debug('create todo start', { todoId: '019...' });
networkServiceLogger.warn('network degraded', { quality: 'slow-2g' });

// 5) Manual variant: same idea via child() chain.
const manualCoreUseCaseLogger = baseLogger
    .child('APP', { color: '#b45309' })
    .child('CORE', { color: '#c2410c' })
    .child('USECASE', { color: '#ea580c' });

manualCoreUseCaseLogger.debug('same logger built manually with child()');

Colored log output

Tree root (APP is only one layout)

The big example above uses one top-level key so every branch shares the same first scope tag ([ APP ] …). Same API, two other common shapes:

A — Single root (any name). One first-level segment for the whole tree; it shows up in every branch’s log prefix.

createLayeredLoggers(baseLogger, {
    APP: {
        alias: 'logger',
        children: {
            CORE: { alias: 'coreLogger', children: { USECASE: { alias: 'coreUseCaseLogger' } } },
        },
    },
});
// → [ APP ] [ CORE ] [ USECASE ] …

B — Multiple roots (no shared “app” segment). Several keys at the first level; each branch has its own prefix chain.

createLayeredLoggers(baseLogger, {
    UI: {
        alias: 'uiLogger',
        children: { CONTAINER: { alias: 'uiContainerLogger' } },
    },
    CORE: {
        alias: 'coreLogger',
        children: { USECASE: { alias: 'coreUseCaseLogger' } },
    },
});
// → [ UI ] [ CONTAINER ] …
// → [ CORE ] [ USECASE ] …

Why this pattern matters

  • You keep one source of truth for logger config and rule state.
  • Every layer gets stable scope prefixes and colors.
  • Alias is configured directly on each layer node, so no second aliases config is needed.
  • If color is omitted on a node, it inherits nearest parent color automatically.
  • Noise-control rules can target architecture boundaries directly:
    • [ CORE ][ USECASE ] | debug | * | *
    • [ SERVICE ][ NETWORK ] | warn,error | * | *
    • [ UI ][ CONTAINER ][ * ] | debug | *render* | *

DevTools Console Control API

Control API is intended for runtime debugging directly in browser console (globalThis.logger by default).

// mode and rules
logger.showMode();
logger.show('all');

// disable by selector
logger.previewDisable('[ CORE ][ USECASE ] | debug | * | *"code":"E_TIMEOUT"*');
logger.disable('[ CORE ][ USECASE ] | debug | * | *"code":"E_TIMEOUT"*');

// re-enable
logger.enable('[ CORE ][ USECASE ] | debug | * | *"code":"E_TIMEOUT"*');

// remove by id
logger.remove(3);

// remove by selector
logger.previewRemove('[ CORE ][ * ]');
logger.remove('[ CORE ][ * ]');

// “solo” one selector: baseline becomes all-disabled, rules cleared, one enable rule added
logger.focus('[ UI ][ CONTAINER ][ * ] | debug');

// baseline all-enabled again and all rules removed (factory-default filtering)
logger.reset();

// revert the last control change (focus/reset/enable/disable/show/remove/clear, …)
logger.undo();

focus(selector) — Show only logs that match this selector: it sets baseline to all-disabled, drops every existing rule, then adds a single enabled rule for selector. Handy in the console when you want one branch (for example UI under CONTAINER) without turning off many disable rules by hand.

reset() — Restore the default filter state: baseline all-enabled and an empty rule list (nothing explicitly disabled).

undo() — Pop one entry from the control history and restore the previous baseline + rules. History is recorded for focus, reset, enable, disable, remove, clear, and for baseline flips when enable/disable is called with the * selector.

Advanced Options (optional)

Use these only if defaults do not fit your project.

const toolkit = createLayeredLoggerToolkit({
    mode: 'all-disabled',
    scope: {
        // default normalize is already "remove spaces + uppercase"
        normalize: (segment) => segment.trim().toLowerCase(),
    },
    persistence: {
        enabled: false, // default is true
    },
    globalControl: {
        enabled: false, // default is true
        key: 'devLogger',
    },
});

// 2b) You can also build loggers manually with child() when needed.
const manualCoreUseCaseLogger = baseLogger
    .child('APP', { color: '#b45309' })
    .child('CORE', { color: '#c2410c' })
    .child('USECASE', { color: '#ea580c' });

manualCoreUseCaseLogger.debug('manual chain logger is also valid');

Selector DSL

[ scopes ] | levels | message | args

  • scopes: required ([ CORE ][ USECASE ]), prefix mode via [ * ] tail.
  • levels: optional (debug,warn), default *.
  • message: optional (*retry*, eq:Sync failed), default *.
  • args: optional (*"code":"E_TIMEOUT"*, eq:{"code":"E_TIMEOUT"}), default *.

Examples:

  • [ CORE ][ USECASE ]
  • [ CORE ][ USECASE ][ * ] | debug,warn
  • [ UI ][ * ] | * | *retry*
  • [ CORE ][ USECASE ] | debug | * | *"code":"E_TIMEOUT"*

Notes

  • This package is intended for development-time logging and filtering.
  • State persistence is best-effort; invalid or broken storage payload is auto-reset.
  • Built-in globals (Date, JSON, Object, …) are read via globalThis when present so the same bundle works across browser, worker, and Node without referencing window.