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

@enclosurejs/diagnostics

v1.1.0

Published

Application self-diagnostics for bug reports and support in Enclosure apps

Readme

@enclosurejs/diagnostics — Application self-diagnostics for bug reports and support

[!IMPORTANT] This package collects build info, platform details, lifecycle phase, capabilities, config, settings, and extension data into structured snapshots and health reports. Sensitive keys are automatically redacted. The output is designed for pasting into support tickets or rendering in a built-in diagnostics panel widget.

The Problem

When users report bugs in desktop apps, support teams need to know: which version, which platform, what config, what's healthy and what's broken. Manually gathering this information is tedious and error-prone — users forget details, developers hardcode console.log dumps, and sensitive data leaks into bug reports.

@enclosurejs/diagnostics solves this by providing a single DiagnosticsService that aggregates everything the DI context knows — build stamp, backend platform, lifecycle phase, capabilities, config, settings — into a structured snapshot with automatic redaction of sensitive keys. Extensions can contribute their own sections via DiagnosticProvider.

Architecture

┌──────────────────────────────────────────────────────────┐
│                  DiagnosticsService                       │
│  ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐  │
│  │   snapshot()  │ │   health()   │ │   toReport()     │  │
│  └──────┬───────┘ └──────┬───────┘ └────────┬─────────┘  │
│         │                │                   │            │
│  ┌──────▼────────────────▼───────────────────▼─────────┐  │
│  │  DI Context (reads tokens):                         │  │
│  │  BackendToken · LifecycleToken · ConfigToken        │  │
│  │  SettingsToken · SystemInfoToken · capabilities()   │  │
│  └─────────────────────────────────────────────────────┘  │
│  ┌─────────────────────────────────────────────────────┐  │
│  │  DiagnosticProvider[] (extension data + health)     │  │
│  └─────────────────────────────────────────────────────┘  │
│  ┌─────────────────────────────────────────────────────┐  │
│  │  redact(obj, patterns) — glob-based key redaction   │  │
│  └─────────────────────────────────────────────────────┘  │
├──────────────────────────────────────────────────────────┤
│  Optional: WidgetRegistry panel (diagnostics-panel slot) │
└──────────────────────────────────────────────────────────┘

Dependency rule: diagnostics imports core, and optionally config + settings (peer dependencies).

Quick Start

import { createApp } from '@enclosurejs/core';
import { createConfigModule } from '@enclosurejs/config';
import { createLoggingModule } from '@enclosurejs/logging';
import { createDiagnosticsModule, DiagnosticsToken } from '@enclosurejs/diagnostics';

const app = createApp({
    modules: [
        createConfigModule({ app: 'MyApp', version: '2.0.0' }),
        createLoggingModule(),
        createDiagnosticsModule({
            widget: true, // register diagnostics panel in WidgetRegistry
            redactPatterns: ['*password*', '*secret*', '*token*', '*key*', 'email'],
        }),
    ],
});

await app.start();

// Collect a snapshot
const diag = app.context.use(DiagnosticsToken);
const snap = diag.snapshot();
console.warn(snap.build.version); // '2.0.0'

// Run health checks
const report = await diag.health();
console.warn(report.status); // 'ok' | 'warn' | 'fail'

// Human-readable text for support tickets
const text = diag.toReport();

How It Works

Snapshot Collection

snapshot() reads from DI tokens that are already bound by other modules:

  • Build infoConfigToken provides app, version, mode, revision, dirty, tag
  • PlatformBackendToken.platform + navigator.userAgent + process.versions.node
  • Capabilitiescontext.capabilities() sorted alphabetically
  • LifecycleLifecycleToken.phase
  • Config — full config object, redacted
  • SettingsSettingsToken.current, redacted (omitted when not bound)
  • Extensions — each DiagnosticProvider.collect() result keyed by provider name

All tokens are resolved via tryUse() — missing tokens produce 'unknown' values, never throw.

Redaction

redact(obj, patterns?) deep-clones an object and replaces values whose key matches any glob pattern with '[REDACTED]'. Default patterns: *password*, *secret*, *token*, *key*. Patterns are case-insensitive. * matches any substring.

Health Checks

health() runs built-in checks (lifecycle phase, config availability, system info) plus all provider health checks. Status propagates worst-wins: fail > warn > ok.

Widget

When widget: true and WidgetRegistryToken is bound, a __diagnostics-panel widget is registered in the diagnostics-panel slot. It renders health status, build info, platform, capabilities, config, settings, and extensions with a Refresh button and a Copy Report button.

API

