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

@zdavison/strangler

v1.0.1

Published

A utility for swapping out implementations at runtime, and comparing them over time to avoid regressions.

Readme

🎋 Strangler

Strangler is a library that helps you build a new API by gradually migrating from an old one.

It's designed to be used with NestJS, but nothing is stopping you from using it anywhere you like.

It provides you a few features:

  • Swapping out a service implementation at runtime using feature flags.
  • Swapping out only individual methods.
  • Falling back to old implementation if the new one is not implemented yet.
  • Optionally running both implementations at the same time and logging any differences in return value, or duration.

Fundamentally, it allows you to do this:

@Module({
  providers: [
    {
      provide: EmailService,
      useFactory: (featureFlagsRepository: FeatureFlagsRepository) =>
        Strangler(
          () => featureFlagsRepository.getStringValue('email.use-v2'),
          new EmailServiceV2(), // new version
          new EmailService(),
          logStranglerComparison('EmailService')
        ),
    },
  ],
  imports: [],
  exports: [],
})
export class EmailModule {}

Overriding individual methods is also supported. Any methods that are not implemented in the NEW service will be called in the OLD service.

  useFactory: (config: ConfigService, featureFlagsRepository: FeatureFlagsRepository) => {
    const oldEmailService = new EmailService(config)
    const newEmailService = new EmailServiceV2(config)
    return Strangler(
      () => featureFlagsRepository.getStringValue('emails.use-v2'),
      {
        // sendEmail is not implemented in the new service, and will fall back to the old implementation.
        sendPromotionalEmail: newEmailService.sendPromotionalEmail,
      }, 
      oldEmailService
    )
  } 

While I wrote Strangler with NestJS in mind, there is no dependency on NestJS and you can use it bare for any object you'd like.

const emailSender = Strangler(
 () => featureFlags.get('emails.use-v2'),
 new SelfHostedEmailService(),
 new SendgridEmailService(),
 logStranglerComparison('EmailService')
)

OnComparison

Strangler accepts an OnComparison function that allows you to handle comparison events yourself however you wish (e.g. updating metrics, logging, etc).

For most cases though, you probably just want to log. logStranglerComparison is included for you to do that easily.

Configuration

Strangler accepts an optional configuration object that allows you to customize its behavior:

interface StranglerConfig {
  /**
   * If the difference in runtime of a method is longer than this, the comparison callback will be called.
   * Value is in milliseconds. Default: 300ms
   */
  acceptableDurationDifference?: number;
  
  /**
   * Logger object with methods for different logging levels.
   * Default: console
   */
  logger?: Partial<typeof console>;
  
  /**
   * The equality function to use to test if results are identical.
   * Default: JSON.stringify(a) === JSON.stringify(b)
   */
  equalityFn?: (a: unknown, b: unknown, parameters?: unknown) => boolean;
  
  /**
   * If true, will wait for the comparison to complete before returning the result.
   * By default, the result is returned immediately after the primary promise completes.
   * Default: false
   */
  waitForComparison?: boolean;
}

Equality Comparison

By default, Strangler uses JSON.stringify to compare results between old and new implementations. You can customize this behavior by providing your own equalityFn in the configuration. This is particularly useful when:

  • Your objects contain circular references
  • You need to ignore certain fields in the comparison
  • You want to implement custom comparison logic

Error Handling

When an error occurs during execution of either implementation, Strangler wraps it in an ExecutionError that includes:

  • The original error (cause)
  • The duration of the execution attempt (duration)

This allows you to track both the error and performance impact of failed executions.

Comparison Modes

Strangler supports four different modes:

  1. 'new': Use only the new implementation
  2. 'old': Use only the old implementation
  3. 'new-compare': Use new implementation but run old in parallel for comparison
  4. 'old-compare': Use old implementation but run new in parallel for comparison

The comparison modes will trigger the OnComparison callback when:

  • The results differ between implementations
  • The execution time difference exceeds acceptableDurationDifference

Limitations

Because calling the feature flag is an async operation, only proxying async methods is supported. In future, we could support sync methods if a sync feature flag provider was added.

🚨 Risks

You must be aware that when using -compare modes, your application will run both implementations at the same time.

This means that if your calls have side-effects, or are not idempotent, you could see unexpected results.

We strongly recommend only using Strangler for APIs that are idempotent, and have no side-effects, such as GET requests that can be executed many times without issue.

Installation

pnpm install @zdavison/strangler

Test

# unit tests
pnpm test