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

@avsbhq/angular

v1.0.0

Published

Angular SDK for the [A vs B](https://app.avsb.cloud) feature flag and A/B testing platform. Supports Angular 17+ with both NgModule-based and standalone bootstrap. Reactive flag reads via RxJS Observables. Includes a structural directive and a pipe for te

Downloads

171

Readme

@avsbhq/angular

Angular SDK for the A vs B feature flag and A/B testing platform. Supports Angular 17+ with both NgModule-based and standalone bootstrap. Reactive flag reads via RxJS Observables. Includes a structural directive and a pipe for template-level flag gating.


1. Install

npm install @avsbhq/angular

Angular 17+, RxJS 7+, and @angular/common are peer dependencies — they must be installed separately (they almost certainly already are).

npm install @angular/core @angular/common rxjs

@avsbhq/core and @avsbhq/browser ship as runtime dependencies; you do not need to install them separately.


2. Quickstart (5 min integration)

Standalone bootstrap (Angular 17+)

// main.ts
import { bootstrapApplication } from '@angular/platform-browser'
import { AppComponent } from './app/app.component'
import { provideAvsb } from '@avsbhq/angular'

bootstrapApplication(AppComponent, {
  providers: [
    provideAvsb({ sdkKey: 'sdk-pub-your-key-here' }),
  ],
})

NgModule bootstrap

// app.module.ts
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { AvsbModule } from '@avsbhq/angular'
import { AppComponent } from './app.component'

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    AvsbModule.forRoot({ sdkKey: 'sdk-pub-your-key-here' }),
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}

Read a flag in a component

import { Component } from '@angular/core'
import { AsyncPipe } from '@angular/common'
import { AvsbService } from '@avsbhq/angular'
import { map } from 'rxjs'

@Component({
  standalone: true,
  imports: [AsyncPipe],
  template: `
    <button *ngIf="showNewCheckout$ | async">
      New checkout flow
    </button>
  `,
})
export class CheckoutButtonComponent {
  private readonly avsb = inject(AvsbService)

  readonly showNewCheckout$ = this.avsb
    .getBoolFlag('new-checkout-flow', false)
    .pipe(map((f) => f.value))
}

3. SDK keys

Client SDK keys are public-facing and safe to ship in Angular bundles. Obtain them from your A vs B project dashboard:

  1. Log in to app.avsb.cloud
  2. Open your project, navigate to Environments
  3. Copy the Client SDK key for the target environment

Store the key in your environment config:

// environment.ts
export const environment = {
  avsbSdkKey: 'sdk-pub-your-key-here',
}
provideAvsb({ sdkKey: environment.avsbSdkKey })

4. Identity (identify, alias, reset)

import { inject } from '@angular/core'
import { AvsbService } from '@avsbhq/angular'

const avsb = inject(AvsbService)

// Set user context after login
avsb.identify({ kind: 'user', key: 'user_123', plan: 'pro', country: 'GB' })

// Alias anonymous -> authenticated (call once, at sign-in)
avsb.alias({ kind: 'user', key: 'anon_xyz' }, { kind: 'user', key: 'user_123' })

// Reset context on logout
avsb.reset()

The identify call updates the evaluation context immediately. Active subscriptions re-evaluate against the new context.


5. Multi-context

Pass a MultiContext to identify when you need to target by multiple independent dimensions (user + organisation + device):

avsb.identify({
  kind: 'multi',
  user: { kind: 'user', key: 'user_123', plan: 'pro' },
  organization: { kind: 'organization', key: 'org_456', tier: 'enterprise' },
})

6. Reading flags

Service — Observable API

AvsbService is the primary interface. Inject it anywhere inside the Angular DI tree.

import { inject } from '@angular/core'
import { AvsbService } from '@avsbhq/angular'

const avsb = inject(AvsbService)

// Full Flag<T> object (value + metadata)
avsb.getFlag<string>('homepage-hero', 'control').subscribe((flag) => {
  console.log(flag.value, flag.source, flag.variationKey)
})

// Typed convenience variants
avsb.getBoolFlag('dark-mode', false).subscribe((f) => { /* f.value is boolean */ })
avsb.getStringFlag('theme', 'light').subscribe((f) => { /* f.value is string */ })
avsb.getNumberFlag('max-items', 10).subscribe((f) => { /* f.value is number */ })
avsb.getJsonFlag<{ ctaText: string }>('hero-config', { ctaText: 'Buy' }).subscribe(...)

// All flags — emits on any change
avsb.getAllFlags().subscribe((flags) => { console.log(Object.keys(flags)) })

All methods require a defaultValue. This value is emitted immediately on subscribe (before the SDK initialises) and whenever the flag is not found.

The Flag<T> object

Every Observable emits a Flag<T> with:

| Field | Type | Description | |---|---|---| | value | T | The variation value | | variationKey | string \| null | Variation key from the datafile | | source | EvaluationSource | Why this value was produced | | ruleId | string \| null | The rule that matched | | ruleType | RuleType \| null | Rule type | | reasons | string[] | Human-readable reasons | | isEnabled() | () => boolean | True when served by a rule and value is truthy | | exists() | () => boolean | False only when source is 'not_found' |

Structural directive

Use *avsbFlag to conditionally render template blocks:

