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

@browsonic/angular

v2.11.0

Published

Angular adapter for @browsonic/sdk — ErrorHandler, BrowsonicService, standalone provideBrowsonic() factory. Apache-2.0.

Readme

@browsonic/angular

Angular adapter for @browsonic/sdkErrorHandler drop-in, BrowsonicService injectable, standalone provideBrowsonic() factory, Router instrumentation, HttpClient companion.

Status: 0.3 surface — Angular 17+ standalone style. Module-NgModule apps work via the same providers (see Quickstart — NgModule). Pure TypeScript: no Angular / RxJS runtime dependency in this package's bundle (@angular/core, @angular/router, @angular/common/http, and rxjs all stay peer-only type imports).

Install

npm install @browsonic/sdk @browsonic/angular

@browsonic/sdk and @angular/core (≥17) are peer dependencies.

Quickstart — Angular 17+ standalone

// src/main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { ErrorHandler } from '@angular/core';
import { getBrowsonic } from '@browsonic/sdk';
import { provideBrowsonic, BrowsonicErrorHandler } from '@browsonic/angular';
import { AppComponent } from './app/app.component';

const sdk = getBrowsonic();
sdk.init({ apiEndpoint: 'https://your-ingest-endpoint.test/v1/events' });

bootstrapApplication(AppComponent, {
  providers: [
    ...provideBrowsonic({ sdk }),
    { provide: ErrorHandler, useExisting: BrowsonicErrorHandler },
  ],
});

Inject the service anywhere

import { Component, inject } from '@angular/core';
import { BrowsonicService } from '@browsonic/angular';

@Component({ selector: 'app-buy', template: '<button (click)="buy()">Buy</button>' })
export class BuyComponent {
  private readonly browsonic = inject(BrowsonicService);

  buy(): void {
    try {
      // …purchase logic
    } catch (err) {
      this.browsonic.captureError(err as Error);
    }
  }
}

Quickstart — NgModule (Angular pre-standalone)

provideBrowsonic() is a standalone provider factory, but the values it returns are plain Provider[] — they work in AppModule.providers unchanged. Apps still on @NgModule({ ... }) bootstrap the same way:

// src/app/app.module.ts
import { NgModule, ErrorHandler } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { getBrowsonic } from '@browsonic/sdk';
import { provideBrowsonic, BrowsonicErrorHandler } from '@browsonic/angular';
import { AppComponent } from './app.component';

const sdk = getBrowsonic();
sdk.init({ apiEndpoint: 'https://your-ingest-endpoint.test/v1/events' });

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule],
  providers: [
    ...provideBrowsonic({ sdk }),
    { provide: ErrorHandler, useExisting: BrowsonicErrorHandler },
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}

Same provider tokens, same BrowsonicService injection sites. The only difference is where the providers array lives — bootstrapApplication for standalone, AppModule.providers for NgModule.

Router instrumentation

installRouterInstrumentation(router, options?) subscribes to Router.events, filters for NavigationEnd (via the urlAfterRedirects structural discriminator), and emits a category: 'navigation' breadcrumb on every successful route change. Returns an unsubscribe() callable for HMR-friendly teardown.

import { Component, OnInit, OnDestroy, inject } from '@angular/core';
import { Router } from '@angular/router';
import { installRouterInstrumentation } from '@browsonic/angular';

@Component({ selector: 'app-root', template: '<router-outlet/>' })
export class AppComponent implements OnInit, OnDestroy {
  private readonly router = inject(Router);
  private off?: () => void;

  ngOnInit(): void {
    this.off = installRouterInstrumentation(this.router);
  }

  ngOnDestroy(): void {
    this.off?.();
  }
}

The structural RouterLike shape only requires an events Observable, so test doubles work without @angular/router.

HttpClient companion

createBrowsonicHttpReporter(options?) returns a (request, error) => void callback that captures HttpClient failures to the SDK. Wire it into your own HttpInterceptor so the adapter stays peer-only on @angular/common/http and rxjs:

// src/app/browsonic-http.interceptor.ts
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpHandler, HttpRequest, HttpEvent } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { createBrowsonicHttpReporter } from '@browsonic/angular';

@Injectable()
export class BrowsonicHttpInterceptor implements HttpInterceptor {
  private readonly report = createBrowsonicHttpReporter({
    // Skip Browsonic's own ingest URL so a failed report can't trigger
    // another report (would loop).
    ignoreUrls: [/\/v1\/events$/],
    // Suppress 401 / 404 — surfaced at the UI layer, not in the dashboard.
    ignoreStatuses: [401, 404],
  });

  intercept(req: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
    return next.handle(req).pipe(
      catchError((err: unknown) => {
        this.report(req, err);
        return throwError(() => err);
      }),
    );
  }
}