| Export | Kind | Purpose | | --------------------------- | --------- | ------------------------------------------------------------------- | | DiagnosticsService | interface | Snapshot, health check, and text report contract | | DiagnosticsServiceImpl | class | Implementation that reads from DI context + providers | | DiagnosticsToken | token | Resolve DiagnosticsService from DI | | createDiagnosticsModule | factory | Creates a Module that wires diagnostics into the app | | redact(obj, patterns?) | fn | Deep-clone with glob-based key redaction | | registerDiagnosticsWidget | fn | Registers the diagnostics panel widget in a WidgetRegistry | | DiagnosticsOptions | type | Options for createDiagnosticsModule (providers, patterns, widget) | | DiagnosticSnapshot | type | Structured snapshot shape (build, platform, capabilities, ...) | | HealthReport | type | { status, checks } — aggregated health result | | HealthCheck | type | { name, status, message? } — individual check result | | DiagnosticProvider | type | Extension point for modules/plugins to contribute diagnostics data | | DiagnosticsWidgetOptions | type | Widget slot and order overrides |

Configuration

createDiagnosticsModule accepts DiagnosticsOptions:

| Option | Type | Default | Description | | ---------------- | ------------------------------- | ------------------------------------------------ | -------------------------------------------- | | redactPatterns | readonly string[] | ['*password*', '*secret*', '*token*', '*key*'] | Glob patterns for sensitive key redaction | | providers | readonly DiagnosticProvider[] | [] | Initial extension providers | | widget | boolean | false | Register diagnostics panel in WidgetRegistry |

Types Exported

| Type | Used by | | ------------------------------ | --------------------------------- | | DiagnosticsService | any code consuming diagnostics | | DiagnosticSnapshot | snapshot consumers | | HealthReport / HealthCheck | health check consumers | | DiagnosticProvider | modules/plugins contributing data | | DiagnosticsOptions | module creation | | DiagnosticsWidgetOptions | widget registration |

Safety

Type Safety

  • All snapshot fields have explicit readonly types — no any in the public surface.
  • DiagnosticProvider.collect() returns Record<string, unknown> — callers own the shape.
  • HealthCheck.status is a string union ('ok' | 'warn' | 'fail'), not an open string.

Error Safety

  • All DI tokens are resolved via tryUse() — missing tokens produce 'unknown', never throw.
  • SystemInfoService calls in health() are wrapped in try/catch — failures degrade to 'warn'.
  • redact() deep-clones — the original object is never mutated.

Runtime Safety

  • Module declares requires: ['config', 'logging'] — won't install before its dependencies.
  • Widget registration is guarded by tryUse(WidgetRegistryToken) — no crash when no frontend.

Benchmarks

No benchmarks — diagnostics is not a hot path. Snapshot collection and health checks run on-demand (user clicks Refresh or pastes a report). The redact() function is O(n) in the size of the config object.

Bundle Size

| Output | File | Size | | ------------ | ------------ | -------- | | Runtime (JS) | index.js | 13.03 KB | | Types (DTS) | index.d.ts | 4.09 KB | | Total | | 17.12 KB |

External dependencies (@enclosurejs/core, @enclosurejs/config, @enclosurejs/settings) are not bundled — they are peer dependencies.

Quality

| Metric | Value | | --------------------- | ------------------------------------------------------------------------------------ | | Unit tests | 51 (all pass) | | Test files | 3 (service.test.ts, redact.test.ts, module.test.ts) | | Source files | 6 (index.ts, types.ts, service.ts, redact.ts, module.ts, widget.ts) | | External dependencies | 0 | | Peer dependencies | @enclosurejs/core (required), @enclosurejs/config + @enclosurejs/settings (optional) | | Coverage thresholds | statements >= 90%, branches >= 85%, functions >= 95%, lines >= 90% |

Quality Layers

Layer 1: STATIC ANALYSIS (every commit)
  tsc --noEmit        strict mode, zero errors
  eslint              ESLint 9 flat config, zero warnings
  prettier --check    formatting

Layer 2: UNIT TESTS (every commit)
  51 tests            snapshot, health checks, toReport, providers,
                      redaction patterns, DI integration, widget registration
  v8 coverage         statements >= 90%, branches >= 85%, functions >= 95%, lines >= 90%

Layer 3: BENCHMARKS
  N/A                 on-demand diagnostics — no hot path

Layer 4: PACKAGE HEALTH
  0 external deps     pure TypeScript + @enclosurejs/core
  tsup build          ESM + DTS output, single entrypoint

File Structure

packages/diagnostics/
├── src/
│   ├── index.ts              Barrel: all public exports
│   ├── types.ts              DiagnosticsService, DiagnosticSnapshot, HealthReport, DiagnosticProvider
│   ├── service.ts            DiagnosticsServiceImpl — aggregates DI tokens into snapshots
│   ├── redact.ts             redact(obj, patterns?) — glob-based key redaction
│   ├── module.ts             createDiagnosticsModule, DiagnosticsToken
│   ├── widget.ts             registerDiagnosticsWidget — WidgetRegistry panel
│   └── __tests__/
│       ├── service.test.ts   25 tests — snapshot, health, toReport, providers, system-info
│       ├── redact.test.ts    13 tests — patterns, nesting, arrays, edge cases
│       └── module.test.ts    13 tests — module wiring, options, widget registration
├── package.json
├── tsconfig.json
└── tsup.config.ts

License

MIT