<!-- Render when flag.isEnabled() is true -->
<div *avsbFlag="'new-dashboard'">
  New dashboard content
</div>

<!-- Render when flag.value === 'variant-a' (value-match mode) -->
<div *avsbFlag="'hero-variant'; default: 'control'; when: 'variant-a'">
  Hero variant A
</div>

Import AvsbFlagDirective into your standalone component or NgModule:

import { AvsbFlagDirective } from '@avsbhq/angular'

@Component({ standalone: true, imports: [AvsbFlagDirective], ... })

Pipe

Use avsbFlag in templates for inline flag reads:

<!-- Returns the flag value directly (not the Flag<T> wrapper) -->
<h1>{{ 'homepage-hero' | avsbFlag : 'control' }}</h1>

<!-- With kind hint (currently informational) -->
{{ 'my-flag' | avsbFlag : false : 'bool' }}

The pipe is pure: false — it subscribes lazily and calls ChangeDetectorRef.markForCheck() on updates. Import AvsbFlagPipe:

import { AvsbFlagPipe } from '@avsbhq/angular'

RxJS helpers (injection-context functions)

Standalone functional helpers callable from constructors, inject(), or runInInjectionContext:

import { flag$, flagValue$, boolFlag$, flagReady$ } from '@avsbhq/angular'

// Inside a constructor or injection context:
const hero$ = flag$<string>('homepage-hero', 'control')
const isEnabled$ = boolFlag$('new-feature', false)
const isReady$ = flagReady$() // Observable<boolean>

Available helpers: flag$, flagValue$, boolFlag$, stringFlag$, numberFlag$, jsonFlag$, allFlags$, flagReady$, track, identify, alias.


7. Tracking events

const avsb = inject(AvsbService)

// Simple event
avsb.track('checkout_clicked')

// With metric value and properties
avsb.track('purchase_completed', {
  value: 149.99,
  properties: { currency: 'GBP', productId: 'prod_123' },
})

8. Error handling

const avsb = inject(AvsbService)

// Subscribe to status changes
avsb.status$.subscribe((status) => {
  if (status === 'error') {
    console.error('SDK error:', avsb.error$.getValue())
  }
})

// Wait for ready before making decisions
avsb.onReady().then((result) => {
  if (!result.success) {
    // SDK initialised in degraded mode; defaults are in use
  }
})

Errors from the underlying client bus propagate to status$ ('error') and error$.


9. SSR / hydration

For Angular Universal (SSR), use Mode B (injection) to pass a pre-initialised server-side client:

// server.ts
import { AvsbNodeClient } from '@avsbhq/node'
import { provideAvsb } from '@avsbhq/angular'

const serverClient = new AvsbNodeClient({ sdkKey: process.env.AVSB_SERVER_KEY })
await serverClient.onReady()

bootstrapApplication(AppComponent, {
  providers: [
    provideAvsb({ client: serverClient }),
  ],
})

In Mode B, the Angular service does not close the client on teardown — the server application manages the lifecycle.


10. Graceful shutdown

In Mode A (sdkKey-only config), the SDK client is closed automatically when the Angular application destroys, via ApplicationRef.onDestroy. You may also call service.destroy() explicitly:

const avsb = inject(AvsbService)

// In app teardown / NgOnDestroy:
avsb.destroy()

11. Testing

Use Mode B to inject a mock client in tests:

import { TestBed } from '@angular/core/testing'
import { provideAvsb } from '@avsbhq/angular'
import { createFlag } from '@avsbhq/core'

const mockClient = {
  onReady: () => Promise.resolve({ success: true, source: 'network' }),
  subscribe: (key, cb) => () => {},
  getSnapshot: (key, defaultValue) =>
    createFlag({ value: true, variationKey: 'on', source: 'rule',
                 ruleId: 'r1', ruleType: 'ab_test', reasons: [] }),
  // ... other methods
}

TestBed.configureTestingModule({
  providers: [provideAvsb({ client: mockClient })],
})

Or bypass DI entirely by constructing AvsbService directly with a mock client:

const service = new AvsbService(mockClient)
service.client$.next(mockClient)

12. Migration from other tools

From LaunchDarkly

| LaunchDarkly | @avsbhq/angular | |---|---| | LDProvider | AvsbModule.forRoot() or provideAvsb() | | useFlags() hook | AvsbService.getFlag() Observable | | useLDClient() | inject(AvsbService) | | ldClient.variation(key, default) | AvsbService.getFlag(key, default) | | ldClient.track(key) | AvsbService.track(key) | | ldClient.identify(context) | AvsbService.identify(context) |

The Flag<T> object has no direct LD equivalent — it carries richer metadata. Use flag.value for the variation value and flag.isEnabled() for the boolean gate pattern.


Known limitations

This package targets Angular 17+ standalone bootstrap (JIT-compatible). It ships standard ESM TypeScript output via tsup rather than ng-packagr partial-ivy output. This means:

  • AOT compilation happens at the consumer's build site (not pre-compiled in this package)
  • The package does not include .ngcc metadata or partial-ivy pre-compilation artifacts
  • Consumers on older Angular versions (< 17) may need to verify decorator metadata emission

Future versions may switch to ng-packagr for AOT pre-compilation.