Register it via HTTP_INTERCEPTORS — standalone or NgModule, both shapes work:

// Standalone (app.config.ts)
import { provideHttpClient, withInterceptorsFromDi, HTTP_INTERCEPTORS } from '@angular/common/http';
providers: [
  provideHttpClient(withInterceptorsFromDi()),
  { provide: HTTP_INTERCEPTORS, useClass: BrowsonicHttpInterceptor, multi: true },
];

// NgModule (AppModule.providers)
import { HTTP_INTERCEPTORS } from '@angular/common/http';
providers: [{ provide: HTTP_INTERCEPTORS, useClass: BrowsonicHttpInterceptor, multi: true }];

The reporter tags the active scope with angular.http.method + angular.http.status, attaches httpUrl (capped at 256 chars) and a truncated httpResponseBody (maxBodyLength: 0 disables body capture entirely). See http-interceptor.ts for all options.

API

BrowsonicErrorHandler

Drop-in for Angular's ErrorHandler provider. Implements the framework's duck-typed handleError(error: unknown): void shape. Calls sdk.captureError, then forwards to console.error so the Angular default dev-tools experience is preserved (consoleFallback: false to disable).

BrowsonicService

Injectable wrapper around the SDK. Methods: setUser, clearUser, captureError, captureMessage, addBreadcrumb, setTag, getSdk.

provideBrowsonic(options?)

Standalone-style provider factory. Returns the providers array that registers BrowsonicErrorHandler and BrowsonicService as singletons. Wire the framework's ErrorHandler token to useExisting: BrowsonicErrorHandler separately so consumers control whether the handler also relays to a custom upstream handler (we don't chain by default — Angular's ErrorHandler is single-binding).

installRouterInstrumentation(router, options?)

Subscribes to a RouterLike.events Observable and emits a category: 'navigation' breadcrumb on every NavigationEnd. Returns an unsubscribe() callable. Structural RouterLike / RouterEventLike types — no @angular/router runtime import. See Router instrumentation.

createBrowsonicHttpReporter(options?)

Returns a (request, error) => void callback that captures HttpClient failures. Filters by ignoreUrls (string or RegExp) and ignoreStatuses. Tags the scope with <ns>.method + <ns>.status, attaches httpUrl + truncated httpResponseBody. Designed to be wired into a consumer-owned HttpInterceptor so the adapter stays peer-only on @angular/common/http and rxjs. See HttpClient companion.

Defensive contract

  • The host app must never crash because reporting failed.
  • All SDK calls are wrapped in try { … } catch {}.
  • BrowsonicErrorHandler.handleError never throws — even when both the SDK and console.error raise, the method returns silently.
  • All forwarders are no-ops when the SDK is unreachable.

Optional /decorated entry-point — @Injectable + signals

If you don't mind @angular/core running at runtime in this package's bundle (rather than the default type-only contract), you can import from @browsonic/angular/decorated to get:

  • BrowsonicDecoratedService@Injectable({ providedIn: 'root' }) version of BrowsonicService. No more provider wiring; just inject(BrowsonicDecoratedService) from any component.
  • provideBrowsonicUserSignal(options?) — provider factory that bridges a WritableSignal<UserContext | null> to sdk.setUser. Set the signal to null to clear the user.
// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideBrowsonicUserSignal } from '@browsonic/angular/decorated';
import { provideBrowsonic, BrowsonicErrorHandler } from '@browsonic/angular';
import { ErrorHandler } from '@angular/core';

export const appConfig: ApplicationConfig = {
  providers: [
    ...provideBrowsonic({ sdk }),
    { provide: ErrorHandler, useExisting: BrowsonicErrorHandler },
    ...provideBrowsonicUserSignal({ initial: null }),
  ],
};

// any component
import { Component, inject } from '@angular/core';
import { BROWSONIC_USER_SIGNAL } from '@browsonic/angular/decorated';

@Component({ selector: 'app-login', template: '...' })
export class LoginComponent {
  private readonly user = inject(BROWSONIC_USER_SIGNAL);
  onLogin(profile: { id: string; email: string }) {
    this.user.set(profile);
  }
}

The default @browsonic/angular entry stays peer-only on @angular/core. Pick /decorated only if you want the decorator + signal ergonomics.

What this package does NOT do

  • NgZone-aware error capture. The default Angular zone catches errors; our ErrorHandler runs inside the zone. If your app uses NgZone.runOutsideAngular, errors there don't reach ErrorHandler — call browsonic.captureError(err) from your try/catch block manually.
  • Server-side rendering capture. Angular SSR runs in Node; the SDK is browser-only. Wire your own server-side telemetry.

License

Apache-2.0. See the repo root LICENSE and the package NOTICE.